mirror of
https://github.com/Significant-Gravitas/Auto-GPT.git
synced 2025-01-08 11:57:32 +08:00
Feat(Builder/tutorial): update tutorial to make it work with latest UI (#8232)
* updates to tutorial * updates to get user to save * Update tutorial.ts * final updates to end of tutorial * Prettier * add back data-id for badge within the blocks * Prettier
This commit is contained in:
parent
72cbbbbbc9
commit
5e2d29f27e
@ -99,6 +99,8 @@ const FlowEditor: React.FC<{
|
||||
|
||||
// State to control if blocks menu should be pinned open
|
||||
const [pinBlocksPopover, setPinBlocksPopover] = useState(false);
|
||||
// State to control if save popover should be pinned open
|
||||
const [pinSavePopover, setPinSavePopover] = useState(false);
|
||||
|
||||
const runnerUIRef = useRef<RunnerUIWrapperRef>(null);
|
||||
|
||||
@ -111,7 +113,7 @@ const FlowEditor: React.FC<{
|
||||
localStorage.removeItem(TUTORIAL_STORAGE_KEY);
|
||||
router.push(pathname);
|
||||
} else if (!localStorage.getItem(TUTORIAL_STORAGE_KEY)) {
|
||||
startTutorial(setPinBlocksPopover);
|
||||
startTutorial(setPinBlocksPopover, setPinSavePopover);
|
||||
localStorage.setItem(TUTORIAL_STORAGE_KEY, "yes");
|
||||
}
|
||||
}, [availableNodes, router, pathname, params]);
|
||||
@ -586,6 +588,7 @@ const FlowEditor: React.FC<{
|
||||
onDescriptionChange={setAgentDescription}
|
||||
agentName={agentName}
|
||||
onNameChange={setAgentName}
|
||||
pinSavePopover={pinSavePopover}
|
||||
/>
|
||||
}
|
||||
></ControlPanel>
|
||||
|
@ -62,6 +62,7 @@ const PrimaryActionBar: React.FC<PrimaryActionBarProps> = ({
|
||||
background: isRunning ? "#FFB3BA" : "#7544DF",
|
||||
opacity: isDisabled ? 0.5 : 1,
|
||||
}}
|
||||
data-id="primary-action-run-agent"
|
||||
>
|
||||
{runButtonIcon}
|
||||
<span className="text-sm font-medium md:text-lg">
|
||||
|
@ -23,6 +23,7 @@ interface SaveControlProps {
|
||||
onSave: (isTemplate: boolean | undefined) => void;
|
||||
onNameChange: (name: string) => void;
|
||||
onDescriptionChange: (description: string) => void;
|
||||
pinSavePopover: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,6 +42,7 @@ export const SaveControl = ({
|
||||
onNameChange,
|
||||
agentDescription,
|
||||
onDescriptionChange,
|
||||
pinSavePopover,
|
||||
}: SaveControlProps) => {
|
||||
/**
|
||||
* Note for improvement:
|
||||
@ -59,11 +61,15 @@ export const SaveControl = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<Popover open={pinSavePopover ? true : undefined}>
|
||||
<Tooltip delayDuration={500}>
|
||||
<TooltipTrigger asChild>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
data-id="save-control-popover-trigger"
|
||||
>
|
||||
<IconSave />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@ -81,6 +87,7 @@ export const SaveControl = ({
|
||||
className="col-span-3"
|
||||
value={agentName}
|
||||
onChange={(e) => onNameChange(e.target.value)}
|
||||
data-id="save-control-name-input"
|
||||
/>
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Input
|
||||
@ -89,6 +96,7 @@ export const SaveControl = ({
|
||||
className="col-span-3"
|
||||
value={agentDescription}
|
||||
onChange={(e) => onDescriptionChange(e.target.value)}
|
||||
data-id="save-control-description-input"
|
||||
/>
|
||||
{agentMeta?.version && (
|
||||
<>
|
||||
@ -105,13 +113,18 @@ export const SaveControl = ({
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex flex-col items-stretch gap-2">
|
||||
<Button className="w-full" onClick={handleSave}>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={handleSave}
|
||||
data-id="save-control-save-agent"
|
||||
>
|
||||
Save {getType()}
|
||||
</Button>
|
||||
{!agentMeta && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="w-full"
|
||||
data-id="save-control-template-button"
|
||||
onClick={() => {
|
||||
isTemplate = true;
|
||||
handleSave();
|
||||
|
@ -4,6 +4,7 @@ import "shepherd.js/dist/css/shepherd.css";
|
||||
|
||||
export const startTutorial = (
|
||||
setPinBlocksPopover: (value: boolean) => void,
|
||||
setPinSavePopover: (value: boolean) => void,
|
||||
) => {
|
||||
const tour = new Shepherd.Tour({
|
||||
useModalOverlay: true,
|
||||
@ -20,27 +21,20 @@ export const startTutorial = (
|
||||
|
||||
// Helper function to disable all blocks except the target block
|
||||
const disableOtherBlocks = (targetBlockSelector: string) => {
|
||||
document
|
||||
.querySelectorAll('[data-id^="add-block-button"]')
|
||||
.forEach((block) => {
|
||||
block.classList.toggle(
|
||||
disableClass,
|
||||
!block.matches(targetBlockSelector),
|
||||
);
|
||||
block.classList.toggle(
|
||||
highlightClass,
|
||||
block.matches(targetBlockSelector),
|
||||
);
|
||||
});
|
||||
document.querySelectorAll('[data-id^="block-card-"]').forEach((block) => {
|
||||
block.classList.toggle(disableClass, !block.matches(targetBlockSelector));
|
||||
block.classList.toggle(
|
||||
highlightClass,
|
||||
block.matches(targetBlockSelector),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Helper function to enable all blocks
|
||||
const enableAllBlocks = () => {
|
||||
document
|
||||
.querySelectorAll('[data-id^="add-block-button"]')
|
||||
.forEach((block) => {
|
||||
block.classList.remove(disableClass, highlightClass);
|
||||
});
|
||||
document.querySelectorAll('[data-id^="block-card-"]').forEach((block) => {
|
||||
block.classList.remove(disableClass, highlightClass);
|
||||
});
|
||||
};
|
||||
|
||||
// Inject CSS for disabling and highlighting blocks
|
||||
@ -78,7 +72,7 @@ export const startTutorial = (
|
||||
const detectConnection = () => {
|
||||
const checkForConnection = () => {
|
||||
const correctConnection = document.querySelector(
|
||||
'[data-testid="rf__edge-1_result_2_a"]',
|
||||
'[data-testid^="rf__edge-"]',
|
||||
);
|
||||
if (correctConnection) {
|
||||
tour.show("press-run-again");
|
||||
@ -117,12 +111,12 @@ export const startTutorial = (
|
||||
function handleMouseUp(event: { target: any }) {
|
||||
const target = event.target;
|
||||
const validConnectionPoint = document.querySelector(
|
||||
'[data-id="2-a-target"]',
|
||||
'[data-id="custom-node-2"] [data-handlepos="left"]',
|
||||
);
|
||||
|
||||
if (validConnectionPoint && !validConnectionPoint.contains(target)) {
|
||||
setTimeout(() => {
|
||||
if (!document.querySelector('[data-testid="rf__edge-1_result_2_a"]')) {
|
||||
if (!document.querySelector('[data-testid^="rf__edge-"]')) {
|
||||
stopConnecting();
|
||||
tour.show("connect-blocks-output");
|
||||
}
|
||||
@ -314,32 +308,127 @@ export const startTutorial = (
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "press-run",
|
||||
title: "Press Run",
|
||||
text: "Start your first flow by pressing the Run button!",
|
||||
attachTo: { element: '[data-id="control-button-2"]', on: "right" },
|
||||
advanceOn: { selector: '[data-id="control-button-2"]', event: "click" },
|
||||
id: "press-initial-save-button",
|
||||
title: "Press Save",
|
||||
text: "First we need to save the flow before we can run it!",
|
||||
attachTo: {
|
||||
element: '[data-id="save-control-popover-trigger"]',
|
||||
on: "left",
|
||||
},
|
||||
advanceOn: {
|
||||
selector: '[data-id="save-control-popover-trigger"]',
|
||||
event: "click",
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
text: "Back",
|
||||
action: tour.back,
|
||||
},
|
||||
],
|
||||
when: {
|
||||
hide: () => setPinSavePopover(true),
|
||||
},
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "enter-agent-name",
|
||||
title: "Enter Agent Name",
|
||||
text: 'Please enter any agent name, here we can just call it "Tutorial" if you\'d like.',
|
||||
attachTo: {
|
||||
element: '[data-id="save-control-name-input"]',
|
||||
on: "bottom",
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
text: "Back",
|
||||
action: tour.back,
|
||||
},
|
||||
{
|
||||
text: "Next",
|
||||
action: tour.next,
|
||||
},
|
||||
],
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-id="save-control-name-input"]'),
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "enter-agent-description",
|
||||
title: "Enter Agent Description",
|
||||
text: "This is where you can add a description if you'd like, but that is optional.",
|
||||
attachTo: {
|
||||
element: '[data-id="save-control-description-input"]',
|
||||
on: "bottom",
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
text: "Back",
|
||||
action: tour.back,
|
||||
},
|
||||
{
|
||||
text: "Next",
|
||||
action: tour.next,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "save-agent",
|
||||
title: "Save the Agent",
|
||||
text: "Now, let's save the agent by clicking the 'Save agent' button.",
|
||||
attachTo: {
|
||||
element: '[data-id="save-control-save-agent"]',
|
||||
on: "top",
|
||||
},
|
||||
advanceOn: {
|
||||
selector: '[data-id="save-control-save-agent"]',
|
||||
event: "click",
|
||||
},
|
||||
buttons: [],
|
||||
when: {
|
||||
hide: () => setPinSavePopover(false),
|
||||
},
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "press-run",
|
||||
title: "Press Run",
|
||||
text: "Start your first flow by pressing the Run button!",
|
||||
attachTo: { element: '[data-id="primary-action-run-agent"]', on: "top" },
|
||||
advanceOn: {
|
||||
selector: '[data-id="primary-action-run-agent"]',
|
||||
event: "click",
|
||||
},
|
||||
buttons: [],
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-id="primary-action-run-agent"]'),
|
||||
when: {
|
||||
hide: () => {
|
||||
setTimeout(() => {
|
||||
fitViewToScreen();
|
||||
}, 500);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
id: "wait-for-processing",
|
||||
title: "Processing",
|
||||
text: "Let's wait for the block to finish being processed...",
|
||||
attachTo: { element: '[data-id="badge-1-QUEUED"]', on: "bottom" },
|
||||
attachTo: {
|
||||
element: '[data-id^="badge-"][data-id$="-QUEUED"]',
|
||||
on: "bottom",
|
||||
},
|
||||
buttons: [],
|
||||
beforeShowPromise: () => waitForElement('[data-id="badge-1-QUEUED"]'),
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-id^="badge-"][data-id$="-QUEUED"]'),
|
||||
when: {
|
||||
show: () => {
|
||||
fitViewToScreen();
|
||||
waitForElement('[data-id="badge-1-COMPLETED"]').then(() => {
|
||||
tour.next();
|
||||
});
|
||||
waitForElement('[data-id^="badge-"][data-id$="-COMPLETED"]').then(
|
||||
() => {
|
||||
tour.next();
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -371,7 +460,7 @@ export const startTutorial = (
|
||||
id: "copy-paste-block",
|
||||
title: "Copy and Paste the Block",
|
||||
text: "Let’s duplicate this block. Click and hold the block with your mouse, then press Ctrl+C (Cmd+C on Mac) to copy and Ctrl+V (Cmd+V on Mac) to paste.",
|
||||
attachTo: { element: `[data-id="custom-node-1"]`, on: "top" },
|
||||
attachTo: { element: '[data-id^="custom-node-"]', on: "top" },
|
||||
buttons: [
|
||||
{
|
||||
text: "Back",
|
||||
@ -392,8 +481,9 @@ export const startTutorial = (
|
||||
id: "focus-second-block",
|
||||
title: "Focus on the New Block",
|
||||
text: "This is your copied Calculator Block. Now, let’s move it to the side of the first block.",
|
||||
attachTo: { element: `[data-id="custom-node-2"]`, on: "top" },
|
||||
beforeShowPromise: () => waitForElement('[data-id="custom-node-2"]'),
|
||||
attachTo: { element: `[data-id^="custom-node-"][data-id$="2"]`, on: "top" },
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-id^="custom-node-"][data-id$="2"]'),
|
||||
buttons: [
|
||||
{
|
||||
text: "Next",
|
||||
@ -405,8 +495,13 @@ export const startTutorial = (
|
||||
tour.addStep({
|
||||
id: "connect-blocks-output",
|
||||
title: "Connect the Blocks: Output",
|
||||
text: "Now, let’s connect the output of the first Calculator Block to the input of the second Calculator Block. Drag from the output pin of the first block to the input pin (A) of the second block.",
|
||||
attachTo: { element: '[data-id="1-1-result-source"]', on: "bottom" },
|
||||
text: "Now, let's connect the output of the first Calculator Block to the input of the second Calculator Block. Drag from the output pin of the first block to the input pin (A) of the second block.",
|
||||
attachTo: {
|
||||
element:
|
||||
'[data-id^="1-"][data-id$="-result-source"]:not([data-id="1-2-result-source"])',
|
||||
on: "bottom",
|
||||
},
|
||||
|
||||
buttons: [
|
||||
{
|
||||
text: "Back",
|
||||
@ -414,7 +509,9 @@ export const startTutorial = (
|
||||
},
|
||||
],
|
||||
beforeShowPromise: () => {
|
||||
return waitForElement('[data-id="1-1-result-source"]');
|
||||
return waitForElement(
|
||||
'[data-id^="1-"][data-id$="-result-source"]:not([data-id="1-2-result-source"])',
|
||||
).then(() => {});
|
||||
},
|
||||
when: {
|
||||
show: () => {
|
||||
@ -422,7 +519,7 @@ export const startTutorial = (
|
||||
resetConnectionState(); // Reset state when revisiting this step
|
||||
tour.modal.show();
|
||||
const outputPin = document.querySelector(
|
||||
'[data-id="1-1-result-source"]',
|
||||
'[data-id^="1-"][data-id$="-result-source"]:not([data-id="1-2-result-source"])',
|
||||
);
|
||||
if (outputPin) {
|
||||
outputPin.addEventListener("mousedown", handleMouseDown);
|
||||
@ -430,7 +527,7 @@ export const startTutorial = (
|
||||
},
|
||||
hide: () => {
|
||||
const outputPin = document.querySelector(
|
||||
'[data-id="1-1-result-source"]',
|
||||
'[data-id^="1-"][data-id$="-result-source"]:not([data-id="1-2-result-source"])',
|
||||
);
|
||||
if (outputPin) {
|
||||
outputPin.removeEventListener("mousedown", handleMouseDown);
|
||||
@ -443,7 +540,10 @@ export const startTutorial = (
|
||||
id: "connect-blocks-input",
|
||||
title: "Connect the Blocks: Input",
|
||||
text: "Now, connect the output to the input pin of the second block (A).",
|
||||
attachTo: { element: '[data-id="1-2-a-target"]', on: "top" },
|
||||
attachTo: {
|
||||
element: '[data-id="1-2-a-target"]',
|
||||
on: "top",
|
||||
},
|
||||
buttons: [],
|
||||
beforeShowPromise: () => {
|
||||
return waitForElement('[data-id="1-2-a-target"]').then(() => {
|
||||
@ -466,9 +566,21 @@ export const startTutorial = (
|
||||
id: "press-run-again",
|
||||
title: "Press Run Again",
|
||||
text: "Now, press the Run button again to execute the flow with the new Calculator Block added!",
|
||||
attachTo: { element: '[data-id="control-button-2"]', on: "right" },
|
||||
advanceOn: { selector: '[data-id="control-button-2"]', event: "click" },
|
||||
attachTo: { element: '[data-id="primary-action-run-agent"]', on: "top" },
|
||||
advanceOn: {
|
||||
selector: '[data-id="primary-action-run-agent"]',
|
||||
event: "click",
|
||||
},
|
||||
buttons: [],
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-id="primary-action-run-agent"]'),
|
||||
when: {
|
||||
hide: () => {
|
||||
setTimeout(() => {
|
||||
fitViewToScreen();
|
||||
}, 500);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tour.addStep({
|
||||
@ -487,9 +599,10 @@ export const startTutorial = (
|
||||
],
|
||||
});
|
||||
|
||||
// Unpin blocks when the tour is completed or canceled
|
||||
// Unpin blocks and save menu when the tour is completed or canceled
|
||||
tour.on("complete", () => {
|
||||
setPinBlocksPopover(false);
|
||||
setPinSavePopover(false);
|
||||
localStorage.setItem("shepherd-tour", "completed"); // Optionally mark the tutorial as completed
|
||||
});
|
||||
|
||||
@ -504,7 +617,9 @@ export const startTutorial = (
|
||||
|
||||
tour.on("cancel", () => {
|
||||
setPinBlocksPopover(false);
|
||||
setPinSavePopover(false);
|
||||
localStorage.setItem("shepherd-tour", "canceled"); // Optionally mark the tutorial as canceled
|
||||
});
|
||||
|
||||
tour.start();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user