Summary
Users should be able to paste images from the clipboard directly into the editor (both visual/BlockNote mode and source/CodeMirror mode). The pasted image gets uploaded as an asset and inserted as a markdown image reference. This is a standard feature in Notion, Confluence, Obsidian, GitHub, and every modern editor.
Proposed behavior
- Paste (
Ctrl+V / Cmd+V) with image data on clipboard → upload to POST /api/kiwi/assets → insert  at cursor position.
- Drag-and-drop — same flow for images dragged into the editor area.
- File naming:
paste-<timestamp>.<ext> (e.g., paste-20260623-143022.png).
- Show a loading placeholder while uploading (e.g.,
![Uploading...]()), then replace with the real URL on success.
- Support formats: PNG, JPEG, GIF, WebP.
- Error handling: if upload fails, show a toast notification and remove the placeholder.
Implementation notes
- Visual mode (BlockNote): BlockNote already handles some paste events. Hook into the
editor.onPaste or use a ProseMirror plugin to intercept clipboardData.items with type.startsWith('image/').
- Source mode (CodeMirror): Use CodeMirror's
EditorView.domEventHandlers to handle paste and drop events.
- Upload endpoint:
POST /api/kiwi/assets (already exists, accepts multipart form data).
- Prioritize shadcn components. Use shadcn
Sonner / toast for upload progress and error notifications — install via npx shadcn@latest add sonner if not present. Alternatively use the existing toast mechanism if one exists in the codebase.
- Max file size: respect the
[assets] max_file_size config (default 10MB).
- For drag-and-drop, add a visual drop zone overlay using a
div with dashed border that appears on dragover.
Acceptance criteria
Summary
Users should be able to paste images from the clipboard directly into the editor (both visual/BlockNote mode and source/CodeMirror mode). The pasted image gets uploaded as an asset and inserted as a markdown image reference. This is a standard feature in Notion, Confluence, Obsidian, GitHub, and every modern editor.
Proposed behavior
Ctrl+V/Cmd+V) with image data on clipboard → upload toPOST /api/kiwi/assets→ insertat cursor position.paste-<timestamp>.<ext>(e.g.,paste-20260623-143022.png).![Uploading...]()), then replace with the real URL on success.Implementation notes
editor.onPasteor use a ProseMirror plugin to interceptclipboardData.itemswithtype.startsWith('image/').EditorView.domEventHandlersto handlepasteanddropevents.POST /api/kiwi/assets(already exists, accepts multipart form data).Sonner/ toast for upload progress and error notifications — install vianpx shadcn@latest add sonnerif not present. Alternatively use the existing toast mechanism if one exists in the codebase.[assets] max_file_sizeconfig (default 10MB).divwith dashed border that appears on dragover.Acceptance criteria