fix(markdown): keep the renderer tree stable while images resolve

This commit is contained in:
Mohamed Boudra
2026-10-15 16:17:52 +02:00
parent 8e1dd51ff0
commit 58f2fed621
3 changed files with 37 additions and 7 deletions
@@ -0,0 +1,18 @@
import React, { type ReactElement } from "react";
import { View } from "react-native";
import { markdownCopyImageSourcesDataSet } from "@/assistant-selection-copy/markup";
import { isWeb } from "@/constants/platform";
export function MarkdownRendererFrame({
imageSources,
children,
}: {
imageSources: Record<string, string>;
children: ReactElement;
}) {
return (
<View dataSet={isWeb ? markdownCopyImageSourcesDataSet(imageSources) : undefined}>
{children}
</View>
);
}
@@ -1,3 +1,5 @@
import { createElement } from "react";
import { MarkdownRendererFrame } from "./frame";
import { describe, expect, it } from "vitest";
import { darkTheme, lightTheme } from "@/styles/theme";
import { createMarkdownStyles } from "./styles";
@@ -248,3 +250,18 @@ it("preserves backslashes and decodes entities inside HTML code", () => {
const source = String.raw`<code>\*literal\* &lt;img&gt;</code>`;
expect(prepareMarkdownSource(source).render()).toBe("`" + String.raw`\*literal\* <img>` + "`");
});
it("keeps the renderer root and child identity as workspace images resolve", () => {
const child = createElement("span", null, "Selected prose and open diagram");
const pending = MarkdownRendererFrame({ imageSources: {}, children: child });
const resolved = MarkdownRendererFrame({
imageSources: { "blob:resolved": "/repo/image.png" },
children: child,
});
expect(resolved.type).toBe(pending.type);
expect(resolved.props.children).toBe(pending.props.children);
expect(resolved.props.children).toBe(child);
expect(resolved.props.dataSet).toEqual({
paseoMarkdownImageSources: JSON.stringify({ "blob:resolved": "/repo/image.png" }),
});
});
@@ -1,5 +1,3 @@
import { View } from "react-native";
import { markdownCopyImageSourcesDataSet } from "@/assistant-selection-copy/markup";
import { memo, useMemo, useState } from "react";
import { EnrichedMarkdownText, type LinkPressEvent } from "react-native-enriched-markdown";
import { withUnistyles } from "react-native-unistyles";
@@ -11,6 +9,7 @@ import { useStableEvent } from "@/hooks/use-stable-event";
import { AttachmentLightbox, type ImageLightboxSource } from "@/components/attachment-lightbox";
import type { Theme } from "@/styles/theme";
import { isWeb } from "@/constants/platform";
import { MarkdownRendererFrame } from "./frame";
import { WEB_RENDERERS } from "./renderers";
import { createMarkdownStyles } from "./styles";
import { useMarkdownSource, type MarkdownImageContext } from "./images";
@@ -95,9 +94,5 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({
{image ? <AttachmentLightbox source={image} onClose={closeImage} /> : null}
</MermaidStreamingContext.Provider>
);
return isWeb && Object.keys(imageSources).length > 0 ? (
<View dataSet={markdownCopyImageSourcesDataSet(imageSources)}>{content}</View>
) : (
content
);
return <MarkdownRendererFrame imageSources={imageSources}>{content}</MarkdownRendererFrame>;
});