Skip to content

Improve performance, efficiency, and code quality#1

Merged
erictli merged 2 commits into
mainfrom
performance-improvements
Jan 31, 2026
Merged

Improve performance, efficiency, and code quality#1
erictli merged 2 commits into
mainfrom
performance-improvements

Conversation

@erictli
Copy link
Copy Markdown
Owner

@erictli erictli commented Jan 31, 2026

Summary

Comprehensive performance and code quality improvements across both the Rust backend and React frontend.

Backend (Rust)

  • Tantivy full-text search: Replaced naive O(n×m) string scanning with indexed full-text search
  • Async I/O: Converted file operations to async using tokio::fs for non-blocking reads/writes
  • Better concurrency: Replaced Mutex with RwLock for settings/cache (multiple readers)
  • Memory leak fix: Added periodic cleanup for file watcher debounce map
  • Cleaner errors: Changed unwrap() to expect() with descriptive messages
  • Smaller binary: Removed unused dependencies (uuid, chrono, regex)

Frontend (React/TypeScript)

  • CommandPalette: Added useMemo for commands, filtered notes/commands, and all items
  • NoteList: Memoized displayItems and wrapped NoteItem with React.memo
  • FormatBar: Wrapped with React.memo to prevent re-renders on every keystroke
  • App: Memoized displayItems to stabilize keyboard handler dependencies
  • NotesContext: Split into separate data/actions contexts to reduce re-render cascade
  • FlipText: Optimized animation with single RAF loop instead of nested setTimeout
  • Editor: Fixed race condition causing off-by-one note selection bug
  • Vite: Added manual chunk splitting for better caching (tiptap, react, tauri)

Test Plan

  • Frontend builds without errors
  • Rust backend compiles without warnings
  • Note selection works correctly (no off-by-one)
  • Search returns relevant results
  • Manual testing of all features

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Full-text search with on-demand index rebuilding and live indexing on note changes.
  • Performance

    • Reduced unnecessary re-renders via memoization across lists, command palette, UI text, and main app.
    • Faster async I/O and improved file-watch debouncing for snappier interactions.
  • Reliability

    • Improved editor load/focus behavior and safer note-loading transitions.
  • Build

    • Production build optimizations for smaller, faster bundles.

✏️ Tip: You can customize this high-level summary in your review settings.

Backend (Rust):
- Implement Tantivy full-text search with indexed queries
- Convert file I/O to async using tokio::fs
- Replace Mutex with RwLock for settings/cache (better read concurrency)
- Fix debounce map memory leak with periodic cleanup
- Fix mutex error handling with descriptive expect messages
- Remove unused dependencies (uuid, chrono, regex)

Frontend (React/TypeScript):
- Add useMemo to CommandPalette for filtered items
- Memoize NoteList displayItems and wrap NoteItem with React.memo
- Wrap FormatBar with React.memo to prevent re-renders on keystrokes
- Memoize App.tsx displayItems for stable keyboard handler deps
- Split NotesContext into separate data/actions contexts
- Optimize FlipText animation with single RAF loop
- Fix note selection race condition in Editor
- Add Vite build optimizations with manual chunk splitting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds a Tantivy-backed search index and async/Tokio-based I/O to the Tauri backend, refactors AppState to use RWLocks and debounce tracking, converts several backend commands to async, applies memoization across multiple React components, splits NotesContext into data/actions, and adds Vite production build optimizations.

Changes

Cohort / File(s) Summary
Backend Cargo
src-tauri/Cargo.toml
Removed uuid, chrono, regex; added tokio = { version = "1", features = ["fs","sync"] }.
Search & AppState
src-tauri/src/lib.rs
Added SearchIndex (Tantivy-backed) and methods (index, delete, search, rebuild). Refactored AppState to use RwLock for settings/notes_cache, added search_index and debounce_map, converted note commands to async, added rebuild_search_index, and switched many file IO paths to async/Tokio.
Frontend Context Split
src/context/NotesContext.tsx
Split single NotesContext into NotesDataContext and NotesActionsContext, added memoized values, separate hooks useNotesData/useNotesActions, and compatibility useNotes.
React Memoization
src/App.tsx, src/components/command-palette/CommandPalette.tsx, src/components/notes/NoteList.tsx, src/components/ui/FlipText.tsx
Introduced useMemo/memo across components: memoized displayItems, commands, filtered lists, NoteItem, and converted FlipText to a memoized component with a unified animation loop.
Editor Loading/Focus
src/components/editor/Editor.tsx
Replaced prevNoteId tracking with loadedNoteIdRef, refined note-loading effect to avoid redundant loads, used requestAnimationFrame for focus/link handling, and adjusted isLoading/isDirty handling.
Vite Build
vite.config.ts
Added build targets, switched to esbuild minify, manualChunks for tiptap/react-vendor/tauri, disabled sourcemaps, raised chunkSizeWarningLimit, and added optimizeDeps includes.

Sequence Diagram(s)

sequenceDiagram
    participant Frontend as Frontend
    participant Handler as Tauri Command
    participant FS as FileSystem (async tokio)
    participant Index as SearchIndex (Tantivy)
    participant Evt as EventEmitter

    Frontend->>Handler: save_note(title, content)
    Handler->>FS: async write note file
    FS-->>Handler: write success
    Handler->>Index: index_note(id, title, content, modified)
    Index-->>Handler: indexed
    Handler->>Evt: emit FileChangeEvent
    Evt-->>Frontend: note updated

    Frontend->>Handler: delete_note(id)
    Handler->>FS: async remove file
    FS-->>Handler: remove success
    Handler->>Index: delete_note(id)
    Index-->>Handler: deleted
    Handler->>Evt: emit FileChangeEvent
    Evt-->>Frontend: note removed

    Frontend->>Handler: rebuild_search_index()
    Handler->>FS: async scan notes folder
    FS-->>Handler: list of note files
    Handler->>Index: rebuild_index(notes_folder)
    loop per note
      Index->>Index: index_note(...)
    end
    Index-->>Handler: rebuild complete
    Handler-->>Frontend: ready
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

A rabbit coded day and night, 🐰
Indexing words till search took flight,
Async hops made disk IO swift,
Memoized leaves gave UI a lift,
Vite packed bundles snug and tight. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title is overly broad and generic, using non-descriptive terms like 'performance, efficiency, and code quality' that don't convey the specific nature of the major changes. Consider a more specific title that highlights the main change, such as 'Add full-text search with Tantivy indexing and async I/O' or 'Implement indexed search and async operations with performance optimizations'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch performance-improvements

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
src-tauri/src/lib.rs (1)

734-814: ⚠️ Potential issue | 🟠 Major

Search index becomes stale when files are modified externally.

The file-change event triggers refreshNotes() which reloads the notes list from disk, but the search index (Tantivy) is never updated. The search index is only rebuilt at startup (line 386), or when notes are explicitly created/edited/deleted through the app (lines 554, 588, 638). Files modified outside the app won't be indexed, causing searches to return stale results. Add a call to rebuild_search_index after debounced external file changes, or selectively re-index changed files instead of refreshing the entire index.

🤖 Fix all issues with AI agents
In `@src/components/editor/Editor.tsx`:
- Around line 313-367: The requestAnimationFrame callback can clear
isLoadingRef.current for a different note if notes switch quickly; capture the
currentNote.id (e.g., const loadingId = loadedNoteIdRef.current) before
scheduling RAF and inside the RAF bail early unless loadedNoteIdRef.current ===
loadingId, so we only set isLoadingRef.current = false and run the
focus/selectAll logic for the same note that initiated the load; update the
useEffect that uses loadedNoteIdRef, isLoadingRef, and requestAnimationFrame
accordingly to avoid clearing the flag for a newer load and prevent onUpdate
from saving the wrong note.
- Line 1: FormatBar is wrapped with React.memo causing its buttons to show stale
state because the editor instance mutates selection/marks without changing
reference; remove the memoization so FormatBar re-renders with the parent Editor
updates: delete the memo import usage and replace any export/default export like
export default memo(FormatBar) with export default FormatBar (or simply export
the component), and remove "memo" from the React import list so FormatBar
receives fresh props and can rely on editor.isActive(...) to reflect current
selection.

In `@vite.config.ts`:
- Around line 34-40: The build.target currently set to "esnext" is too
bleeding-edge for WebView compatibility; update the Vite config's build.target
(in vite.config.ts) to a supported baseline such as "es2021" or implement
platform-specific targets per Tauri guidance (e.g., "chrome105" for Windows
WebView2 and "safari13" for macOS/Linux WebKit) so bundles run on older
WebKitGTK and macOS versions; modify the build.target value accordingly and
refer to the Tauri Vite setup docs for exact target strings.

Comment thread src/components/editor/Editor.tsx Outdated
Comment thread src/components/editor/Editor.tsx
Comment thread vite.config.ts
- Remove React.memo from FormatBar: editor instance is mutable so memo
  causes stale isActive() state for toolbar buttons
- Fix RAF race condition in Editor: capture note ID before scheduling
  and bail in callback if a different note started loading
- Change Vite build.target from "esnext" to ["es2021", "chrome105", "safari14"]
  for better WebView compatibility across platforms

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@erictli erictli merged commit 68bed23 into main Jan 31, 2026
1 check passed
@erictli erictli deleted the performance-improvements branch February 2, 2026 14:55
@coderabbitai coderabbitai Bot mentioned this pull request Mar 2, 2026
10 tasks
Shayaan-Azeem pushed a commit to Shayaan-Azeem/scratch that referenced this pull request Mar 10, 2026
* Improve performance, efficiency, and code quality

Backend (Rust):
- Implement Tantivy full-text search with indexed queries
- Convert file I/O to async using tokio::fs
- Replace Mutex with RwLock for settings/cache (better read concurrency)
- Fix debounce map memory leak with periodic cleanup
- Fix mutex error handling with descriptive expect messages
- Remove unused dependencies (uuid, chrono, regex)

Frontend (React/TypeScript):
- Add useMemo to CommandPalette for filtered items
- Memoize NoteList displayItems and wrap NoteItem with React.memo
- Wrap FormatBar with React.memo to prevent re-renders on keystrokes
- Memoize App.tsx displayItems for stable keyboard handler deps
- Split NotesContext into separate data/actions contexts
- Optimize FlipText animation with single RAF loop
- Fix note selection race condition in Editor
- Add Vite build optimizations with manual chunk splitting

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

* Fix review comments: FormatBar memo, RAF race condition, build target

- Remove React.memo from FormatBar: editor instance is mutable so memo
  causes stale isActive() state for toolbar buttons
- Fix RAF race condition in Editor: capture note ID before scheduling
  and bail in callback if a different note started loading
- Change Vite build.target from "esnext" to ["es2021", "chrome105", "safari14"]
  for better WebView compatibility across platforms

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot mentioned this pull request Mar 19, 2026
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