feat(ui): global keyboard shortcuts for record toggle + Settings (T7.4, FR-UX-3)
Ctrl+Shift+R toggles recording, Ctrl+, toggles Settings, Escape closes whichever overlay (consent notice, then Settings) is open. Shortcuts never hijack typing in an input/textarea/select/contenteditable target; Escape is exempt from that guard so it always closes an open modal. "Template apply" from the roadmap item isn't included — there's no template-selection UI yet (that lands with T8.1 in Phase 8).
This commit is contained in:
+52
-5
@@ -34,7 +34,12 @@
|
||||
systemPrefersDark = media.matches;
|
||||
const onSystemThemeChange = (e: MediaQueryListEvent) => (systemPrefersDark = e.matches);
|
||||
media.addEventListener("change", onSystemThemeChange);
|
||||
return () => media.removeEventListener("change", onSystemThemeChange);
|
||||
|
||||
window.addEventListener("keydown", onGlobalKeydown);
|
||||
return () => {
|
||||
media.removeEventListener("change", onSystemThemeChange);
|
||||
window.removeEventListener("keydown", onGlobalKeydown);
|
||||
};
|
||||
});
|
||||
|
||||
function startRecording() {
|
||||
@@ -42,6 +47,39 @@
|
||||
recording.start(undefined, settings.settings.default_record);
|
||||
}
|
||||
|
||||
function toggleRecording() {
|
||||
if (recording.state === "idle") startRecording();
|
||||
else recording.stop();
|
||||
}
|
||||
|
||||
// Global shortcuts (T7.4, FR-UX-3): record start/stop, view toggles.
|
||||
// "Template apply" isn't included — there's no template UI yet (Phase 8, T8.1).
|
||||
function isEditableTarget(target: EventTarget | null): boolean {
|
||||
if (!(target instanceof HTMLElement)) return false;
|
||||
return (
|
||||
target.tagName === "INPUT" ||
|
||||
target.tagName === "TEXTAREA" ||
|
||||
target.tagName === "SELECT" ||
|
||||
target.isContentEditable
|
||||
);
|
||||
}
|
||||
|
||||
function onGlobalKeydown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
if (showConsent) showConsent = false;
|
||||
else if (showSettings) showSettings = false;
|
||||
return;
|
||||
}
|
||||
if (isEditableTarget(e.target)) return; // never hijack typing in a field
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "r") {
|
||||
e.preventDefault();
|
||||
toggleRecording();
|
||||
} else if (e.ctrlKey && e.key === ",") {
|
||||
e.preventDefault();
|
||||
showSettings = !showSettings;
|
||||
}
|
||||
}
|
||||
|
||||
// Mid-meeting retention toggle (FR-REC-1); gates on the same one-time
|
||||
// consent notice as the Settings default-record toggle (FR-REC-2).
|
||||
function onToggleRetention(e: Event) {
|
||||
@@ -67,9 +105,17 @@
|
||||
<span class="muted">local · private</span>
|
||||
<div class="spacer"></div>
|
||||
{#if recording.state === "idle"}
|
||||
<button onclick={startRecording}>● Record</button>
|
||||
<button
|
||||
onclick={startRecording}
|
||||
title="Start recording (Ctrl+Shift+R)"
|
||||
aria-keyshortcuts="Control+Shift+R">● Record</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => recording.stop()}>■ Stop</button>
|
||||
<button
|
||||
onclick={() => recording.stop()}
|
||||
title="Stop recording (Ctrl+Shift+R)"
|
||||
aria-keyshortcuts="Control+Shift+R">■ Stop</button
|
||||
>
|
||||
<span class="rec">Recording…</span>
|
||||
<LevelMeter rms={recording.levelRms} peak={recording.levelPeak} />
|
||||
{#if settings.hardware}
|
||||
@@ -99,8 +145,9 @@
|
||||
<button
|
||||
class="icon"
|
||||
aria-label="Settings"
|
||||
title="Settings"
|
||||
onclick={() => (showSettings = true)}>⚙</button
|
||||
title="Settings (Ctrl+,)"
|
||||
aria-keyshortcuts="Control+,"
|
||||
onclick={() => (showSettings = !showSettings)}>⚙</button
|
||||
>
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user