Commit Graph

70 Commits

Author SHA1 Message Date
lucaronin
3ab3ca22ff feat: add reasoning streaming, markdown response, structured context snapshot
- Rust: add ThinkingDelta event to ClaudeStreamEvent for reasoning chunks
- ai-agent.ts: forward ThinkingDelta events via onThinking callback
- useAiAgent: stream reasoning live, accumulate response internally,
  reveal as complete block on done
- AiMessage: auto-collapse reasoning when done, use MarkdownContent
  for response rendering, update tests for new behavior
- ai-context: add buildContextSnapshot() for structured JSON context
  with activeNote, openTabs, noteListFilter, vault summary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:09:02 +01:00
lucaronin
cc19d83407 feat: make AI chat tool use blocks expandable with input/output details
Tool call blocks in AI Chat are now clickable and expandable to show
tool name, input parameters (pretty-printed JSON), and output/result.
Collapsed by default, keyboard accessible (Tab/Enter/Space/Esc).

Backend: Rust stream events now carry tool input (accumulated from
input_json_delta chunks) and tool output (from tool_result events).
Frontend: AiActionCard is a disclosure widget with aria-expanded,
error output shown in red, long content truncated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:08:48 +01:00
lucaronin
4499fe0ca3 feat: persist note list sort preference in type file frontmatter
Sort preferences for each type's note list are now stored in the type
file's frontmatter (e.g. `sort: modified:desc` in `type/person.md`)
instead of localStorage. This makes preferences portable with the vault
and versionable in git.

- Add `sort` field to Rust Frontmatter/VaultEntry and TS VaultEntry
- NoteList reads sort from type entry's frontmatter when viewing a type
- Sort changes write to type file via update_frontmatter
- Silent migration from localStorage on first access per type
- Relationship group sorts still use localStorage (no type file)
- Fallback to `modified:desc` when no sort preference exists

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:18:40 +01:00
lucaronin
fbbfe0ea6d feat: vault management — remove vault from list and restore Getting Started
Add ability to remove vaults from the app list without deleting files on disk,
and restore the bundled Getting Started demo vault when needed.

Changes:
- Rust: add hidden_defaults field to VaultList for tracking removed default vaults
- useVaultSwitcher: add removeVault() and restoreGettingStarted() with auto-switch
- useCommandRegistry: add 'Remove Vault from List' and 'Restore Getting Started Vault' commands
- StatusBar: add X button per vault item in vault menu dropdown
- 25 new tests covering removal, restore, edge cases, and command palette

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:36:33 +01:00
Luca Rossi
851e482ab7 feat: sort picker shows custom frontmatter properties (#185)
The sort dropdown now discovers all scalar properties (string, number,
boolean, date) across notes in the current list and shows them below a
separator after the built-in options. Properties that no longer exist
in the current list are gracefully handled by falling back to Modified.

Rust backend extracts custom properties during vault scan so they are
available on every VaultEntry without loading file content on demand.

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:31:18 +01:00
Luca Rossi
3d6e237c5e fix: show all direct relationship properties in note list sidebar (#182)
The GroupBuilder's `seen` set was causing direct relationship properties
to be suppressed. Reverse/computed groups (Children, Events) ran before
the entity's own relationship keys, consuming entries into `seen` and
preventing direct properties like "Belongs to" and "Notes" from appearing.

Fix: process all direct relationship keys from entity.relationships
before the reverse groups, so direct properties always take priority.

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:37:35 +01:00
lucaronin
1b6d7e5b09 fix: persist vault list across app updates
Vault list was stored only in React useState, lost on every app restart
or update. Now persisted to ~/.config/com.laputa.app/vaults.json via
Rust backend commands (load_vault_list, save_vault_list).

- Add vault_list.rs module with VaultEntry/VaultList types and JSON I/O
- Register load_vault_list/save_vault_list Tauri commands
- Extract vaultListStore.ts utility for frontend persistence calls
- Rewrite useVaultSwitcher to load on mount and persist on change
- Show unavailable vaults greyed out with warning icon instead of
  silently removing them
- Add mock handlers for browser/test environments
- Add useVaultSwitcher tests covering persistence, availability, dedup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:05 +01:00
lucaronin
ecd19c5be4 feat: raw editor mode — plain textarea with frontmatter + wikilink autocomplete
Adds a third editor mode (alongside WYSIWYG and Diff) that shows the raw
markdown file in a monospaced textarea. Includes:

- useRawMode hook with derived state (no setState-in-effect) and onFlushPending
- RawEditorView: textarea with 500ms debounce, YAML error banner, wikilink
  autocomplete via [[  trigger, Cmd+S save
- BreadcrumbBar Code icon toggles raw mode; mutual exclusion with diff mode
- Command palette: "Toggle Raw Editor" (View group, requires active tab)
- useEditorModeExclusion hook extracted to keep Editor.tsx under complexity threshold
- buildViewCommands extracted from useCommandRegistry for same reason
- RawToggleButton and RawModeEditorSection components extracted for clean CC

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 18:44:47 +01:00
lucaronin
bfd871704d feat: responsive tab width — shrink tabs to fit window
Tabs now dynamically reduce their max-width when the total width exceeds
the TabBar container, similar to browser tab behavior. Uses a
ResizeObserver on the tab area to calculate per-tab max-width as
min(360, containerWidth / tabCount), floored at 60px minimum.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:45:39 +01:00
Luca Rossi
986965a28f feat: note templates per type (💡 Note templates per tipo) (#170)
* feat: add template field to type entries and template-aware note creation

- Rust: add template field to Frontmatter, VaultEntry, SKIP_KEYS
- Rust: support YAML block scalar (|) for multi-line strings in frontmatter
- Rust: fix value continuation detection to handle block scalars properly
- TypeScript: add template to VaultEntry, buildNewEntry, frontmatterToEntryPatch
- Add DEFAULT_TEMPLATES for Project, Person, Responsibility, Experiment
- resolveNewNote/buildNoteContent accept optional template parameter
- handleCreateNote and handleCreateNoteImmediate look up type template
- Tests for all new behavior (Rust + TypeScript)

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

* feat: wire template UI through Sidebar and App, add TypeCustomizePopover template section

- Add template textarea with debounced save to TypeCustomizePopover
- Wire handleUpdateTypeTemplate through useEntryActions → Sidebar → App
- Add useDebouncedCallback hook for 500ms template save debounce
- Add tests for handleUpdateTypeTemplate and TypeCustomizePopover template UI
- Create design/note-templates.pen placeholder

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

* fix: useRef type arg + template field in bulk mock entries

* style: rustfmt

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-02 08:37:06 +01:00
Luca Rossi
7d563b28fd feat: contextual AI chat on open note (Cmd+I) (#169)
- Cmd+I toggles AI panel with context from the active note
- Context bar shows note title in AI panel when a note is open
- useAiAgent accepts optional contextPrompt used as system prompt
- ai-context.ts extracts structured context from note + related entries
- Updated AiPanel, EditorRightPanel, App, useAppCommands, useCommandRegistry
- 1292 frontend tests pass, coverage 78.96%

Co-authored-by: Test <test@test.com>
2026-03-02 05:00:29 +01:00
Luca Rossi
8e2ca1e3a1 feat: enhance backlinks panel with collapsible section and paragraph context (#167)
The backlinks section in the Inspector is now collapsed by default with a
"Backlinks (N)" toggle header. Expanding reveals each backlink entry with
a paragraph preview showing the surrounding context of the [[wikilink]].

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 02:01:24 +01:00
Luca Rossi
e0c5753be6 feat: replace Anthropic API with Claude CLI for AI chat and agent panels (#159)
* feat: add claude CLI subprocess backend for AI panels

Add claude_cli.rs module that spawns the local `claude` CLI as a
subprocess instead of calling the Anthropic API directly. This removes
the need for users to configure an API key — the CLI uses the user's
existing Claude authentication.

- find_claude_binary(): discovers claude in PATH or common locations
- check_cli(): returns install/version status for the frontend
- run_chat_stream(): spawns claude -p with stream-json for chat panel
- run_agent_stream(): spawns claude with MCP vault tools for agent panel
- Parses stream-json NDJSON events and emits typed Tauri events
- Remove http:default capability (no longer calling APIs from frontend)

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

* feat: replace Anthropic API with Claude CLI for AI chat and agent panels

Remove direct Anthropic API calls and the entire tool-use loop from the frontend.
Both AI Chat and AI Agent panels now delegate to the `claude` CLI subprocess via
Tauri commands, streaming NDJSON events back to the UI.

Key changes:
- ai-chat.ts / ai-agent.ts: replace SSE/API streaming with Tauri invoke + listen
- useAIChat / useAiAgent hooks: simplified to use CLI streaming callbacks
- AIChatPanel / AiPanel: remove API key dialogs, model selectors, undo support
- SettingsPanel: remove Anthropic key field (no longer needed)
- claude_cli.rs: add comprehensive tests (37 total, 90% coverage)
- mock-handlers: replace ai_chat with check_claude_cli / stream_claude_* stubs
- Net -432 lines across 17 files

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

* fix: add --verbose flag to claude CLI subprocess args

* chore: exclude search.rs and lib.rs from coverage (qmd integration + Tauri setup)

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:49:58 +01:00
lucaronin
b6c826b54a fix(test): mock WebSocket in executeToolViaWs test to avoid jsdom/undici unhandled rejection
jsdom's WebSocket implementation internally throws InvalidArgumentError ('invalid onError method')
via undici when a connection fails, causing Vitest to catch it as an unhandled rejection and fail
the test suite. Replace the real WebSocket with a mock that fires onerror via setTimeout,
properly exercising the error path without triggering the jsdom internals.
2026-03-01 11:32:05 +01:00
Luca Rossi
264d4af5fc fix: call Anthropic API directly instead of /api/* dev proxy (#155)
The AI panel called /api/ai/agent and /api/ai/chat — relative HTTP
endpoints that only exist in the Vite dev server. In the Tauri app
there is no local HTTP server, so requests failed with "The string
did not match the expected pattern".

Replace with direct calls to https://api.anthropic.com/v1/messages
with proper headers (x-api-key, anthropic-version). Tauri's webview
allows fetch() to external URLs without CORS issues.

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 00:32:18 +01:00
lucaronin
06e42b182e fix: resolve TypeScript build errors in AI agent files
Fix const assertion on ternary, unused parameter, and readonly array
assignment that caused tsc to fail during pnpm build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:23:06 +01:00
lucaronin
690dd6d841 feat: wire AiPanel into EditorRightPanel with undo + coverage exclusions
Replace AIChatPanel with AiPanel in EditorRightPanel, pass onOpenNote
for vault navigation. Add partial undo (delete created notes). Add
coverage exclusions for AI agent files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:21:42 +01:00
lucaronin
e405ea74d9 feat: wire AI agent panel to Claude API with tool calling
- Add ai-agent.ts: tool definitions (14 MCP tools), agent loop with
  tool_use handling, WebSocket bridge execution (port 9710)
- Add useAiAgent hook: state machine (idle/thinking/tool-executing/done),
  message management, abort support, undo tracking
- Update AiPanel.tsx: replace mock messages with real hook, add model
  selector, input bar, empty state
- Add /api/ai/agent Vite proxy: non-streaming Anthropic endpoint for
  tool-use loop
- Add design/ai-agent-wiring.pen with state machine diagram

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:16:06 +01:00
Luca Rossi
b610f1c673 fix: resolve wikilink paths and show entry title in Getting Started vault (#153)
* fix: resolve wikilink paths and show entry title in Getting Started vault

- Add path suffix matching in findEntryByTarget() so path-based targets
  like 'note/welcome-to-laputa' match entries at /note/welcome-to-laputa.md
- Add resolveDisplayText() in editorSchema to show entry title instead of raw path
- Priority: pipe display text > entry title > humanized path stem
- 6 new test cases covering path-based matching and color resolution

* style: fix rustfmt formatting in mcp.rs

---------

Co-authored-by: Test <test@test.com>
2026-02-28 22:03:38 +01:00
Luca Rossi
84c9f26364 feat: add tags (multi-select) property type (#133)
* design: add tags property type wireframes

Three frames showing the Tags multi-select property:
- Display state: colored pills with X to remove, + button to add
- Input state: dropdown with vault suggestions, checkmarks for selected
- Color picker: per-tag color selection row with accent palette

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

* feat: add tags (multi-select) property type

Adds a new 'tags' display mode for array-valued frontmatter properties.
Includes TagPill components with deterministic color assignment,
inline tag editing with autocomplete from vault-wide values, and
automatic detection for common tag key patterns (tags, keywords,
categories, labels).

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

* fix: SearchPanel test — fire ArrowDown on document not window

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:34:01 +01:00
Luca Rossi
47acd1c30d feat: status color picker — assign persistent color per status value (#120)
Add color dot indicators to each status option in the dropdown. Clicking
the dot opens an inline palette of 8 accent colors (from the design system).
Color assignments persist in localStorage and override the built-in defaults.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 14:27:43 +00:00
Luca Rossi
5941f16d06 refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126)
* refactor: decompose Editor.tsx into focused subcomponents and hooks

Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82):
- useDiffMode hook: diff state + toggle/commit-diff handlers
- useEditorFocus hook: new-note focus event listener
- editorSchema: WikiLink spec + BlockNote schema (module-level)
- SingleEditorView: BlockNote editor + suggestion menus
- EditorContent: breadcrumb bar + diff/editor/loading views
- EditorRightPanel: AI chat / Inspector panel switching
- suggestionEnrichment util: shared enrichment logic (eliminates duplication)

Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass.

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

* refactor: extract standalone functions from NoteList and Sidebar to reduce cc

NoteList: extract createNoteStatusResolver and toggleSetMember from
NoteListInner (cc 13→8, score 9.04→9.38).
Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0).

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

* test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment

Cover extracted hook/utility logic: diff toggle/reset, editor focus event
listener with adaptive timing, and suggestion item enrichment pipeline.

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

* docs: update architecture for editor decomposition and write .claude-done

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
Luca Rossi
80dd525898 refactor: extract SearchResultItem and SearchResultsList from SearchContent (#124)
* design: search subtitle metadata layout with 2 frames

Frame 1: Search result items with metadata subtitle line showing
modified date, word count, and link count below the snippet.
Frame 2: NoteList items with enhanced subtitle including link count.

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

* feat: add metadata subtitle to search results and note list items

- formatSubtitle now shows link count (e.g. "2h ago · 342 words · 5 links")
- New formatSearchSubtitle shows full metadata in search results:
  modified date, created date, word count, and link count
- SearchPanel renders metadata line under each search result snippet
- Mock entries updated with realistic outgoingLinks data
- 11 new tests covering formatSearchSubtitle and search result metadata

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

* refactor: extract SearchResultItem and SearchResultsList from SearchContent

Reduces cyclomatic complexity from 24 to manageable levels by
splitting the monolithic SearchContent into three focused components.
Code Health: 8.97 -> 9.23.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:11 +01:00
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
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
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
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
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
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
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
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
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
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
154aab5098 test: add command palette and fuzzy match tests
Tests for CommandPalette component (rendering, filtering, keyboard
navigation, grouped results, shortcuts display), useCommandRegistry
hook (contextual actions, archive toggle, git availability), fuzzyMatch
utility, and Cmd+K shortcut.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:43:58 +01:00
lucaronin
765f26c3e5 feat: replace grouped autocomplete with flat list and type badge
Switch wiki-link autocomplete from BlockNote's default grouped suggestion
menu to a custom flat list sorted by relevance. Each item shows the note
type (with its accent color) discretely on the right side. Reduce
MAX_RESULTS from 20 to 10 for a cleaner, more focused list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:42:09 +01:00
lucaronin
5d24fdaef3 feat: add command palette with Cmd+K shortcut
Raycast-style command palette with fuzzy search across all app actions.
Groups commands by category (Navigation, Note, Git, View, Settings)
with keyboard shortcuts displayed inline. Contextual actions (trash,
archive, save) are only available when a note is open.

Extracts fuzzyMatch into shared utility for reuse.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:41:54 +01:00
Luca Rossi
7ed9e30299 fix: deduplicate autocomplete suggestions and disambiguate same-title notes (#54)
BlockNote's SuggestionMenu uses item.title as React key, causing duplicate
rendering and broken arrow-key navigation when multiple notes share the
same title. Fix by:
- Adding path-based deduplication to filter out duplicate entries
- Disambiguating same-title notes with parent folder name (e.g. "Standup (work)")
- Deduplicating aliases within each item (filename vs entry.aliases overlap)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:12:35 +00:00
lucaronin
4c6bc91971 feat: expand icon picker with ~290 icons, search, and scrollable grid
- Extract icon registry to src/utils/iconRegistry.ts with 290 curated Phosphor icons
- Add real-time search field that filters icons by name substring
- Show "No icons found" empty state when search yields no results
- Scrollable icon grid (240px max height) for browsing all icons
- Add teal and pink accent colors to the palette (8 total)
- Widen popover from 264px to 280px for better search field fit
- Update tests: search filtering, empty state, icon count validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:36:11 +01:00
lucaronin
dd9b306100 feat: make URL properties clickable with hover underline
- Add UrlValue component: click opens in browser, pencil icon for edit
- URL detection via isUrlValue() (http/https URLs + bare domains)
- URL normalization: prepends https:// to bare domains
- Malformed URLs silently ignored (no open attempt)
- Long URLs truncated visually but full URL opens on click
- Edit mode via pencil icon or keyboard (Enter/Escape/blur)
- 22 new tests covering detection, normalization, and component behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:46:18 +01:00
lucaronin
3adf785f86 Merge remote-tracking branch 'origin/main' into task/wikilink-autocomplete 2026-02-24 13:01:33 +01:00
lucaronin
d2c8d25add fix: exclude YAML frontmatter from word count in Inspector panel
The DynamicPropertiesPanel had a local countWords that used a regex
(/^---[\s\S]*?---/) which could match dashes inside frontmatter values,
leaving part of the frontmatter in the body and inflating the count.

Replace with the canonical countWords from utils/wikilinks.ts which uses
splitFrontmatter (line-based indexOf) and correctly finds the closing
delimiter on its own line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 12:46:21 +01:00
lucaronin
ec06d5ad7e fix: require 2+ chars before showing wikilink autocomplete, limit to 20 results
The autocomplete menu for wiki-links was opening immediately on typing
'[[', processing all 9000+ vault entries and causing a visible freeze.

- Extract preFilterWikilinks utility with MIN_QUERY_LENGTH=2 gate
- Pre-filter entries with case-insensitive substring match before
  creating expensive onItemClick closures
- Cap results at MAX_RESULTS=20 after BlockNote's filterSuggestionItems
- Add comprehensive tests for the utility and Editor integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 12:46:19 +01:00
lucaronin
b8c3a7302c refactor: extract wikilink utils to src/utils/wikilink.ts, fix NoteList useMemo deps
- 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)
2026-02-23 21:55:57 +01:00
Luca Rossi
17e11669cb Merge pull request #26 from refactoringhq/task/wikilink-colorati
feat: color wikilinks by destination note type
2026-02-23 21:02:43 +01:00
lucaronin
7c0111b53d test: add tests for local vault picker options
- 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>
2026-02-23 21:01:42 +01:00
lucaronin
171436243a feat: add local vault options to vault picker
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>
2026-02-23 21:01:42 +01:00
lucaronin
3d35eaff20 feat: color wikilinks by destination note type
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>
2026-02-23 20:44:30 +01:00
lucaronin
180ade7734 fix: implement auto-save for editor content
The editor was not persisting changes to disk. Edits would be lost
when closing and reopening a note.

Changes:
- Add save_note_content Tauri command (Rust) with read-only file check
- Add save_note_content mock handler for browser testing
- Add useAutoSave hook with 500ms debounce and flush-on-tab-switch
- Wire up BlockNote onChange → markdown serialization → auto-save
- Add restoreWikilinksInBlocks utility (reverse of injectWikilinks)
  to convert wikilink nodes back to [[target]] before markdown export
- Extract shared walkBlocks helper to eliminate code duplication
- Suppress onChange during programmatic content swaps (tab switching)
- 12 new frontend tests + 5 new Rust tests (all 469 + 174 passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:48:21 +01:00