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-22 17:48:39 +01:00
|
|
|
import { useCallback, useRef } from 'react'
|
2026-02-17 12:10:21 +01:00
|
|
|
import { invoke } from '@tauri-apps/api/core'
|
|
|
|
|
import { isTauri, mockInvoke, addMockEntry, updateMockContent } from '../mock-tauri'
|
|
|
|
|
import type { VaultEntry } from '../types'
|
|
|
|
|
import type { FrontmatterValue } from '../components/Inspector'
|
2026-02-22 10:11:52 +01:00
|
|
|
import { useTabManagement } from './useTabManagement'
|
|
|
|
|
import { updateMockFrontmatter, deleteMockFrontmatterProperty } from './mockFrontmatterHelpers'
|
|
|
|
|
|
|
|
|
|
interface NewEntryParams {
|
|
|
|
|
path: string
|
|
|
|
|
slug: string
|
|
|
|
|
title: string
|
|
|
|
|
type: string
|
|
|
|
|
status: string | null
|
2026-02-17 12:10:21 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:22:44 +01:00
|
|
|
interface RenameResult {
|
|
|
|
|
new_path: string
|
|
|
|
|
updated_files: number
|
|
|
|
|
}
|
|
|
|
|
|
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-22 17:48:39 +01:00
|
|
|
export interface NoteActionsConfig {
|
|
|
|
|
addEntry: (entry: VaultEntry, content: string) => void
|
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 16:11:59 +01:00
|
|
|
removeEntry: (path: string) => void
|
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-22 17:48:39 +01:00
|
|
|
updateContent: (path: string, content: string) => void
|
|
|
|
|
entries: VaultEntry[]
|
|
|
|
|
setToastMessage: (msg: string | null) => void
|
|
|
|
|
updateEntry: (path: string, patch: Partial<VaultEntry>) => void
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:22:44 +01:00
|
|
|
async function performRename(
|
|
|
|
|
path: string,
|
|
|
|
|
newTitle: string,
|
|
|
|
|
vaultPath: string,
|
|
|
|
|
): Promise<RenameResult> {
|
|
|
|
|
if (isTauri()) {
|
|
|
|
|
return invoke<RenameResult>('rename_note', { vaultPath, oldPath: path, newTitle })
|
|
|
|
|
}
|
|
|
|
|
return mockInvoke<RenameResult>('rename_note', { vault_path: vaultPath, old_path: path, new_title: newTitle })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildRenamedEntry(entry: VaultEntry, newTitle: string, newPath: string): VaultEntry {
|
|
|
|
|
const slug = newTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '')
|
|
|
|
|
return { ...entry, path: newPath, filename: `${slug}.md`, title: newTitle }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 12:10:17 +01:00
|
|
|
async function loadNoteContent(path: string): Promise<string> {
|
|
|
|
|
return isTauri()
|
|
|
|
|
? invoke<string>('get_note_content', { path })
|
|
|
|
|
: mockInvoke<string>('get_note_content', { path })
|
2026-02-17 12:10:21 +01:00
|
|
|
}
|
|
|
|
|
|
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 16:11:59 +01:00
|
|
|
/** Persist a newly created note to disk. Returns a Promise for error handling. */
|
|
|
|
|
function persistNewNote(path: string, content: string): Promise<void> {
|
|
|
|
|
if (!isTauri()) return Promise.resolve()
|
|
|
|
|
return invoke<void>('save_note_content', { path, content }).then(() => {})
|
2026-02-24 23:03:01 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function buildNewEntry({ path, slug, title, type, status }: NewEntryParams): VaultEntry {
|
2026-02-22 10:11:52 +01:00
|
|
|
const now = Math.floor(Date.now() / 1000)
|
|
|
|
|
return {
|
|
|
|
|
path, filename: `${slug}.md`, title, isA: type,
|
|
|
|
|
aliases: [], belongsTo: [], relatedTo: [],
|
|
|
|
|
status, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null,
|
|
|
|
|
modifiedAt: now, createdAt: now, fileSize: 0,
|
2026-02-25 15:04:49 +01:00
|
|
|
snippet: '', relationships: {}, icon: null, color: null, order: null, outgoingLinks: [],
|
2026-02-17 12:10:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function slugify(text: string): string {
|
2026-02-22 10:11:52 +01:00
|
|
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '')
|
|
|
|
|
}
|
2026-02-17 12:10:21 +01:00
|
|
|
|
2026-02-24 23:03:01 +01:00
|
|
|
/** Generate a unique "Untitled <type>" name by checking existing entries and pending names. */
|
|
|
|
|
export function generateUntitledName(entries: VaultEntry[], type: string, pending?: Set<string>): string {
|
2026-02-21 19:15:39 +01:00
|
|
|
const baseName = `Untitled ${type.toLowerCase()}`
|
|
|
|
|
const existingTitles = new Set(entries.map(e => e.title))
|
2026-02-24 23:03:01 +01:00
|
|
|
if (pending) pending.forEach(n => existingTitles.add(n))
|
2026-02-21 19:15:39 +01:00
|
|
|
let title = baseName
|
|
|
|
|
let counter = 2
|
|
|
|
|
while (existingTitles.has(title)) {
|
|
|
|
|
title = `${baseName} ${counter}`
|
|
|
|
|
counter++
|
|
|
|
|
}
|
|
|
|
|
return title
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function entryMatchesTarget(e: VaultEntry, targetLower: string, targetAsWords: string): boolean {
|
2026-02-22 10:11:52 +01:00
|
|
|
if (e.title.toLowerCase() === targetLower) return true
|
|
|
|
|
if (e.aliases.some((a) => a.toLowerCase() === targetLower)) return true
|
|
|
|
|
const pathStem = e.path.replace(/^.*\/Laputa\//, '').replace(/\.md$/, '')
|
|
|
|
|
if (pathStem.toLowerCase() === targetLower) return true
|
|
|
|
|
const fileStem = e.filename.replace(/\.md$/, '')
|
|
|
|
|
if (fileStem.toLowerCase() === targetLower.split('/').pop()) return true
|
|
|
|
|
return e.title.toLowerCase() === targetAsWords
|
2026-02-17 12:10:21 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
async function invokeFrontmatter(command: string, args: Record<string, unknown>): Promise<string> {
|
|
|
|
|
return invoke<string>(command, args)
|
|
|
|
|
}
|
2026-02-21 17:24:01 +01:00
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
function applyMockFrontmatterUpdate(path: string, key: string, value: FrontmatterValue): string {
|
|
|
|
|
const content = updateMockFrontmatter(path, key, value)
|
|
|
|
|
updateMockContent(path, content)
|
|
|
|
|
return content
|
2026-02-21 17:24:01 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
function applyMockFrontmatterDelete(path: string, key: string): string {
|
|
|
|
|
const content = deleteMockFrontmatterProperty(path, key)
|
|
|
|
|
updateMockContent(path, content)
|
|
|
|
|
return content
|
2026-02-21 17:24:01 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
const TYPE_FOLDER_MAP: Record<string, string> = {
|
|
|
|
|
Note: 'note', Project: 'project', Experiment: 'experiment',
|
|
|
|
|
Responsibility: 'responsibility', Procedure: 'procedure',
|
|
|
|
|
Person: 'person', Event: 'event', Topic: 'topic',
|
2026-02-21 10:50:50 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
const NO_STATUS_TYPES = new Set(['Topic', 'Person'])
|
|
|
|
|
|
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-22 17:48:39 +01:00
|
|
|
const ENTRY_DELETE_MAP: Record<string, Partial<VaultEntry>> = {
|
2026-02-23 15:24:57 +01:00
|
|
|
type: { isA: null }, is_a: { isA: null }, status: { status: null }, color: { color: null },
|
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-22 17:48:39 +01:00
|
|
|
icon: { icon: null }, owner: { owner: null }, cadence: { cadence: null },
|
|
|
|
|
aliases: { aliases: [] }, belongs_to: { belongsTo: [] }, related_to: { relatedTo: [] },
|
|
|
|
|
archived: { archived: false }, trashed: { trashed: false }, order: { order: null },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Map a frontmatter key+value to the corresponding VaultEntry field(s). */
|
|
|
|
|
export function frontmatterToEntryPatch(
|
|
|
|
|
op: 'update' | 'delete', key: string, value?: FrontmatterValue,
|
|
|
|
|
): Partial<VaultEntry> {
|
|
|
|
|
const k = key.toLowerCase().replace(/\s+/g, '_')
|
|
|
|
|
if (op === 'delete') return ENTRY_DELETE_MAP[k] ?? {}
|
|
|
|
|
const str = value != null ? String(value) : null
|
|
|
|
|
const arr = Array.isArray(value) ? value.map(String) : []
|
|
|
|
|
const updates: Record<string, Partial<VaultEntry>> = {
|
2026-02-23 15:24:57 +01:00
|
|
|
type: { isA: str }, is_a: { isA: str }, status: { status: str }, color: { color: str },
|
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-22 17:48:39 +01:00
|
|
|
icon: { icon: str }, owner: { owner: str }, cadence: { cadence: str },
|
|
|
|
|
aliases: { aliases: arr }, belongs_to: { belongsTo: arr }, related_to: { relatedTo: arr },
|
|
|
|
|
archived: { archived: Boolean(value) }, trashed: { trashed: Boolean(value) },
|
|
|
|
|
order: { order: typeof value === 'number' ? value : null },
|
|
|
|
|
}
|
|
|
|
|
return updates[k] ?? {}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
function addEntryWithMock(entry: VaultEntry, content: string, addEntry: (e: VaultEntry, c: string) => void) {
|
|
|
|
|
if (!isTauri()) addMockEntry(entry, content)
|
|
|
|
|
addEntry(entry, content)
|
2026-02-21 10:50:50 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function buildNoteContent(title: string, type: string, status: string | null): string {
|
2026-02-23 15:24:57 +01:00
|
|
|
const lines = ['---', `title: ${title}`, `type: ${type}`]
|
2026-02-22 10:55:45 +01:00
|
|
|
if (status) lines.push(`status: ${status}`)
|
|
|
|
|
lines.push('---')
|
|
|
|
|
return `${lines.join('\n')}\n\n# ${title}\n\n`
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function resolveNewNote(title: string, type: string): { entry: VaultEntry; content: string } {
|
2026-02-22 10:55:45 +01:00
|
|
|
const folder = TYPE_FOLDER_MAP[type] || slugify(type)
|
|
|
|
|
const slug = slugify(title)
|
|
|
|
|
const status = NO_STATUS_TYPES.has(type) ? null : 'Active'
|
|
|
|
|
const entry = buildNewEntry({ path: `/Users/luca/Laputa/${folder}/${slug}.md`, slug, title, type, status })
|
|
|
|
|
return { entry, content: buildNoteContent(title, type, status) }
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export function resolveNewType(typeName: string): { entry: VaultEntry; content: string } {
|
2026-02-22 10:55:45 +01:00
|
|
|
const slug = slugify(typeName)
|
|
|
|
|
const entry = buildNewEntry({ path: `/Users/luca/Laputa/type/${slug}.md`, slug, title: typeName, type: 'Type', status: null })
|
2026-02-23 15:24:57 +01:00
|
|
|
return { entry, content: `---\ntype: Type\n---\n\n# ${typeName}\n\n` }
|
2026-02-22 10:55:45 +01:00
|
|
|
}
|
|
|
|
|
|
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-22 17:48:39 +01:00
|
|
|
function findWikilinkTarget(entries: VaultEntry[], target: string): VaultEntry | undefined {
|
|
|
|
|
const targetLower = target.toLowerCase()
|
|
|
|
|
const targetAsWords = target.split('/').pop()?.replace(/-/g, ' ').toLowerCase() ?? targetLower
|
|
|
|
|
return entries.find((e) => entryMatchesTarget(e, targetLower, targetAsWords))
|
|
|
|
|
}
|
|
|
|
|
|
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 16:11:59 +01:00
|
|
|
/** Navigate to a wikilink target, logging a warning if not found. */
|
|
|
|
|
function navigateWikilink(entries: VaultEntry[], target: string, selectNote: (e: VaultEntry) => void): void {
|
|
|
|
|
const found = findWikilinkTarget(entries, target)
|
|
|
|
|
if (found) selectNote(found)
|
|
|
|
|
else console.warn(`Navigation target not found: ${target}`)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 00:03:38 +01:00
|
|
|
/** Dispatch focus-editor event with perf timing marker. */
|
|
|
|
|
function signalFocusEditor(): void {
|
|
|
|
|
window.dispatchEvent(new CustomEvent('laputa:focus-editor', { detail: { t0: performance.now() } }))
|
|
|
|
|
}
|
|
|
|
|
|
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 16:11:59 +01:00
|
|
|
/** Persist to disk; on failure, call the revert handler. */
|
|
|
|
|
function persistOptimistic(path: string, content: string, onFail: (p: string) => void): void {
|
|
|
|
|
persistNewNote(path, content).catch(() => onFail(path))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 11:41:46 +01:00
|
|
|
/** Optimistically open tab, add entry to vault, and persist to disk.
|
|
|
|
|
* Tab creation (setTabs/setActiveTabPath) runs at normal priority so the
|
|
|
|
|
* tab appears instantly. addEntry uses startTransition internally so the
|
|
|
|
|
* expensive entries update (NoteList re-filter/sort on 9000+ entries) is
|
|
|
|
|
* deferred and doesn't block the tab from rendering. */
|
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 16:11:59 +01:00
|
|
|
function createAndPersist(
|
|
|
|
|
resolved: { entry: VaultEntry; content: string },
|
|
|
|
|
addFn: (e: VaultEntry, c: string) => void,
|
|
|
|
|
openTab: (e: VaultEntry, c: string) => void,
|
|
|
|
|
onFail: (p: string) => void,
|
|
|
|
|
): void {
|
|
|
|
|
openTab(resolved.entry, resolved.content)
|
2026-02-26 11:41:46 +01:00
|
|
|
addEntryWithMock(resolved.entry, resolved.content, addFn)
|
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 16:11:59 +01:00
|
|
|
persistOptimistic(resolved.entry.path, resolved.content, onFail)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 10:55:45 +01:00
|
|
|
async function executeFrontmatterOp(op: 'update' | 'delete', path: string, key: string, value?: FrontmatterValue): Promise<string> {
|
|
|
|
|
if (op === 'update') {
|
|
|
|
|
return isTauri() ? invokeFrontmatter('update_frontmatter', { path, key, value }) : applyMockFrontmatterUpdate(path, key, value!)
|
|
|
|
|
}
|
|
|
|
|
return isTauri() ? invokeFrontmatter('delete_frontmatter_property', { path, key }) : applyMockFrontmatterDelete(path, key)
|
|
|
|
|
}
|
|
|
|
|
|
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-22 17:48:39 +01:00
|
|
|
function renameToastMessage(updatedFiles: number): string {
|
|
|
|
|
if (updatedFiles === 0) return 'Renamed'
|
|
|
|
|
return `Renamed — updated ${updatedFiles} wiki link${updatedFiles > 1 ? 's' : ''}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Reload content for open tabs whose wikilinks may have changed after a rename. */
|
|
|
|
|
async function reloadTabsAfterRename(
|
|
|
|
|
tabPaths: string[],
|
|
|
|
|
updateTabContent: (path: string, content: string) => void,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
for (const tabPath of tabPaths) {
|
|
|
|
|
try {
|
|
|
|
|
updateTabContent(tabPath, await loadNoteContent(tabPath))
|
|
|
|
|
} catch { /* skip tabs that fail to reload */ }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 16:11:59 +01:00
|
|
|
/** Run a frontmatter update/delete and apply the result to state. */
|
|
|
|
|
async function runFrontmatterAndApply(
|
|
|
|
|
op: 'update' | 'delete', path: string, key: string, value: FrontmatterValue | undefined,
|
|
|
|
|
callbacks: { updateTab: (p: string, c: string) => void; updateEntry: (p: string, patch: Partial<VaultEntry>) => void; toast: (m: string | null) => void },
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
callbacks.updateTab(path, await executeFrontmatterOp(op, path, key, value))
|
|
|
|
|
const patch = frontmatterToEntryPatch(op, key, value)
|
|
|
|
|
if (Object.keys(patch).length > 0) callbacks.updateEntry(path, patch)
|
|
|
|
|
callbacks.toast(op === 'update' ? 'Property updated' : 'Property deleted')
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(`Failed to ${op} frontmatter:`, err)
|
|
|
|
|
callbacks.toast(`Failed to ${op} property`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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-22 17:48:39 +01:00
|
|
|
export function useNoteActions(config: NoteActionsConfig) {
|
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 16:11:59 +01:00
|
|
|
const { addEntry, removeEntry, updateContent, entries, setToastMessage, updateEntry } = config
|
2026-02-22 10:11:52 +01:00
|
|
|
const tabMgmt = useTabManagement()
|
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 16:11:59 +01:00
|
|
|
const { setTabs, handleSelectNote, openTabWithContent, handleCloseTab, activeTabPathRef, handleSwitchTab } = tabMgmt
|
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-22 17:48:39 +01:00
|
|
|
const tabsRef = useRef(tabMgmt.tabs)
|
2026-02-23 08:53:43 +01:00
|
|
|
// eslint-disable-next-line react-hooks/refs
|
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-22 17:48:39 +01:00
|
|
|
tabsRef.current = tabMgmt.tabs
|
2026-02-17 12:10:21 +01:00
|
|
|
|
2026-02-22 10:11:52 +01:00
|
|
|
const updateTabContent = useCallback((path: string, newContent: string) => {
|
2026-02-22 10:55:45 +01:00
|
|
|
setTabs((prev) => prev.map((t) => t.entry.path === path ? { ...t, content: newContent } : t))
|
2026-02-22 10:11:52 +01:00
|
|
|
updateContent(path, newContent)
|
|
|
|
|
}, [setTabs, updateContent])
|
2026-02-17 12:10:21 +01:00
|
|
|
|
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 16:11:59 +01:00
|
|
|
const handleNavigateWikilink = useCallback(
|
|
|
|
|
(target: string) => navigateWikilink(entries, target, handleSelectNote),
|
|
|
|
|
[entries, handleSelectNote],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const revertOptimisticNote = useCallback((path: string) => {
|
|
|
|
|
handleCloseTab(path)
|
|
|
|
|
removeEntry(path)
|
|
|
|
|
setToastMessage('Failed to create note — disk write error')
|
|
|
|
|
}, [handleCloseTab, removeEntry, setToastMessage])
|
2026-02-17 12:10:21 +01:00
|
|
|
|
2026-02-24 23:03:01 +01:00
|
|
|
const pendingNamesRef = useRef<Set<string>>(new Set())
|
|
|
|
|
|
2026-02-22 10:55:45 +01:00
|
|
|
const handleCreateNote = useCallback((title: string, type: string) => {
|
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 16:11:59 +01:00
|
|
|
createAndPersist(resolveNewNote(title, type), addEntry, openTabWithContent, revertOptimisticNote)
|
|
|
|
|
}, [openTabWithContent, addEntry, revertOptimisticNote])
|
2026-02-24 23:03:01 +01:00
|
|
|
|
|
|
|
|
const handleCreateNoteImmediate = useCallback((type?: string) => {
|
|
|
|
|
const noteType = type || 'Note'
|
|
|
|
|
const title = generateUntitledName(entries, noteType, pendingNamesRef.current)
|
|
|
|
|
pendingNamesRef.current.add(title)
|
|
|
|
|
handleCreateNote(title, noteType)
|
2026-02-26 00:03:38 +01:00
|
|
|
signalFocusEditor()
|
2026-02-24 23:03:01 +01:00
|
|
|
setTimeout(() => pendingNamesRef.current.delete(title), 500)
|
|
|
|
|
}, [entries, handleCreateNote])
|
2026-02-17 12:10:21 +01:00
|
|
|
|
2026-02-22 10:55:45 +01:00
|
|
|
const handleCreateType = useCallback((typeName: string) => {
|
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 16:11:59 +01:00
|
|
|
createAndPersist(resolveNewType(typeName), addEntry, openTabWithContent, revertOptimisticNote)
|
|
|
|
|
}, [openTabWithContent, addEntry, revertOptimisticNote])
|
2026-02-21 09:41:46 +01:00
|
|
|
|
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 16:11:59 +01:00
|
|
|
const fmCallbacks = { updateTab: updateTabContent, updateEntry, toast: setToastMessage }
|
|
|
|
|
|
|
|
|
|
const runFrontmatterOp = useCallback(
|
|
|
|
|
(op: 'update' | 'delete', path: string, key: string, value?: FrontmatterValue) =>
|
|
|
|
|
runFrontmatterAndApply(op, path, key, value, fmCallbacks),
|
|
|
|
|
[updateTabContent, updateEntry, setToastMessage], // eslint-disable-line react-hooks/exhaustive-deps -- fmCallbacks is stable when deps are
|
|
|
|
|
)
|
2026-02-17 12:10:21 +01:00
|
|
|
|
2026-02-21 19:22:44 +01:00
|
|
|
const handleRenameNote = useCallback(async (
|
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 16:11:59 +01:00
|
|
|
path: string, newTitle: string, vaultPath: string,
|
2026-02-21 19:22:44 +01:00
|
|
|
onEntryRenamed: (oldPath: string, newEntry: Partial<VaultEntry> & { path: string }, newContent: string) => void,
|
|
|
|
|
) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await performRename(path, newTitle, vaultPath)
|
|
|
|
|
const newContent = await loadNoteContent(result.new_path)
|
2026-02-22 12:10:17 +01:00
|
|
|
const entry = entries.find((e) => e.path === path)
|
|
|
|
|
const newEntry = buildRenamedEntry(entry ?? {} as VaultEntry, newTitle, result.new_path)
|
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 16:11:59 +01:00
|
|
|
const otherTabPaths = tabsRef.current.filter(t => t.entry.path !== path).map(t => t.entry.path)
|
2026-02-21 19:22:44 +01:00
|
|
|
setTabs((prev) => prev.map((t) => t.entry.path === path ? { entry: newEntry, content: newContent } : t))
|
2026-02-22 12:10:17 +01:00
|
|
|
if (activeTabPathRef.current === path) handleSwitchTab(result.new_path)
|
2026-02-21 19:22:44 +01:00
|
|
|
onEntryRenamed(path, newEntry, newContent)
|
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-22 17:48:39 +01:00
|
|
|
await reloadTabsAfterRename(otherTabPaths, updateTabContent)
|
|
|
|
|
setToastMessage(renameToastMessage(result.updated_files))
|
2026-02-21 19:22:44 +01:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to rename note:', err)
|
|
|
|
|
setToastMessage('Failed to rename note')
|
|
|
|
|
}
|
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-22 17:48:39 +01:00
|
|
|
}, [entries, setTabs, activeTabPathRef, handleSwitchTab, updateTabContent, setToastMessage])
|
2026-02-21 19:22:44 +01:00
|
|
|
|
2026-02-17 12:10:21 +01:00
|
|
|
return {
|
2026-02-22 10:55:45 +01:00
|
|
|
...tabMgmt,
|
2026-02-17 12:10:21 +01:00
|
|
|
handleNavigateWikilink,
|
|
|
|
|
handleCreateNote,
|
2026-02-24 23:03:01 +01:00
|
|
|
handleCreateNoteImmediate,
|
2026-02-21 09:41:46 +01:00
|
|
|
handleCreateType,
|
2026-02-22 10:55:45 +01:00
|
|
|
handleUpdateFrontmatter: useCallback((path: string, key: string, value: FrontmatterValue) => runFrontmatterOp('update', path, key, value), [runFrontmatterOp]),
|
|
|
|
|
handleDeleteProperty: useCallback((path: string, key: string) => runFrontmatterOp('delete', path, key), [runFrontmatterOp]),
|
|
|
|
|
handleAddProperty: useCallback((path: string, key: string, value: FrontmatterValue) => runFrontmatterOp('update', path, key, value), [runFrontmatterOp]),
|
2026-02-21 19:22:44 +01:00
|
|
|
handleRenameNote,
|
2026-02-17 12:10:21 +01:00
|
|
|
}
|
|
|
|
|
}
|