"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useEffect, useRef, useState, } from "react";
import { cn } from "@stigmer/theme";
const KEYBOARD_STEP_PX = 20;
/**
 * Read the initial pane width for a given storage key, falling back to
 * `defaultSize` when there is no valid persisted value. Pure of React state so
 * it can seed `useState` and re-seed on a key/pane change.
 */
function readInitialWidth(storageKey, defaultSize, minSize, maxSize) {
    if (storageKey) {
        try {
            const stored = localStorage.getItem(storageKey);
            if (stored) {
                const parsed = Number(stored);
                if (Number.isFinite(parsed) && parsed >= minSize && parsed <= maxSize) {
                    return parsed;
                }
            }
        }
        catch {
            /* localStorage may be unavailable */
        }
    }
    return defaultSize;
}
/**
 * Horizontal split layout with one drag-resizable pixel-sized pane and one
 * flexible pane.
 *
 * By default the right region (`secondary`) is pixel-sized and the left
 * (`primary`) flexes. Set `resizablePane="primary"` to flip which side carries
 * the width — the session layout uses this to invert between a chat-dominant
 * layout (inspector on the right, fixed) and a workspace-dominant layout (chat
 * on the left, fixed). Render order never changes, so flipping re-flows the row
 * without remounting either child (preserving streaming/scroll state).
 *
 * Design:
 * - Pointer-based drag with `setPointerCapture` for reliable tracking
 * - `requestAnimationFrame` coalescing during drag (DD-009 pattern)
 * - Keyboard accessible: arrow keys nudge by {@link KEYBOARD_STEP_PX}, oriented
 *   so the pane always grows toward its own side
 * - Optional `localStorage` persistence via `storageKey`, re-initialized when
 *   the key or resizable side changes (no remount required)
 * - Optional responsive collapse of the pixel pane below `lg`
 * - Styled with `--stgm-*` tokens; no hardcoded colors
 *
 * @example
 * ```tsx
 * // Chat-dominant: inspector fixed on the right (default)
 * <ResizableSplit
 *   primary={<Conversation />}
 *   secondary={<Inspector />}
 *   storageKey="stgm-session-inspector-width"
 *   responsiveCollapse="secondary"
 * />
 *
 * // Workspace-dominant: chat fixed on the left
 * <ResizableSplit
 *   resizablePane="primary"
 *   primary={<Conversation />}
 *   secondary={<WorkspaceSurface />}
 *   storageKey="stgm-session-chat-width"
 *   responsiveCollapse="primary"
 * />
 * ```
 */
export function ResizableSplit({ primary, secondary, resizablePane = "secondary", defaultSize = 384, minSize = 280, maxSize = 800, storageKey, responsiveCollapse = "none", collapsedPane = "none", ariaLabel = "Resize panel", onResize, className, }) {
    const isPrimaryResizable = resizablePane === "primary";
    const isCollapsed = collapsedPane !== "none";
    const [panelWidth, setPanelWidth] = useState(() => readInitialWidth(storageKey, defaultSize, minSize, maxSize));
    // Re-initialize the width when the storage key or resizable side changes —
    // the session layout swaps both together on a mode flip and expects the width
    // to come from the new key, not carry over. Adjust-state-during-render
    // (matching `useSessionPanel`) so it lands before paint without a remount.
    const [prevStorageKey, setPrevStorageKey] = useState(storageKey);
    const [prevResizablePane, setPrevResizablePane] = useState(resizablePane);
    if (storageKey !== prevStorageKey || resizablePane !== prevResizablePane) {
        setPrevStorageKey(storageKey);
        setPrevResizablePane(resizablePane);
        setPanelWidth(readInitialWidth(storageKey, defaultSize, minSize, maxSize));
    }
    const containerRef = useRef(null);
    const rafRef = useRef(0);
    const isDraggingRef = useRef(false);
    // Notify consumer of the current width whenever it changes (incl. on re-init).
    const onResizeRef = useRef(onResize);
    onResizeRef.current = onResize;
    useEffect(() => {
        onResizeRef.current?.(panelWidth);
    }, [panelWidth]);
    const clampWidth = useCallback((raw) => Math.round(Math.max(minSize, Math.min(maxSize, raw))), [minSize, maxSize]);
    const persist = useCallback((width) => {
        if (!storageKey)
            return;
        try {
            localStorage.setItem(storageKey, String(width));
        }
        catch {
            /* quota or security error */
        }
    }, [storageKey]);
    const handlePointerDown = useCallback((e) => {
        e.preventDefault();
        e.currentTarget.setPointerCapture(e.pointerId);
        isDraggingRef.current = true;
    }, []);
    const handlePointerMove = useCallback((e) => {
        if (!isDraggingRef.current || !containerRef.current)
            return;
        const clientX = e.clientX;
        cancelAnimationFrame(rafRef.current);
        rafRef.current = requestAnimationFrame(() => {
            const container = containerRef.current;
            if (!container)
                return;
            const rect = container.getBoundingClientRect();
            // The resizable pane's width is the distance from its own edge to the
            // pointer: left edge for a primary pane, right edge for a secondary one.
            const raw = isPrimaryResizable
                ? clientX - rect.left
                : rect.right - clientX;
            setPanelWidth(clampWidth(raw));
        });
    }, [clampWidth, isPrimaryResizable]);
    const handlePointerUp = useCallback((e) => {
        if (!isDraggingRef.current)
            return;
        isDraggingRef.current = false;
        e.currentTarget.releasePointerCapture(e.pointerId);
        cancelAnimationFrame(rafRef.current);
        persist(panelWidth);
    }, [persist, panelWidth]);
    const handleKeyDown = useCallback((e) => {
        // Arrow keys grow the pane toward its own side: a primary (left) pane
        // grows on ArrowRight, a secondary (right) pane grows on ArrowLeft.
        let delta = 0;
        if (e.key === "ArrowLeft")
            delta = isPrimaryResizable ? -KEYBOARD_STEP_PX : KEYBOARD_STEP_PX;
        else if (e.key === "ArrowRight")
            delta = isPrimaryResizable ? KEYBOARD_STEP_PX : -KEYBOARD_STEP_PX;
        if (delta === 0)
            return;
        e.preventDefault();
        setPanelWidth((prev) => {
            const next = clampWidth(prev + delta);
            persist(next);
            return next;
        });
    }, [clampWidth, persist, isPrimaryResizable]);
    // Cleanup rAF on unmount
    useEffect(() => () => cancelAnimationFrame(rafRef.current), []);
    const fixedPaneClass = "shrink-0 overflow-hidden";
    const flexPaneClass = "min-w-0 flex-1";
    // A collapsed pane hides entirely and its sibling flexes to fill the row,
    // overriding the fixed/flex split. Width state is untouched, so expanding
    // restores the previous size.
    const primaryPaneClass = collapsedPane === "primary"
        ? "hidden"
        : collapsedPane === "secondary" || !isPrimaryResizable
            ? flexPaneClass
            : fixedPaneClass;
    const secondaryPaneClass = collapsedPane === "secondary"
        ? "hidden"
        : collapsedPane === "primary" || isPrimaryResizable
            ? flexPaneClass
            : fixedPaneClass;
    return (_jsxs("div", { ref: containerRef, className: cn("flex min-h-0 flex-1", className), children: [_jsx("div", { className: cn(primaryPaneClass, responsiveCollapse === "primary" && "max-lg:hidden"), style: isPrimaryResizable && !isCollapsed ? { width: panelWidth } : undefined, children: primary }), _jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-valuenow": panelWidth, "aria-valuemin": minSize, "aria-valuemax": maxSize, "aria-label": ariaLabel, "aria-hidden": isCollapsed || undefined, tabIndex: isCollapsed ? -1 : 0, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onKeyDown: handleKeyDown, className: cn("group relative z-10 w-1 shrink-0 cursor-col-resize touch-none select-none", "bg-[var(--stgm-border,#e5e5e5)]", "hover:bg-[var(--stgm-primary,#6366f1)]", "focus-visible:bg-[var(--stgm-primary,#6366f1)] focus-visible:outline-none", "active:bg-[var(--stgm-primary,#6366f1)]", "transition-colors duration-100", responsiveCollapse !== "none" && "max-lg:hidden", isCollapsed && "hidden"), children: _jsx("div", { className: "absolute inset-y-0 -left-1.5 -right-1.5" }) }), _jsx("div", { className: cn(secondaryPaneClass, responsiveCollapse === "secondary" && "max-lg:hidden"), style: !isPrimaryResizable && !isCollapsed ? { width: panelWidth } : undefined, children: secondary })] }));
}
//# sourceMappingURL=ResizableSplit.js.map