'use client'; import { useId, useMemo, useState, useSyncExternalStore } from 'react'; import { ChevronDown, ChevronRight } from '@/components/icons'; import { get as getActivePlugin, offersForSlot, subscribe } from '@/lib/plugin-sandbox/registry'; import { PluginIframeSlot } from './plugin-iframe-slot'; const STORAGE_PREFIX = 'plugin-sidebar-section-collapsed:'; function readCollapsed(pluginId: string): boolean { try { return localStorage.getItem(STORAGE_PREFIX + pluginId) === '1'; } catch { return false; } } function writeCollapsed(pluginId: string, collapsed: boolean): void { try { if (collapsed) localStorage.setItem(STORAGE_PREFIX + pluginId, '1'); else localStorage.removeItem(STORAGE_PREFIX + pluginId); } catch { // Storage may be blocked; the section just opens expanded next time. } } function PluginSidebarSection({ pluginId, extraProps }: { pluginId: string; extraProps: Record }) { const [collapsed, setCollapsed] = useState(() => readCollapsed(pluginId)); const bodyId = useId(); const name = getActivePlugin(pluginId)?.plugin.name || pluginId; const toggle = () => { setCollapsed((prev) => { writeCollapsed(pluginId, !prev); return !prev; }); }; return (
{/* Same look as the sidebar's own section headers (Folders, Tags). */} {/* A collapsed section has no iframe at all, so it costs nothing. */}
); } /** * The `sidebar-section` slot: one collapsible section per plugin in the mail * sidebar, below the folders and tags. The header carries the plugin's name; * whether it is folded is remembered per plugin in this browser. */ export function PluginSidebarSections({ mailboxId }: { mailboxId: string | null }) { const getSnapshot = () => offersForSlot('sidebar-section'); const offers = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); const extraProps = useMemo(() => ({ mailboxId }), [mailboxId]); if (offers.length === 0) return null; return (
{offers.map((offer) => ( ))}
); }