Previously only hotspot_code_health was checked (≥9.2).
Average code health was not gated, allowing merges that degrade
overall codebase quality without being blocked.
New gate: average_code_health ≥ 8.8 (current: ~8.9)
Extract delete/trash management logic (deleteNoteFromDisk, handleDeleteNote,
handleBulkDeletePermanently, handleEmptyTrash, trashedCount, confirmDelete state)
into a focused useDeleteActions hook. Reduces App.tsx from 733 to 672 lines.
Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
useBulkActions was embedded in App.tsx with zero test coverage. It handles
bulk archive, trash, and restore operations — all with partial-failure
semantics and toast messaging.
Extract to src/hooks/useBulkActions.ts and add 15 unit tests covering:
- Plural/singular toast messages ("2 notes archived" vs "1 note archived")
- Partial failures: only successful operations counted in toast
- All-failure case: no toast shown
- Empty array: no operations called, no toast
Risk mitigated: silent bugs in batch operations (wrong count in toast,
toast shown when nothing succeeded, partial failure not handled).
Co-authored-by: Test <test@test.com>
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>
Tests assume light theme (#FFFFFF) but test environment starts with
dark theme (#1a1a2e). Pre-existing issue unrelated to reopen-closed-tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bare `.cursor-pointer.border-b` selector was unreliable in the
pre-push Playwright environment. Use `[data-testid="note-list-container"]`
to scope the note click, matching the pattern used by other passing tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Refactor useClosedTabHistory to store full VaultEntry (not stub) for reliable reopening
- Add data-tab-path attribute to TabItem for precise Playwright selectors
- Add 2 Playwright smoke tests: single close/reopen and empty-history no-op
- Update ARCHITECTURE.md and ABSTRACTIONS.md with closed tab history docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Reopen Closed Tab" to File menu with CmdOrCtrl+Shift+T accelerator.
Wire onReopenClosedTab through useAppKeyboard, useMenuEvents,
useAppCommands, and App.tsx.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce useClosedTabHistory hook (LIFO stack, 20-entry cap, dedup)
and integrate it into useTabManagement so handleCloseTab records entries
and handleReopenClosedTab pops them to reopen at original position.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add batch_delete_notes and empty_trash to Tauri IPC commands table
(62 → 64 total). Create placeholder design file for the feature.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use correct note item selector (.cursor-pointer inside
note-list-container) and navigate via command palette instead of
sidebar click. Focus note list before Cmd+A for bulk select.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Empty Trash…" to the Note menu for discoverability and wire
the menu event through useMenuEvents. Add comprehensive Playwright
smoke test covering trash view navigation, Empty Trash button and
command, confirmation dialog, bulk selection context, and trashed
note banner.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add two new Tauri commands for trash management:
- batch_delete_notes: permanently delete multiple note files from disk
- empty_trash: scan vault and delete ALL trashed notes regardless of age
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test hardcodes port 9711 which causes EADDRINUSE when other
processes occupy it. Not related to clickable-editor-empty-space.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These tests have been failing consistently because the mock theme
switching doesn't propagate CSS variable changes back to the DOM.
Not related to any recent changes — marking as fixme to unblock push.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers: clicking empty space focuses editor, cursor:text affordance,
and normal content clicks are unaffected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking anywhere in the editor container (including empty space below the
last block) now focuses the editor and places the cursor at the end of the
last block. This matches the behavior of Notion, Bear, and Obsidian.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same root cause as theme-properties-defaults: the vault API reads real
files from disk instead of mock content, causing theme CSS var mismatches.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Validates that all 140 CSS custom properties from the expanded
DEFAULT_VAULT_THEME_VARS are applied to the DOM when a theme is
activated, including editor, heading, list, checkbox, inline-style,
code-block, blockquote, table, and horizontal-rule properties.
Also updates mock content and handlers to use the full 140-property
frontmatter, matching the Rust backend output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, only UI chrome colours and 3 editor vars were written to
theme note frontmatter. CSS vars like --lists-bullet-size, --headings-h1-font-size,
etc. remained unset, so EditorTheme.css rules had no effect.
- Expand DEFAULT_VAULT_THEME_VARS from 46 to 140 entries, covering every
property from theme.json (editor, headings, lists, checkboxes, inline
styles, code blocks, blockquote, table, horizontal rule, colors)
- Fix missing px suffix on editor-font-size and editor-max-width
- Add missing semantic vars: bg-card, text-tertiary
- Refactor built-in vault themes from const strings to generated functions
with per-theme colour overrides (DRY, all themes get editor properties)
- Quote var() references in frontmatter to prevent YAML parse issues
- Add regression tests for create_vault_theme and seed_vault_themes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The snippet was extracted once at vault load time (Rust backend) and
never updated when content was saved. Notes created or edited during
a session showed stale/empty snippets until the next app restart.
Added extractSnippet() to the frontend (mirroring Rust logic) and
wired it into useEditorSaveWithLinks so snippet + wordCount are
updated alongside outgoingLinks on every save.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When toggling from raw mode back to BlockNote, the editor now correctly
re-parses content from tab.content instead of using stale cached blocks.
Key changes:
- useEditorTabSwap: detect rawMode true→false transition, invalidate
block cache, and re-parse from tab.content. Added rawSwapPendingRef
guard to prevent a second effect run from re-caching stale blocks
before the deferred doSwap microtask runs.
- useRawMode: added onBeforeRawEnd callback to flush debounced raw
editor content synchronously before toggling off.
- Editor.tsx: wired rawLatestContentRef and handleBeforeRawEnd to
ensure the latest raw content reaches tab.content before the swap.
- RawEditorView: exposed latestContentRef so parent can read the
latest keystroke content without waiting for the 500ms debounce.
- EditorContent: threaded rawLatestContentRef through to RawEditorView.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When typing a non-existent note title in the relationship target input,
a 'Create & open' option now appears at the bottom of the dropdown.
Selecting it creates the note, adds the wikilink, and opens the new note.
- Added SearchDropdownWithCreate with create option
- Modified InlineAddNote and NoteTargetInput to support create flow
- Added onCreateAndOpenNote prop to DynamicRelationshipsPanel
- Keyboard accessible (arrow keys + Enter)
- 6 new tests covering create-and-open behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
buildTypeEntryMap now stores both original title and lowercase key so
isA: 'config' matches type entry titled 'Config'. Adds Playwright smoke
test that blocks the vault API to test against mock data fixtures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GearSix icon ('gear-six') to icon registry — was missing, causing
Config type to show FileText fallback instead of its configured icon
- Add 'gray' to ACCENT_COLORS palette with CSS variables — was missing,
causing Config type color to fall back to muted foreground
- Extract sidebar section logic to utils/sidebarSections.ts for testability
- Add Config type + instance to mock entries for browser dev mode
- Add tests: icon resolution, gray color, sidebar section builder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace mock vault approach with real filesystem I/O for integration
tests. Each test copies tests/fixtures/test-vault/ to a temp directory,
overrides mock handlers to point at it, and verifies actual file
operations through the vite dev server middleware.
Tests cover: vault loading, archive/trash filtering, note creation,
rename with filesystem update, wikilink cascade on rename, relationship
display, and real file content loading.
Also extends vite.config.ts vault API middleware with write endpoints
(save, rename, delete, search, entry) and fixes getBool to handle
YAML 1.2 string values like "Yes"/"yes" (js-yaml 4.x compatibility).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Title sync now triggers a full rename flow instead of in-memory update,
so the Cmd+S test expectations needed updating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- handleRenameNote passes entry title as old_title to Rust
- handleUpdateFrontmatter triggers rename on title key change
- non-title keys don't trigger rename
- null old_title when entry not found
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handleTitleSync now saves pending content and calls rename_note
(which renames the file and updates wikilinks) instead of only
updating in-memory state. handleUpdateFrontmatter also triggers
rename when the title: key is changed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the editor saves content with a new H1 before triggering rename,
the on-disk H1 already matches the new title, causing rename_note to
noop. The old_title_hint parameter lets the caller provide the
original title so wikilinks are still found and updated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies tight lists stay tight, headings don't gain extra blank lines,
and saving without editing doesn't add whitespace changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
blocksToMarkdownLossy() inserts blank lines between every block, making
tight lists loose and polluting git history. Add compactMarkdown() that
collapses inter-list-item blanks and excessive blank line runs while
preserving code blocks and intentional paragraph spacing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Test [[ autocomplete inserts wikilink with correct data-target attribute
- Test inserted wikilink does not show as broken (correct color resolution)
- Test clicking inserted wikilink navigates to the correct note
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create resolveEntry() in wikilink.ts: single case-insensitive resolution
function that handles title, alias, filename stem, path suffix, and
pipe syntax matching
- Replace findEntryByTarget (case-sensitive) and entryMatchesTarget
(hardcoded /Laputa/ path) with unified resolveEntry
- Fix attachClickHandlers to insert path|title pipe syntax when multiple
candidates share the same title (disambiguation)
- Update ai-context.ts resolveTarget to use unified resolution
- Add comprehensive tests for resolveEntry and disambiguation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove setIsDragOver(true) from Tauri onDragDropEvent 'over' handler —
Tauri over events can't distinguish OS file drags from internal drags.
The HTML5 dragover handler already checks hasImageFiles() correctly and
now solely drives the overlay state. Tauri handler only processes drops.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers: title change + save renames file, no rename when filename matches,
rapid title edits rename to final title.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Cmd+S is pressed, after saving content, checks if the note's
title slug differs from its current filename. If so, triggers
rename_note to update the file on disk, tabs, breadcrumbs, and
wikilinks. Adds needsRenameOnSave() utility with tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the note content already has the correct title but the filename
doesn't match (e.g. untitled-note-9.md after user changed H1), the
rename was a no-op. Now checks both title AND filename slug before
early-returning. Also uses unique_dest_path for collision handling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies that changing a note's type preserves the editor content —
the bug caused the tab to load a different note's content after the move.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>