Add multi-window support: notes can be opened in dedicated secondary
Tauri windows with editor-only layout (no sidebar, no note list).
Triggers: Cmd+Shift+Click on notes, Cmd+K → "Open in New Window",
Cmd+Shift+O shortcut, Note → "Open in New Window" menu bar item.
Secondary windows have their own auto-save, theme, and wikilink
navigation. Closing a secondary window does not affect the main window.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed property rows gap from gap-2 (8px) to gap-1.5 (6px) to match
the Info section's vertical density.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Labels and values each get w-1/2 so neither can squeeze the other.
Long values no longer cause short labels like "URL" to truncate to "U…".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inbox shows notes without valid outgoing relationships (body wikilinks
or frontmatter refs), helping users find captured but unorganized notes.
Includes time-period filter pills (This week/month/quarter/All time),
Cmd+K command, and macOS menu bar entry. Broken wikilinks (targeting
non-existent notes) are not counted as relationships.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Content is automatically persisted 500ms after the last edit in both
BlockNote and raw editor modes. Cmd+S still works as immediate flush.
Tab close flushes any pending auto-save to prevent data loss.
Updated the "unsaved" tab indicator to show "Auto-saving…" with pulse.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The theme live-reload mechanism was already working correctly — the
previous QA failures were caused by flawed test methodology (modifying
files on disk while the editor had them open, so Cmd+S overwrote with
stale content). Added unit and Playwright tests that verify ALL theme
properties (including bullet-size/color) update when editing the raw
editor buffer and saving. Removed old skipped test file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These properties have dedicated UI elsewhere (trash/archive banner, emoji picker)
and should not appear as generic editable properties.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Note was explicitly excluded from collectActiveTypes() via `e.isA !== 'Note'`.
Removed the exclusion so Note appears like any other type. Untyped entries
(isA === null) now count as Note in both type collection and section filtering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire notifyThemeSaved through the frontmatter update chain so CSS
variables update immediately when:
- The user edits a theme note in raw mode and presses Cmd+S
- A frontmatter property is changed via the inspector panel
Also expose CodeMirror EditorView on the DOM for Playwright test access,
add unit tests for onNotePersisted, and a new Playwright smoke test
that verifies the full raw-editor → save → CSS-vars-update flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Topics used a separate 'topic' selection kind that only showed reverse
relatedTo matches. Now topics use 'entity' kind like all other types,
going through buildRelationshipGroups to show all frontmatter
relationships, children, events, referenced-by, and backlinks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add emoji icon rendering before the note title in TabBar, BreadcrumbBar,
and PinnedCard components to ensure consistent emoji display everywhere
a note title appears. Sidebar, NoteItem, wikilinks, relationships, and
backlinks already had emoji support from prior commits.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The first snippet test consistently fails because Virtuoso doesn't
render snippet elements in the initial viewport during smoke tests.
Remove the unreliable test; keep the formatting validation tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move title-section inside a shared .editor-scroll-area wrapper so both
title and editor content center within the same scrollable context,
fixing alignment drift at wide widths caused by scrollbar offset.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display the note's frontmatter emoji before its title in the editor
wikilink renderer, relationship LinkButtons, and backlinks panel for
consistent emoji visibility across the app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
H1 headings are body content, not the title source of truth. Remove
useHeadingTitleSync hook and frontmatter-title-from-H1 logic in the
editor. Rust rename_note no longer modifies H1 in file content — only
updates the frontmatter title: field.
TitleField now uses optimistic UI: the new title displays immediately
after commit while the async file rename runs in background, preventing
UI freezes on slow filesystems or large vaults.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously restore/unarchive set `Trashed: false` / `archived: false` in
frontmatter. Now uses handleDeleteProperty to remove the fields entirely,
keeping frontmatter clean as if the note was never trashed/archived.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed:
1. CodeMirror raw editor didn't reflect frontmatter changes (e.g. Trashed: true)
after trash/archive because useCodeMirror only used content as initial state.
Added content-sync effect with external-sync guard to prevent infinite loops.
2. Entry actions (trash/archive/restore/unarchive) showed "Property updated" toast
instead of contextual message because runFrontmatterAndApply overwrote it.
Added silent option to suppress toast when caller manages its own feedback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace curated ~270 emoji subset with full Unicode set (1900+) via
unicode-emoji-json. Emoji search now works by English name (e.g.
"rocket" → 🚀). All emojis visible in continuous scroll with sticky
category headers and icon-based quick-nav tabs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rework feedback: filter pills row used py-1.5 padding instead of a
fixed 45px height, causing visual misalignment with the breadcrumb bar.
Replace padding with explicit h-[45px] to match exactly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When viewing a type in the sidebar, the note list now shows filter pills
below the header to switch between Open, Archived, and Trashed notes.
Each pill shows a count badge. Bulk actions are context-aware: Trashed
filter offers Restore/Archive/Delete permanently, Archived filter offers
Unarchive/Trash. Cmd+K commands added for switching filters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
handleSelectNote skipped syncNoteTitle for tabs already open (early
return). Moved sync call into handleSelectNoteWithSync so it always
runs, even when switching to an existing tab via Cmd+P. Also removes
H1 fallback from extract_title — title now comes from frontmatter or
filename only, per the title/filename contract.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Every note can now have an optional emoji icon (frontmatter `icon` field).
The icon is displayed in the editor header, note list, search results,
and Quick Open. Includes command palette commands "Set Note Icon" and
"Remove Note Icon", plus full test coverage (Vitest + Playwright).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- handleSelectNote syncs title frontmatter before loading content
- handleSelectNoteWithSync reloads entry after open to update display title
- Added sync_note_title to mock handlers
- Fixed rapid-switching test to flush sync microtask
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repair Vault command now runs flatten_vault() and migrate_is_a_to_type()
before restoring themes and config, ensuring vaults adopt flat structure.
Also fixes config.md type definition to use `type: Type` instead of
legacy `Is A: Type`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DEFAULT_VAULTS was hard-coded to the main repo path, causing the Vault API
to read stale/duplicate files when running from worktrees. Now uses Vite's
define to inject the correct path at build time. Also fix theme heading
titles (remove redundant "Theme" suffix) and make smoke tests resilient
to Theme-type notes appearing first in the note list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Editor mode is now stored as a global preference in VaultConfig instead of
being tracked per-tab. Toggling raw mode persists across tab switches and
app restarts via the editor_mode field in config/ui.config.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
39 tests for useNoteCreation covering creation, daily notes, templates,
optimistic revert, and unsaved cleanup. 12 tests for useNoteRename
covering rename operations, toast messages, and error handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
useNoteActions.ts reduced from 213 to 125 lines by extracting frontmatter
helpers into frontmatterOps.ts and removing re-exports. Consumers now
import directly from the extracted modules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract note creation (CRUD, daily notes, types, optimistic persistence) into
useNoteCreation and rename operations into useNoteRename. useNoteActions now
composes both hooks plus frontmatter/navigation logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract PropertyValueCells, TypeSelector, and AddPropertyForm into dedicated
files. Move shared display-mode constants to utils/propertyTypes to satisfy
react-refresh lint rule. All tests pass, no behavior change.
Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accept Promise<unknown> instead of Promise<void> to match the actual
reloadVault return type (Promise<VaultEntry[]>).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On vault load, detects files in non-protected subfolders via
vault_health_check. Shows an amber banner offering to flatten them
to the vault root. After migration, reloads the vault automatically.
The banner can be dismissed without migrating.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a TitleField component between the breadcrumb bar and BlockNote
editor that serves as the primary title editing surface. The H1 block
inside BlockNote is hidden via CSS. The title field:
- Shows the note title in a prominent input field
- Displays the expected filename when it differs from the current one
- Triggers onTitleSync on blur/Enter to rename the file
- Responds to laputa:focus-editor selectTitle events for new notes
- Reverts on Escape or empty input
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multi-pass resolution now prioritizes filename stem (strongest) over
alias over title. Removes path-based matching (e.path.endsWith). Legacy
path-style targets like "person/alice" still work by extracting the
last segment "alice" and matching by filename/title.
This matches the flat vault convention where filename IS the note's
identity — filename stem is always slugify(title).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Notes with only headings/rules after H1 (e.g. project templates, daily
notes) previously showed empty snippets. Now extract_snippet collects
sub-heading text as fallback. Also hides the snippet div when empty to
avoid blank gaps in the note list. Bumps cache version to 8 for rescan.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The banner is now rendered as a flex sibling below the list container,
ensuring it displays correctly in both unit tests (JSDOM) and real
browsers (Playwright/Chromium).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When notes are permanently deleted, the Changes note list now shows a
"N notes deleted" banner so the counter matches the visible list items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Snippet extraction was including raw list markers (* , - , + , 1. ) in
the preview text, producing ugly leading spaces. Notes whose snippets
were cached before this fix showed stale/incorrect previews.
- Add strip_list_marker() in both Rust and TS to remove bullet/ordered
list prefixes before snippet assembly
- Trim final snippet to remove leading/trailing whitespace
- Bump CACHE_VERSION 6 → 7 to force full rescan on next vault load,
ensuring all entries get clean snippets
- Add Rust + Vitest tests for list marker stripping
- Update Playwright smoke test with stricter snippet assertions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: handleCreateNoteForRelationship used `await persistNewNote()`
which forced an early React flush. The subsequent frontmatter update
(onAdd/onAddProperty) triggered setTabs in a microtask that collided with
the render batch, causing a radix-ui infinite setState loop
("Maximum update depth exceeded") and a blank white screen.
Two fixes applied:
1. Make handleCreateNoteForRelationship synchronous (fire-and-forget
persistence) to keep all state updates batched — mirrors the working
handleCreateNoteImmediate pattern.
2. Defer onAdd/onAddProperty via setTimeout(0) so the frontmatter update
runs after the tab-switch render completes, avoiding the radix-ui
ref composition loop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WKWebView doesn't auto-invalidate ::before/::after styles when CSS custom
properties change on document.documentElement. Add `void root.offsetHeight`
to force reflow. Also add a version counter in useThemeApplier to prevent
stale async fetches from overwriting live-reload CSS vars.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>