Files
fusion/packages/dashboard/app/components/settings/sections/KeyboardShortcutsSection.tsx
gsxdsm f992e6aefa FN-7553: add dedicated keyboard shortcuts settings section
Adds a standalone Keyboard Shortcuts settings section with an easier-to-use capture input and support for more shortcut actions.
- Introduce a new KeyboardShortcutsSection with a dedicated ShortcutCaptureInput component for recording key combos
- Split keyboard-shortcut settings out of the general settings section into their own settings tab
- Extend the shortcut schema/types and useDashboardKeyboardShortcuts hook to support additional actions
- Update settings save-split, section-keys, and defaults/parity tests to cover the new section
- Update dashboard docs and add a changeset for the new settings section

Files changed:
 .changeset/fn-7553-keyboard-shortcuts-section.md   |   7 +
 docs/dashboard-guide.md                            |  22 ++-
 packages/core/src/__tests__/global-settings.test.ts     |   9 +-
 packages/core/src/__tests__/settings-defaults.test.ts   |   4 +
 packages/core/src/__tests__/settings-parity.test.ts     |   2 +-
 packages/core/src/settings-schema.ts               |   6 +-
 packages/core/src/types.ts                         |  12 ++
 packages/dashboard/app/App.tsx                     |  26 +++-
 packages/dashboard/app/__tests__/App.keyboard-shortcuts.test.tsx  |  47 ++++++-
 packages/dashboard/app/components/SettingsModal.css     |  56 ++++++--
 packages/dashboard/app/components/SettingsModal.tsx     |  30 +++-
 packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx |  63 ---------
 packages/dashboard/app/components/__tests__/SettingsModal.keyboardShortcuts.test.tsx |  156 +++++++++++++++++++++
 packages/dashboard/app/components/settings/__tests__/section-keys.test.ts |   1 +
 packages/dashboard/app/components/settings/save-split.ts |   6 +-
 packages/dashboard/app/components/settings/sections/GlobalGeneralSection.tsx |  38 -----
 packages/dashboard/app/components/settings/sections/KeyboardShortcutsSection.tsx |  78 +++++++++++
 packages/dashboard/app/components/settings/sections/ShortcutCaptureInput.tsx |  118 ++++++++++++++++
 packages/dashboard/app/components/settings/sections/__tests__/KeyboardShortcutsSection.test.tsx |  113 +++++++++++++++
 packages/dashboard/app/hooks/__tests__/useDashboardKeyboardShortcuts.test.tsx |  73 +++++++++-
 packages/dashboard/app/hooks/useDashboardKeyboardShortcuts.ts |  38 ++++-
 packages/dashboard/app/utils/__tests__/keyboardShortcuts.test.ts |  37 ++++-
 packages/dashboard/app/utils/keyboardShortcuts.ts  |  54 ++++++-
 23 files changed, 854 insertions(+), 142 deletions(-)

Fusion-Task-Id: FN-7553

Fusion-Task-Lineage: 51c1ad69-fbd3-45ce-8d2d-45d9962d7b76

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-04 21:28:19 -07:00

79 lines
3.8 KiB
TypeScript

import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import type { SectionBaseProps } from "./context";
import { ShortcutCaptureInput } from "./ShortcutCaptureInput";
import {
DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS,
SHORTCUT_CATEGORIES,
describeShortcutValidation,
getShortcutActionLabel,
normalizeKeyboardShortcut,
resolveDashboardKeyboardShortcuts,
type DashboardShortcutAction,
} from "../../../utils/keyboardShortcuts";
export interface KeyboardShortcutsSectionProps extends SectionBaseProps {
scopeBanner: ReactNode;
}
/*
FNXC:DashboardShortcuts 2026-07-04-00:00:
FN-7553 promotes keyboard shortcuts from two bare inputs buried in Global General to their own dedicated settings section, grouped by category (Communication/Workspace/Navigation/Tasks from SHORTCUT_CATEGORIES) with a press-to-record capture control per row. `dashboardKeyboardShortcuts` ownership moved here from `global-general` (save-split.ts GLOBAL_SECTION_KEYS + section-keys.ts) so exactly one section owns the key for save/reset.
*/
export function KeyboardShortcutsSection({ scopeBanner, form, setForm }: KeyboardShortcutsSectionProps) {
const { t } = useTranslation("app");
const shortcutValues = resolveDashboardKeyboardShortcuts(form.dashboardKeyboardShortcuts);
const shortcutValidationMessage = describeShortcutValidation(shortcutValues);
const updateShortcut = (action: DashboardShortcutAction, value: string) => setForm((f) => ({
...f,
dashboardKeyboardShortcuts: {
...resolveDashboardKeyboardShortcuts(f.dashboardKeyboardShortcuts),
[action]: value,
},
}));
return (
<>
{scopeBanner}
<h4 className="settings-section-heading">{t("settings.keyboardShortcuts.title", "Keyboard Shortcuts")}</h4>
<p className="settings-description">{t("settings.keyboardShortcuts.hint", "Configure global dashboard shortcuts. Click Record and press a combination, or type one manually. Shortcuts are ignored while typing in inputs, editors, chat composers, and terminal fields. Leave blank to disable an action.")}</p>
<div className="form-group settings-keyboard-shortcuts" data-testid="keyboard-shortcuts-settings">
{SHORTCUT_CATEGORIES.map((category) => (
<div className="shortcut-category" key={category.id}>
<h5 className="settings-section-heading">{t(`settings.keyboardShortcuts.category.${category.id}`, category.label)}</h5>
{category.actions.map((action) => {
const parsed = normalizeKeyboardShortcut(shortcutValues[action]);
const inputId = `dashboardShortcut-${action}`;
const hintId = `${inputId}Hint`;
return (
<div className="shortcut-row" key={action}>
<label htmlFor={inputId}>{t(`settings.keyboardShortcuts.action.${action}`, getShortcutActionLabel(action))}</label>
<ShortcutCaptureInput
id={inputId}
value={shortcutValues[action]}
defaultValue={DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS[action]}
invalid={!parsed.valid}
describedById={hintId}
onChange={(value) => updateShortcut(action, value)}
/>
<small id={hintId}>
{parsed.valid
? t("settings.keyboardShortcuts.rowHint", "Default: {{default}}. Leave blank to disable.", { default: DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS[action] })
: parsed.error}
</small>
</div>
);
})}
</div>
))}
{shortcutValidationMessage && (
<small className="settings-description shortcut-conflict-banner" role="alert">{shortcutValidationMessage}</small>
)}
</div>
</>
);
}
export default KeyboardShortcutsSection;