Skip to content
Open
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
1 change: 1 addition & 0 deletions react-compiler.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const REACT_COMPILER_ENABLED_DIRS = [
"src/components/shared/ManageComponent/DeprecatePublishedComponentButton.tsx",
"src/components/shared/ManageComponent/PublishComponent.tsx",
"src/components/shared/ManageComponent/hooks/useComponentCanvasTasks.ts",
"src/components/shared/TaskDetails/Actions/UnpackSubgraphButton.tsx",

// 11-20 useCallback/useMemo
// "src/components/ui", // 12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ interface AddTaskResult {
* Options for creating input/output nodes.
* Omits position-related fields (annotations) which are automatically set.
*/
type IONodeOptions = Omit<Partial<InputSpec>, "annotations">;
type IONodeOptions =
| Omit<Partial<InputSpec>, "annotations">
| Omit<Partial<OutputSpec>, "annotations">;

/**
* Creates a task, input, or output node and adds it to the component specification.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export const removeGraphOutput = (
return componentSpec;
};

const removeTask = (taskIdToRemove: string, componentSpec: ComponentSpec) => {
export const removeTask = (
taskIdToRemove: string,
componentSpec: ComponentSpec,
) => {
if (isGraphImplementation(componentSpec.implementation)) {
const graphSpec = componentSpec.implementation.graph;

Expand Down
5 changes: 5 additions & 0 deletions src/components/shared/TaskDetails/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DownloadYamlButton } from "./Actions/DownloadYamlButton";
import { DuplicateTaskButton } from "./Actions/DuplicateTaskButton";
import { EditComponentButton } from "./Actions/EditComponentButton";
import { NavigateToSubgraphButton } from "./Actions/NavigateToSubgraphButton";
import { UnpackSubgraphButton } from "./Actions/UnpackSubgraphButton";
import { UpgradeTaskButton } from "./Actions/UpgradeTaskButton";

interface TaskActionsProps {
Expand Down Expand Up @@ -59,6 +60,9 @@ const TaskActions = ({
const navigateToSubgraph = isSubgraphNode && taskId && !readOnly && (
<NavigateToSubgraphButton taskId={taskId} />
);
const unpackSubgraphButton = isSubgraphNode && taskId && !readOnly && (
<UnpackSubgraphButton taskId={taskId} />
);
const deleteComponent = onDelete && !readOnly && (
<DeleteComponentButton onDelete={onDelete} />
);
Expand All @@ -72,6 +76,7 @@ const TaskActions = ({
duplicateTask,
upgradeTask,
navigateToSubgraph,
unpackSubgraphButton,
deleteComponent,
].filter(Boolean);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState } from "react";

import { useComponentSpec } from "@/providers/ComponentSpecProvider";
import { unpackSubgraph } from "@/utils/nodes/unpacking/unpackSubgraph";
import {
getSubgraphDescription,
updateSubgraphSpec,
} from "@/utils/subgraphUtils";

import { ActionButton } from "../../Buttons/ActionButton";
import { removeTask } from "../../ReactFlow/FlowCanvas/utils/removeNode";

interface UnpackSubgraphButtonProps {
taskId: string;
}

export const UnpackSubgraphButton = ({ taskId }: UnpackSubgraphButtonProps) => {
const {
currentGraphSpec,
currentSubgraphSpec,
currentSubgraphPath,
componentSpec,
setComponentSpec,
} = useComponentSpec();

const [isLoading, setIsLoading] = useState(false);

const taskSpec = currentGraphSpec.tasks[taskId];
const subgraphDescription = taskSpec ? getSubgraphDescription(taskSpec) : "";

function handleUnpackSubgraph() {
setIsLoading(true);
const updatedSubgraphSpec = unpackSubgraph(taskId, currentSubgraphSpec);

const cleanedSubgraphSpec = removeTask(taskId, updatedSubgraphSpec);

const newRootSpec = updateSubgraphSpec(
componentSpec,
currentSubgraphPath,
cleanedSubgraphSpec,
);

setComponentSpec(newRootSpec);
setIsLoading(false);
}

return (
<ActionButton
tooltip={`Unpack Subgraph: ${subgraphDescription}`}
icon="PackageOpen"
onClick={handleUnpackSubgraph}
disabled={isLoading}
/>
);
};
65 changes: 65 additions & 0 deletions src/utils/graphUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { XYPosition } from "@xyflow/react";

import {
type ComponentSpec,
type GraphSpec,
isGraphImplementation,
} from "./componentSpec";
import { extractPositionFromAnnotations } from "./nodes/extractPositionFromAnnotations";

export const calculateSpecCenter = (
componentSpec: ComponentSpec,
): XYPosition => {
if (!isGraphImplementation(componentSpec.implementation)) {
return { x: 0, y: 0 };
}

const graphSpec: GraphSpec = componentSpec.implementation.graph;

const allPositions: XYPosition[] = [];

// Collect positions from tasks
Object.values(graphSpec.tasks).forEach((task) => {
const taskPosition = extractPositionFromAnnotations(task.annotations);
if (taskPosition) {
allPositions.push(taskPosition);
}
});

// Collect positions from inputs
componentSpec.inputs?.forEach((input) => {
const inputPosition = extractPositionFromAnnotations(input.annotations);
if (inputPosition) {
allPositions.push(inputPosition);
}
});

// Collect positions from outputs
componentSpec.outputs?.forEach((output) => {
const outputPosition = extractPositionFromAnnotations(output.annotations);
if (outputPosition) {
allPositions.push(outputPosition);
}
});

if (allPositions.length === 0) {
return { x: 0, y: 0 };
}

const sumX = allPositions.reduce((sum, pos) => sum + pos.x, 0);
const sumY = allPositions.reduce((sum, pos) => sum + pos.y, 0);

return {
x: sumX / allPositions.length,
y: sumY / allPositions.length,
};
};

export const normalizeNodePositionInGroup = (
nodePosition: XYPosition | undefined,
groupPosition: XYPosition | undefined,
groupCenter: XYPosition,
): XYPosition => ({
x: (groupPosition?.x || 0) + (nodePosition?.x || 0) - groupCenter.x,
y: (groupPosition?.y || 0) + (nodePosition?.y || 0) - groupCenter.y,
});
Loading