@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
|
@@ -561,11 +561,6 @@
|
|
|
561
561
|
background-color: var(--fd-error-hover);
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
-
.interrupt-bubble__retry-btn:focus-visible {
|
|
565
|
-
outline: 2px solid var(--fd-ring);
|
|
566
|
-
outline-offset: 2px;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
564
|
/* Body - prompt content area, full width */
|
|
570
565
|
.interrupt-bubble__body {
|
|
571
566
|
padding: var(--fd-space-xl);
|
|
@@ -648,11 +643,6 @@
|
|
|
648
643
|
background-color: var(--fd-error-muted);
|
|
649
644
|
}
|
|
650
645
|
|
|
651
|
-
.interrupt-bubble__cancel-btn:focus-visible {
|
|
652
|
-
outline: 2px solid var(--fd-ring);
|
|
653
|
-
outline-offset: 2px;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
646
|
.interrupt-bubble__cancel-btn:disabled {
|
|
657
647
|
opacity: 0.5;
|
|
658
648
|
cursor: not-allowed;
|
|
@@ -540,11 +540,6 @@
|
|
|
540
540
|
color: var(--fd-error);
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
-
.review-prompt__bulk-btn:focus-visible {
|
|
544
|
-
outline: 2px solid var(--fd-ring);
|
|
545
|
-
outline-offset: 2px;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
543
|
.review-prompt__bulk-btn:disabled {
|
|
549
544
|
opacity: 0.5;
|
|
550
545
|
cursor: not-allowed;
|
|
@@ -642,11 +637,6 @@
|
|
|
642
637
|
color: var(--fd-error-foreground);
|
|
643
638
|
}
|
|
644
639
|
|
|
645
|
-
.review-prompt__toggle-btn:focus-visible {
|
|
646
|
-
outline: 2px solid var(--fd-ring);
|
|
647
|
-
outline-offset: 2px;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
640
|
.review-prompt__toggle-btn:disabled {
|
|
651
641
|
opacity: 0.5;
|
|
652
642
|
cursor: not-allowed;
|
|
@@ -739,11 +729,6 @@
|
|
|
739
729
|
border-color: var(--fd-border-strong);
|
|
740
730
|
}
|
|
741
731
|
|
|
742
|
-
.review-prompt__html-toggle-btn:focus-visible {
|
|
743
|
-
outline: 2px solid var(--fd-ring);
|
|
744
|
-
outline-offset: 2px;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
732
|
/* Raw HTML code display */
|
|
748
733
|
.review-prompt__raw-html {
|
|
749
734
|
font-family: var(--fd-review-font-mono);
|
|
@@ -842,11 +827,6 @@
|
|
|
842
827
|
transform: translateY(-1px);
|
|
843
828
|
}
|
|
844
829
|
|
|
845
|
-
.review-prompt__submit:focus-visible {
|
|
846
|
-
outline: 2px solid var(--fd-ring);
|
|
847
|
-
outline-offset: 2px;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
830
|
.review-prompt__submit:disabled {
|
|
851
831
|
opacity: 0.5;
|
|
852
832
|
cursor: not-allowed;
|
|
@@ -254,7 +254,6 @@
|
|
|
254
254
|
.text-prompt__input:focus,
|
|
255
255
|
.text-prompt__textarea:focus {
|
|
256
256
|
border-color: var(--fd-interrupt-completed-border);
|
|
257
|
-
box-shadow: 0 0 0 3px var(--fd-interrupt-completed-shadow);
|
|
258
257
|
}
|
|
259
258
|
|
|
260
259
|
.text-prompt__input:disabled,
|
|
@@ -316,11 +315,6 @@
|
|
|
316
315
|
transform: translateY(-1px);
|
|
317
316
|
}
|
|
318
317
|
|
|
319
|
-
.text-prompt__submit:focus-visible {
|
|
320
|
-
outline: 2px solid var(--fd-ring);
|
|
321
|
-
outline-offset: 2px;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
318
|
.text-prompt__submit:disabled {
|
|
325
319
|
opacity: 0.5;
|
|
326
320
|
cursor: not-allowed;
|
|
@@ -510,12 +510,12 @@
|
|
|
510
510
|
|
|
511
511
|
.flowdrop-main-layout__sidebar::-webkit-scrollbar-track {
|
|
512
512
|
background: var(--fd-scrollbar-track);
|
|
513
|
-
border-radius:
|
|
513
|
+
border-radius: var(--fd-scrollbar-radius);
|
|
514
514
|
}
|
|
515
515
|
|
|
516
516
|
.flowdrop-main-layout__sidebar::-webkit-scrollbar-thumb {
|
|
517
517
|
background: var(--fd-scrollbar-thumb);
|
|
518
|
-
border-radius:
|
|
518
|
+
border-radius: var(--fd-scrollbar-radius);
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
.flowdrop-main-layout__sidebar::-webkit-scrollbar-thumb:hover {
|
|
@@ -585,7 +585,6 @@
|
|
|
585
585
|
}
|
|
586
586
|
|
|
587
587
|
.flowdrop-main-layout__divider:focus {
|
|
588
|
-
outline: none;
|
|
589
588
|
background-color: var(--fd-primary-muted);
|
|
590
589
|
}
|
|
591
590
|
|
|
@@ -669,12 +668,12 @@
|
|
|
669
668
|
|
|
670
669
|
.flowdrop-main-layout__panel--bottom::-webkit-scrollbar-track {
|
|
671
670
|
background: var(--fd-scrollbar-track);
|
|
672
|
-
border-radius:
|
|
671
|
+
border-radius: var(--fd-scrollbar-radius);
|
|
673
672
|
}
|
|
674
673
|
|
|
675
674
|
.flowdrop-main-layout__panel--bottom::-webkit-scrollbar-thumb {
|
|
676
675
|
background: var(--fd-scrollbar-thumb);
|
|
677
|
-
border-radius:
|
|
676
|
+
border-radius: var(--fd-scrollbar-radius);
|
|
678
677
|
}
|
|
679
678
|
|
|
680
679
|
.flowdrop-main-layout__panel--bottom::-webkit-scrollbar-thumb:hover {
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
return { text: atomCfg.placeholder ?? '', empty: true };
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
// Pill
|
|
144
|
+
// Pill is a fixed 40px tall (20px grid), so a single port centers at 20px.
|
|
145
145
|
// Distribute handles as a % of node height: 50% for one port, evenly otherwise.
|
|
146
146
|
function portTopPct(index: number, count: number): number {
|
|
147
147
|
return ((index + 1) / (count + 1)) * 100;
|
|
@@ -150,12 +150,6 @@
|
|
|
150
150
|
function openConfig(): void {
|
|
151
151
|
data.onConfigOpen?.({ id: nodeId, type: nodeType, data });
|
|
152
152
|
}
|
|
153
|
-
function handleKeydown(event: KeyboardEvent): void {
|
|
154
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
155
|
-
event.preventDefault();
|
|
156
|
-
openConfig();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
153
|
</script>
|
|
160
154
|
|
|
161
155
|
{#each inPorts as port, index (port.id)}
|
|
@@ -170,6 +164,11 @@
|
|
|
170
164
|
/>
|
|
171
165
|
{/each}
|
|
172
166
|
|
|
167
|
+
<!-- Presentational: focus, keyboard and selection live on xyflow's node wrapper
|
|
168
|
+
(see UniversalNode, which maps Enter/Space to opening config). click is a
|
|
169
|
+
mouse convenience. -->
|
|
170
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
171
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
173
172
|
<div
|
|
174
173
|
class="flowdrop-atom-node"
|
|
175
174
|
class:flowdrop-atom-node--selected={selected}
|
|
@@ -179,14 +178,13 @@
|
|
|
179
178
|
class:flowdrop-atom-node--rect={isRect}
|
|
180
179
|
style={nodeStyle}
|
|
181
180
|
onclick={openConfig}
|
|
182
|
-
onkeydown={handleKeydown}
|
|
183
|
-
role="button"
|
|
184
|
-
tabindex="0"
|
|
185
181
|
>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
<div class="flowdrop-atom-node__pill">
|
|
183
|
+
{#if atomCfg.prefix && !display.empty}
|
|
184
|
+
<span class="flowdrop-atom-node__prefix" aria-hidden="true">{atomCfg.prefix}</span>
|
|
185
|
+
{/if}
|
|
186
|
+
<span class="flowdrop-atom-node__body" title={display.text}>{display.text}</span>
|
|
187
|
+
</div>
|
|
190
188
|
</div>
|
|
191
189
|
|
|
192
190
|
{#each outPorts as port, index (port.id)}
|
|
@@ -202,54 +200,68 @@
|
|
|
202
200
|
{/each}
|
|
203
201
|
|
|
204
202
|
<style>
|
|
203
|
+
/* Transparent slot: defines the node's bounding box so handles anchor
|
|
204
|
+
consistently, while the pill sits narrow and vertically centered inside. */
|
|
205
205
|
.flowdrop-atom-node {
|
|
206
206
|
position: relative;
|
|
207
|
-
|
|
207
|
+
box-sizing: border-box;
|
|
208
|
+
display: flex;
|
|
208
209
|
align-items: center;
|
|
210
|
+
justify-content: center;
|
|
209
211
|
width: fit-content;
|
|
210
|
-
|
|
211
|
-
min-height:
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
/* 40px tall → a single port centers at 20px (50%); capped at 60px / 120px. */
|
|
213
|
+
min-height: 40px;
|
|
214
|
+
max-height: 60px;
|
|
215
|
+
max-width: 120px;
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
z-index: 10;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* The visible, themed pill — hugs its text and stays compact. */
|
|
221
|
+
.flowdrop-atom-node__pill {
|
|
222
|
+
box-sizing: border-box;
|
|
223
|
+
display: inline-flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
max-width: 100%;
|
|
226
|
+
min-width: 32px;
|
|
227
|
+
padding: 4px 10px;
|
|
228
|
+
background-color: var(--fd-node-bg);
|
|
229
|
+
backdrop-filter: var(--fd-node-backdrop-filter);
|
|
214
230
|
/* --fd-atom-node-color is set inline only when the server provides a color;
|
|
215
231
|
otherwise it falls back to the neutral border token. */
|
|
216
|
-
border:
|
|
232
|
+
border: var(--fd-node-border-width) solid var(--fd-atom-node-color, var(--fd-node-border));
|
|
217
233
|
border-radius: 999px;
|
|
218
234
|
box-shadow: var(--fd-shadow-sm);
|
|
219
235
|
color: var(--fd-foreground);
|
|
220
|
-
cursor: pointer;
|
|
221
236
|
transition:
|
|
222
237
|
box-shadow var(--fd-transition-fast),
|
|
223
238
|
border-color var(--fd-transition-fast);
|
|
224
|
-
z-index: 10;
|
|
225
239
|
}
|
|
226
240
|
|
|
227
|
-
.flowdrop-atom-node--rect {
|
|
241
|
+
.flowdrop-atom-node--rect .flowdrop-atom-node__pill {
|
|
228
242
|
border-radius: var(--fd-radius-md);
|
|
229
243
|
}
|
|
230
244
|
|
|
231
|
-
.flowdrop-atom-node:hover {
|
|
232
|
-
box-shadow: var(--fd-shadow
|
|
245
|
+
.flowdrop-atom-node:hover .flowdrop-atom-node__pill {
|
|
246
|
+
box-shadow: var(--fd-node-shadow);
|
|
233
247
|
border-color: var(--fd-atom-node-color, var(--fd-node-border-hover));
|
|
234
248
|
}
|
|
235
249
|
|
|
236
|
-
.flowdrop-atom-node--selected {
|
|
250
|
+
.flowdrop-atom-node--selected .flowdrop-atom-node__pill {
|
|
237
251
|
box-shadow:
|
|
238
252
|
0 0 0 2px color-mix(in srgb, var(--fd-atom-node-color, var(--fd-primary)) 30%, transparent),
|
|
239
|
-
var(--fd-shadow
|
|
253
|
+
var(--fd-node-shadow);
|
|
240
254
|
border-color: var(--fd-atom-node-color, var(--fd-primary));
|
|
241
255
|
}
|
|
242
256
|
|
|
243
|
-
.
|
|
244
|
-
|
|
245
|
-
outline-offset: 2px;
|
|
246
|
-
}
|
|
257
|
+
/* Focus ring is centralized in base.css (drawn on the .svelte-flow__node
|
|
258
|
+
wrapper, which is the focusable element). */
|
|
247
259
|
|
|
248
260
|
.flowdrop-atom-node--processing {
|
|
249
261
|
opacity: 0.7;
|
|
250
262
|
}
|
|
251
263
|
|
|
252
|
-
.flowdrop-atom-node--error {
|
|
264
|
+
.flowdrop-atom-node--error .flowdrop-atom-node__pill {
|
|
253
265
|
border-color: var(--fd-error) !important;
|
|
254
266
|
background-color: var(--fd-error-muted) !important;
|
|
255
267
|
}
|
|
@@ -264,14 +276,14 @@
|
|
|
264
276
|
margin-right: 2px;
|
|
265
277
|
color: var(--fd-muted-foreground);
|
|
266
278
|
font-size: var(--fd-text-sm);
|
|
267
|
-
line-height:
|
|
279
|
+
line-height: 20px;
|
|
268
280
|
}
|
|
269
281
|
|
|
270
282
|
.flowdrop-atom-node__body {
|
|
271
283
|
/* min-width:0 lets the body ellipsize as a flex sibling of the prefix */
|
|
272
284
|
min-width: 0;
|
|
273
285
|
font-size: var(--fd-text-sm);
|
|
274
|
-
line-height:
|
|
286
|
+
line-height: 20px;
|
|
275
287
|
white-space: nowrap;
|
|
276
288
|
overflow: hidden;
|
|
277
289
|
text-overflow: ellipsis;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { Position, Handle } from '@xyflow/svelte';
|
|
13
13
|
import type { WorkflowNode, NodePort, Branch } from '../../types/index.js';
|
|
14
14
|
import Icon from '@iconify/svelte';
|
|
15
|
+
import NodeConfigButton from './NodeConfigButton.svelte';
|
|
15
16
|
import { getNodeIcon } from '../../utils/icons.js';
|
|
16
17
|
import {
|
|
17
18
|
getDataTypeColorToken,
|
|
@@ -120,13 +121,6 @@
|
|
|
120
121
|
*/
|
|
121
122
|
const visibleBranches = $derived(branches.filter((branch) => isBranchVisible(branch.name)));
|
|
122
123
|
|
|
123
|
-
/**
|
|
124
|
-
* Handle node click - only handle selection, no config opening
|
|
125
|
-
*/
|
|
126
|
-
function handleNodeClick(): void {
|
|
127
|
-
// Node selection is handled by Svelte Flow
|
|
128
|
-
}
|
|
129
|
-
|
|
130
124
|
/**
|
|
131
125
|
* Handle double-click to open config
|
|
132
126
|
*/
|
|
@@ -140,16 +134,6 @@
|
|
|
140
134
|
}
|
|
141
135
|
}
|
|
142
136
|
|
|
143
|
-
/**
|
|
144
|
-
* Handle keyboard events for accessibility
|
|
145
|
-
*/
|
|
146
|
-
function handleKeydown(event: KeyboardEvent): void {
|
|
147
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
148
|
-
event.preventDefault();
|
|
149
|
-
handleNodeClick();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
137
|
/**
|
|
154
138
|
* Check if a branch is active
|
|
155
139
|
*/
|
|
@@ -159,14 +143,13 @@
|
|
|
159
143
|
</script>
|
|
160
144
|
|
|
161
145
|
<!-- Node Container -->
|
|
146
|
+
<!-- Presentational: focus, keyboard and selection live on xyflow's node
|
|
147
|
+
wrapper (see UniversalNode). double-click is a mouse convenience. -->
|
|
148
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
162
149
|
<div
|
|
163
150
|
class="flowdrop-workflow-node flowdrop-workflow-node--gateway"
|
|
164
151
|
class:flowdrop-workflow-node--selected={props.selected}
|
|
165
|
-
onclick={handleNodeClick}
|
|
166
152
|
ondblclick={handleNodeDoubleClick}
|
|
167
|
-
onkeydown={handleKeydown}
|
|
168
|
-
role="button"
|
|
169
|
-
tabindex="0"
|
|
170
153
|
aria-label={graph.gatewayNode({ title: displayTitle })}
|
|
171
154
|
aria-describedby="node-description-{props.data.nodeId || 'unknown'}"
|
|
172
155
|
>
|
|
@@ -185,7 +168,7 @@
|
|
|
185
168
|
</div>
|
|
186
169
|
|
|
187
170
|
<!-- Node Title - uses instanceTitle override if set -->
|
|
188
|
-
<h3 class="flowdrop-text--sm flowdrop-font--medium flowdrop-
|
|
171
|
+
<h3 class="flowdrop-text--sm flowdrop-font--medium flowdrop-flex--1">
|
|
189
172
|
{displayTitle}
|
|
190
173
|
</h3>
|
|
191
174
|
</div>
|
|
@@ -204,13 +187,13 @@
|
|
|
204
187
|
<div class="flowdrop-workflow-node__ports-list">
|
|
205
188
|
{#each visibleInputPorts as port (port.id)}
|
|
206
189
|
<div class="flowdrop-workflow-node__port">
|
|
207
|
-
<!-- Input Handle:
|
|
190
|
+
<!-- Input Handle: one grid row (20px) from the top so it aligns with the label, at node edge -->
|
|
208
191
|
<Handle
|
|
209
192
|
type="target"
|
|
210
193
|
position={Position.Left}
|
|
211
194
|
id={`${props.data.nodeId}-input-${port.id}`}
|
|
212
195
|
class="flowdrop-workflow-node__handle"
|
|
213
|
-
style="top:
|
|
196
|
+
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: {getDataTypeColorToken(
|
|
214
197
|
checker,
|
|
215
198
|
port.dataType
|
|
216
199
|
)}; --fd-handle-border-color: var(--fd-handle-border);"
|
|
@@ -293,13 +276,13 @@
|
|
|
293
276
|
</div>
|
|
294
277
|
</div>
|
|
295
278
|
|
|
296
|
-
<!-- Output Handle:
|
|
279
|
+
<!-- Output Handle: one grid row (20px) from the top so it aligns with the label, at node edge -->
|
|
297
280
|
<Handle
|
|
298
281
|
type="source"
|
|
299
282
|
position={Position.Right}
|
|
300
283
|
id={`${props.data.nodeId}-output-${branch.name}`}
|
|
301
284
|
class={`flowdrop-workflow-node__handle ${isActive ? 'flowdrop-workflow-node__handle--active' : ''}`}
|
|
302
|
-
style="top:
|
|
285
|
+
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: {isActive
|
|
303
286
|
? getDataTypeColorToken(checker, 'trigger')
|
|
304
287
|
: getDataTypeColorToken(
|
|
305
288
|
checker,
|
|
@@ -323,22 +306,17 @@
|
|
|
323
306
|
<!-- Note: When all branches are hidden due to hideUnconnectedHandles, we don't show anything -->
|
|
324
307
|
|
|
325
308
|
<!-- Config button -->
|
|
326
|
-
<
|
|
327
|
-
class="flowdrop-workflow-node__config-btn"
|
|
328
|
-
onclick={handleNodeDoubleClick}
|
|
329
|
-
title="Configure node"
|
|
330
|
-
>
|
|
331
|
-
<Icon icon="mdi:cog" />
|
|
332
|
-
</button>
|
|
309
|
+
<NodeConfigButton onclick={handleNodeDoubleClick} title="Configure node" />
|
|
333
310
|
</div>
|
|
334
311
|
|
|
335
312
|
<style>
|
|
336
313
|
.flowdrop-workflow-node {
|
|
337
314
|
position: relative;
|
|
338
|
-
background-color: var(--fd-
|
|
339
|
-
|
|
340
|
-
border
|
|
341
|
-
|
|
315
|
+
background-color: var(--fd-node-bg);
|
|
316
|
+
backdrop-filter: var(--fd-node-backdrop-filter);
|
|
317
|
+
border: var(--fd-node-border-width) solid var(--fd-node-border);
|
|
318
|
+
border-radius: var(--fd-node-radius);
|
|
319
|
+
box-shadow: var(--fd-node-shadow);
|
|
342
320
|
width: var(--fd-node-default-width);
|
|
343
321
|
z-index: 10;
|
|
344
322
|
color: var(--fd-foreground);
|
|
@@ -350,42 +328,49 @@
|
|
|
350
328
|
}
|
|
351
329
|
|
|
352
330
|
.flowdrop-workflow-node:hover {
|
|
353
|
-
box-shadow: var(--fd-shadow-
|
|
331
|
+
box-shadow: var(--fd-node-shadow-hover);
|
|
354
332
|
border-color: var(--fd-node-border-hover);
|
|
355
333
|
}
|
|
356
334
|
|
|
357
335
|
.flowdrop-workflow-node--selected {
|
|
358
336
|
box-shadow:
|
|
359
337
|
0 0 0 2px var(--fd-primary-muted),
|
|
360
|
-
var(--fd-shadow-
|
|
338
|
+
var(--fd-node-shadow-hover);
|
|
361
339
|
border-color: var(--fd-primary);
|
|
362
340
|
}
|
|
363
341
|
|
|
364
342
|
.flowdrop-workflow-node--selected:hover {
|
|
365
343
|
box-shadow:
|
|
366
344
|
0 0 0 2px var(--fd-primary-muted),
|
|
367
|
-
var(--fd-shadow-
|
|
345
|
+
var(--fd-node-shadow-hover);
|
|
368
346
|
border-color: var(--fd-primary);
|
|
369
347
|
}
|
|
370
348
|
|
|
371
|
-
.
|
|
372
|
-
|
|
373
|
-
outline-offset: 2px;
|
|
374
|
-
}
|
|
349
|
+
/* Focus ring is centralized in base.css (drawn on the .svelte-flow__node
|
|
350
|
+
wrapper, which is the focusable element). */
|
|
375
351
|
|
|
376
352
|
.flowdrop-workflow-node__header {
|
|
377
353
|
box-sizing: border-box;
|
|
378
|
-
padding
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
354
|
+
/* Bottom padding absorbs BOTH the node's own top border and the header
|
|
355
|
+
divider, so the body below the header lands on the 20px grid measured
|
|
356
|
+
from the node's outer top edge: node-border + header = 100/120/140. */
|
|
357
|
+
padding: var(--fd-node-header-gap) var(--fd-space-xl)
|
|
358
|
+
calc(
|
|
359
|
+
var(--fd-node-header-gap) - var(--fd-node-border-width) -
|
|
360
|
+
var(--fd-node-header-divider-width)
|
|
361
|
+
);
|
|
362
|
+
border-bottom: var(--fd-node-header-divider-width) solid var(--fd-node-header-divider-color);
|
|
363
|
+
background: var(--fd-node-header-bg);
|
|
364
|
+
border-top-left-radius: var(--fd-node-radius);
|
|
365
|
+
border-top-right-radius: var(--fd-node-radius);
|
|
383
366
|
display: flex;
|
|
384
367
|
flex-direction: column;
|
|
385
|
-
gap: var(--fd-node-header-gap);
|
|
368
|
+
gap: calc(var(--fd-node-header-gap) * 2);
|
|
369
|
+
/* node-border (1.5) + header = 100/120/140. Header itself is
|
|
370
|
+
4*gap + title + desc-line - node-border; each extra desc line adds 20. */
|
|
386
371
|
min-height: calc(
|
|
387
|
-
var(--fd-node-header-gap) *
|
|
388
|
-
var(--fd-node-header-desc-line)
|
|
372
|
+
var(--fd-node-header-gap) * 4 + var(--fd-node-header-title-height) +
|
|
373
|
+
var(--fd-node-header-desc-line) - var(--fd-node-border-width)
|
|
389
374
|
);
|
|
390
375
|
}
|
|
391
376
|
|
|
@@ -416,9 +401,10 @@
|
|
|
416
401
|
display: flex;
|
|
417
402
|
align-items: center;
|
|
418
403
|
justify-content: center;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
404
|
+
/* px (not rem) so the icon stays grid-locked regardless of root font-size */
|
|
405
|
+
width: 36px;
|
|
406
|
+
height: 36px;
|
|
407
|
+
border-radius: 8px;
|
|
422
408
|
background: color-mix(in srgb, var(--_icon-color) var(--fd-node-icon-bg-opacity), transparent);
|
|
423
409
|
flex-shrink: 0;
|
|
424
410
|
transition: all var(--fd-transition-normal);
|
|
@@ -434,15 +420,22 @@
|
|
|
434
420
|
}
|
|
435
421
|
|
|
436
422
|
.flowdrop-workflow-node__icon-wrapper :global(.flowdrop-workflow-node__icon) {
|
|
437
|
-
width:
|
|
438
|
-
height:
|
|
423
|
+
width: 20px;
|
|
424
|
+
height: 20px;
|
|
439
425
|
color: var(--fd-node-icon);
|
|
440
426
|
}
|
|
441
427
|
|
|
442
428
|
.flowdrop-workflow-node__header-title h3 {
|
|
443
429
|
margin: 0;
|
|
444
|
-
|
|
430
|
+
/* half the title block so two lines fill it exactly on the 20px grid */
|
|
431
|
+
line-height: calc(var(--fd-node-header-title-height) / 2);
|
|
445
432
|
color: var(--fd-foreground);
|
|
433
|
+
display: -webkit-box;
|
|
434
|
+
-webkit-line-clamp: 2;
|
|
435
|
+
line-clamp: 2;
|
|
436
|
+
-webkit-box-orient: vertical;
|
|
437
|
+
overflow: hidden;
|
|
438
|
+
min-width: 0;
|
|
446
439
|
}
|
|
447
440
|
|
|
448
441
|
.flowdrop-workflow-node__ports {
|
|
@@ -452,27 +445,52 @@
|
|
|
452
445
|
.flowdrop-workflow-node__ports-list {
|
|
453
446
|
display: flex;
|
|
454
447
|
flex-direction: column;
|
|
455
|
-
gap:
|
|
456
|
-
padding:
|
|
448
|
+
gap: 0;
|
|
449
|
+
/* No vertical padding: sections stack flush and node height stays a
|
|
450
|
+
multiple of 20. The one exception is the clearance below the header
|
|
451
|
+
divider, applied to the first section only (below). */
|
|
452
|
+
padding: 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* The first port section sits directly below the header divider; give it a
|
|
456
|
+
full 20px grid row of clearance so the first port lands on the grid. */
|
|
457
|
+
.flowdrop-workflow-node__header
|
|
458
|
+
+ .flowdrop-workflow-node__ports
|
|
459
|
+
.flowdrop-workflow-node__ports-list {
|
|
460
|
+
padding-top: calc(var(--fd-node-header-gap) * 2);
|
|
457
461
|
}
|
|
458
462
|
|
|
459
463
|
.flowdrop-workflow-node__port {
|
|
460
464
|
display: flex;
|
|
461
|
-
align-items:
|
|
465
|
+
align-items: flex-start;
|
|
462
466
|
gap: 0;
|
|
463
|
-
|
|
464
|
-
|
|
467
|
+
/* Fixed three-row (60px) height for every port — node height stays
|
|
468
|
+
predictable whether or not a port carries a description. */
|
|
469
|
+
height: calc(var(--fd-node-port-row-height) * 3);
|
|
470
|
+
padding: 0;
|
|
465
471
|
position: relative;
|
|
466
472
|
}
|
|
467
473
|
|
|
468
474
|
.flowdrop-workflow-node__port-content {
|
|
469
|
-
padding:
|
|
475
|
+
padding: var(--fd-node-header-gap) var(--fd-space-xl) 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/* Each line in a port occupies one 20px grid row: a label-only port
|
|
479
|
+
centers its single row, a label + description fills both. */
|
|
480
|
+
.flowdrop-workflow-node__port-content > div {
|
|
481
|
+
min-height: var(--fd-node-port-row-height);
|
|
482
|
+
align-items: center;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.flowdrop-workflow-node__port-content > p {
|
|
486
|
+
min-height: var(--fd-node-port-row-height);
|
|
487
|
+
line-height: var(--fd-node-port-row-height);
|
|
470
488
|
}
|
|
471
489
|
|
|
472
490
|
.flowdrop-badge {
|
|
473
|
-
padding:
|
|
491
|
+
padding: 2px 4px;
|
|
474
492
|
border-radius: var(--fd-radius-sm);
|
|
475
|
-
font-size:
|
|
493
|
+
font-size: 10px;
|
|
476
494
|
font-weight: 500;
|
|
477
495
|
text-transform: uppercase;
|
|
478
496
|
letter-spacing: 0.05em;
|
|
@@ -484,8 +502,8 @@
|
|
|
484
502
|
}
|
|
485
503
|
|
|
486
504
|
.flowdrop-badge--sm {
|
|
487
|
-
font-size:
|
|
488
|
-
padding:
|
|
505
|
+
font-size: 10px;
|
|
506
|
+
padding: 2px 4px;
|
|
489
507
|
}
|
|
490
508
|
|
|
491
509
|
.workflow-node__no-branches {
|
|
@@ -537,12 +555,12 @@
|
|
|
537
555
|
|
|
538
556
|
.flowdrop-text--xs {
|
|
539
557
|
font-size: var(--fd-text-xs);
|
|
540
|
-
line-height:
|
|
558
|
+
line-height: 16px;
|
|
541
559
|
}
|
|
542
560
|
|
|
543
561
|
.flowdrop-text--sm {
|
|
544
562
|
font-size: var(--fd-text-sm);
|
|
545
|
-
line-height:
|
|
563
|
+
line-height: 20px;
|
|
546
564
|
}
|
|
547
565
|
|
|
548
566
|
.flowdrop-text--gray {
|
|
@@ -568,34 +586,8 @@
|
|
|
568
586
|
text-align: right;
|
|
569
587
|
}
|
|
570
588
|
|
|
571
|
-
.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
right: var(--fd-space-xs);
|
|
575
|
-
width: 1.5rem;
|
|
576
|
-
height: 1.5rem;
|
|
577
|
-
background-color: var(--fd-backdrop);
|
|
578
|
-
border: 1px solid var(--fd-border);
|
|
579
|
-
border-radius: var(--fd-radius-sm);
|
|
580
|
-
color: var(--fd-muted-foreground);
|
|
581
|
-
cursor: pointer;
|
|
582
|
-
display: flex;
|
|
583
|
-
align-items: center;
|
|
584
|
-
justify-content: center;
|
|
585
|
-
opacity: 0;
|
|
586
|
-
transition: all var(--fd-transition-normal);
|
|
587
|
-
backdrop-filter: blur(4px);
|
|
588
|
-
z-index: 15;
|
|
589
|
-
font-size: var(--fd-text-sm);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
.flowdrop-workflow-node:hover .flowdrop-workflow-node__config-btn {
|
|
593
|
-
opacity: 1;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
.flowdrop-workflow-node__config-btn:hover {
|
|
597
|
-
background-color: var(--fd-muted);
|
|
598
|
-
border-color: var(--fd-border-strong);
|
|
599
|
-
color: var(--fd-foreground);
|
|
589
|
+
/* Reveal the NodeConfigButton (gear) when the node is hovered. */
|
|
590
|
+
.flowdrop-workflow-node:hover {
|
|
591
|
+
--fd-config-btn-opacity: 1;
|
|
600
592
|
}
|
|
601
593
|
</style>
|