/** @jsxImportSource preact */ import {formatKey} from '../keyboard-shortcuts/keyboard-shortcuts'; import type {KeyboardShortcut} from '../keyboard-shortcuts/keyboard-shortcuts'; import type {WidgetPanel, WidgetPanelTheme} from './widget-containers'; import type {JSX} from 'preact'; export type KeyboardShortcutsPanelProps = { /** Optional list of keyboard shortcuts to render in the panel. */ keyboardShortcuts?: KeyboardShortcut[]; /** Optional theme override applied to this panel subtree. */ theme?: WidgetPanelTheme; }; /** * A panel definition representing keyboard shortcut details for a modal/tab container. */ export class KeyboardShortcutsPanel implements WidgetPanel { id = 'keyboard-shortcuts'; title = 'Keyboard Shortcuts'; theme?: WidgetPanelTheme; content: JSX.Element; constructor({keyboardShortcuts = [], theme = 'inherit'}: KeyboardShortcutsPanelProps = {}) { this.theme = theme; this.content = ; } } const KEY_STYLE: JSX.CSSProperties = { borderRadius: '6px', border: '1px solid rgba(148, 163, 184, 0.85)', backgroundColor: 'rgba(248, 250, 252, 1)', padding: '2px 8px', fontSize: '11px', color: 'var(--button-text, #0f172a)', whiteSpace: 'nowrap' }; const SHORTCUT_BADGE_STYLE: JSX.CSSProperties = { display: 'inline-flex', alignItems: 'center', borderRadius: '999px', border: '1px solid rgba(148, 163, 184, 0.75)', backgroundColor: 'rgba(248, 250, 252, 1)', padding: '1px 6px', fontSize: '10px', lineHeight: '12px', color: 'rgb(71, 85, 105)', whiteSpace: 'nowrap' }; const SHORTCUT_ROW_STYLE: JSX.CSSProperties = { display: 'grid', gridTemplateColumns: 'minmax(104px, 148px) minmax(0, 1fr) auto', gap: '10px 14px', alignItems: 'start', padding: '10px 0', borderBottom: '1px solid rgba(226, 232, 240, 0.8)' }; const SHORTCUT_KEYS_STYLE: JSX.CSSProperties = { display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', gap: '10px', minWidth: 0 }; const SHORTCUT_DESCRIPTION_STYLE: JSX.CSSProperties = { fontSize: '13px', lineHeight: '18px', fontWeight: 500, color: 'rgb(71, 85, 105)', textAlign: 'left', minWidth: 0, width: '100%' }; const SHORTCUT_BADGES_STYLE: JSX.CSSProperties = { display: 'flex', alignItems: 'flex-start', gap: '6px', flexWrap: 'wrap', justifySelf: 'end' }; const SHORTCUT_CHORD_STYLE: JSX.CSSProperties = { display: 'inline-flex', alignItems: 'center', gap: '2px', color: 'rgb(51, 65, 85)', fontSize: '11px', whiteSpace: 'nowrap' }; function KeyboardSettingsPanelContent({ keyboardShortcuts }: { keyboardShortcuts: KeyboardShortcut[]; }) { const shortcutRows = buildKeyboardShortcutRows(keyboardShortcuts); return (
{shortcutRows.map((row) => (
{row.shortcuts.map((shortcut, index) => (
))}
{row.description} {row.badges.length > 0 ? ( {row.badges.map((badge) => ( {badge} ))} ) : null}
))}
); } function ShortcutKey({shortcut}: {shortcut: KeyboardShortcut}) { const parts: JSX.Element[] = []; if (shortcut.commandKey) { parts.push( ); } if (shortcut.ctrlKey) { parts.push( ^ ); } if (shortcut.shiftKey) { parts.push( Shift ); } if (shortcut.key) { parts.push( {formatKey(shortcut.key)} ); } if (shortcut.dragMouse) { parts.push( drag mouse ); } return (
{parts.map((part, index) => ( {part} ))}
); } type KeyboardShortcutRow = { badges: string[]; description: string; key: string; shortcuts: KeyboardShortcut[]; }; function buildKeyboardShortcutRows(shortcuts: KeyboardShortcut[]): KeyboardShortcutRow[] { const rows: KeyboardShortcutRow[] = []; for (let index = 0; index < shortcuts.length; index += 1) { const shortcut = shortcuts[index]; const nextShortcut = shortcuts[index + 1]; const shortcutDisplayPair = shortcut?.displayPair; if ( shortcut && shortcutDisplayPair && nextShortcut && canPairShortcuts(shortcut, nextShortcut) ) { rows.push({ badges: mergeShortcutBadges(shortcut, nextShortcut), description: shortcutDisplayPair.description, key: `${shortcutDisplayPair.id}-${index}`, shortcuts: [shortcut, nextShortcut] }); index += 1; } else if (shortcut) { rows.push({ badges: [...(shortcut.badges ?? [])], description: shortcut.description, key: `${shortcut.name}-${shortcut.key}-${index}`, shortcuts: [shortcut] }); } } return rows; } function canPairShortcuts( shortcut: KeyboardShortcut, nextShortcut: KeyboardShortcut | undefined ): nextShortcut is KeyboardShortcut { if (!nextShortcut) { return false; } const shortcutPair = shortcut.displayPair; const nextShortcutPair = nextShortcut.displayPair; if (!shortcutPair || !nextShortcutPair) { return false; } return ( shortcutPair.position === 'primary' && nextShortcutPair.position === 'secondary' && shortcutPair.id === nextShortcutPair.id && shortcutPair.description === nextShortcutPair.description ); } function mergeShortcutBadges(...shortcuts: KeyboardShortcut[]): string[] { const seenBadges = new Set(); const mergedBadges: string[] = []; for (const shortcut of shortcuts) { for (const badge of shortcut.badges ?? []) { if (!seenBadges.has(badge)) { seenBadges.add(badge); mergedBadges.push(badge); } } } return mergedBadges; }