Skip to content

Perf: improve rendering performance of Notes with big number of Notes#357

Merged
chmurson merged 13 commits intomainfrom
perf-reduce-number-of-notes-rerender
Dec 28, 2025
Merged

Perf: improve rendering performance of Notes with big number of Notes#357
chmurson merged 13 commits intomainfrom
perf-reduce-number-of-notes-rerender

Conversation

@chmurson
Copy link
Collaborator

@chmurson chmurson commented Dec 28, 2025

What?

  • improve context & memoization in few places to reduce re-renders
  • improve Badge tooltip (the one for migration) performance, by rendering it only on active or hovered on note
  • improve performance around getting sections titles by moving its fetching upstream to NotesManager from NoteLink
  • update note content eagerly on save

Summary by CodeRabbit

  • New Features

    • Notes now show section/subsection titles; note/tooltips use stable IDs.
    • New-note textarea auto-focuses and preserves selection after creation.
    • Loading UI waits for both notes and titles, showing extra skeletons until ready.
  • Bug Fixes

    • Selection clears after delete; saves trim content and block empty saves.
    • “No notes available” no longer appears prematurely while loading.
  • Refactor

    • Simplified note data/readiness flow; note content is captured on save rather than via continuous change events.

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

@netlify
Copy link

netlify bot commented Dec 28, 2025

Deploy Preview for graypaper-reader ready!

Name Link
🔨 Latest commit f2e01f7
🔍 Latest deploy log https://app.netlify.com/projects/graypaper-reader/deploys/6951aa6efe3ec10008e646ea
😎 Deploy Preview https://deploy-preview-357--graypaper-reader.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

📝 Walkthrough

Walkthrough

Replaces direct NotesContext usage with a new hook useNoteManagerNotes that composes notes and async section-title metadata with caching; shifts textarea handling from controlled ChangeEvent state to ref-based reads; threads sectionTitles through Note components; and simplifies NoteLink to a stateless showTooltip API.

Changes

Cohort / File(s) Summary
New Hook for Note State Management
src/components/NoteManager/useNoteManagerNotes.ts
Adds useNoteManagerNotes and type INotesMangerNote; composes data from NotesContext and CodeSyncContext, resolves section titles asynchronously with per-key cache, exposes readiness flags and wrapped handlers (add/delete/update).
NoteManager Integration
src/components/NoteManager/NoteManager.tsx
Replaces direct NotesContext usage with useNoteManagerNotes; introduces readyAndLoaded (notesReady ∧ sectionTitlesLoaded); adapts to INotesMangerNote shape (note.noteObject), adds locationRef, updates add/delete/update flows and selection/clear-selection wiring.
NewNote: ref-based save flow
src/components/NoteManager/components/NewNote.tsx
Removes local noteContent state and ChangeEvent handlers; reads textarea via textAreaRef on save; adds sectionTitles into layout context from CodeSyncContext; focuses textarea on selection changes; validation uses textarea value.
Note: ref-based save & sectionTitles prop
src/components/NoteManager/components/Note.tsx
Adds sectionTitles prop; uses textAreaRef to read content on save (prevents empty saves); removes content-change handler; resets hover/focus/dropdown on active changes; trims content before saving.
NotesList: new note shape & mapping
src/components/NoteManager/components/NotesList.tsx
Changes prop type to INotesMangerNote[]; accesses note.noteObject for keys/props; passes sectionTitles={note.metadata} to Note; updates memoization and first-active logic.
NoteContext interface change
src/components/NoteManager/components/NoteContext.tsx
Removes handleNoteContentChange from ISingleNoteContext; adds sectionTitles: { sectionTitle; subSectionTitle }; removes unused ChangeEvent import.
NoteLayout / Textarea behavior
src/components/NoteManager/components/NoteLayout.tsx
Removes handleNoteContentChange wiring; switches NoteSimpleTextarea to defaultValue + ref usage; SelectedText now renders NoteLink with showTooltip={true}.
NoteLink simplification
src/components/NoteManager/components/NoteLink.tsx
Converts NoteLink to stateless NoteLink({ showTooltip }); consumes sectionTitles from context; introduces unique tooltip id via useId; removes CodeSyncContext dependency and internal section state.
CodeSyncProvider context addition
src/components/CodeSyncProvider/CodeSyncProvider.tsx
Exposes isSynctexLoaded boolean on ICodeSyncContext (derived from synctexData presence).
Other wiring changed
src/components/NoteManager/...
Various files updated to remove old change-event handlers and use refs/sectionTitles; readiness gating moved to readyAndLoaded and skeleton/no-notes rendering updated accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as UI (NoteManager / Components)
  participant Hook as useNoteManagerNotes
  participant NotesCtx as NotesContext
  participant CodeSync as CodeSyncContext
  participant Cache as Metadata Cache

  UI->>Hook: mount / render — request notes + readiness
  Hook->>NotesCtx: read notes, activeNotes, handlers, notesReady
  loop each note
    Hook->>Cache: lookup metadata by note.key
    alt cache miss
      Hook->>CodeSync: getSectionTitles(selectionStart)
      CodeSync-->>Hook: sectionTitle, subSectionTitle
      Hook->>Cache: store metadata for note.key
    else cache hit
      Cache-->>Hook: metadata
    end
  end
  Hook-->>UI: return INotesMangerNote[] (noteObject + metadata), notesReady, sectionTitlesLoaded, handlers
  UI->>Hook: user triggers add/update/delete
  Hook->>NotesCtx: forward add/update/delete
  NotesCtx-->>Hook: confirmation/update
  Hook-->>UI: updated notes state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I hopped through hooks to fetch each titled trail,
I nudged the refs and saved a careful tale.
Tooltips hum softly when the burrow is still,
Cached crumbs lead the way down every sill.
A little rabbit coded, happy with the drill. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main performance objective of the changeset: optimizing rendering performance for notes by reducing re-renders through context refactoring, memoization, and eliminating redundant work.
✨ 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 perf-reduce-number-of-notes-rerender

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ceace98 and f2e01f7.

⛔ Files ignored due to path filters (2)
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-opening-dropdown-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-opening-dropdown-light-mode-linux.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/NoteManager/useNoteManagerNotes.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:09:55.992Z
Learning: In `src/components/NoteManager/NoteManager.tsx`, the direct mutation of `note.original.content` in `memoizedHandleUpdateNote` before calling `updateNote` is intentional and used for optimistic UI updates.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:25.396Z
Learning: In src/components/NoteManager/NoteManager.tsx, direct mutation of `note.original.content` before calling `updateNote` is intentional for optimistic UI updates. This pattern is documented with an explicit comment in the code.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/CodeSyncProvider/CodeSyncProvider.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (3)
src/components/CodeSyncProvider/CodeSyncProvider.tsx (3)

22-22: LGTM! Load state flag added to context interface.

The new isSynctexLoaded boolean provides consumers with a simple way to detect when Synctex data is available, which aligns with the PR's performance optimization goals.


200-200: LGTM! Clean boolean derivation.

The !! coercion correctly converts the synctexData state into an explicit boolean flag for the context.


210-210: LGTM! Context value correctly includes the new flag.

The isSynctexLoaded flag is properly added to both the context value and the dependency array. While including it in the dependency array is technically redundant (since the callbacks already depend on synctexData), it improves code clarity and ensures the context updates correctly.

Also applies to: 219-219


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 and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 28, 2025

Visual Regression Test Report ✅ Passed

Github run id: 20560209608

🔗 Artifacts: Download

Copy link

@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

🧹 Nitpick comments (2)
src/components/NoteManager/useNoteManagerNotes.ts (1)

67-74: Remove unused cacheByKey field.

Line 73 sets a cacheByKey field on noteManagerNote, but this field is not part of the INotesMangerNote type definition and is never read anywhere in the codebase. This appears to be dead code or an incomplete implementation.

🔎 Proposed fix
         const noteManagerNote = {
           noteObject: note,
           metadata: {
             sectionTitle: sectionTitles.sectionTitle,
             subSectionTitle: sectionTitles.subSectionTitle,
           },
-          cacheByKey: new Set<string>(notes.map((note) => note.key)),
         };
src/components/NoteManager/components/Note.tsx (1)

46-64: Create a new object before passing to onEditNote rather than mutating state directly.

Line 60 mutates the noteDirty state object directly with noteDirty.content = content before passing it to onEditNote. While the callback is synchronous and works correctly, directly mutating state objects violates React best practices. Instead, create a new object: const updatedNote = { ...noteDirty, content }; onEditNote(note, updatedNote);

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f13225c and 7e9d788.

📒 Files selected for processing (8)
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/NoteManager/useNoteManagerNotes.ts
  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/NoteManager/useNoteManagerNotes.ts
  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/NoteManager/useNoteManagerNotes.ts
  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/NoteManager/useNoteManagerNotes.ts
  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: FluffyLabs/graypaper-reader PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-26T10:17:47.372Z
Learning: Write imperative, present-tense commit subject lines under 72 characters with helpful scopes (e.g., `feat(notes): add label filter`)
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/components/NotesList.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteLink.tsx
  • src/components/NoteManager/components/NoteLayout.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Tailwind CSS v4 is used; avoid non-default width utilities like border-b-1 unless configured. Prefer border-b (1px) or border-b-[1px]. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/NoteLink.tsx
🧬 Code graph analysis (6)
src/components/NoteManager/useNoteManagerNotes.ts (4)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/NotesProvider.tsx (2)
  • NotesContext (18-18)
  • INotesContext (20-40)
src/components/CodeSyncProvider/CodeSyncProvider.tsx (2)
  • CodeSyncContext (31-31)
  • ICodeSyncContext (11-22)
src/hooks/useLatestCallback.ts (1)
  • useLatestCallback (4-8)
src/components/NoteManager/components/NotesList.tsx (1)
src/components/NoteManager/useNoteManagerNotes.ts (1)
  • INotesMangerNote (7-13)
src/components/NoteManager/components/Note.tsx (4)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/utils/validateMath.ts (1)
  • validateMath (3-14)
src/components/NoteManager/components/NoteLink.tsx (1)
  • NoteLink (7-60)
src/components/NoteManager/components/NoteLayout.tsx (1)
  • NoteLayout (63-70)
src/components/NoteManager/NoteManager.tsx (7)
src/components/NoteManager/useNoteManagerNotes.ts (1)
  • useNoteManagerNotes (15-99)
src/components/SelectionProvider/SelectionProvider.tsx (2)
  • SelectionContext (35-35)
  • ISelectionContext (21-29)
shared/links-metadata/src/types.ts (1)
  • ISynctexBlockId (1-4)
src/hooks/useLatestCallback.ts (1)
  • useLatestCallback (4-8)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/components/NotesProvider/utils/areSelectionsEqual.ts (1)
  • areSelectionsEqual (8-13)
src/components/NoteManager/components/NoteLink.tsx (1)
src/components/NoteManager/components/NoteContext.tsx (1)
  • useNoteContext (26-32)
src/components/NoteManager/components/NoteLayout.tsx (4)
src/components/NoteManager/components/NoteLink.tsx (1)
  • NoteLink (7-60)
src/components/NoteManager/components/NoteContext.tsx (1)
  • useNoteContext (26-32)
src/components/NoteManager/components/SimpleComponents/Textarea.tsx (1)
  • NoteSimpleTextarea (6-10)
src/components/NoteManager/components/SimpleComponents/index.ts (1)
  • NoteSimpleTextarea (2-2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (10)
src/components/NoteManager/components/NoteContext.tsx (1)

1-1: LGTM! Clean context refactor.

The removal of ChangeEvent and addition of sectionTitles correctly reflects the architectural shift from onChange-based content tracking to centralized section title management.

Also applies to: 21-21

src/components/NoteManager/components/NoteLink.tsx (1)

7-9: LGTM! Effective performance optimization.

The refactor to accept showTooltip and use useId() for unique tooltip IDs is well-executed. Conditionally rendering the Tooltip component only when needed will improve performance with large numbers of notes.

Also applies to: 27-30, 57-57

src/components/NoteManager/components/NoteLayout.tsx (1)

36-36: LGTM! Consistent refactor to uncontrolled textarea pattern.

The changes correctly implement the shift from onChange-based content tracking to ref-based reading:

  • Line 36: showTooltip={true} ensures tooltip is always rendered when editing
  • Line 46: Removal of handleNoteContentChange aligns with context updates
  • Line 60: defaultValue with props spreading maintains keyboard handlers while eliminating controlled state

Also applies to: 46-46, 60-60

src/components/NoteManager/components/NewNote.tsx (2)

32-64: LGTM! Proper ref-based content reading with validation.

The refactor correctly:

  • Reads content from textAreaRef.current?.value at save time (line 32)
  • Adds empty content validation after trimming (lines 55-58)
  • Updates noteDirty.content before calling onEditNote (line 60)

This approach is cleaner than maintaining controlled state and aligns with the PR's architecture.


159-159: LGTM! Appropriate default for new notes.

The empty sectionTitles constant is correct for new notes that don't yet have a finalized location.

src/components/NoteManager/components/NotesList.tsx (1)

16-16: LGTM! Correct adaptation to new note structure.

The component properly:

  • Updates the prop type to INotesMangerNote[] (line 16)
  • Accesses note.noteObject for active state checks (lines 33-34)
  • Passes note.metadata as sectionTitles to child components (line 42)

The data flow is correct and consistent with the architectural changes.

Also applies to: 33-46

src/components/NoteManager/NoteManager.tsx (2)

31-39: LGTM! Hook integration and readiness gating implemented correctly.

The changes properly:

  • Replace direct NotesContext usage with useNoteManagerNotes hook (lines 31-39)
  • Clear selection on delete for better UX (line 47)
  • Combine readiness checks for both notes and section titles (line 126)
  • Access note.noteObject for active checks (line 124)

The readiness gating ensures UI doesn't render until all data is available.

Also applies to: 44-50, 124-126


52-58: Critical: Mutation occurs before handler can validate or reject the update.

Line 54 mutates note.original.content before latestUpdateNote.current() is called. However, handleUpdateNote in NotesProvider has an early return on line 160 if the note's source is Remote—the mutation will persist even though the update is refused, creating state inconsistency.

Move the mutation inside the handler after validation succeeds, or validate the note source before mutating.

src/components/NoteManager/components/Note.tsx (2)

234-240: LGTM! Proper state management and performance optimization.

The changes correctly implement:

  • State reset when note becomes inactive (lines 234-240) prevents stale hover/focus/dropdown state
  • Tooltip optimization with showTooltip={isHovered} (line 269) aligns with performance goals
  • Ref wiring to textarea (line 284) enables ref-based content reading

Also applies to: 269-269, 284-284


23-23: LGTM! Correct sectionTitles propagation.

The sectionTitles prop is properly:

  • Added to the component's type signature (line 23)
  • Accepted as a parameter (line 30)
  • Passed through to the layout context (line 173)
  • Included in the dependency array (line 190)

This enables downstream components like NoteLink to access section titles from context.

Also applies to: 30-30, 173-173, 190-190

Copy link

@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: 0

🧹 Nitpick comments (1)
src/components/NoteManager/components/NewNote.tsx (1)

181-206: Error handling for async section title fetching is optional but recommended.

The CodeSyncContext is always provided at the app root level (App.tsx), so the type assertion is safe in practice. However, the async calls to getSectionTitleAtSynctexBlock and getSubsectionTitleAtSynctexBlock lack error handling, which could cause unhandled promise rejections if the underlying texStore.getTexAsLines fails.

Consider wrapping the async operations in a try-catch block as defensive programming:

  useEffect(() => {
    let cancelled = false;
    (async () => {
      if (selectionStart && selectionEnd) {
+       try {
          const newSectionTitle = (await getSectionTitleAtSynctexBlock(selectionStart)) ?? "";
          const newSubSectionTitle = (await getSubsectionTitleAtSynctexBlock(selectionStart)) ?? "";
          if (cancelled) return;
          setSectionTitles({
            sectionTitle: newSectionTitle,
            subSectionTitle: newSubSectionTitle,
          });
+       } catch (error) {
+         console.error("Failed to fetch section titles:", error);
+         if (cancelled) return;
+         setSectionTitles({
+           sectionTitle: "",
+           subSectionTitle: "",
+         });
+       }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [selectionStart, selectionEnd, getSectionTitleAtSynctexBlock, getSubsectionTitleAtSynctexBlock]);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e9d788 and 640193e.

📒 Files selected for processing (1)
  • src/components/NoteManager/components/NewNote.tsx
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/NoteManager/components/NewNote.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/NoteManager/components/NewNote.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/NoteManager/components/NewNote.tsx
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/NoteManager/components/NewNote.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/NoteManager/components/NewNote.tsx
🧬 Code graph analysis (1)
src/components/NoteManager/components/NewNote.tsx (4)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
shared/links-metadata/src/types.ts (1)
  • ISynctexBlockId (1-4)
src/components/CodeSyncProvider/CodeSyncProvider.tsx (2)
  • CodeSyncContext (31-31)
  • ICodeSyncContext (11-22)
src/components/NoteManager/components/NoteContext.tsx (1)
  • ISingleNoteContext (6-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (3)
src/components/NoteManager/components/NewNote.tsx (3)

32-46: LGTM! Clean ref-based content reading pattern.

The refactor from state-based to ref-based content access is well-executed. The validation flow properly checks for both math validation errors and empty content before saving.


66-72: Good addition of focus management.

The handleSelectionChange callback ensures the textarea receives focus when the selection changes, improving the user experience.


149-149: Content field intentionally empty in dummy object.

The content field is now always an empty string since content is read directly from the textarea ref on save. This is consistent with the ref-based approach and the dependency array correctly excludes noteContent.

Also applies to: 157-157

Copy link

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

🧹 Nitpick comments (2)
src/components/NoteManager/useNoteManagerNotes.ts (2)

7-13: Typo in type name: INotesMangerNote should be INotesManagerNote.

The type is missing an 'a' in "Manager". Since this is exported and used in other files, consider fixing for consistency and discoverability.

🔎 Proposed fix
-export type INotesMangerNote = {
+export type INotesManagerNote = {
   noteObject: IDecoratedNote;
   metadata: {
     sectionTitle: string;
     subSectionTitle: string;
   };
 };

15-22: Consider adding null guards for context consumption.

The type assertions (as INotesContext and as ICodeSyncContext) assume the contexts are never null. If this hook is ever used outside the provider tree, it will fail silently. Adding a runtime check or using a custom hook that throws on missing context would improve robustness.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 640193e and 45a1d79.

📒 Files selected for processing (2)
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
🧬 Code graph analysis (2)
src/components/NoteManager/NoteManager.tsx (5)
src/components/SelectionProvider/SelectionProvider.tsx (2)
  • SelectionContext (35-35)
  • ISelectionContext (21-29)
src/hooks/useLatestCallback.ts (1)
  • useLatestCallback (4-8)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/components/NotesProvider/utils/areSelectionsEqual.ts (1)
  • areSelectionsEqual (8-13)
src/components/NoteManager/useNoteManagerNotes.ts (4)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/NotesProvider.tsx (2)
  • NotesContext (18-18)
  • INotesContext (20-40)
src/components/CodeSyncProvider/CodeSyncProvider.tsx (2)
  • CodeSyncContext (31-31)
  • ICodeSyncContext (11-22)
src/hooks/useLatestCallback.ts (1)
  • useLatestCallback (4-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (6)
src/components/NoteManager/useNoteManagerNotes.ts (2)

48-102: LGTM!

The async effect with cancellation pattern is well-implemented. The cache-first approach with parallel fetching via Promise.all is good for performance. The cancellation guard at line 80 correctly prevents state updates after unmount.


116-132: LGTM!

Clean helper function with parallel fetching and appropriate null coalescing for fallback values.

src/components/NoteManager/NoteManager.tsx (4)

31-39: LGTM!

Clean destructuring from the new useNoteManagerNotes hook. The integration properly replaces direct context consumption with the composed hook.


95-96: LGTM!

Good use of ref pattern to keep memoizedHandleSelectNote stable while accessing fresh locationParams and setLocationParams values. This avoids unnecessary re-renders of child components.


134-172: LGTM!

The render logic properly gates UI based on readyAndLoaded state. The skeleton/no-notes message conditions are mutually exclusive and correct. The opacity and pointer-events styling provides clear visual feedback during loading.


128-132: LGTM!

Simple effect to reset the temporary new-note state once notes are fully loaded. The dependency on readyAndLoaded is correct.

Copy link

@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: 0

🧹 Nitpick comments (1)
src/components/NoteManager/useNoteManagerNotes.ts (1)

7-13: Fix typo in exported type name.

The type name INotesMangerNote has a typo (missing 'a'). It should be INotesManagerNote for consistency with the file and hook name.

🔎 Proposed fix
-export type INotesMangerNote = {
+export type INotesManagerNote = {
   noteObject: IDecoratedNote;
   metadata: {
     sectionTitle: string;
     subSectionTitle: string;
   };
 };

Then update all references in this file and consumers (e.g., NoteManager.tsx, NotesList.tsx).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45a1d79 and ceace98.

⛔ Files ignored due to path filters (8)
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-note-activation-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-note-activation-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-note-edit-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-note-edit-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-opening-dropdown-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-after-opening-dropdown-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-initial-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/notes-tab-initial-light-mode-linux.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:09:55.992Z
Learning: In `src/components/NoteManager/NoteManager.tsx`, the direct mutation of `note.original.content` in `memoizedHandleUpdateNote` before calling `updateNote` is intentional and used for optimistic UI updates.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:25.396Z
Learning: In src/components/NoteManager/NoteManager.tsx, direct mutation of `note.original.content` before calling `updateNote` is intentional for optimistic UI updates. This pattern is documented with an explicit comment in the code.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.
📚 Learning: 2025-12-28T21:10:25.396Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:25.396Z
Learning: In src/components/NoteManager/NoteManager.tsx, direct mutation of `note.original.content` before calling `updateNote` is intentional for optimistic UI updates. This pattern is documented with an explicit comment in the code.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-12-28T21:09:55.992Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:09:55.992Z
Learning: In src/components/NoteManager/NoteManager.tsx, note.original.content is mutated directly within memoizedHandleUpdateNote before updateNote is called to support optimistic UI updates. This is intentional for this component; during reviews, don't flag this as an improper mutation. Ensure the mutation is isolated, safe, and revertible, and consider documenting the rationale in code comments.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
📚 Learning: 2025-12-28T21:09:55.992Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:09:55.992Z
Learning: In `src/components/NoteManager/NoteManager.tsx`, the direct mutation of `note.original.content` in `memoizedHandleUpdateNote` before calling `updateNote` is intentional and used for optimistic UI updates.

Applied to files:

  • src/components/NoteManager/useNoteManagerNotes.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (6)
src/components/NoteManager/useNoteManagerNotes.ts (2)

30-43: LGTM! Cache invalidation on mutations is correct.

Both updateNote and deleteNote properly invalidate cache entries before delegating to upstream handlers, ensuring metadata stays in sync with note changes.


115-131: LGTM! Efficient concurrent metadata fetching.

The helper correctly fetches section and subsection titles in parallel and handles null values appropriately.

src/components/NoteManager/NoteManager.tsx (4)

31-39: LGTM! Clean hook integration.

The migration from direct NotesContext usage to useNoteManagerNotes is well-structured, and all necessary values are properly destructured.


44-50: LGTM! Correct delegation to hook's delete handler.

The delete flow properly delegates to the hook's deleteNote (which handles cache invalidation) and clears the selection. Dependencies are correct.


96-123: LGTM! Smart use of ref to stabilize callback.

The locationRef pattern correctly stabilizes memoizedHandleSelectNote by avoiding locationParams and setLocationParams in its dependency array, while ensuring the callback always reads current values.


125-174: LGTM! Rendering logic correctly handles dual loading states.

The readyAndLoaded check appropriately gates rendering on both notesReady and sectionTitlesLoaded, preventing premature empty states and correctly managing skeleton display. The isActiveNotes check correctly accesses note.noteObject given the new data structure.

@chmurson chmurson merged commit 5846bd7 into main Dec 28, 2025
9 checks passed
@chmurson chmurson deleted the perf-reduce-number-of-notes-rerender branch December 28, 2025 22:17
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