"use client"; import { useCallback, useRef, useState } from "react"; import { IconLayout, IconCheck, IconDeviceFloppy, IconColumns3, IconFileText, IconDeviceDesktop, IconBrandVscode, IconTrash, } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { Input } from "@kandev/ui/input"; import { Label } from "@kandev/ui/label"; import { Checkbox } from "@kandev/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@kandev/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@kandev/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { Badge } from "@kandev/ui/badge"; import { useDockviewStore, type BuiltInPreset } from "@/lib/state/dockview-store"; import { useAppStore } from "@/components/state-provider"; import { updateUserSettings } from "@/lib/api/domains/settings-api"; import type { SavedLayout } from "@/lib/types/http"; type PresetOption = { id: BuiltInPreset; label: string; description: string; icon: React.ElementType; }; const BUILT_IN_PRESETS: PresetOption[] = [ { id: "default", label: "Default", description: "Sidebar + Agent + Changes/Files/Terminal", icon: IconColumns3, }, { id: "plan", label: "Plan Mode", description: "Sidebar + Agent + Plan (side-by-side)", icon: IconFileText, }, { id: "preview", label: "Preview Mode", description: "Sidebar + Agent + Browser (side-by-side)", icon: IconDeviceDesktop, }, { id: "vscode", label: "VS Code", description: "Sidebar + Agent/VS Code (tabbed)", icon: IconBrandVscode, }, ]; function SaveLayoutDialog({ open, onOpenChange, }: { open: boolean; onOpenChange: (open: boolean) => void; }) { const [name, setName] = useState(""); const [isDefault, setIsDefault] = useState(false); const [saving, setSaving] = useState(false); const captureCurrentLayout = useDockviewStore((s) => s.captureCurrentLayout); const savedLayouts = useAppStore((s) => s.userSettings.savedLayouts); const handleSave = useCallback(async () => { const trimmed = name.trim(); if (!trimmed) return; setSaving(true); try { const layout = captureCurrentLayout(); const newLayout: SavedLayout = { id: `layout-${Date.now()}`, name: trimmed, is_default: isDefault, layout, created_at: new Date().toISOString(), }; // If this layout is the default, unset other defaults const updated = savedLayouts.map((l) => (isDefault ? { ...l, is_default: false } : l)); await updateUserSettings({ saved_layouts: [...updated, newLayout], }); setName(""); setIsDefault(false); onOpenChange(false); } finally { setSaving(false); } }, [name, isDefault, captureCurrentLayout, savedLayouts, onOpenChange]); return ( Save Current Layout Save your current panel arrangement as a reusable layout.
setName(e.target.value)} placeholder="My layout" autoFocus onKeyDown={(e) => { if (e.key === "Enter") handleSave(); }} />
setIsDefault(checked === true)} />
); } function SavedLayoutItems({ layouts, onApply, onDelete, }: { layouts: SavedLayout[]; onApply: (layout: SavedLayout) => void; onDelete: (layoutId: string) => void; }) { if (layouts.length === 0) { return
No saved layouts
; } return layouts.map((layout) => ( onApply(layout)} > {layout.name} {layout.is_default && ( default )} )); } export function LayoutPresetSelector() { const [saveDialogOpen, setSaveDialogOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); const [tooltipOpen, setTooltipOpen] = useState(false); const recentlyClosedRef = useRef(false); const applyBuiltInPreset = useDockviewStore((s) => s.applyBuiltInPreset); const applyCustomLayout = useDockviewStore((s) => s.applyCustomLayout); const savedLayouts = useAppStore((s) => s.userSettings.savedLayouts); const handleApplyCustom = useCallback( (layout: SavedLayout) => applyCustomLayout({ id: layout.id, name: layout.name, isDefault: layout.is_default, layout: layout.layout, createdAt: layout.created_at, }), [applyCustomLayout], ); const handleDeleteLayout = useCallback( async (layoutId: string) => { await updateUserSettings({ saved_layouts: savedLayouts.filter((l) => l.id !== layoutId) }); }, [savedLayouts], ); const handleDropdownOpenChange = useCallback((open: boolean) => { setDropdownOpen(open); if (!open) { recentlyClosedRef.current = true; setTooltipOpen(false); setTimeout(() => { recentlyClosedRef.current = false; }, 200); } }, []); const handleTooltipOpenChange = useCallback( (open: boolean) => { if (open && (dropdownOpen || recentlyClosedRef.current)) return; setTooltipOpen(open); }, [dropdownOpen], ); return ( <> Layout presets Presets {BUILT_IN_PRESETS.map((preset) => { const Icon = preset.icon; return ( applyBuiltInPreset(preset.id)} className="cursor-pointer" >
{preset.label} {preset.description}
); })}
Saved Layouts setSaveDialogOpen(true)} className="cursor-pointer"> Save current layout...
); }