'use client'; import * as React from 'react'; import { cn } from '../../lib/utils'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; import { StatusBadge } from '../badge/StatusBadge'; import { EmptyState } from '../states/EmptyState'; import { NODE_STATUS_BADGE } from './exec-status'; import type { JsonSchema, NodeExecView, NodeTypeDef, WfNode } from './types'; export interface NodeInspectorProps { /** The selected node, or null when nothing is selected. */ node: WfNode | null | undefined; /** Node-type def supplying the parameter schema. */ nodeType?: NodeTypeDef; /** Emitted with the node id + full next parameter object on any change. */ onParametersChange?: (nodeId: string, parameters: Record) => void; /** When present, adds read-only Input/Output exec tabs. */ execution?: NodeExecView; className?: string; } function fieldId(nodeId: string, key: string): string { return `nodeparam-${nodeId}-${key}`; } /** Field label with a required-marker asterisk when the param is required. */ function FieldLabel({ id, label, required, }: { id: string; label: string; required?: boolean; }) { return ( ); } function SchemaField({ nodeId, name, schema, value, onChange, required, }: { nodeId: string; name: string; schema: JsonSchema; value: unknown; onChange: (next: unknown) => void; required?: boolean; }) { const id = fieldId(nodeId, name); const label = schema.title ?? name; if (schema.enum && schema.enum.length > 0) { return (
); } if (schema.type === 'boolean') { return (
onChange(e.target.checked)} className="h-4 w-4 rounded border-border" />
); } if (schema.type === 'number' || schema.type === 'integer') { return (
onChange(e.target.value === '' ? undefined : Number(e.target.value)) } className="rounded-md border border-border bg-background px-2 py-1.5 text-sm outline-none focus:ring-2 focus:ring-primary" />
); } // default: string return (
onChange(e.target.value)} className="rounded-md border border-border bg-background px-2 py-1.5 text-sm outline-none focus:ring-2 focus:ring-primary" /> {schema.description && (

{schema.description}

)}
); } function JsonBlock({ value }: { value: Record | null | undefined }) { const isEmpty = value == null || Object.keys(value).length === 0; if (isEmpty) { return

No data.

; } return (
      {JSON.stringify(value, null, 2)}
    
); } function ParamForm({ node, schema, onParametersChange, }: { node: WfNode; schema: JsonSchema | undefined; onParametersChange?: NodeInspectorProps['onParametersChange']; }) { const params = (node.parameters ?? {}) as Record; const properties = schema?.properties ?? {}; const keys = Object.keys(properties); const requiredKeys = new Set(schema?.required ?? []); const handleChange = (key: string, next: unknown) => { const updated = { ...params, [key]: next }; onParametersChange?.(node.id, updated); }; if (keys.length === 0) { return (

This node type has no configurable parameters.

); } return (
{keys.map((key) => ( handleChange(key, next)} /> ))}
); } /** * Editor inspector panel for the selected node. Renders a JSON-Schema-driven * read/write form from the node-type's `parameterSchema` (string/number/ * boolean/enum fields), emitting the full next `parameters` object via * `onParametersChange`. When an {@link NodeExecView} is supplied, adds * read-only Input/Output tabs alongside the parameters tab. */ export function NodeInspector({ node, nodeType, onParametersChange, execution, className, }: NodeInspectorProps) { if (!node) { return ( ); } const header = (

{node.name}

{execution && ( )}
{node.type}
); const paramForm = (
); if (!execution) { return (
{header} {paramForm}
); } return (
{header} Parameters Input Output {paramForm}
); }