@flowdrop/flowdrop 1.4.0 → 1.5.0

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 (100) hide show
  1. package/README.md +68 -24
  2. package/dist/adapters/WorkflowAdapter.js +2 -22
  3. package/dist/adapters/agentspec/autoLayout.d.ts +51 -5
  4. package/dist/adapters/agentspec/autoLayout.js +120 -23
  5. package/dist/chat/commandClassifier.d.ts +19 -0
  6. package/dist/chat/commandClassifier.js +30 -0
  7. package/dist/chat/index.d.ts +27 -0
  8. package/dist/chat/index.js +32 -0
  9. package/dist/chat/responseParser.d.ts +21 -0
  10. package/dist/chat/responseParser.js +87 -0
  11. package/dist/commands/batch.d.ts +18 -0
  12. package/dist/commands/batch.js +56 -0
  13. package/dist/commands/executor.d.ts +37 -0
  14. package/dist/commands/executor.js +1044 -0
  15. package/dist/commands/index.d.ts +14 -0
  16. package/dist/commands/index.js +17 -0
  17. package/dist/commands/parser.d.ts +16 -0
  18. package/dist/commands/parser.js +278 -0
  19. package/dist/commands/positioner.d.ts +19 -0
  20. package/dist/commands/positioner.js +33 -0
  21. package/dist/commands/storeIntegration.svelte.d.ts +16 -0
  22. package/dist/commands/storeIntegration.svelte.js +67 -0
  23. package/dist/commands/types.d.ts +343 -0
  24. package/dist/commands/types.js +45 -0
  25. package/dist/components/App.svelte +351 -12
  26. package/dist/components/App.svelte.d.ts +3 -0
  27. package/dist/components/CanvasController.svelte +38 -0
  28. package/dist/components/CanvasController.svelte.d.ts +32 -0
  29. package/dist/components/ConfigMappingRow.svelte +130 -0
  30. package/dist/components/ConfigMappingRow.svelte.d.ts +8 -0
  31. package/dist/components/ConfigPanel.svelte +56 -7
  32. package/dist/components/ConfigPanel.svelte.d.ts +2 -0
  33. package/dist/components/FlowDropEdge.svelte +2 -10
  34. package/dist/components/LogsSidebar.svelte +5 -5
  35. package/dist/components/NodeSidebar.svelte +15 -49
  36. package/dist/components/NodeSwapPicker.svelte +537 -0
  37. package/dist/components/NodeSwapPicker.svelte.d.ts +16 -0
  38. package/dist/components/PortMappingRow.svelte +209 -0
  39. package/dist/components/PortMappingRow.svelte.d.ts +12 -0
  40. package/dist/components/SwapMappingEditor.svelte +550 -0
  41. package/dist/components/SwapMappingEditor.svelte.d.ts +12 -0
  42. package/dist/components/WorkflowEditor.svelte +99 -4
  43. package/dist/components/WorkflowEditor.svelte.d.ts +8 -0
  44. package/dist/components/chat/AIChatPanel.svelte +658 -0
  45. package/dist/components/chat/AIChatPanel.svelte.d.ts +13 -0
  46. package/dist/components/chat/CommandPreview.svelte +184 -0
  47. package/dist/components/chat/CommandPreview.svelte.d.ts +9 -0
  48. package/dist/components/console/CommandConsole.stories.svelte +93 -0
  49. package/dist/components/console/CommandConsole.stories.svelte.d.ts +27 -0
  50. package/dist/components/console/CommandConsole.svelte +259 -0
  51. package/dist/components/console/CommandConsole.svelte.d.ts +11 -0
  52. package/dist/components/console/ConsoleAutocomplete.svelte +139 -0
  53. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +21 -0
  54. package/dist/components/console/ConsoleInput.svelte +712 -0
  55. package/dist/components/console/ConsoleInput.svelte.d.ts +16 -0
  56. package/dist/components/console/ConsoleOutput.svelte +121 -0
  57. package/dist/components/console/ConsoleOutput.svelte.d.ts +11 -0
  58. package/dist/components/console/formatters.d.ts +26 -0
  59. package/dist/components/console/formatters.js +118 -0
  60. package/dist/components/interrupt/index.d.ts +1 -0
  61. package/dist/components/interrupt/index.js +1 -0
  62. package/dist/config/endpoints.d.ts +8 -0
  63. package/dist/config/endpoints.js +5 -0
  64. package/dist/core/index.d.ts +5 -0
  65. package/dist/core/index.js +9 -0
  66. package/dist/editor/index.d.ts +3 -1
  67. package/dist/editor/index.js +4 -2
  68. package/dist/helpers/proximityConnect.js +8 -1
  69. package/dist/helpers/workflowEditorHelper.d.ts +3 -53
  70. package/dist/helpers/workflowEditorHelper.js +13 -228
  71. package/dist/playground/index.d.ts +1 -1
  72. package/dist/playground/index.js +1 -1
  73. package/dist/schemas/v1/workflow.schema.json +107 -22
  74. package/dist/services/chatService.d.ts +65 -0
  75. package/dist/services/chatService.js +131 -0
  76. package/dist/services/historyService.d.ts +6 -4
  77. package/dist/services/historyService.js +21 -6
  78. package/dist/stores/interruptStore.svelte.js +6 -1
  79. package/dist/stores/playgroundStore.svelte.d.ts +1 -1
  80. package/dist/stores/playgroundStore.svelte.js +11 -2
  81. package/dist/stores/portCoordinateStore.svelte.d.ts +4 -0
  82. package/dist/stores/portCoordinateStore.svelte.js +20 -26
  83. package/dist/stores/workflowStore.svelte.d.ts +31 -2
  84. package/dist/stores/workflowStore.svelte.js +84 -64
  85. package/dist/types/chat.d.ts +63 -0
  86. package/dist/types/chat.js +9 -0
  87. package/dist/types/events.d.ts +28 -2
  88. package/dist/types/events.js +1 -0
  89. package/dist/types/index.d.ts +8 -0
  90. package/dist/types/settings.d.ts +6 -0
  91. package/dist/types/settings.js +3 -0
  92. package/dist/utils/edgeStyling.d.ts +42 -0
  93. package/dist/utils/edgeStyling.js +176 -0
  94. package/dist/utils/nodeIds.d.ts +31 -0
  95. package/dist/utils/nodeIds.js +42 -0
  96. package/dist/utils/nodeSwap.d.ts +221 -0
  97. package/dist/utils/nodeSwap.js +686 -0
  98. package/package.json +6 -1
  99. package/dist/helpers/nodeLayoutHelper.d.ts +0 -14
  100. package/dist/helpers/nodeLayoutHelper.js +0 -19
@@ -0,0 +1,130 @@
1
+ <!--
2
+ ConfigMappingRow Component
3
+ A single row in the config mapping editor showing carry/reset toggle.
4
+ Styled with BEM syntax.
5
+ -->
6
+
7
+ <script lang="ts">
8
+ import type { EditableConfigMapping } from "../utils/nodeSwap.js";
9
+
10
+ interface Props {
11
+ mapping: EditableConfigMapping;
12
+ onToggle: (key: string) => void;
13
+ }
14
+
15
+ const { mapping, onToggle }: Props = $props();
16
+
17
+ function formatValue(value: unknown): string {
18
+ if (value === null || value === undefined) return "—";
19
+ if (typeof value === "string") return value || '""';
20
+ if (typeof value === "boolean") return value ? "true" : "false";
21
+ if (typeof value === "number") return String(value);
22
+ return JSON.stringify(value);
23
+ }
24
+ </script>
25
+
26
+ <div class="config-mapping-row">
27
+ <div class="config-mapping-row__info">
28
+ <span class="config-mapping-row__key">{mapping.title}</span>
29
+ </div>
30
+
31
+ {#if !mapping.isFlat}
32
+ <div class="config-mapping-row__complex">
33
+ Complex value — will use default
34
+ </div>
35
+ {:else}
36
+ <div class="config-mapping-row__values">
37
+ {#if mapping.carryOver}
38
+ <span class="config-mapping-row__value config-mapping-row__value--carried">
39
+ {formatValue(mapping.oldValue)}
40
+ </span>
41
+ {:else}
42
+ <span class="config-mapping-row__value config-mapping-row__value--default">
43
+ {formatValue(mapping.newDefault)}
44
+ </span>
45
+ {/if}
46
+ </div>
47
+
48
+ <button
49
+ class="config-mapping-row__toggle"
50
+ class:config-mapping-row__toggle--carry={mapping.carryOver}
51
+ onclick={() => onToggle(mapping.key)}
52
+ type="button"
53
+ >
54
+ {mapping.carryOver ? "Carry over" : "Use default"}
55
+ </button>
56
+ {/if}
57
+ </div>
58
+
59
+ <style>
60
+ .config-mapping-row {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: var(--fd-space-xs);
64
+ padding: var(--fd-space-xs) 0;
65
+ font-size: var(--fd-text-xs);
66
+ }
67
+
68
+ .config-mapping-row__info {
69
+ flex: 0 0 auto;
70
+ min-width: 4rem;
71
+ }
72
+
73
+ .config-mapping-row__key {
74
+ font-weight: 500;
75
+ color: var(--fd-foreground);
76
+ }
77
+
78
+ .config-mapping-row__complex {
79
+ flex: 1;
80
+ color: var(--fd-muted-foreground);
81
+ font-style: italic;
82
+ font-size: var(--fd-text-xs);
83
+ }
84
+
85
+ .config-mapping-row__values {
86
+ flex: 1;
87
+ min-width: 0;
88
+ overflow: hidden;
89
+ text-overflow: ellipsis;
90
+ white-space: nowrap;
91
+ }
92
+
93
+ .config-mapping-row__value {
94
+ font-family: var(--fd-font-mono, monospace);
95
+ font-size: var(--fd-text-xs);
96
+ }
97
+
98
+ .config-mapping-row__value--carried {
99
+ color: var(--fd-success);
100
+ }
101
+
102
+ .config-mapping-row__value--default {
103
+ color: var(--fd-muted-foreground);
104
+ }
105
+
106
+ .config-mapping-row__toggle {
107
+ flex-shrink: 0;
108
+ padding: 0.1875rem 0.5rem;
109
+ border: 1px solid var(--fd-border);
110
+ border-radius: var(--fd-radius-sm);
111
+ background-color: var(--fd-background);
112
+ color: var(--fd-muted-foreground);
113
+ font-size: var(--fd-text-xs);
114
+ font-weight: 500;
115
+ cursor: pointer;
116
+ transition: all var(--fd-transition-fast);
117
+ white-space: nowrap;
118
+ }
119
+
120
+ .config-mapping-row__toggle:hover {
121
+ border-color: var(--fd-border-strong);
122
+ color: var(--fd-foreground);
123
+ }
124
+
125
+ .config-mapping-row__toggle--carry {
126
+ background-color: color-mix(in srgb, var(--fd-success) 10%, transparent);
127
+ border-color: color-mix(in srgb, var(--fd-success) 30%, transparent);
128
+ color: var(--fd-success);
129
+ }
130
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { EditableConfigMapping } from "../utils/nodeSwap.js";
2
+ interface Props {
3
+ mapping: EditableConfigMapping;
4
+ onToggle: (key: string) => void;
5
+ }
6
+ declare const ConfigMappingRow: import("svelte").Component<Props, {}, "">;
7
+ type ConfigMappingRow = ReturnType<typeof ConfigMappingRow>;
8
+ export default ConfigMappingRow;
@@ -8,6 +8,7 @@
8
8
 
9
9
  <script lang="ts">
10
10
  import type { Snippet } from "svelte";
11
+ import Icon from "@iconify/svelte";
11
12
  import ReadOnlyDetails from "./ReadOnlyDetails.svelte";
12
13
  import { getUiSettings } from "../stores/settingsStore.svelte.js";
13
14
 
@@ -37,6 +38,8 @@
37
38
  configTitle?: string;
38
39
  /** Callback function when the panel is closed */
39
40
  onClose: () => void;
41
+ /** Optional callback to initiate node swap — when provided, shows swap button */
42
+ onSwap?: () => void;
40
43
  /** Slot content for the configuration form */
41
44
  children?: Snippet;
42
45
  }
@@ -48,6 +51,7 @@
48
51
  details = [],
49
52
  configTitle = "Configuration",
50
53
  onClose,
54
+ onSwap,
51
55
  children,
52
56
  }: Props = $props();
53
57
 
@@ -66,13 +70,25 @@
66
70
  <!-- Header -->
67
71
  <div class="config-panel__header">
68
72
  <h2 class="config-panel__title">{title}</h2>
69
- <button
70
- class="config-panel__close"
71
- onclick={onClose}
72
- aria-label="Close panel"
73
- >
74
- ×
75
- </button>
73
+ <div class="config-panel__actions">
74
+ {#if onSwap}
75
+ <button
76
+ class="config-panel__action-btn"
77
+ onclick={onSwap}
78
+ aria-label="Swap node"
79
+ title="Swap node type"
80
+ >
81
+ <Icon icon="heroicons:arrows-right-left" />
82
+ </button>
83
+ {/if}
84
+ <button
85
+ class="config-panel__close"
86
+ onclick={onClose}
87
+ aria-label="Close panel"
88
+ >
89
+ ×
90
+ </button>
91
+ </div>
76
92
  </div>
77
93
 
78
94
  <!-- Details Section (between header and content) -->
@@ -118,6 +134,34 @@
118
134
  color: var(--fd-foreground);
119
135
  }
120
136
 
137
+ .config-panel__actions {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 0.25rem;
141
+ }
142
+
143
+ .config-panel__action-btn {
144
+ background: none;
145
+ border: none;
146
+ font-size: 1rem;
147
+ line-height: 1;
148
+ cursor: pointer;
149
+ color: var(--fd-muted-foreground);
150
+ padding: 0.25rem;
151
+ border-radius: var(--fd-radius-sm);
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ transition:
156
+ color var(--fd-transition-fast),
157
+ background-color var(--fd-transition-fast);
158
+ }
159
+
160
+ .config-panel__action-btn:hover {
161
+ color: var(--fd-primary);
162
+ background-color: var(--fd-subtle);
163
+ }
164
+
121
165
  .config-panel__close {
122
166
  background: none;
123
167
  border: none;
@@ -179,6 +223,11 @@
179
223
  padding: 0.125rem;
180
224
  }
181
225
 
226
+ .config-panel--compact .config-panel__action-btn {
227
+ font-size: 0.875rem;
228
+ padding: 0.125rem;
229
+ }
230
+
182
231
  .config-panel--compact .config-panel__details {
183
232
  padding: 0.5rem 0.75rem;
184
233
  }
@@ -24,6 +24,8 @@ interface Props {
24
24
  configTitle?: string;
25
25
  /** Callback function when the panel is closed */
26
26
  onClose: () => void;
27
+ /** Optional callback to initiate node swap — when provided, shows swap button */
28
+ onSwap?: () => void;
27
29
  /** Slot content for the configuration form */
28
30
  children?: Snippet;
29
31
  }
@@ -90,16 +90,8 @@
90
90
  const adjX = targetX - Math.cos(angleRad) * ARROW_LENGTH_PX;
91
91
  const adjY = targetY - Math.sin(angleRad) * ARROW_LENGTH_PX;
92
92
 
93
- // 5. Recompute the bezier path with the shortened target
94
- const [shortenedPath] = getBezierPath({
95
- sourceX,
96
- sourceY,
97
- targetX: adjX,
98
- targetY: adjY,
99
- sourcePosition,
100
- targetPosition,
101
- curvature: pathOptions?.curvature,
102
- });
93
+ // 5. Build shortened path from existing control points — no second getBezierPath() call
94
+ const shortenedPath = `M ${cp.p0x},${cp.p0y} C ${cp.p1x},${cp.p1y} ${cp.p2x},${cp.p2y} ${adjX},${adjY}`;
103
95
 
104
96
  return { path: shortenedPath, labelX: lx, labelY: ly, angleDeg };
105
97
  });
@@ -114,7 +114,7 @@
114
114
  /**
115
115
  * Filter logs by selected node
116
116
  */
117
- let filteredLogs = $derived(() => {
117
+ let filteredLogs = $derived.by(() => {
118
118
  if (props.selectedNode) {
119
119
  return props.logs.filter((log) => log.nodeId === props.selectedNode?.id);
120
120
  }
@@ -132,7 +132,7 @@
132
132
  * Export logs
133
133
  */
134
134
  function exportLogs(): void {
135
- const logText = filteredLogs()
135
+ const logText = filteredLogs
136
136
  .map(
137
137
  (log) =>
138
138
  `[${formatTimestamp(log.timestamp)}] ${log.level.toUpperCase()}: ${log.message}${log.nodeId ? ` (Node: ${log.nodeId})` : ""}`,
@@ -198,9 +198,9 @@
198
198
  <!-- Content -->
199
199
  <div class="logs-sidebar__content">
200
200
  <!-- Logs List -->
201
- {#if filteredLogs().length > 0}
201
+ {#if filteredLogs.length > 0}
202
202
  <div class="logs-sidebar__logs" bind:this={logsContainer}>
203
- {#each filteredLogs() as log, index (index)}
203
+ {#each filteredLogs as log, index (index)}
204
204
  <div
205
205
  class="logs-sidebar__log-entry"
206
206
  class:logs-sidebar__log-entry--error={log.level === "error"}
@@ -276,7 +276,7 @@
276
276
 
277
277
  {#if props.logs.length > 0}
278
278
  <p class="logs-sidebar__info-text">
279
- {filteredLogs().length} of {props.logs.length} log entries
279
+ {filteredLogs.length} of {props.logs.length} log entries
280
280
  </p>
281
281
  {/if}
282
282
  </div>
@@ -15,8 +15,8 @@
15
15
  import { getNodeIcon, getCategoryIcon } from "../utils/icons.js";
16
16
  import { getCategoryColorToken } from "../utils/colors.js";
17
17
  import { getCategoryLabel } from "../stores/categoriesStore.svelte.js";
18
- import { SvelteSet } from "svelte/reactivity";
19
18
  import { getUiSettings } from "../stores/settingsStore.svelte.js";
19
+ import { extractConfigDefaults } from "../utils/nodeIds.js";
20
20
 
21
21
  interface Props {
22
22
  nodes: NodeMetadata[];
@@ -54,6 +54,17 @@
54
54
  let filteredNodes = $derived(getFilteredNodes());
55
55
  let categories = $derived(getCategories());
56
56
 
57
+ /** Group already-filtered results by category in a single pass */
58
+ let filteredNodesByCategory = $derived.by(() => {
59
+ const map = new Map<string, NodeMetadata[]>();
60
+ for (const node of filteredNodes) {
61
+ let list = map.get(node.category);
62
+ if (!list) { list = []; map.set(node.category, list); }
63
+ list.push(node);
64
+ }
65
+ return map;
66
+ });
67
+
57
68
  /**
58
69
  * Get all unique categories from node types, preserving API order
59
70
  * Categories appear in the order their first node appears in the API response
@@ -61,7 +72,7 @@
61
72
  function getCategories(): NodeCategory[] {
62
73
  if (formatCompatibleNodes.length === 0) return [];
63
74
  // Use a Set to track uniqueness while preserving insertion order
64
- const seen = new SvelteSet<NodeCategory>();
75
+ const seen = new Set<NodeCategory>();
65
76
  const orderedCategories: NodeCategory[] = [];
66
77
  for (const node of formatCompatibleNodes) {
67
78
  if (!seen.has(node.category)) {
@@ -106,31 +117,13 @@
106
117
  function handleNodeDragStart(event: DragEvent, nodeType: NodeMetadata): void {
107
118
  if (!event.dataTransfer) return;
108
119
 
109
- // Extract initial config from configSchema with proper null checks
110
- let initialConfig: Record<string, unknown> = {};
111
- if (
112
- nodeType.configSchema &&
113
- typeof nodeType.configSchema === "object" &&
114
- nodeType.configSchema.properties &&
115
- typeof nodeType.configSchema.properties === "object"
116
- ) {
117
- // JSON Schema format - extract defaults
118
- Object.entries(nodeType.configSchema.properties).forEach(
119
- ([key, prop]) => {
120
- if (prop && typeof prop === "object" && "default" in prop) {
121
- initialConfig[key] = prop.default;
122
- }
123
- },
124
- );
125
- }
126
-
127
120
  // Create a new node instance from the node type
128
121
  const newNodeData = {
129
122
  type: "node",
130
123
  nodeType: nodeType.id,
131
124
  nodeData: {
132
125
  label: nodeType.name,
133
- config: initialConfig,
126
+ config: extractConfigDefaults(nodeType.configSchema),
134
127
  metadata: nodeType,
135
128
  },
136
129
  };
@@ -175,33 +168,6 @@
175
168
  return getCategoryLabel(category);
176
169
  }
177
170
 
178
- /**
179
- * Get node types for category
180
- * Preserves the API order - no client-side sorting applied
181
- */
182
- function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
183
- return formatCompatibleNodes.filter((node) => node.category === category);
184
- }
185
-
186
- /**
187
- * Get filtered nodes for category
188
- */
189
- function getFilteredNodesForCategory(category: NodeCategory): NodeMetadata[] {
190
- let nodes = getNodesForCategory(category);
191
-
192
- // Filter by search query
193
- if (searchInput.trim()) {
194
- const query = searchInput.toLowerCase();
195
- nodes = nodes.filter(
196
- (node) =>
197
- node.name.toLowerCase().includes(query) ||
198
- node.description.toLowerCase().includes(query) ||
199
- node.tags?.some((tag) => tag.toLowerCase().includes(query)),
200
- );
201
- }
202
-
203
- return nodes;
204
- }
205
171
  </script>
206
172
 
207
173
  <!-- Components Sidebar -->
@@ -356,7 +322,7 @@
356
322
  <!-- Category-specific details -->
357
323
  <div class="flowdrop-category-list">
358
324
  {#each categories as category (category)}
359
- {@const categoryNodes = getFilteredNodesForCategory(category)}
325
+ {@const categoryNodes = filteredNodesByCategory.get(category) ?? []}
360
326
  {#if categoryNodes.length > 0}
361
327
  <!-- Flat style: label + dot+name rows (shown/hidden via CSS token) -->
362
328
  <div class="fd-sidebar-flat-section">