Skip to content

refactor: centralize shortcut lifecycle with TanStack Hotkeys - #572

Merged
wibus-wee merged 8 commits into
mainfrom
fix/shortcut-lifecycle
Sep 10, 2026
Merged

refactor: centralize shortcut lifecycle with TanStack Hotkeys#572
wibus-wee merged 8 commits into
mainfrom
fix/shortcut-lifecycle

Conversation

@wibus-wee

@wibus-wee wibus-wee commented Sep 10, 2026

Copy link
Copy Markdown
Member

Related issue

Refs #288

Problem / pressure

Issue #288 needs configurable workspace shortcuts, but the current foundation mixed DOM listeners, key parsing, command policy, and persisted binding refresh inside one mutable registry. One authenticated cloud success path also omitted AppCommands, and binding changes stayed stale in other open renderer windows.

Summary

  • Pin @tanstack/hotkeys 0.8.0 and use its parser, validation, canonical normalization, DOM matcher, event normalization, and display formatter.
  • Mount one private CommandShortcutHost per renderer through AppInitializer; it owns one capture-phase keydown listener with symmetric React cleanup.
  • Keep CommandRegistry focused on Lody semantics: command stacks, mounted precedence, when, KeyScope, user overrides, conflict lookup, and analytics.
  • Keep useCommand / registry as the only application-command registration port.
  • Refresh validated localStorage bindings on host mount and cross-window storage events.
  • Migrate persisted $mod tokens to the library-native Mod form at read boundaries while retaining Electron accelerator compatibility.
  • Route every authenticated workspace success path through the same AppCommands host.

Visual explanation

flowchart LR
    A[AppInitializer] --> H[CommandShortcutHost]
    H -->|one capture listener| T[TanStack Hotkeys]
    T -->|canonical match| R[CommandRegistry]
    U[useCommand] -->|register and dispose| R
    S[user-bindings storage] -->|validated reload| R
    R -->|live snapshot| H
Loading

Before / after

Before After
Registry manually owned DOM and storage listeners plus key matching. Renderer host owns lifecycle, TanStack owns hotkey mechanics, registry owns command policy.
Lody binding syntax required a custom compiler into tinykeys syntax. Persisted and runtime bindings use the library-native single-chord syntax directly.
One cloud-authenticated layout omitted live app commands. All authenticated success layouts share AuthenticatedWorkspaceContent.
A binding edit updated only the writer renderer. Other open renderers rebuild from validated localStorage state.

Library boundary and risk

TanStack Hotkeys is used for generic shortcut mechanics, but not its manager: the manager currently installs bubble-phase listeners, while Lody needs one capture-phase listener so command policy can negotiate with focus traps and editor scopes. Lody retains only domain behavior and the recording-boundary event.code adaptation needed to preserve physical letter, digit, and punctuation semantics on macOS Option/Shift input.

The package is still alpha, so the dependency is exact-pinned. Upgrades must rerun matcher, capture, registry, settings migration, and Electron accelerator contracts.

Verification

  • The unchanged single-window shortcut journey passed on the exact merge-base b4d54439 and the refactored head: 1 scenario, 8 steps on each.
  • The strengthened two-window counterfactual failed on b4d54439 at the secondary renderer synchronization checkpoint: 7 steps passed, 1 failed, 2 skipped.
  • The same two-window journey passed on current head: 1 scenario, 10 steps. It opens the auxiliary window through the product app.openWindow IPC and verifies both real renderers.
  • Coverage includes default Mod+K, physical punctuation Mod+,, physical Mod+Shift+9 rebinding in window A, old-binding invalidation and new-binding execution in both windows, and restoration after reloading renderer B.
  • Current-head Desktop P0 smoke passed after a fresh build: 4 scenarios, 28 steps.
  • pnpm check, pnpm format, pnpm --filter @lody/e2e check, pnpm run docs check, and git diff --check passed locally under Node 22.

Context handoff

Instructions for reviewing agents

  • Review focus: Verify one host per renderer, capture-listener cleanup, TanStack canonical matching, $mod migration, and storage refresh symmetry.
  • Decisions to challenge: Check the boundary between generic hotkey mechanics and registry policy, especially runtime-equivalent Mod / Control / Meta collisions and editor scopes.
  • Plausible failures / evidence gaps: The real two-window Electron journey was run on macOS; Linux and Windows coverage depends on the repository CI matrix.

Authoring context

  • User goal / directives: Establish a maintainable shortcut lifecycle and one registration port before implementing Cmd+1-9 and swipe behavior from Issue [Feature Request] Easier workspace switching: sidebar swipe and Cmd+1-9 shortcuts #288.
  • Constraints / non-goals: This PR does not add workspace slots, key mappings, gestures, or change OS-global and component-local keyboard ownership.
  • Risk-bearing decisions: Public shortcut syntax moves from legacy $mod to library-native Mod; reads migrate old persisted values. The alpha dependency is exact-pinned.
  • Destructive or irreversible behavior: No user binding is deleted; migration rewrites only the legacy primary-modifier token.
  • Deliberately not done or tested: Issue [Feature Request] Easier workspace switching: sidebar swipe and Cmd+1-9 shortcuts #288 workspace-slot shortcuts and gestures remain for later PRs.
  • Unknowns / confidence: High confidence in command-host and cross-window behavior from focused DOM tests, baseline-vs-head counterfactuals, and the real two-window Electron P0 journey.

@wibus-wee wibus-wee changed the title fix: unify shortcut lifecycle across windows refactor: centralize shortcut lifecycle with tinykeys Sep 10, 2026
@wibus-wee wibus-wee changed the title refactor: centralize shortcut lifecycle with tinykeys refactor: centralize shortcut lifecycle with TanStack Hotkeys Sep 10, 2026
@wibus-wee wibus-wee added the e2e-full Run the full desktop E2E suite label Sep 10, 2026
@wibus-wee
wibus-wee marked this pull request as ready for review September 10, 2026 12:17
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T14:54:54.269556Z f23a20d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@wibus-wee wibus-wee removed the e2e-full Run the full desktop E2E suite label Sep 10, 2026
@wibus-wee
wibus-wee merged commit 3993af4 into main Sep 10, 2026
6 checks passed
@wibus-wee
wibus-wee deleted the fix/shortcut-lifecycle branch September 10, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant