refactor: split NoteEditor into focused components + consolidate contexts

Phase 1: NoteEditor Split (64KB → 9 focused components)
- components/note-editor/: types.ts, context, toolbar, title-block,
  content-area, metadata-section, full-page, dialog compositions
- Maintains backwards compatibility via re-export from note-editor.tsx

Phase 2: Context Consolidation (5 → 3 contexts)
- NotebooksContext absorbs LabelContext (labels CRUD)
- EditorUIContext merges HomeViewContext + NotebookDragContext
- Removed: LabelContext, home-view-context, notebook-drag-context

Phase 3: React Query Infrastructure
- Added QueryProvider with @tanstack/react-query
- lib/query-keys.ts: centralized query key definitions
- lib/query-hooks.ts: useNotes, useNotebooksQuery, useLabelsQuery
- lib/use-refresh.ts: hybrid invalidateQueries + triggerRefresh helper
- NotebooksContext: invalidateQueries on mutations (with triggerRefresh fallback)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Antigravity
2026-05-08 14:31:08 +00:00
parent a58610003d
commit 91b1201112
35 changed files with 2606 additions and 344 deletions

View File

@@ -37,6 +37,7 @@ const NOTE_LIST_SELECT = {
checkItems: true,
labels: true,
images: true,
illustrationSvg: true,
links: true,
reminder: true,
isReminderDone: true,
@@ -789,6 +790,7 @@ export async function updateNote(id: string, data: {
checkItems?: CheckItem[] | null
labels?: string[] | null
images?: string[] | null
illustrationSvg?: string | null
links?: any[] | null
reminder?: Date | null
isMarkdown?: boolean
@@ -844,6 +846,7 @@ export async function updateNote(id: string, data: {
// labels handled by syncNoteLabels below
delete updateData.labels
if ('images' in data) updateData.images = data.images ? JSON.stringify(data.images) : null
if ('illustrationSvg' in data) updateData.illustrationSvg = data.illustrationSvg
if ('links' in data) updateData.links = data.links ? JSON.stringify(data.links) : null
if ('notebookId' in data) updateData.notebookId = data.notebookId
// Explicitly handle size to ensure it propagates
@@ -852,7 +855,7 @@ export async function updateNote(id: string, data: {
// Only update contentUpdatedAt for actual content changes, NOT for property changes
// (size, color, isPinned, isArchived are properties, not content)
// skipContentTimestamp=true is used by the inline editor to avoid bumping "Récent" on every auto-save
const contentFields = ['title', 'content', 'checkItems', 'images', 'links']
const contentFields = ['title', 'content', 'checkItems', 'images', 'links', 'illustrationSvg']
const isContentChange = contentFields.some(field => field in data)
if (isContentChange && !options?.skipContentTimestamp) {
updateData.contentUpdatedAt = new Date()