diff --git a/src/components/shared/ComponentEditor/components/PreviewTaskNodeCard.tsx b/src/components/shared/ComponentEditor/components/PreviewTaskNodeCard.tsx index f4c51bf7e..62640647b 100644 --- a/src/components/shared/ComponentEditor/components/PreviewTaskNodeCard.tsx +++ b/src/components/shared/ComponentEditor/components/PreviewTaskNodeCard.tsx @@ -1,13 +1,13 @@ -import { QueryErrorResetBoundary } from "@tanstack/react-query"; -import { Suspense, useRef } from "react"; +import { ReactFlowProvider } from "@xyflow/react"; +import { useRef } from "react"; import { ErrorBoundary } from "react-error-boundary"; import { Skeleton } from "@/components/ui/skeleton"; -import { TaskNodeProvider } from "@/providers/TaskNodeProvider"; -import type { TaskNodeData } from "@/types/taskNode"; +import type { TaskNodeViewProps } from "@/routes/v2/shared/nodes/TaskNode/TaskNode"; +import { TaskNodeCard } from "@/routes/v2/shared/nodes/TaskNode/TaskNodeCard"; +import { SpecProvider } from "@/routes/v2/shared/providers/SpecContext"; -import { TaskNodeCard } from "../../ReactFlow/FlowCanvas/TaskNode/TaskNodeCard"; -import { usePreviewTaskNodeData } from "../usePreviewTaskNodeData"; +import { buildPreviewTaskNodeViewProps } from "../utils/buildPreviewTaskNodeViewProps"; import { PointersEventBlock } from "./PointersEventBlock"; export const PreviewTaskNodeCard = ({ @@ -15,32 +15,28 @@ export const PreviewTaskNodeCard = ({ }: { componentText: string; }) => { - const previewNodeData = usePreviewTaskNodeData(componentText); - const lastValidDataRef = useRef(null); + const viewProps = buildPreviewTaskNodeViewProps(componentText); + const lastValidPropsRef = useRef(null); - if (previewNodeData) { - lastValidDataRef.current = previewNodeData; + if (viewProps) { + lastValidPropsRef.current = viewProps; } - const displayData = previewNodeData || lastValidDataRef.current; + const displayProps = viewProps ?? lastValidPropsRef.current; - if (!displayData) { + if (!displayProps) { return ; } return ( - - {({ reset }) => ( - null}> - - - - - - - - - )} - + null}> + + + + + + + + ); }; diff --git a/src/components/shared/ComponentEditor/usePreviewTaskNodeData.tsx b/src/components/shared/ComponentEditor/usePreviewTaskNodeData.tsx deleted file mode 100644 index a853108d4..000000000 --- a/src/components/shared/ComponentEditor/usePreviewTaskNodeData.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useQueryClient } from "@tanstack/react-query"; - -import type { TaskNodeData } from "@/types/taskNode"; -import type { HydratedComponentReference } from "@/utils/componentSpec"; -import { generateTaskSpec } from "@/utils/nodes/generateTaskSpec"; -import { componentSpecFromYaml } from "@/utils/yaml"; - -export const usePreviewTaskNodeData = ( - componentText: string, -): TaskNodeData | false => { - const queryClient = useQueryClient(); - - try { - const spec = componentSpecFromYaml(componentText); - const name = spec.name ?? "component-preview"; - - const digest = `preview:${name}`; - - const componentRef: HydratedComponentReference = { - text: componentText, - spec, - name, - digest, - }; - - queryClient.setQueryData( - ["component", "hydrate", `digest:${digest}`], - componentRef, - ); - - const taskSpec = generateTaskSpec(componentRef); - return { - taskSpec, - taskId: `preview-${name}`, - isGhost: false, - readOnly: true, - }; - } catch { - return false; - } -}; diff --git a/src/components/shared/ComponentEditor/utils/buildPreviewTaskNodeViewProps.ts b/src/components/shared/ComponentEditor/utils/buildPreviewTaskNodeViewProps.ts new file mode 100644 index 000000000..c2dbc1cdb --- /dev/null +++ b/src/components/shared/ComponentEditor/utils/buildPreviewTaskNodeViewProps.ts @@ -0,0 +1,57 @@ +import type { TaskNodeViewProps } from "@/routes/v2/shared/nodes/TaskNode/TaskNode"; +import { AggregatorOutputType } from "@/types/aggregator"; +import { isGraphImplementation } from "@/utils/componentSpec"; +import { componentSpecFromYaml } from "@/utils/yaml"; + +const noop = () => {}; + +/** + * Maps raw component YAML into the pure-data props consumed by the v2 + * TaskNodeCard, so a single isolated component can be previewed without the + * editor's MobX spec model or ReactFlow node context. + */ +export function buildPreviewTaskNodeViewProps( + componentText: string, +): TaskNodeViewProps | null { + let spec; + try { + spec = componentSpecFromYaml(componentText); + } catch { + return null; + } + + return { + id: "preview", + entityId: "preview", + taskName: spec.name ?? "component-preview", + selected: false, + isHovered: false, + isSubgraph: isGraphImplementation(spec.implementation), + collapsed: false, + description: spec.description ?? "", + inputs: (spec.inputs ?? []).map((input) => ({ + name: input.name, + type: input.type, + optional: input.optional, + default: input.default, + })), + outputs: (spec.outputs ?? []).map((output) => ({ + name: output.name, + type: output.type, + })), + connectedInputNames: new Set(), + connectedOutputNames: new Set(), + annotations: [], + taskColor: undefined, + cacheDisabled: false, + digest: undefined, + inputDisplayValues: {}, + isAggregator: false, + outputType: AggregatorOutputType.JsonArray, + onOutputTypeChange: noop, + onNodeClick: noop, + onInputClick: noop, + onOutputClick: noop, + onHandleClick: noop, + }; +} diff --git a/src/utils/nodes/generateTaskSpec.ts b/src/utils/nodes/generateTaskSpec.ts deleted file mode 100644 index c7320c7c2..000000000 --- a/src/utils/nodes/generateTaskSpec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { EDITOR_POSITION_ANNOTATION } from "../annotations"; -import type { ComponentReference, TaskSpec } from "../componentSpec"; - -export const generateTaskSpec = ( - componentRef: ComponentReference, -): TaskSpec => { - return { - componentRef, - annotations: { - [EDITOR_POSITION_ANNOTATION]: JSON.stringify({ - x: 0, - y: 0, - }), - } as { [k: string]: unknown }, - }; -};