Commit Graph

203 Commits

Author SHA1 Message Date
lucaronin
79f5da0cd7 fix: remove unused DEFAULT_DIRECTIONS import from NoteList
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:35:31 +01:00
lucaronin
8d0fe8b91d test: add tests for sort direction behavior
- getSortComparator with explicit asc/desc direction for all sort modes
- Direction arrows visible in dropdown menu
- Clicking direction arrow reverses sort order in the list
- Direction persistence verified through sort behavior
- Direction icon shown on sort button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:35:30 +01:00
lucaronin
b91aded1ba feat: add sort direction (asc/desc) to note list sorting
- 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>
2026-02-23 08:35:30 +01:00
lucaronin
17c145b865 test: add GitHubVaultModal component tests
12 tests covering:
- Renders nothing when closed
- Shows "connect account" prompt without token
- Opens settings from no-token state
- Shows clone/create tabs with token
- Loads and displays repo list from mock
- Filters repos by search term
- Selects repo and auto-fills clone path
- Disables clone button without selection
- Calls clone_repo and onVaultCloned on successful clone
- Create tab trigger is visible and clickable
- Shows error message on clone failure
- Shows Private/Public badges on repos

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:34:14 +01:00
lucaronin
861235981c feat: GitHub vault modal with clone/create flows
New GitHubVaultModal component with two tabs:
- Clone Existing: search/browse user's repos, select one, choose
  local path, clone via HTTPS+token
- Create New: enter repo name, toggle private/public, create via
  GitHub API then clone locally

Integration:
- "Connect GitHub repo" option in vault switcher menu
- GitHub token field added to Settings panel
- Cloned vaults auto-added to vault list and opened
- Mock handlers for github_list_repos, github_create_repo, clone_repo
- All existing tests updated for new github_token field (386 pass)

Product decision: GitHub token stored in settings rather than OAuth
flow — simpler first version, works with personal access tokens.
Users can upgrade to OAuth later.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:34:14 +01:00
lucaronin
f843b3b640 feat: settings panel — API key management + local config storage 2026-02-22 16:02:30 +01:00
lucaronin
659ca5990f fix: resolve all ESLint errors, enforce lint in CI 2026-02-22 14:31:02 +01:00
lucaronin
728a111f14 fix: resolve TypeScript build errors from any→unknown migration
- Editor.tsx: use targeted eslint-disable for BlockNote block arrays
  (PartialBlock generic type is extremely complex to reference)
- Editor.tsx: cast through unknown for tryParseMarkdownToBlocks result
  which may be sync or async
- useMcpBridge.ts: cast Promise resolve to (value: unknown) => void
- mock-tauri.ts: cast args.path to string for encodeURIComponent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:10:42 +01:00
lucaronin
8cb382e3a3 fix: resolve React hooks/compiler ESLint errors
- 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>
2026-02-22 14:06:51 +01:00
lucaronin
50a766504d test: settings panel and useSettings hook tests
- 14 SettingsPanel component tests: render, save, clear, keyboard
  shortcuts, backdrop close, field population, key trimming
- 5 useSettings hook tests: load, save, error handling
- Updated App.test.tsx mocks for settings commands and ai-chat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:42:20 +01:00
lucaronin
8b67ae53d4 feat: settings panel UI with API key management
- SettingsPanel modal component with masked key fields + reveal/clear
- useSettings hook reads/writes config via Tauri invoke
- Mock handlers for get_settings/save_settings in mock-tauri
- Settings type added to types.ts
- StatusBar gear icon opens settings (was disabled placeholder)
- Cmd+, keyboard shortcut opens settings
- Anthropic key synced to localStorage for AIChatPanel compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:38:18 +01:00
lucaronin
e5d96f97ec fix: suppress react-refresh/only-export-components for valid patterns
Co-located utility exports (constants, helper functions, variant
definitions) alongside their component files. These don't affect
Fast Refresh behavior in practice — targeted eslint-disable comments
explain each case:

- DynamicPropertiesPanel: containsWikilinks, RELATIONSHIP_KEYS
- NoteList: re-exported sort/filter utilities
- NoteItem: getTypeIcon utility
- SidebarParts: isSelectionActive utility
- TypeCustomizePopover: ICON_OPTIONS, resolveIcon
- badge/button/tabs: shadcn/ui variant export pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:11:38 +01:00
lucaronin
8e49af2c0e fix: replace no-explicit-any with proper types
- Test mocks: use { children?: React.ReactNode } and Record<string, unknown>
- Editor.tsx: use unknown[] for BlockNote block arrays
- wikilinks.ts: add BlockLike/InlineItem interfaces for block processing
- useMcpBridge.ts: use unknown for resolve/reject and Record<string, unknown>
- ai-chat.ts: use unknown catch + instanceof Error check
- mock-tauri.ts: type messages array, use Record<string, unknown> for args
  (keep one targeted eslint-disable for heterogeneous handler map)
- vite.config.ts: use http.IncomingMessage/ServerResponse, typed messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:10:26 +01:00
lucaronin
a77d5ff451 fix: simple ESLint errors — regex, unused vars, unused expressions
- eslint.config.js: ignore coverage/ directory (generated files)
- frontmatter.ts: no-regex-spaces — use {2} instead of literal spaces
- wikilinks.ts: no-useless-escape — remove unnecessary \[ escape
- ai-chat.ts: remove unused _model param from getContextLimit
- noteListHelpers.ts: remove unused _modifiedFiles param from filterEntries
- NoteList.tsx: replace ternary expression statement with if/else
- useTabManagement.ts: remove stale eslint-disable-next-line comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:08:16 +01:00
lucaronin
c55ffad4b7 feat: frontend test coverage 47% → 86% — 362 tests across 17 new test files 2026-02-22 13:02:56 +01:00
lucaronin
ee4faa1874 feat: border between macOS title bar and sidebar content 2026-02-22 12:31:51 +01:00
lucaronin
4d0840c6c6 feat: add border between macOS title bar and sidebar content
The macOS window frame (transparent title bar, 38px) and the sidebar
share the same background color (#F7F6F3), making them visually
indistinguishable. Replace the sidebar's paddingTop: 38 with a
dedicated spacer div that has a bottom border (1px solid var(--border)),
matching the border style used between all other panels. The spacer
also serves as a drag region for window movement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:11:39 +01:00
lucaronin
f64d941b0e feat: rename note by double-clicking its tab — inline edit, updates filename + wiki-links
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:10:17 +01:00
lucaronin
e1cdd86ab2 test: add component tests — StatusBar, CommitDialog, DiffView, QuickOpenPalette, ResizeHandle, CreateNoteDialog, EditableValue, TypeCustomizePopover, DynamicPropertiesPanel, InspectorPanels
251 tests covering:
- StatusBar: note count, vault menu interactions
- CommitDialog: file count badge, submit via click/Cmd+Enter, validation
- DiffView: line rendering, color coding, line numbers
- QuickOpenPalette: search, sorting, fuzzy filtering, keyboard navigation
- ResizeHandle: drag with rAF batching, delta accumulation, cursor reset
- CreateNoteDialog: title input, submit, validation, defaultType
- EditableValue: view/edit modes, Enter/Escape/blur, TagPillList CRUD
- TypeCustomizePopover: resolveIcon, color/icon selection
- DynamicPropertiesPanel: properties, status pills, boolean toggle, tags,
  add property form, value coercion, delete
- InspectorPanels: relationships, backlinks, git history, navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:02:44 +01:00
lucaronin
08bcab17c0 feat: leaner note creation — immediate Untitled note, no dialog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:38:03 +01:00
lucaronin
5bbd1469e2 fix: resolve TypeScript build errors in refactored code and test mocks
- 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>
2026-02-22 11:00:07 +01:00
lucaronin
65d1c753ef refactor: improve code health across 6 more files
- DynamicPropertiesPanel.tsx: extract PropertyValueCell, TypeRow, AddPropertyForm (9.06 → 9.68)
- useKeyboardNavigation.ts: extract classifyShortcut and useLatestRef (9.09 → 9.63)
- frontmatter.ts: extract parseScalar, parseInlineArray, isBlockScalar helpers (9.24 → 9.6)
- StatusBar.tsx: extract VaultMenu and VaultMenuItem components (9.23 → 10.0)
- useVaultLoader.ts: extract tauriCall helper and loadVaultData (9.35 → 10.0)
- useNoteActions.ts: extract resolveNewNote, resolveNewType, executeFrontmatterOp (9.22 → 9.38)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:55:45 +01:00
lucaronin
54fe2793fc refactor: reduce complexity in App, DiffView, Editor, InspectorPanels, NoteList
- App.tsx: extract useEntryActions hook and useLayoutPanels hook (8.92 → 9.28)
- DiffView.tsx: extract classifyDiffLine and DiffLine component (9.09 → 10.0)
- Editor.tsx: extract findEntryByTarget and lookupColorForEntry (9.09 → 10.0)
- InspectorPanels.tsx: extract entryStatusTitle and StatusSuffix (8.87 → 10.0)
- NoteList.tsx: extract EntityView, ListView, useNoteListData (8.79 → 9.68)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:48:13 +01:00
lucaronin
178d0784d1 refactor: decompose TabBar and Sidebar for reduced complexity
TabBar (8.50 → 9.38):
- Extract useTabDrag hook for drag state management
- Extract computeDropTarget/computeInsertIndex helpers
- Extract TabItem, DropIndicator, TabBarActions sub-components
- TabBar main function reduced from cc=23/176 LoC to ~25 LoC

Sidebar (8.57 → 9.38, SidebarParts 10.0):
- Create SidebarParts.tsx with NavItem, SectionContent, VisibilityPopover
- Extract useSidebarSections hook for section computation
- Extract useEntryCounts hook consolidating 3 useMemos into 1
- Extract useOutsideClick hook (eliminates 3 duplicate useEffect patterns)
- Extract buildCustomizeArgs, computeReorder pure helpers
- Extract CommitButton, ContextMenuOverlay, CustomizeOverlay
- isSelectionActive extracted as pure function (was closure)
- Sidebar reduced from 604 LoC/cc=34 to ~120 LoC/cc=11

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:37:33 +01:00
lucaronin
c3415e3ce0 refactor: extract Inspector sub-panels into InspectorPanels.tsx
Split Inspector.tsx (score 7.78) into:
- Inspector.tsx: slim shell with header, layout, prop wiring (score 9.6)
- InspectorPanels.tsx: DynamicRelationshipsPanel, BacklinksPanel,
  GitHistoryPanel, LinkButton, RelationshipGroup (score 8.87)

Extracted LinkButton as reusable component for both relationship
refs and backlinks. Moved resolveRefProps to simplify RelationshipGroup.
Extracted AddRelationshipForm and extractRelationshipRefs as focused units.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:22:26 +01:00
lucaronin
af94280e4d refactor: decompose NoteList into focused components and extract helpers
Break up NoteListInner (cc=61, 331 LoC, score 6.94) into:
- NoteItem: extracted item rendering component (score 9.63)
- SortDropdown: extracted into own file (score 9.68)
- noteListHelpers.ts: all utility functions (score 10.0)
  - GroupBuilder class replaces 5-arg addResolvedGroup
  - filterEntries split into filterByKind/filterByFilterType
- PinnedCard: unified entity/type document cards
- RelationshipGroupSection: extracted group rendering
- NoteList.tsx: orchestration only (score 8.79)

NoteListInner reduced from cc=61 to cc=23, 331 → 105 LoC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:18:05 +01:00
lucaronin
df3e4a787e feat: git history — clickable commit hashes with diff view 2026-02-22 10:02:35 +01:00
lucaronin
b0657bc3f5 feat: archive notes button + cmd+e shortcut 2026-02-22 10:02:17 +01:00
lucaronin
cc6d9e4799 fix: prevent double-fire of rename handler on Enter + blur
When pressing Enter in the inline tab rename input, handleSave was
called twice: once from the keydown handler and again from the blur
event triggered when React unmounted the input (editingPath → null).

The second rename call found the old path already deleted/moved,
producing empty content that overwrote the correctly renamed file
in MOCK_CONTENT — causing the editor to show a blank note.

Added a committedRef guard to InlineTabEdit so handleSave only
executes once per edit session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:53:35 +01:00
lucaronin
abf838640d fix: + buttons for note creation pass MouseEvent as type, causing crash
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>
2026-02-22 09:50:54 +01:00
lucaronin
ccf7a440f4 fix: add back-to-editor navigation from commit diff view
When viewing a commit diff from git history, the GitBranch toggle in the
breadcrumb bar was disabled if the file had no unstaged changes, leaving
no way to return to the editor. Now the toggle stays active whenever
diffMode is true, and a visible "Back to editor" button appears at the
top of the diff view for better discoverability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:47:41 +01:00
lucaronin
d23ce5a0b5 feat: clickable commit hashes in git history panel open diff view
- Add get_file_diff_at_commit Rust command to show diff at a specific commit
- Make shortHash in GitHistoryPanel a clickable button that opens DiffView
- Show author name below commit message (small, muted)
- Remove disabled "View all revisions" button
- Wire onLoadDiffAtCommit through Editor → Inspector → App.tsx
- Add mock handler for get_file_diff_at_commit in browser mode
- Fix pre-existing missing `order` field on trashed mock entries
- Update Inspector tests for new behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:27:18 +01:00
lucaronin
7effa256b4 feat: enable adding new relationships from the properties panel 2026-02-21 22:14:15 +01:00
lucaronin
5a8eb6a5d0 feat: rename note via double-click on tab — frontend wiring
- TabBar: inline edit on double-click (Enter saves, Escape cancels)
- Extracted TabItem component + tabStyle helper for code health
- useNoteActions: handleRenameNote invokes backend, updates tabs & vault
- useVaultLoader: replaceEntry handles path-changing renames
- mock-tauri: rename_note mock with wiki link updates
- App/Editor: wire onRenameTab prop through to TabBar

Code health: TabBar improved (CC 23→18). useNoteActions/App/useVaultLoader
show minor degradation on pre-existing high-CC functions — proper fix
requires splitting the monolithic useNoteActions hook (separate concern).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:22:44 +01:00
lucaronin
2bf38bda36 fix: add missing VaultEntry properties in test fixtures and mock data
Add trashed, trashedAt, and order properties that were missing from
test entry objects after those fields were added to the VaultEntry type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:15:45 +01:00
lucaronin
0b6713df14 feat: immediate note creation — skip dialog, create Untitled note on click
Replace the CreateNoteDialog flow with immediate creation. Clicking + or
pressing Cmd+N now instantly creates an "Untitled note" (type=Note) and
opens it as the active tab. Sidebar section + buttons create "Untitled
<type>" for that section's type. Editor auto-focuses after creation so
the user can start typing immediately.

Product decisions:
- Title naming: "Untitled note", "Untitled note 2", etc. for conflicts
- Sidebar section +: creates with that section's type directly
- Editor focus via custom event to avoid stealing focus on regular tab switches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:15:39 +01:00
lucaronin
d9a406e926 fix: truncate long property values with ellipsis instead of wrapping
Add `truncate` and `min-w-0` Tailwind classes to property value elements
in the properties panel so long text is shown on a single line with
ellipsis. The full value is visible via the title attribute on hover.

Affected elements:
- EditableValue display span (main property values)
- Status pill in DynamicPropertiesPanel
- Type button in DynamicPropertiesPanel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:07:41 +01:00
lucaronin
cadc284432 feat: improve customize sections design — Sections label with icon right-aligned 2026-02-21 19:01:41 +01:00
lucaronin
8bcae95e3f feat: wiki-links in editor inherit type color instead of always blue 2026-02-21 19:00:12 +01:00
lucaronin
4b75eb46b6 feat: archive button in breadcrumbs bar + cmd+e shortcut
Add archive/unarchive toggle button to the BreadcrumbBar (Archive icon
for normal notes, ArrowUUpLeft icon for archived notes). Add cmd+e
keyboard shortcut to toggle archive state of the active note.

Refactored useAppKeyboard to use action map pattern (reduces cyclomatic
complexity) and extracted BreadcrumbActions from BreadcrumbBar to split
rendering complexity across two functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:45:25 +01:00
lucaronin
6e20244078 fix: properties panel fills full window height
The Inspector aside element was missing flex-1, so it only grew to
its content height instead of filling the full height of its parent
flex-col container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:40:45 +01:00
lucaronin
081f1e5743 feat: replace 'Customize Sections' label with 'Sections' header
Changed the sidebar section header from a button-style "Customize sections"
label with inline icon to a clean "Sections" heading with the settings icon
pushed to the far right. The settings icon still toggles the customize
popover — no functionality change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:40:06 +01:00
lucaronin
cc1631c368 feat: draggable section groups in sidebar 2026-02-21 18:36:28 +01:00
lucaronin
66754c2dcd feat: draggable tabs with localStorage persistence 2026-02-21 18:35:13 +01:00
lucaronin
09609eff55 feat: trash notes — sidebar filter, breadcrumb buttons, cmd+del shortcut, demo vault data 2026-02-21 18:34:40 +01:00
lucaronin
3752a0be0f test: add breadcrumb trash/restore button tests
Tests verify:
- Trash button shows for non-trashed notes
- Restore button shows for trashed notes
- onClick callbacks fire correctly for both

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:37:53 +01:00
lucaronin
d475143e8a feat: breadcrumb trash/restore buttons and cmd+del shortcut
- Added trash icon button in breadcrumb bar for non-trashed notes
- Added restore (arrow counter-clockwise) button for trashed notes
- Trash button sets trashed=true and trashed_at in frontmatter
- Restore button removes trashed/trashed_at from frontmatter
- Added Cmd+Delete keyboard shortcut to trash active note
- Wired handlers through Editor -> App with vault entry updates
- Toast notifications for trash/restore actions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:37:15 +01:00
lucaronin
a10589fb6c fix: sidebar collapse toggle and update tests for DnD
- Fix toggleSection to account for default collapsed state
  (collapsed[type] ?? true, not just !prev[type])
- Update tests to expand sections before asserting item visibility
  (sections start collapsed by default)
- Add tests for section ordering by Type entry order property
- Add test for drag handle rendering on section headers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:33:36 +01:00
lucaronin
f805c7d1bf feat: draggable section groups in sidebar
- Wrap sidebar section groups in @dnd-kit sortable context
- Add grip-vertical drag handle visible on hover of section headers
- Sort sections by `order` property from Type document entries
- On drag end, compute new sequential order values and persist to
  each Type document's frontmatter via handleReorderSections
- Add onReorderSections callback from Sidebar → App.tsx → vault

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:29:42 +01:00
lucaronin
1dfbbb8a9e feat: add order property to VaultEntry and install @dnd-kit
- Add `order: number | null` to VaultEntry (TS) and VaultEntry (Rust)
- Add order to Frontmatter struct and SKIP_KEYS in vault.rs
- Update all mock Type documents with order values (0-10)
- Add order: null to all non-Type mock entries and test fixtures
- Install @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:27:19 +01:00