@djangocfg/ui-tools 2.1.298 → 2.1.300

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-74WIW7L3.mjs → DocsLayout-MWRKNFXR.mjs} +3 -3
  3. package/dist/{DocsLayout-74WIW7L3.mjs.map → DocsLayout-MWRKNFXR.mjs.map} +1 -1
  4. package/dist/{DocsLayout-IA55EXRN.cjs → DocsLayout-NWJUF42A.cjs} +48 -48
  5. package/dist/{DocsLayout-IA55EXRN.cjs.map → DocsLayout-NWJUF42A.cjs.map} +1 -1
  6. package/dist/{chunk-2BBXP3DH.mjs → chunk-CKD7GNE5.mjs} +220 -187
  7. package/dist/chunk-CKD7GNE5.mjs.map +1 -0
  8. package/dist/{chunk-Q6FNLXLZ.cjs → chunk-SEXWBCLX.cjs} +256 -222
  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-2BBXP3DH.mjs.map +0 -1
  27. package/dist/chunk-Q6FNLXLZ.cjs.map +0 -1
  28. package/src/components/markdown/MarkdownMessage.tsx +0 -721
@@ -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: [
@@ -189,22 +240,6 @@ function buildUrlTransform(extraProtocols) {
189
240
  };
190
241
  }
191
242
  __name(buildUrlTransform, "buildUrlTransform");
192
- var extractTextFromChildren = /* @__PURE__ */ __name((children) => {
193
- if (typeof children === "string") {
194
- return children;
195
- }
196
- if (typeof children === "number") {
197
- return String(children);
198
- }
199
- if (React3.isValidElement(children)) {
200
- const props = children.props;
201
- return extractTextFromChildren(props.children);
202
- }
203
- if (Array.isArray(children)) {
204
- return children.map(extractTextFromChildren).join("");
205
- }
206
- return "";
207
- }, "extractTextFromChildren");
208
243
  var CodeBlock = /* @__PURE__ */ __name(({ code, language, isUser, isCompact = false }) => {
209
244
  const theme = useResolvedTheme();
210
245
  return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
@@ -234,25 +269,40 @@ var CodeBlock = /* @__PURE__ */ __name(({ code, language, isUser, isCompact = fa
234
269
  )
235
270
  ] });
236
271
  }, "CodeBlock");
237
- 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) {
238
292
  const textSize = isCompact ? "text-xs" : "text-sm";
239
293
  const headingBase = isCompact ? "text-sm" : "text-base";
240
294
  const headingSm = isCompact ? "text-xs" : "text-sm";
241
295
  return {
242
- // Headings - scaled for chat context
243
296
  h1: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h1", { className: `${headingBase} font-bold mb-2 mt-3 first:mt-0`, children }), "h1"),
244
297
  h2: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h2", { className: `${headingSm} font-bold mb-2 mt-3 first:mt-0`, children }), "h2"),
245
298
  h3: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h3", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h3"),
246
299
  h4: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h4", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h4"),
247
300
  h5: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h5", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h5"),
248
301
  h6: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h6", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h6"),
249
- // Paragraphs - optimized for chat readability
250
302
  p: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("p", { className: `${textSize} mb-4 last:mb-0 leading-7 break-words font-light`, children }), "p"),
251
- // Lists - compact
252
303
  ul: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
253
304
  ol: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
254
305
  li: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("li", { className: "break-words", children }), "li"),
255
- // Links - appropriate for chat context
256
306
  a: /* @__PURE__ */ __name(({ href, children }) => /* @__PURE__ */ jsx(
257
307
  "a",
258
308
  {
@@ -263,11 +313,10 @@ var createMarkdownComponents = /* @__PURE__ */ __name((isUser = false, isCompact
263
313
  children
264
314
  }
265
315
  ), "a"),
266
- // Code blocks - using CodeBlock component with copy functionality
267
316
  pre: /* @__PURE__ */ __name(({ children }) => {
268
317
  let codeContent = "";
269
318
  let language = "plaintext";
270
- if (React3.isValidElement(children)) {
319
+ if (React6.isValidElement(children)) {
271
320
  const child = children;
272
321
  if (child.type === "code" || typeof child.type === "function" && child.type.name === "code") {
273
322
  const codeProps = child.props;
@@ -290,92 +339,28 @@ var createMarkdownComponents = /* @__PURE__ */ __name((isUser = false, isCompact
290
339
  return /* @__PURE__ */ jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
291
340
  } catch (error) {
292
341
  console.warn("CodeBlock failed, using fallback:", error);
293
- return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
294
- /* @__PURE__ */ jsx(
295
- CopyButton,
296
- {
297
- value: codeContent,
298
- variant: "ghost",
299
- className: `
300
- absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
301
- h-8 w-8
302
- ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
303
- `,
304
- title: "Copy code"
305
- }
306
- ),
307
- /* @__PURE__ */ jsx("pre", { className: `
308
- p-3 rounded text-xs font-mono overflow-x-auto
309
- ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
310
- `, children: /* @__PURE__ */ jsx("code", { children: codeContent }) })
311
- ] });
342
+ return /* @__PURE__ */ jsx(CodeBlockFallback, { code: codeContent, language, isUser, isCompact });
312
343
  }
313
344
  }, "pre"),
314
- // Inline code
315
345
  code: /* @__PURE__ */ __name(({ children, className }) => {
316
346
  if (className?.includes("language-")) {
317
347
  return /* @__PURE__ */ jsx("code", { className, children });
318
348
  }
319
- const codeContent = extractTextFromChildren(children);
320
- 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) });
321
350
  }, "code"),
322
- // Blockquotes
323
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"),
324
- // Tables - compact for chat
325
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"),
326
353
  thead: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("thead", { className: "bg-muted/50", children }), "thead"),
327
354
  tbody: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tbody", { children }), "tbody"),
328
355
  tr: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/50", children }), "tr"),
329
356
  th: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("th", { className: "px-2 py-1 text-left font-medium break-words", children }), "th"),
330
357
  td: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1 break-words", children }), "td"),
331
- // Horizontal rule
332
358
  hr: /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("hr", { className: "my-3 border-0 h-px bg-border" }), "hr"),
333
- // Strong and emphasis
334
359
  strong: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }), "strong"),
335
360
  em: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }), "em")
336
361
  };
337
- }, "createMarkdownComponents");
338
- var hasMarkdownSyntax = /* @__PURE__ */ __name((text) => {
339
- if (text.trim().includes("\n")) {
340
- return true;
341
- }
342
- if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) {
343
- return true;
344
- }
345
- const markdownPatterns = [
346
- /^#{1,6}\s/m,
347
- // Headers
348
- /\*\*[^*]+\*\*/,
349
- // Bold
350
- /\*[^*]+\*/,
351
- // Italic
352
- /__[^_]+__/,
353
- // Bold (underscore)
354
- /_[^_]+_/,
355
- // Italic (underscore)
356
- /\[.+\]\(.+\)/,
357
- // Links
358
- /!\[.*\]\(.+\)/,
359
- // Images
360
- /```[\s\S]*```/,
361
- // Code blocks
362
- /`[^`]+`/,
363
- // Inline code
364
- /^\s*[-*+]\s/m,
365
- // Unordered lists
366
- /^\s*\d+\.\s/m,
367
- // Ordered lists
368
- /^\s*>/m,
369
- // Blockquotes
370
- /\|.+\|/,
371
- // Tables
372
- /^---+$/m,
373
- // Horizontal rules
374
- /~~[^~]+~~/
375
- // Strikethrough
376
- ];
377
- return markdownPatterns.some((pattern) => pattern.test(text));
378
- }, "hasMarkdownSyntax");
362
+ }
363
+ __name(createMarkdownComponents, "createMarkdownComponents");
379
364
  var CollapseToggle = /* @__PURE__ */ __name(({
380
365
  isCollapsed,
381
366
  onClick,
@@ -399,48 +384,77 @@ var CollapseToggle = /* @__PURE__ */ __name(({
399
384
  `,
400
385
  children: isCollapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
401
386
  readMoreLabel,
402
- /* @__PURE__ */ jsx(
403
- "svg",
404
- {
405
- className: "w-3 h-3",
406
- fill: "none",
407
- stroke: "currentColor",
408
- viewBox: "0 0 24 24",
409
- children: /* @__PURE__ */ jsx(
410
- "path",
411
- {
412
- strokeLinecap: "round",
413
- strokeLinejoin: "round",
414
- strokeWidth: 2,
415
- d: "M19 9l-7 7-7-7"
416
- }
417
- )
418
- }
419
- )
387
+ /* @__PURE__ */ jsx(Chevron, { direction: "down" })
420
388
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
421
389
  showLessLabel,
422
- /* @__PURE__ */ jsx(
423
- "svg",
424
- {
425
- className: "w-3 h-3",
426
- fill: "none",
427
- stroke: "currentColor",
428
- viewBox: "0 0 24 24",
429
- children: /* @__PURE__ */ jsx(
430
- "path",
431
- {
432
- strokeLinecap: "round",
433
- strokeLinejoin: "round",
434
- strokeWidth: 2,
435
- d: "M5 15l7-7 7 7"
436
- }
437
- )
438
- }
439
- )
390
+ /* @__PURE__ */ jsx(Chevron, { direction: "up" })
440
391
  ] })
441
392
  }
442
393
  );
443
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");
444
458
  var MarkdownMessage = /* @__PURE__ */ __name(({
445
459
  content,
446
460
  className = "",
@@ -448,6 +462,7 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
448
462
  isCompact = false,
449
463
  customComponents,
450
464
  extraHrefProtocols,
465
+ linkRules,
451
466
  collapsible = false,
452
467
  maxLength,
453
468
  maxLines,
@@ -456,54 +471,71 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
456
471
  defaultExpanded = false,
457
472
  onCollapseChange
458
473
  }) => {
459
- const trimmedContent = content.trim();
460
- 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(() => {
461
490
  if (!collapsible) return {};
462
- const effectiveMaxLength = maxLength ?? 1e3;
463
- const effectiveMaxLines = maxLines ?? 10;
464
- return { maxLength: effectiveMaxLength, maxLines: effectiveMaxLines, defaultExpanded };
491
+ return {
492
+ maxLength: maxLength ?? 1e3,
493
+ maxLines: maxLines ?? 10,
494
+ defaultExpanded
495
+ };
465
496
  }, [collapsible, maxLength, maxLines, defaultExpanded]);
466
- const {
467
- isCollapsed,
468
- toggleCollapsed,
469
- displayContent,
470
- shouldCollapse
471
- } = useCollapsibleContent(
472
- trimmedContent,
473
- collapsible ? collapsibleOptions : {}
474
- );
475
- React3.useEffect(() => {
497
+ const { isCollapsed, toggleCollapsed, displayContent, shouldCollapse } = useCollapsibleContent(trimmedContent, collapsible ? collapsibleOptions : {});
498
+ React6.useEffect(() => {
476
499
  if (collapsible && shouldCollapse && onCollapseChange) {
477
500
  onCollapseChange(isCollapsed);
478
501
  }
479
502
  }, [isCollapsed, collapsible, shouldCollapse, onCollapseChange]);
480
- const components = React3.useMemo(() => {
503
+ const components = React6.useMemo(() => {
481
504
  const base = createMarkdownComponents(isUser, isCompact);
482
- return customComponents ? { ...base, ...customComponents } : base;
483
- }, [isUser, isCompact, customComponents]);
484
- const schema = React3.useMemo(() => buildSchema(extraHrefProtocols), [extraHrefProtocols]);
485
- const urlTransform = React3.useMemo(() => buildUrlTransform(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
+ );
486
512
  const textSizeClass = isCompact ? "text-xs" : "text-sm";
487
513
  const proseClass = isCompact ? "prose-xs" : "prose-sm";
488
- const isPlainText = !customComponents && !hasMarkdownSyntax(displayContent);
514
+ const isPlainText = !effectiveCustomComponents && !hasMarkdownSyntax(displayContent);
489
515
  if (isPlainText) {
490
- return /* @__PURE__ */ jsxs("span", { className: `${textSizeClass} leading-7 break-words whitespace-pre-line font-light ${className}`, children: [
491
- displayContent,
492
- collapsible && shouldCollapse && /* @__PURE__ */ jsxs(Fragment, { children: [
493
- isCollapsed && "... ",
494
- /* @__PURE__ */ jsx(
495
- CollapseToggle,
496
- {
497
- isCollapsed,
498
- onClick: toggleCollapsed,
499
- readMoreLabel,
500
- showLessLabel,
501
- isUser,
502
- isCompact
503
- }
504
- )
505
- ] })
506
- ] });
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
+ );
507
539
  }
508
540
  return /* @__PURE__ */ jsxs("div", { className, children: [
509
541
  /* @__PURE__ */ jsx(
@@ -515,7 +547,8 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
515
547
  [&>*]:leading-7
516
548
  `,
517
549
  style: {
518
- // 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.
519
552
  "--tw-prose-body": "inherit",
520
553
  "--tw-prose-headings": "inherit",
521
554
  "--tw-prose-bold": "inherit",
@@ -1301,7 +1334,7 @@ var usePlaygroundContext = /* @__PURE__ */ __name(() => {
1301
1334
  var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
1302
1335
  const [state, dispatch] = useReducer(reducer, void 0, createInitialState);
1303
1336
  const abortControllerRef = useRef(null);
1304
- const apiKeys = React3.useMemo(
1337
+ const apiKeys = React6.useMemo(
1305
1338
  () => config.apiKeys ?? [],
1306
1339
  [config.apiKeys]
1307
1340
  );
@@ -1471,6 +1504,6 @@ var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
1471
1504
  return /* @__PURE__ */ jsx(PlaygroundContext.Provider, { value: contextValue, children });
1472
1505
  }, "PlaygroundProvider");
1473
1506
 
1474
- 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 };
1475
- //# sourceMappingURL=chunk-2BBXP3DH.mjs.map
1476
- //# sourceMappingURL=chunk-2BBXP3DH.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