Commit Graph

27 Commits

Author SHA1 Message Date
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
4ad3777ad7 feat: replace 'Is a' / 'is_a' with 'type' as the canonical frontmatter key
- Rust Frontmatter struct now parses both 'type' (new) and 'Is A'/'is_a' (legacy),
  with 'type' taking precedence via resolve_type()
- Added migrate_is_a_to_type() vault-wide migration (runs on startup, also exposed as Tauri command)
- Frontend: buildNoteContent and resolveNewType now emit 'type:' in YAML
- Inspector SKIP_KEYS hides 'is_a', 'Is A', 'type', 'title' from property list
  (TypeRow already renders the type with its dedicated UI)
- frontmatterToEntryPatch handles both 'type' and legacy 'is_a' keys
- Mock data updated: all YAML content uses 'type:' instead of 'is_a:'/'Is A:'
- Tests updated to expect 'type:' in generated frontmatter

Product decision: internal VaultEntry field stays as `isA` (TS) / `is_a` (Rust)
to minimize blast radius. The user-facing change is the YAML key and inspector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:34:22 +01:00
lucaronin
d14f6f59bd fix: resolve all ESLint errors (lint now exits 0)
- useAppKeyboard: move all logic inside useEffect, fixes refs-in-render
  and no-unused-expressions for view mode shortcut handler
- SettingsPanel: refactor to inner component to fix setState-in-effect;
  remove unused maskKey function
- NoteList: remove re-exports (consumers import from noteListHelpers directly);
  fix no-unused-expressions in toggleGroup; eslint-disable for Icon-in-render
- useNoteActions: eslint-disable tabsRef.current assignment (valid pattern)
- Test files: fix no-explicit-any in useKeyboardNavigation, useSettings,
  useVaultLoader, wikilinks tests; update NoteList.test import path
2026-02-23 08:53:43 +01:00
lucaronin
a4bdc359ce fix: sync in-memory entries after property/rename changes
Two propagation gaps fixed:

1. Property changes (type, status, color, etc.) now immediately sync to
   the entries state via frontmatterToEntryPatch(), so sidebar, note list,
   and relations panel reflect updates without restart.

2. After renaming a note, all other open tabs reload their content from
   disk, picking up updated wikilinks immediately.

Design decisions:
- Used key-mapping approach (frontmatterToEntryPatch) instead of a new
  parse_md_file Tauri command — simpler, works in both Tauri and mock
  modes, covers all VaultEntry fields.
- Converted useNoteActions to config object to avoid excess arguments.
- Extracted findWikilinkTarget, reloadTabsAfterRename, renameToastMessage
  as standalone functions to reduce hook complexity (cc 10→9).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:34:14 +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
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
c67ba46062 refactor: export pure functions from useNoteActions + update coverage config
Export buildNewEntry, slugify, entryMatchesTarget, buildNoteContent,
resolveNewNote, resolveNewType as public functions for direct unit testing.

Add coverage exclusions for untestable files: shadcn/ui primitives,
AI modules (useAIChat, useMcpBridge, ai-chat, AIChatPanel), and types.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:02:07 +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
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
2692ca4a8b refactor: extract useTabManagement hook and mock frontmatter helpers from useNoteActions
Break up the monolithic useNoteActions hook (cc=37, 232 LoC, score 6.89) into:
- useTabManagement: owns tab state and all tab operations (score 9.53)
- mockFrontmatterHelpers: extracted and simplified YAML manipulation (score 9.09+)
- useNoteActions: now focused on note creation and frontmatter operations (score 9.22)

Key improvements:
- Extracted entryMatchesTarget() to reduce wikilink matching complexity
- Deduplicated replaceKeyInLines/removeKeyFromLines into processKeyInLines
- Converted buildNewEntry to use object param (was 5 positional args)
- Overall complexity reduced from cc=37 to cc=11 in useNoteActions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:11:52 +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
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
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
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
lucaronin
d8283184c1 feat: draggable tabs with localStorage persistence
- TabBar: HTML5 drag-and-drop on tab elements (grab/grabbing cursor,
  0.5 opacity on dragged tab, 2px primary-color drop indicator)
- useNoteActions: handleReorderTabs splices tab array, saves order
  to localStorage under "laputa-tab-order" key
- Tab order auto-persists on every change; restored on mount
- Wired through Editor → App props chain

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:24:01 +01:00
lucaronin
8121bbf468 test: add tests for trash notes filtering and UI
- Added filterEntries tests: trash excluded from all/section/topic/archived,
  trash filter returns only trashed entries
- Added NoteList render tests: trash header, TRASHED badge, 30-day warning
  banner, empty trash state
- Updated all test files with trashed/trashedAt fields on VaultEntry mocks
- Updated useNoteActions to include trashed/trashedAt on new entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:56:48 +01:00
lucaronin
311c5e434b feat: archive notes — filter sidebar, hide archived, relationship indicators 2026-02-21 16:44:09 +01:00
lucaronin
a28e4528bd fix: add archived field to all VaultEntry test fixtures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:36:18 +01:00
lucaronin
f36cfd1808 feat: add icon and color fields to VaultEntry
- Add icon (string|null) and color (string|null) to VaultEntry interface
- Update Rust VaultEntry struct and frontmatter parsing to read icon/color
- Add icon/color to SKIP_KEYS so they don't appear as relationships
- Set mock data: Recipe type → cooking-pot/orange, Book type → book-open/green
- Update all test files with the new required fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 13:23:10 +01:00
lucaronin
827ec42d6c feat: add keyboard navigation between tabs and notes
- Tab navigation: Cmd+Option+Left/Right (Tauri), Cmd+Shift+Left/Right (browser)
- Note list navigation: Cmd+Option+Up/Down moves through visible notes
- Navigating notes replaces the current tab content (no new tab opened)
- Wraps around at list boundaries
- New useKeyboardNavigation hook detects Tauri vs browser environment
- Extracted loadNoteContent and replaceTabWithEntry helpers to reduce
  complexity in useNoteActions
- Exported filterEntries, sortByModified, buildRelationshipGroups from
  NoteList for reuse in keyboard navigation

Note: useNoteActions (30→31 CC) and App (143→153 LOC) show minimal
degradation on pre-existing code health issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 10:50:50 +01:00
lucaronin
170de2a7e0 feat: implement create new type feature
Core implementation:
- CreateTypeDialog: simple dialog with name field for creating new types
- handleCreateType in useNoteActions: creates Type documents in type/ folder
- Dynamic sidebar sections: custom types (Recipe, Book, etc.) appear as new
  sidebar sections after built-in ones, each with a + button for instances
- Updated CreateNoteDialog: accepts custom types, shows them with blue accent
- handleCreateNote now supports custom types (folder = lowercased type name)

Product decisions:
- The + on Types section opens CreateTypeDialog (not CreateNoteDialog)
- Custom type sections use FileText icon and blue accent color by default
- Section labels are pluralized (e.g., "Recipes", "Books")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 09:41:46 +01:00
lucaronin
7da76688d2 feat: use generic relationships map in NoteList context view
Updates buildRelationshipGroups to use the new relationships map from
VaultEntry instead of only belongsTo/relatedTo. Now shows Has, Topics,
and all other wikilink-containing frontmatter fields as groups. Group
order: Has, Children, Events, Topics, other fields alphabetically,
Referenced By, Backlinks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 19:11:01 +01:00
lucaronin
b239de3719 fix: prevent crash when opening notes
Move setActiveTabPath after tab content is loaded to prevent Editor's
useEffect from firing before the tab exists in the tabs array. Add
try/catch around editor.replaceBlocks for defensive error handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:52:10 +01:00
lucaronin
829cd2ad95 feat: add vault switcher in status bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:45:31 +01:00
lucaronin
0714184e3c perf: keep tab editors mounted for instant switching (Obsidian pattern)
Instead of unmounting/remounting BlockNoteTab on every tab switch
(via key={activeTabPath}), render all open tabs simultaneously and
toggle visibility with CSS display:none/block. This eliminates
editor recreation and markdown re-parsing on switch. Also adds a
loading skeleton for new tabs and uses a ref for tabs to avoid
stale closures in handleSelectNote.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:32:57 +01:00
lucaronin
47deb6f3b7 refactor: extract useVaultLoader and useNoteActions hooks from App.tsx 2026-02-17 12:10:21 +01:00