Download apps/desktop/src/lib/keybinds/combo.test.ts from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 10.7 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/desktop/src/lib/keybinds/combo.test.ts
- Command line
-
hf download hf://SaylorTwift/hermes-agent/apps/desktop/src/lib/keybinds/combo.test.ts
-
curl -L -o combo.test.ts https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/desktop/src/lib/keybinds/combo.test.ts
10.7 kB
| import { afterEach, describe, expect, it, vi } from 'vitest' | |
| // `IS_MAC` is resolved once at module load from `navigator`, so each platform | |
| // case overrides the platform and re-imports the module fresh. | |
| async function loadCombo(platform: string) { | |
| Object.defineProperty(window.navigator, 'platform', { value: platform, configurable: true }) | |
| vi.resetModules() | |
| return import('./combo') | |
| } | |
| function keydown(init: KeyboardEventInit): KeyboardEvent { | |
| return new KeyboardEvent('keydown', init) | |
| } | |
| afterEach(() => { | |
| vi.resetModules() | |
| }) | |
| describe('comboFromEvent β ctrl as a distinct modifier on macOS', () => { | |
| it('reports Control+Tab as "ctrl+tab" on macOS (not Cmd)', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'Tab', ctrlKey: true }))).toBe('ctrl+tab') | |
| expect(comboFromEvent(keydown({ code: 'Tab', ctrlKey: true, shiftKey: true }))).toBe('ctrl+shift+tab') | |
| }) | |
| it('keeps Cmd as "mod" and distinct from Control on macOS', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'KeyK', metaKey: true }))).toBe('mod+k') | |
| expect(comboFromEvent(keydown({ code: 'KeyK', ctrlKey: true }))).toBe('ctrl+k') | |
| }) | |
| it('uses layout-aware letters for Cmd shortcuts on non-QWERTY layouts', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'KeyI', key: 'c', metaKey: true }))).toBe('mod+c') | |
| expect(comboFromEvent(keydown({ code: 'KeyI', key: 'C', metaKey: true, shiftKey: true }))).toBe('mod+shift+c') | |
| }) | |
| it('keeps shifted punctuation anchored to the physical key token', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'Slash', key: '?', metaKey: true, shiftKey: true }))).toBe('mod+shift+/') | |
| }) | |
| it('uses layout-aware punctuation for Cmd shortcuts on non-QWERTY layouts', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| // Dvorak puts "." on the physical QWERTY V key β β. must still reach the | |
| // command center rather than resolving to the physical token. | |
| expect(comboFromEvent(keydown({ code: 'KeyV', key: '.', metaKey: true }))).toBe('mod+.') | |
| expect(comboFromEvent(keydown({ code: 'KeyW', key: ',', metaKey: true }))).toBe('mod+,') | |
| expect(comboFromEvent(keydown({ code: 'BracketLeft', key: '/', metaKey: true }))).toBe('mod+/') | |
| // AZERTY reaches "," from the physical QWERTY M key. | |
| expect(comboFromEvent(keydown({ code: 'KeyM', key: ',', metaKey: true }))).toBe('mod+,') | |
| }) | |
| it("keeps digits physical so AZERTY's shifted number row still binds", async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| // On AZERTY the unshifted "1" key types "&", and "1" only with Shift held. | |
| // Both must resolve to the same `mod+1` the QWERTY user gets. | |
| expect(comboFromEvent(keydown({ code: 'Digit1', key: '&', metaKey: true }))).toBe('mod+1') | |
| expect(comboFromEvent(keydown({ code: 'Digit1', key: '1', metaKey: true }))).toBe('mod+1') | |
| }) | |
| it('falls back to the physical key for glyphs we do not ship as tokens', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| // Option-modified glyphs, dead keys, and non-Latin scripts are not combo | |
| // tokens, so the physical code keeps the binding reachable. | |
| expect(comboFromEvent(keydown({ code: 'KeyK', key: 'Λ', metaKey: true, altKey: true }))).toBe('mod+alt+k') | |
| expect(comboFromEvent(keydown({ code: 'KeyN', key: 'Dead', metaKey: true, altKey: true }))).toBe('mod+alt+n') | |
| expect(comboFromEvent(keydown({ code: 'KeyK', key: 'Π»', metaKey: true }))).toBe('mod+k') | |
| }) | |
| it('treats Control as the "mod" accelerator off macOS', async () => { | |
| const { comboFromEvent } = await loadCombo('Win32') | |
| expect(comboFromEvent(keydown({ code: 'Tab', ctrlKey: true }))).toBe('mod+tab') | |
| expect(comboFromEvent(keydown({ code: 'Tab', ctrlKey: true, shiftKey: true }))).toBe('mod+shift+tab') | |
| }) | |
| it('recognizes PageUp and PageDown chords', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'PageUp', ctrlKey: true }))).toBe('ctrl+pageup') | |
| expect(comboFromEvent(keydown({ code: 'PageDown', ctrlKey: true }))).toBe('ctrl+pagedown') | |
| }) | |
| }) | |
| describe('canonicalizeCombo', () => { | |
| it('leaves "ctrl+β¦" untouched on macOS', async () => { | |
| const { canonicalizeCombo } = await loadCombo('MacIntel') | |
| expect(canonicalizeCombo('ctrl+tab')).toBe('ctrl+tab') | |
| expect(canonicalizeCombo('ctrl+shift+tab')).toBe('ctrl+shift+tab') | |
| }) | |
| it('folds "ctrl+β¦" to "mod+β¦" off macOS so a real Control press resolves', async () => { | |
| const { canonicalizeCombo } = await loadCombo('Win32') | |
| expect(canonicalizeCombo('ctrl+tab')).toBe('mod+tab') | |
| expect(canonicalizeCombo('ctrl+shift+tab')).toBe('mod+shift+tab') | |
| // Non-ctrl combos are unchanged. | |
| expect(canonicalizeCombo('mod+k')).toBe('mod+k') | |
| }) | |
| }) | |
| describe('formatCombo β honest Control labels', () => { | |
| it('renders the Control glyph on macOS', async () => { | |
| const { formatCombo, formatModifierToken } = await loadCombo('MacIntel') | |
| expect(formatCombo('ctrl+tab')).toBe('ββ₯') | |
| expect(formatCombo('ctrl+shift+tab')).toBe('ββ§β₯') | |
| expect(formatCombo('mod+enter')).toBe('ββ΅') | |
| expect(formatModifierToken('mod')).toBe('β') | |
| }) | |
| it.each(['Linux x86_64', 'Win32'])('renders "Ctrl+β¦" off macOS on %s (base key keeps its glyph)', async platform => { | |
| const { formatCombo, formatModifierToken } = await loadCombo(platform) | |
| expect(formatCombo('ctrl+tab')).toBe('Ctrl+β₯') | |
| expect(formatCombo('ctrl+shift+tab')).toBe('Ctrl+Shift+β₯') | |
| expect(formatCombo('mod+enter')).toBe('Ctrl+β΅') | |
| expect(formatModifierToken('mod')).toBe('Ctrl') | |
| }) | |
| it('renders PageUp and PageDown with compact labels', async () => { | |
| const { formatCombo } = await loadCombo('Win32') | |
| expect(formatCombo('ctrl+pageup')).toBe('Ctrl+PgUp') | |
| expect(formatCombo('ctrl+pagedown')).toBe('Ctrl+PgDn') | |
| }) | |
| }) | |
| describe('actionAllowedInInput', () => { | |
| it('keeps primary-modifier chords global while typing, gating bare/Shift combos to the allowlist', async () => { | |
| const { actionAllowedInInput } = await loadCombo('MacIntel') | |
| // Mod/Ctrl chords are deliberate two-key gestures β they fire even with | |
| // focus in the composer (βN, βT, ββ§N, βK, βTab, β¦), matching every browser | |
| // and chat app and the pre-#86586 behavior. | |
| expect(actionAllowedInInput('session.new', 'mod+n')).toBe(true) | |
| expect(actionAllowedInInput('session.newTab', 'mod+t')).toBe(true) | |
| expect(actionAllowedInInput('session.newWindow', 'mod+shift+n')).toBe(true) | |
| expect(actionAllowedInInput('session.next', 'ctrl+tab')).toBe(true) | |
| expect(actionAllowedInInput('session.prev', 'ctrl+shift+tab')).toBe(true) | |
| expect(actionAllowedInInput('nav.commandPalette', 'mod+k')).toBe(true) | |
| expect(actionAllowedInInput('view.findInPage', 'mod+f')).toBe(true) | |
| expect(actionAllowedInInput('nav.skills', 'mod+k')).toBe(true) | |
| expect(actionAllowedInInput('view.showTerminal', 'ctrl+`')).toBe(true) | |
| expect(actionAllowedInInput('profile.next', 'mod+shift+]')).toBe(true) | |
| // A global action rebound onto a text-editing chord (Ctrl+A select-all, | |
| // Ctrl+E line-end, β¦) fires deliberately while typing β mod-chords are | |
| // two-key gestures, the pre-#86586 semantic. Unbound chords (the shipped | |
| // default for a/e/u/backspace) never reach this function: the dispatcher | |
| // returns before the gate when no action is bound, so the input keeps its | |
| // native editing behavior out of the box. An editing-chord exclusion set | |
| // would have to dodge shipped defaults (βK, βW, βD, βF, βB) or re-break them. | |
| expect(actionAllowedInInput('session.new', 'ctrl+a')).toBe(true) | |
| // Bare modifiers are not real chords β `comboFromEvent` never yields | |
| // them, so a malformed stored binding must not pass the shape-only check. | |
| expect(actionAllowedInInput('session.new', 'mod')).toBe(false) | |
| expect(actionAllowedInInput('session.new', 'ctrl')).toBe(false) | |
| // Bare/Shift-only combos must never hijack typing: a rebound βN β 'n' | |
| // (or the pre-#76185 'shift+n') must not fire while the user types N. | |
| expect(actionAllowedInInput('session.new', 'n')).toBe(false) | |
| expect(actionAllowedInInput('session.new', 'shift+n')).toBe(false) | |
| }) | |
| it('leaves text navigation chords with the focused input even when rebound to an allowed action', async () => { | |
| const { actionAllowedInInput } = await loadCombo('Win32') | |
| expect(actionAllowedInInput('session.next', 'mod+right')).toBe(false) | |
| expect(actionAllowedInInput('session.prev', 'mod+left')).toBe(false) | |
| expect(actionAllowedInInput('nav.commandPalette', 'mod+pageup')).toBe(false) | |
| expect(actionAllowedInInput('view.findInPage', 'mod+end')).toBe(false) | |
| }) | |
| }) | |
| describe('comboFromEvent β IME composition keydowns never resolve to combos (#84957)', () => { | |
| it('returns null while a composition is in progress (isComposing)', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| // Typing δ½ with a Chinese IME: the preedit keydowns carry isComposing. | |
| // Before the guard, these canonicalized to combos and fired keybinds | |
| // (e.g. dispatched `session.new` mid-composition). | |
| expect(comboFromEvent(keydown({ code: 'KeyN', isComposing: true, key: 'n' }))).toBeNull() | |
| expect(comboFromEvent(keydown({ code: 'Enter', isComposing: true, key: 'Enter' }))).toBeNull() | |
| expect(comboFromEvent(keydown({ code: 'Space', isComposing: true, key: ' ' }))).toBeNull() | |
| }) | |
| it('returns null for the legacy key="Process" (VK_PROCESSKEY) keydown', async () => { | |
| const { comboFromEvent } = await loadCombo('Win32') | |
| expect(comboFromEvent(keydown({ code: 'KeyW', key: 'Process' }))).toBeNull() | |
| }) | |
| it('ignores IME-synthesized modifier-name keys on non-modifier codes', async () => { | |
| const { comboFromEvent } = await loadCombo('Win32') | |
| // Q9 2002-style legacy IMEs synthesize key="Control" with code="KeyW", | |
| // which would otherwise canonicalize to a phantom ctrl+w (close tab). | |
| expect(comboFromEvent(keydown({ code: 'KeyW', key: 'Control' }))).toBeNull() | |
| expect(comboFromEvent(keydown({ code: 'KeyA', key: 'Shift' }))).toBeNull() | |
| }) | |
| it('still resolves real combos after composition ends', async () => { | |
| const { comboFromEvent } = await loadCombo('MacIntel') | |
| expect(comboFromEvent(keydown({ code: 'KeyN', isComposing: false, key: 'n', metaKey: true }))).toBe('mod+n') | |
| }) | |
| }) | |