From d9d24dcfe6ecf9b15bd440a1003d7fb0a58165a2 Mon Sep 17 00:00:00 2001 From: Lluis Agusti Date: Thu, 19 Feb 2026 22:14:37 +0800 Subject: [PATCH 1/5] chore: wip --- .../app/(platform)/build/BuilderContent.tsx | 13 + .../CustomNode/components/NodeHeader.tsx | 1 - .../build/components/MCPToolDialog.tsx | 2 - .../NewBlockMenu/HorizontalScroll.tsx | 2 +- .../legacy-builder/BlocksControl.tsx | 9 + .../legacy-builder/CustomNode/CustomNode.tsx | 1 - .../components/legacy-builder/DataTable.tsx | 2 + .../components/legacy-builder/Flow/Flow.tsx | 14 +- .../components/legacy-builder/NodeOutputs.tsx | 2 + .../legacy-builder/agent-run-draft-view.tsx | 5 +- .../src/app/(platform)/build/page.tsx | 18 +- .../ChatContainer/ChatContainer.tsx | 82 +++---- .../ChatInput/components/AudioWaveform.tsx | 1 + .../ChatMessagesContainer.tsx | 40 ++-- .../components/ChatSidebar/ChatSidebar.tsx | 22 +- .../components/EmptySession/EmptySession.tsx | 116 ++++----- .../MorphingTextAnimation.tsx | 84 +++---- .../ToolAccordion/ToolAccordion.tsx | 121 +++++----- .../src/app/(platform)/copilot/layout.tsx | 14 ++ .../(platform)/copilot/styleguide/layout.tsx | 14 ++ .../components/ClarificationQuestionsCard.tsx | 2 +- .../copilot/tools/GenericTool/GenericTool.tsx | 4 +- .../AgentDetailsCard/AgentDetailsCard.tsx | 145 ++++++------ .../BlockOutputCard/BlockOutputCard.tsx | 1 + .../SetupRequirementsCard.tsx | 223 +++++++++--------- .../tools/ViewAgentOutput/ViewAgentOutput.tsx | 1 + .../components/SidebarItemCard.tsx | 8 + .../components/FlowRunsTimeline.tsx | 189 +-------------- .../components/FlowRunsTimelineChart.tsx | 189 +++++++++++++++ autogpt_platform/frontend/src/app/page.tsx | 19 +- .../src/components/__legacy__/ui/render.tsx | 20 +- .../APIKeyCredentialsModal.tsx | 2 +- .../HotScopedCredentialsModal.tsx | 22 +- .../CronScheduler/cron-scheduler-dialog.tsx | 30 ++- .../CronScheduler/cron-scheduler.tsx | 2 +- .../renderers/MarkdownRenderer.tsx | 18 +- .../ActivityDropdown/ActivityDropdown.tsx | 1 - 37 files changed, 769 insertions(+), 670 deletions(-) create mode 100644 autogpt_platform/frontend/src/app/(platform)/build/BuilderContent.tsx create mode 100644 autogpt_platform/frontend/src/app/(platform)/copilot/layout.tsx create mode 100644 autogpt_platform/frontend/src/app/(platform)/copilot/styleguide/layout.tsx create mode 100644 autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimelineChart.tsx diff --git a/autogpt_platform/frontend/src/app/(platform)/build/BuilderContent.tsx b/autogpt_platform/frontend/src/app/(platform)/build/BuilderContent.tsx new file mode 100644 index 000000000000..9dfd77197d15 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/build/BuilderContent.tsx @@ -0,0 +1,13 @@ +"use client"; +import { ReactFlowProvider } from "@xyflow/react"; +import { Flow } from "./components/FlowEditor/Flow/Flow"; + +export function BuilderContent() { + return ( +
+ + + +
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx index 9a3add62b617..4226fcb9ca54 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx @@ -55,7 +55,6 @@ export const NodeHeader = ({ data, nodeId }: Props) => { id="node-title-input" value={editedTitle} onChange={(e) => setEditedTitle(e.target.value)} - autoFocus className={cn( "m-0 h-fit w-full border-none bg-transparent p-0 focus:outline-none focus:ring-0", "font-sans text-[1rem] font-semibold leading-[1.5rem] text-zinc-800", diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx index e01fb3a4b853..8161587f7a3c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx @@ -300,7 +300,6 @@ export function MCPToolDialog({ value={serverUrl} onChange={(e) => setServerUrl(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleDiscoverTools()} - autoFocus /> @@ -327,7 +326,6 @@ export function MCPToolDialog({ value={manualToken} onChange={(e) => setManualToken(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleDiscoverTools()} - autoFocus /> )} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/HorizontalScroll.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/HorizontalScroll.tsx index bee0b85721b8..c79950cd81eb 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/HorizontalScroll.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/HorizontalScroll.tsx @@ -52,7 +52,7 @@ export const HorizontalScroll: React.FC = ({ return; } const handleScroll = () => updateScrollState(); - element.addEventListener("scroll", handleScroll); + element.addEventListener("scroll", handleScroll, { passive: true }); window.addEventListener("resize", handleScroll); return () => { element.removeEventListener("scroll", handleScroll); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx index 99b66fe1dc4e..bfd9dfa40e38 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx @@ -298,12 +298,21 @@ export function BlocksControl({ key={category} data-testid="blocks-category" role="button" + tabIndex={0} className={`cursor-pointer rounded-xl border px-2 py-2 text-xs font-medium dark:border-slate-700 dark:text-white ${colorClass}`} onClick={() => setSelectedCategory( selectedCategory === category ? null : category, ) } + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + setSelectedCategory( + selectedCategory === category ? null : category, + ); + } + }} > {beautifyString((category || "All").toLowerCase())} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx index 834603cc4aad..bb57e9f26ba7 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx @@ -940,7 +940,6 @@ export const CustomNode = React.memo( className={`${blockClasses} ${errorClass} ${statusClass}`} data-id={`custom-node-${id}`} id={data.block_id} - z-index={1} data-testid={data.block_id} data-blockid={data.block_id} data-blockname={data.title} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/DataTable.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/DataTable.tsx index c58bdac64249..7a467e93010d 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/DataTable.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/DataTable.tsx @@ -125,6 +125,7 @@ export default function DataTable({ if (enableEnhancedOutputHandling && renderer) { const metadata: OutputMetadata = {}; return ( + // eslint-disable-next-line react/no-array-index-key - >(Object.fromEntries(nodes.map((node) => [node.id, node.position]))); + >(() => Object.fromEntries(nodes.map((node) => [node.id, node.position]))); // Add realtime execution status tracking for FloatingReviewsPanel useFlowRealtime(); @@ -619,9 +619,13 @@ const FlowEditor: React.FC<{ return () => cancelAnimationFrame(rafId); }, [fitView, hasAutoFramed, nodes.length]); - useEffect(() => { + // Reset auto-frame when flow changes (render-time sync instead of useEffect) + const prevFlowKeyRef = useRef(`${flowID}-${flowVersion}`); + const flowKey = `${flowID}-${flowVersion}`; + if (flowKey !== prevFlowKeyRef.current) { + prevFlowKeyRef.current = flowKey; setHasAutoFramed(false); - }, [flowID, flowVersion]); + } const navigateToNode = useCallback( (nodeId: string) => { @@ -1186,7 +1190,9 @@ const FlowEditor: React.FC<{ const WrappedFlowEditor: typeof FlowEditor = (props) => ( - + + + ); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeOutputs.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeOutputs.tsx index 2111db7d996a..967f94dadfd0 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeOutputs.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeOutputs.tsx @@ -110,6 +110,7 @@ export default function NodeOutputs({ if (enableEnhancedOutputHandling && renderer) { const metadata: OutputMetadata = {}; return ( + // eslint-disable-next-line react/no-array-index-key graph.credentials_input_schema.properties, - [graph], - ); + const agentCredentialsInputFields = graph.credentials_input_schema.properties; const credentialFields = useMemo( function getCredentialFields() { return Object.entries(agentCredentialsInputFields); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx index a8ed8a5e8e31..66182812e2ab 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx @@ -1,13 +1,11 @@ -"use client"; -import { ReactFlowProvider } from "@xyflow/react"; -import { Flow } from "./components/FlowEditor/Flow/Flow"; +import type { Metadata } from "next"; +import { BuilderContent } from "./BuilderContent"; + +export const metadata: Metadata = { + title: "Build", + description: "Build your agent", +}; export default function BuilderPage() { - return ( -
- - - -
- ); + return ; } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx index 8dcce72bae19..2fd5d73a4afc 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx @@ -1,7 +1,7 @@ "use client"; import { ChatInput } from "@/app/(platform)/copilot/components/ChatInput/ChatInput"; import { UIDataTypes, UIMessage, UITools } from "ai"; -import { LayoutGroup, motion } from "framer-motion"; +import { LayoutGroup, LazyMotion, domAnimation, m } from "framer-motion"; import { ReactNode } from "react"; import { ChatMessagesContainer } from "../ChatMessagesContainer/ChatMessagesContainer"; import { CopilotChatActionsProvider } from "../CopilotChatActionsProvider/CopilotChatActionsProvider"; @@ -38,45 +38,47 @@ export const ChatContainer = ({ const inputLayoutId = "copilot-2-chat-input"; return ( - - -
- {sessionId ? ( -
- - -
- + + +
+ {sessionId ? ( +
+ - -
- ) : ( - - )} -
-
-
+ +
+ + +
+ ) : ( + + )} +
+ + + ); }; diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/components/AudioWaveform.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/components/AudioWaveform.tsx index 50414adc89b0..9c55ce861994 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/components/AudioWaveform.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/components/AudioWaveform.tsx @@ -117,6 +117,7 @@ export function AudioWaveform({ {bars.map((height, i) => { const barHeight = Math.max(minBarHeight, height); return ( + // eslint-disable-next-line react/no-array-index-key
when an fails to load for workspace files. */ function WorkspaceMediaImage(props: React.JSX.IntrinsicElements["img"]) { - const { src, alt, ...rest } = props; + const { src, alt } = props; const [imgFailed, setImgFailed] = useState(false); const isWorkspace = src?.includes("/workspace/files/") ?? false; @@ -79,16 +80,17 @@ function WorkspaceMediaImage(props: React.JSX.IntrinsicElements["img"]) { } return ( - // eslint-disable-next-line @next/next/no-img-element - {alt { if (isWorkspace) setImgFailed(true); }} - {...rest} /> ); } @@ -195,12 +197,12 @@ export const ChatMessagesContainer = ({ "group-[.is-assistant]:bg-transparent group-[.is-assistant]:text-slate-900" } > - {message.parts.map((part, i) => { + {message.parts.map((part) => { switch (part.type) { case "text": return ( {resolveWorkspaceUrls(part.text)} @@ -209,7 +211,7 @@ export const ChatMessagesContainer = ({ case "tool-find_block": return ( ); @@ -217,7 +219,7 @@ export const ChatMessagesContainer = ({ case "tool-find_library_agent": return ( ); @@ -225,14 +227,14 @@ export const ChatMessagesContainer = ({ case "tool-get_doc_page": return ( ); case "tool-run_block": return ( ); @@ -240,42 +242,42 @@ export const ChatMessagesContainer = ({ case "tool-schedule_agent": return ( ); case "tool-create_agent": return ( ); case "tool-edit_agent": return ( ); case "tool-view_agent_output": return ( ); case "tool-search_feature_requests": return ( ); case "tool-create_feature_request": return ( ); @@ -285,7 +287,7 @@ export const ChatMessagesContainer = ({ if (part.type.startsWith("tool-")) { return ( ); diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSidebar/ChatSidebar.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSidebar/ChatSidebar.tsx index 8fc9c3f438b6..0b3490473e2e 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSidebar/ChatSidebar.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSidebar/ChatSidebar.tsx @@ -25,7 +25,7 @@ import { import { cn } from "@/lib/utils"; import { DotsThree, PlusCircleIcon, PlusIcon } from "@phosphor-icons/react"; import { useQueryClient } from "@tanstack/react-query"; -import { motion } from "framer-motion"; +import { LazyMotion, domAnimation, m } from "framer-motion"; import { parseAsString, useQueryState } from "nuqs"; import { useState } from "react"; import { DeleteChatDialog } from "../DeleteChatDialog/DeleteChatDialog"; @@ -129,7 +129,7 @@ export function ChatSidebar() { } return ( - <> + - New Chat
-
+ )} {!isCollapsed && ( -
- + )} {!isCollapsed && ( - )) )} - + )} {!isCollapsed && sessionId && ( - New Chat - + )} @@ -286,6 +286,6 @@ export function ChatSidebar() { onConfirm={handleConfirmDelete} onCancel={handleCancelDelete} /> - + ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/EmptySession/EmptySession.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/EmptySession/EmptySession.tsx index cbbe6c570e51..3e8f4a6273ec 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/EmptySession/EmptySession.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/EmptySession/EmptySession.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/Button/Button"; import { Text } from "@/components/atoms/Text/Text"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { SpinnerGapIcon } from "@phosphor-icons/react"; -import { motion } from "framer-motion"; +import { LazyMotion, domAnimation, m } from "framer-motion"; import { useEffect, useState } from "react"; import { getGreetingName, @@ -29,7 +29,7 @@ export function EmptySession({ const greetingName = getGreetingName(user); const quickActions = getQuickActions(); const [loadingAction, setLoadingAction] = useState(null); - const [inputPlaceholder, setInputPlaceholder] = useState( + const [inputPlaceholder, setInputPlaceholder] = useState(() => getInputPlaceholder(), ); @@ -49,63 +49,65 @@ export function EmptySession({ } return ( -
- -
- - Hey, {greetingName} - - - Tell me about your work — I'll find what to automate. - + +
+ +
+ + Hey, {greetingName} + + + Tell me about your work — I'll find what to automate. + -
- - - +
+ + + +
-
-
- {quickActions.map((action) => ( - - ))} -
- -
+
+ {quickActions.map((action) => ( + + ))} +
+ +
+ ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/MorphingTextAnimation/MorphingTextAnimation.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/MorphingTextAnimation/MorphingTextAnimation.tsx index aac615bb2bbb..fab39b437b4b 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/MorphingTextAnimation/MorphingTextAnimation.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/MorphingTextAnimation/MorphingTextAnimation.tsx @@ -1,5 +1,5 @@ import { cn } from "@/lib/utils"; -import { AnimatePresence, motion } from "framer-motion"; +import { AnimatePresence, LazyMotion, domAnimation, m } from "framer-motion"; interface Props { text: string; @@ -10,45 +10,47 @@ export function MorphingTextAnimation({ text, className }: Props) { const letters = text.split(""); return ( -
- - - - {letters.map((char, index) => ( - - {char === " " ? "\u00A0" : char} - - ))} - - - -
+ +
+ + + + {letters.map((char, index) => ( + // eslint-disable-next-line react/no-array-index-key + + {char === " " ? "\u00A0" : char} + + ))} + + + +
+
); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ToolAccordion/ToolAccordion.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ToolAccordion/ToolAccordion.tsx index e53df15e6c5e..efab3c78acf1 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ToolAccordion/ToolAccordion.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ToolAccordion/ToolAccordion.tsx @@ -2,7 +2,13 @@ import { cn } from "@/lib/utils"; import { CaretDownIcon } from "@phosphor-icons/react"; -import { AnimatePresence, motion, useReducedMotion } from "framer-motion"; +import { + AnimatePresence, + LazyMotion, + domAnimation, + m, + useReducedMotion, +} from "framer-motion"; import { useId } from "react"; import { useToolAccordion } from "./useToolAccordion"; @@ -38,65 +44,66 @@ export function ToolAccordion({ }); return ( -
- + + - - {isExpanded && ( - -
{children}
-
- )} -
-
+ + {isExpanded && ( + +
{children}
+
+ )} +
+
+ ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/layout.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/layout.tsx new file mode 100644 index 000000000000..9550212ccbbc --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/layout.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Copilot", + description: "Chat with your AI copilot", +}; + +export default function CopilotLayout({ + children, +}: { + children: React.ReactNode; +}) { + return children; +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/styleguide/layout.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/styleguide/layout.tsx new file mode 100644 index 000000000000..1f07f930b3a7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/styleguide/layout.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Copilot Styleguide", + description: "Copilot UI component styleguide", +}; + +export default function StyleguideLayout({ + children, +}: { + children: React.ReactNode; +}) { + return children; +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/CreateAgent/components/ClarificationQuestionsCard.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/CreateAgent/components/ClarificationQuestionsCard.tsx index abcb04731ead..3925f24fa95b 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/CreateAgent/components/ClarificationQuestionsCard.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/CreateAgent/components/ClarificationQuestionsCard.tsx @@ -161,7 +161,7 @@ export function ClarificationQuestionsCard({ return (
- {todos.map((todo, i) => ( -
+ {todos.map((todo) => ( +
{todo.status === "completed" ? ( - - Run this agent with example values or your own inputs. - + +
+ + Run this agent with example values or your own inputs. + -
- - -
- - - {showInputForm && buildInputSchema(output.agent.inputs) && ( - + + - +
+ + + {showInputForm && buildInputSchema(output.agent.inputs) && ( + +
+ Enter your inputs + setInputValues(v.formData ?? {})} + uiSchema={{ + "ui:submitButtonOptions": { norender: true }, + }} + initialValues={inputValues} + formContext={{ + showHandles: false, + size: "small", }} - > - Cancel - + /> +
+ + +
-
- - )} - -
+ + )} + +
+ ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/BlockOutputCard/BlockOutputCard.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/BlockOutputCard/BlockOutputCard.tsx index 4051927653e1..bd412bc534b1 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/BlockOutputCard/BlockOutputCard.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/BlockOutputCard/BlockOutputCard.tsx @@ -103,6 +103,7 @@ function OutputKeySection({
{visibleItems.map((item, i) => ( + // eslint-disable-next-line react/no-array-index-key ))}
diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/SetupRequirementsCard/SetupRequirementsCard.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/SetupRequirementsCard/SetupRequirementsCard.tsx index e98fea28504a..db2b7816c407 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/SetupRequirementsCard/SetupRequirementsCard.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunBlock/components/SetupRequirementsCard/SetupRequirementsCard.tsx @@ -6,7 +6,7 @@ import { Text } from "@/components/atoms/Text/Text"; import { CredentialsGroupedView } from "@/components/contextual/CredentialsInput/components/CredentialsGroupedView/CredentialsGroupedView"; import { FormRenderer } from "@/components/renderers/InputRenderer/FormRenderer"; import type { CredentialsMetaInput } from "@/lib/autogpt-server-api/types"; -import { AnimatePresence, motion } from "framer-motion"; +import { AnimatePresence, LazyMotion, domAnimation, m } from "framer-motion"; import { useState } from "react"; import { useCopilotChatActions } from "../../../../components/CopilotChatActionsProvider/useCopilotChatActions"; import { @@ -75,123 +75,124 @@ export function SetupRequirementsCard({ output }: Props) { } return ( -
- {output.message} - - {credentialFields.length > 0 && ( -
- - {isAllCredentialsComplete && !hasSentCredentials && ( + +
+ {output.message} + + {credentialFields.length > 0 && ( +
+ + {isAllCredentialsComplete && !hasSentCredentials && ( + + )} +
+ )} + + {inputSchema && ( +
- )} -
- )} - - {inputSchema && ( -
- -
- )} - - - {showInputForm && inputSchema && ( - -
- Block inputs - setInputValues(v.formData ?? {})} - uiSchema={{ - "ui:submitButtonOptions": { norender: true }, - }} - initialValues={inputValues} - formContext={{ - showHandles: false, - size: "small", - }} - /> -
- - -
-
-
+
)} - - - {expectedInputs.length > 0 && !inputSchema && ( -
- - Expected inputs - -
- {expectedInputs.map((input) => ( -
-
- - {input.title} - - - {input.required ? "Required" : "Optional"} - + + + {showInputForm && inputSchema && ( + +
+ Block inputs + setInputValues(v.formData ?? {})} + uiSchema={{ + "ui:submitButtonOptions": { norender: true }, + }} + initialValues={inputValues} + formContext={{ + showHandles: false, + size: "small", + }} + /> +
+ +
- - {input.name} • {input.type} - {input.description ? ` \u2022 ${input.description}` : ""} -
- ))} +
+ )} +
+ + {expectedInputs.length > 0 && !inputSchema && ( +
+ + Expected inputs + +
+ {expectedInputs.map((input) => ( +
+
+ + {input.title} + + + {input.required ? "Required" : "Optional"} + +
+ + {input.name} • {input.type} + {input.description ? ` \u2022 ${input.description}` : ""} + +
+ ))} +
-
- )} -
+ )} +
+ ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/ViewAgentOutput/ViewAgentOutput.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/ViewAgentOutput/ViewAgentOutput.tsx index e7265ac7cb78..9dcfae58a639 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/ViewAgentOutput/ViewAgentOutput.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/ViewAgentOutput/ViewAgentOutput.tsx @@ -209,6 +209,7 @@ export function ViewAgentOutputTool({ part }: Props) {
{items.slice(0, 3).map((item, i) => ( + // eslint-disable-next-line react/no-array-index-key ))}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx index a438568b745e..e33a9d43b804 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx @@ -25,11 +25,19 @@ export function SidebarItemCard({ }: Props) { return (
{ + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onClick?.(); + } + }} >
{icon} diff --git a/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimeline.tsx b/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimeline.tsx index c54209e1dfa1..aa5323fceaf7 100644 --- a/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimeline.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimeline.tsx @@ -1,189 +1,8 @@ -import { GraphExecutionMeta, LibraryAgent } from "@/lib/autogpt-server-api"; -import { - ComposedChart, - DefaultLegendContentProps, - Legend, - Line, - ResponsiveContainer, - Scatter, - Tooltip, - XAxis, - YAxis, -} from "recharts"; -import { differenceInHours, format } from "date-fns"; -import { Card } from "@/components/__legacy__/ui/card"; -import { cn, hashString } from "@/lib/utils"; -import React from "react"; -import { FlowRunStatusBadge } from "@/app/(platform)/monitoring/components/FlowRunStatusBadge"; +import dynamic from "next/dynamic"; -export const FlowRunsTimeline = ({ - flows, - executions, - dataMin, - className, -}: { - flows: LibraryAgent[]; - executions: GraphExecutionMeta[]; - dataMin: "dataMin" | number; - className?: string; -}) => ( - /* TODO: make logarithmic? */ - - - { - const now = new Date(); - const time = new Date(unixTime); - return differenceInHours(now, time) < 24 - ? format(time, "HH:mm") - : format(time, "yyyy-MM-dd HH:mm"); - }} - name="Time" - scale="time" - /> - (s > 90 ? `${Math.round(s / 60)}m` : `${s}s`)} - /> - { - if (payload && payload.length) { - const data: GraphExecutionMeta & { - time: number; - _duration: number; - } = payload[0].payload; - const flow = flows.find((f) => f.graph_id === data.graph_id); - return ( - -

- Agent: {flow ? flow.name : "Unknown"} -

-
- Status:  - -
-

- Started:{" "} - {data.started_at - ? format(data.started_at, "yyyy-MM-dd HH:mm:ss") - : "—"} -

- {data.stats && ( -

- Duration / run time:{" "} - {formatDuration(data.stats.duration)} /{" "} - {formatDuration(data.stats.node_exec_time)} -

- )} -
- ); - } - return null; - }} - /> - {flows.map((flow) => ( - e.graph_id == flow.graph_id && e.started_at) - .map((e) => ({ - ...e, - time: - (e.started_at?.getTime() ?? 0) + - (e.stats?.node_exec_time ?? 0) * 1000, - _duration: e.stats?.node_exec_time ?? 0, - }))} - name={flow.name} - fill={`hsl(${(hashString(flow.id) * 137.5) % 360}, 70%, 50%)`} - /> - ))} - {executions - .filter((e) => e.started_at && e.ended_at) - .map((execution) => ( - - ))} - } - wrapperStyle={{ - bottom: 0, - left: 0, - right: 0, - width: "100%", - display: "flex", - justifyContent: "center", - }} - /> -
-
+export const FlowRunsTimeline = dynamic( + () => import("./FlowRunsTimelineChart"), + { ssr: false }, ); export default FlowRunsTimeline; - -const ScrollableLegend: React.FC< - DefaultLegendContentProps & { className?: string } -> = ({ payload, className }) => { - return ( -
- {payload?.map((entry, index) => { - if (entry.type == "none") return; - return ( - - - {entry.value} - - ); - })} -
- ); -}; - -function formatDuration(seconds: number): string { - return ( - (seconds < 100 ? seconds.toPrecision(2) : Math.round(seconds)).toString() + - "s" - ); -} diff --git a/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimelineChart.tsx b/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimelineChart.tsx new file mode 100644 index 000000000000..dbba1a71952b --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/monitoring/components/FlowRunsTimelineChart.tsx @@ -0,0 +1,189 @@ +import { GraphExecutionMeta, LibraryAgent } from "@/lib/autogpt-server-api"; +import { + ComposedChart, + DefaultLegendContentProps, + Legend, + Line, + ResponsiveContainer, + Scatter, + Tooltip, + XAxis, + YAxis, +} from "recharts"; +import { differenceInHours, format } from "date-fns"; +import { Card } from "@/components/__legacy__/ui/card"; +import { cn, hashString } from "@/lib/utils"; +import React from "react"; +import { FlowRunStatusBadge } from "@/app/(platform)/monitoring/components/FlowRunStatusBadge"; + +const FlowRunsTimelineChart = ({ + flows, + executions, + dataMin, + className, +}: { + flows: LibraryAgent[]; + executions: GraphExecutionMeta[]; + dataMin: "dataMin" | number; + className?: string; +}) => ( + /* TODO: make logarithmic? */ + + + { + const now = new Date(); + const time = new Date(unixTime); + return differenceInHours(now, time) < 24 + ? format(time, "HH:mm") + : format(time, "yyyy-MM-dd HH:mm"); + }} + name="Time" + scale="time" + /> + (s > 90 ? `${Math.round(s / 60)}m` : `${s}s`)} + /> + { + if (payload && payload.length) { + const data: GraphExecutionMeta & { + time: number; + _duration: number; + } = payload[0].payload; + const flow = flows.find((f) => f.graph_id === data.graph_id); + return ( + +

+ Agent: {flow ? flow.name : "Unknown"} +

+
+ Status:  + +
+

+ Started:{" "} + {data.started_at + ? format(data.started_at, "yyyy-MM-dd HH:mm:ss") + : "---"} +

+ {data.stats && ( +

+ Duration / run time:{" "} + {formatDuration(data.stats.duration)} /{" "} + {formatDuration(data.stats.node_exec_time)} +

+ )} +
+ ); + } + return null; + }} + /> + {flows.map((flow) => ( + e.graph_id == flow.graph_id && e.started_at) + .map((e) => ({ + ...e, + time: + (e.started_at?.getTime() ?? 0) + + (e.stats?.node_exec_time ?? 0) * 1000, + _duration: e.stats?.node_exec_time ?? 0, + }))} + name={flow.name} + fill={`hsl(${(hashString(flow.id) * 137.5) % 360}, 70%, 50%)`} + /> + ))} + {executions + .filter((e) => e.started_at && e.ended_at) + .map((execution) => ( + + ))} + } + wrapperStyle={{ + bottom: 0, + left: 0, + right: 0, + width: "100%", + display: "flex", + justifyContent: "center", + }} + /> +
+
+); + +export default FlowRunsTimelineChart; + +const ScrollableLegend: React.FC< + DefaultLegendContentProps & { className?: string } +> = ({ payload, className }) => { + return ( +
+ {payload?.map((entry) => { + if (entry.type == "none") return; + return ( + + + {entry.value} + + ); + })} +
+ ); +}; + +function formatDuration(seconds: number): string { + return ( + (seconds < 100 ? seconds.toPrecision(2) : Math.round(seconds)).toString() + + "s" + ); +} diff --git a/autogpt_platform/frontend/src/app/page.tsx b/autogpt_platform/frontend/src/app/page.tsx index ce67760eda22..cdf29157071b 100644 --- a/autogpt_platform/frontend/src/app/page.tsx +++ b/autogpt_platform/frontend/src/app/page.tsx @@ -1,15 +1,12 @@ -"use client"; +import { redirect } from "next/navigation"; -import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner"; -import { useRouter } from "next/navigation"; -import { useEffect } from "react"; +import type { Metadata } from "next"; -export default function Page() { - const router = useRouter(); - - useEffect(() => { - router.replace("/copilot"); - }, [router]); +export const metadata: Metadata = { + title: "AutoGPT Platform", + description: "AutoGPT Platform", +}; - return ; +export default function Page() { + redirect("/copilot"); } diff --git a/autogpt_platform/frontend/src/components/__legacy__/ui/render.tsx b/autogpt_platform/frontend/src/components/__legacy__/ui/render.tsx index b290c5180974..e0a0130f2b28 100644 --- a/autogpt_platform/frontend/src/components/__legacy__/ui/render.tsx +++ b/autogpt_platform/frontend/src/components/__legacy__/ui/render.tsx @@ -1,6 +1,7 @@ "use client"; import * as React from "react"; +import Image from "next/image"; const getYouTubeVideoId = (url: string) => { const regExp = @@ -76,6 +77,7 @@ const VideoRenderer: React.FC<{ videoUrl: string }> = ({ videoUrl }) => { width="100%" height="315" src={`https://www.youtube.com/embed/${videoId}`} + title="Embedded content" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen > @@ -92,15 +94,15 @@ const VideoRenderer: React.FC<{ videoUrl: string }> = ({ videoUrl }) => { const ImageRenderer: React.FC<{ imageUrl: string }> = ({ imageUrl }) => { return (
- - Image - + Image
); }; diff --git a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx index 1c455863ddda..a6ebd923920b 100644 --- a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx +++ b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx @@ -93,7 +93,7 @@ export function APIKeyCredentialsModal({ Required scope(s) for this block:{" "} {schema.credentials_scopes?.map((s, i, a) => ( - + {s} {i < a.length - 1 && ", "} diff --git a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/HotScopedCredentialsModal/HotScopedCredentialsModal.tsx b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/HotScopedCredentialsModal/HotScopedCredentialsModal.tsx index 63d2ae1ac573..43f615c580e3 100644 --- a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/HotScopedCredentialsModal/HotScopedCredentialsModal.tsx +++ b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/HotScopedCredentialsModal/HotScopedCredentialsModal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useRef, useState } from "react"; import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -66,20 +66,21 @@ export function HostScopedCredentialsModal({ }); const [headerPairs, setHeaderPairs] = useState< - Array<{ key: string; value: string }> - >([{ key: "", value: "" }]); + Array<{ id: string; key: string; value: string }> + >([{ id: crypto.randomUUID(), key: "", value: "" }]); - // Update form values when siblingInputs change - useEffect(() => { + // Update form values when siblingInputs change (render-time sync instead of useEffect) + const prevHostRef = useRef(currentHost); + if (currentHost !== prevHostRef.current) { + prevHostRef.current = currentHost; if (currentHost) { form.setValue("host", currentHost); form.setValue("title", currentHost); } else { - // Reset to empty when no current host form.setValue("host", ""); form.setValue("title", "Manual Entry"); } - }, [currentHost, form]); + } if ( !credentials || @@ -92,7 +93,10 @@ export function HostScopedCredentialsModal({ const { provider, providerName, createHostScopedCredentials } = credentials; const addHeaderPair = () => { - setHeaderPairs([...headerPairs, { key: "", value: "" }]); + setHeaderPairs([ + ...headerPairs, + { id: crypto.randomUUID(), key: "", value: "" }, + ]); }; const removeHeaderPair = (index: number) => { @@ -192,7 +196,7 @@ export function HostScopedCredentialsModal({ {headerPairs.map((pair, index) => ( -
+
void; @@ -53,15 +54,15 @@ export function CronSchedulerDialog(props: CronSchedulerDialogProps) { const userTimezone = useUserTimezone(); const timezoneDisplay = getTimezoneDisplayName(userTimezone || "UTC"); - // Reset state when dialog opens - useEffect(() => { - if (open) { - const defaultName = - props.mode === "with-name" ? props.defaultScheduleName || "" : ""; - setScheduleName(defaultName); - setCronExpression(defaultCronExpression); - } - }, [open, props, defaultCronExpression]); + // Reset state when dialog opens (render-time sync instead of useEffect) + const prevOpenRef = useRef(open); + if (open && !prevOpenRef.current) { + const defaultName = + props.mode === "with-name" ? props.defaultScheduleName || "" : ""; + setScheduleName(defaultName); + setCronExpression(defaultCronExpression); + } + prevOpenRef.current = open; const handleDone = () => { if (props.mode === "with-name" && !scheduleName.trim()) { @@ -100,8 +101,11 @@ export function CronSchedulerDialog(props: CronSchedulerDialogProps) {
{props.mode === "with-name" && (
- + setScheduleName(e.target.value)} placeholder="Enter a name for this schedule" @@ -121,9 +125,9 @@ export function CronSchedulerDialog(props: CronSchedulerDialogProps) {

No timezone set. Schedule will run in UTC. - + Set your timezone - +

) : ( diff --git a/autogpt_platform/frontend/src/components/contextual/CronScheduler/cron-scheduler.tsx b/autogpt_platform/frontend/src/components/contextual/CronScheduler/cron-scheduler.tsx index a0e1b9f1b316..910cf31538e1 100644 --- a/autogpt_platform/frontend/src/components/contextual/CronScheduler/cron-scheduler.tsx +++ b/autogpt_platform/frontend/src/components/contextual/CronScheduler/cron-scheduler.tsx @@ -452,7 +452,7 @@ export function CronScheduler({ const monthNumber = i + 1; return (
@@ -326,6 +327,7 @@ export function MCPToolDialog({ value={manualToken} onChange={(e) => setManualToken(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleDiscoverTools()} + autoFocus />
)} diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx index 2a6da7899ff2..885877786fc5 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx @@ -89,6 +89,7 @@ export function ActivityDropdown({ className="!focus:border-1 w-full pr-10" wrapperClassName="!mb-0" autoComplete="off" + autoFocus />
@@ -327,7 +326,6 @@ export function MCPToolDialog({ value={manualToken} onChange={(e) => setManualToken(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleDiscoverTools()} - autoFocus />
)} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenuContent/GraphContent.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenuContent/GraphContent.tsx index d45b1e7534fa..b99eb4d1687f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenuContent/GraphContent.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenuContent/GraphContent.tsx @@ -85,12 +85,20 @@ export const GraphSearchContent: React.FC = ({
onNodeSelect(node.id)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onNodeSelect(node.id); + } + }} onMouseEnter={() => { setSelectedIndex(index); onNodeHover?.(node.id); diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx index e33a9d43b804..d18655bc2c84 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/sidebar/SidebarRunsList/components/SidebarItemCard.tsx @@ -57,7 +57,13 @@ export function SidebarItemCard({
{actions ? ( -
e.stopPropagation()}>{actions}
+
e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + > + {actions} +
) : null}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx index 885877786fc5..2a6da7899ff2 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx @@ -89,7 +89,6 @@ export function ActivityDropdown({ className="!focus:border-1 w-full pr-10" wrapperClassName="!mb-0" autoComplete="off" - autoFocus />