"use client"; import * as Tabs from "@radix-ui/react-tabs"; import { useLayoutEffect, useRef } from "react"; import { useSearchParams } from "next/navigation"; import { Sparkles } from "lucide-react"; import { AgentsMode } from "./agents-mode"; import { ChatMode } from "./chat-mode"; import { changeAIMode, parseAIMode } from "./workspace-mode"; import { createComposerFocusMemory } from "./workspace-interaction"; export function AIWorkspace({ workspaceSlug }: { workspaceSlug: string }) { const searchParams = useSearchParams(); const mode = parseAIMode(searchParams.get("tab")); const rootRef = useRef(null); const focusMemory = useRef(createComposerFocusMemory()); useLayoutEffect(() => { const root = rootRef.current; const tabHasFocus = !!root ?.querySelector('[role="tablist"]') ?.contains(document.activeElement); if (focusMemory.current.activate(mode, tabHasFocus)) { root ?.querySelector(`[data-composer-mode="${mode}"]`) ?.focus({ preventScroll: true }); } }, [mode]); return ( { const target = event.target; // Tabs retain keyboard focus; only focus within a panel changes its memory. if ( !(target instanceof HTMLElement) || !target.closest('[role="tabpanel"]') ) return; focusMemory.current.record(mode, target.dataset.composerMode === mode); }} onValueChange={(value) => { // Next integrates native history with useSearchParams, including popstate. changeAIMode(window.location, parseAIMode(value), (href) => { window.history.pushState(null, "", href); }); }} className="flex h-full min-h-0 flex-col" >

AI Assistant

Chat about your workspace or draft reusable agents

{(["chat", "agents"] as const).map((value) => ( {value === "chat" ? "Chat" : "Agents"} ))}
{/* Keeping both trees mounted retains drafts, Chat transport, and scroll containers. */}
); }