- 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>
Default, hover (underline + pointer), and edit mode states
for URL properties in the Inspector panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add remove (X) button on hover for each related note in a relationship
group, and inline add control with autocomplete to add new notes to
existing relations. Changes update frontmatter via the existing
update/delete property pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The transparent titlebar window blended with the app content because
both shared the same background color. An inset box-shadow using the
existing --border-primary color creates a subtle separation. The 10px
border-radius matches the macOS window corner radius.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Pre-existing type mismatch where useEditorSave returns Promise<boolean>
but useCommitFlow expected Promise<void>. The return value is unused.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Two bugs caused stale dirty indicators after rename:
1. handleRenameTab didn't call loadModifiedFiles() after rename
2. handleSave (Cmd+S) skipped onAfterSave when nothing was pending
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous client_id pointed to a GitHub App that did not have
device authorization flow enabled, causing "failed to start login"
errors. Switch to the refactoringhq OAuth App (Ov23liwee215tDMs9u4L)
which supports device flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tauri invoke errors are strings (not Error instances). The new test
verifies the frontend displays the actual backend error message.
Also tests that the login button is disabled during the OAuth flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The GitHub App (Ov23liCuBz7Z5hKk6T8c) does not have Device
authorization flow enabled — GitHub returns 404. The error message
now includes specific setup steps so the user can fix the
configuration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The frontend was swallowing Tauri backend errors (which are strings,
not Error instances) and always showing generic "Failed to start login."
Now the actual error message is displayed. Also adds User-Agent header
to device flow requests, a clear error message for 404 responses, and
a test for the 404 case.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Drag cancel (dragEnd without drop) does not trigger reorder
- Drag from last tab toward first produces correct reorder
- Active tab path is preserved after reorder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handleDrop and handleDragOver closed over dragIndex/dropIndex state
values. When the last dragover and drop events fire in the same React
render cycle, the drop handler reads stale state (often null), causing
computeDropTarget to return null and skip the reorder.
Mirror dragIndex/dropIndex into refs that are updated synchronously
alongside setState. Event handlers now read from refs, ensuring they
always see the latest values regardless of React's batching schedule.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
window.prompt() doesn't work in Tauri WebView on macOS — silently returns null.
Removed handleCreateNewVault + create_vault_dir Tauri command entirely.
Open Local Folder is equivalent; Finder lets users create folders inline.
Also removes tauriCall helper (was only used by handleCreateNewVault).
573 frontend tests pass.
The save-regression cherry-pick (e51bafd) re-introduced the pre-refactor
vault.rs alongside vault/mod.rs, causing E0761 (ambiguous module) on CI.
vault/mod.rs (from PR #21 refactor-vault-rs) is the correct implementation
and already exports all needed functions (save_note_content, get_note_content,
purge_trash, etc.). Removing the duplicate vault.rs resolves the CI failure.
The tab-swap useEffect in Editor.tsx watched [activeTabPath, tabs, editor].
When save updated tabs via setTabs, the effect re-ran and re-applied stale
cached blocks from the initial tab load, visually reverting the editor.
Subsequent edits would then save old content, effectively losing changes.
Fix: skip the block swap when activeTabPath hasn't changed (only tab content
was updated). Also refresh the cache with current editor blocks so a later
tab switch doesn't revert to stale content.
Added regression tests for the save → update round-trip (JS + Rust).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The useImageDrop hook's handleDrop was uploading files and inserting
image blocks, but BlockNote already has a native dropFile ProseMirror
plugin that does the same via editor.uploadFile — causing duplicate
uploads and image block insertions on every drop.
Fix: handleDrop now only resets the visual overlay state. BlockNote's
native handler handles the actual upload (which calls uploadImageFile)
and block insertion with proper drop-position support.
Also fix build: exclude test files from tsconfig.app.json (test files
use vi.Mock types from vitest, not available in the app build config).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover: has_legacy_is_a, extract_is_a_value, migrate_file_is_a_to_type,
and migrate_is_a_to_type (public function). migration.rs was at 0% coverage.
E2E tests cover:
- Drag & drop image into editor inserts image block with data URL
- Drop zone overlay appears during image drag
- Non-image file drops are correctly ignored
Design file shows three states: idle, drag-over (dashed border +
overlay label), and after-drop (image block inserted inline).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract uploadImageFile to shared hook, add useImageDrop hook that
handles dragover/dragleave/drop events on the editor container.
Drops image files (jpg, png, gif, webp), uploads them via the existing
save_image flow, and inserts BlockNote image blocks at the drop position.
Visual feedback via drop overlay and dashed border.
Refactored Editor.tsx uploadFile to use the shared function.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>