Two propagation gaps fixed:
1. Property changes (type, status, color, etc.) now immediately sync to
the entries state via frontmatterToEntryPatch(), so sidebar, note list,
and relations panel reflect updates without restart.
2. After renaming a note, all other open tabs reload their content from
disk, picking up updated wikilinks immediately.
Design decisions:
- Used key-mapping approach (frontmatterToEntryPatch) instead of a new
parse_md_file Tauri command — simpler, works in both Tauri and mock
modes, covers all VaultEntry fields.
- Converted useNoteActions to config object to avoid excess arguments.
- Extracted findWikilinkTarget, reloadTabsAfterRename, renameToastMessage
as standalone functions to reduce hook complexity (cc 10→9).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New GitHubVaultModal component with two tabs:
- Clone Existing: search/browse user's repos, select one, choose
local path, clone via HTTPS+token
- Create New: enter repo name, toggle private/public, create via
GitHub API then clone locally
Integration:
- "Connect GitHub repo" option in vault switcher menu
- GitHub token field added to Settings panel
- Cloned vaults auto-added to vault list and opened
- Mock handlers for github_list_repos, github_create_repo, clone_repo
- All existing tests updated for new github_token field (386 pass)
Product decision: GitHub token stored in settings rather than OAuth
flow — simpler first version, works with personal access tokens.
Users can upgrade to OAuth later.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap ref assignments in useEffect to fix refs-during-render in
useTabManagement, useKeyboardNavigation, and Editor
- Rewrite useAppKeyboard to define keyMap inside useEffect, eliminating
ref access during render
- Add eslint-disable for react-hooks/set-state-in-effect on legitimate
dialog reset patterns (CommitDialog, CreateNoteDialog, CreateTypeDialog,
QuickOpenPalette, AIChatPanel, useVaultLoader)
- Add eslint-disable for react-hooks/static-components on icon lookups
(NoteItem, NoteList PinnedCard) — stateless icon components from a
static map
- Add eslint-disable for react-hooks/purity on Date.now() in
NoteItem TrashDateLine — intentionally memoized on trashedAt
- Remove unused `model` param from buildSystemPrompt (ai-chat.ts) and
update callers
- Fix exhaustive-deps: suppress vault object dep in App.tsx git history
effect (vault object is unstable, loadGitHistory is the real dep)
- Remove unused `modifiedFiles` from useNoteListData params and caller
- Add missing `ref` to Sidebar useOutsideClick deps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install @tauri-apps/plugin-updater + plugin-process (npm + cargo)
- Configure tauri.conf.json: updater endpoint pointing to GitHub Releases,
signing pubkey, createUpdaterArtifacts enabled
- Register updater + process plugins in Rust (desktop-only, #[cfg(desktop)])
- Add updater:default + process:default capabilities
- Add useUpdater hook: checks for updates 3s after startup, shows native
confirm dialog, downloads+installs+relaunches on accept
- Silently catches errors so update failures never block the app
- Add comprehensive tests for the useUpdater hook (5 test cases)
Product decision: Using window.confirm() for the update dialog rather than
a custom React modal. This keeps the implementation simple and the native
dialog is appropriate for a system-level action like app restart. Can be
upgraded to a custom UI later if desired.
Private key generated at ~/.tauri/laputa.key — NOT committed.
For CI: set TAURI_SIGNING_PRIVATE_KEY + TAURI_SIGNING_PRIVATE_KEY_PASSWORD
as GitHub Secrets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add get_file_diff_at_commit Rust command to show diff at a specific commit
- Make shortHash in GitHistoryPanel a clickable button that opens DiffView
- Show author name below commit message (small, muted)
- Remove disabled "View all revisions" button
- Wire onLoadDiffAtCommit through Editor → Inspector → App.tsx
- Add mock handler for get_file_diff_at_commit in browser mode
- Fix pre-existing missing `order` field on trashed mock entries
- Update Inspector tests for new behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the CreateNoteDialog flow with immediate creation. Clicking + or
pressing Cmd+N now instantly creates an "Untitled note" (type=Note) and
opens it as the active tab. Sidebar section + buttons create "Untitled
<type>" for that section's type. Editor auto-focuses after creation so
the user can start typing immediately.
Product decisions:
- Title naming: "Untitled note", "Untitled note 2", etc. for conflicts
- Sidebar section +: creates with that section's type directly
- Editor focus via custom event to avoid stealing focus on regular tab switches
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add archive/unarchive toggle button to the BreadcrumbBar (Archive icon
for normal notes, ArrowUUpLeft icon for archived notes). Add cmd+e
keyboard shortcut to toggle archive state of the active note.
Refactored useAppKeyboard to use action map pattern (reduces cyclomatic
complexity) and extracted BreadcrumbActions from BreadcrumbBar to split
rendering complexity across two functions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added trash icon button in breadcrumb bar for non-trashed notes
- Added restore (arrow counter-clockwise) button for trashed notes
- Trash button sets trashed=true and trashed_at in frontmatter
- Restore button removes trashed/trashed_at from frontmatter
- Added Cmd+Delete keyboard shortcut to trash active note
- Wired handlers through Editor -> App with vault entry updates
- Toast notifications for trash/restore actions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap sidebar section groups in @dnd-kit sortable context
- Add grip-vertical drag handle visible on hover of section headers
- Sort sections by `order` property from Type document entries
- On drag end, compute new sequential order values and persist to
each Type document's frontmatter via handleReorderSections
- Add onReorderSections callback from Sidebar → App.tsx → vault
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TabBar: HTML5 drag-and-drop on tab elements (grab/grabbing cursor,
0.5 opacity on dragged tab, 2px primary-color drop indicator)
- useNoteActions: handleReorderTabs splices tab array, saves order
to localStorage under "laputa-tab-order" key
- Tab order auto-persists on every change; restored on mount
- Wired through Editor → App props chain
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add TypeCustomizePopover component with color swatches (6 accent colors)
and icon grid (35 curated Phosphor icons)
- Add right-click context menu on sidebar section group headers with
"Customize icon & color…" option
- Sidebar renders custom icons/colors from type entry frontmatter
- NoteList uses custom icons/colors for type badges and pinned cards
- Wire up onCustomizeType in App.tsx to update type frontmatter
- Add updateEntry to useVaultLoader for instant in-memory updates
- Add --accent-orange-light CSS variable
- typeColors utility now accepts optional custom color key overrides
Product decision: Built-in types can also be customized (override their
defaults), not just custom types. The picker is a simple popover that
appears after right-click → "Customize icon & color…".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tab navigation: Cmd+Option+Left/Right (Tauri), Cmd+Shift+Left/Right (browser)
- Note list navigation: Cmd+Option+Up/Down moves through visible notes
- Navigating notes replaces the current tab content (no new tab opened)
- Wraps around at list boundaries
- New useKeyboardNavigation hook detects Tauri vs browser environment
- Extracted loadNoteContent and replaceTabWithEntry helpers to reduce
complexity in useNoteActions
- Exported filterEntries, sortByModified, buildRelationshipGroups from
NoteList for reuse in keyboard navigation
Note: useNoteActions (30→31 CC) and App (143→153 LOC) show minimal
degradation on pre-existing code health issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The editor's useCreateBlockNote was missing the uploadFile callback,
so BlockNote had no handler for image uploads (drag-drop, paste, or
toolbar button). Images now convert to data URLs for immediate display.
In Tauri mode, uploaded images are also persisted to the vault's
attachments/ directory via a new save_image backend command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Core implementation:
- CreateTypeDialog: simple dialog with name field for creating new types
- handleCreateType in useNoteActions: creates Type documents in type/ folder
- Dynamic sidebar sections: custom types (Recipe, Book, etc.) appear as new
sidebar sections after built-in ones, each with a + button for instances
- Updated CreateNoteDialog: accepts custom types, shows them with blue accent
- handleCreateNote now supports custom types (folder = lowercased type name)
Product decisions:
- The + on Types section opens CreateTypeDialog (not CreateNoteDialog)
- Custom type sections use FileText icon and blue accent color by default
- Section labels are pluralized (e.g., "Recipes", "Books")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each sidebar section header (Projects, People, Topics, etc.) now shows
a + button on hover. Clicking it opens the CreateNoteDialog pre-filled
with that section's type, so users can quickly create new items without
manually selecting the type.
- Add defaultType prop to CreateNoteDialog for pre-selecting type
- Add onCreateType callback to Sidebar with hover-visible + button
- Wire up in App.tsx with unified openCreateDialog handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moves global keyboard shortcut handling into a dedicated hook.
App.tsx now scores 10.0 code health.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces Inspector panel when activated via Sparkle button in editor
info bar. Includes context pills, message list with typing indicator,
quick action pills (Summarize/Expand/Fix grammar), model selector,
and mock responses with 1200ms delay. E2E test covers full flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove .dark CSS variables block from index.css
- Remove theme toggle state and Cmd+Shift+T shortcut from Sidebar
- Remove dark class initialization from main.tsx
- Hardcode BlockNote theme to 'light' in Editor
- shadcn dark: classes left in place (harmless, no-op without .dark class)
Install Tailwind CSS v4 + shadcn/ui with Radix UI primitives. Migrate all
UI components (Sidebar, NoteList, Editor tabs, Inspector, dialogs, palette)
from BEM CSS to Tailwind utility classes and shadcn components (Button,
Input, Dialog, Badge, etc.). Map theme system to shadcn CSS variables while
preserving BlockNote editor styles untouched. Remove 8 old CSS files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Custom 'wikilink' inline content type via createReactInlineContentSpec
- Extended BlockNote schema with wikilink spec
- Suggestion menu triggered by [[ showing all vault entries
- Markdown round-trip: [[target]] → placeholder tokens → wikilink inline content
- Click handler on .wikilink elements for navigation
- Removed old hack (regex preprocessing to wikilink.internal URLs + DOM click interception on <a> tags)
- Added .wikilink CSS styles
- Passed entries prop from App.tsx to Editor for suggestion menu
Sidebar shows "Commit & Push" button when uncommitted changes exist.
Clicking opens a commit message dialog. After committing, auto-pushes and
shows success/error toast. Modified files count refreshes after commit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Editor tab bar shows Edit/Diff toggle button when viewing a modified file.
Diff view renders git diff output with green (added), red (removed), and
gray (context) line coloring. Uses `get_file_diff` Tauri command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New "Changes" sidebar filter shows vault-wide uncommitted changes via
`get_modified_files` Tauri command (git status --porcelain). NoteList shows
modified files with M/A/D/? status indicators. Badge on sidebar shows count.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace mock git history with real `git log` data via new `get_file_history`
Tauri command. Adds git.rs module with commands for file history, modified files,
file diff, commit, and push (all registered, later tasks wire up the UI).
Updates GitCommit type to include shortHash field.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Note list items now show preview snippets and relative dates. Type badges
have distinct colors (blue=Project, purple=Responsibility, etc.). Selected
state uses a blue left border. Active pill uses blue highlight. Hover
effects improved across sidebar and note list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cmd+N opens create note dialog, Cmd+S shows save toast,
Cmd+W closes active tab, Cmd+P opens quick open palette.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cmd+P opens a VS Code-style search palette overlay with fuzzy matching
across all note titles. Arrow keys to navigate, Enter to select, Esc to close.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show mock git history per file: commit hash (monospace), relative
date, commit message, author. Added GitCommit type, mock git
history generator, and 'View all revisions' placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scan all vault content for wikilinks referencing the current note
(by title, alias, filename stem, or path). Display backlinks as
clickable links with type badges and count. Added get_all_content
mock handler for content scanning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show selected note metadata: type, status (colored pill), owner,
cadence, modified date, word count (computed from content minus
frontmatter), and placeholder 'Add property' button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wikilinks are detected via regex, rendered as styled spans with hidden
brackets, and navigate to the linked note on click. Uses mousedown
instead of click to catch the event before CM6 removes the widget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace single selectedNote with tabs state in App (open/close/switch)
- Tab bar above editor: title + x close button, active tab highlighted
- Clicking note opens new tab or switches to existing
- Closing active tab switches to adjacent tab
- Fix race condition: editor re-creates when tab content loads async
- Add Playwright test verifying multi-tab behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install CM6 packages (state, view, lang-markdown, theme-one-dark, commands, language)
- Replace Editor placeholder with real CodeMirror 6 instance
- Add realistic markdown content to all mock entries in mock-tauri.ts
- Wire up note selection: clicking a note in NoteList loads its content in the editor
- Dark theme matching app colors, line numbers, active line highlighting, line wrapping
- Update NoteList with selected state styling
- Update Playwright screenshot test to capture editor with note selected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>