F3.7 — slash command /image:
- DEFAULT_SLASH_COMMANDS gains an 'image' entry: triggers a window
prompt for the URL (and optional alt text), then dispatches
insertImage. The native prompt is MVP — consumers wanting a custom
dialog can hide the slash entry via slashCommands override and call
applyWordsCommand({ type: 'insertImage', src }) from their own UI.
- WordsSlashCommandId union + WordsCommandName union extended with
'image' / 'insert-image' respectively.
- slashCommandToWordsCommand returns undefined for 'image' so the
prompt path in commitSlashCommand owns it.
F3.8 — paste/drop with onUploadImage callback:
- New WordsProps.onUploadImage prop: (file: File) => Promise<{url, alt?}>.
Consumer-injected upload pipeline (S3, R2, own backend, etc.).
- onpaste detects image files in clipboardData.files. If present + the
callback is wired, intercepts the paste (drops text/html processing)
and inserts each image with a blob: URL + status='pending'.
- New ondragover + ondrop handlers gate on dataTransferHasImageFiles
and the callback being set; trigger insertImageFiles on drop.
- insertImageFiles inserts pending placeholders, awaits the upload
promise for each, then either swaps src for the final URL (success)
or flips status='error' (rejection). Matches blocks by blob URL
(unique) so concurrent uploads + concurrent edits stay coherent.
- replaceImageBlock / markImageBlockError use mapImageBlocksBySrc to
rewrite the document via replaceDocument command. No new command
type needed.
- Helpers added: collectImageFiles, dataTransferHasImageFiles,
mapImageBlocksBySrc — all pure functions at module scope.
Test fixture: wordsOpts gains the onUploadImage state slot so the 16
existing provider tests typecheck without behavior change.
152/152 tests pass in the full words soma scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>