/** * The shortcut sheet (⌘/): every action that has a key, grouped, in the * person's own notation. It reads the action registry, so it never drifts from * what the keys actually do. */ import { Fragment, type RefObject } from "react"; import { useAppActions } from "../../lib/actions/actions-provider"; import { applicableChords, chordLabel, type Chord } from "../../lib/actions/chords"; import { appActions, findAction, type ActionGroup, type AppAction, } from "../../lib/actions/registry"; import { Dialog, DialogContent, DialogDescription, DialogTitle } from "../../../ui/dialog"; import { Keycaps } from "./keycaps"; const GROUPS: ReadonlyArray<{ group: ActionGroup; label: string }> = [ { group: "chat", label: "Chats" }, { group: "navigate", label: "Move around" }, { group: "view", label: "View" }, { group: "help", label: "Help" }, ]; /** Titles that read better on the sheet than in menus, where the chat is implied. */ const SHEET_TITLES: Record = { "chat.copyLink": "Copy chat link", "chat.pin": "Pin or unpin chat", "go.inbox": "Go to inbox", "go.previousChat": "Previous chat", "go.nextChat": "Next chat", "go.pinnedChat": "Pinned chats 1 to 9", "commandMenu.open": "Search chats and actions", }; type SheetRow = { action: AppAction; title: string; chords: Chord[]; summary?: string | undefined; /** Only the desktop app has this key. */ desktopOnly: boolean; }; export default function ShortcutSheet(props: { open: boolean; onOpenChange: (open: boolean) => void; /** Where focus goes back to on close. */ returnFocus: RefObject; }) { const actions = useAppActions(); if (!actions) return null; const { platform, surface } = actions.env; const menuAction = findAction("commandMenu.open"); const menuChord = menuAction ? actions.chordsFor(menuAction)[0] : undefined; const rows: SheetRow[] = appActions().flatMap((action) => { const here = applicableChords(action.chords, surface, platform); const desktop = surface === "web" ? applicableChords(action.chords, "desktop", platform) : []; if (here.length === 0 && desktop.length === 0) return []; const title = SHEET_TITLES[action.id] ?? (typeof action.title === "string" ? action.title : action.title(actions.env, null)); const summary = action.chordSummary?.(platform); return [ { action, title, chords: here.length > 0 ? here : desktop, summary, desktopOnly: here.length === 0, }, ]; }); return ( { event.preventDefault(); (event.currentTarget as HTMLElement | null)?.focus(); }} onCloseAutoFocus={(event) => { const target = props.returnFocus.current; if (target?.isConnected) { event.preventDefault(); target.focus({ preventScroll: true }); } }} >
Keyboard shortcuts {menuChord ? ( <> Press {" "} anywhere to find a chat or run any action. ) : ( "Every action is also in the command menu." )}
{GROUPS.map(({ group, label }) => { const groupRows = rows.filter((row) => row.action.group === group); if (groupRows.length === 0) return null; return (

{label}

{groupRows.map((row) => (
{row.title} {row.desktopOnly ? ( Desktop app ) : null}
{row.summary ? ( ) : ( row.chords.map((chord, index) => ( {index > 0 ? or : null} )) )}
))}
); })}
); }