diff --git a/src/app/extensions/page.tsx b/src/app/extensions/page.tsx index 2349106..b92179b 100644 --- a/src/app/extensions/page.tsx +++ b/src/app/extensions/page.tsx @@ -31,7 +31,7 @@ function ExtensionsPageInner() { return (
-
+

Extensions

setTab(v as ExtTab)}> diff --git a/src/components/layout/AppShell.tsx b/src/components/layout/AppShell.tsx index fb0aae6..7d4d5e2 100644 --- a/src/components/layout/AppShell.tsx +++ b/src/components/layout/AppShell.tsx @@ -6,10 +6,16 @@ import { TooltipProvider } from "@/components/ui/tooltip"; import { NavRail } from "./NavRail"; import { ChatListPanel } from "./ChatListPanel"; import { RightPanel } from "./RightPanel"; +import { ResizeHandle } from "./ResizeHandle"; import { UpdateDialog } from "./UpdateDialog"; import { PanelContext, type PanelContent } from "@/hooks/usePanel"; import { UpdateContext, type UpdateInfo } from "@/hooks/useUpdate"; +const CHATLIST_MIN = 180; +const CHATLIST_MAX = 400; +const RIGHTPANEL_MIN = 200; +const RIGHTPANEL_MAX = 480; + const LG_BREAKPOINT = 1024; const CHECK_INTERVAL = 8 * 60 * 60 * 1000; // 8 hours const DISMISSED_VERSION_KEY = "codepilot_dismissed_update_version"; @@ -19,6 +25,36 @@ export function AppShell({ children }: { children: React.ReactNode }) { const [chatListOpen, setChatListOpenRaw] = useState(false); + // Panel width state with localStorage persistence + const [chatListWidth, setChatListWidth] = useState(() => { + if (typeof window === "undefined") return 240; + return parseInt(localStorage.getItem("codepilot_chatlist_width") || "240"); + }); + const [rightPanelWidth, setRightPanelWidth] = useState(() => { + if (typeof window === "undefined") return 288; + return parseInt(localStorage.getItem("codepilot_rightpanel_width") || "288"); + }); + + const handleChatListResize = useCallback((delta: number) => { + setChatListWidth((w) => Math.min(CHATLIST_MAX, Math.max(CHATLIST_MIN, w + delta))); + }, []); + const handleChatListResizeEnd = useCallback(() => { + setChatListWidth((w) => { + localStorage.setItem("codepilot_chatlist_width", String(w)); + return w; + }); + }, []); + + const handleRightPanelResize = useCallback((delta: number) => { + setRightPanelWidth((w) => Math.min(RIGHTPANEL_MAX, Math.max(RIGHTPANEL_MIN, w - delta))); + }, []); + const handleRightPanelResizeEnd = useCallback(() => { + setRightPanelWidth((w) => { + localStorage.setItem("codepilot_rightpanel_width", String(w)); + return w; + }); + }, []); + // Panel state const isChatRoute = pathname.startsWith("/chat/") || pathname === "/chat"; const isChatDetailRoute = pathname.startsWith("/chat/"); @@ -165,7 +201,10 @@ export function AppShell({ children }: { children: React.ReactNode }) { hasUpdate={updateInfo?.updateAvailable ?? false} skipPermissionsActive={skipPermissionsActive} /> - + + {chatListOpen && ( + + )}
{/* Electron draggable title bar region */}
{children}
- {isChatDetailRoute && } + {isChatDetailRoute && panelOpen && ( + + )} + {isChatDetailRoute && }
diff --git a/src/components/layout/ChatListPanel.tsx b/src/components/layout/ChatListPanel.tsx index 35d36ea..314dc0a 100644 --- a/src/components/layout/ChatListPanel.tsx +++ b/src/components/layout/ChatListPanel.tsx @@ -21,6 +21,7 @@ import type { ChatSession } from "@/types"; interface ChatListPanelProps { open: boolean; + width?: number; } function formatRelativeTime(dateStr: string): string { @@ -69,7 +70,7 @@ const MODE_BADGE_CONFIG = { ask: { label: "Ask", className: "bg-green-500/10 text-green-500" }, } as const; -export function ChatListPanel({ open }: ChatListPanelProps) { +export function ChatListPanel({ open, width }: ChatListPanelProps) { const pathname = usePathname(); const router = useRouter(); const { streamingSessionId, pendingApprovalSessionId } = usePanel(); @@ -150,7 +151,7 @@ export function ChatListPanel({ open }: ChatListPanelProps) { if (!open) return null; return ( -