diff --git a/packages/session-ui/component-tests/markdown.spec.ts b/packages/session-ui/component-tests/markdown.spec.ts
index c1c1ab40609..8c120b3ad77 100644
--- a/packages/session-ui/component-tests/markdown.spec.ts
+++ b/packages/session-ui/component-tests/markdown.spec.ts
@@ -983,3 +983,63 @@ story("loads file URLs and Windows paths, and leaves unreadable images as alt te
await harness.getByLabel("Markdown text").fill("Still usable")
await expect(harness.locator('[data-component="markdown"]')).toHaveText("Still usable")
})
+
+async function copySelection(page: Page, select: "markdown" | "harness") {
+ await page.evaluate((select) => {
+ const harness = document.querySelector('[data-testid="markdown-fixture"]')!
+ const range = document.createRange()
+ range.selectNodeContents(select === "markdown" ? harness.querySelector('[data-component="markdown"]')! : harness)
+ getSelection()!.removeAllRanges()
+ getSelection()!.addRange(range)
+ document.addEventListener(
+ "copy",
+ (event) => {
+ document.body.dataset.copiedText = event.clipboardData?.getData("text/plain")
+ document.body.dataset.copiedHtml = event.clipboardData?.getData("text/html")
+ },
+ { once: true },
+ )
+ }, select)
+ await page.keyboard.press("ControlOrMeta+C")
+
+ return page.evaluate(() => ({ text: document.body.dataset.copiedText, html: document.body.dataset.copiedHtml }))
+}
+
+story("copies selected Markdown as semantic HTML without theme or highlight colours", async ({ page }) => {
+ await page.evaluate(async (fixture) => {
+ document.body.style.color = "rgb(237, 237, 237)"
+ const { mountMarkdown } = await import(fixture)
+ await mountMarkdown({
+ text: "## Answer\n\nSome **bold** and [docs](https://opencode.ai/docs).\n\n- one\n- two\n\n```ts\nconst value = 42\n```",
+ })
+ }, fixture)
+ const harness = page.getByTestId("markdown-fixture")
+ await expect(harness.locator('[data-component="markdown"]')).toHaveAttribute("data-markdown-ready", "")
+ await expect(harness.locator("pre code span[style]").first()).toBeAttached()
+
+ const copied = await copySelection(page, "markdown")
+
+ expect(copied.text).toContain("Some bold and docs.")
+ expect(copied.text).toContain("const value = 42")
+ expect(copied.html).toContain("
Answer
")
+ expect(copied.html).toContain('Some bold and docs.
')
+ expect(copied.html).toContain("one")
+ expect(copied.html).toMatch(/([^<]*<\/span>)+<\/code><\/pre>/)
+ expect(copied.html).not.toMatch(/style=|class=|