Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export const BuilderActions = memo(() => {
flowID: parseAsString,
});
return (
<div className="absolute bottom-4 left-[50%] z-[100] flex -translate-x-1/2 items-center gap-4 rounded-full bg-white p-2 px-2 shadow-lg">
<div
data-id="builder-actions"
className="absolute bottom-4 left-[50%] z-[100] flex -translate-x-1/2 items-center gap-4 rounded-full bg-white p-2 px-2 shadow-lg"
>
<AgentOutputs flowID={flowID} />
<RunGraph flowID={flowID} />
<ScheduleGraph flowID={flowID} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const AgentOutputs = ({ flowID }: { flowID: string | null }) => {
<Button
variant="outline"
size="icon"
data-id="agent-outputs-button"
disabled={!flowID || !hasOutputs()}
>
<BookOpenIcon className="size-4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const RunGraph = ({ flowID }: { flowID: string | null }) => {
<Button
size="icon"
variant={isGraphRunning ? "destructive" : "primary"}
data-id={isGraphRunning ? "stop-graph-button" : "run-graph-button"}
onClick={isGraphRunning ? handleStopGraph : handleRunGraph}
disabled={!flowID || isExecutingGraph || isTerminatingGraph}
loading={isExecutingGraph || isTerminatingGraph || isSaving}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
import { GraphExecutionMeta } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/use-agent-runs";
import { useGraphStore } from "@/app/(platform)/build/stores/graphStore";
import { useShallow } from "zustand/react/shallow";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useSaveGraph } from "@/app/(platform)/build/hooks/useSaveGraph";
import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore";
import { ApiError } from "@/lib/autogpt-server-api/helpers"; // Check if this exists
import { useTutorialStore } from "@/app/(platform)/build/stores/tutorialStore";

export const useRunGraph = () => {
const { saveGraph, isSaving } = useSaveGraph({
Expand All @@ -33,6 +34,29 @@ export const useRunGraph = () => {
useShallow((state) => state.clearAllNodeErrors),
);

// Tutorial integration - force open dialog when tutorial requests it
const forceOpenRunInputDialog = useTutorialStore(
(state) => state.forceOpenRunInputDialog,
);
const setForceOpenRunInputDialog = useTutorialStore(
(state) => state.setForceOpenRunInputDialog,
);

// Sync tutorial state with dialog state
useEffect(() => {
if (forceOpenRunInputDialog && !openRunInputDialog) {
setOpenRunInputDialog(true);
}
}, [forceOpenRunInputDialog, openRunInputDialog]);

// Reset tutorial state when dialog closes
const handleSetOpenRunInputDialog = (isOpen: boolean) => {
setOpenRunInputDialog(isOpen);
if (!isOpen && forceOpenRunInputDialog) {
setForceOpenRunInputDialog(false);
}
};

const [{ flowID, flowVersion, flowExecutionID }, setQueryStates] =
useQueryStates({
flowID: parseAsString,
Expand Down Expand Up @@ -138,6 +162,6 @@ export const useRunGraph = () => {
isExecutingGraph,
isTerminatingGraph,
openRunInputDialog,
setOpenRunInputDialog,
setOpenRunInputDialog: handleSetOpenRunInputDialog,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Text } from "@/components/atoms/Text/Text";
import { FormRenderer } from "@/components/renderers/InputRenderer/FormRenderer";
import { useRunInputDialog } from "./useRunInputDialog";
import { CronSchedulerDialog } from "../CronSchedulerDialog/CronSchedulerDialog";
import { useTutorialStore } from "@/app/(platform)/build/stores/tutorialStore";
import { useEffect } from "react";

export const RunInputDialog = ({
isOpen,
Expand Down Expand Up @@ -37,6 +39,21 @@ export const RunInputDialog = ({
isExecutingGraph,
} = useRunInputDialog({ setIsOpen });

// Tutorial integration - track input values for the tutorial
const setTutorialInputValues = useTutorialStore(
(state) => state.setTutorialInputValues,
);
const isTutorialRunning = useTutorialStore(
(state) => state.isTutorialRunning,
);

// Update tutorial store when input values change
useEffect(() => {
if (isTutorialRunning) {
setTutorialInputValues(inputValues);
}
}, [inputValues, isTutorialRunning, setTutorialInputValues]);

return (
<>
<Dialog
Expand All @@ -48,16 +65,16 @@ export const RunInputDialog = ({
styling={{ maxWidth: "600px", minWidth: "600px" }}
>
<Dialog.Content>
<div className="space-y-6 p-1">
<div className="space-y-6 p-1" data-id="run-input-dialog-content">
{/* Credentials Section */}
{hasCredentials() && (
<div>
<div data-id="run-input-credentials-section">
<div className="mb-4">
<Text variant="h4" className="text-gray-900">
Credentials
</Text>
</div>
<div className="px-2">
<div className="px-2" data-id="run-input-credentials-form">
<FormRenderer
jsonSchema={credentialsSchema as RJSFSchema}
handleChange={(v) => handleCredentialChange(v.formData)}
Expand All @@ -75,34 +92,40 @@ export const RunInputDialog = ({

{/* Inputs Section */}
{hasInputs() && (
<div>
<div data-id="run-input-inputs-section">
<div className="mb-4">
<Text variant="h4" className="text-gray-900">
Inputs
</Text>
</div>
<FormRenderer
jsonSchema={inputSchema as RJSFSchema}
handleChange={(v) => handleInputChange(v.formData)}
uiSchema={uiSchema}
initialValues={{}}
formContext={{
showHandles: false,
size: "large",
}}
/>
<div data-id="run-input-inputs-form">
<FormRenderer
jsonSchema={inputSchema as RJSFSchema}
handleChange={(v) => handleInputChange(v.formData)}
uiSchema={uiSchema}
initialValues={{}}
formContext={{
showHandles: false,
size: "large",
}}
/>
</div>
</div>
)}

{/* Action Button */}
<div className="flex justify-end pt-2">
<div
className="flex justify-end pt-2"
data-id="run-input-actions-section"
>
{purpose === "run" && (
<Button
variant="primary"
size="large"
className="group h-fit min-w-0 gap-2"
onClick={handleManualRun}
loading={isExecutingGraph}
data-id="run-input-manual-run-button"
>
{!isExecutingGraph && (
<PlayIcon className="size-5 transition-transform group-hover:scale-110" />
Expand All @@ -116,6 +139,7 @@ export const RunInputDialog = ({
size="large"
className="group h-fit min-w-0 gap-2"
onClick={() => setOpenCronSchedulerDialog(true)}
data-id="run-input-schedule-button"
>
<ClockIcon className="size-5 transition-transform group-hover:scale-110" />
<span className="font-semibold">Schedule Run</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const ScheduleGraph = ({ flowID }: { flowID: string | null }) => {
<Button
variant="outline"
size="icon"
data-id="schedule-graph-button"
onClick={handleScheduleGraph}
disabled={!flowID}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import {
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import {
ChalkboardIcon,
CircleNotchIcon,
FrameCornersIcon,
MinusIcon,
PlusIcon,
} from "@phosphor-icons/react/dist/ssr";
import { LockIcon, LockOpenIcon } from "lucide-react";
import { memo } from "react";
import { memo, useEffect, useState } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import { useTutorialStore } from "@/app/(platform)/build/stores/tutorialStore";
import { startTutorial, setTutorialLoadingCallback } from "../../tutorial";

export const CustomControls = memo(
({
Expand All @@ -22,27 +27,65 @@ export const CustomControls = memo(
setIsLocked: (isLocked: boolean) => void;
}) => {
const { zoomIn, zoomOut, fitView } = useReactFlow();
const { isTutorialRunning, setIsTutorialRunning } = useTutorialStore();
const [isTutorialLoading, setIsTutorialLoading] = useState(false);
const searchParams = useSearchParams();
const router = useRouter();

useEffect(() => {
setTutorialLoadingCallback(setIsTutorialLoading);
return () => setTutorialLoadingCallback(() => {});
}, []);

const handleTutorialClick = () => {
if (isTutorialLoading) return;

const flowId = searchParams.get("flowID");
if (flowId) {
router.push("/build?view=new");
return;
}

startTutorial();
setIsTutorialRunning(true);
};

const controls = [
{
id: "zoom-in-button",
icon: <PlusIcon className="size-4" />,
label: "Zoom In",
onClick: () => zoomIn(),
className: "h-10 w-10 border-none",
},
{
id: "zoom-out-button",
icon: <MinusIcon className="size-4" />,
label: "Zoom Out",
onClick: () => zoomOut(),
className: "h-10 w-10 border-none",
},
{
id: "tutorial-button",
icon: isTutorialLoading ? (
<CircleNotchIcon className="size-4 animate-spin" />
) : (
<ChalkboardIcon className="size-4" />
),
label: isTutorialLoading ? "Loading Tutorial..." : "Start Tutorial",
onClick: handleTutorialClick,
className: `h-10 w-10 border-none ${isTutorialRunning || isTutorialLoading ? "bg-zinc-100" : "bg-white"}`,
disabled: isTutorialLoading,
},
{
id: "fit-view-button",
icon: <FrameCornersIcon className="size-4" />,
label: "Fit View",
onClick: () => fitView({ padding: 0.2, duration: 800, maxZoom: 1 }),
className: "h-10 w-10 border-none",
},
{
id: "lock-button",
icon: !isLocked ? (
<LockOpenIcon className="size-4" />
) : (
Expand All @@ -55,15 +98,20 @@ export const CustomControls = memo(
];

return (
<div className="absolute bottom-4 left-4 z-10 flex flex-col items-center gap-2 rounded-full bg-white px-1 py-2 shadow-lg">
{controls.map((control, index) => (
<Tooltip key={index} delayDuration={300}>
<div
data-id="custom-controls"
className="absolute bottom-4 left-4 z-10 flex flex-col items-center gap-2 rounded-full bg-white px-1 py-2 shadow-lg"
>
{controls.map((control) => (
<Tooltip key={control.id} delayDuration={0}>
<TooltipTrigger asChild>
<Button
variant="icon"
size={"small"}
onClick={control.onClick}
className={control.className}
data-id={control.id}
disabled={"disabled" in control ? control.disabled : false}
>
{control.icon}
<span className="sr-only">{control.label}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const InputNodeHandle = ({
position={Position.Left}
id={cleanedHandleId}
className={"-ml-6 mr-2"}
data-tutorial-id={`input-handler-${nodeId}-${cleanedHandleId}`}
>
<div className="pointer-events-none">
<CircleIcon
Expand Down Expand Up @@ -62,6 +63,7 @@ const OutputNodeHandle = ({
position={Position.Right}
id={field_name}
className={"-mr-2 ml-2"}
data-tutorial-id={`output-handler-${nodeId}-${field_name}`}
>
<div className="pointer-events-none">
<CircleIcon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const NodeContainer = ({
status && nodeStyleBasedOnStatus[status],
hasErrors ? nodeStyleBasedOnStatus[AgentExecutionStatus.FAILED] : "",
)}
data-id={`custom-node-${nodeId}`}
>
{children}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export const NodeDataRenderer = ({ nodeId }: { nodeId: string }) => {
}

return (
<div className="flex flex-col gap-3 rounded-b-xl border-t border-zinc-200 px-4 py-4">
<div
data-tutorial-id={`node-output`}
className="flex flex-col gap-3 rounded-b-xl border-t border-zinc-200 px-4 py-4"
>
<div className="flex items-center justify-between">
<Text variant="body-medium" className="!font-semibold text-slate-700">
Node Output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export const FormCreator: React.FC<FormCreatorProps> = React.memo(
: hardcodedValues;

return (
<div className={className}>
<div
className={className}
data-id={`form-creator-container-${nodeId}-node`}
>
<FormRenderer
jsonSchema={jsonSchema}
handleChange={handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export const OutputHandler = ({
const isBroken = brokenOutputs.has(fullKey);

return shouldShow ? (
<div key={fullKey} className="flex flex-col items-end gap-2">
<div
key={fullKey}
className="flex flex-col items-end gap-2"
data-tutorial-id={`output-handler-${nodeId}-${fieldTitle}`}
>
<div className="relative flex items-center gap-2">
{fieldSchema?.description && (
<TooltipProvider>
Expand Down
Loading
Loading