@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
@@ -3,9 +3,13 @@ import React6, { lazy, createContext, useState, useMemo, useCallback, Suspense,
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';
6
+ import rehypeExternalLinks from 'rehype-external-links';
6
7
  import rehypeRaw from 'rehype-raw';
7
8
  import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
9
+ import remarkBreaks from 'remark-breaks';
10
+ import remarkEmoji from 'remark-emoji';
8
11
  import remarkGfm from 'remark-gfm';
12
+ import remarkSmartypants from 'remark-smartypants';
9
13
  import { CopyButton } from '@djangocfg/ui-core/components';
10
14
  import { useResolvedTheme, useSessionStorage } from '@djangocfg/ui-core/hooks';
11
15
  import consola2 from 'consola';
@@ -188,7 +192,7 @@ function hasMarkdownSyntax(text) {
188
192
  return patterns.some((p) => p.test(text));
189
193
  }
190
194
  __name(hasMarkdownSyntax, "hasMarkdownSyntax");
191
- var MermaidClient = lazy(() => import('./Mermaid.client-XFQ74OYN.mjs'));
195
+ var MermaidClient = lazy(() => import('./Mermaid.client-SXRRI2YW.mjs'));
192
196
  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");
193
197
  var Mermaid = /* @__PURE__ */ __name((props) => {
194
198
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsx(MermaidClient, { ...props }) });
@@ -251,39 +255,38 @@ function buildUrlTransform(extraProtocols) {
251
255
  };
252
256
  }
253
257
  __name(buildUrlTransform, "buildUrlTransform");
254
- var CodeBlock = /* @__PURE__ */ __name(({ code, language, isUser, isCompact = false }) => {
258
+ var CodeBlock = /* @__PURE__ */ __name(({ code, language, isCompact = false }) => {
255
259
  const theme = useResolvedTheme();
260
+ const textSizeClass = isCompact ? "text-xs" : "text-sm";
256
261
  return /* @__PURE__ */ jsx("div", { className: "my-3", children: /* @__PURE__ */ jsx(
257
262
  PrettyCode_default,
258
263
  {
259
264
  data: code,
260
265
  language,
261
- className: isCompact ? "text-xs" : "text-sm",
262
- customBg: isUser ? "bg-white/10" : "bg-muted dark:bg-muted",
266
+ className: textSizeClass,
267
+ customBg: "bg-code",
263
268
  mode: theme,
264
- isCompact
269
+ isCompact,
270
+ scrollIsolation: false
265
271
  }
266
272
  ) });
267
273
  }, "CodeBlock");
268
- var CodeBlockFallback = /* @__PURE__ */ __name(({ code, isUser }) => /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
269
- /* @__PURE__ */ jsx(
270
- CopyButton,
271
- {
272
- value: code,
273
- variant: "ghost",
274
- className: `
275
- absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity
276
- h-8 w-8
277
- ${isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground"}
278
- `,
279
- title: "Copy code"
280
- }
281
- ),
282
- /* @__PURE__ */ jsx("pre", { className: `
283
- p-3 rounded text-xs font-mono overflow-x-auto
284
- ${isUser ? "bg-white/10 text-white" : "bg-muted text-foreground"}
285
- `, children: /* @__PURE__ */ jsx("code", { children: code }) })
286
- ] }), "CodeBlockFallback");
274
+ var CodeBlockFallback = /* @__PURE__ */ __name(({ code, isUser }) => {
275
+ const copyHoverClass = isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground";
276
+ const copyButtonClass = `absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity h-8 w-8 ${copyHoverClass}`;
277
+ return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
278
+ /* @__PURE__ */ jsx(
279
+ CopyButton,
280
+ {
281
+ value: code,
282
+ variant: "ghost",
283
+ className: copyButtonClass,
284
+ title: "Copy code"
285
+ }
286
+ ),
287
+ /* @__PURE__ */ jsx("pre", { className: "p-3 rounded text-xs font-mono overflow-x-auto bg-code text-code-foreground border border-code-border", children: /* @__PURE__ */ jsx("code", { children: code }) })
288
+ ] });
289
+ }, "CodeBlockFallback");
287
290
  function createMarkdownComponents(isUser = false, isCompact = false) {
288
291
  const textSize = isCompact ? "text-xs" : "text-sm";
289
292
  const headingBase = isCompact ? "text-sm" : "text-base";
@@ -299,13 +302,18 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
299
302
  ul: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
300
303
  ol: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
301
304
  li: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("li", { className: "break-words", children }), "li"),
302
- a: /* @__PURE__ */ __name(({ href, children }) => /* @__PURE__ */ jsx(
305
+ // `target` / `rel` for external links are NOT set here the
306
+ // rehype-external-links plugin tags them on the rehype side, so
307
+ // every `<a>` that sanitize let through gets the same security
308
+ // treatment regardless of which renderer (default vs linkRules
309
+ // override) emitted it. Doing it twice here would just duplicate
310
+ // attributes; doing it only here would miss the linkRules path.
311
+ a: /* @__PURE__ */ __name(({ href, children, ...rest }) => /* @__PURE__ */ jsx(
303
312
  "a",
304
313
  {
314
+ ...rest,
305
315
  href,
306
316
  className: `${textSize} ${isUser ? "text-white/90 underline hover:text-white" : "text-primary underline hover:text-primary/80"} transition-colors break-all`,
307
- target: href?.startsWith("http") ? "_blank" : void 0,
308
- rel: href?.startsWith("http") ? "noopener noreferrer" : void 0,
309
317
  children
310
318
  }
311
319
  ), "a"),
@@ -329,7 +337,7 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
329
337
  return /* @__PURE__ */ jsx("div", { className: "my-3 p-3 bg-muted rounded text-sm text-muted-foreground", children: "No content available" });
330
338
  }
331
339
  if (language === "mermaid") {
332
- return /* @__PURE__ */ jsx("div", { className: "my-3 max-w-full overflow-x-auto", children: /* @__PURE__ */ jsx(Mermaid_default, { chart: codeContent, className: "max-w-[600px] mx-auto", isCompact }) });
340
+ return /* @__PURE__ */ jsx("div", { className: "my-3 w-full", children: /* @__PURE__ */ jsx(Mermaid_default, { chart: codeContent, isCompact }) });
333
341
  }
334
342
  try {
335
343
  return /* @__PURE__ */ jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
@@ -342,16 +350,43 @@ function createMarkdownComponents(isUser = false, isCompact = false) {
342
350
  if (className?.includes("language-")) {
343
351
  return /* @__PURE__ */ jsx("code", { className, children });
344
352
  }
345
- 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) });
353
+ const inlineCodeClass = isUser ? "bg-primary-foreground/15 text-primary-foreground" : "bg-code-inline text-code-inline-foreground";
354
+ return /* @__PURE__ */ jsx("code", { className: `px-1 py-0.5 rounded font-mono text-[0.875em] ${inlineCodeClass} break-all`, children: extractTextFromChildren(children) });
346
355
  }, "code"),
347
- 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"),
356
+ // Modern chat convention drops italic on blockquotes italic +
357
+ // tight bubble = hard to read. Border-left at 2px (4px reads
358
+ // heavy in a 320–480px bubble). On the saturated user bubble we
359
+ // use a primary-foreground tint; on the assistant bubble we use
360
+ // the muted-foreground role for de-emphasis.
361
+ blockquote: /* @__PURE__ */ __name(({ children }) => {
362
+ const cls = isUser ? "border-primary-foreground/40 text-primary-foreground/80" : "border-border text-muted-foreground";
363
+ return /* @__PURE__ */ jsx("blockquote", { className: `${textSize} border-l-2 pl-3 my-3 break-words ${cls}`, children });
364
+ }, "blockquote"),
365
+ // Tables: outer wrapper handles overflow, inner `<table>`
366
+ // inherits the chat-density text size. Borders / header use
367
+ // semantic tokens — `border-code-border` for the assistant
368
+ // (matches the code-fence panel for visual cohesion when both
369
+ // appear in the same reply); primary-foreground/N for the user
370
+ // bubble so lines read against the saturated `bg-primary`.
348
371
  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"),
349
- thead: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("thead", { className: "bg-muted/50", children }), "thead"),
372
+ thead: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("thead", { className: isUser ? "bg-primary-foreground/10" : "bg-muted/40", children }), "thead"),
350
373
  tbody: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tbody", { children }), "tbody"),
351
- tr: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/50", children }), "tr"),
352
- th: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("th", { className: "px-2 py-1 text-left font-medium break-words", children }), "th"),
353
- td: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1 break-words", children }), "td"),
354
- hr: /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("hr", { className: "my-3 border-0 h-px bg-border" }), "hr"),
374
+ tr: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tr", { className: isUser ? "border-b border-primary-foreground/15" : "border-b border-border", children }), "tr"),
375
+ th: /* @__PURE__ */ __name(({ children }) => {
376
+ const borderCls = isUser ? "border-primary-foreground/25" : "border-border";
377
+ return /* @__PURE__ */ jsx("th", { className: `px-2 py-1.5 text-left font-semibold border-b ${borderCls} break-words`, children });
378
+ }, "th"),
379
+ td: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5 break-words", children }), "td"),
380
+ // Soft separator. ChatGPT / Slack / Linear strip the visible
381
+ // line, Claude.ai keeps a hairline. We follow Claude — present
382
+ // but quiet. Palette switches by role so the hairline reads on
383
+ // both surfaces.
384
+ hr: /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx(
385
+ "hr",
386
+ {
387
+ className: `my-4 border-0 h-px ${isUser ? "bg-primary-foreground/20" : "bg-border"}`
388
+ }
389
+ ), "hr"),
355
390
  strong: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }), "strong"),
356
391
  em: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }), "em")
357
392
  };
@@ -547,9 +582,14 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
547
582
  ${isUser ? "prose-invert" : "dark:prose-invert"}
548
583
  [&>*]:leading-relaxed
549
584
  [&>*:first-child]:mt-0 [&>*:last-child]:mb-0
550
- [&_p]:my-0 [&_p+p]:mt-2
551
- [&_ul]:my-2 [&_ol]:my-2 [&_pre]:my-2 [&_blockquote]:my-2
552
- [&_h1]:mt-3 [&_h1]:mb-1 [&_h2]:mt-3 [&_h2]:mb-1 [&_h3]:mt-2 [&_h3]:mb-1
585
+ [&_p]:my-2
586
+ [&_ul]:my-2 [&_ol]:my-2 [&_ul]:pl-5 [&_ol]:pl-5
587
+ [&_li]:my-1 [&_li>p]:my-0
588
+ [&_pre]:my-3
589
+ [&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold
590
+ [&_h2]:mt-3.5 [&_h2]:mb-1.5 [&_h2]:text-[15px] [&_h2]:font-semibold
591
+ [&_h3]:mt-3 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-medium
592
+ [&_h4]:mt-3 [&_h4]:mb-1 [&_h4]:text-sm [&_h4]:font-medium
553
593
  `,
554
594
  style: {
555
595
  // Inherit colors from parent — fixes issues with external
@@ -563,8 +603,12 @@ var MarkdownMessage = /* @__PURE__ */ __name(({
563
603
  children: /* @__PURE__ */ jsx(
564
604
  ReactMarkdown,
565
605
  {
566
- remarkPlugins: [remarkGfm],
567
- rehypePlugins: [rehypeRaw, [rehypeSanitize, schema]],
606
+ remarkPlugins: [remarkGfm, remarkBreaks, remarkSmartypants, remarkEmoji],
607
+ rehypePlugins: [
608
+ rehypeRaw,
609
+ [rehypeSanitize, schema],
610
+ [rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }]
611
+ ],
568
612
  components,
569
613
  urlTransform,
570
614
  children: displayContent
@@ -1510,5 +1554,5 @@ var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
1510
1554
  }, "PlaygroundProvider");
1511
1555
 
1512
1556
  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 };
1513
- //# sourceMappingURL=chunk-47NGNO5U.mjs.map
1514
- //# sourceMappingURL=chunk-47NGNO5U.mjs.map
1557
+ //# sourceMappingURL=chunk-6HNAPVZ2.mjs.map
1558
+ //# sourceMappingURL=chunk-6HNAPVZ2.mjs.map