- One settings-scoped checkbox rule (accent, size, focus ring) so the Advanced-settings toggle, SettingsToggleRow, ntfy/webhook card headers, and MCP toggle stop falling back to the browser-default accent. - Fix the empty/off-screen help bubble on mobile: .notification-provider-header and .settings-field-label-row are now positioned ancestors for SettingsHelpTip. - Migrate every remaining inline <small> description across settings sections to the shared SettingsHelpTip "?" affordance (validation errors and live status stay inline). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
84 lines
5.2 KiB
TypeScript
84 lines
5.2 KiB
TypeScript
import { useTranslation } from "react-i18next";
|
|
import type { SectionBaseProps } from "./context";
|
|
import { SettingsHelpTip } from "../SettingsHelpTip";
|
|
import { ShortcutCaptureInput } from "./ShortcutCaptureInput";
|
|
import {
|
|
DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS,
|
|
SHORTCUT_CATEGORIES,
|
|
describeShortcutValidation,
|
|
getShortcutActionLabel,
|
|
normalizeKeyboardShortcut,
|
|
resolveDashboardKeyboardShortcuts,
|
|
type DashboardShortcutAction,
|
|
} from "../../../utils/keyboardShortcuts";
|
|
|
|
export type KeyboardShortcutsSectionProps = SectionBaseProps;
|
|
|
|
/*
|
|
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.
|
|
|
|
FNXC:SettingsStyling 2026-07-15-17:35:
|
|
This section stays off the shared settings row primitives, unlike its neighbors. It renders no plain setting: every row is a ShortcutCaptureInput bound to one action inside the single `dashboardKeyboardShortcuts` map. A descriptor row keys on a settings field name and there is no field per shortcut — so there is nothing here for a toggle/text/select row to own, and no `.search.ts` sibling. Shortcut discovery is served by the nav entry's `searchableText` in SettingsModal instead.
|
|
|
|
FNXC:SettingsHelp 2026-07-16-12:45:
|
|
Inline help moved behind the shared "?" affordance — operator requirement: no inline description paragraphs in Settings. The section description sits beside the heading; each row's static "Default: … Leave blank to disable." hint moved into a tip beside its label. The per-row `small` (still `aria-describedby` target via `hintId`) now carries ONLY live capture validation (`normalizeKeyboardShortcut` errors), and the conflict banner stays inline — validation feedback never hides behind a "?".
|
|
*/
|
|
export function KeyboardShortcutsSection({ 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 (
|
|
<>
|
|
<div className="settings-field-label-row">
|
|
<h4 className="settings-section-heading">{t("settings.keyboardShortcuts.title", "Keyboard Shortcuts")}</h4>
|
|
<SettingsHelpTip settingKey="dashboardKeyboardShortcuts">{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.")}</SettingsHelpTip>
|
|
</div>
|
|
<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}>
|
|
<div className="settings-field-label-row">
|
|
<label htmlFor={inputId}>{t(`settings.keyboardShortcuts.action.${action}`, getShortcutActionLabel(action))}</label>
|
|
<SettingsHelpTip settingKey={inputId}>{t("settings.keyboardShortcuts.rowHint", "Default: {{default}}. Leave blank to disable.", { default: DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS[action] })}</SettingsHelpTip>
|
|
</div>
|
|
<ShortcutCaptureInput
|
|
id={inputId}
|
|
value={shortcutValues[action]}
|
|
defaultValue={DEFAULT_DASHBOARD_KEYBOARD_SHORTCUTS[action]}
|
|
invalid={!parsed.valid}
|
|
describedById={hintId}
|
|
onChange={(value) => updateShortcut(action, value)}
|
|
/>
|
|
{/* FNXC:SettingsHelp 2026-07-16-12:45: The small keeps its hintId (aria-describedby target) but now carries only live validation errors; the static default hint moved behind the "?" above. */}
|
|
<small id={hintId}>{parsed.valid ? null : parsed.error}</small>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
))}
|
|
{shortcutValidationMessage && (
|
|
<small className="settings-description shortcut-conflict-banner" role="alert">{shortcutValidationMessage}</small>
|
|
)}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default KeyboardShortcutsSection;
|