feat: enhance connection validation and node creation logic, improve type handling in various components

This commit is contained in:
2026-03-09 21:45:05 +01:00
parent 649d866bef
commit eb1413322f
14 changed files with 77 additions and 81 deletions

View File

@@ -43,7 +43,7 @@ import { NodeFooterEdgeIndicators } from '../base/NodeFooterEdgeIndicators'
import { NodeHeaderTitle } from '../base/NodeHeaderTitle'
import { NodeMenubar } from '../base/NodeMenubar'
export type ConfigNodeData = { configType?: ConfigTypeId; content?: string; title?: string }
export type ConfigNodeData = { configType?: ConfigTypeId; content?: string; title?: string; /** @deprecated use content */ plantuml?: string }
type Props = AbstractNodeProps<ConfigNodeData>
@@ -324,7 +324,7 @@ function ConfigNodeComponent({ id, data, width, height, selected }: Props) {
/>
</div>
<div ref={editorContainerRef} className="min-h-0 flex-1 w-full nodrag nopan overflow-hidden border-t border-input">
<div ref={editorContainerRef as React.RefObject<HTMLDivElement>} className="min-h-0 flex-1 w-full nodrag nopan overflow-hidden border-t border-input">
<CodeMirror
// @ts-expect-error ref is { view, state, editor }; package ref type not in our node_modules
ref={editorRef}

View File

@@ -131,7 +131,7 @@ function FunctionNodeComponent({ id, data, width, height, selected }: Props) {
}
/>
</div>
<div ref={editorContainerRef} className="min-h-0 flex-1 w-full nodrag nopan overflow-hidden border-t border-input">
<div ref={editorContainerRef as React.RefObject<HTMLDivElement>} className="min-h-0 flex-1 w-full nodrag nopan overflow-hidden border-t border-input">
<CodeMirror
// @ts-expect-error ref is { view, state, editor }; package ref type not in our node_modules
ref={editorRef}

View File

@@ -51,8 +51,8 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
const incomingIds = sourceIds
const srcId = incomingIds.length > 0 ? incomingIds[0] : null
const srcNode = nodes.find((n: any) => n.id === srcId)
const configTypeId = srcNode?.type === 'config' ? getConfigTypeId(srcNode.data) : 'plantuml'
const sourceContent = srcNode?.type === 'config' ? getConfigContent(srcNode.data) : ''
const configTypeId = srcNode?.type === 'config' ? getConfigTypeId((srcNode.data ?? undefined) as Record<string, unknown> | undefined) : 'plantuml'
const sourceContent = srcNode?.type === 'config' ? getConfigContent((srcNode.data ?? undefined) as Record<string, unknown> | undefined) : ''
const srcData = srcNode?.data ?? {}
/** Set of node IDs that can affect this render node (configs in the chain + variables/functions feeding them) */
@@ -94,7 +94,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
if (!node) return
visited.add(nodeId)
out.add(nodeId)
const content = getConfigContent(node.data)
const content = getConfigContent((node.data ?? undefined) as Record<string, unknown> | undefined)
for (const ref of getTemplateRefs(content)) {
const refId = resolveRef(ref)
if (refId && nodes.some((n: any) => n.id === refId && n.type === 'config') && isReachable(refId, id))
@@ -223,7 +223,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
throw new Error(`Referenced config not connected to renderer: ${templateName}`)
visited.add(refId)
configIdsUsed.add(refId)
const content = getConfigContent(node.data)
const content = getConfigContent((node.data ?? undefined) as Record<string, unknown> | undefined)
for (const ref of getTemplateRefs(content)) addConfigAndRefs(ref, visited)
}
@@ -238,7 +238,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
if (refId !== srcId && !isReachable(refId, id))
throw new Error(`Referenced config not connected to renderer: ${name}`)
return {
src: getConfigContent(node.data),
src: getConfigContent((node.data ?? undefined) as Record<string, unknown> | undefined),
path: name,
}
},
@@ -338,7 +338,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
for (const fid of functionIdsToRegister) {
const src = nodes.find((n: any) => n.id === fid)
if (!src || src.type !== 'function') continue
const body = src.data?.body ?? 'return args[0];'
const body = (src.data as { body?: string } | undefined)?.body ?? 'return args[0];'
const parsed = parseFunctionSignature(body)
const connectedVarIds = new Set<string>(functionConnectedVariableIds[fid] ?? [])
const connectedFuncIds = functionConnectedFunctionIds[fid] ?? []
@@ -432,7 +432,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
env.render(srcId!, nunjucksContext, async (nunjucksErr: Error | null, afterNunjucks: string) => {
if (cancelled || thisRunId !== runIdRef.current) return
if (nunjucksErr) {
setSvgContent(null)
setRenderedContent(null)
setError({ kind: 'render', message: `Nunjucks: ${nunjucksErr.message}` })
setLoading(false)
return
@@ -677,10 +677,10 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
</EmptyContent>
</Empty>
) : error ? (
srcData?.renderError ? (
srcData.renderError(error)
) : srcData?.errorHtml ? (
<div className="p-3 text-xs text-red-700 dark:text-red-400" dangerouslySetInnerHTML={{ __html: String(srcData.errorHtml) }} />
(srcData as { renderError?: (err: { kind: string; message: string }) => React.ReactNode; errorHtml?: string })?.renderError ? (
(srcData as { renderError: (err: { kind: string; message: string }) => React.ReactNode }).renderError(error)
) : (srcData as { errorHtml?: string })?.errorHtml ? (
<div className="p-3 text-xs text-red-700 dark:text-red-400" dangerouslySetInnerHTML={{ __html: String((srcData as { errorHtml: string }).errorHtml) }} />
) : (
<div className="flex flex-col gap-2 p-3">
<p className="text-xs text-red-700 dark:text-red-400">{error.message}</p>
@@ -706,7 +706,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
minScale={0.2}
maxScale={4}
centerOnInit
onInit={(ref) => ref?.centerView(1, 0, 0)}
onInit={(ref) => ref?.centerView(1, 200, 'easeOut')}
panning={{ disabled: true }}
wheel={{ disabled: true }}
doubleClick={{ disabled: true }}