@urbicon-ui/blocks 6.34.0 → 6.36.0

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 (127) hide show
  1. package/README.md +11 -1
  2. package/dist/components/Calendar/CalendarHeader.svelte +32 -26
  3. package/dist/components/Calendar/CalendarMiniMonth.svelte +10 -10
  4. package/dist/components/Calendar/calendar.variants.js +19 -5
  5. package/dist/components/Chat/Chat/Chat.svelte +53 -0
  6. package/dist/components/Chat/Chat/Chat.svelte.d.ts +4 -0
  7. package/dist/components/Chat/Chat/chat.variants.d.ts +20 -0
  8. package/dist/components/Chat/Chat/chat.variants.js +15 -0
  9. package/dist/components/Chat/Chat/index.d.ts +47 -0
  10. package/dist/components/Chat/Chat/index.js +2 -0
  11. package/dist/components/Chat/ChatMessage/ChatMessage.svelte +342 -0
  12. package/dist/components/Chat/ChatMessage/ChatMessage.svelte.d.ts +4 -0
  13. package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +138 -0
  14. package/dist/components/Chat/ChatMessage/chat-message.variants.js +118 -0
  15. package/dist/components/Chat/ChatMessage/index.d.ts +102 -0
  16. package/dist/components/Chat/ChatMessage/index.js +2 -0
  17. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte +301 -0
  18. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte.d.ts +6 -0
  19. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.d.ts +23 -0
  20. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.js +32 -0
  21. package/dist/components/Chat/ChatMessageList/chat-scroll.d.ts +45 -0
  22. package/dist/components/Chat/ChatMessageList/chat-scroll.js +52 -0
  23. package/dist/components/Chat/ChatMessageList/index.d.ts +93 -0
  24. package/dist/components/Chat/ChatMessageList/index.js +3 -0
  25. package/dist/components/Chat/CitationChip/CitationChip.svelte +107 -0
  26. package/dist/components/Chat/CitationChip/CitationChip.svelte.d.ts +4 -0
  27. package/dist/components/Chat/CitationChip/citation-chip.variants.d.ts +40 -0
  28. package/dist/components/Chat/CitationChip/citation-chip.variants.js +42 -0
  29. package/dist/components/Chat/CitationChip/index.d.ts +69 -0
  30. package/dist/components/Chat/CitationChip/index.js +2 -0
  31. package/dist/components/Chat/CodeBlock/CodeBlock.svelte +120 -0
  32. package/dist/components/Chat/CodeBlock/CodeBlock.svelte.d.ts +4 -0
  33. package/dist/components/Chat/CodeBlock/code-block.variants.d.ts +40 -0
  34. package/dist/components/Chat/CodeBlock/code-block.variants.js +51 -0
  35. package/dist/components/Chat/CodeBlock/index.d.ts +55 -0
  36. package/dist/components/Chat/CodeBlock/index.js +2 -0
  37. package/dist/components/Chat/PromptInput/PromptInput.svelte +432 -0
  38. package/dist/components/Chat/PromptInput/PromptInput.svelte.d.ts +4 -0
  39. package/dist/components/Chat/PromptInput/index.d.ts +165 -0
  40. package/dist/components/Chat/PromptInput/index.js +2 -0
  41. package/dist/components/Chat/PromptInput/prompt-input.variants.d.ts +90 -0
  42. package/dist/components/Chat/PromptInput/prompt-input.variants.js +102 -0
  43. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte +107 -0
  44. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte.d.ts +4 -0
  45. package/dist/components/Chat/ReasoningDisclosure/index.d.ts +85 -0
  46. package/dist/components/Chat/ReasoningDisclosure/index.js +2 -0
  47. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.d.ts +20 -0
  48. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.js +31 -0
  49. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte +99 -0
  50. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte.d.ts +9 -0
  51. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte +82 -0
  52. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte.d.ts +9 -0
  53. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte +118 -0
  54. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte.d.ts +4 -0
  55. package/dist/components/Chat/StreamingMarkdown/index.d.ts +86 -0
  56. package/dist/components/Chat/StreamingMarkdown/index.js +2 -0
  57. package/dist/components/Chat/StreamingMarkdown/md-context.d.ts +58 -0
  58. package/dist/components/Chat/StreamingMarkdown/md-context.js +1 -0
  59. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.d.ts +145 -0
  60. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.js +73 -0
  61. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte +174 -0
  62. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte.d.ts +4 -0
  63. package/dist/components/Chat/ToolCallCard/index.d.ts +84 -0
  64. package/dist/components/Chat/ToolCallCard/index.js +2 -0
  65. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.d.ts +38 -0
  66. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.js +38 -0
  67. package/dist/components/Chat/chat.types.d.ts +82 -0
  68. package/dist/components/Chat/chat.types.js +7 -0
  69. package/dist/components/Chat/index.d.ts +22 -0
  70. package/dist/components/Chat/index.js +14 -0
  71. package/dist/components/Chat/markdown/blocks.d.ts +54 -0
  72. package/dist/components/Chat/markdown/blocks.js +462 -0
  73. package/dist/components/Chat/markdown/inline.d.ts +31 -0
  74. package/dist/components/Chat/markdown/inline.js +446 -0
  75. package/dist/components/Chat/markdown/repair.d.ts +23 -0
  76. package/dist/components/Chat/markdown/repair.js +337 -0
  77. package/dist/components/Chat/markdown/streaming-harness.d.ts +23 -0
  78. package/dist/components/Chat/markdown/streaming-harness.js +62 -0
  79. package/dist/components/Chat/markdown/types.d.ts +139 -0
  80. package/dist/components/Chat/markdown/types.js +13 -0
  81. package/dist/components/Chat/markdown/url-policy.d.ts +33 -0
  82. package/dist/components/Chat/markdown/url-policy.js +109 -0
  83. package/dist/components/FileUpload/FileUpload.svelte +52 -125
  84. package/dist/components/FileUpload/FileUpload.svelte.d.ts +1 -1
  85. package/dist/components/FileUpload/index.d.ts +2 -28
  86. package/dist/components/Planner/PlannerHeader.svelte +16 -13
  87. package/dist/components/Planner/planner.variants.js +14 -3
  88. package/dist/components/index.d.ts +1 -0
  89. package/dist/components/index.js +1 -0
  90. package/dist/i18n/index.d.ts +268 -382
  91. package/dist/i18n/index.js +83 -9
  92. package/dist/internal/core/CoreIconButton.svelte +66 -0
  93. package/dist/internal/core/CoreIconButton.svelte.d.ts +13 -0
  94. package/dist/internal/core/CoreSpinner.svelte +49 -0
  95. package/dist/internal/core/CoreSpinner.svelte.d.ts +8 -0
  96. package/dist/internal/core/spinner-geometry.d.ts +6 -0
  97. package/dist/internal/core/spinner-geometry.js +6 -0
  98. package/dist/mint/registry.d.ts +20 -2
  99. package/dist/mint/registry.js +20 -2
  100. package/dist/primitives/Badge/Badge.svelte +5 -5
  101. package/dist/primitives/Badge/badge.variants.js +17 -1
  102. package/dist/primitives/Button/Button.svelte +4 -2
  103. package/dist/primitives/Collapsible/Collapsible.svelte +5 -0
  104. package/dist/primitives/Dialog/Dialog.svelte +7 -5
  105. package/dist/primitives/Dialog/dialog.variants.d.ts +7 -0
  106. package/dist/primitives/Dialog/dialog.variants.js +17 -0
  107. package/dist/primitives/Drawer/Drawer.svelte +7 -5
  108. package/dist/primitives/Drawer/drawer.variants.d.ts +8 -0
  109. package/dist/primitives/Drawer/drawer.variants.js +17 -0
  110. package/dist/primitives/Spinner/Spinner.svelte +3 -1
  111. package/dist/primitives/SplitPane/SplitPane.svelte +293 -0
  112. package/dist/primitives/SplitPane/SplitPane.svelte.d.ts +4 -0
  113. package/dist/primitives/SplitPane/index.d.ts +88 -0
  114. package/dist/primitives/SplitPane/index.js +2 -0
  115. package/dist/primitives/SplitPane/split-pane.utils.d.ts +73 -0
  116. package/dist/primitives/SplitPane/split-pane.utils.js +94 -0
  117. package/dist/primitives/SplitPane/split-pane.variants.d.ts +40 -0
  118. package/dist/primitives/SplitPane/split-pane.variants.js +56 -0
  119. package/dist/primitives/Toast/Toaster.svelte +17 -2
  120. package/dist/primitives/index.d.ts +2 -0
  121. package/dist/primitives/index.js +1 -0
  122. package/dist/utils/file-intake.d.ts +78 -0
  123. package/dist/utils/file-intake.js +144 -0
  124. package/dist/utils/index.d.ts +1 -0
  125. package/dist/utils/index.js +1 -0
  126. package/dist/utils/variants.js +44 -10
  127. package/package.json +4 -6
@@ -0,0 +1,82 @@
1
+ <!--
2
+ Recursive inline-node renderer for StreamingMarkdown. Internal — not exported.
3
+
4
+ Whitespace discipline: every branch keeps its element glued to the control-flow
5
+ tags (no newline between `}` and `<strong>`), because stray template whitespace
6
+ becomes a visible space inside underlines / code backgrounds. Guarded by the
7
+ exact-textContent test in StreamingMarkdown.svelte.test.ts.
8
+ -->
9
+ <script lang="ts">
10
+ import { resolveIcon } from '../../../icons';
11
+ import ImageIconDefault from '../../../icons/ImageIcon.svelte';
12
+ import CitationChip from '../CitationChip/CitationChip.svelte';
13
+ import type { CitationSource } from '../CitationChip';
14
+ import type { InlineNode } from '../markdown/types';
15
+ import { checkLinkUrl } from '../markdown/url-policy';
16
+ import type { MdRenderContext } from './md-context';
17
+ import MdInlineSelf from './MdInline.svelte';
18
+
19
+ const ImageIcon = resolveIcon('image', ImageIconDefault);
20
+
21
+ let { nodes, ctx }: { nodes: InlineNode[]; ctx: MdRenderContext } = $props();
22
+
23
+ // The citation snippet override receives the same sanitized-URL contract as
24
+ // renderers.link/image: a source.url that fails the policy is stripped
25
+ // before it ever reaches consumer markup (review finding, P1 wave).
26
+ function safeCitationSource(source: CitationSource | undefined): CitationSource | undefined {
27
+ if (!source?.url) return source;
28
+ const check = checkLinkUrl(source.url, ctx.urlPolicy);
29
+ return check.ok ? { ...source, url: check.href } : { ...source, url: undefined };
30
+ }
31
+ </script>
32
+
33
+ <!--
34
+ Index keys throughout: inline/block node arrays are write-once (the engine
35
+ replaces the whole block object on change, never reorders within it), so
36
+ position IS identity here.
37
+ -->
38
+ {#each nodes as node, i (i)}
39
+ {#if node.kind === 'text'}{node.text}{:else if node.kind === 'strong'}<strong
40
+ ><MdInlineSelf nodes={node.children} {ctx} /></strong
41
+ >{:else if node.kind === 'em'}<em><MdInlineSelf nodes={node.children} {ctx} /></em
42
+ >{:else if node.kind === 'strike'}<del><MdInlineSelf nodes={node.children} {ctx} /></del
43
+ >{:else if node.kind === 'code'}<code class={ctx.classes.inlineCode}>{node.text}</code
44
+ >{:else if node.kind === 'link'}{#if ctx.renderers?.link}{@render ctx.renderers.link({
45
+ href: node.href,
46
+ title: node.title,
47
+ children: node.children,
48
+ blocked: node.blocked === true
49
+ })}{:else if node.blocked}<span class={ctx.classes.linkBlocked}
50
+ ><MdInlineSelf nodes={node.children} {ctx} /></span
51
+ >{:else}<a
52
+ class={ctx.classes.link}
53
+ href={node.href}
54
+ title={node.title}
55
+ target={ctx.linkTarget}
56
+ rel={ctx.linkTarget === '_blank' ? 'noopener noreferrer' : undefined}
57
+ ><MdInlineSelf nodes={node.children} {ctx} /></a
58
+ >{/if}{:else if node.kind === 'image'}{#if ctx.renderers?.image}{@render ctx.renderers.image({
59
+ src: node.src,
60
+ alt: node.alt,
61
+ title: node.title,
62
+ blocked: node.blocked === true
63
+ })}{:else if node.blocked}<span class={ctx.classes.imageBlocked}
64
+ ><ImageIcon class="size-3.5 shrink-0" /><span class="truncate">{node.alt}</span></span
65
+ >{:else}<img
66
+ class={ctx.classes.image}
67
+ src={node.src}
68
+ alt={node.alt}
69
+ title={node.title}
70
+ loading="lazy"
71
+ />{/if}{:else if node.kind === 'citation'}{#if ctx.renderers?.citation}{@render ctx.renderers.citation(
72
+ {
73
+ id: node.id,
74
+ source: safeCitationSource(ctx.citations?.get(node.id)?.source),
75
+ index: ctx.citations?.get(node.id)?.index
76
+ }
77
+ )}{:else}{@const entry = ctx.citations?.get(node.id)}{#if entry}<CitationChip
78
+ source={entry.source}
79
+ index={entry.index}
80
+ urlPolicy={ctx.urlPolicy}
81
+ />{:else}{`[${node.id}]`}{/if}{/if}{:else if node.kind === 'break'}<br />{/if}
82
+ {/each}
@@ -0,0 +1,9 @@
1
+ import type { InlineNode } from '../markdown/types.js';
2
+ import type { MdRenderContext } from './md-context.js';
3
+ type $$ComponentProps = {
4
+ nodes: InlineNode[];
5
+ ctx: MdRenderContext;
6
+ };
7
+ declare const MdInline: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type MdInline = ReturnType<typeof MdInline>;
9
+ export default MdInline;
@@ -0,0 +1,118 @@
1
+ <script lang="ts">
2
+ import { getBlocksConfig, resolveSlotClasses } from '../../../provider';
3
+ import { createIncrementalParser } from '../markdown/blocks';
4
+ import type { MarkdownParseOptions, MarkdownUrlPolicy } from '../markdown/types';
5
+ import type { CitationSource } from '../CitationChip';
6
+ import MdBlock from './MdBlock.svelte';
7
+ import type { MdRenderContext } from './md-context';
8
+ import {
9
+ streamingMarkdownVariants,
10
+ type StreamingMarkdownVariants
11
+ } from './streaming-markdown.variants';
12
+ import type { StreamingMarkdownProps } from './index';
13
+
14
+ let {
15
+ content,
16
+ streaming = false,
17
+ sources,
18
+ urlPolicy,
19
+ renderers,
20
+ autolink = false,
21
+ headingLevelStart = 1,
22
+ linkTarget = '_blank',
23
+ tableRegionLabel = 'Table',
24
+ size = 'md',
25
+ class: className = '',
26
+ unstyled: unstyledProp = false,
27
+ slotClasses: slotClassesProp = {},
28
+ preset,
29
+ ...restProps
30
+ }: StreamingMarkdownProps = $props();
31
+
32
+ const blocksConfig = getBlocksConfig();
33
+ const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
34
+
35
+ const variantProps: StreamingMarkdownVariants = $derived({ size });
36
+ const styles = $derived(streamingMarkdownVariants(variantProps));
37
+
38
+ const slotClasses = $derived(
39
+ resolveSlotClasses(blocksConfig, 'StreamingMarkdown', preset, variantProps, slotClassesProp)
40
+ );
41
+
42
+ /** Final class string per slot — computed once here so MdBlock/MdInline stay dumb. */
43
+ const classes = $derived.by(() => {
44
+ const out: Record<string, string> = {};
45
+ for (const [name, slotFn] of Object.entries(styles)) {
46
+ const override = slotClasses?.[name as keyof typeof slotClasses];
47
+ out[name] = unstyled ? (override ?? '') : slotFn({ class: override });
48
+ }
49
+ return out;
50
+ });
51
+
52
+ const citations = $derived(
53
+ sources?.length
54
+ ? new Map(sources.map((source, i) => [source.id, { source, index: i + 1 }]))
55
+ : undefined
56
+ );
57
+
58
+ // ── Parser lifecycle ───────────────────────────────────────────────────────
59
+ // The incremental parser is plain (non-reactive) state, rebuilt only when a
60
+ // parse *option* changes: the citation-id set, autolink, or the urlPolicy
61
+ // reference (keep `urlPolicy` referentially stable in consumers). A rebuild
62
+ // re-parses the full content; block keys are index-sequential in both the
63
+ // old and new parser, so the keyed {#each} patches instead of remounting.
64
+ // Appends mutate the parser inside $derived.by — legal, since none of the
65
+ // mutated values are reactive state; the guard on `parser.source` makes
66
+ // re-evaluation idempotent.
67
+ let parser = createIncrementalParser();
68
+ let parserOptionsKey: string | undefined;
69
+ let parserPolicy: MarkdownUrlPolicy | undefined;
70
+
71
+ const doc = $derived.by(() => {
72
+ const ids = sources?.map((s) => s.id) ?? [];
73
+ // JSON keeps distinct id sets distinct - a plain join can collide and
74
+ // would then skip a required parser rebuild.
75
+ const optionsKey = `${autolink ? 1 : 0}|${JSON.stringify(ids)}`;
76
+ if (optionsKey !== parserOptionsKey || urlPolicy !== parserPolicy) {
77
+ const options: MarkdownParseOptions = {
78
+ urlPolicy,
79
+ autolink,
80
+ citationIds: ids.length ? new Set(ids) : undefined
81
+ };
82
+ parser = createIncrementalParser(options);
83
+ parserOptionsKey = optionsKey;
84
+ parserPolicy = urlPolicy;
85
+ }
86
+ if (content !== parser.source) {
87
+ if (content.startsWith(parser.source)) {
88
+ parser.append(content.slice(parser.source.length));
89
+ } else {
90
+ parser.reset();
91
+ parser.append(content);
92
+ }
93
+ }
94
+ return parser.document;
95
+ });
96
+
97
+ const ctx = $derived<MdRenderContext>({
98
+ classes,
99
+ renderers,
100
+ citations,
101
+ urlPolicy,
102
+ headingLevelStart,
103
+ linkTarget,
104
+ tableRegionLabel
105
+ });
106
+ </script>
107
+
108
+ <div
109
+ class={unstyled
110
+ ? [slotClasses?.base, className].filter(Boolean).join(' ')
111
+ : styles.base({ class: [slotClasses?.base, className] })}
112
+ {...restProps}
113
+ >
114
+ {#each doc.blocks as block (block.key)}
115
+ <MdBlock {block} {ctx} />
116
+ {/each}
117
+ {#if streaming}<span class={classes.cursor} aria-hidden="true"></span>{/if}
118
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { StreamingMarkdownProps } from './index.js';
2
+ declare const StreamingMarkdown: import("svelte").Component<StreamingMarkdownProps, {}, "">;
3
+ type StreamingMarkdown = ReturnType<typeof StreamingMarkdown>;
4
+ export default StreamingMarkdown;
@@ -0,0 +1,86 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { CitationSource } from '../CitationChip/index.js';
3
+ import type { MarkdownUrlPolicy } from '../markdown/types.js';
4
+ import type { MarkdownRenderers } from './md-context.js';
5
+ import type { StreamingMarkdownSlots, StreamingMarkdownVariants } from './streaming-markdown.variants.js';
6
+ /**
7
+ * Props interface for StreamingMarkdown component
8
+ *
9
+ * @description Streaming-safe markdown renderer for LLM output. Parses a growing
10
+ * `content` string incrementally (settled blocks are never re-rendered) and renders
11
+ * to a real component tree — never through `{@html}`, so raw HTML in the source
12
+ * stays inert text. Links and images pass a strict-by-default URL policy
13
+ * (external images blocked unless allowlisted). Citation markers like `[1]`
14
+ * resolve to CitationChip when a matching entry exists in `sources`; code fences
15
+ * render through CodeBlock. Per-node-type `renderers` snippets hook in syntax
16
+ * highlighting, custom links, images, or citations without adding dependencies.
17
+ *
18
+ * @tag ai
19
+ * @tag display
20
+ * @related CodeBlock
21
+ * @related CitationChip
22
+ * @stability experimental
23
+ *
24
+ * @example
25
+ * ```svelte
26
+ * <StreamingMarkdown content={message.text} streaming={message.status === 'streaming'} />
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```svelte
31
+ * <StreamingMarkdown
32
+ * content={answer}
33
+ * sources={[{ id: '1', title: 'Design tokens', url: 'https://ui.urbicon.de/tokens' }]}
34
+ * urlPolicy={{ allowedImagePrefixes: ['https://cdn.example.com/'] }}
35
+ * />
36
+ * ```
37
+ */
38
+ export interface StreamingMarkdownProps extends StreamingMarkdownVariants, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
39
+ /** Markdown source. Append-only growth streams incrementally; any other change re-parses. */
40
+ content: string;
41
+ /** Shows a pulsing cursor after the last block while the answer streams @default false */
42
+ streaming?: boolean;
43
+ /**
44
+ * Citation sources. Ids activate `[id]` / `【id】` markers in the content,
45
+ * rendering them as CitationChip (1-based index follows array order).
46
+ * Markers without a matching id stay plain text.
47
+ */
48
+ sources?: CitationSource[];
49
+ /**
50
+ * URL policy for links and images. Strict by default: links limited to
51
+ * http/https/mailto/tel + relative, every external image blocked. Keep the
52
+ * object referentially stable — a new reference re-parses the content.
53
+ */
54
+ urlPolicy?: MarkdownUrlPolicy;
55
+ /** Per-node-type snippet overrides (code highlighting, router links, lightboxes, …) */
56
+ renderers?: MarkdownRenderers;
57
+ /** Recognize bare `https://…` autolinks (GFM-style) @default false */
58
+ autolink?: boolean;
59
+ /**
60
+ * DOM heading level that markdown `#` maps to (deeper levels shift along,
61
+ * clamped at h6). Visual sizing keeps following the author's level. Set to
62
+ * 3 in chat surfaces so message headings stay out of the page outline.
63
+ * @default 1
64
+ */
65
+ headingLevelStart?: 1 | 2 | 3 | 4 | 5 | 6;
66
+ /** Target for rendered links; `rel="noopener noreferrer"` is added for `_blank` @default '_blank' */
67
+ linkTarget?: '_blank' | '_self';
68
+ /** aria-label for the focusable scrollable region around tables @default 'Table' */
69
+ tableRegionLabel?: string;
70
+ /** Type scale: `md` inherits the surrounding font size, `sm` is compact @default 'md' */
71
+ size?: 'sm' | 'md';
72
+ /** Custom CSS class */
73
+ class?: string;
74
+ /** Remove default styles */
75
+ unstyled?: boolean;
76
+ /** Per-slot class overrides. Slots: base | paragraph | heading1–6 | inlineCode | link | linkBlocked | image | imageBlocked | listUnordered | listOrdered | listItem | taskItem | taskCheckbox | blockquote | codeBlock | tableWrapper | table | tableRow | tableHeadCell | tableCell | hr | cursor */
77
+ slotClasses?: Partial<Record<StreamingMarkdownSlots, string>>;
78
+ /**
79
+ * Apply a named preset registered via `<BlocksProvider presets={{ StreamingMarkdown: {...} }}>`.
80
+ * Prefer this over `class` overrides for reusable custom looks.
81
+ */
82
+ preset?: string;
83
+ }
84
+ export type { MarkdownRenderers } from './md-context.js';
85
+ export { default as StreamingMarkdown } from './StreamingMarkdown.svelte';
86
+ export { type StreamingMarkdownSlots, type StreamingMarkdownVariants, streamingMarkdownVariants } from './streaming-markdown.variants.js';
@@ -0,0 +1,2 @@
1
+ export { default as StreamingMarkdown } from './StreamingMarkdown.svelte';
2
+ export { streamingMarkdownVariants } from './streaming-markdown.variants.js';
@@ -0,0 +1,58 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { CitationSource } from '../CitationChip/index.js';
3
+ import type { InlineNode, MarkdownUrlPolicy } from '../markdown/types.js';
4
+ /**
5
+ * Snippet overrides per markdown node type. Each snippet fully replaces the
6
+ * built-in renderer for that node — the hook point for syntax highlighting,
7
+ * lightboxes, router-aware links, or custom citation chips, without pulling
8
+ * any of those dependencies into the core.
9
+ */
10
+ export interface MarkdownRenderers {
11
+ codeBlock?: Snippet<[{
12
+ code: string;
13
+ lang?: string;
14
+ open?: boolean;
15
+ }]>;
16
+ /** `href` is already policy-checked: empty string when `blocked` is true. */
17
+ link?: Snippet<[{
18
+ href: string;
19
+ title?: string;
20
+ children: InlineNode[];
21
+ blocked: boolean;
22
+ }]>;
23
+ /** `src` is already policy-checked: empty string when `blocked` is true. */
24
+ image?: Snippet<[{
25
+ src: string;
26
+ alt: string;
27
+ title?: string;
28
+ blocked: boolean;
29
+ }]>;
30
+ /** `source.url` is already policy-checked: stripped (undefined) when blocked. */
31
+ citation?: Snippet<[{
32
+ id: string;
33
+ source?: CitationSource;
34
+ index?: number;
35
+ }]>;
36
+ }
37
+ /**
38
+ * Internal render context threaded through MdBlock/MdInline. One object
39
+ * instead of five props; deliberately not a Svelte context — the tree is
40
+ * private to StreamingMarkdown and explicit prop flow keeps it inspectable.
41
+ */
42
+ export interface MdRenderContext {
43
+ /** Resolved class string per variant slot (tv() + slotClasses + unstyled already applied). */
44
+ classes: Readonly<Record<string, string>>;
45
+ renderers?: MarkdownRenderers;
46
+ /** Citation id → source + 1-based display index; only set when `sources` were provided. */
47
+ citations?: ReadonlyMap<string, {
48
+ source: CitationSource;
49
+ index: number;
50
+ }>;
51
+ /** Forwarded to CitationChip so popover links obey the same policy as inline links. */
52
+ urlPolicy?: MarkdownUrlPolicy;
53
+ /** Markdown h1 renders as this DOM heading level (clamped at h6). */
54
+ headingLevelStart: number;
55
+ linkTarget: '_blank' | '_self';
56
+ /** aria-label for the focusable scrollable table region. */
57
+ tableRegionLabel: string;
58
+ }
@@ -0,0 +1,145 @@
1
+ import { type SlotNames, type VariantProps } from '../../../utils/variants.js';
2
+ export declare const streamingMarkdownVariants: ((props?: {
3
+ size?: "sm" | "md" | undefined;
4
+ } | undefined) => {
5
+ base: (props?: ({
6
+ size?: "sm" | "md" | undefined;
7
+ } & {
8
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
9
+ }) | undefined) => string;
10
+ paragraph: (props?: ({
11
+ size?: "sm" | "md" | undefined;
12
+ } & {
13
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
14
+ }) | undefined) => string;
15
+ heading1: (props?: ({
16
+ size?: "sm" | "md" | undefined;
17
+ } & {
18
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
19
+ }) | undefined) => string;
20
+ heading2: (props?: ({
21
+ size?: "sm" | "md" | undefined;
22
+ } & {
23
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
24
+ }) | undefined) => string;
25
+ heading3: (props?: ({
26
+ size?: "sm" | "md" | undefined;
27
+ } & {
28
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
29
+ }) | undefined) => string;
30
+ heading4: (props?: ({
31
+ size?: "sm" | "md" | undefined;
32
+ } & {
33
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
34
+ }) | undefined) => string;
35
+ heading5: (props?: ({
36
+ size?: "sm" | "md" | undefined;
37
+ } & {
38
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
39
+ }) | undefined) => string;
40
+ heading6: (props?: ({
41
+ size?: "sm" | "md" | undefined;
42
+ } & {
43
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
44
+ }) | undefined) => string;
45
+ inlineCode: (props?: ({
46
+ size?: "sm" | "md" | undefined;
47
+ } & {
48
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
49
+ }) | undefined) => string;
50
+ link: (props?: ({
51
+ size?: "sm" | "md" | undefined;
52
+ } & {
53
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
54
+ }) | undefined) => string;
55
+ linkBlocked: (props?: ({
56
+ size?: "sm" | "md" | undefined;
57
+ } & {
58
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
59
+ }) | undefined) => string;
60
+ image: (props?: ({
61
+ size?: "sm" | "md" | undefined;
62
+ } & {
63
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
64
+ }) | undefined) => string;
65
+ imageBlocked: (props?: ({
66
+ size?: "sm" | "md" | undefined;
67
+ } & {
68
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
69
+ }) | undefined) => string;
70
+ listUnordered: (props?: ({
71
+ size?: "sm" | "md" | undefined;
72
+ } & {
73
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
74
+ }) | undefined) => string;
75
+ listOrdered: (props?: ({
76
+ size?: "sm" | "md" | undefined;
77
+ } & {
78
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
79
+ }) | undefined) => string;
80
+ listItem: (props?: ({
81
+ size?: "sm" | "md" | undefined;
82
+ } & {
83
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
84
+ }) | undefined) => string;
85
+ taskItem: (props?: ({
86
+ size?: "sm" | "md" | undefined;
87
+ } & {
88
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
89
+ }) | undefined) => string;
90
+ taskCheckbox: (props?: ({
91
+ size?: "sm" | "md" | undefined;
92
+ } & {
93
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
94
+ }) | undefined) => string;
95
+ blockquote: (props?: ({
96
+ size?: "sm" | "md" | undefined;
97
+ } & {
98
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
99
+ }) | undefined) => string;
100
+ codeBlock: (props?: ({
101
+ size?: "sm" | "md" | undefined;
102
+ } & {
103
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
104
+ }) | undefined) => string;
105
+ tableWrapper: (props?: ({
106
+ size?: "sm" | "md" | undefined;
107
+ } & {
108
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
109
+ }) | undefined) => string;
110
+ table: (props?: ({
111
+ size?: "sm" | "md" | undefined;
112
+ } & {
113
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
114
+ }) | undefined) => string;
115
+ tableRow: (props?: ({
116
+ size?: "sm" | "md" | undefined;
117
+ } & {
118
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
119
+ }) | undefined) => string;
120
+ tableHeadCell: (props?: ({
121
+ size?: "sm" | "md" | undefined;
122
+ } & {
123
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
124
+ }) | undefined) => string;
125
+ tableCell: (props?: ({
126
+ size?: "sm" | "md" | undefined;
127
+ } & {
128
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
129
+ }) | undefined) => string;
130
+ hr: (props?: ({
131
+ size?: "sm" | "md" | undefined;
132
+ } & {
133
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
134
+ }) | undefined) => string;
135
+ cursor: (props?: ({
136
+ size?: "sm" | "md" | undefined;
137
+ } & {
138
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
139
+ }) | undefined) => string;
140
+ }) & {
141
+ readonly config: import("../../../utils/variants.js").TVConfig;
142
+ };
143
+ export type StreamingMarkdownVariants = VariantProps<typeof streamingMarkdownVariants>;
144
+ /** Slot names derived from the tv() config — single source of truth for slotClasses. */
145
+ export type StreamingMarkdownSlots = SlotNames<typeof streamingMarkdownVariants>;
@@ -0,0 +1,73 @@
1
+ import { tv } from '../../../utils/variants.js';
2
+ export const streamingMarkdownVariants = tv({
3
+ slots: {
4
+ // Inherits the surrounding type scale at `md` so chat surfaces stay in
5
+ // control of the base font size; `sm` opts into a compact scale.
6
+ base: 'min-w-0 break-words text-text-primary',
7
+ paragraph: 'mt-4 leading-relaxed first:mt-0',
8
+ heading1: 'mt-6 text-2xl font-semibold leading-tight first:mt-0',
9
+ heading2: 'mt-6 text-xl font-semibold leading-tight first:mt-0',
10
+ heading3: 'mt-5 text-lg font-semibold leading-snug first:mt-0',
11
+ heading4: 'mt-5 text-base font-semibold leading-snug first:mt-0',
12
+ heading5: 'mt-4 text-sm font-semibold first:mt-0',
13
+ heading6: 'mt-4 text-sm font-medium text-text-secondary first:mt-0',
14
+ inlineCode: 'rounded-modify bg-neutral-subtle px-1 py-0.5 font-mono text-[0.9em]',
15
+ link: [
16
+ 'rounded-modify text-primary underline underline-offset-2',
17
+ 'hover:text-primary-emphasis',
18
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50'
19
+ ],
20
+ // Policy-blocked link: inert text, dotted underline as the "this was a
21
+ // link" cue. The original URL never reaches the DOM.
22
+ linkBlocked: 'text-text-secondary underline decoration-dotted underline-offset-2',
23
+ image: 'mt-4 max-w-full rounded-contain border border-border-subtle first:mt-0',
24
+ // Alt-text fallback chip for policy-blocked images.
25
+ imageBlocked: [
26
+ 'mt-1 inline-flex max-w-full items-center gap-1.5 rounded-modify',
27
+ 'bg-neutral-subtle px-2 py-1 text-xs text-text-secondary'
28
+ ],
29
+ // Lists hug their introducing sentence (mt-2, not mt-4) — that is the
30
+ // typographically correct rhythm and keeps nested lists compact too.
31
+ listUnordered: 'mt-2 list-disc space-y-1 ps-5 marker:text-text-tertiary first:mt-0',
32
+ listOrdered: 'mt-2 list-decimal space-y-1 ps-5 marker:text-text-tertiary first:mt-0',
33
+ listItem: 'leading-relaxed',
34
+ // Task items replace the list marker with a checkbox; -ms-5 cancels the
35
+ // list padding so the checkbox sits where the marker was.
36
+ taskItem: '-ms-5 flex list-none items-start gap-2 leading-relaxed',
37
+ taskCheckbox: 'pointer-events-none mt-1 size-3.5 accent-primary',
38
+ blockquote: 'mt-4 border-s-2 border-border-emphasis ps-4 text-text-secondary first:mt-0',
39
+ codeBlock: 'mt-4 first:mt-0',
40
+ tableWrapper: [
41
+ 'mt-4 overflow-x-auto rounded-contain border border-border-subtle first:mt-0',
42
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50'
43
+ ],
44
+ table: 'w-full border-collapse text-sm',
45
+ tableRow: '',
46
+ // Cell alignment comes from the column's delimiter row and is applied
47
+ // per cell in the renderer (text-start/center/end), never here.
48
+ tableHeadCell: 'border-b border-border-default bg-surface-elevated px-3 py-2 font-semibold',
49
+ tableCell: 'border-b border-border-subtle px-3 py-2 align-top',
50
+ hr: 'mt-6 border-t border-border-subtle first:mt-0',
51
+ cursor: [
52
+ '-mb-0.5 ms-0.5 inline-block h-[1em] w-0.5 bg-text-secondary',
53
+ 'motion-safe:animate-pulse'
54
+ ]
55
+ },
56
+ variants: {
57
+ size: {
58
+ sm: {
59
+ base: 'text-sm',
60
+ heading1: 'text-xl',
61
+ heading2: 'text-lg',
62
+ heading3: 'text-base',
63
+ heading4: 'text-sm',
64
+ heading5: 'text-sm',
65
+ heading6: 'text-xs'
66
+ },
67
+ md: {}
68
+ }
69
+ },
70
+ defaultVariants: {
71
+ size: 'md'
72
+ }
73
+ });