* feat: darken title bar headers for visual separation from content
Add --bg-titlebar (#EDECE9) CSS variable and apply it to all four
header bars (TabBar, SidebarTitleBar, NoteList header, Inspector
header) so the top drag region is subtly darker than the app content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add design file for titlebar-darker task
Single frame showing the three-tier color hierarchy:
title bar (#EDECE9) → sidebar (#F7F6F3) → content (#FFFFFF).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The previous commit replaced modifiedFiles with getNoteStatus, but main
had tests for the 'changes filter' view that depend on modifiedFiles.
This commit restores full backward compat:
- Both modifiedFiles and getNoteStatus props are accepted
- getNoteStatus takes precedence when provided (used by App.tsx)
- modifiedFiles automatically derives status='modified' when getNoteStatus
is not provided (used by tests and legacy callers)
- isChangesView / 'Changes' header / changes filter all restored
NoteItem continues to use noteStatus prop (new | modified | clean),
so both green (new) and orange (modified) dots work correctly.
New notes created in-session show a green dot; existing notes with
uncommitted git changes show an orange dot. Saving a new note clears
the green dot; git commit clears the orange dot.
Introduces NoteStatus type ('new' | 'modified' | 'clean'), extracts
useNewNoteTracker hook and resolveNoteStatus pure function, and adds
onNoteSaved callback to useEditorSave for clearing new-note tracking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking "N pending" in the status bar or the "Changes" nav item in
the sidebar shows a filtered list of notes with uncommitted changes.
The view updates in real-time as files are saved or committed.
- Add 'changes' filter to SidebarSelection type
- Make StatusBar "N pending" indicator clickable
- Add "Changes" NavItem in sidebar (visible when modifiedCount > 0)
- Filter NoteList by modifiedFiles paths for changes view
- Show "No pending changes" empty state
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>
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)
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>
- Add SortDirection type and SortConfig interface to noteListHelpers
- getSortComparator now accepts optional direction parameter
- SortDropdown shows ↑/↓ arrow buttons per option in the menu
- Button label shows current direction arrow (ArrowUp/ArrowDown)
- Persistence updated to store {option, direction} with backward compat
for old string-only preferences
- Default directions: modified/created=desc, title/status=asc
- Status sort tiebreaker always uses newest-first regardless of direction
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap ref assignments in useEffect to fix refs-during-render in
useTabManagement, useKeyboardNavigation, and Editor
- Rewrite useAppKeyboard to define keyMap inside useEffect, eliminating
ref access during render
- Add eslint-disable for react-hooks/set-state-in-effect on legitimate
dialog reset patterns (CommitDialog, CreateNoteDialog, CreateTypeDialog,
QuickOpenPalette, AIChatPanel, useVaultLoader)
- Add eslint-disable for react-hooks/static-components on icon lookups
(NoteItem, NoteList PinnedCard) — stateless icon components from a
static map
- Add eslint-disable for react-hooks/purity on Date.now() in
NoteItem TrashDateLine — intentionally memoized on trashedAt
- Remove unused `model` param from buildSystemPrompt (ai-chat.ts) and
update callers
- Fix exhaustive-deps: suppress vault object dep in App.tsx git history
effect (vault object is unstable, loadGitHistory is the real dep)
- Remove unused `modifiedFiles` from useNoteListData params and caller
- Add missing `ref` to Sidebar useOutsideClick deps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused cn import from NoteList.tsx
- Fix SidebarSelection type narrowing in NoteList entity view
- Fix SortableSection onToggle type in Sidebar Omit type
- Fix TypeRow isA prop to accept null
- Align useEntryActions handleUpdateFrontmatter value type
- Add missing trashed/trashedAt/order to test mock VaultEntry objects
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
onClick={onCreateNote} passes the React MouseEvent as the first arg
to handleCreateNoteImmediate(type?), which treats it as a truthy type
string. generateUntitledName then calls .toLowerCase() on the event
object, throwing a TypeError and silently aborting note creation.
Wrap both NoteList and TabBar onClick handlers to call with no args.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each relationship subsection header now has a sort dropdown control
(ArrowsDownUp icon + current sort label). Users can choose to sort by:
- Modified date (default, newest first)
- Created date (newest first)
- Title (alphabetical A-Z)
- Status (Active > Paused > Done > Finished)
The sort control also appears in the main note list header for flat
list views. Sort preferences are persisted per group in localStorage.
Also updates mock data with varied createdAt/status values for testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sidebar: replaced disabled Trash placeholder with active filter showing
count badge and destructive color when selected
- Sidebar: trashed notes excluded from section groups and All Notes count
- NoteList: trash filter shows only trashed entries with TRASHED badge
- NoteList: 30-day warning banner when expired trash notes exist
- NoteList: per-note warning text for notes past 30-day retention
- Inspector: trash icon shown before name in relationship pills for
trashed entries, with (trashed) label and dimmed styling
- Inspector: backlinks show trash icon for trashed entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Archive filter in sidebar with count badge
- Hide archived notes from section groups and All Notes count
- Exclude archived notes from sectionGroup and topic filtering
- Show (archived) indicator on relationship chips and backlinks
- Show ARCHIVED badge on note list items
- Display 'Archive' header in NoteList when filter is active
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add TypeCustomizePopover component with color swatches (6 accent colors)
and icon grid (35 curated Phosphor icons)
- Add right-click context menu on sidebar section group headers with
"Customize icon & color…" option
- Sidebar renders custom icons/colors from type entry frontmatter
- NoteList uses custom icons/colors for type badges and pinned cards
- Wire up onCustomizeType in App.tsx to update type frontmatter
- Add updateEntry to useVaultLoader for instant in-memory updates
- Add --accent-orange-light CSS variable
- typeColors utility now accepts optional custom color key overrides
Product decision: Built-in types can also be customized (override their
defaults), not just custom types. The picker is a simple popover that
appears after right-click → "Customize icon & color…".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tab navigation: Cmd+Option+Left/Right (Tauri), Cmd+Shift+Left/Right (browser)
- Note list navigation: Cmd+Option+Up/Down moves through visible notes
- Navigating notes replaces the current tab content (no new tab opened)
- Wraps around at list boundaries
- New useKeyboardNavigation hook detects Tauri vs browser environment
- Extracted loadNoteContent and replaceTabWithEntry helpers to reduce
complexity in useNoteActions
- Exported filterEntries, sortByModified, buildRelationshipGroups from
NoteList for reuse in keyboard navigation
Note: useNoteActions (30→31 CC) and App (143→153 LOC) show minimal
degradation on pre-existing code health issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Type is a special relationship handled separately by the sidebar,
so it should not appear as one of the generic relationship groups
in the note list entity view. Added case-insensitive filter to
exclude the "type" key from the otherKeys loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix null-safety issues (string | null → string), add missing VaultEntry
fields in test fixtures, remove unused imports, and fix type assertion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When viewing a type document (e.g., type/project.md) in entity view,
show an "Instances" relationship group listing all entries of that
type. For example, the Project type document shows all projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When viewing a section group (e.g., Projects), the corresponding type
document (type/project.md) is now pinned at the top of the NoteList
as a prominent card. Clicking it navigates to the type document.
Instances of that type are listed below.
This makes the type document the entry point for each category,
reinforcing the "types are files" mental model.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Type to color maps (accent-blue) in typeColors.ts
- Add StackSimple icon for Type in NoteList, Inspector, and Sidebar
- Add "Types" section group to sidebar navigation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- font-mono-label: font-weight 500, remove letter-spacing
- NoteList: move timestamp below snippet in note items
- Sidebar: add border-r with sidebar-border color
- ResizeHandle: overlap sidebar border with -ml-1 (no visible gap)
- Sidebar items: text-muted-foreground for inactive group items
- NoteList: uniform 14px vertical padding across all note items
- NoteList header: fixed height 45px to match tab bar
Title now takes full width on top, with timestamp displayed below
in 10px muted text, followed by the snippet. Applies to both flat
list and context view items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update NoteList group headers (RELATED NOTES, EVENTS, etc.) to use
font-mono-label class with bg-muted background bar per design.
Update Inspector section titles (Properties, Relationships, Backlinks,
History) and property labels (Type, Status, Modified, Words) to use
font-mono-overline class. Update status badge to IBM Plex Mono with
border-radius 16px per design spec.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes unused GROUP_PRIORITY and BACKLINKS_KEY constants, replaces
the reference with a string literal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strips the |display-text alias from wikilinks before matching (e.g.
[[essay/foo|Foo]] now correctly resolves to essay/foo.md). This was
preventing the Has, Topics, and other relationship groups from resolving
entries whose wikilink references included aliases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates buildRelationshipGroups to use the new relationships map from
VaultEntry instead of only belongsTo/relatedTo. Now shows Has, Topics,
and all other wikilink-containing frontmatter fields as groups. Group
order: Has, Children, Events, Topics, other fields alphabetically,
Referenced By, Backlinks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a note is selected from the sidebar (entity selection), NoteList
now shows a context view instead of a flat list:
- Prominent top card with type-colored background, bold title, snippet,
timestamp, and type icon
- Grouped relationship sections (Children, Events, Referenced By,
Belongs To, Related To, Backlinks) with collapse/expand chevrons
- Backlinks detected by scanning allContent for wikilink references
- Groups show ALL-CAPS headers with count and CaretDown/CaretRight toggle
- Normal flat list shown when sidebar selection is "All Notes"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a small (14px) Phosphor icon in the top-right corner of each note
item, colored by type using getTypeColor(). Uses the same icon mapping
as Sidebar: Wrench=Project, Flask=Experiment, Target=Responsibility,
ArrowsClockwise=Procedure, Users=Person, CalendarBlank=Event,
Tag=Topic, FileText=Note (default).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the filter pills row ("All 12", "Projects 1", etc.) and all
associated filtering logic (typeFilter state, typeCounts memo, displayed
memo, TYPE_PILLS constant). The note list now always shows all notes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Active filter pills now show the accent color matching the type they
represent, instead of hardcoded blue.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The selected note's border-left and background now reflect the note's
type color instead of hardcoded blue.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace plain .map() rendering with Virtuoso for virtual scrolling of
9000+ notes. Add useMemo to all filtering/sorting chains (filtered,
sorted, searched, typeCounts, displayed). Use entry.snippet directly
instead of computing snippets from allContent. Wrap NoteList in
React.memo to prevent unnecessary re-renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove .dark CSS variables block from index.css
- Remove theme toggle state and Cmd+Shift+T shortcut from Sidebar
- Remove dark class initialization from main.tsx
- Hardcode BlockNote theme to 'light' in Editor
- shadcn dark: classes left in place (harmless, no-op without .dark class)