@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,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkWGEGR3DF_cjs = require('./chunk-WGEGR3DF.cjs');
4
- var React3 = require('react');
4
+ var React6 = require('react');
5
5
  require('@djangocfg/ui-core/styles/palette');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var ReactMarkdown = require('react-markdown');
@@ -15,28 +15,13 @@ var openapiSampler = require('openapi-sampler');
15
15
 
16
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
17
 
18
- var React3__default = /*#__PURE__*/_interopDefault(React3);
18
+ var React6__default = /*#__PURE__*/_interopDefault(React6);
19
19
  var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
20
20
  var rehypeRaw__default = /*#__PURE__*/_interopDefault(rehypeRaw);
21
21
  var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
22
22
  var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
23
23
  var consola2__default = /*#__PURE__*/_interopDefault(consola2);
24
24
 
25
- var MermaidClient = React3.lazy(() => import('./Mermaid.client-RSWUUHIL.cjs'));
26
- var LoadingFallback = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center min-h-[100px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-primary" }) }), "LoadingFallback");
27
- var Mermaid = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
28
- return /* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsxRuntime.jsx(MermaidClient, { ...props }) });
29
- }, "Mermaid");
30
- var Mermaid_default = Mermaid;
31
- var PrettyCodeClient = React3.lazy(() => import('./PrettyCode.client-RPDIE5CH.cjs'));
32
- var LoadingFallback2 = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative rounded-sm border border-border overflow-hidden bg-muted dark:bg-zinc-900", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
33
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-pulse h-4 w-4 rounded-full bg-muted-foreground/20" }),
34
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Loading code..." })
35
- ] }) }) }), "LoadingFallback");
36
- var PrettyCode = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
37
- return /* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback2, {}), children: /* @__PURE__ */ jsxRuntime.jsx(PrettyCodeClient, { ...props }) });
38
- }, "PrettyCode");
39
- var PrettyCode_default = PrettyCode;
40
25
  function smartTruncate(content, maxLength) {
41
26
  if (content.length <= maxLength) {
42
27
  return content;
@@ -110,10 +95,10 @@ function fixUnclosedMarkdown(content) {
110
95
  chunkWGEGR3DF_cjs.__name(fixUnclosedMarkdown, "fixUnclosedMarkdown");
111
96
  function useCollapsibleContent(content, options = {}) {
112
97
  const { maxLength, maxLines, defaultExpanded = false } = options;
113
- const [isCollapsed, setIsCollapsed] = React3.useState(!defaultExpanded);
98
+ const [isCollapsed, setIsCollapsed] = React6.useState(!defaultExpanded);
114
99
  const originalLength = content.length;
115
100
  const originalLineCount = content.split("\n").length;
116
- const { shouldCollapse, truncatedContent } = React3.useMemo(() => {
101
+ const { shouldCollapse, truncatedContent } = React6.useMemo(() => {
117
102
  if (maxLength === void 0 && maxLines === void 0) {
118
103
  return { shouldCollapse: false, truncatedContent: content };
119
104
  }
@@ -129,16 +114,16 @@ function useCollapsibleContent(content, options = {}) {
129
114
  }
130
115
  return { shouldCollapse: needsCollapse, truncatedContent: result };
131
116
  }, [content, maxLength, maxLines, originalLineCount]);
132
- const displayContent = React3.useMemo(() => {
117
+ const displayContent = React6.useMemo(() => {
133
118
  if (!shouldCollapse || !isCollapsed) {
134
119
  return content;
135
120
  }
136
121
  return truncatedContent;
137
122
  }, [content, truncatedContent, shouldCollapse, isCollapsed]);
138
- const toggleCollapsed = React3.useCallback(() => {
123
+ const toggleCollapsed = React6.useCallback(() => {
139
124
  setIsCollapsed((prev) => !prev);
140
125
  }, []);
141
- const setCollapsed = React3.useCallback((collapsed) => {
126
+ const setCollapsed = React6.useCallback((collapsed) => {
142
127
  setIsCollapsed(collapsed);
143
128
  }, []);
144
129
  return {
@@ -152,6 +137,72 @@ function useCollapsibleContent(content, options = {}) {
152
137
  };
153
138
  }
154
139
  chunkWGEGR3DF_cjs.__name(useCollapsibleContent, "useCollapsibleContent");
140
+ function extractTextFromChildren(children) {
141
+ if (typeof children === "string") return children;
142
+ if (typeof children === "number") return String(children);
143
+ if (React6__default.default.isValidElement(children)) {
144
+ const props = children.props;
145
+ return extractTextFromChildren(props.children);
146
+ }
147
+ if (Array.isArray(children)) {
148
+ return children.map(extractTextFromChildren).join("");
149
+ }
150
+ return "";
151
+ }
152
+ chunkWGEGR3DF_cjs.__name(extractTextFromChildren, "extractTextFromChildren");
153
+ function hasMarkdownSyntax(text) {
154
+ if (text.trim().includes("\n")) return true;
155
+ if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) return true;
156
+ const patterns = [
157
+ /^#{1,6}\s/m,
158
+ // Headers
159
+ /\*\*[^*]+\*\*/,
160
+ // Bold
161
+ /\*[^*]+\*/,
162
+ // Italic
163
+ /__[^_]+__/,
164
+ // Bold (underscore)
165
+ /_[^_]+_/,
166
+ // Italic (underscore)
167
+ /\[.+\]\(.+\)/,
168
+ // Links
169
+ /!\[.*\]\(.+\)/,
170
+ // Images
171
+ /```[\s\S]*```/,
172
+ // Code blocks
173
+ /`[^`]+`/,
174
+ // Inline code
175
+ /^\s*[-*+]\s/m,
176
+ // Unordered lists
177
+ /^\s*\d+\.\s/m,
178
+ // Ordered lists
179
+ /^\s*>/m,
180
+ // Blockquotes
181
+ /\|.+\|/,
182
+ // Tables
183
+ /^---+$/m,
184
+ // Horizontal rules
185
+ /~~[^~]+~~/
186
+ // Strikethrough
187
+ ];
188
+ return patterns.some((p) => p.test(text));
189
+ }
190
+ chunkWGEGR3DF_cjs.__name(hasMarkdownSyntax, "hasMarkdownSyntax");
191
+ var MermaidClient = React6.lazy(() => import('./Mermaid.client-RSWUUHIL.cjs'));
192
+ var LoadingFallback = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center min-h-[100px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-primary" }) }), "LoadingFallback");
193
+ var Mermaid = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
194
+ return /* @__PURE__ */ jsxRuntime.jsx(React6.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsxRuntime.jsx(MermaidClient, { ...props }) });
195
+ }, "Mermaid");
196
+ var Mermaid_default = Mermaid;
197
+ var PrettyCodeClient = React6.lazy(() => import('./PrettyCode.client-RPDIE5CH.cjs'));
198
+ var LoadingFallback2 = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative rounded-sm border border-border overflow-hidden bg-muted dark:bg-zinc-900", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
199
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-pulse h-4 w-4 rounded-full bg-muted-foreground/20" }),
200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Loading code..." })
201
+ ] }) }) }), "LoadingFallback");
202
+ var PrettyCode = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
203
+ return /* @__PURE__ */ jsxRuntime.jsx(React6.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback2, {}), children: /* @__PURE__ */ jsxRuntime.jsx(PrettyCodeClient, { ...props }) });
204
+ }, "PrettyCode");
205
+ var PrettyCode_default = PrettyCode;
155
206
  var HTML_SCHEMA_BASE = {
156
207
  ...rehypeSanitize.defaultSchema,
157
208
  tagNames: [
@@ -185,22 +236,21 @@ function buildSchema(extraProtocols) {
185
236
  };
186
237
  }
187
238
  chunkWGEGR3DF_cjs.__name(buildSchema, "buildSchema");
188
- var extractTextFromChildren = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((children) => {
189
- if (typeof children === "string") {
190
- return children;
191
- }
192
- if (typeof children === "number") {
193
- return String(children);
194
- }
195
- if (React3__default.default.isValidElement(children)) {
196
- const props = children.props;
197
- return extractTextFromChildren(props.children);
198
- }
199
- if (Array.isArray(children)) {
200
- return children.map(extractTextFromChildren).join("");
201
- }
202
- return "";
203
- }, "extractTextFromChildren");
239
+ function buildUrlTransform(extraProtocols) {
240
+ if (!extraProtocols || extraProtocols.length === 0) return void 0;
241
+ const lower = extraProtocols.map((p) => p.toLowerCase() + ":");
242
+ return (url) => {
243
+ const u = url.trim().toLowerCase();
244
+ for (const p of lower) {
245
+ if (u.startsWith(p)) return url;
246
+ }
247
+ if (/^(https?:|mailto:|tel:|xmpp:|irc:|ircs:|#|\/|\.\/|\.\.\/|\?)/i.test(u) || /^[a-z0-9._~!$&'()*+,;=:@%-]+$/i.test(u)) {
248
+ return url;
249
+ }
250
+ return "";
251
+ };
252
+ }
253
+ chunkWGEGR3DF_cjs.__name(buildUrlTransform, "buildUrlTransform");
204
254
  var CodeBlock = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, language, isUser, isCompact = false }) => {
205
255
  const theme = hooks.useResolvedTheme();
206
256
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group my-3", children: [
@@ -230,25 +280,40 @@ var CodeBlock = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, language, isUs
230
280
  )
231
281
  ] });
232
282
  }, "CodeBlock");
233
- var createMarkdownComponents = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((isUser = false, isCompact = false) => {
283
+ var CodeBlockFallback = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, isUser }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group my-3", children: [
284
+ /* @__PURE__ */ jsxRuntime.jsx(
285
+ components.CopyButton,
286
+ {
287
+ value: code,
288
+ variant: "ghost",
289
+ className: `
290
+ absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
291
+ h-8 w-8
292
+ ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
293
+ `,
294
+ title: "Copy code"
295
+ }
296
+ ),
297
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: `
298
+ p-3 rounded text-xs font-mono overflow-x-auto
299
+ ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
300
+ `, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) })
301
+ ] }), "CodeBlockFallback");
302
+ function createMarkdownComponents(isUser = false, isCompact = false) {
234
303
  const textSize = isCompact ? "text-xs" : "text-sm";
235
304
  const headingBase = isCompact ? "text-sm" : "text-base";
236
305
  const headingSm = isCompact ? "text-xs" : "text-sm";
237
306
  return {
238
- // Headings - scaled for chat context
239
307
  h1: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h1", { className: `${headingBase} font-bold mb-2 mt-3 first:mt-0`, children }), "h1"),
240
308
  h2: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { className: `${headingSm} font-bold mb-2 mt-3 first:mt-0`, children }), "h2"),
241
309
  h3: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h3"),
242
310
  h4: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h4", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h4"),
243
311
  h5: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h5", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h5"),
244
312
  h6: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h6", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h6"),
245
- // Paragraphs - optimized for chat readability
246
313
  p: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${textSize} mb-4 last:mb-0 leading-7 break-words font-light`, children }), "p"),
247
- // Lists - compact
248
314
  ul: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
249
315
  ol: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
250
316
  li: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "break-words", children }), "li"),
251
- // Links - appropriate for chat context
252
317
  a: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx(
253
318
  "a",
254
319
  {
@@ -259,11 +324,10 @@ var createMarkdownComponents = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((isUser
259
324
  children
260
325
  }
261
326
  ), "a"),
262
- // Code blocks - using CodeBlock component with copy functionality
263
327
  pre: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => {
264
328
  let codeContent = "";
265
329
  let language = "plaintext";
266
- if (React3__default.default.isValidElement(children)) {
330
+ if (React6__default.default.isValidElement(children)) {
267
331
  const child = children;
268
332
  if (child.type === "code" || typeof child.type === "function" && child.type.name === "code") {
269
333
  const codeProps = child.props;
@@ -286,92 +350,28 @@ var createMarkdownComponents = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((isUser
286
350
  return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
287
351
  } catch (error) {
288
352
  console.warn("CodeBlock failed, using fallback:", error);
289
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group my-3", children: [
290
- /* @__PURE__ */ jsxRuntime.jsx(
291
- components.CopyButton,
292
- {
293
- value: codeContent,
294
- variant: "ghost",
295
- className: `
296
- absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
297
- h-8 w-8
298
- ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
299
- `,
300
- title: "Copy code"
301
- }
302
- ),
303
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: `
304
- p-3 rounded text-xs font-mono overflow-x-auto
305
- ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
306
- `, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: codeContent }) })
307
- ] });
353
+ return /* @__PURE__ */ jsxRuntime.jsx(CodeBlockFallback, { code: codeContent, language, isUser, isCompact });
308
354
  }
309
355
  }, "pre"),
310
- // Inline code
311
356
  code: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children, className }) => {
312
357
  if (className?.includes("language-")) {
313
358
  return /* @__PURE__ */ jsxRuntime.jsx("code", { className, children });
314
359
  }
315
- const codeContent = extractTextFromChildren(children);
316
- return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "px-1.5 py-0.5 rounded text-xs font-mono bg-muted text-foreground break-all", children: codeContent });
360
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "px-1.5 py-0.5 rounded text-xs font-mono bg-muted text-foreground break-all", children: extractTextFromChildren(children) });
317
361
  }, "code"),
318
- // Blockquotes
319
362
  blockquote: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { className: `${textSize} border-l-2 border-border pl-3 my-2 italic text-muted-foreground break-words`, children }), "blockquote"),
320
- // Tables - compact for chat
321
363
  table: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto my-3", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: `min-w-full ${textSize} border-collapse`, children }) }), "table"),
322
364
  thead: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-muted/50", children }), "thead"),
323
365
  tbody: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { children }), "tbody"),
324
366
  tr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-border/50", children }), "tr"),
325
367
  th: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-2 py-1 text-left font-medium break-words", children }), "th"),
326
368
  td: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-2 py-1 break-words", children }), "td"),
327
- // Horizontal rule
328
369
  hr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "my-3 border-0 h-px bg-border" }), "hr"),
329
- // Strong and emphasis
330
370
  strong: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "font-semibold", children }), "strong"),
331
371
  em: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { className: "italic", children }), "em")
332
372
  };
333
- }, "createMarkdownComponents");
334
- var hasMarkdownSyntax = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((text) => {
335
- if (text.trim().includes("\n")) {
336
- return true;
337
- }
338
- if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) {
339
- return true;
340
- }
341
- const markdownPatterns = [
342
- /^#{1,6}\s/m,
343
- // Headers
344
- /\*\*[^*]+\*\*/,
345
- // Bold
346
- /\*[^*]+\*/,
347
- // Italic
348
- /__[^_]+__/,
349
- // Bold (underscore)
350
- /_[^_]+_/,
351
- // Italic (underscore)
352
- /\[.+\]\(.+\)/,
353
- // Links
354
- /!\[.*\]\(.+\)/,
355
- // Images
356
- /```[\s\S]*```/,
357
- // Code blocks
358
- /`[^`]+`/,
359
- // Inline code
360
- /^\s*[-*+]\s/m,
361
- // Unordered lists
362
- /^\s*\d+\.\s/m,
363
- // Ordered lists
364
- /^\s*>/m,
365
- // Blockquotes
366
- /\|.+\|/,
367
- // Tables
368
- /^---+$/m,
369
- // Horizontal rules
370
- /~~[^~]+~~/
371
- // Strikethrough
372
- ];
373
- return markdownPatterns.some((pattern) => pattern.test(text));
374
- }, "hasMarkdownSyntax");
373
+ }
374
+ chunkWGEGR3DF_cjs.__name(createMarkdownComponents, "createMarkdownComponents");
375
375
  var CollapseToggle = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
376
376
  isCollapsed,
377
377
  onClick,
@@ -395,48 +395,77 @@ var CollapseToggle = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
395
395
  `,
396
396
  children: isCollapsed ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
397
397
  readMoreLabel,
398
- /* @__PURE__ */ jsxRuntime.jsx(
399
- "svg",
400
- {
401
- className: "w-3 h-3",
402
- fill: "none",
403
- stroke: "currentColor",
404
- viewBox: "0 0 24 24",
405
- children: /* @__PURE__ */ jsxRuntime.jsx(
406
- "path",
407
- {
408
- strokeLinecap: "round",
409
- strokeLinejoin: "round",
410
- strokeWidth: 2,
411
- d: "M19 9l-7 7-7-7"
412
- }
413
- )
414
- }
415
- )
398
+ /* @__PURE__ */ jsxRuntime.jsx(Chevron, { direction: "down" })
416
399
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
417
400
  showLessLabel,
418
- /* @__PURE__ */ jsxRuntime.jsx(
419
- "svg",
420
- {
421
- className: "w-3 h-3",
422
- fill: "none",
423
- stroke: "currentColor",
424
- viewBox: "0 0 24 24",
425
- children: /* @__PURE__ */ jsxRuntime.jsx(
426
- "path",
427
- {
428
- strokeLinecap: "round",
429
- strokeLinejoin: "round",
430
- strokeWidth: 2,
431
- d: "M5 15l7-7 7 7"
432
- }
433
- )
434
- }
435
- )
401
+ /* @__PURE__ */ jsxRuntime.jsx(Chevron, { direction: "up" })
436
402
  ] })
437
403
  }
438
404
  );
439
405
  }, "CollapseToggle");
406
+ function Chevron({ direction }) {
407
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx(
408
+ "path",
409
+ {
410
+ strokeLinecap: "round",
411
+ strokeLinejoin: "round",
412
+ strokeWidth: 2,
413
+ d: direction === "down" ? "M19 9l-7 7-7-7" : "M5 15l7-7 7 7"
414
+ }
415
+ ) });
416
+ }
417
+ chunkWGEGR3DF_cjs.__name(Chevron, "Chevron");
418
+ function applyPreprocess(source, rules) {
419
+ if (!rules || rules.length === 0) return source;
420
+ let s = source;
421
+ for (const rule of rules) {
422
+ if (!rule.preprocess) continue;
423
+ try {
424
+ s = rule.preprocess(s);
425
+ } catch (err) {
426
+ console.warn(
427
+ `[MarkdownMessage] linkRule "${rule.name ?? "(anonymous)"}" preprocess threw; skipping`,
428
+ err
429
+ );
430
+ }
431
+ }
432
+ return s;
433
+ }
434
+ chunkWGEGR3DF_cjs.__name(applyPreprocess, "applyPreprocess");
435
+ function collectProtocols(extraHrefProtocols, rules) {
436
+ const set = /* @__PURE__ */ new Set();
437
+ if (extraHrefProtocols) for (const p of extraHrefProtocols) set.add(p);
438
+ if (rules) {
439
+ for (const r of rules) {
440
+ if (r.protocols) for (const p of r.protocols) set.add(p);
441
+ }
442
+ }
443
+ return set.size === 0 ? void 0 : Array.from(set);
444
+ }
445
+ chunkWGEGR3DF_cjs.__name(collectProtocols, "collectProtocols");
446
+ function buildLinkRulesComponent(rules, isUser, callerA) {
447
+ const Renderer = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
448
+ const { href, children } = props;
449
+ if (typeof href === "string") {
450
+ for (const rule of rules) {
451
+ if (rule.match(href)) {
452
+ return React6__default.default.createElement(
453
+ React6__default.default.Fragment,
454
+ null,
455
+ rule.render({ href, children, isUser })
456
+ );
457
+ }
458
+ }
459
+ }
460
+ if (callerA && typeof callerA === "function") {
461
+ const Caller = callerA;
462
+ return React6__default.default.createElement(Caller, props);
463
+ }
464
+ return React6__default.default.createElement("a", props, children);
465
+ }, "Renderer");
466
+ return Renderer;
467
+ }
468
+ chunkWGEGR3DF_cjs.__name(buildLinkRulesComponent, "buildLinkRulesComponent");
440
469
  var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
441
470
  content,
442
471
  className = "",
@@ -444,6 +473,7 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
444
473
  isCompact = false,
445
474
  customComponents,
446
475
  extraHrefProtocols,
476
+ linkRules,
447
477
  collapsible = false,
448
478
  maxLength,
449
479
  maxLines,
@@ -452,53 +482,71 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
452
482
  defaultExpanded = false,
453
483
  onCollapseChange
454
484
  }) => {
455
- const trimmedContent = content.trim();
456
- const collapsibleOptions = React3__default.default.useMemo(() => {
485
+ const preprocessed = React6__default.default.useMemo(
486
+ () => applyPreprocess(content, linkRules),
487
+ [content, linkRules]
488
+ );
489
+ const effectiveProtocols = React6__default.default.useMemo(
490
+ () => collectProtocols(extraHrefProtocols, linkRules),
491
+ [extraHrefProtocols, linkRules]
492
+ );
493
+ const effectiveCustomComponents = React6__default.default.useMemo(() => {
494
+ if (!linkRules || linkRules.length === 0) return customComponents;
495
+ const callerA = customComponents?.a;
496
+ const aRenderer = buildLinkRulesComponent(linkRules, isUser, callerA);
497
+ return { ...customComponents ?? {}, a: aRenderer };
498
+ }, [customComponents, linkRules, isUser]);
499
+ const trimmedContent = preprocessed.trim();
500
+ const collapsibleOptions = React6__default.default.useMemo(() => {
457
501
  if (!collapsible) return {};
458
- const effectiveMaxLength = maxLength ?? 1e3;
459
- const effectiveMaxLines = maxLines ?? 10;
460
- return { maxLength: effectiveMaxLength, maxLines: effectiveMaxLines, defaultExpanded };
502
+ return {
503
+ maxLength: maxLength ?? 1e3,
504
+ maxLines: maxLines ?? 10,
505
+ defaultExpanded
506
+ };
461
507
  }, [collapsible, maxLength, maxLines, defaultExpanded]);
462
- const {
463
- isCollapsed,
464
- toggleCollapsed,
465
- displayContent,
466
- shouldCollapse
467
- } = useCollapsibleContent(
468
- trimmedContent,
469
- collapsible ? collapsibleOptions : {}
470
- );
471
- React3__default.default.useEffect(() => {
508
+ const { isCollapsed, toggleCollapsed, displayContent, shouldCollapse } = useCollapsibleContent(trimmedContent, collapsible ? collapsibleOptions : {});
509
+ React6__default.default.useEffect(() => {
472
510
  if (collapsible && shouldCollapse && onCollapseChange) {
473
511
  onCollapseChange(isCollapsed);
474
512
  }
475
513
  }, [isCollapsed, collapsible, shouldCollapse, onCollapseChange]);
476
- const components = React3__default.default.useMemo(() => {
514
+ const components = React6__default.default.useMemo(() => {
477
515
  const base = createMarkdownComponents(isUser, isCompact);
478
- return customComponents ? { ...base, ...customComponents } : base;
479
- }, [isUser, isCompact, customComponents]);
480
- const schema = React3__default.default.useMemo(() => buildSchema(extraHrefProtocols), [extraHrefProtocols]);
516
+ return effectiveCustomComponents ? { ...base, ...effectiveCustomComponents } : base;
517
+ }, [isUser, isCompact, effectiveCustomComponents]);
518
+ const schema = React6__default.default.useMemo(() => buildSchema(effectiveProtocols), [effectiveProtocols]);
519
+ const urlTransform = React6__default.default.useMemo(
520
+ () => buildUrlTransform(effectiveProtocols),
521
+ [effectiveProtocols]
522
+ );
481
523
  const textSizeClass = isCompact ? "text-xs" : "text-sm";
482
524
  const proseClass = isCompact ? "prose-xs" : "prose-sm";
483
- const isPlainText = !customComponents && !hasMarkdownSyntax(displayContent);
525
+ const isPlainText = !effectiveCustomComponents && !hasMarkdownSyntax(displayContent);
484
526
  if (isPlainText) {
485
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${textSizeClass} leading-7 break-words whitespace-pre-line font-light ${className}`, children: [
486
- displayContent,
487
- collapsible && shouldCollapse && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
488
- isCollapsed && "... ",
489
- /* @__PURE__ */ jsxRuntime.jsx(
490
- CollapseToggle,
491
- {
492
- isCollapsed,
493
- onClick: toggleCollapsed,
494
- readMoreLabel,
495
- showLessLabel,
496
- isUser,
497
- isCompact
498
- }
499
- )
500
- ] })
501
- ] });
527
+ return /* @__PURE__ */ jsxRuntime.jsxs(
528
+ "span",
529
+ {
530
+ className: `${textSizeClass} leading-7 break-words whitespace-pre-line font-light ${className}`,
531
+ children: [
532
+ displayContent,
533
+ collapsible && shouldCollapse && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
534
+ isCollapsed && "... ",
535
+ /* @__PURE__ */ jsxRuntime.jsx(
536
+ CollapseToggle,
537
+ {
538
+ isCollapsed,
539
+ onClick: toggleCollapsed,
540
+ readMoreLabel,
541
+ showLessLabel,
542
+ isUser,
543
+ isCompact
544
+ }
545
+ )
546
+ ] })
547
+ ]
548
+ }
549
+ );
502
550
  }
503
551
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
504
552
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -510,7 +558,8 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
510
558
  [&>*]:leading-7
511
559
  `,
512
560
  style: {
513
- // Inherit colors from parent - fixes issues with external CSS variables
561
+ // Inherit colors from parent fixes issues with external
562
+ // CSS variables overriding prose tokens.
514
563
  "--tw-prose-body": "inherit",
515
564
  "--tw-prose-headings": "inherit",
516
565
  "--tw-prose-bold": "inherit",
@@ -523,6 +572,7 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
523
572
  remarkPlugins: [remarkGfm__default.default],
524
573
  rehypePlugins: [rehypeRaw__default.default, [rehypeSanitize__default.default, schema]],
525
574
  components,
575
+ urlTransform,
526
576
  children: displayContent
527
577
  }
528
578
  )
@@ -1286,16 +1336,16 @@ function reducer(state, action) {
1286
1336
  }
1287
1337
  }
1288
1338
  chunkWGEGR3DF_cjs.__name(reducer, "reducer");
1289
- var PlaygroundContext = React3.createContext(void 0);
1339
+ var PlaygroundContext = React6.createContext(void 0);
1290
1340
  var usePlaygroundContext = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
1291
- const context = React3.useContext(PlaygroundContext);
1341
+ const context = React6.useContext(PlaygroundContext);
1292
1342
  if (!context) throw new Error("usePlaygroundContext must be used within a PlaygroundProvider");
1293
1343
  return context;
1294
1344
  }, "usePlaygroundContext");
1295
1345
  var PlaygroundProvider = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children, config }) => {
1296
- const [state, dispatch] = React3.useReducer(reducer, void 0, createInitialState);
1297
- const abortControllerRef = React3.useRef(null);
1298
- const apiKeys = React3__default.default.useMemo(
1346
+ const [state, dispatch] = React6.useReducer(reducer, void 0, createInitialState);
1347
+ const abortControllerRef = React6.useRef(null);
1348
+ const apiKeys = React6__default.default.useMemo(
1299
1349
  () => config.apiKeys ?? [],
1300
1350
  [config.apiKeys]
1301
1351
  );
@@ -1308,28 +1358,28 @@ var PlaygroundProvider = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children, c
1308
1358
  AUTH_BEARER_STORAGE,
1309
1359
  ""
1310
1360
  );
1311
- const hasHydratedAuthRef = React3.useRef(false);
1312
- React3.useEffect(() => {
1361
+ const hasHydratedAuthRef = React6.useRef(false);
1362
+ React6.useEffect(() => {
1313
1363
  if (hasHydratedAuthRef.current) return;
1314
1364
  hasHydratedAuthRef.current = true;
1315
1365
  if (storedApiKeyId) dispatch({ type: "SET_API_KEY", apiKeyId: storedApiKeyId });
1316
1366
  if (storedBearer) dispatch({ type: "SET_MANUAL_TOKEN", token: storedBearer });
1317
1367
  }, []);
1318
- React3.useEffect(() => {
1368
+ React6.useEffect(() => {
1319
1369
  if (!hasHydratedAuthRef.current) return;
1320
1370
  setStoredApiKeyId(state.selectedApiKey);
1321
1371
  }, [state.selectedApiKey, setStoredApiKeyId]);
1322
- React3.useEffect(() => {
1372
+ React6.useEffect(() => {
1323
1373
  if (!hasHydratedAuthRef.current) return;
1324
1374
  setStoredBearer(state.manualApiToken);
1325
1375
  }, [state.manualApiToken, setStoredBearer]);
1326
- React3.useEffect(() => {
1376
+ React6.useEffect(() => {
1327
1377
  if (!hasHydratedAuthRef.current) return;
1328
1378
  if (!isLoadingApiKeys && apiKeys.length > 0 && !state.selectedApiKey && !storedApiKeyId) {
1329
1379
  dispatch({ type: "SET_API_KEY", apiKeyId: apiKeys[0]?.id || null });
1330
1380
  }
1331
1381
  }, [apiKeys, isLoadingApiKeys, state.selectedApiKey, storedApiKeyId]);
1332
- React3.useEffect(() => {
1382
+ React6.useEffect(() => {
1333
1383
  try {
1334
1384
  const headers = parseRequestHeaders(state.requestHeaders);
1335
1385
  if (state.selectedApiKey) {
@@ -1351,33 +1401,33 @@ var PlaygroundProvider = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children, c
1351
1401
  consola2__default.default.error("Error updating headers:", error);
1352
1402
  }
1353
1403
  }, [state.selectedApiKey, apiKeys]);
1354
- React3.useEffect(() => {
1404
+ React6.useEffect(() => {
1355
1405
  if (!state.selectedEndpoint) return;
1356
1406
  const updated = new UrlBuilder(state.selectedEndpoint, state.parameters).build();
1357
1407
  if (updated !== state.requestUrl) {
1358
1408
  dispatch({ type: "SYNC_URL", url: updated });
1359
1409
  }
1360
1410
  }, [state.parameters, state.selectedEndpoint]);
1361
- const setCurrentStep = React3.useCallback((step) => dispatch({ type: "SET_STEP", step }), []);
1362
- const goToNextStep = React3.useCallback(() => dispatch({ type: "NEXT_STEP" }), []);
1363
- const goToPreviousStep = React3.useCallback(() => dispatch({ type: "PREV_STEP" }), []);
1364
- const setSelectedEndpoint = React3.useCallback((endpoint) => dispatch({ type: "SELECT_ENDPOINT", endpoint }), []);
1365
- const setSelectedCategory = React3.useCallback((category) => dispatch({ type: "SET_CATEGORY", category }), []);
1366
- const setSearchTerm = React3.useCallback((term) => dispatch({ type: "SET_SEARCH", term }), []);
1367
- const setSelectedVersion = React3.useCallback((version) => dispatch({ type: "SET_VERSION", version }), []);
1368
- const setRequestUrl = React3.useCallback((url) => dispatch({ type: "SET_REQUEST_URL", url }), []);
1369
- const setRequestMethod = React3.useCallback((method) => dispatch({ type: "SET_REQUEST_METHOD", method }), []);
1370
- const setRequestHeaders = React3.useCallback((headers) => dispatch({ type: "SET_REQUEST_HEADERS", headers }), []);
1371
- const setRequestBody = React3.useCallback((body) => dispatch({ type: "SET_REQUEST_BODY", body }), []);
1372
- const setSelectedApiKey = React3.useCallback((apiKeyId) => dispatch({ type: "SET_API_KEY", apiKeyId }), []);
1373
- const setManualApiToken = React3.useCallback((token) => dispatch({ type: "SET_MANUAL_TOKEN", token }), []);
1374
- const setParameters = React3.useCallback((parameters) => dispatch({ type: "SET_PARAMETERS", parameters }), []);
1375
- const setResponse = React3.useCallback((response) => dispatch({ type: "SET_RESPONSE", response }), []);
1376
- const setLoading = React3.useCallback((loading) => dispatch({ type: "SET_LOADING", loading }), []);
1377
- const setSidebarOpen = React3.useCallback((open) => dispatch({ type: "SET_SIDEBAR", open }), []);
1378
- const setActiveSchemaId = React3.useCallback((id) => dispatch({ type: "SET_ACTIVE_SCHEMA_ID", id }), []);
1379
- const clearAll = React3.useCallback(() => dispatch({ type: "RESET" }), []);
1380
- const sendRequest = React3.useCallback(async () => {
1411
+ const setCurrentStep = React6.useCallback((step) => dispatch({ type: "SET_STEP", step }), []);
1412
+ const goToNextStep = React6.useCallback(() => dispatch({ type: "NEXT_STEP" }), []);
1413
+ const goToPreviousStep = React6.useCallback(() => dispatch({ type: "PREV_STEP" }), []);
1414
+ const setSelectedEndpoint = React6.useCallback((endpoint) => dispatch({ type: "SELECT_ENDPOINT", endpoint }), []);
1415
+ const setSelectedCategory = React6.useCallback((category) => dispatch({ type: "SET_CATEGORY", category }), []);
1416
+ const setSearchTerm = React6.useCallback((term) => dispatch({ type: "SET_SEARCH", term }), []);
1417
+ const setSelectedVersion = React6.useCallback((version) => dispatch({ type: "SET_VERSION", version }), []);
1418
+ const setRequestUrl = React6.useCallback((url) => dispatch({ type: "SET_REQUEST_URL", url }), []);
1419
+ const setRequestMethod = React6.useCallback((method) => dispatch({ type: "SET_REQUEST_METHOD", method }), []);
1420
+ const setRequestHeaders = React6.useCallback((headers) => dispatch({ type: "SET_REQUEST_HEADERS", headers }), []);
1421
+ const setRequestBody = React6.useCallback((body) => dispatch({ type: "SET_REQUEST_BODY", body }), []);
1422
+ const setSelectedApiKey = React6.useCallback((apiKeyId) => dispatch({ type: "SET_API_KEY", apiKeyId }), []);
1423
+ const setManualApiToken = React6.useCallback((token) => dispatch({ type: "SET_MANUAL_TOKEN", token }), []);
1424
+ const setParameters = React6.useCallback((parameters) => dispatch({ type: "SET_PARAMETERS", parameters }), []);
1425
+ const setResponse = React6.useCallback((response) => dispatch({ type: "SET_RESPONSE", response }), []);
1426
+ const setLoading = React6.useCallback((loading) => dispatch({ type: "SET_LOADING", loading }), []);
1427
+ const setSidebarOpen = React6.useCallback((open) => dispatch({ type: "SET_SIDEBAR", open }), []);
1428
+ const setActiveSchemaId = React6.useCallback((id) => dispatch({ type: "SET_ACTIVE_SCHEMA_ID", id }), []);
1429
+ const clearAll = React6.useCallback(() => dispatch({ type: "RESET" }), []);
1430
+ const sendRequest = React6.useCallback(async () => {
1381
1431
  if (!state.requestUrl) {
1382
1432
  consola2__default.default.error("No URL provided");
1383
1433
  return;
@@ -1475,6 +1525,7 @@ exports.buildHarRequest = buildHarRequest;
1475
1525
  exports.deduplicateEndpoints = deduplicateEndpoints;
1476
1526
  exports.dereferenceSchema = dereferenceSchema;
1477
1527
  exports.endpointToMarkdown = endpointToMarkdown;
1528
+ exports.extractTextFromChildren = extractTextFromChildren;
1478
1529
  exports.findApiKeyById = findApiKeyById;
1479
1530
  exports.formatBytes = formatBytes;
1480
1531
  exports.isValidJson = isValidJson;
@@ -1490,5 +1541,5 @@ exports.toMarkdown = toMarkdown;
1490
1541
  exports.toRawJson = toRawJson;
1491
1542
  exports.useCollapsibleContent = useCollapsibleContent;
1492
1543
  exports.usePlaygroundContext = usePlaygroundContext;
1493
- //# sourceMappingURL=chunk-QTO5LWMK.cjs.map
1494
- //# sourceMappingURL=chunk-QTO5LWMK.cjs.map
1544
+ //# sourceMappingURL=chunk-SEXWBCLX.cjs.map
1545
+ //# sourceMappingURL=chunk-SEXWBCLX.cjs.map