From 58f2fed621607198f0c3a0a97514087296122276 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 15 Oct 2026 16:17:52 +0200 Subject: [PATCH] fix(markdown): keep the renderer tree stable while images resolve --- packages/app/src/components/markdown/frame.tsx | 18 ++++++++++++++++++ .../src/components/markdown/renderer.test.ts | 17 +++++++++++++++++ .../app/src/components/markdown/renderer.tsx | 9 ++------- 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 packages/app/src/components/markdown/frame.tsx diff --git a/packages/app/src/components/markdown/frame.tsx b/packages/app/src/components/markdown/frame.tsx new file mode 100644 index 0000000000..86e8a4df13 --- /dev/null +++ b/packages/app/src/components/markdown/frame.tsx @@ -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; + children: ReactElement; +}) { + return ( + + {children} + + ); +} diff --git a/packages/app/src/components/markdown/renderer.test.ts b/packages/app/src/components/markdown/renderer.test.ts index 2ccf9c88ea..e673a5881c 100644 --- a/packages/app/src/components/markdown/renderer.test.ts +++ b/packages/app/src/components/markdown/renderer.test.ts @@ -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`\*literal\* <img>`; expect(prepareMarkdownSource(source).render()).toBe("`" + String.raw`\*literal\* ` + "`"); }); + +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" }), + }); +}); diff --git a/packages/app/src/components/markdown/renderer.tsx b/packages/app/src/components/markdown/renderer.tsx index 485b753c91..5852be4877 100644 --- a/packages/app/src/components/markdown/renderer.tsx +++ b/packages/app/src/components/markdown/renderer.tsx @@ -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 ? : null} ); - return isWeb && Object.keys(imageSources).length > 0 ? ( - {content} - ) : ( - content - ); + return {content}; });