diff --git a/packages/app/src/context/command.tsx b/packages/app/src/context/command.tsx index 39bd11a1b..dc5228b82 100644 --- a/packages/app/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -24,6 +24,15 @@ function normalizeKey(key: string) { return key.toLowerCase() } +function signature(key: string, ctrl: boolean, meta: boolean, shift: boolean, alt: boolean) { + const mask = (ctrl ? 1 : 0) | (meta ? 2 : 0) | (shift ? 4 : 0) | (alt ? 8 : 0) + return `${key}:${mask}` +} + +function signatureFromEvent(event: KeyboardEvent) { + return signature(normalizeKey(event.key), event.ctrlKey, event.metaKey, event.shiftKey, event.altKey) +} + export type KeybindConfig = string export interface Keybind { @@ -223,6 +232,30 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex const suspended = () => suspendCount() > 0 + const palette = createMemo(() => { + const config = settings.keybinds.get(PALETTE_ID) ?? DEFAULT_PALETTE_KEYBIND + const keybinds = parseKeybind(config) + return new Set(keybinds.map((kb) => signature(kb.key, kb.ctrl, kb.meta, kb.shift, kb.alt))) + }) + + const keymap = createMemo(() => { + const map = new Map() + for (const option of options()) { + if (option.id.startsWith(SUGGESTED_PREFIX)) continue + if (option.disabled) continue + if (!option.keybind) continue + + const keybinds = parseKeybind(option.keybind) + for (const kb of keybinds) { + if (!kb.key) continue + const sig = signature(kb.key, kb.ctrl, kb.meta, kb.shift, kb.alt) + if (map.has(sig)) continue + map.set(sig, option) + } + } + return map + }) + const run = (id: string, source?: "palette" | "keybind" | "slash") => { for (const option of options()) { if (option.id === id || option.id === "suggested." + id) { @@ -239,24 +272,18 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex const handleKeyDown = (event: KeyboardEvent) => { if (suspended() || dialog.active) return - const paletteKeybinds = parseKeybind(settings.keybinds.get(PALETTE_ID) ?? DEFAULT_PALETTE_KEYBIND) - if (matchKeybind(paletteKeybinds, event)) { + const sig = signatureFromEvent(event) + + if (palette().has(sig)) { event.preventDefault() showPalette() return } - for (const option of options()) { - if (option.disabled) continue - if (!option.keybind) continue - - const keybinds = parseKeybind(option.keybind) - if (matchKeybind(keybinds, event)) { - event.preventDefault() - option.onSelect?.("keybind") - return - } - } + const option = keymap().get(sig) + if (!option) return + event.preventDefault() + option.onSelect?.("keybind") } onMount(() => { diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index 1023d8df3..5bb6f92ec 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -218,7 +218,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( } function enrich(project: { worktree: string; expanded: boolean }) { - const [childStore] = globalSync.child(project.worktree) + const [childStore] = globalSync.child(project.worktree, { bootstrap: false }) const projectID = childStore.project const metadata = projectID ? globalSync.data.project.find((x) => x.id === projectID) diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx index 58e7fbf83..1c5953b3f 100644 --- a/packages/app/src/context/notification.tsx +++ b/packages/app/src/context/notification.tsx @@ -88,7 +88,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi switch (event.type) { case "session.idle": { const sessionID = event.properties.sessionID - const [syncStore] = globalSync.child(directory) + const [syncStore] = globalSync.child(directory, { bootstrap: false }) const match = Binary.search(syncStore.session, sessionID, (s) => s.id) const session = match.found ? syncStore.session[match.index] : undefined if (session?.parentID) break @@ -115,7 +115,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi } case "session.error": { const sessionID = event.properties.sessionID - const [syncStore] = globalSync.child(directory) + const [syncStore] = globalSync.child(directory, { bootstrap: false }) const match = sessionID ? Binary.search(syncStore.session, sessionID, (s) => s.id) : undefined const session = sessionID && match?.found ? syncStore.session[match.index] : undefined if (session?.parentID) break