@flowdrop/flowdrop 1.11.0 → 1.13.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/dist/api/enhanced-client.d.ts +29 -16
- package/dist/api/enhanced-client.js +0 -14
- package/dist/components/ConfigForm.svelte +1 -0
- package/dist/components/PipelineStatus.svelte +9 -12
- package/dist/components/SchemaForm.svelte +1 -0
- package/dist/components/WorkflowEditor.svelte +3 -0
- package/dist/components/form/FormAutocomplete.svelte +67 -10
- package/dist/components/form/FormField.svelte +21 -0
- package/dist/components/form/FormFieldLight.svelte +1 -0
- package/dist/components/interrupt/ChoicePrompt.svelte +24 -5
- package/dist/components/interrupt/ConfirmationPrompt.svelte +5 -0
- package/dist/components/interrupt/InterruptBubble.svelte +88 -17
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +11 -0
- package/dist/components/interrupt/ReviewPrompt.svelte +20 -0
- package/dist/components/interrupt/TextInputPrompt.svelte +5 -0
- package/dist/components/nodes/GatewayNode.svelte +2 -6
- package/dist/components/nodes/WorkflowNode.svelte +2 -6
- package/dist/components/playground/ChatBubble.svelte +289 -0
- package/dist/components/playground/ChatBubble.svelte.d.ts +10 -0
- package/dist/components/playground/ChatInput.svelte +359 -0
- package/dist/components/playground/ChatInput.svelte.d.ts +14 -0
- package/dist/components/playground/ChatPanel.svelte +100 -724
- package/dist/components/playground/ChatPanel.svelte.d.ts +9 -26
- package/dist/components/playground/ControlPanel.svelte +496 -0
- package/dist/components/playground/ControlPanel.svelte.d.ts +20 -0
- package/dist/components/playground/ExecutionConsole.svelte +163 -0
- package/dist/components/playground/ExecutionConsole.svelte.d.ts +14 -0
- package/dist/components/playground/HierarchyTrail.svelte +88 -0
- package/dist/components/playground/HierarchyTrail.svelte.d.ts +7 -0
- package/dist/components/playground/LogRow.svelte +178 -0
- package/dist/components/playground/LogRow.svelte.d.ts +8 -0
- package/dist/components/playground/MessageBubble.stories.svelte +89 -0
- package/dist/components/playground/MessageBubble.svelte +25 -737
- package/dist/components/playground/MessageBubble.svelte.d.ts +3 -11
- package/dist/components/playground/MessageCard.svelte +106 -0
- package/dist/components/playground/MessageCard.svelte.d.ts +10 -0
- package/dist/components/playground/MessageMarkdown.svelte +160 -0
- package/dist/components/playground/MessageMarkdown.svelte.d.ts +7 -0
- package/dist/components/playground/MessageNotice.svelte +120 -0
- package/dist/components/playground/MessageNotice.svelte.d.ts +9 -0
- package/dist/components/playground/MessageStream.svelte +367 -0
- package/dist/components/playground/MessageStream.svelte.d.ts +27 -0
- package/dist/components/playground/MessageTagChip.svelte +99 -0
- package/dist/components/playground/MessageTagChip.svelte.d.ts +7 -0
- package/dist/components/playground/MessageTagStrip.svelte +37 -0
- package/dist/components/playground/MessageTagStrip.svelte.d.ts +7 -0
- package/dist/components/playground/PipelineKanbanView.svelte +284 -0
- package/dist/components/playground/PipelineKanbanView.svelte.d.ts +11 -0
- package/dist/components/playground/PipelinePanel.svelte +204 -65
- package/dist/components/playground/PipelinePanel.svelte.d.ts +3 -1
- package/dist/components/playground/PipelineTableView.svelte +376 -0
- package/dist/components/playground/PipelineTableView.svelte.d.ts +11 -0
- package/dist/components/playground/Playground.svelte +262 -1200
- package/dist/components/playground/Playground.svelte.d.ts +0 -13
- package/dist/components/playground/PlaygroundStudio.svelte +113 -61
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -1
- package/dist/components/playground/messageDisplay.d.ts +19 -0
- package/dist/components/playground/messageDisplay.js +62 -0
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +22 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +77 -0
- package/dist/form/autocomplete.d.ts +1 -0
- package/dist/form/autocomplete.js +1 -0
- package/dist/form/index.d.ts +17 -0
- package/dist/form/index.js +19 -0
- package/dist/messages/defaults.d.ts +29 -0
- package/dist/messages/defaults.js +30 -0
- package/dist/playground/index.d.ts +6 -1
- package/dist/playground/index.js +6 -0
- package/dist/playground/mount.d.ts +3 -0
- package/dist/playground/mount.js +3 -2
- package/dist/schemas/v1/workflow.schema.json +10 -1
- package/dist/services/categoriesApi.d.ts +2 -1
- package/dist/services/categoriesApi.js +5 -3
- package/dist/services/portConfigApi.d.ts +2 -1
- package/dist/services/portConfigApi.js +5 -3
- package/dist/stores/playgroundStore.svelte.d.ts +6 -0
- package/dist/stores/playgroundStore.svelte.js +21 -1
- package/dist/svelte-app.d.ts +1 -0
- package/dist/svelte-app.js +5 -5
- package/dist/types/index.d.ts +41 -2
- package/dist/types/playground.d.ts +81 -2
- package/dist/types/playground.js +19 -7
- package/dist/utils/nodeStatus.js +15 -5
- package/package.json +6 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageStream Component
|
|
3
|
+
|
|
4
|
+
Renders the playground message feed with interrupt UI inline. No input area.
|
|
5
|
+
This is the shared primitive used by ChatPanel (conversational) and
|
|
6
|
+
ExecutionConsole (workflow runtime surface).
|
|
7
|
+
|
|
8
|
+
The empty/welcome state is delegated to consumers via the `welcome` and
|
|
9
|
+
`emptySession` snippets so each wrapper renders context-appropriate copy.
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { tick, untrack, type Snippet } from 'svelte';
|
|
14
|
+
import MessageBubble from './MessageBubble.svelte';
|
|
15
|
+
import { InterruptBubble } from '../interrupt/index.js';
|
|
16
|
+
import type { PlaygroundMessage } from '../../types/playground.js';
|
|
17
|
+
import {
|
|
18
|
+
isInterruptMetadata,
|
|
19
|
+
extractInterruptMetadata,
|
|
20
|
+
metadataToInterrupt
|
|
21
|
+
} from '../../types/interrupt.js';
|
|
22
|
+
import {
|
|
23
|
+
getMessages,
|
|
24
|
+
getChatMessages,
|
|
25
|
+
getIsExecuting,
|
|
26
|
+
getCurrentSession,
|
|
27
|
+
getShowLogs
|
|
28
|
+
} from '../../stores/playgroundStore.svelte.js';
|
|
29
|
+
import {
|
|
30
|
+
getInterruptsMap,
|
|
31
|
+
interruptActions,
|
|
32
|
+
getInterruptByMessageId
|
|
33
|
+
} from '../../stores/interruptStore.svelte.js';
|
|
34
|
+
import { m } from '../../messages/index.js';
|
|
35
|
+
|
|
36
|
+
interface Props {
|
|
37
|
+
/** Whether to show timestamps on messages */
|
|
38
|
+
showTimestamps?: boolean;
|
|
39
|
+
/** Whether to auto-scroll to bottom on new messages */
|
|
40
|
+
autoScroll?: boolean;
|
|
41
|
+
/** Whether to enable markdown rendering in messages */
|
|
42
|
+
enableMarkdown?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this surface is permitted to show log messages.
|
|
45
|
+
* When true, the store's showLogs toggle takes effect.
|
|
46
|
+
* When false (default), only chat messages are ever shown regardless of the toggle.
|
|
47
|
+
* Set to true on execution surfaces (e.g. ExecutionConsole); leave false on pure chat surfaces.
|
|
48
|
+
*/
|
|
49
|
+
allowLogs?: boolean;
|
|
50
|
+
/** Render system messages in compact inline form */
|
|
51
|
+
compactSystemMessages?: boolean;
|
|
52
|
+
/** Called when an interrupt is resolved */
|
|
53
|
+
onInterruptResolved?: () => void;
|
|
54
|
+
/** Custom render for the no-session welcome state */
|
|
55
|
+
welcome?: Snippet;
|
|
56
|
+
/** Custom render for the empty-session state */
|
|
57
|
+
emptySession?: Snippet;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let {
|
|
61
|
+
showTimestamps = true,
|
|
62
|
+
autoScroll = true,
|
|
63
|
+
enableMarkdown = true,
|
|
64
|
+
allowLogs = false,
|
|
65
|
+
compactSystemMessages = true,
|
|
66
|
+
onInterruptResolved,
|
|
67
|
+
welcome,
|
|
68
|
+
emptySession
|
|
69
|
+
}: Props = $props();
|
|
70
|
+
|
|
71
|
+
const states = $derived(m().playground.states);
|
|
72
|
+
|
|
73
|
+
/** Reference to the messages container for scrolling */
|
|
74
|
+
let messagesContainer: HTMLDivElement | undefined;
|
|
75
|
+
|
|
76
|
+
const displayMessages = $derived(
|
|
77
|
+
allowLogs && getShowLogs() ? getMessages() : getChatMessages()
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
let previousMessageCount = 0;
|
|
81
|
+
let userScrolledUp = false;
|
|
82
|
+
|
|
83
|
+
function handleScroll() {
|
|
84
|
+
if (!messagesContainer) return;
|
|
85
|
+
const { scrollTop, scrollHeight, clientHeight } = messagesContainer;
|
|
86
|
+
userScrolledUp = scrollHeight - scrollTop - clientHeight > 50;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isFormFocused(): boolean {
|
|
90
|
+
if (!messagesContainer) return false;
|
|
91
|
+
const activeElement = document.activeElement;
|
|
92
|
+
if (!activeElement) return false;
|
|
93
|
+
const isFormControl =
|
|
94
|
+
activeElement.tagName === 'INPUT' ||
|
|
95
|
+
activeElement.tagName === 'TEXTAREA' ||
|
|
96
|
+
activeElement.tagName === 'SELECT' ||
|
|
97
|
+
activeElement.tagName === 'BUTTON' ||
|
|
98
|
+
activeElement.getAttribute('contenteditable') === 'true';
|
|
99
|
+
return isFormControl && messagesContainer.contains(activeElement);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function isInterruptMessage(message: PlaygroundMessage): boolean {
|
|
103
|
+
return isInterruptMetadata(message.metadata as Record<string, unknown> | undefined);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sync interrupt messages into the interrupt store. Runs in an effect to
|
|
108
|
+
* avoid Svelte 5's state_unsafe_mutation error during render.
|
|
109
|
+
*/
|
|
110
|
+
$effect(() => {
|
|
111
|
+
const interruptMessages = displayMessages.filter(isInterruptMessage);
|
|
112
|
+
|
|
113
|
+
for (const message of interruptMessages) {
|
|
114
|
+
const existing = getInterruptByMessageId(message.id);
|
|
115
|
+
if (!existing) {
|
|
116
|
+
const metadata = extractInterruptMetadata(
|
|
117
|
+
message.metadata as Record<string, unknown> | undefined
|
|
118
|
+
);
|
|
119
|
+
if (metadata) {
|
|
120
|
+
const interrupt = metadataToInterrupt(metadata, message.id, message.content);
|
|
121
|
+
interruptActions.addInterrupt(interrupt);
|
|
122
|
+
|
|
123
|
+
if (message.status === 'completed') {
|
|
124
|
+
interruptActions.resolveInterrupt(interrupt.id, metadata.response_value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const interruptsByMessageId = $derived(
|
|
132
|
+
new Map(
|
|
133
|
+
Array.from(getInterruptsMap().values())
|
|
134
|
+
.filter((i) => i.messageId)
|
|
135
|
+
.map((i) => [i.messageId, i])
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
function getInterruptForMessage(message: PlaygroundMessage) {
|
|
140
|
+
return interruptsByMessageId.get(message.id);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const showWelcome = $derived(!getCurrentSession() && displayMessages.length === 0);
|
|
144
|
+
const showEmptyChat = $derived(getCurrentSession() !== null && displayMessages.length === 0);
|
|
145
|
+
|
|
146
|
+
// Reset scroll-tracking when session changes
|
|
147
|
+
$effect(() => {
|
|
148
|
+
if (getCurrentSession()) {
|
|
149
|
+
userScrolledUp = false;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
$effect(() => {
|
|
154
|
+
const currentCount = displayMessages.length;
|
|
155
|
+
|
|
156
|
+
if (!autoScroll || !messagesContainer) {
|
|
157
|
+
untrack(() => { previousMessageCount = currentCount; });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const hasNewMessage = currentCount > previousMessageCount;
|
|
162
|
+
untrack(() => { previousMessageCount = currentCount; });
|
|
163
|
+
|
|
164
|
+
if (!hasNewMessage || userScrolledUp || isFormFocused()) return;
|
|
165
|
+
|
|
166
|
+
tick().then(() => {
|
|
167
|
+
if (messagesContainer) {
|
|
168
|
+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<div class="message-stream" role="log" aria-label={m().playground.controlPanel.messageStreamLabel} bind:this={messagesContainer} onscroll={handleScroll}>
|
|
175
|
+
{#if showWelcome}
|
|
176
|
+
{#if welcome}
|
|
177
|
+
{@render welcome()}
|
|
178
|
+
{/if}
|
|
179
|
+
{:else if showEmptyChat}
|
|
180
|
+
{#if emptySession}
|
|
181
|
+
{@render emptySession()}
|
|
182
|
+
{/if}
|
|
183
|
+
{:else}
|
|
184
|
+
{#each displayMessages as message, index (message.id)}
|
|
185
|
+
{#if isInterruptMessage(message)}
|
|
186
|
+
{@const interrupt = getInterruptForMessage(message)}
|
|
187
|
+
{#if interrupt}
|
|
188
|
+
<InterruptBubble
|
|
189
|
+
{interrupt}
|
|
190
|
+
showTimestamp={showTimestamps}
|
|
191
|
+
onResolved={onInterruptResolved}
|
|
192
|
+
hierarchy={message.hierarchy}
|
|
193
|
+
tags={message.tags}
|
|
194
|
+
/>
|
|
195
|
+
{/if}
|
|
196
|
+
{:else}
|
|
197
|
+
<MessageBubble
|
|
198
|
+
{message}
|
|
199
|
+
showTimestamp={showTimestamps}
|
|
200
|
+
isLast={index === displayMessages.length - 1}
|
|
201
|
+
{enableMarkdown}
|
|
202
|
+
{compactSystemMessages}
|
|
203
|
+
/>
|
|
204
|
+
{/if}
|
|
205
|
+
{/each}
|
|
206
|
+
|
|
207
|
+
{#if getIsExecuting()}
|
|
208
|
+
<div class="message-stream__typing">
|
|
209
|
+
<div class="message-stream__typing-indicator">
|
|
210
|
+
<span></span>
|
|
211
|
+
<span></span>
|
|
212
|
+
<span></span>
|
|
213
|
+
</div>
|
|
214
|
+
<span class="message-stream__typing-text">{states.processing}</span>
|
|
215
|
+
</div>
|
|
216
|
+
{/if}
|
|
217
|
+
{/if}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<style>
|
|
221
|
+
.message-stream {
|
|
222
|
+
flex: 1;
|
|
223
|
+
min-height: 0;
|
|
224
|
+
overflow-y: auto;
|
|
225
|
+
padding: var(--fd-space-3xl);
|
|
226
|
+
|
|
227
|
+
/* Establish a containment context so message rows can adapt to the
|
|
228
|
+
stream's actual width (not the viewport's). The matching @container
|
|
229
|
+
queries (for .log-row) live below in the same <style> block, so
|
|
230
|
+
renaming the container only requires editing this file. */
|
|
231
|
+
container-type: inline-size;
|
|
232
|
+
container-name: fd-message-stream;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Shared fade-in for newly-appended message rows. `-global-` so
|
|
236
|
+
ChatBubble.svelte / MessageCard.svelte can reference it without
|
|
237
|
+
redeclaring. Honour reduced-motion in the same place. */
|
|
238
|
+
@keyframes -global-fd-fade-in {
|
|
239
|
+
from {
|
|
240
|
+
opacity: 0;
|
|
241
|
+
transform: translateY(6px);
|
|
242
|
+
}
|
|
243
|
+
to {
|
|
244
|
+
opacity: 1;
|
|
245
|
+
transform: translateY(0);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@media (prefers-reduced-motion: reduce) {
|
|
250
|
+
:global(.message-bubble),
|
|
251
|
+
:global(.message-card) {
|
|
252
|
+
animation: none;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Container-query reshaping for log rows. Lives next to the
|
|
257
|
+
container-name declaration so the coupling is local — selectors are
|
|
258
|
+
:global because .log-row is a sibling component's class.
|
|
259
|
+
|
|
260
|
+
Tier 1 (≤720px): two rows — level/body, then tags/timestamp.
|
|
261
|
+
Tier 2 (≤480px): collapse further; body forces internal line break. */
|
|
262
|
+
@container fd-message-stream (max-width: 720px) {
|
|
263
|
+
:global(.log-row) {
|
|
264
|
+
display: grid;
|
|
265
|
+
grid-template-columns: auto 1fr auto;
|
|
266
|
+
grid-template-areas:
|
|
267
|
+
"level body body"
|
|
268
|
+
". tags timestamp";
|
|
269
|
+
align-items: baseline;
|
|
270
|
+
row-gap: var(--fd-space-2xs);
|
|
271
|
+
column-gap: var(--fd-space-sm);
|
|
272
|
+
}
|
|
273
|
+
:global(.log-row__level) { grid-area: level; }
|
|
274
|
+
:global(.log-row__body) { grid-area: body; min-width: 0; }
|
|
275
|
+
:global(.log-row__tags) { grid-area: tags; justify-self: start; }
|
|
276
|
+
:global(.log-row__timestamp) { grid-area: timestamp; justify-self: end; }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@container fd-message-stream (max-width: 480px) {
|
|
280
|
+
:global(.log-row) {
|
|
281
|
+
grid-template-columns: auto 1fr;
|
|
282
|
+
grid-template-areas:
|
|
283
|
+
"level body"
|
|
284
|
+
". tags";
|
|
285
|
+
}
|
|
286
|
+
:global(.log-row__text) {
|
|
287
|
+
flex-basis: 100%;
|
|
288
|
+
min-width: 0;
|
|
289
|
+
}
|
|
290
|
+
:global(.log-row__timestamp) {
|
|
291
|
+
display: none;
|
|
292
|
+
}
|
|
293
|
+
/* Drop the source + node chips: source is implied by the level
|
|
294
|
+
colour, node duplicates the hierarchy trail's last entry. Keeping
|
|
295
|
+
them at this width forced each chip onto its own line and made
|
|
296
|
+
log rows 5–6 lines tall. */
|
|
297
|
+
:global(.log-row__source),
|
|
298
|
+
:global(.log-row__node) {
|
|
299
|
+
display: none;
|
|
300
|
+
}
|
|
301
|
+
/* Reclaim horizontal room by tightening the row's own padding —
|
|
302
|
+
can't shrink the stream's padding from inside its own
|
|
303
|
+
container query. */
|
|
304
|
+
:global(.log-row) {
|
|
305
|
+
padding-left: var(--fd-space-xs);
|
|
306
|
+
padding-right: var(--fd-space-xs);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.message-stream__typing {
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
gap: var(--fd-space-xs);
|
|
314
|
+
padding: var(--fd-space-md) var(--fd-space-xl);
|
|
315
|
+
margin-top: var(--fd-space-xs);
|
|
316
|
+
background-color: var(--fd-muted);
|
|
317
|
+
border-radius: var(--fd-radius-2xl);
|
|
318
|
+
width: fit-content;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.message-stream__typing-indicator {
|
|
322
|
+
display: flex;
|
|
323
|
+
gap: var(--fd-space-3xs);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.message-stream__typing-indicator span {
|
|
327
|
+
width: var(--fd-space-2xs);
|
|
328
|
+
height: var(--fd-space-2xs);
|
|
329
|
+
background-color: var(--fd-muted-foreground);
|
|
330
|
+
border-radius: var(--fd-radius-full);
|
|
331
|
+
animation: message-stream-bounce 1.4s ease-in-out infinite;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.message-stream__typing-indicator span:nth-child(1) {
|
|
335
|
+
animation-delay: 0s;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.message-stream__typing-indicator span:nth-child(2) {
|
|
339
|
+
animation-delay: 0.2s;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.message-stream__typing-indicator span:nth-child(3) {
|
|
343
|
+
animation-delay: 0.4s;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@keyframes message-stream-bounce {
|
|
347
|
+
0%,
|
|
348
|
+
60%,
|
|
349
|
+
100% {
|
|
350
|
+
transform: translateY(0);
|
|
351
|
+
}
|
|
352
|
+
30% {
|
|
353
|
+
transform: translateY(-0.25rem);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.message-stream__typing-text {
|
|
358
|
+
font-size: var(--fd-text-sm);
|
|
359
|
+
color: var(--fd-muted-foreground);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
@media (max-width: 640px) {
|
|
363
|
+
.message-stream {
|
|
364
|
+
padding: var(--fd-space-md) 0;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Whether to show timestamps on messages */
|
|
4
|
+
showTimestamps?: boolean;
|
|
5
|
+
/** Whether to auto-scroll to bottom on new messages */
|
|
6
|
+
autoScroll?: boolean;
|
|
7
|
+
/** Whether to enable markdown rendering in messages */
|
|
8
|
+
enableMarkdown?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether this surface is permitted to show log messages.
|
|
11
|
+
* When true, the store's showLogs toggle takes effect.
|
|
12
|
+
* When false (default), only chat messages are ever shown regardless of the toggle.
|
|
13
|
+
* Set to true on execution surfaces (e.g. ExecutionConsole); leave false on pure chat surfaces.
|
|
14
|
+
*/
|
|
15
|
+
allowLogs?: boolean;
|
|
16
|
+
/** Render system messages in compact inline form */
|
|
17
|
+
compactSystemMessages?: boolean;
|
|
18
|
+
/** Called when an interrupt is resolved */
|
|
19
|
+
onInterruptResolved?: () => void;
|
|
20
|
+
/** Custom render for the no-session welcome state */
|
|
21
|
+
welcome?: Snippet;
|
|
22
|
+
/** Custom render for the empty-session state */
|
|
23
|
+
emptySession?: Snippet;
|
|
24
|
+
}
|
|
25
|
+
declare const MessageStream: import("svelte").Component<Props, {}, "">;
|
|
26
|
+
type MessageStream = ReturnType<typeof MessageStream>;
|
|
27
|
+
export default MessageStream;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageTagChip Component
|
|
3
|
+
|
|
4
|
+
Renders a single server-emitted MessageTag as a compact chip. Semantic
|
|
5
|
+
color comes from tag.color, visual emphasis from tag.variant. Used by
|
|
6
|
+
MessageBubble and InterruptBubble.
|
|
7
|
+
|
|
8
|
+
Styling: a single base rule reads from CSS custom properties; one rule
|
|
9
|
+
per color sets --chip-c, one rule per variant sets bg/fg/border in terms
|
|
10
|
+
of --chip-c. Adding a color is one line.
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import Icon from '@iconify/svelte';
|
|
15
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
tag: MessageTag;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { tag }: Props = $props();
|
|
22
|
+
|
|
23
|
+
const color = $derived(tag.color ?? 'muted');
|
|
24
|
+
const variant = $derived(tag.variant ?? 'subtle');
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<span
|
|
28
|
+
class="message-tag-chip"
|
|
29
|
+
data-color={color}
|
|
30
|
+
data-variant={variant}
|
|
31
|
+
aria-label={tag.type ? `${tag.type}: ${tag.label}` : undefined}
|
|
32
|
+
>
|
|
33
|
+
{#if tag.icon}
|
|
34
|
+
<Icon icon={tag.icon} class="message-tag-chip__icon" aria-hidden="true" />
|
|
35
|
+
{/if}
|
|
36
|
+
<span class="message-tag-chip__label">{tag.label}</span>
|
|
37
|
+
</span>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.message-tag-chip {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--fd-space-3xs);
|
|
44
|
+
padding: 0 var(--fd-space-3xs);
|
|
45
|
+
border-radius: var(--fd-radius-sm);
|
|
46
|
+
font-family: var(--fd-font-mono);
|
|
47
|
+
font-size: var(--fd-text-2xs);
|
|
48
|
+
line-height: 1.4;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
min-width: 0;
|
|
51
|
+
max-width: 100%;
|
|
52
|
+
background-color: var(--chip-bg);
|
|
53
|
+
color: var(--chip-fg);
|
|
54
|
+
border: 1px solid var(--chip-border, transparent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.message-tag-chip__label {
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
text-overflow: ellipsis;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.message-tag-chip :global(.message-tag-chip__icon) {
|
|
63
|
+
flex-shrink: 0;
|
|
64
|
+
font-size: 0.875em;
|
|
65
|
+
opacity: 0.8;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Color hooks — one line per color. To add a color, add a row here. */
|
|
69
|
+
.message-tag-chip[data-color='muted'] { --chip-c: var(--fd-muted-foreground); --chip-c-on: var(--fd-background); }
|
|
70
|
+
.message-tag-chip[data-color='primary'] { --chip-c: var(--fd-primary); --chip-c-on: var(--fd-primary-foreground); }
|
|
71
|
+
.message-tag-chip[data-color='success'] { --chip-c: var(--fd-success, oklch(55% 0.15 145)); --chip-c-on: white; }
|
|
72
|
+
.message-tag-chip[data-color='warning'] { --chip-c: var(--fd-warning); --chip-c-on: var(--fd-background); }
|
|
73
|
+
.message-tag-chip[data-color='error'] { --chip-c: var(--fd-error); --chip-c-on: white; }
|
|
74
|
+
.message-tag-chip[data-color='info'] { --chip-c: var(--fd-info); --chip-c-on: var(--fd-background); }
|
|
75
|
+
|
|
76
|
+
/* Variants — derive bg/fg/border from --chip-c. */
|
|
77
|
+
.message-tag-chip[data-variant='subtle'] {
|
|
78
|
+
--chip-bg: color-mix(in srgb, var(--chip-c) 14%, transparent);
|
|
79
|
+
--chip-fg: var(--chip-c);
|
|
80
|
+
}
|
|
81
|
+
.message-tag-chip[data-variant='subtle'][data-color='muted'] {
|
|
82
|
+
/* Muted is the only color we render against the design's --fd-muted
|
|
83
|
+
surface for legibility; the color-mix path would lose contrast. */
|
|
84
|
+
--chip-bg: var(--fd-muted);
|
|
85
|
+
--chip-fg: var(--fd-muted-foreground);
|
|
86
|
+
}
|
|
87
|
+
.message-tag-chip[data-variant='solid'] {
|
|
88
|
+
--chip-bg: var(--chip-c);
|
|
89
|
+
--chip-fg: var(--chip-c-on);
|
|
90
|
+
}
|
|
91
|
+
.message-tag-chip[data-variant='outline'] {
|
|
92
|
+
--chip-bg: transparent;
|
|
93
|
+
--chip-fg: var(--chip-c);
|
|
94
|
+
--chip-border: var(--chip-c);
|
|
95
|
+
}
|
|
96
|
+
.message-tag-chip[data-variant='outline'][data-color='muted'] {
|
|
97
|
+
--chip-border: var(--fd-border);
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
tag: MessageTag;
|
|
4
|
+
}
|
|
5
|
+
declare const MessageTagChip: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type MessageTagChip = ReturnType<typeof MessageTagChip>;
|
|
7
|
+
export default MessageTagChip;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageTagStrip Component
|
|
3
|
+
|
|
4
|
+
A flex-wrapped row of MessageTagChips. Encapsulates the layout the four
|
|
5
|
+
message variants used to copy individually.
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
10
|
+
import MessageTagChip from './MessageTagChip.svelte';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
tags: MessageTag[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { tags }: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if tags.length > 0}
|
|
20
|
+
<!-- No role/label — each chip is already labelled (via tag.type when set)
|
|
21
|
+
and a one-element group adds nothing for AT users. -->
|
|
22
|
+
<span class="message-tag-strip">
|
|
23
|
+
{#each tags as tag (tag.id)}
|
|
24
|
+
<MessageTagChip {tag} />
|
|
25
|
+
{/each}
|
|
26
|
+
</span>
|
|
27
|
+
{/if}
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.message-tag-strip {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: var(--fd-space-2xs);
|
|
35
|
+
min-width: 0;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
tags: MessageTag[];
|
|
4
|
+
}
|
|
5
|
+
declare const MessageTagStrip: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type MessageTagStrip = ReturnType<typeof MessageTagStrip>;
|
|
7
|
+
export default MessageTagStrip;
|