@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,342 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getBlocksConfig, resolveSlotClasses } from '../../../provider';
|
|
3
|
+
import { Alert, Avatar, Button, Skeleton, Tooltip } from '../../../primitives';
|
|
4
|
+
import { resolveIcon } from '../../../icons';
|
|
5
|
+
import SparklesIconDefault from '../../../icons/SparklesIcon.svelte';
|
|
6
|
+
import UserIconDefault from '../../../icons/UserIcon.svelte';
|
|
7
|
+
import InfoIconDefault from '../../../icons/InfoCircleIcon.svelte';
|
|
8
|
+
import CopyIconDefault from '../../../icons/CopyIcon.svelte';
|
|
9
|
+
import CheckIconDefault from '../../../icons/CheckIcon.svelte';
|
|
10
|
+
import RefreshIconDefault from '../../../icons/RefreshIcon.svelte';
|
|
11
|
+
import FileIconDefault from '../../../icons/FileIcon.svelte';
|
|
12
|
+
import { formatFileSize } from '../../../utils/file-intake';
|
|
13
|
+
import StreamingMarkdown from '../StreamingMarkdown/StreamingMarkdown.svelte';
|
|
14
|
+
import CitationChip from '../CitationChip/CitationChip.svelte';
|
|
15
|
+
import ReasoningDisclosure from '../ReasoningDisclosure/ReasoningDisclosure.svelte';
|
|
16
|
+
import ToolCallCard from '../ToolCallCard/ToolCallCard.svelte';
|
|
17
|
+
import type { CitationSource } from '../CitationChip';
|
|
18
|
+
import { checkLinkUrl } from '../markdown/url-policy.js';
|
|
19
|
+
import type { ChatMessagePart, ChatRole } from '../chat.types';
|
|
20
|
+
import { chatMessageVariants, type ChatMessageVariants } from './chat-message.variants';
|
|
21
|
+
import type { ChatMessageProps } from './index';
|
|
22
|
+
|
|
23
|
+
type AttachmentPart = Extract<ChatMessagePart, { type: 'attachment' }>;
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
message,
|
|
27
|
+
urlPolicy,
|
|
28
|
+
onRegenerate,
|
|
29
|
+
onRetry,
|
|
30
|
+
avatar,
|
|
31
|
+
actions,
|
|
32
|
+
metadata,
|
|
33
|
+
partRenderers,
|
|
34
|
+
layout = 'bubble',
|
|
35
|
+
density = 'comfortable',
|
|
36
|
+
roleLabels,
|
|
37
|
+
copyLabel = 'Copy message',
|
|
38
|
+
copiedLabel = 'Copied',
|
|
39
|
+
regenerateLabel = 'Regenerate',
|
|
40
|
+
retryLabel = 'Retry',
|
|
41
|
+
errorLabel = 'Something went wrong',
|
|
42
|
+
abortedLabel = 'Generation stopped',
|
|
43
|
+
class: className,
|
|
44
|
+
unstyled: unstyledProp = false,
|
|
45
|
+
slotClasses: slotClassesProp = {},
|
|
46
|
+
preset,
|
|
47
|
+
...restProps
|
|
48
|
+
}: ChatMessageProps = $props();
|
|
49
|
+
|
|
50
|
+
const blocksConfig = getBlocksConfig();
|
|
51
|
+
const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
|
|
52
|
+
|
|
53
|
+
const SparklesIcon = resolveIcon('sparkles', SparklesIconDefault);
|
|
54
|
+
const UserIcon = resolveIcon('user', UserIconDefault);
|
|
55
|
+
const InfoIcon = resolveIcon('info', InfoIconDefault);
|
|
56
|
+
const CopyIcon = resolveIcon('copy', CopyIconDefault);
|
|
57
|
+
const CheckIcon = resolveIcon('check', CheckIconDefault);
|
|
58
|
+
const RefreshIcon = resolveIcon('refresh', RefreshIconDefault);
|
|
59
|
+
const FileIcon = resolveIcon('file', FileIconDefault);
|
|
60
|
+
|
|
61
|
+
const variantProps: ChatMessageVariants = $derived({ layout, density, role: message.role });
|
|
62
|
+
const styles = $derived(chatMessageVariants(variantProps));
|
|
63
|
+
const slotClasses = $derived(
|
|
64
|
+
resolveSlotClasses(blocksConfig, 'ChatMessage', preset, variantProps, slotClassesProp)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
function cls(name: keyof typeof slotClasses, extra?: string | (string | undefined)[]) {
|
|
68
|
+
if (unstyled) {
|
|
69
|
+
const own = slotClasses?.[name];
|
|
70
|
+
return [own, ...(Array.isArray(extra) ? extra : [extra])].filter(Boolean).join(' ');
|
|
71
|
+
}
|
|
72
|
+
const slotFns = styles as Record<string, (args: { class?: unknown }) => string>;
|
|
73
|
+
return slotFns[name]({
|
|
74
|
+
class: [slotClasses?.[name], ...(Array.isArray(extra) ? extra : [extra])]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const status = $derived(message.status ?? 'complete');
|
|
79
|
+
const lastIndex = $derived(message.parts.length - 1);
|
|
80
|
+
|
|
81
|
+
// Sources are collected out of the flow into a single numbered footer row and
|
|
82
|
+
// also feed StreamingMarkdown so `[id]` markers resolve to chips. Deduped by
|
|
83
|
+
// id (first occurrence wins): models routinely cite one source repeatedly,
|
|
84
|
+
// and a duplicate id would crash the keyed footer each (review finding).
|
|
85
|
+
const sources = $derived.by<CitationSource[]>(() => {
|
|
86
|
+
const byId = new Map<string, CitationSource>();
|
|
87
|
+
for (const p of message.parts) {
|
|
88
|
+
if (p.type === 'source' && !byId.has(p.id)) {
|
|
89
|
+
byId.set(p.id, { id: p.id, title: p.title, url: p.url, snippet: p.snippet });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [...byId.values()];
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Concatenated text parts — what the copy action writes to the clipboard.
|
|
96
|
+
const copyText = $derived(
|
|
97
|
+
message.parts.flatMap((p) => (p.type === 'text' ? [p.text] : [])).join('\n\n')
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Zero-parts streaming placeholder. An empty *trailing* text part already gets
|
|
101
|
+
// StreamingMarkdown's cursor, so only the genuinely empty message needs this.
|
|
102
|
+
const showPlaceholder = $derived(message.status === 'streaming' && message.parts.length === 0);
|
|
103
|
+
|
|
104
|
+
const DEFAULT_ROLE_LABELS: Record<ChatRole, string> = {
|
|
105
|
+
user: 'You',
|
|
106
|
+
assistant: 'Assistant',
|
|
107
|
+
system: 'System'
|
|
108
|
+
};
|
|
109
|
+
const roleLabel = $derived(roleLabels?.[message.role] ?? DEFAULT_ROLE_LABELS[message.role]);
|
|
110
|
+
|
|
111
|
+
const createdAtIso = $derived(message.createdAt?.toISOString());
|
|
112
|
+
const createdAtTime = $derived(
|
|
113
|
+
message.createdAt?.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// ── Copy interaction ─────────────────────────────────────────────────────
|
|
117
|
+
let copied = $state(false);
|
|
118
|
+
let resetTimer: ReturnType<typeof setTimeout> | undefined;
|
|
119
|
+
|
|
120
|
+
async function copyMessage() {
|
|
121
|
+
try {
|
|
122
|
+
await navigator.clipboard.writeText(copyText);
|
|
123
|
+
copied = true;
|
|
124
|
+
if (resetTimer) clearTimeout(resetTimer);
|
|
125
|
+
resetTimer = setTimeout(() => {
|
|
126
|
+
copied = false;
|
|
127
|
+
resetTimer = undefined;
|
|
128
|
+
}, 2000);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
// Never confirm a copy that failed (e.g. denied clipboard permission).
|
|
131
|
+
console.error('ChatMessage: failed to copy message', err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
$effect(() => () => {
|
|
136
|
+
if (resetTimer) clearTimeout(resetTimer);
|
|
137
|
+
});
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
{#snippet defaultAvatar(role: ChatRole)}
|
|
141
|
+
<Avatar size="sm" class={cls('avatar')}>
|
|
142
|
+
{#if role === 'assistant'}
|
|
143
|
+
<SparklesIcon size={16} />
|
|
144
|
+
{:else if role === 'user'}
|
|
145
|
+
<UserIcon size={16} />
|
|
146
|
+
{:else}
|
|
147
|
+
<InfoIcon size={16} />
|
|
148
|
+
{/if}
|
|
149
|
+
</Avatar>
|
|
150
|
+
{/snippet}
|
|
151
|
+
|
|
152
|
+
{#snippet attachmentChip(part: AttachmentPart)}
|
|
153
|
+
{@const linkCheck =
|
|
154
|
+
part.url != null ? checkLinkUrl(part.url, urlPolicy) : ({ ok: false } as const)}
|
|
155
|
+
{#if linkCheck.ok}
|
|
156
|
+
<a
|
|
157
|
+
class={cls('attachment')}
|
|
158
|
+
href={linkCheck.href}
|
|
159
|
+
target="_blank"
|
|
160
|
+
rel="noopener noreferrer"
|
|
161
|
+
download
|
|
162
|
+
>
|
|
163
|
+
<FileIcon size={16} class={cls('attachmentIcon')} />
|
|
164
|
+
<span class={cls('attachmentName')}>{part.name}</span>
|
|
165
|
+
{#if part.size != null}<span class={cls('attachmentSize')}>{formatFileSize(part.size)}</span
|
|
166
|
+
>{/if}
|
|
167
|
+
</a>
|
|
168
|
+
{:else}
|
|
169
|
+
<span class={cls('attachment')}>
|
|
170
|
+
<FileIcon size={16} class={cls('attachmentIcon')} />
|
|
171
|
+
<span class={cls('attachmentName')}>{part.name}</span>
|
|
172
|
+
{#if part.size != null}<span class={cls('attachmentSize')}>{formatFileSize(part.size)}</span
|
|
173
|
+
>{/if}
|
|
174
|
+
</span>
|
|
175
|
+
{/if}
|
|
176
|
+
{/snippet}
|
|
177
|
+
|
|
178
|
+
{#snippet body()}
|
|
179
|
+
<div class={cls('partsFlow')}>
|
|
180
|
+
{#each message.parts as part, index (index)}
|
|
181
|
+
<!--
|
|
182
|
+
Parts are append-only during streaming: array position IS identity, so
|
|
183
|
+
the index key is stable here (the documented exception to the
|
|
184
|
+
no-index-as-key rule).
|
|
185
|
+
-->
|
|
186
|
+
{#if part.type === 'text'}
|
|
187
|
+
{#if partRenderers?.text}
|
|
188
|
+
{@render partRenderers.text(part)}
|
|
189
|
+
{:else}
|
|
190
|
+
<StreamingMarkdown
|
|
191
|
+
content={part.text}
|
|
192
|
+
streaming={message.status === 'streaming' && index === lastIndex}
|
|
193
|
+
sources={sources.length ? sources : undefined}
|
|
194
|
+
{urlPolicy}
|
|
195
|
+
size="sm"
|
|
196
|
+
/>
|
|
197
|
+
{/if}
|
|
198
|
+
{:else if part.type === 'reasoning'}
|
|
199
|
+
{#if partRenderers?.reasoning}
|
|
200
|
+
{@render partRenderers.reasoning(part)}
|
|
201
|
+
{:else}
|
|
202
|
+
<!-- Streaming while it is the trailing part: once the answer text
|
|
203
|
+
starts flowing behind it, the reasoning is settled. -->
|
|
204
|
+
<ReasoningDisclosure
|
|
205
|
+
reasoning={part}
|
|
206
|
+
streaming={status === 'streaming' && index === lastIndex}
|
|
207
|
+
{urlPolicy}
|
|
208
|
+
/>
|
|
209
|
+
{/if}
|
|
210
|
+
{:else if part.type === 'tool-call'}
|
|
211
|
+
{#if partRenderers?.['tool-call']}
|
|
212
|
+
{@render partRenderers['tool-call'](part)}
|
|
213
|
+
{:else}
|
|
214
|
+
<ToolCallCard toolCall={part} />
|
|
215
|
+
{/if}
|
|
216
|
+
{:else if part.type === 'attachment'}
|
|
217
|
+
{#if partRenderers?.attachment}
|
|
218
|
+
{@render partRenderers.attachment(part)}
|
|
219
|
+
{:else}
|
|
220
|
+
{@render attachmentChip(part)}
|
|
221
|
+
{/if}
|
|
222
|
+
{:else if part.type === 'a2ui'}
|
|
223
|
+
<!-- default: render nothing. A2UIView lands in P4 via partRenderers. -->
|
|
224
|
+
{#if partRenderers?.a2ui}{@render partRenderers.a2ui(part)}{/if}
|
|
225
|
+
{/if}
|
|
226
|
+
<!-- part.type === 'source' is intentionally skipped: sources render in the
|
|
227
|
+
citation footer, never inline. -->
|
|
228
|
+
{/each}
|
|
229
|
+
|
|
230
|
+
{#if showPlaceholder}
|
|
231
|
+
<div class={cls('placeholder')}>
|
|
232
|
+
<Skeleton variant="text" width="40%" />
|
|
233
|
+
</div>
|
|
234
|
+
{/if}
|
|
235
|
+
</div>
|
|
236
|
+
{/snippet}
|
|
237
|
+
|
|
238
|
+
{#snippet retryAction()}
|
|
239
|
+
<Button size="sm" onclick={onRetry}>
|
|
240
|
+
<RefreshIcon size={14} />
|
|
241
|
+
{retryLabel}
|
|
242
|
+
</Button>
|
|
243
|
+
{/snippet}
|
|
244
|
+
|
|
245
|
+
{#snippet defaultActions()}
|
|
246
|
+
<div class={cls('actions')}>
|
|
247
|
+
<!-- No copy for text-less messages (tool-call/reasoning-only): copying an
|
|
248
|
+
empty string and confirming "Copied" would be a lie (review finding). -->
|
|
249
|
+
{#if copyText}
|
|
250
|
+
<Tooltip label={copyLabel}>
|
|
251
|
+
<button
|
|
252
|
+
type="button"
|
|
253
|
+
class={cls('actionButton')}
|
|
254
|
+
onclick={copyMessage}
|
|
255
|
+
aria-label={copied ? copiedLabel : copyLabel}
|
|
256
|
+
>
|
|
257
|
+
{#if copied}
|
|
258
|
+
<CheckIcon size={16} />
|
|
259
|
+
{:else}
|
|
260
|
+
<CopyIcon size={16} />
|
|
261
|
+
{/if}
|
|
262
|
+
</button>
|
|
263
|
+
</Tooltip>
|
|
264
|
+
{/if}
|
|
265
|
+
{#if onRegenerate}
|
|
266
|
+
<Tooltip label={regenerateLabel}>
|
|
267
|
+
<button
|
|
268
|
+
type="button"
|
|
269
|
+
class={cls('actionButton')}
|
|
270
|
+
onclick={onRegenerate}
|
|
271
|
+
aria-label={regenerateLabel}
|
|
272
|
+
>
|
|
273
|
+
<RefreshIcon size={16} />
|
|
274
|
+
</button>
|
|
275
|
+
</Tooltip>
|
|
276
|
+
{/if}
|
|
277
|
+
</div>
|
|
278
|
+
{/snippet}
|
|
279
|
+
|
|
280
|
+
<div class={cls('root', className)} data-role={message.role} data-status={status} {...restProps}>
|
|
281
|
+
{#if layout === 'plain'}
|
|
282
|
+
<div class={cls('header')}>
|
|
283
|
+
{#if avatar}
|
|
284
|
+
{@render avatar({ role: message.role })}
|
|
285
|
+
{:else}
|
|
286
|
+
{@render defaultAvatar(message.role)}
|
|
287
|
+
{/if}
|
|
288
|
+
<span class={cls('roleName')}>{roleLabel}</span>
|
|
289
|
+
</div>
|
|
290
|
+
<div class={cls('bubble')}>
|
|
291
|
+
{@render body()}
|
|
292
|
+
</div>
|
|
293
|
+
{:else}
|
|
294
|
+
<div class={cls('container')}>
|
|
295
|
+
{#if message.role !== 'user'}
|
|
296
|
+
{#if avatar}
|
|
297
|
+
{@render avatar({ role: message.role })}
|
|
298
|
+
{:else}
|
|
299
|
+
{@render defaultAvatar(message.role)}
|
|
300
|
+
{/if}
|
|
301
|
+
{/if}
|
|
302
|
+
<div class={cls('bubble')}>
|
|
303
|
+
{@render body()}
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
{/if}
|
|
307
|
+
|
|
308
|
+
{#if sources.length}
|
|
309
|
+
<div class={cls('sourcesFooter')}>
|
|
310
|
+
{#each sources as source, i (source.id)}
|
|
311
|
+
<CitationChip {source} index={i + 1} {urlPolicy} />
|
|
312
|
+
{/each}
|
|
313
|
+
</div>
|
|
314
|
+
{/if}
|
|
315
|
+
|
|
316
|
+
{#if status === 'error' || status === 'aborted'}
|
|
317
|
+
<div class={cls('statusAlert')}>
|
|
318
|
+
<Alert
|
|
319
|
+
intent={status === 'error' ? 'danger' : 'warning'}
|
|
320
|
+
title={status === 'error' ? errorLabel : abortedLabel}
|
|
321
|
+
actions={onRetry ? retryAction : undefined}
|
|
322
|
+
/>
|
|
323
|
+
</div>
|
|
324
|
+
{/if}
|
|
325
|
+
|
|
326
|
+
<div class={cls('footer')}>
|
|
327
|
+
{#if actions}
|
|
328
|
+
{@render actions({ message })}
|
|
329
|
+
{:else}
|
|
330
|
+
{@render defaultActions()}
|
|
331
|
+
{/if}
|
|
332
|
+
{#if metadata}
|
|
333
|
+
{@render metadata({ message })}
|
|
334
|
+
{:else if createdAtTime}
|
|
335
|
+
<time class={cls('metadata')} datetime={createdAtIso}>{createdAtTime}</time>
|
|
336
|
+
{/if}
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<!-- Copy confirmation for screen readers — a live status region, present
|
|
340
|
+
before `copied` flips so it always announces the change. -->
|
|
341
|
+
<span class="sr-only" role="status">{copied ? copiedLabel : ''}</span>
|
|
342
|
+
</div>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { type SlotNames, type VariantProps } from '../../../utils/variants.js';
|
|
2
|
+
export declare const chatMessageVariants: ((props?: {
|
|
3
|
+
layout?: "bubble" | "plain" | undefined;
|
|
4
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
5
|
+
density?: "comfortable" | "compact" | undefined;
|
|
6
|
+
} | undefined) => {
|
|
7
|
+
root: (props?: ({
|
|
8
|
+
layout?: "bubble" | "plain" | undefined;
|
|
9
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
10
|
+
density?: "comfortable" | "compact" | undefined;
|
|
11
|
+
} & {
|
|
12
|
+
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;
|
|
13
|
+
}) | undefined) => string;
|
|
14
|
+
container: (props?: ({
|
|
15
|
+
layout?: "bubble" | "plain" | undefined;
|
|
16
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
17
|
+
density?: "comfortable" | "compact" | undefined;
|
|
18
|
+
} & {
|
|
19
|
+
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;
|
|
20
|
+
}) | undefined) => string;
|
|
21
|
+
header: (props?: ({
|
|
22
|
+
layout?: "bubble" | "plain" | undefined;
|
|
23
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
24
|
+
density?: "comfortable" | "compact" | undefined;
|
|
25
|
+
} & {
|
|
26
|
+
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;
|
|
27
|
+
}) | undefined) => string;
|
|
28
|
+
roleName: (props?: ({
|
|
29
|
+
layout?: "bubble" | "plain" | undefined;
|
|
30
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
31
|
+
density?: "comfortable" | "compact" | 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
|
+
avatar: (props?: ({
|
|
36
|
+
layout?: "bubble" | "plain" | undefined;
|
|
37
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
38
|
+
density?: "comfortable" | "compact" | undefined;
|
|
39
|
+
} & {
|
|
40
|
+
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;
|
|
41
|
+
}) | undefined) => string;
|
|
42
|
+
bubble: (props?: ({
|
|
43
|
+
layout?: "bubble" | "plain" | undefined;
|
|
44
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
45
|
+
density?: "comfortable" | "compact" | undefined;
|
|
46
|
+
} & {
|
|
47
|
+
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;
|
|
48
|
+
}) | undefined) => string;
|
|
49
|
+
partsFlow: (props?: ({
|
|
50
|
+
layout?: "bubble" | "plain" | undefined;
|
|
51
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
52
|
+
density?: "comfortable" | "compact" | undefined;
|
|
53
|
+
} & {
|
|
54
|
+
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;
|
|
55
|
+
}) | undefined) => string;
|
|
56
|
+
attachment: (props?: ({
|
|
57
|
+
layout?: "bubble" | "plain" | undefined;
|
|
58
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
59
|
+
density?: "comfortable" | "compact" | undefined;
|
|
60
|
+
} & {
|
|
61
|
+
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;
|
|
62
|
+
}) | undefined) => string;
|
|
63
|
+
attachmentIcon: (props?: ({
|
|
64
|
+
layout?: "bubble" | "plain" | undefined;
|
|
65
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
66
|
+
density?: "comfortable" | "compact" | 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
|
+
attachmentName: (props?: ({
|
|
71
|
+
layout?: "bubble" | "plain" | undefined;
|
|
72
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
73
|
+
density?: "comfortable" | "compact" | undefined;
|
|
74
|
+
} & {
|
|
75
|
+
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;
|
|
76
|
+
}) | undefined) => string;
|
|
77
|
+
attachmentSize: (props?: ({
|
|
78
|
+
layout?: "bubble" | "plain" | undefined;
|
|
79
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
80
|
+
density?: "comfortable" | "compact" | undefined;
|
|
81
|
+
} & {
|
|
82
|
+
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;
|
|
83
|
+
}) | undefined) => string;
|
|
84
|
+
sourcesFooter: (props?: ({
|
|
85
|
+
layout?: "bubble" | "plain" | undefined;
|
|
86
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
87
|
+
density?: "comfortable" | "compact" | undefined;
|
|
88
|
+
} & {
|
|
89
|
+
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;
|
|
90
|
+
}) | undefined) => string;
|
|
91
|
+
placeholder: (props?: ({
|
|
92
|
+
layout?: "bubble" | "plain" | undefined;
|
|
93
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
94
|
+
density?: "comfortable" | "compact" | undefined;
|
|
95
|
+
} & {
|
|
96
|
+
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;
|
|
97
|
+
}) | undefined) => string;
|
|
98
|
+
statusAlert: (props?: ({
|
|
99
|
+
layout?: "bubble" | "plain" | undefined;
|
|
100
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
101
|
+
density?: "comfortable" | "compact" | 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
|
+
footer: (props?: ({
|
|
106
|
+
layout?: "bubble" | "plain" | undefined;
|
|
107
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
108
|
+
density?: "comfortable" | "compact" | undefined;
|
|
109
|
+
} & {
|
|
110
|
+
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;
|
|
111
|
+
}) | undefined) => string;
|
|
112
|
+
actions: (props?: ({
|
|
113
|
+
layout?: "bubble" | "plain" | undefined;
|
|
114
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
115
|
+
density?: "comfortable" | "compact" | undefined;
|
|
116
|
+
} & {
|
|
117
|
+
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;
|
|
118
|
+
}) | undefined) => string;
|
|
119
|
+
actionButton: (props?: ({
|
|
120
|
+
layout?: "bubble" | "plain" | undefined;
|
|
121
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
122
|
+
density?: "comfortable" | "compact" | undefined;
|
|
123
|
+
} & {
|
|
124
|
+
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;
|
|
125
|
+
}) | undefined) => string;
|
|
126
|
+
metadata: (props?: ({
|
|
127
|
+
layout?: "bubble" | "plain" | undefined;
|
|
128
|
+
role?: "user" | "system" | "assistant" | undefined;
|
|
129
|
+
density?: "comfortable" | "compact" | undefined;
|
|
130
|
+
} & {
|
|
131
|
+
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;
|
|
132
|
+
}) | undefined) => string;
|
|
133
|
+
}) & {
|
|
134
|
+
readonly config: import("../../../utils/variants.js").TVConfig;
|
|
135
|
+
};
|
|
136
|
+
export type ChatMessageVariants = VariantProps<typeof chatMessageVariants>;
|
|
137
|
+
/** Slot names derived from the tv() config — single source of truth for slotClasses. */
|
|
138
|
+
export type ChatMessageSlots = SlotNames<typeof chatMessageVariants>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { tv } from '../../../utils/variants.js';
|
|
2
|
+
export const chatMessageVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
// Group scope drives the hover/focus reveal of the action bar. `min-w-0`
|
|
5
|
+
// so long code/markdown scrolls inside the message, never the page.
|
|
6
|
+
root: ['group/message flex w-full min-w-0 flex-col'],
|
|
7
|
+
// Bubble layout: avatar beside the bubble. Plain layout ignores this and
|
|
8
|
+
// uses `header` + `content` instead.
|
|
9
|
+
container: ['flex min-w-0 items-start gap-3'],
|
|
10
|
+
// Plain-layout header row: avatar + role name above the full-width content.
|
|
11
|
+
header: ['flex min-w-0 items-center gap-2'],
|
|
12
|
+
roleName: ['text-sm font-medium text-text-secondary select-none'],
|
|
13
|
+
avatar: ['shrink-0'],
|
|
14
|
+
// The content bubble (bubble layout) / the full-width column (plain layout).
|
|
15
|
+
bubble: ['min-w-0 max-w-full'],
|
|
16
|
+
// Vertical stack of the rendered parts.
|
|
17
|
+
partsFlow: ['flex min-w-0 flex-col'],
|
|
18
|
+
// reasoning and tool-call parts render through ReasoningDisclosure /
|
|
19
|
+
// ToolCallCard (own slotClasses there); override via partRenderers.
|
|
20
|
+
// attachments
|
|
21
|
+
attachment: [
|
|
22
|
+
'inline-flex max-w-full items-center gap-2 rounded-modify border border-border-subtle bg-surface-base px-2.5 py-1.5 text-sm no-underline',
|
|
23
|
+
'transition-[color,background-color] duration-[var(--blocks-duration-fast)] ease-out'
|
|
24
|
+
],
|
|
25
|
+
attachmentIcon: ['shrink-0 text-text-tertiary'],
|
|
26
|
+
attachmentName: ['min-w-0 truncate text-text-primary'],
|
|
27
|
+
attachmentSize: ['shrink-0 text-xs text-text-tertiary'],
|
|
28
|
+
// sources footer
|
|
29
|
+
sourcesFooter: ['flex flex-wrap items-center gap-1.5 pt-1'],
|
|
30
|
+
// streaming placeholder (zero parts)
|
|
31
|
+
placeholder: ['py-1'],
|
|
32
|
+
// status alert
|
|
33
|
+
statusAlert: ['mt-1'],
|
|
34
|
+
// footer row holding actions + metadata
|
|
35
|
+
footer: ['flex items-center gap-2'],
|
|
36
|
+
actions: [
|
|
37
|
+
'flex items-center gap-1 opacity-0',
|
|
38
|
+
'transition-opacity duration-[var(--blocks-duration-fast)] ease-out',
|
|
39
|
+
'group-hover/message:opacity-100 group-focus-within/message:opacity-100'
|
|
40
|
+
],
|
|
41
|
+
actionButton: [
|
|
42
|
+
'inline-flex items-center justify-center rounded-modify p-1.5',
|
|
43
|
+
'text-text-tertiary cursor-pointer',
|
|
44
|
+
'transition-[color,background-color] duration-[var(--blocks-duration-fast)] ease-out',
|
|
45
|
+
'hover:bg-surface-hover hover:text-text-primary',
|
|
46
|
+
'focus-visible:outline-none focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1'
|
|
47
|
+
],
|
|
48
|
+
metadata: ['text-xs text-text-tertiary']
|
|
49
|
+
},
|
|
50
|
+
variants: {
|
|
51
|
+
layout: {
|
|
52
|
+
bubble: {},
|
|
53
|
+
// Document-like: full-width content under an avatar + role header row.
|
|
54
|
+
plain: {
|
|
55
|
+
bubble: 'w-full',
|
|
56
|
+
partsFlow: 'gap-3'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
role: {
|
|
60
|
+
user: {},
|
|
61
|
+
assistant: {},
|
|
62
|
+
system: {}
|
|
63
|
+
},
|
|
64
|
+
density: {
|
|
65
|
+
comfortable: {
|
|
66
|
+
root: 'gap-1.5',
|
|
67
|
+
partsFlow: 'gap-2'
|
|
68
|
+
},
|
|
69
|
+
compact: {
|
|
70
|
+
root: 'gap-1',
|
|
71
|
+
partsFlow: 'gap-1.5'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
compoundVariants: [
|
|
76
|
+
// ── bubble layout: role tint + alignment ────────────────────────────────
|
|
77
|
+
{
|
|
78
|
+
layout: 'bubble',
|
|
79
|
+
role: 'user',
|
|
80
|
+
class: {
|
|
81
|
+
// Reverse so the (avatar-less) bubble packs to the right edge.
|
|
82
|
+
container: 'flex-row-reverse',
|
|
83
|
+
bubble: 'max-w-[85%] rounded-contain bg-primary-subtle'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
layout: 'bubble',
|
|
88
|
+
role: 'assistant',
|
|
89
|
+
class: {
|
|
90
|
+
bubble: 'max-w-[85%] rounded-contain bg-surface-elevated'
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
layout: 'bubble',
|
|
95
|
+
role: 'system',
|
|
96
|
+
class: {
|
|
97
|
+
container: 'justify-center',
|
|
98
|
+
bubble: 'max-w-[90%] rounded-contain border border-border-subtle bg-surface-base text-sm text-text-secondary'
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
// ── bubble layout: density padding ──────────────────────────────────────
|
|
102
|
+
{
|
|
103
|
+
layout: 'bubble',
|
|
104
|
+
density: 'comfortable',
|
|
105
|
+
class: { bubble: 'px-3.5 py-2.5' }
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
layout: 'bubble',
|
|
109
|
+
density: 'compact',
|
|
110
|
+
class: { bubble: 'px-3 py-2' }
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
defaultVariants: {
|
|
114
|
+
layout: 'bubble',
|
|
115
|
+
role: 'assistant',
|
|
116
|
+
density: 'comfortable'
|
|
117
|
+
}
|
|
118
|
+
});
|