"use client"; import { BotIcon, CircleCheckBigIcon, FileTextIcon, FlaskConicalIcon, SquareCheckIcon, SquarePenIcon, TerminalIcon, } from "lucide-react"; import "@/styles/dockview.css"; import { useEffect, useMemo, useState } from "react"; import { DockviewReact, themeAbyssSpaced } from "dockview"; import type { AddPanelOptions, DockviewReadyEvent, DockviewApi } from "dockview"; const iconMap = { FileTextIcon, FlaskConicalIcon, CircleCheckBigIcon, SquarePenIcon, SquareCheckIcon, TerminalIcon, BotIcon, } as const; type IconKey = keyof typeof iconMap; interface DockviewProps { options: (AddPanelOptions & { node: React.ReactNode; icon: IconKey; })[]; storageKey: string; } const DockView = ({ options, storageKey }: DockviewProps) => { const [api, setApi] = useState(); const { components, tabComponents } = useMemo(() => { const comps: Record React.ReactNode> = {}; const tabs: Record React.ReactNode> = {}; options.forEach((option) => { const { id, icon, node, title } = option; comps[id] = () => <>{node}; const Icon = iconMap[icon]; tabs[id] = () => (
); }); return { components: comps, tabComponents: tabs }; }, [options]); useEffect(() => { if (!api) return; const disposable = api.onDidLayoutChange(() => { localStorage.setItem(storageKey, JSON.stringify(api.toJSON())); }); return () => disposable.dispose(); }, [api, storageKey]); const handleReady = (event: DockviewReadyEvent) => { setApi(event.api); const serializedLayout = localStorage.getItem(storageKey); const addDefaultPanels = () => { options.forEach((option) => { event.api.addPanel({ ...option }); }); }; if (serializedLayout) { try { event.api.fromJSON(JSON.parse(serializedLayout)); } catch (error) { console.error("Failed to parse layout:", error); localStorage.removeItem(storageKey); addDefaultPanels(); } } else { addDefaultPanels(); } }; return ( ); }; export default DockView;