diff --git a/tools/ui/src/app.d.ts b/tools/ui/src/app.d.ts
index 5309dce8f4..639a16df21 100644
--- a/tools/ui/src/app.d.ts
+++ b/tools/ui/src/app.d.ts
@@ -137,7 +137,6 @@ declare global {
declare global {
interface Window {
- idxThemeStyle?: number;
idxCodeBlock?: number;
// File System Access API - not in the DOM lib and unavailable in some browsers
diff --git a/tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte b/tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte
index 87b41bd00d..5eda49645c 100644
--- a/tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte
+++ b/tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte
@@ -1,22 +1,12 @@
diff --git a/tools/ui/src/lib/components/app/content/MarkdownContent/markdown-processor.ts b/tools/ui/src/lib/components/app/content/MarkdownContent/markdown-processor.ts
new file mode 100644
index 0000000000..e973a6a4b5
--- /dev/null
+++ b/tools/ui/src/lib/components/app/content/MarkdownContent/markdown-processor.ts
@@ -0,0 +1,112 @@
+// Shared remark/rehype pipeline factory for MarkdownContent.
+//
+// The frozen plugin chain is expensive to build ( ~15 plugin instances ),
+// and MarkdownContent used to rebuild it on every processMarkdown call:
+// once per block at mount, and again on every coalesced chunk while
+// streaming. Pipelines without attachments are shared process-wide per
+// math flag; attachment-bearing pipelines are cached by the attachments
+// array identity, which changes whenever extras are updated.
+
+import { rehypeEnhanceCodeBlocks } from './plugins/rehype/enhance-code-blocks';
+import { rehypeEnhanceLinks } from './plugins/rehype/enhance-links';
+import { rehypeEnhanceMermaidBlocks } from './plugins/rehype/enhance-mermaid-blocks';
+import { rehypeEnhanceSvgBlocks } from './plugins/rehype/enhance-svg-blocks';
+import { rehypeFileBadge } from './plugins/rehype/file-badge';
+import { rehypeMermaidPre } from './plugins/rehype/mermaid-pre';
+import { rehypeRtlSupport } from './plugins/rehype/rehype-rtl-support';
+import { rehypeResolveAttachmentImages } from './plugins/rehype/resolve-attachment-images';
+import { rehypeSvgPre } from './plugins/rehype/svg-pre';
+import { rehypeRestoreTableHtml } from './plugins/rehype/table-html-restorer';
+import { remarkLiteralHtml } from './plugins/remark/literal-html';
+import { FileTypeText } from '$lib/enums/files.enums';
+import type { DatabaseMessageExtra } from '$lib/types/database';
+import type { Root as HastRoot } from 'hast';
+import { all as lowlightAll } from 'lowlight';
+import type { Root as MdastRoot } from 'mdast';
+import rehypeHighlight from 'rehype-highlight';
+import rehypeKatex from 'rehype-katex';
+import rehypeStringify from 'rehype-stringify';
+import { remark } from 'remark';
+import remarkBreaks from 'remark-breaks';
+import remarkGfm from 'remark-gfm';
+import remarkMath from 'remark-math';
+import remarkRehype from 'remark-rehype';
+
+export interface MarkdownProcessor {
+ parse(markdown: string): MdastRoot;
+ run(tree: MdastRoot): Promise;
+ stringify(tree: HastRoot): string;
+}
+
+export interface MarkdownProcessorOptions {
+ attachments?: DatabaseMessageExtra[];
+ disableMath?: boolean;
+}
+
+const sharedPipelines = new Map();
+const attachmentPipelines = new WeakMap