From f5fb20cc55f53930472722dfcd18ee6f542e7dc3 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 15 Oct 2026 15:33:33 +0200 Subject: [PATCH] fix(markdown): preserve encoded image paths after rebase Keep the shared renderer image normalization and file-read regression coverage that originally lived in the main integration merge. --- .../components/markdown/image-source.test.ts | 56 ++++++++++++++++++- .../app/src/components/markdown/images.ts | 12 ++-- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/packages/app/src/components/markdown/image-source.test.ts b/packages/app/src/components/markdown/image-source.test.ts index 8277e57492..3605d7a13a 100644 --- a/packages/app/src/components/markdown/image-source.test.ts +++ b/packages/app/src/components/markdown/image-source.test.ts @@ -1,4 +1,9 @@ -import { describe, expect, it } from "vitest"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { prepareMarkdownSource } from "./source"; +import { resolveAssistantImageSource } from "@/utils/assistant-image-source"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { resolveMarkdownFileDirectory, resolveMarkdownPreviewImageSource } from "./image-source"; @@ -163,3 +168,52 @@ describe("resolveMarkdownPreviewImageSource", () => { ).toBe("C:/Users/test/repo/images/hero.png"); }); }); + +describe("local assistant Markdown images", () => { + let workspaceRoot: string; + const imageBytes = Buffer.from( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==", + "base64", + ); + + beforeEach(async () => { + workspaceRoot = await mkdtemp(path.join(os.tmpdir(), "assistant-markdown-image-")); + await mkdir(path.join(workspaceRoot, "My Apps", "Мои"), { recursive: true }); + await writeFile(path.join(workspaceRoot, "My Apps", "Мои", "test.png"), imageBytes); + await writeFile(path.join(workspaceRoot, "literal%20name.png"), imageBytes); + }); + + afterEach(async () => { + await rm(workspaceRoot, { recursive: true, force: true }); + }); + + it.each([ + "absolute angle", + "absolute encoded", + "relative angle", + "relative encoded", + "file URI", + "literal percent", + ])("reads the actual image for the %s destination", async (form) => { + const absolute = path.join(workspaceRoot, "My Apps", "Мои", "test.png"); + const destinations: Record = { + "absolute angle": `<${absolute}>`, + "absolute encoded": encodeURI(absolute), + "relative angle": "", + "relative encoded": encodeURI("My Apps/Мои/test.png"), + "file URI": `file://${encodeURI(absolute)}`, + "literal percent": `${workspaceRoot}/literal%2520name.png`, + }; + const prepared = prepareMarkdownSource(`![test](${destinations[form]})`); + expect(prepared.images).toHaveLength(1); + const source = resolveMarkdownPreviewImageSource({ + source: prepared.images[0]!.source, + markdownPath: "", + workspaceRoot, + })!; + const target = resolveAssistantImageSource({ source, workspaceRoot }); + expect(target?.kind).toBe("file_rpc"); + if (target?.kind !== "file_rpc") throw new Error("Expected a local image read target"); + await expect(readFile(path.resolve(target.cwd, target.path))).resolves.toEqual(imageBytes); + }); +}); diff --git a/packages/app/src/components/markdown/images.ts b/packages/app/src/components/markdown/images.ts index 26f53e8f9e..0df11589e7 100644 --- a/packages/app/src/components/markdown/images.ts +++ b/packages/app/src/components/markdown/images.ts @@ -40,13 +40,11 @@ export function useMarkdownSource(text: string, context: MarkdownImageContext) { ); const sources = [...new Set(prepared.images.map((image) => image.source))]; const acquisitions = sources.map((source) => { - const resolved = context.filePath - ? resolveMarkdownPreviewImageSource({ - source, - markdownPath: context.filePath, - workspaceRoot: context.workspaceRoot, - }) - : source; + const resolved = resolveMarkdownPreviewImageSource({ + source, + markdownPath: context.filePath ?? "", + workspaceRoot: context.workspaceRoot, + }); return createAssistantImageFileAcquisition({ port: context.client ? {