Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions src/components/Editor/PipelineDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useContextPanel } from "@/providers/ContextPanelProvider";
import {
type InputSpec,
type OutputSpec,
type TypeSpecType,
typeSpecToString,
} from "@/utils/componentSpec";
import { getComponentFileFromList } from "@/utils/componentStore";
import { USER_PIPELINES_LIST_NAME } from "@/utils/constants";
Expand All @@ -36,17 +36,6 @@ const PipelineDetails = () => {

const [isYamlOpen, setIsYamlOpen] = useState(false);

// Utility function to convert TypeSpecType to string
const typeSpecToString = (typeSpec?: TypeSpecType): string => {
if (typeSpec === undefined) {
return "Any";
}
if (typeof typeSpec === "string") {
return typeSpec;
}
return JSON.stringify(typeSpec);
};

// State for file metadata
const [fileMeta, setFileMeta] = useState<{
creationTime?: Date;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useQuery } from "@tanstack/react-query";

import { hydrateComponentReference } from "@/services/componentService";
import type { TaskNodeData } from "@/types/taskNode";
import type { TaskNodeData } from "@/types/nodes";
import type { HydratedComponentReference } from "@/utils/componentSpec";
import { generateTaskSpec } from "@/utils/nodes/generateTaskSpec";
import { createEmptyTaskCallbacks } from "@/utils/nodes/taskCallbackUtils";

export const usePreviewTaskNodeData = (componentText: string) => {
const { data: componentRef, isLoading } = useQuery({
Expand All @@ -30,5 +31,8 @@ const generatePreviewTaskNodeData = (
taskId: previewTaskId,
isGhost: false,
readOnly: true,
connectable: false,
highlighted: false,
callbacks: createEmptyTaskCallbacks(),
};
};
12 changes: 6 additions & 6 deletions src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const useScheduleExecutionOnceWhenConditionMet = (
};

const FlowCanvas = ({
readOnly,
readOnly = false,
nodesConnectable,
children,
...rest
Expand Down Expand Up @@ -312,7 +312,7 @@ const FlowCanvas = ({
() => ({
connectable: !readOnly && !!nodesConnectable,
readOnly,
nodeCallbacks,
callbacks: nodeCallbacks,
}),
[readOnly, nodesConnectable, nodeCallbacks],
);
Expand Down Expand Up @@ -498,14 +498,14 @@ const FlowCanvas = ({
return;
}

const { taskSpec: droppedTask, taskType } = getTaskFromEvent(event);
const { taskSpec: droppedTask, nodeType } = getTaskFromEvent(event);

if (!taskType) {
if (!nodeType) {
console.error("Dropped task type not identified.");
return;
}

if (!droppedTask && taskType === "task") {
if (!droppedTask && nodeType === "task") {
console.error("Unable to find dropped task.");
return;
}
Expand Down Expand Up @@ -563,7 +563,7 @@ const FlowCanvas = ({
const position = getPositionFromEvent(event, reactFlowInstance);

const newSubgraphSpec = addTask(
taskType,
nodeType,
droppedTask,
position,
currentSubgraphSpec,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { memo, useMemo } from "react";

import { cn } from "@/lib/utils";
import { TaskNodeProvider } from "@/providers/TaskNodeProvider";
import type { TaskNodeData } from "@/types/taskNode";
import type { TaskNodeData } from "@/types/nodes";
import type { ComponentReference } from "@/utils/componentSpec";
import { generateTaskSpec } from "@/utils/nodes/generateTaskSpec";
import { createEmptyTaskCallbacks } from "@/utils/nodes/taskCallbackUtils";

import { TaskNodeCard } from "../TaskNode/TaskNodeCard";

Expand Down Expand Up @@ -74,5 +75,9 @@ const generateGhostTaskNodeData = (
taskSpec,
taskId: ghostTaskId,
isGhost: true,
readOnly: true,
highlighted: false,
connectable: false,
callbacks: createEmptyTaskCallbacks(),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type NodeProps, useReactFlow } from "@xyflow/react";
import { memo, type PropsWithChildren, useMemo } from "react";

import { cn } from "@/lib/utils";
import type { HintNodeData } from "@/types/hintNode";
import type { HintNodeData } from "@/types/nodes";

const HintNode = ({ data }: NodeProps) => {
const { getZoom } = useReactFlow();
Expand Down
48 changes: 18 additions & 30 deletions src/components/shared/ReactFlow/FlowCanvas/IONode/IONode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@ import { Paragraph } from "@/components/ui/typography";
import { cn } from "@/lib/utils";
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
import { useContextPanel } from "@/providers/ContextPanelProvider";
import type { IONodeData } from "@/types/nodes";
import { isInputSpec, typeSpecToString } from "@/utils/componentSpec";

interface IONodeProps {
type: "input" | "output";
data: {
label: string;
value?: string;
default?: string;
type?: string;
readOnly?: boolean;
};
data: IONodeData;
selected: boolean;
deletable: boolean;
}
Expand All @@ -28,10 +24,9 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
const { currentGraphSpec, currentSubgraphSpec } = useComponentSpec();
const { setContent, clearContent } = useContextPanel();

const isInput = type === "input";
const isOutput = type === "output";
const { spec, readOnly } = data;

const readOnly = !!data.readOnly;
const isInput = isInputSpec(spec, type);

const handleType = isInput ? "source" : "target";
const handlePosition = isInput ? Position.Right : Position.Left;
Expand All @@ -44,15 +39,14 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
const borderColor = selected ? selectedColor : defaultColor;

const input = useMemo(
() =>
currentSubgraphSpec.inputs?.find((input) => input.name === data.label),
[currentSubgraphSpec.inputs, data.label],
() => currentSubgraphSpec.inputs?.find((input) => input.name === spec.name),
[currentSubgraphSpec.inputs, spec.name],
);

const output = useMemo(
() =>
currentSubgraphSpec.outputs?.find((output) => output.name === data.label),
[currentSubgraphSpec.outputs, data.label],
currentSubgraphSpec.outputs?.find((output) => output.name === spec.name),
[currentSubgraphSpec.outputs, spec.name],
);

useEffect(() => {
Expand All @@ -67,7 +61,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
);
}

if (output && isOutput) {
if (output && !isInput) {
const outputConnectedDetails = getOutputConnectedDetails(
currentGraphSpec,
output.name,
Expand All @@ -92,7 +86,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {

const connectedOutput = getOutputConnectedDetails(
currentGraphSpec,
data.label,
spec.name,
);
const outputConnectedValue = connectedOutput.outputName;
const outputConnectedType = connectedOutput.outputType;
Expand All @@ -103,30 +97,24 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {

const handleClassName = isInput ? "translate-x-1.5" : "-translate-x-1.5";

const hasDataValue = !!data.value;
const hasDataDefault = !!data.default;

const inputValue = hasDataValue
? data.value
: hasDataDefault
? data.default
: null;

const inputValue = isInput ? spec.value || spec.default || null : null;
const outputValue = outputConnectedValue ?? null;

const value = isInput ? inputValue : outputValue;

const typeValue = isInput
? typeSpecToString(spec.type)
: typeSpecToString(outputConnectedType);

return (
<Card className={cn("border-2 max-w-[300px] p-0", borderColor)}>
<CardHeader className="px-2 py-2.5">
<CardTitle className="break-words">{data.label}</CardTitle>
<CardTitle className="break-words">{spec.name}</CardTitle>
</CardHeader>
<CardContent className="p-2 max-w-[250px]">
<BlockStack gap="2">
{/* type */}
<Paragraph size="xs" font="mono" className="truncate text-slate-700">
<span className="font-bold">Type:</span>{" "}
{outputConnectedType ?? data.type ?? "Any"}
<span className="font-bold">Type:</span> {typeValue}
</Paragraph>

{!!outputConnectedTaskId && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { MultilineTextInputDialog } from "@/components/shared/Dialogs/MultilineTextInputDialog";
import type { ArgumentInput } from "@/types/arguments";
import { typeSpecToString } from "@/utils/componentSpec";

import { getInputValue, typeSpecToString } from "./utils";
import { getInputValue } from "./utils";

export const ArgumentInputDialog = ({
argument,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,10 @@ import { useCallbackOnUnmount } from "@/hooks/useCallbackOnUnmount";
import useToastNotification from "@/hooks/useToastNotification";
import { cn } from "@/lib/utils";
import type { ArgumentInput } from "@/types/arguments";
import { typeSpecToString } from "@/utils/componentSpec";

import { ArgumentInputDialog } from "./ArgumentInputDialog";
import {
getDefaultValue,
getInputValue,
getPlaceholder,
typeSpecToString,
} from "./utils";
import { getDefaultValue, getInputValue, getPlaceholder } from "./utils";

export const ArgumentInputField = ({
argument,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { ArgumentInput } from "@/types/arguments";
import type {
ArgumentType,
TaskSpec,
TypeSpecType,
} from "@/utils/componentSpec";
import type { ArgumentType, TaskSpec } from "@/utils/componentSpec";

export const getArgumentInputs = (taskSpec: TaskSpec) => {
const componentSpec = taskSpec.componentRef.spec;
Expand Down Expand Up @@ -40,16 +36,6 @@ export const getArgumentInputs = (taskSpec: TaskSpec) => {
return argumentInputs;
};

export const typeSpecToString = (typeSpec?: TypeSpecType): string => {
if (typeSpec === undefined) {
return "Any";
}
if (typeof typeSpec === "string") {
return typeSpec;
}
return JSON.stringify(typeSpec);
};

export const getPlaceholder = (argument: ArgumentType) => {
if (typeof argument === "string" || !argument) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { memo, useMemo } from "react";
import type { ContainerExecutionStatus } from "@/api/types.gen";
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
import { TaskNodeProvider } from "@/providers/TaskNodeProvider";
import type { TaskNodeData } from "@/types/taskNode";
import type { TaskNodeData } from "@/types/nodes";
import { isCacheDisabled } from "@/utils/cache";

import { StatusIndicator } from "./StatusIndicator";
Expand Down
25 changes: 13 additions & 12 deletions src/components/shared/ReactFlow/FlowCanvas/utils/addTask.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { XYPosition } from "@xyflow/react";

import type { TaskType } from "@/types/taskNode";
import type {
ComponentSpec,
GraphSpec,
InputSpec,
OutputSpec,
TaskSpec,
import type { NodeType } from "@/types/nodes";
import {
type ComponentSpec,
type GraphSpec,
type InputSpec,
isGraphImplementation,
type OutputSpec,
type TaskSpec,
} from "@/utils/componentSpec";
import { deepClone } from "@/utils/deepClone";
import {
Expand All @@ -16,14 +17,14 @@ import {
} from "@/utils/unique";

const addTask = (
taskType: TaskType,
nodeType: NodeType,
taskSpec: TaskSpec | null,
position: XYPosition,
componentSpec: ComponentSpec,
): ComponentSpec => {
const newComponentSpec = deepClone(componentSpec);

if (!("graph" in newComponentSpec.implementation)) {
if (!isGraphImplementation(newComponentSpec.implementation)) {
console.error("Implementation does not contain a graph.");
return newComponentSpec;
}
Expand All @@ -34,7 +35,7 @@ const addTask = (
"editor.position": JSON.stringify(nodePosition),
};

if (taskType === "task") {
if (nodeType === "task") {
if (!taskSpec) {
console.error("A taskSpec is required to create a task node.");
return newComponentSpec;
Expand Down Expand Up @@ -77,7 +78,7 @@ const addTask = (
newComponentSpec.implementation.graph = newGraphSpec;
}

if (taskType === "input") {
if (nodeType === "input") {
const inputId = getUniqueInputName(newComponentSpec);
const inputSpec: InputSpec = {
name: inputId,
Expand All @@ -88,7 +89,7 @@ const addTask = (
newComponentSpec.inputs = inputs;
}

if (taskType === "output") {
if (nodeType === "output") {
const outputId = getUniqueOutputName(newComponentSpec);
const outputSpec: OutputSpec = {
name: outputId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Node } from "@xyflow/react";
import { describe, expect, it, vi } from "vitest";

import type { NodeCallbacks, TaskNodeData } from "@/types/taskNode";
import type { TaskNodeData } from "@/types/nodes";
import type {
ComponentSpec,
InputSpec,
Expand Down Expand Up @@ -86,15 +86,6 @@ const createMockTaskNodeCallbacks = () => ({
onUpgrade: vi.fn(),
});

const createMockNodeCallbacks = (): NodeCallbacks => ({
setArguments: vi.fn(),
setAnnotations: vi.fn(),
setCacheStaleness: vi.fn(),
onDelete: vi.fn(),
onDuplicate: vi.fn(),
onUpgrade: vi.fn(),
});

const createMockTaskNode = (
taskId: string,
taskSpec: TaskSpec,
Expand All @@ -108,8 +99,10 @@ const createMockTaskNode = (
taskId,
label: "Test Task",
highlighted: false,
readOnly: false,
isGhost: false,
connectable: true,
callbacks: createMockTaskNodeCallbacks(),
nodeCallbacks: createMockNodeCallbacks(),
},
selected: false,
dragging: false,
Expand Down
Loading