fix: wire up BlockNote uploadFile to enable image uploads in editor

The editor's useCreateBlockNote was missing the uploadFile callback,
so BlockNote had no handler for image uploads (drag-drop, paste, or
toolbar button). Images now convert to data URLs for immediate display.

In Tauri mode, uploaded images are also persisted to the vault's
attachments/ directory via a new save_image backend command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-02-21 10:07:02 +01:00
parent d94236c482
commit db1da4430e
7 changed files with 146 additions and 2 deletions

View File

@@ -4,6 +4,8 @@ import { filterSuggestionItems } from '@blocknote/core/extensions'
import { createReactInlineContentSpec, useCreateBlockNote, SuggestionMenuController } from '@blocknote/react'
import { BlockNoteView } from '@blocknote/mantine'
import '@blocknote/mantine/style.css'
import { invoke } from '@tauri-apps/api/core'
import { isTauri } from '../mock-tauri'
import type { VaultEntry, GitCommit } from '../types'
import { Inspector, type FrontmatterValue } from './Inspector'
import { AIChatPanel } from './AIChatPanel'
@@ -45,6 +47,7 @@ interface EditorProps {
onAddProperty?: (path: string, key: string, value: FrontmatterValue) => Promise<void>
showAIChat?: boolean
onToggleAIChat?: () => void
vaultPath?: string
}
// --- Custom Inline Content: WikiLink ---
@@ -147,13 +150,42 @@ export const Editor = memo(function Editor({
inspectorEntry, inspectorContent, allContent, gitHistory,
onUpdateFrontmatter, onDeleteProperty, onAddProperty,
showAIChat, onToggleAIChat,
vaultPath,
}: EditorProps) {
const [diffMode, setDiffMode] = useState(false)
const [diffContent, setDiffContent] = useState<string | null>(null)
const [diffLoading, setDiffLoading] = useState(false)
// Ref for vaultPath so the uploadFile closure always sees the latest value
const vaultPathRef = useRef(vaultPath)
vaultPathRef.current = vaultPath
// Single editor instance — reused across all tabs
const editor = useCreateBlockNote({ schema })
const editor = useCreateBlockNote({
schema,
uploadFile: async (file: File) => {
const dataUrl = await new Promise<string>((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result as string)
reader.onerror = () => reject(reader.error)
reader.readAsDataURL(file)
})
// In Tauri mode, also persist the file to the vault's attachments directory
if (isTauri() && vaultPathRef.current) {
const base64 = dataUrl.split(',')[1]
if (base64) {
invoke('save_image', {
vaultPath: vaultPathRef.current,
filename: file.name,
data: base64,
}).catch(err => console.warn('Failed to save image to vault:', err))
}
}
return dataUrl
},
})
// Cache parsed blocks per tab path for instant switching
const tabCacheRef = useRef<Map<string, any[]>>(new Map())
const prevActivePathRef = useRef<string | null>(null)