@d34dman/flowdrop 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/NodeSidebar.svelte +1 -0
  3. package/dist/components/form/FormCodeEditor.svelte +6 -1
  4. package/dist/components/interrupt/ChoicePrompt.svelte +389 -0
  5. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +21 -0
  6. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -0
  7. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +23 -0
  8. package/dist/components/interrupt/FormPrompt.svelte +223 -0
  9. package/dist/components/interrupt/FormPrompt.svelte.d.ts +21 -0
  10. package/dist/components/interrupt/InterruptBubble.svelte +621 -0
  11. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +16 -0
  12. package/dist/components/interrupt/TextInputPrompt.svelte +333 -0
  13. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +21 -0
  14. package/dist/components/interrupt/index.d.ts +13 -0
  15. package/dist/components/interrupt/index.js +15 -0
  16. package/dist/components/nodes/GatewayNode.svelte +1 -3
  17. package/dist/components/nodes/IdeaNode.svelte +30 -35
  18. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  19. package/dist/components/nodes/SimpleNode.svelte +1 -3
  20. package/dist/components/nodes/TerminalNode.svelte +1 -3
  21. package/dist/components/nodes/ToolNode.svelte +2 -2
  22. package/dist/components/nodes/WorkflowNode.svelte +1 -3
  23. package/dist/components/playground/ChatPanel.svelte +144 -7
  24. package/dist/components/playground/ChatPanel.svelte.d.ts +2 -0
  25. package/dist/components/playground/MessageBubble.svelte +1 -3
  26. package/dist/components/playground/Playground.svelte +50 -5
  27. package/dist/components/playground/PlaygroundModal.svelte +8 -7
  28. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  29. package/dist/config/endpoints.d.ts +12 -0
  30. package/dist/config/endpoints.js +7 -0
  31. package/dist/playground/index.d.ts +5 -0
  32. package/dist/playground/index.js +21 -0
  33. package/dist/playground/mount.d.ts +3 -3
  34. package/dist/playground/mount.js +30 -22
  35. package/dist/services/interruptService.d.ts +133 -0
  36. package/dist/services/interruptService.js +279 -0
  37. package/dist/stores/interruptStore.d.ts +200 -0
  38. package/dist/stores/interruptStore.js +424 -0
  39. package/dist/stores/playgroundStore.d.ts +11 -1
  40. package/dist/stores/playgroundStore.js +34 -0
  41. package/dist/styles/base.css +89 -0
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/interrupt.d.ts +305 -0
  44. package/dist/types/interrupt.js +126 -0
  45. package/dist/types/interruptState.d.ts +211 -0
  46. package/dist/types/interruptState.js +308 -0
  47. package/dist/utils/colors.js +1 -0
  48. package/dist/utils/connections.js +2 -2
  49. package/dist/utils/icons.js +1 -0
  50. package/package.json +1 -1
@@ -0,0 +1,333 @@
1
+ <!--
2
+ TextInputPrompt Component
3
+
4
+ Renders a text input prompt for text-type interrupts.
5
+ Supports single-line input and multiline textarea.
6
+ Shows the entered text when resolved.
7
+ Styled with BEM syntax.
8
+ -->
9
+
10
+ <script lang="ts">
11
+ import Icon from '@iconify/svelte';
12
+ import type { TextConfig } from '../../types/interrupt.js';
13
+
14
+ /**
15
+ * Component props
16
+ */
17
+ interface Props {
18
+ /** Text configuration from the interrupt */
19
+ config: TextConfig;
20
+ /** Whether this interrupt has been resolved */
21
+ isResolved: boolean;
22
+ /** The resolved value if resolved */
23
+ resolvedValue?: string;
24
+ /** Whether the form is currently submitting */
25
+ isSubmitting: boolean;
26
+ /** Error message if submission failed */
27
+ error?: string;
28
+ /** Callback when user submits text */
29
+ onSubmit: (value: string) => void;
30
+ }
31
+
32
+ let { config, isResolved, resolvedValue, isSubmitting, error, onSubmit }: Props = $props();
33
+
34
+ /** Local state for input value */
35
+ let inputValue = $state(config.defaultValue ?? '');
36
+
37
+ /** Display value - either resolved or current input */
38
+ const displayValue = $derived(isResolved ? (resolvedValue ?? '') : inputValue);
39
+
40
+ /** Whether the input is multiline */
41
+ const isMultiline = $derived(config.multiline ?? false);
42
+
43
+ /** Character count */
44
+ const charCount = $derived(inputValue.length);
45
+
46
+ /** Check if input is valid */
47
+ const isValidInput = $derived(
48
+ inputValue.length > 0 &&
49
+ (config.minLength === undefined || inputValue.length >= config.minLength) &&
50
+ (config.maxLength === undefined || inputValue.length <= config.maxLength)
51
+ );
52
+
53
+ /**
54
+ * Handle input change
55
+ */
56
+ function handleInput(event: Event): void {
57
+ if (isResolved || isSubmitting) return;
58
+ const target = event.target as HTMLInputElement | HTMLTextAreaElement;
59
+ inputValue = target.value;
60
+ }
61
+
62
+ /**
63
+ * Handle form submission
64
+ */
65
+ function handleSubmit(): void {
66
+ if (!isValidInput || isResolved || isSubmitting) return;
67
+ onSubmit(inputValue);
68
+ }
69
+
70
+ /**
71
+ * Handle Enter key for single-line input
72
+ */
73
+ function handleKeyDown(event: KeyboardEvent): void {
74
+ if (event.key === 'Enter' && !isMultiline && !event.shiftKey) {
75
+ event.preventDefault();
76
+ handleSubmit();
77
+ }
78
+ }
79
+ </script>
80
+
81
+ <div
82
+ class="text-prompt"
83
+ class:text-prompt--resolved={isResolved}
84
+ class:text-prompt--submitting={isSubmitting}
85
+ >
86
+ <!-- Message -->
87
+ <p class="text-prompt__message">{config.message}</p>
88
+
89
+ <!-- Error message -->
90
+ {#if error}
91
+ <div class="text-prompt__error">
92
+ <Icon icon="mdi:alert-circle" />
93
+ <span>{error}</span>
94
+ </div>
95
+ {/if}
96
+
97
+ <!-- Input field -->
98
+ <div class="text-prompt__input-wrapper">
99
+ {#if isMultiline}
100
+ <textarea
101
+ class="text-prompt__textarea"
102
+ class:text-prompt__textarea--resolved={isResolved}
103
+ value={displayValue}
104
+ placeholder={config.placeholder ?? 'Enter your response...'}
105
+ disabled={isResolved || isSubmitting}
106
+ oninput={handleInput}
107
+ onkeydown={handleKeyDown}
108
+ rows={4}
109
+ minlength={config.minLength}
110
+ maxlength={config.maxLength}
111
+ ></textarea>
112
+ {:else}
113
+ <input
114
+ type="text"
115
+ class="text-prompt__input"
116
+ class:text-prompt__input--resolved={isResolved}
117
+ value={displayValue}
118
+ placeholder={config.placeholder ?? 'Enter your response...'}
119
+ disabled={isResolved || isSubmitting}
120
+ oninput={handleInput}
121
+ onkeydown={handleKeyDown}
122
+ minlength={config.minLength}
123
+ maxlength={config.maxLength}
124
+ />
125
+ {/if}
126
+ </div>
127
+
128
+ <!-- Character count -->
129
+ {#if !isResolved && (config.minLength !== undefined || config.maxLength !== undefined)}
130
+ <div class="text-prompt__char-count">
131
+ <span
132
+ class:text-prompt__char-count--warning={config.maxLength !== undefined &&
133
+ charCount > config.maxLength * 0.9}
134
+ >
135
+ {charCount}
136
+ {#if config.maxLength !== undefined}
137
+ / {config.maxLength}
138
+ {/if}
139
+ {#if config.minLength !== undefined}
140
+ (min: {config.minLength})
141
+ {/if}
142
+ </span>
143
+ </div>
144
+ {/if}
145
+
146
+ <!-- Submit button -->
147
+ {#if !isResolved}
148
+ <div class="text-prompt__actions">
149
+ <button
150
+ type="button"
151
+ class="text-prompt__submit"
152
+ onclick={handleSubmit}
153
+ disabled={!isValidInput || isSubmitting}
154
+ >
155
+ {#if isSubmitting}
156
+ <span class="text-prompt__spinner"></span>
157
+ {:else}
158
+ <Icon icon="mdi:send" />
159
+ {/if}
160
+ <span>Submit</span>
161
+ </button>
162
+ </div>
163
+ {/if}
164
+
165
+ <!-- Resolved indicator -->
166
+ {#if isResolved}
167
+ <div class="text-prompt__resolved-badge">
168
+ <Icon icon="mdi:check-circle" />
169
+ <span>Response submitted</span>
170
+ </div>
171
+ {/if}
172
+ </div>
173
+
174
+ <style>
175
+ /* Uses design tokens from base.css: --flowdrop-interrupt-*, --color-ref-* */
176
+ .text-prompt {
177
+ display: flex;
178
+ flex-direction: column;
179
+ gap: 0.75rem;
180
+ }
181
+
182
+ .text-prompt--resolved {
183
+ opacity: 0.85;
184
+ }
185
+
186
+ .text-prompt--submitting {
187
+ pointer-events: none;
188
+ }
189
+
190
+ .text-prompt__message {
191
+ margin: 0;
192
+ font-size: 0.9375rem;
193
+ line-height: 1.5;
194
+ color: var(--color-ref-gray-800, #1f2937);
195
+ }
196
+
197
+ .text-prompt__error {
198
+ display: flex;
199
+ align-items: center;
200
+ gap: 0.375rem;
201
+ padding: 0.5rem 0.75rem;
202
+ background-color: var(--color-ref-red-50, #fef2f2);
203
+ border-radius: 0.375rem;
204
+ color: var(--color-ref-red-600, #dc2626);
205
+ font-size: 0.8125rem;
206
+ }
207
+
208
+ .text-prompt__input-wrapper {
209
+ display: flex;
210
+ flex-direction: column;
211
+ }
212
+
213
+ .text-prompt__input,
214
+ .text-prompt__textarea {
215
+ width: 100%;
216
+ padding: 0.75rem 1rem;
217
+ font-size: 0.9375rem;
218
+ font-family: inherit;
219
+ line-height: 1.5;
220
+ color: var(--color-ref-gray-800, #1f2937);
221
+ background-color: #ffffff;
222
+ border: 1px solid var(--color-ref-gray-300, #d1d5db);
223
+ border-radius: 0.5rem;
224
+ outline: none;
225
+ transition: all 0.15s ease;
226
+ }
227
+
228
+ .text-prompt__input::placeholder,
229
+ .text-prompt__textarea::placeholder {
230
+ color: var(--color-ref-gray-400, #9ca3af);
231
+ }
232
+
233
+ .text-prompt__input:focus,
234
+ .text-prompt__textarea:focus {
235
+ border-color: var(--flowdrop-interrupt-completed-border);
236
+ box-shadow: 0 0 0 3px var(--flowdrop-interrupt-completed-shadow);
237
+ }
238
+
239
+ .text-prompt__input:disabled,
240
+ .text-prompt__textarea:disabled {
241
+ background-color: var(--color-ref-gray-50, #f9fafb);
242
+ cursor: not-allowed;
243
+ }
244
+
245
+ /* Resolved state - neutral blue to match other interrupt prompts */
246
+ .text-prompt__input--resolved,
247
+ .text-prompt__textarea--resolved {
248
+ background-color: var(--color-ref-blue-50, #eff6ff);
249
+ border-color: var(--flowdrop-interrupt-completed-border);
250
+ }
251
+
252
+ .text-prompt__textarea {
253
+ resize: vertical;
254
+ min-height: 100px;
255
+ }
256
+
257
+ .text-prompt__char-count {
258
+ font-size: 0.75rem;
259
+ color: var(--color-ref-gray-500, #6b7280);
260
+ text-align: right;
261
+ padding-right: 0.25rem;
262
+ }
263
+
264
+ .text-prompt__char-count--warning {
265
+ color: var(--color-ref-amber-500, #f59e0b);
266
+ }
267
+
268
+ .text-prompt__actions {
269
+ display: flex;
270
+ gap: 0.75rem;
271
+ }
272
+
273
+ .text-prompt__submit {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ justify-content: center;
277
+ gap: 0.5rem;
278
+ padding: 0.625rem 1.25rem;
279
+ border-radius: 0.5rem;
280
+ font-size: 0.875rem;
281
+ font-weight: 600;
282
+ font-family: inherit;
283
+ cursor: pointer;
284
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
285
+ border: none;
286
+ min-height: 2.5rem;
287
+ background: var(--flowdrop-interrupt-btn-primary-bg);
288
+ color: #ffffff;
289
+ box-shadow: 0 1px 3px var(--flowdrop-interrupt-btn-primary-shadow);
290
+ }
291
+
292
+ .text-prompt__submit:hover:not(:disabled) {
293
+ background: var(--flowdrop-interrupt-btn-primary-bg-hover);
294
+ box-shadow: 0 4px 12px var(--flowdrop-interrupt-btn-primary-shadow);
295
+ transform: translateY(-1px);
296
+ }
297
+
298
+ .text-prompt__submit:disabled {
299
+ opacity: 0.5;
300
+ cursor: not-allowed;
301
+ transform: none;
302
+ box-shadow: none;
303
+ }
304
+
305
+ .text-prompt__spinner {
306
+ width: 1rem;
307
+ height: 1rem;
308
+ border: 2px solid rgba(255, 255, 255, 0.3);
309
+ border-top-color: #ffffff;
310
+ border-radius: 50%;
311
+ animation: text-spin 0.6s linear infinite;
312
+ }
313
+
314
+ @keyframes text-spin {
315
+ to {
316
+ transform: rotate(360deg);
317
+ }
318
+ }
319
+
320
+ /* Resolved badge - neutral blue theme */
321
+ .text-prompt__resolved-badge {
322
+ display: inline-flex;
323
+ align-items: center;
324
+ gap: 0.375rem;
325
+ padding: 0.375rem 0.75rem;
326
+ background-color: var(--flowdrop-interrupt-badge-completed-bg);
327
+ border-radius: 9999px;
328
+ color: var(--flowdrop-interrupt-badge-completed-text);
329
+ font-size: 0.75rem;
330
+ font-weight: 500;
331
+ align-self: flex-start;
332
+ }
333
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { TextConfig } from '../../types/interrupt.js';
2
+ /**
3
+ * Component props
4
+ */
5
+ interface Props {
6
+ /** Text configuration from the interrupt */
7
+ config: TextConfig;
8
+ /** Whether this interrupt has been resolved */
9
+ isResolved: boolean;
10
+ /** The resolved value if resolved */
11
+ resolvedValue?: string;
12
+ /** Whether the form is currently submitting */
13
+ isSubmitting: boolean;
14
+ /** Error message if submission failed */
15
+ error?: string;
16
+ /** Callback when user submits text */
17
+ onSubmit: (value: string) => void;
18
+ }
19
+ declare const TextInputPrompt: import("svelte").Component<Props, {}, "">;
20
+ type TextInputPrompt = ReturnType<typeof TextInputPrompt>;
21
+ export default TextInputPrompt;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Interrupt Components
3
+ *
4
+ * UI components for Human-in-the-Loop (HITL) interrupt handling
5
+ * in the FlowDrop playground.
6
+ *
7
+ * @module components/interrupt
8
+ */
9
+ export { default as InterruptBubble } from './InterruptBubble.svelte';
10
+ export { default as ConfirmationPrompt } from './ConfirmationPrompt.svelte';
11
+ export { default as ChoicePrompt } from './ChoicePrompt.svelte';
12
+ export { default as TextInputPrompt } from './TextInputPrompt.svelte';
13
+ export { default as FormPrompt } from './FormPrompt.svelte';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Interrupt Components
3
+ *
4
+ * UI components for Human-in-the-Loop (HITL) interrupt handling
5
+ * in the FlowDrop playground.
6
+ *
7
+ * @module components/interrupt
8
+ */
9
+ // Main container component
10
+ export { default as InterruptBubble } from './InterruptBubble.svelte';
11
+ // Prompt components
12
+ export { default as ConfirmationPrompt } from './ConfirmationPrompt.svelte';
13
+ export { default as ChoicePrompt } from './ChoicePrompt.svelte';
14
+ export { default as TextInputPrompt } from './TextInputPrompt.svelte';
15
+ export { default as FormPrompt } from './FormPrompt.svelte';
@@ -31,9 +31,7 @@
31
31
  * Falls back to the original label if not set.
32
32
  * This allows users to customize the node title per-instance via config.
33
33
  */
34
- const displayTitle = $derived(
35
- (props.data.config?.instanceTitle as string) || props.data.label
36
- );
34
+ const displayTitle = $derived((props.data.config?.instanceTitle as string) || props.data.label);
37
35
 
38
36
  /**
39
37
  * Instance-specific description override from config.
@@ -7,10 +7,10 @@
7
7
  -->
8
8
 
9
9
  <script lang="ts">
10
- import { Position, Handle } from "@xyflow/svelte";
11
- import type { ConfigValues, NodeMetadata } from "../../types/index.js";
12
- import Icon from "@iconify/svelte";
13
- import { getDataTypeColor } from "../../utils/colors.js";
10
+ import { Position, Handle } from '@xyflow/svelte';
11
+ import type { ConfigValues, NodeMetadata } from '../../types/index.js';
12
+ import Icon from '@iconify/svelte';
13
+ import { getDataTypeColor } from '../../utils/colors.js';
14
14
 
15
15
  /**
16
16
  * IdeaNode component props
@@ -44,7 +44,7 @@
44
44
  (props.data.config?.title as string) ||
45
45
  props.data.label ||
46
46
  props.data.metadata?.name ||
47
- "New Idea"
47
+ 'New Idea'
48
48
  );
49
49
 
50
50
  /**
@@ -57,7 +57,7 @@
57
57
  (props.data.config?.instanceDescription as string) ||
58
58
  (props.data.config?.description as string) ||
59
59
  props.data.metadata?.description ||
60
- "Click to add description..."
60
+ 'Click to add description...'
61
61
  );
62
62
 
63
63
  /**
@@ -66,39 +66,29 @@
66
66
  const ideaIcon = $derived(
67
67
  (props.data.config?.icon as string) ||
68
68
  (props.data.metadata?.icon as string) ||
69
- "mdi:lightbulb-outline"
69
+ 'mdi:lightbulb-outline'
70
70
  );
71
71
 
72
72
  /**
73
73
  * Get accent color from config or metadata, with fallback
74
74
  */
75
75
  const ideaColor = $derived(
76
- (props.data.config?.color as string) ||
77
- (props.data.metadata?.color as string) ||
78
- "#6366f1"
76
+ (props.data.config?.color as string) || (props.data.metadata?.color as string) || '#6366f1'
79
77
  );
80
78
 
81
79
  /**
82
80
  * Port visibility configuration from config
83
81
  * Left and Right are enabled by default, Top and Bottom are disabled by default
84
82
  */
85
- const enableLeftPort = $derived(
86
- (props.data.config?.enableLeftPort as boolean) ?? true
87
- );
88
- const enableRightPort = $derived(
89
- (props.data.config?.enableRightPort as boolean) ?? true
90
- );
91
- const enableTopPort = $derived(
92
- (props.data.config?.enableTopPort as boolean) ?? false
93
- );
94
- const enableBottomPort = $derived(
95
- (props.data.config?.enableBottomPort as boolean) ?? false
96
- );
83
+ const enableLeftPort = $derived((props.data.config?.enableLeftPort as boolean) ?? true);
84
+ const enableRightPort = $derived((props.data.config?.enableRightPort as boolean) ?? true);
85
+ const enableTopPort = $derived((props.data.config?.enableTopPort as boolean) ?? false);
86
+ const enableBottomPort = $derived((props.data.config?.enableBottomPort as boolean) ?? false);
97
87
 
98
88
  /**
99
89
  * Data type for idea flow connections
100
90
  */
101
- const IDEA_DATA_TYPE = "idea";
91
+ const IDEA_DATA_TYPE = 'idea';
102
92
 
103
93
  /**
104
94
  * Opens the configuration sidebar for editing idea properties
@@ -106,8 +96,8 @@
106
96
  function openConfigSidebar(): void {
107
97
  if (props.data.onConfigOpen) {
108
98
  const nodeForConfig = {
109
- id: props.data.nodeId || "unknown",
110
- type: "idea",
99
+ id: props.data.nodeId || 'unknown',
100
+ type: 'idea',
111
101
  data: props.data
112
102
  };
113
103
  props.data.onConfigOpen(nodeForConfig);
@@ -133,7 +123,7 @@
133
123
  * @param event - The keyboard event
134
124
  */
135
125
  function handleKeydown(event: KeyboardEvent): void {
136
- if (event.key === "Enter" || event.key === " ") {
126
+ if (event.key === 'Enter' || event.key === ' ') {
137
127
  event.preventDefault();
138
128
  handleDoubleClick();
139
129
  }
@@ -159,7 +149,9 @@
159
149
  <Handle
160
150
  type="target"
161
151
  position={Position.Left}
162
- style="background-color: {getDataTypeColor(IDEA_DATA_TYPE)}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
152
+ style="background-color: {getDataTypeColor(
153
+ IDEA_DATA_TYPE
154
+ )}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
163
155
  id={`${props.data.nodeId}-input-left`}
164
156
  />
165
157
  {/if}
@@ -169,7 +161,9 @@
169
161
  <Handle
170
162
  type="target"
171
163
  position={Position.Top}
172
- style="background-color: {getDataTypeColor(IDEA_DATA_TYPE)}; border-color: #ffffff; left: 50%; transform: translateX(-50%); z-index: 30;"
164
+ style="background-color: {getDataTypeColor(
165
+ IDEA_DATA_TYPE
166
+ )}; border-color: #ffffff; left: 50%; transform: translateX(-50%); z-index: 30;"
173
167
  id={`${props.data.nodeId}-input-top`}
174
168
  />
175
169
  {/if}
@@ -210,11 +204,7 @@
210
204
  </div>
211
205
 
212
206
  <!-- Config button -->
213
- <button
214
- class="flowdrop-idea-node__config-btn"
215
- onclick={openConfigSidebar}
216
- title="Configure idea"
217
- >
207
+ <button class="flowdrop-idea-node__config-btn" onclick={openConfigSidebar} title="Configure idea">
218
208
  <Icon icon="mdi:cog" />
219
209
  </button>
220
210
 
@@ -223,7 +213,9 @@
223
213
  <Handle
224
214
  type="source"
225
215
  position={Position.Right}
226
- style="background-color: {getDataTypeColor(IDEA_DATA_TYPE)}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
216
+ style="background-color: {getDataTypeColor(
217
+ IDEA_DATA_TYPE
218
+ )}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
227
219
  id={`${props.data.nodeId}-output-right`}
228
220
  />
229
221
  {/if}
@@ -233,7 +225,9 @@
233
225
  <Handle
234
226
  type="source"
235
227
  position={Position.Bottom}
236
- style="background-color: {getDataTypeColor(IDEA_DATA_TYPE)}; border-color: #ffffff; left: 50%; transform: translateX(-50%); z-index: 30;"
228
+ style="background-color: {getDataTypeColor(
229
+ IDEA_DATA_TYPE
230
+ )}; border-color: #ffffff; left: 50%; transform: translateX(-50%); z-index: 30;"
237
231
  id={`${props.data.nodeId}-output-bottom`}
238
232
  />
239
233
  {/if}
@@ -337,6 +331,7 @@
337
331
  line-height: 1.5;
338
332
  display: -webkit-box;
339
333
  -webkit-line-clamp: 3;
334
+ line-clamp: 3;
340
335
  -webkit-box-orient: vertical;
341
336
  overflow: hidden;
342
337
  }
@@ -1,4 +1,4 @@
1
- import type { ConfigValues, NodeMetadata } from "../../types/index.js";
1
+ import type { ConfigValues, NodeMetadata } from '../../types/index.js';
2
2
  type $$ComponentProps = {
3
3
  data: {
4
4
  label: string;
@@ -57,9 +57,7 @@
57
57
  * Falls back to the original label if not set.
58
58
  * This allows users to customize the node title per-instance via config.
59
59
  */
60
- const displayTitle = $derived(
61
- (props.data.config?.instanceTitle as string) || props.data.label
62
- );
60
+ const displayTitle = $derived((props.data.config?.instanceTitle as string) || props.data.label);
63
61
 
64
62
  /**
65
63
  * Instance-specific description override from config.
@@ -167,9 +167,7 @@
167
167
  * This allows users to customize the node description per-instance via config.
168
168
  */
169
169
  const displayDescription = $derived(
170
- (props.data.config?.instanceDescription as string) ||
171
- props.data.metadata?.description ||
172
- ""
170
+ (props.data.config?.instanceDescription as string) || props.data.metadata?.description || ''
173
171
  );
174
172
 
175
173
  /**
@@ -58,7 +58,7 @@
58
58
  (props.data.metadata?.name as string) ||
59
59
  (props.data.config?.toolName as string) ||
60
60
  props.data.label ||
61
- "Tool"
61
+ 'Tool'
62
62
  );
63
63
 
64
64
  /**
@@ -70,7 +70,7 @@
70
70
  (props.data.config?.instanceDescription as string) ||
71
71
  (props.data.metadata?.description as string) ||
72
72
  (props.data.config?.toolDescription as string) ||
73
- "A configurable tool for agents"
73
+ 'A configurable tool for agents'
74
74
  );
75
75
 
76
76
  let toolVersion = $derived(
@@ -33,9 +33,7 @@
33
33
  * Falls back to the original label if not set.
34
34
  * This allows users to customize the node title per-instance via config.
35
35
  */
36
- const displayTitle = $derived(
37
- (props.data.config?.instanceTitle as string) || props.data.label
38
- );
36
+ const displayTitle = $derived((props.data.config?.instanceTitle as string) || props.data.label);
39
37
 
40
38
  /**
41
39
  * Instance-specific description override from config.