From e5730d18cd4fa9e95770ef7c6fd5b6c265dcec18 Mon Sep 17 00:00:00 2001 From: Mohammad Javad Naderi Date: Sat, 4 Jul 2026 18:10:11 +0330 Subject: [PATCH 01/10] fix(ui): support bidirectional chat markdown --- .changeset/chat-bidi-text.md | 5 ++ .../kilo-ui/src/components/message-part.tsx | 2 +- .../tests/unit/kilo-ui-contract.test.ts | 57 +++++++++++++++++++ packages/ui/src/components/markdown.css | 14 ++--- packages/ui/src/components/markdown.tsx | 17 ++++++ packages/ui/src/components/message-part.tsx | 2 +- packages/ui/src/context/marked.tsx | 2 + 7 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 .changeset/chat-bidi-text.md diff --git a/.changeset/chat-bidi-text.md b/.changeset/chat-bidi-text.md new file mode 100644 index 00000000000..7f984c6eeaf --- /dev/null +++ b/.changeset/chat-bidi-text.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Support bidirectional text in chat messages so right-to-left and mixed-language conversations render in the correct direction. diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index 7fca6106088..2ffb6a4f428 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -849,7 +849,7 @@ export function UserMessageDisplay(props: {
{props.header} -
+
diff --git a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts index 03b83d60f65..01743ec010b 100644 --- a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts @@ -18,7 +18,9 @@ import fs from "node:fs" import path from "node:path" const MONOREPO_ROOT = path.resolve(import.meta.dir, "../../../..") +const KILO_VSCODE_DIR = path.join(MONOREPO_ROOT, "packages/kilo-vscode") const KILO_UI_DIR = path.join(MONOREPO_ROOT, "packages/kilo-ui") +const HAPPY_DOM_FILE = Bun.resolveSync("happy-dom", KILO_VSCODE_DIR) const BASIC_TOOL_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/basic-tool.tsx") const DATA_CONTEXT_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/context/data.tsx") const MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/message-part.tsx") @@ -159,6 +161,61 @@ describe("Assistant Markdown streaming contract (source)", () => { }) }) +describe("Markdown bidirectional rendering contract", () => { + it("adds dir to direction-owning markdown blocks without nesting it inside containers", () => { + const result = check(` + import { Window } from ${JSON.stringify(HAPPY_DOM_FILE)} + + const window = new Window() + globalThis.window = window + globalThis.document = window.document + globalThis.Node = window.Node + globalThis.HTMLElement = window.HTMLElement + globalThis.HTMLAnchorElement = window.HTMLAnchorElement + globalThis.customElements = window.customElements + + const { markBidi } = await import("@opencode-ai/ui/markdown") + const root = document.createElement("div") + root.innerHTML = \` +

عنوان ۱

+

عنوان ۲

+

عنوان ۳

+

عنوان ۴

+
عنوان ۵
+
عنوان ۶
+

متن فارسی با inline

+

نقل قول فارسی

+
  • مورد اول
    • مورد دوم
+
ستون
کیلو
+
const value = "کیلو"
+ \` + + markBidi(root) + + const assert = (condition, message) => { + if (condition) return + throw new Error(message) + } + + for (const tag of ["h1", "h2", "h3", "h4", "h5", "h6"]) { + assert(root.querySelector(tag)?.getAttribute("dir") === "auto", tag + " dir missing") + } + assert(root.querySelector("p")?.getAttribute("dir") === "auto", "paragraph dir missing") + assert(root.querySelector("blockquote")?.getAttribute("dir") === "auto", "blockquote dir missing") + assert(root.querySelector("ul")?.getAttribute("dir") === "auto", "list dir missing") + assert(root.querySelector("table")?.getAttribute("dir") === "auto", "table dir missing") + assert(root.querySelector("pre")?.getAttribute("dir") === "auto", "fenced code dir missing") + assert(root.querySelector(":not(pre) > code")?.getAttribute("dir") === "auto", "inline code dir missing") + + assert(!root.querySelector("blockquote p")?.hasAttribute("dir"), "nested blockquote paragraph should not own dir") + assert(!root.querySelector("li")?.hasAttribute("dir"), "list item should not own dir") + assert(!root.querySelector("td")?.hasAttribute("dir"), "table cell should not own dir") + `) + + expect(result.ok, `Markdown bidi check failed: ${result.output}`).toBe(true) + }) +}) + describe("Edit tool diff-first click contract (source)", () => { const src = fs.readFileSync(KILO_MESSAGE_PART_FILE, "utf-8") diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css index 1190222956f..64599c9f59e 100644 --- a/packages/ui/src/components/markdown.css +++ b/packages/ui/src/components/markdown.css @@ -61,7 +61,7 @@ margin-top: 8px; margin-bottom: 12px; margin-left: 0; - padding-left: 32px; + padding-inline-start: 32px; /* kilocode_change */ list-style-position: outside; } @@ -71,7 +71,7 @@ ol { list-style-type: decimal; - padding-left: 2.25rem; + padding-inline-start: 2.25rem; /* kilocode_change */ } li { @@ -97,18 +97,18 @@ li > ol { margin-top: 0.25rem; margin-bottom: 0.25rem; - padding-left: 1rem; /* Minimal indent for nesting only */ + padding-inline-start: 1rem; /* Minimal indent for nesting only */ /* kilocode_change */ } li > ol { - padding-left: 1.75rem; + padding-inline-start: 1.75rem; /* kilocode_change */ } /* Blockquotes */ blockquote { - border-left: 2px solid var(--border-weak-base); + border-inline-start: 2px solid var(--border-weak-base); /* kilocode_change */ margin: 1.5rem 0; - padding-left: 0.5rem; + padding-inline-start: 0.5rem; /* kilocode_change */ color: var(--text-weak); font-style: normal; } @@ -255,7 +255,7 @@ /* Minimal borders for structure, matching TUI "lines" roughly but keeping it web-clean */ border-bottom: 1px solid var(--border-weaker-base); padding: 12px; - text-align: left; + text-align: start; /* kilocode_change */ vertical-align: top; } diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 822b94c02fa..7b35e1626f2 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -182,12 +182,29 @@ function markCodeLinks(root: HTMLDivElement) { } } +// kilocode_change start: add bidi direction after Marked renders, so we keep +// Marked's HTML generation intact while avoiding nested dir="auto" on list, +// quote, and table contents. +export function markBidi(root: HTMLElement) { + const blocks = "p, h1, h2, h3, h4, h5, h6, blockquote, ul, ol, table" + for (const child of Array.from(root.children)) { + if (child.matches(blocks)) child.setAttribute("dir", "auto") + } + + const code = Array.from(root.querySelectorAll("pre, :not(pre) > code")) + for (const node of code) { + node.setAttribute("dir", "auto") + } +} +// kilocode_change end + function decorate(root: HTMLDivElement, labels: CopyLabels) { const blocks = Array.from(root.querySelectorAll("pre")) for (const block of blocks) { ensureCodeWrapper(block, labels) } markCodeLinks(root) + markBidi(root) // kilocode_change } function setupCodeCopy(root: HTMLDivElement, getLabels: () => CopyLabels) { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 8782cdff6ec..db17c74915d 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1167,7 +1167,7 @@ export function UserMessageDisplay(props: { <>
-
+
{/* kilocode_change */}
diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx index ce91a223990..b0be5caa893 100644 --- a/packages/ui/src/context/marked.tsx +++ b/packages/ui/src/context/marked.tsx @@ -153,6 +153,8 @@ function replaceWithHighlighted(block: Element, html: string, sourceHash: string temp.innerHTML = html const highlighted = temp.firstElementChild if (!highlighted) return + const dir = pre.getAttribute("dir") // kilocode_change + if (dir) highlighted.setAttribute("dir", dir) // kilocode_change // Store a hash of the source code so the morphdom guard in Markdown can detect // mid-stream content changes without keeping the full source in the DOM. highlighted.setAttribute("data-source-hash", sourceHash) From a8bb361ecc2847cdf4fd6b4be844f96d53df8c0e Mon Sep 17 00:00:00 2001 From: Mohammad Javad Naderi Date: Sat, 4 Jul 2026 19:03:28 +0330 Subject: [PATCH 02/10] fix(ui): address bidi markdown review feedback --- .../tests/unit/kilo-ui-contract.test.ts | 85 +++++++------------ packages/ui/src/components/markdown.css | 2 +- packages/ui/src/components/markdown.tsx | 17 +--- packages/ui/src/components/message-part.tsx | 3 +- packages/ui/src/kilocode/markdown-bidi.ts | 18 ++++ 5 files changed, 54 insertions(+), 71 deletions(-) create mode 100644 packages/ui/src/kilocode/markdown-bidi.ts diff --git a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts index 01743ec010b..4ecce45d4c9 100644 --- a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts @@ -16,11 +16,11 @@ import { describe, it, expect } from "bun:test" import fs from "node:fs" import path from "node:path" +import { Window } from "happy-dom" +import { markBidi } from "../../../ui/src/kilocode/markdown-bidi" const MONOREPO_ROOT = path.resolve(import.meta.dir, "../../../..") -const KILO_VSCODE_DIR = path.join(MONOREPO_ROOT, "packages/kilo-vscode") const KILO_UI_DIR = path.join(MONOREPO_ROOT, "packages/kilo-ui") -const HAPPY_DOM_FILE = Bun.resolveSync("happy-dom", KILO_VSCODE_DIR) const BASIC_TOOL_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/basic-tool.tsx") const DATA_CONTEXT_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/context/data.tsx") const MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/message-part.tsx") @@ -163,56 +163,37 @@ describe("Assistant Markdown streaming contract (source)", () => { describe("Markdown bidirectional rendering contract", () => { it("adds dir to direction-owning markdown blocks without nesting it inside containers", () => { - const result = check(` - import { Window } from ${JSON.stringify(HAPPY_DOM_FILE)} - - const window = new Window() - globalThis.window = window - globalThis.document = window.document - globalThis.Node = window.Node - globalThis.HTMLElement = window.HTMLElement - globalThis.HTMLAnchorElement = window.HTMLAnchorElement - globalThis.customElements = window.customElements - - const { markBidi } = await import("@opencode-ai/ui/markdown") - const root = document.createElement("div") - root.innerHTML = \` -

عنوان ۱

-

عنوان ۲

-

عنوان ۳

-

عنوان ۴

-
عنوان ۵
-
عنوان ۶
-

متن فارسی با inline

-

نقل قول فارسی

-
  • مورد اول
    • مورد دوم
-
ستون
کیلو
-
const value = "کیلو"
- \` - - markBidi(root) - - const assert = (condition, message) => { - if (condition) return - throw new Error(message) - } - - for (const tag of ["h1", "h2", "h3", "h4", "h5", "h6"]) { - assert(root.querySelector(tag)?.getAttribute("dir") === "auto", tag + " dir missing") - } - assert(root.querySelector("p")?.getAttribute("dir") === "auto", "paragraph dir missing") - assert(root.querySelector("blockquote")?.getAttribute("dir") === "auto", "blockquote dir missing") - assert(root.querySelector("ul")?.getAttribute("dir") === "auto", "list dir missing") - assert(root.querySelector("table")?.getAttribute("dir") === "auto", "table dir missing") - assert(root.querySelector("pre")?.getAttribute("dir") === "auto", "fenced code dir missing") - assert(root.querySelector(":not(pre) > code")?.getAttribute("dir") === "auto", "inline code dir missing") - - assert(!root.querySelector("blockquote p")?.hasAttribute("dir"), "nested blockquote paragraph should not own dir") - assert(!root.querySelector("li")?.hasAttribute("dir"), "list item should not own dir") - assert(!root.querySelector("td")?.hasAttribute("dir"), "table cell should not own dir") - `) - - expect(result.ok, `Markdown bidi check failed: ${result.output}`).toBe(true) + const win = new Window() + const root = win.document.createElement("div") + root.innerHTML = ` +

عنوان ۱

+

عنوان ۲

+

عنوان ۳

+

عنوان ۴

+
عنوان ۵
+
عنوان ۶
+

متن فارسی با inline

+

نقل قول فارسی

+
  • مورد اول
    • مورد دوم
+
ستون
کیلو
+
const value = "کیلو"
+ ` + + markBidi(root) + + for (const tag of ["h1", "h2", "h3", "h4", "h5", "h6"]) { + expect(root.querySelector(tag)?.getAttribute("dir")).toBe("auto") + } + expect(root.querySelector("p")?.getAttribute("dir")).toBe("auto") + expect(root.querySelector("blockquote")?.getAttribute("dir")).toBe("auto") + expect(root.querySelector("ul")?.getAttribute("dir")).toBe("auto") + expect(root.querySelector("table")?.getAttribute("dir")).toBe("auto") + expect(root.querySelector("pre")?.getAttribute("dir")).toBe("auto") + expect(root.querySelector(":not(pre) > code")?.getAttribute("dir")).toBe("auto") + + expect(root.querySelector("blockquote p")?.hasAttribute("dir")).toBe(false) + expect(root.querySelector("li")?.hasAttribute("dir")).toBe(false) + expect(root.querySelector("td")?.hasAttribute("dir")).toBe(false) }) }) diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css index 64599c9f59e..d246cb2302f 100644 --- a/packages/ui/src/components/markdown.css +++ b/packages/ui/src/components/markdown.css @@ -60,7 +60,7 @@ ol { margin-top: 8px; margin-bottom: 12px; - margin-left: 0; + margin-inline-start: 0; /* kilocode_change */ padding-inline-start: 32px; /* kilocode_change */ list-style-position: outside; } diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 7b35e1626f2..0e905b5e2a5 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -8,6 +8,7 @@ import { ComponentProps, createEffect, createResource, createSignal, onCleanup, import { isServer } from "solid-js/web" import { stream } from "./markdown-stream" import { tryFastRender } from "../kilocode/markdown-fast-path" // kilocode_change +import { markBidi } from "../kilocode/markdown-bidi" // kilocode_change import { hasMermaid, preserveMermaid, renderMermaid, type MermaidLabels } from "../kilocode/markdown-mermaid" // kilocode_change import { preserveStreamingHighlight } from "../kilocode/markdown-stream-highlight" // kilocode_change import { createIncrementalMarkdown, type MarkdownBlock } from "../kilocode/markdown-incremental-dom" // kilocode_change @@ -182,22 +183,6 @@ function markCodeLinks(root: HTMLDivElement) { } } -// kilocode_change start: add bidi direction after Marked renders, so we keep -// Marked's HTML generation intact while avoiding nested dir="auto" on list, -// quote, and table contents. -export function markBidi(root: HTMLElement) { - const blocks = "p, h1, h2, h3, h4, h5, h6, blockquote, ul, ol, table" - for (const child of Array.from(root.children)) { - if (child.matches(blocks)) child.setAttribute("dir", "auto") - } - - const code = Array.from(root.querySelectorAll("pre, :not(pre) > code")) - for (const node of code) { - node.setAttribute("dir", "auto") - } -} -// kilocode_change end - function decorate(root: HTMLDivElement, labels: CopyLabels) { const blocks = Array.from(root.querySelectorAll("pre")) for (const block of blocks) { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index db17c74915d..4e75d22d12e 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1167,8 +1167,7 @@ export function UserMessageDisplay(props: { <>
-
- {/* kilocode_change */} +
{/* kilocode_change */}
{/* kilocode_change start */} diff --git a/packages/ui/src/kilocode/markdown-bidi.ts b/packages/ui/src/kilocode/markdown-bidi.ts new file mode 100644 index 00000000000..7909a9765a4 --- /dev/null +++ b/packages/ui/src/kilocode/markdown-bidi.ts @@ -0,0 +1,18 @@ +export function markBidi(root: HTMLElement) { + // The decoration intentionally only marks direct markdown children. It does + // not recursively add dir="auto" to nested paragraphs, list items, table + // cells, or quote contents. + // This matters because the HTML directionality algorithm for dir=auto skips + // text inside descendants that already have their own dir attribute. + // If every nested element gets dir="auto", then a parent list, table, or + // blockquote may fail to detect its RTL text and keep the wrong direction. + const blocks = "p, h1, h2, h3, h4, h5, h6, blockquote, ul, ol, table" + for (const child of Array.from(root.children)) { + if (child.matches(blocks)) child.setAttribute("dir", "auto") + } + + const code = Array.from(root.querySelectorAll("pre, :not(pre) > code")) + for (const node of code) { + node.setAttribute("dir", "auto") + } +} From 47eb5a1f874701bf4a6501844c9c2e39e86bd49d Mon Sep 17 00:00:00 2001 From: Mohammad Javad Naderi Date: Wed, 8 Jul 2026 16:37:47 +0330 Subject: [PATCH 03/10] fix(ui): distinguish user message bubbles --- packages/kilo-ui/src/components/message-part.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/kilo-ui/src/components/message-part.css b/packages/kilo-ui/src/components/message-part.css index 5a3fd732da0..bf6a604d249 100644 --- a/packages/kilo-ui/src/components/message-part.css +++ b/packages/kilo-ui/src/components/message-part.css @@ -392,6 +392,8 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] { } [data-slot="user-message-text"] { + background-color: color-mix(in srgb, var(--text-base) 7%, transparent); + border: 1px solid transparent; transition: opacity 0.3s ease; &[data-queued] { @@ -408,6 +410,11 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] { } } +body.vscode-high-contrast [data-component="user-message"] [data-slot="user-message-text"], +body.vscode-high-contrast-light [data-component="user-message"] [data-slot="user-message-text"] { + border-color: var(--border-weak-base); +} + [data-component="tool-output"] { margin-bottom: 0px; } From 0ea9d14de843278a717f3865ec6ae12f82c7d9b7 Mon Sep 17 00:00:00 2001 From: Mohammad Javad Naderi Date: Thu, 9 Jul 2026 15:06:16 +0330 Subject: [PATCH 04/10] fix(ui): simplify markdown bidi direction Mark the markdown container with one auto direction instead of decorating each rendered block after parsing. Assistant replies usually have one dominant natural language, but coding responses often start list items and paragraphs with identifiers, method names, or HTTP terms. Per-block auto direction lets those English tokens choose the direction for whole lists even when the surrounding answer is right-to-left. Keeping direction on the root makes the answer follow the dominant prose direction more consistently, while code and pre elements keep their own auto direction so code-like content does not decide the container direction. This also removes the post-render bidi decoration pass. --- .../tests/unit/kilo-ui-contract.test.ts | 38 ------------------- packages/ui/src/components/markdown.tsx | 3 +- packages/ui/src/context/marked.tsx | 6 +-- .../ui/src/kilocode/markdown-bidi.test.ts | 19 ++++++++++ packages/ui/src/kilocode/markdown-bidi.ts | 18 --------- 5 files changed, 23 insertions(+), 61 deletions(-) create mode 100644 packages/ui/src/kilocode/markdown-bidi.test.ts delete mode 100644 packages/ui/src/kilocode/markdown-bidi.ts diff --git a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts index 4ecce45d4c9..03b83d60f65 100644 --- a/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts @@ -16,8 +16,6 @@ import { describe, it, expect } from "bun:test" import fs from "node:fs" import path from "node:path" -import { Window } from "happy-dom" -import { markBidi } from "../../../ui/src/kilocode/markdown-bidi" const MONOREPO_ROOT = path.resolve(import.meta.dir, "../../../..") const KILO_UI_DIR = path.join(MONOREPO_ROOT, "packages/kilo-ui") @@ -161,42 +159,6 @@ describe("Assistant Markdown streaming contract (source)", () => { }) }) -describe("Markdown bidirectional rendering contract", () => { - it("adds dir to direction-owning markdown blocks without nesting it inside containers", () => { - const win = new Window() - const root = win.document.createElement("div") - root.innerHTML = ` -

عنوان ۱

-

عنوان ۲

-

عنوان ۳

-

عنوان ۴

-
عنوان ۵
-
عنوان ۶
-

متن فارسی با inline

-

نقل قول فارسی

-
  • مورد اول
    • مورد دوم
-
ستون
کیلو
-
const value = "کیلو"
- ` - - markBidi(root) - - for (const tag of ["h1", "h2", "h3", "h4", "h5", "h6"]) { - expect(root.querySelector(tag)?.getAttribute("dir")).toBe("auto") - } - expect(root.querySelector("p")?.getAttribute("dir")).toBe("auto") - expect(root.querySelector("blockquote")?.getAttribute("dir")).toBe("auto") - expect(root.querySelector("ul")?.getAttribute("dir")).toBe("auto") - expect(root.querySelector("table")?.getAttribute("dir")).toBe("auto") - expect(root.querySelector("pre")?.getAttribute("dir")).toBe("auto") - expect(root.querySelector(":not(pre) > code")?.getAttribute("dir")).toBe("auto") - - expect(root.querySelector("blockquote p")?.hasAttribute("dir")).toBe(false) - expect(root.querySelector("li")?.hasAttribute("dir")).toBe(false) - expect(root.querySelector("td")?.hasAttribute("dir")).toBe(false) - }) -}) - describe("Edit tool diff-first click contract (source)", () => { const src = fs.readFileSync(KILO_MESSAGE_PART_FILE, "utf-8") diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 0e905b5e2a5..a46b5fbd1e4 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -8,7 +8,6 @@ import { ComponentProps, createEffect, createResource, createSignal, onCleanup, import { isServer } from "solid-js/web" import { stream } from "./markdown-stream" import { tryFastRender } from "../kilocode/markdown-fast-path" // kilocode_change -import { markBidi } from "../kilocode/markdown-bidi" // kilocode_change import { hasMermaid, preserveMermaid, renderMermaid, type MermaidLabels } from "../kilocode/markdown-mermaid" // kilocode_change import { preserveStreamingHighlight } from "../kilocode/markdown-stream-highlight" // kilocode_change import { createIncrementalMarkdown, type MarkdownBlock } from "../kilocode/markdown-incremental-dom" // kilocode_change @@ -189,7 +188,6 @@ function decorate(root: HTMLDivElement, labels: CopyLabels) { ensureCodeWrapper(block, labels) } markCodeLinks(root) - markBidi(root) // kilocode_change } function setupCodeCopy(root: HTMLDivElement, getLabels: () => CopyLabels) { @@ -546,6 +544,7 @@ export function Markdown( return (