Commit Graph

1533 Commits

Author SHA1 Message Date
Test
832181b9a9 style: cargo fmt --all v0.20260303.272 2026-03-03 02:44:00 +01:00
Test
bc011692cd style: rustfmt lib.rs 2026-03-03 02:43:15 +01:00
Test
0604a13cdd fix: add missing X import and onRemoveVault prop to StatusBar after rebase 2026-03-03 02:38:44 +01:00
Test
eb77607401 feat: auto-index vault on open + qmd auto-install + status bar progress
Search now works out of the box on any vault without manual qmd
installation. On vault open, the app checks the index status and
auto-triggers background indexing (qmd update + embed) when needed.
If qmd is not installed, it auto-installs via bun.

Changes:
- New indexing.rs module: find_qmd_binary (cached), check_index_status,
  ensure_collection, run_full_index with progress callbacks,
  run_incremental_update, auto_install_qmd
- search.rs: delegates to indexing::find_qmd_binary (removes duplication)
- lib.rs: new Tauri commands (get_index_status, start_indexing,
  trigger_incremental_index)
- useIndexing hook: auto-triggers on vault open, listens for
  indexing-progress events, auto-dismisses complete state after 5s
- StatusBar: IndexingBadge shows phase + progress counts with spinner
- App.tsx: wires up useIndexing, triggers incremental index on file save
- design/search-bundle-qmd.pen: documents indexing progress UI states

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:37:49 +01:00
Test
e305c29e6e style: rustfmt vault_list.rs v0.20260303.271 2026-03-03 02:36:33 +01:00
Test
5b1804e5e1 feat: vault management — remove vault from list and restore Getting Started
Add ability to remove vaults from the app list without deleting files on disk,
and restore the bundled Getting Started demo vault when needed.

Changes:
- Rust: add hidden_defaults field to VaultList for tracking removed default vaults
- useVaultSwitcher: add removeVault() and restoreGettingStarted() with auto-switch
- useCommandRegistry: add 'Remove Vault from List' and 'Restore Getting Started Vault' commands
- StatusBar: add X button per vault item in vault menu dropdown
- 25 new tests covering removal, restore, edge cases, and command palette

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:36:33 +01:00
Luca Rossi
2c5cfe2923 feat: sort picker shows custom frontmatter properties (#185)
The sort dropdown now discovers all scalar properties (string, number,
boolean, date) across notes in the current list and shows them below a
separator after the built-in options. Properties that no longer exist
in the current list are gracefully handled by falling back to Modified.

Rust backend extracts custom properties during vault scan so they are
available on every VaultEntry without loading file content on demand.

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260303.270
2026-03-03 02:31:18 +01:00
Luca Rossi
35144aedfb feat: show archived note indicator banner in editor (#183)
Adds a subtle ArchivedNoteBanner component below the breadcrumb bar
when a note is archived. Banner includes:
- Muted gray background with archive icon + 'Archived' label
- 'Unarchive' button (ArrowUUpLeft icon) wired to same handler as Cmd+E
- Keyboard hint shown in button title

Editor remains fully editable (banner is purely informational).
Indicator appears/disappears reactively via entry.archived from store.

Co-authored-by: Test <test@test.com>
2026-03-03 02:31:10 +01:00
Luca Rossi
4d7252c78f feat: add command palette toggles for all BreadcrumbBar panels (#184)
Adds toggle commands to Cmd+K for:
- Toggle Properties Panel (prop/inspector)
- Toggle Diff Mode (diff) — disabled without note changes
- Toggle Backlinks (back) — disabled without note

Updates:
- useCommandRegistry.ts: 5 new commands with proper disabled states
- useAppCommands.ts: wires onToggleDiff and onToggleBacklinks
- Editor.tsx: added diffToggleRef prop (mirrors rawToggleRef pattern)
- App.tsx: creates diffToggleRef, passes to Editor, wires commands

10 new tests in useCommandRegistry.test.ts covering all new commands.

Co-authored-by: Test <test@test.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.20260303.268
2026-03-03 02:00:48 +01:00
Luca Rossi
eb9a3d889f fix: show all direct relationship properties in note list sidebar (#182)
The GroupBuilder's `seen` set was causing direct relationship properties
to be suppressed. Reverse/computed groups (Children, Events) ran before
the entity's own relationship keys, consuming entries into `seen` and
preventing direct properties like "Belongs to" and "Notes" from appearing.

Fix: process all direct relationship keys from entity.relationships
before the reverse groups, so direct properties always take priority.

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260303.267
2026-03-03 01:37:35 +01:00
Luca Rossi
6d2988b722 feat: trashed notes read-only with visible banner in editor (#181)
* feat: make trashed notes read-only with visible banner in editor

When a note is in the Trash, the editor now shows a banner below the
breadcrumb ("This note is in the Trash") with Restore and Delete
permanently buttons. The BlockNote editor is set to read-only mode,
preventing accidental edits while still allowing navigation and copy.

- Add TrashedNoteBanner component with restore/delete actions
- Pass editable={false} to BlockNoteView when note is trashed
- Add delete_note Rust command for permanent file deletion
- Wire onDeleteNote through Editor → EditorContent → App
- Extract EditorBody to reduce EditorContent cyclomatic complexity

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

* style: rustfmt fix

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:37:25 +01:00
Test
a33a000c57 fix: remove persistLastVault assertions from onboarding test v0.20260303.265 2026-03-03 00:59:09 +01:00
Test
f0b456bb8c fix: remove stale persistLastVault and unused imports after rebase 2026-03-03 00:57:47 +01:00
Test
b489fa8e3e fix: persist vault list across app updates
Vault list was stored only in React useState, lost on every app restart
or update. Now persisted to ~/.config/com.laputa.app/vaults.json via
Rust backend commands (load_vault_list, save_vault_list).

- Add vault_list.rs module with VaultEntry/VaultList types and JSON I/O
- Register load_vault_list/save_vault_list Tauri commands
- Extract vaultListStore.ts utility for frontend persistence calls
- Rewrite useVaultSwitcher to load on mount and persist on change
- Show unavailable vaults greyed out with warning icon instead of
  silently removing them
- Add mock handlers for browser/test environments
- Add useVaultSwitcher tests covering persistence, availability, dedup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:05 +01:00
Test
1a92b4694c fix: persist last vault path so app reopens correct vault after update
The vault path was stored only in React state (useState), which resets
on every app restart. Now the last active vault path is written to
~/Library/Application Support/com.laputa.app/last-vault.txt on every
vault switch, and loaded on startup. If the saved vault no longer exists,
the existing onboarding flow shows the vault picker instead of silently
falling back to the demo vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260302.264
2026-03-03 00:33:55 +01:00
Test
6b9ff9a4a2 feat: add "New Type" command to command palette (Cmd+K) 2026-03-03 00:31:10 +01:00
Luca Rossi
97d2182c0e test: add asserting wikilink navigation E2E test — screenshot.spec.ts test had no expect() calls (#180)
Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.20260302.262
2026-03-02 23:55:57 +01:00
Test
80baa74175 feat: add 'Check for Updates' command to command palette
Users can now trigger an update check from Cmd+K → "Check for Updates"
without leaving the app. Shows toast for up-to-date/error states,
and the existing UpdateBanner for available updates. Command is
disabled while an update is downloading or ready to install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:53:37 +01:00
Test
f71e6d07e7 feat: rename demo vault from 'Demo v2' to 'Getting Started', remove 'Laputa' vault entry
- Rename 'Demo v2' → 'Getting Started' in vault switcher
- Remove 'Laputa' personal vault from default vault list
- Update default Getting Started vault path from Documents/Laputa to Documents/Getting Started
- Update mock handlers and tests to reflect new vault path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:49:36 +01:00
Test
b8a0702e3c fix: use compile-time env!() macro for BUILD_NUMBER instead of runtime std::env::var
The build number was always showing 'b0' because build.rs sets BUILD_NUMBER
via cargo:rustc-env (compile-time), but lib.rs was reading it with
std::env::var (runtime) which falls back to "0" when unset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260302.259
2026-03-02 23:42:24 +01:00
Luca Rossi
c48f337c4d fix: bundle mcp-server into release app so AI Chat works (#178)
* feat: bundle mcp-server into release app so AI Chat works

- Add esbuild bundle script (scripts/bundle-mcp-server.mjs) that compiles
  mcp-server/index.js and ws-bridge.js into self-contained CJS bundles
- Output goes to src-tauri/resources/mcp-server/ (gitignored)
- Add Tauri resources config to copy bundles into Contents/Resources/mcp-server/
- Update mcp_server_dir() to look in Contents/Resources/ (not Contents/) in release
- Add bundle-mcp npm script; hook it into tauri beforeBuildCommand
- Exclude generated resources from ESLint

Previously AI Chat showed 'mcp-server not found at .../Contents/mcp-server'
because the release path lacked the 'Resources' segment and no files were bundled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bundle mcp-server resources before Rust tests

* fix: add mcp-server as pnpm workspace package so esbuild can resolve its deps in CI

* fix: exclude src-tauri/target from eslint to fix CI lint failure

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.20260302.258
2026-03-02 23:31:04 +01:00
Test
b32d46f482 style: fix rustfmt formatting in cache test assertions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:29:27 +01:00
Test
fd100e1c3b fix: sync changes badge count with list by invalidating stale vault cache
When .laputa-cache.json was committed to git, cloned vaults carried
absolute paths from the original machine. The badge (from get_modified_files)
used fresh local paths while the list filtered entries by stale cached paths,
causing a mismatch.

Three-layer fix:
- Invalidate cache when vault_path differs from current machine (CACHE_VERSION bump)
- Exclude .laputa-cache.json via .git/info/exclude to prevent future commits
- Defense-in-depth: match entries by relative path suffix in NoteList
- Surface error message when modified files fetch fails

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:29:27 +01:00
Test
249db95c9e fix: correct git_uncommitted_files call after rebase conflict v0.20260302.256 2026-03-02 23:22:31 +01:00
Test
3c27b63908 fix: add aria-label to InlineRenameInput for test accessibility 2026-03-02 23:20:49 +01:00
Test
a246de4483 style: apply rustfmt to cache.rs test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 23:17:36 +01:00
Test
2793024904 feat: add Rename section to sidebar context menu
- Add handleRenameSection to useEntryActions: writes/deletes 'sidebar label'
  frontmatter key on the Type note with optimistic in-memory update
- Add InlineRenameInput to SidebarParts: autoFocus input rendered in-place
  of section title, submits on Enter/blur, cancels on Escape
- Add 'Rename section…' as first item in sidebar section context menu
- Wire onRenameSection from App.tsx through Sidebar props
- Add 10 new tests (4 unit, 6 component) covering the full rename flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 23:17:36 +01:00
Test
f0ef9cacec fix: update_same_commit picks up modified files, not only new ones
The cache invalidation only re-parsed new untracked files when the git
HEAD hash was unchanged. Modified (uncommitted) files were served stale,
so editing a Type note's 'sidebar label' frontmatter key had no effect
until the next git commit triggered a full incremental diff.

Replace git_uncommitted_new_files (status ?? / A only) with
git_uncommitted_files (all porcelain entries) and apply the same
remove-stale + re-parse logic used by update_different_commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 23:17:26 +01:00
Test
d2538e121f style: apply rustfmt to theme.rs and vault/cache.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260302.255
2026-03-02 23:12:36 +01:00
Test
531666b031 fix: remove unused is_new_file_status function
Clippy flagged dead code in vault/cache.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:11:30 +01:00
Test
e239d71a48 fix: add isDark to useThemeManager return value 2026-03-02 23:09:02 +01:00
Test
e2f1fe239e fix: remove stale deriveThemeVariables call and update test assertions after conflict resolution 2026-03-02 23:07:58 +01:00
Test
8495f0e2e6 test: add theme command registry tests and minor fixes 2026-03-02 23:06:12 +01:00
Test
19bc3c67e3 wip: themes-editable — theme management system WIP (13 files, 1014 insertions) 2026-03-02 23:06:12 +01:00
Test
628d97d909 chore: sync Sidebar.tsx dragHandleProps removal from main v0.20260302.254 2026-03-02 21:57:43 +01:00
Test
aa6b31317c chore: sync SidebarParts dragHandle removal from main 2026-03-02 21:57:43 +01:00
Test
b24ba8a4c6 fix: position context menu and customize popover at right-click coordinates
The CustomizeIconColor popover was hardcoded to (20, 100) — always wrong.
Bottom sections also failed because the context menu could extend below the
viewport, making "Customize icon & color" unreachable.

- Capture context menu click position and use it to position the customize
  popover (via new `customizePos` state in Sidebar)
- Clamp context menu position to flip above the click point when near the
  bottom of the viewport (CONTEXT_MENU_HEIGHT guard)
- Clamp customize popover to stay within viewport bounds via clampToViewport()
- Add 6 tests covering context menu flow, positioning, and color callback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 21:57:43 +01:00
Test
b3f47f4507 chore: remove dragHandleProps from SectionContent destructuring 2026-03-02 21:56:50 +01:00
Test
6fcd0552d0 chore: remove remaining dragHandleProps from SortableSection 2026-03-02 21:55:31 +01:00
Test
8747a84453 style: remove drag handle icon from sidebar sections 2026-03-02 21:29:16 +01:00
Test
2eb3080050 style: align section icons with main nav items in sidebar 2026-03-02 21:29:16 +01:00
Test
99b64c0fac style: remove letter-spacing from status/property labels in inspector 2026-03-02 21:29:16 +01:00
Test
b2a68a9a67 style: reduce editor body font to 15px, apply -0.5 letter-spacing to h2/h3/h4 2026-03-02 21:29:16 +01:00
Test
f5bbcb81a4 fix: defer H1 selection to second rAF so content swap completes first
The previous implementation called selectFirstHeading() in the same rAF
as editor.focus(), but the new note's content (applied via queueMicrotask
inside a React effect) hadn't been swapped in yet. React's MessageChannel
scheduler runs the re-render between animation frames, so the heading
block didn't exist in the TipTap document when the selection ran.

Fix: move selectFirstHeading() into a nested requestAnimationFrame inside
doFocus(). Between rAF 1 (focus) and rAF 2 (select), all pending
macrotasks — React's re-render + the queueMicrotask content swap — run
to completion, guaranteeing the H1 block is in the document before we
try to select it.

Updated tests: add rAF mock to the timeout-path select test (which now
needs it since selection is deferred via rAF); add a regression test
that explicitly verifies focus in rAF1 and selection in rAF2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20260302.251
2026-03-02 21:27:26 +01:00
Test
db5e53f77e fix: register Cmd+\ keyboard shortcut to toggle raw editor mode
Cmd+\ was not wired to the raw editor toggle. Added onToggleRawEditor
to KeyboardActions interface and mapped '\\' in the cmdKeyMap so the
shortcut fires handleToggleRaw via the existing rawToggleRef.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20260302.250
2026-03-02 21:02:54 +01:00
Test
90e67adc41 fix: editor background matches vault theme in dark mode
BlockNote's internal CSS sets `.bn-editor { background-color: var(--bn-colors-editor-background); }`
using its own hardcoded variables (#ffffff light, #1f1f1f dark). Our `.bn-container` rule set
`background: var(--bg-primary)` but this didn't cascade to `.bn-editor` which has its own rule.

Override the BlockNote internal CSS variables (`--bn-colors-editor-background` etc.) on
`.bn-container` so BlockNote's own rules pick up our vault theme colors. CSS custom properties
cascade normally, and our selector specificity (2 classes) matches BlockNote's dark theme rule
(1 class + 1 attribute) — source order wins since our CSS loads after BlockNote's.

Fixes: editor content area now seamlessly blends with sidebar and container in any vault theme.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20260302.249
2026-03-02 19:33:57 +01:00
Test
185feb5a2c feat: raw editor mode — plain textarea with frontmatter + wikilink autocomplete
Adds a third editor mode (alongside WYSIWYG and Diff) that shows the raw
markdown file in a monospaced textarea. Includes:

- useRawMode hook with derived state (no setState-in-effect) and onFlushPending
- RawEditorView: textarea with 500ms debounce, YAML error banner, wikilink
  autocomplete via [[  trigger, Cmd+S save
- BreadcrumbBar Code icon toggles raw mode; mutual exclusion with diff mode
- Command palette: "Toggle Raw Editor" (View group, requires active tab)
- useEditorModeExclusion hook extracted to keep Editor.tsx under complexity threshold
- buildViewCommands extracted from useCommandRegistry for same reason
- RawToggleButton and RawModeEditorSection components extracted for clean CC

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20260302.248
2026-03-02 18:44:47 +01:00
Test
f04dfdbd37 feat: auto-focus editor with H1 title selected on new note creation
When a new note is created (Cmd+N or via command palette), the editor
immediately focuses and selects all text in the H1 title block, so the
user can start typing the note name right away without clicking.

- signalFocusEditor now accepts { selectTitle?: boolean } and passes it
  in the laputa:focus-editor event detail
- handleCreateNoteImmediate dispatches with selectTitle: true
- useEditorFocus reads selectTitle from event and calls selectFirstHeading
- selectFirstHeading walks the ProseMirror document to find the first
  heading node and uses TipTap's chain().setTextSelection() to select it
- Opening existing notes is unaffected (selectTitle defaults to false)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20260302.247
2026-03-02 18:31:53 +01:00
Test
3c27403f86 fix: use globalThis instead of global in test setup (TS build compatibility) v0.20260302.246 2026-03-02 14:45:39 +01:00
Test
276b3c1a37 feat: responsive tab width — shrink tabs to fit window
Tabs now dynamically reduce their max-width when the total width exceeds
the TabBar container, similar to browser tab behavior. Uses a
ResizeObserver on the tab area to calculate per-tab max-width as
min(360, containerWidth / tabCount), floored at 60px minimum.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:45:39 +01:00