@runtypelabs/react-flow 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +14 -0
- package/README.md +21 -21
- package/dist/index.js +152 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -52
- package/dist/index.mjs.map +1 -1
- package/example/CHANGELOG.md +16 -0
- package/example/node_modules/.bin/vite +2 -2
- package/example/package.json +1 -1
- package/example/src/App.tsx +269 -166
- package/example/src/main.tsx +1 -2
- package/example/tsconfig.json +0 -1
- package/example/vite.config.ts +0 -1
- package/package.json +2 -2
- package/src/components/RuntypeFlowEditor.tsx +57 -21
- package/src/components/nodes/BaseNode.tsx +7 -6
- package/src/components/nodes/CodeNode.tsx +9 -3
- package/src/components/nodes/ConditionalNode.tsx +15 -12
- package/src/components/nodes/FetchUrlNode.tsx +10 -8
- package/src/components/nodes/PromptNode.tsx +10 -6
- package/src/components/nodes/SendEmailNode.tsx +2 -5
- package/src/hooks/useFlowValidation.ts +1 -5
- package/src/hooks/useRuntypeFlow.ts +3 -12
- package/src/index.ts +0 -1
- package/src/types/index.ts +1 -2
- package/src/utils/adapter.ts +85 -75
- package/src/utils/layout.ts +21 -25
- package/tsconfig.json +0 -1
- package/tsup.config.ts +0 -1
package/example/src/App.tsx
CHANGED
|
@@ -130,7 +130,8 @@ const sampleSteps: FlowStep[] = [
|
|
|
130
130
|
config: {
|
|
131
131
|
mode: 'instruction',
|
|
132
132
|
model: 'gpt-4',
|
|
133
|
-
userPrompt:
|
|
133
|
+
userPrompt:
|
|
134
|
+
'Generate detailed premium insights for {{user_data.name}} based on their usage patterns:\n\n{{analysis}}',
|
|
134
135
|
responseFormat: 'json',
|
|
135
136
|
outputVariable: 'premium_insights',
|
|
136
137
|
},
|
|
@@ -160,7 +161,8 @@ const sampleSteps: FlowStep[] = [
|
|
|
160
161
|
config: {
|
|
161
162
|
mode: 'instruction',
|
|
162
163
|
model: 'gpt-3.5-turbo',
|
|
163
|
-
userPrompt:
|
|
164
|
+
userPrompt:
|
|
165
|
+
'Generate a friendly upgrade pitch for {{user_data.name}} highlighting premium benefits. Keep it short and compelling.',
|
|
164
166
|
responseFormat: 'text',
|
|
165
167
|
outputVariable: 'upgrade_pitch',
|
|
166
168
|
},
|
|
@@ -322,7 +324,10 @@ function DispatchPanel({ isOpen, onClose, steps, flowName }: DispatchPanelProps)
|
|
|
322
324
|
const client = createClient({ apiKey: 'your-api-key' })
|
|
323
325
|
|
|
324
326
|
// Execute the flow
|
|
325
|
-
const response = await client.dispatch.execute(${JSON.stringify(dispatchConfig, null, 2)
|
|
327
|
+
const response = await client.dispatch.execute(${JSON.stringify(dispatchConfig, null, 2)
|
|
328
|
+
.split('\n')
|
|
329
|
+
.map((line, i) => (i === 0 ? line : ' ' + line))
|
|
330
|
+
.join('\n')})
|
|
326
331
|
|
|
327
332
|
console.log(response)`}</pre>
|
|
328
333
|
</div>
|
|
@@ -330,9 +335,7 @@ console.log(response)`}</pre>
|
|
|
330
335
|
</>
|
|
331
336
|
) : (
|
|
332
337
|
<>
|
|
333
|
-
<p style={descriptionStyle}>
|
|
334
|
-
Individual step configurations in the flow:
|
|
335
|
-
</p>
|
|
338
|
+
<p style={descriptionStyle}>Individual step configurations in the flow:</p>
|
|
336
339
|
{steps.map((step, index) => (
|
|
337
340
|
<div key={step.id} style={stepCardStyle}>
|
|
338
341
|
<div style={stepHeaderStyle}>
|
|
@@ -353,9 +356,12 @@ console.log(response)`}</pre>
|
|
|
353
356
|
|
|
354
357
|
{/* Footer */}
|
|
355
358
|
<div style={panelFooterStyle}>
|
|
356
|
-
<button
|
|
357
|
-
|
|
358
|
-
|
|
359
|
+
<button
|
|
360
|
+
style={copyButtonStyle}
|
|
361
|
+
onClick={() => {
|
|
362
|
+
navigator.clipboard.writeText(JSON.stringify(dispatchConfig, null, 2))
|
|
363
|
+
}}
|
|
364
|
+
>
|
|
359
365
|
<CopyIcon /> Copy Config
|
|
360
366
|
</button>
|
|
361
367
|
</div>
|
|
@@ -376,7 +382,13 @@ interface ExecutionPanelProps {
|
|
|
376
382
|
onRunFlow: () => void
|
|
377
383
|
}
|
|
378
384
|
|
|
379
|
-
function ExecutionPanel({
|
|
385
|
+
function ExecutionPanel({
|
|
386
|
+
isOpen,
|
|
387
|
+
onClose,
|
|
388
|
+
isRunning,
|
|
389
|
+
flowProgress,
|
|
390
|
+
onRunFlow,
|
|
391
|
+
}: ExecutionPanelProps) {
|
|
380
392
|
if (!isOpen) return null
|
|
381
393
|
|
|
382
394
|
const getStatusIcon = (status: string) => {
|
|
@@ -421,17 +433,15 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
421
433
|
{!flowProgress && !isRunning && (
|
|
422
434
|
<div style={{ textAlign: 'center', padding: '40px 20px' }}>
|
|
423
435
|
<div style={{ fontSize: '48px', marginBottom: '16px' }}>🚀</div>
|
|
424
|
-
<h3
|
|
436
|
+
<h3
|
|
437
|
+
style={{ fontSize: '18px', fontWeight: 600, color: '#cdd6f4', marginBottom: '8px' }}
|
|
438
|
+
>
|
|
425
439
|
Ready to Execute
|
|
426
440
|
</h3>
|
|
427
441
|
<p style={{ fontSize: '13px', color: '#a6adc8', marginBottom: '24px' }}>
|
|
428
442
|
Click the button below to run this flow using the Runtype API
|
|
429
443
|
</p>
|
|
430
|
-
<button
|
|
431
|
-
style={runButtonStyle}
|
|
432
|
-
onClick={onRunFlow}
|
|
433
|
-
disabled={isRunning}
|
|
434
|
-
>
|
|
444
|
+
<button style={runButtonStyle} onClick={onRunFlow} disabled={isRunning}>
|
|
435
445
|
<PlayIcon /> Run Flow
|
|
436
446
|
</button>
|
|
437
447
|
</div>
|
|
@@ -442,7 +452,13 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
442
452
|
{/* Progress Header */}
|
|
443
453
|
<div style={progressHeaderStyle}>
|
|
444
454
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
445
|
-
{isRunning ?
|
|
455
|
+
{isRunning ? (
|
|
456
|
+
<SpinnerIcon />
|
|
457
|
+
) : flowProgress?.isError ? (
|
|
458
|
+
<ErrorIcon />
|
|
459
|
+
) : (
|
|
460
|
+
<CheckIcon />
|
|
461
|
+
)}
|
|
446
462
|
<div>
|
|
447
463
|
<div style={{ fontSize: '14px', fontWeight: 600, color: '#cdd6f4' }}>
|
|
448
464
|
{flowProgress?.flowName || 'Executing Flow...'}
|
|
@@ -451,8 +467,8 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
451
467
|
{isRunning
|
|
452
468
|
? `Step ${(flowProgress?.currentStepIndex ?? 0) + 1} of ${flowProgress?.totalSteps || '?'}`
|
|
453
469
|
: flowProgress?.isError
|
|
454
|
-
|
|
455
|
-
|
|
470
|
+
? 'Execution failed'
|
|
471
|
+
: `Completed in ${flowProgress?.executionTime || 0}ms`}
|
|
456
472
|
</div>
|
|
457
473
|
</div>
|
|
458
474
|
</div>
|
|
@@ -485,7 +501,7 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
485
501
|
</span>
|
|
486
502
|
)}
|
|
487
503
|
</div>
|
|
488
|
-
|
|
504
|
+
|
|
489
505
|
{/* Streaming Text Output */}
|
|
490
506
|
{step.streamingText && (
|
|
491
507
|
<div style={streamingOutputStyle}>
|
|
@@ -494,14 +510,10 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
494
510
|
</pre>
|
|
495
511
|
</div>
|
|
496
512
|
)}
|
|
497
|
-
|
|
513
|
+
|
|
498
514
|
{/* Error */}
|
|
499
|
-
{step.error &&
|
|
500
|
-
|
|
501
|
-
{step.error}
|
|
502
|
-
</div>
|
|
503
|
-
)}
|
|
504
|
-
|
|
515
|
+
{step.error && <div style={errorOutputStyle}>{step.error}</div>}
|
|
516
|
+
|
|
505
517
|
{/* Result (collapsed by default) */}
|
|
506
518
|
{step.result && !step.streamingText && (
|
|
507
519
|
<details style={{ marginTop: '8px' }}>
|
|
@@ -510,8 +522,8 @@ function ExecutionPanel({ isOpen, onClose, isRunning, flowProgress, onRunFlow }:
|
|
|
510
522
|
</summary>
|
|
511
523
|
<div style={codeBlockStyle}>
|
|
512
524
|
<pre style={{ ...preStyle, fontSize: '10px' }}>
|
|
513
|
-
{typeof step.result === 'string'
|
|
514
|
-
? step.result
|
|
525
|
+
{typeof step.result === 'string'
|
|
526
|
+
? step.result
|
|
515
527
|
: JSON.stringify(step.result, null, 2)}
|
|
516
528
|
</pre>
|
|
517
529
|
</div>
|
|
@@ -557,7 +569,7 @@ function FlowBrowserPanel({ isOpen, onClose, onLoadFlow }: FlowBrowserPanelProps
|
|
|
557
569
|
try {
|
|
558
570
|
const response = await fetch(`${API_BASE_URL}/flows`, {
|
|
559
571
|
headers: {
|
|
560
|
-
|
|
572
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
561
573
|
},
|
|
562
574
|
})
|
|
563
575
|
if (!response.ok) {
|
|
@@ -580,7 +592,7 @@ function FlowBrowserPanel({ isOpen, onClose, onLoadFlow }: FlowBrowserPanelProps
|
|
|
580
592
|
// Fetch flow details with embedded steps
|
|
581
593
|
const flowResponse = await fetch(`${API_BASE_URL}/flows/${flowId}`, {
|
|
582
594
|
headers: {
|
|
583
|
-
|
|
595
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
584
596
|
},
|
|
585
597
|
})
|
|
586
598
|
if (!flowResponse.ok) {
|
|
@@ -634,26 +646,30 @@ function FlowBrowserPanel({ isOpen, onClose, onLoadFlow }: FlowBrowserPanelProps
|
|
|
634
646
|
)}
|
|
635
647
|
|
|
636
648
|
{error && (
|
|
637
|
-
<div
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
649
|
+
<div
|
|
650
|
+
style={{
|
|
651
|
+
padding: '16px',
|
|
652
|
+
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
|
653
|
+
borderRadius: '8px',
|
|
654
|
+
marginBottom: '16px',
|
|
655
|
+
border: '1px solid rgba(239, 68, 68, 0.3)',
|
|
656
|
+
}}
|
|
657
|
+
>
|
|
658
|
+
<div
|
|
659
|
+
style={{ color: '#ef4444', fontSize: '13px', fontWeight: 600, marginBottom: '4px' }}
|
|
660
|
+
>
|
|
645
661
|
Error
|
|
646
662
|
</div>
|
|
647
|
-
<div style={{ color: '#fca5a5', fontSize: '12px' }}>
|
|
648
|
-
{error}
|
|
649
|
-
</div>
|
|
663
|
+
<div style={{ color: '#fca5a5', fontSize: '12px' }}>{error}</div>
|
|
650
664
|
</div>
|
|
651
665
|
)}
|
|
652
666
|
|
|
653
667
|
{!isLoading && flows.length === 0 && !error && (
|
|
654
668
|
<div style={{ textAlign: 'center', padding: '40px 20px' }}>
|
|
655
669
|
<div style={{ fontSize: '48px', marginBottom: '16px' }}>📁</div>
|
|
656
|
-
<h3
|
|
670
|
+
<h3
|
|
671
|
+
style={{ fontSize: '16px', fontWeight: 600, color: '#cdd6f4', marginBottom: '8px' }}
|
|
672
|
+
>
|
|
657
673
|
No Flows Found
|
|
658
674
|
</h3>
|
|
659
675
|
<p style={{ fontSize: '13px', color: '#a6adc8' }}>
|
|
@@ -685,28 +701,44 @@ function FlowBrowserPanel({ isOpen, onClose, onLoadFlow }: FlowBrowserPanelProps
|
|
|
685
701
|
e.currentTarget.style.backgroundColor = '#181825'
|
|
686
702
|
}}
|
|
687
703
|
>
|
|
688
|
-
<div
|
|
704
|
+
<div
|
|
705
|
+
style={{
|
|
706
|
+
display: 'flex',
|
|
707
|
+
alignItems: 'center',
|
|
708
|
+
justifyContent: 'space-between',
|
|
709
|
+
}}
|
|
710
|
+
>
|
|
689
711
|
<div style={{ flex: 1 }}>
|
|
690
|
-
<div
|
|
712
|
+
<div
|
|
713
|
+
style={{
|
|
714
|
+
fontSize: '14px',
|
|
715
|
+
fontWeight: 600,
|
|
716
|
+
color: '#cdd6f4',
|
|
717
|
+
marginBottom: '4px',
|
|
718
|
+
}}
|
|
719
|
+
>
|
|
691
720
|
{flow.name}
|
|
692
721
|
</div>
|
|
693
722
|
<div style={{ fontSize: '11px', color: '#6c7086' }}>
|
|
694
723
|
{flow.status} • Updated: {new Date(flow.updated_at).toLocaleDateString()}
|
|
695
|
-
{flow.last_run_at &&
|
|
724
|
+
{flow.last_run_at &&
|
|
725
|
+
` • Last run: ${new Date(flow.last_run_at).toLocaleDateString()}`}
|
|
696
726
|
</div>
|
|
697
727
|
</div>
|
|
698
728
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
699
729
|
{loadingFlowId === flow.id ? (
|
|
700
730
|
<SpinnerIcon />
|
|
701
731
|
) : (
|
|
702
|
-
<div
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
732
|
+
<div
|
|
733
|
+
style={{
|
|
734
|
+
padding: '6px 12px',
|
|
735
|
+
backgroundColor: '#89b4fa',
|
|
736
|
+
color: '#1e1e2e',
|
|
737
|
+
borderRadius: '6px',
|
|
738
|
+
fontSize: '12px',
|
|
739
|
+
fontWeight: 600,
|
|
740
|
+
}}
|
|
741
|
+
>
|
|
710
742
|
Load
|
|
711
743
|
</div>
|
|
712
744
|
)}
|
|
@@ -753,53 +785,59 @@ export default function App() {
|
|
|
753
785
|
const [isInitialized, setIsInitialized] = useState(false)
|
|
754
786
|
|
|
755
787
|
// Handler that updates both steps AND nodes
|
|
756
|
-
const handleStepChangeWithNodes = useCallback(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return step
|
|
769
|
-
})
|
|
770
|
-
)
|
|
771
|
-
|
|
772
|
-
// Update nodes state to reflect the change
|
|
773
|
-
setNodes((nds) =>
|
|
774
|
-
nds.map((node) => {
|
|
775
|
-
if (node.id === stepId) {
|
|
776
|
-
const updatedStep = {
|
|
777
|
-
...node.data.step,
|
|
778
|
-
...updates,
|
|
779
|
-
config: updates.config
|
|
780
|
-
? { ...node.data.step.config, ...updates.config }
|
|
781
|
-
: node.data.step.config,
|
|
788
|
+
const handleStepChangeWithNodes = useCallback(
|
|
789
|
+
(stepId: string, updates: Partial<FlowStep>) => {
|
|
790
|
+
// console.log('[App] handleStepChangeWithNodes:', stepId, updates)
|
|
791
|
+
// Update steps state
|
|
792
|
+
setSteps((prev) =>
|
|
793
|
+
prev.map((step) => {
|
|
794
|
+
if (step.id === stepId) {
|
|
795
|
+
return {
|
|
796
|
+
...step,
|
|
797
|
+
...updates,
|
|
798
|
+
config: updates.config ? { ...step.config, ...updates.config } : step.config,
|
|
799
|
+
}
|
|
782
800
|
}
|
|
783
|
-
return
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
801
|
+
return step
|
|
802
|
+
})
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
// Update nodes state to reflect the change
|
|
806
|
+
setNodes((nds) =>
|
|
807
|
+
nds.map((node) => {
|
|
808
|
+
if (node.id === stepId) {
|
|
809
|
+
const updatedStep = {
|
|
810
|
+
...node.data.step,
|
|
811
|
+
...updates,
|
|
812
|
+
config: updates.config
|
|
813
|
+
? { ...node.data.step.config, ...updates.config }
|
|
814
|
+
: node.data.step.config,
|
|
815
|
+
}
|
|
816
|
+
return {
|
|
817
|
+
...node,
|
|
818
|
+
data: {
|
|
819
|
+
...node.data,
|
|
820
|
+
step: updatedStep,
|
|
821
|
+
label: updatedStep.name,
|
|
822
|
+
},
|
|
823
|
+
}
|
|
790
824
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
825
|
+
return node
|
|
826
|
+
})
|
|
827
|
+
)
|
|
828
|
+
},
|
|
829
|
+
[setNodes]
|
|
830
|
+
)
|
|
796
831
|
|
|
797
832
|
// Handler that updates both steps AND nodes for deletion
|
|
798
|
-
const handleStepDeleteWithNodes = useCallback(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
833
|
+
const handleStepDeleteWithNodes = useCallback(
|
|
834
|
+
(stepId: string) => {
|
|
835
|
+
setSteps((prev) => prev.filter((step) => step.id !== stepId))
|
|
836
|
+
setNodes((nds) => nds.filter((node) => node.id !== stepId))
|
|
837
|
+
setEdges((eds) => eds.filter((edge) => edge.source !== stepId && edge.target !== stepId))
|
|
838
|
+
},
|
|
839
|
+
[setNodes, setEdges]
|
|
840
|
+
)
|
|
803
841
|
|
|
804
842
|
// Initialize nodes on first render
|
|
805
843
|
React.useEffect(() => {
|
|
@@ -814,7 +852,14 @@ export default function App() {
|
|
|
814
852
|
setEdges(initialEdges)
|
|
815
853
|
setIsInitialized(true)
|
|
816
854
|
}
|
|
817
|
-
}, [
|
|
855
|
+
}, [
|
|
856
|
+
isInitialized,
|
|
857
|
+
steps,
|
|
858
|
+
handleStepChangeWithNodes,
|
|
859
|
+
handleStepDeleteWithNodes,
|
|
860
|
+
setNodes,
|
|
861
|
+
setEdges,
|
|
862
|
+
])
|
|
818
863
|
|
|
819
864
|
// Derive current steps from nodes
|
|
820
865
|
const currentSteps = useMemo(() => {
|
|
@@ -827,7 +872,7 @@ export default function App() {
|
|
|
827
872
|
setSteps(flow.steps)
|
|
828
873
|
setFlowName(flow.name)
|
|
829
874
|
setLoadedFlowId(flow.id)
|
|
830
|
-
|
|
875
|
+
|
|
831
876
|
// Reset initialization so nodes get rebuilt
|
|
832
877
|
setIsInitialized(false)
|
|
833
878
|
}, [])
|
|
@@ -838,38 +883,41 @@ export default function App() {
|
|
|
838
883
|
)
|
|
839
884
|
|
|
840
885
|
// Add a new step
|
|
841
|
-
const addStep = useCallback(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
setNodes((nds) => [...nds, newNode])
|
|
858
|
-
|
|
859
|
-
// Add edge from last node
|
|
860
|
-
if (nodes.length > 0) {
|
|
861
|
-
const lastNode = nodes[nodes.length - 1]
|
|
862
|
-
setEdges((eds) => [
|
|
863
|
-
...eds,
|
|
864
|
-
{
|
|
865
|
-
id: `edge-${lastNode.id}-${newNode.id}`,
|
|
866
|
-
source: lastNode.id,
|
|
867
|
-
target: newNode.id,
|
|
868
|
-
type: 'smoothstep',
|
|
886
|
+
const addStep = useCallback(
|
|
887
|
+
(type: FlowStep['type']) => {
|
|
888
|
+
const newStep = createDefaultStep(type, nodes.length)
|
|
889
|
+
setSteps((prev) => [...prev, newStep])
|
|
890
|
+
|
|
891
|
+
const newNode: RuntypeNode = {
|
|
892
|
+
id: newStep.id,
|
|
893
|
+
type: newStep.type,
|
|
894
|
+
position: { x: 400, y: nodes.length * 230 + 50 },
|
|
895
|
+
data: {
|
|
896
|
+
step: newStep,
|
|
897
|
+
label: newStep.name,
|
|
898
|
+
onChange: handleStepChangeWithNodes,
|
|
899
|
+
onDelete: handleStepDeleteWithNodes,
|
|
869
900
|
},
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
setNodes((nds) => [...nds, newNode])
|
|
904
|
+
|
|
905
|
+
// Add edge from last node
|
|
906
|
+
if (nodes.length > 0) {
|
|
907
|
+
const lastNode = nodes[nodes.length - 1]
|
|
908
|
+
setEdges((eds) => [
|
|
909
|
+
...eds,
|
|
910
|
+
{
|
|
911
|
+
id: `edge-${lastNode.id}-${newNode.id}`,
|
|
912
|
+
source: lastNode.id,
|
|
913
|
+
target: newNode.id,
|
|
914
|
+
type: 'smoothstep',
|
|
915
|
+
},
|
|
916
|
+
])
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
[nodes, setNodes, setEdges, handleStepChangeWithNodes, handleStepDeleteWithNodes]
|
|
920
|
+
)
|
|
873
921
|
|
|
874
922
|
// Execute flow via Runtype API
|
|
875
923
|
const executeFlow = useCallback(async () => {
|
|
@@ -918,7 +966,7 @@ export default function App() {
|
|
|
918
966
|
method: 'POST',
|
|
919
967
|
headers: {
|
|
920
968
|
'Content-Type': 'application/json',
|
|
921
|
-
|
|
969
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
922
970
|
},
|
|
923
971
|
body: JSON.stringify(dispatchRequest),
|
|
924
972
|
})
|
|
@@ -1004,8 +1052,8 @@ export default function App() {
|
|
|
1004
1052
|
setFlowProgress((prev) => {
|
|
1005
1053
|
if (!prev) return null
|
|
1006
1054
|
const stepIndex = data.index !== undefined ? data.index : prev.steps.length
|
|
1007
|
-
const existingIndex = prev.steps.findIndex(s => s.id === data.id)
|
|
1008
|
-
|
|
1055
|
+
const existingIndex = prev.steps.findIndex((s) => s.id === data.id)
|
|
1056
|
+
|
|
1009
1057
|
if (existingIndex >= 0) {
|
|
1010
1058
|
const newSteps = [...prev.steps]
|
|
1011
1059
|
newSteps[existingIndex] = {
|
|
@@ -1019,7 +1067,7 @@ export default function App() {
|
|
|
1019
1067
|
currentStepIndex: Math.max(prev.currentStepIndex, stepIndex),
|
|
1020
1068
|
}
|
|
1021
1069
|
}
|
|
1022
|
-
|
|
1070
|
+
|
|
1023
1071
|
return {
|
|
1024
1072
|
...prev,
|
|
1025
1073
|
steps: [
|
|
@@ -1039,8 +1087,8 @@ export default function App() {
|
|
|
1039
1087
|
case 'step_chunk':
|
|
1040
1088
|
setFlowProgress((prev) => {
|
|
1041
1089
|
if (!prev) return null
|
|
1042
|
-
const stepIndex = prev.steps.findIndex(s => s.id === data.id)
|
|
1043
|
-
|
|
1090
|
+
const stepIndex = prev.steps.findIndex((s) => s.id === data.id)
|
|
1091
|
+
|
|
1044
1092
|
if (stepIndex < 0) {
|
|
1045
1093
|
const chunkStepIndex = data.index !== undefined ? data.index : prev.steps.length
|
|
1046
1094
|
return {
|
|
@@ -1057,11 +1105,12 @@ export default function App() {
|
|
|
1057
1105
|
].sort((a, b) => a.index - b.index),
|
|
1058
1106
|
}
|
|
1059
1107
|
}
|
|
1060
|
-
|
|
1108
|
+
|
|
1061
1109
|
const newSteps = [...prev.steps]
|
|
1062
1110
|
newSteps[stepIndex] = {
|
|
1063
1111
|
...newSteps[stepIndex],
|
|
1064
|
-
streamingText:
|
|
1112
|
+
streamingText:
|
|
1113
|
+
(newSteps[stepIndex].streamingText || '') + (data.text || data.content || ''),
|
|
1065
1114
|
}
|
|
1066
1115
|
return { ...prev, steps: newSteps }
|
|
1067
1116
|
})
|
|
@@ -1070,16 +1119,16 @@ export default function App() {
|
|
|
1070
1119
|
case 'step_complete':
|
|
1071
1120
|
setFlowProgress((prev) => {
|
|
1072
1121
|
if (!prev) return null
|
|
1073
|
-
const stepIndex = prev.steps.findIndex(s => s.id === data.id)
|
|
1122
|
+
const stepIndex = prev.steps.findIndex((s) => s.id === data.id)
|
|
1074
1123
|
if (stepIndex >= 0) {
|
|
1075
1124
|
const newSteps = [...prev.steps]
|
|
1076
1125
|
const step = newSteps[stepIndex]
|
|
1077
|
-
|
|
1126
|
+
|
|
1078
1127
|
let finalText = step.streamingText
|
|
1079
1128
|
if (!finalText && data.output?.message) {
|
|
1080
1129
|
finalText = data.output.message
|
|
1081
1130
|
}
|
|
1082
|
-
|
|
1131
|
+
|
|
1083
1132
|
newSteps[stepIndex] = {
|
|
1084
1133
|
...step,
|
|
1085
1134
|
status: 'completed',
|
|
@@ -1087,7 +1136,7 @@ export default function App() {
|
|
|
1087
1136
|
streamingText: finalText,
|
|
1088
1137
|
executionTime: data.executionTime,
|
|
1089
1138
|
}
|
|
1090
|
-
|
|
1139
|
+
|
|
1091
1140
|
return {
|
|
1092
1141
|
...prev,
|
|
1093
1142
|
steps: newSteps.sort((a, b) => a.index - b.index),
|
|
@@ -1100,7 +1149,7 @@ export default function App() {
|
|
|
1100
1149
|
case 'step_error':
|
|
1101
1150
|
setFlowProgress((prev) => {
|
|
1102
1151
|
if (!prev) return null
|
|
1103
|
-
const stepIndex = prev.steps.findIndex(s => s.id === data.id)
|
|
1152
|
+
const stepIndex = prev.steps.findIndex((s) => s.id === data.id)
|
|
1104
1153
|
if (stepIndex >= 0) {
|
|
1105
1154
|
const newSteps = [...prev.steps]
|
|
1106
1155
|
newSteps[stepIndex] = {
|
|
@@ -1151,22 +1200,13 @@ export default function App() {
|
|
|
1151
1200
|
Flow: <strong style={{ color: '#cdd6f4' }}>{flowName}</strong>
|
|
1152
1201
|
</span>
|
|
1153
1202
|
)}
|
|
1154
|
-
<button
|
|
1155
|
-
style={loadFlowButtonStyle}
|
|
1156
|
-
onClick={() => setShowFlowBrowser(true)}
|
|
1157
|
-
>
|
|
1203
|
+
<button style={loadFlowButtonStyle} onClick={() => setShowFlowBrowser(true)}>
|
|
1158
1204
|
<FolderIcon /> Load Flow
|
|
1159
1205
|
</button>
|
|
1160
|
-
<button
|
|
1161
|
-
style={runFlowButtonStyle}
|
|
1162
|
-
onClick={() => setShowExecutionPanel(true)}
|
|
1163
|
-
>
|
|
1206
|
+
<button style={runFlowButtonStyle} onClick={() => setShowExecutionPanel(true)}>
|
|
1164
1207
|
<PlayIcon /> Run Flow
|
|
1165
1208
|
</button>
|
|
1166
|
-
<button
|
|
1167
|
-
style={viewConfigButtonStyle}
|
|
1168
|
-
onClick={() => setShowDispatchPanel(true)}
|
|
1169
|
-
>
|
|
1209
|
+
<button style={viewConfigButtonStyle} onClick={() => setShowDispatchPanel(true)}>
|
|
1170
1210
|
<CodeIcon /> View Dispatch Config
|
|
1171
1211
|
</button>
|
|
1172
1212
|
</div>
|
|
@@ -1268,7 +1308,14 @@ export default function App() {
|
|
|
1268
1308
|
|
|
1269
1309
|
function CloseIcon() {
|
|
1270
1310
|
return (
|
|
1271
|
-
<svg
|
|
1311
|
+
<svg
|
|
1312
|
+
width="18"
|
|
1313
|
+
height="18"
|
|
1314
|
+
viewBox="0 0 24 24"
|
|
1315
|
+
fill="none"
|
|
1316
|
+
stroke="currentColor"
|
|
1317
|
+
strokeWidth="2"
|
|
1318
|
+
>
|
|
1272
1319
|
<line x1="18" y1="6" x2="6" y2="18" />
|
|
1273
1320
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
1274
1321
|
</svg>
|
|
@@ -1277,7 +1324,14 @@ function CloseIcon() {
|
|
|
1277
1324
|
|
|
1278
1325
|
function CopyIcon() {
|
|
1279
1326
|
return (
|
|
1280
|
-
<svg
|
|
1327
|
+
<svg
|
|
1328
|
+
width="14"
|
|
1329
|
+
height="14"
|
|
1330
|
+
viewBox="0 0 24 24"
|
|
1331
|
+
fill="none"
|
|
1332
|
+
stroke="currentColor"
|
|
1333
|
+
strokeWidth="2"
|
|
1334
|
+
>
|
|
1281
1335
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
1282
1336
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
1283
1337
|
</svg>
|
|
@@ -1286,7 +1340,14 @@ function CopyIcon() {
|
|
|
1286
1340
|
|
|
1287
1341
|
function CodeIcon() {
|
|
1288
1342
|
return (
|
|
1289
|
-
<svg
|
|
1343
|
+
<svg
|
|
1344
|
+
width="16"
|
|
1345
|
+
height="16"
|
|
1346
|
+
viewBox="0 0 24 24"
|
|
1347
|
+
fill="none"
|
|
1348
|
+
stroke="currentColor"
|
|
1349
|
+
strokeWidth="2"
|
|
1350
|
+
>
|
|
1290
1351
|
<polyline points="16 18 22 12 16 6" />
|
|
1291
1352
|
<polyline points="8 6 2 12 8 18" />
|
|
1292
1353
|
</svg>
|
|
@@ -1295,7 +1356,14 @@ function CodeIcon() {
|
|
|
1295
1356
|
|
|
1296
1357
|
function PlayIcon() {
|
|
1297
1358
|
return (
|
|
1298
|
-
<svg
|
|
1359
|
+
<svg
|
|
1360
|
+
width="16"
|
|
1361
|
+
height="16"
|
|
1362
|
+
viewBox="0 0 24 24"
|
|
1363
|
+
fill="none"
|
|
1364
|
+
stroke="currentColor"
|
|
1365
|
+
strokeWidth="2"
|
|
1366
|
+
>
|
|
1299
1367
|
<polygon points="5 3 19 12 5 21 5 3" />
|
|
1300
1368
|
</svg>
|
|
1301
1369
|
)
|
|
@@ -1303,7 +1371,14 @@ function PlayIcon() {
|
|
|
1303
1371
|
|
|
1304
1372
|
function FolderIcon() {
|
|
1305
1373
|
return (
|
|
1306
|
-
<svg
|
|
1374
|
+
<svg
|
|
1375
|
+
width="16"
|
|
1376
|
+
height="16"
|
|
1377
|
+
viewBox="0 0 24 24"
|
|
1378
|
+
fill="none"
|
|
1379
|
+
stroke="currentColor"
|
|
1380
|
+
strokeWidth="2"
|
|
1381
|
+
>
|
|
1307
1382
|
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
|
|
1308
1383
|
</svg>
|
|
1309
1384
|
)
|
|
@@ -1311,7 +1386,14 @@ function FolderIcon() {
|
|
|
1311
1386
|
|
|
1312
1387
|
function RefreshIcon() {
|
|
1313
1388
|
return (
|
|
1314
|
-
<svg
|
|
1389
|
+
<svg
|
|
1390
|
+
width="14"
|
|
1391
|
+
height="14"
|
|
1392
|
+
viewBox="0 0 24 24"
|
|
1393
|
+
fill="none"
|
|
1394
|
+
stroke="currentColor"
|
|
1395
|
+
strokeWidth="2"
|
|
1396
|
+
>
|
|
1315
1397
|
<polyline points="23 4 23 10 17 10" />
|
|
1316
1398
|
<polyline points="1 20 1 14 7 14" />
|
|
1317
1399
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
|
@@ -1338,7 +1420,14 @@ function SpinnerIcon() {
|
|
|
1338
1420
|
|
|
1339
1421
|
function CheckIcon() {
|
|
1340
1422
|
return (
|
|
1341
|
-
<svg
|
|
1423
|
+
<svg
|
|
1424
|
+
width="16"
|
|
1425
|
+
height="16"
|
|
1426
|
+
viewBox="0 0 24 24"
|
|
1427
|
+
fill="none"
|
|
1428
|
+
stroke="currentColor"
|
|
1429
|
+
strokeWidth="2"
|
|
1430
|
+
>
|
|
1342
1431
|
<polyline points="20 6 9 17 4 12" />
|
|
1343
1432
|
</svg>
|
|
1344
1433
|
)
|
|
@@ -1346,7 +1435,14 @@ function CheckIcon() {
|
|
|
1346
1435
|
|
|
1347
1436
|
function ErrorIcon() {
|
|
1348
1437
|
return (
|
|
1349
|
-
<svg
|
|
1438
|
+
<svg
|
|
1439
|
+
width="16"
|
|
1440
|
+
height="16"
|
|
1441
|
+
viewBox="0 0 24 24"
|
|
1442
|
+
fill="none"
|
|
1443
|
+
stroke="currentColor"
|
|
1444
|
+
strokeWidth="2"
|
|
1445
|
+
>
|
|
1350
1446
|
<circle cx="12" cy="12" r="10" />
|
|
1351
1447
|
<line x1="15" y1="9" x2="9" y2="15" />
|
|
1352
1448
|
<line x1="9" y1="9" x2="15" y2="15" />
|
|
@@ -1356,7 +1452,14 @@ function ErrorIcon() {
|
|
|
1356
1452
|
|
|
1357
1453
|
function PendingIcon() {
|
|
1358
1454
|
return (
|
|
1359
|
-
<svg
|
|
1455
|
+
<svg
|
|
1456
|
+
width="16"
|
|
1457
|
+
height="16"
|
|
1458
|
+
viewBox="0 0 24 24"
|
|
1459
|
+
fill="none"
|
|
1460
|
+
stroke="currentColor"
|
|
1461
|
+
strokeWidth="2"
|
|
1462
|
+
>
|
|
1360
1463
|
<circle cx="12" cy="12" r="10" />
|
|
1361
1464
|
</svg>
|
|
1362
1465
|
)
|
|
@@ -1679,10 +1782,10 @@ const stepHeaderStyle: React.CSSProperties = {
|
|
|
1679
1782
|
|
|
1680
1783
|
const stepBadgeStyle = (type: string): React.CSSProperties => {
|
|
1681
1784
|
const colors: Record<string, { bg: string; text: string }> = {
|
|
1682
|
-
|
|
1785
|
+
prompt: { bg: '#cba6f7', text: '#1e1e2e' },
|
|
1683
1786
|
'fetch-url': { bg: '#89b4fa', text: '#1e1e2e' },
|
|
1684
1787
|
'transform-data': { bg: '#f9e2af', text: '#1e1e2e' },
|
|
1685
|
-
|
|
1788
|
+
conditional: { bg: '#f5c2e7', text: '#1e1e2e' },
|
|
1686
1789
|
'send-email': { bg: '#a6e3a1', text: '#1e1e2e' },
|
|
1687
1790
|
}
|
|
1688
1791
|
const color = colors[type] || { bg: '#6c7086', text: '#cdd6f4' }
|