diff --git a/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx index 27dfb170c4..02228c8678 100644 --- a/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx +++ b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { useState } from "react"; import { fn } from "storybook/test"; import { DesktopPanelView, type DesktopPanelViewProps } from "./DesktopPanel"; @@ -6,6 +7,9 @@ const defaults: DesktopPanelViewProps = { status: "idle", reconnect: fn(), attach: fn(), + isControlling: false, + onTakeControl: fn(), + onReleaseControl: fn(), }; const meta: Meta = { @@ -14,11 +18,30 @@ const meta: Meta = { args: defaults, decorators: [ (Story) => ( -
+
), ], + render: function RenderComponent(args) { + const [isControlling, setIsControlling] = useState(args.isControlling); + return ( + setIsControlling(true)} + onReleaseControl={() => setIsControlling(false)} + /> + ); + }, }; export default meta; type Story = StoryObj; @@ -33,6 +56,10 @@ export const Connected: Story = { args: { status: "connected" }, }; +export const ConnectedControlling: Story = { + args: { status: "connected", isControlling: true }, +}; + export const Disconnected: Story = { args: { status: "disconnected" }, }; diff --git a/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx index bbd8549380..0e99b51084 100644 --- a/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx +++ b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx @@ -1,7 +1,9 @@ +import { HandIcon, MousePointer2Icon } from "lucide-react"; import type { FC } from "react"; import { useState } from "react"; import { Button } from "#/components/Button/Button"; import { Spinner } from "#/components/Spinner/Spinner"; +import { cn } from "#/utils/cn"; import { useDesktopConnection } from "../../hooks/useDesktopConnection"; type DesktopConnectionStatus = @@ -21,6 +23,9 @@ export interface DesktopPanelViewProps { status: DesktopConnectionStatus; reconnect: () => void; attach: (container: HTMLElement) => void; + isControlling: boolean; + onTakeControl: () => void; + onReleaseControl: () => void; } export const DesktopPanel: FC = ({ chatId, isVisible }) => { @@ -32,12 +37,24 @@ export const DesktopPanel: FC = ({ chatId, isVisible }) => { setActivated(true); } + const [isControlling, setIsControlling] = useState(false); + if (!isVisible && isControlling) { + setIsControlling(false); + } + const { status, reconnect, attach } = useDesktopConnection({ chatId, activated, }); return ( - + setIsControlling(true)} + onReleaseControl={() => setIsControlling(false)} + /> ); }; @@ -45,6 +62,9 @@ export const DesktopPanelView: FC = ({ status, reconnect, attach, + isControlling, + onTakeControl, + onReleaseControl, }) => { if (status === "connecting") { return ( @@ -89,11 +109,43 @@ export const DesktopPanelView: FC = ({ // status === "connected" return ( -
{ - if (el) attach(el); - }} - className="h-full w-full" - /> +
+ {/* "Release Control" button — top-right, only when controlling */} + {isControlling && ( + + )} + {/* VNC container — pointer-events toggled */} +
{ + if (el) attach(el); + }} + className={cn("h-full w-full", !isControlling && "pointer-events-none")} + /> + {/* "Take Control" hover overlay — only when NOT controlling */} + {!isControlling && ( +
+ + + +
+ )} +
); };