diff --git a/src/lib/components/chat/Messages/Markdown.svelte b/src/lib/components/chat/Messages/Markdown.svelte
index ed1a0c505c..50c50d5725 100644
--- a/src/lib/components/chat/Messages/Markdown.svelte
+++ b/src/lib/components/chat/Messages/Markdown.svelte
@@ -8,6 +8,7 @@
import markedKatexExtension from '$lib/utils/marked/katex-extension';
import { disableSingleTilde } from '$lib/utils/marked/strikethrough-extension';
import { mentionExtension } from '$lib/utils/marked/mention-extension';
+ import colonFenceExtension from '$lib/utils/marked/colon-fence-extension';
import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
import footnoteExtension from '$lib/utils/marked/footnote-extension';
@@ -48,6 +49,7 @@
marked.use(markedExtension(options));
marked.use(citationExtension(options));
marked.use(footnoteExtension(options));
+ marked.use(colonFenceExtension(options));
marked.use(disableSingleTilde);
marked.use({
extensions: [
diff --git a/src/lib/components/chat/Messages/Markdown/ColonFenceBlock.svelte b/src/lib/components/chat/Messages/Markdown/ColonFenceBlock.svelte
new file mode 100644
index 0000000000..1f140d2977
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown/ColonFenceBlock.svelte
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+ {label}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte b/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
index 924b8b399d..cac7cdde66 100644
--- a/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
+++ b/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
@@ -23,6 +23,7 @@
import HtmlToken from './HTMLToken.svelte';
import Clipboard from '$lib/components/icons/Clipboard.svelte';
+ import ColonFenceBlock from './ColonFenceBlock.svelte';
export let id: string;
export let tokens: Token[];
@@ -434,6 +435,17 @@
{#if token.text}
{/if}
+ {:else if token.type === 'colonFence'}
+
{:else if token.type === 'space'}
{:else}
diff --git a/src/lib/utils/marked/colon-fence-extension.ts b/src/lib/utils/marked/colon-fence-extension.ts
new file mode 100644
index 0000000000..c9dca5ee4b
--- /dev/null
+++ b/src/lib/utils/marked/colon-fence-extension.ts
@@ -0,0 +1,56 @@
+/**
+ * Marked extension for colon-fence blocks (:::type ... :::)
+ *
+ * Used by newer OpenAI chat models to wrap semantically distinct content:
+ * :::writing – reusable prose (letters, articles, docs)
+ * :::code_execution – code execution output
+ * :::search_results – web search results
+ *
+ * The extension is generic and will tokenize any ::: block.
+ */
+
+function colonFenceTokenizer(this: any, src: string) {
+ // Match :::type at the start of a line, optionally followed by content, then closing :::
+ const match = /^:::([\w-]+)\n([\s\S]*?)(?:\n:::(?:\s*$|\n))/m.exec(src);
+ if (match) {
+ const fenceType = match[1];
+ const text = match[2].trim();
+ const raw = match[0];
+
+ const tokens: any[] = [];
+ this.lexer.blockTokens(text, tokens);
+
+ return {
+ type: 'colonFence',
+ raw,
+ fenceType,
+ text,
+ tokens
+ };
+ }
+}
+
+function colonFenceStart(src: string) {
+ const idx = src.match(/^:::\w/m);
+ return idx ? idx.index! : -1;
+}
+
+function colonFenceRenderer(token: any) {
+ return `${token.text}
`;
+}
+
+function colonFenceExtension() {
+ return {
+ name: 'colonFence',
+ level: 'block' as const,
+ start: colonFenceStart,
+ tokenizer: colonFenceTokenizer,
+ renderer: colonFenceRenderer
+ };
+}
+
+export default function (options = {}) {
+ return {
+ extensions: [colonFenceExtension()]
+ };
+}