Skip to content

Commit 9d4be69

Browse files
committed
further plugin improvments
1 parent d968c93 commit 9d4be69

8 files changed

Lines changed: 477 additions & 80 deletions

File tree

interview_coder/AppModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ final class AppModel: NSObject, ObservableObject {
226226
static var globalTTSSkipBack: (() -> Void)? = nil
227227
static var globalTTSSkipForward: (() -> Void)? = nil
228228
static var globalTTSToggleRate: (() -> Void)? = nil
229+
static var globalShowPluginTestHarness: ((AppPluginManager.InstalledPlugin) -> Void)? = nil
229230
// Tab controls (global)
230231
static var globalTabNext: (() -> Void)? = nil
231232
static var globalTabPrev: (() -> Void)? = nil

interview_coder/Apps/AppPlugin.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ protocol AppPlugin {
4848
func settingsView(context: AppSettingsContext) -> AnyView?
4949
// Optional: provide status badges for display in Apps list
5050
func statusBadges() -> [PluginStatusBadge]
51+
// Optional: provide a debug preview of what will be executed for an action
52+
func debugPreview(action: AppAction, context: AppContext) -> String?
5153
}
5254

5355
extension AppPlugin {
@@ -63,4 +65,5 @@ extension AppPlugin {
6365

6466
func settingsView(context: AppSettingsContext) -> AnyView? { nil }
6567
func statusBadges() -> [PluginStatusBadge] { [] }
68+
func debugPreview(action: AppAction, context: AppContext) -> String? { nil }
6669
}

interview_coder/Apps/Default/CalendarPlugin.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ final class CalendarPlugin: AppPlugin {
140140
default: return [PluginStatusBadge("Calendar: Unknown", kind: .info)]
141141
}
142142
}
143+
144+
func debugPreview(action: AppAction, context: AppContext) -> String? {
145+
switch action.id {
146+
case "cal_new":
147+
let title = context.conversationText.split(separator: "\n").first.map(String.init) ?? "New Appointment"
148+
let durMin = Int(context.settings["default_duration_min"] ?? "60") ?? 60
149+
let loc = context.settings["default_location"] ?? ""
150+
return "Create event: \(title) (\(durMin) min) \(loc.isEmpty ? "" : "@ " + loc)"
151+
default:
152+
return nil
153+
}
154+
}
143155
}
144156

145157
private struct CalendarSettingsView: View {

interview_coder/Apps/Default/MailPlugin.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,23 @@ final class MailPlugin: AppPlugin {
8383
}
8484

8585
func statusBadges() -> [PluginStatusBadge] {
86-
[PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
86+
var err: NSDictionary?
87+
NSAppleScript(source: "tell application \"Mail\" to get name")?.executeAndReturnError(&err)
88+
if let n = err?[NSAppleScript.errorNumber] as? Int, n == -1743 {
89+
return [PluginStatusBadge("Automation: Denied", kind: .error), PluginStatusBadge("Custom Settings", kind: .success)]
90+
}
91+
if err == nil { return [PluginStatusBadge("Automation: Granted", kind: .success), PluginStatusBadge("Custom Settings", kind: .success)] }
92+
return [PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
93+
}
94+
95+
func debugPreview(action: AppAction, context: AppContext) -> String? {
96+
let subjURL = context.pluginFolderURL.appendingPathComponent("Prompts/email_subject.txt")
97+
let bodyURL = context.pluginFolderURL.appendingPathComponent("Prompts/email_body.txt")
98+
var subject = (try? String(contentsOf: subjURL))?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Draft from Codex++"
99+
var body = (try? String(contentsOf: bodyURL))?.trimmingCharacters(in: .whitespacesAndNewlines) ?? context.conversationText
100+
let signature = context.settings["signature"] ?? ""
101+
if !signature.isEmpty { body += "\n\n" + signature }
102+
return "SUBJECT: \(subject)\n\nBODY:\n\(body)"
87103
}
88104
}
89105

interview_coder/Apps/Default/SafariPlugin.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,25 @@ final class SafariPlugin: AppPlugin {
3535
func performInput(action: AppAction, context: AppContext) async throws -> String { "" }
3636

3737
func performOutput(action: AppAction, context: AppContext) async throws {
38-
// Heuristic: find first URL in the context and open; otherwise open Safari
38+
// Heuristic: find first URL; open in background if configured
39+
let openInBackground = (context.settings["open_background"] ?? "false").lowercased() == "true"
3940
if let url = firstURL(in: context.conversationText) {
40-
if !NSWorkspace.shared.open(url) {
41-
NSWorkspace.shared.launchApplication(withBundleIdentifier: "com.apple.Safari", options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
41+
if openInBackground {
42+
let esc = url.absoluteString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"")
43+
let script = "tell application \"Safari\" to make new document with properties {URL:\"\(esc)\"}"
44+
var err: NSDictionary?
45+
_ = NSAppleScript(source: script)?.executeAndReturnError(&err)
46+
} else {
47+
_ = NSWorkspace.shared.open(url)
4248
}
4349
} else {
44-
NSWorkspace.shared.launchApplication(withBundleIdentifier: "com.apple.Safari", options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
50+
if let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Safari") {
51+
try? NSWorkspace.shared.openApplication(at: appURL, configuration: .init(),
52+
completionHandler: nil)
53+
} else {
54+
var err: NSDictionary?
55+
_ = NSAppleScript(source: "tell application \"Safari\" to activate")?.executeAndReturnError(&err)
56+
}
4557
}
4658
}
4759

@@ -58,7 +70,18 @@ final class SafariPlugin: AppPlugin {
5870
}
5971

6072
func statusBadges() -> [PluginStatusBadge] {
61-
[PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
73+
var err: NSDictionary?
74+
NSAppleScript(source: "tell application \"Safari\" to get name")?.executeAndReturnError(&err)
75+
if let n = err?[NSAppleScript.errorNumber] as? Int, n == -1743 {
76+
return [PluginStatusBadge("Automation: Denied", kind: .error), PluginStatusBadge("Custom Settings", kind: .success)]
77+
}
78+
if err == nil { return [PluginStatusBadge("Automation: Granted", kind: .success), PluginStatusBadge("Custom Settings", kind: .success)] }
79+
return [PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
80+
}
81+
82+
func debugPreview(action: AppAction, context: AppContext) -> String? {
83+
if let url = firstURL(in: context.conversationText) { return "Open URL: \(url.absoluteString)" }
84+
return "Open Safari"
6285
}
6386
}
6487

interview_coder/Apps/Default/TerminalPlugin.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,18 @@ final class TerminalPlugin: AppPlugin {
4747
alert.informativeText = String(commands.prefix(500))
4848
alert.addButton(withTitle: "Send")
4949
alert.addButton(withTitle: "Cancel")
50-
if alert.runModal() != .alertFirstButtonReturn { return }
50+
if let w = NSApp.windows.first(where: { $0.isVisible }) {
51+
NSApp.activate(ignoringOtherApps: true)
52+
let proceed: Bool = await withCheckedContinuation { (cont: CheckedContinuation<Bool, Never>) in
53+
alert.beginSheetModal(for: w) { resp in
54+
cont.resume(returning: resp == .alertFirstButtonReturn)
55+
}
56+
}
57+
if !proceed { return }
58+
} else {
59+
NSApp.activate(ignoringOtherApps: true)
60+
if alert.runModal() != .alertFirstButtonReturn { return }
61+
}
5162
}
5263
// Use AppleScript to run in new Terminal window from this app (triggers proper TCC prompt)
5364
let esc = commands.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"")
@@ -61,7 +72,21 @@ final class TerminalPlugin: AppPlugin {
6172
}
6273

6374
func statusBadges() -> [PluginStatusBadge] {
64-
[PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
75+
var err: NSDictionary?
76+
NSAppleScript(source: "tell application \"Terminal\" to get name")?.executeAndReturnError(&err)
77+
if let n = err?[NSAppleScript.errorNumber] as? Int, n == -1743 {
78+
return [PluginStatusBadge("Automation: Denied", kind: .error), PluginStatusBadge("Custom Settings", kind: .success)]
79+
}
80+
if err == nil { return [PluginStatusBadge("Automation: Granted", kind: .success), PluginStatusBadge("Custom Settings", kind: .success)] }
81+
return [PluginStatusBadge("Automation: Unknown", kind: .info), PluginStatusBadge("Custom Settings", kind: .success)]
82+
}
83+
84+
func debugPreview(action: AppAction, context: AppContext) -> String? {
85+
var commands = context.conversationText.trimmingCharacters(in: .whitespacesAndNewlines)
86+
let prefix = (context.settings["prefix"] ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
87+
if !prefix.isEmpty { commands = prefix + "\n" + commands }
88+
if let wd = context.settings["working_dir"], !wd.isEmpty { commands = "cd \(wd)\n" + commands }
89+
return commands
6590
}
6691
}
6792

0 commit comments

Comments
 (0)