You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
When users paste HTML containing inline data: URI images, the current behavior (post-#5897) strips them entirely. This task instead converts each data: URI on paste into an uploaded checksum-backed image via imageProcessor.processFile, matching the shape of every other image in the editor.
Complexity: Medium Target branch: hotfixes
Context
All other images in the editor are checksum-backed via imageProcessor.processFile (toolbar insert, drag-and-drop). This task adds paste as a third entry point.
Paste is sync (ProseMirror's transformPastedHTML); uploads are async. Needs a placeholder-and-resolve pattern.
The Change
Currently transformPastedHTML strips every <img>. Replace with scheme-based routing:
data: URIs: keep the node, marked as pending.
All other schemes: continue to strip.
An async resolver walks the doc post-paste, decodes each pending data: URI to a File, runs imageProcessor.processFile, and on success swaps the node's attributes: src to the returned storageUrl, permanentSrc to ${checksum}.${file_format}, plus width/height from the returned metadata. On failure, remove the pending node silently.
To keep pending state out of saved markdown, getMarkdown() returns null whenever any image carries the pending marker. Both TipTapEditor.vue watchers short-circuit on null. The serializer's case 'image' branch also returns '' for pending nodes as defense in depth.
How to Get There
Open an exercise's TipTap editor.
Paste HTML containing an inline <img src="data:image/png;base64,..."> (e.g. from a tool that inlines images, or a hand-crafted snippet).
imageProcessor.processFile at frontend/shared/views/TipTapEditor/TipTapEditor/services/imageService.js — the existing upload flow used by toolbar insert and drag-and-drop.
AI usage
Used Claude (Opus 4.7) to draft this issue from the spec written during #5897's brainstorming. I reviewed each section and adjusted scope and phrasing.
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
When users paste HTML containing inline
data:URI images, the current behavior (post-#5897) strips them entirely. This task instead converts eachdata:URI on paste into an uploaded checksum-backed image viaimageProcessor.processFile, matching the shape of every other image in the editor.Complexity: Medium
Target branch: hotfixes
Context
imageProcessor.processFile(toolbar insert, drag-and-drop). This task adds paste as a third entry point.transformPastedHTML); uploads are async. Needs a placeholder-and-resolve pattern.The Change
Currently
transformPastedHTMLstrips every<img>. Replace with scheme-based routing:data:URIs: keep the node, marked as pending.An async resolver walks the doc post-paste, decodes each pending
data:URI to aFile, runsimageProcessor.processFile, and on success swaps the node's attributes:srcto the returnedstorageUrl,permanentSrcto${checksum}.${file_format}, pluswidth/heightfrom the returned metadata. On failure, remove the pending node silently.To keep pending state out of saved markdown,
getMarkdown()returnsnullwhenever any image carries the pending marker. BothTipTapEditor.vuewatchers short-circuit onnull. The serializer'scase 'image'branch also returns''for pending nodes as defense in depth.How to Get There
<img src="data:image/png;base64,...">(e.g. from a tool that inlines images, or a hand-crafted snippet).Out of Scope
Acceptance Criteria
General
transformPastedHTMLkeepsdata:URI imgs (with a pending marker); strips all other schemes.imageextension gainspendingIdandpendingSrcattrs.pendingId(de-duped), decodespendingSrc→File, callsimageProcessor.processFile.src← storageUrl,permanentSrc←${checksum}.${file_format},width/heightfrom metadata, pending attrs cleared.getMarkdown()returnsnullwhile any image carriespendingId.TipTapEditor.vuewatchers short-circuit onnullfromgetMarkdown().case 'image'returns''for pending nodes (defense in depth).Testing
References
data:-URI strip branch with upload-and-keep).imageProcessor.processFileatfrontend/shared/views/TipTapEditor/TipTapEditor/services/imageService.js— the existing upload flow used by toolbar insert and drag-and-drop.AI usage
Used Claude (Opus 4.7) to draft this issue from the spec written during #5897's brainstorming. I reviewed each section and adjusted scope and phrasing.