@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
@@ -34,21 +34,23 @@ function buildMetadata(existing, updates) {
34
34
  /** Global workflow state */
35
35
  let workflowState = $state(null);
36
36
  // =========================================================================
37
- // Dirty State Tracking
37
+ // Dirty State Tracking (Version Counter)
38
38
  // =========================================================================
39
39
  /**
40
- * State for tracking if there are unsaved changes
40
+ * Monotonic edit version bumps on every mutation action.
41
41
  *
42
- * This is set to true whenever the workflow changes after initialization.
43
- * It can be reset to false by calling markAsSaved().
42
+ * Used for two purposes:
43
+ * 1. O(1) dirty detection: isDirty = _editVersion !== _savedVersion
44
+ * 2. Save verification protocol: include the version in the save request,
45
+ * backend echoes it back. If the echoed version doesn't match, the save
46
+ * didn't persist the version the client submitted.
44
47
  */
45
- let isDirtyState = $state(false);
48
+ let _editVersion = $state(0);
46
49
  /**
47
- * Snapshot of the workflow when it was last saved
48
- *
49
- * Used to compare current state with saved state.
50
+ * Edit version captured at the last successful save.
51
+ * When _editVersion === _savedVersion, the workflow is clean.
50
52
  */
51
- let savedSnapshot = null;
53
+ let _savedVersion = $state(0);
52
54
  /**
53
55
  * Callback for dirty state changes
54
56
  *
@@ -87,10 +89,14 @@ export function getWorkflowStore() {
87
89
  /**
88
90
  * Get the current dirty state reactively
89
91
  *
92
+ * Reads both _editVersion and _savedVersion, so Svelte tracks them
93
+ * as reactive dependencies — any component using this will re-render
94
+ * when dirty state changes.
95
+ *
90
96
  * @returns true if there are unsaved changes
91
97
  */
92
98
  export function getIsDirty() {
93
- return isDirtyState;
99
+ return _editVersion !== _savedVersion;
94
100
  }
95
101
  /**
96
102
  * Get the workflow ID reactively
@@ -239,53 +245,21 @@ export function setOnWorkflowChange(callback) {
239
245
  // Internal Helpers
240
246
  // =========================================================================
241
247
  /**
242
- * Create a snapshot of the workflow for comparison
243
- *
244
- * @param workflow - The workflow to snapshot
245
- * @returns A JSON string representation for comparison
246
- */
247
- function createSnapshot(workflow) {
248
- if (!workflow)
249
- return null;
250
- // Only include the parts that matter for "dirty" detection
251
- const toSnapshot = {
252
- name: workflow.name,
253
- description: workflow.description,
254
- nodes: workflow.nodes.map((n) => ({
255
- id: n.id,
256
- position: n.position,
257
- data: {
258
- label: n.data.label,
259
- config: n.data.config,
260
- },
261
- })),
262
- edges: workflow.edges.map((e) => ({
263
- id: e.id,
264
- source: e.source,
265
- target: e.target,
266
- sourceHandle: e.sourceHandle,
267
- targetHandle: e.targetHandle,
268
- })),
269
- };
270
- return JSON.stringify(toSnapshot);
271
- }
272
- /**
273
- * Update dirty state based on current workflow
274
- *
275
- * Compares current workflow with saved snapshot.
248
+ * Bump the edit version and notify dirty state change if needed.
249
+ * Called by every mutation action.
276
250
  */
277
- function updateDirtyState() {
278
- const currentSnapshot = createSnapshot(workflowState);
279
- const newIsDirty = currentSnapshot !== savedSnapshot;
280
- if (newIsDirty !== isDirtyState) {
281
- isDirtyState = newIsDirty;
251
+ function bumpVersion() {
252
+ _editVersion++;
253
+ // Dirty state just flipped from clean → dirty
254
+ if (_editVersion - 1 === _savedVersion) {
282
255
  if (onDirtyStateChangeCallback) {
283
- onDirtyStateChangeCallback(newIsDirty);
256
+ onDirtyStateChangeCallback(true);
284
257
  }
285
258
  }
286
259
  }
287
260
  /**
288
- * Mark the workflow change and update dirty state
261
+ * Notify external listeners of a workflow change.
262
+ * Does NOT bump the version — callers that mutate must call bumpVersion() explicitly.
289
263
  *
290
264
  * @param changeType - The type of change that occurred
291
265
  */
@@ -293,17 +267,17 @@ function notifyWorkflowChange(changeType) {
293
267
  if (workflowState && onWorkflowChangeCallback) {
294
268
  onWorkflowChangeCallback(workflowState, changeType);
295
269
  }
296
- updateDirtyState();
297
270
  }
298
271
  /**
299
- * Mark the current workflow state as saved
272
+ * Mark the current workflow state as saved.
300
273
  *
301
- * Clears the dirty state by updating the saved snapshot.
274
+ * Captures the current edit version so isDirty becomes false.
275
+ * Call this after a successful backend save.
302
276
  */
303
277
  export function markAsSaved() {
304
- savedSnapshot = createSnapshot(workflowState);
305
- isDirtyState = false;
306
- if (onDirtyStateChangeCallback) {
278
+ const wasDirty = _editVersion !== _savedVersion;
279
+ _savedVersion = _editVersion;
280
+ if (wasDirty && onDirtyStateChangeCallback) {
307
281
  onDirtyStateChangeCallback(false);
308
282
  }
309
283
  }
@@ -313,7 +287,22 @@ export function markAsSaved() {
313
287
  * @returns true if there are unsaved changes
314
288
  */
315
289
  export function isDirty() {
316
- return isDirtyState;
290
+ return _editVersion !== _savedVersion;
291
+ }
292
+ /**
293
+ * Get the current edit version.
294
+ *
295
+ * Use this for the save verification protocol:
296
+ * 1. Before save: `const v = getEditVersion()`
297
+ * 2. Include `v` in the save request payload
298
+ * 3. Backend echoes `v` in the response
299
+ * 4. If echoed version matches: `markAsSaved()`
300
+ * 5. If not: the save didn't persist the version you submitted — reset from backend response
301
+ *
302
+ * @returns The current monotonic edit version
303
+ */
304
+ export function getEditVersion() {
305
+ return _editVersion;
317
306
  }
318
307
  /**
319
308
  * Enable or disable history recording
@@ -390,7 +379,7 @@ function hasWorkflowDataChanged(currentWorkflow, newNodes, newEdges) {
390
379
  if (currentNode.position.x !== newNode.position.x ||
391
380
  currentNode.position.y !== newNode.position.y)
392
381
  return true;
393
- if (JSON.stringify(currentNode.data) !== JSON.stringify(newNode.data))
382
+ if (currentNode.data !== newNode.data)
394
383
  return true;
395
384
  }
396
385
  // Check if edges have changed
@@ -426,9 +415,9 @@ export const workflowActions = {
426
415
  */
427
416
  initialize: (workflow) => {
428
417
  workflowState = workflow;
429
- // Set the saved snapshot - workflow is "clean" after initialization
430
- savedSnapshot = createSnapshot(workflow);
431
- isDirtyState = false;
418
+ // Reset version counters workflow is "clean" after initialization
419
+ _editVersion = 0;
420
+ _savedVersion = 0;
432
421
  if (onDirtyStateChangeCallback) {
433
422
  onDirtyStateChangeCallback(false);
434
423
  }
@@ -443,6 +432,7 @@ export const workflowActions = {
443
432
  */
444
433
  updateWorkflow: (workflow) => {
445
434
  workflowState = workflow;
435
+ bumpVersion();
446
436
  notifyWorkflowChange("metadata");
447
437
  },
448
438
  /**
@@ -453,6 +443,7 @@ export const workflowActions = {
453
443
  restoreFromHistory: (workflow) => {
454
444
  isRestoringFromHistory = true;
455
445
  workflowState = workflow;
446
+ bumpVersion();
456
447
  notifyWorkflowChange("metadata");
457
448
  isRestoringFromHistory = false;
458
449
  },
@@ -476,6 +467,7 @@ export const workflowActions = {
476
467
  updateNumber: (workflowState.metadata?.updateNumber ?? 0) + 1,
477
468
  }),
478
469
  };
470
+ bumpVersion();
479
471
  notifyWorkflowChange("node_move");
480
472
  },
481
473
  /**
@@ -498,6 +490,7 @@ export const workflowActions = {
498
490
  updateNumber: (workflowState.metadata?.updateNumber ?? 0) + 1,
499
491
  }),
500
492
  };
493
+ bumpVersion();
501
494
  notifyWorkflowChange("edge_add");
502
495
  },
503
496
  /**
@@ -511,6 +504,7 @@ export const workflowActions = {
511
504
  name,
512
505
  metadata: buildMetadata(workflowState.metadata),
513
506
  };
507
+ bumpVersion();
514
508
  notifyWorkflowChange("name");
515
509
  },
516
510
  /**
@@ -525,6 +519,7 @@ export const workflowActions = {
525
519
  nodes: [...workflowState.nodes, node],
526
520
  metadata: buildMetadata(workflowState.metadata),
527
521
  };
522
+ bumpVersion();
528
523
  notifyWorkflowChange("node_add");
529
524
  },
530
525
  /**
@@ -543,6 +538,7 @@ export const workflowActions = {
543
538
  edges: workflowState.edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId),
544
539
  metadata: buildMetadata(workflowState.metadata),
545
540
  };
541
+ bumpVersion();
546
542
  notifyWorkflowChange("node_remove");
547
543
  },
548
544
  /**
@@ -557,6 +553,7 @@ export const workflowActions = {
557
553
  edges: [...workflowState.edges, edge],
558
554
  metadata: buildMetadata(workflowState.metadata),
559
555
  };
556
+ bumpVersion();
560
557
  notifyWorkflowChange("edge_add");
561
558
  },
562
559
  /**
@@ -571,6 +568,7 @@ export const workflowActions = {
571
568
  edges: workflowState.edges.filter((edge) => edge.id !== edgeId),
572
569
  metadata: buildMetadata(workflowState.metadata),
573
570
  };
571
+ bumpVersion();
574
572
  notifyWorkflowChange("edge_remove");
575
573
  },
576
574
  /**
@@ -587,6 +585,7 @@ export const workflowActions = {
587
585
  nodes: workflowState.nodes.map((node) => node.id === nodeId ? { ...node, ...updates } : node),
588
586
  metadata: buildMetadata(workflowState.metadata),
589
587
  };
588
+ bumpVersion();
590
589
  notifyWorkflowChange("node_config");
591
590
  },
592
591
  /**
@@ -596,8 +595,8 @@ export const workflowActions = {
596
595
  */
597
596
  clear: () => {
598
597
  workflowState = null;
599
- savedSnapshot = null;
600
- isDirtyState = false;
598
+ _editVersion = 0;
599
+ _savedVersion = 0;
601
600
  historyService.clear();
602
601
  if (onDirtyStateChangeCallback) {
603
602
  onDirtyStateChangeCallback(false);
@@ -613,6 +612,7 @@ export const workflowActions = {
613
612
  ...workflowState,
614
613
  metadata: buildMetadata(workflowState.metadata, metadata),
615
614
  };
615
+ bumpVersion();
616
616
  notifyWorkflowChange("metadata");
617
617
  },
618
618
  /**
@@ -635,8 +635,28 @@ export const workflowActions = {
635
635
  }),
636
636
  metadata: buildMetadata(workflowState.metadata, updates.metadata ?? undefined),
637
637
  };
638
+ bumpVersion();
638
639
  notifyWorkflowChange("metadata");
639
640
  },
641
+ /**
642
+ * Swap a node — atomically replaces nodes and edges with a descriptive history entry.
643
+ *
644
+ * Unlike batchUpdate, this uses `"node_swap"` as the change type and
645
+ * records a meaningful description for the undo history.
646
+ */
647
+ swapNode: (updates) => {
648
+ pushToHistory(updates.description ?? "Swap node");
649
+ if (!workflowState)
650
+ return;
651
+ workflowState = {
652
+ ...workflowState,
653
+ nodes: updates.nodes,
654
+ edges: updates.edges,
655
+ metadata: buildMetadata(workflowState.metadata),
656
+ };
657
+ bumpVersion();
658
+ notifyWorkflowChange("node_swap");
659
+ },
640
660
  /**
641
661
  * Push current state to history manually
642
662
  *
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Chat Types for FlowDrop LLM Chat Interface
3
+ *
4
+ * Provides type definitions for the chat panel's communication
5
+ * with backend LLM integrations.
6
+ *
7
+ * @module types/chat
8
+ */
9
+ /**
10
+ * Role for chat history messages
11
+ */
12
+ export type ChatMessageRole = "user" | "assistant";
13
+ /**
14
+ * A single message in the chat history
15
+ */
16
+ export interface ChatHistoryMessage {
17
+ role: ChatMessageRole;
18
+ content: string;
19
+ }
20
+ /**
21
+ * Request payload sent to the chat endpoint
22
+ */
23
+ export interface ChatRequest {
24
+ /** The user's natural language message */
25
+ message: string;
26
+ /** Serialized current workflow state (nodes + edges) */
27
+ workflowState: unknown;
28
+ /** Optional conversation history for context */
29
+ history?: ChatHistoryMessage[];
30
+ }
31
+ /**
32
+ * Response payload from the chat endpoint
33
+ */
34
+ export interface ChatResponse {
35
+ /** The LLM's response content (may contain markdown and code blocks) */
36
+ content: string;
37
+ /** Optional conversation ID for backend session tracking */
38
+ conversationId?: string;
39
+ }
40
+ /**
41
+ * Result of parsing an LLM response to extract DSL commands
42
+ */
43
+ export interface ExtractedCommands {
44
+ /** The full explanation text (content outside code blocks) */
45
+ explanation: string;
46
+ /** Extracted DSL command strings */
47
+ commands: string[];
48
+ }
49
+ /**
50
+ * Status of a single command in the preview
51
+ */
52
+ export type CommandExecutionStatus = "pending" | "executing" | "success" | "error";
53
+ /**
54
+ * A single command shown in the command preview UI
55
+ */
56
+ export interface CommandPreviewItem {
57
+ /** The raw DSL command string */
58
+ raw: string;
59
+ /** Current execution status */
60
+ status: CommandExecutionStatus;
61
+ /** Optional result or error message after execution */
62
+ result?: string;
63
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Chat Types for FlowDrop LLM Chat Interface
3
+ *
4
+ * Provides type definitions for the chat panel's communication
5
+ * with backend LLM integrations.
6
+ *
7
+ * @module types/chat
8
+ */
9
+ export {};
@@ -6,13 +6,14 @@
6
6
  *
7
7
  * @module types/events
8
8
  */
9
- import type { Workflow, NodeExecutionInfo } from "./index.js";
9
+ import type { Workflow, NodeExecutionInfo, WorkflowNode } from "./index.js";
10
+ import type { SwapEventContext, SwapResult } from "../utils/nodeSwap.js";
10
11
  /**
11
12
  * Types of workflow changes
12
13
  *
13
14
  * Used to identify what kind of change triggered the onWorkflowChange event.
14
15
  */
15
- export type WorkflowChangeType = "node_add" | "node_remove" | "node_move" | "node_config" | "edge_add" | "edge_remove" | "metadata" | "name" | "description";
16
+ export type WorkflowChangeType = "node_add" | "node_remove" | "node_move" | "node_config" | "node_swap" | "edge_add" | "edge_remove" | "metadata" | "name" | "description";
16
17
  /**
17
18
  * High-level event handlers for enterprise integration
18
19
  *
@@ -113,6 +114,22 @@ export interface FlowDropEventHandlers {
113
114
  * @returns true to suppress default error handling, false/void to show default toast
114
115
  */
115
116
  onApiError?: (error: Error, operation: string) => boolean | void;
117
+ /**
118
+ * Called before a node swap is executed.
119
+ * Return false to cancel the swap.
120
+ *
121
+ * @param context - Data-only swap context (oldNode, newMetadata, preview, overrides)
122
+ * @returns false to cancel, true/void to proceed
123
+ */
124
+ onBeforeSwap?: (context: SwapEventContext) => boolean | void | Promise<boolean | void>;
125
+ /**
126
+ * Called after a node swap is successfully executed.
127
+ *
128
+ * @param result - The swap result with updated nodes/edges
129
+ * @param oldNode - The node that was replaced
130
+ * @param newNodeId - The ID of the newly created node
131
+ */
132
+ onAfterSwap?: (result: SwapResult, oldNode: WorkflowNode, newNodeId: string) => void;
116
133
  /**
117
134
  * Called when an Agent Spec execution starts
118
135
  *
@@ -174,6 +191,15 @@ export interface FlowDropFeatures {
174
191
  * @default true
175
192
  */
176
193
  showToasts?: boolean;
194
+ /**
195
+ * Enable node swap functionality
196
+ *
197
+ * When enabled, users can swap a node for a different type
198
+ * while preserving compatible connections and configuration.
199
+ *
200
+ * @default true
201
+ */
202
+ enableNodeSwap?: boolean;
177
203
  }
178
204
  /**
179
205
  * Default feature values
@@ -15,6 +15,7 @@ export const DEFAULT_FEATURES = {
15
15
  autoSaveDraft: true,
16
16
  autoSaveDraftInterval: 30000,
17
17
  showToasts: true,
18
+ enableNodeSwap: true,
18
19
  };
19
20
  /**
20
21
  * Merge user-provided features with defaults
@@ -535,6 +535,14 @@ export interface NodeExtensions {
535
535
  * Allows overriding the node type's configEdit settings for specific instances
536
536
  */
537
537
  configEdit?: ConfigEditOptions;
538
+ /**
539
+ * Set when this node was created by swapping another node.
540
+ * Stores the immediate predecessor only — not a full chain.
541
+ * Sequential swaps (A→B→C) each store only their direct predecessor.
542
+ */
543
+ swap?: {
544
+ previousNodeId: string;
545
+ };
538
546
  /**
539
547
  * Namespaced extension data from 3rd party integrations
540
548
  * Use your package/organization name as the key (e.g., "myapp", "acme:analyzer")
@@ -57,6 +57,12 @@ export interface UISettings {
57
57
  compactMode: boolean;
58
58
  /** Active theme name — overridden by the theme prop when explicitly provided */
59
59
  theme: "default" | "minimal";
60
+ /** Whether the command console panel is open */
61
+ consoleOpen: boolean;
62
+ /** Height of the command console panel in pixels */
63
+ consoleHeight: number;
64
+ /** Active tab in the bottom panel */
65
+ bottomPanelTab: "console" | "chat";
60
66
  }
61
67
  /**
62
68
  * Application behavior and automation settings
@@ -66,6 +66,9 @@ export const DEFAULT_UI_SETTINGS = {
66
66
  sidebarCollapsed: false,
67
67
  compactMode: false,
68
68
  theme: "default",
69
+ consoleOpen: false,
70
+ consoleHeight: 300,
71
+ bottomPanelTab: "console",
69
72
  };
70
73
  /**
71
74
  * Default behavior settings
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Edge Styling Utility
3
+ *
4
+ * Standalone functions for determining edge categories and applying
5
+ * visual styling to workflow edges based on source port data types.
6
+ * Used by both the visual editor and the command DSL system.
7
+ */
8
+ import type { WorkflowNode as WorkflowNodeType, WorkflowEdge, EdgeCategory } from "../types/index.js";
9
+ /**
10
+ * Check if a port ID matches a dynamic branch in a Gateway node.
11
+ * Gateway nodes store branches in config.branches array.
12
+ */
13
+ export declare function isGatewayBranch(node: WorkflowNodeType, portId: string): boolean;
14
+ /**
15
+ * Get the data type of a port from a node's metadata.
16
+ * Also handles dynamic ports like Gateway branches.
17
+ */
18
+ export declare function getPortDataType(node: WorkflowNodeType, portId: string, portType: "input" | "output"): string | null;
19
+ /**
20
+ * Determine the edge category based on source port data type.
21
+ * Note: This does not check for loopback edges — use getEdgeCategoryWithLoopback() for that.
22
+ */
23
+ export declare function getEdgeCategory(sourcePortDataType: string | null): EdgeCategory;
24
+ /**
25
+ * Determine the full edge category including loopback detection.
26
+ * Loopback edges take precedence over source port data type.
27
+ */
28
+ export declare function getEdgeCategoryWithLoopback(edge: WorkflowEdge, sourcePortDataType: string | null): EdgeCategory;
29
+ /**
30
+ * Apply custom styling to a connection edge based on its source/target nodes.
31
+ *
32
+ * Sets:
33
+ * - edge.data.metadata.edgeType (trigger/tool/loopback/data)
34
+ * - edge.style, edge.class, edge.markerEnd based on category
35
+ * - edge.data.targetNodeType and edge.data.targetCategory
36
+ */
37
+ export declare function applyConnectionStyling(edge: WorkflowEdge, sourceNode: WorkflowNodeType, targetNode: WorkflowNodeType): void;
38
+ /**
39
+ * Update existing edges with custom styling rules.
40
+ * Batch operation that applies styling to all edges using a node map for O(1) lookup.
41
+ */
42
+ export declare function updateEdgeStyles(edges: WorkflowEdge[], nodes: WorkflowNodeType[]): WorkflowEdge[];