Skip to content

Commit 03e0a74

Browse files
nperez0111claude
andcommitted
fix(export): skip placeholder text for empty file blocks (#434)
Empty image/video/audio/file blocks (no url set) previously leaked their "Add image"/"Add video"/etc. UI text into HTML and markdown exports. Each block's toExternalHTML now emits the bare element (<img>, <video>, <audio>, <embed>) with no src, so the placeholder text is gone from exports while the block still round-trips back to an empty block on re-import via the existing tag-name parsers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b0d0985 commit 03e0a74

14 files changed

Lines changed: 26 additions & 32 deletions

File tree

packages/core/src/api/exporters/markdown/htmlToMarkdown.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ function serializeNode(node: Node, ctx: SerializeContext): string {
8484
return serializeVideo(el, ctx);
8585
case "audio":
8686
return serializeAudio(el, ctx);
87+
case "embed":
88+
return serializeEmbed(el, ctx);
8789
case "figure":
8890
return serializeFigure(el, ctx);
8991
case "a":
@@ -501,29 +503,33 @@ function formatSeparatorRow(colWidths: number[], colCount: number): string {
501503
function serializeImage(el: HTMLElement, ctx: SerializeContext): string {
502504
const src = el.getAttribute("src") || "";
503505
const alt = el.getAttribute("alt") || "";
504-
if (!src) {
505-
return ctx.indent + "Add image\n\n";
506-
}
506+
// Empty placeholder — preserve the block-level break, matching how
507+
// serializeParagraph/serializeHeading emit `\n\n` for empty content.
508+
if (!src) {return "\n\n";}
507509
return ctx.indent + `![${alt}](${src})\n\n`;
508510
}
509511

510512
function serializeVideo(el: HTMLElement, ctx: SerializeContext): string {
511513
const src =
512514
el.getAttribute("src") || el.getAttribute("data-url") || "";
513515
const name = el.getAttribute("data-name") || el.getAttribute("title") || "";
514-
if (!src) {
515-
return ctx.indent + "Add video\n\n";
516-
}
516+
if (!src) {return "\n\n";}
517517
return ctx.indent + `![${name}](${src})\n\n`;
518518
}
519519

520520
function serializeAudio(el: HTMLElement, ctx: SerializeContext): string {
521521
const src = el.getAttribute("src") || "";
522-
if (!src) {return "";}
522+
if (!src) {return "\n\n";}
523523
// Audio has no visible representation in markdown; output as link with empty text
524524
return ctx.indent + `[](${src})\n\n`;
525525
}
526526

527+
function serializeEmbed(el: HTMLElement, ctx: SerializeContext): string {
528+
const src = el.getAttribute("src") || "";
529+
if (!src) {return "\n\n";}
530+
return ctx.indent + `[](${src})\n\n`;
531+
}
532+
527533
function serializeFigure(el: HTMLElement, ctx: SerializeContext): string {
528534
let result = "";
529535

packages/core/src/blocks/Audio/block.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,8 @@ export const audioToExternalHTML =
129129
>,
130130
) => {
131131
if (!block.props.url) {
132-
const div = document.createElement("p");
133-
div.textContent = "Add audio";
134-
135132
return {
136-
dom: div,
133+
dom: document.createElement("audio"),
137134
};
138135
}
139136

packages/core/src/blocks/File/block.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,8 @@ export const createFileBlockSpec = createBlockSpec(createFileBlockConfig, {
7575
},
7676
toExternalHTML(block) {
7777
if (!block.props.url) {
78-
const div = document.createElement("p");
79-
div.textContent = "Add file";
80-
8178
return {
82-
dom: div,
79+
dom: document.createElement("embed"),
8380
};
8481
}
8582

packages/core/src/blocks/Image/block.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,8 @@ export const imageToExternalHTML =
141141
>,
142142
) => {
143143
if (!block.props.url) {
144-
const div = document.createElement("p");
145-
div.textContent = "Add image";
146-
147144
return {
148-
dom: div,
145+
dom: document.createElement("img"),
149146
};
150147
}
151148

packages/core/src/blocks/Video/block.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,8 @@ export const createVideoBlockSpec = createBlockSpec(
105105
},
106106
toExternalHTML(block) {
107107
if (!block.props.url) {
108-
const div = document.createElement("p");
109-
div.textContent = "Add video";
110-
111108
return {
112-
dom: div,
109+
dom: document.createElement("video"),
113110
};
114111
}
115112

tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocks.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ <h1>Heading 1</h1>
4646
</td>
4747
</tr>
4848
</table>
49-
<p>Add image</p>
49+
<img />
5050
<hr />
5151
<p>Paragraph 2</p>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<p>Add audio</p>
1+
<audio></audio>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<p>Add file</p>
1+
<embed />
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<p>Add image</p>
1+
<img />
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Add audio
1+

0 commit comments

Comments
 (0)