@djangocfg/ui-tools 2.1.302 → 2.1.304

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 (30) hide show
  1. package/README.md +1 -1
  2. package/dist/{DocsLayout-ZHNRRAKR.mjs → DocsLayout-W5JLRNSZ.mjs} +3 -3
  3. package/dist/{DocsLayout-ZHNRRAKR.mjs.map → DocsLayout-W5JLRNSZ.mjs.map} +1 -1
  4. package/dist/{DocsLayout-4PQLBZHE.cjs → DocsLayout-ZXD2CUOH.cjs} +48 -48
  5. package/dist/{DocsLayout-4PQLBZHE.cjs.map → DocsLayout-ZXD2CUOH.cjs.map} +1 -1
  6. package/dist/{Mermaid.client-XFQ74OYN.mjs → Mermaid.client-SXRRI2YW.mjs} +43 -6
  7. package/dist/Mermaid.client-SXRRI2YW.mjs.map +1 -0
  8. package/dist/{Mermaid.client-RSWUUHIL.cjs → Mermaid.client-W76R5AKJ.cjs} +43 -6
  9. package/dist/Mermaid.client-W76R5AKJ.cjs.map +1 -0
  10. package/dist/{chunk-47NGNO5U.mjs → chunk-6HNAPVZ2.mjs} +86 -42
  11. package/dist/chunk-6HNAPVZ2.mjs.map +1 -0
  12. package/dist/{chunk-M4BLG3RZ.cjs → chunk-FYLR232K.cjs} +90 -42
  13. package/dist/chunk-FYLR232K.cjs.map +1 -0
  14. package/dist/index.cjs +11 -11
  15. package/dist/index.d.cts +7 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.mjs +5 -5
  18. package/package.json +10 -6
  19. package/src/components/markdown/MarkdownMessage/CodeBlock.tsx +43 -26
  20. package/src/components/markdown/MarkdownMessage/MarkdownMessage.story.tsx +311 -0
  21. package/src/components/markdown/MarkdownMessage/MarkdownMessage.tsx +35 -5
  22. package/src/components/markdown/MarkdownMessage/README.md +111 -0
  23. package/src/components/markdown/MarkdownMessage/components.tsx +77 -17
  24. package/src/tools/Mermaid/Mermaid.client.tsx +10 -1
  25. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +76 -3
  26. package/src/tools/Mermaid/index.tsx +7 -0
  27. package/dist/Mermaid.client-RSWUUHIL.cjs.map +0 -1
  28. package/dist/Mermaid.client-XFQ74OYN.mjs.map +0 -1
  29. package/dist/chunk-47NGNO5U.mjs.map +0 -1
  30. package/dist/chunk-M4BLG3RZ.cjs.map +0 -1
@@ -5,9 +5,13 @@ 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');
8
+ var rehypeExternalLinks = require('rehype-external-links');
8
9
  var rehypeRaw = require('rehype-raw');
9
10
  var rehypeSanitize = require('rehype-sanitize');
11
+ var remarkBreaks = require('remark-breaks');
12
+ var remarkEmoji = require('remark-emoji');
10
13
  var remarkGfm = require('remark-gfm');
14
+ var remarkSmartypants = require('remark-smartypants');
11
15
  var components = require('@djangocfg/ui-core/components');
12
16
  var hooks = require('@djangocfg/ui-core/hooks');
13
17
  var consola2 = require('consola');
@@ -17,9 +21,13 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
21
 
18
22
  var React6__default = /*#__PURE__*/_interopDefault(React6);
19
23
  var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
24
+ var rehypeExternalLinks__default = /*#__PURE__*/_interopDefault(rehypeExternalLinks);
20
25
  var rehypeRaw__default = /*#__PURE__*/_interopDefault(rehypeRaw);
21
26
  var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
27
+ var remarkBreaks__default = /*#__PURE__*/_interopDefault(remarkBreaks);
28
+ var remarkEmoji__default = /*#__PURE__*/_interopDefault(remarkEmoji);
22
29
  var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
30
+ var remarkSmartypants__default = /*#__PURE__*/_interopDefault(remarkSmartypants);
23
31
  var consola2__default = /*#__PURE__*/_interopDefault(consola2);
24
32
 
25
33
  function smartTruncate(content, maxLength) {
@@ -199,7 +207,7 @@ function hasMarkdownSyntax(text) {
199
207
  return patterns.some((p) => p.test(text));
200
208
  }
201
209
  chunkWGEGR3DF_cjs.__name(hasMarkdownSyntax, "hasMarkdownSyntax");
202
- var MermaidClient = React6.lazy(() => import('./Mermaid.client-RSWUUHIL.cjs'));
210
+ var MermaidClient = React6.lazy(() => import('./Mermaid.client-W76R5AKJ.cjs'));
203
211
  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");
204
212
  var Mermaid = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((props) => {
205
213
  return /* @__PURE__ */ jsxRuntime.jsx(React6.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsxRuntime.jsx(MermaidClient, { ...props }) });
@@ -262,39 +270,38 @@ function buildUrlTransform(extraProtocols) {
262
270
  };
263
271
  }
264
272
  chunkWGEGR3DF_cjs.__name(buildUrlTransform, "buildUrlTransform");
265
- var CodeBlock = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, language, isUser, isCompact = false }) => {
273
+ var CodeBlock = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, language, isCompact = false }) => {
266
274
  const theme = hooks.useResolvedTheme();
275
+ const textSizeClass = isCompact ? "text-xs" : "text-sm";
267
276
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-3", children: /* @__PURE__ */ jsxRuntime.jsx(
268
277
  PrettyCode_default,
269
278
  {
270
279
  data: code,
271
280
  language,
272
- className: isCompact ? "text-xs" : "text-sm",
273
- customBg: isUser ? "bg-white/10" : "bg-muted dark:bg-muted",
281
+ className: textSizeClass,
282
+ customBg: "bg-code",
274
283
  mode: theme,
275
- isCompact
284
+ isCompact,
285
+ scrollIsolation: false
276
286
  }
277
287
  ) });
278
288
  }, "CodeBlock");
279
- var CodeBlockFallback = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, isUser }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group my-3", children: [
280
- /* @__PURE__ */ jsxRuntime.jsx(
281
- components.CopyButton,
282
- {
283
- value: code,
284
- variant: "ghost",
285
- className: `
286
- absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
287
- h-8 w-8
288
- ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
289
- `,
290
- title: "Copy code"
291
- }
292
- ),
293
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: `
294
- p-3 rounded text-xs font-mono overflow-x-auto
295
- ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
296
- `, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) })
297
- ] }), "CodeBlockFallback");
289
+ var CodeBlockFallback = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, isUser }) => {
290
+ const copyHoverClass = isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground";
291
+ const copyButtonClass = `absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity h-8 w-8 ${copyHoverClass}`;
292
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group my-3", children: [
293
+ /* @__PURE__ */ jsxRuntime.jsx(
294
+ components.CopyButton,
295
+ {
296
+ value: code,
297
+ variant: "ghost",
298
+ className: copyButtonClass,
299
+ title: "Copy code"
300
+ }
301
+ ),
302
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "p-3 rounded text-xs font-mono overflow-x-auto bg-code text-code-foreground border border-code-border", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) })
303
+ ] });
304
+ }, "CodeBlockFallback");
298
305
  function createMarkdownComponents(isUser = false, isCompact = false) {
299
306
  const textSize = isCompact ? "text-xs" : "text-sm";
300
307
  const headingBase = isCompact ? "text-sm" : "text-base";
@@ -310,13 +317,18 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
310
317
  ul: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
311
318
  ol: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
312
319
  li: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "break-words", children }), "li"),
313
- a: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx(
320
+ // `target` / `rel` for external links are NOT set here the
321
+ // rehype-external-links plugin tags them on the rehype side, so
322
+ // every `<a>` that sanitize let through gets the same security
323
+ // treatment regardless of which renderer (default vs linkRules
324
+ // override) emitted it. Doing it twice here would just duplicate
325
+ // attributes; doing it only here would miss the linkRules path.
326
+ a: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ href, children, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx(
314
327
  "a",
315
328
  {
329
+ ...rest,
316
330
  href,
317
331
  className: `${textSize} ${isUser ? "text-white/90 underline hover:text-white" : "text-primary underline hover:text-primary/80"} transition-colors break-all`,
318
- target: href?.startsWith("http") ? "_blank" : void 0,
319
- rel: href?.startsWith("http") ? "noopener noreferrer" : void 0,
320
332
  children
321
333
  }
322
334
  ), "a"),
@@ -340,7 +352,7 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
340
352
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-3 p-3 bg-muted rounded text-sm text-muted-foreground", children: "No content available" });
341
353
  }
342
354
  if (language === "mermaid") {
343
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-3 max-w-full overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx(Mermaid_default, { chart: codeContent, className: "max-w-[600px] mx-auto", isCompact }) });
355
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-3 w-full", children: /* @__PURE__ */ jsxRuntime.jsx(Mermaid_default, { chart: codeContent, isCompact }) });
344
356
  }
345
357
  try {
346
358
  return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
@@ -353,16 +365,43 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
353
365
  if (className?.includes("language-")) {
354
366
  return /* @__PURE__ */ jsxRuntime.jsx("code", { className, children });
355
367
  }
356
- 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) });
368
+ const inlineCodeClass = isUser ? "bg-primary-foreground/15 text-primary-foreground" : "bg-code-inline text-code-inline-foreground";
369
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: `px-1 py-0.5 rounded font-mono text-[0.875em] ${inlineCodeClass} break-all`, children: extractTextFromChildren(children) });
357
370
  }, "code"),
358
- 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"),
371
+ // Modern chat convention drops italic on blockquotes italic +
372
+ // tight bubble = hard to read. Border-left at 2px (4px reads
373
+ // heavy in a 320–480px bubble). On the saturated user bubble we
374
+ // use a primary-foreground tint; on the assistant bubble we use
375
+ // the muted-foreground role for de-emphasis.
376
+ blockquote: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => {
377
+ const cls = isUser ? "border-primary-foreground/40 text-primary-foreground/80" : "border-border text-muted-foreground";
378
+ return /* @__PURE__ */ jsxRuntime.jsx("blockquote", { className: `${textSize} border-l-2 pl-3 my-3 break-words ${cls}`, children });
379
+ }, "blockquote"),
380
+ // Tables: outer wrapper handles overflow, inner `<table>`
381
+ // inherits the chat-density text size. Borders / header use
382
+ // semantic tokens — `border-code-border` for the assistant
383
+ // (matches the code-fence panel for visual cohesion when both
384
+ // appear in the same reply); primary-foreground/N for the user
385
+ // bubble so lines read against the saturated `bg-primary`.
359
386
  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"),
360
- thead: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-muted/50", children }), "thead"),
387
+ thead: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { className: isUser ? "bg-primary-foreground/10" : "bg-muted/40", children }), "thead"),
361
388
  tbody: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { children }), "tbody"),
362
- tr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-border/50", children }), "tr"),
363
- th: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-2 py-1 text-left font-medium break-words", children }), "th"),
364
- td: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-2 py-1 break-words", children }), "td"),
365
- hr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "my-3 border-0 h-px bg-border" }), "hr"),
389
+ tr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: isUser ? "border-b border-primary-foreground/15" : "border-b border-border", children }), "tr"),
390
+ th: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => {
391
+ const borderCls = isUser ? "border-primary-foreground/25" : "border-border";
392
+ return /* @__PURE__ */ jsxRuntime.jsx("th", { className: `px-2 py-1.5 text-left font-semibold border-b ${borderCls} break-words`, children });
393
+ }, "th"),
394
+ td: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-2 py-1.5 break-words", children }), "td"),
395
+ // Soft separator. ChatGPT / Slack / Linear strip the visible
396
+ // line, Claude.ai keeps a hairline. We follow Claude — present
397
+ // but quiet. Palette switches by role so the hairline reads on
398
+ // both surfaces.
399
+ hr: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx(
400
+ "hr",
401
+ {
402
+ className: `my-4 border-0 h-px ${isUser ? "bg-primary-foreground/20" : "bg-border"}`
403
+ }
404
+ ), "hr"),
366
405
  strong: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "font-semibold", children }), "strong"),
367
406
  em: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { className: "italic", children }), "em")
368
407
  };
@@ -558,9 +597,14 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
558
597
  ${isUser ? "prose-invert" : "dark:prose-invert"}
559
598
  [&>*]:leading-relaxed
560
599
  [&>*:first-child]:mt-0 [&>*:last-child]:mb-0
561
- [&_p]:my-0 [&_p+p]:mt-2
562
- [&_ul]:my-2 [&_ol]:my-2 [&_pre]:my-2 [&_blockquote]:my-2
563
- [&_h1]:mt-3 [&_h1]:mb-1 [&_h2]:mt-3 [&_h2]:mb-1 [&_h3]:mt-2 [&_h3]:mb-1
600
+ [&_p]:my-2
601
+ [&_ul]:my-2 [&_ol]:my-2 [&_ul]:pl-5 [&_ol]:pl-5
602
+ [&_li]:my-1 [&_li>p]:my-0
603
+ [&_pre]:my-3
604
+ [&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold
605
+ [&_h2]:mt-3.5 [&_h2]:mb-1.5 [&_h2]:text-[15px] [&_h2]:font-semibold
606
+ [&_h3]:mt-3 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-medium
607
+ [&_h4]:mt-3 [&_h4]:mb-1 [&_h4]:text-sm [&_h4]:font-medium
564
608
  `,
565
609
  style: {
566
610
  // Inherit colors from parent — fixes issues with external
@@ -574,8 +618,12 @@ var MarkdownMessage = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({
574
618
  children: /* @__PURE__ */ jsxRuntime.jsx(
575
619
  ReactMarkdown__default.default,
576
620
  {
577
- remarkPlugins: [remarkGfm__default.default],
578
- rehypePlugins: [rehypeRaw__default.default, [rehypeSanitize__default.default, schema]],
621
+ remarkPlugins: [remarkGfm__default.default, remarkBreaks__default.default, remarkSmartypants__default.default, remarkEmoji__default.default],
622
+ rehypePlugins: [
623
+ rehypeRaw__default.default,
624
+ [rehypeSanitize__default.default, schema],
625
+ [rehypeExternalLinks__default.default, { target: "_blank", rel: ["noopener", "noreferrer"] }]
626
+ ],
579
627
  components,
580
628
  urlTransform,
581
629
  children: displayContent
@@ -1546,5 +1594,5 @@ exports.toMarkdown = toMarkdown;
1546
1594
  exports.toRawJson = toRawJson;
1547
1595
  exports.useCollapsibleContent = useCollapsibleContent;
1548
1596
  exports.usePlaygroundContext = usePlaygroundContext;
1549
- //# sourceMappingURL=chunk-M4BLG3RZ.cjs.map
1550
- //# sourceMappingURL=chunk-M4BLG3RZ.cjs.map
1597
+ //# sourceMappingURL=chunk-FYLR232K.cjs.map
1598
+ //# sourceMappingURL=chunk-FYLR232K.cjs.map