Shows all five type colors (red, yellow, green, purple, blue) with
matching dotted underlines — demonstrating the currentColor fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The underline was implemented via border-bottom with a hardcoded
var(--accent-blue), ignoring the per-type color set on the element.
Changed to currentColor so the underline inherits the dynamic color.
Also removed the no-op textDecorationColor inline style (text-decoration
is none; the visual underline comes from border-bottom).
Bonus: fixed pre-existing TS build error in useEditorSave.test.ts
(vi.Mock namespace → Mock type import).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pre-existing build error: vi.Mock namespace not found by tsc.
Import Mock type from vitest instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3 frames showing the dirty indicator UI states:
- NoteList: orange dot before title for modified/added notes
- TabBar: orange dot between title and close button
- StatusBar: "N pending" counter with CircleDot icon
Design decision: used accent-orange (--accent-orange) for the
indicator to differentiate from type-colored icons and provide
a warm "uncommitted changes" signal without being alarming.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- NoteItem: orange dot before title for uncommitted modified notes
- TabBar: orange dot on tabs with uncommitted changes
- StatusBar: "N pending" counter with CircleDot icon when modified files exist
- useEditorSave: onAfterSave callback to refresh modified files after save
- mock-tauri: track dynamically saved files as modified, clear on commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update ARCHITECTURE.md to document react-virtuoso virtual rendering
in NoteList for large vault performance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace require('react') with ESM imports of createElement and types
from React. This fixes the TS2591 error in tsc build for setup.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 7 new tests covering virtual list behavior with large datasets:
- 9000-entry rendering without crash
- Items rendered via Virtuoso mock
- Search filtering on large dataset
- Sorting correctness with large dataset
- Section group filtering with mixed types
- Selection highlighting in virtualized list
- Click handler on virtualized items
Add design/performance-note-list.pen with 4 frames:
1. Default state — 9000+ notes with scrollbar
2. Scrolled mid-list — items 4500+
3. Search filtering — active query narrowing results
4. Empty search result — no matching notes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the direct .map() rendering in ListView with react-virtuoso's
Virtuoso component. This ensures only visible items are in the DOM,
making the list performant with 9000+ notes.
Key changes:
- ListView now uses <Virtuoso> with data prop and overscan={200}
- PinnedCard and TrashWarningBanner rendered as Virtuoso Header component
- Empty state still renders without virtualization (no items to virtualize)
- mock-tauri.ts now generates 9000 bulk entries for testing
- Test setup mocks react-virtuoso for JSDOM compatibility
Product decision: EntityView (relationship groups) is NOT virtualized
because relationship groups are typically small (<100 items). Only the
flat ListView needed virtualization since it can contain 9000+ items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move wikilinkTarget/wikilinkDisplay from InspectorPanels.tsx to src/utils/wikilink.ts
(fixes react-refresh/only-export-components lint error — non-component exports
must live in utility files, not component files)
- Remove unused modifiedFiles from useNoteListData deps array and interface
(fixes react-hooks/exhaustive-deps warning — was in dep array but not used)
Playwright tests verify the vault menu shows all three add-vault
options (open local folder, create new vault, connect GitHub) with
correct testids and visual appearance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy of ui-design.pen as starting point. New frames for vault picker
local folder options to be added when Pencil MCP is available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- StatusBar: 6 new tests for open local folder, create new vault options
- vault-dialog: 3 tests for pickFolder browser fallback behavior
- Rust lib: 3 tests for create_vault_dir (new dir, nested, existing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Open local folder" and "Create new vault" options to the vault
picker dropdown. Uses tauri-plugin-dialog for native folder picker
in Tauri mode, falls back to prompt() in browser mode.
- Rust: add tauri-plugin-dialog, create_vault_dir command
- Frontend: pickFolder utility, StatusBar UI, App.tsx handlers
- Mock: create_vault_dir mock handler for browser testing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Computed reverse relationships — purely frontend, no Rust changes.
The useReferencedBy hook scans all entries' relationships maps to find
those referencing the current note via frontmatter wikilinks.
References are grouped by relationship key (e.g. "via Belongs to",
"via Related to") and displayed between Relationships and Backlinks.
Architecture decision: "Referenced by" is read-only. To remove a
reverse reference, navigate to the source note. Deleting a note
automatically removes it from all computed reverse references.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two frames showing the colored wikilink feature:
1. Editor view with wikilinks colored per note type (red=Experiment,
yellow=Person/Event, purple=Responsibility, green=Topic)
2. Broken link state with muted color + dashed underline + reduced opacity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three frames showing the new bidirectional relationships UI:
1. Inspector with multiple references grouped by relationship type
2. Empty state (no references)
3. Many backlinks scenario with references from multiple relationship types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wikilinks in the editor now show the accent color of the target note's
type (Project=red, Person=yellow, Topic=green, etc.) instead of always
being blue. Broken links (target not found) show muted text with a
dashed underline.
- Extract wikilink color resolution to src/utils/wikilinkColors.ts
- Use getTypeColor from typeColors.ts (single source of truth)
- Support alias and pipe-syntax matching in findEntryByTarget
- Add wikilink--broken CSS class for non-existent targets
- Add 17 unit tests covering all color resolution cases
- Update mock data with wikilinks to various note types + broken link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows the before state (with duplicate is_a property row highlighted in
orange) and the after state (clean inspector with only the Type row).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rust Frontmatter struct now parses both 'type' (new) and 'Is A'/'is_a' (legacy),
with 'type' taking precedence via resolve_type()
- Added migrate_is_a_to_type() vault-wide migration (runs on startup, also exposed as Tauri command)
- Frontend: buildNoteContent and resolveNewType now emit 'type:' in YAML
- Inspector SKIP_KEYS hides 'is_a', 'Is A', 'type', 'title' from property list
(TypeRow already renders the type with its dedicated UI)
- frontmatterToEntryPatch handles both 'type' and legacy 'is_a' keys
- Mock data updated: all YAML content uses 'type:' instead of 'is_a:'/'Is A:'
- Tests updated to expect 'type:' in generated frontmatter
Product decision: internal VaultEntry field stays as `isA` (TS) / `is_a` (Rust)
to minimize blast radius. The user-facing change is the YAML key and inspector.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two frames showing click vs Cmd+Click behavior:
1. Click — Replace Current Tab (note replaces active tab content)
2. Cmd+Click — Open New Tab (new tab created, original preserved)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regular click on a note in NoteList now replaces the current tab content
instead of always creating a new tab. Cmd+Click (or Ctrl+Click) opens
in a new tab. If the note is already open in any tab, clicking just
switches to that tab regardless of modifier key.
- NoteItem: simplified to accept single onClickNote callback
- NoteList: routes click via metaKey/ctrlKey to onReplaceActiveTab or onSelectNote
- useTabManagement: handleReplaceActiveTab checks all tabs before replacing
- Extracted loadAndSetTab/isTabOpen/routeNoteClick helpers for code health
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>