Skip to content

feat(shortcuts): remap single-key shortcuts per device - #93

Merged
ilramdhan merged 3 commits into
mainfrom
feat/shortcut-remap
Oct 8, 2026
Merged

ilramdhan merged 3 commits into
mainfrom
feat/shortcut-remap

Conversation

@ilramdhan

@ilramdhan ilramdhan commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

Single-key shortcuts can now be remapped per device (WCAG 2.1.4), and the project tree's touch hit areas no longer overlap (WCAG 2.5.8).

Shortcut remapping

  • src/lib/shortcuts.ts is now the source of effective bindings: SHORTCUTS stays the defaults; applyOverrides, findConflict, remapShortcut, parseOverrides, isAllowedShortcutKey are pure; a small store (getEffectiveShortcuts, subscribeShortcuts, setShortcutKey, resetShortcut, resetAllShortcuts) persists { id: key } in localStorage (second-brain-shortcut-overrides, try/catch like the single-key switch). Invalid, unknown, non-remappable or conflicting stored entries are dropped on read.
  • matchShortcut defaults to the effective bindings, so the handlers in _authenticated.tsx, use-keyboard-nav.ts (lists, kanban, timeline, NotesBoard) and calendar.tsx follow remaps without further changes.
  • useShortcuts() (src/hooks/use-shortcuts.ts, useSyncExternalStore) re-renders the ? cheat-sheet, the sidebar "Tugas cepat" key hint and new CommandMenu hints (quick task, shortcut sheet).
  • Only single-key bindings are remappable. Ctrl/⌘+K and arrow/Enter/Home/End/Esc (inList) bindings stay fixed and are shown read-only ("Tetap").
  • Conflicts are rejected with "Sudah dipakai untuk X" / "Already used for X" (same scope, or global vs any scope). Forbidden: lone modifiers, Tab, Esc, Enter, Space, arrows, F-keys, any Ctrl/⌘/Alt combo. No swapping.
  • Settings: new "Pintasan keyboard" card (the single-key switch moved there from Tampilan) with ShortcutSettings: "Ubah" → record mode (next key; Esc cancels, Tab cancels and moves focus so there is no keyboard trap), per-row reset, "Reset semua". Errors are role=alert, success role=status.
  • i18n ID+EN in src/lib/i18n/core.ts; kbDescription now shows the effective cheat-sheet key.

Project tree touch targets (/projects, tree view, TreeNode in projects.index.tsx)

  • On coarse: rows are min-h-11 with py-0, so the centred 44px tap-area of the icon-sm expander / add-sub-project buttons fills the row height instead of spilling into neighbouring rows; the name link stretches to the full row height. Desktop unchanged. No changes to button.tsx.

ACCESSIBILITY.md updated (2.1.4 now remappable per device; 2.5.8 project tree gap closed, generic remaining gap kept).

Closes #

Type of change

  • feat – new feature
  • fix – bug fix
  • refactor / perf – no behavior change / performance
  • docs / test / chore / ci
  • Breaking change (describe the migration path below)

How to test

  1. Settings → Pintasan keyboard → "Ubah" on "Tugas cepat", press n: status "Tugas cepat sekarang N". Leave settings; n opens quick task, q does nothing; sidebar shows N; ? sheet and ⌘K palette hint show N.
  2. "Ubah" on "Tugas cepat", press t: "Sudah dipakai untuk Lompat ke hari ini" (calendar). Press Tab/F5/Ctrl+P: refused. Esc cancels.
  3. In a list (tasks list/kanban/timeline/notes) remap "Berikutnya" to s; s moves down, ↓ still works. Calendar: remap t → o.
  4. Per-row reset and "Reset semua" restore defaults; reload keeps remaps; corrupt the localStorage value → defaults.
  5. Turn "Pintasan satu tombol" off: remapped keys stop too, hints disappear.
  6. Phone / devtools touch emulation: /projects tree view rows are 44px; expander and + buttons don't overlap rows above/below. Desktop look unchanged.

Screenshots / recordings

Not captured (agent run). UI uses existing Button/IconButton variants and kbd styling from the cheat-sheet.

Checklist

  • bun run lint, bunx tsc --noEmit and bun run test pass locally (eslint 0 errors, prettier clean, tsc clean, vitest 106 files / 1106 tests)
  • bun run build passes (vite build OK; check-migrations and check-n8n OK)
  • Follows the architecture rules in AGENTS.md (Button system, no raw <button>, no height/colour overrides)
  • Database changes: none
  • No secrets, tokens or personal data in code, logs, fixtures or screenshots
  • Server-only secrets stay in *.server.ts / server functions and are never exposed via VITE_*
  • Accessibility considered: keyboard reachable, labelled controls, visible focus, sufficient contrast (see ACCESSIBILITY.md)
  • UI strings added for both Indonesian and English where the i18n layer applies
  • PR title follows Conventional Commits
  • Docs updated (ACCESSIBILITY.md)
  • No force-push / history rewrite of commits already on main

Notes for reviewers

  • Each shortcut has at most one remappable binding (its letter/symbol); the arrow/Enter twin stays. Remapping navNext in "list" does not affect calDown in "calendar" (separate scopes), matching the owner rule "global conflicts with every scope".
  • The project tree lives on /projects (tree view), not the app sidebar; the sidebar has no project tree.
  • Overrides are per device, like the single-key switch; per-account sync would need a migration (out of scope).
  • Remapped keys stay lower-case for letters; anyShift on quick task is preserved, so N and n both work.

🤖 Generated with Claude Code

Effective bindings (defaults + per-device overrides) in src/lib/shortcuts.ts
with a subscribe/get store and useShortcuts hook; Settings → Pintasan lets
users change, reset and reset all single-key shortcuts, rejecting conflicts
and forbidden keys. Cheat-sheet, sidebar and command palette hints follow
the effective keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
demo-2ndbrain.ilramdhan.dev Ready Ready Preview Oct 8, 2026 9:14am UTC
second-brain Ready Ready Preview Oct 8, 2026 9:14am UTC

ilramdhan and others added 2 commits October 8, 2026 16:18
…s UI

Unit tests for which bindings are remappable, forbidden keys, same-scope
and global conflicts, override merging, stale storage and the store's
subscribe/persist cycle. Component tests for record, reject, Esc/Tab
cancel, per-row reset and "Reset all". Recording now refuses Ctrl/Cmd/Alt
combos (only the key string was validated before) and Tab leaves record
mode instead of being swallowed, so there is no keyboard trap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On coarse pointers each project tree row is at least 44px tall with no
vertical padding, so the centred 44px hit area of the expander and
add-sub-project buttons fills the row rather than spilling into the rows
above and below; the name link spans the full row height. Desktop rows
are unchanged. ACCESSIBILITY.md updated for 2.1.4 (now remappable per
device) and 2.5.8 (project tree gap closed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026

Copy link
Copy Markdown

Deployment failed for project demo-2ndbrain.ilramdhan.dev with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/ilramdhans-projects?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Oct 8, 2026

Copy link
Copy Markdown

Deployment failed for project second-brain with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/ilramdhans-projects?upgradeToPro=build-rate-limit

@ilramdhan
ilramdhan marked this pull request as ready for review October 8, 2026 09:18
Copilot AI balanced review requested due to automatic review settings October 8, 2026 09:18
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ilramdhan
ilramdhan merged commit a407cfd into main Oct 8, 2026
13 of 15 checks passed
@ilramdhan
ilramdhan deleted the feat/shortcut-remap branch October 8, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants