Commit Graph

77 Commits

Author SHA1 Message Date
Test
7daf6898c1 feat: auto-rename untitled notes + Rust title resolution tests
- Add auto_rename_untitled Rust command: one-shot rename of untitled-*
  files based on H1 heading, with collision handling (-2, -3)
- Wire auto-rename into frontend save flow (useAppSave.ts)
- Update Rust tests: extract_title now uses H1 > frontmatter > filename
- Add extract_h1_title tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:14:43 +02:00
Test
377a3f8d0e feat: H1-as-title — title resolution, TitleField hiding, breadcrumb filename
- Rust: extract_title now prioritizes H1 > frontmatter > filename
- Rust: add has_h1 field to VaultEntry for frontend TitleField control
- Frontend: hide TitleField + icon picker when note has H1 in body
- Frontend: breadcrumb shows filename stem instead of display title
- Frontend: new notes use untitled-{type}-{timestamp}.md, no title in frontmatter
- CSS: only hide first H1 in BlockNote when TitleField is visible
- Updated all tests for new title resolution and note creation behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:08:17 +02:00
Test
e581ad3691 refactor: remove Trash system — delete is now permanent with confirm modal
Remove all vestiges of the abandoned Trash system: trashed/trashedAt fields
from types, frontmatter parsing, sidebar filtering, editor banners, inspector
components, mock data, and all related tests. Delete is already permanent via
useDeleteActions with a confirmation dialog. Notes with trashed:true in
existing vault frontmatter are now treated as normal notes (the flag is
ignored by the parser).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:21:56 +02:00
Test
088e0bca8e feat: show YAML name field as title for .yml files in breadcrumb bar
When a .yml file is opened in the raw editor, the breadcrumb bar now
shows the YAML `name` field (e.g., "Active Projects") instead of the
filename. Falls back to filename if no name field is present.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 04:19:14 +02:00
Test
6693615791 refactor: replace relationship-based inbox logic with _organized property
Inbox now uses an explicit _organized frontmatter property instead of
inferring organization from relationships. This is simpler and gives
users direct control over inbox membership.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:45:25 +02:00
Test
c0a64e181b fix: apply rustfmt formatting to vault module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 13:32:43 +02:00
Test
4c44f8e966 feat: add type-specific property chips in note list
Parse _list_properties_display from type file frontmatter and render
property/relationship values as chips below note snippets. Add a
SlidersHorizontal config popover in the note list header (sectionGroup
views only) with checkboxes and drag-to-reorder via dnd-kit. Changes
are saved back to the type file's frontmatter immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 12:52:54 +02:00
Test
4d6f713a59 fix: apply rustfmt formatting to vault scanner code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:39:55 +02:00
Test
284af17483 feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:

- Folder view shows all file kinds; other views (All Notes, type sections)
  continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
Test
4ae07446a8 feat: add custom views backend — YAML parser, filter engine, Tauri commands
Introduce `.laputa/views/*.yml` for user-defined filtered note lists.
Rust backend: serde_yaml parsing, recursive AND/OR filter evaluation
with 10 operators (equals, contains, any_of, is_empty, before/after,
etc.), wikilink stem matching, and file CRUD. Three Tauri commands:
list_views, save_view_cmd, delete_view_cmd.

ADR 0040 documents the architecture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:57:22 +02:00
Test
fd642889c8 fix: apply rustfmt formatting to git dates code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:03:41 +02:00
Test
e2d0a46608 feat: use git history for note creation/modification dates
Replace unreliable filesystem ctime/mtime with git log timestamps.
A single batch `git log` walks the full commit history to extract
created_at (oldest commit) and modified_at (newest commit) for each
.md file. Falls back to filesystem dates for non-git vaults and
uncommitted files. Cache version bumped to 10 for full rescan.

ADR 0039 documents the decision.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:00:22 +02:00
Test
33db64822d feat: add favorites section with star button, frontmatter persistence, and drag-to-reorder
Adds FAVORITES sidebar section backed by _favorite and _favorite_index
frontmatter properties. Star button in breadcrumb bar toggles favorite
state. Drag-to-reorder updates _favorite_index on all affected notes.
Section auto-hides when empty.

ADR 0038 documents the decision to use frontmatter for portability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:44:23 +02:00
Test
f84faac7c3 fix: vault scanner fallback parser for malformed YAML frontmatter
Files with malformed YAML (e.g. Notion exports containing unescaped
double quotes inside double-quoted strings) caused gray_matter to return
Pod::Null instead of a parsed Hash. This silently reset all frontmatter
fields to defaults, making trashed/archived notes appear in Inbox.

Add a line-by-line fallback parser that extracts simple key:value pairs
when gray_matter fails, so critical fields (Trashed, Archived, type)
are never lost. Bump cache version to 9 to force rescan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 22:40:25 +02:00
Test
6640e74a74 fix: address folder tree QA feedback — recursive filtering, folder creation, path display, remove flatten banner
1. Folder selection now recursively includes notes from subfolders
2. + button creates a new folder with inline rename (Tauri command + UI)
3. Note list items show full path when note is in a subdirectory
4. Remove flat vault migration banner and all related code (Rust commands,
   hook, component, smoke test) — subfolders are now first-class

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:29:24 +02:00
Test
491e5d3962 style: cargo fmt — fix pre-existing Rust formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:54:18 +02:00
Test
c4001ec3f6 feat: detect external file renames and offer wikilink update via banner
When the app regains focus, checks git diff for renames (--diff-filter=R).
If renamed .md files are found, shows a non-blocking banner with "Update
wikilinks" and "Ignore" buttons. The update reuses the existing vault-wide
wikilink replacement logic from rename.rs.

New Tauri commands: detect_renames, update_wikilinks_for_renames.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:58:32 +02:00
Test
eb7a45adf9 feat: scan subdirectories and expose folder tree for sidebar
Extend the Rust vault scanner to index .md files in all visible
subdirectories (hidden dirs excluded). Add FolderNode struct and
list_vault_folders Tauri command that returns the directory tree.
On the frontend, add FolderNode type, extend SidebarSelection with
{ kind: 'folder'; path: string }, and add isInFolder() filtering
in noteListHelpers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:06:11 +02:00
Test
41edd75837 revert: remove pinned properties feature
Reverts commits 47b5e45, 5c76939, 1eecae0, 660208a, 86a9f65, 259d5b6.
Removes PinnedPropertiesBar, usePinnedProperties hook, Rust backend
for _pinned_properties, and all related tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:20:31 +01:00
Test
86a9f65f88 feat: add Rust backend for pinned properties + refactor hook complexity
- Add PinnedPropertyConfig struct to VaultEntry (Rust)
- Extract _pinned_properties from YAML frontmatter (key:icon format)
- Filter underscore-prefixed system properties from properties/relationships
- Bump vault cache version to 9
- Refactor usePinnedProperties hook (cc 15→8) for CodeScene compliance
- Add 6 Rust tests for pinned properties extraction
- Add pinnedProperties field to TypeScript VaultEntry interface
- Wire _pinned_properties mapping in frontmatterOps entry patch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:37:52 +01:00
Test
c559188f5a fix: remove legacy _themes/ dir creation, seed type defs at vault root
Repair Vault and app startup no longer create or recreate the _themes/
directory (legacy JSON theme store). All config files — theme notes,
theme.md, config.md, AGENTS.md — are seeded exclusively at vault root.

- Remove seed_default_themes() and all _themes/ creation paths
- Add migrate_legacy_themes_dir() to clean up _themes/ with only defaults
- Stop list_themes() from auto-creating _themes/ when absent
- Remove _themes from KEEP_FOLDERS and PROTECTED_FOLDERS (flatten-safe)
- Fix create_theme() to error instead of recreating _themes/
- Remove _themes/ seeding from create_getting_started_vault()
- Update all affected tests (642 Rust + 2253 frontend pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:39:37 +01:00
Test
e6b3278fea refactor: remove owner/cadence from Frontmatter, source created_at from filesystem
Remove owner, cadence, created_at, and created_time as hardcoded fields from
the Frontmatter struct — they don't drive app logic and belong in generic
properties. Owner and cadence values now flow through to the properties map
(including single-element array unwrapping). Creation date is sourced from
filesystem metadata (birthtime on macOS) instead of frontmatter.

Also fixes title extraction: add H1 heading extraction to extract_title()
(priority: frontmatter title → H1 → filename slug). Previously titles fell
back directly to filename when no frontmatter title was set.

StringOrList is retained for scalar Frontmatter fields as a defensive measure:
YAML values can arrive as single-element arrays (e.g. Status: [Active]) which
would cause entire deserialization to fail without it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:56:33 +01:00
Test
3a24178759 fix: remove unused import and dead code from title sync refactor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:08:07 +01:00
Test
a1688f8488 feat: title/filename sync on note open + always write title in rename
- New sync_title_on_open function: detects desync between title frontmatter
  and filename, corrects it (filename wins as source of truth)
- Registered sync_note_title Tauri command
- rename_note now always writes title to frontmatter (not just when present)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:45:30 +01:00
Test
56b923c650 refactor: extract_title reads from frontmatter title, not H1
Title is now sourced from the `title` frontmatter field with filename-
derived fallback (slug_to_title). H1 headings are treated as body content.
Added `title` to Frontmatter struct and SKIP_KEYS. Made title_to_slug
pub(super) for reuse across vault submodules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:41:49 +01:00
Test
50dec4bcf3 refactor: extract vault/mod.rs into submodules — fix CodeScene hotspot health
vault/mod.rs was 1820 lines with duplicated code already extracted to
entry.rs, frontmatter.rs, file.rs but never wired up. Slim mod.rs to
delegate to submodules. Score: 7.85 → 10.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:07:29 +01:00
Test
6dc6b5e515 refactor: improve code health for vault/mod.rs and Editor.tsx — fix CodeScene hotspot score
Extract scan_vault helpers (is_md_file, try_parse_md, scan_root_md_files, scan_protected_folders)
to eliminate deep nesting and reduce cyclomatic complexity. Break up large assertion blocks in tests.
Extract useEditorSetup and useRawModeWithFlush hooks from Editor component to reduce complexity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 12:07:33 +01:00
Test
67dbda8f24 style: rustfmt vault mod.rs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:38:47 +01:00
Test
17f94647b7 fix: uniform StringOrList normalization for all frontmatter string fields
Replace per-field Option<String> with Option<StringOrList> for all string
fields in the Frontmatter struct (owner, cadence, status, icon, color,
sidebar_label, template, sort, view, trashed_at, created_at, created_time).

Previously, fields like Owner stored as YAML arrays (e.g. `Owner: [Luca]`)
caused serde to fail the entire Frontmatter deserialization, defaulting all
fields to None — including is_a, which broke the type badge display.

Now all string fields use StringOrList with into_scalar() normalization:
- Single-element array → unwrap to scalar
- Multi-element array → take first element
- Scalar → unchanged
- Empty array → None
- Absent → None

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:37:16 +01:00
Test
2f028404b1 style: rustfmt vault mod.rs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:29:39 +01:00
Test
1550c02e4a fix: add missing VaultEntry fields (belongs_to, status, owner, cadence)
The vault module split left the mod.rs VaultEntry struct missing fields
that were added in entry.rs but never wired in. Adds the missing fields,
populates them from frontmatter, adds status/owner/cadence to SKIP_KEYS,
and fixes conflicting test expectations from the incomplete merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:20:58 +01:00
Test
19d53da8fb fix: use contains() instead of iter().any() for clippy manual_contains
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:45:57 +01:00
Test
15a747094b Resolve merge conflict: keep split vault module from origin/main 2026-03-16 19:25:49 +01:00
Test
4c6b0430f4 refactor: split vault/mod.rs monolith into entry, frontmatter, file modules
Extract VaultEntry struct to entry.rs (64 lines), YAML parsing to
frontmatter.rs (323 lines), and file I/O helpers to file.rs (59 lines).
Tests moved to mod_tests.rs. mod.rs reduced from 1679 to 189 lines,
now purely orchestration (parse_md_file, reload_entry, scan_vault).

All 612 tests pass, public API unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:03:14 +01:00
Test
7a341c2a5e fix: clippy manual_contains lint in vault frontmatter filter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:33:13 +01:00
Test
85fa0b8680 fix: frontmatter parsing fails when unknown fields have list values
The Frontmatter struct deserialization failed completely when any unknown
field had a list value (e.g. Owner: [Luca], Cadence: [Weekly]) because
serde expected a string but got an array. This caused unwrap_or_default()
to return all-None, losing type/status/archived for ~7000+ notes.

Two fixes:
1. Filter parse_frontmatter input to only known keys, preventing unknown
   fields from causing deserialization failures
2. Change Owner and Cadence to StringOrList to handle both formats
2026-03-16 17:12:58 +01:00
Test
08668854b9 fix: flat vault type resolution — remove type/ prefix from links and protected folders
- Type relationship links: [[type/essay]] → [[essay]] (matches flat vault structure)
- PROTECTED_FOLDERS / KEEP_FOLDERS: only attachments, _themes, assets
- TypeSelector navigation: type/slug → slug
- Updated all 14 affected test files
- All 612 Rust tests + 2151 frontend tests pass
2026-03-16 16:19:24 +01:00
Test
f89b199b79 fix: cargo fmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 05:06:24 +01:00
Test
a88c9fc2d0 feat: add vault_health_check command
Detects two classes of vault issues:
1. Stray files in non-protected subfolders (won't be scanned)
2. Filename-title mismatches (filename ≠ slugify(title))

Returns a VaultHealthReport with stray_files and title_mismatches.
Registered as a Tauri command for frontend use.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 05:06:24 +01:00
Test
40bf800c39 refactor: restrict scan_vault to root + protected folders only
Flat vault enforcement: scan_vault now only picks up .md files at the
vault root and inside protected folders (type/, config/, attachments/,
_themes/, theme/). Files in arbitrary subfolders are no longer indexed.
This prevents stray files from being included and enforces the flat
vault convention where all notes live at the root.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 05:06:24 +01:00
Test
b3126044e8 refactor: flatten vault structure — simplify migration API and flatten demo vault
- Simplify flatten_vault API to return usize instead of MigrationResult struct
- Add KEEP_FOLDERS: attachments/ and _themes/ alongside type/, config/, theme/
- Use HashSet for collision tracking in unique_filename
- Update wikilinks from path-based [[folder/slug]] to title-based [[slug]]
- Clean up empty directories after flattening
- Flatten demo-vault-v2: move all notes from type-based subfolders to root
- Update smoke tests for flat vault structure
- Remove migrate_to_flat_vault from repair_vault (one-time migration only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 23:40:47 +01:00
Test
9ce74e7081 style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:56:30 +01:00
Test
deec1be7e0 refactor: remove dead code from flat vault migration
- Remove move_note_to_type_folder function and all its tests
- Remove MoveResult, type_to_folder_slug from rename.rs
- Remove infer_type_from_folder, capitalize_first, title_case_folder
- These were all made dead by the flat vault migration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:55:23 +01:00
Test
112f68c66d feat: flatten vault structure — remove type-based folders
- scan_vault now scans root + system folders (type/, config/, theme/) only
- Type determined purely by frontmatter, no folder inference
- Remove move_note_to_type_folder (Rust command + frontend logic)
- Remove TYPE_FOLDER_MAP — new notes created at vault root
- Add migrate_to_flat_vault command (moves subfolder notes to root,
  updates path-based wikilinks to title-based, cleans empty dirs)
- Migration runs automatically via Cmd+K > Repair Vault
- Update getting_started sample files for flat structure
- Update all tests (611 cargo + 2131 vitest passing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:43:00 +01:00
Test
13d3b2d375 feat: add batch_delete_notes and empty_trash Rust commands
Add two new Tauri commands for trash management:
- batch_delete_notes: permanently delete multiple note files from disk
- empty_trash: scan vault and delete ALL trashed notes regardless of age

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 00:33:43 +01:00
Test
d1021b9131 fix: prevent crash in handleCreateNoteImmediate — slugify fallback + try/catch
- slugify now returns 'untitled' instead of empty string when input has only
  special characters, preventing invalid paths like '/vault//note.md'
- handleCreateNoteImmediate wrapped in try/catch — worst case shows a toast
  error instead of crashing the app
- Added Rust test for deeply nested directory creation in save_note_content
- Regression tests for slugify edge cases and handleCreateNoteImmediate with
  special-character types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:50:05 +01:00
Test
fc4ba24c4e fix: exclude trashed notes from search results and autocomplete
Trashed notes were appearing in search (Cmd+F), Quick Open (Ctrl+P),
wikilink autocomplete ([[), and person mention autocomplete (@).

Rust: add is_file_trashed() to check frontmatter, filter search_vault results.
Frontend: filter trashed entries from useNoteSearch, baseItems in both
editor views, and mock search_vault handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 12:42:56 +01:00
Test
474353718a fix: handle Archived/Trashed Yes/No string values in frontmatter parser
The Rust YAML parser only accepted boolean values (true/false) for the
archived and trashed fields. When the vault writes Archived: Yes or
Trashed: Yes (YAML string, not boolean), serde silently returned None
and the note appeared as non-archived/non-trashed.

Add a custom deserializer that accepts both booleans and string
representations (Yes/yes/YES/true/1 → true, No/no/false/0 → false).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:59:27 +01:00
Test
b10e9facad fix: reload vault invalidates cache and rescans from filesystem
Reload Vault (Cmd+K) now calls the new `reload_vault` Tauri command which
deletes the cache file before scanning, guaranteeing a full filesystem
rescan. Previously it called `list_vault` which used incremental git-based
cache updates that could miss recent changes (e.g. trashing a note then
reloading showed stale data).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:14:34 +01:00
Test
a66eedbecd style: rustfmt vault/mod.rs test formatting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:52:56 +01:00