diff --git a/src/ui/applicationShell.js b/src/ui/applicationShell.js index ac921b34b..1e4daaefd 100644 --- a/src/ui/applicationShell.js +++ b/src/ui/applicationShell.js @@ -542,6 +542,7 @@ export class StyleManager extends ShellFacade { _toggleOrbit: (...args) => this._toggleOrbit(...args), toggleCleanView: (...args) => this.toggleCleanView(...args), _toggleCctvEnabled: (...args) => this._toggleCctvEnabled(...args), + _togglePanel: (panelId) => this._panelChrome._togglePanel(panelId), _setBloomEnabled: (...args) => this._setBloomEnabled(...args), _setBloomIntensity: (...args) => this._setBloomIntensity(...args), _setSharpenEnabled: (...args) => this._setSharpenEnabled(...args), diff --git a/src/ui/applicationShortcuts.js b/src/ui/applicationShortcuts.js index 57214a570..d83e0caab 100644 --- a/src/ui/applicationShortcuts.js +++ b/src/ui/applicationShortcuts.js @@ -27,6 +27,11 @@ export function bindApplicationShortcuts({ event.target?.matches?.('select, input, textarea') || event.target === searchInput; if (isFormControl && event.key !== 'Escape') return; + // Browser and OS shortcuts (Cmd+C, Ctrl+F, Alt+1…) are not ours. + if (event.metaKey || event.ctrlKey || event.altKey) { + if (event.key === 'Escape') actions.dismissSearch(); + return; + } if (STYLE_KEYS[event.key]) actions.setStyle(STYLE_KEYS[event.key]); if (event.key === 'Escape') actions.dismissSearch(); diff --git a/src/ui/displayBindings.js b/src/ui/displayBindings.js index 73c4fbee6..5a00dc663 100644 --- a/src/ui/displayBindings.js +++ b/src/ui/displayBindings.js @@ -64,8 +64,8 @@ export class DisplayBindings { }, toggleOrbit: () => this._toggleOrbit(), toggleCleanView: () => this.toggleCleanView(), - toggleLayers: () => - document.getElementById('data-panel').classList.toggle('active'), + // Same collapse as the panel's own button, so its rail entry stays. + toggleLayers: () => this._togglePanel('data-panel'), cycleDetection: () => { this.shareLinkManager?.claimRestoreLane?.('visual'); this.claimDetection(); diff --git a/src/ui/visualInput.test.mjs b/src/ui/visualInput.test.mjs index d49a19bda..82ca6becc 100644 --- a/src/ui/visualInput.test.mjs +++ b/src/ui/visualInput.test.mjs @@ -117,9 +117,19 @@ test('the supplied search target retains editing even without form markup', () = assert.deepEqual(f.calls, [['dismissSearch']]); }); -test('shortcut extraction does not change repeat or modifier policy', () => { +test('browser shortcuts with Cmd, Ctrl or Alt never reach app shortcuts', () => { const f = shortcuts(); - f.press('h', new Element(), { repeat: true, ctrlKey: true }); + for (const modifier of ['metaKey', 'ctrlKey', 'altKey']) + for (const key of ['1', 'h', 'o', 'v', 'f', 'd', 'c']) + f.press(key, new Element(), { [modifier]: true }); + assert.deepEqual(f.calls, []); + f.press('Escape', new Element(), { metaKey: true }); + assert.deepEqual(f.calls, [['dismissSearch']]); +}); + +test('a held key still repeats its shortcut', () => { + const f = shortcuts(); + f.press('h', new Element(), { repeat: true }); assert.deepEqual(f.calls, [['toggleHud']]); });