@flowdrop/flowdrop 2.0.0-beta.2 → 2.0.0-beta.4
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/CHANGELOG.md +59 -0
- package/MIGRATION-2.0.md +13 -0
- package/README.md +5 -5
- package/dist/components/App.svelte +36 -191
- package/dist/components/App.svelte.d.ts +2 -7
- package/dist/components/Button.stories.svelte +65 -0
- package/dist/components/Button.stories.svelte.d.ts +19 -0
- package/dist/components/Button.svelte +62 -0
- package/dist/components/Button.svelte.d.ts +24 -0
- package/dist/components/CanvasIconButton.svelte +76 -0
- package/dist/components/CanvasIconButton.svelte.d.ts +18 -0
- package/dist/components/ConfigForm.svelte +4 -23
- package/dist/components/ConfigPanel.svelte +4 -3
- package/dist/components/EditorStatusBar.stories.svelte +44 -0
- package/dist/components/EditorStatusBar.stories.svelte.d.ts +27 -0
- package/dist/components/EditorStatusBar.svelte +99 -0
- package/dist/components/EditorStatusBar.svelte.d.ts +15 -0
- package/dist/components/IconButton.svelte +80 -0
- package/dist/components/IconButton.svelte.d.ts +30 -0
- package/dist/components/Input.svelte +74 -0
- package/dist/components/Input.svelte.d.ts +17 -0
- package/dist/components/LogoWordmark.svelte +113 -0
- package/dist/components/LogoWordmark.svelte.d.ts +26 -0
- package/dist/components/Navbar.svelte +17 -63
- package/dist/components/Navbar.svelte.d.ts +3 -0
- package/dist/components/NodeSidebar.svelte +17 -122
- package/dist/components/NodeSwapPicker.svelte +10 -28
- package/dist/components/PortMappingRow.svelte +0 -2
- package/dist/components/SchemaForm.svelte +0 -12
- package/dist/components/Select.svelte +53 -0
- package/dist/components/Select.svelte.d.ts +15 -0
- package/dist/components/SettingsModal.svelte +0 -5
- package/dist/components/SettingsPanel.svelte +2 -6
- package/dist/components/Textarea.svelte +39 -0
- package/dist/components/Textarea.svelte.d.ts +12 -0
- package/dist/components/ThemeToggle.svelte +15 -94
- package/dist/components/UniversalNode.svelte +32 -1
- package/dist/components/WorkflowEditor.svelte +62 -51
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -0
- package/dist/components/chat/AIChatPanel.svelte +1 -1
- package/dist/components/console/ConsoleAutocomplete.svelte +1 -1
- package/dist/components/console/ConsoleOutput.svelte +2 -2
- package/dist/components/form/FormArray.svelte +37 -173
- package/dist/components/form/FormAutocomplete.svelte +10 -6
- package/dist/components/form/FormCheckboxGroup.svelte +1 -5
- package/dist/components/form/FormCodeEditor.svelte +9 -7
- package/dist/components/form/FormField.svelte +5 -44
- package/dist/components/form/FormFieldLight.svelte +8 -47
- package/dist/components/form/FormFieldset.svelte +1 -1
- package/dist/components/form/FormMarkdownEditor.svelte +8 -5
- package/dist/components/form/FormNumberField.svelte +4 -36
- package/dist/components/form/FormRangeField.svelte +18 -27
- package/dist/components/form/FormSelect.svelte +13 -75
- package/dist/components/form/FormTemplateEditor.svelte +6 -4
- package/dist/components/form/FormTextField.svelte +3 -35
- package/dist/components/form/FormTextarea.svelte +4 -39
- package/dist/components/form/FormToggle.svelte +0 -4
- package/dist/components/form/resolveFieldType.d.ts +24 -0
- package/dist/components/form/resolveFieldType.js +55 -0
- package/dist/components/icons/CloseIcon.svelte +6 -0
- package/dist/components/icons/CloseIcon.svelte.d.ts +26 -0
- package/dist/components/icons/CommandLineIcon.svelte +15 -0
- package/dist/components/icons/CommandLineIcon.svelte.d.ts +26 -0
- package/dist/components/icons/MenuIcon.svelte +4 -0
- package/dist/components/icons/MenuIcon.svelte.d.ts +26 -0
- package/dist/components/icons/MenuOpenIcon.svelte +6 -0
- package/dist/components/icons/MenuOpenIcon.svelte.d.ts +26 -0
- package/dist/components/interrupt/ChoicePrompt.svelte +0 -10
- package/dist/components/interrupt/ConfirmationPrompt.svelte +0 -5
- package/dist/components/interrupt/InterruptBubble.svelte +0 -10
- package/dist/components/interrupt/ReviewPrompt.svelte +0 -20
- package/dist/components/interrupt/TextInputPrompt.svelte +0 -6
- package/dist/components/layouts/MainLayout.svelte +4 -5
- package/dist/components/nodes/AtomNode.svelte +46 -34
- package/dist/components/nodes/GatewayNode.svelte +91 -99
- package/dist/components/nodes/IdeaNode.svelte +62 -90
- package/dist/components/nodes/NodeConfigButton.svelte +86 -0
- package/dist/components/nodes/NodeConfigButton.svelte.d.ts +15 -0
- package/dist/components/nodes/NotesNode.svelte +70 -81
- package/dist/components/nodes/SimpleNode.svelte +28 -78
- package/dist/components/nodes/SquareNode.svelte +79 -109
- package/dist/components/nodes/TerminalNode.svelte +28 -86
- package/dist/components/nodes/ToolNode.svelte +82 -95
- package/dist/components/nodes/WorkflowNode.svelte +91 -100
- package/dist/components/playground/ChatInput.svelte +0 -1
- package/dist/components/playground/InputCollector.svelte +11 -48
- package/dist/components/playground/PlaygroundApp.svelte +1 -1
- package/dist/components/playground/PlaygroundStudio.svelte +0 -5
- package/dist/messages/index.d.ts +1 -1
- package/dist/messages/index.js +1 -1
- package/dist/openapi/v1/openapi.yaml +2 -2
- package/dist/playground/mount.d.ts +9 -5
- package/dist/playground/mount.js +9 -5
- package/dist/skins/drafter.d.ts +30 -0
- package/dist/skins/drafter.js +198 -0
- package/dist/skins/index.d.ts +2 -1
- package/dist/skins/index.js +4 -2
- package/dist/styles/base.css +285 -14
- package/dist/styles/tokens.css +60 -2
- package/dist/svelte-app.d.ts +6 -0
- package/dist/svelte-app.js +71 -109
- package/dist/themes/drafter.d.ts +2 -0
- package/dist/themes/drafter.js +15 -0
- package/dist/themes/index.d.ts +2 -1
- package/dist/themes/index.js +8 -2
- package/dist/types/events.d.ts +18 -0
- package/dist/types/events.js +2 -1
- package/dist/types/settings.d.ts +1 -1
- package/dist/types/settings.js +1 -1
- package/dist/types/skin.d.ts +1 -1
- package/dist/types/theme.d.ts +16 -2
- package/dist/utils/connections.js +14 -50
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors';
|
|
11
11
|
import { getInstance } from '../../stores/getInstance.svelte.js';
|
|
12
12
|
import type { NodeMetadata, NodePort } from '../../types/index.js';
|
|
13
|
-
import
|
|
13
|
+
import NodeConfigButton from './NodeConfigButton.svelte';
|
|
14
14
|
import AlertCircleIcon from '../icons/AlertCircleIcon.svelte';
|
|
15
15
|
|
|
16
16
|
interface ToolNodeParameter {
|
|
@@ -128,6 +128,13 @@
|
|
|
128
128
|
props.data.metadata?.outputs?.find((port: NodePort) => port.dataType === portDataType)
|
|
129
129
|
);
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Vertical center of the first port handle (px). Single source of truth shared
|
|
133
|
+
* by the input/output handles and the badge overlay, so the badge stays level
|
|
134
|
+
* with the first port.
|
|
135
|
+
*/
|
|
136
|
+
const firstPortTop = 40;
|
|
137
|
+
|
|
131
138
|
/**
|
|
132
139
|
* Handle configuration sidebar - using global ConfigSidebar
|
|
133
140
|
*/
|
|
@@ -149,23 +156,6 @@
|
|
|
149
156
|
function handleDoubleClick(): void {
|
|
150
157
|
openConfigSidebar();
|
|
151
158
|
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Handle click events
|
|
155
|
-
*/
|
|
156
|
-
function handleClick(): void {
|
|
157
|
-
// Node selection is handled by Svelte Flow
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Handle keyboard events for accessibility
|
|
162
|
-
*/
|
|
163
|
-
function handleKeydown(event: KeyboardEvent): void {
|
|
164
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
165
|
-
event.preventDefault();
|
|
166
|
-
handleDoubleClick();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
159
|
</script>
|
|
170
160
|
|
|
171
161
|
<!-- Tool Input Handle (optional): center at 40px (multiple of 10), 20px connection area -->
|
|
@@ -174,7 +164,7 @@
|
|
|
174
164
|
type="target"
|
|
175
165
|
position={Position.Left}
|
|
176
166
|
id={`${props.data.nodeId}-input-${toolInputPort.id}`}
|
|
177
|
-
style="top:
|
|
167
|
+
style="top: {firstPortTop}px; transform: translateY(-50%); z-index: 30; --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
|
|
178
168
|
checker,
|
|
179
169
|
portDataType
|
|
180
170
|
)}); --fd-handle-border-color: var(--fd-handle-border);"
|
|
@@ -182,17 +172,16 @@
|
|
|
182
172
|
{/if}
|
|
183
173
|
|
|
184
174
|
<!-- Tool Node -->
|
|
175
|
+
<!-- Presentational: focus, keyboard and selection live on xyflow's node
|
|
176
|
+
wrapper (see UniversalNode). double-click is a mouse convenience. -->
|
|
177
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
185
178
|
<div
|
|
186
179
|
class="flowdrop-tool-node"
|
|
187
180
|
class:flowdrop-tool-node--selected={props.selected}
|
|
188
181
|
class:flowdrop-tool-node--processing={props.isProcessing}
|
|
189
182
|
class:flowdrop-tool-node--error={props.isError}
|
|
190
183
|
style={nodeStyle}
|
|
191
|
-
onclick={handleClick}
|
|
192
184
|
ondblclick={handleDoubleClick}
|
|
193
|
-
onkeydown={handleKeydown}
|
|
194
|
-
role="button"
|
|
195
|
-
tabindex="0"
|
|
196
185
|
>
|
|
197
186
|
<!-- Node Header -->
|
|
198
187
|
<div class="flowdrop-tool-node__header">
|
|
@@ -217,8 +206,8 @@
|
|
|
217
206
|
</div>
|
|
218
207
|
</div>
|
|
219
208
|
|
|
220
|
-
<!-- Tool Badge -
|
|
221
|
-
<div class="flowdrop-tool-node__badge">{displayBadge}</div>
|
|
209
|
+
<!-- Tool Badge - overlay aligned to the first port's vertical center -->
|
|
210
|
+
<div class="flowdrop-tool-node__badge" style="top: {firstPortTop}px">{displayBadge}</div>
|
|
222
211
|
</div>
|
|
223
212
|
|
|
224
213
|
<!-- Tool Description - uses instanceDescription override if set -->
|
|
@@ -242,9 +231,7 @@
|
|
|
242
231
|
{/if}
|
|
243
232
|
|
|
244
233
|
<!-- Config button -->
|
|
245
|
-
<
|
|
246
|
-
<CogIcon />
|
|
247
|
-
</button>
|
|
234
|
+
<NodeConfigButton onclick={openConfigSidebar} title="Configure tool" />
|
|
248
235
|
</div>
|
|
249
236
|
|
|
250
237
|
<!-- Tool Output Handle (optional): center at 40px (multiple of 10), 20px connection area -->
|
|
@@ -253,7 +240,7 @@
|
|
|
253
240
|
type="source"
|
|
254
241
|
position={Position.Right}
|
|
255
242
|
id={`${props.data.nodeId}-output-${toolOutputPort.id}`}
|
|
256
|
-
style="top:
|
|
243
|
+
style="top: {firstPortTop}px; transform: translateY(-50%); z-index: 30; --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
|
|
257
244
|
checker,
|
|
258
245
|
portDataType
|
|
259
246
|
)}); --fd-handle-border-color: var(--fd-handle-border);"
|
|
@@ -263,44 +250,46 @@
|
|
|
263
250
|
<style>
|
|
264
251
|
.flowdrop-tool-node {
|
|
265
252
|
position: relative;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
253
|
+
box-sizing: border-box;
|
|
254
|
+
background-color: var(--fd-node-bg);
|
|
255
|
+
backdrop-filter: var(--fd-node-backdrop-filter);
|
|
256
|
+
border: var(--fd-node-border-width) solid var(--fd-tool-node-color);
|
|
257
|
+
border-radius: var(--fd-node-radius);
|
|
269
258
|
width: var(--fd-node-default-width);
|
|
270
|
-
|
|
259
|
+
/* A tool has at most 2 ports on a side (handles at 40px & 80px), so 100px
|
|
260
|
+
is the fixed floor; the grid-aligned header keeps it a 20px multiple. */
|
|
261
|
+
min-height: 100px;
|
|
271
262
|
display: flex;
|
|
272
263
|
flex-direction: column;
|
|
273
264
|
cursor: pointer;
|
|
274
265
|
transition: all var(--fd-transition-fast);
|
|
275
|
-
box-shadow: var(--fd-shadow
|
|
266
|
+
box-shadow: var(--fd-node-shadow);
|
|
276
267
|
overflow: visible;
|
|
277
268
|
z-index: 10;
|
|
278
269
|
color: var(--fd-foreground);
|
|
279
270
|
}
|
|
280
271
|
|
|
281
272
|
.flowdrop-tool-node:hover {
|
|
282
|
-
box-shadow: var(--fd-shadow-
|
|
273
|
+
box-shadow: var(--fd-node-shadow-hover);
|
|
283
274
|
border-color: var(--fd-tool-node-color);
|
|
284
275
|
}
|
|
285
276
|
|
|
286
277
|
.flowdrop-tool-node--selected {
|
|
287
278
|
box-shadow:
|
|
288
279
|
0 0 0 2px color-mix(in srgb, var(--fd-tool-node-color) 30%, transparent),
|
|
289
|
-
var(--fd-shadow-
|
|
280
|
+
var(--fd-node-shadow-hover);
|
|
290
281
|
border-color: var(--fd-tool-node-color);
|
|
291
282
|
}
|
|
292
283
|
|
|
293
284
|
.flowdrop-tool-node--selected:hover {
|
|
294
285
|
box-shadow:
|
|
295
286
|
0 0 0 2px color-mix(in srgb, var(--fd-tool-node-color) 30%, transparent),
|
|
296
|
-
var(--fd-shadow-
|
|
287
|
+
var(--fd-node-shadow-hover);
|
|
297
288
|
border-color: var(--fd-tool-node-color);
|
|
298
289
|
}
|
|
299
290
|
|
|
300
|
-
.
|
|
301
|
-
|
|
302
|
-
outline-offset: 2px;
|
|
303
|
-
}
|
|
291
|
+
/* Focus ring is centralized in base.css (drawn on the .svelte-flow__node
|
|
292
|
+
wrapper, which is the focusable element). */
|
|
304
293
|
|
|
305
294
|
.flowdrop-tool-node--processing {
|
|
306
295
|
opacity: 0.7;
|
|
@@ -312,10 +301,18 @@
|
|
|
312
301
|
}
|
|
313
302
|
|
|
314
303
|
.flowdrop-tool-node__header {
|
|
315
|
-
|
|
304
|
+
box-sizing: border-box;
|
|
305
|
+
flex: 1;
|
|
306
|
+
display: flex;
|
|
307
|
+
flex-direction: column;
|
|
308
|
+
/* px on the 20px grid. Bottom padding absorbs BOTH node borders (top + bottom)
|
|
309
|
+
so the OUTER node height lands on a 20px multiple: with a 40px title row and
|
|
310
|
+
a 20px-per-line description, outer = 60 + 20·lines (80, 100, 120…). */
|
|
311
|
+
padding: var(--fd-node-header-gap) 20px
|
|
312
|
+
calc(var(--fd-node-header-gap) - var(--fd-node-border-width) * 2);
|
|
316
313
|
/* Light mode: mix tool color with white (95%) for subtle tint */
|
|
317
314
|
background-color: color-mix(in srgb, var(--fd-tool-node-color) 5%, white);
|
|
318
|
-
border-radius: var(--fd-radius
|
|
315
|
+
border-radius: var(--fd-node-radius);
|
|
319
316
|
border: none;
|
|
320
317
|
}
|
|
321
318
|
|
|
@@ -329,8 +326,10 @@
|
|
|
329
326
|
.flowdrop-tool-node__header-content {
|
|
330
327
|
display: flex;
|
|
331
328
|
align-items: center;
|
|
332
|
-
gap:
|
|
333
|
-
|
|
329
|
+
gap: 12px;
|
|
330
|
+
/* Two grid rows (title + version), so the icon + text block is exactly 40px;
|
|
331
|
+
the description stacks flush below and each wrapped line adds one 20px row. */
|
|
332
|
+
min-height: var(--fd-node-header-title-height);
|
|
334
333
|
}
|
|
335
334
|
|
|
336
335
|
/* Squircle icon wrapper - Apple-style rounded square background */
|
|
@@ -338,9 +337,10 @@
|
|
|
338
337
|
display: var(--fd-node-icon-display, flex);
|
|
339
338
|
align-items: center;
|
|
340
339
|
justify-content: center;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
340
|
+
/* px (not rem) so the icon stays grid-locked regardless of root font-size */
|
|
341
|
+
width: 36px;
|
|
342
|
+
height: 36px;
|
|
343
|
+
border-radius: 8px;
|
|
344
344
|
background: color-mix(
|
|
345
345
|
in srgb,
|
|
346
346
|
var(--fd-tool-node-color) var(--fd-node-icon-bg-opacity),
|
|
@@ -365,41 +365,64 @@
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
.flowdrop-tool-node__title {
|
|
368
|
-
font-size:
|
|
368
|
+
font-size: 16px;
|
|
369
369
|
font-weight: 600;
|
|
370
370
|
color: var(--fd-foreground);
|
|
371
371
|
margin: 0;
|
|
372
|
-
|
|
372
|
+
/* one 20px grid row */
|
|
373
|
+
line-height: var(--fd-node-port-row-height);
|
|
374
|
+
overflow: hidden;
|
|
375
|
+
text-overflow: ellipsis;
|
|
376
|
+
white-space: nowrap;
|
|
373
377
|
}
|
|
374
378
|
|
|
375
379
|
.flowdrop-tool-node__version {
|
|
376
380
|
font-size: var(--fd-text-xs);
|
|
377
381
|
color: var(--fd-muted-foreground);
|
|
378
382
|
font-weight: 500;
|
|
379
|
-
|
|
383
|
+
/* one 20px grid row */
|
|
384
|
+
line-height: var(--fd-node-port-row-height);
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
.flowdrop-tool-node__badge {
|
|
388
|
+
/* Overlay so it takes no row space — the title gets the full width. The
|
|
389
|
+
inline `top` is set to the first port's center; translateY keeps the
|
|
390
|
+
badge vertically aligned with that port. */
|
|
391
|
+
position: absolute;
|
|
392
|
+
right: 20px;
|
|
393
|
+
transform: translateY(-50%);
|
|
394
|
+
z-index: 12;
|
|
383
395
|
background-color: color-mix(in srgb, var(--fd-tool-node-color) 15%, transparent);
|
|
384
396
|
color: var(--fd-tool-node-color);
|
|
385
397
|
border: 1px solid color-mix(in srgb, var(--fd-tool-node-color) 30%, transparent);
|
|
386
|
-
font-size:
|
|
398
|
+
font-size: 10px;
|
|
387
399
|
font-weight: 700;
|
|
388
|
-
padding:
|
|
400
|
+
padding: 4px 8px;
|
|
389
401
|
border-radius: var(--fd-radius-sm);
|
|
390
402
|
letter-spacing: 0.05em;
|
|
403
|
+
/* Lay flat: sit back at reduced opacity so it reads as a subtle marker and
|
|
404
|
+
any title underneath stays legible. */
|
|
405
|
+
opacity: 0.4;
|
|
391
406
|
}
|
|
392
407
|
|
|
393
408
|
.flowdrop-tool-node__description {
|
|
394
409
|
font-size: var(--fd-text-xs);
|
|
395
410
|
color: var(--fd-muted-foreground);
|
|
396
411
|
margin: 0;
|
|
397
|
-
line
|
|
412
|
+
/* each line is one 20px grid row; clamp so the node grows in clean 20px steps */
|
|
413
|
+
line-height: var(--fd-node-port-row-height);
|
|
414
|
+
min-height: var(--fd-node-port-row-height);
|
|
415
|
+
overflow: hidden;
|
|
416
|
+
text-overflow: ellipsis;
|
|
417
|
+
display: -webkit-box;
|
|
418
|
+
-webkit-line-clamp: 2;
|
|
419
|
+
line-clamp: 2;
|
|
420
|
+
-webkit-box-orient: vertical;
|
|
398
421
|
}
|
|
399
422
|
|
|
400
423
|
.flowdrop-tool-node__icon-wrapper :global(.flowdrop-tool-node__icon) {
|
|
401
|
-
width:
|
|
402
|
-
height:
|
|
424
|
+
width: 20px;
|
|
425
|
+
height: 20px;
|
|
403
426
|
color: var(--fd-node-icon);
|
|
404
427
|
}
|
|
405
428
|
|
|
@@ -430,40 +453,9 @@
|
|
|
430
453
|
height: 12px;
|
|
431
454
|
}
|
|
432
455
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
.flowdrop-tool-node__config-btn {
|
|
439
|
-
position: absolute;
|
|
440
|
-
top: 0.5rem;
|
|
441
|
-
right: 0.5rem;
|
|
442
|
-
width: 1.5rem;
|
|
443
|
-
height: 1.5rem;
|
|
444
|
-
background-color: var(--fd-backdrop);
|
|
445
|
-
border: 1px solid var(--fd-border);
|
|
446
|
-
border-radius: var(--fd-radius-sm);
|
|
447
|
-
color: var(--fd-muted-foreground);
|
|
448
|
-
cursor: pointer;
|
|
449
|
-
display: flex;
|
|
450
|
-
align-items: center;
|
|
451
|
-
justify-content: center;
|
|
452
|
-
opacity: 0;
|
|
453
|
-
transition: all var(--fd-transition-normal);
|
|
454
|
-
backdrop-filter: blur(4px);
|
|
455
|
-
z-index: 15;
|
|
456
|
-
font-size: var(--fd-text-sm);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.flowdrop-tool-node:hover .flowdrop-tool-node__config-btn {
|
|
460
|
-
opacity: 1;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
.flowdrop-tool-node__config-btn:hover {
|
|
464
|
-
background-color: var(--fd-muted);
|
|
465
|
-
border-color: var(--fd-border-strong);
|
|
466
|
-
color: var(--fd-foreground);
|
|
456
|
+
/* Reveal the NodeConfigButton (gear) when the node is hovered. */
|
|
457
|
+
.flowdrop-tool-node:hover {
|
|
458
|
+
--fd-config-btn-opacity: 1;
|
|
467
459
|
}
|
|
468
460
|
|
|
469
461
|
@keyframes spin {
|
|
@@ -486,11 +478,6 @@
|
|
|
486
478
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--fd-tool-node-color) 30%, transparent) !important;
|
|
487
479
|
}
|
|
488
480
|
|
|
489
|
-
:global(.svelte-flow__node-tool .svelte-flow__handle:focus) {
|
|
490
|
-
outline: 2px solid var(--fd-tool-node-color) !important;
|
|
491
|
-
outline-offset: 2px !important;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
481
|
/* Circle dot icon — shown in minimal skin via --fd-node-circle-display */
|
|
495
482
|
.flowdrop-tool-node__color-dot {
|
|
496
483
|
width: 10px;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { dynamicPortToNodePort } from '../../types/index.js';
|
|
17
17
|
import Icon from '@iconify/svelte';
|
|
18
18
|
import { getNodeIcon } from '../../utils/icons.js';
|
|
19
|
-
import
|
|
19
|
+
import NodeConfigButton from './NodeConfigButton.svelte';
|
|
20
20
|
import {
|
|
21
21
|
getDataTypeColorToken,
|
|
22
22
|
getCategoryColorToken,
|
|
@@ -165,13 +165,6 @@
|
|
|
165
165
|
allOutputPorts.filter((port) => isPortVisible(port, 'output'))
|
|
166
166
|
);
|
|
167
167
|
|
|
168
|
-
/**
|
|
169
|
-
* Handle node click - only handle selection, no config opening
|
|
170
|
-
*/
|
|
171
|
-
function handleNodeClick(): void {
|
|
172
|
-
// Node selection is handled by Svelte Flow
|
|
173
|
-
}
|
|
174
|
-
|
|
175
168
|
/**
|
|
176
169
|
* Handle double-click to open config
|
|
177
170
|
*/
|
|
@@ -196,23 +189,17 @@
|
|
|
196
189
|
</script>
|
|
197
190
|
|
|
198
191
|
<!-- Node Container -->
|
|
192
|
+
<!-- Presentational: focus, keyboard and selection live on xyflow's node
|
|
193
|
+
wrapper (see UniversalNode). double-click is a mouse convenience. -->
|
|
194
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
199
195
|
<div
|
|
200
196
|
class="flowdrop-workflow-node"
|
|
201
197
|
class:flowdrop-workflow-node--selected={props.selected}
|
|
202
|
-
onclick={handleNodeClick}
|
|
203
198
|
ondblclick={handleDoubleClick}
|
|
204
199
|
onmouseup={() => {
|
|
205
200
|
isHandleInteraction = false;
|
|
206
201
|
}}
|
|
207
202
|
data-handle-interaction={isHandleInteraction}
|
|
208
|
-
role="button"
|
|
209
|
-
tabindex="0"
|
|
210
|
-
onkeydown={(e) => {
|
|
211
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
212
|
-
e.preventDefault();
|
|
213
|
-
handleDoubleClick();
|
|
214
|
-
}
|
|
215
|
-
}}
|
|
216
203
|
aria-label={graph.workflowNode({ name: props.data.metadata.name })}
|
|
217
204
|
aria-describedby="node-description-{props.data.nodeId || 'unknown'}"
|
|
218
205
|
>
|
|
@@ -236,7 +223,7 @@
|
|
|
236
223
|
></span>
|
|
237
224
|
|
|
238
225
|
<!-- Node Title - Icon and Title on same line -->
|
|
239
|
-
<h3 class="flowdrop-text--sm flowdrop-font--medium flowdrop-
|
|
226
|
+
<h3 class="flowdrop-text--sm flowdrop-font--medium flowdrop-flex--1">
|
|
240
227
|
{displayTitle}
|
|
241
228
|
</h3>
|
|
242
229
|
|
|
@@ -258,13 +245,13 @@
|
|
|
258
245
|
<div class="flowdrop-workflow-node__ports-list">
|
|
259
246
|
{#each visibleInputPorts as port (port.id)}
|
|
260
247
|
<div class="flowdrop-workflow-node__port">
|
|
261
|
-
<!-- Input Handle:
|
|
248
|
+
<!-- Input Handle: one grid row (20px) from the top so it aligns with the label, at node edge -->
|
|
262
249
|
<Handle
|
|
263
250
|
type="target"
|
|
264
251
|
position={Position.Left}
|
|
265
252
|
id={`${props.data.nodeId}-input-${port.id}`}
|
|
266
253
|
class="flowdrop-workflow-node__handle"
|
|
267
|
-
style="top:
|
|
254
|
+
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColorToken(
|
|
268
255
|
checker,
|
|
269
256
|
port.dataType
|
|
270
257
|
)}); --fd-handle-border-color: var(--fd-handle-border);"
|
|
@@ -339,13 +326,13 @@
|
|
|
339
326
|
{/if}
|
|
340
327
|
</div>
|
|
341
328
|
|
|
342
|
-
<!-- Output Handle:
|
|
329
|
+
<!-- Output Handle: one grid row (20px) from the top so it aligns with the label, at node edge -->
|
|
343
330
|
<Handle
|
|
344
331
|
type="source"
|
|
345
332
|
position={Position.Right}
|
|
346
333
|
id={`${props.data.nodeId}-output-${port.id}`}
|
|
347
334
|
class="flowdrop-workflow-node__handle"
|
|
348
|
-
style="top:
|
|
335
|
+
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColorToken(
|
|
349
336
|
checker,
|
|
350
337
|
port.dataType
|
|
351
338
|
)}); --fd-handle-border-color: var(--fd-handle-border);"
|
|
@@ -358,22 +345,17 @@
|
|
|
358
345
|
{/if}
|
|
359
346
|
|
|
360
347
|
<!-- Config button -->
|
|
361
|
-
<
|
|
362
|
-
class="flowdrop-workflow-node__config-btn"
|
|
363
|
-
onclick={openConfigSidebar}
|
|
364
|
-
title="Configure node"
|
|
365
|
-
>
|
|
366
|
-
<CogIcon />
|
|
367
|
-
</button>
|
|
348
|
+
<NodeConfigButton onclick={openConfigSidebar} title="Configure node" />
|
|
368
349
|
</div>
|
|
369
350
|
|
|
370
351
|
<style>
|
|
371
352
|
.flowdrop-workflow-node {
|
|
372
353
|
position: relative;
|
|
373
|
-
background-color: var(--fd-
|
|
374
|
-
|
|
375
|
-
border
|
|
376
|
-
|
|
354
|
+
background-color: var(--fd-node-bg);
|
|
355
|
+
backdrop-filter: var(--fd-node-backdrop-filter);
|
|
356
|
+
border: var(--fd-node-border-width) solid var(--fd-node-border);
|
|
357
|
+
border-radius: var(--fd-node-radius);
|
|
358
|
+
box-shadow: var(--fd-node-shadow);
|
|
377
359
|
width: var(--fd-node-default-width);
|
|
378
360
|
z-index: 10;
|
|
379
361
|
color: var(--fd-foreground);
|
|
@@ -381,42 +363,49 @@
|
|
|
381
363
|
}
|
|
382
364
|
|
|
383
365
|
.flowdrop-workflow-node:hover {
|
|
384
|
-
box-shadow: var(--fd-shadow-
|
|
366
|
+
box-shadow: var(--fd-node-shadow-hover);
|
|
385
367
|
border-color: var(--fd-node-border-hover);
|
|
386
368
|
}
|
|
387
369
|
|
|
388
370
|
.flowdrop-workflow-node--selected {
|
|
389
371
|
box-shadow:
|
|
390
372
|
0 0 0 2px var(--fd-primary-muted),
|
|
391
|
-
var(--fd-shadow-
|
|
373
|
+
var(--fd-node-shadow-hover);
|
|
392
374
|
border-color: var(--fd-primary);
|
|
393
375
|
}
|
|
394
376
|
|
|
395
377
|
.flowdrop-workflow-node--selected:hover {
|
|
396
378
|
box-shadow:
|
|
397
379
|
0 0 0 2px var(--fd-primary-muted),
|
|
398
|
-
var(--fd-shadow-
|
|
380
|
+
var(--fd-node-shadow-hover);
|
|
399
381
|
border-color: var(--fd-primary);
|
|
400
382
|
}
|
|
401
383
|
|
|
402
|
-
.
|
|
403
|
-
|
|
404
|
-
outline-offset: 2px;
|
|
405
|
-
}
|
|
384
|
+
/* Focus ring is centralized in base.css (drawn on the .svelte-flow__node
|
|
385
|
+
wrapper, which is the focusable element). */
|
|
406
386
|
|
|
407
387
|
.flowdrop-workflow-node__header {
|
|
408
388
|
box-sizing: border-box;
|
|
409
|
-
padding
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
389
|
+
/* Bottom padding absorbs BOTH the node's own top border and the header
|
|
390
|
+
divider, so the body below the header lands on the 20px grid measured
|
|
391
|
+
from the node's outer top edge: node-border + header = 100/120/140. */
|
|
392
|
+
padding: var(--fd-node-header-gap) var(--fd-space-xl)
|
|
393
|
+
calc(
|
|
394
|
+
var(--fd-node-header-gap) - var(--fd-node-border-width) -
|
|
395
|
+
var(--fd-node-header-divider-width)
|
|
396
|
+
);
|
|
397
|
+
border-bottom: var(--fd-node-header-divider-width) solid var(--fd-node-header-divider-color);
|
|
398
|
+
background: var(--fd-node-header-bg);
|
|
399
|
+
border-top-left-radius: var(--fd-node-radius);
|
|
400
|
+
border-top-right-radius: var(--fd-node-radius);
|
|
414
401
|
display: flex;
|
|
415
402
|
flex-direction: column;
|
|
416
|
-
gap: var(--fd-node-header-gap);
|
|
403
|
+
gap: calc(var(--fd-node-header-gap) * 2);
|
|
404
|
+
/* node-border (1.5) + header = 100/120/140. Header itself is
|
|
405
|
+
4*gap + title + desc-line - node-border; each extra desc line adds 20. */
|
|
417
406
|
min-height: calc(
|
|
418
|
-
var(--fd-node-header-gap) *
|
|
419
|
-
var(--fd-node-header-desc-line)
|
|
407
|
+
var(--fd-node-header-gap) * 4 + var(--fd-node-header-title-height) +
|
|
408
|
+
var(--fd-node-header-desc-line) - var(--fd-node-border-width)
|
|
420
409
|
);
|
|
421
410
|
}
|
|
422
411
|
|
|
@@ -447,9 +436,10 @@
|
|
|
447
436
|
display: var(--fd-node-icon-display, flex);
|
|
448
437
|
align-items: center;
|
|
449
438
|
justify-content: center;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
439
|
+
/* px (not rem) so the icon stays grid-locked regardless of root font-size */
|
|
440
|
+
width: 36px;
|
|
441
|
+
height: 36px;
|
|
442
|
+
border-radius: 8px;
|
|
453
443
|
background: color-mix(in srgb, var(--_icon-color) var(--fd-node-icon-bg-opacity), transparent);
|
|
454
444
|
flex-shrink: 0;
|
|
455
445
|
transition: all var(--fd-transition-normal);
|
|
@@ -465,8 +455,8 @@
|
|
|
465
455
|
}
|
|
466
456
|
|
|
467
457
|
.flowdrop-workflow-node__icon-wrapper :global(.flowdrop-workflow-node__icon) {
|
|
468
|
-
width:
|
|
469
|
-
height:
|
|
458
|
+
width: 20px;
|
|
459
|
+
height: 20px;
|
|
470
460
|
color: var(--fd-node-icon);
|
|
471
461
|
}
|
|
472
462
|
|
|
@@ -481,7 +471,14 @@
|
|
|
481
471
|
|
|
482
472
|
.flowdrop-workflow-node__header-title h3 {
|
|
483
473
|
margin: 0;
|
|
484
|
-
|
|
474
|
+
/* half the title block so two lines fill it exactly on the 20px grid */
|
|
475
|
+
line-height: calc(var(--fd-node-header-title-height) / 2);
|
|
476
|
+
display: -webkit-box;
|
|
477
|
+
-webkit-line-clamp: 2;
|
|
478
|
+
line-clamp: 2;
|
|
479
|
+
-webkit-box-orient: vertical;
|
|
480
|
+
overflow: hidden;
|
|
481
|
+
min-width: 0;
|
|
485
482
|
}
|
|
486
483
|
|
|
487
484
|
@keyframes pulse {
|
|
@@ -501,27 +498,52 @@
|
|
|
501
498
|
.flowdrop-workflow-node__ports-list {
|
|
502
499
|
display: flex;
|
|
503
500
|
flex-direction: column;
|
|
504
|
-
gap:
|
|
505
|
-
padding:
|
|
501
|
+
gap: 0;
|
|
502
|
+
/* No vertical padding: sections stack flush and node height stays a
|
|
503
|
+
multiple of 20. The one exception is the clearance below the header
|
|
504
|
+
divider, applied to the first section only (below). */
|
|
505
|
+
padding: 0;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* The first port section sits directly below the header divider; give it a
|
|
509
|
+
full 20px grid row of clearance so the first port lands on the grid. */
|
|
510
|
+
.flowdrop-workflow-node__header
|
|
511
|
+
+ .flowdrop-workflow-node__ports
|
|
512
|
+
.flowdrop-workflow-node__ports-list {
|
|
513
|
+
padding-top: calc(var(--fd-node-header-gap) * 2);
|
|
506
514
|
}
|
|
507
515
|
|
|
508
516
|
.flowdrop-workflow-node__port {
|
|
509
517
|
display: flex;
|
|
510
|
-
align-items:
|
|
518
|
+
align-items: flex-start;
|
|
511
519
|
gap: 0;
|
|
512
|
-
|
|
513
|
-
|
|
520
|
+
/* Fixed three-row (60px) height for every port — node height stays
|
|
521
|
+
predictable whether or not a port carries a description. */
|
|
522
|
+
height: calc(var(--fd-node-port-row-height) * 3);
|
|
523
|
+
padding: 0;
|
|
514
524
|
position: relative;
|
|
515
525
|
}
|
|
516
526
|
|
|
517
527
|
.flowdrop-workflow-node__port-content {
|
|
518
|
-
padding:
|
|
528
|
+
padding: var(--fd-node-header-gap) var(--fd-space-xl) 0;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Each line in a port occupies one 20px grid row: a label-only port
|
|
532
|
+
centers its single row, a label + description fills both. */
|
|
533
|
+
.flowdrop-workflow-node__port-content > div {
|
|
534
|
+
min-height: var(--fd-node-port-row-height);
|
|
535
|
+
align-items: center;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.flowdrop-workflow-node__port-content > p {
|
|
539
|
+
min-height: var(--fd-node-port-row-height);
|
|
540
|
+
line-height: var(--fd-node-port-row-height);
|
|
519
541
|
}
|
|
520
542
|
|
|
521
543
|
.flowdrop-badge {
|
|
522
|
-
padding:
|
|
544
|
+
padding: 2px 4px;
|
|
523
545
|
border-radius: var(--fd-radius-sm);
|
|
524
|
-
font-size:
|
|
546
|
+
font-size: 10px;
|
|
525
547
|
font-weight: 500;
|
|
526
548
|
text-transform: uppercase;
|
|
527
549
|
letter-spacing: 0.05em;
|
|
@@ -533,8 +555,8 @@
|
|
|
533
555
|
}
|
|
534
556
|
|
|
535
557
|
.flowdrop-badge--sm {
|
|
536
|
-
font-size:
|
|
537
|
-
padding:
|
|
558
|
+
font-size: 10px;
|
|
559
|
+
padding: 2px 4px;
|
|
538
560
|
}
|
|
539
561
|
|
|
540
562
|
/* Handle overrides: hover scale (base 20px/12px from base.css) */
|
|
@@ -569,12 +591,12 @@
|
|
|
569
591
|
|
|
570
592
|
.flowdrop-text--xs {
|
|
571
593
|
font-size: var(--fd-text-xs);
|
|
572
|
-
line-height:
|
|
594
|
+
line-height: 16px;
|
|
573
595
|
}
|
|
574
596
|
|
|
575
597
|
.flowdrop-text--sm {
|
|
576
598
|
font-size: var(--fd-text-sm);
|
|
577
|
-
line-height:
|
|
599
|
+
line-height: 20px;
|
|
578
600
|
}
|
|
579
601
|
|
|
580
602
|
.flowdrop-text--gray {
|
|
@@ -595,39 +617,8 @@
|
|
|
595
617
|
text-align: right;
|
|
596
618
|
}
|
|
597
619
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
.flowdrop-workflow-node__config-btn {
|
|
604
|
-
position: absolute;
|
|
605
|
-
top: var(--fd-space-xs);
|
|
606
|
-
right: var(--fd-space-xs);
|
|
607
|
-
width: 1.5rem;
|
|
608
|
-
height: 1.5rem;
|
|
609
|
-
background-color: var(--fd-backdrop);
|
|
610
|
-
border: 1px solid var(--fd-border);
|
|
611
|
-
border-radius: var(--fd-radius-sm);
|
|
612
|
-
color: var(--fd-muted-foreground);
|
|
613
|
-
cursor: pointer;
|
|
614
|
-
display: flex;
|
|
615
|
-
align-items: center;
|
|
616
|
-
justify-content: center;
|
|
617
|
-
opacity: 0;
|
|
618
|
-
transition: all var(--fd-transition-normal);
|
|
619
|
-
backdrop-filter: var(--fd-backdrop-blur);
|
|
620
|
-
z-index: 15;
|
|
621
|
-
font-size: var(--fd-text-sm);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
.flowdrop-workflow-node:hover .flowdrop-workflow-node__config-btn {
|
|
625
|
-
opacity: 1;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
.flowdrop-workflow-node__config-btn:hover {
|
|
629
|
-
background-color: var(--fd-muted);
|
|
630
|
-
border-color: var(--fd-border-strong);
|
|
631
|
-
color: var(--fd-foreground);
|
|
620
|
+
/* Reveal the NodeConfigButton (gear) when the node is hovered. */
|
|
621
|
+
.flowdrop-workflow-node:hover {
|
|
622
|
+
--fd-config-btn-opacity: 1;
|
|
632
623
|
}
|
|
633
624
|
</style>
|