mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
refactor(frontend): Move OttoChatWidget out of root layout (#9951)
- Resolves #9950 ### Changes 🏗️ - Move `<OttoChatWidget>` from root layout into `FlowEditor` - Pass graph info directly into `OttoChatWidget` instead of using `useAgentGraph` - Rearrange z-indices of elements in the builder ### 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: - Go to `/build` - [x] -> chat widget should show up in the bottom right corner - Open the widget and ask Otto something - [x] -> should work normally - Add a few blocks and save the graph - [x] -> "Include graph data" should show up - Click "Include graph data" and ask Otto something about your graph - [x] -> Otto should be aware of the graph structure and metadata
This commit is contained in:
committed by
GitHub
parent
0a79e1c5fd
commit
cf9cf4e7dd
@@ -10,7 +10,6 @@ import "./globals.css";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { Providers } from "@/app/providers";
|
||||
import TallyPopupSimple from "@/components/TallyPopup";
|
||||
import OttoChatWidget from "@/components/OttoChatWidget";
|
||||
import { GoogleAnalytics } from "@/components/analytics/google-analytics";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
||||
@@ -57,9 +56,6 @@ export default async function RootLayout({
|
||||
<div className="flex min-h-screen flex-col items-stretch justify-items-stretch">
|
||||
{children}
|
||||
<TallyPopupSimple />
|
||||
<Suspense fallback={null}>
|
||||
<OttoChatWidget />
|
||||
</Suspense>
|
||||
</div>
|
||||
<Toaster />
|
||||
</Providers>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
import React, {
|
||||
createContext,
|
||||
useState,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
MouseEvent,
|
||||
createContext,
|
||||
Suspense,
|
||||
} from "react";
|
||||
import {
|
||||
ReactFlow,
|
||||
@@ -48,6 +49,7 @@ import RunnerUIWrapper, {
|
||||
RunnerUIWrapperRef,
|
||||
} from "@/components/RunnerUIWrapper";
|
||||
import PrimaryActionBar from "@/components/PrimaryActionButton";
|
||||
import OttoChatWidget from "@/components/OttoChatWidget";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { useCopyPaste } from "../hooks/useCopyPaste";
|
||||
import { CronScheduler } from "./cronScheduler";
|
||||
@@ -676,7 +678,7 @@ const FlowEditor: React.FC<{
|
||||
<Controls />
|
||||
<Background className="dark:bg-slate-800" />
|
||||
<ControlPanel
|
||||
className="absolute z-10"
|
||||
className="absolute z-20"
|
||||
controls={editorControls}
|
||||
topChildren={
|
||||
<BlocksControl
|
||||
@@ -701,6 +703,7 @@ const FlowEditor: React.FC<{
|
||||
}
|
||||
></ControlPanel>
|
||||
<PrimaryActionBar
|
||||
className="absolute bottom-0 left-1/2 z-20 -translate-x-1/2"
|
||||
onClickAgentOutputs={() => runnerUIRef.current?.openRunnerOutput()}
|
||||
onClickRunAgent={() => {
|
||||
if (!savedAgent) {
|
||||
@@ -740,6 +743,12 @@ const FlowEditor: React.FC<{
|
||||
scheduleRunner={scheduleRunner}
|
||||
requestSaveAndRun={requestSaveAndRun}
|
||||
/>
|
||||
<Suspense fallback={null}>
|
||||
<OttoChatWidget
|
||||
graphID={flowID}
|
||||
className="fixed bottom-4 right-4 z-20"
|
||||
/>
|
||||
</Suspense>
|
||||
</FlowContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useSearchParams, usePathname } from "next/navigation";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import useAgentGraph from "../hooks/useAgentGraph";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { GraphID } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
import type { GraphID } from "@/lib/autogpt-server-api/types";
|
||||
import { askOtto } from "@/app/(platform)/build/actions";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Message {
|
||||
type: "user" | "assistant";
|
||||
content: string;
|
||||
}
|
||||
|
||||
const OttoChatWidget = () => {
|
||||
export default function OttoChatWidget({
|
||||
graphID,
|
||||
className,
|
||||
}: {
|
||||
graphID?: GraphID;
|
||||
className?: string;
|
||||
}): React.ReactNode {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [includeGraphData, setIncludeGraphData] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const flowID = searchParams.get("flowID");
|
||||
const { nodes, edges } = useAgentGraph(
|
||||
flowID ? (flowID as GraphID) : undefined,
|
||||
);
|
||||
const { toast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
// Add welcome message when component mounts
|
||||
@@ -34,7 +32,7 @@ const OttoChatWidget = () => {
|
||||
setMessages([
|
||||
{
|
||||
type: "assistant",
|
||||
content: "Hello im Otto! Ask me anything about AutoGPT!",
|
||||
content: "Hello, I am Otto! Ask me anything about AutoGPT!",
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -84,7 +82,7 @@ const OttoChatWidget = () => {
|
||||
userMessage,
|
||||
conversationHistory,
|
||||
includeGraphData,
|
||||
flowID || undefined,
|
||||
graphID,
|
||||
);
|
||||
|
||||
// Check if the response contains an error
|
||||
@@ -131,13 +129,13 @@ const OttoChatWidget = () => {
|
||||
};
|
||||
|
||||
// Don't render the chat widget if we're not on the build page or in local mode
|
||||
if (process.env.NEXT_PUBLIC_BEHAVE_AS !== "CLOUD" || pathname !== "/build") {
|
||||
if (process.env.NEXT_PUBLIC_BEHAVE_AS !== "CLOUD") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-50">
|
||||
<div className={className}>
|
||||
<button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="inline-flex h-14 w-14 items-center justify-center whitespace-nowrap rounded-2xl bg-[rgba(65,65,64,1)] text-neutral-50 shadow transition-colors hover:bg-neutral-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90 dark:focus-visible:ring-neutral-300"
|
||||
@@ -160,7 +158,13 @@ const OttoChatWidget = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-50 flex h-[600px] w-[600px] flex-col rounded-lg border bg-background shadow-xl">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-[600px] w-[600px] flex-col rounded-lg border bg-background shadow-xl",
|
||||
className,
|
||||
"z-40",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between border-b p-4">
|
||||
<h2 className="font-semibold">Otto Assistant</h2>
|
||||
@@ -269,7 +273,7 @@ const OttoChatWidget = () => {
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
{nodes && edges && (
|
||||
{graphID && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -303,6 +307,4 @@ const OttoChatWidget = () => {
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OttoChatWidget;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button } from "./ui/button";
|
||||
import { Clock, LogOut, ChevronLeft } from "lucide-react";
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { FaSpinner } from "react-icons/fa";
|
||||
import { Clock, LogOut } from "lucide-react";
|
||||
import { IconPlay, IconSquare } from "@/components/ui/icons";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { FaSpinner } from "react-icons/fa";
|
||||
|
||||
interface PrimaryActionBarProps {
|
||||
onClickAgentOutputs: () => void;
|
||||
@@ -18,6 +19,7 @@ interface PrimaryActionBarProps {
|
||||
isScheduling: boolean;
|
||||
requestStopRun: () => void;
|
||||
runAgentTooltip: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const PrimaryActionBar: React.FC<PrimaryActionBarProps> = ({
|
||||
@@ -29,6 +31,7 @@ const PrimaryActionBar: React.FC<PrimaryActionBarProps> = ({
|
||||
isScheduling,
|
||||
requestStopRun,
|
||||
runAgentTooltip,
|
||||
className,
|
||||
}) => {
|
||||
const runButtonLabel = !isRunning ? "Run" : "Stop";
|
||||
|
||||
@@ -37,8 +40,13 @@ const PrimaryActionBar: React.FC<PrimaryActionBarProps> = ({
|
||||
const runButtonOnClick = !isRunning ? onClickRunAgent : requestStopRun;
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-0 left-1/2 z-50 flex w-fit -translate-x-1/2 transform select-none items-center justify-center p-4">
|
||||
<div className={`flex gap-1 md:gap-4`}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex w-fit select-none items-center justify-center p-4",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex gap-1 md:gap-4">
|
||||
<Tooltip key="ViewOutputs" delayDuration={500}>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
|
||||
@@ -56,7 +56,7 @@ const TallyPopupSimple = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-1 right-24 z-50 hidden select-none items-center gap-4 p-3 transition-all duration-300 ease-in-out md:flex">
|
||||
<div className="fixed bottom-1 right-24 z-20 hidden select-none items-center gap-4 p-3 transition-all duration-300 ease-in-out md:flex">
|
||||
{show_tutorial && (
|
||||
<Button
|
||||
variant="default"
|
||||
|
||||
Reference in New Issue
Block a user