@flowdrop/flowdrop 1.3.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 (114) 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 +431 -17
  26. package/dist/components/App.svelte.d.ts +10 -0
  27. package/dist/components/CanvasBanner.stories.svelte +6 -2
  28. package/dist/components/CanvasController.svelte +38 -0
  29. package/dist/components/CanvasController.svelte.d.ts +32 -0
  30. package/dist/components/ConfigMappingRow.svelte +130 -0
  31. package/dist/components/ConfigMappingRow.svelte.d.ts +8 -0
  32. package/dist/components/ConfigPanel.svelte +56 -7
  33. package/dist/components/ConfigPanel.svelte.d.ts +2 -0
  34. package/dist/components/FlowDropEdge.svelte +8 -57
  35. package/dist/components/Logo.svelte +14 -14
  36. package/dist/components/LogsSidebar.svelte +5 -5
  37. package/dist/components/Navbar.svelte +58 -10
  38. package/dist/components/Navbar.svelte.d.ts +7 -0
  39. package/dist/components/NodeSidebar.svelte +238 -362
  40. package/dist/components/NodeSwapPicker.svelte +537 -0
  41. package/dist/components/NodeSwapPicker.svelte.d.ts +16 -0
  42. package/dist/components/PortMappingRow.svelte +209 -0
  43. package/dist/components/PortMappingRow.svelte.d.ts +12 -0
  44. package/dist/components/SwapMappingEditor.svelte +550 -0
  45. package/dist/components/SwapMappingEditor.svelte.d.ts +12 -0
  46. package/dist/components/WorkflowEditor.svelte +99 -4
  47. package/dist/components/WorkflowEditor.svelte.d.ts +8 -0
  48. package/dist/components/chat/AIChatPanel.svelte +658 -0
  49. package/dist/components/chat/AIChatPanel.svelte.d.ts +13 -0
  50. package/dist/components/chat/CommandPreview.svelte +184 -0
  51. package/dist/components/chat/CommandPreview.svelte.d.ts +9 -0
  52. package/dist/components/console/CommandConsole.stories.svelte +93 -0
  53. package/dist/components/console/CommandConsole.stories.svelte.d.ts +27 -0
  54. package/dist/components/console/CommandConsole.svelte +259 -0
  55. package/dist/components/console/CommandConsole.svelte.d.ts +11 -0
  56. package/dist/components/console/ConsoleAutocomplete.svelte +139 -0
  57. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +21 -0
  58. package/dist/components/console/ConsoleInput.svelte +712 -0
  59. package/dist/components/console/ConsoleInput.svelte.d.ts +16 -0
  60. package/dist/components/console/ConsoleOutput.svelte +121 -0
  61. package/dist/components/console/ConsoleOutput.svelte.d.ts +11 -0
  62. package/dist/components/console/formatters.d.ts +26 -0
  63. package/dist/components/console/formatters.js +118 -0
  64. package/dist/components/interrupt/index.d.ts +1 -0
  65. package/dist/components/interrupt/index.js +1 -0
  66. package/dist/components/nodes/SimpleNode.stories.svelte +64 -0
  67. package/dist/components/nodes/SimpleNode.svelte +27 -11
  68. package/dist/components/nodes/SquareNode.stories.svelte +45 -0
  69. package/dist/components/nodes/SquareNode.svelte +27 -11
  70. package/dist/components/nodes/WorkflowNode.stories.svelte +63 -0
  71. package/dist/config/endpoints.d.ts +8 -0
  72. package/dist/config/endpoints.js +5 -0
  73. package/dist/core/index.d.ts +5 -0
  74. package/dist/core/index.js +9 -0
  75. package/dist/editor/index.d.ts +3 -1
  76. package/dist/editor/index.js +4 -2
  77. package/dist/helpers/proximityConnect.js +8 -1
  78. package/dist/helpers/workflowEditorHelper.d.ts +3 -53
  79. package/dist/helpers/workflowEditorHelper.js +13 -228
  80. package/dist/playground/index.d.ts +1 -1
  81. package/dist/playground/index.js +1 -1
  82. package/dist/schemas/v1/workflow.schema.json +107 -22
  83. package/dist/services/chatService.d.ts +65 -0
  84. package/dist/services/chatService.js +131 -0
  85. package/dist/services/historyService.d.ts +6 -4
  86. package/dist/services/historyService.js +21 -6
  87. package/dist/skins/slate.js +16 -0
  88. package/dist/stores/interruptStore.svelte.js +6 -1
  89. package/dist/stores/playgroundStore.svelte.d.ts +1 -1
  90. package/dist/stores/playgroundStore.svelte.js +11 -2
  91. package/dist/stores/portCoordinateStore.svelte.d.ts +4 -0
  92. package/dist/stores/portCoordinateStore.svelte.js +20 -26
  93. package/dist/stores/workflowStore.svelte.d.ts +31 -2
  94. package/dist/stores/workflowStore.svelte.js +84 -64
  95. package/dist/stories/EdgeDecorator.svelte +4 -4
  96. package/dist/styles/base.css +48 -0
  97. package/dist/svelte-app.d.ts +7 -1
  98. package/dist/svelte-app.js +4 -1
  99. package/dist/types/chat.d.ts +63 -0
  100. package/dist/types/chat.js +9 -0
  101. package/dist/types/events.d.ts +28 -2
  102. package/dist/types/events.js +1 -0
  103. package/dist/types/index.d.ts +8 -0
  104. package/dist/types/settings.d.ts +6 -0
  105. package/dist/types/settings.js +3 -0
  106. package/dist/utils/edgeStyling.d.ts +42 -0
  107. package/dist/utils/edgeStyling.js +176 -0
  108. package/dist/utils/nodeIds.d.ts +31 -0
  109. package/dist/utils/nodeIds.js +42 -0
  110. package/dist/utils/nodeSwap.d.ts +221 -0
  111. package/dist/utils/nodeSwap.js +686 -0
  112. package/package.json +6 -1
  113. package/dist/helpers/nodeLayoutHelper.d.ts +0 -14
  114. package/dist/helpers/nodeLayoutHelper.js +0 -19
@@ -38,7 +38,13 @@
38
38
  "$ref": "#/$defs/WorkflowMetadata"
39
39
  }
40
40
  },
41
- "required": ["id", "name", "nodes", "edges", "metadata"],
41
+ "required": [
42
+ "id",
43
+ "name",
44
+ "nodes",
45
+ "edges",
46
+ "metadata"
47
+ ],
42
48
  "$defs": {
43
49
  "AutocompleteConfig": {
44
50
  "type": "object",
@@ -89,7 +95,9 @@
89
95
  "description": "Whether to allow multiple selections.\nWhen true, users can select multiple values displayed as tags.\n"
90
96
  }
91
97
  },
92
- "required": ["url"]
98
+ "required": [
99
+ "url"
100
+ ]
93
101
  },
94
102
  "Branch": {
95
103
  "type": "object",
@@ -117,7 +125,10 @@
117
125
  "description": "Whether this is the default/fallback branch"
118
126
  }
119
127
  },
120
- "required": ["name", "label"]
128
+ "required": [
129
+ "name",
130
+ "label"
131
+ ]
121
132
  },
122
133
  "ConfigProperty": {
123
134
  "type": "object",
@@ -125,7 +136,14 @@
125
136
  "properties": {
126
137
  "type": {
127
138
  "type": "string",
128
- "enum": ["string", "number", "boolean", "array", "object", "integer"],
139
+ "enum": [
140
+ "string",
141
+ "number",
142
+ "boolean",
143
+ "array",
144
+ "object",
145
+ "integer"
146
+ ],
129
147
  "description": "JSON Schema type"
130
148
  },
131
149
  "title": {
@@ -251,14 +269,18 @@
251
269
  ]
252
270
  }
253
271
  },
254
- "required": ["type"]
272
+ "required": [
273
+ "type"
274
+ ]
255
275
  },
256
276
  "ConfigSchema": {
257
277
  "type": "object",
258
278
  "properties": {
259
279
  "type": {
260
280
  "type": "string",
261
- "enum": ["object"]
281
+ "enum": [
282
+ "object"
283
+ ]
262
284
  },
263
285
  "properties": {
264
286
  "type": "object",
@@ -279,7 +301,10 @@
279
301
  "default": false
280
302
  }
281
303
  },
282
- "required": ["type", "properties"]
304
+ "required": [
305
+ "type",
306
+ "properties"
307
+ ]
283
308
  },
284
309
  "DynamicPort": {
285
310
  "type": "object",
@@ -313,7 +338,11 @@
313
338
  "description": "Whether this port is required for execution"
314
339
  }
315
340
  },
316
- "required": ["name", "label", "dataType"]
341
+ "required": [
342
+ "name",
343
+ "label",
344
+ "dataType"
345
+ ]
317
346
  },
318
347
  "NodeCategory": {
319
348
  "type": "string",
@@ -404,7 +433,11 @@
404
433
  "description": "Whether the node is currently being executed"
405
434
  }
406
435
  },
407
- "required": ["status", "executionCount", "isExecuting"]
436
+ "required": [
437
+ "status",
438
+ "executionCount",
439
+ "isExecuting"
440
+ ]
408
441
  },
409
442
  "NodeExecutionStatus": {
410
443
  "type": "string",
@@ -548,7 +581,11 @@
548
581
  },
549
582
  "type": {
550
583
  "type": "string",
551
- "enum": ["input", "output", "metadata"],
584
+ "enum": [
585
+ "input",
586
+ "output",
587
+ "metadata"
588
+ ],
552
589
  "description": "Port direction (input receives data, output sends data)"
553
590
  },
554
591
  "dataType": {
@@ -577,7 +614,12 @@
577
614
  ]
578
615
  }
579
616
  },
580
- "required": ["id", "name", "type", "dataType"]
617
+ "required": [
618
+ "id",
619
+ "name",
620
+ "type",
621
+ "dataType"
622
+ ]
581
623
  },
582
624
  "NodeType": {
583
625
  "type": "string",
@@ -635,7 +677,9 @@
635
677
  "description": "Optional description for this option"
636
678
  }
637
679
  },
638
- "required": ["const"]
680
+ "required": [
681
+ "const"
682
+ ]
639
683
  },
640
684
  "PortDataSchema": {
641
685
  "type": "object",
@@ -643,7 +687,14 @@
643
687
  "properties": {
644
688
  "type": {
645
689
  "type": "string",
646
- "enum": ["object", "array", "string", "number", "integer", "boolean"],
690
+ "enum": [
691
+ "object",
692
+ "array",
693
+ "string",
694
+ "number",
695
+ "integer",
696
+ "boolean"
697
+ ],
647
698
  "description": "The JSON Schema type. Use `object` for nested properties,\n`array` for lists with `items` schema.\n"
648
699
  },
649
700
  "title": {
@@ -692,7 +743,10 @@
692
743
  "description": "Y coordinate"
693
744
  }
694
745
  },
695
- "required": ["x", "y"]
746
+ "required": [
747
+ "x",
748
+ "y"
749
+ ]
696
750
  },
697
751
  "TemplateVariable": {
698
752
  "type": "object",
@@ -750,7 +804,10 @@
750
804
  "description": "Source node ID"
751
805
  }
752
806
  },
753
- "required": ["name", "type"]
807
+ "required": [
808
+ "name",
809
+ "type"
810
+ ]
754
811
  },
755
812
  "TemplateVariablesConfig": {
756
813
  "type": "object",
@@ -797,7 +854,9 @@
797
854
  "description": "Map of available variables keyed by variable name"
798
855
  }
799
856
  },
800
- "required": ["variables"]
857
+ "required": [
858
+ "variables"
859
+ ]
801
860
  },
802
861
  "WorkflowEdge": {
803
862
  "type": "object",
@@ -828,7 +887,12 @@
828
887
  "type": {
829
888
  "type": "string",
830
889
  "description": "Connection line type",
831
- "enum": ["default", "straight", "step", "smoothstep"]
890
+ "enum": [
891
+ "default",
892
+ "straight",
893
+ "step",
894
+ "smoothstep"
895
+ ]
832
896
  },
833
897
  "selectable": {
834
898
  "type": "boolean",
@@ -855,7 +919,11 @@
855
919
  "properties": {
856
920
  "edgeType": {
857
921
  "type": "string",
858
- "enum": ["trigger", "tool", "data"],
922
+ "enum": [
923
+ "trigger",
924
+ "tool",
925
+ "data"
926
+ ],
859
927
  "description": "Edge type for visual styling"
860
928
  },
861
929
  "sourcePortDataType": {
@@ -879,7 +947,11 @@
879
947
  }
880
948
  }
881
949
  },
882
- "required": ["id", "source", "target"]
950
+ "required": [
951
+ "id",
952
+ "source",
953
+ "target"
954
+ ]
883
955
  },
884
956
  "WorkflowMetadata": {
885
957
  "type": "object",
@@ -923,7 +995,11 @@
923
995
  "default": "flowdrop"
924
996
  }
925
997
  },
926
- "required": ["version", "createdAt", "updatedAt"]
998
+ "required": [
999
+ "version",
1000
+ "createdAt",
1001
+ "updatedAt"
1002
+ ]
927
1003
  },
928
1004
  "WorkflowNode": {
929
1005
  "type": "object",
@@ -984,10 +1060,19 @@
984
1060
  ]
985
1061
  }
986
1062
  },
987
- "required": ["label", "config", "metadata"]
1063
+ "required": [
1064
+ "label",
1065
+ "config",
1066
+ "metadata"
1067
+ ]
988
1068
  }
989
1069
  },
990
- "required": ["id", "type", "position", "data"]
1070
+ "required": [
1071
+ "id",
1072
+ "type",
1073
+ "position",
1074
+ "data"
1075
+ ]
991
1076
  }
992
1077
  }
993
1078
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Chat Service
3
+ *
4
+ * Handles API interactions for the LLM Chat feature including
5
+ * sending messages, retrieving history, and clearing history.
6
+ *
7
+ * @module services/chatService
8
+ */
9
+ import type { ChatRequest, ChatResponse, ChatHistoryMessage } from "../types/chat.js";
10
+ /**
11
+ * Chat Service class
12
+ *
13
+ * Provides methods to interact with the chat API endpoints
14
+ * for LLM-powered workflow building assistance.
15
+ */
16
+ export declare class ChatService {
17
+ private static instance;
18
+ private constructor();
19
+ /**
20
+ * Get the singleton instance of ChatService
21
+ *
22
+ * @returns The ChatService singleton instance
23
+ */
24
+ static getInstance(): ChatService;
25
+ /**
26
+ * Get the endpoint configuration
27
+ *
28
+ * @throws Error if endpoint configuration is not set
29
+ * @returns The endpoint configuration
30
+ */
31
+ private getConfig;
32
+ /**
33
+ * Generic API request helper
34
+ *
35
+ * @param url - The URL to fetch
36
+ * @param options - Fetch options
37
+ * @returns The parsed JSON response
38
+ */
39
+ private request;
40
+ /**
41
+ * Send a message to the chat endpoint
42
+ *
43
+ * @param workflowId - The workflow ID
44
+ * @param request - The chat request payload
45
+ * @returns The chat response from the LLM
46
+ */
47
+ sendMessage(workflowId: string, request: ChatRequest): Promise<ChatResponse>;
48
+ /**
49
+ * Get conversation history for a workflow
50
+ *
51
+ * @param workflowId - The workflow ID
52
+ * @returns Array of chat history messages
53
+ */
54
+ getHistory(workflowId: string): Promise<ChatHistoryMessage[]>;
55
+ /**
56
+ * Clear conversation history for a workflow
57
+ *
58
+ * @param workflowId - The workflow ID
59
+ */
60
+ clearHistory(workflowId: string): Promise<void>;
61
+ }
62
+ /**
63
+ * Pre-instantiated ChatService singleton
64
+ */
65
+ export declare const chatService: ChatService;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Chat Service
3
+ *
4
+ * Handles API interactions for the LLM Chat feature including
5
+ * sending messages, retrieving history, and clearing history.
6
+ *
7
+ * @module services/chatService
8
+ */
9
+ import { buildEndpointUrl, getEndpointHeaders } from "../config/endpoints.js";
10
+ import { getEndpointConfig } from "./api.js";
11
+ import { logger } from "../utils/logger.js";
12
+ /**
13
+ * Chat Service class
14
+ *
15
+ * Provides methods to interact with the chat API endpoints
16
+ * for LLM-powered workflow building assistance.
17
+ */
18
+ export class ChatService {
19
+ static instance;
20
+ constructor() { }
21
+ /**
22
+ * Get the singleton instance of ChatService
23
+ *
24
+ * @returns The ChatService singleton instance
25
+ */
26
+ static getInstance() {
27
+ if (!ChatService.instance) {
28
+ ChatService.instance = new ChatService();
29
+ }
30
+ return ChatService.instance;
31
+ }
32
+ /**
33
+ * Get the endpoint configuration
34
+ *
35
+ * @throws Error if endpoint configuration is not set
36
+ * @returns The endpoint configuration
37
+ */
38
+ getConfig() {
39
+ const config = getEndpointConfig();
40
+ if (!config) {
41
+ throw new Error("Endpoint configuration not set. Call setEndpointConfig() first.");
42
+ }
43
+ return config;
44
+ }
45
+ /**
46
+ * Generic API request helper
47
+ *
48
+ * @param url - The URL to fetch
49
+ * @param options - Fetch options
50
+ * @returns The parsed JSON response
51
+ */
52
+ async request(url, options = {}) {
53
+ const config = this.getConfig();
54
+ const headers = getEndpointHeaders(config, "chat");
55
+ const response = await fetch(url, {
56
+ ...options,
57
+ headers: {
58
+ ...headers,
59
+ ...options.headers,
60
+ },
61
+ });
62
+ if (!response.ok) {
63
+ const errorData = await response.json().catch(() => ({}));
64
+ const errorMessage = errorData.error ||
65
+ errorData.message ||
66
+ `HTTP ${response.status}: ${response.statusText}`;
67
+ throw new Error(errorMessage);
68
+ }
69
+ const json = await response.json();
70
+ // Unwrap the { success, data } envelope used by the Drupal backend.
71
+ if (json && typeof json === "object" && "data" in json) {
72
+ return json.data;
73
+ }
74
+ return json;
75
+ }
76
+ // =========================================================================
77
+ // Chat Operations
78
+ // =========================================================================
79
+ /**
80
+ * Send a message to the chat endpoint
81
+ *
82
+ * @param workflowId - The workflow ID
83
+ * @param request - The chat request payload
84
+ * @returns The chat response from the LLM
85
+ */
86
+ async sendMessage(workflowId, request) {
87
+ const config = this.getConfig();
88
+ const url = buildEndpointUrl(config, config.endpoints.chat.sendMessage, {
89
+ id: workflowId,
90
+ });
91
+ logger.debug("[ChatService] Sending message to", url);
92
+ return this.request(url, {
93
+ method: "POST",
94
+ body: JSON.stringify(request),
95
+ });
96
+ }
97
+ /**
98
+ * Get conversation history for a workflow
99
+ *
100
+ * @param workflowId - The workflow ID
101
+ * @returns Array of chat history messages
102
+ */
103
+ async getHistory(workflowId) {
104
+ const config = this.getConfig();
105
+ const url = buildEndpointUrl(config, config.endpoints.chat.getHistory, {
106
+ id: workflowId,
107
+ });
108
+ logger.debug("[ChatService] Getting history from", url);
109
+ return this.request(url);
110
+ }
111
+ /**
112
+ * Clear conversation history for a workflow
113
+ *
114
+ * @param workflowId - The workflow ID
115
+ */
116
+ async clearHistory(workflowId) {
117
+ const config = this.getConfig();
118
+ const url = buildEndpointUrl(config, config.endpoints.chat.clearHistory, {
119
+ id: workflowId,
120
+ });
121
+ logger.debug("[ChatService] Clearing history at", url);
122
+ await fetch(url, {
123
+ method: "DELETE",
124
+ headers: getEndpointHeaders(config, "chat"),
125
+ });
126
+ }
127
+ }
128
+ /**
129
+ * Pre-instantiated ChatService singleton
130
+ */
131
+ export const chatService = ChatService.getInstance();
@@ -140,8 +140,10 @@ export declare class HistoryService {
140
140
  * Cancel the current transaction without committing
141
141
  *
142
142
  * Discards the transaction without adding to history.
143
+ * Returns the snapshot captured at transaction start so the caller
144
+ * can restore the store to its pre-transaction state.
143
145
  */
144
- cancelTransaction(): void;
146
+ cancelTransaction(): Workflow | null;
145
147
  /**
146
148
  * Clear all history
147
149
  *
@@ -189,9 +191,9 @@ export declare class HistoryService {
189
191
  /**
190
192
  * Create a deep clone of a workflow
191
193
  *
192
- * Uses JSON parse/stringify to properly strip non-serializable values
193
- * like functions (e.g., onConfigOpen callbacks) that are added to nodes
194
- * at runtime but shouldn't be persisted in history.
194
+ * Strips non-serializable values (e.g., onConfigOpen callbacks) before cloning
195
+ * so structuredClone doesn't throw on function references.
196
+ * Falls back to JSON round-trip if structuredClone still fails (e.g., Svelte proxies).
195
197
  */
196
198
  private cloneWorkflow;
197
199
  /**
@@ -182,11 +182,15 @@ export class HistoryService {
182
182
  * Cancel the current transaction without committing
183
183
  *
184
184
  * Discards the transaction without adding to history.
185
+ * Returns the snapshot captured at transaction start so the caller
186
+ * can restore the store to its pre-transaction state.
185
187
  */
186
188
  cancelTransaction() {
189
+ const snapshot = this.transactionSnapshot;
187
190
  this.inTransaction = false;
188
191
  this.transactionSnapshot = null;
189
192
  this.transactionDescription = null;
193
+ return snapshot;
190
194
  }
191
195
  /**
192
196
  * Clear all history
@@ -292,14 +296,25 @@ export class HistoryService {
292
296
  /**
293
297
  * Create a deep clone of a workflow
294
298
  *
295
- * Uses JSON parse/stringify to properly strip non-serializable values
296
- * like functions (e.g., onConfigOpen callbacks) that are added to nodes
297
- * at runtime but shouldn't be persisted in history.
299
+ * Strips non-serializable values (e.g., onConfigOpen callbacks) before cloning
300
+ * so structuredClone doesn't throw on function references.
301
+ * Falls back to JSON round-trip if structuredClone still fails (e.g., Svelte proxies).
298
302
  */
299
303
  cloneWorkflow(workflow) {
300
- // Always use JSON parse/stringify to strip functions and other non-serializable values
301
- // structuredClone would fail on workflows containing callback functions
302
- return JSON.parse(JSON.stringify(workflow));
304
+ const cleaned = {
305
+ ...workflow,
306
+ nodes: workflow.nodes.map((n) => ({
307
+ ...n,
308
+ data: { ...n.data, onConfigOpen: undefined },
309
+ })),
310
+ };
311
+ try {
312
+ return structuredClone(cleaned);
313
+ }
314
+ catch {
315
+ // Fallback for environments where structuredClone can't handle proxied objects
316
+ return JSON.parse(JSON.stringify(cleaned));
317
+ }
303
318
  }
304
319
  /**
305
320
  * Notify all subscribers of state changes
@@ -42,6 +42,14 @@ export const slateSkin = {
42
42
  "sidebar-category-color": "#c0c0d8",
43
43
  // Flat item text: dark for light bg readability
44
44
  "sidebar-flat-item-color": "#4a4a6a",
45
+ // Logo: monochrome purple-grey to match skin
46
+ "logo-bg": "#eeeef8",
47
+ "logo-stroke": "#5a5a7a",
48
+ "logo-line-fill": "#5a5a7a",
49
+ "logo-drop": "#009cde",
50
+ "logo-circle": "#f46351",
51
+ "logo-left": "#ccbaf4",
52
+ "logo-right": "#ffc423",
45
53
  },
46
54
  darkTokens: {
47
55
  // --- Dark mode color palette (deep navy / original slate) ---
@@ -74,5 +82,13 @@ export const slateSkin = {
74
82
  "sidebar-category-color": "#3a3a55",
75
83
  // Flat item text: muted, not full-brightness white
76
84
  "sidebar-flat-item-color": "#8888aa",
85
+ // Logo: monochrome purple-grey to match dark skin
86
+ "logo-bg": "none",
87
+ "logo-stroke": "#9898b8",
88
+ "logo-line-fill": "none",
89
+ "logo-drop": "none",
90
+ "logo-circle": "none",
91
+ "logo-left": "none",
92
+ "logo-right": "none",
77
93
  },
78
94
  };
@@ -56,7 +56,12 @@ export function getPendingInterrupts() {
56
56
  * Get count of pending interrupts
57
57
  */
58
58
  export function getPendingInterruptCount() {
59
- return getPendingInterruptIds().length;
59
+ let count = 0;
60
+ for (const interrupt of interrupts.values()) {
61
+ if (!isTerminalState(interrupt.machineState))
62
+ count++;
63
+ }
64
+ return count;
60
65
  }
61
66
  /**
62
67
  * Get resolved interrupts array
@@ -124,7 +124,7 @@ export declare const playgroundActions: {
124
124
  setMessages: (messageList: PlaygroundMessage[]) => void;
125
125
  /**
126
126
  * Add a message to the current session
127
- * Messages are automatically sorted chronologically after adding
127
+ * Uses binary search insertion for O(log n) instead of full sort.
128
128
  *
129
129
  * @param message - The message to add
130
130
  */
@@ -324,12 +324,21 @@ export const playgroundActions = {
324
324
  },
325
325
  /**
326
326
  * Add a message to the current session
327
- * Messages are automatically sorted chronologically after adding
327
+ * Uses binary search insertion for O(log n) instead of full sort.
328
328
  *
329
329
  * @param message - The message to add
330
330
  */
331
331
  addMessage: (message) => {
332
- _messages = sortMessagesChronologically([..._messages, message]);
332
+ const seq = message.sequenceNumber ?? 0;
333
+ let lo = 0, hi = _messages.length;
334
+ while (lo < hi) {
335
+ const mid = (lo + hi) >>> 1;
336
+ if ((_messages[mid].sequenceNumber ?? 0) <= seq)
337
+ lo = mid + 1;
338
+ else
339
+ hi = mid;
340
+ }
341
+ _messages = [..._messages.slice(0, lo), message, ..._messages.slice(lo)];
333
342
  },
334
343
  /**
335
344
  * Add multiple messages to the current session
@@ -36,6 +36,10 @@ export declare function updateNodePortCoordinates(node: WorkflowNodeType, getInt
36
36
  * @param nodeId - ID of the node to remove
37
37
  */
38
38
  export declare function removeNodePortCoordinates(nodeId: string): void;
39
+ /**
40
+ * Clear all port coordinates (lifecycle cleanup).
41
+ */
42
+ export declare function clearPortCoordinates(): void;
39
43
  /**
40
44
  * Get coordinates for a specific handle.
41
45
  *