@runtypelabs/react-flow 0.1.4 → 0.1.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.
@@ -6,6 +6,5 @@ import '@xyflow/react/dist/style.css'
6
6
  ReactDOM.createRoot(document.getElementById('root')!).render(
7
7
  <React.StrictMode>
8
8
  <App />
9
- </React.StrictMode>,
9
+ </React.StrictMode>
10
10
  )
11
-
@@ -18,4 +18,3 @@
18
18
  },
19
19
  "include": ["src"]
20
20
  }
21
-
@@ -10,4 +10,3 @@ export default defineConfig({
10
10
  dedupe: ['react', 'react-dom'],
11
11
  },
12
12
  })
13
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/react-flow",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "React Flow adapter for building visual flow editors with Runtype",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -16,7 +16,7 @@
16
16
  "*.css"
17
17
  ],
18
18
  "dependencies": {
19
- "@runtypelabs/sdk": "0.3.0"
19
+ "@runtypelabs/sdk": "0.5.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "@xyflow/react": "^12.0.0",
@@ -10,12 +10,7 @@ import {
10
10
  ConnectionLineType,
11
11
  } from '@xyflow/react'
12
12
 
13
- import type {
14
- RuntypeFlowEditorProps,
15
- RuntypeNode,
16
- FlowStep,
17
- SupportedNodeType,
18
- } from '../types'
13
+ import type { RuntypeFlowEditorProps, RuntypeNode, FlowStep, SupportedNodeType } from '../types'
19
14
  import { useRuntypeFlow } from '../hooks/useRuntypeFlow'
20
15
  import { useFlowValidation } from '../hooks/useFlowValidation'
21
16
  import { PromptNode } from './nodes/PromptNode'
@@ -159,9 +154,13 @@ const nodeTypes: NodeTypes = {
159
154
  }
160
155
 
161
156
  // Default node for unsupported step types
162
- function DefaultNode(props: { data: { step: FlowStep; label: string }; selected?: boolean; id: string }) {
157
+ function DefaultNode(props: {
158
+ data: { step: FlowStep; label: string }
159
+ selected?: boolean
160
+ id: string
161
+ }) {
163
162
  const { data, selected, id } = props
164
-
163
+
165
164
  return (
166
165
  <BaseNode
167
166
  data={data as any}
@@ -361,9 +360,7 @@ export function RuntypeFlowEditor({
361
360
  }}
362
361
  />
363
362
 
364
- {hasUnsavedChanges && (
365
- <span style={styles.unsavedBadge}>Unsaved</span>
366
- )}
363
+ {hasUnsavedChanges && <span style={styles.unsavedBadge}>Unsaved</span>}
367
364
 
368
365
  <div style={styles.toolbarDivider} />
369
366
 
@@ -438,9 +435,7 @@ export function RuntypeFlowEditor({
438
435
  </span>
439
436
  <span>{nodes.length} steps</span>
440
437
  {isLoading && <span>Loading...</span>}
441
- {error && (
442
- <span style={{ color: '#dc2626' }}>Error: {error.message}</span>
443
- )}
438
+ {error && <span style={{ color: '#dc2626' }}>Error: {error.message}</span>}
444
439
  </div>
445
440
  </Panel>
446
441
  </ReactFlow>
@@ -469,7 +464,14 @@ export function RuntypeFlowEditor({
469
464
 
470
465
  function PlusIcon() {
471
466
  return (
472
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
467
+ <svg
468
+ width="14"
469
+ height="14"
470
+ viewBox="0 0 24 24"
471
+ fill="none"
472
+ stroke="currentColor"
473
+ strokeWidth="2"
474
+ >
473
475
  <line x1="12" y1="5" x2="12" y2="19" />
474
476
  <line x1="5" y1="12" x2="19" y2="12" />
475
477
  </svg>
@@ -478,7 +480,14 @@ function PlusIcon() {
478
480
 
479
481
  function PromptIcon() {
480
482
  return (
481
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
483
+ <svg
484
+ width="14"
485
+ height="14"
486
+ viewBox="0 0 24 24"
487
+ fill="none"
488
+ stroke="currentColor"
489
+ strokeWidth="2"
490
+ >
482
491
  <path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" />
483
492
  <path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" />
484
493
  </svg>
@@ -487,7 +496,14 @@ function PromptIcon() {
487
496
 
488
497
  function GlobeIcon() {
489
498
  return (
490
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
499
+ <svg
500
+ width="14"
501
+ height="14"
502
+ viewBox="0 0 24 24"
503
+ fill="none"
504
+ stroke="currentColor"
505
+ strokeWidth="2"
506
+ >
491
507
  <circle cx="12" cy="12" r="10" />
492
508
  <path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
493
509
  <path d="M2 12h20" />
@@ -497,7 +513,14 @@ function GlobeIcon() {
497
513
 
498
514
  function CodeIcon() {
499
515
  return (
500
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
516
+ <svg
517
+ width="14"
518
+ height="14"
519
+ viewBox="0 0 24 24"
520
+ fill="none"
521
+ stroke="currentColor"
522
+ strokeWidth="2"
523
+ >
501
524
  <polyline points="16 18 22 12 16 6" />
502
525
  <polyline points="8 6 2 12 8 18" />
503
526
  </svg>
@@ -506,7 +529,14 @@ function CodeIcon() {
506
529
 
507
530
  function BranchIcon() {
508
531
  return (
509
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
532
+ <svg
533
+ width="14"
534
+ height="14"
535
+ viewBox="0 0 24 24"
536
+ fill="none"
537
+ stroke="currentColor"
538
+ strokeWidth="2"
539
+ >
510
540
  <line x1="6" y1="3" x2="6" y2="15" />
511
541
  <circle cx="18" cy="6" r="3" />
512
542
  <circle cx="6" cy="18" r="3" />
@@ -517,7 +547,14 @@ function BranchIcon() {
517
547
 
518
548
  function MailIcon() {
519
549
  return (
520
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
550
+ <svg
551
+ width="14"
552
+ height="14"
553
+ viewBox="0 0 24 24"
554
+ fill="none"
555
+ stroke="currentColor"
556
+ strokeWidth="2"
557
+ >
521
558
  <rect width="20" height="16" x="2" y="4" rx="2" />
522
559
  <path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
523
560
  </svg>
@@ -525,4 +562,3 @@ function MailIcon() {
525
562
  }
526
563
 
527
564
  export default RuntypeFlowEditor
528
-
@@ -134,12 +134,12 @@ const styles = {
134
134
  // ============================================================================
135
135
 
136
136
  export const NODE_HEADER_COLORS: Record<string, string> = {
137
- prompt: '#f3e8ff', // Purple tint
138
- 'fetch-url': '#dbeafe', // Blue tint
137
+ prompt: '#f3e8ff', // Purple tint
138
+ 'fetch-url': '#dbeafe', // Blue tint
139
139
  'transform-data': '#fef3c7', // Amber tint
140
- conditional: '#fce7f3', // Pink tint
140
+ conditional: '#fce7f3', // Pink tint
141
141
  'send-email': '#d1fae5', // Green tint
142
- default: '#f3f4f6', // Gray tint
142
+ default: '#f3f4f6', // Gray tint
143
143
  }
144
144
 
145
145
  // ============================================================================
@@ -211,7 +211,9 @@ export const BaseNode = memo(function BaseNode({
211
211
  </div>
212
212
 
213
213
  {/* Body - nodrag class prevents React Flow from intercepting events */}
214
- <div style={styles.body} className="nodrag">{children}</div>
214
+ <div style={styles.body} className="nodrag">
215
+ {children}
216
+ </div>
215
217
 
216
218
  {/* Source Handle (output) - on right for horizontal flow */}
217
219
  {showSourceHandle && (
@@ -354,4 +356,3 @@ export const MailIcon = () => (
354
356
  )
355
357
 
356
358
  export default BaseNode
357
-
@@ -108,7 +108,7 @@ export const CodeNode = memo(function CodeNode(props: NodeProps) {
108
108
  const { data, selected, id } = props as NodeProps & { data: RuntypeNodeData }
109
109
  const { step, onChange } = data
110
110
  const config = step.config as TransformDataStepConfig
111
-
111
+
112
112
  const [isExpanded, setIsExpanded] = useState(false)
113
113
 
114
114
  const handleChange = useCallback(
@@ -154,7 +154,14 @@ export const CodeNode = memo(function CodeNode(props: NodeProps) {
154
154
  </div>
155
155
 
156
156
  <div style={styles.field}>
157
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
157
+ <div
158
+ style={{
159
+ display: 'flex',
160
+ justifyContent: 'space-between',
161
+ alignItems: 'center',
162
+ marginBottom: '4px',
163
+ }}
164
+ >
158
165
  <label style={styles.label}>Code</label>
159
166
  <span style={styles.languageBadge(language)}>{language}</span>
160
167
  </div>
@@ -249,4 +256,3 @@ export const CodeNode = memo(function CodeNode(props: NodeProps) {
249
256
  })
250
257
 
251
258
  export default CodeNode
252
-
@@ -91,7 +91,7 @@ export const ConditionalNode = memo(function ConditionalNode(props: NodeProps) {
91
91
  const { data, selected, id } = props as NodeProps & { data: RuntypeNodeData }
92
92
  const { step, onChange } = data
93
93
  const config = step.config as ConditionalStepConfig
94
-
94
+
95
95
  const [isExpanded, setIsExpanded] = useState(false)
96
96
 
97
97
  const handleChange = useCallback(
@@ -174,18 +174,14 @@ export const ConditionalNode = memo(function ConditionalNode(props: NodeProps) {
174
174
  <TrueIcon /> True Branch
175
175
  </div>
176
176
  <div style={styles.branchCount}>{trueStepsCount}</div>
177
- <div style={styles.branchCountLabel}>
178
- step{trueStepsCount !== 1 ? 's' : ''}
179
- </div>
177
+ <div style={styles.branchCountLabel}>step{trueStepsCount !== 1 ? 's' : ''}</div>
180
178
  </div>
181
179
  <div style={styles.branchCard('false')}>
182
180
  <div style={styles.branchLabel('false')}>
183
181
  <FalseIcon /> False Branch
184
182
  </div>
185
183
  <div style={styles.branchCount}>{falseStepsCount}</div>
186
- <div style={styles.branchCountLabel}>
187
- step{falseStepsCount !== 1 ? 's' : ''}
188
- </div>
184
+ <div style={styles.branchCountLabel}>step{falseStepsCount !== 1 ? 's' : ''}</div>
189
185
  </div>
190
186
  </div>
191
187
 
@@ -194,10 +190,18 @@ export const ConditionalNode = memo(function ConditionalNode(props: NodeProps) {
194
190
  <div style={styles.field}>
195
191
  <label style={styles.label}>Condition Examples</label>
196
192
  <div style={{ fontSize: '11px', color: '#6b7280', lineHeight: 1.6 }}>
197
- <div><code>status === 'active'</code> - Check equality</div>
198
- <div><code>count {'>'} 10</code> - Numeric comparison</div>
199
- <div><code>data && data.length {'>'} 0</code> - Check array</div>
200
- <div><code>_record.metadata.type === 'premium'</code> - API input</div>
193
+ <div>
194
+ <code>status === 'active'</code> - Check equality
195
+ </div>
196
+ <div>
197
+ <code>count {'>'} 10</code> - Numeric comparison
198
+ </div>
199
+ <div>
200
+ <code>data && data.length {'>'} 0</code> - Check array
201
+ </div>
202
+ <div>
203
+ <code>_record.metadata.type === 'premium'</code> - API input
204
+ </div>
201
205
  </div>
202
206
  </div>
203
207
  </div>
@@ -261,4 +265,3 @@ const FalseIcon = () => (
261
265
  )
262
266
 
263
267
  export default ConditionalNode
264
-
@@ -101,7 +101,7 @@ export const FetchUrlNode = memo(function FetchUrlNode(props: NodeProps) {
101
101
  const { data, selected, id } = props as NodeProps & { data: RuntypeNodeData }
102
102
  const { step, onChange } = data
103
103
  const config = step.config as FetchUrlStepConfig
104
-
104
+
105
105
  const [isExpanded, setIsExpanded] = useState(false)
106
106
 
107
107
  const handleChange = useCallback(
@@ -164,7 +164,14 @@ export const FetchUrlNode = memo(function FetchUrlNode(props: NodeProps) {
164
164
  </div>
165
165
 
166
166
  <div style={styles.field}>
167
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
167
+ <div
168
+ style={{
169
+ display: 'flex',
170
+ justifyContent: 'space-between',
171
+ alignItems: 'center',
172
+ marginBottom: '4px',
173
+ }}
174
+ >
168
175
  <label style={styles.label}>Method</label>
169
176
  <span style={styles.methodBadge(method)}>{method}</span>
170
177
  </div>
@@ -191,11 +198,7 @@ export const FetchUrlNode = memo(function FetchUrlNode(props: NodeProps) {
191
198
  placeholder="https://api.example.com/endpoint"
192
199
  onKeyDown={(e) => e.stopPropagation()}
193
200
  />
194
- {config.http?.url && (
195
- <div style={styles.urlPreview}>
196
- {config.http.url}
197
- </div>
198
- )}
201
+ {config.http?.url && <div style={styles.urlPreview}>{config.http.url}</div>}
199
202
  </div>
200
203
 
201
204
  {isExpanded && (
@@ -296,4 +299,3 @@ export const FetchUrlNode = memo(function FetchUrlNode(props: NodeProps) {
296
299
  })
297
300
 
298
301
  export default FetchUrlNode
299
-
@@ -94,7 +94,7 @@ export const PromptNode = memo(function PromptNode(props: NodeProps) {
94
94
  const { data, selected, id } = props as NodeProps & { data: RuntypeNodeData }
95
95
  const { step, onChange } = data
96
96
  const config = step.config as PromptStepConfig
97
-
97
+
98
98
  const [isExpanded, setIsExpanded] = useState(false)
99
99
 
100
100
  const handleChange = useCallback(
@@ -147,11 +147,16 @@ export const PromptNode = memo(function PromptNode(props: NodeProps) {
147
147
  </div>
148
148
 
149
149
  <div style={styles.field}>
150
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
150
+ <div
151
+ style={{
152
+ display: 'flex',
153
+ justifyContent: 'space-between',
154
+ alignItems: 'center',
155
+ marginBottom: '4px',
156
+ }}
157
+ >
151
158
  <label style={styles.label}>Mode</label>
152
- <span style={styles.modeBadge(isAgentMode)}>
153
- {isAgentMode ? 'Agent' : 'Instruction'}
154
- </span>
159
+ <span style={styles.modeBadge(isAgentMode)}>{isAgentMode ? 'Agent' : 'Instruction'}</span>
155
160
  </div>
156
161
  <select
157
162
  style={styles.select}
@@ -267,4 +272,3 @@ export const PromptNode = memo(function PromptNode(props: NodeProps) {
267
272
  })
268
273
 
269
274
  export default PromptNode
270
-
@@ -101,7 +101,7 @@ export const SendEmailNode = memo(function SendEmailNode(props: NodeProps) {
101
101
  const { data, selected, id } = props as NodeProps & { data: RuntypeNodeData }
102
102
  const { step, onChange } = data
103
103
  const config = step.config as SendEmailStepConfig
104
-
104
+
105
105
  const [isExpanded, setIsExpanded] = useState(false)
106
106
 
107
107
  const handleChange = useCallback(
@@ -155,9 +155,7 @@ export const SendEmailNode = memo(function SendEmailNode(props: NodeProps) {
155
155
  placeholder="recipient@example.com or {{_record.metadata.email}}"
156
156
  onKeyDown={(e) => e.stopPropagation()}
157
157
  />
158
- {hasVariables(config.to || '') && (
159
- <span style={styles.variableHint}>Uses variable</span>
160
- )}
158
+ {hasVariables(config.to || '') && <span style={styles.variableHint}>Uses variable</span>}
161
159
  </div>
162
160
 
163
161
  <div style={styles.field}>
@@ -308,4 +306,3 @@ export const SendEmailNode = memo(function SendEmailNode(props: NodeProps) {
308
306
  })
309
307
 
310
308
  export default SendEmailNode
311
-
@@ -385,10 +385,7 @@ export function useFlowValidation(options: UseFlowValidationOptions): UseFlowVal
385
385
  }, [steps])
386
386
 
387
387
  // Helper functions
388
- const validateStepFn = useCallback(
389
- (step: FlowStep) => validateStep(step, steps),
390
- [steps]
391
- )
388
+ const validateStepFn = useCallback((step: FlowStep) => validateStep(step, steps), [steps])
392
389
 
393
390
  const isStepValid = useCallback(
394
391
  (stepId: string) => {
@@ -421,4 +418,3 @@ export function useFlowValidation(options: UseFlowValidationOptions): UseFlowVal
421
418
  }
422
419
 
423
420
  export default useFlowValidation
424
-
@@ -9,13 +9,7 @@ import {
9
9
  type Connection,
10
10
  } from '@xyflow/react'
11
11
  import type { RuntypeClient } from '@runtypelabs/sdk'
12
- import type {
13
- FlowStep,
14
- RuntypeNode,
15
- RuntypeEdge,
16
- SavedFlow,
17
- UseRuntypeFlowReturn,
18
- } from '../types'
12
+ import type { FlowStep, RuntypeNode, RuntypeEdge, SavedFlow, UseRuntypeFlowReturn } from '../types'
19
13
  import {
20
14
  flowStepsToNodes,
21
15
  nodesToFlowSteps,
@@ -111,9 +105,7 @@ export function useRuntypeFlow(options: UseRuntypeFlowOptions): UseRuntypeFlowRe
111
105
  const handleStepDelete = useCallback(
112
106
  (stepId: string) => {
113
107
  setNodes((nds) => nds.filter((node) => node.id !== stepId))
114
- setEdges((eds) =>
115
- eds.filter((edge) => edge.source !== stepId && edge.target !== stepId)
116
- )
108
+ setEdges((eds) => eds.filter((edge) => edge.source !== stepId && edge.target !== stepId))
117
109
  setHasUnsavedChanges(true)
118
110
  },
119
111
  [setNodes, setEdges]
@@ -197,7 +189,7 @@ export function useRuntypeFlow(options: UseRuntypeFlowOptions): UseRuntypeFlowRe
197
189
  await client.flows.update(flowId, {
198
190
  name: flowName,
199
191
  description: flowDescription,
200
- flow_steps: steps.map(step => ({
192
+ flow_steps: steps.map((step) => ({
201
193
  type: step.type,
202
194
  name: step.name,
203
195
  order: step.order,
@@ -400,4 +392,3 @@ export function useRuntypeFlow(options: UseRuntypeFlowOptions): UseRuntypeFlowRe
400
392
  }
401
393
 
402
394
  export default useRuntypeFlow
403
-
package/src/index.ts CHANGED
@@ -25,4 +25,3 @@ export {
25
25
  cloneStep,
26
26
  } from './utils/adapter'
27
27
  export { autoLayout, centerNodes, snapToGrid, getNodesBoundingBox } from './utils/layout'
28
-
@@ -168,7 +168,7 @@ export const SUPPORTED_NODE_TYPES = [
168
168
  'send-email',
169
169
  ] as const
170
170
 
171
- export type SupportedNodeType = typeof SUPPORTED_NODE_TYPES[number]
171
+ export type SupportedNodeType = (typeof SUPPORTED_NODE_TYPES)[number]
172
172
 
173
173
  /**
174
174
  * Check if a step type has a custom node component
@@ -329,4 +329,3 @@ export interface StepAddEvent {
329
329
  step: FlowStep
330
330
  position: { x: number; y: number }
331
331
  }
332
-