Skip to content

refactor: code quality cleanup — dead code, boilerplate, types, constants, AI comments#77

Merged
VariableThe merged 3 commits into
mainfrom
feat/keybinds-and-timers-v0.5.6
Jun 29, 2026
Merged

refactor: code quality cleanup — dead code, boilerplate, types, constants, AI comments#77
VariableThe merged 3 commits into
mainfrom
feat/keybinds-and-timers-v0.5.6

Conversation

@VariableThe

@VariableThe VariableThe commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Summary

Comprehensive code quality cleanup across the entire codebase:

Dead Code Removal

  • resumeTimer (no-op stub in useTimerStore)
  • onSwipeGesture (ignores callback in api.ts)
  • themePreset/setThemePreset (duplicated in useAppStore — all consumers use useSettingsStore)
  • prevNotesRef (assigned but never read in useReminders)

Boilerplate Consolidation

  • useAppStore.ts: 9 setters reduced via booleanSetter/simpleSetter helpers
  • api.ts: 5 identical listener patterns → single onEvent helper (63→40 lines)
  • KeybindsModal.tsx: 9 parallel useState/getShortcut calls → data-driven config array (223→140 lines)
  • useSettingsStore.ts: 11 redundant individual setters removed — use setSettings instead

Rust Fixes

  • Fixed clippy needless_borrows_for_generic_args in notifications.rs
  • Added [lints.rust] unexpected_cfgs = "allow" for old objc crate macros

Type Safety

  • any → typed GraphControls interface in GraphView
  • Promise<unknown> → properly typed response for openAIChat
  • Unsafe as casts replaced with wrapper functions

Magic Numbers → Named Constants

~25 magic numbers extracted: toast timeouts, z-indices, force parameters, debounce intervals, canvas dimensions, etc.

Comment Cleanup

Removed ~15 pedagogical/AI-generated comments that explain the obvious

Verification

  • ✅ ESLint: 0 errors
  • ✅ TypeScript: 0 errors
  • ✅ Tests: 35/35 pass
  • ✅ Build: succeeds
  • ✅ cargo clippy -D warnings: 0 warnings
  • ⚠️ npm audit: 1 high (pre-existing expr-eval vuln)

Summary by CodeRabbit

  • New Features

    • Improved note, timer, and reminder responsiveness with smoother delays and more consistent timing.
    • Updated keybinding management to make shortcut settings easier to review and reset.
  • Bug Fixes

    • Improved window and graph behavior for more reliable positioning and interaction.
    • Fixed reminder scheduling to better avoid outdated updates.
    • Refined AI chat response handling for more stable results.
  • Chores

    • Cleaned up app settings and store behavior by removing unused legacy options.

…ants, AI comments

- Remove dead code: resumeTimer (no-op), onSwipeGesture (ignores callback),
  themePreset from useAppStore (duplicated), prevNotesRef (unused)
- Consolidate boilerplate: booleanSetter helper in useAppStore, onEvent helper
  in api.ts, data-driven KeybindsModal, removed 11 redundant setters
- Fix Rust: clippy needless_borrows_for_generic_args, unexpected_cfgs lint
- Improve types: GraphControls interface, typed openAIChat response
- Extract ~25 magic numbers to named constants
- Remove ~15 pedagogical AI-style comments
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@VariableThe, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c2c6e1b-d7a3-4b37-972c-754e9ddcf86f

📥 Commits

Reviewing files that changed from the base of the PR and between 5289386 and 587bf21.

📒 Files selected for processing (14)
  • AUDIT_LOG.md
  • CHANGELOG.md
  • README.md
  • features.md
  • src-tauri/Cargo.toml
  • src-tauri/src/macos.rs
  • src/App.tsx
  • src/api.ts
  • src/components/KeybindsModal.tsx
  • src/components/TimersPage.tsx
  • src/hooks/useReminders.ts
  • src/lib/editor/extensions.ts
  • src/setupTests.ts
  • src/types.d.ts
📝 Walkthrough

Walkthrough

This PR replaces inline magic numbers with named constants across Rust (lib.rs, notifications.rs) and TypeScript (stores, components, editor utilities, GraphView). It also consolidates useAppStore and useSettingsStore setter APIs, removes themePreset from AppState, refactors KeybindsModal to a data-driven config, removes onSwipeGesture from ElectronAPI, and tightens the openAIChat return type.

Changes

Refactor and Cleanup

Layer / File(s) Summary
ElectronAPI type updates and onSwipeGesture removal
src/types.d.ts, src/api.ts, src/lib/editor/extensions.ts, src/setupTests.ts
openAIChat return type changed from Promise<unknown> to a structured choices/error shape; onSwipeGesture removed from ElectronAPI, tauriApi, and test mocks; onEvent helper centralizes Tauri listen/unlisten for remaining subscriptions.
AppStore and SettingsStore consolidation
src/store/useAppStore.ts, src/store/useSettingsStore.ts, src/store/useAppStore.test.ts
AppState removes themePreset and setThemePreset; useAppStore rewritten with internal booleanSetter/simpleSetter helpers. SettingsState drops all per-field setters in favor of a single setSettings(partial | fn). Tests updated to match.
KeybindsModal data-driven refactor
src/components/KeybindsModal.tsx
ShortcutConfig type and centralized shortcuts array replace per-shortcut useState; a single values record replaces individual fields; handleSave and handleResetDefaults iterate the config; rendered rows map over globalShortcuts/appShortcuts.
Timer store and reminders hook cleanup
src/store/useTimerStore.ts, src/hooks/useReminders.ts
resumeTimer removed from TimerState; COMPLETED_TIMER_CLEANUP_MS constant introduced. useReminders drops prevNotesRef/useRef and simplifies pending-reminder computation while preserving scheduleToken monotonicity.
Frontend magic-number constants
src/App.tsx, src/components/Editor.tsx, src/components/TimersPage.tsx, src/lib/editor/MathEvaluator.ts, src/lib/editor/VariableScope.ts, src/GraphView.tsx
Named constants replace inline literals for toast timeout, focus delay, zIndex values, save debounce, tick interval, math/scope debounce delays, and all GraphView camera/force/label/sizing parameters. GraphControls interface replaces any typing.
Rust timing constants and build lint config
src-tauri/Cargo.toml, src-tauri/src/lib.rs, src-tauri/src/commands/notifications.rs
FOCUS_LOSS_DEBOUNCE_MS and WINDOW_STATE_RESTORE_DELAY_MS replace inline from_millis literals; notification body ownership corrected; unexpected_cfgs lint set to allow in Cargo.toml.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • VariableThe/PaperCache#76: Both PRs modify KeybindsModal.tsx and the timer store's cleanup behavior at the same code locations.
  • VariableThe/PaperCache#12: That PR changed ElectronAPI.onSwipeGesture to return an unsubscribe disposer; this PR removes it entirely from the interface.
  • VariableThe/PaperCache#15: Both PRs modify ElectronAPI.openAIChat's contract in src/types.d.ts.

Poem

A rabbit named Const hopped through the code,
swapping 5000 for names on the road.
"No more bare numbers!" she twitched her nose,
Stores got slimmer, and neatness grows.
onSwipeGesture? Gone like last night's clover—
The magic numbers era is finally over! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s broad code-quality cleanup across dead code, boilerplate, types, constants, and comment removal.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/keybinds-and-timers-v0.5.6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/hooks/useReminders.ts (1)

44-50: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make reminder scheduling last-write-wins before calling Electron.

Line 49 checks the token only after Line 47 has already sent pending to scheduleReminders, so an older in-flight call can still overwrite newer reminder schedules when it resolves later.

Possible fix: serialize scheduling and skip superseded payloads
 let scheduleToken = 0
+let scheduleQueue: Promise<void> = Promise.resolve()
     const pending = collectFutureReminders(notes)
     const token = ++scheduleToken
 
-    window.electronAPI
-      .scheduleReminders(pending)
-      .then(() => {
-        if (token !== scheduleToken) return
-      })
-      // eslint-disable-next-line no-console
-      .catch((e) => console.error('Failed to schedule reminders', e))
+    scheduleQueue = scheduleQueue
+      .catch(() => undefined)
+      .then(async () => {
+        if (token !== scheduleToken) return
+        await window.electronAPI.scheduleReminders(pending)
+      })
+      // eslint-disable-next-line no-console
+      .catch((e) => {
+        if (token === scheduleToken) console.error('Failed to schedule reminders', e)
+      })
   }, [notes])
🤖 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/useReminders.ts` around lines 44 - 50, In useReminders, the token
check currently happens only after window.electronAPI.scheduleReminders(pending)
has already been invoked, so stale payloads can still be sent and later
overwrite newer schedules. Move the last-write-wins guard so it skips superseded
pending data before calling scheduleReminders, and ensure only the latest
scheduling request is allowed to reach Electron. Use the existing scheduleToken
logic in useReminders to serialize or gate requests so older in-flight updates
cannot apply after a newer one.
src/App.tsx (1)

123-127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

searchInputRef is never attached, so this focus path is a no-op.

NoteSearch is rendered without a forwarded ref here, so searchInputRef.current stays null and this branch can never focus the search box. Either pass the ref into NoteSearch or remove this effect if the child owns focus now.

🤖 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/App.tsx` around lines 123 - 127, The focus effect in App.tsx is a no-op
because searchInputRef is never connected to the rendered NoteSearch input.
Either forward and attach searchInputRef through NoteSearch so the useEffect can
focus the actual search box, or remove the App-level focus logic if NoteSearch
now manages its own focus. Use the existing useEffect, searchInputRef, and
NoteSearch symbols to wire the ref correctly or delete the dead path.
🧹 Nitpick comments (4)
src/components/KeybindsModal.tsx (1)

123-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the global shortcut action/key mapping into ShortcutConfig.

This loop is still hardcoded to exactly two global shortcuts. Adding another section: 'global' entry will silently reuse the 'new-note' action/path.

♻️ Proposed refactor
 interface ShortcutConfig {
   key: string
   label: string
   storageKey: string
   defaultKey: string
   section: 'global' | 'app'
+  globalAction?: 'toggle' | 'new-note'
+  legacyStorageKey?: string
 }

   const shortcuts: ShortcutConfig[] = [
     {
       key: 'shortcutToggle',
       label: 'Toggle App Visibility',
       storageKey: SETTINGS_KEYS.SHORTCUT_TOGGLE,
       defaultKey: `${defaultMod}+Shift+C`,
       section: 'global',
+      globalAction: 'toggle',
+      legacyStorageKey: SETTINGS_KEYS.SHORTCUT_TOGGLE,
     },
     {
       key: 'shortcutNewNote',
       label: 'New Note (Global)',
       storageKey: SETTINGS_KEYS.SHORTCUT_NEWNOTE,
       defaultKey: `${defaultMod}+Shift+N`,
       section: 'global',
+      globalAction: 'new-note',
+      legacyStorageKey: SETTINGS_KEYS.SHORTCUT_NEWNOTE,
     },

   const handleSave = () => {
     for (const sc of shortcuts) {
-      if (sc.section === 'global') {
-        const oldShortcutKey =
-          sc.key === 'shortcutToggle' ? 'papercache-shortcut-toggle' : 'papercache-shortcut-newnote'
-        const oldShortcut = localStorage.getItem(oldShortcutKey) || sc.defaultKey
-        const action = sc.key === 'shortcutToggle' ? 'toggle' : 'new-note'
+      if (sc.section === 'global' && sc.globalAction) {
+        const oldShortcut =
+          localStorage.getItem(sc.legacyStorageKey ?? sc.storageKey) || sc.defaultKey
         if (window.electronAPI.updateGlobalShortcut) {
-          window.electronAPI.updateGlobalShortcut(action, oldShortcut, values[sc.key])
+          window.electronAPI.updateGlobalShortcut(sc.globalAction, oldShortcut, values[sc.key])
         }
-        localStorage.setItem(oldShortcutKey, values[sc.key])
+        if (sc.legacyStorageKey) {
+          localStorage.setItem(sc.legacyStorageKey, values[sc.key])
+        }
       }
       localStorage.setItem(sc.storageKey, values[sc.key])
     }
🤖 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/components/KeybindsModal.tsx` around lines 123 - 130, The global shortcut
loop in KeybindsModal.tsx is still hardcoded to two cases, so add the action and
storage key mapping to ShortcutConfig and use that metadata here instead of
branching on sc.key. Update the code that builds oldShortcutKey and action
inside the shortcuts loop to read from each ShortcutConfig entry, so any new
section: 'global' shortcut can be handled without silently falling back to
'new-note'. Keep the updateGlobalShortcut call using the resolved action, stored
shortcut value, and values[sc.key] from the same config-driven source.
src-tauri/Cargo.toml (1)

36-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope the unexpected_cfgs suppression more narrowly.

Line 37 disables typo checking for every #[cfg] in this crate, not just the legacy objc macro case called out in the PR notes. Prefer whitelisting the specific cfgs or localizing the allow so real platform-gating mistakes still surface.

🤖 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-tauri/Cargo.toml` around lines 36 - 37, The current lints.rust setting
for unexpected_cfgs is too broad and suppresses cfg typo checks across the
entire crate. Narrow the suppression by updating the Cargo.toml lint
configuration to only whitelist the specific legacy objc-related cfg case or
move the allow closer to the affected code path, using the existing lints.rust
section as the place to adjust. Keep real #[cfg] validation enabled everywhere
else so platform-gating mistakes still surface.
src/api.ts (1)

58-59: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Expose these bridge methods as Promise<void>. src/types.d.ts and the test mock still declare pauseShortcuts/resumeShortcuts as void, so callers can’t await the invoke(...) result or handle failures. Update the contract and mocks, and drop the cast in src/api.ts.

🤖 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/api.ts` around lines 58 - 59, The bridge methods pauseShortcuts and
resumeShortcuts are typed inconsistently with their async invoke behavior, so
update the contract to return Promise<void> instead of void. Change the
declarations in src/types.d.ts and the test mock to match, then remove the
unnecessary cast in src/api.ts so the functions directly return the invoke(...)
Promise. Ensure the API surface and mock names stay aligned for pauseShortcuts
and resumeShortcuts.
src/components/TimersPage.tsx (1)

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

Use TICK_INTERVAL_MS for the initial schedule too.

The follow-up tick now uses the constant, but the first setTimeout in this loop still hard-codes 250. That leaves the countdown with split sources of truth.

♻️ Proposed fix
-    timeoutRef.current = setTimeout(tick, 250)
+    timeoutRef.current = setTimeout(tick, TICK_INTERVAL_MS)

Also applies to: 40-40

🤖 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/components/TimersPage.tsx` at line 1, The countdown loop in TimersPage
has split sources of truth because the first schedule still hard-codes 250 while
the follow-up uses TICK_INTERVAL_MS. Update the initial setTimeout in the timer
loop to use TICK_INTERVAL_MS as well, and keep the change aligned with the timer
scheduling logic in TimersPage so both the first and subsequent ticks share the
same constant.
🤖 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 `@src/api.ts`:
- Around line 5-14: The onEvent helper in src/api.ts can leak a listener if
cleanup happens before listen(...).then(...) assigns unlisten. Update onEvent to
track a disposed flag alongside unlisten, so the returned cleanup marks the
callback as disposed and, when the listen promise resolves, immediately invokes
the resolved unlisten if disposal already happened; keep the fix centered on
onEvent and its listen/unlisten flow.

In `@src/lib/editor/extensions.ts`:
- Around line 174-178: The completion handling in window.electronAPI.openAIChat
assumes any non-empty choices array is valid, but the new response shape allows
message.content to be missing. Update the response validation in the editor
extension so it checks the selected choice’s message.content actually exists and
is non-empty before treating the call as success; otherwise fall back to the
error path or an explicit empty-state handling. Use the openAIChat call site and
the completion/choices handling logic to locate the fix.

---

Outside diff comments:
In `@src/App.tsx`:
- Around line 123-127: The focus effect in App.tsx is a no-op because
searchInputRef is never connected to the rendered NoteSearch input. Either
forward and attach searchInputRef through NoteSearch so the useEffect can focus
the actual search box, or remove the App-level focus logic if NoteSearch now
manages its own focus. Use the existing useEffect, searchInputRef, and
NoteSearch symbols to wire the ref correctly or delete the dead path.

In `@src/hooks/useReminders.ts`:
- Around line 44-50: In useReminders, the token check currently happens only
after window.electronAPI.scheduleReminders(pending) has already been invoked, so
stale payloads can still be sent and later overwrite newer schedules. Move the
last-write-wins guard so it skips superseded pending data before calling
scheduleReminders, and ensure only the latest scheduling request is allowed to
reach Electron. Use the existing scheduleToken logic in useReminders to
serialize or gate requests so older in-flight updates cannot apply after a newer
one.

---

Nitpick comments:
In `@src-tauri/Cargo.toml`:
- Around line 36-37: The current lints.rust setting for unexpected_cfgs is too
broad and suppresses cfg typo checks across the entire crate. Narrow the
suppression by updating the Cargo.toml lint configuration to only whitelist the
specific legacy objc-related cfg case or move the allow closer to the affected
code path, using the existing lints.rust section as the place to adjust. Keep
real #[cfg] validation enabled everywhere else so platform-gating mistakes still
surface.

In `@src/api.ts`:
- Around line 58-59: The bridge methods pauseShortcuts and resumeShortcuts are
typed inconsistently with their async invoke behavior, so update the contract to
return Promise<void> instead of void. Change the declarations in src/types.d.ts
and the test mock to match, then remove the unnecessary cast in src/api.ts so
the functions directly return the invoke(...) Promise. Ensure the API surface
and mock names stay aligned for pauseShortcuts and resumeShortcuts.

In `@src/components/KeybindsModal.tsx`:
- Around line 123-130: The global shortcut loop in KeybindsModal.tsx is still
hardcoded to two cases, so add the action and storage key mapping to
ShortcutConfig and use that metadata here instead of branching on sc.key. Update
the code that builds oldShortcutKey and action inside the shortcuts loop to read
from each ShortcutConfig entry, so any new section: 'global' shortcut can be
handled without silently falling back to 'new-note'. Keep the
updateGlobalShortcut call using the resolved action, stored shortcut value, and
values[sc.key] from the same config-driven source.

In `@src/components/TimersPage.tsx`:
- Line 1: The countdown loop in TimersPage has split sources of truth because
the first schedule still hard-codes 250 while the follow-up uses
TICK_INTERVAL_MS. Update the initial setTimeout in the timer loop to use
TICK_INTERVAL_MS as well, and keep the change aligned with the timer scheduling
logic in TimersPage so both the first and subsequent ticks share the same
constant.
🪄 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

Run ID: e2f3606a-885c-42c6-99b4-5c0ad001ce11

📥 Commits

Reviewing files that changed from the base of the PR and between 772f2b7 and 5289386.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • src-tauri/Cargo.toml
  • src-tauri/src/commands/notifications.rs
  • src-tauri/src/lib.rs
  • src/App.tsx
  • src/GraphView.tsx
  • src/api.ts
  • src/components/Editor.tsx
  • src/components/KeybindsModal.tsx
  • src/components/TimersPage.tsx
  • src/hooks/useReminders.ts
  • src/lib/editor/MathEvaluator.ts
  • src/lib/editor/VariableScope.ts
  • src/lib/editor/extensions.ts
  • src/setupTests.ts
  • src/store/useAppStore.test.ts
  • src/store/useAppStore.ts
  • src/store/useSettingsStore.ts
  • src/store/useTimerStore.ts
  • src/types.d.ts
💤 Files with no reviewable changes (1)
  • src/store/useAppStore.test.ts

Comment thread src/api.ts
Comment thread src/lib/editor/extensions.ts
… ref, stale guard, cfg scope, shortcut loop, timer constant
…de quality cleanup and keybinds customization
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.

1 participant