also looking at this
Claude/app issues review e7f207
#16
8 files
+112
-35
| @@ -2,7 +2,7 @@ import { app, appleScript, clipboard, globalShortcuts, nativeWindow, nativeTray, | ||
| 2 | 2 | import { createExecutor } from './execution' |
| 3 | 3 | import { createApplicationIconLoader } from './application-icons' |
| 4 | 4 | import { clipboardDecision } from './clipboard' |
| 5 | import { describeScriptFailure } from './applescript' | |
| 5 | import { PermissionError, describeScriptFailure } from './applescript' | |
| 6 | 6 | import { contactOpenScript } from './contacts' |
| 7 | 7 | import { eventOpenScript } from './calendar' |
| 8 | 8 | import { createSources } from './sources' |
| @@ -72,10 +72,14 @@ export async function registerLauncher(show: () => void): Promise<() => void> { | ||
| 72 | 72 | export const behavior = { paste: false } |
| 73 | 73 | |
| 74 | 74 | const runScript = (source: string) => appleScript.execute(source) |
| 75 | async function script(source: string | null, refusal: string) { | |
| 75 | /** `app` is the application the script talks to, so a refusal names the Automation switch that has to be on. */ | |
| 76 | async function script(source: string | null, app: string, refusal: string) { | |
| 76 | 77 | if (!source) throw new Error(refusal) |
| 77 | 78 | const outcome = await runScript(source) |
| 78 | if (!outcome.ok) throw new Error(`${refusal} ${describeScriptFailure('that app', outcome).message}`) | |
| 79 | if (!outcome.ok) { | |
| 80 | const { message, permission } = describeScriptFailure(app, outcome) | |
| 81 | throw new PermissionError(`${refusal} ${message}`, permission) | |
| 82 | } | |
| 79 | 83 | } |
| 80 | 84 | |
| 81 | 85 | /** Type Command V into the app that regains focus when the palette hides. Needs the Accessibility permission. */ |
| @@ -86,7 +90,7 @@ export async function pasteIntoFrontmost(): Promise<void> { | ||
| 86 | 90 | } |
| 87 | 91 | // The palette is hiding; wait for macOS to hand focus back before sending the keystroke. |
| 88 | 92 | await new Promise(resolve => setTimeout(resolve, 180)) |
| 89 | await script('tell application "System Events" to keystroke "v" using command down', 'Launchpad could not paste.') | |
| 93 | await script('tell application "System Events" to keystroke "v" using command down', 'System Events', 'Launchpad could not paste.') | |
| 90 | 94 | } |
| 91 | 95 | |
| 92 | 96 | export const execute = createExecutor({ |
| @@ -98,9 +102,9 @@ export const execute = createExecutor({ | ||
| 98 | 102 | openUrl: url => shell.openExternal(url), |
| 99 | 103 | openPath: path => shell.openPath(path), |
| 100 | 104 | }), |
| 101 | focusWindow: token => script(windowFocusScript(token), 'Launchpad could not switch to that window.'), | |
| 102 | openContact: id => script(contactOpenScript(id), 'Launchpad could not open that contact.'), | |
| 103 | openEvent: uid => script(eventOpenScript(uid), 'Launchpad could not open that event.'), | |
| 105 | focusWindow: token => script(windowFocusScript(token), 'System Events', 'Launchpad could not switch to that window.'), | |
| 106 | openContact: id => script(contactOpenScript(id), 'Contacts', 'Launchpad could not open that contact.'), | |
| 107 | openEvent: uid => script(eventOpenScript(uid), 'Calendar', 'Launchpad could not open that event.'), | |
| 104 | 108 | expandText: text => expandSnippet(text, { now: new Date(), uuid: () => crypto.randomUUID(), clipboard: () => clipboard.readText().then(value => value ?? '') }), |
| 105 | 109 | pasteText: async () => { if (behavior.paste) await pasteIntoFrontmost() }, |
| 106 | 110 | }) |