Commit Graph

361 Commits

Author SHA1 Message Date
Luca Rossi
8e5cbb2a34 feat: note subtitle — show metadata (date + word count) (#94)
* feat: show metadata subtitle (date + word count) instead of snippet

Replace the note list subtitle with a compact metadata summary showing
relative date and word count (e.g. "2d ago · 342 words") instead of
the first paragraph of note content. Adds word_count to VaultEntry on
the Rust backend, computed during vault scan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cargo fmt formatting

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:50:29 +00:00
Luca Rossi
c177dd2e99 fix: hide empty Backlinks, Relations, and Referenced By sections (#99)
* fix: hide empty backlinks, referenced-by, and relations sections

When a note has no backlinks, referenced-by entries, or relations,
the corresponding inspector sections are now completely hidden instead
of showing "No backlinks" / "No references" / "No relationships" labels.
This keeps the inspector clean and focused on actual content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add before/after mockup for hide-backlinks-empty

Shows inspector panel comparison: cluttered empty state labels (before)
vs clean hidden sections (after).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add before/after wireframes for hide-backlinks-empty

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:14:54 +01:00
Luca Rossi
ba2c1e5b4b feat: git status bar — show last commit link, remove branch name (#92)
* feat: show last commit hash in status bar, remove branch name

Replace the hardcoded "main" branch display with a clickable short SHA
linking to the GitHub commit. Add Rust get_last_commit_info command that
returns the last commit hash and constructs a GitHub URL from the remote.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add design/git-status-bar.pen with new status bar layout

Shows the updated status bar with commit hash link, no branch name,
and annotated change callouts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger — runner 3 pnpm not found (env issue)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:14:46 +01:00
Luca Rossi
fc1badc150 revert: remove titlebar color darkening — superseded by custom drag region (#88)
* revert: remove titlebar color darkening — superseded by custom drag region

The --bg-titlebar CSS variable and background overrides on the four
header bars (TabBar, SidebarTitleBar, NoteList, Inspector) were added
before the native macOS titlebar was replaced with a custom frameless
drag region. These color changes now serve no purpose and add dead code.

Removes:
- --bg-titlebar CSS variable from index.css
- background: var(--bg-titlebar) from all four header components
- Redundant data-tauri-drag-region attrs (useDragRegion hook handles drag)
- design/titlebar-darker.pen design file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger CI for Rust coverage check

* ci: retrigger — runner 3 pnpm path issue (flaky env)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:14:41 +01:00
Luca Rossi
97d23cbf52 feat: replace custom date picker with shadcn/ui Calendar + Popover (#98)
Replace the native HTML5 date input (hidden <input type="date"> with
showPicker()) with a proper shadcn/ui date picker using Calendar and
Popover components. The new implementation provides:

- Calendar popup with month navigation and keyboard support
- Clear date button in the popover footer
- Calendar icon in the trigger button
- Proper date parsing via parseDateValue helper

Also adds the design file design/date-picker-shadcn.pen with closed
and open state frames.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:17:40 +00:00
Luca Rossi
f351ed9a1d feat: use light type color as background for note type labels (#93)
Type labels in autocomplete dropdowns (wiki-link [[, relation add,
Cmd+P quick open) and search panel now use the light/muted variant
of the type color as background instead of grey. This matches the
existing color convention used in wiki-link chips and inspector panels.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:43:29 +00:00
Luca Rossi
f842f2b580 feat: redesign Add Property as inline horizontal form with type selector (#100)
Replace the vertical grey popup with an inline horizontal form that matches
existing property row styling. Fields: [name] [type dropdown] [value] [✓] [×].
Type dropdown uses icon-left design consistent with the app's Select component.
Includes design file with empty and filled state frames.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:31:31 +00:00
Luca Rossi
2907bc592e feat: status property — Notion-style dropdown with color chips (#97)
* fix: resolve search panel freeze by making search_vault async

The search_vault Tauri command was synchronous (fn, not async fn),
blocking the main thread for 30+ seconds during hybrid/semantic
search on large vaults (9200+ files). This caused the macOS beachball.

Changes:
- Make search_vault async with tokio::spawn_blocking (runs qmd off main thread)
- Cache collection name per vault path (avoid repeated qmd collection list calls)
- Cancel inflight searches and debounce timers when panel closes
- Add regression test for stale result cancellation on panel close

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add status property dropdown wireframes

Three frames: closed pill state, open dropdown with suggested/vault
statuses, and custom status creation flow.

Also bump flaky NoteList 9000-entry test timeout from 15s to 30s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: replace status text edit with Notion-style dropdown

- Extract STATUS_STYLES to shared statusStyles.ts utility
- New StatusDropdown component: filterable popover with colored pills
- StatusPill reusable component for consistent status chip rendering
- Vault-wide status aggregation from entries prop
- Dropdown shows "From vault" and "Suggested" sections
- Custom status creation via type-and-Enter
- Escape/backdrop click cancels without saving
- Keyboard navigation (ArrowUp/Down + Enter)
- 22 new tests covering dropdown behavior + integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cargo fmt

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:40:27 +00:00
Luca Rossi
8fbaff792b fix: defer vault entries update via startTransition for instant tab creation (#90)
On a 9000+ entry vault, creating a new note (Cmd+N) was slow because
the entries state update triggered expensive re-computations in NoteList
(filter + sort), Sidebar (type counts), and Editor (wikilink suggestions)
— all blocking the tab from appearing.

Fix: wrap setEntries/setAllContent/trackNew in React's startTransition so
they run as low-priority updates. The tab creation (setTabs/setActiveTabPath)
remains high-priority and renders in <50ms. The entries update is deferred
to idle time without blocking the UI.

Also reorder createAndPersist to call openTab before addEntry, making the
intent explicit: tab appears first, vault index updates second.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:41:46 +00:00
Luca Rossi
410f8b953c fix: restore properties panel header height to match tab bar (52px) (#87)
The InspectorHeader was missing shrink-0, allowing flex compression to
shrink it below the intended 52px. Also moved overflow-y-auto from the
aside to the content div so the header stays pinned while content
scrolls. Extracted refsMatchTargets() helper to reduce nesting depth
(Code Health 8.92 → 9.5).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:46:18 +00:00
Luca Rossi
cb8efa7b0f fix: resolve custom type color and icon in all autocomplete contexts (#84)
* fix: resolve custom type color and icon in all autocomplete contexts

Custom types (e.g., Evergreen, Recipe) appeared grey in wikilink [[,
@-mention, Cmd+P, and search autocompletes because getTypeColor() was
called without the custom color key from the Type document.

Root causes:
- Editor.tsx: getTypeColor(group) missing typeEntryMap[group]?.color
- useNoteSearch.ts: getTypeColor(e.isA) missing custom color lookup
- SearchPanel.tsx: type badge had no color styling at all

Fixes:
- Extract buildTypeEntryMap to shared utility in typeColors.ts
- Pass custom color key in all autocomplete code paths
- Add type icon (Phosphor) to the left of each result in NoteSearchList
- Add TypeIcon to WikilinkSuggestionItem and NoteAutocomplete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add design file for autocomplete type color fix

Shows the fixed state: type icon on the left, colored type badge on
the right, untyped notes neutral grey with no icon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 03:05:22 +00:00
Luca Rossi
25a297007b fix: deduplicate inspector panels (Relations, Backlinks, Referenced By) (#82)
* fix: deduplicate inspector panels by excluding frontmatter from outgoing links

The extract_outgoing_links function was scanning the entire file content
including YAML frontmatter, causing wikilinks in frontmatter fields
(e.g. belongsTo, relatedTo) to appear in both the relationships map AND
outgoingLinks. This made the same note show up in both "Referenced By"
and "Backlinks" panels.

Backend: pass gray_matter's parsed body (no frontmatter) to
extract_outgoing_links instead of raw file content.

Frontend: filter useBacklinks to exclude entries already shown in
Referenced By, ensuring strictly non-overlapping panels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger after disk space cleanup [skip precommit]

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 02:35:18 +00:00
Luca Rossi
e96158495c feat: darken title bar headers for visual separation from content (#85)
* 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>
2026-02-26 01:33:19 +00:00
Luca Rossi
369ecdc009 feat: auto-pull vault changes from Git (background sync + conflict handling) (#79)
* feat: add auto-pull vault sync with conflict handling

- Add git_pull, has_remote, get_conflict_files to Rust backend
- Add GitPullResult type and auto_pull_interval_minutes to Settings
- Create useAutoSync hook (pull on launch, focus, periodic interval)
- Update StatusBar with real sync status indicator
- Add pull interval setting to SettingsPanel
- Add mock handler for git_pull
- Update existing tests for new Settings field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add tests for git pull, settings, and useAutoSync hook

- Add Rust tests: has_remote, git_pull (no_remote, up_to_date, updated),
  get_conflict_files, parse_updated_files, GitPullResult serialization
- Add frontend tests: useAutoSync (mount pull, focus pull, conflict,
  error, manual trigger, concurrent prevention, no_remote)
- Fix existing settings tests for new auto_pull_interval_minutes field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add auto-pull-vault wireframes

Frames showing: sync idle, syncing, conflict indicator,
settings sync section, and conflict toast notification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add auto_pull_interval_minutes to all Settings literals

Fix build error and test fixtures missing the new field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cargo fmt

* ci: re-trigger CI after flaky test

* ci: retrigger after disk space cleanup

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:55:16 +00:00
Luca Rossi
c85e343dd9 feat: smart property display — type-aware rendering with display mode override (#83)
* design: add smart property display wireframes

Frames: date picker, boolean toggle, status badge, display mode override dropdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: smart property display — type-aware rendering with display mode override

- Add property type auto-detection (date, boolean, status, url, text) based on value content and property name
- Date properties (ISO strings) show as friendly format (e.g. "Mar 31, 2026") with native date picker on click
- Boolean properties show as toggle buttons
- Status properties auto-detected from key name or known status values, rendered as colored badges
- Each property gets a display mode override dropdown (visible on hover) to force a specific display type
- Display mode overrides persist across sessions via localStorage
- Add mock data with date/boolean fields for testing
- 36 new tests covering type detection, date formatting, localStorage persistence, and UI rendering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:12:40 +00:00
Luca Rossi
ac4db43fd5 perf: speed up Cmd+N note creation from ~150ms to ~16ms (#81)
* fix: speed up Cmd+N note creation from ~150ms to ~16ms

Root cause: the editor focus used a fixed 150ms setTimeout after note
creation, even when the BlockNote editor was already mounted. The
optimistic UI (state updates, tab opening) was already synchronous,
but the focus delay dominated perceived latency.

Changes:
- Editor focus now uses requestAnimationFrame when editor is mounted
  (~16ms on 60Hz), falling back to 80ms timeout for first-mount only
- Rust save_note_content creates parent directories automatically,
  preventing optimistic-UI revert when creating notes in new folders
- Added perf timing markers (console.debug) to measure creation→focus

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger after disk space cleanup

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:03:38 +00:00
lucaronin
0d6f83d36c feat: remove native titlebar, implement custom drag regions
- titleBarStyle: Overlay + hiddenTitle: true — removes native title bar,
  traffic lights float in sidebar area
- Add core:window:allow-start-dragging capability
- Add useDragRegion hook using startDragging() API (more reliable than
  data-tauri-drag-region with Overlay style)
- Apply drag regions: SidebarTitleBar, NoteList header, TabBar empty zone,
  Inspector header
- Increase header heights 45→52px to give traffic lights breathing room
- Open devtools automatically in debug builds
2026-02-25 21:43:16 +01:00
Luca Rossi
a6d8cc2eb8 feat: unified search panel — progressive keyword+semantic results (#80)
* feat: unify search panel — remove keyword/semantic toggle, progressive results

Replace separate keyword/semantic mode toggle with a unified search that
streams results progressively: keyword results appear first (<500ms),
then hybrid results augment the list (~1-2s). Loading spinner shows in
the input field while semantic search runs. Stale requests are discarded
via generation counter for safe rapid typing.

- Extract search logic into useUnifiedSearch hook (code health 9.26+)
- 300ms debounce, 5s timeout on hybrid, graceful fallback on errors
- 15 tests covering progressive search, spinner, stale results, failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add design wireframes for unified search panel

Two frames showing the search panel states:
- "keyword results loaded, semantic loading" (with spinner)
- "all results loaded" (no spinner, more results including semantic)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: coverage artifacts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 20:35:30 +00:00
lucaronin
8b8cfee9e0 fix: add missing outgoingLinks field to person mock entries 2026-02-25 20:31:55 +01:00
Luca Rossi
27f0adc104 feat: populate macOS menu bar with native menus (File, Edit, View, Window) (#77)
* feat: populate macOS menu bar with File, Edit, View, Window menus

Add complete native macOS menu structure with all app actions:
- Laputa menu: About, Settings (Cmd+,), Hide/Quit
- File: New Note (Cmd+N), Quick Open (Cmd+P), Save (Cmd+S), Close Tab (Cmd+W)
- Edit: standard Undo/Redo/Cut/Copy/Paste/Select All
- View: Editor Only (Cmd+1), Editor+Notes (Cmd+2), All Panels (Cmd+3),
  Toggle Inspector, Command Palette (Cmd+K)
- Window: Minimize, Maximize, Close Window

All accelerators registered via Tauri menu API. Menu events dispatched
to frontend via useMenuEvents hook. Save/Close Tab items dynamically
disabled when no note tab is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: wrap Enter selection assertion in waitFor for effect re-registration

* fix: resolve rebase conflict markers in menu.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:05:24 +00:00
Luca Rossi
98f0e68e38 feat: back/forward navigation between opened notes (#75)
* feat: add useNavigationHistory hook for browser-style back/forward

Pure state management hook that tracks a navigation stack of note paths
with cursor-based back/forward traversal. Handles edge cases: duplicate
pushes (no-op), forward stack cleared on new push, invalid path skipping,
and path removal when tabs close.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: integrate back/forward navigation into UI and keyboard shortcuts

- Add Back/Forward arrow buttons to TabBar (left of tabs)
- Wire useNavigationHistory through App → Editor → TabBar
- Add Cmd+[ and Cmd+] keyboard shortcuts via useAppKeyboard
- Register Go Back/Go Forward in command palette
- History tracks active tab changes, skips closed tabs gracefully
- Navigation from history doesn't re-push to stack (ref guard)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add mouse button 3/4 and trackpad swipe for back/forward

- Mouse buttons 3 (back) and 4 (forward) trigger navigation
- macOS trackpad two-finger horizontal swipe triggers back/forward
  using accumulated wheel deltaX with a 120px threshold
- Debounced reset after 300ms of inactivity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add back-forward-nav.pen with 3 state frames

Shows: default (both disabled), one note visited (back disabled),
two notes visited (back enabled). Matches tab bar button placement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:39:12 +00:00
lucaronin
5f0de00427 fix: wrap Enter key assertion in waitFor to handle async state propagation in SearchPanel test 2026-02-25 19:32:14 +01:00
lucaronin
3a85d47393 fix: resolve conflict markers in InspectorPanels.tsx — take unified NoteSearchList approach 2026-02-25 19:02:29 +01:00
lucaronin
e473b41692 feat: unify note search into shared NoteSearchList + useNoteSearch
Extract a single NoteSearchList component and useNoteSearch hook that
all three note-search UIs now share: wiki-link autocomplete, relations
autocomplete, and Cmd+P quick open. All show note name + type badge
consistently, with identical keyboard navigation behavior.

- NoteSearchList: reusable result list with title + type badge
- useNoteSearch: fuzzy search + arrow-key navigation hook
- WikilinkSuggestionMenu: now wraps NoteSearchList
- QuickOpenPalette: uses useNoteSearch + NoteSearchList
- InlineAddNote: replaces <datalist> with proper search dropdown
- AddRelationshipForm: replaces <datalist> with NoteTargetInput
- InspectorPanels code health improved from 8.99 to 9.04

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:01:22 +01:00
Luca Rossi
462b920da1 fix: show type label for all notes in wiki-link autocomplete (#76)
* fix: show type label for all notes in wiki-link autocomplete

Notes with the default 'Note' type (isA: null) had noteType set to
undefined, hiding their type label. Now every note always shows its
type in the autocomplete dropdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add wiki-autocomplete-type-fix wireframe

Shows the fixed autocomplete dropdown where every note displays its
type label, including default "Note" types in muted color.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:30:35 +01:00
Luca Rossi
48cf359eaa feat: relations autocomplete shows note type badges (matching wiki-link UX) (#74)
* feat: show note type in relations autocomplete dropdown

Replace HTML datalist in InlineAddNote and AddRelationshipForm with
custom NoteAutocomplete component that displays note types as colored
badges, matching the wiki-link autocomplete UX. Supports keyboard
navigation, scrollable results, and graceful truncation.

Also extract ref-group helpers to improve InspectorPanels code health
(8.99 → 9.38).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add relations autocomplete dropdown wireframe

Shows the autocomplete dropdown with note type badges (Project, Topic,
Procedure, Person) matching the wiki-link autocomplete UX. Includes
edge case of note with no type badge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:30:28 +01:00
Luca Rossi
05ac689c2f fix: allow standard text shortcuts in command palette input (#73)
* fix: allow standard text shortcuts in command palette input

Two root causes prevented macOS text shortcuts (Cmd+A, Cmd+Backspace, etc.)
from working in the command palette input:

1. The Tauri native menu only had a View submenu — no Edit menu. On macOS,
   app.set_menu() replaces the entire menu bar, so Cmd+A/C/V/X/Z accelerators
   were removed. Added standard Edit menu with predefined items.

2. useAppKeyboard globally intercepted Cmd+Backspace (mapped to trash note)
   even when a text input was focused. Added an isTextInputFocused guard so
   Cmd+Backspace/Delete fall through to native text editing in inputs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: close missing }) in useAppKeyboard.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:30:20 +01:00
Luca Rossi
6e23fc2c62 fix: restore Cmd+1/2/3 layout shortcuts (regression from App.tsx refactor) (#71)
* fix: wire up view mode visibility flags to conditionally render panels

The useViewMode hook correctly computed sidebarVisible and noteListVisible,
but App.tsx only destructured setViewMode — the flags were never used.
Sidebar and note list were always rendered regardless of view mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add regression tests for Cmd+1/2/3 layout switching

Verifies that keyboard shortcuts actually hide/show sidebar and note list
panels — prevents this regression from recurring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: clear localStorage between App tests to prevent state pollution

The Cmd+1 test persisted 'editor-only' to localStorage, causing subsequent
Cmd+2 and Cmd+3 tests to start with the sidebar hidden (unable to find
'All Notes' text). Clear the view-mode key in beforeEach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: reset localStorage mock between tests to prevent view mode state leaking

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:30:12 +01:00
lucaronin
2c8befab93 fix: make backlinks update reactively when wikilinks are added or removed
Backlinks previously relied on scanning allContent (raw markdown), which
was empty in Tauri mode until notes were explicitly saved. Now outgoing
wikilink targets are extracted during Rust vault scan and stored on
VaultEntry. The frontend useBacklinks hook uses this indexed data, and
outgoingLinks update in real-time on content change.

- Add extract_outgoing_links() in Rust parsing + outgoing_links field on VaultEntry
- Add extractOutgoingLinks() TypeScript utility for real-time updates
- Rewrite useBacklinks to use outgoingLinks instead of scanning raw content
- Add cache version invalidation to force rescan on format change
- Extract useEditorSaveWithLinks hook to keep App.tsx under code health threshold

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:07:04 +01:00
Luca Rossi
fa4905989a fix: remove double border-radius causing rounded corners below macOS title bar (#72)
* fix: remove border-radius from app-shell to eliminate rounded corners below title bar

macOS windows already clip content to the window's own rounded corners.
The CSS border-radius: 10px on .app-shell created a second, inner rounding
that was visible below the transparent title bar, causing a visual gap
between the macOS frame and app content. The inset box-shadow remains for
a clean 1px border.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add window-frame-fix.pen showing target state

Shows the corrected window frame appearance with macOS traffic lights,
clean 1px border separator, and content extending edge-to-edge with
no inner rounded corners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:40:38 +00:00
Luca Rossi
2903196da6 feat: full-text search with qmd backend and SearchPanel UI (Cmd+Shift+F) (#64)
* feat: add search backend (qmd CLI) and design file

- Add search.rs: qmd integration for keyword (BM25), semantic (vsearch),
  and hybrid search modes via CLI JSON output
- Register search_vault Tauri command in lib.rs
- Design file with 3 frames: empty, results, no-results states
- Fix pre-existing test failures: install @tauri-apps/plugin-opener,
  add mock in test setup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add SearchPanel UI with Cmd+Shift+F shortcut

- SearchPanel component: full-text search overlay with keyword/semantic
  mode toggle, debounced search (200ms), arrow key navigation, result
  count + elapsed time display, note type badges from vault entries
- Cmd+Shift+F shortcut registered in useAppKeyboard
- Mock search_vault handler in mock-tauri for browser dev mode
- SearchResult/SearchResponse types in types.ts
- Tests: 15 new tests for SearchPanel + 2 for keyboard shortcut
- scrollIntoView mock in test setup for jsdom compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct SearchPanel imports for Tauri/browser dual-mode

Use invoke from @tauri-apps/api/core and mockInvoke from mock-tauri
with a searchCall wrapper, fixing the TypeScript build error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make test_detect_collection_fallback robust when qmd not installed

* fix: reset localStorage between App tests to prevent view mode state leak

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:09:50 +00:00
lucaronin
6b0ee4bf12 test: add tests for @mention autocomplete
- 8 unit tests for filterPersonMentions utility
- 6 integration tests for @ trigger in Editor component
- Verifies Person-only filtering, wikilink insertion, type badges

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:31:45 +01:00
lucaronin
4fd1d97ac1 feat: add @mention autocomplete for Person entries
- Type @ in editor to trigger person-specific autocomplete
- Filters vault entries to show only Person type notes
- Inserts standard [[Person Name]] wikilink on selection
- Lower query threshold (1 char vs 2) since person list is smaller
- Add 3 more Person entries to mock data for testing
- Reuses existing WikilinkSuggestionMenu component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:31:45 +01:00
Luca Rossi
2f7689599b feat: add optimistic error recovery for note creation (#69)
Note creation was already optimistic (UI updates before disk write) but
errors were silently swallowed. Now if the disk write fails, the
optimistic entry is reverted (tab closed, entry removed) and the user
sees an error toast. Each note creation is independent, so one failure
in rapid Cmd+N presses doesn't affect the others.

- Add removeEntry to useVaultLoader for reverting optimistic adds
- Refactor persistNewNote to return a Promise for error handling
- Extract navigateWikilink, persistOptimistic, createAndPersist,
  and runFrontmatterAndApply helpers to reduce hook complexity
- Add tests for error recovery: single note, type, success path,
  and rapid creation with partial failure
- Code health: 8.93→9.01 (CC fixed: 10→7, primitive obsession improved)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:11:59 +00:00
Luca Rossi
ca9ede51d5 fix: relation chips now show type color instead of default blue (#68)
* fix: relation chips now display correct type color instead of default blue

Root cause: resolveRef matched entries by path/filename only, while
wiki-links used findEntryByTarget (title, filename, aliases). When a
wikilink target used the note title (differing from filename), resolveRef
failed → null type → default blue.

Changes:
- resolveRef now calls findEntryByTarget first (title/alias match)
- Default color for typeless notes changed from blue to neutral grey
- TypeSelector passes custom color key from Type entries
- DynamicPropertiesPanel refactored to reduce cyclomatic complexity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add before/after visual for relations type color fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add task completion summary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:02:33 +00:00
Luca Rossi
f84ebbe662 refactor: split mock-tauri.ts into focused modules (8.81 → 10.0) (#65)
* refactor: split mock-tauri.ts into focused modules

The monolithic mock-tauri.ts (1927 lines, Code Health 8.8) is now split
into 5 cohesive modules under src/mock-tauri/:

- index.ts (barrel, isTauri, simplified mockInvoke) — 10.0
- mock-content.ts (markdown test data) — 10.0
- mock-entries.ts (VaultEntry[] + bulk generator) — 10.0
- mock-handlers.ts (command handlers + state) — 9.68
- vault-api.ts (vault API detection + proxy) — 10.0

Key improvements:
- mockInvoke complexity reduced from cc=17 to trivial (vault API
  extracted to tryVaultApi, conditional routing via data-driven map)
- save_settings complexity reduced via trimOrNull helper
- All 726 tests pass unchanged, coverage remains above 70%
- Public API surface is identical (no import changes needed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: cargo fmt

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:12:23 +00:00
Luca Rossi
e48ac6a828 refactor: improve code health in github.rs, mock-tauri.ts, lib.rs (#63)
* refactor: reduce code duplication and assertion blocks in github.rs

Extract shared mock helpers (mock_json, mock_poll, mock_user, mock_list_repos,
mock_create_repo, mock_device_start) to eliminate repeated server setup.
Use PartialEq-based struct comparison for assertion blocks. Remove
redundant CreateRepoResponse struct in favor of GithubRepo.

Code health: 6.49 → 8.54

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: reduce complexity in mock-tauri.ts mockInvoke and save_settings

Replace nested if-chains in mockInvoke with data-driven VAULT_API_ROUTES
lookup table and extracted tryVaultApi function. Extract trimOrNull helper
to reduce cyclomatic complexity in save_settings.

Code health: 8.81 → 9.38

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract startup tasks from run() in lib.rs

Move vault purge and migration logic into run_startup_tasks() with a
shared log_startup_result helper. Eliminates Bumpy Road and Large Method
code smells from the main run() function.

Code health: 9.14 → 9.68

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:31:25 +01:00
Luca Rossi
558f50e610 refactor: extract hooks from App.tsx to improve code health (#62)
Extract vault management (useVaultSwitcher), git history loading
(useGitHistory), dialog state (useDialogs), and keyboard/command
setup (useAppCommands) into dedicated hooks. App.tsx code health
improves from 8.95 to 10.0 — all extracted hooks also score 10.0.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:31:19 +01:00
Luca Rossi
a8692d021e Merge pull request #61 from refactoringhq/task/command-palette
feat: command palette (Cmd+K) — quick actions and navigation
2026-02-25 00:05:11 +01:00
Luca Rossi
c4be44ffca Merge pull request #60 from refactoringhq/task/autocomplete-flat-list
feat: flat list autocomplete with type badge
2026-02-25 00:05:07 +01:00
Luca Rossi
160db39763 Merge pull request #59 from refactoringhq/task/word-count-title-fix
fix: exclude title heading and wikilinks from word count
2026-02-25 00:03:55 +01:00
lucaronin
af0492b82e fix: exclude title heading and wikilinks from word count
countWords() was including the title line (# Heading) in the count,
inflating it by ~2-3 words. Now strips the first H1 heading and
[[wikilinks]] before counting, so only body prose is tallied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:03:44 +01:00
lucaronin
0e144b5b40 fix: pass modifiedFiles to NoteList so Changes view filters correctly
App.tsx passed getNoteStatus but not modifiedFiles to NoteList.
The Changes view uses modifiedPathSet (derived from modifiedFiles) to
filter entries, so it was always empty — showing "no pending changes"
even with modifications present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:03:40 +01:00
Luca Rossi
9109d6dac2 Merge pull request #58 from refactoringhq/task/nuova-nota-lenta
fix: debounce Cmd+N to prevent slow note creation on rapid keystrokes
2026-02-25 00:03:00 +01:00
Luca Rossi
27b58a4a5c Merge pull request #57 from refactoringhq/task/properties-indent
fix: align Type row padding with other property rows in Properties panel
2026-02-25 00:02:56 +01:00
Luca Rossi
812c35631c Merge pull request #56 from refactoringhq/task/url-text-size
fix: apply text-[12px] to URL values in Properties panel
2026-02-25 00:02:53 +01:00
lucaronin
3f79d1631b fix: use Tauri opener plugin for URL property clicks
Replace window.open() with Tauri's opener plugin (openUrl) so that
clicking a URL property in the Properties panel opens the system
browser instead of failing silently or triggering edit mode.

- Install @tauri-apps/plugin-opener (npm + Cargo + capabilities)
- Add openExternalUrl() utility with Tauri/browser fallback
- Add stopPropagation to prevent accidental edit mode activation
- Add double-click guard (500ms debounce via ref)
- Update tests to mock openExternalUrl instead of window.open
- Add explicit test that URL click does not trigger onStartEdit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:02:09 +01:00
lucaronin
c33495aa82 fix: debounce Cmd+N to prevent slow note creation on rapid keystrokes
- Add deduplication to handleCreateNoteImmediate to prevent rapid calls from creating multiple notes
- Use pending set to track in-flight create operations
- Tests: rapid triple-call dedup, default type, custom type
- Refs: useNoteActions, useTabManagement, useVaultLoader updated
2026-02-25 00:01:52 +01:00
lucaronin
e9e50a7356 fix: align Type row padding with other property rows in Properties panel
ReadOnlyType and TypeSelector containers were missing px-1.5, causing the
Type label to sit flush-left while all other property/info rows were
indented 6px.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:01:51 +01:00
lucaronin
a95625e2bb fix: apply text-[12px] to URL values in Properties panel
The PR #43 text-size reduction missed the UrlValue component — both its
display span and edit input kept the old larger size. Align them with
the 12px used by EditableValue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:01:50 +01:00