@djangocfg/ui-tools 2.1.297 → 2.1.299

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +126 -2
  2. package/dist/{DocsLayout-3HNAQQRE.mjs → DocsLayout-MWRKNFXR.mjs} +3 -3
  3. package/dist/{DocsLayout-3HNAQQRE.mjs.map → DocsLayout-MWRKNFXR.mjs.map} +1 -1
  4. package/dist/{DocsLayout-O4ONSD67.cjs → DocsLayout-NWJUF42A.cjs} +48 -48
  5. package/dist/{DocsLayout-O4ONSD67.cjs.map → DocsLayout-NWJUF42A.cjs.map} +1 -1
  6. package/dist/{chunk-DKJTH4GE.mjs → chunk-CKD7GNE5.mjs} +236 -186
  7. package/dist/chunk-CKD7GNE5.mjs.map +1 -0
  8. package/dist/{chunk-QTO5LWMK.cjs → chunk-SEXWBCLX.cjs} +272 -221
  9. package/dist/chunk-SEXWBCLX.cjs.map +1 -0
  10. package/dist/index.cjs +13 -9
  11. package/dist/index.d.cts +82 -59
  12. package/dist/index.d.ts +82 -59
  13. package/dist/index.mjs +4 -4
  14. package/package.json +6 -6
  15. package/src/components/markdown/MarkdownMessage/CodeBlock.tsx +69 -0
  16. package/src/components/markdown/MarkdownMessage/CollapseToggle.tsx +60 -0
  17. package/src/components/markdown/MarkdownMessage/MarkdownMessage.story.tsx +171 -0
  18. package/src/components/markdown/MarkdownMessage/MarkdownMessage.tsx +202 -0
  19. package/src/components/markdown/MarkdownMessage/components.tsx +154 -0
  20. package/src/components/markdown/MarkdownMessage/index.ts +13 -0
  21. package/src/components/markdown/MarkdownMessage/linkRules.ts +83 -0
  22. package/src/components/markdown/MarkdownMessage/plainText.ts +50 -0
  23. package/src/components/markdown/MarkdownMessage/sanitize.ts +78 -0
  24. package/src/components/markdown/MarkdownMessage/types.ts +104 -0
  25. package/src/components/markdown/index.ts +6 -1
  26. package/dist/chunk-DKJTH4GE.mjs.map +0 -1
  27. package/dist/chunk-QTO5LWMK.cjs.map +0 -1
  28. package/src/components/markdown/MarkdownMessage.tsx +0 -686
@@ -1,5 +1,5 @@
1
1
  import { __name } from './chunk-CGILA3WO.mjs';
2
- import React3, { lazy, createContext, useState, useMemo, useCallback, Suspense, useContext, useReducer, useRef, useEffect } from 'react';
2
+ import React6, { lazy, createContext, useState, useMemo, useCallback, Suspense, useContext, useReducer, useRef, useEffect } from 'react';
3
3
  import '@djangocfg/ui-core/styles/palette';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import ReactMarkdown from 'react-markdown';
@@ -7,25 +7,10 @@ import rehypeRaw from 'rehype-raw';
7
7
  import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
8
8
  import remarkGfm from 'remark-gfm';
9
9
  import { CopyButton } from '@djangocfg/ui-core/components';
10
- import { useSessionStorage, useResolvedTheme } from '@djangocfg/ui-core/hooks';
10
+ import { useResolvedTheme, useSessionStorage } from '@djangocfg/ui-core/hooks';
11
11
  import consola2 from 'consola';
12
12
  import { sample } from 'openapi-sampler';
13
13
 
14
- var MermaidClient = lazy(() => import('./Mermaid.client-XFQ74OYN.mjs'));
15
- var LoadingFallback = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center min-h-[100px]", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-primary" }) }), "LoadingFallback");
16
- var Mermaid = /* @__PURE__ */ __name((props) => {
17
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsx(MermaidClient, { ...props }) });
18
- }, "Mermaid");
19
- var Mermaid_default = Mermaid;
20
- var PrettyCodeClient = lazy(() => import('./PrettyCode.client-SPMTQEG4.mjs'));
21
- var LoadingFallback2 = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "relative rounded-sm border border-border overflow-hidden bg-muted dark:bg-zinc-900", children: /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
22
- /* @__PURE__ */ jsx("div", { className: "animate-pulse h-4 w-4 rounded-full bg-muted-foreground/20" }),
23
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Loading code..." })
24
- ] }) }) }), "LoadingFallback");
25
- var PrettyCode = /* @__PURE__ */ __name((props) => {
26
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback2, {}), children: /* @__PURE__ */ jsx(PrettyCodeClient, { ...props }) });
27
- }, "PrettyCode");
28
- var PrettyCode_default = PrettyCode;
29
14
  function smartTruncate(content, maxLength) {
30
15
  if (content.length <= maxLength) {
31
16
  return content;
@@ -141,6 +126,72 @@ function useCollapsibleContent(content, options = {}) {
141
126
  };
142
127
  }
143
128
  __name(useCollapsibleContent, "useCollapsibleContent");
129
+ function extractTextFromChildren(children) {
130
+ if (typeof children === "string") return children;
131
+ if (typeof children === "number") return String(children);
132
+ if (React6.isValidElement(children)) {
133
+ const props = children.props;
134
+ return extractTextFromChildren(props.children);
135
+ }
136
+ if (Array.isArray(children)) {
137
+ return children.map(extractTextFromChildren).join("");
138
+ }
139
+ return "";
140
+ }
141
+ __name(extractTextFromChildren, "extractTextFromChildren");
142
+ function hasMarkdownSyntax(text) {
143
+ if (text.trim().includes("\n")) return true;
144
+ if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) return true;
145
+ const patterns = [
146
+ /^#{1,6}\s/m,
147
+ // Headers
148
+ /\*\*[^*]+\*\*/,
149
+ // Bold
150
+ /\*[^*]+\*/,
151
+ // Italic
152
+ /__[^_]+__/,
153
+ // Bold (underscore)
154
+ /_[^_]+_/,
155
+ // Italic (underscore)
156
+ /\[.+\]\(.+\)/,
157
+ // Links
158
+ /!\[.*\]\(.+\)/,
159
+ // Images
160
+ /```[\s\S]*```/,
161
+ // Code blocks
162
+ /`[^`]+`/,
163
+ // Inline code
164
+ /^\s*[-*+]\s/m,
165
+ // Unordered lists
166
+ /^\s*\d+\.\s/m,
167
+ // Ordered lists
168
+ /^\s*>/m,
169
+ // Blockquotes
170
+ /\|.+\|/,
171
+ // Tables
172
+ /^---+$/m,
173
+ // Horizontal rules
174
+ /~~[^~]+~~/
175
+ // Strikethrough
176
+ ];
177
+ return patterns.some((p) => p.test(text));
178
+ }
179
+ __name(hasMarkdownSyntax, "hasMarkdownSyntax");
180
+ var MermaidClient = lazy(() => import('./Mermaid.client-XFQ74OYN.mjs'));
181
+ var LoadingFallback = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center min-h-[100px]", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-primary" }) }), "LoadingFallback");
182
+ var Mermaid = /* @__PURE__ */ __name((props) => {
183
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsx(MermaidClient, { ...props }) });
184
+ }, "Mermaid");
185
+ var Mermaid_default = Mermaid;
186
+ var PrettyCodeClient = lazy(() => import('./PrettyCode.client-SPMTQEG4.mjs'));
187
+ var LoadingFallback2 = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "relative rounded-sm border border-border overflow-hidden bg-muted dark:bg-zinc-900", children: /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
188
+ /* @__PURE__ */ jsx("div", { className: "animate-pulse h-4 w-4 rounded-full bg-muted-foreground/20" }),
189
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Loading code..." })
190
+ ] }) }) }), "LoadingFallback");
191
+ var PrettyCode = /* @__PURE__ */ __name((props) => {
192
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback2, {}), children: /* @__PURE__ */ jsx(PrettyCodeClient, { ...props }) });
193
+ }, "PrettyCode");
194
+ var PrettyCode_default = PrettyCode;
144
195
  var HTML_SCHEMA_BASE = {
145
196
  ...defaultSchema,
146
197
  tagNames: [
@@ -174,22 +225,21 @@ function buildSchema(extraProtocols) {
174
225
  };
175
226
  }
176
227
  __name(buildSchema, "buildSchema");
177
- var extractTextFromChildren = /* @__PURE__ */ __name((children) => {
178
- if (typeof children === "string") {
179
- return children;
180
- }
181
- if (typeof children === "number") {
182
- return String(children);
183
- }
184
- if (React3.isValidElement(children)) {
185
- const props = children.props;
186
- return extractTextFromChildren(props.children);
187
- }
188
- if (Array.isArray(children)) {
189
- return children.map(extractTextFromChildren).join("");
190
- }
191
- return "";
192
- }, "extractTextFromChildren");
228
+ function buildUrlTransform(extraProtocols) {
229
+ if (!extraProtocols || extraProtocols.length === 0) return void 0;
230
+ const lower = extraProtocols.map((p) => p.toLowerCase() + ":");
231
+ return (url) => {
232
+ const u = url.trim().toLowerCase();
233
+ for (const p of lower) {
234
+ if (u.startsWith(p)) return url;
235
+ }
236
+ if (/^(https?:|mailto:|tel:|xmpp:|irc:|ircs:|#|\/|\.\/|\.\.\/|\?)/i.test(u) || /^[a-z0-9._~!$&'()*+,;=:@%-]+$/i.test(u)) {
237
+ return url;
238
+ }
239
+ return "";
240
+ };
241
+ }
242
+ __name(buildUrlTransform, "buildUrlTransform");
193
243
  var CodeBlock = /* @__PURE__ */ __name(({ code, language, isUser, isCompact = false }) => {
194
244
  const theme = useResolvedTheme();
195
245
  return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
@@ -219,25 +269,40 @@ var CodeBlock = /* @__PURE__ */ __name(({ code, language, isUser, isCompact = fa
219
269
  )
220
270
  ] });
221
271
  }, "CodeBlock");
222
- var createMarkdownComponents = /* @__PURE__ */ __name((isUser = false, isCompact = false) => {
272
+ var CodeBlockFallback = /* @__PURE__ */ __name(({ code, isUser }) => /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
273
+ /* @__PURE__ */ jsx(
274
+ CopyButton,
275
+ {
276
+ value: code,
277
+ variant: "ghost",
278
+ className: `
279
+ absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
280
+ h-8 w-8
281
+ ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
282
+ `,
283
+ title: "Copy code"
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsx("pre", { className: `
287
+ p-3 rounded text-xs font-mono overflow-x-auto
288
+ ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
289
+ `, children: /* @__PURE__ */ jsx("code", { children: code }) })
290
+ ] }), "CodeBlockFallback");
291
+ function createMarkdownComponents(isUser = false, isCompact = false) {
223
292
  const textSize = isCompact ? "text-xs" : "text-sm";
224
293
  const headingBase = isCompact ? "text-sm" : "text-base";
225
294
  const headingSm = isCompact ? "text-xs" : "text-sm";
226
295
  return {
227
- // Headings - scaled for chat context
228
296
  h1: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h1", { className: `${headingBase} font-bold mb-2 mt-3 first:mt-0`, children }), "h1"),
229
297
  h2: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h2", { className: `${headingSm} font-bold mb-2 mt-3 first:mt-0`, children }), "h2"),
230
298
  h3: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h3", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h3"),
231
299
  h4: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h4", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h4"),
232
300
  h5: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h5", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h5"),
233
301
  h6: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h6", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h6"),
234
- // Paragraphs - optimized for chat readability
235
302
  p: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("p", { className: `${textSize} mb-4 last:mb-0 leading-7 break-words font-light`, children }), "p"),
236
- // Lists - compact
237
303
  ul: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
238
304
  ol: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
239
305
  li: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("li", { className: "break-words", children }), "li"),
240
- // Links - appropriate for chat context
241
306
  a: /* @__PURE__ */ __name(({ href, children }) => /* @__PURE__ */ jsx(
242
307
  "a",
243
308
  {
@@ -248,11 +313,10 @@ var createMarkdownComponents = /* @__PURE__ */ __name((isUser = false, isCompact
248
313
  children
249
314
  }
250
315
  ), "a"),
251
- // Code blocks - using CodeBlock component with copy functionality
252
316
  pre: /* @__PURE__ */ __name(({ children }) => {
253
317
  let codeContent = "";
254
318
  let language = "plaintext";
255
- if (React3.isValidElement(children)) {
319
+ if (React6.isValidElement(children)) {
256
320
  const child = children;
257
321
  if (child.type === "code" || typeof child.type === "function" && child.type.name === "code") {
258
322
  const codeProps = child.props;
@@ -275,92 +339,28 @@ var createMarkdownComponents = /* @__PURE__ */ __name((isUser = false, isCompact
275
339
  return /* @__PURE__ */ jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
276
340
  } catch (error) {
277
341
  console.warn("CodeBlock failed, using fallback:", error);
278
- return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
279
- /* @__PURE__ */ jsx(
280
- CopyButton,
281
- {
282
- value: codeContent,
283
- variant: "ghost",
284
- className: `
285
- absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
286
- h-8 w-8
287
- ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
288
- `,
289
- title: "Copy code"
290
- }
291
- ),
292
- /* @__PURE__ */ jsx("pre", { className: `
293
- p-3 rounded text-xs font-mono overflow-x-auto
294
- ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
295
- `, children: /* @__PURE__ */ jsx("code", { children: codeContent }) })
296
- ] });
342
+ return /* @__PURE__ */ jsx(CodeBlockFallback, { code: codeContent, language, isUser, isCompact });
297
343
  }
298
344
  }, "pre"),
299
- // Inline code
300
345
  code: /* @__PURE__ */ __name(({ children, className }) => {
301
346
  if (className?.includes("language-")) {
302
347
  return /* @__PURE__ */ jsx("code", { className, children });
303
348
  }
304
- const codeContent = extractTextFromChildren(children);
305
- return /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 rounded text-xs font-mono bg-muted text-foreground break-all", children: codeContent });
349
+ return /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 rounded text-xs font-mono bg-muted text-foreground break-all", children: extractTextFromChildren(children) });
306
350
  }, "code"),
307
- // Blockquotes
308
351
  blockquote: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("blockquote", { className: `${textSize} border-l-2 border-border pl-3 my-2 italic text-muted-foreground break-words`, children }), "blockquote"),
309
- // Tables - compact for chat
310
352
  table: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto my-3", children: /* @__PURE__ */ jsx("table", { className: `min-w-full ${textSize} border-collapse`, children }) }), "table"),
311
353
  thead: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("thead", { className: "bg-muted/50", children }), "thead"),
312
354
  tbody: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tbody", { children }), "tbody"),
313
355
  tr: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/50", children }), "tr"),
314
356
  th: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("th", { className: "px-2 py-1 text-left font-medium break-words", children }), "th"),
315
357
  td: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1 break-words", children }), "td"),
316
- // Horizontal rule
317
358
  hr: /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("hr", { className: "my-3 border-0 h-px bg-border" }), "hr"),
318
- // Strong and emphasis
319
359
  strong: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }), "strong"),
320
360
  em: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }), "em")
321
361
  };
322
- }, "createMarkdownComponents");
323
- var hasMarkdownSyntax = /* @__PURE__ */ __name((text) => {
324
- if (text.trim().includes("\n")) {
325
- return true;
326
- }
327
- if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) {
328
- return true;
329
- }
330
- const markdownPatterns = [
331
- /^#{1,6}\s/m,
332
- // Headers
333
- /\*\*[^*]+\*\*/,
334
- // Bold
335
- /\*[^*]+\*/,
336
- // Italic
337
- /__[^_]+__/,
338
- // Bold (underscore)
339
- /_[^_]+_/,
340
- // Italic (underscore)
341
- /\[.+\]\(.+\)/,
342
- // Links
343
- /!\[.*\]\(.+\)/,
344
- // Images
345
- /```[\s\S]*```/,
346
- // Code blocks
347
- /`[^`]+`/,
348
- // Inline code
349
- /^\s*[-*+]\s/m,
350
- // Unordered lists
351
- /^\s*\d+\.\s/m,
352
- // Ordered lists
353
- /^\s*>/m,
354
- // Blockquotes
355
- /\|.+\|/,
356
- // Tables
357
- /^---+$/m,
358
- // Horizontal rules
359
- /~~[^~]+~~/
360
- // Strikethrough
361
- ];
362
- return markdownPatterns.some((pattern) => pattern.test(text));
363
- }, "hasMarkdownSyntax");
362
+ }
363
+ __name(createMarkdownComponents, "createMarkdownComponents");
364
364
  var CollapseToggle = /* @__PURE__ */ __name(({
365
365
  isCollapsed,
366
366
  onClick,
@@ -384,48 +384,77 @@ var CollapseToggle = /* @__PURE__ */ __name(({
384
384
  `,
385
385
  children: isCollapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
386
386
  readMoreLabel,
387
- /* @__PURE__ */ jsx(
388
- "svg",
389
- {
390
- className: "w-3 h-3",
391
- fill: "none",
392
- stroke: "currentColor",
393
- viewBox: "0 0 24 24",
394
- children: /* @__PURE__ */ jsx(
395
- "path",
396
- {
397
- strokeLinecap: "round",
398
- strokeLinejoin: "round",
399
- strokeWidth: 2,
400
- d: "M19 9l-7 7-7-7"
401
- }
402
- )
403
- }
404
- )
387
+ /* @__PURE__ */ jsx(Chevron, { direction: "down" })
405
388
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
406
389
  showLessLabel,
407
- /* @__PURE__ */ jsx(
408
- "svg",
409
- {
410
- className: "w-3 h-3",
411
- fill: "none",
412
- stroke: "currentColor",
413
- viewBox: "0 0 24 24",
414
- children: /* @__PURE__ */ jsx(
415
- "path",
416
- {
417
- strokeLinecap: "round",
418
- strokeLinejoin: "round",
419
- strokeWidth: 2,
420
- d: "M5 15l7-7 7 7"
421
- }
422
- )
423
- }
424
- )
390
+ /* @__PURE__ */ jsx(Chevron, { direction: "up" })
425
391
  ] })
426
392
  }
427
393
  );
428
394
  }, "CollapseToggle");
395
+ function Chevron({ direction }) {
396
+ return /* @__PURE__ */ jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
397
+ "path",
398
+ {
399
+ strokeLinecap: "round",
400
+ strokeLinejoin: "round",
401
+ strokeWidth: 2,
402
+ d: direction === "down" ? "M19 9l-7 7-7-7" : "M5 15l7-7 7 7"
403
+ }
404
+ ) });
405
+ }
406
+ __name(Chevron, "Chevron");
407
+ function applyPreprocess(source, rules) {
408
+ if (!rules || rules.length === 0) return source;
409
+ let s = source;
410
+ for (const rule of rules) {
411
+ if (!rule.preprocess) continue;
412
+ try {
413
+ s = rule.preprocess(s);
414
+ } catch (err) {
415
+ console.warn(
416
+ `[MarkdownMessage] linkRule "${rule.name ?? "(anonymous)"}" preprocess threw; skipping`,
417
+ err
418
+ );
419
+ }
420
+ }
421
+ return s;
422
+ }
423
+ __name(applyPreprocess, "applyPreprocess");
424
+ function collectProtocols(extraHrefProtocols, rules) {
425
+ const set = /* @__PURE__ */ new Set();
426
+ if (extraHrefProtocols) for (const p of extraHrefProtocols) set.add(p);
427
+ if (rules) {
428
+ for (const r of rules) {
429
+ if (r.protocols) for (const p of r.protocols) set.add(p);
430
+ }
431
+ }
432
+ return set.size === 0 ? void 0 : Array.from(set);
433
+ }
434
+ __name(collectProtocols, "collectProtocols");
435
+ function buildLinkRulesComponent(rules, isUser, callerA) {
436
+ const Renderer = /* @__PURE__ */ __name((props) => {
437
+ const { href, children } = props;
438
+ if (typeof href === "string") {
439
+ for (const rule of rules) {
440
+ if (rule.match(href)) {
441
+ return React6.createElement(
442
+ React6.Fragment,
443
+ null,
444
+ rule.render({ href, children, isUser })
445
+ );
446
+ }
447
+ }
448
+ }
449
+ if (callerA && typeof callerA === "function") {
450
+ const Caller = callerA;
451
+ return React6.createElement(Caller, props);
452
+ }
453
+ return React6.createElement("a", props, children);
454
+ }, "Renderer");
455
+ return Renderer;
456
+ }
457
+ __name(buildLinkRulesComponent, "buildLinkRulesComponent");
429
458
  var MarkdownMessage = /* @__PURE__ */ __name(({
430
459
  content,
431
460
  className = "",
@@ -433,6 +462,7 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
433
462
  isCompact = false,
434
463
  customComponents,
435
464
  extraHrefProtocols,
465
+ linkRules,
436
466
  collapsible = false,
437
467
  maxLength,
438
468
  maxLines,
@@ -441,53 +471,71 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
441
471
  defaultExpanded = false,
442
472
  onCollapseChange
443
473
  }) => {
444
- const trimmedContent = content.trim();
445
- const collapsibleOptions = React3.useMemo(() => {
474
+ const preprocessed = React6.useMemo(
475
+ () => applyPreprocess(content, linkRules),
476
+ [content, linkRules]
477
+ );
478
+ const effectiveProtocols = React6.useMemo(
479
+ () => collectProtocols(extraHrefProtocols, linkRules),
480
+ [extraHrefProtocols, linkRules]
481
+ );
482
+ const effectiveCustomComponents = React6.useMemo(() => {
483
+ if (!linkRules || linkRules.length === 0) return customComponents;
484
+ const callerA = customComponents?.a;
485
+ const aRenderer = buildLinkRulesComponent(linkRules, isUser, callerA);
486
+ return { ...customComponents ?? {}, a: aRenderer };
487
+ }, [customComponents, linkRules, isUser]);
488
+ const trimmedContent = preprocessed.trim();
489
+ const collapsibleOptions = React6.useMemo(() => {
446
490
  if (!collapsible) return {};
447
- const effectiveMaxLength = maxLength ?? 1e3;
448
- const effectiveMaxLines = maxLines ?? 10;
449
- return { maxLength: effectiveMaxLength, maxLines: effectiveMaxLines, defaultExpanded };
491
+ return {
492
+ maxLength: maxLength ?? 1e3,
493
+ maxLines: maxLines ?? 10,
494
+ defaultExpanded
495
+ };
450
496
  }, [collapsible, maxLength, maxLines, defaultExpanded]);
451
- const {
452
- isCollapsed,
453
- toggleCollapsed,
454
- displayContent,
455
- shouldCollapse
456
- } = useCollapsibleContent(
457
- trimmedContent,
458
- collapsible ? collapsibleOptions : {}
459
- );
460
- React3.useEffect(() => {
497
+ const { isCollapsed, toggleCollapsed, displayContent, shouldCollapse } = useCollapsibleContent(trimmedContent, collapsible ? collapsibleOptions : {});
498
+ React6.useEffect(() => {
461
499
  if (collapsible && shouldCollapse && onCollapseChange) {
462
500
  onCollapseChange(isCollapsed);
463
501
  }
464
502
  }, [isCollapsed, collapsible, shouldCollapse, onCollapseChange]);
465
- const components = React3.useMemo(() => {
503
+ const components = React6.useMemo(() => {
466
504
  const base = createMarkdownComponents(isUser, isCompact);
467
- return customComponents ? { ...base, ...customComponents } : base;
468
- }, [isUser, isCompact, customComponents]);
469
- const schema = React3.useMemo(() => buildSchema(extraHrefProtocols), [extraHrefProtocols]);
505
+ return effectiveCustomComponents ? { ...base, ...effectiveCustomComponents } : base;
506
+ }, [isUser, isCompact, effectiveCustomComponents]);
507
+ const schema = React6.useMemo(() => buildSchema(effectiveProtocols), [effectiveProtocols]);
508
+ const urlTransform = React6.useMemo(
509
+ () => buildUrlTransform(effectiveProtocols),
510
+ [effectiveProtocols]
511
+ );
470
512
  const textSizeClass = isCompact ? "text-xs" : "text-sm";
471
513
  const proseClass = isCompact ? "prose-xs" : "prose-sm";
472
- const isPlainText = !customComponents && !hasMarkdownSyntax(displayContent);
514
+ const isPlainText = !effectiveCustomComponents && !hasMarkdownSyntax(displayContent);
473
515
  if (isPlainText) {
474
- return /* @__PURE__ */ jsxs("span", { className: `${textSizeClass} leading-7 break-words whitespace-pre-line font-light ${className}`, children: [
475
- displayContent,
476
- collapsible && shouldCollapse && /* @__PURE__ */ jsxs(Fragment, { children: [
477
- isCollapsed && "... ",
478
- /* @__PURE__ */ jsx(
479
- CollapseToggle,
480
- {
481
- isCollapsed,
482
- onClick: toggleCollapsed,
483
- readMoreLabel,
484
- showLessLabel,
485
- isUser,
486
- isCompact
487
- }
488
- )
489
- ] })
490
- ] });
516
+ return /* @__PURE__ */ jsxs(
517
+ "span",
518
+ {
519
+ className: `${textSizeClass} leading-7 break-words whitespace-pre-line font-light ${className}`,
520
+ children: [
521
+ displayContent,
522
+ collapsible && shouldCollapse && /* @__PURE__ */ jsxs(Fragment, { children: [
523
+ isCollapsed && "... ",
524
+ /* @__PURE__ */ jsx(
525
+ CollapseToggle,
526
+ {
527
+ isCollapsed,
528
+ onClick: toggleCollapsed,
529
+ readMoreLabel,
530
+ showLessLabel,
531
+ isUser,
532
+ isCompact
533
+ }
534
+ )
535
+ ] })
536
+ ]
537
+ }
538
+ );
491
539
  }
492
540
  return /* @__PURE__ */ jsxs("div", { className, children: [
493
541
  /* @__PURE__ */ jsx(
@@ -499,7 +547,8 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
499
547
  [&>*]:leading-7
500
548
  `,
501
549
  style: {
502
- // Inherit colors from parent - fixes issues with external CSS variables
550
+ // Inherit colors from parent fixes issues with external
551
+ // CSS variables overriding prose tokens.
503
552
  "--tw-prose-body": "inherit",
504
553
  "--tw-prose-headings": "inherit",
505
554
  "--tw-prose-bold": "inherit",
@@ -512,6 +561,7 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
512
561
  remarkPlugins: [remarkGfm],
513
562
  rehypePlugins: [rehypeRaw, [rehypeSanitize, schema]],
514
563
  components,
564
+ urlTransform,
515
565
  children: displayContent
516
566
  }
517
567
  )
@@ -1284,7 +1334,7 @@ var usePlaygroundContext = /* @__PURE__ */ __name(() => {
1284
1334
  var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
1285
1335
  const [state, dispatch] = useReducer(reducer, void 0, createInitialState);
1286
1336
  const abortControllerRef = useRef(null);
1287
- const apiKeys = React3.useMemo(
1337
+ const apiKeys = React6.useMemo(
1288
1338
  () => config.apiKeys ?? [],
1289
1339
  [config.apiKeys]
1290
1340
  );
@@ -1454,6 +1504,6 @@ var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
1454
1504
  return /* @__PURE__ */ jsx(PlaygroundContext.Provider, { value: contextValue, children });
1455
1505
  }, "PlaygroundProvider");
1456
1506
 
1457
- export { CODE_SAMPLE_TARGETS, MarkdownMessage, Mermaid_default, PlaygroundProvider, PrettyCode_default, UrlBuilder, buildHarRequest, deduplicateEndpoints, dereferenceSchema, endpointToMarkdown, findApiKeyById, formatBytes, isValidJson, joinUrl, parseRequestHeaders, relativePath, renderSnippet, resolveAbsolute, resolveBaseUrl, sampleSchemaJson, toCompactJson, toMarkdown, toRawJson, useCollapsibleContent, usePlaygroundContext };
1458
- //# sourceMappingURL=chunk-DKJTH4GE.mjs.map
1459
- //# sourceMappingURL=chunk-DKJTH4GE.mjs.map
1507
+ export { CODE_SAMPLE_TARGETS, MarkdownMessage, Mermaid_default, PlaygroundProvider, PrettyCode_default, UrlBuilder, buildHarRequest, deduplicateEndpoints, dereferenceSchema, endpointToMarkdown, extractTextFromChildren, findApiKeyById, formatBytes, isValidJson, joinUrl, parseRequestHeaders, relativePath, renderSnippet, resolveAbsolute, resolveBaseUrl, sampleSchemaJson, toCompactJson, toMarkdown, toRawJson, useCollapsibleContent, usePlaygroundContext };
1508
+ //# sourceMappingURL=chunk-CKD7GNE5.mjs.map
1509
+ //# sourceMappingURL=chunk-CKD7GNE5.mjs.map