@d34dman/flowdrop 0.0.40 → 0.0.42

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.
@@ -25,11 +25,14 @@
25
25
  isSubmitting: boolean;
26
26
  /** Error message if submission failed */
27
27
  error?: string;
28
+ /** Username of the person who resolved the interrupt */
29
+ resolvedByUserName?: string;
28
30
  /** Callback when user submits selection */
29
31
  onSubmit: (value: string | string[]) => void;
30
32
  }
31
33
 
32
- let { config, isResolved, resolvedValue, isSubmitting, error, onSubmit }: Props = $props();
34
+ let { config, isResolved, resolvedValue, isSubmitting, error, resolvedByUserName, onSubmit }: Props =
35
+ $props();
33
36
 
34
37
  /** Local state for selected values */
35
38
  let selectedValues = $state<Set<string>>(new Set());
@@ -189,7 +192,9 @@
189
192
  {#if isResolved}
190
193
  <div class="choice-prompt__resolved-badge">
191
194
  <Icon icon="mdi:check-circle" />
192
- <span>Response submitted</span>
195
+ <span>
196
+ {resolvedByUserName ? `Response submitted by ${resolvedByUserName}` : 'Response submitted'}
197
+ </span>
193
198
  </div>
194
199
  {/if}
195
200
  </div>
@@ -13,6 +13,8 @@ interface Props {
13
13
  isSubmitting: boolean;
14
14
  /** Error message if submission failed */
15
15
  error?: string;
16
+ /** Username of the person who resolved the interrupt */
17
+ resolvedByUserName?: string;
16
18
  /** Callback when user submits selection */
17
19
  onSubmit: (value: string | string[]) => void;
18
20
  }
@@ -25,14 +25,24 @@
25
25
  isSubmitting: boolean;
26
26
  /** Error message if submission failed */
27
27
  error?: string;
28
+ /** Username of the person who resolved the interrupt */
29
+ resolvedByUserName?: string;
28
30
  /** Callback when user confirms (Yes) */
29
31
  onConfirm: () => void;
30
32
  /** Callback when user declines (No) */
31
33
  onDecline: () => void;
32
34
  }
33
35
 
34
- let { config, isResolved, resolvedValue, isSubmitting, error, onConfirm, onDecline }: Props =
35
- $props();
36
+ let {
37
+ config,
38
+ isResolved,
39
+ resolvedValue,
40
+ isSubmitting,
41
+ error,
42
+ resolvedByUserName,
43
+ onConfirm,
44
+ onDecline
45
+ }: Props = $props();
36
46
 
37
47
  /** Computed label for confirm button */
38
48
  const confirmLabel = $derived(config.confirmLabel ?? 'Yes');
@@ -108,7 +118,9 @@
108
118
  {#if isResolved}
109
119
  <div class="confirmation-prompt__resolved-badge">
110
120
  <Icon icon="mdi:check-circle" />
111
- <span>Response submitted</span>
121
+ <span>
122
+ {resolvedByUserName ? `Response submitted by ${resolvedByUserName}` : 'Response submitted'}
123
+ </span>
112
124
  </div>
113
125
  {/if}
114
126
  </div>
@@ -13,6 +13,8 @@ interface Props {
13
13
  isSubmitting: boolean;
14
14
  /** Error message if submission failed */
15
15
  error?: string;
16
+ /** Username of the person who resolved the interrupt */
17
+ resolvedByUserName?: string;
16
18
  /** Callback when user confirms (Yes) */
17
19
  onConfirm: () => void;
18
20
  /** Callback when user declines (No) */
@@ -26,11 +26,14 @@
26
26
  isSubmitting: boolean;
27
27
  /** Error message if submission failed */
28
28
  error?: string;
29
+ /** Username of the person who resolved the interrupt */
30
+ resolvedByUserName?: string;
29
31
  /** Callback when user submits form */
30
32
  onSubmit: (value: Record<string, unknown>) => void;
31
33
  }
32
34
 
33
- let { config, isResolved, resolvedValue, isSubmitting, error, onSubmit }: Props = $props();
35
+ let { config, isResolved, resolvedValue, isSubmitting, error, resolvedByUserName, onSubmit }: Props =
36
+ $props();
34
37
 
35
38
  /** Local state for form values */
36
39
  let formValues = $state<Record<string, unknown>>(config.defaultValues ?? {});
@@ -119,7 +122,9 @@
119
122
  {#if isResolved}
120
123
  <div class="form-prompt__resolved-badge">
121
124
  <Icon icon="mdi:check-circle" />
122
- <span>Response submitted</span>
125
+ <span>
126
+ {resolvedByUserName ? `Response submitted by ${resolvedByUserName}` : 'Response submitted'}
127
+ </span>
123
128
  </div>
124
129
  {/if}
125
130
  </div>
@@ -13,6 +13,8 @@ interface Props {
13
13
  isSubmitting: boolean;
14
14
  /** Error message if submission failed */
15
15
  error?: string;
16
+ /** Username of the person who resolved the interrupt */
17
+ resolvedByUserName?: string;
16
18
  /** Callback when user submits form */
17
19
  onSubmit: (value: Record<string, unknown>) => void;
18
20
  }
@@ -221,6 +221,16 @@
221
221
 
222
222
  // Determine the actual resolved value to pass to prompt components
223
223
  const displayResolvedValue = $derived(resolvedValue ?? currentInterrupt.responseValue);
224
+
225
+ /**
226
+ * Extract the username of who resolved the interrupt from metadata.
227
+ * This is provided by the backend when the interrupt is resolved.
228
+ */
229
+ const resolvedByUserName = $derived(
230
+ typeof currentInterrupt.metadata?.resolvedByUserName === 'string'
231
+ ? currentInterrupt.metadata.resolvedByUserName
232
+ : undefined
233
+ );
224
234
  </script>
225
235
 
226
236
  <div
@@ -287,6 +297,7 @@
287
297
  resolvedValue={displayResolvedValue as boolean | undefined}
288
298
  {isSubmitting}
289
299
  {error}
300
+ {resolvedByUserName}
290
301
  onConfirm={() => handleResolve(true)}
291
302
  onDecline={() => handleResolve(false)}
292
303
  />
@@ -297,6 +308,7 @@
297
308
  resolvedValue={displayResolvedValue as string | string[] | undefined}
298
309
  {isSubmitting}
299
310
  {error}
311
+ {resolvedByUserName}
300
312
  onSubmit={(value) => handleResolve(value)}
301
313
  />
302
314
  {:else if currentInterrupt.type === 'text'}
@@ -306,6 +318,7 @@
306
318
  resolvedValue={displayResolvedValue as string | undefined}
307
319
  {isSubmitting}
308
320
  {error}
321
+ {resolvedByUserName}
309
322
  onSubmit={(value) => handleResolve(value)}
310
323
  />
311
324
  {:else if currentInterrupt.type === 'form'}
@@ -315,6 +328,7 @@
315
328
  resolvedValue={displayResolvedValue as Record<string, unknown> | undefined}
316
329
  {isSubmitting}
317
330
  {error}
331
+ {resolvedByUserName}
318
332
  onSubmit={(value) => handleResolve(value)}
319
333
  />
320
334
  {/if}
@@ -25,11 +25,14 @@
25
25
  isSubmitting: boolean;
26
26
  /** Error message if submission failed */
27
27
  error?: string;
28
+ /** Username of the person who resolved the interrupt */
29
+ resolvedByUserName?: string;
28
30
  /** Callback when user submits text */
29
31
  onSubmit: (value: string) => void;
30
32
  }
31
33
 
32
- let { config, isResolved, resolvedValue, isSubmitting, error, onSubmit }: Props = $props();
34
+ let { config, isResolved, resolvedValue, isSubmitting, error, resolvedByUserName, onSubmit }: Props =
35
+ $props();
33
36
 
34
37
  /** Local state for input value */
35
38
  let inputValue = $state(config.defaultValue ?? '');
@@ -166,7 +169,9 @@
166
169
  {#if isResolved}
167
170
  <div class="text-prompt__resolved-badge">
168
171
  <Icon icon="mdi:check-circle" />
169
- <span>Response submitted</span>
172
+ <span>
173
+ {resolvedByUserName ? `Response submitted by ${resolvedByUserName}` : 'Response submitted'}
174
+ </span>
170
175
  </div>
171
176
  {/if}
172
177
  </div>
@@ -13,6 +13,8 @@ interface Props {
13
13
  isSubmitting: boolean;
14
14
  /** Error message if submission failed */
15
15
  error?: string;
16
+ /** Username of the person who resolved the interrupt */
17
+ resolvedByUserName?: string;
16
18
  /** Callback when user submits text */
17
19
  onSubmit: (value: string) => void;
18
20
  }
@@ -66,6 +66,13 @@
66
66
  * Used when showChatInput is false.
67
67
  */
68
68
  predefinedMessage?: string;
69
+ /**
70
+ * Whether to display system messages in compact mode.
71
+ * When true, system messages appear as minimal inline text
72
+ * instead of full chat bubbles to reduce visual noise.
73
+ * @default true
74
+ */
75
+ compactSystemMessages?: boolean;
69
76
  }
70
77
 
71
78
  let {
@@ -79,7 +86,8 @@
79
86
  onInterruptResolved,
80
87
  showChatInput = true,
81
88
  showRunButton = true,
82
- predefinedMessage = 'Run workflow'
89
+ predefinedMessage = 'Run workflow',
90
+ compactSystemMessages = true
83
91
  }: Props = $props();
84
92
 
85
93
  /**
@@ -109,6 +117,46 @@
109
117
  */
110
118
  const displayMessages = $derived(showLogsInline ? $messages : $chatMessages);
111
119
 
120
+ /**
121
+ * Track previous message count for detecting new messages.
122
+ * We only want to auto-scroll when NEW messages are added,
123
+ * not when existing messages are updated.
124
+ */
125
+ let previousMessageCount = $state(0);
126
+
127
+ /**
128
+ * Check if user is near the bottom of the scroll container.
129
+ * Used to determine if we should auto-scroll when new messages arrive.
130
+ * If user has scrolled up to read previous messages, we don't interrupt them.
131
+ *
132
+ * @param threshold - Pixels from bottom to consider "near bottom"
133
+ * @returns True if user is within threshold of the bottom
134
+ */
135
+ function isNearBottom(threshold: number = 100): boolean {
136
+ if (!messagesContainer) return true;
137
+ const { scrollTop, scrollHeight, clientHeight } = messagesContainer;
138
+ return scrollHeight - scrollTop - clientHeight <= threshold;
139
+ }
140
+
141
+ /**
142
+ * Check if a form element inside the messages container has focus.
143
+ * When user is interacting with a form (e.g., interrupt prompt),
144
+ * we should not auto-scroll as it disrupts their input.
145
+ */
146
+ function isFormFocused(): boolean {
147
+ if (!messagesContainer) return false;
148
+ const activeElement = document.activeElement;
149
+ if (!activeElement) return false;
150
+ // Check if active element is a form control inside the messages container
151
+ const isFormControl =
152
+ activeElement.tagName === 'INPUT' ||
153
+ activeElement.tagName === 'TEXTAREA' ||
154
+ activeElement.tagName === 'SELECT' ||
155
+ activeElement.tagName === 'BUTTON' ||
156
+ activeElement.getAttribute('contenteditable') === 'true';
157
+ return isFormControl && messagesContainer.contains(activeElement);
158
+ }
159
+
112
160
  /**
113
161
  * Check if a message is an interrupt request
114
162
  */
@@ -280,16 +328,55 @@
280
328
  });
281
329
 
282
330
  /**
283
- * Auto-scroll to bottom when messages change
331
+ * Smart auto-scroll to bottom when NEW messages are added.
332
+ *
333
+ * Only scrolls if:
334
+ * 1. autoScroll prop is enabled
335
+ * 2. New messages were actually added (not just updates)
336
+ * 3. User is already near the bottom (hasn't scrolled up to read)
337
+ * 4. User is not interacting with a form inside the chat
338
+ *
339
+ * This prevents disruptive scrolling when:
340
+ * - User is reading previous messages
341
+ * - User is filling out an interrupt form
342
+ * - Messages are being updated (e.g., status changes)
284
343
  */
285
344
  $effect(() => {
286
- if (autoScroll && messagesContainer && displayMessages.length > 0) {
287
- tick().then(() => {
288
- if (messagesContainer) {
289
- messagesContainer.scrollTop = messagesContainer.scrollHeight;
290
- }
291
- });
345
+ const currentCount = displayMessages.length;
346
+
347
+ // Skip if auto-scroll is disabled or no container
348
+ if (!autoScroll || !messagesContainer) {
349
+ previousMessageCount = currentCount;
350
+ return;
351
+ }
352
+
353
+ // Check if this is a NEW message (count increased)
354
+ const hasNewMessage = currentCount > previousMessageCount;
355
+
356
+ // Update the tracked count
357
+ previousMessageCount = currentCount;
358
+
359
+ // Only scroll if there's a new message
360
+ if (!hasNewMessage) {
361
+ return;
362
+ }
363
+
364
+ // Don't scroll if user has scrolled up to read previous messages
365
+ if (!isNearBottom()) {
366
+ return;
367
+ }
368
+
369
+ // Don't scroll if user is interacting with a form
370
+ if (isFormFocused()) {
371
+ return;
292
372
  }
373
+
374
+ // Safe to scroll to bottom
375
+ tick().then(() => {
376
+ if (messagesContainer) {
377
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
378
+ }
379
+ });
293
380
  });
294
381
 
295
382
  /**
@@ -453,6 +540,7 @@
453
540
  showTimestamp={showTimestamps}
454
541
  isLast={index === displayMessages.length - 1}
455
542
  {enableMarkdown}
543
+ {compactSystemMessages}
456
544
  />
457
545
  {/if}
458
546
  {/each}
@@ -33,6 +33,13 @@ interface Props {
33
33
  * Used when showChatInput is false.
34
34
  */
35
35
  predefinedMessage?: string;
36
+ /**
37
+ * Whether to display system messages in compact mode.
38
+ * When true, system messages appear as minimal inline text
39
+ * instead of full chat bubbles to reduce visual noise.
40
+ * @default true
41
+ */
42
+ compactSystemMessages?: boolean;
36
43
  }
37
44
  declare const ChatPanel: import("svelte").Component<Props, {}, "">;
38
45
  type ChatPanel = ReturnType<typeof ChatPanel>;
@@ -4,13 +4,18 @@
4
4
  Renders individual messages in the playground chat interface.
5
5
  Supports different message roles with distinct styling.
6
6
  Supports markdown rendering for message content.
7
+ Supports compact mode for system messages to reduce visual noise.
7
8
  Styled with BEM syntax.
8
9
  -->
9
10
 
10
11
  <script lang="ts">
11
12
  import Icon from '@iconify/svelte';
12
13
  import { marked } from 'marked';
13
- import type { PlaygroundMessage, PlaygroundMessageRole } from '../../types/playground.js';
14
+ import type {
15
+ PlaygroundMessage,
16
+ PlaygroundMessageMetadata,
17
+ PlaygroundMessageRole
18
+ } from '../../types/playground.js';
14
19
 
15
20
  /**
16
21
  * Component props
@@ -24,9 +29,28 @@
24
29
  isLast?: boolean;
25
30
  /** Whether to render markdown content */
26
31
  enableMarkdown?: boolean;
27
- }
32
+ /**
33
+ * Use compact display mode for system messages.
34
+ * When true, system messages are rendered as minimal inline text
35
+ * instead of full chat bubbles to reduce visual noise.
36
+ * @default true
37
+ */
38
+ compactSystemMessages?: boolean;
39
+ }
40
+
41
+ let {
42
+ message,
43
+ showTimestamp = true,
44
+ isLast = false,
45
+ enableMarkdown = true,
46
+ compactSystemMessages = true
47
+ }: Props = $props();
28
48
 
29
- let { message, showTimestamp = true, isLast = false, enableMarkdown = true }: Props = $props();
49
+ /**
50
+ * Determine if this message should render in compact mode.
51
+ * Only system messages use compact mode when enabled.
52
+ */
53
+ const useCompactMode = $derived(message.role === 'system' && compactSystemMessages);
30
54
 
31
55
  /**
32
56
  * Render content as markdown or plain text
@@ -60,18 +84,22 @@
60
84
  * Get the display label for the message role
61
85
  *
62
86
  * @param role - The message role
87
+ * @param metadata - Optional message metadata containing userName for user messages
63
88
  * @returns Display label
64
89
  */
65
- function getRoleLabel(role: PlaygroundMessageRole): string {
90
+ function getRoleLabel(
91
+ role: PlaygroundMessageRole,
92
+ metadata?: PlaygroundMessageMetadata
93
+ ): string {
66
94
  switch (role) {
67
95
  case 'user':
68
- return 'You';
96
+ return metadata?.userName ?? 'You';
69
97
  case 'assistant':
70
98
  return 'Assistant';
71
99
  case 'system':
72
100
  return 'System';
73
101
  case 'log':
74
- return message.metadata?.nodeLabel ?? 'Log';
102
+ return metadata?.nodeLabel ?? 'Log';
75
103
  default:
76
104
  return 'Message';
77
105
  }
@@ -121,68 +149,79 @@
121
149
  }
122
150
  </script>
123
151
 
124
- <div
125
- class="message-bubble"
126
- class:message-bubble--user={message.role === 'user'}
127
- class:message-bubble--assistant={message.role === 'assistant'}
128
- class:message-bubble--system={message.role === 'system'}
129
- class:message-bubble--log={message.role === 'log'}
130
- class:message-bubble--log-error={message.role === 'log' && message.metadata?.level === 'error'}
131
- class:message-bubble--log-warning={message.role === 'log' &&
132
- message.metadata?.level === 'warning'}
133
- class:message-bubble--last={isLast}
134
- >
135
- <!-- Avatar / Icon -->
136
- <div class="message-bubble__avatar">
137
- <Icon icon={getRoleIcon(message.role)} />
152
+ {#if useCompactMode}
153
+ <!-- Compact system message: minimal inline text without bubble -->
154
+ <div class="system-notice" class:system-notice--last={isLast}>
155
+ <Icon icon="mdi:information-outline" class="system-notice__icon" />
156
+ <span class="system-notice__text">{message.content}</span>
157
+ {#if showTimestamp}
158
+ <span class="system-notice__timestamp">{formatTimestamp(message.timestamp)}</span>
159
+ {/if}
138
160
  </div>
139
-
140
- <!-- Content -->
141
- <div class="message-bubble__content">
142
- <!-- Header -->
143
- <div class="message-bubble__header">
144
- <span class="message-bubble__role">{getRoleLabel(message.role)}</span>
145
- {#if message.role === 'log' && message.metadata?.level}
146
- <span class="message-bubble__log-level message-bubble__log-level--{message.metadata.level}">
147
- <Icon icon={getLogLevelIcon()} />
148
- {message.metadata.level.toUpperCase()}
149
- </span>
150
- {/if}
151
- {#if showTimestamp}
152
- <span class="message-bubble__timestamp">{formatTimestamp(message.timestamp)}</span>
153
- {/if}
161
+ {:else}
162
+ <div
163
+ class="message-bubble"
164
+ class:message-bubble--user={message.role === 'user'}
165
+ class:message-bubble--assistant={message.role === 'assistant'}
166
+ class:message-bubble--system={message.role === 'system'}
167
+ class:message-bubble--log={message.role === 'log'}
168
+ class:message-bubble--log-error={message.role === 'log' && message.metadata?.level === 'error'}
169
+ class:message-bubble--log-warning={message.role === 'log' &&
170
+ message.metadata?.level === 'warning'}
171
+ class:message-bubble--last={isLast}
172
+ >
173
+ <!-- Avatar / Icon -->
174
+ <div class="message-bubble__avatar">
175
+ <Icon icon={getRoleIcon(message.role)} />
154
176
  </div>
155
177
 
156
- <!-- Message Text -->
157
- <div class="message-bubble__text">
158
- {#if enableMarkdown && message.role !== 'log'}
159
- <!-- Markdown content - marked.js sanitizes content by default -->
160
- <!-- eslint-disable-next-line svelte/no-at-html-tags -->
161
- {@html renderedContent}
162
- {:else}
163
- {message.content}
164
- {/if}
165
- </div>
166
-
167
- <!-- Metadata Footer -->
168
- {#if message.metadata?.duration !== undefined || message.nodeId}
169
- <div class="message-bubble__footer">
170
- {#if message.nodeId}
171
- <span class="message-bubble__node" title="Node ID: {message.nodeId}">
172
- <Icon icon="mdi:graph" />
173
- {message.metadata?.nodeLabel ?? message.nodeId}
178
+ <!-- Content -->
179
+ <div class="message-bubble__content">
180
+ <!-- Header -->
181
+ <div class="message-bubble__header">
182
+ <span class="message-bubble__role">{getRoleLabel(message.role, message.metadata)}</span>
183
+ {#if message.role === 'log' && message.metadata?.level}
184
+ <span class="message-bubble__log-level message-bubble__log-level--{message.metadata.level}">
185
+ <Icon icon={getLogLevelIcon()} />
186
+ {message.metadata.level.toUpperCase()}
174
187
  </span>
175
188
  {/if}
176
- {#if message.metadata?.duration !== undefined}
177
- <span class="message-bubble__duration" title="Execution duration">
178
- <Icon icon="mdi:timer-outline" />
179
- {formatDuration(message.metadata.duration)}
180
- </span>
189
+ {#if showTimestamp}
190
+ <span class="message-bubble__timestamp">{formatTimestamp(message.timestamp)}</span>
181
191
  {/if}
182
192
  </div>
183
- {/if}
193
+
194
+ <!-- Message Text -->
195
+ <div class="message-bubble__text">
196
+ {#if enableMarkdown && message.role !== 'log'}
197
+ <!-- Markdown content - marked.js sanitizes content by default -->
198
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
199
+ {@html renderedContent}
200
+ {:else}
201
+ {message.content}
202
+ {/if}
203
+ </div>
204
+
205
+ <!-- Metadata Footer -->
206
+ {#if message.metadata?.duration !== undefined || message.nodeId}
207
+ <div class="message-bubble__footer">
208
+ {#if message.nodeId}
209
+ <span class="message-bubble__node" title="Node ID: {message.nodeId}">
210
+ <Icon icon="mdi:graph" />
211
+ {message.metadata?.nodeLabel ?? message.nodeId}
212
+ </span>
213
+ {/if}
214
+ {#if message.metadata?.duration !== undefined}
215
+ <span class="message-bubble__duration" title="Execution duration">
216
+ <Icon icon="mdi:timer-outline" />
217
+ {formatDuration(message.metadata.duration)}
218
+ </span>
219
+ {/if}
220
+ </div>
221
+ {/if}
222
+ </div>
184
223
  </div>
185
- </div>
224
+ {/if}
186
225
 
187
226
  <style>
188
227
  .message-bubble {
@@ -542,4 +581,51 @@
542
581
  font-size: 1rem;
543
582
  }
544
583
  }
584
+
585
+ /* ========================================
586
+ Compact System Notice Styles
587
+ Minimal inline display for system messages
588
+ ======================================== */
589
+
590
+ .system-notice {
591
+ display: flex;
592
+ align-items: center;
593
+ justify-content: center;
594
+ gap: 0.375rem;
595
+ padding: 0.375rem 0.75rem;
596
+ margin: 0.25rem 0;
597
+ font-size: 0.75rem;
598
+ color: #9ca3af;
599
+ text-align: center;
600
+ }
601
+
602
+ .system-notice--last {
603
+ margin-bottom: 0.75rem;
604
+ }
605
+
606
+ /* Icon styling - using :global for Iconify component */
607
+ .system-notice :global(.system-notice__icon) {
608
+ flex-shrink: 0;
609
+ font-size: 0.875rem;
610
+ color: #d1d5db;
611
+ }
612
+
613
+ .system-notice__text {
614
+ color: #6b7280;
615
+ line-height: 1.4;
616
+ }
617
+
618
+ .system-notice__timestamp {
619
+ flex-shrink: 0;
620
+ font-size: 0.625rem;
621
+ color: #d1d5db;
622
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
623
+ }
624
+
625
+ /* Responsive: hide timestamp on small screens for compactness */
626
+ @media (max-width: 640px) {
627
+ .system-notice__timestamp {
628
+ display: none;
629
+ }
630
+ }
545
631
  </style>
@@ -11,6 +11,13 @@ interface Props {
11
11
  isLast?: boolean;
12
12
  /** Whether to render markdown content */
13
13
  enableMarkdown?: boolean;
14
+ /**
15
+ * Use compact display mode for system messages.
16
+ * When true, system messages are rendered as minimal inline text
17
+ * instead of full chat bubbles to reduce visual noise.
18
+ * @default true
19
+ */
20
+ compactSystemMessages?: boolean;
14
21
  }
15
22
  declare const MessageBubble: import("svelte").Component<Props, {}, "">;
16
23
  type MessageBubble = ReturnType<typeof MessageBubble>;
@@ -264,6 +264,8 @@ export interface InterruptMessageMetadata {
264
264
  multiple?: boolean;
265
265
  min_selections?: number;
266
266
  max_selections?: number;
267
+ /** Username of the person who resolved the interrupt */
268
+ resolvedByUserName?: string;
267
269
  }
268
270
  /**
269
271
  * Type guard to check if message metadata indicates an interrupt
@@ -48,7 +48,8 @@ export function extractInterruptMetadata(metadata) {
48
48
  max_length: metadata.max_length,
49
49
  multiple: metadata.multiple,
50
50
  min_selections: metadata.min_selections,
51
- max_selections: metadata.max_selections
51
+ max_selections: metadata.max_selections,
52
+ resolvedByUserName: metadata.resolvedByUserName
52
53
  };
53
54
  }
54
55
  /**
@@ -74,6 +74,8 @@ export interface PlaygroundMessageMetadata {
74
74
  nodeLabel?: string;
75
75
  /** Node output data */
76
76
  outputs?: Record<string, unknown>;
77
+ /** User's display name for user-role messages (from backend) */
78
+ userName?: string;
77
79
  /** Allow additional properties */
78
80
  [key: string]: unknown;
79
81
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@d34dman/flowdrop",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "0.0.40",
5
+ "version": "0.0.42",
6
6
  "scripts": {
7
7
  "dev": "vite dev",
8
8
  "build": "vite build && npm run prepack",