P2-01: Notes tab — per-paper markdown notes (SQLite) - #21
Merged
Conversation
📝 WalkthroughWalkthroughAdds schema version 3 for per-paper notes, repository CRUD operations, Electron IPC and preload methods, a debounced NotesPanel with clear and lifecycle-save behavior, and routing from the Notes tab to the new panel. ChangesNotes feature
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant NotesPanel
participant PreloadAPI
participant ElectronMain
participant NotesRepository
participant SQLite
NotesPanel->>PreloadAPI: notesGet(slug)
PreloadAPI->>ElectronMain: invoke vellum:notes-get
ElectronMain->>NotesRepository: getNote(db, slug)
NotesRepository->>SQLite: SELECT note by paper_slug
SQLite-->>NotesRepository: note row or no row
NotesRepository-->>ElectronMain: NoteRecord or undefined
ElectronMain-->>PreloadAPI: NoteRecord or null
PreloadAPI-->>NotesPanel: loaded note
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/app/NotesPanel.tsx`:
- Around line 47-55: Update the save and delete flows in flushPendingSave,
clearNote, and the debounced onChange handler so rejected notesSave/notesDelete
calls are not silently treated as successful: surface a clear failed-save status
to the user and preserve or re-arm the dirty state where the edit must remain
retryable, especially for debounced saves and flushes. For clearNote, restore
the prior note/body state when deletion fails so the UI does not falsely show an
empty persisted note.
- Around line 61-89: Reset the note body immediately when the slug changes,
before starting notesGet in the useEffect, so the textarea cannot display the
previous paper’s content while the new note loads. Keep the existing slug-empty
handling, cancellation logic, and loaded/error result behavior unchanged.
🪄 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: 46b1db1e-bf29-4a58-ba5f-b6b371ab6b4d
📒 Files selected for processing (13)
core/notes/repo.test.tscore/notes/repo.tscore/store/db.test.tscore/store/migrate.test.tscore/store/migrate.tscore/store/schema.tselectron/main.tselectron/preload.tssrc/app/NotesPanel.module.csssrc/app/NotesPanel.test.tsxsrc/app/NotesPanel.tsxsrc/app/RightPanel.test.tsxsrc/app/RightPanel.tsx
This was referenced Jul 23, 2026
This was referenced Aug 11, 2026
This was referenced Aug 11, 2026
Add a Notes tab in the right panel backed by a new `notes` table (one freeform markdown note per paper, keyed by paper_slug). Storage: migration 3 creates the `notes` table with `paper_slug` as the primary key (natural 1:1 key, no id indirection) and `ON DELETE CASCADE` so a paper's note is removed with the paper. `core/notes/repo.ts` exposes getNote / upsertNote / deleteNote; upsert is INSERT ... ON CONFLICT. Seam: three IPC channels (vellum:notes-get / -save / -delete) with slug and body validation in the main process; renderer reaches them only via window.vellum. UI: NotesPanel renders a markdown editor bound to the open paper's slug, with debounced autosave and a clear-note action. Wired into RightPanel's Notes tab (was a "coming soon" stub). Acceptance ([P2-01]): - notes CRUD per paper ✓ - persists across restart ✓ - autosave ✓ - tests ✓ (repo CRUD, migration, autosave debounce/flush, persistence, RightPanel wiring) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dkritarth
force-pushed
the
p2-01-notes
branch
from
September 12, 2026 15:10
6290285 to
ab0dcd2
Compare
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User outcome
Users can write, view, and clear freeform markdown research notes for each paper in the right panel "Notes" tab. Notes automatically save on debounce and persist across app restarts in the local SQLite database.
Acceptance criteria
Automated verification
core/notes/repo.test.ts(8/8 tests passing)src/app/NotesPanel.test.tsx(9/9 tests passing)src/app/RightPanel.test.tsx(6/6 tests passing)npm run buildcleanly builds Electron bundle.Live Electron verification
vellum:notes-saveupserts row into SQLitenotestable.vellum:notes-getrestores persisted note content on reopen.Visual and console evidence
Limitations and follow-ups
Independent review
All changes respect the strict storage split (SQLite for notes state, never raw paper bytes) and use typed IPC boundaries via preload.
Closes #29