@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.
- package/README.md +11 -1
- package/dist/components/Calendar/CalendarHeader.svelte +32 -26
- package/dist/components/Calendar/CalendarMiniMonth.svelte +10 -10
- package/dist/components/Calendar/calendar.variants.js +19 -5
- package/dist/components/Chat/Chat/Chat.svelte +53 -0
- package/dist/components/Chat/Chat/Chat.svelte.d.ts +4 -0
- package/dist/components/Chat/Chat/chat.variants.d.ts +20 -0
- package/dist/components/Chat/Chat/chat.variants.js +15 -0
- package/dist/components/Chat/Chat/index.d.ts +47 -0
- package/dist/components/Chat/Chat/index.js +2 -0
- package/dist/components/Chat/ChatMessage/ChatMessage.svelte +342 -0
- package/dist/components/Chat/ChatMessage/ChatMessage.svelte.d.ts +4 -0
- package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +138 -0
- package/dist/components/Chat/ChatMessage/chat-message.variants.js +118 -0
- package/dist/components/Chat/ChatMessage/index.d.ts +102 -0
- package/dist/components/Chat/ChatMessage/index.js +2 -0
- package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte +301 -0
- package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte.d.ts +6 -0
- package/dist/components/Chat/ChatMessageList/chat-message-list.variants.d.ts +23 -0
- package/dist/components/Chat/ChatMessageList/chat-message-list.variants.js +32 -0
- package/dist/components/Chat/ChatMessageList/chat-scroll.d.ts +45 -0
- package/dist/components/Chat/ChatMessageList/chat-scroll.js +52 -0
- package/dist/components/Chat/ChatMessageList/index.d.ts +93 -0
- package/dist/components/Chat/ChatMessageList/index.js +3 -0
- package/dist/components/Chat/CitationChip/CitationChip.svelte +107 -0
- package/dist/components/Chat/CitationChip/CitationChip.svelte.d.ts +4 -0
- package/dist/components/Chat/CitationChip/citation-chip.variants.d.ts +40 -0
- package/dist/components/Chat/CitationChip/citation-chip.variants.js +42 -0
- package/dist/components/Chat/CitationChip/index.d.ts +69 -0
- package/dist/components/Chat/CitationChip/index.js +2 -0
- package/dist/components/Chat/CodeBlock/CodeBlock.svelte +120 -0
- package/dist/components/Chat/CodeBlock/CodeBlock.svelte.d.ts +4 -0
- package/dist/components/Chat/CodeBlock/code-block.variants.d.ts +40 -0
- package/dist/components/Chat/CodeBlock/code-block.variants.js +51 -0
- package/dist/components/Chat/CodeBlock/index.d.ts +55 -0
- package/dist/components/Chat/CodeBlock/index.js +2 -0
- package/dist/components/Chat/PromptInput/PromptInput.svelte +432 -0
- package/dist/components/Chat/PromptInput/PromptInput.svelte.d.ts +4 -0
- package/dist/components/Chat/PromptInput/index.d.ts +165 -0
- package/dist/components/Chat/PromptInput/index.js +2 -0
- package/dist/components/Chat/PromptInput/prompt-input.variants.d.ts +90 -0
- package/dist/components/Chat/PromptInput/prompt-input.variants.js +102 -0
- package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte +107 -0
- package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte.d.ts +4 -0
- package/dist/components/Chat/ReasoningDisclosure/index.d.ts +85 -0
- package/dist/components/Chat/ReasoningDisclosure/index.js +2 -0
- package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.d.ts +20 -0
- package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.js +31 -0
- package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte +99 -0
- package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte.d.ts +9 -0
- package/dist/components/Chat/StreamingMarkdown/MdInline.svelte +82 -0
- package/dist/components/Chat/StreamingMarkdown/MdInline.svelte.d.ts +9 -0
- package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte +118 -0
- package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte.d.ts +4 -0
- package/dist/components/Chat/StreamingMarkdown/index.d.ts +86 -0
- package/dist/components/Chat/StreamingMarkdown/index.js +2 -0
- package/dist/components/Chat/StreamingMarkdown/md-context.d.ts +58 -0
- package/dist/components/Chat/StreamingMarkdown/md-context.js +1 -0
- package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.d.ts +145 -0
- package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.js +73 -0
- package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte +174 -0
- package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte.d.ts +4 -0
- package/dist/components/Chat/ToolCallCard/index.d.ts +84 -0
- package/dist/components/Chat/ToolCallCard/index.js +2 -0
- package/dist/components/Chat/ToolCallCard/tool-call-card.variants.d.ts +38 -0
- package/dist/components/Chat/ToolCallCard/tool-call-card.variants.js +38 -0
- package/dist/components/Chat/chat.types.d.ts +82 -0
- package/dist/components/Chat/chat.types.js +7 -0
- package/dist/components/Chat/index.d.ts +22 -0
- package/dist/components/Chat/index.js +14 -0
- package/dist/components/Chat/markdown/blocks.d.ts +54 -0
- package/dist/components/Chat/markdown/blocks.js +462 -0
- package/dist/components/Chat/markdown/inline.d.ts +31 -0
- package/dist/components/Chat/markdown/inline.js +446 -0
- package/dist/components/Chat/markdown/repair.d.ts +23 -0
- package/dist/components/Chat/markdown/repair.js +337 -0
- package/dist/components/Chat/markdown/streaming-harness.d.ts +23 -0
- package/dist/components/Chat/markdown/streaming-harness.js +62 -0
- package/dist/components/Chat/markdown/types.d.ts +139 -0
- package/dist/components/Chat/markdown/types.js +13 -0
- package/dist/components/Chat/markdown/url-policy.d.ts +33 -0
- package/dist/components/Chat/markdown/url-policy.js +109 -0
- package/dist/components/FileUpload/FileUpload.svelte +52 -125
- package/dist/components/FileUpload/FileUpload.svelte.d.ts +1 -1
- package/dist/components/FileUpload/index.d.ts +2 -28
- package/dist/components/Planner/PlannerHeader.svelte +16 -13
- package/dist/components/Planner/planner.variants.js +14 -3
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/i18n/index.d.ts +268 -382
- package/dist/i18n/index.js +83 -9
- package/dist/internal/core/CoreIconButton.svelte +66 -0
- package/dist/internal/core/CoreIconButton.svelte.d.ts +13 -0
- package/dist/internal/core/CoreSpinner.svelte +49 -0
- package/dist/internal/core/CoreSpinner.svelte.d.ts +8 -0
- package/dist/internal/core/spinner-geometry.d.ts +6 -0
- package/dist/internal/core/spinner-geometry.js +6 -0
- package/dist/mint/registry.d.ts +20 -2
- package/dist/mint/registry.js +20 -2
- package/dist/primitives/Badge/Badge.svelte +5 -5
- package/dist/primitives/Badge/badge.variants.js +17 -1
- package/dist/primitives/Button/Button.svelte +4 -2
- package/dist/primitives/Collapsible/Collapsible.svelte +5 -0
- package/dist/primitives/Dialog/Dialog.svelte +7 -5
- package/dist/primitives/Dialog/dialog.variants.d.ts +7 -0
- package/dist/primitives/Dialog/dialog.variants.js +17 -0
- package/dist/primitives/Drawer/Drawer.svelte +7 -5
- package/dist/primitives/Drawer/drawer.variants.d.ts +8 -0
- package/dist/primitives/Drawer/drawer.variants.js +17 -0
- package/dist/primitives/Spinner/Spinner.svelte +3 -1
- package/dist/primitives/SplitPane/SplitPane.svelte +293 -0
- package/dist/primitives/SplitPane/SplitPane.svelte.d.ts +4 -0
- package/dist/primitives/SplitPane/index.d.ts +88 -0
- package/dist/primitives/SplitPane/index.js +2 -0
- package/dist/primitives/SplitPane/split-pane.utils.d.ts +73 -0
- package/dist/primitives/SplitPane/split-pane.utils.js +94 -0
- package/dist/primitives/SplitPane/split-pane.variants.d.ts +40 -0
- package/dist/primitives/SplitPane/split-pane.variants.js +56 -0
- package/dist/primitives/Toast/Toaster.svelte +17 -2
- package/dist/primitives/index.d.ts +2 -0
- package/dist/primitives/index.js +1 -0
- package/dist/utils/file-intake.d.ts +78 -0
- package/dist/utils/file-intake.js +144 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/variants.js +44 -10
- 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,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,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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
});
|