Split workspace into electron and ui packages
This commit is contained in:
23
packages/ui/src/lib/shortcuts/agent.ts
Normal file
23
packages/ui/src/lib/shortcuts/agent.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { keyboardRegistry } from "../keyboard-registry"
|
||||
|
||||
export function registerAgentShortcuts(focusModelSelector: () => void, openAgentSelector: () => void) {
|
||||
const isMac = () => navigator.platform.toLowerCase().includes("mac")
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "focus-model",
|
||||
key: "M",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac(), shift: true },
|
||||
handler: focusModelSelector,
|
||||
description: "focus model",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "open-agent-selector",
|
||||
key: "A",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac(), shift: true },
|
||||
handler: openAgentSelector,
|
||||
description: "open agent",
|
||||
context: "global",
|
||||
})
|
||||
}
|
||||
67
packages/ui/src/lib/shortcuts/escape.ts
Normal file
67
packages/ui/src/lib/shortcuts/escape.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { keyboardRegistry } from "../keyboard-registry"
|
||||
|
||||
type EscapeKeyState = "idle" | "firstPress"
|
||||
|
||||
const ESCAPE_DEBOUNCE_TIMEOUT = 1000
|
||||
|
||||
let escapeKeyState: EscapeKeyState = "idle"
|
||||
let escapeTimeoutId: number | null = null
|
||||
let onEscapeStateChange: ((inDebounce: boolean) => void) | null = null
|
||||
|
||||
export function setEscapeStateChangeHandler(handler: (inDebounce: boolean) => void) {
|
||||
onEscapeStateChange = handler
|
||||
}
|
||||
|
||||
function resetEscapeState() {
|
||||
escapeKeyState = "idle"
|
||||
if (escapeTimeoutId !== null) {
|
||||
clearTimeout(escapeTimeoutId)
|
||||
escapeTimeoutId = null
|
||||
}
|
||||
if (onEscapeStateChange) {
|
||||
onEscapeStateChange(false)
|
||||
}
|
||||
}
|
||||
|
||||
export function registerEscapeShortcut(
|
||||
isSessionBusy: () => boolean,
|
||||
abortSession: () => Promise<void>,
|
||||
blurInput: () => void,
|
||||
closeModal: () => void,
|
||||
) {
|
||||
keyboardRegistry.register({
|
||||
id: "escape",
|
||||
key: "Escape",
|
||||
modifiers: {},
|
||||
handler: () => {
|
||||
const hasOpenModal = document.querySelector('[role="dialog"]') !== null
|
||||
|
||||
if (hasOpenModal) {
|
||||
closeModal()
|
||||
resetEscapeState()
|
||||
return
|
||||
}
|
||||
|
||||
if (isSessionBusy()) {
|
||||
if (escapeKeyState === "idle") {
|
||||
escapeKeyState = "firstPress"
|
||||
if (onEscapeStateChange) {
|
||||
onEscapeStateChange(true)
|
||||
}
|
||||
escapeTimeoutId = window.setTimeout(() => {
|
||||
resetEscapeState()
|
||||
}, ESCAPE_DEBOUNCE_TIMEOUT)
|
||||
} else if (escapeKeyState === "firstPress") {
|
||||
resetEscapeState()
|
||||
abortSession()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resetEscapeState()
|
||||
blurInput()
|
||||
},
|
||||
description: "cancel/close",
|
||||
context: "global",
|
||||
})
|
||||
}
|
||||
23
packages/ui/src/lib/shortcuts/input.ts
Normal file
23
packages/ui/src/lib/shortcuts/input.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { keyboardRegistry } from "../keyboard-registry"
|
||||
|
||||
export function registerInputShortcuts(clearInput: () => void, focusInput: () => void) {
|
||||
const isMac = () => navigator.platform.toLowerCase().includes("mac")
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "clear-input",
|
||||
key: "k",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac() },
|
||||
handler: clearInput,
|
||||
description: "clear input",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "focus-input",
|
||||
key: "p",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac() },
|
||||
handler: focusInput,
|
||||
description: "focus input",
|
||||
context: "global",
|
||||
})
|
||||
}
|
||||
118
packages/ui/src/lib/shortcuts/navigation.ts
Normal file
118
packages/ui/src/lib/shortcuts/navigation.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { keyboardRegistry } from "../keyboard-registry"
|
||||
import { instances, activeInstanceId, setActiveInstanceId } from "../../stores/instances"
|
||||
import { getSessionFamily, activeSessionId, setActiveSession, activeParentSessionId } from "../../stores/sessions"
|
||||
|
||||
export function registerNavigationShortcuts() {
|
||||
const isMac = () => navigator.platform.toLowerCase().includes("mac")
|
||||
|
||||
const buildNavigationOrder = (instanceId: string): string[] => {
|
||||
const parentId = activeParentSessionId().get(instanceId)
|
||||
if (!parentId) return []
|
||||
|
||||
const familySessions = getSessionFamily(instanceId, parentId)
|
||||
if (familySessions.length === 0) return []
|
||||
|
||||
const [parentSession, ...childSessions] = familySessions
|
||||
if (!parentSession) return []
|
||||
|
||||
const sortedChildren = childSessions.slice().sort((a, b) => b.time.updated - a.time.updated)
|
||||
|
||||
return [parentSession.id, "info", ...sortedChildren.map((session) => session.id)]
|
||||
}
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "instance-prev",
|
||||
key: "[",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac() },
|
||||
handler: () => {
|
||||
const ids = Array.from(instances().keys())
|
||||
if (ids.length <= 1) return
|
||||
const current = ids.indexOf(activeInstanceId() || "")
|
||||
const prev = current <= 0 ? ids.length - 1 : current - 1
|
||||
if (ids[prev]) setActiveInstanceId(ids[prev])
|
||||
},
|
||||
description: "previous instance",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "instance-next",
|
||||
key: "]",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac() },
|
||||
handler: () => {
|
||||
const ids = Array.from(instances().keys())
|
||||
if (ids.length <= 1) return
|
||||
const current = ids.indexOf(activeInstanceId() || "")
|
||||
const next = (current + 1) % ids.length
|
||||
if (ids[next]) setActiveInstanceId(ids[next])
|
||||
},
|
||||
description: "next instance",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "session-prev",
|
||||
key: "[",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac(), shift: true },
|
||||
handler: () => {
|
||||
const instanceId = activeInstanceId()
|
||||
if (!instanceId) return
|
||||
|
||||
const navigationIds = buildNavigationOrder(instanceId)
|
||||
if (navigationIds.length === 0) return
|
||||
|
||||
const currentActiveId = activeSessionId().get(instanceId)
|
||||
let currentIndex = navigationIds.indexOf(currentActiveId || "")
|
||||
|
||||
if (currentIndex === -1) {
|
||||
currentIndex = navigationIds.length - 1
|
||||
}
|
||||
|
||||
const targetIndex = currentIndex <= 0 ? navigationIds.length - 1 : currentIndex - 1
|
||||
const targetSessionId = navigationIds[targetIndex]
|
||||
|
||||
setActiveSession(instanceId, targetSessionId)
|
||||
},
|
||||
description: "previous session",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "session-next",
|
||||
key: "]",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac(), shift: true },
|
||||
handler: () => {
|
||||
const instanceId = activeInstanceId()
|
||||
if (!instanceId) return
|
||||
|
||||
const navigationIds = buildNavigationOrder(instanceId)
|
||||
if (navigationIds.length === 0) return
|
||||
|
||||
const currentActiveId = activeSessionId().get(instanceId)
|
||||
let currentIndex = navigationIds.indexOf(currentActiveId || "")
|
||||
|
||||
if (currentIndex === -1) {
|
||||
currentIndex = 0
|
||||
}
|
||||
|
||||
const targetIndex = (currentIndex + 1) % navigationIds.length
|
||||
const targetSessionId = navigationIds[targetIndex]
|
||||
|
||||
setActiveSession(instanceId, targetSessionId)
|
||||
},
|
||||
description: "next session",
|
||||
context: "global",
|
||||
})
|
||||
|
||||
keyboardRegistry.register({
|
||||
id: "switch-to-info",
|
||||
key: "l",
|
||||
modifiers: { ctrl: !isMac(), meta: isMac(), shift: true },
|
||||
handler: () => {
|
||||
const instanceId = activeInstanceId()
|
||||
if (instanceId) setActiveSession(instanceId, "info")
|
||||
},
|
||||
description: "info tab",
|
||||
context: "global",
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user