summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/context/command.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/desktop/src/context/command.tsx')
-rw-r--r--packages/desktop/src/context/command.tsx243
1 files changed, 0 insertions, 243 deletions
diff --git a/packages/desktop/src/context/command.tsx b/packages/desktop/src/context/command.tsx
deleted file mode 100644
index f91a1cf05..000000000
--- a/packages/desktop/src/context/command.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-import { createMemo, createSignal, onCleanup, onMount, Show, type Accessor } from "solid-js"
-import { createSimpleContext } from "@opencode-ai/ui/context"
-import { useDialog } from "@opencode-ai/ui/context/dialog"
-import { Dialog } from "@opencode-ai/ui/dialog"
-import { List } from "@opencode-ai/ui/list"
-
-const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
-
-export type KeybindConfig = string
-
-export interface Keybind {
- key: string
- ctrl: boolean
- meta: boolean
- shift: boolean
- alt: boolean
-}
-
-export interface CommandOption {
- id: string
- title: string
- description?: string
- category?: string
- keybind?: KeybindConfig
- slash?: string
- suggested?: boolean
- disabled?: boolean
- onSelect?: (source?: "palette" | "keybind" | "slash") => void
-}
-
-export function parseKeybind(config: string): Keybind[] {
- if (!config || config === "none") return []
-
- return config.split(",").map((combo) => {
- const parts = combo.trim().toLowerCase().split("+")
- const keybind: Keybind = {
- key: "",
- ctrl: false,
- meta: false,
- shift: false,
- alt: false,
- }
-
- for (const part of parts) {
- switch (part) {
- case "ctrl":
- case "control":
- keybind.ctrl = true
- break
- case "meta":
- case "cmd":
- case "command":
- keybind.meta = true
- break
- case "mod":
- if (IS_MAC) keybind.meta = true
- else keybind.ctrl = true
- break
- case "alt":
- case "option":
- keybind.alt = true
- break
- case "shift":
- keybind.shift = true
- break
- default:
- keybind.key = part
- break
- }
- }
-
- return keybind
- })
-}
-
-export function matchKeybind(keybinds: Keybind[], event: KeyboardEvent): boolean {
- const eventKey = event.key.toLowerCase()
-
- for (const kb of keybinds) {
- const keyMatch = kb.key === eventKey
- const ctrlMatch = kb.ctrl === (event.ctrlKey || false)
- const metaMatch = kb.meta === (event.metaKey || false)
- const shiftMatch = kb.shift === (event.shiftKey || false)
- const altMatch = kb.alt === (event.altKey || false)
-
- if (keyMatch && ctrlMatch && metaMatch && shiftMatch && altMatch) {
- return true
- }
- }
-
- return false
-}
-
-export function formatKeybind(config: string): string {
- if (!config || config === "none") return ""
-
- const keybinds = parseKeybind(config)
- if (keybinds.length === 0) return ""
-
- const kb = keybinds[0]
- const parts: string[] = []
-
- if (kb.ctrl) parts.push(IS_MAC ? "⌃" : "Ctrl")
- if (kb.alt) parts.push(IS_MAC ? "⌥" : "Alt")
- if (kb.shift) parts.push(IS_MAC ? "⇧" : "Shift")
- if (kb.meta) parts.push(IS_MAC ? "⌘" : "Meta")
-
- if (kb.key) {
- const displayKey = kb.key.length === 1 ? kb.key.toUpperCase() : kb.key.charAt(0).toUpperCase() + kb.key.slice(1)
- parts.push(displayKey)
- }
-
- return IS_MAC ? parts.join("") : parts.join("+")
-}
-
-function DialogCommand(props: { options: CommandOption[] }) {
- const dialog = useDialog()
-
- return (
- <Dialog title="Commands">
- <List
- search={{ placeholder: "Search commands", autofocus: true }}
- emptyMessage="No commands found"
- items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
- key={(x) => x?.id}
- filterKeys={["title", "description", "category"]}
- groupBy={(x) => x.category ?? ""}
- onSelect={(option) => {
- if (option) {
- dialog.close()
- option.onSelect?.("palette")
- }
- }}
- >
- {(option) => (
- <div class="w-full flex items-center justify-between gap-4">
- <div class="flex items-center gap-2 min-w-0">
- <span class="text-14-regular text-text-strong whitespace-nowrap">{option.title}</span>
- <Show when={option.description}>
- <span class="text-14-regular text-text-weak truncate">{option.description}</span>
- </Show>
- </div>
- <Show when={option.keybind}>
- <span class="text-12-regular text-text-subtle shrink-0">{formatKeybind(option.keybind!)}</span>
- </Show>
- </div>
- )}
- </List>
- </Dialog>
- )
-}
-
-export const { use: useCommand, provider: CommandProvider } = createSimpleContext({
- name: "Command",
- init: () => {
- const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
- const [suspendCount, setSuspendCount] = createSignal(0)
- const dialog = useDialog()
-
- const options = createMemo(() => {
- const all = registrations().flatMap((x) => x())
- const suggested = all.filter((x) => x.suggested && !x.disabled)
- return [
- ...suggested.map((x) => ({
- ...x,
- id: "suggested." + x.id,
- category: "Suggested",
- })),
- ...all,
- ]
- })
-
- const suspended = () => suspendCount() > 0
-
- const showPalette = () => {
- if (!dialog.active) {
- dialog.show(() => <DialogCommand options={options().filter((x) => !x.disabled)} />)
- }
- }
-
- const handleKeyDown = (event: KeyboardEvent) => {
- if (suspended()) return
-
- const paletteKeybinds = parseKeybind("mod+shift+p")
- if (matchKeybind(paletteKeybinds, event)) {
- 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
- }
- }
- }
-
- onMount(() => {
- document.addEventListener("keydown", handleKeyDown)
- })
-
- onCleanup(() => {
- document.removeEventListener("keydown", handleKeyDown)
- })
-
- return {
- register(cb: () => CommandOption[]) {
- const results = createMemo(cb)
- setRegistrations((arr) => [results, ...arr])
- onCleanup(() => {
- setRegistrations((arr) => arr.filter((x) => x !== results))
- })
- },
- trigger(id: string, source?: "palette" | "keybind" | "slash") {
- for (const option of options()) {
- if (option.id === id || option.id === "suggested." + id) {
- option.onSelect?.(source)
- return
- }
- }
- },
- keybind(id: string) {
- const option = options().find((x) => x.id === id || x.id === "suggested." + id)
- if (!option?.keybind) return ""
- return formatKeybind(option.keybind)
- },
- show: showPalette,
- keybinds(enabled: boolean) {
- setSuspendCount((count) => count + (enabled ? -1 : 1))
- },
- suspended,
- get options() {
- return options()
- },
- }
- },
-})