@d34dman/flowdrop 0.0.4 → 0.0.6

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.
@@ -93,13 +93,6 @@
93
93
  props.data.metadata?.outputs?.find((port) => port.dataType !== 'trigger')
94
94
  );
95
95
 
96
- // Use trigger port if present, otherwise use first data port
97
- let firstInputPort = $derived(triggerInputPort || firstDataInputPort);
98
- let firstOutputPort = $derived(triggerOutputPort || firstDataOutputPort);
99
-
100
- let hasInput = $derived(!!firstInputPort);
101
- let hasOutput = $derived(!!firstOutputPort);
102
-
103
96
  // Check if we need to show both trigger and data ports
104
97
  let hasBothInputTypes = $derived(!!triggerInputPort && !!firstDataInputPort);
105
98
  let hasBothOutputTypes = $derived(!!triggerOutputPort && !!firstDataOutputPort);
@@ -20,14 +20,6 @@
20
20
  } from '../utils/nodeWrapper.js';
21
21
  import { resolveComponentName } from '../utils/nodeTypes.js';
22
22
 
23
- interface Props {
24
- data: WorkflowNode['data'] & {
25
- nodeId?: string;
26
- onConfigOpen?: (node: { id: string; type: string; data: WorkflowNode['data'] }) => void;
27
- };
28
- selected?: boolean;
29
- }
30
-
31
23
  let {
32
24
  data,
33
25
  selected = false
@@ -8,7 +8,6 @@
8
8
  import {
9
9
  SvelteFlow,
10
10
  ConnectionLineType,
11
- MarkerType,
12
11
  Controls,
13
12
  Background,
14
13
  BackgroundVariant,
@@ -16,29 +15,25 @@
16
15
  SvelteFlowProvider
17
16
  } from '@xyflow/svelte';
18
17
  import '@xyflow/svelte/dist/style.css';
19
- import WorkflowNode from './WorkflowNode.svelte';
20
- import NotesNode from './NotesNode.svelte';
21
- import SimpleNode from './SimpleNode.svelte';
22
- import SquareNode from './SquareNode.svelte';
23
- import ToolNode from './ToolNode.svelte';
24
18
  import type {
25
19
  WorkflowNode as WorkflowNodeType,
26
20
  NodeMetadata,
27
21
  Workflow,
28
22
  WorkflowEdge
29
23
  } from '../types/index.js';
30
- import { hasCycles } from '../utils/connections.js';
31
24
  import CanvasBanner from './CanvasBanner.svelte';
32
- import { workflowApi, nodeApi, setApiBaseUrl, setEndpointConfig } from '../services/api.js';
33
- import { v4 as uuidv4 } from 'uuid';
34
25
  import { tick } from 'svelte';
35
26
  import type { EndpointConfig } from '../config/endpoints.js';
36
27
  import ConnectionLine from './ConnectionLine.svelte';
37
- import { resolveComponentName } from '../utils/nodeTypes.js';
38
28
  import { workflowStore, workflowActions } from '../stores/workflowStore.js';
39
- import { nodeExecutionService } from '../services/nodeExecutionService.js';
40
- import type { NodeExecutionInfo } from '../types/index.js';
41
29
  import UniversalNode from './UniversalNode.svelte';
30
+ import {
31
+ EdgeStylingHelper,
32
+ NodeOperationsHelper,
33
+ WorkflowOperationsHelper,
34
+ ConfigurationHelper
35
+ } from '../helpers/workflowEditorHelper.js';
36
+ import type { NodeExecutionInfo } from '../types/index.js';
42
37
 
43
38
  interface Props {
44
39
  nodes?: NodeMetadata[];
@@ -70,9 +65,6 @@
70
65
  });
71
66
  });
72
67
 
73
- // Initialize from props only once, not on every re-render
74
- let availableNodes = $state<NodeMetadata[]>([]);
75
-
76
68
  // Create a local currentWorkflow variable that we can control directly
77
69
  let currentWorkflow = $state<Workflow | null>(null);
78
70
 
@@ -136,59 +128,47 @@
136
128
  async function loadNodeExecutionInfo(): Promise<void> {
137
129
  if (!currentWorkflow?.nodes) return;
138
130
 
139
- // Only load execution info if we have a pipelineId (for pipeline status mode)
140
- if (!props.pipelineId) return;
141
-
142
- try {
143
- const nodeIds = currentWorkflow.nodes.map((node) => node.id);
144
- const executionInfo = await nodeExecutionService.getMultipleNodeExecutionInfo(
145
- nodeIds,
146
- props.pipelineId
147
- );
148
-
149
- // Update nodes with execution information without triggering reactive updates
150
- const updatedNodes = currentWorkflow.nodes.map((node) => ({
151
- ...node,
152
- data: {
153
- ...node.data,
154
- executionInfo: executionInfo[node.id] || {
155
- status: 'idle',
131
+ const executionInfo = await NodeOperationsHelper.loadNodeExecutionInfo(
132
+ currentWorkflow,
133
+ props.pipelineId
134
+ );
135
+
136
+ // Update nodes with execution information without triggering reactive updates
137
+ const updatedNodes = currentWorkflow.nodes.map((node) => ({
138
+ ...node,
139
+ data: {
140
+ ...node.data,
141
+ executionInfo:
142
+ executionInfo[node.id] ||
143
+ ({
144
+ status: 'idle' as const,
156
145
  executionCount: 0,
157
146
  isExecuting: false
158
- }
159
- }
160
- }));
161
-
162
- // Update the flow nodes to reflect the changes
163
- flowNodes = updatedNodes.map((node) => ({
164
- ...node,
165
- data: {
166
- ...node.data,
167
- onConfigOpen: props.openConfigSidebar
168
- }
169
- }));
147
+ } as NodeExecutionInfo)
148
+ }
149
+ }));
150
+
151
+ // Update the flow nodes to reflect the changes
152
+ flowNodes = updatedNodes.map((node) => ({
153
+ ...node,
154
+ data: {
155
+ ...node.data,
156
+ onConfigOpen: props.openConfigSidebar
157
+ }
158
+ }));
170
159
 
171
- // Update currentWorkflow without triggering reactive effects
172
- currentWorkflow.nodes = updatedNodes;
173
- } catch (error) {
174
- console.error('Failed to load node execution info:', error);
175
- }
160
+ // Update currentWorkflow without triggering reactive effects
161
+ currentWorkflow.nodes = updatedNodes;
176
162
  }
177
163
 
178
164
  // Function to update currentWorkflow when SvelteFlow changes nodes/edges
179
165
  function updateCurrentWorkflowFromSvelteFlow(): void {
180
166
  if (currentWorkflow) {
181
- currentWorkflow = {
182
- ...currentWorkflow,
183
- nodes: flowNodes,
184
- edges: flowEdges,
185
- metadata: {
186
- ...currentWorkflow.metadata,
187
- updatedAt: new Date().toISOString(),
188
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
189
- updateNumber: (currentWorkflow.metadata?.updateNumber || 0) + 1
190
- }
191
- };
167
+ currentWorkflow = WorkflowOperationsHelper.updateWorkflow(
168
+ currentWorkflow,
169
+ flowNodes,
170
+ flowEdges
171
+ );
192
172
 
193
173
  // Update the global store
194
174
  updateGlobalStore();
@@ -261,49 +241,6 @@
261
241
  }
262
242
  }
263
243
 
264
- /**
265
- * Apply custom styling to connection edges based on rules:
266
- * - Dashed lines for connections to tool nodes
267
- * - Arrow markers pointing towards input ports
268
- */
269
- function applyConnectionStyling(
270
- edge: WorkflowEdge,
271
- sourceNode: WorkflowNodeType,
272
- targetNode: WorkflowNodeType
273
- ): void {
274
- // Rule 1: Dashed lines for tool nodes
275
- // A node is a tool node when it uses the ToolNode component,
276
- // which happens when sourceNode.type === 'tool'
277
- const isToolNode = sourceNode.type === 'tool';
278
-
279
- // Use inline styles for dashed lines (more reliable than CSS classes)
280
- if (isToolNode) {
281
- edge.style = 'stroke-dasharray: 0 4 0; stroke: amber !important;';
282
- edge.class = 'flowdrop--edge--tool';
283
- } else {
284
- edge.style = 'stroke: grey;';
285
- }
286
-
287
- // Store metadata in edge data for debugging
288
- edge.data = {
289
- ...edge.data,
290
- isToolConnection: isToolNode,
291
- targetNodeType: targetNode.type,
292
- targetCategory: targetNode.data.metadata.category
293
- };
294
-
295
- // Rule 2: Always add arrow pointing towards input port
296
- // This replaces the default arrows we removed
297
- if (!isToolNode) {
298
- edge.markerEnd = {
299
- type: MarkerType.ArrowClosed,
300
- width: 16,
301
- height: 16,
302
- color: 'grey'
303
- };
304
- }
305
- }
306
-
307
244
  /**
308
245
  * Update existing edges with our custom styling rules
309
246
  * This ensures all edges (including existing ones) follow our rules
@@ -312,35 +249,15 @@
312
249
  // Wait for any pending DOM updates
313
250
  await tick();
314
251
 
315
- const updatedEdges = flowEdges.map((edge) => {
316
- // Find source and target nodes
317
- const sourceNode = flowNodes.find((node) => node.id === edge.source);
318
- const targetNode = flowNodes.find((node) => node.id === edge.target);
319
-
320
- if (!sourceNode || !targetNode) {
321
- console.warn('Could not find nodes for edge:', edge.id);
322
- return edge;
323
- }
324
-
325
- // Create a copy of the edge and apply styling
326
- const updatedEdge = { ...edge };
327
- applyConnectionStyling(updatedEdge, sourceNode, targetNode);
328
-
329
- return updatedEdge;
330
- });
252
+ const updatedEdges = EdgeStylingHelper.updateEdgeStyles(flowEdges, flowNodes);
331
253
 
332
254
  // Update currentWorkflow with the styled edges
333
255
  if (currentWorkflow) {
334
- currentWorkflow = {
335
- ...currentWorkflow,
336
- edges: updatedEdges,
337
- metadata: {
338
- ...currentWorkflow.metadata,
339
- updatedAt: new Date().toISOString(),
340
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
341
- updateNumber: (currentWorkflow.metadata?.updateNumber || 0) + 1
342
- }
343
- };
256
+ currentWorkflow = WorkflowOperationsHelper.updateWorkflow(
257
+ currentWorkflow,
258
+ flowNodes,
259
+ updatedEdges
260
+ );
344
261
 
345
262
  // Update the global store
346
263
  updateGlobalStore();
@@ -349,259 +266,18 @@
349
266
 
350
267
  // Edge styling will be handled when edges are first created or manually updated
351
268
 
352
- // Configure endpoints and load nodes when props change
269
+ // Configure endpoints when props change
353
270
  $effect(() => {
354
271
  if (props.endpointConfig) {
355
- setEndpointConfig(props.endpointConfig);
356
- // Load nodes after setting endpoint config
357
- loadNodesFromApi();
358
- } else if (props.nodes) {
359
- // If we have nodes prop, use them directly
360
- availableNodes = props.nodes;
272
+ ConfigurationHelper.configureEndpoints(props.endpointConfig);
361
273
  }
362
274
  });
363
275
 
364
- /**
365
- * Load nodes from API if not provided
366
- */
367
- async function loadNodesFromApi(): Promise<void> {
368
- // If nodes are provided via props, use them
369
- if (props.nodes && props.nodes.length > 0) {
370
- availableNodes = props.nodes;
371
- return;
372
- }
373
-
374
- // Otherwise, load from API
375
- try {
376
- const fetchedNodes = await nodeApi.getNodes();
377
-
378
- availableNodes = fetchedNodes;
379
- } catch (error) {
380
- console.error('❌ Failed to load nodes from API:', error);
381
-
382
- // Use fallback sample nodes
383
- availableNodes = [
384
- {
385
- id: 'text-input',
386
- name: 'Text Input',
387
- category: 'inputs',
388
- description: 'Simple text input field',
389
- version: '1.0.0',
390
- icon: 'mdi:text-box',
391
- inputs: [],
392
- outputs: [{ id: 'text', name: 'text', type: 'output', dataType: 'string' }]
393
- },
394
- {
395
- id: 'text-output',
396
- name: 'Text Output',
397
- category: 'outputs',
398
- description: 'Display text output',
399
- version: '1.0.0',
400
- icon: 'mdi:text-box-outline',
401
- inputs: [{ id: 'text', name: 'text', type: 'input', dataType: 'string' }],
402
- outputs: []
403
- }
404
- ];
405
- }
406
- }
407
-
408
- /**
409
- * Clear workflow
410
- */
411
- function clearWorkflow(): void {
412
- if (currentWorkflow) {
413
- currentWorkflow = {
414
- ...currentWorkflow,
415
- nodes: [],
416
- edges: [],
417
- metadata: {
418
- ...currentWorkflow.metadata,
419
- updatedAt: new Date().toISOString(),
420
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
421
- updateNumber: (currentWorkflow.metadata?.updateNumber || 0) + 1
422
- }
423
- };
424
-
425
- // Update the global store
426
- updateGlobalStore();
427
- }
428
- }
429
-
430
- // ConfigSidebar functions are now handled by the parent App component
431
-
432
- async function handleConfigSave(newConfig: Record<string, unknown>): Promise<void> {
433
- console.log('🔧 WorkflowEditor: handleConfigSave called with:', newConfig);
434
-
435
- if (props.selectedNodeForConfig) {
436
- console.log('🔧 WorkflowEditor: Updating config for node:', props.selectedNodeForConfig.id);
437
-
438
- // Wait for any pending DOM updates
439
- await tick();
440
-
441
- // Update the node's config
442
- props.selectedNodeForConfig.data.config = { ...newConfig };
443
-
444
- // Update the node in currentWorkflow
445
- // NOTE: We do NOT change the node's type field anymore
446
- // All nodes use 'universalNode' and UniversalNode handles internal switching
447
- if (currentWorkflow) {
448
- currentWorkflow = {
449
- ...currentWorkflow,
450
- nodes: currentWorkflow.nodes.map((node) =>
451
- node.id === props.selectedNodeForConfig.id
452
- ? {
453
- ...node,
454
- data: { ...node.data, config: { ...newConfig } }
455
- }
456
- : node
457
- ),
458
- metadata: {
459
- ...currentWorkflow.metadata,
460
- updatedAt: new Date().toISOString(),
461
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
462
- updateNumber: (currentWorkflow.metadata?.updateNumber || 0) + 1
463
- }
464
- };
465
-
466
- console.log('🔧 WorkflowEditor: Updated currentWorkflow, calling updateGlobalStore');
467
- // Update the global store
468
- updateGlobalStore();
469
- } else {
470
- console.warn('⚠️ WorkflowEditor: No currentWorkflow available for config update');
471
- }
472
- } else {
473
- console.warn('⚠️ WorkflowEditor: No selectedNodeForConfig available for config update');
474
- }
475
- props.closeConfigSidebar?.();
476
- }
477
-
478
- /**
479
- * Save workflow
480
- */
481
- async function saveWorkflow(): Promise<void> {
482
- try {
483
- // Wait for any pending DOM updates before saving
484
- await tick();
485
-
486
- // Use current workflow from local variable
487
- if (!currentWorkflow) {
488
- console.warn('⚠️ No workflow data available to save');
489
- return;
490
- }
491
-
492
- // Determine the workflow ID based on whether we have an existing workflow
493
- let workflowId: string;
494
- if (currentWorkflow.id) {
495
- // Use the existing workflow ID
496
- workflowId = currentWorkflow.id;
497
- } else {
498
- // Generate a new UUID for a new workflow
499
- workflowId = uuidv4();
500
- }
501
-
502
- const workflow: Workflow = {
503
- id: workflowId,
504
- name: currentWorkflow.name || 'Untitled Workflow',
505
- nodes: currentWorkflow.nodes || [],
506
- edges: currentWorkflow.edges || [],
507
- metadata: {
508
- version: '1.0.0',
509
- createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
510
- updatedAt: new Date().toISOString()
511
- }
512
- };
513
-
514
- console.log('💾 WorkflowEditor: Saving workflow to backend:');
515
- console.log(' - ID:', workflow.id);
516
- console.log(' - Name:', workflow.name);
517
- console.log(' - Nodes count:', workflow.nodes.length);
518
- console.log(' - Edges count:', workflow.edges.length);
519
- console.log(' - Full workflow object:', JSON.stringify(workflow, null, 2));
520
-
521
- const savedWorkflow = await workflowApi.saveWorkflow(workflow);
522
-
523
- console.log('✅ WorkflowEditor: Received workflow from backend:');
524
- console.log(' - ID:', savedWorkflow.id);
525
- console.log(' - Name:', savedWorkflow.name);
526
- console.log(' - Nodes count:', savedWorkflow.nodes?.length || 0);
527
- console.log(' - Edges count:', savedWorkflow.edges?.length || 0);
528
-
529
- // Update the workflow ID if it changed (new workflow)
530
- // Keep our current workflow state, only update ID and metadata from backend
531
- if (savedWorkflow.id && savedWorkflow.id !== workflow.id) {
532
- console.log('🔄 Updating workflow ID from', workflow.id, 'to', savedWorkflow.id);
533
- workflowActions.batchUpdate({
534
- nodes: workflow.nodes,
535
- edges: workflow.edges,
536
- name: workflow.name,
537
- metadata: {
538
- ...workflow.metadata,
539
- ...savedWorkflow.metadata
540
- }
541
- });
542
- }
543
-
544
- console.log('🔍 WorkflowEditor: Workflow store after save:', $workflowStore);
545
-
546
- // Note: Notes node configurations (content, noteType) are automatically
547
- // saved as part of the node.data.config object and will be restored
548
- // when the workflow is loaded.
549
-
550
- // Update the workflow ID if it was a new workflow
551
- if (!currentWorkflow.id) {
552
- console.log('🆕 New workflow created with ID:', savedWorkflow.id);
553
- } else {
554
- console.log('🔄 Existing workflow updated with ID:', savedWorkflow.id);
555
- }
556
- } catch (error) {
557
- console.error('❌ Failed to save workflow:', error);
558
- // Here you would typically show a user-friendly error message
559
- }
560
- }
561
-
562
- /**
563
- * Export workflow
564
- */
565
- async function exportWorkflow(): Promise<void> {
566
- // Wait for any pending DOM updates before exporting
567
- await tick();
568
-
569
- // Use current workflow from local variable
570
- if (!currentWorkflow) {
571
- console.warn('⚠️ No workflow data available to export');
572
- return;
573
- }
574
-
575
- // Use the same ID logic as saveWorkflow
576
- const workflowId = currentWorkflow.id || uuidv4();
577
-
578
- const workflow: Workflow = {
579
- id: workflowId,
580
- name: currentWorkflow.name || 'Untitled Workflow',
581
- nodes: currentWorkflow.nodes || [],
582
- edges: currentWorkflow.edges || [],
583
- metadata: {
584
- version: '1.0.0',
585
- createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
586
- updatedAt: new Date().toISOString()
587
- }
588
- };
589
-
590
- const dataStr = JSON.stringify(workflow, null, 2);
591
- const dataBlob = new Blob([dataStr], { type: 'application/json' });
592
- const url = URL.createObjectURL(dataBlob);
593
- const link = document.createElement('a');
594
- link.href = url;
595
- link.download = `${workflow.name}.json`;
596
- link.click();
597
- URL.revokeObjectURL(url);
598
- }
599
-
600
276
  /**
601
277
  * Check if workflow has cycles
602
278
  */
603
279
  function checkWorkflowCycles(): boolean {
604
- return hasCycles(flowNodes, flowEdges);
280
+ return WorkflowOperationsHelper.checkWorkflowCycles(flowNodes, flowEdges);
605
281
  }
606
282
  </script>
607
283
 
@@ -631,92 +307,23 @@
631
307
  y: e.clientY - rect.top
632
308
  };
633
309
 
634
- // Create the node manually since SvelteFlow isn't receiving the event
635
- try {
636
- const parsedData = JSON.parse(nodeTypeData);
637
-
638
- // Handle both old format (with type: "node") and new format (direct NodeMetadata)
639
- let nodeType: NodeMetadata;
640
- let nodeData: {
641
- label: string;
642
- config: Record<string, unknown>;
643
- metadata: NodeMetadata;
644
- };
645
-
646
- if (parsedData.type === 'node') {
647
- // Old format from sidebar
648
- nodeType = parsedData.nodeData.metadata;
649
- nodeData = parsedData.nodeData;
650
- } else {
651
- // New format (direct NodeMetadata)
652
- nodeType = parsedData;
653
-
654
- // Extract initial config from configSchema
655
- let initialConfig = {};
656
- if (nodeType.configSchema && typeof nodeType.configSchema === 'object') {
657
- // If configSchema is a JSON Schema, extract default values
658
- if (nodeType.configSchema.properties) {
659
- // JSON Schema format - extract defaults
660
- Object.entries(nodeType.configSchema.properties).forEach(([key, prop]) => {
661
- if (prop && typeof prop === 'object' && 'default' in prop) {
662
- initialConfig[key] = prop.default;
663
- }
664
- });
665
- } else {
666
- // Simple object format - use as is
667
- initialConfig = { ...nodeType.configSchema };
668
- }
669
- }
670
-
671
- nodeData = {
672
- label: nodeType.name,
673
- config: initialConfig,
674
- metadata: nodeType
675
- };
676
- }
677
-
678
- const newNodeId = uuidv4();
679
-
680
- // All nodes use 'universalNode' type
681
- // UniversalNode component handles internal switching based on metadata and config
682
- const newNode: WorkflowNodeType = {
683
- id: newNodeId,
684
- type: 'universalNode',
685
- position, // Use the position calculated from the drop event
686
- deletable: true,
687
- data: {
688
- ...nodeData,
689
- nodeId: newNodeId // Use the same ID
690
- }
691
- };
692
-
693
- // Add node to currentWorkflow
694
- if (currentWorkflow) {
695
- console.log('🔧 WorkflowEditor: Adding new node to currentWorkflow:', newNode.id);
696
- currentWorkflow = {
697
- ...currentWorkflow,
698
- nodes: [...currentWorkflow.nodes, newNode],
699
- metadata: {
700
- ...currentWorkflow.metadata,
701
- updatedAt: new Date().toISOString(),
702
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
703
- updateNumber: (currentWorkflow.metadata?.updateNumber || 0) + 1
704
- }
705
- };
706
-
707
- console.log(
708
- '🔧 WorkflowEditor: Updated currentWorkflow with new node, calling updateGlobalStore'
709
- );
710
- // Update the global store
711
- updateGlobalStore();
712
- } else {
713
- console.warn('⚠️ WorkflowEditor: No currentWorkflow available for new node');
714
- }
310
+ // Create the node using the helper
311
+ const newNode = NodeOperationsHelper.createNodeFromDrop(nodeTypeData, position);
312
+
313
+ if (newNode && currentWorkflow) {
314
+ console.log('🔧 WorkflowEditor: Adding new node to currentWorkflow:', newNode.id);
315
+ currentWorkflow = WorkflowOperationsHelper.addNode(currentWorkflow, newNode);
316
+
317
+ console.log(
318
+ '🔧 WorkflowEditor: Updated currentWorkflow with new node, calling updateGlobalStore'
319
+ );
320
+ // Update the global store
321
+ updateGlobalStore();
715
322
 
716
323
  // Wait for DOM update to ensure SvelteFlow updates
717
324
  await tick();
718
- } catch (error) {
719
- console.error('Error parsing node data:', error);
325
+ } else if (!currentWorkflow) {
326
+ console.warn('⚠️ WorkflowEditor: No currentWorkflow available for new node');
720
327
  }
721
328
  }
722
329
  }}
@@ -7,17 +7,10 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import { Position, Handle } from '@xyflow/svelte';
10
- import type { WorkflowNode, NodeExecutionStatus } from '../types/index.js';
10
+ import type { WorkflowNode } from '../types/index.js';
11
11
  import Icon from '@iconify/svelte';
12
12
  import { getNodeIcon } from '../utils/icons.js';
13
13
  import { getDataTypeColorToken, getCategoryColorToken } from '../utils/colors.js';
14
- import {
15
- getStatusColor,
16
- getStatusIcon,
17
- getStatusLabel,
18
- formatExecutionDuration,
19
- formatLastExecuted
20
- } from '../utils/nodeStatus.js';
21
14
 
22
15
  interface Props {
23
16
  data: WorkflowNode['data'] & {
@@ -24,7 +24,8 @@ export interface ApiConfig {
24
24
  }
25
25
  /**
26
26
  * Default API configuration
27
- * Can be overridden by environment variables or runtime config
27
+ * For library usage, configuration should be provided at runtime
28
+ * This provides sensible defaults that can be overridden
28
29
  */
29
30
  export declare const defaultApiConfig: ApiConfig;
30
31
  /**
@@ -4,10 +4,11 @@
4
4
  */
5
5
  /**
6
6
  * Default API configuration
7
- * Can be overridden by environment variables or runtime config
7
+ * For library usage, configuration should be provided at runtime
8
+ * This provides sensible defaults that can be overridden
8
9
  */
9
10
  export const defaultApiConfig = {
10
- baseUrl: import.meta.env.VITE_API_BASE_URL || '/api/flowdrop',
11
+ baseUrl: '/api/flowdrop',
11
12
  endpoints: {
12
13
  workflows: {
13
14
  list: '/workflows',