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>
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>
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>
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.
- 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
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>
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>
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
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.
On fresh MacBook installs, the bundled qmd binary fails to run due to:
missing execute permissions, macOS quarantine attributes, and no fallback
when qmd is completely absent. This fix addresses all three issues:
- Runtime: ensure +x permissions and remove quarantine on bundled qmd
- Runtime: auto-install qmd via bun when binary not found anywhere
- Build: ad-hoc code-sign qmd and .dylib files in bundle-qmd.sh
- Build: create placeholder resource dirs so fresh clones build cleanly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the fragile auto-install-via-bun approach with a bundled qmd binary.
The build script (scripts/bundle-qmd.sh) compiles qmd into a standalone
binary using `bun build --compile`, then packages it with sqlite-vec native
extensions and a node-llama-cpp stub for keyword-only search.
Key changes:
- find_qmd_binary() now returns QmdBinary with path + work_dir, checks
bundled resource first (app bundle and dev mode), then system paths
- All Command::new(qmd_path) calls updated to use QmdBinary::command()
which sets the correct working directory for node_modules resolution
- Removed auto_install_qmd() and find_bun() — no longer needed
- Tauri config bundles resources/qmd/** into the app
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move Go Back + Go Forward from View menu to Go menu where they
logically belong. Move Toggle Raw Editor, Toggle AI Chat, and
Toggle Backlinks into the Note menu for better discoverability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new Pulse sidebar section that shows chronological git commit
history for the vault. Commits are grouped by day with message, time,
short hash (clickable GitHub link when remote configured), file list
with add/modify/delete status icons, and summary badges. Clicking a
file opens the note in the editor. Disabled with tooltip for non-git
vaults. Accessible via sidebar click or "Go to Pulse" command palette.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rust backend for vault-specific configuration stored as
config/ui.config.md — a regular vault note with YAML frontmatter.
Adds `view` field to VaultEntry for per-type view mode preferences.
Registers get_vault_config and save_vault_config Tauri commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing command palette commands to menu bar and reorganize into
logical groups: File, Edit, View, Go, Note, Vault, Window. New menus
expose navigation filters, note actions, vault management, themes, and
git operations. Context-sensitive items (archive, trash, diff, raw
editor, commit, conflicts) are greyed out when not applicable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove --tools "" restriction so the agent has native bash/read/write/edit
access. Set vault path as working directory for the subprocess.
Simplify MCP to 4 Laputa-specific tools (search_notes, get_vault_context,
get_note, open_note) — everything else is handled by native tools.
Add file operation detection from Write/Edit tool calls to auto-open
created notes and refresh modified notes in the UI. Enhanced tool call
labels show bash commands, file paths, and note names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
[[target]] now shows as "target" and [[target|alias]] shows as "alias"
in note list previews, instead of raw bracket syntax.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New restore_default_themes() Rust fn: seeds both _themes/ and theme/ dirs
- Per-file idempotent: never overwrites existing files with content
- Fixed ensure_vault_themes() to include minimal.md (was missing)
- New 'Restore Default Themes' command in Cmd+K Appearance group
- 3 new Rust tests + 4 new frontend tests
- 1676 frontend tests passed
The previous conflict detection only worked for merge-based pulls
(--no-rebase) but failed to detect pre-existing conflicts from
interrupted rebases or prior sessions. This fixes three root causes:
1. Rust: add is_rebase_in_progress/is_merge_in_progress/get_conflict_mode
helpers, and dispatch git_commit_conflict_resolution between
`git commit` (merge) and `git rebase --continue` (rebase)
2. Frontend: add startup conflict check via get_conflict_files before
pulling, so pre-existing conflicts are detected on app launch
3. App.tsx: handleOpenConflictResolver now fetches conflicts directly
when the cached list is empty, preventing the silent early-return
Also exposes get_conflict_files and get_conflict_mode as Tauri commands
so the frontend can independently check conflict state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Frontend wrote `trashed` (lowercase) but Rust parser expected `Trashed`
(title-case via serde rename). On restart, the lowercase key didn't match,
defaulted to false, and trashed notes reappeared.
- Frontend: use title-case keys (Trashed, Trashed at) matching vault convention
- Rust: add serde aliases for lowercase keys (backward compat with already-written files)
- Add regression tests for both title-case and lowercase parsing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
useMcpRegistration is fully replaced by useMcpStatus which combines
detection + registration. Added design placeholder for MCP status bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add check_mcp_status Tauri command that detects whether the MCP server
is installed, Claude CLI is missing, or config needs setup. The status
bar shows a warning badge (MCP ⚠) when not installed, clickable to
trigger install. Also available via command palette "Install MCP Server".
Replaces useMcpRegistration with useMcpStatus which combines detection
and registration in a single hook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rust: add ThinkingDelta event to ClaudeStreamEvent for reasoning chunks
- ai-agent.ts: forward ThinkingDelta events via onThinking callback
- useAiAgent: stream reasoning live, accumulate response internally,
reveal as complete block on done
- AiMessage: auto-collapse reasoning when done, use MarkdownContent
for response rendering, update tests for new behavior
- ai-context: add buildContextSnapshot() for structured JSON context
with activeNote, openTabs, noteListFilter, vault summary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
seed_vault_themes now writes individual missing/empty files instead of
skipping when the theme/ directory already exists. A new
ensure_vault_themes Tauri command is called by useThemeManager on every
vault open so vaults without a theme/ folder get default.md and dark.md
seeded automatically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Separate 'unavailable' (qmd not installed) from 'error' (indexing failed)
phases. Unavailable state is hidden from the status bar instead of showing
a persistent orange error. Actual errors show "Index failed — retry" with
click-to-retry. Both phases auto-dismiss after a timeout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tool call blocks in AI Chat are now clickable and expandable to show
tool name, input parameters (pretty-printed JSON), and output/result.
Collapsed by default, keyboard accessible (Tab/Enter/Space/Esc).
Backend: Rust stream events now carry tool input (accumulated from
input_json_delta chunks) and tool output (from tool_result events).
Frontend: AiActionCard is a disclosure widget with aria-expanded,
error output shown in red, long content truncated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- build.rs: remove unreliable git rev-list --count logic
- lib.rs: parse build number from Tauri package version at runtime
- Release version 0.20260303.281 -> 'b281'
- Dev version 0.1.0 -> 'dev'
- StatusBar.tsx: build number is clickable (triggers check for updates)
- App.tsx: pass handleCheckForUpdates to StatusBar
- Tests: unit tests for parse_build_label + StatusBar click behavior
git diff --name-only --diff-filter=U only works during an active merge
(while MERGE_HEAD exists). When the vault has stale conflict state —
e.g. after a reboot — git diff returns empty, causing conflictFiles=[]
and making the StatusBar click handler, command palette entry, and
conflict resolver modal all non-functional.
Switch to git ls-files --unmerged which reads unmerged index entries
directly and works regardless of MERGE_HEAD state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add APP_CHECK_FOR_UPDATES constant and menu item in menu.rs (between About and Settings)
- Wire onCheckForUpdates handler through useMenuEvents and useAppCommands
- Add test for app-check-for-updates dispatch
Sort preferences for each type's note list are now stored in the type
file's frontmatter (e.g. `sort: modified:desc` in `type/person.md`)
instead of localStorage. This makes preferences portable with the vault
and versionable in git.
- Add `sort` field to Rust Frontmatter/VaultEntry and TS VaultEntry
- NoteList reads sort from type entry's frontmatter when viewing a type
- Sort changes write to type file via update_frontmatter
- Silent migration from localStorage on first access per type
- Relationship group sorts still use localStorage (no type file)
- Fallback to `modified:desc` when no sort preference exists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The merge of the dark-theme-editor feature (13ee62c) into the
themes-editable rewrite (7169634) broke the entire theming UI:
themes weren't listed, switching didn't work, and new theme creation
was broken.
Root causes and fixes:
- Stale theme ID from old JSON system ("untitled-2") never cleared:
added detection that clears IDs not matching any known vault theme,
with a ref to skip IDs just set by switchTheme/createTheme
- set_active_theme Rust command only accepted String, not Option:
now accepts Option<String> so null can clear the setting
- Theme colors empty in UI: entryToThemeFile now extracts colors
from frontmatter content via extractColorsFromContent
- color-scheme/data-theme-mode not set: added updateColorScheme
and clearColorScheme to sync DOM attributes on theme apply/clear
- isDark broken in Tauri (allContent is {}): moved isDark tracking
into useThemeApplier as state, updated when vars are applied
- SettingsPanel passed activeThemeId as name to createTheme: fixed
to call createTheme() with no arguments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add git_resolve_conflict and git_commit_conflict_resolution Rust commands
- Create useConflictResolver hook for per-file resolution state management
- Create ConflictResolverModal with keyboard shortcuts (K/T/O/Enter/Esc)
- Make StatusBar conflict chip clickable to open resolver modal
- Add 'Resolve Conflicts' command to command palette (conditional)
- Pause auto-pull while conflict resolver modal is open
- Tests: 5 new Rust tests, 10 new hook tests, 1 new auto-sync test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>