refactor(frontend): fix new builder buttons (#11696)

## Changes 🏗️

<img width="800" height="964" alt="Screenshot 2026-01-05 at 15 26 21"
src="https://github.com/user-attachments/assets/f8c7fc47-894a-4db2-b2f1-62b4d70e8453"
/>

- Adjust the new builder to use the Design System components
- Re-structure imports to match formatting rules
- Small improvement on `use-get-flag`
- Move file which is the main hook

## Checklist 📋

### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Run locally and check the new buttons look good
This commit is contained in:
Ubbe
2026-01-05 16:09:47 +07:00
committed by GitHub
parent 290d0d9a9b
commit 003affca43
14 changed files with 65 additions and 99 deletions

View File

@@ -16,6 +16,7 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/__legacy__/ui/sheet";
import { Button } from "@/components/atoms/Button/Button";
import {
Tooltip,
TooltipContent,
@@ -25,7 +26,6 @@ import {
import { BookOpenIcon } from "@phosphor-icons/react";
import { useMemo } from "react";
import { useShallow } from "zustand/react/shallow";
import { BuilderActionButton } from "../BuilderActionButton";
export const AgentOutputs = ({ flowID }: { flowID: string | null }) => {
const hasOutputs = useGraphStore(useShallow((state) => state.hasOutputs));
@@ -76,9 +76,13 @@ export const AgentOutputs = ({ flowID }: { flowID: string | null }) => {
<Tooltip>
<TooltipTrigger asChild>
<SheetTrigger asChild>
<BuilderActionButton disabled={!flowID || !hasOutputs()}>
<BookOpenIcon className="size-6" />
</BuilderActionButton>
<Button
variant="outline"
size="icon"
disabled={!flowID || !hasOutputs()}
>
<BookOpenIcon className="size-4" />
</Button>
</SheetTrigger>
</TooltipTrigger>
<TooltipContent>

View File

@@ -1,37 +0,0 @@
import { Button } from "@/components/atoms/Button/Button";
import { ButtonProps } from "@/components/atoms/Button/helpers";
import { cn } from "@/lib/utils";
import { CircleNotchIcon } from "@phosphor-icons/react";
export const BuilderActionButton = ({
children,
className,
isLoading,
...props
}: ButtonProps & { isLoading?: boolean }) => {
return (
<Button
variant="icon"
size={"small"}
className={cn(
"relative h-12 w-12 min-w-0 text-lg",
"bg-gradient-to-br from-zinc-50 to-zinc-200",
"border border-zinc-200",
"shadow-[inset_0_3px_0_0_rgba(255,255,255,0.5),0_2px_4px_0_rgba(0,0,0,0.2)]",
"dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.1),0_2px_4px_0_rgba(0,0,0,0.4)]",
"hover:border-none hover:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.5),0_1px_2px_0_rgba(0,0,0,0.2)]",
"active:border-none active:shadow-[inset_0_2px_4px_0_rgba(0,0,0,0.2)]",
"transition-all duration-150",
"disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
{!isLoading ? (
children
) : (
<CircleNotchIcon className="size-6 animate-spin" />
)}
</Button>
);
};

View File

@@ -1,12 +1,12 @@
import { ShareIcon } from "@phosphor-icons/react";
import { BuilderActionButton } from "../BuilderActionButton";
import { Button } from "@/components/atoms/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { usePublishToMarketplace } from "./usePublishToMarketplace";
import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal";
import { ShareIcon } from "@phosphor-icons/react";
import { usePublishToMarketplace } from "./usePublishToMarketplace";
export const PublishToMarketplace = ({ flowID }: { flowID: string | null }) => {
const { handlePublishToMarketplace, publishState, handleStateChange } =
@@ -16,12 +16,14 @@ export const PublishToMarketplace = ({ flowID }: { flowID: string | null }) => {
<>
<Tooltip>
<TooltipTrigger asChild>
<BuilderActionButton
<Button
variant="outline"
size="icon"
onClick={handlePublishToMarketplace}
disabled={!flowID}
>
<ShareIcon className="size-6 drop-shadow-sm" />
</BuilderActionButton>
<ShareIcon className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Publish to Marketplace</TooltipContent>
</Tooltip>

View File

@@ -1,15 +1,14 @@
import { useRunGraph } from "./useRunGraph";
import { useGraphStore } from "@/app/(platform)/build/stores/graphStore";
import { useShallow } from "zustand/react/shallow";
import { PlayIcon, StopIcon } from "@phosphor-icons/react";
import { cn } from "@/lib/utils";
import { RunInputDialog } from "../RunInputDialog/RunInputDialog";
import { Button } from "@/components/atoms/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { BuilderActionButton } from "../BuilderActionButton";
import { PlayIcon, StopIcon } from "@phosphor-icons/react";
import { useShallow } from "zustand/react/shallow";
import { RunInputDialog } from "../RunInputDialog/RunInputDialog";
import { useRunGraph } from "./useRunGraph";
export const RunGraph = ({ flowID }: { flowID: string | null }) => {
const {
@@ -29,21 +28,19 @@ export const RunGraph = ({ flowID }: { flowID: string | null }) => {
<>
<Tooltip>
<TooltipTrigger asChild>
<BuilderActionButton
className={cn(
isGraphRunning &&
"border-red-500 bg-gradient-to-br from-red-400 to-red-500 shadow-[inset_0_2px_0_0_rgba(255,255,255,0.5),0_2px_4px_0_rgba(0,0,0,0.2)]",
)}
<Button
size="icon"
variant={isGraphRunning ? "destructive" : "primary"}
onClick={isGraphRunning ? handleStopGraph : handleRunGraph}
disabled={!flowID || isExecutingGraph || isTerminatingGraph}
isLoading={isExecutingGraph || isTerminatingGraph || isSaving}
loading={isExecutingGraph || isTerminatingGraph || isSaving}
>
{!isGraphRunning ? (
<PlayIcon className="size-6 drop-shadow-sm" />
<PlayIcon className="size-4" />
) : (
<StopIcon className="size-6 drop-shadow-sm" />
<StopIcon className="size-4" />
)}
</BuilderActionButton>
</Button>
</TooltipTrigger>
<TooltipContent>
{isGraphRunning ? "Stop agent" : "Run agent"}

View File

@@ -1,14 +1,14 @@
import { ClockIcon } from "@phosphor-icons/react";
import { RunInputDialog } from "../RunInputDialog/RunInputDialog";
import { useScheduleGraph } from "./useScheduleGraph";
import { Button } from "@/components/atoms/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { ClockIcon } from "@phosphor-icons/react";
import { CronSchedulerDialog } from "../CronSchedulerDialog/CronSchedulerDialog";
import { BuilderActionButton } from "../BuilderActionButton";
import { RunInputDialog } from "../RunInputDialog/RunInputDialog";
import { useScheduleGraph } from "./useScheduleGraph";
export const ScheduleGraph = ({ flowID }: { flowID: string | null }) => {
const {
@@ -23,12 +23,14 @@ export const ScheduleGraph = ({ flowID }: { flowID: string | null }) => {
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<BuilderActionButton
<Button
variant="outline"
size="icon"
onClick={handleScheduleGraph}
disabled={!flowID}
>
<ClockIcon className="size-6" />
</BuilderActionButton>
<ClockIcon className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Schedule Graph</p>

View File

@@ -24,7 +24,7 @@ export const ControlPanelButton: React.FC<Props> = ({
role={as === "div" ? "button" : undefined}
disabled={as === "button" ? disabled : undefined}
className={cn(
"flex h-[4.25rem] w-[4.25rem] items-center justify-center whitespace-normal bg-white p-[1.38rem] text-zinc-800 shadow-none hover:cursor-pointer hover:bg-zinc-100 hover:text-zinc-950 focus:ring-0",
"flex w-auto items-center justify-center whitespace-normal bg-white px-4 py-4 text-zinc-800 shadow-none hover:cursor-pointer hover:bg-zinc-100 hover:text-zinc-950 focus:ring-0",
selected &&
"bg-violet-50 text-violet-700 hover:cursor-default hover:bg-violet-50 hover:text-violet-700 active:bg-violet-50 active:text-violet-700",
disabled && "cursor-not-allowed opacity-50 hover:cursor-not-allowed",

View File

@@ -1,18 +1,17 @@
import React from "react";
import { useControlPanelStore } from "@/app/(platform)/build/stores/controlPanelStore";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/__legacy__/ui/popover";
import { BlockMenuContent } from "../BlockMenuContent/BlockMenuContent";
import { ControlPanelButton } from "../../ControlPanelButton";
import { LegoIcon } from "@phosphor-icons/react";
import { useControlPanelStore } from "@/app/(platform)/build/stores/controlPanelStore";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { LegoIcon } from "@phosphor-icons/react";
import { ControlPanelButton } from "../../ControlPanelButton";
import { BlockMenuContent } from "../BlockMenuContent/BlockMenuContent";
export const BlockMenu = () => {
const { blockMenuOpen, setBlockMenuOpen } = useControlPanelStore();
@@ -28,7 +27,7 @@ export const BlockMenu = () => {
selected={blockMenuOpen}
className="rounded-none"
>
<LegoIcon className="h-6 w-6" />
<LegoIcon className="size-5" />
</ControlPanelButton>
</PopoverTrigger>
</TooltipTrigger>

View File

@@ -7,10 +7,10 @@ import { useNewControlPanel } from "./useNewControlPanel";
import { GraphExecutionID } from "@/lib/autogpt-server-api";
// import { ControlPanelButton } from "../ControlPanelButton";
// import { GraphSearchMenu } from "../GraphMenu/GraphMenu";
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { Separator } from "@/components/__legacy__/ui/separator";
import { NewSaveControl } from "./NewSaveControl/NewSaveControl";
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { CustomNode } from "../FlowEditor/nodes/CustomNode/CustomNode";
import { NewSaveControl } from "./NewSaveControl/NewSaveControl";
import { UndoRedoButtons } from "./UndoRedoButtons";
export type Control = {
@@ -56,7 +56,7 @@ export const NewControlPanel = memo(
return (
<section
className={cn(
"absolute left-4 top-10 z-10 w-[4.25rem] overflow-hidden rounded-[1rem] border-none bg-white p-0 shadow-[0_1px_5px_0_rgba(0,0,0,0.1)]",
"absolute left-4 top-10 z-10 overflow-hidden rounded-[1rem] border-none bg-white p-0 shadow-[0_1px_5px_0_rgba(0,0,0,0.1)]",
)}
>
<div className="flex flex-col items-center justify-center rounded-[1rem] p-0">

View File

@@ -1,22 +1,21 @@
import React from "react";
import { Card, CardContent, CardFooter } from "@/components/__legacy__/ui/card";
import { Form, FormField } from "@/components/__legacy__/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/__legacy__/ui/popover";
import { Card, CardContent, CardFooter } from "@/components/__legacy__/ui/card";
import { Button } from "@/components/atoms/Button/Button";
import { Input } from "@/components/atoms/Input/Input";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { useNewSaveControl } from "./useNewSaveControl";
import { Form, FormField } from "@/components/__legacy__/ui/form";
import { ControlPanelButton } from "../ControlPanelButton";
import { useControlPanelStore } from "../../../stores/controlPanelStore";
import { FloppyDiskIcon } from "@phosphor-icons/react";
import { Input } from "@/components/atoms/Input/Input";
import { Button } from "@/components/atoms/Button/Button";
import { useControlPanelStore } from "../../../stores/controlPanelStore";
import { ControlPanelButton } from "../ControlPanelButton";
import { useNewSaveControl } from "./useNewSaveControl";
export const NewSaveControl = () => {
const { form, isSaving, graphVersion, handleSave } = useNewSaveControl();
@@ -33,7 +32,7 @@ export const NewSaveControl = () => {
selected={saveControlOpen}
className="rounded-none"
>
<FloppyDiskIcon className="h-6 w-6" />
<FloppyDiskIcon className="size-5" />
</ControlPanelButton>
</PopoverTrigger>
</TooltipTrigger>

View File

@@ -1,13 +1,13 @@
import React from "react";
import { CustomNode } from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/__legacy__/ui/popover";
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
import { GraphSearchContent } from "../GraphMenuContent/GraphContent";
import React from "react";
import { ControlPanelButton } from "../../ControlPanelButton";
import { CustomNode } from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode";
import { GraphSearchContent } from "../GraphMenuContent/GraphContent";
import { useGraphMenu } from "./useGraphMenu";
interface GraphSearchMenuProps {
@@ -50,7 +50,7 @@ export const GraphSearchMenu: React.FC<GraphSearchMenuProps> = ({
selected={blockMenuSelected === "search"}
className="rounded-none"
>
<MagnifyingGlassIcon className="h-5 w-6" strokeWidth={2} />
<MagnifyingGlassIcon className="size-5" strokeWidth={2} />
</ControlPanelButton>
</PopoverTrigger>

View File

@@ -1,12 +1,12 @@
import { Separator } from "@/components/__legacy__/ui/separator";
import { ControlPanelButton } from "./ControlPanelButton";
import { ArrowUUpLeftIcon, ArrowUUpRightIcon } from "@phosphor-icons/react";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { ArrowUUpLeftIcon, ArrowUUpRightIcon } from "@phosphor-icons/react";
import { useHistoryStore } from "../../stores/historyStore";
import { ControlPanelButton } from "./ControlPanelButton";
import { useEffect } from "react";
@@ -43,7 +43,7 @@ export const UndoRedoButtons = () => {
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<ControlPanelButton as="button" disabled={!canUndo()} onClick={undo}>
<ArrowUUpLeftIcon className="h-6 w-6" />
<ArrowUUpLeftIcon className="size-5" />
</ControlPanelButton>
</TooltipTrigger>
<TooltipContent side="right">Undo</TooltipContent>
@@ -52,7 +52,7 @@ export const UndoRedoButtons = () => {
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<ControlPanelButton as="button" disabled={!canRedo()} onClick={redo}>
<ArrowUUpRightIcon className="h-6 w-6" />
<ArrowUUpRightIcon className="size-5" />
</ControlPanelButton>
</TooltipTrigger>
<TooltipContent side="right">Redo</TooltipContent>

View File

@@ -8,8 +8,8 @@ import { ReactFlowProvider } from "@xyflow/react";
import { useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { BuilderViewTabs } from "./components/BuilderViewTabs/BuilderViewTabs";
import { useBuilderView } from "./components/BuilderViewTabs/useBuilderViewTabs";
import { Flow } from "./components/FlowEditor/Flow/Flow";
import { useBuilderView } from "./useBuilderView";
function BuilderContent() {
const query = useSearchParams();

View File

@@ -1,7 +1,7 @@
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo } from "react";
import { BuilderView } from "./BuilderViewTabs";
import { BuilderView } from "./components/BuilderViewTabs/BuilderViewTabs";
export function useBuilderView() {
const isNewFlowEditorEnabled = useGetFlag(Flag.NEW_FLOW_EDITOR);

View File

@@ -56,7 +56,7 @@ export function useGetFlag<T extends Flag>(flag: T): FlagValues[T] | null {
const envEnabled = process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "true";
const clientId = process.env.NEXT_PUBLIC_LAUNCHDARKLY_CLIENT_ID;
const isLaunchDarklyConfigured = envEnabled && clientId;
const isLaunchDarklyConfigured = envEnabled && Boolean(clientId);
if (!isLaunchDarklyConfigured || isPwMockEnabled) {
return mockFlags[flag];