Commit Graph

956 Commits

Author SHA1 Message Date
lucaronin
41cc824180 test: add Playwright smoke test for AI note visibility and tab opening
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:56:37 +01:00
lucaronin
a0eb1a292d test: add unit tests for detectFileOperation and parseBashFileCreation
24 tests covering Write/Edit/Bash file detection, edge cases
(malformed JSON, files outside vault, non-md files, undefined input),
and the parseBashFileCreation helper for redirect/tee patterns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:41:06 +01:00
lucaronin
415bbb5413 fix: AI-created notes now trigger vault refresh and auto-open in tab
Root causes:
- toolInputMapRef in useAiAgent was overwritten by tool_progress events
  (which arrive with input=undefined AFTER the assistant message set
  the full input), causing detectFileOperation to receive undefined
  and skip file creation detection entirely.
- MCP open_note only broadcast open_tab without vault_changed, so
  the note list didn't refresh when Claude Code called open_note.
- detectFileOperation only handled Write/Edit but not Bash commands
  that create .md files via redirects.

Fixes:
- Preserve accumulated input in toolInputMapRef (input ?? prev?.input)
- MCP open_note now broadcasts vault_changed before open_tab
- detectFileOperation now detects Bash redirect patterns (>, >>, tee)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:39:34 +01:00
lucaronin
fe9ebc9063 fix: remove --resume from AI chat to fix conversation history
The AI chat was using both --resume (CLI session resumption) AND formatted
conversation history in the prompt simultaneously. This dual-context approach
confused the model — it saw the conversation twice (from session + from prompt
markup), leading to "I don't have context" responses on follow-ups.

Fix: remove --resume entirely from chat mode. Each CLI call is now independent,
with full conversation history formatted into the prompt via
<conversation_history> markup. trimHistory handles graceful truncation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:08:24 +01:00
lucaronin
a7928ea9d7 feat: restore MCP UI-steering tools (highlight_editor, refresh_vault)
Add highlight_editor and refresh_vault tools to the MCP stdio server
so Claude Code can visually highlight UI elements and trigger vault
rescans. Also fix outdated test.js imports after the ai-agent-full-shell
simplification removed write operations from vault.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:03:21 +01:00
lucaronin
872ccf1efe test: add Playwright smoke test for wikilink rendering in AI chat
Update mock agent response to include [[wikilinks]] for testing.
Add smoke test verifying wikilinks render as clickable elements
with correct text, attributes, and styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:58:08 +01:00
lucaronin
f54929c08b feat: render [[wikilinks]] as clickable links in AI chat
- System prompts instruct AI to use [[Note Title]] wikilink syntax
- preprocessWikilinks converts [[Target]] to markdown links
- Custom urlTransform allows wikilink:// scheme through sanitizer
- Click handler resolves target via findEntryByTarget and opens note
- Styled as colored chips matching primary accent
- Works in both AiPanel (agent) and AIChatPanel (legacy chat)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:40:44 +01:00
lucaronin
36065b0ca5 fix: clippy errors — reduce visibility of internal functions, fix PI approx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:19:45 +01:00
lucaronin
1dccc0239c feat: reindex vault command, indexing status bar, sync-triggered reindex
- Add "Reindex Vault" command to command palette and menu bar
- Show "Indexed Xm ago" in status bar when idle, clickable to reindex
- After git pull with updates, auto-trigger incremental reindex
- Add lastIndexedTime state to useIndexing, populated from backend metadata
- Add triggerFullReindex to useIndexing (retryIndexing is now an alias)
- Add onSyncUpdated callback to useAutoSync
- Extract formatIndexedElapsed to utils/indexingHelpers.ts
- Tests: 20 new unit tests across 5 files, 2 Playwright smoke tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:14:12 +01:00
lucaronin
47fe307d45 feat: add last_indexed_commit persistence to indexing backend
Store last_indexed_commit and last_indexed_at in .laputa-index.json
after every successful full or incremental index. Include these in
IndexStatus so the frontend can display staleness. Add
needs_reindex_after_sync() helper that compares HEAD vs stored commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:00:58 +01:00
lucaronin
4c28fa8e25 fix: rustfmt import formatting in commands.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:38:08 +01:00
lucaronin
6e102b9f37 test: Playwright smoke test for push error UX
Expose mockHandlers on window for Playwright overrides. Test that
rejected push shows "Pull first" message, auth error shows
"authentication error", and success shows "Committed and pushed".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:35:35 +01:00
lucaronin
902a545c23 feat: surface actionable push error messages in frontend
Update commitWithPush to parse GitPushResult from backend and show
specific messages (rejected, auth, network) instead of generic
"push failed". Tests verify rejected + network error scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:31:46 +01:00
lucaronin
a4dac661ac feat: add GitPushResult with error classification for push failures
Replaces raw string return from git_push with a structured GitPushResult
that classifies errors as rejected/auth_error/network_error/error, each
with an actionable user-facing message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:29:54 +01:00
lucaronin
bd6487c1d2 style: rustfmt formatting fix in cache test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:22:33 +01:00
lucaronin
875d8177a4 fix: add visible key to frontmatterToEntryPatch maps
The ENTRY_DELETE_MAP and update map in frontmatterToEntryPatch were
missing the 'visible' key. When handleDeleteProperty or
handleUpdateFrontmatter was called for 'visible', the in-memory
VaultEntry was not updated, causing the sidebar to stay stale even
after the file on disk was correctly modified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:20:51 +01:00
lucaronin
21bfaa8c30 fix: include full conversation history in AI chat requests
- Add trimHistory() to keep most recent messages within 100k token budget
- Add formatMessageWithHistory() to prepend conversation context to each message
- Wire up in useAIChat.ts: sendMessage now includes full history
- Keep --resume as belt-and-suspenders for same-session continuity
- 14 new tests in ai-chat.test.ts and useAIChat.test.ts
2026-03-06 23:47:59 +01:00
lucaronin
7610f1b89a test: fix Sidebar tests after Favorites removal 2026-03-06 23:22:14 +01:00
lucaronin
95f6c05053 refactor: remove Favorites and Untagged from sidebar
- Remove Favorites NavItem, Star icon import, go-favorites command
- Remove Untagged NavItem, TagSimple icon import
- Remove favorites from SidebarFilter union type
- Update tests: Sidebar.test.tsx, useMenuEvents.test.ts, App.test.tsx
- Remove GO_FAVORITES constant and menu item from menu.rs
2026-03-06 23:19:37 +01:00
lucaronin
3f6b5b9a06 refactor: rustfmt formatting fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:15:24 +01:00
lucaronin
ddd65926d8 test: Playwright smoke test for trash → Changes badge
Also track mock frontmatter writes in mockSavedSinceCommit so the
Changes panel updates correctly in browser dev mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:08:18 +01:00
lucaronin
7a7ba6086a fix: refresh Changes panel after trash/archive operations
Trash, archive, restore, and unarchive wrote frontmatter to disk but
never called loadModifiedFiles, so the change didn't appear in the
Changes panel until the next manual refresh.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:01:05 +01:00
lucaronin
7f6f5c2a27 Pulse: fix slow note open — O(1) map lookup, no reloadVault on click
Root cause: clicking a note in Pulse used an inline arrow function that:
1. Was recreated on every render (new prop ref → PulseView memo bypassed)
2. Called vault.reloadVault() (full 9000-note rescan) when path didn't match

Fix:
- Add entriesByPath Map (useMemo) — O(1) lookups instead of O(n) .find()
- Add handlePulseOpenNote (useCallback) — stable ref, never triggers reloadVault
  (Pulse notes always exist in vault; no reload needed)
- Wire PulseView to handlePulseOpenNote instead of inline arrow
- Also use entriesByPath in openNoteByPath (MCP bridge)
2026-03-06 22:25:55 +01:00
lucaronin
ef4208697e style: rustfmt vault_config.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:58:39 +01:00
lucaronin
e5aaacdc27 fix: add missing visible field to buildNewEntry in useNoteActions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:57:00 +01:00
lucaronin
78c45970bf test: add Playwright smoke test for visible type property
Also updates vite.config.ts vault API parser to include all VaultEntry
fields (visible, icon, color, order, etc.) so the dev server returns
complete entries for sidebar filtering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:52:40 +01:00
lucaronin
486b0e9ed1 feat: migrate hidden_sections from ui.config to visible property on Type notes
On startup, reads hidden_sections from config/ui.config.md, creates or
updates Type notes with visible: false, then re-saves config without
hidden_sections. Idempotent and safe to run multiple times.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:44:30 +01:00
lucaronin
4df386d310 refactor: remove hidden_sections from VaultConfig and delete useSectionVisibility
Sidebar section visibility is now controlled entirely by the `visible`
property on Type notes. Removes all hidden_sections references from
Rust struct, TypeScript interface, config migration, mock data, and tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:41:00 +01:00
lucaronin
48c3bd11aa feat: add handleToggleTypeVisibility to useEntryActions
Toggle visible property on Type note frontmatter: sets visible:false
to hide, deletes visible property to show (defaults to visible).
Wire up in App.tsx via onToggleTypeVisibility prop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:35:18 +01:00
lucaronin
f7ac01ad1f feat: filter sidebar sections by Type entry visible property
Replace useSectionVisibility hook with direct filtering on
typeEntryMap[type]?.visible !== false. Add onToggleTypeVisibility
callback prop. Tests updated to verify visible:false hides sections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:33:47 +01:00
lucaronin
a7d94b354e feat: add visible field to VaultEntry for Type note sidebar visibility
Parse `visible` boolean from Type note frontmatter (defaults to None/true
when absent). Add to SKIP_KEYS to exclude from relationships and properties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:30:18 +01:00
lucaronin
1817cb3794 🐛 Auto-untrack .laputa-cache.json and .laputa/settings.json from git
These are machine-local files that should never be version controlled:
- .laputa-cache.json: contains absolute paths, changes on every machine
- .laputa/settings.json: per-machine UI settings

Fix: ensure_cache_excluded() now:
1. Adds both files to .git/info/exclude (git-level ignore, no .gitignore needed)
2. Runs `git rm --cached --ignore-unmatch` on vault open to un-track them
   if they were committed in older vaults

This is idempotent and self-healing — existing vaults fix themselves
automatically on next app launch without any manual steps.
2026-03-06 21:10:37 +01:00
lucaronin
66f58446b4 Pulse: lazy pagination with IntersectionObserver infinite scroll
- Page size reduced from 30 → 20 commits per fetch
- Backend: add `skip` param to get_vault_pulse (git log --skip)
  → true pagination instead of re-fetching everything
- Frontend: IntersectionObserver sentinel at bottom of feed
  → auto-loads next page when user scrolls near end
- Append-only updates (no full re-render on load more)
- Add IntersectionObserver mock to test setup
2026-03-06 21:04:24 +01:00
lucaronin
5fdece6d2d 💅 Pulse: add right border, collapse commit files by default
- Add border-r border-[var(--sidebar-border)] to PulseView container
- CommitCard files default to collapsed (expanded: false) for cleaner initial state
- Update tests to reflect new collapsed-by-default behavior
2026-03-06 21:00:35 +01:00
lucaronin
99faee6773 fix: address clippy lints in title_case_folder
Use char array pattern and function reference instead of closures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:11:57 +01:00
lucaronin
d81e2111fc test: add regression tests for hyphenated folder sidebar duplicates
- Sidebar unit test: entries with isA 'Monday Ideas' produce exactly
  one section header (not two)
- Playwright smoke test: verify no duplicate section labels in sidebar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:09:29 +01:00
lucaronin
40b5d31003 fix: normalize hyphenated folder names in infer_type_from_folder()
Split on hyphens and underscores, capitalize each word, join with
spaces. E.g. monday-ideas → Monday Ideas, key-result → Key Result.
This prevents duplicate sidebar sections when folder name differs
from the Type file title.

Bump CACHE_VERSION to 5 to force rescan on existing caches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:59:51 +01:00
lucaronin
bfc79c870b refactor: apply rustfmt formatting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:43:43 +01:00
lucaronin
1c2552a1bb fix: vault cache misses files in new directories, breaking theme restore
Root cause: git_uncommitted_files() used `git status --porcelain` which
reports new untracked directories as `?? theme/` instead of listing
individual files inside. The .md filter skipped directory entries, so
files in newly-created directories (theme/default.md, etc.) were
invisible to the cache system.

Fix: additionally use `git ls-files --others --exclude-standard` which
lists individual untracked files, resolving directories to their
contents.

Also:
- Add type/theme.md definition (icon: palette) to restore and getting-started vault
- Seed theme/*.md vault notes in getting-started vault
- Fix mock create_vault_theme to add entries for dev mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:38:54 +01:00
lucaronin
d7c210222c fix: exclude .laputa-cache.json and settings.json from vault git tracking
These files are machine-specific and should never be committed or cause
conflicts when syncing vaults across devices.

Changes:
- init_repo() now writes .gitignore with .laputa-cache.json and
  .laputa/settings.json excluded before the first commit
- .DS_Store and common editor artifacts also excluded
- openConflictFileRef falls back to openLocalFile() for non-note files
  so 'Open in editor' works for .json conflict files (opens in system
  default app, e.g. TextEdit/VS Code)
- Removed stale git.rs (replaced by git/mod.rs from refactor)
- 2 new Rust tests for .gitignore creation behavior
2026-03-06 15:22:48 +01:00
lucaronin
8712ce8e13 fix: write .gitignore on vault init to exclude .DS_Store
macOS creates .DS_Store files in every folder which were being tracked
by git, causing constant conflicts when vaults are synced across machines.

init_repo() now writes a .gitignore before the first commit with:
- .DS_Store, .AppleDouble, .LSOverride
- ._ thumbnail files
- Common editor artifacts (.vscode/, .idea/, *.swp)

The file is only written if one doesn't already exist, so user-customized
.gitignore files are respected.

Two new Rust tests verify the behavior.
2026-03-06 15:19:25 +01:00
lucaronin
f4af2b4b19 refactor: split Rust backend into sub-modules — git, theme, github, frontmatter, commands
- git.rs (1907 lines) → 7 files: mod, history, status, commit, remote, conflict, pulse
- theme.rs (1075 lines) → 4 files: mod, defaults, seed, create
- github.rs (886 lines) → 4 files: mod, api, auth, clone
- frontmatter.rs (827 lines) → 3 files: mod, yaml, ops
- lib.rs (772 lines) → lib.rs (160) + commands.rs (650)

484 tests pass, clippy clean, rustfmt clean, coverage 85.42%
2026-03-06 13:16:32 +01:00
lucaronin
282c3bebab chore: exclude tools/, scripts/ from CodeScene analysis
tools/qmd/ contains vendored qmd source code (not our code) that was
dragging the overall code health score down to 6.53.
scripts/ contains one-shot utility scripts, not production code.

Excluding both from CodeScene so the health metric reflects only
the actual app codebase (src/, src-tauri/src/).
2026-03-06 11:32:22 +01:00
lucaronin
ebdc39b308 fix: latest.json must point to .tar.gz not .dmg for Tauri in-app updater
The Tauri updater plugin requires the .app.tar.gz artifact as the update
URL — not the .dmg installer. The DMG is for fresh installs only.
This was causing 'Install Update' to silently fail on all macOS builds
since the auto-updater infrastructure was set up.

Change ARM_DMG → ARM_TARBALL, pointing to the .app.tar.gz artifact.
2026-03-06 10:54:04 +01:00
lucaronin
b0b0464f99 feat: add Playwright smoke test infrastructure for task-scoped QA
Adds headless Chromium smoke tests that run before push, catching
UI/UX bugs before Brian QA. Includes shared helpers for command
palette and keyboard shortcut testing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:39:20 +01:00
lucaronin
aaecd9a626 ci: import Apple certificate before Tauri build so beforeBuildCommand can codesign
bundle-qmd.sh signs qmd/qmd and vec0.dylib with Developer ID + hardened
runtime, but the certificate wasn't in the keychain yet when it ran
(Tauri imports the cert internally, AFTER beforeBuildCommand completes).

Fix: add explicit 'Import Apple Developer certificate' step before the
Tauri build step, using security create-keychain + security import.
This makes the cert available to codesign during beforeBuildCommand.
2026-03-06 09:10:46 +01:00
lucaronin
e7f43d5d10 fix: sign qmd binaries with Developer ID + hardened runtime for notarization
Apple notarization rejected qmd/qmd and qmd/vec0.dylib due to:
- Not signed with valid Developer ID certificate
- Hardened runtime not enabled

Changes:
- bundle-qmd.sh: use APPLE_SIGNING_IDENTITY + --options runtime --timestamp in CI
  (falls back to ad-hoc signing in dev when no identity is set)
- useThemeManager.test.ts: add ensure_vault_themes to mock (added by theme
  editor feature, missing from stale theme ID test mock)
2026-03-06 08:43:54 +01:00
lucaronin
c876997fba docs: add mandatory Playwright Phase 1 QA step before laputa-task-done
Claude Code must now run Playwright smoke tests against the dev server
before firing the done signal. This catches UI/UX bugs (missing Cmd+K
commands, broken shortcuts, layout issues) before Brian's native QA.

- Phase 1 (Playwright, headless) = Claude Code's responsibility
- Phase 2 (native Tauri, keyboard-only) = Brian's responsibility

Phase 1 covers: command palette entries, keyboard shortcuts, Tab
navigation, UI state changes. Phase 2 covers: file system, git, native
Tauri behaviors that can't run in the browser.
2026-03-06 08:36:01 +01:00
lucaronin
049b4a9def fix: bundle qmd source in tools/qmd/ so CI can compile it
bundle-qmd.sh was trying to install qmd via 'bun install -g qmd' which
installs a different public npm package, not Luca's qmd tool. CI runners
(runner user) don't have the local qmd installation.

Fix:
- Copy qmd source (src/, package.json, tsconfig.json, bun.lock) to tools/qmd/
- Update bundle-qmd.sh to prefer tools/qmd/ as QMD_SRC
- Run 'bun install --frozen-lockfile' in QMD_SRC if node_modules missing
- Update sqlite-vec lookup to find packages from node_modules after bun install
- Compilation uses 'cd $QMD_SRC && bun build --compile src/qmd.ts'
- Add tools/ to eslint globalIgnores (qmd source has its own lint standards)
- Local dev machines still work (tools/qmd/ takes priority over global install)
2026-03-06 08:22:20 +01:00
lucaronin
7c9067527b ci: add bun setup step to release workflow (required by bundle-qmd.sh)
bundle-qmd.sh uses bun to compile the qmd binary. Release runners
don't have bun pre-installed on self-hosted macOS runners.
Add oven-sh/setup-bun@v2 before the Rust setup step.
2026-03-06 07:23:37 +01:00