@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.
- package/LICENSE +21 -0
- package/dist/components/NodeSidebar.svelte +1 -0
- package/dist/components/form/FormCodeEditor.svelte +6 -1
- package/dist/components/interrupt/ChoicePrompt.svelte +389 -0
- package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -0
- package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +23 -0
- package/dist/components/interrupt/FormPrompt.svelte +223 -0
- package/dist/components/interrupt/FormPrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/InterruptBubble.svelte +621 -0
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +16 -0
- package/dist/components/interrupt/TextInputPrompt.svelte +333 -0
- package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/index.d.ts +13 -0
- package/dist/components/interrupt/index.js +15 -0
- package/dist/components/nodes/GatewayNode.svelte +1 -3
- package/dist/components/nodes/IdeaNode.svelte +30 -35
- package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
- package/dist/components/nodes/SimpleNode.svelte +1 -3
- package/dist/components/nodes/TerminalNode.svelte +1 -3
- package/dist/components/nodes/ToolNode.svelte +2 -2
- package/dist/components/nodes/WorkflowNode.svelte +1 -3
- package/dist/components/playground/ChatPanel.svelte +144 -7
- package/dist/components/playground/ChatPanel.svelte.d.ts +2 -0
- package/dist/components/playground/MessageBubble.svelte +1 -3
- package/dist/components/playground/Playground.svelte +50 -5
- package/dist/components/playground/PlaygroundModal.svelte +8 -7
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
- package/dist/config/endpoints.d.ts +12 -0
- package/dist/config/endpoints.js +7 -0
- package/dist/playground/index.d.ts +5 -0
- package/dist/playground/index.js +21 -0
- package/dist/playground/mount.d.ts +3 -3
- package/dist/playground/mount.js +30 -22
- package/dist/services/interruptService.d.ts +133 -0
- package/dist/services/interruptService.js +279 -0
- package/dist/stores/interruptStore.d.ts +200 -0
- package/dist/stores/interruptStore.js +424 -0
- package/dist/stores/playgroundStore.d.ts +11 -1
- package/dist/stores/playgroundStore.js +34 -0
- package/dist/styles/base.css +89 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/interrupt.d.ts +305 -0
- package/dist/types/interrupt.js +126 -0
- package/dist/types/interruptState.d.ts +211 -0
- package/dist/types/interruptState.js +308 -0
- package/dist/utils/colors.js +1 -0
- package/dist/utils/connections.js +2 -2
- package/dist/utils/icons.js +1 -0
- 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
|
|
11
|
-
import type { ConfigValues, NodeMetadata } from
|
|
12
|
-
import Icon from
|
|
13
|
-
import { getDataTypeColor } from
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
);
|
|
88
|
-
const
|
|
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 =
|
|
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 ||
|
|
110
|
-
type:
|
|
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 ===
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|