Skip to content

feat(settings): regroup Settings + Floating Navigator inline shortcut + cross-window pin sync#106

Merged
ryota-murakami merged 4 commits into
mainfrom
feat/settings-regroup-floating-shortcut
Jun 24, 2026
Merged

feat(settings): regroup Settings + Floating Navigator inline shortcut + cross-window pin sync#106
ryota-murakami merged 4 commits into
mainfrom
feat/settings-regroup-floating-shortcut

Conversation

@ryota-murakami

@ryota-murakami ryota-murakami commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Regroups Settings into concern-based sections, adds a Floating Navigator inline
toggle-shortcut, and keeps the floating pin in sync across windows.

  • §6a — Regroup the web Settings page into concern-based sections
    (Tasks / Sound / Appearance), each under a real <section> header
    (SettingsSection). The old headerless PreferencesSettings card is retired.
  • §6c — Drop the Electron-leaning BrainDump appearance items from the web
    Preferences; they now live only in the Electron settings page.
  • §6b — Floating Navigator inline toggle-shortcut, mirroring BrainDump's
    (FloatingNavigatorSettings + useShortcutCapture), plus the tray accelerator
    display and the Zod IPC schema for the new channel.
  • CQ-1useShortcutCapture hook (VSCode-style chord capture) shared by both
    shortcut rows.
  • §6e — Shortcut conflict-substitution fix (Design B): applyShortcutRebind
    reads the effective binding back via getRegisteredShortcuts() and rejects a
    silently substituted fallback as a conflict, restoring the previous accel so
    the UI never shows a key the user did not choose.
  • §6d — Keep-on-top cross-window sync: changing always-on-top from Settings or
    the in-window toggle broadcasts floating-window-always-on-top-changed; the
    floating preload re-dispatches it as a DOM CustomEvent so the pin button
    re-syncs its aria-pressed.

QA

Everything runnable locally is green:

  • pnpm validate — unit + lint + build + typecheck + theme → 🟢
  • pnpm test:electron320 pass / 33 files 🟢 (incl. WindowManager.always-on-top
    = §6d broadcast, ipc-contract = §6b channels, applyShortcutRebind 4 tests = §6e,
    ShortcutManager)

Coverage by area:

Area Evidence
§6e substitution (Design B) applyShortcutRebind.test.ts — 4 tests incl. "rejects a silently substituted fallback and restores the previous binding" (full unit coverage)
§6b Floating shortcut renderer FloatingNavigatorSettings + useShortcutCapture (7 tests); main via ipc-contract + tray (in the 320)
§6a/§6c Settings regroup ElectronSettingsPage null-on-web guard; the 3 web panels carry no unstable-dep useEffect (no render loop); CI web E2E walks the route
§6d cross-window sync links 1/2/4 unit-green; link 3 (preload real-IPC forward) = new e2e/electron/floating-always-on-top-sync.spec.ts, gated by this PR's CI electron E2E

Honest residual (structurally not locally provable — release-time)

  • §6d full visible signed-in pin-flip across windows: dev-electron's floating
    CSP blocks the branch renderer, the packaged app loads the prod renderer, and the
    prod-build harness floating window is signed out (no pin button). The new seam
    spec proves the real main→preload→DOM IPC hop; the visual end-to-end is
    release-time packaged native QA — same precedent as keep-on-top feat(electron): Keep-on-top preference for Floating Navigator + BrainDump #92
    ("native always-on-top smoke OWED at release").

Notes

  • §6b deviation from plan CQ-2 (approved): reads/writes the canonical
    shortcuts.toggleFloatingNavigator directly — no floating.shortcut mirror.
    Strictly avoids drift; behavior identical-or-better than the mirrored sketch.
  • Release: this PR lands unbumped (corelive convention: features land
    unbumped, the release bump is a separate chore(release) + lightweight v* tag).
    Decision for this cycle: after merge, bump + cut an Electron release so the
    main-process halves (§6d / §6e / §6b tray+Zod) reach packaged users now. The
    release-time packaged native QA runs at that tag.

Test plan

  • pnpm validate 🟢
  • pnpm test:electron 320 🟢
  • CI e2e.electron (incl. new floating-always-on-top-sync seam spec)
  • CI e2e.web
  • Release-time packaged native QA: visible always-on-top cross-window pin sync

Merge gate: CI green and explicit owner OK. Do not merge before both.

Summary by CodeRabbit

  • New Features

    • Added Floating Navigator settings, including a Keep on top toggle and optional toggle-shortcut capture.
    • Introduced global shortcut configuration for Floating Navigator (get/set with conflict handling).
    • Updated Settings page to use clearer, sectioned layouts (Tasks, Sound, Appearance, and Electron options).
  • Bug Fixes

    • Floating “always-on-top” now syncs live across windows/screens via main→preload→UI events.
    • Shortcut rebinds now detect silent substitution conflicts and reliably roll back on failure.
  • Tests

    • Added/updated unit, IPC contract, and Electron e2e coverage for these behaviors.

…s-window pin sync

Regroup /settings into concern-based sections (Tasks, Sound, Appearance,
Brain Dump, Floating Navigator, Application, Updates), splitting the
PreferencesSettings + FloatingWindowSettings monoliths into focused
per-concern components, each under a semantic <h2> section heading.

- §6a regroup: web-common labels in page.tsx; Electron labels inside
  ElectronSettingsPage (after the isElectron gate, so web shows no orphan
  headers); D1 flatten (Caption is the sole section heading); D2 fold
  "Settings Window" into Application; Arch-1 relocate show-on-all-desktops.
- §6b Floating Navigator inline toggle-shortcut: reads/writes the canonical
  shortcuts.toggleFloatingNavigator (no separate mirror, so it never drifts
  from a generic-keybind rebind) via 2 IPC handlers + preload + Zod + types,
  mirroring the BrainDump rebind UI.
- §6c remove Electron-leaning BrainDump appearance items from web
  Preferences (moved into the Electron Brain Dump card).
- §6e conflict-substitution fix (Design B): reject a silently substituted
  accelerator by reading getRegisteredShortcuts() back and restoring the
  previous binding; shared applyShortcutRebind helper for both toggles.
- §6d keep-on-top cross-window sync: WindowManager broadcasts the change to
  the floating window so its own pin button live-updates instead of lying
  until relaunch (RED-test-proven on the consume side; emit side unit-tested).
- CQ-1 extract useShortcutCapture hook (shared by BrainDump + Floating).

Gates: pnpm validate (661 unit + typecheck + build + lint + packages +
theme) and the electron suite (320 tests) all green.
Add a live-Electron E2E that drives the canonical Settings-bridge setter
(floatingPanels.setFloatingNavigatorAlwaysOnTop) and asserts the floating
window receives the broadcast as a DOM CustomEvent. This proves the one §6d
link unit tests can't reach: main-process broadcast -> preload module-level
forward -> page window event, over real IPC.

Auth-independent (the preload forward is module-scoped, so it fires whether
the floating page shows the signed-in navigator or the signed-out front-door
card; the CI harness is signed out). Links 1/2/4 stay unit-covered
(WindowManager.always-on-top + FloatingNavigator.test.tsx).
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
corelive Ready Ready Preview, Comment Jun 24, 2026 12:01pm

Request Review

@codecov-commenter

codecov-commenter commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.10506% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.18%. Comparing base (42e88c3) to head (f6bc42e).

Files with missing lines Patch % Lines
src/components/electron/FloatingPanelToggle.tsx 71.42% 6 Missing and 2 partials ⚠️
.../components/electron/FloatingNavigatorSettings.tsx 81.08% 5 Missing and 2 partials ⚠️
src/hooks/useFloatingPanelPreference.ts 87.23% 5 Missing and 1 partial ⚠️
src/components/electron/BrainDumpAppearance.tsx 90.62% 2 Missing and 1 partial ⚠️
src/components/settings/SoundPreferences.tsx 91.42% 2 Missing and 1 partial ⚠️
src/components/electron/BrainDumpSettings.tsx 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #106      +/-   ##
==========================================
+ Coverage   66.89%   67.18%   +0.28%     
==========================================
  Files         133      138       +5     
  Lines        4308     4376      +68     
  Branches     1172     1195      +23     
==========================================
+ Hits         2882     2940      +58     
- Misses       1212     1218       +6     
- Partials      214      218       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ryota-murakami

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c148900-f882-4648-a16e-ec2835fbaf00

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Floating Navigator shortcut configuration and always-on-top broadcast plumbing, then restructures settings into sectioned web and Electron layouts with new focused preference components.

Changes

Floating Navigator settings: always-on-top sync, shortcut config, and settings page reorganization

Layer / File(s) Summary
Shortcut contracts and capture flow
plans/2026-06-24-settings-regroup-and-floating-shortcut.md, electron/types/ipc.ts, electron/ipc/ipc-schemas.ts, electron/types/electron-api.d.ts, electron/main.ts, electron/preload.ts, electron/utils/applyShortcutRebind.ts, electron/__tests__/applyShortcutRebind.test.ts, electron/__tests__/ipc-contract.test.ts, src/hooks/useShortcutCapture.ts, src/hooks/useShortcutCapture.test.ts, src/components/electron/FloatingNavigatorSettings.tsx, src/components/electron/FloatingNavigatorSettings.test.tsx
Adds shortcut get/set IPC for Floating Navigator, a shared shortcut rebind helper, the preload bridge methods, and the shortcut capture hook and UI wired through FloatingNavigatorSettings. Tests cover schema parsing, rebind outcomes, and shortcut capture rollback behavior.
Always-on-top broadcast path
electron/WindowManager.ts, electron/preload-floating.ts, electron/types/ipc.ts, electron/__tests__/WindowManager.always-on-top.test.ts, src/components/floating-navigator/FloatingNavigator.tsx, src/components/floating-navigator/FloatingNavigator.test.tsx, e2e/electron/floating-always-on-top-sync.spec.ts
Broadcasts floating-window-always-on-top-changed from the main process to the floating renderer, forwards it as a DOM CustomEvent, and updates the floating navigator pin state from that event. Unit tests and an e2e spec validate the broadcast and live update path.
Web settings sections and common preferences
src/components/settings/SettingsSection.tsx, src/app/settings/page.tsx, src/components/settings/SoundPreferences.tsx, src/components/settings/SoundPreferences.test.tsx, src/components/settings/TaskPreferences.tsx, src/components/settings/TaskPreferences.test.tsx, src/components/settings/PreferencesSettings.tsx, src/components/settings/PreferencesSettings.test.tsx
Introduces SettingsSection, splits the web preferences UI into SoundPreferences and TaskPreferences, rewires the settings page to section blocks, and removes the monolithic preferences component and its tests.
Electron settings sections and preference components
src/components/electron/BrainDumpAppearance.tsx, src/components/electron/BrainDumpAppearance.test.tsx, src/components/electron/BrainDumpSettings.tsx, src/components/electron/ElectronSettingsPage.tsx, src/components/electron/ElectronSettingsPage.test.tsx, src/components/electron/FloatingPanelToggle.tsx, src/components/electron/FloatingPanelToggle.test.tsx, src/components/electron/FloatingWindowSettings.tsx, src/components/electron/FloatingWindowSettings.test.tsx, src/components/electron/FloatingNavigatorSettings.tsx, src/components/electron/FloatingNavigatorSettings.test.tsx, src/components/electron/AppUpdateSettings.tsx, src/components/electron/StartupWindowSettings.tsx, src/components/ThemeSelector.tsx
Adds BrainDumpAppearance, FloatingPanelToggle, and FloatingNavigatorSettings, refactors BrainDumpSettings to use the shortcut hook, and rewrites the Electron settings layout around SettingsSection blocks. The card-based subpanels are flattened, the old floating window settings component is removed, and the Electron settings tests are updated for the new structure.

Sequence Diagram(s)

sequenceDiagram
  participant SettingsUI as Settings Renderer
  participant MainProcess as WindowManager (main)
  participant FloatingPreload as preload-floating
  participant FloatingUI as FloatingNavigator DOM

  SettingsUI->>MainProcess: electronAPI.floatingPanels.setFloatingNavigatorAlwaysOnTop(false)
  MainProcess->>MainProcess: BrowserWindow.setAlwaysOnTop(false)
  MainProcess->>FloatingPreload: typedSend("floating-window-always-on-top-changed", {alwaysOnTop: false})
  FloatingPreload->>FloatingUI: dispatchEvent(CustomEvent "floating-window-always-on-top-changed")
  FloatingUI->>FloatingUI: useInitialEffect listener: setIsAlwaysOnTop(false)
Loading
sequenceDiagram
  participant FloatingNavigatorSettings as FloatingNavigatorSettings
  participant useShortcutCapture as useShortcutCapture hook
  participant PreloadBridge as electronAPI.floatingPanels
  participant MainIPC as main.ts IPC handler
  participant applyShortcutRebind as applyShortcutRebind

  FloatingNavigatorSettings->>useShortcutCapture: capture(accelerator)
  useShortcutCapture->>useShortcutCapture: optimistic setShortcut(accelerator)
  useShortcutCapture->>PreloadBridge: setFloatingNavigatorShortcut(accelerator)
  PreloadBridge->>MainIPC: IPC floating-config-set-shortcut
  MainIPC->>applyShortcutRebind: applyShortcutRebind(rebinder, id, accelerator, previous)
  applyShortcutRebind-->>MainIPC: true / false (conflict)
  MainIPC-->>PreloadBridge: boolean result
  PreloadBridge-->>useShortcutCapture: resolved boolean
  alt resolved false (conflict)
    useShortcutCapture->>useShortcutCapture: rollback to lastGoodShortcutRef + onError(CONFLICT_MESSAGE)
  else resolved true
    useShortcutCapture->>useShortcutCapture: update lastGoodShortcutRef
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • laststance/corelive#46: Both PRs touch src/components/electron/BrainDumpSettings.tsx and related settings-state handling.
  • laststance/corelive#92: This PR extends the same floating-window always-on-top path with live broadcast and renderer synchronization.
  • laststance/corelive#23: The IPC typing and typed-send plumbing used here aligns with the existing typed IPC infrastructure in that PR.

Poem

🐇✨ I hopped through sections, neat and bright,
and tucked each toggle in its proper light.
A shortcut sings, a pin state glows,
and through the windows, synchronized it flows.
My whiskers twitch—what tidy code we’ve spun!
Hooray for settings, now neatly done.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: settings regrouping, Floating Navigator inline shortcut, and cross-window pin synchronization.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/settings-regroup-floating-shortcut

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
electron/__tests__/ipc-contract.test.ts (1)

150-159: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a max-length boundary assertion for this new shortcut schema test.

This case currently checks type/arity only; it should also assert that over-limit accelerators are rejected.

Proposed test addition
     it('accepts empty string (disable shortcut) for floating-config-set-shortcut', () => {
       const setShortcut = IPC_ARG_SCHEMAS['floating-config-set-shortcut']
       // Empty string is the "disable the global toggle" sentinel.
       expect(() => setShortcut.parse([''])).not.toThrow()
       expect(() => setShortcut.parse(['CommandOrControl+3'])).not.toThrow()
+      expect(() => setShortcut.parse(['x'.repeat(65)])).toThrow(ZodError)
       // A malicious renderer cannot smuggle a non-string past the trust boundary.
       expect(() => setShortcut.parse([null])).toThrow(ZodError)
       expect(() => setShortcut.parse([])).toThrow(ZodError)
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/__tests__/ipc-contract.test.ts` around lines 150 - 159, The new
shortcut schema test in IPC_ARG_SCHEMAS['floating-config-set-shortcut'] only
checks type and arity; extend it to also verify that overly long accelerator
strings are rejected. Update the existing it('accepts empty string...') case to
include a max-length boundary assertion for setShortcut.parse, using a clearly
over-limit string, so the test covers both valid shortcuts and the length guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/main.ts`:
- Around line 1476-1490: The rollback in this conflict path is using the stale
braindump.shortcut mirror instead of the canonical shortcut value. In the
shortcut handling block around applyShortcutRebind() and the shortcutManager
flow, read and pass the canonical shortcuts.toggleBrainDump value for rollback
so a conflict restores the real live binding, not an outdated mirror entry.
Ensure the fallback path in this update logic keeps the canonical key as the
source of truth even when braindump.shortcut is out of sync.

In `@electron/utils/applyShortcutRebind.ts`:
- Around line 48-63: The rollback in applyShortcutRebind() is not verified, so a
failed or substituted restore can still leave the shortcut state inconsistent.
Update the rollback path to check the return value/effective registration from
the second rebinder.updateShortcuts() call, using the existing previous,
didRegister, and getRegisteredShortcuts() logic, and only return false once
you’ve confirmed the prior accelerator was actually restored; otherwise
handle/report the restore failure explicitly.

In `@plans/2026-06-24-settings-regroup-and-floating-shortcut.md`:
- Line 14: Resolve the markdownlint failures in the plan doc by cleaning up
blockquote spacing, code-span spacing, fenced code block language annotations,
and table formatting so the document passes MD028/MD038/MD040/MD058. Update the
affected markdown sections in the plan content, especially the examples around
the shortcut flow and table rows, and ensure all fenced blocks use an explicit
language tag via the relevant markdown sections rather than leaving them
unlabeled.

In `@src/components/settings/SoundPreferences.tsx`:
- Around line 139-142: The `SoundPreferences` timbre audition path is firing an
async `previewTimbre()` without handling failures, which can leave rejected
promises unhandled while Redux still updates. Update the timbre selection flow
in `SoundPreferences` so the `previewTimbre(timbre.id, soundVolume)` call is
awaited or wrapped in explicit error handling, and make sure any failure from
`previewTimbre` is caught and logged/handled locally without breaking the user
gesture path.

In `@src/hooks/useShortcutCapture.ts`:
- Around line 84-91: The capture flow in useShortcutCapture treats persist()
returning undefined as a successful save, which incorrectly commits an
accelerator that was never persisted. Update the persist result handling in
capture() so that the branch for ok == null (or otherwise undefined) also
reverts to lastGoodShortcutRef.current and calls onError, similar to the
existing KEYBINDING_CONFLICT_MESSAGE path. Keep the success assignment to
lastGoodShortcutRef.current only for a definite true result.

---

Nitpick comments:
In `@electron/__tests__/ipc-contract.test.ts`:
- Around line 150-159: The new shortcut schema test in
IPC_ARG_SCHEMAS['floating-config-set-shortcut'] only checks type and arity;
extend it to also verify that overly long accelerator strings are rejected.
Update the existing it('accepts empty string...') case to include a max-length
boundary assertion for setShortcut.parse, using a clearly over-limit string, so
the test covers both valid shortcuts and the length guard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c2832235-cf55-488f-bb34-2f0d0c6a1b88

📥 Commits

Reviewing files that changed from the base of the PR and between 42e88c3 and 480d4f1.

📒 Files selected for processing (40)
  • e2e/electron/floating-always-on-top-sync.spec.ts
  • electron/WindowManager.ts
  • electron/__tests__/WindowManager.always-on-top.test.ts
  • electron/__tests__/applyShortcutRebind.test.ts
  • electron/__tests__/ipc-contract.test.ts
  • electron/ipc/ipc-schemas.ts
  • electron/main.ts
  • electron/preload-floating.ts
  • electron/preload.ts
  • electron/types/electron-api.d.ts
  • electron/types/ipc.ts
  • electron/utils/applyShortcutRebind.ts
  • plans/2026-06-24-settings-regroup-and-floating-shortcut.md
  • src/app/settings/page.tsx
  • src/components/ThemeSelector.tsx
  • src/components/electron/AppUpdateSettings.tsx
  • src/components/electron/BrainDumpAppearance.test.tsx
  • src/components/electron/BrainDumpAppearance.tsx
  • src/components/electron/BrainDumpSettings.tsx
  • src/components/electron/ElectronSettingsPage.test.tsx
  • src/components/electron/ElectronSettingsPage.tsx
  • src/components/electron/FloatingNavigatorSettings.test.tsx
  • src/components/electron/FloatingNavigatorSettings.tsx
  • src/components/electron/FloatingPanelToggle.test.tsx
  • src/components/electron/FloatingPanelToggle.tsx
  • src/components/electron/FloatingWindowSettings.test.tsx
  • src/components/electron/FloatingWindowSettings.tsx
  • src/components/electron/StartupWindowSettings.tsx
  • src/components/floating-navigator/FloatingNavigator.test.tsx
  • src/components/floating-navigator/FloatingNavigator.tsx
  • src/components/settings/PreferencesSettings.test.tsx
  • src/components/settings/PreferencesSettings.tsx
  • src/components/settings/SettingsSection.tsx
  • src/components/settings/SoundPreferences.test.tsx
  • src/components/settings/SoundPreferences.tsx
  • src/components/settings/TaskPreferences.test.tsx
  • src/components/settings/TaskPreferences.tsx
  • src/hooks/useFloatingPanelPreference.ts
  • src/hooks/useShortcutCapture.test.ts
  • src/hooks/useShortcutCapture.ts
💤 Files with no reviewable changes (4)
  • src/components/electron/FloatingWindowSettings.test.tsx
  • src/components/settings/PreferencesSettings.tsx
  • src/components/electron/FloatingWindowSettings.tsx
  • src/components/settings/PreferencesSettings.test.tsx

Comment thread electron/main.ts Outdated
Comment thread electron/preload.ts
Comment thread electron/utils/applyShortcutRebind.ts
Comment thread plans/2026-06-24-settings-regroup-and-floating-shortcut.md
Comment thread src/components/settings/SoundPreferences.tsx
Comment thread src/hooks/useShortcutCapture.ts
Resolve the actionable findings from CodeRabbit's review of PR #106:

- main.ts (Major): source the BrainDump rebind rollback target from the
  CANONICAL shortcuts.toggleBrainDump, not the braindump.shortcut mirror, so a
  conflict restores the real live binding even when the generic keybind UI
  rebound the canonical key without updating the mirror.
- preload.ts (Major): getFloatingNavigatorShortcut now surfaces a load failure
  instead of returning '' — '' is the real "intentionally unbound" value, so
  masking an IPC error as '' would seed the wrong shortcut into rollback state.
  The sole consumer already catches and shows "Failed to load shortcut".
- applyShortcutRebind.ts (Major): verify the rollback restore via a shared
  restorePreviousOrThrow helper and throw when it fails, so a conflict can't
  silently leave persisted config and the live registration out of sync.
  (updateShortcuts({id:''}) returns true, so empty-previous never spuriously
  throws — confirmed against ShortcutManager.updateShortcuts.)
- useShortcutCapture.ts (Minor): treat persist()->undefined (no desktop bridge)
  as a no-op revert, not a successful save, so an un-persisted accelerator is
  never committed as the new last-good rollback target.
- ipc-contract.test.ts (Nitpick): assert an over-length accelerator is rejected.
- plan doc (Minor): markdownlint — tag the 4 ASCII diagram fences as `text`
  and fix the MD028 blockquote blank.

Skipped (false positive): the previewTimbre() .catch finding — previewTimbre ->
playTimbre wraps its whole body (incl. the await) in a try/catch and is
documented "never throws", so a .catch would be unreachable and contradicts the
engine's own documented call pattern.
@ryota-murakami

Copy link
Copy Markdown
Collaborator Author

CodeRabbit review — resolution (commit f6bc42e)

Thanks for the review. All 6 actionable findings + the nitpick are addressed in f6bc42e; one is a documented skip.

# Finding Sev Resolution
1 main.ts — rollback used the braindump.shortcut mirror 🟠 Major Fixed. previous now reads the canonical shortcuts.toggleBrainDump, so a conflict restores the real live binding even if the generic keybind UI rebound the canonical key without touching the mirror.
2 preload.tsgetFloatingNavigatorShortcut returned '' on load error 🟠 Major Fixed. Now re-throws. '' is the real "intentionally unbound" value; the sole consumer already .catches and surfaces "Failed to load shortcut", so a load error no longer seeds disabled-state into rollback.
3 applyShortcutRebind.ts — rollback restore unverified 🟠 Major Fixed. Extracted restorePreviousOrThrow; both rollback paths verify the restore and throw on failure (callers already try/catch→log→return false). Confirmed updateShortcuts({id:''}) returns true, so an empty previous (re-disable) never spuriously throws.
4 plan doc — markdownlint 🟡 Minor Fixed the substantive part: tagged the 4 ASCII-diagram fences text (MD040) + fixed the MD028 blockquote blank. Left the remaining MD038/MD058 spacing nits — purely cosmetic on a non-CI-gated planning artifact.
5 SoundPreferences.tsxvoid previewTimbre() unhandled rejection 🟡 Minor Skipped (false positive). previewTimbreplayTimbre wraps its entire body (incl. the await decodeTimbre) in a try/catch and is documented "never throws (a cue must not break a flow)" (soundEngine.ts:250). A .catch would be unreachable and contradicts the engine's own documented call pattern (soundEngine.ts:293 shows bare void previewTimbre(...)).
6 useShortcutCapture.tspersist()→undefined treated as success 🟡 Minor Fixed. ok === undefined (no desktop bridge) now reverts the optimistic value without committing it as the new last-good (and without an error toast — it's not a conflict). Added a unit test for the branch.
7 ipc-contract.test.ts — max-length boundary 🔵 Nitpick Fixed. Added an over-length ('x'.repeat(65)) → ZodError assertion.

Local gate green on CI-matched Node 24.13.0: pnpm validate (662 unit tests, typecheck, build, lint, theme).

@ryota-murakami

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
plans/2026-06-24-settings-regroup-and-floating-shortcut.md (1)

235-235: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the MD038 code-span spacing at Line 235.

The code span has an internal trailing space (requested==='' ), which violates markdownlint MD038. Remove the space before the closing backtick.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plans/2026-06-24-settings-regroup-and-floating-shortcut.md` at line 235, The
markdown table entry in the settings regroup plan has a code span spacing issue:
the `requested==='' ` token contains an extra trailing space that triggers
MD038. Update the same table row by removing the space inside that inline code
span so the `requested`, `bound`, and conflict-check text remain unchanged, and
verify the surrounding note still reads correctly.

Source: Linters/SAST tools

🧹 Nitpick comments (1)
src/hooks/useShortcutCapture.test.ts (1)

64-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tighten the no-error assertion.

not.toHaveBeenCalledWith(KEYBINDING_CONFLICT_MESSAGE) only rules out the conflict copy; the test would still pass if the hook erroneously surfaced the generic SHORTCUT_SAVE_FAILED_MESSAGE. Since the no-bridge branch should emit only the initial onError(null), assert that explicitly.

♻️ Stronger assertion
     expect(result.current.shortcut).toBe('Alt+Space')
-    expect(onError).not.toHaveBeenCalledWith(KEYBINDING_CONFLICT_MESSAGE)
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(null)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useShortcutCapture.test.ts` around lines 64 - 67, The test in
useShortcutCapture.test should assert the exact no-error behavior for the
no-bridge path, not just exclude KEYBINDING_CONFLICT_MESSAGE. Update the
expectation around onError so it verifies the hook only emits the initial
null/error-clear call and does not surface SHORTCUT_SAVE_FAILED_MESSAGE or any
other error; use the existing useShortcutCapture and onError assertions to make
the intent explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@plans/2026-06-24-settings-regroup-and-floating-shortcut.md`:
- Line 235: The markdown table entry in the settings regroup plan has a code
span spacing issue: the `requested==='' ` token contains an extra trailing space
that triggers MD038. Update the same table row by removing the space inside that
inline code span so the `requested`, `bound`, and conflict-check text remain
unchanged, and verify the surrounding note still reads correctly.

---

Nitpick comments:
In `@src/hooks/useShortcutCapture.test.ts`:
- Around line 64-67: The test in useShortcutCapture.test should assert the exact
no-error behavior for the no-bridge path, not just exclude
KEYBINDING_CONFLICT_MESSAGE. Update the expectation around onError so it
verifies the hook only emits the initial null/error-clear call and does not
surface SHORTCUT_SAVE_FAILED_MESSAGE or any other error; use the existing
useShortcutCapture and onError assertions to make the intent explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d1298131-c1e5-444d-8851-b6d1209a1e36

📥 Commits

Reviewing files that changed from the base of the PR and between 480d4f1 and f6bc42e.

📒 Files selected for processing (8)
  • electron/__tests__/applyShortcutRebind.test.ts
  • electron/__tests__/ipc-contract.test.ts
  • electron/main.ts
  • electron/preload.ts
  • electron/utils/applyShortcutRebind.ts
  • plans/2026-06-24-settings-regroup-and-floating-shortcut.md
  • src/hooks/useShortcutCapture.test.ts
  • src/hooks/useShortcutCapture.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • electron/tests/applyShortcutRebind.test.ts
  • electron/utils/applyShortcutRebind.ts
  • electron/tests/ipc-contract.test.ts
  • electron/main.ts
  • src/hooks/useShortcutCapture.ts

@ryota-murakami ryota-murakami merged commit 293d37c into main Jun 24, 2026
25 checks passed
@ryota-murakami ryota-murakami deleted the feat/settings-regroup-floating-shortcut branch June 24, 2026 12:08
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