@dashfoo/react 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Panel"],"sources":["../src/hooks/persistence.ts","../src/hooks/store.ts","../src/hooks/drag-hooks.tsx","../src/hooks/layout-store.ts","../src/lib/tab-insertion.ts","../src/lib/warn-once.ts","../src/components/drag-overlays.tsx","../src/components/drag-adapter.tsx","../src/components/layout-root.tsx","../src/lib/tab-ids.ts","../src/components/tabset/tabset-store.ts","../src/components/tabset/tabset-content.tsx","../src/components/tab-overflow.tsx","../src/components/tabset/tabset-overflow.tsx","../src/hooks/use-tab-overflow.ts","../src/lib/tab-selection.ts","../src/components/tabset/tabset-root.tsx","../src/lib/merge-refs.ts","../src/components/tabset/tabset-tab.tsx","../src/components/tabset-icons.tsx","../src/components/tabset/tabset-tab-controls.tsx","../src/components/tabset/tabset-tablist.tsx","../src/components/tabset/tabset-toolbar.tsx","../src/components/tabset/tabset-view.tsx","../src/components/row-view.tsx","../src/components/layout.tsx","../src/components/dashfoo-layout.tsx","../src/components/drag-root.tsx","../src/components/panel.tsx","../src/components/tabset/tabset.ts","../src/hooks/responsive.ts"],"sourcesContent":["\"use client\";\n\nimport type { Dashfoo } from \"@dashfoo/core\";\nimport { fromJSON, toJSON } from \"@dashfoo/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n// A minimal localStorage-shaped backend so layouts can persist to anything: the\n// browser, sessionStorage, an in-memory map, or a custom store.\ntype StorageAdapter = {\n getItem: (key: string) => string | null;\n removeItem: (key: string) => void;\n setItem: (key: string, value: string) => void;\n};\n\nconst memoryStorageAdapter = (): StorageAdapter => {\n const store = new Map<string, string>();\n return {\n getItem: (key) => store.get(key) ?? null,\n removeItem: (key) => {\n store.delete(key);\n },\n setItem: (key, value) => {\n store.set(key, value);\n },\n };\n};\n\n// SSR-safe browser adapter: reads return null and writes are swallowed (with a\n// warning) when storage is unavailable or throws (no window, private mode, quota).\nconst localStorageAdapter: StorageAdapter = {\n getItem: (key) => {\n if (typeof window === \"undefined\") {\n return null;\n }\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n removeItem: (key) => {\n if (typeof window === \"undefined\") {\n return;\n }\n try {\n window.localStorage.removeItem(key);\n } catch {\n // storage unavailable — nothing to remove.\n }\n },\n setItem: (key, value) => {\n if (typeof window === \"undefined\") {\n return;\n }\n try {\n window.localStorage.setItem(key, value);\n } catch (error) {\n // A persistence failure (quota, private mode) must not be silent.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] failed to persist layout\", error);\n }\n },\n};\n\n// A resolved persistence target: where to save and how often. DashfooLayout's\n// `persist` prop normalizes its input to this.\ntype PersistConfig = {\n debounceMs: number;\n key: string;\n storage: StorageAdapter;\n};\n\ntype Persistence = {\n clear: () => void;\n // undefined only in controlled mode, where no defaultModel seeds the load.\n initialModel: Dashfoo | undefined;\n save: (model: Dashfoo) => void;\n};\n\n// The load/save half of persistence, behind the DashfooLayout `persist` prop.\n// Loads the saved model once (validated via serialize.ts, falling back to\n// `defaultModel` on miss or corruption) and\n// debounce-saves every change. Resetting the live model is the caller's job —\n// this primitive only owns the storage side. A null config makes it a no-op, so\n// callers can run the hook unconditionally.\nconst usePersistence = (\n config: PersistConfig | null,\n defaultModel: Dashfoo | undefined,\n): Persistence => {\n // Refs keep the callbacks stable while always seeing the latest config; synced\n // in an effect (never written during render).\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n });\n\n // Load the saved model once. The initializer stays pure (no writes during\n // render); a corrupt stored value is pruned in the mount effect below.\n const [initialModel] = useState<Dashfoo | undefined>(() => {\n if (config === null || defaultModel === undefined) {\n return defaultModel;\n }\n const raw = config.storage.getItem(config.key);\n if (raw === null) {\n return defaultModel;\n }\n try {\n return fromJSON(raw);\n } catch {\n return defaultModel;\n }\n });\n\n useEffect(() => {\n const current = configRef.current;\n if (current === null) {\n return;\n }\n const raw = current.storage.getItem(current.key);\n if (raw === null) {\n return;\n }\n try {\n fromJSON(raw);\n } catch (error) {\n // Mirror the setItem warn: a discarded corrupt layout must not be silent.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] discarding unreadable persisted layout\", error);\n current.storage.removeItem(current.key);\n }\n }, []);\n\n // We load + prune once, but save/flush/clear target the *current* key. Changing\n // config.key for a mounted layout would save the stale model under the new key\n // and clobber it; warn instead of doing that silently. Remounting is the fix.\n const loadedKey = useRef(config?.key);\n useEffect(() => {\n if (config !== null && config.key !== loadedKey.current) {\n // oxlint-disable-next-line no-console\n console.warn(\n \"[dashfoo] persist key changed for a mounted layout; the displayed layout still reflects the previous key — remount (key={persistKey}) to load the new key\",\n );\n loadedKey.current = config.key;\n }\n }, [config]);\n\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The pending write carries the key and storage it was produced for, so a\n // config change while a save is queued cannot write the old value into the\n // new target.\n const pending = useRef<{ key: string; storage: StorageAdapter; value: string } | null>(null);\n\n const flush = useCallback((): void => {\n if (timer.current !== null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n if (pending.current !== null) {\n pending.current.storage.setItem(pending.current.key, pending.current.value);\n pending.current = null;\n }\n }, []);\n\n const save = useCallback(\n (next: Dashfoo): void => {\n const current = configRef.current;\n if (current === null) {\n return;\n }\n pending.current = { key: current.key, storage: current.storage, value: toJSON(next) };\n if (timer.current !== null) {\n clearTimeout(timer.current);\n }\n timer.current = setTimeout(flush, current.debounceMs);\n },\n [flush],\n );\n\n // Flush a pending save on unmount so the last change is never lost — and on\n // page teardown: a reload or navigation never unmounts React, so the debounced\n // timer dies with the page. pagehide is the teardown signal (bfcache-safe);\n // visibilitychange→hidden covers backgrounded tabs killed without a pagehide\n // (mobile). flush() is idempotent, so overlapping signals are harmless.\n useEffect(() => {\n const handlePageHide = (): void => {\n flush();\n };\n const handleVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") {\n flush();\n }\n };\n window.addEventListener(\"pagehide\", handlePageHide);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () => {\n window.removeEventListener(\"pagehide\", handlePageHide);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n flush();\n };\n }, [flush]);\n\n const clear = useCallback((): void => {\n if (timer.current !== null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n pending.current = null;\n const current = configRef.current;\n if (current !== null) {\n current.storage.removeItem(current.key);\n }\n }, []);\n\n return { clear, initialModel, save };\n};\n\nexport { localStorageAdapter, memoryStorageAdapter, usePersistence };\nexport type { Persistence, PersistConfig, StorageAdapter };\n","\"use client\";\n\nimport type { Action, Dashfoo } from \"@dashfoo/core\";\nimport { canRedo, canUndo, dashfooMachine, normalize, reducer } from \"@dashfoo/core\";\nimport { useActorRef, useSelector } from \"@xstate/react\";\nimport { useCallback, useEffect } from \"react\";\n\ntype DashfooStore = {\n canRedo: () => boolean;\n canUndo: () => boolean;\n dispatch: (action: Action) => void;\n model: Dashfoo;\n redo: () => void;\n setModel: (model: Dashfoo) => void;\n undo: () => void;\n};\n\ntype UseDashfooStoreOptions = {\n defaultModel?: Dashfoo;\n model?: Dashfoo;\n onAction?: (action: Action) => Action | null;\n onActiveTabsetChange?: (tabsetId: string | undefined) => void;\n onMaximizedTabsetChange?: (tabsetId: string | undefined) => void;\n onModelChange?: (model: Dashfoo, action?: Action) => void;\n};\n\n// Binds a dashfooMachine actor to React. Uncontrolled (defaultModel) lets the\n// actor own the document with full undo/redo; controlled (model) makes the prop\n// the source of truth and routes every change through onModelChange. The\n// actor/inspector stays in sync once the host round-trips `after` back into the\n// `model` prop (it is not synced synchronously within dispatch).\nconst useDashfooStore = (options: UseDashfooStoreOptions): DashfooStore => {\n const {\n defaultModel,\n model: controlledModel,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange,\n } = options;\n const initialModel = controlledModel ?? defaultModel;\n if (initialModel === undefined) {\n throw new Error(\"useDashfooStore requires either a `model` or a `defaultModel`.\");\n }\n\n // Normalize at the boundary so a host-supplied model satisfies the same\n // invariants (clamped selection, no empty tabsets, live maximizedTabsetId) the\n // reducer guarantees — every entry point holds a canonical model.\n const actorRef = useActorRef(dashfooMachine, { input: { model: normalize(initialModel) } });\n const history = useSelector(actorRef, (snapshot) => snapshot.context.history);\n\n useEffect(() => {\n if (controlledModel !== undefined) {\n actorRef.send({ model: normalize(controlledModel), type: \"SET_MODEL\" });\n }\n }, [actorRef, controlledModel]);\n\n const model = controlledModel ?? history.present;\n\n // Single notification path so dispatch and undo/redo stay in lockstep. The\n // Object.is guard makes a no-op transition (e.g. undo at the bottom of the\n // stack, where the history helper returns the same object) emit nothing, so we\n // never write spurious persistence on a change that did not happen.\n const notify = useCallback(\n (before: Dashfoo, after: Dashfoo, action?: Action) => {\n if (Object.is(before, after)) {\n return;\n }\n onModelChange?.(after, action);\n if (after.activeTabsetId !== before.activeTabsetId) {\n onActiveTabsetChange?.(after.activeTabsetId);\n }\n if (after.maximizedTabsetId !== before.maximizedTabsetId) {\n onMaximizedTabsetChange?.(after.maximizedTabsetId);\n }\n },\n [onModelChange, onActiveTabsetChange, onMaximizedTabsetChange],\n );\n\n const dispatch = useCallback(\n (action: Action) => {\n // onAction may veto (null) or replace the action before it mutates anything.\n const resolved = onAction ? onAction(action) : action;\n if (!resolved) {\n return;\n }\n // Uncontrolled: the actor owns history. Controlled: derive the next model by\n // hand (the host round-trips it through the model prop). normalize the\n // controlled `before` too, else a normalization-only id change fires spuriously.\n const transition = ((): { after: Dashfoo; before: Dashfoo } => {\n if (controlledModel === undefined) {\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ action: resolved, type: \"DISPATCH\" });\n return { after: actorRef.getSnapshot().context.history.present, before };\n }\n return { after: reducer(controlledModel, resolved), before: normalize(controlledModel) };\n })();\n notify(transition.before, transition.after, resolved);\n },\n [actorRef, controlledModel, notify, onAction],\n );\n\n const undo = useCallback(() => {\n // actor history is always empty in controlled mode, so undo would be a dead\n // send that still emitted a spurious onModelChange.\n if (controlledModel !== undefined) {\n return;\n }\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ type: \"UNDO\" });\n const after = actorRef.getSnapshot().context.history.present;\n notify(before, after);\n }, [actorRef, controlledModel, notify]);\n\n const redo = useCallback(() => {\n if (controlledModel !== undefined) {\n return;\n }\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ type: \"REDO\" });\n const after = actorRef.getSnapshot().context.history.present;\n notify(before, after);\n }, [actorRef, controlledModel, notify]);\n\n // Replace the whole document, resetting undo history. Drives an uncontrolled\n // reset (e.g. clearing a persisted layout back to its default) without a\n // remount; normalized at the boundary like every other entry point.\n const setModel = useCallback(\n (next: Dashfoo) => {\n actorRef.send({ model: normalize(next), type: \"SET_MODEL\" });\n },\n [actorRef],\n );\n\n // Functions, not booleans: they read the live actor snapshot so a caller\n // reading them right after undo/redo (e.g. inside onModelChange) sees the fresh\n // value, not the one-render-stale useSelector result.\n return {\n canRedo: () => canRedo(actorRef.getSnapshot().context.history),\n canUndo: () => canUndo(actorRef.getSnapshot().context.history),\n dispatch,\n model,\n redo,\n setModel,\n undo,\n };\n};\n\nexport { useDashfooStore };\nexport type { DashfooStore, UseDashfooStoreOptions };\n","\"use client\";\n\nimport type { DragSubject, TabNode } from \"@dashfoo/core\";\nimport { Accessibility, Draggable, DragDropManager, Feedback, PointerSensor } from \"@dnd-kit/dom\";\nimport type { Context } from \"react\";\nimport { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\n// Shared drag contexts + the draggable/droppable hooks. Lives apart from\n// drag-adapter.tsx so the contexts can be owned here without a circular import:\n// drag-adapter imports these for DragProvider and re-exports the hooks, and this\n// module never reaches back into drag-adapter.\n\nconst INTERACTIVE_SELECTOR = `\n input:not([disabled]),\n select:not([disabled]),\n textarea:not([disabled]),\n button:not([disabled]),\n a[href],\n [contenteditable]:not([contenteditable=\"false\"])\n`;\n\n// The sensor's default preventActivation vetoes any pointerdown whose target is\n// an element child of the draggable: closest() walks up to the trigger button\n// itself and calls it \"interactive\". Plain-text labels dodge that (text nodes\n// are not elements), but custom labels render elements inside the trigger and\n// would silently lose dragging. Allow the draggable itself as the handle while\n// still refusing genuinely interactive children nested in a label.\nconst preventActivation = (event: PointerEvent, source: Draggable): boolean => {\n const { target } = event;\n if (!(target instanceof Element)) {\n return false;\n }\n const interactive = target.closest(INTERACTIVE_SELECTOR);\n return interactive !== null && interactive !== source.element;\n};\n\n// Plugins drop the screen-reader announcer and the visual Feedback plugin (the\n// adapter renders its own preview). Sensors keep only a configured\n// PointerSensor — the KeyboardSensor's nudge model double-binds the arrow keys\n// the tab strip already uses for roving-tabindex navigation, so keyboard\n// docking needs its own interaction design rather than that sensor.\nconst createDragManager = (): DragDropManager =>\n new DragDropManager({\n plugins: (defaults) =>\n defaults.filter((plugin) => plugin !== Accessibility && plugin !== Feedback),\n sensors: () => [PointerSensor.configure({ preventActivation })],\n });\n\ntype DragContextValue = {\n manager: DragDropManager;\n registerTabset: (id: string, element: HTMLElement | null) => void;\n};\n\nconst DragContext = createContext<DragContextValue | null>(null);\n\n// A manager owned by DashfooDragProvider, shared between a layout and external\n// tab sources outside it so drags can cross that boundary. The annotation keeps\n// declaration emit from expanding @dnd-kit's non-exported generic defaults.\nconst SharedDragManagerContext: Context<DragDropManager | null> =\n createContext<DragDropManager | null>(null);\n\n// The live drag subject rides a scoped zustand store (created per DragLayer, fed\n// by the dragDockMachine subscription) instead of a context value, so a drag\n// start/end re-renders only the parts that select the subject — never the\n// provider subtree.\ntype DragSubjectState = { subject: DragSubject | null };\n\ntype DragSubjectStore = StoreApi<DragSubjectState>;\n\nconst createDragSubjectStore = (): DragSubjectStore =>\n createStore<DragSubjectState>(() => ({ subject: null }));\n\n// Stable empty store so useDragSubject keeps an unconditional hook order (and\n// returns null) outside a DragLayer — parts must keep working drag-free.\nconst nullSubjectStore = createDragSubjectStore();\n\nconst DragSubjectStoreContext: Context<DragSubjectStore | null> =\n createContext<DragSubjectStore | null>(null);\n\n// Tracks a Draggable's element across renders without rebuilding it. Returned by\n// the draggable hooks; their effects own the Draggable's lifecycle.\ntype DraggableHandle = {\n draggableRef: { current: Draggable | null };\n elementRef: { current: Element | null };\n ref: (element: Element | null) => void;\n};\n\nconst useDraggableHandle = (): DraggableHandle => {\n const elementRef = useRef<Element | null>(null);\n const draggableRef = useRef<Draggable | null>(null);\n const ref = useCallback((element: Element | null): void => {\n elementRef.current = element;\n if (draggableRef.current) {\n draggableRef.current.element = element ?? undefined;\n }\n }, []);\n return { draggableRef, elementRef, ref };\n};\n\n// What a draggable carries: the dnd-kit id and the stable data (drag kind +\n// tabset id), both derived from identity. The chip label is passed separately so\n// it never sits in the construction effect's deps. data must be referentially\n// stable (the wrappers memoize it) so the construction effect tracks it without\n// rebuilding on every render.\ntype DraggableDescriptor = { data: Record<string, unknown>; id: string };\n\n// Owns the full Draggable lifecycle shared by the tab and tabset hooks. The\n// construction effect is keyed on identity only (manager, id, disabled, the\n// stable data) — never on the chip label — so a tab click or rename can't tear\n// down and rebuild the live instance. The label is folded into the live data by\n// a separate effect, since data is only read at drag start; both effects fire in\n// the same mount commit, so the label is current well before any pointer drag.\n// `Draggable.data` is a public accessor in @dnd-kit/abstract, so the in-place\n// update is type-safe.\nconst useDraggableEntity = (\n descriptor: DraggableDescriptor,\n disabled: boolean,\n label: string,\n): { ref: (element: Element | null) => void } => {\n const context = useContext(DragContext);\n // Internal draggables (tabs, grips) live under DragProvider; external tab\n // sources sit outside it and reach the manager via DashfooDragProvider.\n const sharedManager = useContext(SharedDragManagerContext);\n const manager = context?.manager ?? sharedManager;\n const { draggableRef, elementRef, ref } = useDraggableHandle();\n const { data, id } = descriptor;\n\n useEffect(() => {\n if (!manager || disabled) {\n return undefined;\n }\n const draggable = new Draggable({ data: { ...data, label: \"\" }, id }, manager);\n draggable.element = elementRef.current ?? undefined;\n draggableRef.current = draggable;\n return () => {\n draggable.destroy();\n draggableRef.current = null;\n };\n }, [data, disabled, draggableRef, elementRef, id, manager]);\n\n useEffect(() => {\n const draggable = draggableRef.current;\n if (draggable) {\n draggable.data = { ...data, label };\n }\n }, [data, draggableRef, label]);\n\n return { ref };\n};\n\nconst useTabDraggable = (\n tabId: string,\n disabled = false,\n label = \"\",\n): { ref: (element: Element | null) => void } => {\n const data = useMemo(() => ({ type: \"tab\" }), []);\n return useDraggableEntity({ data, id: tabId }, disabled, label);\n};\n\n// The whole tabset is draggable from its grip. A distinct dnd-kit id (grip-*)\n// avoids colliding with the tabset's own registered id; the real tabset id rides\n// in `data` and becomes the moveTabset subject. The label feeds the overlay chip.\nconst useTabsetDraggable = (\n tabsetId: string,\n disabled = false,\n label = \"\",\n): { ref: (element: Element | null) => void } => {\n const data = useMemo(() => ({ tabsetId, type: \"tabset\" }), [tabsetId]);\n return useDraggableEntity({ data, id: `grip-${tabsetId}` }, disabled, label);\n};\n\n// Registers the tabset element so the adapter can hit-test the pointer against it.\nconst useTabsetDroppable = (tabsetId: string): { ref: (element: HTMLElement | null) => void } => {\n const context = useContext(DragContext);\n const ref = useCallback(\n (element: HTMLElement | null): void => {\n context?.registerTabset(tabsetId, element);\n },\n [context, tabsetId],\n );\n return { ref };\n};\n\ntype ExternalTabSourceOptions = {\n // Must return a fresh TabNode (unique id) per call — each drop inserts a new\n // tab, and duplicate ids would fail the model's invariants.\n createTab: () => TabNode;\n disabled?: boolean;\n label?: string;\n};\n\n// Makes any element outside a layout a drag source that inserts a new tab when\n// dropped on a DashfooLayout under the same DashfooDragProvider. The adapter\n// recognizes the { type: \"external\" } payload and commits an addNode instead of\n// a move. The ref-indirection keeps `data` referentially stable (the\n// construction effect tracks it) while drags always call the latest createTab.\nconst useExternalTabSource = ({\n createTab,\n disabled = false,\n label = \"\",\n}: ExternalTabSourceOptions): { ref: (element: Element | null) => void } => {\n const id = useId();\n const createTabRef = useRef(createTab);\n // Dependency-less on purpose: a plain ref sync so drags always call the latest createTab.\n useEffect(() => {\n createTabRef.current = createTab;\n });\n const data = useMemo(\n () => ({ createTab: (): TabNode => createTabRef.current(), type: \"external\" }),\n [],\n );\n return useDraggableEntity({ data, id: `external-${id}` }, disabled, label);\n};\n\nconst useDragSubject = (): DragSubject | null => {\n const store = useContext(DragSubjectStoreContext);\n return useStore(store ?? nullSubjectStore, (state) => state.subject);\n};\n\nexport type { DragContextValue, DragSubjectStore, ExternalTabSourceOptions };\nexport {\n createDragManager,\n createDragSubjectStore,\n DragContext,\n DragSubjectStoreContext,\n SharedDragManagerContext,\n useDragSubject,\n useExternalTabSource,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n};\n","\"use client\";\n\nimport type { Action, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport type { ReactNode } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\n// The layout-wide configuration every part below <Layout.Root> coordinates\n// through: resolved chrome flags, slot renderers, and the model dispatcher.\n// A scoped zustand store rather than a plain context value so parts subscribe\n// per-field — changing one flag re-renders only the parts that select it.\ntype LayoutState = {\n closableTabs: boolean;\n dispatch: (action: Action) => void;\n draggableTabsets: boolean;\n keepMounted: boolean;\n maximizable: boolean;\n maximizedTabsetId: string | undefined;\n renamableTabs: boolean;\n renderTab: (tab: TabNode) => ReactNode;\n renderTabLabel: ((tab: TabNode) => ReactNode) | undefined;\n renderTabsetToolbar: ((tabset: TabsetNode) => ReactNode) | undefined;\n splitDock: boolean;\n tabLocation: \"bottom\" | \"top\";\n tabsetMinSize: number;\n tabStripEnabled: boolean;\n};\n\ntype LayoutStore = StoreApi<LayoutState>;\n\nconst createLayoutStore = (initial: LayoutState): LayoutStore =>\n createStore<LayoutState>(() => initial);\n\n// Carries only the store instance — state always flows through selectors.\nconst LayoutStoreContext = createContext<LayoutStore | null>(null);\n\nconst useLayout = <T>(selector: (state: LayoutState) => T): T => {\n const store = useContext(LayoutStoreContext);\n if (store === null) {\n throw new Error(\"dashfoo components must be rendered inside <DashfooLayout> or <Layout.Root>.\");\n }\n return useStore(store, selector);\n};\n\nexport { createLayoutStore, LayoutStoreContext, useLayout };\nexport type { LayoutState, LayoutStore };\n","import type { Point, Rect } from \"@dashfoo/core\";\n\n// Pure drag math for the tab strip — rects in, geometry out. The drag adapter\n// gathers the rects from the DOM and feeds them here, so this stays unit-testable\n// without a live drag.\n\ntype Zone = { height: number; width: number; x: number; y: number };\n\n// Is the point inside the rect? (Rect is DOMRect-compatible: x/y/width/height.)\nconst pointInRect = (point: Point, rect: Rect): boolean =>\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height;\n\n// Which slot in the strip the pointer is over: the first tab whose midpoint is\n// right of the pointer, else the end.\nconst insertionIndex = (rects: ReadonlyArray<Rect>, pointerX: number): number => {\n const found = rects.findIndex((rect) => pointerX < rect.x + rect.width / 2);\n return found === -1 ? rects.length : found;\n};\n\n// A tab-sized ghost marking where the dragged tab will land, mirroring the dock\n// pane preview. Measured against whole tab-item rects (label + close), so the\n// \"after the last tab\" position sits past the last close button rather than\n// between the label and the close. The neighbors also anchor the ghost's y and\n// height, so it sits exactly where a real tab does (tabs don't fill the strip);\n// an empty strip falls back to the dragged tab's own height, bottom-aligned the\n// way tabs rest on the strip's baseline. Clamped so the ghost never leaves the\n// strip.\nconst insertionGhostRect = (\n stripRect: Rect,\n itemRects: ReadonlyArray<Rect>,\n index: number,\n tabSize: { height: number; width: number },\n): Zone => {\n const at = itemRects[index];\n const last = itemRects.at(-1);\n const anchor = at ?? last;\n const x = at?.x ?? (last ? last.x + last.width : stripRect.x);\n const width = Math.min(tabSize.width, stripRect.width);\n const height = anchor?.height ?? Math.min(tabSize.height, stripRect.height);\n return {\n height,\n width,\n x: Math.max(stripRect.x, Math.min(x, stripRect.x + stripRect.width - width)),\n y: anchor?.y ?? stripRect.y + stripRect.height - height,\n };\n};\n\n// Whether a drop should commit, or is a no-op to be suppressed: dragging a tabset\n// onto itself (grip id is `grip-${tabsetId}`), or the sole tab of a tabset back\n// onto that same tabset — both change nothing once empties collapse.\nconst shouldAllowDrop = (\n draggedId: string | undefined,\n targetId: string,\n tabIds: ReadonlyArray<string>,\n): boolean => {\n if (draggedId === `grip-${targetId}`) {\n return false;\n }\n if (tabIds.length === 1 && tabIds[0] === draggedId) {\n return false;\n }\n return true;\n};\n\nexport { insertionGhostRect, insertionIndex, pointInRect, shouldAllowDrop };\nexport type { Zone };\n","// Dev warnings for soft misuse (duplicate parts, orphan tabs, missing editors)\n// fire once per key so a render loop can't flood the console, mirroring the\n// unregistered-component warning in dashfoo-layout.\nconst warned = new Set<string>();\n\nconst warnOnce = (key: string, message: string): void => {\n if (warned.has(key)) {\n return;\n }\n warned.add(key);\n // oxlint-disable-next-line no-console\n console.warn(`[dashfoo] ${message}`);\n};\n\nexport { warnOnce };\n","\"use client\";\n\nimport type { dragDockMachine } from \"@dashfoo/core\";\nimport { zoneRect } from \"@dashfoo/core\";\nimport { useSelector } from \"@xstate/react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport type { ActorRefFrom } from \"xstate\";\n\nimport type { Zone } from \"../lib/tab-insertion\";\nimport { insertionGhostRect } from \"../lib/tab-insertion\";\n\n// The drag adapter's two overlays: the dock indicator (where the drop will\n// land) and the pointer-anchored preview chip. Split from drag-adapter.tsx to\n// keep that module focused on wiring @dnd-kit to the drag machine.\n\ntype DragActor = ActorRefFrom<typeof dragDockMachine>;\n\nconst overlayBase: CSSProperties = {\n boxSizing: \"border-box\",\n pointerEvents: \"none\",\n position: \"fixed\",\n // Overridable so a theme can drop it under prefers-reduced-motion.\n transition: \"var(--dashfoo-dock-transition, left 60ms, top 60ms, width 60ms, height 60ms)\",\n zIndex: 9999,\n};\n\n// Only position/size are fixed inline; every visual property is an overridable\n// CSS var with a neutral fallback, so a consumer's data-dashfoo=\"dock-indicator\"\n// theme fully owns the look.\nconst paneStyle = (zone: Zone): CSSProperties => ({\n ...overlayBase,\n background: \"var(--dashfoo-dock-fill, oklch(0.556 0 0 / 0.18))\",\n border:\n \"var(--dashfoo-dock-border-width, 1px) solid var(--dashfoo-dock-border, oklch(0.708 0 0 / 0.75))\",\n borderRadius: \"var(--dashfoo-dock-radius, 6px)\",\n height: zone.height,\n left: zone.x,\n top: zone.y,\n width: zone.width,\n});\n\n// The insertion ghost shares the pane's fill and border but carries the tab's\n// corner shape, so it reads as the tab it previews (the shipped theme rounds\n// only the top corners, like its tabs).\nconst ghostStyle = (zone: Zone): CSSProperties => ({\n ...paneStyle(zone),\n borderRadius: \"var(--dashfoo-dock-tab-radius, var(--dashfoo-dock-radius, 6px))\",\n});\n\n// Whole-tab-item rects (label + close button), excluding the dragged tab. The\n// insertion ghost lands on these boundaries so the \"after the last tab\" position\n// sits past the close button, not between the label and the close.\nconst tabItemRects = (strip: Element, excludeId?: string): Array<DOMRect> =>\n [...strip.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab-item\"]')].flatMap((item) =>\n item.querySelector<HTMLElement>('[data-dashfoo=\"tab\"]')?.dataset.tabId === excludeId\n ? []\n : [item.getBoundingClientRect()],\n );\n\n// When the dragged source's own size is unknown (e.g. it vanished mid-drag),\n// fall back to the target strip's average tab size, else a plausible default.\nconst FALLBACK_GHOST_SIZE = { height: 32, width: 96 };\n\ntype GhostSize = { height: number; width: number };\n\nconst ghostSize = (\n sourceSize: GhostSize | undefined,\n itemRects: ReadonlyArray<DOMRect>,\n): GhostSize => {\n if (sourceSize) {\n return sourceSize;\n }\n if (itemRects.length > 0) {\n return {\n height: itemRects.reduce((sum, rect) => sum + rect.height, 0) / itemRects.length,\n width: itemRects.reduce((sum, rect) => sum + rect.width, 0) / itemRects.length,\n };\n }\n return FALLBACK_GHOST_SIZE;\n};\n\n// A \"where it will land\" indicator driven off the machine's live intent: a\n// tab-sized ghost in the tab bar for a stack, the matching content half for a\n// split. One unkeyed div, so React reuses the DOM node and the position/size\n// transition morphs between the two shapes instead of remounting.\nconst DockIndicator = ({\n actorRef,\n getSourceSize,\n getTabsetElement,\n}: {\n actorRef: DragActor;\n getSourceSize: () => GhostSize | undefined;\n getTabsetElement: (id: string) => HTMLElement | undefined;\n}): ReactNode => {\n const intent = useSelector(actorRef, (snapshot) => snapshot.context.intent);\n const draggedId = useSelector(actorRef, (snapshot) => snapshot.context.subject?.id);\n if (!intent) {\n return null;\n }\n const element = getTabsetElement(intent.targetId);\n if (!element) {\n return null;\n }\n if (intent.location === \"center\") {\n const strip = element.querySelector('[data-dashfoo=\"tabstrip\"]');\n if (strip) {\n const items = tabItemRects(strip, draggedId);\n const ghost = insertionGhostRect(\n strip.getBoundingClientRect(),\n items,\n intent.index ?? 0,\n ghostSize(getSourceSize(), items),\n );\n return <div data-dashfoo=\"dock-indicator\" style={ghostStyle(ghost)} />;\n }\n }\n const zone = zoneRect(element.getBoundingClientRect(), intent.location);\n return <div data-dashfoo=\"dock-indicator\" style={paneStyle(zone)} />;\n};\n\nconst previewStyle: CSSProperties = { left: 0, position: \"fixed\", top: 0, zIndex: 9999 };\n\ntype DragPreviewState = { label: string; x: number; y: number };\n\n// The pointer-anchored chip that follows the cursor while dragging.\nconst DragPreview = ({\n overlayRef,\n preview,\n}: {\n overlayRef: (element: HTMLDivElement | null) => void;\n preview: DragPreviewState | null;\n}): ReactNode => {\n if (!preview) {\n return null;\n }\n return (\n <div\n data-dashfoo=\"drag-preview\"\n ref={overlayRef}\n style={{ ...previewStyle, transform: `translate(${preview.x}px, ${preview.y}px)` }}\n >\n {preview.label}\n </div>\n );\n};\n\nexport { DockIndicator, DragPreview };\nexport type { DragPreviewState, GhostSize };\n","\"use client\";\n\nimport type { Action, DockLocation, DragSubject, DropIntent, Point } from \"@dashfoo/core\";\nimport { dragDockMachine, resolveDockTarget, tabNodeSchema } from \"@dashfoo/core\";\nimport type { DragEndEvent, DragMoveEvent, DragStartEvent } from \"@dnd-kit/dom\";\nimport { useActorRef } from \"@xstate/react\";\nimport type { ReactNode } from \"react\";\nimport {\n useCallback,\n useContext,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport type { DragContextValue } from \"../hooks/drag-hooks\";\nimport {\n createDragManager,\n createDragSubjectStore,\n DragContext,\n DragSubjectStoreContext,\n SharedDragManagerContext,\n useDragSubject,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n} from \"../hooks/drag-hooks\";\nimport { LayoutStoreContext } from \"../hooks/layout-store\";\nimport { insertionIndex, pointInRect, shouldAllowDrop } from \"../lib/tab-insertion\";\nimport { warnOnce } from \"../lib/warn-once\";\n\nimport type { DragPreviewState, GhostSize } from \"./drag-overlays\";\nimport { DockIndicator, DragPreview } from \"./drag-overlays\";\n\n// This module is the drag adapter: it (with ./drag-hooks) is where @dnd-kit is\n// touched. It wires the framework-agnostic @dnd-kit/dom core (no React bindings)\n// to the already unit-tested dragDockMachine — the PointerSensor supplies\n// activation + a live pointer, the adapter hit-tests that pointer against the\n// registered tabsets, and the machine owns the lifecycle and emits a moveNode\n// COMMIT forwarded via onCommit. The drag preview is our own overlay, so there is\n// no Feedback plugin, no placeholder clone, and none of the CSS workarounds those\n// required.\n\n// The dragged tab is excluded so its own slot never counts toward the order —\n// the insertion index and ghost are measured against the tabs it will land among.\nconst tabRects = (strip: Element, excludeId?: string): Array<DOMRect> =>\n [...strip.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].flatMap((tab) =>\n tab.dataset.tabId === excludeId ? [] : [tab.getBoundingClientRect()],\n );\n\n// The tab strip is always a \"stack as a tab\" target, with an insertion index that\n// places the tab at a specific slot. Only the content area below it resolves to\n// the center/split zones via resolveDockTarget (content center appends). The\n// adapter gathers rects from the DOM; the pure math lives in ./tab-insertion.\nconst intentForTabset = (\n id: string,\n element: HTMLElement,\n point: Point,\n draggedId?: string,\n): DropIntent => {\n const strip = element.querySelector('[data-dashfoo=\"tabstrip\"]');\n if (strip && pointInRect(point, strip.getBoundingClientRect())) {\n return {\n index: insertionIndex(tabRects(strip, draggedId), point.x),\n location: \"center\",\n targetId: id,\n };\n }\n const target = resolveDockTarget(point, element.getBoundingClientRect());\n const location: DockLocation = target.kind === \"tab\" ? \"center\" : `split-${target.edge}`;\n if (location === \"center\" && strip) {\n return { index: tabRects(strip, draggedId).length, location, targetId: id };\n }\n return { location, targetId: id };\n};\n\n// The pointer-anchored preview chip follows the cursor at this offset.\nconst PREVIEW_OFFSET: Point = { x: 12, y: 8 };\n\nconst labelOf = (source: { data?: Record<string, unknown> } | null): string => {\n const raw = source?.data?.label;\n return typeof raw === \"string\" ? raw : \"\";\n};\n\nconst isTabFactory = (value: unknown): value is () => unknown => typeof value === \"function\";\n\n// The size the insertion ghost should take: the dragged tab-item's own box\n// (label + close), else the raw source element's (tabset grips, external chips).\nconst sourceSizeOf = (element: Element | null | undefined): GhostSize | undefined => {\n if (!element) {\n return undefined;\n }\n const item = element.closest('[data-dashfoo=\"tab-item\"]') ?? element;\n const rect = item.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0 ? { height: rect.height, width: rect.width } : undefined;\n};\n\n// A tabset grip carries { tabsetId, type: \"tabset\" }; an external source\n// (useExternalTabSource) carries { createTab, type: \"external\" } and its tab is\n// built and validated at drag start; a plain tab carries its id. null aborts\n// the drag — the machine stays idle, so the following OVER/DROP are ignored.\nconst subjectFor = (source: {\n data?: Record<string, unknown>;\n id: string | number;\n}): DragSubject | null => {\n const data = source.data;\n if (data?.type === \"tabset\") {\n return { id: String(data.tabsetId), kind: \"tabset\" };\n }\n if (data?.type === \"external\") {\n if (!isTabFactory(data.createTab)) {\n // A wired-up source that can't produce a tab must not fail silently.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source is missing its createTab function\");\n return null;\n }\n let candidate: unknown;\n try {\n candidate = data.createTab();\n } catch (error) {\n // A consumer callback that throws must abort the drag, not break dragging.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source createTab threw\", error);\n return null;\n }\n const parsed = tabNodeSchema.safeParse(candidate);\n if (!parsed.success) {\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source returned an invalid tab\", parsed.error);\n return null;\n }\n return { id: String(source.id), kind: \"external\", tab: parsed.data };\n }\n return { id: String(source.id), kind: \"tab\" };\n};\n\ntype DragProviderProps = {\n children: ReactNode;\n // Defaults to the layout store's dispatch when rendered under Layout.Root.\n onCommit?: (action: Action) => void;\n // Defaults to the layout store's splitDock (model global), else true.\n splitDock?: boolean;\n};\n\nconst DragProvider = ({ children, onCommit, splitDock }: DragProviderProps): ReactNode => {\n const actorRef = useActorRef(dragDockMachine);\n // Nullable read on purpose (not the throwing useLayout hook): the drag layer\n // also works standalone with an explicit onCommit, e.g. in tests.\n const layoutStore = useContext(LayoutStoreContext);\n const tabsets = useRef(new Map<string, HTMLElement>());\n const overlayRef = useRef<HTMLDivElement | null>(null);\n // Captured once at drag start so the insertion ghost keeps the dragged tab's\n // size even after the pointer leaves its original strip.\n const sourceSize = useRef<GhostSize | undefined>(undefined);\n const [preview, setPreview] = useState<DragPreviewState | null>(null);\n\n // One manager for the whole layout — the shared one when a DashfooDragProvider\n // sits above (so external sources participate in this layout's drags), else\n // our own. useState lazily constructs it once; the destroy rides a\n // useInsertionEffect cleanup (not useEffect) so StrictMode's simulated unmount\n // doesn't tear down the live instance — the same pattern @dnd-kit/react uses\n // internally.\n const sharedManager = useContext(SharedDragManagerContext);\n const [ownManager] = useState(() => (sharedManager ? null : createDragManager()));\n useInsertionEffect(() => () => ownManager?.destroy(), [ownManager]);\n const manager = sharedManager ?? ownManager;\n if (manager === null) {\n // Only reachable when a DashfooDragProvider unmounts while its layout stays\n // mounted — surface the misuse instead of dragging against a dead manager.\n throw new Error(\n \"[dashfoo] DashfooDragProvider unmounted while its layout is still mounted; keep the provider above the layout or remount the layout\",\n );\n }\n\n // The drag subject store: fed by the machine subscription, read by parts via\n // useDragSubject selectors. Guarded so OVER pulses (every pointer move) don't\n // notify subscribers — only an actual subject change (drag start/end) does.\n const [subjectStore] = useState(() => createDragSubjectStore());\n useEffect(() => {\n const subscription = actorRef.subscribe((snapshot) => {\n const subject = snapshot.context.subject;\n if (subjectStore.getState().subject !== subject) {\n subjectStore.setState({ subject });\n }\n });\n return () => {\n subscription.unsubscribe();\n };\n }, [actorRef, subjectStore]);\n\n useEffect(() => {\n const subscription = actorRef.on(\"COMMIT\", (emitted) => {\n const commit = onCommit ?? layoutStore?.getState().dispatch;\n if (!commit) {\n warnOnce(\n \"drag-layer-no-commit\",\n \"Layout.DragLayer has neither an onCommit prop nor a <Layout.Root> above it; drops are ignored\",\n );\n return;\n }\n commit(emitted.action);\n });\n return () => {\n subscription.unsubscribe();\n };\n }, [actorRef, layoutStore, onCommit]);\n\n const registerTabset = useCallback((id: string, element: HTMLElement | null): void => {\n if (element) {\n tabsets.current.set(id, element);\n } else {\n tabsets.current.delete(id);\n }\n }, []);\n\n const getTabsetElement = useCallback((id: string) => tabsets.current.get(id), []);\n\n const getSourceSize = useCallback(() => sourceSize.current, []);\n\n // Which registered tabset sits under the pointer. Tabsets tile (never overlap),\n // so the first rect that contains the point is the unambiguous target — no\n // dnd-kit collision detection needed.\n const tabsetAt = useCallback((point: Point): { element: HTMLElement; id: string } | undefined => {\n for (const [id, element] of tabsets.current) {\n if (pointInRect(point, element.getBoundingClientRect())) {\n return { element, id };\n }\n }\n return undefined;\n }, []);\n\n // The dock intent for a pointer over a tabset, or null when the drop would be a\n // no-op: a tabset dragged onto itself, or the sole tab of a tabset dropped back\n // onto that same tabset. Otherwise the tabset resolves to center/split.\n const resolveIntent = useCallback(\n (\n targetId: string,\n element: HTMLElement,\n point: Point,\n draggedId?: string,\n ): DropIntent | null => {\n const tabIds = [...element.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].map(\n (tab) => tab.dataset.tabId ?? \"\",\n );\n if (!shouldAllowDrop(draggedId, targetId, tabIds)) {\n return null;\n }\n const intent = intentForTabset(targetId, element, point, draggedId);\n // When splitting is disabled, a drop over the body stacks instead of\n // splits. Resolved at drag time (getState, not a subscription) so a model\n // global flip mid-session is honored without re-rendering the layer.\n const splitDockResolved = splitDock ?? layoutStore?.getState().splitDock ?? true;\n if (!splitDockResolved && intent.location.startsWith(\"split-\")) {\n return { location: \"center\", targetId };\n }\n return intent;\n },\n [layoutStore, splitDock],\n );\n\n // Position the preview imperatively (transform only) so following the pointer\n // never triggers a React re-render.\n const positionOverlay = useCallback((point: Point): void => {\n const element = overlayRef.current;\n if (element) {\n element.style.transform = `translate(${point.x + PREVIEW_OFFSET.x}px, ${point.y + PREVIEW_OFFSET.y}px)`;\n }\n }, []);\n\n const attachOverlay = useCallback((element: HTMLDivElement | null): void => {\n overlayRef.current = element;\n }, []);\n\n useEffect(() => {\n const handleStart = (event: DragStartEvent): void => {\n const source = event.operation.source;\n if (!source) {\n return;\n }\n const subject = subjectFor(source);\n if (!subject) {\n return;\n }\n const point = event.operation.position.current;\n sourceSize.current = sourceSizeOf(source.element);\n setPreview({\n label: labelOf(source),\n x: point.x + PREVIEW_OFFSET.x,\n y: point.y + PREVIEW_OFFSET.y,\n });\n actorRef.send({ subject, type: \"START\" });\n };\n\n // dnd-kit's dragmove gives the live pointer on every move, which drives the\n // preview position and the indicator's intent.\n const handleMove = (event: DragMoveEvent): void => {\n const op = event.operation;\n const point = op.position.current;\n positionOverlay(point);\n const draggedId = op.source ? String(op.source.id) : undefined;\n const hit = tabsetAt(point);\n actorRef.send({\n intent: hit ? resolveIntent(hit.id, hit.element, point, draggedId) : null,\n type: \"OVER\",\n });\n };\n\n // Recompute the dock zone from the final pointer, then set the intent and\n // commit in one synchronous pair.\n const handleEnd = (event: DragEndEvent): void => {\n setPreview(null);\n sourceSize.current = undefined;\n if (event.canceled) {\n actorRef.send({ type: \"CANCEL\" });\n return;\n }\n const op = event.operation;\n const point = op.position.current;\n const draggedId = op.source ? String(op.source.id) : undefined;\n const hit = tabsetAt(point);\n if (hit) {\n actorRef.send({\n intent: resolveIntent(hit.id, hit.element, point, draggedId),\n type: \"OVER\",\n });\n }\n actorRef.send({ type: \"DROP\" });\n };\n const offStart = manager.monitor.addEventListener(\"dragstart\", handleStart);\n const offMove = manager.monitor.addEventListener(\"dragmove\", handleMove);\n const offEnd = manager.monitor.addEventListener(\"dragend\", handleEnd);\n return () => {\n offStart();\n offMove();\n offEnd();\n };\n }, [actorRef, manager, positionOverlay, resolveIntent, tabsetAt]);\n\n const contextValue = useMemo<DragContextValue>(\n () => ({ manager, registerTabset }),\n [manager, registerTabset],\n );\n\n return (\n <DragContext.Provider value={contextValue}>\n <DragSubjectStoreContext.Provider value={subjectStore}>\n {children}\n <DockIndicator\n actorRef={actorRef}\n getSourceSize={getSourceSize}\n getTabsetElement={getTabsetElement}\n />\n <DragPreview overlayRef={attachOverlay} preview={preview} />\n </DragSubjectStoreContext.Provider>\n </DragContext.Provider>\n );\n};\n\n// The draggable/droppable hooks and the drag contexts live in ./drag-hooks to\n// keep this module under the size cap and avoid a circular import; re-exported\n// here so consumers still import the whole drag surface from \"./drag-adapter\".\nexport {\n DragProvider,\n subjectFor,\n useDragSubject,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n};\nexport type { DragProviderProps };\n","\"use client\";\n\nimport type { Action, Dashfoo, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\nimport { useLayoutEffect, useState } from \"react\";\n\nimport type { LayoutState } from \"../hooks/layout-store\";\nimport { createLayoutStore, LayoutStoreContext } from \"../hooks/layout-store\";\n\nconst DEFAULT_TABSET_MIN_SIZE = 320;\n\nconst rootStyle = { display: \"flex\", height: \"100%\", width: \"100%\" } as const;\n\ntype LayoutRootProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n children: ReactNode;\n closableTabs?: boolean;\n dispatch: (action: Action) => void;\n draggableTabsets?: boolean;\n keepMounted?: boolean;\n maximizable?: boolean;\n model: Dashfoo;\n renamableTabs?: boolean;\n renderTab: (tab: TabNode) => ReactNode;\n renderTabLabel?: (tab: TabNode) => ReactNode;\n renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;\n};\n\n// The frame of a hand-built layout: creates the scoped layout store every part\n// below subscribes to. Takes the model and dispatch (the two store fields the\n// parts need) rather than a whole DashfooStore — wire useDashfooStore and pass\n// store.model / store.dispatch.\nconst LayoutRoot = ({\n children,\n closableTabs = true,\n dispatch,\n draggableTabsets = true,\n keepMounted = false,\n maximizable = true,\n model,\n renamableTabs = true,\n renderTab,\n renderTabLabel,\n renderTabsetToolbar,\n style,\n ...props\n}: LayoutRootProps): ReactNode => {\n // Model-level globals act as a default layer under the component props: a\n // feature is on unless the prop, the global, or the per-node flag turns it off.\n const global = model.global;\n const snapshot: LayoutState = {\n closableTabs: closableTabs && global.tabEnableClose !== false,\n dispatch,\n draggableTabsets,\n keepMounted,\n maximizable: maximizable && global.tabSetEnableMaximize !== false,\n maximizedTabsetId: model.maximizedTabsetId,\n renamableTabs: renamableTabs && global.tabEnableRename !== false,\n renderTab,\n renderTabLabel,\n renderTabsetToolbar,\n splitDock: global.enableSplitDock !== false,\n tabLocation: global.tabLocation ?? \"top\",\n tabsetMinSize: global.tabSetMinSize ?? DEFAULT_TABSET_MIN_SIZE,\n tabStripEnabled: global.tabSetEnableTabStrip !== false,\n };\n\n const [store] = useState(() => createLayoutStore(snapshot));\n // Props→store sync. useLayoutEffect so subscribers see fresh state before\n // paint; never during render — setState on an external store mid-render is\n // unsafe.\n useLayoutEffect(() => {\n store.setState(snapshot);\n });\n\n const layoutStyle: CSSProperties =\n global.splitterSize === undefined\n ? { ...rootStyle, ...style }\n : ({\n ...rootStyle,\n \"--dashfoo-splitter-size\": `${global.splitterSize}px`,\n ...style,\n } as CSSProperties);\n\n return (\n <LayoutStoreContext.Provider value={store}>\n <div {...props} data-dashfoo=\"layout\" style={layoutStyle}>\n {children}\n </div>\n </LayoutStoreContext.Provider>\n );\n};\n\nexport { LayoutRoot };\nexport type { LayoutRootProps };\n","// Stable ids wiring each tab to its panel (aria-controls / aria-labelledby).\n// Shared by tabset-view and tab-button so the wiring stays in one place with no\n// circular import between those two modules.\nconst tabDomId = (tabsetId: string, tabId: string): string => `dashfoo-tab-${tabsetId}-${tabId}`;\nconst panelDomId = (tabsetId: string): string => `dashfoo-panel-${tabsetId}`;\n\nexport { panelDomId, tabDomId };\n","\"use client\";\n\nimport type { Action, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\nimport { tabDomId } from \"../../lib/tab-ids\";\nimport { warnOnce } from \"../../lib/warn-once\";\nimport type { OverflowItem } from \"../tab-overflow\";\n\n// The per-tabset coordination store behind <Tabset.Root>. Model-derived fields\n// (node, visualSelected, flags) are synced in by Root each render; coordination\n// state (rename editor, overflow, the registered tablist) lives here so sibling\n// parts stay in lockstep without prop threading. editingTabId being a single\n// field guarantees at most one open rename editor per tabset.\n// Model-derived fields (node, visualSelected, flags) are synced in by Root each\n// render; coordination state (editingTabId, overflowItems, tablistElement,\n// restoreFocus) is owned here; actions read live state via get(). restoreFocus\n// is set by closeTab so Root's post-delete effect knows to move focus to the\n// newly-selected tab instead of leaving it on <body>.\ntype TabsetState = {\n activeTab: TabNode | undefined;\n cancelRename: () => void;\n closeTab: (tabId: string) => void;\n commitRename: (tabId: string, value: string) => void;\n dispatch: (action: Action) => void;\n editingTabId: string | null;\n isMaximized: boolean;\n node: TabsetNode;\n overflowItems: Array<OverflowItem>;\n registerRenameInput: () => () => void;\n registerTablist: (element: HTMLDivElement | null) => void;\n renameInputCount: number;\n renameTab: (tabId: string, name: string) => void;\n restoreFocus: boolean;\n selectOverflowTab: (tabId: string) => void;\n selectTab: (index: number, options?: { focus?: boolean }) => void;\n showMaximize: boolean;\n startEditing: (tabId: string) => void;\n tablistElement: HTMLDivElement | null;\n tabsClosable: boolean;\n tabsRenamable: boolean;\n toggleMaximize: () => void;\n visualSelected: number;\n};\n\ntype TabsetStore = StoreApi<TabsetState>;\n\ntype TabsetSnapshot = Pick<\n TabsetState,\n | \"activeTab\"\n | \"dispatch\"\n | \"isMaximized\"\n | \"node\"\n | \"showMaximize\"\n | \"tabsClosable\"\n | \"tabsRenamable\"\n | \"visualSelected\"\n>;\n\nconst tabButtonIn = (\n scope: HTMLElement | null,\n tabsetId: string,\n tabId: string,\n): HTMLElement | null =>\n scope?.querySelector<HTMLElement>(`#${CSS.escape(tabDomId(tabsetId, tabId))}`) ?? null;\n\nconst createTabsetStore = (initial: TabsetSnapshot): TabsetStore =>\n createStore<TabsetState>((set, get) => ({\n ...initial,\n cancelRename: () => set({ editingTabId: null }),\n closeTab: (tabId) => {\n set({ restoreFocus: true });\n get().dispatch({ tabId, type: \"deleteTab\" });\n },\n commitRename: (tabId, value) => {\n set({ editingTabId: null });\n const { node, renameTab } = get();\n const trimmed = value.trim();\n const current = node.children.find((tab) => tab.id === tabId)?.name;\n if (trimmed && trimmed !== current) {\n renameTab(tabId, trimmed);\n }\n },\n editingTabId: null,\n overflowItems: [],\n registerRenameInput: () => {\n set((state) => ({ renameInputCount: state.renameInputCount + 1 }));\n return () => set((state) => ({ renameInputCount: state.renameInputCount - 1 }));\n },\n registerTablist: (element) => {\n if (element) {\n const current = get().tablistElement;\n if (current && current !== element) {\n warnOnce(\n `duplicate-tablist-${get().node.id}`,\n \"more than one <Tabset.Tablist> under the same <Tabset.Root>; the last one wins for keyboard focus and overflow\",\n );\n }\n set({ tablistElement: element });\n return;\n }\n set({ tablistElement: null });\n },\n renameInputCount: 0,\n renameTab: (tabId, name) => get().dispatch({ name, tabId, type: \"renameTab\" }),\n restoreFocus: false,\n selectOverflowTab: (tabId) => {\n const { dispatch, node, tablistElement } = get();\n const index = node.children.findIndex((tab) => tab.id === tabId);\n if (index === -1) {\n return;\n }\n dispatch({ index, tabsetId: node.id, type: \"selectTab\" });\n tabButtonIn(tablistElement, node.id, tabId)?.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n },\n selectTab: (index, options) => {\n const { dispatch, node, tablistElement } = get();\n dispatch({ index, tabsetId: node.id, type: \"selectTab\" });\n if (options?.focus) {\n const tabId = node.children[index]?.id;\n if (tabId) {\n tabButtonIn(tablistElement, node.id, tabId)?.focus();\n }\n }\n },\n startEditing: (tabId) => {\n const { node, renameInputCount, tabsRenamable } = get();\n const tab = node.children.find((child) => child.id === tabId);\n if (!tabsRenamable || tab?.enableRename === false) {\n return;\n }\n if (renameInputCount === 0) {\n // The trigger must not vanish into nothing when no editor is composed.\n warnOnce(\n `missing-rename-input-${node.id}`,\n \"startEditing needs a <Tabset.RenameInput> in the composition; ignoring\",\n );\n return;\n }\n set({ editingTabId: tabId });\n },\n tablistElement: null,\n toggleMaximize: () => {\n const { dispatch, isMaximized, node } = get();\n dispatch({ tabsetId: isMaximized ? null : node.id, type: \"setMaximizedTabset\" });\n },\n }));\n\n// Carries only the store instance — state always flows through selectors.\nconst TabsetStoreContext = createContext<TabsetStore | null>(null);\n\nconst useTabset = <T>(selector: (state: TabsetState) => T): T => {\n const store = useContext(TabsetStoreContext);\n if (store === null) {\n throw new Error(\"dashfoo Tabset parts must be rendered inside <Tabset.Root>.\");\n }\n return useStore(store, selector);\n};\n\n// Identity only — which tab a Trigger/CloseButton/RenameInput belongs to.\n// Derived state (selected, editing) comes from useTabset selectors.\ntype TabContextValue = { index: number; tab: TabNode };\n\nconst TabContext = createContext<TabContextValue | null>(null);\n\nconst useTab = (): TabContextValue => {\n const value = useContext(TabContext);\n if (value === null) {\n throw new Error(\"dashfoo tab parts must be rendered inside <Tabset.Tab>.\");\n }\n return value;\n};\n\nexport { createTabsetStore, TabContext, TabsetStoreContext, useTab, useTabset };\nexport type { TabContextValue, TabsetSnapshot, TabsetState, TabsetStore };\n","\"use client\";\n\nimport type { TabNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { panelDomId, tabDomId } from \"../../lib/tab-ids\";\n\nimport { useTabset } from \"./tabset-store\";\n\nconst contentStyle: CSSProperties = { flex: 1, minHeight: 0, overflow: \"auto\" };\n\n// Content is resolved through a component (not inline) so the panel has a stable\n// element identity and React keeps its state across re-renders instead of\n// remounting the inline result. Covers both the registry default (renderTab)\n// and a consumer-supplied render prop.\nconst TabPanel = ({\n render,\n tab,\n}: {\n render?: (tab: TabNode) => ReactNode;\n tab: TabNode;\n}): ReactNode => {\n const renderTab = useLayout((state) => state.renderTab);\n return render ? render(tab) : renderTab(tab);\n};\n\ntype TabsetContentProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n // Overrides the layout's renderTab for this tabset's panels.\n children?: (tab: TabNode) => ReactNode;\n};\n\nconst TabsetContent = ({ children, style, ...props }: TabsetContentProps): ReactNode => {\n const node = useTabset((state) => state.node);\n const visualSelected = useTabset((state) => state.visualSelected);\n const keepMounted = useLayout((state) => state.keepMounted);\n\n const mergedStyle = { ...contentStyle, ...style };\n\n // keepMounted renders every panel (inactive ones hidden) so their state\n // survives a tab switch; otherwise only the active panel mounts.\n if (keepMounted && node.children.length > 0) {\n return node.children.map((tab, index) => (\n <div\n {...props}\n aria-labelledby={tabDomId(node.id, tab.id)}\n data-dashfoo=\"tabcontent\"\n hidden={index !== visualSelected || undefined}\n id={index === visualSelected ? panelDomId(node.id) : undefined}\n key={tab.id}\n role={index === visualSelected ? \"tabpanel\" : undefined}\n style={mergedStyle}\n tabIndex={index === visualSelected ? 0 : undefined}\n >\n <TabPanel render={children} tab={tab} />\n </div>\n ));\n }\n\n const active = node.children[visualSelected];\n if (active) {\n return (\n <div\n {...props}\n aria-labelledby={tabDomId(node.id, active.id)}\n data-dashfoo=\"tabcontent\"\n id={panelDomId(node.id)}\n role=\"tabpanel\"\n style={mergedStyle}\n tabIndex={0}\n >\n <TabPanel render={children} tab={active} />\n </div>\n );\n }\n return <div {...props} data-dashfoo=\"tabcontent\" style={mergedStyle} />;\n};\n\nexport { TabsetContent };\nexport type { TabsetContentProps };\n","\"use client\";\n\nimport type { CSSProperties, FocusEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nconst OverflowIcon = (): ReactNode => (\n <svg aria-hidden=\"true\" fill=\"currentColor\" height=\"10\" viewBox=\"0 0 10 10\" width=\"10\">\n <circle cx=\"2\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"8\" cy=\"5\" r=\"1\" />\n </svg>\n);\n\nconst menuStyle: CSSProperties = {\n position: \"absolute\",\n right: 0,\n top: \"100%\",\n zIndex: 10,\n};\n\ntype OverflowItem = { id: string; name: string };\n\n// A dependency-free overflow control: a button that toggles a menu of the hidden\n// tabs. Implements the WAI-ARIA APG menu-button keyboard model — roving tabindex,\n// arrow/Home/End navigation, Escape-to-close with focus return. Closes on outside\n// pointerdown and focusout.\nconst TabOverflowMenu = ({\n items,\n onSelect,\n}: {\n items: Array<OverflowItem>;\n onSelect: (id: string) => void;\n}): ReactNode => {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(0);\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n const handlePointerDown = (event: PointerEvent): void => {\n if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n };\n }, [open]);\n\n // moving DOM focus onto the first menuitem is a side effect of the menu becoming\n // visible, so it belongs in an effect rather than the click handler (the items don't\n // exist yet there). activeIndex is reset to 0 on open, so we always land on the first.\n useEffect(() => {\n if (open) {\n itemRefs.current[0]?.focus();\n }\n }, [open]);\n\n const closeAndReturnFocus = (): void => {\n setOpen(false);\n triggerRef.current?.focus();\n };\n\n const focusItem = (index: number): void => {\n setActiveIndex(index);\n itemRefs.current[index]?.focus();\n };\n\n const handleToggle = (): void => {\n setActiveIndex(0);\n setOpen((value) => !value);\n };\n\n const handleSelect = (id: string): void => {\n onSelect(id);\n closeAndReturnFocus();\n };\n\n const handleMenuKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n const last = items.length - 1;\n switch (event.key) {\n case \"ArrowDown\": {\n event.preventDefault();\n focusItem(activeIndex >= last ? 0 : activeIndex + 1);\n break;\n }\n case \"ArrowUp\": {\n event.preventDefault();\n focusItem(activeIndex <= 0 ? last : activeIndex - 1);\n break;\n }\n case \"Home\": {\n event.preventDefault();\n focusItem(0);\n break;\n }\n case \"End\": {\n event.preventDefault();\n focusItem(last);\n break;\n }\n case \"Escape\": {\n event.preventDefault();\n closeAndReturnFocus();\n break;\n }\n default: {\n break;\n }\n }\n };\n\n // Tab out of the menu (or any other focus leaving the root) closes it without\n // stealing focus, complementing the outside-pointerdown handler for keyboard users.\n const handleFocusOut = (event: FocusEvent<HTMLDivElement>): void => {\n if (event.relatedTarget instanceof Node && rootRef.current?.contains(event.relatedTarget)) {\n return;\n }\n setOpen(false);\n };\n\n return (\n <div data-dashfoo=\"tab-overflow-root\" ref={rootRef} style={{ position: \"relative\" }}>\n <button\n aria-expanded={open}\n aria-haspopup=\"menu\"\n aria-label=\"More tabs\"\n data-dashfoo=\"tab-overflow\"\n onClick={handleToggle}\n ref={triggerRef}\n type=\"button\"\n >\n <OverflowIcon />\n </button>\n {open ? (\n <div\n data-dashfoo=\"tab-overflow-menu\"\n onBlur={handleFocusOut}\n onKeyDown={handleMenuKeyDown}\n role=\"menu\"\n style={menuStyle}\n tabIndex={-1}\n >\n {items.map((item, index) => (\n <button\n data-dashfoo=\"tab-overflow-item\"\n key={item.id}\n onClick={() => handleSelect(item.id)}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n role=\"menuitem\"\n tabIndex={index === activeIndex ? 0 : -1}\n type=\"button\"\n >\n {item.name}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n );\n};\n\nexport { TabOverflowMenu };\nexport type { OverflowItem };\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { TabOverflowMenu } from \"../tab-overflow\";\n\nimport { useTabset } from \"./tabset-store\";\n\n// The menu of tabs clipped out of the tablist's visible box. Measurement lives\n// in Tabset.Root (the store's overflowItems); this part only renders the\n// APG menu-button when there is something to show.\nconst TabsetOverflowMenu = (): ReactNode => {\n const overflowItems = useTabset((state) => state.overflowItems);\n const selectOverflowTab = useTabset((state) => state.selectOverflowTab);\n\n if (overflowItems.length === 0) {\n return null;\n }\n return <TabOverflowMenu items={overflowItems} onSelect={selectOverflowTab} />;\n};\n\nexport { TabsetOverflowMenu };\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n// The ids of the tabs clipped by the tablist's visible box — past the right edge\n// OR scrolled off before the left edge. +/-1 absorbs sub-pixel rounding. One\n// pass: keep the id only when the tab is both overflowing and identifiable.\nconst overflowingIds = (tablist: Element): Array<string> => {\n const box = tablist.getBoundingClientRect();\n return [...tablist.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].flatMap((tab) => {\n const rect = tab.getBoundingClientRect();\n const clipped = rect.right > box.right + 1 || rect.left < box.left - 1;\n return clipped && tab.dataset.tabId ? [tab.dataset.tabId] : [];\n });\n};\n\n// Recomputes the overflowing ids when the tablist resizes, scrolls, or its tab\n// labels/widths change. Takes the element (not a ref) so a tablist that mounts\n// late — or is swapped by a custom composition — re-arms the observers.\n// `signature` folds tab id+name so renames recompute even when the count is\n// unchanged. Returns [] until measured (and in jsdom, where rects are 0).\nconst useTabOverflow = (tablist: HTMLElement | null, signature: string): Array<string> => {\n const [overflow, setOverflow] = useState<Array<string>>([]);\n\n useEffect(() => {\n // No tablist (e.g. the strip is disabled) means nothing can overflow.\n const recompute = (): void => {\n setOverflow((previous) => {\n const next = tablist ? overflowingIds(tablist) : [];\n return next.length === 0 && previous.length === 0 ? previous : next;\n });\n };\n recompute();\n if (!tablist) {\n return;\n }\n const observer = new ResizeObserver(recompute);\n observer.observe(tablist);\n // Scrolling the strip changes which tabs are clipped on the left/right without\n // resizing it, so listen for scroll too.\n tablist.addEventListener(\"scroll\", recompute, { passive: true });\n return () => {\n observer.disconnect();\n tablist.removeEventListener(\"scroll\", recompute);\n };\n }, [tablist, signature]);\n\n return overflow;\n};\n\nexport { overflowingIds, useTabOverflow };\n","// The neighbour to show as active when the selected tab is dragged out: the next\n// tab if there is one, else the previous, else -1 (the tabset is emptying).\nconst fallbackSelectedIndex = (count: number, removedIndex: number): number =>\n removedIndex + 1 < count ? removedIndex + 1 : removedIndex - 1;\n\nexport { fallbackSelectedIndex };\n","\"use client\";\n\nimport type { TabsetNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useStore } from \"zustand\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { useTabOverflow } from \"../../hooks/use-tab-overflow\";\nimport { tabDomId } from \"../../lib/tab-ids\";\nimport { fallbackSelectedIndex } from \"../../lib/tab-selection\";\nimport { useDragSubject, useTabsetDroppable } from \"../drag-adapter\";\n\nimport type { TabsetSnapshot } from \"./tabset-store\";\nimport { createTabsetStore, TabsetStoreContext } from \"./tabset-store\";\n\n// height/width 100% (not flex:1) so the tabset fills its parent whether that\n// parent is a flex item or a plain block — rrp wraps panel content in a block\n// div, where flex:1 would collapse to content height.\nconst tabsetStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100%\",\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n};\n\ntype TabsetRootProps = ComponentProps<\"div\"> & { node: TabsetNode };\n\n// Creates the per-tabset store and owns everything that needs the whole tabset:\n// the droppable registration, the drag-out selection fallback, overflow\n// measurement, and focus restore after a close.\nconst TabsetRoot = ({\n children,\n node,\n ref: userRef,\n style,\n ...props\n}: TabsetRootProps): ReactNode => {\n const closableTabs = useLayout((state) => state.closableTabs);\n const dispatch = useLayout((state) => state.dispatch);\n const maximizable = useLayout((state) => state.maximizable);\n const maximizedTabsetId = useLayout((state) => state.maximizedTabsetId);\n const renamableTabs = useLayout((state) => state.renamableTabs);\n const tabLocation = useLayout((state) => state.tabLocation);\n\n const { ref } = useTabsetDroppable(node.id);\n const dragSubject = useDragSubject();\n const tabsetRef = useRef<HTMLDivElement | null>(null);\n\n const isMaximized = maximizedTabsetId === node.id;\n const showMaximize = maximizable && node.enableMaximize !== false;\n const tabsClosable = closableTabs && node.enableClose !== false;\n // Renaming has no tabset-level flag — only the layout flag and per-tab opt-out.\n const tabsRenamable = renamableTabs;\n\n // While the selected tab is being dragged out of THIS tabset, show a neighbour\n // as active so the strip + content preview \"as if that tab were already gone\".\n // Purely visual — the model selection is untouched and resumes on drop/cancel.\n const draggingTabIndex =\n dragSubject?.kind === \"tab\" ? node.children.findIndex((tab) => tab.id === dragSubject.id) : -1;\n const visualSelected =\n draggingTabIndex !== -1 && draggingTabIndex === node.selected\n ? fallbackSelectedIndex(node.children.length, draggingTabIndex)\n : node.selected;\n const activeTab = node.children[visualSelected];\n const isDragSource = dragSubject?.kind === \"tabset\" && dragSubject.id === node.id;\n\n const snapshot: TabsetSnapshot = {\n activeTab,\n dispatch,\n isMaximized,\n node,\n showMaximize,\n tabsClosable,\n tabsRenamable,\n visualSelected,\n };\n const [store] = useState(() => createTabsetStore(snapshot));\n\n // Props→store sync. useLayoutEffect (not useEffect) so subscribers re-render\n // before paint and DOM-focus effects below see fresh state; never during\n // render — setState on an external store mid-render is unsafe.\n useLayoutEffect(() => {\n store.setState(snapshot);\n });\n\n // Overflow measurement runs here (not in the menu part) because the default\n // composition's toolbar-visibility condition needs the count, and the tablist\n // element registration already lives in the store.\n const tablistElement = useStore(store, (state) => state.tablistElement);\n // A rename or scroll changes clipping without changing the count, so the\n // signature folds each tab's id+name into the deps the hook watches.\n const overflowSignature = node.children.map((tab) => `${tab.id}:${tab.name}`).join(\"|\");\n const overflow = useTabOverflow(tablistElement, overflowSignature);\n // Resolve overflowing ids to names once per change instead of a find() per item.\n const overflowItems = useMemo(() => {\n const names = new Map(node.children.map((tab) => [tab.id, tab.name]));\n return overflow.map((id) => ({ id, name: names.get(id) ?? id }));\n }, [overflow, node.children]);\n useLayoutEffect(() => {\n store.setState({ overflowItems });\n }, [overflowItems, store]);\n\n // Runs after a closeTab commits. Focus the now-active tab's button, or fall\n // back to the tabset container if the tabset emptied so focus never lands on\n // <body>. Scoped to the root (not the tablist) so tabs rendered outside a\n // tablist still get focus restore.\n useEffect(() => {\n if (!store.getState().restoreFocus) {\n return;\n }\n store.setState({ restoreFocus: false });\n const activeTabId = node.children[node.selected]?.id;\n if (activeTabId) {\n tabsetRef.current\n ?.querySelector<HTMLElement>(`#${CSS.escape(tabDomId(node.id, activeTabId))}`)\n ?.focus();\n } else {\n tabsetRef.current?.focus();\n }\n }, [node.children, node.selected, node.id, store]);\n\n return (\n <TabsetStoreContext.Provider value={store}>\n <div\n {...props}\n data-dashfoo=\"tabset\"\n data-dragging-source={isDragSource || undefined}\n data-tab-location={tabLocation}\n ref={(element) => {\n ref(element);\n tabsetRef.current = element;\n if (typeof userRef === \"function\") {\n userRef(element);\n } else if (userRef) {\n userRef.current = element;\n }\n }}\n style={{ ...tabsetStyle, ...style }}\n // -1 so an emptied tabset can receive focus on close without entering the\n // Tab order.\n tabIndex={-1}\n >\n {children}\n </div>\n </TabsetStoreContext.Provider>\n );\n};\n\nexport { TabsetRoot };\nexport type { TabsetRootProps };\n","import type { Ref } from \"react\";\n\nconst applyRef = <T>(ref: Ref<T> | undefined, value: T | null): void => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref) {\n ref.current = value;\n }\n};\n\n// Fans one element out to several refs so a part can keep its internal ref\n// (draggable wiring, focus lookups) while still honoring a consumer-supplied one.\nconst mergeRefs =\n <T>(...refs: Array<Ref<T> | undefined>) =>\n (value: T | null): void => {\n for (const ref of refs) {\n applyRef(ref, value);\n }\n };\n\nexport { mergeRefs };\n","\"use client\";\n\nimport type { TabNode } from \"@dashfoo/core\";\nimport type { ComponentProps, MouseEvent, ReactNode } from \"react\";\nimport { useContext, useEffect, useMemo, useRef } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { mergeRefs } from \"../../lib/merge-refs\";\nimport { panelDomId, tabDomId } from \"../../lib/tab-ids\";\nimport { warnOnce } from \"../../lib/warn-once\";\nimport { useDragSubject, useTabDraggable } from \"../drag-adapter\";\n\nimport { TabContext, TabsetStoreContext, useTab, useTabset } from \"./tabset-store\";\n\ntype TabsetTabProps = ComponentProps<\"span\"> & { tab: TabNode };\n\n// The per-tab wrapper: provides identity (which tab, which index) to Trigger,\n// CloseButton, and RenameInput, and owns the focus-return-after-rename effect.\nconst TabsetTab = ({ children, ref: userRef, tab, ...props }: TabsetTabProps): ReactNode => {\n const store = useContext(TabsetStoreContext);\n const node = useTabset((state) => state.node);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n // `data-dragging` is driven off the drag machine's subject, which is\n // authoritative for the whole drag — the source tab stays in the strip (dimmed)\n // while the overlay chip follows the pointer.\n const dragSubject = useDragSubject();\n const isDragging = dragSubject?.kind === \"tab\" && dragSubject.id === tab.id;\n const itemRef = useRef<HTMLSpanElement | null>(null);\n const wasEditing = useRef(false);\n\n const index = node.children.findIndex((child) => child.id === tab.id);\n\n // The orphan check must not run at render time: right after a tab moves\n // between tabsets, this render sees the new `tab` prop against the store's\n // not-yet-synced node, so a legitimately moved tab transiently looks\n // orphaned (the index self-corrects before paint). The effect reads the live\n // store instead — Root's layout-effect sync always lands before any passive\n // effect flushes, so only a genuinely missing tab warns.\n const orphanAtRender = index === -1;\n useEffect(() => {\n const settled = store?.getState().node.children.some((child) => child.id === tab.id);\n if (settled === false) {\n warnOnce(\n `orphan-tab-${tab.id}`,\n `<Tabset.Tab> for \"${tab.id}\" is not a child of its Tabset.Root's node; it renders but can never be selected`,\n );\n }\n }, [orphanAtRender, store, tab.id]);\n\n // Return focus to the tab button once the inline editor closes.\n useEffect(() => {\n if (wasEditing.current && !editing) {\n itemRef.current?.querySelector<HTMLElement>('[data-dashfoo=\"tab\"]')?.focus();\n }\n wasEditing.current = editing;\n }, [editing]);\n\n const contextValue = useMemo(() => ({ index, tab }), [index, tab]);\n const refCallback = useMemo(() => mergeRefs<HTMLSpanElement>(itemRef, userRef), [userRef]);\n\n return (\n <TabContext.Provider value={contextValue}>\n <span\n {...props}\n data-dashfoo=\"tab-item\"\n data-dragging={isDragging || undefined}\n ref={refCallback}\n >\n {children}\n </span>\n </TabContext.Provider>\n );\n};\n\ntype TabsetTriggerProps = ComponentProps<\"button\">;\n\n// The tab button itself — a sibling of the close control (not its parent, which\n// would be invalid markup and would pollute the tab's accessible name). Hidden\n// while the rename editor is open; the draggable hook stays mounted so the\n// Draggable instance survives the swap.\nconst TabsetTrigger = ({\n children,\n onClick,\n onDoubleClick,\n ref: userRef,\n ...props\n}: TabsetTriggerProps): ReactNode => {\n const { index, tab } = useTab();\n const node = useTabset((state) => state.node);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const selectTab = useTabset((state) => state.selectTab);\n const startEditing = useTabset((state) => state.startEditing);\n const tabsRenamable = useTabset((state) => state.tabsRenamable);\n const visualSelected = useTabset((state) => state.visualSelected);\n const renderTabLabel = useLayout((state) => state.renderTabLabel);\n const { ref } = useTabDraggable(tab.id, tab.enableDrag === false, tab.name);\n const refCallback = useMemo(() => mergeRefs<HTMLButtonElement>(ref, userRef), [ref, userRef]);\n\n if (editing) {\n return null;\n }\n\n const selected = index === visualSelected;\n const renamable = tabsRenamable && tab.enableRename !== false;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n selectTab(index);\n };\n\n const handleDoubleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onDoubleClick?.(event);\n if (renamable) {\n startEditing(tab.id);\n }\n };\n\n return (\n <button\n aria-label={renderTabLabel ? tab.name : undefined}\n {...props}\n aria-controls={panelDomId(node.id)}\n aria-selected={selected}\n data-dashfoo=\"tab\"\n data-tab-id={tab.id}\n id={tabDomId(node.id, tab.id)}\n onClick={handleClick}\n onDoubleClick={handleDoubleClick}\n ref={refCallback}\n role=\"tab\"\n tabIndex={selected ? 0 : -1}\n type=\"button\"\n >\n {children ?? (renderTabLabel ? renderTabLabel(tab) : tab.name)}\n </button>\n );\n};\n\nexport { TabsetTab, TabsetTrigger };\nexport type { TabsetTabProps, TabsetTriggerProps };\n","import type { CSSProperties, ReactNode } from \"react\";\n\nconst iconStyle: CSSProperties = { pointerEvents: \"none\" };\n\n// Shared lucide-style icon frame (24-unit grid, currentColor stroke). iconStyle\n// sets pointer-events:none so a pointerdown on a grip lands on the button (the\n// draggable), not the svg path — otherwise the drag sensor never activates.\nconst Icon = ({ children }: { children: ReactNode }): ReactNode => (\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"12\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n style={iconStyle}\n viewBox=\"0 0 24 24\"\n width=\"12\"\n >\n {children}\n </svg>\n);\n\nconst CloseIcon = (): ReactNode => (\n <Icon>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Icon>\n);\n\nconst GripIcon = (): ReactNode => (\n <Icon>\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"19\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"19\" cy=\"12\" r=\"1\" />\n <circle cx=\"5\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n <circle cx=\"19\" cy=\"19\" r=\"1\" />\n <circle cx=\"5\" cy=\"19\" r=\"1\" />\n </Icon>\n);\n\nconst MaximizeIcon = ({ maximized }: { maximized: boolean }): ReactNode =>\n maximized ? (\n <Icon>\n <path d=\"m14 10 7-7\" />\n <path d=\"M20 10h-6V4\" />\n <path d=\"m3 21 7-7\" />\n <path d=\"M4 14h6v6\" />\n </Icon>\n ) : (\n <Icon>\n <path d=\"M15 3h6v6\" />\n <path d=\"m21 3-7 7\" />\n <path d=\"m3 21 7-7\" />\n <path d=\"M9 21H3v-6\" />\n </Icon>\n );\n\nexport { CloseIcon, GripIcon, MaximizeIcon };\n","\"use client\";\n\nimport type { ComponentProps, FocusEvent, KeyboardEvent, MouseEvent, ReactNode } from \"react\";\nimport { useEffect, useRef } from \"react\";\n\nimport { CloseIcon } from \"../tabset-icons\";\n\nimport { useTab, useTabset } from \"./tabset-store\";\n\ntype TabsetRenameInputProps = ComponentProps<\"input\">;\n\n// Inline rename editor. Enter/Escape set `done` so the unmount blur does not\n// re-commit after a deliberate commit or cancel. A separate inner component so\n// each editing session mounts fresh (autofocus + select, reset `done`).\nconst RenameEditor = ({\n defaultValue,\n onBlur,\n onKeyDown,\n ref: userRef,\n ...props\n}: TabsetRenameInputProps): ReactNode => {\n const { tab } = useTab();\n const cancelRename = useTabset((state) => state.cancelRename);\n const commitRename = useTabset((state) => state.commitRename);\n const ref = useRef<HTMLInputElement | null>(null);\n const done = useRef(false);\n\n useEffect(() => {\n ref.current?.focus();\n ref.current?.select();\n }, []);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>): void => {\n onKeyDown?.(event);\n if (event.key === \"Enter\") {\n done.current = true;\n commitRename(tab.id, event.currentTarget.value);\n } else if (event.key === \"Escape\") {\n done.current = true;\n cancelRename();\n }\n };\n\n const handleCommitOnBlur = (event: FocusEvent<HTMLInputElement>): void => {\n onBlur?.(event);\n if (done.current) {\n return;\n }\n done.current = true;\n commitRename(tab.id, event.currentTarget.value);\n };\n\n return (\n <input\n aria-label={`Rename ${tab.name}`}\n {...props}\n data-dashfoo=\"tab-rename\"\n defaultValue={defaultValue ?? tab.name}\n onBlur={handleCommitOnBlur}\n onKeyDown={handleKeyDown}\n ref={(element) => {\n ref.current = element;\n if (typeof userRef === \"function\") {\n userRef(element);\n } else if (userRef) {\n userRef.current = element;\n }\n }}\n type=\"text\"\n />\n );\n};\n\n// Renders only while its tab is being renamed. Always composed (it registers its\n// presence on mount so startEditing can warn when no editor exists), but inert\n// until the trigger's double-click flips editingTabId.\nconst TabsetRenameInput = (props: TabsetRenameInputProps): ReactNode => {\n const { tab } = useTab();\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const registerRenameInput = useTabset((state) => state.registerRenameInput);\n\n useEffect(() => registerRenameInput(), [registerRenameInput]);\n\n if (!editing) {\n return null;\n }\n return <RenameEditor {...props} />;\n};\n\ntype TabsetCloseButtonProps = ComponentProps<\"button\">;\n\nconst TabsetCloseButton = ({ children, onClick, ...props }: TabsetCloseButtonProps): ReactNode => {\n const { index, tab } = useTab();\n const closeTab = useTabset((state) => state.closeTab);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const tabsClosable = useTabset((state) => state.tabsClosable);\n const visualSelected = useTabset((state) => state.visualSelected);\n\n const closable = tabsClosable && tab.enableClose !== false;\n if (!closable || editing) {\n return null;\n }\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n // Closing must not bubble into the trigger and select the closing tab.\n event.stopPropagation();\n closeTab(tab.id);\n };\n\n return (\n <button\n aria-label={`Close ${tab.name}`}\n {...props}\n data-dashfoo=\"tab-close\"\n onClick={handleClick}\n // Roving tabindex: only the active tab's close button is tabbable, so\n // Tab does not stop on a close button inside every tab in the tablist.\n tabIndex={index === visualSelected ? 0 : -1}\n type=\"button\"\n >\n {children ?? <CloseIcon />}\n </button>\n );\n};\n\nexport { TabsetCloseButton, TabsetRenameInput };\nexport type { TabsetCloseButtonProps, TabsetRenameInputProps };\n","\"use client\";\n\nimport type { ComponentProps, CSSProperties, KeyboardEvent, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { mergeRefs } from \"../../lib/merge-refs\";\n\nimport { useTabset } from \"./tabset-store\";\n\n// The strip is a presentational row: the tablist (the actual tabs) plus room for\n// a trailing toolbar. role=\"tablist\" lives on the inner tablist so the toolbar is\n// not announced as a tab. The drag adapter hit-tests [data-dashfoo=\"tabstrip\"],\n// so the attribute is applied after the spread — it is structural, not theming.\nconst stripStyle: CSSProperties = { display: \"flex\", flexShrink: 0 };\n\nconst tablistStyle: CSSProperties = { display: \"flex\", minWidth: 0, overflowX: \"auto\" };\n\ntype TabsetTabStripProps = ComponentProps<\"div\">;\n\nconst TabsetTabStrip = ({ style, ...props }: TabsetTabStripProps): ReactNode => (\n <div {...props} data-dashfoo=\"tabstrip\" style={{ ...stripStyle, ...style }} />\n);\n\ntype TabsetTablistProps = ComponentProps<\"div\">;\n\n// Owns the roving-tabindex keyboard model (WAI-ARIA APG Tabs): arrows move and\n// select, Home/End jump to the ends, and focus follows the new selection.\nconst TabsetTablist = ({\n onKeyDown,\n ref: userRef,\n style,\n ...props\n}: TabsetTablistProps): ReactNode => {\n const node = useTabset((state) => state.node);\n const registerTablist = useTabset((state) => state.registerTablist);\n const selectTab = useTabset((state) => state.selectTab);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n onKeyDown?.(event);\n if (event.defaultPrevented) {\n return;\n }\n // Only act when the key comes from a tab — otherwise a focused close button\n // (a tablist child) would have its arrows/Home/End hijacked.\n if (!(event.target instanceof HTMLElement) || !event.target.closest('[role=\"tab\"]')) {\n return;\n }\n const count = node.children.length;\n if (count === 0) {\n return;\n }\n const targets: Record<string, number> = {\n ArrowLeft: (node.selected - 1 + count) % count,\n ArrowRight: (node.selected + 1) % count,\n End: count - 1,\n Home: 0,\n };\n const next = targets[event.key];\n if (next === undefined) {\n return;\n }\n event.preventDefault();\n selectTab(next, { focus: true });\n };\n\n // Stable callback ref so React doesn't detach/re-register the tablist on every\n // render (a fresh inline ref identity would churn the store registration).\n const refCallback = useMemo(\n () => mergeRefs<HTMLDivElement>(registerTablist, userRef),\n [registerTablist, userRef],\n );\n\n return (\n <div\n aria-label={node.name ?? \"Tabs\"}\n {...props}\n aria-orientation=\"horizontal\"\n data-dashfoo=\"tablist\"\n onKeyDown={handleKeyDown}\n ref={refCallback}\n role=\"tablist\"\n style={{ ...tablistStyle, ...style }}\n tabIndex={-1}\n />\n );\n};\n\nexport { TabsetTablist, TabsetTabStrip };\nexport type { TabsetTablistProps, TabsetTabStripProps };\n","\"use client\";\n\nimport type { ComponentProps, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { mergeRefs } from \"../../lib/merge-refs\";\nimport { useTabsetDraggable } from \"../drag-adapter\";\nimport { GripIcon, MaximizeIcon } from \"../tabset-icons\";\n\nimport { useTabset } from \"./tabset-store\";\n\nconst toolbarStyle: CSSProperties = {\n alignItems: \"center\",\n display: \"flex\",\n flexShrink: 0,\n marginInlineStart: \"auto\",\n};\n\ntype TabsetToolbarProps = ComponentProps<\"div\">;\n\nconst TabsetToolbar = ({ style, ...props }: TabsetToolbarProps): ReactNode => (\n <div {...props} data-dashfoo=\"tabset-toolbar\" style={{ ...toolbarStyle, ...style }} />\n);\n\ntype TabsetGripProps = ComponentProps<\"button\">;\n\n// The whole tabset is draggable from its grip. It carries the active tab's name\n// so the drag overlay can label the chip. Self-hides when tabset dragging is off\n// or the tabset is maximized (nowhere to dock).\nconst TabsetGrip = ({ children, ref: userRef, ...props }: TabsetGripProps): ReactNode => {\n const activeTabName = useTabset((state) => state.activeTab?.name ?? \"\");\n const isMaximized = useTabset((state) => state.isMaximized);\n const node = useTabset((state) => state.node);\n const draggableTabsets = useLayout((state) => state.draggableTabsets);\n const hidden = !draggableTabsets || isMaximized;\n const { ref } = useTabsetDraggable(node.id, hidden, activeTabName);\n const refCallback = useMemo(() => mergeRefs<HTMLButtonElement>(ref, userRef), [ref, userRef]);\n\n if (hidden) {\n return null;\n }\n\n return (\n <button\n aria-label=\"Move tabset\"\n {...props}\n data-dashfoo=\"tabset-grip\"\n ref={refCallback}\n type=\"button\"\n >\n {children ?? <GripIcon />}\n </button>\n );\n};\n\ntype TabsetMaximizeButtonProps = ComponentProps<\"button\">;\n\nconst TabsetMaximizeButton = ({\n children,\n onClick,\n ...props\n}: TabsetMaximizeButtonProps): ReactNode => {\n const isMaximized = useTabset((state) => state.isMaximized);\n const showMaximize = useTabset((state) => state.showMaximize);\n const toggleMaximize = useTabset((state) => state.toggleMaximize);\n\n if (!showMaximize) {\n return null;\n }\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n toggleMaximize();\n };\n\n return (\n <button\n aria-label={isMaximized ? \"Restore\" : \"Maximize\"}\n {...props}\n aria-pressed={isMaximized}\n data-dashfoo=\"tabset-maximize\"\n onClick={handleClick}\n type=\"button\"\n >\n {children ?? <MaximizeIcon maximized={isMaximized} />}\n </button>\n );\n};\n\nexport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar };\nexport type { TabsetGripProps, TabsetMaximizeButtonProps, TabsetToolbarProps };\n","\"use client\";\n\nimport type { TabsetNode } from \"@dashfoo/core\";\nimport type { ReactNode } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\n\nimport { TabsetContent } from \"./tabset-content\";\nimport { TabsetOverflowMenu } from \"./tabset-overflow\";\nimport { TabsetRoot } from \"./tabset-root\";\nimport { useTabset } from \"./tabset-store\";\nimport { TabsetTab, TabsetTrigger } from \"./tabset-tab\";\nimport { TabsetCloseButton, TabsetRenameInput } from \"./tabset-tab-controls\";\nimport { TabsetTablist, TabsetTabStrip } from \"./tabset-tablist\";\nimport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar } from \"./tabset-toolbar\";\n\n// The canonical composition of the Tabset parts — what DashfooLayout and\n// Layout.Rows render by default, and the reference for anyone composing the\n// parts by hand. Lives inside Tabset.Root because the toolbar-visibility\n// condition needs the store's overflow count.\nconst DefaultTabsetLayout = (): ReactNode => {\n const draggableTabsets = useLayout((state) => state.draggableTabsets);\n const renderTabsetToolbar = useLayout((state) => state.renderTabsetToolbar);\n const tabLocation = useLayout((state) => state.tabLocation);\n const tabStripEnabled = useLayout((state) => state.tabStripEnabled);\n const isMaximized = useTabset((state) => state.isMaximized);\n const node = useTabset((state) => state.node);\n const overflowCount = useTabset((state) => state.overflowItems.length);\n const showMaximize = useTabset((state) => state.showMaximize);\n\n // The toolbar exists only when it has content; each part also self-hides so\n // the condition and the parts can never disagree.\n const showToolbar =\n showMaximize ||\n renderTabsetToolbar !== undefined ||\n overflowCount > 0 ||\n (draggableTabsets && !isMaximized);\n\n const strip = tabStripEnabled ? (\n <TabsetTabStrip>\n <TabsetTablist>\n {node.children.map((tab) => (\n <TabsetTab key={tab.id} tab={tab}>\n <TabsetTrigger />\n <TabsetRenameInput />\n <TabsetCloseButton />\n </TabsetTab>\n ))}\n </TabsetTablist>\n {showToolbar ? (\n <TabsetToolbar>\n <TabsetOverflowMenu />\n <TabsetGrip />\n {renderTabsetToolbar?.(node)}\n <TabsetMaximizeButton />\n </TabsetToolbar>\n ) : null}\n </TabsetTabStrip>\n ) : null;\n\n const content = <TabsetContent />;\n\n return tabLocation === \"bottom\" ? (\n <>\n {content}\n {strip}\n </>\n ) : (\n <>\n {strip}\n {content}\n </>\n );\n};\n\ntype TabsetViewProps = { node: TabsetNode };\n\nconst TabsetView = ({ node }: TabsetViewProps): ReactNode => (\n <TabsetRoot node={node}>\n <DefaultTabsetLayout />\n </TabsetRoot>\n);\n\nexport { TabsetView };\nexport type { TabsetViewProps };\n","\"use client\";\n\nimport type { Dimension, RowNode, TabsetNode } from \"@dashfoo/core\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { Fragment, useLayoutEffect, useMemo, useRef } from \"react\";\nimport type { GroupImperativeHandle, Layout, Orientation } from \"react-resizable-panels\";\nimport { Group, Panel, Separator } from \"react-resizable-panels\";\n\nimport { useLayout } from \"../hooks/layout-store\";\n\nimport { TabsetView } from \"./tabset/tabset-view\";\n\n// This module is the resize adapter: the only place that imports\n// react-resizable-panels. It maps the model's responsive weights to rrp's\n// percentage layout and unit-typed min/max constraints to rrp sizes, and commits\n// a drag (on release) back to the document as an adjustSplit action.\n\nconst dimensionToSize = (dimension: Dimension): string => `${dimension.value}${dimension.unit}`;\nconst dimensionToPixels = (dimension: Dimension | undefined): number | undefined =>\n dimension?.unit === \"px\" ? dimension.value : undefined;\n\nconst groupStyle: CSSProperties = { display: \"flex\", flex: 1, minHeight: 0, minWidth: 0 };\nconst WEIGHT_EPSILON = 0.01;\n\ntype LayoutChild = RowNode[\"children\"][number];\n\nconst layoutFromWeights = (children: RowNode[\"children\"], total: number): Layout =>\n children.reduce<Layout>((layout, child) => {\n layout[child.id] = ((child.weight ?? 1) / total) * 100;\n return layout;\n }, {});\n\nconst layoutsMatch = (current: Layout, expected: Layout): boolean => {\n const currentIds = Object.keys(current);\n const expectedIds = Object.keys(expected);\n if (currentIds.length !== expectedIds.length) {\n return false;\n }\n return expectedIds.every(\n (id) => Math.abs((current[id] ?? Number.NaN) - expected[id]) <= WEIGHT_EPSILON,\n );\n};\n\nconst descendantMinSize = (\n child: LayoutChild,\n axis: RowNode[\"orientation\"],\n tabsetMinSize: number,\n): number | undefined => {\n const explicit = dimensionToPixels(child.min);\n if (explicit !== undefined) {\n return explicit;\n }\n if (child.min !== undefined) {\n return undefined;\n }\n if (child.type === \"tabset\") {\n return tabsetMinSize;\n }\n\n const childMinimums: Array<number> = [];\n for (const grandchild of child.children) {\n const minimum = descendantMinSize(grandchild, axis, tabsetMinSize);\n if (minimum === undefined) {\n return undefined;\n }\n childMinimums.push(minimum);\n }\n if (childMinimums.length === 0) {\n return undefined;\n }\n\n return child.orientation === axis\n ? childMinimums.reduce((sum, value) => sum + value, 0)\n : Math.max(...childMinimums);\n};\n\ntype RowViewProps = {\n node: RowNode;\n // Substitutes a custom tabset composition at every leaf of the split tree;\n // defaults to the stock TabsetView.\n renderTabset?: (node: TabsetNode) => ReactNode;\n};\n\nconst RowView = ({ node, renderTabset }: RowViewProps): ReactNode => {\n const dispatch = useLayout((state) => state.dispatch);\n const tabsetMinSize = useLayout((state) => state.tabsetMinSize);\n const orientation: Orientation = node.orientation === \"row\" ? \"horizontal\" : \"vertical\";\n const total = node.children.reduce((sum, child) => sum + (child.weight ?? 1), 0);\n const desiredLayout = useMemo(\n () => layoutFromWeights(node.children, total),\n [node.children, total],\n );\n\n // rrp fires onLayoutChanged once on mount with its measured layout. That is not\n // a user resize — committing it would rewrite the authored weights and push a\n // spurious undo entry — so the first call (per mount) is ignored.\n const measured = useRef(false);\n const groupRef = useRef<GroupImperativeHandle | null>(null);\n const syncing = useRef(false);\n\n useLayoutEffect(() => {\n const group = groupRef.current;\n if (!group) {\n return;\n }\n if (layoutsMatch(group.getLayout(), desiredLayout)) {\n return;\n }\n\n syncing.current = true;\n group.setLayout(desiredLayout);\n const frame = requestAnimationFrame(() => {\n syncing.current = false;\n });\n return () => {\n cancelAnimationFrame(frame);\n syncing.current = false;\n };\n }, [desiredLayout]);\n\n const handleLayoutChanged = (layout: Layout): void => {\n if (!measured.current) {\n measured.current = true;\n return;\n }\n if (syncing.current) {\n return;\n }\n const weights = node.children.map((child) => layout[child.id] ?? child.weight ?? 1);\n const weightsChanged = weights.some(\n (weight, index) => Math.abs(weight - (node.children[index]?.weight ?? 1)) > WEIGHT_EPSILON,\n );\n\n if (!weightsChanged) {\n return;\n }\n\n dispatch({\n rowId: node.id,\n type: \"adjustSplit\",\n weights,\n });\n };\n\n return (\n <Group\n data-dashfoo=\"row\"\n groupRef={groupRef}\n key={node.children.map((child) => child.id).join(\"|\")}\n onLayoutChanged={handleLayoutChanged}\n orientation={orientation}\n style={groupStyle}\n >\n {node.children.map((child, index) => {\n const percent = ((child.weight ?? 1) / total) * 100;\n let min = child.min ? dimensionToSize(child.min) : undefined;\n if (min === undefined) {\n const minimum = descendantMinSize(child, node.orientation, tabsetMinSize);\n min = minimum === undefined ? undefined : `${minimum}px`;\n }\n const max = child.max ? dimensionToSize(child.max) : undefined;\n\n return (\n <Fragment key={child.id}>\n {index > 0 ? <Separator data-dashfoo=\"splitter\" /> : null}\n <Panel defaultSize={`${percent}%`} id={child.id} maxSize={max} minSize={min}>\n {child.type === \"row\" ? (\n <RowView node={child} renderTabset={renderTabset} />\n ) : (\n (renderTabset?.(child) ?? <TabsetView node={child} />)\n )}\n </Panel>\n </Fragment>\n );\n })}\n </Group>\n );\n};\n\nexport { RowView };\nexport type { RowViewProps };\n","\"use client\";\n\nimport { DragProvider } from \"./drag-adapter\";\nimport { LayoutRoot } from \"./layout-root\";\nimport { RowView } from \"./row-view\";\nimport { TabsetView } from \"./tabset/tabset-view\";\n\n// Compound namespace for hand-built layouts, same pattern as Panel/Tabset:\n// Root provides the layout store, DragLayer opts the tree into drag-dock,\n// Rows renders the split tree, and Tabset is the stock tabset composition for\n// leaves that don't need custom chrome.\nconst Layout = {\n DragLayer: DragProvider,\n Root: LayoutRoot,\n Rows: RowView,\n Tabset: TabsetView,\n} as const;\n\nexport { Layout };\nexport type { DragProviderProps as LayoutDragLayerProps } from \"./drag-adapter\";\nexport type { LayoutRootProps } from \"./layout-root\";\nexport type { RowViewProps as LayoutRowsProps } from \"./row-view\";\nexport type { TabsetViewProps as LayoutTabsetProps } from \"./tabset/tabset-view\";\n","\"use client\";\n\nimport type { Action, Dashfoo, DockLocation, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport { findTabset } from \"@dashfoo/core\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { forwardRef, useCallback, useImperativeHandle, useMemo } from \"react\";\n\nimport type { PersistConfig, StorageAdapter } from \"../hooks/persistence\";\nimport { localStorageAdapter, usePersistence } from \"../hooks/persistence\";\nimport { useDashfooStore } from \"../hooks/store\";\n\nimport { Layout } from \"./layout\";\n\nconst DEFAULT_PERSIST_DEBOUNCE_MS = 300;\n\n// `persist` accepts a bare localStorage key or a full target (custom storage,\n// debounce). Controlled mode (a `model` prop, no `defaultModel`) skips it —\n// persistence implies the library owns the state.\ntype PersistInput = string | { debounceMs?: number; key: string; storage?: StorageAdapter };\n\nconst resolvePersist = (\n persist: PersistInput | undefined,\n hasDefaultModel: boolean,\n): PersistConfig | null => {\n if (persist === undefined || !hasDefaultModel) {\n return null;\n }\n if (typeof persist === \"string\") {\n return { debounceMs: DEFAULT_PERSIST_DEBOUNCE_MS, key: persist, storage: localStorageAdapter };\n }\n return {\n debounceMs: persist.debounceMs ?? DEFAULT_PERSIST_DEBOUNCE_MS,\n key: persist.key,\n storage: persist.storage ?? localStorageAdapter,\n };\n};\n\ntype TabComponent = ComponentType<{ node: TabNode }>;\n\n// Component names already warned about, so the dev warning fires at most once each.\nconst warnedComponents = new Set<string>();\n\n// Imperative API exposed via ref — lets a host drive the layout from a toolbar,\n// command palette, or keyboard shortcuts without rebuilding the store's plumbing.\ntype DashfooHandle = {\n addTab: (\n tab: TabNode,\n target: { index?: number; location?: DockLocation; targetId: string },\n ) => void;\n canRedo: () => boolean;\n canUndo: () => boolean;\n closeTab: (tabId: string) => void;\n dispatch: (action: Action) => void;\n getModel: () => Dashfoo;\n maximizeTabset: (tabsetId: string | null) => void;\n redo: () => void;\n renameTab: (tabId: string, name: string) => void;\n // Reset to the original defaultModel, clearing undo history and any persisted\n // copy. No-op in controlled mode (the host owns the model).\n resetLayout: () => void;\n selectTab: (tabsetId: string, index: number) => void;\n undo: () => void;\n};\n\ntype DashfooLayoutProps = {\n closableTabs?: boolean;\n components?: Record<string, TabComponent>;\n defaultModel?: Dashfoo;\n draggableTabsets?: boolean;\n factory?: (tab: TabNode) => ReactNode;\n keepMounted?: boolean;\n maximizable?: boolean;\n model?: Dashfoo;\n onAction?: (action: Action) => Action | null;\n onActiveTabsetChange?: (tabsetId: string | undefined) => void;\n onMaximizedTabsetChange?: (tabsetId: string | undefined) => void;\n onModelChange?: (model: Dashfoo, action?: Action) => void;\n persist?: PersistInput;\n renamableTabs?: boolean;\n renderTabLabel?: (tab: TabNode) => ReactNode;\n renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;\n};\n\n// The batteries-included component: owns the store (controlled or uncontrolled),\n// resolves tab content via a components registry or a factory, and assembles the\n// same Layout/Tabset primitives a hand-built layout would use.\n// forwardRef (not the React 19 ref-as-prop) keeps the React 18 peer working.\nconst DashfooLayout = forwardRef<DashfooHandle, DashfooLayoutProps>((props, ref): ReactNode => {\n const {\n closableTabs = true,\n components,\n defaultModel,\n draggableTabsets = true,\n factory,\n keepMounted = false,\n maximizable = true,\n model,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange,\n persist,\n renamableTabs = true,\n renderTabLabel,\n renderTabsetToolbar,\n } = props;\n\n const persistConfig = useMemo(\n () => resolvePersist(persist, defaultModel !== undefined),\n [persist, defaultModel],\n );\n const persistence = usePersistence(persistConfig, defaultModel);\n\n // The save side of persistence rides onModelChange; the consumer's own handler\n // still fires after it.\n const handleModelChange = useCallback(\n (next: Dashfoo, action?: Action): void => {\n persistence.save(next);\n onModelChange?.(next, action);\n },\n [onModelChange, persistence],\n );\n\n const store = useDashfooStore({\n defaultModel: persistence.initialModel ?? defaultModel,\n model,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange: handleModelChange,\n });\n\n useImperativeHandle(\n ref,\n () => ({\n addTab: (tab, target) =>\n store.dispatch({\n index: target.index,\n location: target.location ?? \"center\",\n tab,\n targetId: target.targetId,\n type: \"addNode\",\n }),\n canRedo: store.canRedo,\n canUndo: store.canUndo,\n closeTab: (tabId) => store.dispatch({ tabId, type: \"deleteTab\" }),\n dispatch: store.dispatch,\n getModel: () => store.model,\n maximizeTabset: (tabsetId) => store.dispatch({ tabsetId, type: \"setMaximizedTabset\" }),\n redo: store.redo,\n renameTab: (tabId, name) => store.dispatch({ name, tabId, type: \"renameTab\" }),\n resetLayout: () => {\n persistence.clear();\n if (defaultModel !== undefined) {\n store.setModel(defaultModel);\n }\n },\n selectTab: (tabsetId, index) => store.dispatch({ index, tabsetId, type: \"selectTab\" }),\n undo: store.undo,\n }),\n [defaultModel, persistence, store],\n );\n\n const renderTab = useCallback(\n (tab: TabNode): ReactNode => {\n if (factory) {\n return factory(tab);\n }\n const Component = components?.[tab.component];\n if (!Component) {\n if (components && !warnedComponents.has(tab.component)) {\n warnedComponents.add(tab.component);\n // A tab whose component is not registered renders nothing — surface it\n // once so the misconfiguration is not silent.\n // oxlint-disable-next-line no-console\n console.warn(`[dashfoo] no component registered for \"${tab.component}\"`);\n }\n return null;\n }\n return <Component node={tab} />;\n },\n [components, factory],\n );\n\n // A maximized tabset fills the frame on its own; otherwise the row tree renders.\n const maximized = store.model.maximizedTabsetId\n ? findTabset(store.model, store.model.maximizedTabsetId)\n : undefined;\n\n return (\n <Layout.Root\n closableTabs={closableTabs}\n dispatch={store.dispatch}\n draggableTabsets={draggableTabsets}\n keepMounted={keepMounted}\n maximizable={maximizable}\n model={store.model}\n renamableTabs={renamableTabs}\n renderTab={renderTab}\n renderTabLabel={renderTabLabel}\n renderTabsetToolbar={renderTabsetToolbar}\n >\n <Layout.DragLayer>\n {maximized ? <Layout.Tabset node={maximized} /> : <Layout.Rows node={store.model.layout} />}\n </Layout.DragLayer>\n </Layout.Root>\n );\n});\n\nDashfooLayout.displayName = \"DashfooLayout\";\n\nexport { DashfooLayout };\nexport type { DashfooHandle, DashfooLayoutProps, TabComponent };\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useInsertionEffect, useState } from \"react\";\n\nimport { createDragManager, SharedDragManagerContext } from \"../hooks/drag-hooks\";\n\n// Shares one DragDropManager between a DashfooLayout and external tab sources\n// (useExternalTabSource) rendered outside it, so a drag can start at a widget\n// list and end on the layout. Optional: a standalone DashfooLayout creates its\n// own manager and behaves as before.\nconst DashfooDragProvider = ({ children }: { children: ReactNode }): ReactNode => {\n // useState lazily constructs the manager once; the destroy rides a\n // useInsertionEffect cleanup (not useEffect) so StrictMode's simulated unmount\n // doesn't tear down the live instance — the same pattern DragProvider uses.\n const [manager] = useState(createDragManager);\n useInsertionEffect(() => () => manager.destroy(), [manager]);\n\n return (\n <SharedDragManagerContext.Provider value={manager}>\n {children}\n </SharedDragManagerContext.Provider>\n );\n};\n\nexport { DashfooDragProvider };\n","\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\n\ntype PanelRootProps = ComponentProps<\"div\">;\ntype PanelHeaderProps = ComponentProps<\"div\">;\ntype PanelBodyProps = ComponentProps<\"div\">;\ntype PanelTitleProps = ComponentProps<\"span\">;\ntype PanelIconProps = ComponentProps<\"span\">;\ntype PanelBadgeProps = ComponentProps<\"span\">;\n\nconst PanelRoot = (props: PanelRootProps): ReactNode => <div {...props} data-dashfoo=\"panel\" />;\nconst PanelHeader = (props: PanelHeaderProps): ReactNode => (\n <div {...props} data-dashfoo=\"panel-header\" />\n);\nconst PanelBody = (props: PanelBodyProps): ReactNode => (\n <div {...props} data-dashfoo=\"panel-body\" />\n);\nconst PanelTitle = (props: PanelTitleProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-title\" />\n);\nconst PanelIcon = (props: PanelIconProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-icon\" />\n);\nconst PanelBadge = (props: PanelBadgeProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-badge\" />\n);\n\nconst Panel = {\n Badge: PanelBadge,\n Body: PanelBody,\n Header: PanelHeader,\n Icon: PanelIcon,\n Root: PanelRoot,\n Title: PanelTitle,\n} as const;\n\nexport { Panel };\nexport type {\n PanelBadgeProps,\n PanelBodyProps,\n PanelHeaderProps,\n PanelIconProps,\n PanelRootProps,\n PanelTitleProps,\n};\n","\"use client\";\n\nimport { TabsetContent } from \"./tabset-content\";\nimport { TabsetOverflowMenu } from \"./tabset-overflow\";\nimport { TabsetRoot } from \"./tabset-root\";\nimport { TabsetTab, TabsetTrigger } from \"./tabset-tab\";\nimport { TabsetCloseButton, TabsetRenameInput } from \"./tabset-tab-controls\";\nimport { TabsetTablist, TabsetTabStrip } from \"./tabset-tablist\";\nimport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar } from \"./tabset-toolbar\";\n\n// Compound namespace, same pattern as Panel: users compose the parts freely and\n// they coordinate through the scoped store Tabset.Root provides.\nconst Tabset = {\n CloseButton: TabsetCloseButton,\n Content: TabsetContent,\n Grip: TabsetGrip,\n MaximizeButton: TabsetMaximizeButton,\n OverflowMenu: TabsetOverflowMenu,\n RenameInput: TabsetRenameInput,\n Root: TabsetRoot,\n Tab: TabsetTab,\n Tablist: TabsetTablist,\n TabStrip: TabsetTabStrip,\n Toolbar: TabsetToolbar,\n Trigger: TabsetTrigger,\n} as const;\n\nexport { Tabset };\nexport type { TabsetContentProps } from \"./tabset-content\";\nexport type { TabsetRootProps } from \"./tabset-root\";\nexport type { TabsetTabProps, TabsetTriggerProps } from \"./tabset-tab\";\nexport type { TabsetCloseButtonProps, TabsetRenameInputProps } from \"./tabset-tab-controls\";\nexport type { TabsetTablistProps, TabsetTabStripProps } from \"./tabset-tablist\";\nexport type {\n TabsetGripProps,\n TabsetMaximizeButtonProps,\n TabsetToolbarProps,\n} from \"./tabset-toolbar\";\n","\"use client\";\n\nimport type { Dashfoo } from \"@dashfoo/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\ntype Breakpoint = {\n id: string;\n model: Dashfoo;\n query?: { maxWidth: number } | { media: string };\n};\n\ntype UseResponsiveModelOptions = { breakpoints: Array<Breakpoint> };\n\ntype ResponsiveModel = {\n breakpoint: string;\n containerRef: (element: HTMLElement | null) => void;\n defaultModel: Dashfoo;\n key: string;\n};\n\nconst hasMatchMedia = (): boolean =>\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n\n// Whether a breakpoint applies at a given container width. No query = catch-all;\n// a maxWidth matches at or below the width; a media query consults matchMedia.\nconst matchBreakpoint = (breakpoint: Breakpoint, width: number): boolean => {\n const { query } = breakpoint;\n if (!query) {\n return true;\n }\n if (\"maxWidth\" in query) {\n return width <= query.maxWidth;\n }\n return hasMatchMedia() && window.matchMedia(query.media).matches;\n};\n\n// The first breakpoint that matches, else the last (the expected catch-all).\nconst activeBreakpoint = (breakpoints: Array<Breakpoint>, width: number): Breakpoint => {\n const fallback = breakpoints.at(-1);\n if (!fallback) {\n throw new Error(\"useResponsiveModel requires at least one breakpoint.\");\n }\n return breakpoints.find((breakpoint) => matchBreakpoint(breakpoint, width)) ?? fallback;\n};\n\n// Picks a model for the active breakpoint, keyed off the container's own width\n// (ResizeObserver) and/or media queries. Spread the result onto DashfooLayout\n// and remount on `key`.\nconst useResponsiveModel = ({ breakpoints }: UseResponsiveModelOptions): ResponsiveModel => {\n // seed the catch-all width so server and client agree on the first render\n // (no hydration mismatch); the ResizeObserver supplies the real container\n // width on mount. innerWidth is the viewport, not our container, so it would\n // pick the wrong breakpoint and thrash the DashfooLayout key={active.id} remount.\n const [width, setWidth] = useState<number>(Number.POSITIVE_INFINITY);\n const [, setMediaTick] = useState(0);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const containerRef = useCallback((element: HTMLElement | null): void => {\n observerRef.current?.disconnect();\n if (!element) {\n return;\n }\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry) {\n setWidth(entry.contentRect.width);\n }\n });\n observer.observe(element);\n observerRef.current = observer;\n }, []);\n\n useEffect(\n () => () => {\n observerRef.current?.disconnect();\n },\n [],\n );\n\n useEffect(() => {\n if (!hasMatchMedia()) {\n return;\n }\n const lists = breakpoints.flatMap((breakpoint) =>\n breakpoint.query && \"media\" in breakpoint.query\n ? [window.matchMedia(breakpoint.query.media)]\n : [],\n );\n if (lists.length === 0) {\n return;\n }\n const handleChange = (): void => {\n setMediaTick((tick) => tick + 1);\n };\n for (const list of lists) {\n list.addEventListener(\"change\", handleChange);\n }\n return () => {\n for (const list of lists) {\n list.removeEventListener(\"change\", handleChange);\n }\n };\n }, [breakpoints]);\n\n const active = activeBreakpoint(breakpoints, width);\n return { breakpoint: active.id, containerRef, defaultModel: active.model, key: active.id };\n};\n\nexport { activeBreakpoint, matchBreakpoint, useResponsiveModel };\nexport type { Breakpoint, ResponsiveModel, UseResponsiveModelOptions };\n"],"mappings":"01BAcA,MAAM,OAA6C,CACjD,IAAM,EAAQ,IAAI,IAClB,MAAO,CACL,QAAU,GAAQ,EAAM,IAAI,CAAG,GAAK,KACpC,WAAa,GAAQ,CACnB,EAAM,OAAO,CAAG,CAClB,EACA,SAAU,EAAK,IAAU,CACvB,EAAM,IAAI,EAAK,CAAK,CACtB,CACF,CACF,EAIM,EAAsC,CAC1C,QAAU,GAAQ,CAChB,GAAI,OAAO,OAAW,IACpB,OAAO,KAET,GAAI,CACF,OAAO,OAAO,aAAa,QAAQ,CAAG,CACxC,MAAQ,CACN,OAAO,IACT,CACF,EACA,WAAa,GAAQ,CACf,YAAO,OAAW,KAGtB,GAAI,CACF,OAAO,aAAa,WAAW,CAAG,CACpC,MAAQ,CAER,CACF,EACA,SAAU,EAAK,IAAU,CACnB,YAAO,OAAW,KAGtB,GAAI,CACF,OAAO,aAAa,QAAQ,EAAK,CAAK,CACxC,OAAS,EAAO,CAGd,QAAQ,KAAK,qCAAsC,CAAK,CAC1D,CACF,CACF,EAuBM,IACJ,EACA,IACgB,CAGhB,IAAM,EAAY,EAAO,CAAM,EAC/B,MAAgB,CACd,EAAU,QAAU,CACtB,CAAC,EAID,GAAM,CAAC,GAAgB,MAAoC,CACzD,GAAI,IAAW,MAAQ,IAAiB,IAAA,GACtC,OAAO,EAET,IAAM,EAAM,EAAO,QAAQ,QAAQ,EAAO,GAAG,EAC7C,GAAI,IAAQ,KACV,OAAO,EAET,GAAI,CACF,OAAO,EAAS,CAAG,CACrB,MAAQ,CACN,OAAO,CACT,CACF,CAAC,EAED,MAAgB,CACd,IAAM,EAAU,EAAU,QAC1B,GAAI,IAAY,KACd,OAEF,IAAM,EAAM,EAAQ,QAAQ,QAAQ,EAAQ,GAAG,EAC3C,OAAQ,KAGZ,GAAI,CACF,EAAS,CAAG,CACd,OAAS,EAAO,CAGd,QAAQ,KAAK,mDAAoD,CAAK,EACtE,EAAQ,QAAQ,WAAW,EAAQ,GAAG,CACxC,CACF,EAAG,CAAC,CAAC,EAKL,IAAM,EAAY,EAAO,GAAQ,GAAG,EACpC,MAAgB,CACV,IAAW,MAAQ,EAAO,MAAQ,EAAU,UAE9C,QAAQ,KACN,2JACF,EACA,EAAU,QAAU,EAAO,IAE/B,EAAG,CAAC,CAAM,CAAC,EAEX,IAAM,EAAQ,EAA6C,IAAI,EAIzD,EAAU,EAAuE,IAAI,EAErF,EAAQ,MAAwB,CAChC,EAAM,UAAY,OACpB,aAAa,EAAM,OAAO,EAC1B,EAAM,QAAU,MAEd,EAAQ,UAAY,OACtB,EAAQ,QAAQ,QAAQ,QAAQ,EAAQ,QAAQ,IAAK,EAAQ,QAAQ,KAAK,EAC1E,EAAQ,QAAU,KAEtB,EAAG,CAAC,CAAC,EAEC,EAAO,EACV,GAAwB,CACvB,IAAM,EAAU,EAAU,QACtB,IAAY,OAGhB,EAAQ,QAAU,CAAE,IAAK,EAAQ,IAAK,QAAS,EAAQ,QAAS,MAAO,EAAO,CAAI,CAAE,EAChF,EAAM,UAAY,MACpB,aAAa,EAAM,OAAO,EAE5B,EAAM,QAAU,WAAW,EAAO,EAAQ,UAAU,EACtD,EACA,CAAC,CAAK,CACR,EAqCA,OA9BA,MAAgB,CACd,IAAM,MAA6B,CACjC,EAAM,CACR,EACM,MAAqC,CACrC,SAAS,kBAAoB,UAC/B,EAAM,CAEV,EAGA,OAFA,OAAO,iBAAiB,WAAY,CAAc,EAClD,SAAS,iBAAiB,mBAAoB,CAAsB,MACvD,CACX,OAAO,oBAAoB,WAAY,CAAc,EACrD,SAAS,oBAAoB,mBAAoB,CAAsB,EACvE,EAAM,CACR,CACF,EAAG,CAAC,CAAK,CAAC,EAcH,CAAE,MAZK,MAAwB,CAChC,EAAM,UAAY,OACpB,aAAa,EAAM,OAAO,EAC1B,EAAM,QAAU,MAElB,EAAQ,QAAU,KAClB,IAAM,EAAU,EAAU,QACtB,IAAY,MACd,EAAQ,QAAQ,WAAW,EAAQ,GAAG,CAE1C,EAAG,CAAC,CAES,EAAG,eAAc,MAAK,CACrC,ECvLM,GAAmB,GAAkD,CACzE,GAAM,CACJ,eACA,MAAO,EACP,WACA,uBACA,0BACA,iBACE,EACE,EAAe,GAAmB,EACxC,GAAI,IAAiB,IAAA,GACnB,MAAU,MAAM,gEAAgE,EAMlF,IAAM,EAAW,EAAY,EAAgB,CAAE,MAAO,CAAE,MAAO,EAAU,CAAY,CAAE,CAAE,CAAC,EACpF,EAAU,EAAY,EAAW,GAAa,EAAS,QAAQ,OAAO,EAE5E,MAAgB,CACV,IAAoB,IAAA,IACtB,EAAS,KAAK,CAAE,MAAO,EAAU,CAAe,EAAG,KAAM,WAAY,CAAC,CAE1E,EAAG,CAAC,EAAU,CAAe,CAAC,EAE9B,IAAM,EAAQ,GAAmB,EAAQ,QAMnC,EAAS,GACZ,EAAiB,EAAgB,IAAoB,CAChD,OAAO,GAAG,EAAQ,CAAK,IAG3B,IAAgB,EAAO,CAAM,EACzB,EAAM,iBAAmB,EAAO,gBAClC,IAAuB,EAAM,cAAc,EAEzC,EAAM,oBAAsB,EAAO,mBACrC,IAA0B,EAAM,iBAAiB,EAErD,EACA,CAAC,EAAe,EAAsB,CAAuB,CAC/D,EAEM,EAAW,EACd,GAAmB,CAElB,IAAM,EAAW,EAAW,EAAS,CAAM,EAAI,EAC/C,GAAI,CAAC,EACH,OAKF,IAAM,OAAyD,CAC7D,GAAI,IAAoB,IAAA,GAAW,CACjC,IAAM,EAAS,EAAS,YAAY,EAAE,QAAQ,QAAQ,QAEtD,OADA,EAAS,KAAK,CAAE,OAAQ,EAAU,KAAM,UAAW,CAAC,EAC7C,CAAE,MAAO,EAAS,YAAY,EAAE,QAAQ,QAAQ,QAAS,QAAO,CACzE,CACA,MAAO,CAAE,MAAO,EAAQ,EAAiB,CAAQ,EAAG,OAAQ,EAAU,CAAe,CAAE,CACzF,GAAG,EACH,EAAO,EAAW,OAAQ,EAAW,MAAO,CAAQ,CACtD,EACA,CAAC,EAAU,EAAiB,EAAQ,CAAQ,CAC9C,EAEM,EAAO,MAAkB,CAG7B,GAAI,IAAoB,IAAA,GACtB,OAEF,IAAM,EAAS,EAAS,YAAY,EAAE,QAAQ,QAAQ,QACtD,EAAS,KAAK,CAAE,KAAM,MAAO,CAAC,EAC9B,IAAM,EAAQ,EAAS,YAAY,EAAE,QAAQ,QAAQ,QACrD,EAAO,EAAQ,CAAK,CACtB,EAAG,CAAC,EAAU,EAAiB,CAAM,CAAC,EAyBtC,MAAO,CACL,YAAe,EAAQ,EAAS,YAAY,EAAE,QAAQ,OAAO,EAC7D,YAAe,EAAQ,EAAS,YAAY,EAAE,QAAQ,OAAO,EAC7D,WACA,QACA,KA5BW,MAAkB,CAC7B,GAAI,IAAoB,IAAA,GACtB,OAEF,IAAM,EAAS,EAAS,YAAY,EAAE,QAAQ,QAAQ,QACtD,EAAS,KAAK,CAAE,KAAM,MAAO,CAAC,EAC9B,IAAM,EAAQ,EAAS,YAAY,EAAE,QAAQ,QAAQ,QACrD,EAAO,EAAQ,CAAK,CACtB,EAAG,CAAC,EAAU,EAAiB,CAAM,CAoBhC,EACH,SAhBe,EACd,GAAkB,CACjB,EAAS,KAAK,CAAE,MAAO,EAAU,CAAI,EAAG,KAAM,WAAY,CAAC,CAC7D,EACA,CAAC,CAAQ,CAYF,EACP,MACF,CACF,ECrHM,IAAqB,EAAqB,IAA+B,CAC7E,GAAM,CAAE,UAAW,EACnB,GAAI,EAAE,aAAkB,SACtB,MAAO,GAET,IAAM,EAAc,EAAO,QAAQ;;;;;;;CAAoB,EACvD,OAAO,IAAgB,MAAQ,IAAgB,EAAO,OACxD,EAOM,OACJ,IAAI,EAAgB,CAClB,QAAU,GACR,EAAS,OAAQ,GAAW,IAAW,GAAiB,IAAW,EAAQ,EAC7E,YAAe,CAAC,GAAc,UAAU,CAAE,oBAAkB,CAAC,CAAC,CAChE,CAAC,EAOG,EAAc,EAAuC,IAAI,EAKzD,EACJ,EAAsC,IAAI,EAUtC,OACJ,OAAqC,CAAE,QAAS,IAAK,EAAE,EAInD,GAAmB,GAAuB,EAE1C,GACJ,EAAuC,IAAI,EAUvC,OAA4C,CAChD,IAAM,EAAa,EAAuB,IAAI,EACxC,EAAe,EAAyB,IAAI,EAOlD,MAAO,CAAE,eAAc,aAAY,IANvB,EAAa,GAAkC,CACzD,EAAW,QAAU,EACjB,EAAa,UACf,EAAa,QAAQ,QAAU,GAAW,IAAA,GAE9C,EAAG,CAAC,CACiC,CAAE,CACzC,EAiBM,GACJ,EACA,EACA,IAC+C,CAC/C,IAAM,EAAU,EAAW,CAAW,EAGhC,EAAgB,EAAW,CAAwB,EACnD,EAAU,GAAS,SAAW,EAC9B,CAAE,eAAc,aAAY,OAAQ,GAAmB,EACvD,CAAE,OAAM,MAAO,EAsBrB,OApBA,MAAgB,CACd,GAAI,CAAC,GAAW,EACd,OAEF,IAAM,EAAY,IAAI,EAAU,CAAE,KAAM,CAAE,GAAG,EAAM,MAAO,EAAG,EAAG,IAAG,EAAG,CAAO,EAG7E,MAFA,GAAU,QAAU,EAAW,SAAW,IAAA,GAC1C,EAAa,QAAU,MACV,CACX,EAAU,QAAQ,EAClB,EAAa,QAAU,IACzB,CACF,EAAG,CAAC,EAAM,EAAU,EAAc,EAAY,EAAI,CAAO,CAAC,EAE1D,MAAgB,CACd,IAAM,EAAY,EAAa,QAC3B,IACF,EAAU,KAAO,CAAE,GAAG,EAAM,OAAM,EAEtC,EAAG,CAAC,EAAM,EAAc,CAAK,CAAC,EAEvB,CAAE,KAAI,CACf,EAEM,IACJ,EACA,EAAW,GACX,EAAQ,KAGD,EAAmB,CAAE,KADf,OAAe,CAAE,KAAM,KAAM,GAAI,CAAC,CAChB,EAAG,GAAI,CAAM,EAAG,EAAU,CAAK,EAM1D,IACJ,EACA,EAAW,GACX,EAAQ,KAGD,EAAmB,CAAE,KADf,OAAe,CAAE,WAAU,KAAM,QAAS,GAAI,CAAC,CAAQ,CACrC,EAAG,GAAI,QAAQ,GAAW,EAAG,EAAU,CAAK,EAIvE,GAAsB,GAAqE,CAC/F,IAAM,EAAU,EAAW,CAAW,EAOtC,MAAO,CAAE,IANG,EACT,GAAsC,CACrC,GAAS,eAAe,EAAU,CAAO,CAC3C,EACA,CAAC,EAAS,CAAQ,CAET,CAAE,CACf,EAeM,IAAwB,CAC5B,YACA,WAAW,GACX,QAAQ,MACkE,CAC1E,IAAM,EAAK,EAAM,EACX,EAAe,EAAO,CAAS,EASrC,OAPA,MAAgB,CACd,EAAa,QAAU,CACzB,CAAC,EAKM,EAAmB,CAAE,KAJf,OACJ,CAAE,cAA0B,EAAa,QAAQ,EAAG,KAAM,UAAW,GAC5E,CAAC,CAE4B,EAAG,GAAI,YAAY,GAAK,EAAG,EAAU,CAAK,CAC3E,EAEM,MAEG,EADO,EAAW,EACL,GAAK,GAAmB,GAAU,EAAM,OAAO,EC3L/D,GAAqB,GACzB,MAA+B,CAAO,EAGlC,EAAqB,EAAkC,IAAI,EAE3D,EAAgB,GAA2C,CAC/D,IAAM,EAAQ,EAAW,CAAkB,EAC3C,GAAI,IAAU,KACZ,MAAU,MAAM,8EAA8E,EAEhG,OAAO,EAAS,EAAO,CAAQ,CACjC,EClCM,GAAe,EAAc,IACjC,EAAM,GAAK,EAAK,GAChB,EAAM,GAAK,EAAK,EAAI,EAAK,OACzB,EAAM,GAAK,EAAK,GAChB,EAAM,GAAK,EAAK,EAAI,EAAK,OAIrB,IAAkB,EAA4B,IAA6B,CAC/E,IAAM,EAAQ,EAAM,UAAW,GAAS,EAAW,EAAK,EAAI,EAAK,MAAQ,CAAC,EAC1E,OAAO,IAAU,GAAK,EAAM,OAAS,CACvC,EAUM,IACJ,EACA,EACA,EACA,IACS,CACT,IAAM,EAAK,EAAU,GACf,EAAO,EAAU,GAAG,EAAE,EACtB,EAAS,GAAM,EACf,EAAI,GAAI,IAAM,EAAO,EAAK,EAAI,EAAK,MAAQ,EAAU,GACrD,EAAQ,KAAK,IAAI,EAAQ,MAAO,EAAU,KAAK,EAC/C,EAAS,GAAQ,QAAU,KAAK,IAAI,EAAQ,OAAQ,EAAU,MAAM,EAC1E,MAAO,CACL,SACA,QACA,EAAG,KAAK,IAAI,EAAU,EAAG,KAAK,IAAI,EAAG,EAAU,EAAI,EAAU,MAAQ,CAAK,CAAC,EAC3E,EAAG,GAAQ,GAAK,EAAU,EAAI,EAAU,OAAS,CACnD,CACF,EAKM,IACJ,EACA,EACA,IAKA,EAHI,IAAc,QAAQ,KAGtB,EAAO,SAAW,GAAK,EAAO,KAAO,GC1DrC,GAAS,IAAI,IAEb,GAAY,EAAa,IAA0B,CACnD,GAAO,IAAI,CAAG,IAGlB,GAAO,IAAI,CAAG,EAEd,QAAQ,KAAK,aAAa,GAAS,EACrC,ECKM,GAA6B,CACjC,UAAW,aACX,cAAe,OACf,SAAU,QAEV,WAAY,+EACZ,OAAQ,IACV,EAKM,GAAa,IAA+B,CAChD,GAAG,GACH,WAAY,oDACZ,OACE,kGACF,aAAc,kCACd,OAAQ,EAAK,OACb,KAAM,EAAK,EACX,IAAK,EAAK,EACV,MAAO,EAAK,KACd,GAKM,GAAc,IAA+B,CACjD,GAAG,GAAU,CAAI,EACjB,aAAc,iEAChB,GAKM,IAAgB,EAAgB,IACpC,CAAC,GAAG,EAAM,iBAA8B,2BAA2B,CAAC,EAAE,QAAS,GAC7E,EAAK,cAA2B,sBAAsB,GAAG,QAAQ,QAAU,EACvE,CAAC,EACD,CAAC,EAAK,sBAAsB,CAAC,CACnC,EAII,GAAsB,CAAE,OAAQ,GAAI,MAAO,EAAG,EAI9C,IACJ,EACA,IAEI,IAGA,EAAU,OAAS,EACd,CACL,OAAQ,EAAU,QAAQ,EAAK,IAAS,EAAM,EAAK,OAAQ,CAAC,EAAI,EAAU,OAC1E,MAAO,EAAU,QAAQ,EAAK,IAAS,EAAM,EAAK,MAAO,CAAC,EAAI,EAAU,MAC1E,EAEK,IAOH,IAAiB,CACrB,WACA,gBACA,sBAKe,CACf,IAAM,EAAS,EAAY,EAAW,GAAa,EAAS,QAAQ,MAAM,EACpE,EAAY,EAAY,EAAW,GAAa,EAAS,QAAQ,SAAS,EAAE,EAClF,GAAI,CAAC,EACH,OAAO,KAET,IAAM,EAAU,EAAiB,EAAO,QAAQ,EAChD,GAAI,CAAC,EACH,OAAO,KAET,GAAI,EAAO,WAAa,SAAU,CAChC,IAAM,EAAQ,EAAQ,cAAc,2BAA2B,EAC/D,GAAI,EAAO,CACT,IAAM,EAAQ,GAAa,EAAO,CAAS,EAO3C,OAAO,EAAC,MAAD,CAAK,eAAa,iBAAiB,MAAO,GANnC,GACZ,EAAM,sBAAsB,EAC5B,EACA,EAAO,OAAS,EAChB,GAAU,EAAc,EAAG,CAAK,CAE8B,CAAC,CAAI,CAAA,CACvE,CACF,CAEA,OAAO,EAAC,MAAD,CAAK,eAAa,iBAAiB,MAAO,GADpC,EAAS,EAAQ,sBAAsB,EAAG,EAAO,QACA,CAAC,CAAI,CAAA,CACrE,EAEM,GAA8B,CAAE,KAAM,EAAG,SAAU,QAAS,IAAK,EAAG,OAAQ,IAAK,EAKjF,IAAe,CACnB,aACA,aAKK,EAIH,EAAC,MAAD,CACE,eAAa,eACb,IAAK,EACL,MAAO,CAAE,GAAG,GAAc,UAAW,aAAa,EAAQ,EAAE,MAAM,EAAQ,EAAE,IAAK,WAEhF,EAAQ,KACN,CAAA,EATE,KCtFL,IAAY,EAAgB,IAChC,CAAC,GAAG,EAAM,iBAA8B,sBAAsB,CAAC,EAAE,QAAS,GACxE,EAAI,QAAQ,QAAU,EAAY,CAAC,EAAI,CAAC,EAAI,sBAAsB,CAAC,CACrE,EAMI,IACJ,EACA,EACA,EACA,IACe,CACf,IAAM,EAAQ,EAAQ,cAAc,2BAA2B,EAC/D,GAAI,GAAS,EAAY,EAAO,EAAM,sBAAsB,CAAC,EAC3D,MAAO,CACL,MAAO,GAAe,GAAS,EAAO,CAAS,EAAG,EAAM,CAAC,EACzD,SAAU,SACV,SAAU,CACZ,EAEF,IAAM,EAAS,EAAkB,EAAO,EAAQ,sBAAsB,CAAC,EACjE,EAAyB,EAAO,OAAS,MAAQ,SAAW,SAAS,EAAO,OAIlF,OAHI,IAAa,UAAY,EACpB,CAAE,MAAO,GAAS,EAAO,CAAS,EAAE,OAAQ,WAAU,SAAU,CAAG,EAErE,CAAE,WAAU,SAAU,CAAG,CAClC,EAGM,EAAwB,CAAE,EAAG,GAAI,EAAG,CAAE,EAEtC,GAAW,GAA8D,CAC7E,IAAM,EAAM,GAAQ,MAAM,MAC1B,OAAO,OAAO,GAAQ,SAAW,EAAM,EACzC,EAEM,GAAgB,GAA2C,OAAO,GAAU,WAI5E,GAAgB,GAA+D,CACnF,GAAI,CAAC,EACH,OAGF,IAAM,GADO,EAAQ,QAAQ,2BAA2B,GAAK,GAC3C,sBAAsB,EACxC,OAAO,EAAK,MAAQ,GAAK,EAAK,OAAS,EAAI,CAAE,OAAQ,EAAK,OAAQ,MAAO,EAAK,KAAM,EAAI,IAAA,EAC1F,EAMM,GAAc,GAGM,CACxB,IAAM,EAAO,EAAO,KACpB,GAAI,GAAM,OAAS,SACjB,MAAO,CAAE,GAAI,OAAO,EAAK,QAAQ,EAAG,KAAM,QAAS,EAErD,GAAI,GAAM,OAAS,WAAY,CAC7B,GAAI,CAAC,GAAa,EAAK,SAAS,EAI9B,OADA,QAAQ,KAAK,kEAAkE,EACxE,KAET,IAAI,EACJ,GAAI,CACF,EAAY,EAAK,UAAU,CAC7B,OAAS,EAAO,CAId,OADA,QAAQ,KAAK,iDAAkD,CAAK,EAC7D,IACT,CACA,IAAM,EAAS,EAAc,UAAU,CAAS,EAMhD,OALK,EAAO,QAKL,CAAE,GAAI,OAAO,EAAO,EAAE,EAAG,KAAM,WAAY,IAAK,EAAO,IAAK,GAHjE,QAAQ,KAAK,yDAA0D,EAAO,KAAK,EAC5E,KAGX,CACA,MAAO,CAAE,GAAI,OAAO,EAAO,EAAE,EAAG,KAAM,KAAM,CAC9C,EAUM,IAAgB,CAAE,WAAU,WAAU,eAA8C,CACxF,IAAM,EAAW,EAAY,CAAe,EAGtC,EAAc,EAAW,CAAkB,EAC3C,EAAU,EAAO,IAAI,GAA0B,EAC/C,EAAa,EAA8B,IAAI,EAG/C,EAAa,EAA8B,IAAA,EAAS,EACpD,CAAC,EAAS,GAAc,EAAkC,IAAI,EAQ9D,EAAgB,EAAW,CAAwB,EACnD,CAAC,GAAc,MAAgB,EAAgB,KAAO,GAAkB,CAAE,EAChF,UAA+B,GAAY,QAAQ,EAAG,CAAC,CAAU,CAAC,EAClE,IAAM,EAAU,GAAiB,EACjC,GAAI,IAAY,KAGd,MAAU,MACR,qIACF,EAMF,GAAM,CAAC,GAAgB,MAAe,GAAuB,CAAC,EAC9D,MAAgB,CACd,IAAM,EAAe,EAAS,UAAW,GAAa,CACpD,IAAM,EAAU,EAAS,QAAQ,QAC7B,EAAa,SAAS,EAAE,UAAY,GACtC,EAAa,SAAS,CAAE,SAAQ,CAAC,CAErC,CAAC,EACD,UAAa,CACX,EAAa,YAAY,CAC3B,CACF,EAAG,CAAC,EAAU,CAAY,CAAC,EAE3B,MAAgB,CACd,IAAM,EAAe,EAAS,GAAG,SAAW,GAAY,CACtD,IAAM,EAAS,GAAY,GAAa,SAAS,EAAE,SACnD,GAAI,CAAC,EAAQ,CACX,EACE,uBACA,+FACF,EACA,MACF,CACA,EAAO,EAAQ,MAAM,CACvB,CAAC,EACD,UAAa,CACX,EAAa,YAAY,CAC3B,CACF,EAAG,CAAC,EAAU,EAAa,CAAQ,CAAC,EAEpC,IAAM,EAAiB,GAAa,EAAY,IAAsC,CAChF,EACF,EAAQ,QAAQ,IAAI,EAAI,CAAO,EAE/B,EAAQ,QAAQ,OAAO,CAAE,CAE7B,EAAG,CAAC,CAAC,EAEC,EAAmB,EAAa,GAAe,EAAQ,QAAQ,IAAI,CAAE,EAAG,CAAC,CAAC,EAE1E,EAAgB,MAAkB,EAAW,QAAS,CAAC,CAAC,EAKxD,EAAW,EAAa,GAAmE,CAC/F,IAAK,GAAM,CAAC,EAAI,KAAY,EAAQ,QAClC,GAAI,EAAY,EAAO,EAAQ,sBAAsB,CAAC,EACpD,MAAO,CAAE,UAAS,IAAG,CAI3B,EAAG,CAAC,CAAC,EAKC,EAAgB,GAElB,EACA,EACA,EACA,IACsB,CAItB,GAAI,CAAC,GAAgB,EAAW,EAHjB,CAAC,GAAG,EAAQ,iBAA8B,sBAAsB,CAAC,EAAE,IAC/E,GAAQ,EAAI,QAAQ,OAAS,EAEe,CAAC,EAC9C,OAAO,KAET,IAAM,EAAS,GAAgB,EAAU,EAAS,EAAO,CAAS,EAQlE,MAHI,EADsB,GAAa,GAAa,SAAS,EAAE,WAAa,KAClD,EAAO,SAAS,WAAW,QAAQ,EACpD,CAAE,SAAU,SAAU,UAAS,EAEjC,CACT,EACA,CAAC,EAAa,CAAS,CACzB,EAIM,EAAkB,EAAa,GAAuB,CAC1D,IAAM,EAAU,EAAW,QACvB,IACF,EAAQ,MAAM,UAAY,aAAa,EAAM,EAAI,EAAe,EAAE,MAAM,EAAM,EAAI,EAAe,EAAE,KAEvG,EAAG,CAAC,CAAC,EAEC,EAAgB,EAAa,GAAyC,CAC1E,EAAW,QAAU,CACvB,EAAG,CAAC,CAAC,EAEL,MAAgB,CACd,IAAM,EAAe,GAAgC,CACnD,IAAM,EAAS,EAAM,UAAU,OAC/B,GAAI,CAAC,EACH,OAEF,IAAM,EAAU,GAAW,CAAM,EACjC,GAAI,CAAC,EACH,OAEF,IAAM,EAAQ,EAAM,UAAU,SAAS,QACvC,EAAW,QAAU,GAAa,EAAO,OAAO,EAChD,EAAW,CACT,MAAO,GAAQ,CAAM,EACrB,EAAG,EAAM,EAAI,EAAe,EAC5B,EAAG,EAAM,EAAI,EAAe,CAC9B,CAAC,EACD,EAAS,KAAK,CAAE,UAAS,KAAM,OAAQ,CAAC,CAC1C,EAIM,EAAc,GAA+B,CACjD,IAAM,EAAK,EAAM,UACX,EAAQ,EAAG,SAAS,QAC1B,EAAgB,CAAK,EACrB,IAAM,EAAY,EAAG,OAAS,OAAO,EAAG,OAAO,EAAE,EAAI,IAAA,GAC/C,EAAM,EAAS,CAAK,EAC1B,EAAS,KAAK,CACZ,OAAQ,EAAM,EAAc,EAAI,GAAI,EAAI,QAAS,EAAO,CAAS,EAAI,KACrE,KAAM,MACR,CAAC,CACH,EAIM,EAAa,GAA8B,CAG/C,GAFA,EAAW,IAAI,EACf,EAAW,QAAU,IAAA,GACjB,EAAM,SAAU,CAClB,EAAS,KAAK,CAAE,KAAM,QAAS,CAAC,EAChC,MACF,CACA,IAAM,EAAK,EAAM,UACX,EAAQ,EAAG,SAAS,QACpB,EAAY,EAAG,OAAS,OAAO,EAAG,OAAO,EAAE,EAAI,IAAA,GAC/C,EAAM,EAAS,CAAK,EACtB,GACF,EAAS,KAAK,CACZ,OAAQ,EAAc,EAAI,GAAI,EAAI,QAAS,EAAO,CAAS,EAC3D,KAAM,MACR,CAAC,EAEH,EAAS,KAAK,CAAE,KAAM,MAAO,CAAC,CAChC,EACM,EAAW,EAAQ,QAAQ,iBAAiB,YAAa,CAAW,EACpE,EAAU,EAAQ,QAAQ,iBAAiB,WAAY,CAAU,EACjE,EAAS,EAAQ,QAAQ,iBAAiB,UAAW,CAAS,EACpE,UAAa,CACX,EAAS,EACT,EAAQ,EACR,EAAO,CACT,CACF,EAAG,CAAC,EAAU,EAAS,EAAiB,EAAe,CAAQ,CAAC,EAEhE,IAAM,EAAe,OACZ,CAAE,UAAS,gBAAe,GACjC,CAAC,EAAS,CAAc,CAC1B,EAEA,OACE,EAAC,EAAY,SAAb,CAAsB,MAAO,WAC3B,EAAC,GAAwB,SAAzB,CAAkC,MAAO,WAAzC,CACG,EACD,EAAC,GAAD,CACY,WACK,gBACG,kBACnB,CAAA,EACD,EAAC,GAAD,CAAa,WAAY,EAAwB,SAAU,CAAA,CAC3B,GACd,CAAA,CAE1B,EC3VM,GAAY,CAAE,QAAS,OAAQ,OAAQ,OAAQ,MAAO,MAAO,EAoB7D,IAAc,CAClB,WACA,eAAe,GACf,WACA,mBAAmB,GACnB,cAAc,GACd,cAAc,GACd,QACA,gBAAgB,GAChB,YACA,iBACA,sBACA,QACA,GAAG,KAC6B,CAGhC,IAAM,EAAS,EAAM,OACf,EAAwB,CAC5B,aAAc,GAAgB,EAAO,iBAAmB,GACxD,WACA,mBACA,cACA,YAAa,GAAe,EAAO,uBAAyB,GAC5D,kBAAmB,EAAM,kBACzB,cAAe,GAAiB,EAAO,kBAAoB,GAC3D,YACA,iBACA,sBACA,UAAW,EAAO,kBAAoB,GACtC,YAAa,EAAO,aAAe,MACnC,cAAe,EAAO,eAAiB,IACvC,gBAAiB,EAAO,uBAAyB,EACnD,EAEM,CAAC,GAAS,MAAe,GAAkB,CAAQ,CAAC,EAI1D,MAAsB,CACpB,EAAM,SAAS,CAAQ,CACzB,CAAC,EAED,IAAM,EACJ,EAAO,eAAiB,IAAA,GACpB,CAAE,GAAG,GAAW,GAAG,CAAM,EACxB,CACC,GAAG,GACH,0BAA2B,GAAG,EAAO,aAAa,IAClD,GAAG,CACL,EAEN,OACE,EAAC,EAAmB,SAApB,CAA6B,MAAO,WAClC,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,SAAS,MAAO,EAC1C,UACE,CAAA,CACsB,CAAA,CAEjC,ECvFM,GAAY,EAAkB,IAA0B,eAAe,EAAS,GAAG,IACnF,EAAc,GAA6B,iBAAiB,ICyD5D,IACJ,EACA,EACA,IAEA,GAAO,cAA2B,IAAI,IAAI,OAAO,EAAS,EAAU,CAAK,CAAC,GAAG,GAAK,KAE9E,GAAqB,GACzB,GAA0B,EAAK,KAAS,CACtC,GAAG,EACH,iBAAoB,EAAI,CAAE,aAAc,IAAK,CAAC,EAC9C,SAAW,GAAU,CACnB,EAAI,CAAE,aAAc,EAAK,CAAC,EAC1B,EAAI,EAAE,SAAS,CAAE,QAAO,KAAM,WAAY,CAAC,CAC7C,EACA,cAAe,EAAO,IAAU,CAC9B,EAAI,CAAE,aAAc,IAAK,CAAC,EAC1B,GAAM,CAAE,OAAM,aAAc,EAAI,EAC1B,EAAU,EAAM,KAAK,EACrB,EAAU,EAAK,SAAS,KAAM,GAAQ,EAAI,KAAO,CAAK,GAAG,KAC3D,GAAW,IAAY,GACzB,EAAU,EAAO,CAAO,CAE5B,EACA,aAAc,KACd,cAAe,CAAC,EAChB,yBACE,EAAK,IAAW,CAAE,iBAAkB,EAAM,iBAAmB,CAAE,EAAE,MACpD,EAAK,IAAW,CAAE,iBAAkB,EAAM,iBAAmB,CAAE,EAAE,GAEhF,gBAAkB,GAAY,CAC5B,GAAI,EAAS,CACX,IAAM,EAAU,EAAI,EAAE,eAClB,GAAW,IAAY,GACzB,EACE,qBAAqB,EAAI,EAAE,KAAK,KAChC,gHACF,EAEF,EAAI,CAAE,eAAgB,CAAQ,CAAC,EAC/B,MACF,CACA,EAAI,CAAE,eAAgB,IAAK,CAAC,CAC9B,EACA,iBAAkB,EAClB,WAAY,EAAO,IAAS,EAAI,EAAE,SAAS,CAAE,OAAM,QAAO,KAAM,WAAY,CAAC,EAC7E,aAAc,GACd,kBAAoB,GAAU,CAC5B,GAAM,CAAE,WAAU,OAAM,kBAAmB,EAAI,EACzC,EAAQ,EAAK,SAAS,UAAW,GAAQ,EAAI,KAAO,CAAK,EAC3D,IAAU,KAGd,EAAS,CAAE,QAAO,SAAU,EAAK,GAAI,KAAM,WAAY,CAAC,EACxD,GAAY,EAAgB,EAAK,GAAI,CAAK,GAAG,eAAe,CAC1D,MAAO,UACP,OAAQ,SACV,CAAC,EACH,EACA,WAAY,EAAO,IAAY,CAC7B,GAAM,CAAE,WAAU,OAAM,kBAAmB,EAAI,EAE/C,GADA,EAAS,CAAE,QAAO,SAAU,EAAK,GAAI,KAAM,WAAY,CAAC,EACpD,GAAS,MAAO,CAClB,IAAM,EAAQ,EAAK,SAAS,IAAQ,GAChC,GACF,GAAY,EAAgB,EAAK,GAAI,CAAK,GAAG,MAAM,CAEvD,CACF,EACA,aAAe,GAAU,CACvB,GAAM,CAAE,OAAM,mBAAkB,iBAAkB,EAAI,EAChD,EAAM,EAAK,SAAS,KAAM,GAAU,EAAM,KAAO,CAAK,EACxD,MAAC,GAAiB,GAAK,eAAiB,IAG5C,IAAI,IAAqB,EAAG,CAE1B,EACE,wBAAwB,EAAK,KAC7B,wEACF,EACA,MACF,CACA,EAAI,CAAE,aAAc,CAAM,CAAC,CAD3B,CAEF,EACA,eAAgB,KAChB,mBAAsB,CACpB,GAAM,CAAE,WAAU,cAAa,QAAS,EAAI,EAC5C,EAAS,CAAE,SAAU,EAAc,KAAO,EAAK,GAAI,KAAM,oBAAqB,CAAC,CACjF,CACF,EAAE,EAGE,EAAqB,EAAkC,IAAI,EAE3D,EAAgB,GAA2C,CAC/D,IAAM,EAAQ,EAAW,CAAkB,EAC3C,GAAI,IAAU,KACZ,MAAU,MAAM,6DAA6D,EAE/E,OAAO,EAAS,EAAO,CAAQ,CACjC,EAMM,GAAa,EAAsC,IAAI,EAEvD,MAAgC,CACpC,IAAM,EAAQ,EAAW,EAAU,EACnC,GAAI,IAAU,KACZ,MAAU,MAAM,yDAAyD,EAE3E,OAAO,CACT,ECtKM,GAA8B,CAAE,KAAM,EAAG,UAAW,EAAG,SAAU,MAAO,EAMxE,IAAY,CAChB,SACA,SAIe,CACf,IAAM,EAAY,EAAW,GAAU,EAAM,SAAS,EACtD,OAAO,EAAS,EAAO,CAAG,EAAI,EAAU,CAAG,CAC7C,EAOM,IAAiB,CAAE,WAAU,QAAO,GAAG,KAA2C,CACtF,IAAM,EAAO,EAAW,GAAU,EAAM,IAAI,EACtC,EAAiB,EAAW,GAAU,EAAM,cAAc,EAC1D,EAAc,EAAW,GAAU,EAAM,WAAW,EAEpD,EAAc,CAAE,GAAG,GAAc,GAAG,CAAM,EAIhD,GAAI,GAAe,EAAK,SAAS,OAAS,EACxC,OAAO,EAAK,SAAS,KAAK,EAAK,IAC7B,EAAC,MAAD,CACE,GAAI,EACJ,kBAAiB,EAAS,EAAK,GAAI,EAAI,EAAE,EACzC,eAAa,aACb,OAAQ,IAAU,GAAkB,IAAA,GACpC,GAAI,IAAU,EAAiB,EAAW,EAAK,EAAE,EAAI,IAAA,GACrD,IAAK,EAAI,GACT,KAAM,IAAU,EAAiB,WAAa,IAAA,GAC9C,MAAO,EACP,SAAU,IAAU,EAAiB,EAAI,IAAA,EAGtC,EADH,EAAC,GAAD,CAAU,OAAQ,EAAe,KAAM,CAAA,CACpC,CACN,EAGH,IAAM,EAAS,EAAK,SAAS,GAgB7B,OAfI,EAEA,EAAC,MAAD,CACE,GAAI,EACJ,kBAAiB,EAAS,EAAK,GAAI,EAAO,EAAE,EAC5C,eAAa,aACb,GAAI,EAAW,EAAK,EAAE,EACtB,KAAK,WACL,MAAO,EACP,SAAU,WAEV,EAAC,GAAD,CAAU,OAAQ,EAAU,IAAK,CAAS,CAAA,CACvC,CAAA,EAGF,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,aAAa,MAAO,CAAc,CAAA,CACxE,ECvEM,OACJ,EAAC,MAAD,CAAK,cAAY,OAAO,KAAK,eAAe,OAAO,KAAK,QAAQ,YAAY,MAAM,cAAlF,CACE,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,IAAI,EAAE,GAAK,CAAA,EAC7B,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,IAAI,EAAE,GAAK,CAAA,EAC7B,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,IAAI,EAAE,GAAK,CAAA,CAC1B,IAGD,GAA2B,CAC/B,SAAU,WACV,MAAO,EACP,IAAK,OACL,OAAQ,EACV,EAQM,IAAmB,CACvB,QACA,cAIe,CACf,GAAM,CAAC,EAAM,GAAW,EAAS,EAAK,EAChC,CAAC,EAAa,GAAkB,EAAS,CAAC,EAC1C,EAAU,EAAuB,IAAI,EACrC,EAAa,EAA0B,IAAI,EAC3C,EAAW,EAAwC,CAAC,CAAC,EAE3D,MAAgB,CACd,GAAI,CAAC,EACH,OAEF,IAAM,EAAqB,GAA8B,CACnD,EAAM,kBAAkB,MAAQ,CAAC,EAAQ,SAAS,SAAS,EAAM,MAAM,GACzE,EAAQ,EAAK,CAEjB,EAEA,OADA,SAAS,iBAAiB,cAAe,CAAiB,MAC7C,CACX,SAAS,oBAAoB,cAAe,CAAiB,CAC/D,CACF,EAAG,CAAC,CAAI,CAAC,EAKT,MAAgB,CACV,GACF,EAAS,QAAQ,IAAI,MAAM,CAE/B,EAAG,CAAC,CAAI,CAAC,EAET,IAAM,MAAkC,CACtC,EAAQ,EAAK,EACb,EAAW,SAAS,MAAM,CAC5B,EAEM,EAAa,GAAwB,CACzC,EAAe,CAAK,EACpB,EAAS,QAAQ,IAAQ,MAAM,CACjC,EAEM,MAA2B,CAC/B,EAAe,CAAC,EAChB,EAAS,GAAU,CAAC,CAAK,CAC3B,EAEM,EAAgB,GAAqB,CACzC,EAAS,CAAE,EACX,EAAoB,CACtB,EA6CA,OACE,EAAC,MAAD,CAAK,eAAa,oBAAoB,IAAK,EAAS,MAAO,CAAE,SAAU,UAAW,WAAlF,CACE,EAAC,SAAD,CACE,gBAAe,EACf,gBAAc,OACd,aAAW,YACX,eAAa,eACb,QAAS,EACT,IAAK,EACL,KAAK,kBAEL,EAAC,GAAD,CAAe,CAAA,CACT,CAAA,EACP,EACC,EAAC,MAAD,CACE,eAAa,oBACb,OAvBgB,GAA4C,CAC9D,EAAM,yBAAyB,MAAQ,EAAQ,SAAS,SAAS,EAAM,aAAa,GAGxF,EAAQ,EAAK,CACf,EAmBQ,UA5DmB,GAA+C,CACxE,IAAM,EAAO,EAAM,OAAS,EAC5B,OAAQ,EAAM,IAAd,CACE,IAAK,YACH,EAAM,eAAe,EACrB,EAAU,GAAe,EAAO,EAAI,EAAc,CAAC,EACnD,MAEF,IAAK,UACH,EAAM,eAAe,EACrB,EAAU,GAAe,EAAI,EAAO,EAAc,CAAC,EACnD,MAEF,IAAK,OACH,EAAM,eAAe,EACrB,EAAU,CAAC,EACX,MAEF,IAAK,MACH,EAAM,eAAe,EACrB,EAAU,CAAI,EACd,MAEF,IAAK,SACH,EAAM,eAAe,EACrB,EAAoB,EACpB,MAEF,QACE,KAEJ,CACF,EA6BQ,KAAK,OACL,MAAO,GACP,SAAU,YAET,EAAM,KAAK,EAAM,IAChB,EAAC,SAAD,CACE,eAAa,oBAEb,YAAe,EAAa,EAAK,EAAE,EACnC,IAAM,GAAS,CACb,EAAS,QAAQ,GAAS,CAC5B,EACA,KAAK,WACL,SAAU,IAAU,EAAc,EAAI,GACtC,KAAK,kBAEJ,EAAK,IACA,EAVD,EAAK,EAUJ,CACT,CACE,CAAA,EACH,IACD,GAET,EC5JM,OAAsC,CAC1C,IAAM,EAAgB,EAAW,GAAU,EAAM,aAAa,EACxD,EAAoB,EAAW,GAAU,EAAM,iBAAiB,EAKtE,OAHI,EAAc,SAAW,EACpB,KAEF,EAAC,GAAD,CAAiB,MAAO,EAAe,SAAU,CAAoB,CAAA,CAC9E,ECZM,GAAkB,GAAoC,CAC1D,IAAM,EAAM,EAAQ,sBAAsB,EAC1C,MAAO,CAAC,GAAG,EAAQ,iBAA8B,sBAAsB,CAAC,EAAE,QAAS,GAAQ,CACzF,IAAM,EAAO,EAAI,sBAAsB,EAEvC,OADgB,EAAK,MAAQ,EAAI,MAAQ,GAAK,EAAK,KAAO,EAAI,KAAO,IACnD,EAAI,QAAQ,MAAQ,CAAC,EAAI,QAAQ,KAAK,EAAI,CAAC,CAC/D,CAAC,CACH,EAOM,IAAkB,EAA6B,IAAqC,CACxF,GAAM,CAAC,EAAU,GAAe,EAAwB,CAAC,CAAC,EAyB1D,OAvBA,MAAgB,CAEd,IAAM,MAAwB,CAC5B,EAAa,GAAa,CACxB,IAAM,EAAO,EAAU,GAAe,CAAO,EAAI,CAAC,EAClD,OAAO,EAAK,SAAW,GAAK,EAAS,SAAW,EAAI,EAAW,CACjE,CAAC,CACH,EAEA,GADA,EAAU,EACN,CAAC,EACH,OAEF,IAAM,EAAW,IAAI,eAAe,CAAS,EAK7C,OAJA,EAAS,QAAQ,CAAO,EAGxB,EAAQ,iBAAiB,SAAU,EAAW,CAAE,QAAS,EAAK,CAAC,MAClD,CACX,EAAS,WAAW,EACpB,EAAQ,oBAAoB,SAAU,CAAS,CACjD,CACF,EAAG,CAAC,EAAS,CAAS,CAAC,EAEhB,CACT,EC9CM,IAAyB,EAAe,IAC5C,EAAe,EAAI,EAAQ,EAAe,EAAI,EAAe,ECgBzD,GAA6B,CACjC,QAAS,OACT,cAAe,SACf,OAAQ,OACR,UAAW,EACX,SAAU,EACV,MAAO,MACT,EAOM,IAAc,CAClB,WACA,OACA,IAAK,EACL,QACA,GAAG,KAC6B,CAChC,IAAM,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAW,EAAW,GAAU,EAAM,QAAQ,EAC9C,EAAc,EAAW,GAAU,EAAM,WAAW,EACpD,EAAoB,EAAW,GAAU,EAAM,iBAAiB,EAChE,EAAgB,EAAW,GAAU,EAAM,aAAa,EACxD,EAAc,EAAW,GAAU,EAAM,WAAW,EAEpD,CAAE,OAAQ,GAAmB,EAAK,EAAE,EACpC,EAAc,EAAe,EAC7B,EAAY,EAA8B,IAAI,EAE9C,EAAc,IAAsB,EAAK,GACzC,EAAe,GAAe,EAAK,iBAAmB,GACtD,EAAe,GAAgB,EAAK,cAAgB,GAEpD,EAAgB,EAKhB,EACJ,GAAa,OAAS,MAAQ,EAAK,SAAS,UAAW,GAAQ,EAAI,KAAO,EAAY,EAAE,EAAI,GACxF,EACJ,IAAqB,IAAM,IAAqB,EAAK,SACjD,GAAsB,EAAK,SAAS,OAAQ,CAAgB,EAC5D,EAAK,SACL,EAAY,EAAK,SAAS,GAC1B,EAAe,GAAa,OAAS,UAAY,EAAY,KAAO,EAAK,GAEzE,EAA2B,CAC/B,YACA,WACA,cACA,OACA,eACA,eACA,gBACA,gBACF,EACM,CAAC,GAAS,MAAe,GAAkB,CAAQ,CAAC,EAK1D,MAAsB,CACpB,EAAM,SAAS,CAAQ,CACzB,CAAC,EASD,IAAM,EAAW,GAJM,EAAS,EAAQ,GAAU,EAAM,cAIX,EADnB,EAAK,SAAS,IAAK,GAAQ,GAAG,EAAI,GAAG,GAAG,EAAI,MAAM,EAAE,KAAK,GACnB,CAAC,EAE3D,EAAgB,MAAc,CAClC,IAAM,EAAQ,IAAI,IAAI,EAAK,SAAS,IAAK,GAAQ,CAAC,EAAI,GAAI,EAAI,IAAI,CAAC,CAAC,EACpE,OAAO,EAAS,IAAK,IAAQ,CAAE,KAAI,KAAM,EAAM,IAAI,CAAE,GAAK,CAAG,EAAE,CACjE,EAAG,CAAC,EAAU,EAAK,QAAQ,CAAC,EAwB5B,OAvBA,MAAsB,CACpB,EAAM,SAAS,CAAE,eAAc,CAAC,CAClC,EAAG,CAAC,EAAe,CAAK,CAAC,EAMzB,MAAgB,CACd,GAAI,CAAC,EAAM,SAAS,EAAE,aACpB,OAEF,EAAM,SAAS,CAAE,aAAc,EAAM,CAAC,EACtC,IAAM,EAAc,EAAK,SAAS,EAAK,WAAW,GAC9C,EACF,EAAU,SACN,cAA2B,IAAI,IAAI,OAAO,EAAS,EAAK,GAAI,CAAW,CAAC,GAAG,GAC3E,MAAM,EAEV,EAAU,SAAS,MAAM,CAE7B,EAAG,CAAC,EAAK,SAAU,EAAK,SAAU,EAAK,GAAI,CAAK,CAAC,EAG/C,EAAC,EAAmB,SAApB,CAA6B,MAAO,WAClC,EAAC,MAAD,CACE,GAAI,EACJ,eAAa,SACb,uBAAsB,GAAgB,IAAA,GACtC,oBAAmB,EACnB,IAAM,GAAY,CAChB,EAAI,CAAO,EACX,EAAU,QAAU,EAChB,OAAO,GAAY,WACrB,EAAQ,CAAO,EACN,IACT,EAAQ,QAAU,EAEtB,EACA,MAAO,CAAE,GAAG,GAAa,GAAG,CAAM,EAGlC,SAAU,GAET,UACE,CAAA,CACsB,CAAA,CAEjC,ECnJM,IAAe,EAAyB,IAA0B,CAClE,OAAO,GAAQ,WACjB,EAAI,CAAK,EACA,IACT,EAAI,QAAU,EAElB,EAIM,GACA,GAAG,IACN,GAA0B,CACzB,IAAK,IAAM,KAAO,EAChB,GAAS,EAAK,CAAK,CAEvB,ECAI,IAAa,CAAE,WAAU,IAAK,EAAS,MAAK,GAAG,KAAuC,CAC1F,IAAM,EAAQ,EAAW,CAAkB,EACrC,EAAO,EAAW,GAAU,EAAM,IAAI,EACtC,EAAU,EAAW,GAAU,EAAM,eAAiB,EAAI,EAAE,EAI5D,EAAc,EAAe,EAC7B,EAAa,GAAa,OAAS,OAAS,EAAY,KAAO,EAAI,GACnE,EAAU,EAA+B,IAAI,EAC7C,EAAa,EAAO,EAAK,EAEzB,EAAQ,EAAK,SAAS,UAAW,GAAU,EAAM,KAAO,EAAI,EAAE,EASpE,MAAgB,CACE,GAAO,SAAS,EAAE,KAAK,SAAS,KAAM,GAAU,EAAM,KAAO,EAAI,EAAE,IACnE,IACd,EACE,cAAc,EAAI,KAClB,qBAAqB,EAAI,GAAG,iFAC9B,CAEJ,EAAG,CAToB,IAAU,GASb,EAAO,EAAI,EAAE,CAAC,EAGlC,MAAgB,CACV,EAAW,SAAW,CAAC,GACzB,EAAQ,SAAS,cAA2B,sBAAsB,GAAG,MAAM,EAE7E,EAAW,QAAU,CACvB,EAAG,CAAC,CAAO,CAAC,EAEZ,IAAM,EAAe,OAAe,CAAE,QAAO,KAAI,GAAI,CAAC,EAAO,CAAG,CAAC,EAC3D,EAAc,MAAc,EAA2B,EAAS,CAAO,EAAG,CAAC,CAAO,CAAC,EAEzF,OACE,EAAC,GAAW,SAAZ,CAAqB,MAAO,WAC1B,EAAC,OAAD,CACE,GAAI,EACJ,eAAa,WACb,gBAAe,GAAc,IAAA,GAC7B,IAAK,EAEJ,UACG,CAAA,CACa,CAAA,CAEzB,EAQM,IAAiB,CACrB,WACA,UACA,gBACA,IAAK,EACL,GAAG,KACgC,CACnC,GAAM,CAAE,QAAO,OAAQ,EAAO,EACxB,EAAO,EAAW,GAAU,EAAM,IAAI,EACtC,EAAU,EAAW,GAAU,EAAM,eAAiB,EAAI,EAAE,EAC5D,EAAY,EAAW,GAAU,EAAM,SAAS,EAChD,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAgB,EAAW,GAAU,EAAM,aAAa,EACxD,EAAiB,EAAW,GAAU,EAAM,cAAc,EAC1D,EAAiB,EAAW,GAAU,EAAM,cAAc,EAC1D,CAAE,OAAQ,GAAgB,EAAI,GAAI,EAAI,aAAe,GAAO,EAAI,IAAI,EACpE,EAAc,MAAc,EAA6B,EAAK,CAAO,EAAG,CAAC,EAAK,CAAO,CAAC,EAE5F,GAAI,EACF,OAAO,KAGT,IAAM,EAAW,IAAU,EACrB,EAAY,GAAiB,EAAI,eAAiB,GAElD,EAAe,GAA+C,CAClE,IAAU,CAAK,EACf,EAAU,CAAK,CACjB,EAEM,EAAqB,GAA+C,CACxE,IAAgB,CAAK,EACjB,GACF,EAAa,EAAI,EAAE,CAEvB,EAEA,OACE,EAAC,SAAD,CACE,aAAY,EAAiB,EAAI,KAAO,IAAA,GACxC,GAAI,EACJ,gBAAe,EAAW,EAAK,EAAE,EACjC,gBAAe,EACf,eAAa,MACb,cAAa,EAAI,GACjB,GAAI,EAAS,EAAK,GAAI,EAAI,EAAE,EAC5B,QAAS,EACT,cAAe,EACf,IAAK,EACL,KAAK,MACL,SAAU,EAAW,EAAI,GACzB,KAAK,kBAEJ,IAAa,EAAiB,EAAe,CAAG,EAAI,EAAI,KACnD,CAAA,CAEZ,ECtIM,GAA2B,CAAE,cAAe,MAAO,EAKnD,GAAQ,CAAE,cACd,EAAC,MAAD,CACE,cAAY,OACZ,KAAK,OACL,OAAO,KACP,OAAO,eACP,cAAc,QACd,eAAe,QACf,YAAY,IACZ,MAAO,GACP,QAAQ,YACR,MAAM,KAEL,UACE,CAAA,EAGD,OACJ,EAAC,EAAD,CAAA,SAAA,CACE,EAAC,OAAD,CAAM,EAAE,YAAc,CAAA,EACtB,EAAC,OAAD,CAAM,EAAE,YAAc,CAAA,CAClB,CAAA,CAAA,EAGF,OACJ,EAAC,EAAD,CAAA,SAAA,CACE,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,IAAI,EAAE,GAAK,CAAA,EAC9B,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,IAAI,EAAE,GAAK,CAAA,EAC9B,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,IAAI,EAAE,GAAK,CAAA,EAC7B,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,KAAK,EAAE,GAAK,CAAA,EAC/B,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,KAAK,EAAE,GAAK,CAAA,EAC/B,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,KAAK,EAAE,GAAK,CAAA,EAC9B,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,KAAK,EAAE,GAAK,CAAA,EAC/B,EAAC,SAAD,CAAQ,GAAG,KAAK,GAAG,KAAK,EAAE,GAAK,CAAA,EAC/B,EAAC,SAAD,CAAQ,GAAG,IAAI,GAAG,KAAK,EAAE,GAAK,CAAA,CAC1B,CAAA,CAAA,EAGF,IAAgB,CAAE,eACtB,EACE,EAAC,EAAD,CAAA,SAAA,CACE,EAAC,OAAD,CAAM,EAAE,YAAc,CAAA,EACtB,EAAC,OAAD,CAAM,EAAE,aAAe,CAAA,EACvB,EAAC,OAAD,CAAM,EAAE,WAAa,CAAA,EACrB,EAAC,OAAD,CAAM,EAAE,WAAa,CAAA,CACjB,CAAA,CAAA,EAEN,EAAC,EAAD,CAAA,SAAA,CACE,EAAC,OAAD,CAAM,EAAE,WAAa,CAAA,EACrB,EAAC,OAAD,CAAM,EAAE,WAAa,CAAA,EACrB,EAAC,OAAD,CAAM,EAAE,WAAa,CAAA,EACrB,EAAC,OAAD,CAAM,EAAE,YAAc,CAAA,CAClB,CAAA,CAAA,EC7CJ,IAAgB,CACpB,eACA,SACA,YACA,IAAK,EACL,GAAG,KACoC,CACvC,GAAM,CAAE,OAAQ,EAAO,EACjB,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAM,EAAgC,IAAI,EAC1C,EAAO,EAAO,EAAK,EAEzB,MAAgB,CACd,EAAI,SAAS,MAAM,EACnB,EAAI,SAAS,OAAO,CACtB,EAAG,CAAC,CAAC,EAEL,IAAM,EAAiB,GAAiD,CACtE,IAAY,CAAK,EACb,EAAM,MAAQ,SAChB,EAAK,QAAU,GACf,EAAa,EAAI,GAAI,EAAM,cAAc,KAAK,GACrC,EAAM,MAAQ,WACvB,EAAK,QAAU,GACf,EAAa,EAEjB,EAEM,EAAsB,GAA8C,CACxE,IAAS,CAAK,EACV,GAAK,UAGT,EAAK,QAAU,GACf,EAAa,EAAI,GAAI,EAAM,cAAc,KAAK,EAChD,EAEA,OACE,EAAC,QAAD,CACE,aAAY,UAAU,EAAI,OAC1B,GAAI,EACJ,eAAa,aACb,aAAc,GAAgB,EAAI,KAClC,OAAQ,EACR,UAAW,EACX,IAAM,GAAY,CAChB,EAAI,QAAU,EACV,OAAO,GAAY,WACrB,EAAQ,CAAO,EACN,IACT,EAAQ,QAAU,EAEtB,EACA,KAAK,MACN,CAAA,CAEL,EAKM,GAAqB,GAA6C,CACtE,GAAM,CAAE,OAAQ,EAAO,EACjB,EAAU,EAAW,GAAU,EAAM,eAAiB,EAAI,EAAE,EAC5D,EAAsB,EAAW,GAAU,EAAM,mBAAmB,EAO1E,OALA,MAAgB,EAAoB,EAAG,CAAC,CAAmB,CAAC,EAEvD,EAGE,EAAC,GAAD,CAAc,GAAI,CAAQ,CAAA,EAFxB,IAGX,EAIM,IAAqB,CAAE,WAAU,UAAS,GAAG,KAA+C,CAChG,GAAM,CAAE,QAAO,OAAQ,EAAO,EACxB,EAAW,EAAW,GAAU,EAAM,QAAQ,EAC9C,EAAU,EAAW,GAAU,EAAM,eAAiB,EAAI,EAAE,EAC5D,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAiB,EAAW,GAAU,EAAM,cAAc,EAGhE,GAAI,EADa,GAAgB,EAAI,cAAgB,KACpC,EACf,OAAO,KAGT,IAAM,EAAe,GAA+C,CAClE,IAAU,CAAK,EAEf,EAAM,gBAAgB,EACtB,EAAS,EAAI,EAAE,CACjB,EAEA,OACE,EAAC,SAAD,CACE,aAAY,SAAS,EAAI,OACzB,GAAI,EACJ,eAAa,YACb,QAAS,EAGT,SAAU,IAAU,EAAiB,EAAI,GACzC,KAAK,kBAEJ,GAAY,EAAC,GAAD,CAAY,CAAA,CACnB,CAAA,CAEZ,EC/GM,GAA4B,CAAE,QAAS,OAAQ,WAAY,CAAE,EAE7D,GAA8B,CAAE,QAAS,OAAQ,SAAU,EAAG,UAAW,MAAO,EAIhF,IAAkB,CAAE,QAAO,GAAG,KAClC,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,WAAW,MAAO,CAAE,GAAG,GAAY,GAAG,CAAM,CAAI,CAAA,EAOzE,IAAiB,CACrB,YACA,IAAK,EACL,QACA,GAAG,KACgC,CACnC,IAAM,EAAO,EAAW,GAAU,EAAM,IAAI,EACtC,EAAkB,EAAW,GAAU,EAAM,eAAe,EAC5D,EAAY,EAAW,GAAU,EAAM,SAAS,EAEhD,EAAiB,GAA+C,CAOpE,GANA,IAAY,CAAK,EACb,EAAM,kBAKN,EAAE,EAAM,kBAAkB,cAAgB,CAAC,EAAM,OAAO,QAAQ,cAAc,EAChF,OAEF,IAAM,EAAQ,EAAK,SAAS,OAC5B,GAAI,IAAU,EACZ,OAQF,IAAM,EAAO,CALX,WAAY,EAAK,SAAW,EAAI,GAAS,EACzC,YAAa,EAAK,SAAW,GAAK,EAClC,IAAK,EAAQ,EACb,KAAM,CAEW,EAAE,EAAM,KACvB,IAAS,IAAA,KAGb,EAAM,eAAe,EACrB,EAAU,EAAM,CAAE,MAAO,EAAK,CAAC,EACjC,EAIM,EAAc,MACZ,EAA0B,EAAiB,CAAO,EACxD,CAAC,EAAiB,CAAO,CAC3B,EAEA,OACE,EAAC,MAAD,CACE,aAAY,EAAK,MAAQ,OACzB,GAAI,EACJ,mBAAiB,aACjB,eAAa,UACb,UAAW,EACX,IAAK,EACL,KAAK,UACL,MAAO,CAAE,GAAG,GAAc,GAAG,CAAM,EACnC,SAAU,EACX,CAAA,CAEL,ECzEM,GAA8B,CAClC,WAAY,SACZ,QAAS,OACT,WAAY,EACZ,kBAAmB,MACrB,EAIM,IAAiB,CAAE,QAAO,GAAG,KACjC,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,iBAAiB,MAAO,CAAE,GAAG,GAAc,GAAG,CAAM,CAAI,CAAA,EAQjF,IAAc,CAAE,WAAU,IAAK,EAAS,GAAG,KAAwC,CACvF,IAAM,EAAgB,EAAW,GAAU,EAAM,WAAW,MAAQ,EAAE,EAChE,EAAc,EAAW,GAAU,EAAM,WAAW,EACpD,EAAO,EAAW,GAAU,EAAM,IAAI,EAEtC,EAAS,CADU,EAAW,GAAU,EAAM,gBACrB,GAAK,EAC9B,CAAE,OAAQ,GAAmB,EAAK,GAAI,EAAQ,CAAa,EAC3D,EAAc,MAAc,EAA6B,EAAK,CAAO,EAAG,CAAC,EAAK,CAAO,CAAC,EAM5F,OAJI,EACK,KAIP,EAAC,SAAD,CACE,aAAW,cACX,GAAI,EACJ,eAAa,cACb,IAAK,EACL,KAAK,kBAEJ,GAAY,EAAC,GAAD,CAAW,CAAA,CAClB,CAAA,CAEZ,EAIM,GAAwB,CAC5B,WACA,UACA,GAAG,KACuC,CAC1C,IAAM,EAAc,EAAW,GAAU,EAAM,WAAW,EACpD,EAAe,EAAW,GAAU,EAAM,YAAY,EACtD,EAAiB,EAAW,GAAU,EAAM,cAAc,EAEhE,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAe,GAA+C,CAClE,IAAU,CAAK,EACf,EAAe,CACjB,EAEA,OACE,EAAC,SAAD,CACE,aAAY,EAAc,UAAY,WACtC,GAAI,EACJ,eAAc,EACd,eAAa,kBACb,QAAS,EACT,KAAK,kBAEJ,GAAY,EAAC,GAAD,CAAc,UAAW,CAAc,CAAA,CAC9C,CAAA,CAEZ,ECpEM,OAAuC,CAC3C,IAAM,EAAmB,EAAW,GAAU,EAAM,gBAAgB,EAC9D,EAAsB,EAAW,GAAU,EAAM,mBAAmB,EACpE,EAAc,EAAW,GAAU,EAAM,WAAW,EACpD,EAAkB,EAAW,GAAU,EAAM,eAAe,EAC5D,EAAc,EAAW,GAAU,EAAM,WAAW,EACpD,EAAO,EAAW,GAAU,EAAM,IAAI,EACtC,EAAgB,EAAW,GAAU,EAAM,cAAc,MAAM,EAK/D,EAJe,EAAW,GAAU,EAAM,YAKnC,GACX,IAAwB,IAAA,IACxB,EAAgB,GACf,GAAoB,CAAC,EAElB,EAAQ,EACZ,EAAC,GAAD,CAAA,SAAA,CACE,EAAC,GAAD,CAAA,SACG,EAAK,SAAS,IAAK,GAClB,EAAC,GAAD,CAA6B,eAA7B,CACE,EAAC,GAAD,CAAgB,CAAA,EAChB,EAAC,GAAD,CAAoB,CAAA,EACpB,EAAC,GAAD,CAAoB,CAAA,CACX,GAJK,EAAI,EAIT,CACZ,CACY,CAAA,EACd,EACC,EAAC,GAAD,CAAA,SAAA,CACE,EAAC,GAAD,CAAqB,CAAA,EACrB,EAAC,GAAD,CAAa,CAAA,EACZ,IAAsB,CAAI,EAC3B,EAAC,EAAD,CAAuB,CAAA,CACV,CAAA,CAAA,EACb,IACU,CAAA,CAAA,EACd,KAEE,EAAU,EAAC,GAAD,CAAgB,CAAA,EAEhC,OAAO,IAAgB,SACrB,EAAA,GAAA,CAAA,SAAA,CACG,EACA,CACD,CAAA,CAAA,EAEF,EAAA,GAAA,CAAA,SAAA,CACG,EACA,CACD,CAAA,CAAA,CAEN,EAIM,IAAc,CAAE,UACpB,EAAC,GAAD,CAAkB,gBAChB,EAAC,GAAD,CAAsB,CAAA,CACZ,CAAA,EC/DR,GAAmB,GAAiC,GAAG,EAAU,QAAQ,EAAU,OACnF,GAAqB,GACzB,GAAW,OAAS,KAAO,EAAU,MAAQ,IAAA,GAEzC,GAA4B,CAAE,QAAS,OAAQ,KAAM,EAAG,UAAW,EAAG,SAAU,CAAE,EAClF,GAAiB,IAIjB,IAAqB,EAA+B,IACxD,EAAS,QAAgB,EAAQ,KAC/B,EAAO,EAAM,KAAQ,EAAM,QAAU,GAAK,EAAS,IAC5C,GACN,CAAC,CAAC,EAED,IAAgB,EAAiB,IAA8B,CACnE,IAAM,EAAa,OAAO,KAAK,CAAO,EAChC,EAAc,OAAO,KAAK,CAAQ,EAIxC,OAHI,EAAW,SAAW,EAAY,OAG/B,EAAY,MAChB,GAAO,KAAK,KAAK,EAAQ,IAAO,KAAc,EAAS,EAAG,GAAK,EAClE,EAJS,EAKX,EAEM,IACJ,EACA,EACA,IACuB,CACvB,IAAM,EAAW,GAAkB,EAAM,GAAG,EAC5C,GAAI,IAAa,IAAA,GACf,OAAO,EAET,GAAI,EAAM,MAAQ,IAAA,GAChB,OAEF,GAAI,EAAM,OAAS,SACjB,OAAO,EAGT,IAAM,EAA+B,CAAC,EACtC,IAAK,IAAM,KAAc,EAAM,SAAU,CACvC,IAAM,EAAU,GAAkB,EAAY,EAAM,CAAa,EACjE,GAAI,IAAY,IAAA,GACd,OAEF,EAAc,KAAK,CAAO,CAC5B,CACI,KAAc,SAAW,EAI7B,OAAO,EAAM,cAAgB,EACzB,EAAc,QAAQ,EAAK,IAAU,EAAM,EAAO,CAAC,EACnD,KAAK,IAAI,GAAG,CAAa,CAC/B,EASM,IAAW,CAAE,OAAM,kBAA4C,CACnE,IAAM,EAAW,EAAW,GAAU,EAAM,QAAQ,EAC9C,EAAgB,EAAW,GAAU,EAAM,aAAa,EACxD,EAA2B,EAAK,cAAgB,MAAQ,aAAe,WACvE,EAAQ,EAAK,SAAS,QAAQ,EAAK,IAAU,GAAO,EAAM,QAAU,GAAI,CAAC,EACzE,EAAgB,MACd,GAAkB,EAAK,SAAU,CAAK,EAC5C,CAAC,EAAK,SAAU,CAAK,CACvB,EAKM,EAAW,EAAO,EAAK,EACvB,EAAW,EAAqC,IAAI,EACpD,EAAU,EAAO,EAAK,EA8C5B,OA5CA,MAAsB,CACpB,IAAM,EAAQ,EAAS,QAIvB,GAHI,CAAC,GAGD,GAAa,EAAM,UAAU,EAAG,CAAa,EAC/C,OAGF,EAAQ,QAAU,GAClB,EAAM,UAAU,CAAa,EAC7B,IAAM,EAAQ,0BAA4B,CACxC,EAAQ,QAAU,EACpB,CAAC,EACD,UAAa,CACX,qBAAqB,CAAK,EAC1B,EAAQ,QAAU,EACpB,CACF,EAAG,CAAC,CAAa,CAAC,EA2BhB,EAAC,GAAD,CACE,eAAa,MACH,WAEV,gBA7ByB,GAAyB,CACpD,GAAI,CAAC,EAAS,QAAS,CACrB,EAAS,QAAU,GACnB,MACF,CACA,GAAI,EAAQ,QACV,OAEF,IAAM,EAAU,EAAK,SAAS,IAAK,GAAU,EAAO,EAAM,KAAO,EAAM,QAAU,CAAC,EAC3D,EAAQ,MAC5B,EAAQ,IAAU,KAAK,IAAI,GAAU,EAAK,SAAS,IAAQ,QAAU,EAAE,EAAI,EAG5D,GAIlB,EAAS,CACP,MAAO,EAAK,GACZ,KAAM,cACN,SACF,CAAC,CACH,EAQiB,cACb,MAAO,YAEN,EAAK,SAAS,KAAK,EAAO,IAAU,CACnC,IAAM,GAAY,EAAM,QAAU,GAAK,EAAS,IAC5C,EAAM,EAAM,IAAM,GAAgB,EAAM,GAAG,EAAI,IAAA,GACnD,GAAI,IAAQ,IAAA,GAAW,CACrB,IAAM,EAAU,GAAkB,EAAO,EAAK,YAAa,CAAa,EACxE,EAAM,IAAY,IAAA,GAAY,IAAA,GAAY,GAAG,EAAQ,GACvD,CACA,IAAM,EAAM,EAAM,IAAM,GAAgB,EAAM,GAAG,EAAI,IAAA,GAErD,OACE,EAAC,EAAD,CAAA,SAAA,CACG,EAAQ,EAAI,EAAC,GAAD,CAAW,eAAa,UAAY,CAAA,EAAI,KACrD,EAACA,GAAD,CAAO,YAAa,GAAG,EAAQ,GAAI,GAAI,EAAM,GAAI,QAAS,EAAK,QAAS,WACrE,EAAM,OAAS,MACd,EAAC,GAAD,CAAS,KAAM,EAAqB,cAAe,CAAA,EAElD,IAAe,CAAK,GAAK,EAAC,GAAD,CAAY,KAAM,CAAQ,CAAA,CAEjD,CAAA,CACC,CAAA,EATK,EAAM,EASX,CAEd,CAAC,CACI,EA3BA,EAAK,SAAS,IAAK,GAAU,EAAM,EAAE,EAAE,KAAK,GAAG,CA2B/C,CAEX,ECtKM,EAAS,CACb,UAAW,GACX,KAAM,GACN,KAAM,GACN,OAAQ,EACV,ECIM,IACJ,EACA,IAEI,IAAY,IAAA,IAAa,CAAC,EACrB,KAEL,OAAO,GAAY,SACd,CAAE,WAAY,IAA6B,IAAK,EAAS,QAAS,CAAoB,EAExF,CACL,WAAY,EAAQ,YAAc,IAClC,IAAK,EAAQ,IACb,QAAS,EAAQ,SAAW,CAC9B,EAMI,GAAmB,IAAI,IA+CvB,GAAgB,GAA+C,EAAO,IAAmB,CAC7F,GAAM,CACJ,eAAe,GACf,aACA,eACA,mBAAmB,GACnB,UACA,cAAc,GACd,cAAc,GACd,QACA,WACA,uBACA,0BACA,gBACA,UACA,gBAAgB,GAChB,iBACA,uBACE,EAME,EAAc,GAJE,MACd,GAAe,EAAS,IAAiB,IAAA,EAAS,EACxD,CAAC,EAAS,CAAY,CAEuB,EAAG,CAAY,EAIxD,EAAoB,GACvB,EAAe,IAA0B,CACxC,EAAY,KAAK,CAAI,EACrB,IAAgB,EAAM,CAAM,CAC9B,EACA,CAAC,EAAe,CAAW,CAC7B,EAEM,EAAQ,GAAgB,CAC5B,aAAc,EAAY,cAAgB,EAC1C,QACA,WACA,uBACA,0BACA,cAAe,CACjB,CAAC,EAED,EACE,OACO,CACL,QAAS,EAAK,IACZ,EAAM,SAAS,CACb,MAAO,EAAO,MACd,SAAU,EAAO,UAAY,SAC7B,MACA,SAAU,EAAO,SACjB,KAAM,SACR,CAAC,EACH,QAAS,EAAM,QACf,QAAS,EAAM,QACf,SAAW,GAAU,EAAM,SAAS,CAAE,QAAO,KAAM,WAAY,CAAC,EAChE,SAAU,EAAM,SAChB,aAAgB,EAAM,MACtB,eAAiB,GAAa,EAAM,SAAS,CAAE,WAAU,KAAM,oBAAqB,CAAC,EACrF,KAAM,EAAM,KACZ,WAAY,EAAO,IAAS,EAAM,SAAS,CAAE,OAAM,QAAO,KAAM,WAAY,CAAC,EAC7E,gBAAmB,CACjB,EAAY,MAAM,EACd,IAAiB,IAAA,IACnB,EAAM,SAAS,CAAY,CAE/B,EACA,WAAY,EAAU,IAAU,EAAM,SAAS,CAAE,QAAO,WAAU,KAAM,WAAY,CAAC,EACrF,KAAM,EAAM,IACd,GACA,CAAC,EAAc,EAAa,CAAK,CACnC,EAEA,IAAM,EAAY,EACf,GAA4B,CAC3B,GAAI,EACF,OAAO,EAAQ,CAAG,EAEpB,IAAM,EAAY,IAAa,EAAI,WAWnC,OAVK,EAUE,EAAC,EAAD,CAAW,KAAM,CAAM,CAAA,GATxB,GAAc,CAAC,GAAiB,IAAI,EAAI,SAAS,IACnD,GAAiB,IAAI,EAAI,SAAS,EAIlC,QAAQ,KAAK,0CAA0C,EAAI,UAAU,EAAE,GAElE,KAGX,EACA,CAAC,EAAY,CAAO,CACtB,EAGM,EAAY,EAAM,MAAM,kBAC1B,EAAW,EAAM,MAAO,EAAM,MAAM,iBAAiB,EACrD,IAAA,GAEJ,OACE,EAAC,EAAO,KAAR,CACgB,eACd,SAAU,EAAM,SACE,mBACL,cACA,cACb,MAAO,EAAM,MACE,gBACJ,YACK,iBACK,+BAErB,EAAC,EAAO,UAAR,CAAA,SACG,EAAY,EAAC,EAAO,OAAR,CAAe,KAAM,CAAY,CAAA,EAAI,EAAC,EAAO,KAAR,CAAa,KAAM,EAAM,MAAM,MAAS,CAAA,CAC1E,CAAA,CACP,CAAA,CAEjB,CAAC,EAED,GAAc,YAAc,gBCtM5B,MAAM,IAAuB,CAAE,cAAmD,CAIhF,GAAM,CAAC,GAAW,EAAS,EAAiB,EAG5C,OAFA,UAA+B,EAAQ,QAAQ,EAAG,CAAC,CAAO,CAAC,EAGzD,EAAC,EAAyB,SAA1B,CAAmC,MAAO,EACvC,UACgC,CAAA,CAEvC,ECKM,GAAQ,CACZ,MALkB,GAClB,EAAC,OAAD,CAAM,GAAI,EAAO,eAAa,aAAe,CAAA,EAK7C,KAfiB,GACjB,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,YAAc,CAAA,EAe3C,OAnBmB,GACnB,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,cAAgB,CAAA,EAmB7C,KAXiB,GACjB,EAAC,OAAD,CAAM,GAAI,EAAO,eAAa,YAAc,CAAA,EAW5C,KAtBiB,GAAqC,EAAC,MAAD,CAAK,GAAI,EAAO,eAAa,OAAS,CAAA,EAuB5F,MAhBkB,GAClB,EAAC,OAAD,CAAM,GAAI,EAAO,eAAa,aAAe,CAAA,CAgB/C,ECvBM,GAAS,CACb,YAAa,GACb,QAAS,GACT,KAAM,GACN,eAAgB,EAChB,aAAc,GACd,YAAa,GACb,KAAM,GACN,IAAK,GACL,QAAS,GACT,SAAU,GACV,QAAS,GACT,QAAS,EACX,ECLM,OACJ,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WAI1D,IAAmB,EAAwB,IAA2B,CAC1E,GAAM,CAAE,SAAU,EAOlB,OANK,EAGD,aAAc,EACT,GAAS,EAAM,SAEjB,GAAc,GAAK,OAAO,WAAW,EAAM,KAAK,EAAE,QALhD,EAMX,EAGM,IAAoB,EAAgC,IAA8B,CACtF,IAAM,EAAW,EAAY,GAAG,EAAE,EAClC,GAAI,CAAC,EACH,MAAU,MAAM,sDAAsD,EAExE,OAAO,EAAY,KAAM,GAAe,GAAgB,EAAY,CAAK,CAAC,GAAK,CACjF,EAKM,IAAsB,CAAE,iBAA8D,CAK1F,GAAM,CAAC,EAAO,GAAY,EAAiB,GAAwB,EAC7D,EAAG,GAAgB,EAAS,CAAC,EAC7B,EAAc,EAA8B,IAAI,EAEhD,EAAe,EAAa,GAAsC,CAEtE,GADA,EAAY,SAAS,WAAW,EAC5B,CAAC,EACH,OAEF,IAAM,EAAW,IAAI,eAAgB,GAAY,CAC/C,IAAM,EAAQ,EAAQ,GAClB,GACF,EAAS,EAAM,YAAY,KAAK,CAEpC,CAAC,EACD,EAAS,QAAQ,CAAO,EACxB,EAAY,QAAU,CACxB,EAAG,CAAC,CAAC,EAEL,UACc,CACV,EAAY,SAAS,WAAW,CAClC,EACA,CAAC,CACH,EAEA,MAAgB,CACd,GAAI,CAAC,GAAc,EACjB,OAEF,IAAM,EAAQ,EAAY,QAAS,GACjC,EAAW,OAAS,UAAW,EAAW,MACtC,CAAC,OAAO,WAAW,EAAW,MAAM,KAAK,CAAC,EAC1C,CAAC,CACP,EACA,GAAI,EAAM,SAAW,EACnB,OAEF,IAAM,MAA2B,CAC/B,EAAc,GAAS,EAAO,CAAC,CACjC,EACA,IAAK,IAAM,KAAQ,EACjB,EAAK,iBAAiB,SAAU,CAAY,EAE9C,UAAa,CACX,IAAK,IAAM,KAAQ,EACjB,EAAK,oBAAoB,SAAU,CAAY,CAEnD,CACF,EAAG,CAAC,CAAW,CAAC,EAEhB,IAAM,EAAS,GAAiB,EAAa,CAAK,EAClD,MAAO,CAAE,WAAY,EAAO,GAAI,eAAc,aAAc,EAAO,MAAO,IAAK,EAAO,EAAG,CAC3F"}
1
+ {"version":3,"file":"index.js","names":["Panel"],"sources":["../src/hooks/persistence.ts","../src/hooks/responsive.ts","../src/hooks/store.ts","../src/hooks/drag-hooks.tsx","../src/hooks/layout-store.ts","../src/lib/tab-insertion.ts","../src/lib/warn-once.ts","../src/components/drag-overlays.tsx","../src/components/drag-adapter.tsx","../src/components/layout-root.tsx","../src/lib/tab-ids.ts","../src/components/tabset/tabset-store.ts","../src/components/tabset/tabset-content.tsx","../src/components/tab-overflow.tsx","../src/components/tabset/tabset-overflow.tsx","../src/hooks/use-tab-overflow.ts","../src/lib/tab-selection.ts","../src/components/tabset/tabset-root.tsx","../src/lib/merge-refs.ts","../src/components/tabset/tabset-tab.tsx","../src/components/tabset-icons.tsx","../src/components/tabset/tabset-tab-controls.tsx","../src/components/tabset/tabset-tablist.tsx","../src/components/tabset/tabset-toolbar.tsx","../src/components/tabset/tabset-view.tsx","../src/components/row-view.tsx","../src/components/layout.tsx","../src/components/dashfoo-layout.tsx","../src/components/drag-root.tsx","../src/components/panel.tsx","../src/components/tabset/tabset.ts"],"sourcesContent":["\"use client\";\n\nimport type { Dashfoo } from \"@dashfoo/core\";\nimport { fromJSON, toJSON } from \"@dashfoo/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\n// A minimal localStorage-shaped backend so layouts can persist to anything: the\n// browser, sessionStorage, an in-memory map, or a custom store.\ntype StorageAdapter = {\n getItem: (key: string) => string | null;\n removeItem: (key: string) => void;\n setItem: (key: string, value: string) => void;\n};\n\nconst memoryStorageAdapter = (): StorageAdapter => {\n const store = new Map<string, string>();\n return {\n getItem: (key) => store.get(key) ?? null,\n removeItem: (key) => {\n store.delete(key);\n },\n setItem: (key, value) => {\n store.set(key, value);\n },\n };\n};\n\n// SSR-safe browser adapter: reads return null and writes are swallowed (with a\n// warning) when storage is unavailable or throws (no window, private mode, quota).\nconst localStorageAdapter: StorageAdapter = {\n getItem: (key) => {\n if (typeof window === \"undefined\") {\n return null;\n }\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n removeItem: (key) => {\n if (typeof window === \"undefined\") {\n return;\n }\n try {\n window.localStorage.removeItem(key);\n } catch {\n // storage unavailable — nothing to remove.\n }\n },\n setItem: (key, value) => {\n if (typeof window === \"undefined\") {\n return;\n }\n try {\n window.localStorage.setItem(key, value);\n } catch (error) {\n // A persistence failure (quota, private mode) must not be silent.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] failed to persist layout\", error);\n }\n },\n};\n\n// A resolved persistence target: where to save and how often. DashfooLayout's\n// `persist` prop normalizes its input to this.\ntype PersistConfig = {\n debounceMs: number;\n key: string;\n storage: StorageAdapter;\n};\n\ntype Persistence = {\n clear: () => void;\n // undefined only in controlled mode, where no defaultModel seeds the load.\n initialModel: Dashfoo | undefined;\n save: (model: Dashfoo) => void;\n};\n\n// The load/save half of persistence, behind the DashfooLayout `persist` prop.\n// Loads the saved model once (validated via serialize.ts, falling back to\n// `defaultModel` on miss or corruption) and\n// debounce-saves every change. Resetting the live model is the caller's job —\n// this primitive only owns the storage side. A null config makes it a no-op, so\n// callers can run the hook unconditionally.\nconst usePersistence = (\n config: PersistConfig | null,\n defaultModel: Dashfoo | undefined,\n): Persistence => {\n // Refs keep the callbacks stable while always seeing the latest config; synced\n // in an effect (never written during render).\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n });\n\n // Load the saved model once. The initializer stays pure (no writes during\n // render); a corrupt stored value is pruned in the mount effect below.\n const [initialModel] = useState<Dashfoo | undefined>(() => {\n if (config === null || defaultModel === undefined) {\n return defaultModel;\n }\n const raw = config.storage.getItem(config.key);\n if (raw === null) {\n return defaultModel;\n }\n try {\n return fromJSON(raw);\n } catch {\n return defaultModel;\n }\n });\n\n useEffect(() => {\n const current = configRef.current;\n if (current === null) {\n return;\n }\n const raw = current.storage.getItem(current.key);\n if (raw === null) {\n return;\n }\n try {\n fromJSON(raw);\n } catch (error) {\n // Mirror the setItem warn: a discarded corrupt layout must not be silent.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] discarding unreadable persisted layout\", error);\n current.storage.removeItem(current.key);\n }\n }, []);\n\n // We load + prune once, but save/flush/clear target the *current* key. Changing\n // config.key for a mounted layout would save the stale model under the new key\n // and clobber it; warn instead of doing that silently. Remounting is the fix.\n const loadedKey = useRef(config?.key);\n useEffect(() => {\n if (config !== null && config.key !== loadedKey.current) {\n // oxlint-disable-next-line no-console\n console.warn(\n \"[dashfoo] persist key changed for a mounted layout; the displayed layout still reflects the previous key — remount (key={persistKey}) to load the new key\",\n );\n loadedKey.current = config.key;\n }\n }, [config]);\n\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The pending write carries the key and storage it was produced for, so a\n // config change while a save is queued cannot write the old value into the\n // new target.\n const pending = useRef<{ key: string; storage: StorageAdapter; value: string } | null>(null);\n\n const flush = useCallback((): void => {\n if (timer.current !== null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n if (pending.current !== null) {\n pending.current.storage.setItem(pending.current.key, pending.current.value);\n pending.current = null;\n }\n }, []);\n\n const save = useCallback(\n (next: Dashfoo): void => {\n const current = configRef.current;\n if (current === null) {\n return;\n }\n pending.current = { key: current.key, storage: current.storage, value: toJSON(next) };\n if (timer.current !== null) {\n clearTimeout(timer.current);\n }\n timer.current = setTimeout(flush, current.debounceMs);\n },\n [flush],\n );\n\n // Flush a pending save on unmount so the last change is never lost — and on\n // page teardown: a reload or navigation never unmounts React, so the debounced\n // timer dies with the page. pagehide is the teardown signal (bfcache-safe);\n // visibilitychange→hidden covers backgrounded tabs killed without a pagehide\n // (mobile). flush() is idempotent, so overlapping signals are harmless.\n useEffect(() => {\n const handlePageHide = (): void => {\n flush();\n };\n const handleVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") {\n flush();\n }\n };\n window.addEventListener(\"pagehide\", handlePageHide);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () => {\n window.removeEventListener(\"pagehide\", handlePageHide);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n flush();\n };\n }, [flush]);\n\n const clear = useCallback((): void => {\n if (timer.current !== null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n pending.current = null;\n const current = configRef.current;\n if (current !== null) {\n current.storage.removeItem(current.key);\n }\n }, []);\n\n // Memoized so callers can hold the whole object in hook deps without their\n // memoization dissolving on every render.\n return useMemo(() => ({ clear, initialModel, save }), [clear, initialModel, save]);\n};\n\nexport { localStorageAdapter, memoryStorageAdapter, usePersistence };\nexport type { Persistence, PersistConfig, StorageAdapter };\n","\"use client\";\n\nimport type { Dashfoo } from \"@dashfoo/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\ntype Breakpoint = {\n // When true, the active breakpoint locks every structural interaction (tab and\n // tabset drag, split resize) — the lock-on-mobile model. Pair it with a stacked\n // model so the narrow view is read-only and tap-navigable. Defaults to false.\n compact?: boolean;\n id: string;\n model: Dashfoo;\n query?: { maxWidth: number } | { media: string };\n};\n\ntype UseResponsiveModelOptions = { breakpoints: Array<Breakpoint> };\n\ntype ResponsiveModel = {\n breakpoint: string;\n containerRef: (element: HTMLElement | null) => void;\n draggableTabs: boolean;\n draggableTabsets: boolean;\n isCompact: boolean;\n model: Dashfoo;\n resizableSplits: boolean;\n};\n\nconst hasMatchMedia = (): boolean =>\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n\n// Whether a breakpoint applies at a given container width. No query = catch-all;\n// a maxWidth matches at or below the width; a media query consults matchMedia.\nconst matchBreakpoint = (breakpoint: Breakpoint, width: number): boolean => {\n const { query } = breakpoint;\n if (!query) {\n return true;\n }\n if (\"maxWidth\" in query) {\n return width <= query.maxWidth;\n }\n return hasMatchMedia() && window.matchMedia(query.media).matches;\n};\n\n// The first breakpoint that matches, else the last (the expected catch-all).\nconst activeBreakpoint = (breakpoints: Array<Breakpoint>, width: number): Breakpoint => {\n const fallback = breakpoints.at(-1);\n if (!fallback) {\n throw new Error(\"useResponsiveModel requires at least one breakpoint.\");\n }\n return breakpoints.find((breakpoint) => matchBreakpoint(breakpoint, width)) ?? fallback;\n};\n\n// Tracks a single element's content-box width via ResizeObserver. Seeds\n// POSITIVE_INFINITY so server and first client render agree on the widest\n// breakpoint (no hydration mismatch, no compact flash on desktop); the observer\n// supplies the real width on mount. The returned ref callback is stable.\nconst useContainerWidth = (): [(element: HTMLElement | null) => void, number] => {\n const [width, setWidth] = useState<number>(Number.POSITIVE_INFINITY);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const containerRef = useCallback((element: HTMLElement | null): void => {\n observerRef.current?.disconnect();\n if (!element) {\n return;\n }\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry) {\n setWidth(entry.contentRect.width);\n }\n });\n observer.observe(element);\n observerRef.current = observer;\n }, []);\n\n useEffect(\n () => () => {\n observerRef.current?.disconnect();\n },\n [],\n );\n\n return [containerRef, width];\n};\n\n// Picks a model for the active breakpoint, keyed off the container's own width\n// (ResizeObserver) and/or media queries, and derives the structural-interaction\n// flags from the breakpoint's `compact`. Feed model + flags onto DashfooLayout\n// (or Layout.Root) as reactive props and attach containerRef to the wrapper —\n// no key/remount, so store, history, and tab state survive a breakpoint cross.\nconst useResponsiveModel = ({ breakpoints }: UseResponsiveModelOptions): ResponsiveModel => {\n const [containerRef, width] = useContainerWidth();\n const [, setMediaTick] = useState(0);\n\n useEffect(() => {\n if (!hasMatchMedia()) {\n return;\n }\n const lists = breakpoints.flatMap((breakpoint) =>\n breakpoint.query && \"media\" in breakpoint.query\n ? [window.matchMedia(breakpoint.query.media)]\n : [],\n );\n if (lists.length === 0) {\n return;\n }\n const handleChange = (): void => {\n setMediaTick((tick) => tick + 1);\n };\n for (const list of lists) {\n list.addEventListener(\"change\", handleChange);\n }\n return () => {\n for (const list of lists) {\n list.removeEventListener(\"change\", handleChange);\n }\n };\n }, [breakpoints]);\n\n const active = activeBreakpoint(breakpoints, width);\n const isCompact = active.compact === true;\n return {\n breakpoint: active.id,\n containerRef,\n draggableTabs: !isCompact,\n draggableTabsets: !isCompact,\n isCompact,\n model: active.model,\n resizableSplits: !isCompact,\n };\n};\n\nexport { activeBreakpoint, matchBreakpoint, useContainerWidth, useResponsiveModel };\nexport type { Breakpoint, ResponsiveModel, UseResponsiveModelOptions };\n","\"use client\";\n\nimport type { Action, Dashfoo } from \"@dashfoo/core\";\nimport { canRedo, canUndo, dashfooMachine, normalize, reducer } from \"@dashfoo/core\";\nimport { useActorRef, useSelector } from \"@xstate/react\";\nimport { useCallback, useEffect, useMemo } from \"react\";\n\ntype DashfooStore = {\n canRedo: () => boolean;\n canUndo: () => boolean;\n dispatch: (action: Action) => void;\n model: Dashfoo;\n redo: () => void;\n setModel: (model: Dashfoo) => void;\n undo: () => void;\n};\n\ntype UseDashfooStoreOptions = {\n defaultModel?: Dashfoo;\n model?: Dashfoo;\n onAction?: (action: Action) => Action | null;\n onActiveTabsetChange?: (tabsetId: string | undefined) => void;\n onMaximizedTabsetChange?: (tabsetId: string | undefined) => void;\n onModelChange?: (model: Dashfoo, action?: Action) => void;\n};\n\n// Binds a dashfooMachine actor to React. Uncontrolled (defaultModel) lets the\n// actor own the document with full undo/redo; controlled (model) makes the prop\n// the source of truth and routes every change through onModelChange. The\n// actor/inspector stays in sync once the host round-trips `after` back into the\n// `model` prop (it is not synced synchronously within dispatch).\nconst useDashfooStore = (options: UseDashfooStoreOptions): DashfooStore => {\n const {\n defaultModel,\n model: controlledModel,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange,\n } = options;\n const initialModel = controlledModel ?? defaultModel;\n if (initialModel === undefined) {\n throw new Error(\"useDashfooStore requires either a `model` or a `defaultModel`.\");\n }\n\n // Normalize at the boundary so a host-supplied model satisfies the same\n // invariants (clamped selection, no empty tabsets, live maximizedTabsetId) the\n // reducer guarantees — every entry point holds a canonical model.\n const actorRef = useActorRef(dashfooMachine, { input: { model: normalize(initialModel) } });\n const history = useSelector(actorRef, (snapshot) => snapshot.context.history);\n\n useEffect(() => {\n if (controlledModel !== undefined) {\n actorRef.send({ model: normalize(controlledModel), type: \"SET_MODEL\" });\n }\n }, [actorRef, controlledModel]);\n\n const model = controlledModel ?? history.present;\n\n // Single notification path so dispatch and undo/redo stay in lockstep. The\n // Object.is guard makes a no-op transition (e.g. undo at the bottom of the\n // stack, where the history helper returns the same object) emit nothing, so we\n // never write spurious persistence on a change that did not happen.\n const notify = useCallback(\n (before: Dashfoo, after: Dashfoo, action?: Action) => {\n if (Object.is(before, after)) {\n return;\n }\n onModelChange?.(after, action);\n if (after.activeTabsetId !== before.activeTabsetId) {\n onActiveTabsetChange?.(after.activeTabsetId);\n }\n if (after.maximizedTabsetId !== before.maximizedTabsetId) {\n onMaximizedTabsetChange?.(after.maximizedTabsetId);\n }\n },\n [onModelChange, onActiveTabsetChange, onMaximizedTabsetChange],\n );\n\n const dispatch = useCallback(\n (action: Action) => {\n // onAction may veto (null) or replace the action before it mutates anything.\n const resolved = onAction ? onAction(action) : action;\n if (!resolved) {\n return;\n }\n // Uncontrolled: the actor owns history. Controlled: derive the next model by\n // hand (the host round-trips it through the model prop). normalize the\n // controlled `before` too, else a normalization-only id change fires spuriously.\n const transition = ((): { after: Dashfoo; before: Dashfoo } => {\n if (controlledModel === undefined) {\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ action: resolved, type: \"DISPATCH\" });\n return { after: actorRef.getSnapshot().context.history.present, before };\n }\n return { after: reducer(controlledModel, resolved), before: normalize(controlledModel) };\n })();\n notify(transition.before, transition.after, resolved);\n },\n [actorRef, controlledModel, notify, onAction],\n );\n\n const undo = useCallback(() => {\n // actor history is always empty in controlled mode, so undo would be a dead\n // send that still emitted a spurious onModelChange.\n if (controlledModel !== undefined) {\n return;\n }\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ type: \"UNDO\" });\n const after = actorRef.getSnapshot().context.history.present;\n notify(before, after);\n }, [actorRef, controlledModel, notify]);\n\n const redo = useCallback(() => {\n if (controlledModel !== undefined) {\n return;\n }\n const before = actorRef.getSnapshot().context.history.present;\n actorRef.send({ type: \"REDO\" });\n const after = actorRef.getSnapshot().context.history.present;\n notify(before, after);\n }, [actorRef, controlledModel, notify]);\n\n // Replace the whole document, resetting undo history. Drives an uncontrolled\n // reset (e.g. clearing a persisted layout back to its default) without a\n // remount; normalized at the boundary like every other entry point.\n const setModel = useCallback(\n (next: Dashfoo) => {\n actorRef.send({ model: normalize(next), type: \"SET_MODEL\" });\n },\n [actorRef],\n );\n\n // Functions, not booleans: they read the live actor snapshot so a caller\n // reading them right after undo/redo (e.g. inside onModelChange) sees the fresh\n // value, not the one-render-stale useSelector result. Memoized so callers can\n // hold the whole store in hook deps without their memoization dissolving on\n // every render.\n return useMemo(\n () => ({\n canRedo: () => canRedo(actorRef.getSnapshot().context.history),\n canUndo: () => canUndo(actorRef.getSnapshot().context.history),\n dispatch,\n model,\n redo,\n setModel,\n undo,\n }),\n [actorRef, dispatch, model, redo, setModel, undo],\n );\n};\n\nexport { useDashfooStore };\nexport type { DashfooStore, UseDashfooStoreOptions };\n","\"use client\";\n\nimport type { DragSubject, DropIntent, TabNode } from \"@dashfoo/core\";\nimport { Accessibility, Draggable, DragDropManager, Feedback, PointerSensor } from \"@dnd-kit/dom\";\nimport type { Context } from \"react\";\nimport { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\n// Shared drag contexts + the draggable/droppable hooks. Lives apart from\n// drag-adapter.tsx so the contexts can be owned here without a circular import:\n// drag-adapter imports these for DragProvider and re-exports the hooks, and this\n// module never reaches back into drag-adapter.\n\nconst INTERACTIVE_SELECTOR = `\n input:not([disabled]),\n select:not([disabled]),\n textarea:not([disabled]),\n button:not([disabled]),\n a[href],\n [contenteditable]:not([contenteditable=\"false\"])\n`;\n\n// The sensor's default preventActivation vetoes any pointerdown whose target is\n// an element child of the draggable: closest() walks up to the trigger button\n// itself and calls it \"interactive\". Plain-text labels dodge that (text nodes\n// are not elements), but custom labels render elements inside the trigger and\n// would silently lose dragging. Allow the draggable itself as the handle while\n// still refusing genuinely interactive children nested in a label.\nconst preventActivation = (event: PointerEvent, source: Draggable): boolean => {\n const { target } = event;\n if (!(target instanceof Element)) {\n return false;\n }\n const interactive = target.closest(INTERACTIVE_SELECTOR);\n return interactive !== null && interactive !== source.element;\n};\n\n// Plugins drop the screen-reader announcer and the visual Feedback plugin (the\n// adapter renders its own preview). Sensors keep only a configured\n// PointerSensor — the KeyboardSensor's nudge model double-binds the arrow keys\n// the tab strip already uses for roving-tabindex navigation, so keyboard\n// docking needs its own interaction design rather than that sensor.\nconst createDragManager = (): DragDropManager =>\n new DragDropManager({\n plugins: (defaults) =>\n defaults.filter((plugin) => plugin !== Accessibility && plugin !== Feedback),\n sensors: () => [PointerSensor.configure({ preventActivation })],\n });\n\ntype DragContextValue = {\n manager: DragDropManager;\n registerTabset: (id: string, element: HTMLElement | null) => void;\n};\n\nconst DragContext = createContext<DragContextValue | null>(null);\n\n// A manager owned by DashfooDragProvider, shared between a layout and external\n// tab sources outside it so drags can cross that boundary. The annotation keeps\n// declaration emit from expanding @dnd-kit's non-exported generic defaults.\nconst SharedDragManagerContext: Context<DragDropManager | null> =\n createContext<DragDropManager | null>(null);\n\n// The live drag subject and drop intent ride a scoped zustand store instead of a\n// context value, so a drag start/end re-renders only the parts that select the\n// subject — never the provider subtree. The store is owned by the nearest\n// DashfooDragProvider when one exists (so anything under it, layout or not, can\n// observe drags) and by the DragLayer otherwise; the dragDockMachine\n// subscription feeds it. Intent updates are value-guarded by the adapter\n// (sameDropIntent) so OVER pulses only notify when the resolved drop changes.\n// intentOwner disambiguates layouts sharing one store: each layer hit-tests only\n// its own tabsets, so a null intent from one layer must not erase the live\n// intent another layer resolved.\ntype DragSubjectState = {\n intent: DropIntent | null;\n intentOwner: string | null;\n subject: DragSubject | null;\n};\n\ntype DragSubjectStore = StoreApi<DragSubjectState>;\n\nconst createDragSubjectStore = (): DragSubjectStore =>\n createStore<DragSubjectState>(() => ({ intent: null, intentOwner: null, subject: null }));\n\n// Whether two intents resolve the same drop. Intents are rebuilt on every\n// pointer move, so identity alone would notify subscribers on each pulse.\nconst sameDropIntent = (a: DropIntent | null, b: DropIntent | null): boolean =>\n a === b ||\n (a !== null &&\n b !== null &&\n a.index === b.index &&\n a.location === b.location &&\n a.targetId === b.targetId);\n\n// Whether two subjects describe the same drag. Layouts sharing a manager each\n// build their own subject object for the same drag, so identity alone would\n// write twice per drag start.\nconst sameDragSubject = (a: DragSubject | null, b: DragSubject | null): boolean =>\n a === b || (a !== null && b !== null && a.id === b.id && a.kind === b.kind);\n\n// Stable empty store so useDragSubject keeps an unconditional hook order (and\n// returns null) outside a DragLayer — parts must keep working drag-free.\nconst nullSubjectStore = createDragSubjectStore();\n\nconst DragSubjectStoreContext: Context<DragSubjectStore | null> =\n createContext<DragSubjectStore | null>(null);\n\n// Tracks a Draggable's element across renders without rebuilding it. Returned by\n// the draggable hooks; their effects own the Draggable's lifecycle.\ntype DraggableHandle = {\n draggableRef: { current: Draggable | null };\n elementRef: { current: Element | null };\n ref: (element: Element | null) => void;\n};\n\nconst useDraggableHandle = (): DraggableHandle => {\n const elementRef = useRef<Element | null>(null);\n const draggableRef = useRef<Draggable | null>(null);\n const ref = useCallback((element: Element | null): void => {\n elementRef.current = element;\n if (draggableRef.current) {\n draggableRef.current.element = element ?? undefined;\n }\n }, []);\n return { draggableRef, elementRef, ref };\n};\n\n// What a draggable carries: the dnd-kit id and the stable data (drag kind +\n// tabset id), both derived from identity. The chip label is passed separately so\n// it never sits in the construction effect's deps. data must be referentially\n// stable (the wrappers memoize it) so the construction effect tracks it without\n// rebuilding on every render.\ntype DraggableDescriptor = { data: Record<string, unknown>; id: string };\n\n// Owns the full Draggable lifecycle shared by the tab and tabset hooks. The\n// construction effect is keyed on identity only (manager, id, disabled, the\n// stable data) — never on the chip label — so a tab click or rename can't tear\n// down and rebuild the live instance. The label is folded into the live data by\n// a separate effect, since data is only read at drag start; both effects fire in\n// the same mount commit, so the label is current well before any pointer drag.\n// `Draggable.data` is a public accessor in @dnd-kit/abstract, so the in-place\n// update is type-safe.\nconst useDraggableEntity = (\n descriptor: DraggableDescriptor,\n disabled: boolean,\n label: string,\n): { ref: (element: Element | null) => void } => {\n const context = useContext(DragContext);\n // Internal draggables (tabs, grips) live under DragProvider; external tab\n // sources sit outside it and reach the manager via DashfooDragProvider.\n const sharedManager = useContext(SharedDragManagerContext);\n const manager = context?.manager ?? sharedManager;\n const { draggableRef, elementRef, ref } = useDraggableHandle();\n const { data, id } = descriptor;\n\n useEffect(() => {\n if (!manager || disabled) {\n return undefined;\n }\n const draggable = new Draggable({ data: { ...data, label: \"\" }, id }, manager);\n draggable.element = elementRef.current ?? undefined;\n draggableRef.current = draggable;\n return () => {\n draggable.destroy();\n draggableRef.current = null;\n };\n }, [data, disabled, draggableRef, elementRef, id, manager]);\n\n useEffect(() => {\n const draggable = draggableRef.current;\n if (draggable) {\n draggable.data = { ...data, label };\n }\n }, [data, draggableRef, label]);\n\n return { ref };\n};\n\nconst useTabDraggable = (\n tabId: string,\n disabled = false,\n label = \"\",\n): { ref: (element: Element | null) => void } => {\n const data = useMemo(() => ({ type: \"tab\" }), []);\n return useDraggableEntity({ data, id: tabId }, disabled, label);\n};\n\n// The whole tabset is draggable from its grip. A distinct dnd-kit id (grip-*)\n// avoids colliding with the tabset's own registered id; the real tabset id rides\n// in `data` and becomes the moveTabset subject. The label feeds the overlay chip.\nconst useTabsetDraggable = (\n tabsetId: string,\n disabled = false,\n label = \"\",\n): { ref: (element: Element | null) => void } => {\n const data = useMemo(() => ({ tabsetId, type: \"tabset\" }), [tabsetId]);\n return useDraggableEntity({ data, id: `grip-${tabsetId}` }, disabled, label);\n};\n\n// Registers the tabset element so the adapter can hit-test the pointer against it.\nconst useTabsetDroppable = (tabsetId: string): { ref: (element: HTMLElement | null) => void } => {\n const context = useContext(DragContext);\n const ref = useCallback(\n (element: HTMLElement | null): void => {\n context?.registerTabset(tabsetId, element);\n },\n [context, tabsetId],\n );\n return { ref };\n};\n\ntype ExternalTabSourceOptions = {\n // Must return a fresh TabNode (unique id) per call — each drop inserts a new\n // tab, and duplicate ids would fail the model's invariants.\n createTab: () => TabNode;\n disabled?: boolean;\n label?: string;\n};\n\n// Makes any element outside a layout a drag source that inserts a new tab when\n// dropped on a DashfooLayout under the same DashfooDragProvider. The adapter\n// recognizes the { type: \"external\" } payload and commits an addNode instead of\n// a move. The ref-indirection keeps `data` referentially stable (the\n// construction effect tracks it) while drags always call the latest createTab.\nconst useExternalTabSource = ({\n createTab,\n disabled = false,\n label = \"\",\n}: ExternalTabSourceOptions): { ref: (element: Element | null) => void } => {\n const id = useId();\n const createTabRef = useRef(createTab);\n // Dependency-less on purpose: a plain ref sync so drags always call the latest createTab.\n useEffect(() => {\n createTabRef.current = createTab;\n });\n const data = useMemo(\n () => ({ createTab: (): TabNode => createTabRef.current(), type: \"external\" }),\n [],\n );\n return useDraggableEntity({ data, id: `external-${id}` }, disabled, label);\n};\n\nconst useDragSubject = (): DragSubject | null => {\n const store = useContext(DragSubjectStoreContext);\n return useStore(store ?? nullSubjectStore, (state) => state.subject);\n};\n\n// The live drop intent: where the drag would land if dropped right now, null\n// when nothing is dragging or the pointer is over no valid target (a no-op\n// drop, a non-editable layout, or empty space). Lets consumers render their own\n// drop indicators alongside the built-in one.\nconst useDropIntent = (): DropIntent | null => {\n const store = useContext(DragSubjectStoreContext);\n return useStore(store ?? nullSubjectStore, (state) => state.intent);\n};\n\nexport type { DragContextValue, DragSubjectStore, ExternalTabSourceOptions };\nexport {\n createDragManager,\n createDragSubjectStore,\n DragContext,\n DragSubjectStoreContext,\n sameDragSubject,\n sameDropIntent,\n SharedDragManagerContext,\n useDragSubject,\n useDropIntent,\n useExternalTabSource,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n};\n","\"use client\";\n\nimport type { Action, SnapConfig, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport type { ReactNode } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\n// The layout-wide configuration every part below <Layout.Root> coordinates\n// through: resolved chrome flags, slot renderers, and the model dispatcher.\n// A scoped zustand store rather than a plain context value so parts subscribe\n// per-field — changing one flag re-renders only the parts that select it.\ntype LayoutState = {\n closableTabs: boolean;\n dispatch: (action: Action) => void;\n draggableTabs: boolean;\n draggableTabsets: boolean;\n editable: boolean;\n keepMounted: boolean;\n maximizable: boolean;\n maximizedTabsetId: string | undefined;\n renamableTabs: boolean;\n renderTab: (tab: TabNode) => ReactNode;\n renderTabLabel: ((tab: TabNode) => ReactNode) | undefined;\n renderTabsetToolbar: ((tabset: TabsetNode) => ReactNode) | undefined;\n resizableSplits: boolean;\n // Magnetic snap defaults for split resize; null = no snapping. A row may still\n // override this via its own `snap` attribute (read in row-view).\n snap: SnapConfig | null;\n splitDock: boolean;\n tabLocation: \"bottom\" | \"top\";\n tabsetMinSize: number;\n tabStripEnabled: boolean;\n};\n\ntype LayoutStore = StoreApi<LayoutState>;\n\nconst createLayoutStore = (initial: LayoutState): LayoutStore =>\n createStore<LayoutState>(() => initial);\n\n// Carries only the store instance — state always flows through selectors.\nconst LayoutStoreContext = createContext<LayoutStore | null>(null);\n\nconst useLayout = <T>(selector: (state: LayoutState) => T): T => {\n const store = useContext(LayoutStoreContext);\n if (store === null) {\n throw new Error(\"dashfoo components must be rendered inside <DashfooLayout> or <Layout.Root>.\");\n }\n return useStore(store, selector);\n};\n\nexport { createLayoutStore, LayoutStoreContext, useLayout };\nexport type { LayoutState, LayoutStore };\n","import type { Point, Rect } from \"@dashfoo/core\";\n\n// Pure drag math for the tab strip — rects in, geometry out. The drag adapter\n// gathers the rects from the DOM and feeds them here, so this stays unit-testable\n// without a live drag.\n\ntype Zone = { height: number; width: number; x: number; y: number };\n\n// Is the point inside the rect? (Rect is DOMRect-compatible: x/y/width/height.)\nconst pointInRect = (point: Point, rect: Rect): boolean =>\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height;\n\n// Which slot in the strip the pointer is over: the first tab whose midpoint is\n// right of the pointer, else the end.\nconst insertionIndex = (rects: ReadonlyArray<Rect>, pointerX: number): number => {\n const found = rects.findIndex((rect) => pointerX < rect.x + rect.width / 2);\n return found === -1 ? rects.length : found;\n};\n\n// How wide the insertion line is, centered on the slot boundary.\nconst INSERTION_LINE_WIDTH = 4;\n\n// The thin vertical line marking where a tab will be inserted. Measured against\n// whole tab-item rects (label + close), so the \"after the last tab\" position sits\n// past the last close button rather than between the label and the close.\n// Clamped so the line never leaves the strip.\nconst insertionLineRect = (\n stripRect: Rect,\n itemRects: ReadonlyArray<Rect>,\n index: number,\n): Zone => {\n const at = itemRects[index];\n const last = itemRects.at(-1);\n const x = at?.x ?? (last ? last.x + last.width : stripRect.x);\n const left = x - INSERTION_LINE_WIDTH / 2;\n return {\n height: stripRect.height,\n width: INSERTION_LINE_WIDTH,\n x: Math.max(stripRect.x, Math.min(left, stripRect.x + stripRect.width - INSERTION_LINE_WIDTH)),\n y: stripRect.y,\n };\n};\n\n// Whether a drop should commit, or is a no-op to be suppressed: dragging a tabset\n// onto itself (grip id is `grip-${tabsetId}`), or the sole tab of a tabset back\n// onto that same tabset — both change nothing once empties collapse.\nconst shouldAllowDrop = (\n draggedId: string | undefined,\n targetId: string,\n tabIds: ReadonlyArray<string>,\n): boolean => {\n if (draggedId === `grip-${targetId}`) {\n return false;\n }\n if (tabIds.length === 1 && tabIds[0] === draggedId) {\n return false;\n }\n return true;\n};\n\nexport { insertionIndex, insertionLineRect, pointInRect, shouldAllowDrop };\nexport type { Zone };\n","// Dev warnings for soft misuse (duplicate parts, orphan tabs, missing editors)\n// fire once per key so a render loop can't flood the console, mirroring the\n// unregistered-component warning in dashfoo-layout.\nconst warned = new Set<string>();\n\nconst warnOnce = (key: string, message: string): void => {\n if (warned.has(key)) {\n return;\n }\n warned.add(key);\n // oxlint-disable-next-line no-console\n console.warn(`[dashfoo] ${message}`);\n};\n\nexport { warnOnce };\n","\"use client\";\n\nimport type { dragDockMachine } from \"@dashfoo/core\";\nimport { zoneRect } from \"@dashfoo/core\";\nimport { useSelector } from \"@xstate/react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport type { ActorRefFrom } from \"xstate\";\n\nimport type { Zone } from \"../lib/tab-insertion\";\nimport { insertionLineRect } from \"../lib/tab-insertion\";\n\n// The drag adapter's two overlays: the dock indicator (where the drop will\n// land) and the pointer-anchored preview chip. Split from drag-adapter.tsx to\n// keep that module focused on wiring @dnd-kit to the drag machine.\n\ntype DragActor = ActorRefFrom<typeof dragDockMachine>;\n\nconst overlayBase: CSSProperties = {\n boxSizing: \"border-box\",\n pointerEvents: \"none\",\n position: \"fixed\",\n // Overridable so a theme can drop it under prefers-reduced-motion.\n transition: \"var(--dashfoo-dock-transition, left 60ms, top 60ms, width 60ms, height 60ms)\",\n zIndex: 9999,\n};\n\n// Only position/size are fixed inline; every visual property is an overridable\n// CSS var with a neutral fallback, so a consumer's data-dashfoo=\"dock-indicator\"\n// theme fully owns the look.\nconst paneStyle = (zone: Zone): CSSProperties => ({\n ...overlayBase,\n background: \"var(--dashfoo-dock-fill, oklch(0.556 0 0 / 0.18))\",\n border:\n \"var(--dashfoo-dock-border-width, 1px) solid var(--dashfoo-dock-border, oklch(0.708 0 0 / 0.75))\",\n borderRadius: \"var(--dashfoo-dock-radius, 6px)\",\n height: zone.height,\n left: zone.x,\n top: zone.y,\n width: zone.width,\n});\n\n// The insertion line shares the pane's fill and border, shrunk to a thin caret\n// between tabs, so the strip indicator and the dock panes read as one family.\nconst lineStyle = (zone: Zone): CSSProperties => ({\n ...paneStyle(zone),\n borderRadius: \"var(--dashfoo-dock-line-radius, 2px)\",\n});\n\n// Whole-tab-item rects (label + close button), excluding the dragged tab. The\n// insertion line lands on these boundaries so the \"after the last tab\" position\n// sits past the close button, not between the label and the close.\nconst tabItemRects = (strip: Element, excludeId?: string): Array<DOMRect> =>\n [...strip.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab-item\"]')].flatMap((item) =>\n item.querySelector<HTMLElement>('[data-dashfoo=\"tab\"]')?.dataset.tabId === excludeId\n ? []\n : [item.getBoundingClientRect()],\n );\n\n// A \"where it will land\" indicator driven off the machine's live intent: an\n// insertion line in the tab bar for a stack, the matching content half for a\n// split. One unkeyed div, so React reuses the DOM node and the position/size\n// transition morphs between the two shapes instead of remounting.\nconst DockIndicator = ({\n actorRef,\n getTabsetElement,\n}: {\n actorRef: DragActor;\n getTabsetElement: (id: string) => HTMLElement | undefined;\n}): ReactNode => {\n const intent = useSelector(actorRef, (snapshot) => snapshot.context.intent);\n const draggedId = useSelector(actorRef, (snapshot) => snapshot.context.subject?.id);\n if (!intent) {\n return null;\n }\n const element = getTabsetElement(intent.targetId);\n if (!element) {\n return null;\n }\n if (intent.location === \"center\") {\n const strip = element.querySelector('[data-dashfoo=\"tabstrip\"]');\n if (strip) {\n const line = insertionLineRect(\n strip.getBoundingClientRect(),\n tabItemRects(strip, draggedId),\n intent.index ?? 0,\n );\n return <div data-dashfoo=\"dock-indicator\" style={lineStyle(line)} />;\n }\n }\n const zone = zoneRect(element.getBoundingClientRect(), intent.location);\n return <div data-dashfoo=\"dock-indicator\" style={paneStyle(zone)} />;\n};\n\nconst previewStyle: CSSProperties = { left: 0, position: \"fixed\", top: 0, zIndex: 9999 };\n\ntype DragPreviewState = { label: string; x: number; y: number };\n\n// The pointer-anchored chip that follows the cursor while dragging.\nconst DragPreview = ({\n overlayRef,\n preview,\n}: {\n overlayRef: (element: HTMLDivElement | null) => void;\n preview: DragPreviewState | null;\n}): ReactNode => {\n if (!preview) {\n return null;\n }\n return (\n <div\n data-dashfoo=\"drag-preview\"\n ref={overlayRef}\n style={{ ...previewStyle, transform: `translate(${preview.x}px, ${preview.y}px)` }}\n >\n {preview.label}\n </div>\n );\n};\n\nexport { DockIndicator, DragPreview };\nexport type { DragPreviewState };\n","\"use client\";\n\nimport type { Action, DockLocation, DragSubject, DropIntent, Point } from \"@dashfoo/core\";\nimport { dragDockMachine, resolveDockTarget, tabNodeSchema } from \"@dashfoo/core\";\nimport type { DragEndEvent, DragMoveEvent, DragStartEvent } from \"@dnd-kit/dom\";\nimport { useActorRef } from \"@xstate/react\";\nimport type { ReactNode } from \"react\";\nimport {\n useCallback,\n useContext,\n useEffect,\n useId,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport type { DragContextValue } from \"../hooks/drag-hooks\";\nimport {\n createDragManager,\n createDragSubjectStore,\n DragContext,\n DragSubjectStoreContext,\n sameDragSubject,\n sameDropIntent,\n SharedDragManagerContext,\n useDragSubject,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n} from \"../hooks/drag-hooks\";\nimport { LayoutStoreContext } from \"../hooks/layout-store\";\nimport { insertionIndex, pointInRect, shouldAllowDrop } from \"../lib/tab-insertion\";\nimport { warnOnce } from \"../lib/warn-once\";\n\nimport type { DragPreviewState } from \"./drag-overlays\";\nimport { DockIndicator, DragPreview } from \"./drag-overlays\";\n\n// This module is the drag adapter: it (with ./drag-hooks) is where @dnd-kit is\n// touched. It wires the framework-agnostic @dnd-kit/dom core (no React bindings)\n// to the already unit-tested dragDockMachine — the PointerSensor supplies\n// activation + a live pointer, the adapter hit-tests that pointer against the\n// registered tabsets, and the machine owns the lifecycle and emits a moveNode\n// COMMIT forwarded via onCommit. The drag preview is our own overlay, so there is\n// no Feedback plugin, no placeholder clone, and none of the CSS workarounds those\n// required.\n\n// The dragged tab is excluded so its own slot never counts toward the order —\n// the insertion index and line are measured against the tabs it will land among.\nconst tabRects = (strip: Element, excludeId?: string): Array<DOMRect> =>\n [...strip.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].flatMap((tab) =>\n tab.dataset.tabId === excludeId ? [] : [tab.getBoundingClientRect()],\n );\n\n// The tab strip is always a \"stack as a tab\" target, with an insertion index that\n// places the tab at a specific slot. Only the content area below it resolves to\n// the center/split zones via resolveDockTarget (content center appends). The\n// adapter gathers rects from the DOM; the pure math lives in ./tab-insertion.\nconst intentForTabset = (\n id: string,\n element: HTMLElement,\n point: Point,\n draggedId?: string,\n): DropIntent => {\n const strip = element.querySelector('[data-dashfoo=\"tabstrip\"]');\n if (strip && pointInRect(point, strip.getBoundingClientRect())) {\n return {\n index: insertionIndex(tabRects(strip, draggedId), point.x),\n location: \"center\",\n targetId: id,\n };\n }\n const target = resolveDockTarget(point, element.getBoundingClientRect());\n const location: DockLocation = target.kind === \"tab\" ? \"center\" : `split-${target.edge}`;\n if (location === \"center\" && strip) {\n return { index: tabRects(strip, draggedId).length, location, targetId: id };\n }\n return { location, targetId: id };\n};\n\n// The pointer-anchored preview chip follows the cursor at this offset.\nconst PREVIEW_OFFSET: Point = { x: 12, y: 8 };\n\nconst labelOf = (source: { data?: Record<string, unknown> } | null): string => {\n const raw = source?.data?.label;\n return typeof raw === \"string\" ? raw : \"\";\n};\n\nconst isTabFactory = (value: unknown): value is () => unknown => typeof value === \"function\";\n\n// A tabset grip carries { tabsetId, type: \"tabset\" }; an external source\n// (useExternalTabSource) carries { createTab, type: \"external\" } and its tab is\n// built and validated at drag start; a plain tab carries its id. null aborts\n// the drag — the machine stays idle, so the following OVER/DROP are ignored.\nconst subjectFor = (source: {\n data?: Record<string, unknown>;\n id: string | number;\n}): DragSubject | null => {\n const data = source.data;\n if (data?.type === \"tabset\") {\n return { id: String(data.tabsetId), kind: \"tabset\" };\n }\n if (data?.type === \"external\") {\n if (!isTabFactory(data.createTab)) {\n // A wired-up source that can't produce a tab must not fail silently.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source is missing its createTab function\");\n return null;\n }\n let candidate: unknown;\n try {\n candidate = data.createTab();\n } catch (error) {\n // A consumer callback that throws must abort the drag, not break dragging.\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source createTab threw\", error);\n return null;\n }\n const parsed = tabNodeSchema.safeParse(candidate);\n if (!parsed.success) {\n // oxlint-disable-next-line no-console\n console.warn(\"[dashfoo] external drag source returned an invalid tab\", parsed.error);\n return null;\n }\n return { id: String(source.id), kind: \"external\", tab: parsed.data };\n }\n return { id: String(source.id), kind: \"tab\" };\n};\n\ntype DragProviderProps = {\n children: ReactNode;\n // Defaults to the layout store's dispatch when rendered under Layout.Root.\n onCommit?: (action: Action) => void;\n // Defaults to the layout store's splitDock (model global), else true.\n splitDock?: boolean;\n};\n\nconst DragProvider = ({ children, onCommit, splitDock }: DragProviderProps): ReactNode => {\n const actorRef = useActorRef(dragDockMachine);\n // Nullable read on purpose (not the throwing useLayout hook): the drag layer\n // also works standalone with an explicit onCommit, e.g. in tests.\n const layoutStore = useContext(LayoutStoreContext);\n // useState (not useRef(new Map())) so the registry is built once instead of\n // allocating a throwaway Map on every render.\n const [tabsets] = useState(() => new Map<string, HTMLElement>());\n const overlayRef = useRef<HTMLDivElement | null>(null);\n const [preview, setPreview] = useState<DragPreviewState | null>(null);\n\n // One manager for the whole layout — the shared one when a DashfooDragProvider\n // sits above (so external sources participate in this layout's drags), else\n // our own. useState lazily constructs it once; the destroy rides a\n // useInsertionEffect cleanup (not useEffect) so StrictMode's simulated unmount\n // doesn't tear down the live instance — the same pattern @dnd-kit/react uses\n // internally.\n const sharedManager = useContext(SharedDragManagerContext);\n const [ownManager] = useState(() => (sharedManager ? null : createDragManager()));\n useInsertionEffect(() => () => ownManager?.destroy(), [ownManager]);\n const manager = sharedManager ?? ownManager;\n if (manager === null) {\n // Only reachable when a DashfooDragProvider unmounts while its layout stays\n // mounted — surface the misuse instead of dragging against a dead manager.\n throw new Error(\n \"[dashfoo] DashfooDragProvider unmounted while its layout is still mounted; keep the provider above the layout or remount the layout\",\n );\n }\n\n // The drag subject store: fed by the machine subscription, read by parts via\n // useDragSubject/useDropIntent selectors. Inherited from DashfooDragProvider\n // when one sits above (so consumers outside the layout can observe drags),\n // else owned here. Guarded so OVER pulses (every pointer move) don't notify\n // subscribers — only a subject change (drag start/end) or a value-level\n // intent change (the resolved drop moved) does. ownerId scopes intent writes:\n // layouts sharing a store each hit-test only their own tabsets, so this\n // layer's null must not erase an intent another layer resolved.\n const inheritedStore = useContext(DragSubjectStoreContext);\n const [subjectStore] = useState(() => inheritedStore ?? createDragSubjectStore());\n const ownerId = useId();\n useEffect(() => {\n const subscription = actorRef.subscribe((snapshot) => {\n const { intent, subject } = snapshot.context;\n const state = subjectStore.getState();\n if (intent !== null) {\n if (!sameDropIntent(state.intent, intent) || state.intentOwner !== ownerId) {\n subjectStore.setState({ intent, intentOwner: ownerId });\n }\n } else if (state.intent !== null && state.intentOwner === ownerId) {\n subjectStore.setState({ intent: null, intentOwner: null });\n }\n if (!sameDragSubject(subjectStore.getState().subject, subject)) {\n subjectStore.setState({ subject });\n }\n });\n return () => {\n subscription.unsubscribe();\n };\n }, [actorRef, ownerId, subjectStore]);\n\n // Consumer-supplied inputs ride refs (the configRef pattern from\n // usePersistence) so the machine subscriptions below stay attached across\n // re-renders — an inline onCommit or a flipped splitDock must not tear down\n // and re-attach the listeners on a hot path.\n const onCommitRef = useRef(onCommit);\n const splitDockRef = useRef(splitDock);\n useEffect(() => {\n onCommitRef.current = onCommit;\n splitDockRef.current = splitDock;\n });\n\n useEffect(() => {\n const subscription = actorRef.on(\"COMMIT\", (emitted) => {\n // Mirrors the resolveIntent gate: even a drag whose `editable` was\n // flipped off mid-flight cannot land a structural action.\n if (layoutStore?.getState().editable === false) {\n return;\n }\n const commit = onCommitRef.current ?? layoutStore?.getState().dispatch;\n if (!commit) {\n warnOnce(\n \"drag-layer-no-commit\",\n \"Layout.DragLayer has neither an onCommit prop nor a <Layout.Root> above it; drops are ignored\",\n );\n return;\n }\n commit(emitted.action);\n });\n return () => {\n subscription.unsubscribe();\n };\n }, [actorRef, layoutStore]);\n\n const registerTabset = useCallback(\n (id: string, element: HTMLElement | null): void => {\n if (element) {\n tabsets.set(id, element);\n } else {\n tabsets.delete(id);\n }\n },\n [tabsets],\n );\n\n const getTabsetElement = useCallback((id: string) => tabsets.get(id), [tabsets]);\n\n const attachOverlay = useCallback((element: HTMLDivElement | null): void => {\n overlayRef.current = element;\n }, []);\n\n // The helpers live inside the effect (per the React docs' \"move it into the\n // effect\" guidance) so the monitor listeners attach once per manager instead\n // of re-subscribing whenever a render rebuilds a callback.\n useEffect(() => {\n // Which registered tabset sits under the pointer. Tabsets tile (never\n // overlap), so the first rect that contains the point is the unambiguous\n // target — no dnd-kit collision detection needed.\n const tabsetAt = (point: Point): { element: HTMLElement; id: string } | undefined => {\n for (const [id, element] of tabsets) {\n if (pointInRect(point, element.getBoundingClientRect())) {\n return { element, id };\n }\n }\n return undefined;\n };\n\n // The dock intent for a pointer over a tabset, or null when the drop would\n // be a no-op: a tabset dragged onto itself, or the sole tab of a tabset\n // dropped back onto that same tabset. Otherwise the tabset resolves to\n // center/split.\n const resolveIntent = (\n targetId: string,\n element: HTMLElement,\n point: Point,\n draggedId?: string,\n ): DropIntent | null => {\n // A non-editable layout is never a drop target — no indicator, no intent.\n // Read at drag time (getState, not a subscription) like splitDock below,\n // so a runtime flip is honored without re-rendering the layer; this also\n // rejects external-source drops arriving via a shared manager.\n if (layoutStore?.getState().editable === false) {\n return null;\n }\n const tabIds = [...element.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].map(\n (tab) => tab.dataset.tabId ?? \"\",\n );\n if (!shouldAllowDrop(draggedId, targetId, tabIds)) {\n return null;\n }\n const intent = intentForTabset(targetId, element, point, draggedId);\n // When splitting is disabled, a drop over the body stacks instead of\n // splits. Resolved at drag time (getState, not a subscription) so a model\n // global flip mid-session is honored without re-rendering the layer.\n const splitDockResolved = splitDockRef.current ?? layoutStore?.getState().splitDock ?? true;\n if (!splitDockResolved && intent.location.startsWith(\"split-\")) {\n return { location: \"center\", targetId };\n }\n return intent;\n };\n\n // Position the preview imperatively (transform only) so following the\n // pointer never triggers a React re-render.\n const positionOverlay = (point: Point): void => {\n const element = overlayRef.current;\n if (element) {\n element.style.transform = `translate(${point.x + PREVIEW_OFFSET.x}px, ${point.y + PREVIEW_OFFSET.y}px)`;\n }\n };\n\n const handleStart = (event: DragStartEvent): void => {\n const source = event.operation.source;\n if (!source) {\n return;\n }\n const subject = subjectFor(source);\n if (!subject) {\n return;\n }\n const point = event.operation.position.current;\n setPreview({\n label: labelOf(source),\n x: point.x + PREVIEW_OFFSET.x,\n y: point.y + PREVIEW_OFFSET.y,\n });\n actorRef.send({ subject, type: \"START\" });\n };\n\n // dnd-kit's dragmove gives the live pointer on every move, which drives the\n // preview position and the indicator's intent.\n const handleMove = (event: DragMoveEvent): void => {\n const op = event.operation;\n const point = op.position.current;\n positionOverlay(point);\n const draggedId = op.source ? String(op.source.id) : undefined;\n const hit = tabsetAt(point);\n actorRef.send({\n intent: hit ? resolveIntent(hit.id, hit.element, point, draggedId) : null,\n type: \"OVER\",\n });\n };\n\n // Recompute the dock zone from the final pointer, then set the intent and\n // commit in one synchronous pair.\n const handleEnd = (event: DragEndEvent): void => {\n setPreview(null);\n if (event.canceled) {\n actorRef.send({ type: \"CANCEL\" });\n return;\n }\n const op = event.operation;\n const point = op.position.current;\n const draggedId = op.source ? String(op.source.id) : undefined;\n const hit = tabsetAt(point);\n if (hit) {\n actorRef.send({\n intent: resolveIntent(hit.id, hit.element, point, draggedId),\n type: \"OVER\",\n });\n }\n actorRef.send({ type: \"DROP\" });\n };\n const offStart = manager.monitor.addEventListener(\"dragstart\", handleStart);\n const offMove = manager.monitor.addEventListener(\"dragmove\", handleMove);\n const offEnd = manager.monitor.addEventListener(\"dragend\", handleEnd);\n return () => {\n offStart();\n offMove();\n offEnd();\n };\n }, [actorRef, layoutStore, manager, tabsets]);\n\n const contextValue = useMemo<DragContextValue>(\n () => ({ manager, registerTabset }),\n [manager, registerTabset],\n );\n\n return (\n <DragContext.Provider value={contextValue}>\n <DragSubjectStoreContext.Provider value={subjectStore}>\n {children}\n <DockIndicator actorRef={actorRef} getTabsetElement={getTabsetElement} />\n <DragPreview overlayRef={attachOverlay} preview={preview} />\n </DragSubjectStoreContext.Provider>\n </DragContext.Provider>\n );\n};\n\n// The draggable/droppable hooks and the drag contexts live in ./drag-hooks to\n// keep this module under the size cap and avoid a circular import; re-exported\n// here so consumers still import the whole drag surface from \"./drag-adapter\".\nexport {\n DragProvider,\n subjectFor,\n useDragSubject,\n useTabDraggable,\n useTabsetDraggable,\n useTabsetDroppable,\n};\nexport type { DragProviderProps };\n","\"use client\";\n\nimport type { Action, Dashfoo, SnapConfig, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode, Ref } from \"react\";\nimport { useLayoutEffect, useState } from \"react\";\n\nimport type { LayoutState } from \"../hooks/layout-store\";\nimport { createLayoutStore, LayoutStoreContext } from \"../hooks/layout-store\";\n\nconst DEFAULT_TABSET_MIN_SIZE = 320;\n\nconst rootStyle = { display: \"flex\", height: \"100%\", width: \"100%\" } as const;\n\ntype LayoutRootProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n children: ReactNode;\n closableTabs?: boolean;\n dispatch: (action: Action) => void;\n draggableTabs?: boolean;\n draggableTabsets?: boolean;\n // The umbrella switch: false turns off every structural edit (tab/tabset drag,\n // close, rename, splitter resize, external drops) in one go, leaving view\n // interactions (tab selection, maximize, overflow) and dispatch untouched.\n editable?: boolean;\n keepMounted?: boolean;\n maximizable?: boolean;\n model: Dashfoo;\n renamableTabs?: boolean;\n renderTab: (tab: TabNode) => ReactNode;\n renderTabLabel?: (tab: TabNode) => ReactNode;\n renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;\n resizableSplits?: boolean;\n // Forwarded onto the root element so a width observer can measure the actual\n // container — the hook behind responsive lock-on-mobile.\n rootRef?: Ref<HTMLDivElement>;\n // Magnetic snap default for split resize; the prop replaces (not merges with)\n // model.global.snap when both are present.\n snap?: SnapConfig;\n};\n\n// The frame of a hand-built layout: creates the scoped layout store every part\n// below subscribes to. Takes the model and dispatch (the two store fields the\n// parts need) rather than a whole DashfooStore — wire useDashfooStore and pass\n// store.model / store.dispatch.\nconst LayoutRoot = ({\n children,\n closableTabs = true,\n dispatch,\n draggableTabs = true,\n draggableTabsets = true,\n editable = true,\n keepMounted = false,\n maximizable = true,\n model,\n renamableTabs = true,\n renderTab,\n renderTabLabel,\n renderTabsetToolbar,\n resizableSplits = true,\n rootRef,\n snap,\n style,\n ...props\n}: LayoutRootProps): ReactNode => {\n // Model-level globals act as a default layer under the component props: a\n // feature is on unless the prop, the global, or the per-node flag turns it off.\n // `editable` is an extra AND-term over every structural capability — but not\n // maximize, which is reversible view state (same class as tab selection) and\n // stays available in a static layout.\n const global = model.global;\n const snapshot: LayoutState = {\n closableTabs: editable && closableTabs && global.tabEnableClose !== false,\n dispatch,\n draggableTabs: editable && draggableTabs && global.tabEnableDrag !== false,\n draggableTabsets: editable && draggableTabsets,\n editable,\n keepMounted,\n maximizable: maximizable && global.tabSetEnableMaximize !== false,\n maximizedTabsetId: model.maximizedTabsetId,\n renamableTabs: editable && renamableTabs && global.tabEnableRename !== false,\n renderTab,\n renderTabLabel,\n renderTabsetToolbar,\n resizableSplits: editable && resizableSplits && global.enableSplitResize !== false,\n snap: snap ?? global.snap ?? null,\n splitDock: global.enableSplitDock !== false,\n tabLocation: global.tabLocation ?? \"top\",\n tabsetMinSize: global.tabSetMinSize ?? DEFAULT_TABSET_MIN_SIZE,\n tabStripEnabled: global.tabSetEnableTabStrip !== false,\n };\n\n const [store] = useState(() => createLayoutStore(snapshot));\n // Props→store sync. useLayoutEffect so subscribers see fresh state before\n // paint; never during render — setState on an external store mid-render is\n // unsafe.\n useLayoutEffect(() => {\n store.setState(snapshot);\n });\n\n const layoutStyle: CSSProperties =\n global.splitterSize === undefined\n ? { ...rootStyle, ...style }\n : ({\n ...rootStyle,\n \"--dashfoo-splitter-size\": `${global.splitterSize}px`,\n ...style,\n } as CSSProperties);\n\n return (\n <LayoutStoreContext.Provider value={store}>\n <div {...props} data-dashfoo=\"layout\" ref={rootRef} style={layoutStyle}>\n {children}\n </div>\n </LayoutStoreContext.Provider>\n );\n};\n\nexport { LayoutRoot };\nexport type { LayoutRootProps };\n","// Stable ids wiring each tab to its panel (aria-controls / aria-labelledby).\n// Shared by tabset-view and tab-button so the wiring stays in one place with no\n// circular import between those two modules.\nconst tabDomId = (tabsetId: string, tabId: string): string => `dashfoo-tab-${tabsetId}-${tabId}`;\nconst panelDomId = (tabsetId: string): string => `dashfoo-panel-${tabsetId}`;\n\nexport { panelDomId, tabDomId };\n","\"use client\";\n\nimport type { Action, TabNode, TabsetNode } from \"@dashfoo/core\";\nimport { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport { createStore, useStore } from \"zustand\";\n\nimport { tabDomId } from \"../../lib/tab-ids\";\nimport { warnOnce } from \"../../lib/warn-once\";\nimport type { OverflowItem } from \"../tab-overflow\";\n\n// The per-tabset coordination store behind <Tabset.Root>. Model-derived fields\n// (node, visualSelected, flags) are synced in by Root each render; coordination\n// state (editingTabId, overflowItems, tablistElement, restoreFocus) lives here\n// so sibling parts stay in lockstep without prop threading; actions read live\n// state via get(). editingTabId being a single field guarantees at most one open\n// rename editor per tabset. restoreFocus is set by closeTab so Root's post-delete\n// effect knows to move focus to the newly-selected tab instead of leaving it on\n// <body>.\ntype TabsetState = {\n activeTab: TabNode | undefined;\n cancelRename: () => void;\n closeTab: (tabId: string) => void;\n commitRename: (tabId: string, value: string) => void;\n dispatch: (action: Action) => void;\n editingTabId: string | null;\n isMaximized: boolean;\n node: TabsetNode;\n overflowItems: Array<OverflowItem>;\n registerRenameInput: () => () => void;\n registerTablist: (element: HTMLDivElement | null) => void;\n renameInputCount: number;\n renameTab: (tabId: string, name: string) => void;\n restoreFocus: boolean;\n selectOverflowTab: (tabId: string) => void;\n selectTab: (index: number, options?: { focus?: boolean }) => void;\n showMaximize: boolean;\n startEditing: (tabId: string) => void;\n tablistElement: HTMLDivElement | null;\n tabsClosable: boolean;\n tabsRenamable: boolean;\n toggleMaximize: () => void;\n visualSelected: number;\n};\n\ntype TabsetStore = StoreApi<TabsetState>;\n\ntype TabsetSnapshot = Pick<\n TabsetState,\n | \"activeTab\"\n | \"dispatch\"\n | \"isMaximized\"\n | \"node\"\n | \"showMaximize\"\n | \"tabsClosable\"\n | \"tabsRenamable\"\n | \"visualSelected\"\n>;\n\nconst tabButtonIn = (\n scope: HTMLElement | null,\n tabsetId: string,\n tabId: string,\n): HTMLElement | null =>\n scope?.querySelector<HTMLElement>(`#${CSS.escape(tabDomId(tabsetId, tabId))}`) ?? null;\n\nconst createTabsetStore = (initial: TabsetSnapshot): TabsetStore =>\n createStore<TabsetState>((set, get) => ({\n ...initial,\n cancelRename: () => set({ editingTabId: null }),\n closeTab: (tabId) => {\n set({ restoreFocus: true });\n get().dispatch({ tabId, type: \"deleteTab\" });\n },\n commitRename: (tabId, value) => {\n set({ editingTabId: null });\n const { node, renameTab } = get();\n const trimmed = value.trim();\n const current = node.children.find((tab) => tab.id === tabId)?.name;\n if (trimmed && trimmed !== current) {\n renameTab(tabId, trimmed);\n }\n },\n editingTabId: null,\n overflowItems: [],\n registerRenameInput: () => {\n set((state) => ({ renameInputCount: state.renameInputCount + 1 }));\n return () => set((state) => ({ renameInputCount: state.renameInputCount - 1 }));\n },\n registerTablist: (element) => {\n if (element) {\n const current = get().tablistElement;\n if (current && current !== element) {\n warnOnce(\n `duplicate-tablist-${get().node.id}`,\n \"more than one <Tabset.Tablist> under the same <Tabset.Root>; the last one wins for keyboard focus and overflow\",\n );\n }\n set({ tablistElement: element });\n return;\n }\n set({ tablistElement: null });\n },\n renameInputCount: 0,\n renameTab: (tabId, name) => get().dispatch({ name, tabId, type: \"renameTab\" }),\n restoreFocus: false,\n selectOverflowTab: (tabId) => {\n const { dispatch, node, tablistElement } = get();\n const index = node.children.findIndex((tab) => tab.id === tabId);\n if (index === -1) {\n return;\n }\n dispatch({ index, tabsetId: node.id, type: \"selectTab\" });\n tabButtonIn(tablistElement, node.id, tabId)?.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n },\n selectTab: (index, options) => {\n const { dispatch, node, tablistElement } = get();\n dispatch({ index, tabsetId: node.id, type: \"selectTab\" });\n if (options?.focus) {\n const tabId = node.children[index]?.id;\n if (tabId) {\n tabButtonIn(tablistElement, node.id, tabId)?.focus();\n }\n }\n },\n startEditing: (tabId) => {\n const { node, renameInputCount, tabsRenamable } = get();\n const tab = node.children.find((child) => child.id === tabId);\n if (!tabsRenamable || tab?.enableRename === false) {\n return;\n }\n if (renameInputCount === 0) {\n // The trigger must not vanish into nothing when no editor is composed.\n warnOnce(\n `missing-rename-input-${node.id}`,\n \"startEditing needs a <Tabset.RenameInput> in the composition; ignoring\",\n );\n return;\n }\n set({ editingTabId: tabId });\n },\n tablistElement: null,\n toggleMaximize: () => {\n const { dispatch, isMaximized, node } = get();\n dispatch({ tabsetId: isMaximized ? null : node.id, type: \"setMaximizedTabset\" });\n },\n }));\n\n// Carries only the store instance — state always flows through selectors.\nconst TabsetStoreContext = createContext<TabsetStore | null>(null);\n\nconst useTabset = <T>(selector: (state: TabsetState) => T): T => {\n const store = useContext(TabsetStoreContext);\n if (store === null) {\n throw new Error(\"dashfoo Tabset parts must be rendered inside <Tabset.Root>.\");\n }\n return useStore(store, selector);\n};\n\n// Identity only — which tab a Trigger/CloseButton/RenameInput belongs to.\n// Derived state (selected, editing) comes from useTabset selectors.\ntype TabContextValue = { index: number; tab: TabNode };\n\nconst TabContext = createContext<TabContextValue | null>(null);\n\nconst useTab = (): TabContextValue => {\n const value = useContext(TabContext);\n if (value === null) {\n throw new Error(\"dashfoo tab parts must be rendered inside <Tabset.Tab>.\");\n }\n return value;\n};\n\nexport { createTabsetStore, TabContext, TabsetStoreContext, useTab, useTabset };\nexport type { TabContextValue, TabsetSnapshot, TabsetState, TabsetStore };\n","\"use client\";\n\nimport type { TabNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { panelDomId, tabDomId } from \"../../lib/tab-ids\";\n\nimport { useTabset } from \"./tabset-store\";\n\nconst contentStyle: CSSProperties = { flex: 1, minHeight: 0, overflow: \"auto\" };\n\n// Content is resolved through a component (not inline) so the panel has a stable\n// element identity and React keeps its state across re-renders instead of\n// remounting the inline result. Covers both the registry default (renderTab)\n// and a consumer-supplied render prop.\nconst TabPanel = ({\n render,\n tab,\n}: {\n render?: (tab: TabNode) => ReactNode;\n tab: TabNode;\n}): ReactNode => {\n const renderTab = useLayout((state) => state.renderTab);\n return render ? render(tab) : renderTab(tab);\n};\n\ntype TabsetContentProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n // Overrides the layout's renderTab for this tabset's panels.\n children?: (tab: TabNode) => ReactNode;\n};\n\nconst TabsetContent = ({ children, style, ...props }: TabsetContentProps): ReactNode => {\n const node = useTabset((state) => state.node);\n const visualSelected = useTabset((state) => state.visualSelected);\n const keepMounted = useLayout((state) => state.keepMounted);\n\n const mergedStyle = { ...contentStyle, ...style };\n\n // keepMounted renders every panel (inactive ones hidden) so their state\n // survives a tab switch; otherwise only the active panel mounts.\n if (keepMounted && node.children.length > 0) {\n return node.children.map((tab, index) => (\n <div\n {...props}\n aria-labelledby={tabDomId(node.id, tab.id)}\n data-dashfoo=\"tabcontent\"\n hidden={index !== visualSelected || undefined}\n id={index === visualSelected ? panelDomId(node.id) : undefined}\n key={tab.id}\n role={index === visualSelected ? \"tabpanel\" : undefined}\n style={mergedStyle}\n tabIndex={index === visualSelected ? 0 : undefined}\n >\n <TabPanel render={children} tab={tab} />\n </div>\n ));\n }\n\n const active = node.children[visualSelected];\n if (active) {\n return (\n <div\n {...props}\n aria-labelledby={tabDomId(node.id, active.id)}\n data-dashfoo=\"tabcontent\"\n id={panelDomId(node.id)}\n role=\"tabpanel\"\n style={mergedStyle}\n tabIndex={0}\n >\n <TabPanel render={children} tab={active} />\n </div>\n );\n }\n return <div {...props} data-dashfoo=\"tabcontent\" style={mergedStyle} />;\n};\n\nexport { TabsetContent };\nexport type { TabsetContentProps };\n","\"use client\";\n\nimport type { CSSProperties, FocusEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nconst OverflowIcon = (): ReactNode => (\n <svg aria-hidden=\"true\" fill=\"currentColor\" height=\"10\" viewBox=\"0 0 10 10\" width=\"10\">\n <circle cx=\"2\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"8\" cy=\"5\" r=\"1\" />\n </svg>\n);\n\nconst menuStyle: CSSProperties = {\n position: \"absolute\",\n right: 0,\n top: \"100%\",\n zIndex: 10,\n};\n\ntype OverflowItem = { id: string; name: string };\n\n// A dependency-free overflow control: a button that toggles a menu of the hidden\n// tabs. Implements the WAI-ARIA APG menu-button keyboard model — roving tabindex,\n// arrow/Home/End navigation, Escape-to-close with focus return. Closes on outside\n// pointerdown and focusout.\nconst TabOverflowMenu = ({\n items,\n onSelect,\n}: {\n items: Array<OverflowItem>;\n onSelect: (id: string) => void;\n}): ReactNode => {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(0);\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n const handlePointerDown = (event: PointerEvent): void => {\n if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n };\n }, [open]);\n\n // moving DOM focus onto the first menuitem is a side effect of the menu becoming\n // visible, so it belongs in an effect rather than the click handler (the items don't\n // exist yet there). activeIndex is reset to 0 on open, so we always land on the first.\n useEffect(() => {\n if (open) {\n itemRefs.current[0]?.focus();\n }\n }, [open]);\n\n const closeAndReturnFocus = (): void => {\n setOpen(false);\n triggerRef.current?.focus();\n };\n\n const focusItem = (index: number): void => {\n setActiveIndex(index);\n itemRefs.current[index]?.focus();\n };\n\n const handleToggle = (): void => {\n setActiveIndex(0);\n setOpen((value) => !value);\n };\n\n const handleSelect = (id: string): void => {\n onSelect(id);\n closeAndReturnFocus();\n };\n\n const handleMenuKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n const last = items.length - 1;\n switch (event.key) {\n case \"ArrowDown\": {\n event.preventDefault();\n focusItem(activeIndex >= last ? 0 : activeIndex + 1);\n break;\n }\n case \"ArrowUp\": {\n event.preventDefault();\n focusItem(activeIndex <= 0 ? last : activeIndex - 1);\n break;\n }\n case \"Home\": {\n event.preventDefault();\n focusItem(0);\n break;\n }\n case \"End\": {\n event.preventDefault();\n focusItem(last);\n break;\n }\n case \"Escape\": {\n event.preventDefault();\n closeAndReturnFocus();\n break;\n }\n default: {\n break;\n }\n }\n };\n\n // Tab out of the menu (or any other focus leaving the root) closes it without\n // stealing focus, complementing the outside-pointerdown handler for keyboard users.\n const handleFocusOut = (event: FocusEvent<HTMLDivElement>): void => {\n if (event.relatedTarget instanceof Node && rootRef.current?.contains(event.relatedTarget)) {\n return;\n }\n setOpen(false);\n };\n\n return (\n <div data-dashfoo=\"tab-overflow-root\" ref={rootRef} style={{ position: \"relative\" }}>\n <button\n aria-expanded={open}\n aria-haspopup=\"menu\"\n aria-label=\"More tabs\"\n data-dashfoo=\"tab-overflow\"\n onClick={handleToggle}\n ref={triggerRef}\n type=\"button\"\n >\n <OverflowIcon />\n </button>\n {open ? (\n <div\n data-dashfoo=\"tab-overflow-menu\"\n onBlur={handleFocusOut}\n onKeyDown={handleMenuKeyDown}\n role=\"menu\"\n style={menuStyle}\n tabIndex={-1}\n >\n {items.map((item, index) => (\n <button\n data-dashfoo=\"tab-overflow-item\"\n key={item.id}\n onClick={() => handleSelect(item.id)}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n role=\"menuitem\"\n tabIndex={index === activeIndex ? 0 : -1}\n type=\"button\"\n >\n {item.name}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n );\n};\n\nexport { TabOverflowMenu };\nexport type { OverflowItem };\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { TabOverflowMenu } from \"../tab-overflow\";\n\nimport { useTabset } from \"./tabset-store\";\n\n// The menu of tabs clipped out of the tablist's visible box. Measurement lives\n// in Tabset.Root (the store's overflowItems); this part only renders the\n// APG menu-button when there is something to show.\nconst TabsetOverflowMenu = (): ReactNode => {\n const overflowItems = useTabset((state) => state.overflowItems);\n const selectOverflowTab = useTabset((state) => state.selectOverflowTab);\n\n if (overflowItems.length === 0) {\n return null;\n }\n return <TabOverflowMenu items={overflowItems} onSelect={selectOverflowTab} />;\n};\n\nexport { TabsetOverflowMenu };\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n// The ids of the tabs clipped by the tablist's visible box — past the right edge\n// OR scrolled off before the left edge. +/-1 absorbs sub-pixel rounding. One\n// pass: keep the id only when the tab is both overflowing and identifiable.\nconst overflowingIds = (tablist: Element): Array<string> => {\n const box = tablist.getBoundingClientRect();\n return [...tablist.querySelectorAll<HTMLElement>('[data-dashfoo=\"tab\"]')].flatMap((tab) => {\n const rect = tab.getBoundingClientRect();\n const clipped = rect.right > box.right + 1 || rect.left < box.left - 1;\n return clipped && tab.dataset.tabId ? [tab.dataset.tabId] : [];\n });\n};\n\n// Recomputes the overflowing ids when the tablist resizes, scrolls, or its tab\n// labels/widths change. Takes the element (not a ref) so a tablist that mounts\n// late — or is swapped by a custom composition — re-arms the observers.\n// `signature` folds tab id+name so renames recompute even when the count is\n// unchanged. Returns [] until measured (and in jsdom, where rects are 0).\nconst useTabOverflow = (tablist: HTMLElement | null, signature: string): Array<string> => {\n const [overflow, setOverflow] = useState<Array<string>>([]);\n\n useEffect(() => {\n // No tablist (e.g. the strip is disabled) means nothing can overflow.\n const recompute = (): void => {\n setOverflow((previous) => {\n const next = tablist ? overflowingIds(tablist) : [];\n return next.length === 0 && previous.length === 0 ? previous : next;\n });\n };\n recompute();\n if (!tablist) {\n return;\n }\n const observer = new ResizeObserver(recompute);\n observer.observe(tablist);\n // Scrolling the strip changes which tabs are clipped on the left/right without\n // resizing it, so listen for scroll too.\n tablist.addEventListener(\"scroll\", recompute, { passive: true });\n return () => {\n observer.disconnect();\n tablist.removeEventListener(\"scroll\", recompute);\n };\n }, [tablist, signature]);\n\n return overflow;\n};\n\nexport { overflowingIds, useTabOverflow };\n","// The neighbour to show as active when the selected tab is dragged out: the next\n// tab if there is one, else the previous, else -1 (the tabset is emptying).\nconst fallbackSelectedIndex = (count: number, removedIndex: number): number =>\n removedIndex + 1 < count ? removedIndex + 1 : removedIndex - 1;\n\nexport { fallbackSelectedIndex };\n","\"use client\";\n\nimport type { TabsetNode } from \"@dashfoo/core\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useStore } from \"zustand\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { useTabOverflow } from \"../../hooks/use-tab-overflow\";\nimport { tabDomId } from \"../../lib/tab-ids\";\nimport { fallbackSelectedIndex } from \"../../lib/tab-selection\";\nimport { useDragSubject, useTabsetDroppable } from \"../drag-adapter\";\n\nimport type { TabsetSnapshot } from \"./tabset-store\";\nimport { createTabsetStore, TabsetStoreContext } from \"./tabset-store\";\n\n// height/width 100% (not flex:1) so the tabset fills its parent whether that\n// parent is a flex item or a plain block — rrp wraps panel content in a block\n// div, where flex:1 would collapse to content height.\nconst tabsetStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100%\",\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n};\n\ntype TabsetRootProps = ComponentProps<\"div\"> & { node: TabsetNode };\n\n// Creates the per-tabset store and owns everything that needs the whole tabset:\n// the droppable registration, the drag-out selection fallback, overflow\n// measurement, and focus restore after a close.\nconst TabsetRoot = ({\n children,\n node,\n ref: userRef,\n style,\n ...props\n}: TabsetRootProps): ReactNode => {\n const closableTabs = useLayout((state) => state.closableTabs);\n const dispatch = useLayout((state) => state.dispatch);\n const maximizable = useLayout((state) => state.maximizable);\n const maximizedTabsetId = useLayout((state) => state.maximizedTabsetId);\n const renamableTabs = useLayout((state) => state.renamableTabs);\n const tabLocation = useLayout((state) => state.tabLocation);\n\n const { ref } = useTabsetDroppable(node.id);\n const dragSubject = useDragSubject();\n const tabsetRef = useRef<HTMLDivElement | null>(null);\n\n const isMaximized = maximizedTabsetId === node.id;\n const showMaximize = maximizable && node.enableMaximize !== false;\n const tabsClosable = closableTabs && node.enableClose !== false;\n // Renaming has no tabset-level flag — only the layout flag and per-tab opt-out.\n const tabsRenamable = renamableTabs;\n\n // While the selected tab is being dragged out of THIS tabset, show a neighbour\n // as active so the strip + content preview \"as if that tab were already gone\".\n // Purely visual — the model selection is untouched and resumes on drop/cancel.\n const draggingTabIndex =\n dragSubject?.kind === \"tab\" ? node.children.findIndex((tab) => tab.id === dragSubject.id) : -1;\n const visualSelected =\n draggingTabIndex !== -1 && draggingTabIndex === node.selected\n ? fallbackSelectedIndex(node.children.length, draggingTabIndex)\n : node.selected;\n const activeTab = node.children[visualSelected];\n const isDragSource = dragSubject?.kind === \"tabset\" && dragSubject.id === node.id;\n\n const snapshot: TabsetSnapshot = {\n activeTab,\n dispatch,\n isMaximized,\n node,\n showMaximize,\n tabsClosable,\n tabsRenamable,\n visualSelected,\n };\n const [store] = useState(() => createTabsetStore(snapshot));\n\n // Props→store sync. useLayoutEffect (not useEffect) so subscribers re-render\n // before paint and DOM-focus effects below see fresh state; never during\n // render — setState on an external store mid-render is unsafe.\n useLayoutEffect(() => {\n store.setState(snapshot);\n });\n\n // Overflow measurement runs here (not in the menu part) because the default\n // composition's toolbar-visibility condition needs the count, and the tablist\n // element registration already lives in the store.\n const tablistElement = useStore(store, (state) => state.tablistElement);\n // A rename or scroll changes clipping without changing the count, so the\n // signature folds each tab's id+name into the deps the hook watches.\n const overflowSignature = node.children.map((tab) => `${tab.id}:${tab.name}`).join(\"|\");\n const overflow = useTabOverflow(tablistElement, overflowSignature);\n // Resolve overflowing ids to names once per change instead of a find() per item.\n const overflowItems = useMemo(() => {\n const names = new Map(node.children.map((tab) => [tab.id, tab.name]));\n return overflow.map((id) => ({ id, name: names.get(id) ?? id }));\n }, [overflow, node.children]);\n useLayoutEffect(() => {\n store.setState({ overflowItems });\n }, [overflowItems, store]);\n\n // Runs after a closeTab commits. Focus the now-active tab's button, or fall\n // back to the tabset container if the tabset emptied so focus never lands on\n // <body>. Scoped to the root (not the tablist) so tabs rendered outside a\n // tablist still get focus restore.\n useEffect(() => {\n if (!store.getState().restoreFocus) {\n return;\n }\n store.setState({ restoreFocus: false });\n const activeTabId = node.children[node.selected]?.id;\n if (activeTabId) {\n tabsetRef.current\n ?.querySelector<HTMLElement>(`#${CSS.escape(tabDomId(node.id, activeTabId))}`)\n ?.focus();\n } else {\n tabsetRef.current?.focus();\n }\n }, [node.children, node.selected, node.id, store]);\n\n return (\n <TabsetStoreContext.Provider value={store}>\n <div\n {...props}\n data-dashfoo=\"tabset\"\n data-dragging-source={isDragSource || undefined}\n data-tab-location={tabLocation}\n ref={(element) => {\n ref(element);\n tabsetRef.current = element;\n if (typeof userRef === \"function\") {\n userRef(element);\n } else if (userRef) {\n userRef.current = element;\n }\n }}\n style={{ ...tabsetStyle, ...style }}\n // -1 so an emptied tabset can receive focus on close without entering the\n // Tab order.\n tabIndex={-1}\n >\n {children}\n </div>\n </TabsetStoreContext.Provider>\n );\n};\n\nexport { TabsetRoot };\nexport type { TabsetRootProps };\n","import type { Ref } from \"react\";\n\nconst applyRef = <T>(ref: Ref<T> | undefined, value: T | null): void => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref) {\n ref.current = value;\n }\n};\n\n// Fans one element out to several refs so a part can keep its internal ref\n// (draggable wiring, focus lookups) while still honoring a consumer-supplied one.\nconst mergeRefs =\n <T>(...refs: Array<Ref<T> | undefined>) =>\n (value: T | null): void => {\n for (const ref of refs) {\n applyRef(ref, value);\n }\n };\n\nexport { mergeRefs };\n","\"use client\";\n\nimport type { TabNode } from \"@dashfoo/core\";\nimport type { ComponentProps, MouseEvent, ReactNode } from \"react\";\nimport { useContext, useEffect, useMemo, useRef } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { mergeRefs } from \"../../lib/merge-refs\";\nimport { panelDomId, tabDomId } from \"../../lib/tab-ids\";\nimport { warnOnce } from \"../../lib/warn-once\";\nimport { useDragSubject, useTabDraggable } from \"../drag-adapter\";\n\nimport { TabContext, TabsetStoreContext, useTab, useTabset } from \"./tabset-store\";\n\ntype TabsetTabProps = ComponentProps<\"span\"> & { tab: TabNode };\n\n// The per-tab wrapper: provides identity (which tab, which index) to Trigger,\n// CloseButton, and RenameInput, and owns the focus-return-after-rename effect.\nconst TabsetTab = ({ children, ref: userRef, tab, ...props }: TabsetTabProps): ReactNode => {\n const store = useContext(TabsetStoreContext);\n const node = useTabset((state) => state.node);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n // `data-dragging` is driven off the drag machine's subject, which is\n // authoritative for the whole drag — the source tab stays in the strip (dimmed)\n // while the overlay chip follows the pointer.\n const dragSubject = useDragSubject();\n const isDragging = dragSubject?.kind === \"tab\" && dragSubject.id === tab.id;\n const itemRef = useRef<HTMLSpanElement | null>(null);\n const wasEditing = useRef(false);\n\n const index = node.children.findIndex((child) => child.id === tab.id);\n\n // The orphan check must not run at render time: right after a tab moves\n // between tabsets, this render sees the new `tab` prop against the store's\n // not-yet-synced node, so a legitimately moved tab transiently looks\n // orphaned (the index self-corrects before paint). The effect reads the live\n // store instead — Root's layout-effect sync always lands before any passive\n // effect flushes, so only a genuinely missing tab warns.\n const orphanAtRender = index === -1;\n useEffect(() => {\n const settled = store?.getState().node.children.some((child) => child.id === tab.id);\n if (settled === false) {\n warnOnce(\n `orphan-tab-${tab.id}`,\n `<Tabset.Tab> for \"${tab.id}\" is not a child of its Tabset.Root's node; it renders but can never be selected`,\n );\n }\n }, [orphanAtRender, store, tab.id]);\n\n // Return focus to the tab button once the inline editor closes.\n useEffect(() => {\n if (wasEditing.current && !editing) {\n itemRef.current?.querySelector<HTMLElement>('[data-dashfoo=\"tab\"]')?.focus();\n }\n wasEditing.current = editing;\n }, [editing]);\n\n const contextValue = useMemo(() => ({ index, tab }), [index, tab]);\n const refCallback = useMemo(() => mergeRefs<HTMLSpanElement>(itemRef, userRef), [userRef]);\n\n return (\n <TabContext.Provider value={contextValue}>\n <span\n {...props}\n data-dashfoo=\"tab-item\"\n data-dragging={isDragging || undefined}\n ref={refCallback}\n >\n {children}\n </span>\n </TabContext.Provider>\n );\n};\n\ntype TabsetTriggerProps = ComponentProps<\"button\">;\n\n// The tab button itself — a sibling of the close control (not its parent, which\n// would be invalid markup and would pollute the tab's accessible name). Hidden\n// while the rename editor is open; the draggable hook stays mounted so the\n// Draggable instance survives the swap.\nconst TabsetTrigger = ({\n children,\n onClick,\n onDoubleClick,\n ref: userRef,\n ...props\n}: TabsetTriggerProps): ReactNode => {\n const { index, tab } = useTab();\n const node = useTabset((state) => state.node);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const selectTab = useTabset((state) => state.selectTab);\n const startEditing = useTabset((state) => state.startEditing);\n const tabsRenamable = useTabset((state) => state.tabsRenamable);\n const visualSelected = useTabset((state) => state.visualSelected);\n const draggableTabs = useLayout((state) => state.draggableTabs);\n const renderTabLabel = useLayout((state) => state.renderTabLabel);\n const { ref } = useTabDraggable(tab.id, !draggableTabs || tab.enableDrag === false, tab.name);\n const refCallback = useMemo(() => mergeRefs<HTMLButtonElement>(ref, userRef), [ref, userRef]);\n\n if (editing) {\n return null;\n }\n\n const selected = index === visualSelected;\n const renamable = tabsRenamable && tab.enableRename !== false;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n selectTab(index);\n };\n\n const handleDoubleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onDoubleClick?.(event);\n if (renamable) {\n startEditing(tab.id);\n }\n };\n\n return (\n <button\n aria-label={renderTabLabel ? tab.name : undefined}\n {...props}\n aria-controls={panelDomId(node.id)}\n aria-selected={selected}\n data-dashfoo=\"tab\"\n data-tab-id={tab.id}\n id={tabDomId(node.id, tab.id)}\n onClick={handleClick}\n onDoubleClick={handleDoubleClick}\n ref={refCallback}\n role=\"tab\"\n tabIndex={selected ? 0 : -1}\n type=\"button\"\n >\n {children ?? (renderTabLabel ? renderTabLabel(tab) : tab.name)}\n </button>\n );\n};\n\nexport { TabsetTab, TabsetTrigger };\nexport type { TabsetTabProps, TabsetTriggerProps };\n","import type { CSSProperties, ReactNode } from \"react\";\n\nconst iconStyle: CSSProperties = { pointerEvents: \"none\" };\n\n// Shared lucide-style icon frame (24-unit grid, currentColor stroke). iconStyle\n// sets pointer-events:none so a pointerdown on a grip lands on the button (the\n// draggable), not the svg path — otherwise the drag sensor never activates.\nconst Icon = ({ children }: { children: ReactNode }): ReactNode => (\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"12\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n style={iconStyle}\n viewBox=\"0 0 24 24\"\n width=\"12\"\n >\n {children}\n </svg>\n);\n\nconst CloseIcon = (): ReactNode => (\n <Icon>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Icon>\n);\n\nconst GripIcon = (): ReactNode => (\n <Icon>\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"19\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"19\" cy=\"12\" r=\"1\" />\n <circle cx=\"5\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n <circle cx=\"19\" cy=\"19\" r=\"1\" />\n <circle cx=\"5\" cy=\"19\" r=\"1\" />\n </Icon>\n);\n\nconst MaximizeIcon = ({ maximized }: { maximized: boolean }): ReactNode =>\n maximized ? (\n <Icon>\n <path d=\"m14 10 7-7\" />\n <path d=\"M20 10h-6V4\" />\n <path d=\"m3 21 7-7\" />\n <path d=\"M4 14h6v6\" />\n </Icon>\n ) : (\n <Icon>\n <path d=\"M15 3h6v6\" />\n <path d=\"m21 3-7 7\" />\n <path d=\"m3 21 7-7\" />\n <path d=\"M9 21H3v-6\" />\n </Icon>\n );\n\nexport { CloseIcon, GripIcon, MaximizeIcon };\n","\"use client\";\n\nimport type { ComponentProps, FocusEvent, KeyboardEvent, MouseEvent, ReactNode } from \"react\";\nimport { useEffect, useMemo, useRef } from \"react\";\n\nimport { mergeRefs } from \"../../lib/merge-refs\";\nimport { CloseIcon } from \"../tabset-icons\";\n\nimport { useTab, useTabset } from \"./tabset-store\";\n\ntype TabsetRenameInputProps = ComponentProps<\"input\">;\n\n// Inline rename editor. Enter/Escape set `done` so the unmount blur does not\n// re-commit after a deliberate commit or cancel. A separate inner component so\n// each editing session mounts fresh (autofocus + select, reset `done`).\nconst RenameEditor = ({\n defaultValue,\n onBlur,\n onKeyDown,\n ref: userRef,\n ...props\n}: TabsetRenameInputProps): ReactNode => {\n const { tab } = useTab();\n const cancelRename = useTabset((state) => state.cancelRename);\n const commitRename = useTabset((state) => state.commitRename);\n const ref = useRef<HTMLInputElement | null>(null);\n const done = useRef(false);\n const refCallback = useMemo(() => mergeRefs<HTMLInputElement>(ref, userRef), [userRef]);\n\n useEffect(() => {\n ref.current?.focus();\n ref.current?.select();\n }, []);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>): void => {\n onKeyDown?.(event);\n if (event.key === \"Enter\") {\n done.current = true;\n commitRename(tab.id, event.currentTarget.value);\n } else if (event.key === \"Escape\") {\n done.current = true;\n cancelRename();\n }\n };\n\n const handleCommitOnBlur = (event: FocusEvent<HTMLInputElement>): void => {\n onBlur?.(event);\n if (done.current) {\n return;\n }\n done.current = true;\n commitRename(tab.id, event.currentTarget.value);\n };\n\n return (\n <input\n aria-label={`Rename ${tab.name}`}\n {...props}\n data-dashfoo=\"tab-rename\"\n defaultValue={defaultValue ?? tab.name}\n onBlur={handleCommitOnBlur}\n onKeyDown={handleKeyDown}\n ref={refCallback}\n type=\"text\"\n />\n );\n};\n\n// Renders only while its tab is being renamed. Always composed (it registers its\n// presence on mount so startEditing can warn when no editor exists), but inert\n// until the trigger's double-click flips editingTabId.\nconst TabsetRenameInput = (props: TabsetRenameInputProps): ReactNode => {\n const { tab } = useTab();\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const registerRenameInput = useTabset((state) => state.registerRenameInput);\n\n useEffect(() => registerRenameInput(), [registerRenameInput]);\n\n if (!editing) {\n return null;\n }\n return <RenameEditor {...props} />;\n};\n\ntype TabsetCloseButtonProps = ComponentProps<\"button\">;\n\nconst TabsetCloseButton = ({ children, onClick, ...props }: TabsetCloseButtonProps): ReactNode => {\n const { index, tab } = useTab();\n const closeTab = useTabset((state) => state.closeTab);\n const editing = useTabset((state) => state.editingTabId === tab.id);\n const tabsClosable = useTabset((state) => state.tabsClosable);\n const visualSelected = useTabset((state) => state.visualSelected);\n\n const closable = tabsClosable && tab.enableClose !== false;\n if (!closable || editing) {\n return null;\n }\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n // Closing must not bubble into the trigger and select the closing tab.\n event.stopPropagation();\n closeTab(tab.id);\n };\n\n return (\n <button\n aria-label={`Close ${tab.name}`}\n {...props}\n data-dashfoo=\"tab-close\"\n onClick={handleClick}\n // Roving tabindex: only the active tab's close button is tabbable, so\n // Tab does not stop on a close button inside every tab in the tablist.\n tabIndex={index === visualSelected ? 0 : -1}\n type=\"button\"\n >\n {children ?? <CloseIcon />}\n </button>\n );\n};\n\nexport { TabsetCloseButton, TabsetRenameInput };\nexport type { TabsetCloseButtonProps, TabsetRenameInputProps };\n","\"use client\";\n\nimport type { ComponentProps, CSSProperties, KeyboardEvent, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { mergeRefs } from \"../../lib/merge-refs\";\n\nimport { useTabset } from \"./tabset-store\";\n\n// The strip is a presentational row: the tablist (the actual tabs) plus room for\n// a trailing toolbar. role=\"tablist\" lives on the inner tablist so the toolbar is\n// not announced as a tab. The drag adapter hit-tests [data-dashfoo=\"tabstrip\"],\n// so the attribute is applied after the spread — it is structural, not theming.\nconst stripStyle: CSSProperties = { display: \"flex\", flexShrink: 0 };\n\nconst tablistStyle: CSSProperties = { display: \"flex\", minWidth: 0, overflowX: \"auto\" };\n\ntype TabsetTabStripProps = ComponentProps<\"div\">;\n\nconst TabsetTabStrip = ({ style, ...props }: TabsetTabStripProps): ReactNode => (\n <div {...props} data-dashfoo=\"tabstrip\" style={{ ...stripStyle, ...style }} />\n);\n\ntype TabsetTablistProps = ComponentProps<\"div\">;\n\n// Owns the roving-tabindex keyboard model (WAI-ARIA APG Tabs): arrows move and\n// select, Home/End jump to the ends, and focus follows the new selection.\nconst TabsetTablist = ({\n onKeyDown,\n ref: userRef,\n style,\n ...props\n}: TabsetTablistProps): ReactNode => {\n const node = useTabset((state) => state.node);\n const registerTablist = useTabset((state) => state.registerTablist);\n const selectTab = useTabset((state) => state.selectTab);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n onKeyDown?.(event);\n if (event.defaultPrevented) {\n return;\n }\n // Only act when the key comes from a tab — otherwise a focused close button\n // (a tablist child) would have its arrows/Home/End hijacked.\n if (!(event.target instanceof HTMLElement) || !event.target.closest('[role=\"tab\"]')) {\n return;\n }\n const count = node.children.length;\n if (count === 0) {\n return;\n }\n const targets: Record<string, number> = {\n ArrowLeft: (node.selected - 1 + count) % count,\n ArrowRight: (node.selected + 1) % count,\n End: count - 1,\n Home: 0,\n };\n const next = targets[event.key];\n if (next === undefined) {\n return;\n }\n event.preventDefault();\n selectTab(next, { focus: true });\n };\n\n // Stable callback ref so React doesn't detach/re-register the tablist on every\n // render (a fresh inline ref identity would churn the store registration).\n const refCallback = useMemo(\n () => mergeRefs<HTMLDivElement>(registerTablist, userRef),\n [registerTablist, userRef],\n );\n\n return (\n <div\n aria-label={node.name ?? \"Tabs\"}\n {...props}\n aria-orientation=\"horizontal\"\n data-dashfoo=\"tablist\"\n onKeyDown={handleKeyDown}\n ref={refCallback}\n role=\"tablist\"\n style={{ ...tablistStyle, ...style }}\n tabIndex={-1}\n />\n );\n};\n\nexport { TabsetTablist, TabsetTabStrip };\nexport type { TabsetTablistProps, TabsetTabStripProps };\n","\"use client\";\n\nimport type { ComponentProps, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\nimport { mergeRefs } from \"../../lib/merge-refs\";\nimport { useTabsetDraggable } from \"../drag-adapter\";\nimport { GripIcon, MaximizeIcon } from \"../tabset-icons\";\n\nimport { useTabset } from \"./tabset-store\";\n\nconst toolbarStyle: CSSProperties = {\n alignItems: \"center\",\n display: \"flex\",\n flexShrink: 0,\n marginInlineStart: \"auto\",\n};\n\ntype TabsetToolbarProps = ComponentProps<\"div\">;\n\nconst TabsetToolbar = ({ style, ...props }: TabsetToolbarProps): ReactNode => (\n <div {...props} data-dashfoo=\"tabset-toolbar\" style={{ ...toolbarStyle, ...style }} />\n);\n\ntype TabsetGripProps = ComponentProps<\"button\">;\n\n// The whole tabset is draggable from its grip. It carries the active tab's name\n// so the drag overlay can label the chip. Self-hides when tabset dragging is off\n// or the tabset is maximized (nowhere to dock).\nconst TabsetGrip = ({ children, ref: userRef, ...props }: TabsetGripProps): ReactNode => {\n const activeTabName = useTabset((state) => state.activeTab?.name ?? \"\");\n const isMaximized = useTabset((state) => state.isMaximized);\n const node = useTabset((state) => state.node);\n const draggableTabsets = useLayout((state) => state.draggableTabsets);\n const hidden = !draggableTabsets || isMaximized;\n const { ref } = useTabsetDraggable(node.id, hidden, activeTabName);\n const refCallback = useMemo(() => mergeRefs<HTMLButtonElement>(ref, userRef), [ref, userRef]);\n\n if (hidden) {\n return null;\n }\n\n return (\n <button\n aria-label=\"Move tabset\"\n {...props}\n data-dashfoo=\"tabset-grip\"\n ref={refCallback}\n type=\"button\"\n >\n {children ?? <GripIcon />}\n </button>\n );\n};\n\ntype TabsetMaximizeButtonProps = ComponentProps<\"button\">;\n\nconst TabsetMaximizeButton = ({\n children,\n onClick,\n ...props\n}: TabsetMaximizeButtonProps): ReactNode => {\n const isMaximized = useTabset((state) => state.isMaximized);\n const showMaximize = useTabset((state) => state.showMaximize);\n const toggleMaximize = useTabset((state) => state.toggleMaximize);\n\n if (!showMaximize) {\n return null;\n }\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n onClick?.(event);\n toggleMaximize();\n };\n\n return (\n <button\n aria-label={isMaximized ? \"Restore\" : \"Maximize\"}\n {...props}\n aria-pressed={isMaximized}\n data-dashfoo=\"tabset-maximize\"\n onClick={handleClick}\n type=\"button\"\n >\n {children ?? <MaximizeIcon maximized={isMaximized} />}\n </button>\n );\n};\n\nexport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar };\nexport type { TabsetGripProps, TabsetMaximizeButtonProps, TabsetToolbarProps };\n","\"use client\";\n\nimport type { TabsetNode } from \"@dashfoo/core\";\nimport type { ReactNode } from \"react\";\n\nimport { useLayout } from \"../../hooks/layout-store\";\n\nimport { TabsetContent } from \"./tabset-content\";\nimport { TabsetOverflowMenu } from \"./tabset-overflow\";\nimport { TabsetRoot } from \"./tabset-root\";\nimport { useTabset } from \"./tabset-store\";\nimport { TabsetTab, TabsetTrigger } from \"./tabset-tab\";\nimport { TabsetCloseButton, TabsetRenameInput } from \"./tabset-tab-controls\";\nimport { TabsetTablist, TabsetTabStrip } from \"./tabset-tablist\";\nimport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar } from \"./tabset-toolbar\";\n\n// Static JSX hoisted to module scope (elements are immutable, so one instance\n// is safely shared) — DefaultTabsetLayout re-renders on every selection change\n// and must not rebuild it each time.\nconst tabsetContent = <TabsetContent />;\n\n// The canonical composition of the Tabset parts — what DashfooLayout and\n// Layout.Rows render by default, and the reference for anyone composing the\n// parts by hand. Lives inside Tabset.Root because the toolbar-visibility\n// condition needs the store's overflow count.\nconst DefaultTabsetLayout = (): ReactNode => {\n const draggableTabsets = useLayout((state) => state.draggableTabsets);\n const renderTabsetToolbar = useLayout((state) => state.renderTabsetToolbar);\n const tabLocation = useLayout((state) => state.tabLocation);\n const tabStripEnabled = useLayout((state) => state.tabStripEnabled);\n const isMaximized = useTabset((state) => state.isMaximized);\n const node = useTabset((state) => state.node);\n const overflowCount = useTabset((state) => state.overflowItems.length);\n const showMaximize = useTabset((state) => state.showMaximize);\n\n // The toolbar exists only when it has content; each part also self-hides so\n // the condition and the parts can never disagree.\n const showToolbar =\n showMaximize ||\n renderTabsetToolbar !== undefined ||\n overflowCount > 0 ||\n (draggableTabsets && !isMaximized);\n\n const strip = tabStripEnabled ? (\n <TabsetTabStrip>\n <TabsetTablist>\n {node.children.map((tab) => (\n <TabsetTab key={tab.id} tab={tab}>\n <TabsetTrigger />\n <TabsetRenameInput />\n <TabsetCloseButton />\n </TabsetTab>\n ))}\n </TabsetTablist>\n {showToolbar ? (\n <TabsetToolbar>\n <TabsetOverflowMenu />\n <TabsetGrip />\n {renderTabsetToolbar?.(node)}\n <TabsetMaximizeButton />\n </TabsetToolbar>\n ) : null}\n </TabsetTabStrip>\n ) : null;\n\n return tabLocation === \"bottom\" ? (\n <>\n {tabsetContent}\n {strip}\n </>\n ) : (\n <>\n {strip}\n {tabsetContent}\n </>\n );\n};\n\ntype TabsetViewProps = { node: TabsetNode };\n\nconst TabsetView = ({ node }: TabsetViewProps): ReactNode => (\n <TabsetRoot node={node}>\n <DefaultTabsetLayout />\n </TabsetRoot>\n);\n\nexport { TabsetView };\nexport type { TabsetViewProps };\n","\"use client\";\n\nimport type { Dimension, RowNode, SnapConfig, TabsetNode } from \"@dashfoo/core\";\nimport { snapEnabled, snapSizes } from \"@dashfoo/core\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { Fragment, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { GroupImperativeHandle, Layout, Orientation } from \"react-resizable-panels\";\nimport { Group, Panel, Separator } from \"react-resizable-panels\";\n\nimport { useLayout } from \"../hooks/layout-store\";\n\nimport { TabsetView } from \"./tabset/tabset-view\";\n\n// This module is the resize adapter: the only place that imports\n// react-resizable-panels. It maps the model's responsive weights to rrp's\n// percentage layout and unit-typed min/max constraints to rrp sizes, and commits\n// a drag (on release) back to the document as an adjustSplit action.\n//\n// Magnetic snapping is layered on top: rrp's continuous onLayoutChange pulls the\n// dragged boundary onto a grid (groupRef.setLayout), and onLayoutChanged commits\n// the already-snapped weights as a single undo step on release.\n\nconst dimensionToSize = (dimension: Dimension): string => `${dimension.value}${dimension.unit}`;\nconst dimensionToPixels = (dimension: Dimension | undefined): number | undefined =>\n dimension?.unit === \"px\" ? dimension.value : undefined;\n\nconst groupStyle: CSSProperties = { display: \"flex\", flex: 1, minHeight: 0, minWidth: 0 };\nconst WEIGHT_EPSILON = 0.01;\n\ntype LayoutChild = RowNode[\"children\"][number];\n\nconst layoutFromWeights = (children: RowNode[\"children\"], total: number): Layout =>\n children.reduce<Layout>((layout, child) => {\n layout[child.id] = ((child.weight ?? 1) / total) * 100;\n return layout;\n }, {});\n\n// rrp's id-keyed Layout <-> the ordered percentage array the pure snap math wants.\nconst orderedFromLayout = (layout: Layout, children: RowNode[\"children\"]): Array<number> =>\n children.map((child) => layout[child.id] ?? 0);\n\nconst layoutFromOrdered = (sizes: Array<number>, children: RowNode[\"children\"]): Layout =>\n children.reduce<Layout>((layout, child, index) => {\n layout[child.id] = sizes[index] ?? 0;\n return layout;\n }, {});\n\nconst layoutsMatch = (current: Layout, expected: Layout): boolean => {\n const currentIds = Object.keys(current);\n const expectedIds = Object.keys(expected);\n if (currentIds.length !== expectedIds.length) {\n return false;\n }\n return expectedIds.every(\n (id) => Math.abs((current[id] ?? Number.NaN) - expected[id]) <= WEIGHT_EPSILON,\n );\n};\n\nconst descendantMinSize = (\n child: LayoutChild,\n axis: RowNode[\"orientation\"],\n tabsetMinSize: number,\n): number | undefined => {\n const explicit = dimensionToPixels(child.min);\n if (explicit !== undefined) {\n return explicit;\n }\n if (child.min !== undefined) {\n return undefined;\n }\n if (child.type === \"tabset\") {\n return tabsetMinSize;\n }\n\n const childMinimums: Array<number> = [];\n for (const grandchild of child.children) {\n const minimum = descendantMinSize(grandchild, axis, tabsetMinSize);\n if (minimum === undefined) {\n return undefined;\n }\n childMinimums.push(minimum);\n }\n if (childMinimums.length === 0) {\n return undefined;\n }\n\n return child.orientation === axis\n ? childMinimums.reduce((sum, value) => sum + value, 0)\n : Math.max(...childMinimums);\n};\n\ntype RowViewProps = {\n node: RowNode;\n // Substitutes a custom tabset composition at every leaf of the split tree;\n // defaults to the stock TabsetView.\n renderTabset?: (node: TabsetNode) => ReactNode;\n};\n\nconst RowView = ({ node, renderTabset }: RowViewProps): ReactNode => {\n const dispatch = useLayout((state) => state.dispatch);\n const resizableSplits = useLayout((state) => state.resizableSplits);\n const tabsetMinSize = useLayout((state) => state.tabsetMinSize);\n const globalSnap = useLayout((state) => state.snap);\n // A row's own snap attribute overrides the layout-wide default; an empty config\n // (or step 0 with no divisions) means this row does not snap.\n const effectiveSnap: SnapConfig | null = node.snap ?? globalSnap;\n const snapActive = snapEnabled(effectiveSnap);\n const orientation: Orientation = node.orientation === \"row\" ? \"horizontal\" : \"vertical\";\n const total = node.children.reduce((sum, child) => sum + (child.weight ?? 1), 0);\n const desiredLayout = useMemo(\n () => layoutFromWeights(node.children, total),\n [node.children, total],\n );\n\n // rrp fires onLayoutChanged once on mount with its measured layout. That is not\n // a user resize — committing it would rewrite the authored weights and push a\n // spurious undo entry — so the first call (per mount) is ignored.\n const measured = useRef(false);\n const groupRef = useRef<GroupImperativeHandle | null>(null);\n const groupElement = useRef<HTMLDivElement | null>(null);\n const syncing = useRef(false);\n // Which boundary the pointer grabbed (captured on the separator's pointerdown),\n // a re-entrancy guard for our own setLayout, and the snapped boundary mirrored\n // into state only on engage/disengage so the highlight re-renders ~twice a drag.\n const activeBoundary = useRef<number | null>(null);\n const snapping = useRef(false);\n const snappedBoundaryRef = useRef<number | null>(null);\n const [snappedBoundary, setSnappedBoundary] = useState<number | null>(null);\n\n const setHighlight = (boundary: number | null): void => {\n if (boundary !== snappedBoundaryRef.current) {\n snappedBoundaryRef.current = boundary;\n setSnappedBoundary(boundary);\n }\n };\n\n // Toggle the flex transition on the group imperatively (not via React state):\n // it must be live in the DOM before the snapping setLayout writes flex this same\n // frame, so the panels glide onto the grid line. Cleared on free-tracking frames\n // so dragging stays 1:1 with the pointer (snap-out is instant).\n const setSnapTransition = (active: boolean): void => {\n const element = groupElement.current;\n if (!element) {\n return;\n }\n if (active) {\n element.dataset.dashfooSnapping = \"true\";\n } else {\n delete element.dataset.dashfooSnapping;\n }\n };\n\n useLayoutEffect(() => {\n const group = groupRef.current;\n if (!group) {\n return;\n }\n const current = group.getLayout();\n const samePanelSet =\n Object.keys(current).length === Object.keys(desiredLayout).length &&\n Object.keys(desiredLayout).every((id) => id in current);\n if (!samePanelSet || layoutsMatch(current, desiredLayout)) {\n return;\n }\n\n syncing.current = true;\n group.setLayout(desiredLayout);\n const frame = requestAnimationFrame(() => {\n syncing.current = false;\n });\n return () => {\n cancelAnimationFrame(frame);\n syncing.current = false;\n };\n }, [desiredLayout]);\n\n // Fires on every pointer move during a drag. Pull the grabbed boundary onto the\n // nearest grid target and push it back into rrp so the panel visibly sticks. The\n // `snapping` guard plus the idempotence check keep our own setLayout from\n // re-triggering this and prevent jitter at the threshold edge, whatever rrp's\n // re-fire timing.\n const handleLayoutChange = (layout: Layout): void => {\n if (snapping.current || syncing.current || !resizableSplits) {\n return;\n }\n if (effectiveSnap === null || !snapActive) {\n return;\n }\n const boundary = activeBoundary.current;\n if (boundary === null) {\n return;\n }\n\n const { sizes, snapped } = snapSizes(\n orderedFromLayout(layout, node.children),\n boundary,\n effectiveSnap,\n );\n setHighlight(snapped ? boundary : null);\n if (!snapped) {\n // Pointer tracking resumes 1:1 — drop the transition so it never lags.\n setSnapTransition(false);\n return;\n }\n\n // Arm the glide before writing the snapped layout this frame.\n setSnapTransition(true);\n const group = groupRef.current;\n const target = layoutFromOrdered(sizes, node.children);\n if (!group || layoutsMatch(layout, target)) {\n return;\n }\n snapping.current = true;\n group.setLayout(target);\n snapping.current = false;\n };\n\n const handleLayoutChanged = (layout: Layout): void => {\n if (!measured.current) {\n measured.current = true;\n return;\n }\n if (syncing.current) {\n return;\n }\n const boundary = activeBoundary.current;\n activeBoundary.current = null;\n setHighlight(null);\n setSnapTransition(false);\n // Belt over the disabled Group/Separator: a non-resizable row never commits\n // an adjustSplit, even if rrp reports a layout change for another reason.\n if (!resizableSplits) {\n return;\n }\n let weights = node.children.map((child) => layout[child.id] ?? child.weight ?? 1);\n // Re-snap the committed value so the model lands exactly on the grid (one undo\n // step), regardless of where the final frame left rrp.\n if (boundary !== null && effectiveSnap !== null && snapActive) {\n weights = snapSizes(weights, boundary, effectiveSnap).sizes;\n }\n const weightsChanged = weights.some(\n (weight, index) => Math.abs(weight - (node.children[index]?.weight ?? 1)) > WEIGHT_EPSILON,\n );\n\n if (!weightsChanged) {\n return;\n }\n\n dispatch({\n rowId: node.id,\n type: \"adjustSplit\",\n weights,\n });\n };\n\n return (\n <Group\n data-dashfoo=\"row\"\n disabled={!resizableSplits}\n elementRef={groupElement}\n groupRef={groupRef}\n key={node.id}\n onLayoutChange={snapActive ? handleLayoutChange : undefined}\n onLayoutChanged={handleLayoutChanged}\n orientation={orientation}\n style={groupStyle}\n >\n {node.children.map((child, index) => {\n const percent = ((child.weight ?? 1) / total) * 100;\n let min = child.min ? dimensionToSize(child.min) : undefined;\n if (min === undefined) {\n const minimum = descendantMinSize(child, node.orientation, tabsetMinSize);\n min = minimum === undefined ? undefined : `${minimum}px`;\n }\n const max = child.max ? dimensionToSize(child.max) : undefined;\n\n return (\n <Fragment key={child.id}>\n {/* The separator stays mounted when resizing is off — removing it\n would collapse the theme-sized gutter and reflow every panel. */}\n {index > 0 ? (\n <Separator\n data-dashfoo=\"splitter\"\n data-dashfoo-snapped={snappedBoundary === index - 1 ? \"true\" : undefined}\n disabled={!resizableSplits}\n onPointerDown={\n snapActive && resizableSplits\n ? () => {\n activeBoundary.current = index - 1;\n }\n : undefined\n }\n />\n ) : null}\n <Panel defaultSize={`${percent}%`} id={child.id} maxSize={max} minSize={min}>\n {child.type === \"row\" ? (\n <RowView node={child} renderTabset={renderTabset} />\n ) : (\n (renderTabset?.(child) ?? <TabsetView node={child} />)\n )}\n </Panel>\n </Fragment>\n );\n })}\n </Group>\n );\n};\n\nexport { RowView };\nexport type { RowViewProps };\n","\"use client\";\n\nimport { DragProvider } from \"./drag-adapter\";\nimport { LayoutRoot } from \"./layout-root\";\nimport { RowView } from \"./row-view\";\nimport { TabsetView } from \"./tabset/tabset-view\";\n\n// Compound namespace for hand-built layouts, same pattern as Panel/Tabset:\n// Root provides the layout store, DragLayer opts the tree into drag-dock,\n// Rows renders the split tree, and Tabset is the stock tabset composition for\n// leaves that don't need custom chrome.\nconst Layout = {\n DragLayer: DragProvider,\n Root: LayoutRoot,\n Rows: RowView,\n Tabset: TabsetView,\n} as const;\n\nexport { Layout };\nexport type { DragProviderProps as LayoutDragLayerProps } from \"./drag-adapter\";\nexport type { LayoutRootProps } from \"./layout-root\";\nexport type { RowViewProps as LayoutRowsProps } from \"./row-view\";\nexport type { TabsetViewProps as LayoutTabsetProps } from \"./tabset/tabset-view\";\n","\"use client\";\n\nimport type {\n Action,\n Dashfoo,\n DockLocation,\n Orientation,\n SnapConfig,\n TabNode,\n TabsetNode,\n} from \"@dashfoo/core\";\nimport { findTabset, stackModel } from \"@dashfoo/core\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from \"react\";\n\nimport type { PersistConfig, StorageAdapter } from \"../hooks/persistence\";\nimport { localStorageAdapter, usePersistence } from \"../hooks/persistence\";\nimport { useContainerWidth } from \"../hooks/responsive\";\nimport { useDashfooStore } from \"../hooks/store\";\n\nimport { Layout } from \"./layout\";\n\nconst DEFAULT_PERSIST_DEBOUNCE_MS = 300;\n\n// `persist` accepts a bare localStorage key or a full target (custom storage,\n// debounce). Controlled mode (a `model` prop, no `defaultModel`) skips it —\n// persistence implies the library owns the state.\ntype PersistInput = string | { debounceMs?: number; key: string; storage?: StorageAdapter };\n\nconst resolvePersist = (\n persist: PersistInput | undefined,\n hasDefaultModel: boolean,\n): PersistConfig | null => {\n if (persist === undefined || !hasDefaultModel) {\n return null;\n }\n if (typeof persist === \"string\") {\n return { debounceMs: DEFAULT_PERSIST_DEBOUNCE_MS, key: persist, storage: localStorageAdapter };\n }\n return {\n debounceMs: persist.debounceMs ?? DEFAULT_PERSIST_DEBOUNCE_MS,\n key: persist.key,\n storage: persist.storage ?? localStorageAdapter,\n };\n};\n\ntype TabComponent = ComponentType<{ node: TabNode }>;\n\n// Component names already warned about, so the dev warning fires at most once each.\nconst warnedComponents = new Set<string>();\n\n// Imperative API exposed via ref — lets a host drive the layout from a toolbar,\n// command palette, or keyboard shortcuts without rebuilding the store's plumbing.\ntype DashfooHandle = {\n addTab: (\n tab: TabNode,\n target: { index?: number; location?: DockLocation; targetId: string },\n ) => void;\n canRedo: () => boolean;\n canUndo: () => boolean;\n closeTab: (tabId: string) => void;\n dispatch: (action: Action) => void;\n getModel: () => Dashfoo;\n maximizeTabset: (tabsetId: string | null) => void;\n redo: () => void;\n renameTab: (tabId: string, name: string) => void;\n // Reset to the original defaultModel, clearing undo history and any persisted\n // copy. No-op in controlled mode (the host owns the model).\n resetLayout: () => void;\n selectTab: (tabsetId: string, index: number) => void;\n undo: () => void;\n};\n\ntype DashfooLayoutProps = {\n closableTabs?: boolean;\n components?: Record<string, TabComponent>;\n defaultModel?: Dashfoo;\n draggableTabs?: boolean;\n draggableTabsets?: boolean;\n // false renders a static dashboard: no drag, close, rename, or resize. Tab\n // selection, maximize, and the imperative ref API keep working.\n editable?: boolean;\n factory?: (tab: TabNode) => ReactNode;\n keepMounted?: boolean;\n maximizable?: boolean;\n model?: Dashfoo;\n onAction?: (action: Action) => Action | null;\n onActiveTabsetChange?: (tabsetId: string | undefined) => void;\n onMaximizedTabsetChange?: (tabsetId: string | undefined) => void;\n onModelChange?: (model: Dashfoo, action?: Action) => void;\n persist?: PersistInput;\n renamableTabs?: boolean;\n renderTabLabel?: (tab: TabNode) => ReactNode;\n renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;\n // At or below `maxWidth` (the layout's own container width, not the viewport)\n // the layout renders one stacked column and locks drag and resize. The stack is\n // a view-only projection, so widening back is lossless and never remounts.\n // `orientation` defaults to \"column\".\n resizableSplits?: boolean;\n responsive?: { maxWidth: number; orientation?: Orientation };\n // Magnetic snap for split resize: the dragged boundary pulls to multiples of\n // `step` (percent of the group) within `threshold`. A row may override this via\n // its own `snap` attribute. Off when omitted; locked off in compact mode.\n snap?: SnapConfig;\n};\n\n// The batteries-included component: owns the store (controlled or uncontrolled),\n// resolves tab content via a components registry or a factory, and assembles the\n// same Layout/Tabset primitives a hand-built layout would use.\n// forwardRef (not the React 19 ref-as-prop) keeps the React 18 peer working.\nconst DashfooLayout = forwardRef<DashfooHandle, DashfooLayoutProps>((props, ref): ReactNode => {\n const {\n closableTabs = true,\n components,\n defaultModel,\n draggableTabs = true,\n draggableTabsets = true,\n editable = true,\n factory,\n keepMounted = false,\n maximizable = true,\n model,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange,\n persist,\n renamableTabs = true,\n renderTabLabel,\n renderTabsetToolbar,\n resizableSplits = true,\n responsive,\n snap,\n } = props;\n\n const persistConfig = useMemo(\n () => resolvePersist(persist, defaultModel !== undefined),\n [persist, defaultModel],\n );\n const persistence = usePersistence(persistConfig, defaultModel);\n\n // The save side of persistence rides onModelChange; the consumer's own handler\n // still fires after it.\n const handleModelChange = useCallback(\n (next: Dashfoo, action?: Action): void => {\n persistence.save(next);\n onModelChange?.(next, action);\n },\n [onModelChange, persistence],\n );\n\n const store = useDashfooStore({\n defaultModel: persistence.initialModel ?? defaultModel,\n model,\n onAction,\n onActiveTabsetChange,\n onMaximizedTabsetChange,\n onModelChange: handleModelChange,\n });\n\n useImperativeHandle(\n ref,\n () => ({\n addTab: (tab, target) =>\n store.dispatch({\n index: target.index,\n location: target.location ?? \"center\",\n tab,\n targetId: target.targetId,\n type: \"addNode\",\n }),\n canRedo: store.canRedo,\n canUndo: store.canUndo,\n closeTab: (tabId) => store.dispatch({ tabId, type: \"deleteTab\" }),\n dispatch: store.dispatch,\n getModel: () => store.model,\n maximizeTabset: (tabsetId) => store.dispatch({ tabsetId, type: \"setMaximizedTabset\" }),\n redo: store.redo,\n renameTab: (tabId, name) => store.dispatch({ name, tabId, type: \"renameTab\" }),\n resetLayout: () => {\n persistence.clear();\n if (defaultModel !== undefined) {\n store.setModel(defaultModel);\n }\n },\n selectTab: (tabsetId, index) => store.dispatch({ index, tabsetId, type: \"selectTab\" }),\n undo: store.undo,\n }),\n [defaultModel, persistence, store],\n );\n\n const renderTab = useCallback(\n (tab: TabNode): ReactNode => {\n if (factory) {\n return factory(tab);\n }\n const Component = components?.[tab.component];\n if (!Component) {\n if (components && !warnedComponents.has(tab.component)) {\n warnedComponents.add(tab.component);\n // A tab whose component is not registered renders nothing — surface it\n // once so the misconfiguration is not silent.\n // oxlint-disable-next-line no-console\n console.warn(`[dashfoo] no component registered for \"${tab.component}\"`);\n }\n return null;\n }\n return <Component node={tab} />;\n },\n [components, factory],\n );\n\n // While compact, render a stacked projection; the canonical store.model is\n // untouched (ids survive stackModel, so tap-to-select still routes), so widening\n // back is lossless.\n const [containerRef, width] = useContainerWidth();\n const isCompact = responsive !== undefined && width <= responsive.maxWidth;\n const view = useMemo(\n () => (isCompact ? stackModel(store.model, responsive?.orientation) : store.model),\n [isCompact, responsive?.orientation, store.model],\n );\n\n // rrp auto-fires onLayoutChanged as it re-measures the stacked structure, which\n // would commit an adjustSplit and corrupt the canonical model. The lock flags\n // can't gate it — they reach the resize adapter via the layout store one commit\n // late, so resizableSplits is stale-true during the swap. Gate at the dispatch\n // boundary instead: a stable dispatch reads isCompact from a ref written during\n // render (current before rrp's onLayoutChanged, which can fire in a child effect\n // ahead of this component's effects).\n const isCompactRef = useRef(isCompact);\n isCompactRef.current = isCompact;\n const dispatch = useCallback(\n (action: Action): void => {\n if (isCompactRef.current && action.type === \"adjustSplit\") {\n return;\n }\n store.dispatch(action);\n },\n [store],\n );\n\n // A maximized tabset fills the frame on its own; otherwise the row tree renders.\n const maximized = view.maximizedTabsetId ? findTabset(view, view.maximizedTabsetId) : undefined;\n\n return (\n <Layout.Root\n closableTabs={closableTabs}\n dispatch={dispatch}\n draggableTabs={isCompact ? false : draggableTabs}\n draggableTabsets={isCompact ? false : draggableTabsets}\n editable={editable}\n keepMounted={keepMounted}\n maximizable={maximizable}\n model={view}\n renamableTabs={renamableTabs}\n renderTab={renderTab}\n renderTabLabel={renderTabLabel}\n renderTabsetToolbar={renderTabsetToolbar}\n resizableSplits={isCompact ? false : resizableSplits}\n rootRef={containerRef}\n snap={snap}\n >\n <Layout.DragLayer>\n {maximized ? <Layout.Tabset node={maximized} /> : <Layout.Rows node={view.layout} />}\n </Layout.DragLayer>\n </Layout.Root>\n );\n});\n\nDashfooLayout.displayName = \"DashfooLayout\";\n\nexport { DashfooLayout };\nexport type { DashfooHandle, DashfooLayoutProps, TabComponent };\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useInsertionEffect, useState } from \"react\";\n\nimport {\n createDragManager,\n createDragSubjectStore,\n DragSubjectStoreContext,\n SharedDragManagerContext,\n} from \"../hooks/drag-hooks\";\n\n// Shares one DragDropManager between a DashfooLayout and external tab sources\n// (useExternalTabSource) rendered outside it, so a drag can start at a widget\n// list and end on the layout. Optional: a standalone DashfooLayout creates its\n// own manager and behaves as before.\nconst DashfooDragProvider = ({ children }: { children: ReactNode }): ReactNode => {\n // useState lazily constructs the manager once; the destroy rides a\n // useInsertionEffect cleanup (not useEffect) so StrictMode's simulated unmount\n // doesn't tear down the live instance — the same pattern DragProvider uses.\n const [manager] = useState(createDragManager);\n useInsertionEffect(() => () => manager.destroy(), [manager]);\n // Hosting the drag store here (the nested DragLayer adopts it instead of\n // creating its own) lets useDragSubject/useDropIntent observe the drag from\n // anywhere under the provider — widget lists, custom drop indicators — not\n // just from inside the layout.\n const [subjectStore] = useState(createDragSubjectStore);\n\n return (\n <SharedDragManagerContext.Provider value={manager}>\n <DragSubjectStoreContext.Provider value={subjectStore}>\n {children}\n </DragSubjectStoreContext.Provider>\n </SharedDragManagerContext.Provider>\n );\n};\n\nexport { DashfooDragProvider };\n","\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\n\ntype PanelRootProps = ComponentProps<\"div\">;\ntype PanelHeaderProps = ComponentProps<\"div\">;\ntype PanelBodyProps = ComponentProps<\"div\">;\ntype PanelTitleProps = ComponentProps<\"span\">;\ntype PanelIconProps = ComponentProps<\"span\">;\ntype PanelBadgeProps = ComponentProps<\"span\">;\n\nconst PanelRoot = (props: PanelRootProps): ReactNode => <div {...props} data-dashfoo=\"panel\" />;\nconst PanelHeader = (props: PanelHeaderProps): ReactNode => (\n <div {...props} data-dashfoo=\"panel-header\" />\n);\nconst PanelBody = (props: PanelBodyProps): ReactNode => (\n <div {...props} data-dashfoo=\"panel-body\" />\n);\nconst PanelTitle = (props: PanelTitleProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-title\" />\n);\nconst PanelIcon = (props: PanelIconProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-icon\" />\n);\nconst PanelBadge = (props: PanelBadgeProps): ReactNode => (\n <span {...props} data-dashfoo=\"panel-badge\" />\n);\n\nconst Panel = {\n Badge: PanelBadge,\n Body: PanelBody,\n Header: PanelHeader,\n Icon: PanelIcon,\n Root: PanelRoot,\n Title: PanelTitle,\n} as const;\n\nexport { Panel };\nexport type {\n PanelBadgeProps,\n PanelBodyProps,\n PanelHeaderProps,\n PanelIconProps,\n PanelRootProps,\n PanelTitleProps,\n};\n","\"use client\";\n\nimport { TabsetContent } from \"./tabset-content\";\nimport { TabsetOverflowMenu } from \"./tabset-overflow\";\nimport { TabsetRoot } from \"./tabset-root\";\nimport { TabsetTab, TabsetTrigger } from \"./tabset-tab\";\nimport { TabsetCloseButton, TabsetRenameInput } from \"./tabset-tab-controls\";\nimport { TabsetTablist, TabsetTabStrip } from \"./tabset-tablist\";\nimport { TabsetGrip, TabsetMaximizeButton, TabsetToolbar } from \"./tabset-toolbar\";\n\n// Compound namespace, same pattern as Panel: users compose the parts freely and\n// they coordinate through the scoped store Tabset.Root provides.\nconst Tabset = {\n CloseButton: TabsetCloseButton,\n Content: TabsetContent,\n Grip: TabsetGrip,\n MaximizeButton: TabsetMaximizeButton,\n OverflowMenu: TabsetOverflowMenu,\n RenameInput: TabsetRenameInput,\n Root: TabsetRoot,\n Tab: TabsetTab,\n Tablist: TabsetTablist,\n TabStrip: TabsetTabStrip,\n Toolbar: TabsetToolbar,\n Trigger: TabsetTrigger,\n} as const;\n\nexport { Tabset };\nexport type { TabsetContentProps } from \"./tabset-content\";\nexport type { TabsetRootProps } from \"./tabset-root\";\nexport type { TabsetTabProps, TabsetTriggerProps } from \"./tabset-tab\";\nexport type { TabsetCloseButtonProps, TabsetRenameInputProps } from \"./tabset-tab-controls\";\nexport type { TabsetTablistProps, TabsetTabStripProps } from \"./tabset-tablist\";\nexport type {\n TabsetGripProps,\n TabsetMaximizeButtonProps,\n TabsetToolbarProps,\n} from \"./tabset-toolbar\";\n"],"mappings":"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"}