Add keyboard shortcuts system with reusable hint components

- Implement centralized keyboard registry with 20+ shortcuts
- Add instance navigation (Cmd+[1-9], Cmd+[/])
- Add session navigation (Cmd+Shift+[1-9], Cmd+Shift+[/])
- Add agent/model cycling (Tab, Cmd+Shift+M)
- Add input shortcuts (Cmd+P focus, Cmd+K clear, ↑↓ history)
- Add command palette (Cmd+Shift+P) with 8 MVP commands
- Implement message history per folder in IndexedDB (max 100)
- Create reusable Kbd and HintRow components
- Replace all keyboard hint rendering with consistent components
- Use text-based shortcuts (Cmd+Shift+M) for clarity
This commit is contained in:
Shantur Rathore
2025-10-23 20:18:45 +01:00
parent 3c5c4755b8
commit 4c98a3df06
21 changed files with 1302 additions and 143 deletions

54
src/components/kbd.tsx Normal file
View File

@@ -0,0 +1,54 @@
import { Component, JSX, For } from "solid-js"
import { isMac } from "../lib/keyboard-utils"
interface KbdProps {
children?: JSX.Element
shortcut?: string
class?: string
}
const Kbd: Component<KbdProps> = (props) => {
const parts = () => {
if (props.children) return [{ text: props.children, isModifier: false }]
if (!props.shortcut) return []
const result: { text: string | JSX.Element; isModifier: boolean }[] = []
const shortcut = props.shortcut.toLowerCase()
const tokens = shortcut.split("+")
tokens.forEach((token, i) => {
const trimmed = token.trim()
if (trimmed === "cmd" || trimmed === "command") {
result.push({ text: isMac() ? "Cmd" : "Ctrl", isModifier: false })
} else if (trimmed === "shift") {
result.push({ text: "Shift", isModifier: false })
} else if (trimmed === "alt" || trimmed === "option") {
result.push({ text: isMac() ? "Option" : "Alt", isModifier: false })
} else if (trimmed === "ctrl") {
result.push({ text: "Ctrl", isModifier: false })
} else {
result.push({ text: trimmed.toUpperCase(), isModifier: false })
}
})
return result
}
return (
<kbd
class={`font-mono bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded text-xs inline-flex items-center gap-0.5 ${props.class || ""}`}
>
<For each={parts()}>
{(part, index) => (
<>
{index() > 0 && <span class="opacity-50">+</span>}
<span>{part.text}</span>
</>
)}
</For>
</kbd>
)
}
export default Kbd