"use client" import { useState, useEffect, useRef, useCallback } from "react" import { usePathname } from "next/navigation" import { MessageSquare } from "lucide-react" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" import { useChatPanel, useChatState, useRenderState, } from "./chat-provider" import { ChatView } from "./chat-view" import { isNative } from "@/lib/native/platform" export function ChatPanelShell() { const { isOpen, open, close, toggle } = useChatPanel() const chat = useChatState() const { spec: renderSpec, isRendering } = useRenderState() const pathname = usePathname() const hasRenderedUI = !!renderSpec?.root || isRendering // dashboard acts as "page" variant only when NOT rendering const isDashboard = pathname === "/dashboard" && !hasRenderedUI // auto-open panel when leaving dashboard with messages const prevIsDashboard = useRef(isDashboard) useEffect(() => { if ( prevIsDashboard.current && !isDashboard && chat.messages.length > 0 ) { open() } prevIsDashboard.current = isDashboard }, [isDashboard, chat.messages.length, open]) // resize state (panel mode only) const [panelWidth, setPanelWidth] = useState(480) const [isResizing, setIsResizing] = useState(false) const dragStartX = useRef(0) const dragStartWidth = useRef(0) useEffect(() => { const onMouseMove = (e: MouseEvent) => { if (!dragStartWidth.current) return const delta = dragStartX.current - e.clientX const next = Math.min( 720, Math.max(320, dragStartWidth.current + delta) ) setPanelWidth(next) } const onMouseUp = () => { if (!dragStartWidth.current) return dragStartWidth.current = 0 setIsResizing(false) document.body.style.cursor = "" document.body.style.userSelect = "" } window.addEventListener("mousemove", onMouseMove) window.addEventListener("mouseup", onMouseUp) return () => { window.removeEventListener("mousemove", onMouseMove) window.removeEventListener("mouseup", onMouseUp) } }, []) const handleResizeStart = useCallback( (e: React.MouseEvent) => { e.preventDefault() setIsResizing(true) dragStartX.current = e.clientX dragStartWidth.current = panelWidth document.body.style.cursor = "col-resize" document.body.style.userSelect = "none" }, [panelWidth] ) // keyboard shortcuts (panel mode only) useEffect(() => { if (isDashboard) return const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === ".") { e.preventDefault() toggle() } if (e.key === "Escape" && isOpen) { close() } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) }, [isDashboard, isOpen, close, toggle]) // native keyboard offset for chat input const [keyboardHeight, setKeyboardHeight] = useState(0) useEffect(() => { if (!isNative()) return let cleanup: (() => void) | undefined async function setupKeyboard() { const { Keyboard } = await import( "@capacitor/keyboard" ) const showListener = await Keyboard.addListener( "keyboardWillShow", (info) => setKeyboardHeight(info.keyboardHeight), ) const hideListener = await Keyboard.addListener( "keyboardWillHide", () => setKeyboardHeight(0), ) cleanup = () => { showListener.remove() hideListener.remove() } } setupKeyboard() return () => cleanup?.() }, []) // container width/style for panel mode const panelStyle = !isDashboard && isOpen ? { width: panelWidth } : undefined const keyboardStyle = keyboardHeight > 0 ? { paddingBottom: keyboardHeight } : undefined return ( <>