@djangocfg/ui-tools 2.1.298 → 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-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
package/README.md CHANGED
@@ -28,7 +28,7 @@ This package contains heavy components that are loaded lazily to keep your initi
28
28
  | `@djangocfg/ui-tools` | Heavy tools with lazy loading |
29
29
  | `@djangocfg/ui-nextjs` | Next.js apps (extends ui-core) |
30
30
 
31
- ## Tools (14)
31
+ ## Tools (15)
32
32
 
33
33
  | Tool | Bundle Size | Description |
34
34
  |------|-------------|-------------|
@@ -42,6 +42,7 @@ This package contains heavy components that are loaded lazily to keep your initi
42
42
  | `LottiePlayer` | ~200KB | Lottie animation player |
43
43
  | `AudioPlayer` | ~200KB | Audio player with WaveSurfer.js |
44
44
  | `VideoPlayer` | ~150KB | Professional video player with Vidstack |
45
+ | `MarkdownMessage` | ~120KB | Read-only chat-tuned markdown renderer (GFM, syntax highlighting, mermaid, declarative `linkRules` for custom URL schemes) |
45
46
  | `JsonTree` | ~100KB | JSON visualization with modes (full/compact/inline) |
46
47
  | `Gallery` | ~50KB | Image/video gallery with carousel, grid, lightbox |
47
48
  | `ImageViewer` | ~50KB | Image viewer with zoom/pan/rotate/flip and gallery navigation |
@@ -558,11 +559,134 @@ const schema = {
558
559
  />
559
560
  ```
560
561
 
562
+ ## Markdown Message
563
+
564
+ Read-only markdown renderer tuned for chat / agent transcripts. Built
565
+ on `react-markdown` + `remark-gfm` + `rehype-sanitize`, with a few
566
+ chat-specific affordances on top:
567
+
568
+ - Syntax-highlighted code blocks with a hover-revealed Copy button
569
+ (delegates to `PrettyCode` from this package).
570
+ - Mermaid diagram rendering for ` ```mermaid ` fences.
571
+ - Plain-text fast path: when content has no markdown syntax we skip
572
+ ReactMarkdown entirely and just render the string with
573
+ `whitespace: pre-line`. Cheaper, identical visual.
574
+ - Optional collapsible "Read more..." for long messages.
575
+ - User vs assistant styling modes (`isUser` prop).
576
+ - **`linkRules` API** — declarative handling of custom URL schemes
577
+ (e.g. `cmdop://machine/<uuid>` → render as a chip; `obsidian://`
578
+ → open in a viewer). One prop replaces the per-consumer
579
+ custom-`a`/sanitize/urlTransform boilerplate.
580
+
581
+ ```tsx
582
+ import { MarkdownMessage } from '@djangocfg/ui-tools';
583
+
584
+ <MarkdownMessage
585
+ content="# Hello\n\nThis is **bold** text and `inline code`."
586
+ isCompact={false}
587
+ />
588
+
589
+ // Chat user message — primary-tinted bubble styling
590
+ <MarkdownMessage content={msg} isUser />
591
+
592
+ // Long content with "Read more..."
593
+ <MarkdownMessage
594
+ content={longText}
595
+ collapsible
596
+ maxLength={300}
597
+ maxLines={5}
598
+ />
599
+ ```
600
+
601
+ ### Custom URL schemes — `linkRules`
602
+
603
+ For any chat that emits its own URL schemes — `cmdop://machine/<uuid>`
604
+ mention chips, `obsidian://open?path=…` deep-links, custom file
605
+ viewers — the recommended approach is `linkRules`:
606
+
607
+ ```tsx
608
+ import { MarkdownMessage, type LinkRule } from '@djangocfg/ui-tools';
609
+
610
+ const machineMention: LinkRule = {
611
+ name: 'machine-mention',
612
+ protocols: ['cmdop'],
613
+
614
+ // Optional: rewrite the source markdown before render. Useful when
615
+ // your composer adds a decorative `@` outside the link
616
+ // (`@[label](href)`) — the chip itself reads as the mention
617
+ // indicator, so rendering "@<chip>" looks like "@@label".
618
+ preprocess: (source) =>
619
+ source.replace(
620
+ /(^|[^A-Za-z0-9_])@(\[[^\]]+\]\(cmdop:\/\/machine\/[^)\s]+\))/g,
621
+ '$1$2',
622
+ ),
623
+
624
+ // Predicate against the resolved href.
625
+ match: (href) => href.startsWith('cmdop://machine/'),
626
+
627
+ // Render whatever you want. `children` is the link's React label.
628
+ render: ({ href, children, isUser }) => {
629
+ const id = href.slice('cmdop://machine/'.length);
630
+ return <MentionChip id={id} isUser={isUser}>{children}</MentionChip>;
631
+ },
632
+ };
633
+
634
+ <MarkdownMessage
635
+ content="Talk to @[Vps-audi](cmdop://machine/abc-123) about deployment."
636
+ linkRules={[machineMention]}
637
+ />
638
+ ```
639
+
640
+ #### Why `linkRules` and not just `customComponents.a`
641
+
642
+ Three concerns have to be aligned for a custom URL scheme to make it
643
+ intact to your renderer, and `customComponents` alone covers only one
644
+ of them:
645
+
646
+ | Concern | What goes wrong without help |
647
+ |---|---|
648
+ | **Sanitize whitelist** | `rehype-sanitize` strips href values for any protocol it doesn't recognise — your renderer receives `href={undefined}`. |
649
+ | **`urlTransform`** | `react-markdown`'s default `urlTransform` runs *before* sanitize and blanks unrecognised schemes the same way — sanitize whitelist is moot if this layer already nuked the href. |
650
+ | **Source preprocess** | Your composer might emit a shape like `@[label](cmdop://...)`; the leading `@` lives outside the link and needs to be stripped before render or the chip ends up next to a literal `@`. |
651
+
652
+ `linkRules` collapses all three into a single declaration:
653
+ - `protocols` is unioned into the sanitize schema.
654
+ - The same protocol opts in the `urlTransform`.
655
+ - `preprocess` runs ahead of render.
656
+ - `match` + `render` replace the per-rule `<a>`.
657
+
658
+ You can still pass `customComponents` and `extraHrefProtocols`
659
+ alongside; rules win on URLs they `match`, everything else falls
660
+ through to your `customComponents.a` (or the built-in chat anchor).
661
+
662
+ ### Anatomy
663
+
664
+ The component lives at `src/components/markdown/MarkdownMessage/`:
665
+
666
+ ```
667
+ MarkdownMessage/
668
+ ├── MarkdownMessage.tsx # composition only
669
+ ├── types.ts # MarkdownMessageProps + LinkRule
670
+ ├── sanitize.ts # buildSchema + buildUrlTransform
671
+ ├── components.tsx # createMarkdownComponents (h/p/ul/a/pre/...)
672
+ ├── CodeBlock.tsx # code block with copy button + fallback
673
+ ├── CollapseToggle.tsx # "Read more..." button
674
+ ├── linkRules.ts # rule application helpers
675
+ ├── plainText.ts # hasMarkdownSyntax + extractTextFromChildren
676
+ └── index.ts # public re-exports
677
+ ```
678
+
679
+ Decomposed in 2.1.299 to keep concerns flat and reviewable. Public
680
+ contract (`MarkdownMessage`, `MarkdownMessageProps`, `LinkRule`,
681
+ `extractTextFromChildren`) re-exported from
682
+ `@djangocfg/ui-tools` directly.
683
+
561
684
  ## Components
562
685
 
563
686
  | Component | Description |
564
687
  |-----------|-------------|
565
- | `Markdown` | Markdown renderer with GFM support |
688
+ | `MarkdownMessage` | Read-only markdown renderer with custom-URL-scheme support via `linkRules` (see above) |
689
+ | `Markdown` | Generic markdown renderer with GFM support |
566
690
 
567
691
  ## Stores
568
692
 
@@ -1,4 +1,4 @@
1
- import { deduplicateEndpoints, dereferenceSchema, resolveBaseUrl, usePlaygroundContext, toMarkdown, toCompactJson, toRawJson, formatBytes, MarkdownMessage, CODE_SAMPLE_TARGETS, buildHarRequest, resolveAbsolute, renderSnippet, PrettyCode_default, relativePath, endpointToMarkdown, isValidJson, findApiKeyById, parseRequestHeaders, UrlBuilder, sampleSchemaJson, joinUrl } from './chunk-2BBXP3DH.mjs';
1
+ import { deduplicateEndpoints, dereferenceSchema, resolveBaseUrl, usePlaygroundContext, toMarkdown, toCompactJson, toRawJson, formatBytes, MarkdownMessage, CODE_SAMPLE_TARGETS, buildHarRequest, resolveAbsolute, renderSnippet, PrettyCode_default, relativePath, endpointToMarkdown, isValidJson, findApiKeyById, parseRequestHeaders, UrlBuilder, sampleSchemaJson, joinUrl } from './chunk-CKD7GNE5.mjs';
2
2
  import { JsonTree_default } from './chunk-LFWQ36LJ.mjs';
3
3
  import './chunk-SSUOENAZ.mjs';
4
4
  import { __name } from './chunk-CGILA3WO.mjs';
@@ -3459,5 +3459,5 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
3459
3459
  }, "DocsLayout");
3460
3460
 
3461
3461
  export { DocsLayout };
3462
- //# sourceMappingURL=DocsLayout-74WIW7L3.mjs.map
3463
- //# sourceMappingURL=DocsLayout-74WIW7L3.mjs.map
3462
+ //# sourceMappingURL=DocsLayout-MWRKNFXR.mjs.map
3463
+ //# sourceMappingURL=DocsLayout-MWRKNFXR.mjs.map