fix: disable Milkdown ImageBlock so paste never strands an upload node

Routing pasted images to the attachment strip wasn't enough — the editor's
ImageBlock feature still handled the paste itself and left a stuck "upload
in progress" node, since there's no public upload URL for it to resolve to.

Turn the feature off entirely. It's the root cause: it's the only thing
that registers paste/drop-to-upload. Image *links* still render through the
base commonmark schema (a plain <img>), and pasted image files continue to
go to the attachment strip via use-file-input. Drop the now-dead image-block
upload/chrome CSS and keep a simple inline-image style.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QtNQeBpkDJPC9obiB25pV6
This commit is contained in:
Claude
2026-06-21 15:42:39 +00:00
parent b3084fd515
commit 3a7d9a7c31
2 changed files with 12 additions and 31 deletions
@@ -110,26 +110,12 @@
margin-top: 0; margin-top: 0;
} }
/* Images come from URLs only (no stable public upload URL), so hide the /* Image *links* render through the base commonmark schema (the ImageBlock
* file uploader — the placeholder then just prompts for a link. */ * feature is off — see markdown-editor.tsx). Keep them within the content
.llink-crepe * column and softly rounded. */
.milkdown .llink-crepe .milkdown .ProseMirror img {
:is(.milkdown-image-block, .milkdown-image-inline) max-width: 100%;
.placeholder max-height: 420px;
.uploader {
display: none;
}
/* Read-only renders the image node view with inert editing chrome — hide it. */
.llink-crepe:not(.llink-crepe--fill) .milkdown .milkdown-image-block .operation,
.llink-crepe:not(.llink-crepe--fill)
.milkdown
.milkdown-image-block
.image-resize-handle {
display: none;
}
.llink-crepe .milkdown .milkdown-image-block img {
border-radius: 8px; border-radius: 8px;
} }
@@ -57,23 +57,18 @@ export function MarkdownEditor({
[Crepe.Feature.BlockEdit]: !readOnly, [Crepe.Feature.BlockEdit]: !readOnly,
[Crepe.Feature.Toolbar]: !readOnly, [Crepe.Feature.Toolbar]: !readOnly,
[Crepe.Feature.Placeholder]: !readOnly, [Crepe.Feature.Placeholder]: !readOnly,
[Crepe.Feature.ImageBlock]: true, // Off on purpose: file uploads aren't supported, so this feature's
// paste/drop handler would only strand an "upload in progress" node in
// the editor. Image *links* still render through the base commonmark
// schema, and pasted image files are routed to the compose attachment
// strip (see use-file-input).
[Crepe.Feature.ImageBlock]: false,
[Crepe.Feature.Latex]: false, [Crepe.Feature.Latex]: false,
[Crepe.Feature.TopBar]: false, [Crepe.Feature.TopBar]: false,
[Crepe.Feature.AI]: false, [Crepe.Feature.AI]: false,
}, },
featureConfigs: { featureConfigs: {
[Crepe.Feature.Placeholder]: { text: placeholder ?? '' }, [Crepe.Feature.Placeholder]: { text: placeholder ?? '' },
// Images come from URLs only (e.g. pasted markdown) — there is no
// stable public upload URL, so the file uploader is hidden in CSS and
// onUpload rejects in case a file ever reaches it anyway (the default
// would serialize an ephemeral blob: URL into the message).
[Crepe.Feature.ImageBlock]: {
blockUploadPlaceholderText: 'Paste an image link…',
inlineUploadPlaceholderText: 'paste an image link',
maxHeight: 420,
onUpload: () => Promise.reject(new Error('Image uploads disabled')),
},
}, },
}); });