add basic condfig and rendering

This commit is contained in:
2026-03-05 23:46:12 +01:00
parent 37426258fb
commit 8e5147cfd1
8 changed files with 285 additions and 48 deletions

View File

@@ -0,0 +1,68 @@
import React from 'react'
import { Handle, Position } from 'reactflow'
import Editor, { loader } from '@monaco-editor/react'
import FlowContext from '../lib/flowContext'
// Ensure Monaco can load its web workers under Vite by pointing to a CDN
// This avoids the editor hanging while trying to locate worker scripts locally.
loader.config({ paths: { vs: 'https://unpkg.com/monaco-editor@latest/min/vs' } })
type Props = {
id: string
data: any
}
export default function ConfigNode({ id, data }: Props) {
const [value, setValue] = React.useState<string>(data?.yaml ?? "# Enter YAML here\n")
const ctx = React.useContext(FlowContext)
const setNodes = ctx?.setNodes
const storedYaml = ctx?.nodes?.find((n: any) => n.id === id)?.data?.yaml ?? ''
React.useEffect(() => {
// keep node data in sync on mount
if (setNodes) {
setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, yaml: value } } : n)))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const onChange = React.useCallback(
(val?: string) => {
const v = val ?? ''
setValue(v)
// debug log to help trace updates
// eslint-disable-next-line no-console
console.debug('ConfigNode:onChange', id, v.substring(0, 60))
if (setNodes) {
setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, yaml: v } } : n)))
}
},
[id, setNodes]
)
return (
<div className="w-80 bg-white rounded shadow hover:shadow-md border border-gray-200">
<div className="px-3 py-2 border-b border-gray-100 flex items-center justify-between">
<div className="text-sm font-medium">Configuration</div>
<div className="text-xs text-gray-400">YAML</div>
</div>
<div style={{ height: 180 }}>
<Editor
height="100%"
defaultLanguage="yaml"
value={value}
theme="vs-light"
onChange={onChange}
options={{ minimap: { enabled: false }, fontSize: 12 }}
/>
</div>
<div className="px-3 py-2 text-xs text-gray-500 border-t border-gray-100">
Stored YAML: {storedYaml ? `${storedYaml.length} chars` : 'none'}
</div>
<Handle type="source" position={Position.Right} id="out" style={{ background: '#10B981', right: -6 }} />
<Handle type="target" position={Position.Left} id="in" style={{ background: '#F97316', left: -6 }} />
</div>
)
}

View File

@@ -3,23 +3,29 @@ import { Handle, Position } from 'reactflow'
export default function CustomNode({ data }: any) {
return (
<div className="p-3 rounded-lg bg-white shadow-md border border-gray-200 w-48">
<div className="text-sm font-medium mb-2">{data.label}</div>
<div className="text-xs text-gray-500">Custom content</div>
<div className="p-3 rounded-lg bg-white shadow hover:shadow-md border border-gray-200 w-56">
<div className="flex items-center justify-between mb-2">
<div className="text-sm font-semibold">{data.label}</div>
<div className="text-xs text-gray-400">ID: {data?.id ?? ''}</div>
</div>
<Handle
type="target"
position={Position.Left}
id="a"
style={{ background: '#10B981', width: 10, height: 10 }}
/>
<div className="text-xs text-gray-500 mb-2">Custom content</div>
<Handle
type="source"
position={Position.Right}
id="b"
style={{ background: '#3B82F6', width: 10, height: 10 }}
/>
<div className="flex justify-between mt-2">
<Handle
type="target"
position={Position.Left}
id="a"
style={{ background: '#F97316', width: 12, height: 12, borderRadius: 3 }}
/>
<Handle
type="source"
position={Position.Right}
id="b"
style={{ background: '#10B981', width: 12, height: 12, borderRadius: 3 }}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,55 @@
import React from 'react'
import { Handle, Position } from 'reactflow'
import yaml from 'js-yaml'
import FlowContext from '../lib/flowContext'
type Props = {
id: string
data: any
}
export default function RenderingNode({ id }: Props) {
const [output, setOutput] = React.useState<string>('')
const ctx = React.useContext(FlowContext)
const nodes = ctx?.nodes ?? []
const edges = ctx?.edges ?? []
const incomingEdges = edges.filter((e: any) => e.target === id)
const incomingIds = incomingEdges.map((e: any) => e.source).sort()
const srcId = incomingIds.length > 0 ? incomingIds[0] : null
const srcNode = nodes.find((n: any) => n.id === srcId)
const yamlText = srcNode && srcNode.type === 'config' ? srcNode?.data?.yaml ?? '' : ''
React.useEffect(() => {
// debug
// eslint-disable-next-line no-console
console.debug('RenderingNode:selection', { id, incomingIds, srcNode: srcNode ? { id: srcNode.id, type: srcNode.type, yaml: (srcNode.data?.yaml ?? '').slice(0, 60) } : null })
if (!yamlText) {
if (incomingIds.length === 0) {
setOutput(`No configuration connected (nodes: ${nodes.length}, edges: ${edges.length})`)
return
}
setOutput('No YAML found on connected configuration node')
return
}
try {
const parsed = yaml.load(yamlText)
const newOutput = JSON.stringify(parsed, null, 2)
setOutput((prev) => (prev === newOutput ? prev : newOutput))
} catch (err: any) {
const msg = 'YAML parse error: ' + err.message
setOutput((prev) => (prev === msg ? prev : msg))
}
}, [id, incomingIds.join(','), yamlText, nodes.length, edges.length])
return (
<div className="w-72 bg-white rounded shadow-sm border p-3">
<div className="text-sm font-medium mb-2">Rendering</div>
<pre className="text-xs text-gray-800 max-h-40 overflow-auto">{output}</pre>
<Handle type="target" position={Position.Left} id="in" style={{ background: '#F97316' }} />
</div>
)
}