{"version":3,"file":"web-DPOnSAGK.mjs","names":["React","createContext","useContext","ControlPanelHost","nesting","inlineDepth","descriptions","sections","ControlPanelHostContext","useControlPanelHost","host","Error","useSectionStyle","ControlPanelHostProvider","t0","$","_c","children","t1","GroupDepthContext","useGroupDepth","GroupDepthProvider","depth","_c","defineReportSink","useResizeObserver","useEventCallback","useEffect","useRef","RefObject","CP_NEEDS_ATTR","PanelOverflowFault","label","page","needs","panelWidth","contentWidth","offender","message","panelOverflowReportSink","EPSILON","usePanelOverflowGuard","ref","$","_c","reported","t0","root","current","surface","closest","isConnected","clientWidth","overflow","measureOverflow","cap","parseFloat","getComputedStyle","maxWidth","Number","isFinite","getBoundingClientRect","width","failLoudly","describe","check","t1","t2","root_0","frame","mutation","MutationObserver","requestAnimationFrame","observe","childList","subtree","attributes","attributeFilter","disconnect","cancelAnimationFrame","Overflow","HTMLElement","edge","right","showing","querySelector","scrollWidth","querySelectorAll","find","el","cell","excess","overflowX","level","getAttribute","textContent","trim","Set","map","name","declared","length","join","selectorOf","Math","round","classes","classList","slice","tagName","toLowerCase","fault","emit","import","meta","env","DEV","Error","_c","cn","MENU_LABEL_PAINT","MenuSurfaceProvider","useRailGuard","Text","React","useSectionStyle","SETTINGS_HEADING","SETTINGS_PROSE","usePanelOverflowGuard","ControlPanelWidthNeed","ControlPanelProps","className","children","ReactNode","ControlPanel","t0","$","_c","ariaLabel","rootRef","t1","t2","t3","ControlPanelSectionProps","label","description","needs","flush","ControlPanelSection","style","undefined","t4","ControlPanelFooterProps","ControlPanelFooter","ControlPanelEmptyProps","ControlPanelEmpty","ControlPanelRuleListProps","ControlPanelRuleList","ControlPanel","_c","_Fragment","dragIndicatorIcon","HANDLE_REVEAL","createContext","useContext","React","LabelMarkContext","ReactNode","ControlPanelLabelMark","t0","$","_c","mark","children","t1","useLabelMark","_c","cn","WithTooltip","Stack","React","useLabelMark","HintedLabelCellProps","hint","description","detail","wrap","descriptionId","className","children","ReactNode","HintedLabelCell","t0","$","_c","mark","t1","undefined","text","wraps","second","t2","t3","t4","t5","t6","cell","t7","DescribedLabelCell","_c","_Fragment","createContext","useCallback","useContext","useMemo","useState","React","PanelStackEntry","key","title","render","ReactNode","PanelStackState","entries","push","entry","pop","close","reset","usePanelStackState","$","_c","t0","Symbol","for","setEntries","t1","prev","at","t2","_temp","t3","prev_1","index","findIndex","entry_0","slice","t4","t5","prev_0","PanelStackApi","depth","PanelStackContext","usePanelStack","api","Error","_c","cn","MENU_ROW_CHECKED","MENU_ROW_PAINT","MENU_VALUE","CheckboxIndicator","SwitchIndicator","RowActions","rowActionsAnchor","Activation","linkProps","React","useCallback","useContext","useId","dragIndicatorIcon","HANDLE_REVEAL","ControlPanelHandleProps","HintedLabelCell","PanelStackContext","PanelStackEntry","symbol","Icon","checkIcon","chevronRightIcon","ControlPanelRowSelect","ControlPanelRowTone","ControlPanelRowExplanation","hint","description","detail","ControlPanelRowCommon","handle","handleProps","trailing","ReactNode","actions","restingActions","tone","muted","wrap","inactive","keepFocus","disabled","className","ref","Ref","HTMLElement","children","ControlPanelRowTarget","onSelect","href","push","activation","ControlPanelRowIndicator","ControlPanelRowProps","icon","select","checked","indicator","selected","TITLE_LINE_CELL","ControlPanelRow","t0","$","_c","t1","t2","onSelectProp","undefined","hintId","stack","Error","title","t3","isLink","isButton","t4","linkPressHandlers","selectHandlers","press","interactive","hasActions","t5","el","current","hostRef","markLeads","t6","mark","leading","occupiesLeadingCell","t7","trailingMark","t8","trailingContent","described","t9","t10","t11","t12","t13","t14","t15","rowClass","t16","role","SELECT_ROLE","selection","t17","toneAttr","t18","needsAttr","t19","describedBy","t20","t21","t22","t23","t24","t25","t26","t27","t28","t29","leadingCells","t30","t31","t32","trailingCell","t33","t34","Symbol","for","t35","t36","t37","t38","SelectRegion","focus","SelectionAttrs","DescribedAttrs","Record","check","radio","switch","SelectHandlers","onClick","MouseEventHandler","onMouseDown","onAuxClick","e","button","preventDefault","target","link","_c","_Fragment","HANDLE_REVEAL","dragIndicatorIcon","cn","RowActions","rowActionsAnchor","Text","React","useId","Icon","dragIndicatorIcon","HANDLE_REVEAL","ControlPanelHandleProps","HintedLabelCell","ControlPanelMark","ControlPanelFit","SettingRowProps","label","ReactNode","hint","control","fit","status","actions","mark","handle","handleProps","disabled","className","SettingRow","t0","$","_c","hintId","t1","t2","undefined","t3","t4","t5","t6","t7","t8","t9","t10","t11","SettingNote","children","SettingDescription","_c","HANDLE_REVEAL","dragIndicatorIcon","React","SettingDescription","SettingNote","SettingRow","ControlPanelMark","ControlPanelBlockProps","label","ReactNode","hint","description","children","status","actions","mark","note","className","ControlPanelBlock","t0","$","_c","t1","t2","t3","t4","_c","_Fragment","Button","cn","React","ComponentProps","symbol","Icon","keyboardArrowDownIcon","ControlPanelFieldProps","Omit","icon","ReactNode","label","placeholder","ControlPanelField","t0","$","_c","className","ref","rest","t1","undefined","empty","t2","t3","t4","t5","t6","t7","t8","t9","t10","Symbol","for","t11","_c","cn","React","useEffect","useId","useState","createPortal","ControlPanelSection","ControlPanelRow","ControlPanelHandleProps","GroupDepthProvider","useControlPanelHost","useGroupDepth","usePanelStack","SettingDescription","SettingNote","SettingRow","ControlPanelMark","ControlPanelGroupProps","label","ReactNode","hint","description","summary","status","actions","mark","note","handle","handleProps","children","className","ControlPanelGroup","t0","$","_c","host","depth","key","page","setPage","close","t1","t2","inline","nesting","inlineDepth","Error","t3","t4","Symbol","for","display","t5","title","render","t6","t7","t8","t9","t10","_c","_Fragment","useEffect","useMemo","useRef","React","ControlPanelSection","ControlPanelRow","PanelStackContext","usePanelStackState","PanelStackApi","PanelStackState","symbol","Icon","focusElement","arrowBackIcon","ControlPanelStackProps","root","ReactNode","onExhausted","state","ControlPanelStack","t0","$","_c","own","entries","stack","push","pop","close","reset","containerRef","depth","length","t1","api","t2","t3","el","current","onKeyDown","event","key","stopPropagation","preventDefault","addEventListener","removeEventListener","prevDepth","t4","t5","grew","first","querySelector","reveal","t6","at","top","t7","index","content","undefined","display","level","t8","Symbol","for","t9","title","t10","t11","t12","entry","index_0","render","map","t13","_c","cn","IconButton","RowActions","rowActionsAnchor","React","symbol","Icon","closeIcon","dragIndicatorIcon","ControlPanelRuleRowProps","prefix","ReactNode","field","operator","value","actions","actionsAtRest","onRemove","removeLabel","handle","handleProps","HTMLAttributes","HTMLElement","ref","Ref","HTMLDivElement","style","CSSProperties","className","HANDLE_REVEAL","ControlPanelRuleRow","t0","$","_c","t1","t2","undefined","t3","t4","t5","t6","t7","t8","t9","t10","t11","t12","t13","t14","t15","t16","closeIcon","_c","React","ControlPanelHandleProps","SettingNote","SettingRow","ControlPanelFit","ControlPanelMark","ControlPanelSettingProps","label","ReactNode","hint","control","fit","status","actions","mark","note","handle","handleProps","disabled","className","ControlPanelSetting","t0","$","_c","t1","t2","t3","_c","_Fragment","cn","MENU_LABEL_PAINT","Text","React","ControlPanelSubheadProps","children","ReactNode","className","ControlPanelSubhead","t0","$","_c","t1","t2","_c","ControlPanelRoot","Popover","PopoverContent","PopoverTrigger","PopoverMaxHeight","React","ComponentProps","ControlPanel","ControlPanelHostProvider","ControlPanelHost","ControlPanelStack","usePanelStackState","ControlPanelPositioning","Pick","ControlPanelAnchoring","trigger","ReactElement","anchor","RefObject","Element","open","onOpenChange","ControlPanelPopoverProps","ControlPanelPopoverOwnProps","maxHeight","label","children","ReactNode","ControlPanelPopover","t0","$","_c","align","t1","side","t2","undefined","stack","t3","isOpen","reset","t4","t5","POPOVER_HOST","t6","t7","t8","nesting","inlineDepth","descriptions","sections","_c","ControlPanel","Popover","PopoverContent","PopoverTrigger","Fill","Stack","Text","IconButton","SETTINGS_TITLE","symbol","React","useRef","useState","ControlPanel","ControlPanelAnchoring","ControlPanelPositioning","ControlPanelHostProvider","ControlPanelHost","ControlPanelStack","usePanelStackState","closeIcon","ControlPanelCardProps","title","open","onOpenChange","children","ReactNode","ControlPanelCard","t0","$","_c","trigger","anchor","align","t1","side","t2","undefined","stack","ownOpen","setOwnOpen","isOpen","t3","next","setOpen","headRef","t4","nowOpen","reset","t5","t6","Symbol","for","openType","current","t7","t8","t9","t10","CARD_HOST","t11","t12","t13","nesting","inlineDepth","descriptions","sections","_c","ControlPanel","React","ControlPanel","ControlPanelHostProvider","ControlPanelHost","ControlPanelStack","ControlPanelPaneProps","label","children","ReactNode","ControlPanelPane","t0","$","_c","t1","PANE_HOST","t2","nesting","inlineDepth","descriptions","sections","_c","ControlPanel","PluginDefinition","ControlPanel","ControlPanelPopover","ControlPanelPopoverProps","ControlPanelWidthNeed","panelOverflowReportSink","PanelOverflowFault","usePanelStack","PanelStackApi","PanelStackEntry","ControlPanelStackProps","ControlPanelProps","ControlPanelSectionProps","ControlPanelFooterProps","ControlPanelEmptyProps","ControlPanelRuleListProps","ControlPanelRowProps","ControlPanelRowSelect","ControlPanelRowIndicator","ControlPanelRowTone","ControlPanelRuleRowProps","ControlPanelFieldProps","ControlPanelSettingProps","ControlPanelBlockProps","ControlPanelSubheadProps","ControlPanelGroupProps","ControlPanelHandleProps","ControlPanelFit","ControlPanelMark","useControlPanelHost","ControlPanelHost","ControlPanelCard","ControlPanelCardProps","ControlPanelPane","ControlPanelPaneProps","description","contributions","ControlPanelHostContext","createContext","host","useContext","$","c","t0","t1","children","GroupDepthContext","depth","SETTINGS_TITLE","SETTINGS_HEADING","SETTINGS_PROSE","CP_NEEDS_ATTR","panelOverflowReportSink","defineReportSink","EPSILON","reported","useRef","label","ref","root","surface","panelWidth","overflow","cap","check","useEventCallback","t2","root_0","frame","mutation","useEffect","edge","showing","offender","el","cell","excess","level","page","needs","name","declared","contentWidth","classes","fault","ariaLabel","rootRef","useRailGuard","className","cn","MenuSurfaceProvider","t3","style","flush","description","Fragment","Text","MENU_LABEL_PAINT","t4","dragIndicatorIcon$1","symbol","HANDLE_REVEAL$1","LabelMarkContext","mark","detail","second","descriptionId","wrap","t5","hint","t6","t7","WithTooltip","wraps","Stack","entries","setEntries","useState","prev","entry","push","pop","index","prev_1","entry_0","key","close","reset","prev_0","PanelStackContext","api","checkIcon","chevronRightIcon","TITLE_LINE_CELL","onSelectProp","indicator","tone","hintId","useId","stack","onSelect","isLink","href","isButton","activation","disabled","keepFocus","press","interactive","hasActions","actions","restingActions","hostRef","markLeads","checked","select","CheckboxIndicator","Icon","leading","icon","occupiesLeadingCell","trailingMark","t8","trailing","SwitchIndicator","trailingContent","described","t9","muted","t10","inactive","t11","selected","t12","t13","rowActionsAnchor","t14","t15","MENU_ROW_PAINT","MENU_ROW_CHECKED","rowClass","t16","SELECT_ROLE","selection","t17","toneAttr","t18","needsAttr","t19","describedBy","t20","handle","t21","handleProps","t22","t23","t24","t25","t26","t27","t28","t29","leadingCells","t30","t31","MENU_VALUE","t32","trailingCell","t33","t34","t35","RowActions","t36","t37","t38","focus","e","target","link","linkProps","control","fit","status","note","keyboardArrowDownIcon","rest","placeholder","empty","Button","setPage","summary","createPortal","arrowBackIcon","own","state","containerRef","onExhausted","event","prevDepth","grew","first","top","content","index_0","closeIcon$1","dragIndicatorIcon","HANDLE_REVEAL","actionsAtRest","removeLabel","operator","prefix","field","value","onRemove","IconButton","ControlPanel","align","side","isOpen","trigger","PopoverTrigger","POPOVER_HOST","anchor","maxHeight","PopoverContent","onOpenChange","open","Popover","closeIcon","ownOpen","setOwnOpen","next","setOpen","headRef","nowOpen","openType","title","Fill","CARD_HOST","PANE_HOST","web_default"],"sources":["../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/host.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/settings-picks.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/width-need.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/overflow-guard.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/control-panel.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/handle.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/label-mark.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/hint.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/stack-context.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/control-panel-row.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/setting-row.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/block.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/field-control.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/group.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/panel-stack.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/rule-row.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/setting.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/subhead.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/namespace.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/control-panel-popover.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/control-panel-card.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/internal/control-panel-pane.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/control-panel/web/index.ts"],"sourcesContent":["import type React from \"react\";\nimport { createContext, useContext } from \"react\";\n\nexport interface ControlPanelHost {\n /**\n * `\"push\"` — a `Group` is a drill row that pushes a panel-stack entry.\n * `\"inline\"` — a `Group` is an indented labelled band, down to `inlineDepth`.\n */\n readonly nesting: \"push\" | \"inline\";\n /**\n * How many levels of `inline` this host has room for. Below it a `Group`\n * pushes anyway, so a list-of-objects-of-lists in a 500px pane does not\n * collapse into nothing.\n */\n readonly inlineDepth: number;\n /** Where a field's description goes: on the band, or behind a `hint`. */\n readonly descriptions: \"band\" | \"hint\";\n /**\n * How a `Section` names its band: `\"label\"` — the shared menu section label\n * (a popover, a pane), or `\"heading\"` — a settings card's heading over\n * body prose (`ControlPanelCard`). The surface's answer, never the call\n * site's, so one `` reads right wherever it renders.\n */\n readonly sections: \"label\" | \"heading\";\n}\n\nconst ControlPanelHostContext = createContext(null);\n\n/**\n * The host's presentation policy — the answers a member needs that belong to the\n * SURFACE rather than to the field.\n *\n * It THROWS when there is no host, the same policy as `usePanelStack()` and for\n * the same reason: a `Group` cannot render correctly in a host that has not said\n * whether to push or to inline, and a silent default shows up as a dead click at\n * depth 2 rather than as an error anyone can find. `ControlPanelPopover`,\n * `ControlPanelPane` and `ControlPanelCard` each publish one, so every panel opened through the\n * vocabulary already has it.\n */\nexport function useControlPanelHost(): ControlPanelHost {\n const host = useContext(ControlPanelHostContext);\n if (!host) {\n throw new Error(\n \"useControlPanelHost() requires a ControlPanelPopover, ControlPanelPane or ControlPanelCard \" +\n \"ancestor. The host decides how a Group presents (push vs inline) and \" +\n \"where a description goes; there is no correct default.\",\n );\n }\n return host;\n}\n\n/**\n * How a `Section` names its band here. Unlike `useControlPanelHost()` this does\n * not throw without a host: a bare `` in a `DialogContent` (the\n * documented second home for one) has no host, and a section there keeps the\n * label it always drew — the one answer that existed before hosts had a say.\n */\nexport function useSectionStyle(): ControlPanelHost[\"sections\"] {\n return useContext(ControlPanelHostContext)?.sections ?? \"label\";\n}\n\n/**\n * Publishes the policy. Not exported from the barrel: a host is one of the three\n * surfaces, and letting a caller invent a third policy at a call site is exactly\n * the `mode` prop this design removed from `Group`.\n */\nexport function ControlPanelHostProvider({\n host,\n children,\n}: {\n host: ControlPanelHost;\n children: React.ReactNode;\n}) {\n return (\n {children}\n );\n}\n\n/**\n * How many inline groups this subtree is already inside. Zero at the panel's\n * top level; each inline `Group` publishes one more for its children, which is\n * what makes `inlineDepth` a budget rather than a per-call-site guess.\n */\nconst GroupDepthContext = createContext(0);\n\nexport function useGroupDepth(): number {\n return useContext(GroupDepthContext);\n}\n\nexport function GroupDepthProvider({\n depth,\n children,\n}: {\n depth: number;\n children: React.ReactNode;\n}) {\n return {children};\n}\n","import type { RolePart } from \"@plugins/primitives/plugins/css/plugins/text/core\";\n\n/**\n * The settings card's three picked parts, read by ``: the card's\n * title, a section's heading and a section's prose. Their tokens are the\n * overlay group's `settingsTitle*` / `settingsHeading*` / `settingsProse*`\n * (`ui/tokens/overlay`), named here by CSS var — the way any component reads\n * a token — because this plugin cannot import that group (theme-engine, which\n * defines it, reaches back here through config_v2). Each fallback is its\n * role token's default, drawn while the token is unset.\n */\nexport const SETTINGS_TITLE: RolePart = {\n role: \"--settings-title-role\",\n emphasis: \"--settings-title-emphasis\",\n family: \"--settings-title-family\",\n fallback: \"subheading\",\n};\n\nexport const SETTINGS_HEADING: RolePart = {\n role: \"--settings-heading-role\",\n emphasis: \"--settings-heading-emphasis\",\n family: \"--settings-heading-family\",\n fallback: \"label\",\n};\n\nexport const SETTINGS_PROSE: RolePart = {\n role: \"--settings-prose-role\",\n emphasis: \"--settings-prose-emphasis\",\n family: \"--settings-prose-family\",\n fallback: \"caption\",\n};\n","/**\n * The width a panel's CONTENT needs — the vocabulary's only width dial, and it\n * is turned by the content, never by the code that opens the panel:\n *\n * - `described` — choices that each carry a visible description line (its\n * width decides how many lines each description wraps to);\n * - `readout` — rows that carry a measured reading beside their label (a\n * meter bar over its value, e.g. \"51% left\"), so label, reading\n * and the row's mark each keep their own column without truncating;\n * - `picker` — a body that is a grid (swatches, icons, covers, a calendar);\n * - `builder` — a six-track rule row (filter, sort).\n *\n * `menu` — a list of short choices — is the baseline and is never declared.\n *\n * An element states its need as `data-cp-needs` ({@link CP_NEEDS_ATTR}):\n * `RuleRow` and a `Row` with a `description` stamp theirs themselves, and\n * content the vocabulary cannot see into (a foreign grid) declares it on the\n * `Section` / `Block` that holds it (`needs`). The popover surface collects\n * the showing page's needs in CSS (`cp-surface` in `app.css`) and takes\n * the highest-ranked one — menu < described < readout < picker < builder — sized by the\n * matching `popoverWidth*` density token, so a theme sizes each bucket.\n *\n * It used to be declared by the OPENER (`ControlPanelPopover size`,\n * `PanelStackEntry.size`), which only knows what it opens by convention: the\n * view-settings menu's Fold rows page opened the filter builder at menu width\n * and its rule rows overflowed.\n */\nexport type ControlPanelWidthNeed =\n \"described\" | \"readout\" | \"picker\" | \"builder\";\n\n/** The attribute an element states its width need with. */\nexport const CP_NEEDS_ATTR = \"data-cp-needs\";\n","import { defineReportSink } from \"@plugins/primitives/plugins/report-sink/core\";\nimport { useResizeObserver } from \"@plugins/primitives/plugins/dom/plugins/element-size/web\";\nimport { useEventCallback } from \"@plugins/primitives/plugins/latest-ref/web\";\nimport { useEffect, useRef, type RefObject } from \"react\";\n\nimport { CP_NEEDS_ATTR } from \"./width-need\";\n\n/**\n * A control panel in a popover whose showing page is WIDER than the panel —\n * the measured backstop behind `width-need.ts`.\n *\n * The surface's width is its content's declared need. A need nobody declared\n * (a foreign grid in a `Section` without `needs`, a new kind of wide control)\n * leaves the panel at a narrower bucket than its content, and the content\n * spills past the panel's edge. That looks *almost* right — a row cut off at\n * the panel edge — which is how it reached a user's screenshot the first time\n * (the Fold rows page drawing the filter builder at menu width). So it is\n * measured where it happens and filed, rather than left to be noticed.\n */\nexport interface PanelOverflowFault {\n /** The panel's accessible label, or `\"unlabelled panel\"`. */\n label: string;\n /** The showing page's title (its back header), or `\"root\"`. */\n page: string;\n /** Every width need the showing page declares (`data-cp-needs`). */\n needs: readonly string[];\n /** The panel's own width, in px. */\n panelWidth: number;\n /** The width its content needs, in px. */\n contentWidth: number;\n /** The element whose content does not fit: the first one past the panel's\n * edge, or the first grid cell its control spills out of. */\n offender: string;\n message: string;\n}\n\n/**\n * Where a panel-overflow fault goes in production. `reports/panel-overflow`\n * registers the mapping to a filed report; with nothing registered `emit` only\n * holds a bounded backlog.\n */\nexport const panelOverflowReportSink = defineReportSink();\n\n/** Sub-pixel layout rounding is not an overflow. */\nconst EPSILON = 0.5;\n\n/**\n * Watch a control panel for content wider than the panel, and fail loudly\n * (report, and throw in dev) the first time it happens.\n *\n * Only a panel inside a `ControlPanelPopover` surface (`cp-surface`) is\n * watched — that is the host whose width is the content's need. A panel the\n * viewport has squeezed below its bucket (`max-width: var(--available-width)`\n * reached) is not a fault: no declaration could have made it fit.\n *\n * Push-based: `useResizeObserver` on the panel (its height follows its content)\n * plus a `MutationObserver` on its subtree (a pushed page, a level switch),\n * each coalesced to one measurement per frame. At most one fault per mount.\n */\nexport function usePanelOverflowGuard(\n ref: RefObject,\n label: string | undefined,\n): void {\n const reported = useRef(false);\n const check = useEventCallback(() => {\n const root = ref.current;\n const surface = root?.closest(\".cp-surface\");\n if (!root || !surface || reported.current || !root.isConnected) return;\n const panelWidth = root.clientWidth;\n // `clientWidth` is 0 for a panel that is not laid out (a closing popup).\n if (panelWidth === 0) return;\n const overflow = measureOverflow(root, panelWidth);\n if (overflow === null) return;\n const cap = parseFloat(getComputedStyle(surface).maxWidth);\n if (\n Number.isFinite(cap) &&\n surface.getBoundingClientRect().width >= cap - EPSILON\n ) {\n return;\n }\n reported.current = true;\n failLoudly(describe(root, label, panelWidth, overflow));\n });\n\n // The panel's height follows its content, so a page growing a row resizes\n // it; the primitive debounces to one measurement per frame.\n useResizeObserver(ref, check);\n\n // A pushed page or a level switch can change the content without changing\n // the panel's size at all.\n useEffect(() => {\n const root = ref.current;\n if (!root) return;\n let frame = 0;\n const mutation = new MutationObserver(() => {\n if (frame === 0) {\n frame = requestAnimationFrame(() => {\n frame = 0;\n check();\n });\n }\n });\n mutation.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"data-cp-showing\", CP_NEEDS_ATTR],\n });\n return () => {\n mutation.disconnect();\n if (frame !== 0) cancelAnimationFrame(frame);\n };\n }, [ref, check]);\n}\n\ninterface Overflow {\n /** The width the content needs, in px. */\n contentWidth: number;\n /** The element whose content does not fit. */\n offender: HTMLElement | null;\n}\n\n/**\n * Content wider than the room it was given, in either of the two shapes it\n * takes:\n *\n * - the PANEL overflows — a rigid child reaches past the panel's edge\n * (`scrollWidth` beyond its `clientWidth`);\n * - a CELL overflows — the row grids' tracks are `min-w-0`, so a builder row\n * in a narrow panel does not push the panel at all: its tracks squeeze, and\n * each control spills over its neighbour INSIDE the row (a date picker drawn\n * over the remove button). Only a cell whose own overflow is visible counts:\n * a cell that clips is truncating on purpose, and its ellipsis is the\n * design, not a fault.\n */\nfunction measureOverflow(\n root: HTMLElement,\n panelWidth: number,\n): Overflow | null {\n const edge = root.getBoundingClientRect().right;\n const showing = root.querySelector(\"[data-cp-showing]\") ?? root;\n if (root.scrollWidth - panelWidth > EPSILON) {\n const offender =\n [...showing.querySelectorAll(\"*\")].find(\n (el) => el.getBoundingClientRect().right > edge + EPSILON,\n ) ?? null;\n return { contentWidth: root.scrollWidth, offender };\n }\n for (const cell of showing.querySelectorAll(\"[data-cp-cell]\")) {\n const excess = cell.scrollWidth - cell.clientWidth;\n if (\n cell.clientWidth > 0 &&\n excess > EPSILON &&\n getComputedStyle(cell).overflowX === \"visible\"\n ) {\n return { contentWidth: panelWidth + excess, offender: cell };\n }\n }\n return null;\n}\n\nfunction describe(\n root: HTMLElement,\n label: string | undefined,\n panelWidth: number,\n { contentWidth, offender }: Overflow,\n): PanelOverflowFault {\n const showing = root.querySelector(\"[data-cp-showing]\") ?? root;\n const level = showing.getAttribute(\"data-cp-level\");\n // The back header is the first row before the levels, and only exists on a\n // pushed page; its text is the page's title.\n const page =\n level === null || level === \"0\"\n ? \"root\"\n : (root.querySelector(\"[data-cp-cell='label']\")?.textContent?.trim() ??\n `level ${level}`);\n const needs = [\n ...new Set(\n [...showing.querySelectorAll(`[${CP_NEEDS_ATTR}]`)].map(\n (el) => el.getAttribute(CP_NEEDS_ATTR) ?? \"\",\n ),\n ),\n ];\n const name = label ?? \"unlabelled panel\";\n const declared = needs.length > 0 ? needs.join(\", \") : \"none (menu)\";\n return {\n label: name,\n page,\n needs,\n panelWidth,\n contentWidth,\n offender: offender ? selectorOf(offender) : \"unknown\",\n message:\n `control panel \"${name}\" (page: ${page}) is ${Math.round(panelWidth)}px ` +\n `wide but its content needs ${Math.round(contentWidth)}px. Declared ` +\n `width needs: ${declared}. The content that does not fit needs a ` +\n `width it does not declare — add \\`needs\\` to the ControlPanel.Section ` +\n `holding it (see control-panel width-need.ts).`,\n };\n}\n\n/** A short, human-readable path for an element: tag, data-cp-cell, classes. */\nfunction selectorOf(el: HTMLElement): string {\n const cell = el.getAttribute(\"data-cp-cell\");\n const classes = [...el.classList].slice(0, 3).join(\".\");\n return (\n el.tagName.toLowerCase() +\n (cell ? `[data-cp-cell=\"${cell}\"]` : \"\") +\n (classes ? `.${classes}` : \"\")\n );\n}\n\n/**\n * Report, then throw in dev — the same policy `viewport-overlay` and\n * `adaptive-bar` take: an overflowing panel is still usable, so production\n * files the fault and keeps painting; dev gets the fastest feedback there is.\n */\nfunction failLoudly(fault: PanelOverflowFault): void {\n panelOverflowReportSink.emit(fault);\n if (import.meta.env.DEV) {\n throw new Error(`control-panel: ${fault.message}`);\n }\n}\n","import {\n cn,\n MENU_LABEL_PAINT,\n MenuSurfaceProvider,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport { useRailGuard } from \"@plugins/primitives/plugins/css/plugins/rail/web\";\nimport { Text } from \"@plugins/primitives/plugins/css/plugins/text/web\";\n\nimport type React from \"react\";\n\nimport { useSectionStyle } from \"./host\";\nimport { SETTINGS_HEADING, SETTINGS_PROSE } from \"./settings-picks\";\n\nimport { usePanelOverflowGuard } from \"./overflow-guard\";\nimport type { ControlPanelWidthNeed } from \"./width-need\";\n\nexport interface ControlPanelProps {\n /**\n * Names the panel for assistive tech. `role=\"group\"` carries no child\n * contract, so the rows below stay free to report their own\n * checkbox/radio/switch state.\n */\n \"aria-label\"?: string;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * The panel BODY — and nothing else. It paints no background, no border, no\n * shadow, no radius, no scroll, no height clamp and NO WIDTH. All of that is the\n * surface's, which is always an `OverlayPanel` contributed by whatever contains\n * this body (`ControlPanelPopover`, a `DialogContent`, a pushed panel-stack\n * entry).\n *\n * Three things follow from that split, and they are the reason for it:\n *\n * - No double elevation, so this primitive needs no `no-adhoc-surface`\n * exemption to exist.\n * - A panel inside a panel is really a popover opened from inside a popover,\n * each with its own correctly-clamped surface — rather than a nested box\n * re-deriving one.\n * - Width can be a ROLE rather than a measurement: there is no width prop here\n * to default, so the widest row cannot decide how wide the panel is.\n *\n * What it DOES own is the geometry: the content inset, the two rails, and the\n * hairline BETWEEN its direct children.\n *\n * The inset is the first inversion, and it has ONE owner: this box applies it as\n * padding, so content that does nothing at all — a raw `` inside a\n * Section, a contributed `FieldRenderer`, a section label — lands on the rail\n * this box published. A `Row` is not an exception to that, it is the CANCELLING\n * case: it bleeds the rail back out so its fill reaches the panel's inner edge,\n * then re-inserts its own padding, landing its leading cell on the very rail it\n * cancelled. Nothing inherits AND pads.\n *\n * That is not a panel rule — it is the repo-wide rail contract, of which this\n * panel was the prototype: the model lives in\n * [`primitives/css/rail`](../../../rail/CLAUDE.md), the utilities that carry it\n * in `app.css`, and `cp-panel` is simply the region owner that publishes an\n * asymmetric pair. `useRailGuard` below is the same rule read back off the\n * rendered DOM in dev, so a child that pads on top of the panel's rail names\n * itself instead of merely looking a little indented.\n *\n * The rail is the ICON rail — the same x every row's icon, indicator and drag\n * handle begins at — and that is the correction v2 made: it used to be the TEXT\n * rail — an interior column of the row grid — so a search field\n * landed 26px right of the section label above it, the panel padded 62px left\n * against 12px right, and one leading glyph on one footer row moved every block\n * in the panel. Which leading tracks exist is now derived from what the panel\n * actually puts in them (`data-cp-handle` / `data-cp-icon` on the rows), so a\n * panel with no handles and no icons has neither column and its content sits\n * flush on one edge.\n *\n * The hairline is the second — the container draws the separators, so an author\n * never places one.\n * A conditionally-null section therefore leaves no orphan rule, a footer is\n * separated because it is a BAND rather than because it opted in, and the rhythm\n * cannot go ragged one panel at a time.\n *\n * \"Between its direct children\" is what that used to say, and it was the bug: a\n * `cp-body > * + *` sibling rule saw only the `display: contents` lineage span\n * that `renderIsolated` wraps every contributed panel in — one span in the filter\n * panel, one per contribution in the settings panel — so not one panel in the app\n * drew a hairline. `display: contents` is transparent to LAYOUT and opaque to\n * SELECTORS, so the mechanism is now split along that line: `cp-body` is a flex\n * container, which lays the bands out at any wrapper depth, and each band carries\n * `cp-band`, which draws its own rule. See both utilities in `app.css`.\n */\nexport function ControlPanel({\n \"aria-label\": ariaLabel,\n className,\n children,\n}: ControlPanelProps) {\n // The rail guard goes on THIS box because this is the publisher: `cp-panel`\n // declares the rail here, and a rail is measured from its publisher's padding\n // box. Attached one level down it would compare children against a rail whose\n // origin it could not see.\n const rootRef = useRailGuard(\"ControlPanel\");\n usePanelOverflowGuard(rootRef, ariaLabel);\n return (\n \n {/* A panel is a menu surface wherever it is mounted (popover or pane):\n a `SectionLabel` inside — a picker's heading, a contributed block's —\n draws the same menu label as this panel's own `Section`s. */}\n {children}\n \n );\n}\n\nexport interface ControlPanelSectionProps {\n /**\n * The section's label on the panel's own content rail — the icon rail. In a\n * popover or a pane it wears the shared menu section label (ui-kit\n * `MENU_LABEL_PAINT`: faint caption, semibold, sentence case), the same as a\n * dropdown's or a `Subhead`'s, so every menu surface names its runs alike. In\n * a settings card (`ControlPanelCard`) it is the card's section HEADING (the\n * overlay group's `settingsHeading` pick; a leading icon sits in it). The\n * host decides (`sections`), never this call site: there used to be an\n * `eyebrow` / `group` choice here, and one answer per surface is the point.\n */\n label?: React.ReactNode;\n /**\n * A muted line under the label, INSIDE the band (a card's section prose, the\n * `settingsProse` pick, in a settings card). It is prose about the\n * band, not a row — so it reserves no track, takes no row height, and\n * invariant #2 never sees it. It lands on the panel's rail beside the label\n * above it by doing nothing at all.\n */\n description?: React.ReactNode;\n /**\n * The width this band's content needs, for content the vocabulary cannot see\n * into — a foreign icon / emoji / swatch grid (`picker`), or a builder page\n * before its first rule row exists (`builder`). Declared HERE, beside the\n * content it is justified by, never by the code that opens the panel. A\n * `RuleRow` and a `Row` with a `description` state theirs themselves. See\n * `width-need.ts`.\n */\n needs?: ControlPanelWidthNeed;\n /**\n * The band's rows draw no leading tracks (no drag gutter, no icon column),\n * even where the panel's other bands occupy them: their labels start on the\n * panel's content edge, flush with this band's label. For a band of settings\n * (switch rows) under a list whose rows carry icons or avatars — the one\n * sanctioned exception to \"one rail per panel\": every label inside the\n * band still starts at one x, the band's own.\n */\n flush?: boolean;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * One band of the panel. It places NO divider of its own — see `ControlPanel`\n * above: it carries `cp-band`, which is a declaration that this box IS a band,\n * and the rule is drawn for it. So a section is still separated by virtue of\n * being one, and the call site still has no divider to forget or to double.\n *\n * It applies NO inset either, and that is the second half of the same\n * inversion. The PANEL owns the content inset, so whatever a caller drops in\n * here — a raw ``, a `FieldRenderer`, arbitrary JSX — inherits it and\n * lands on the panel's rail beside every row's leading cell, with no wrapper\n * here and no opt-in at the call site.\n *\n * The label is no exception, and it used to be the only one: it carried a\n * `cp-rail-icon` class that hung it BACK one column, because the panel's inset\n * was the text rail and the label wanted the icon rail. Now the inset IS the\n * icon rail, that offset computes to zero — so the class was deleted rather\n * than defaulted, and the label lands on one left edge with every icon,\n * indicator, drag handle and loose control in the panel by doing nothing.\n */\nexport function ControlPanelSection({\n label,\n description,\n needs,\n flush,\n className,\n children,\n}: ControlPanelSectionProps) {\n const style = useSectionStyle();\n return (\n \n {style === \"heading\" ? (\n <>\n {label != null ? (\n \n {label}\n \n ) : null}\n {description != null ? (\n \n {description}\n \n ) : null}\n \n ) : (\n <>\n {label != null ? (\n \n {label}\n \n ) : null}\n {description != null ? (\n \n {description}\n \n ) : null}\n \n )}\n {children}\n \n );\n}\n\nexport interface ControlPanelFooterProps {\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * The panel's closing band — Clear / Save as preset / Delete, as full-width ROWS\n * on the same rails, never ghost buttons pinned to opposite corners.\n *\n * It carries no chrome, and that is the point: its separating hairline comes from\n * being a BAND (`cp-band`), exactly like a section's. `data-cp-footer` is the\n * only thing that distinguishes it, and everything the marker buys is CSS: the\n * band PINS to the bottom of the scrolling surface (the actions the panel was\n * opened for stay reachable while a long body scrolls under them), pays its own\n * inset back so its fill covers what scrolls under it, and covers the panel's\n * bottom padding with ink. All of it lives at `cp-band` in `app.css`.\n *\n * One convention the marker cannot enforce: FOOTER ACTIONS TAKE `trailing`, NOT\n * `icon`. A footer row is still a row, so a leading glyph here reserves the icon\n * column for every row in the panel — measured, six theme rows indented 26px for\n * a single ✕ on the seventh.\n */\nexport function ControlPanelFooter({\n className,\n children,\n}: ControlPanelFooterProps) {\n return (\n
\n {children}\n
\n );\n}\n\nexport interface ControlPanelEmptyProps {\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * The \"nothing here yet\" line — on the panel's own rail, so it reads as part of\n * the section it closes rather than as loose prose floating in the panel.\n *\n * It carries NO rail class to get there. The panel's content box starts on that\n * rail (the inset-ownership rule in `app.css`), so this lands there by being\n * ordinary content — the same way a consumer's own `` does.\n */\nexport function ControlPanelEmpty({\n className,\n children,\n}: ControlPanelEmptyProps) {\n return (\n \n {children}\n \n );\n}\n\nexport interface ControlPanelRuleListProps {\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * The vertical run of `RuleRow`s inside a builder. A plain stack — the rule rows\n * are not separated from each other, because a filter is ONE sentence read down\n * the page, not a list of unrelated settings.\n */\nexport function ControlPanelRuleList({\n className,\n children,\n}: ControlPanelRuleListProps) {\n return
{children}
;\n}\n","import type React from \"react\";\n\nimport { symbol } from \"@plugins/ui/plugins/icons/core\";\n\nexport const dragIndicatorIcon = symbol(\"drag-indicator\");\n\n/**\n * dnd-kit listeners/attributes (and an activator `ref`) for a row's drag\n * handle — the one shape every member that can hang a handle takes, so a\n * sortable list hands `Row`, `Setting` and `Group` the same bag.\n */\nexport type ControlPanelHandleProps = React.HTMLAttributes & {\n ref?: React.Ref;\n};\n\n/**\n * The hover-reveal recipe for the drag handle. Opacity and pointer-events are\n * coupled in both directions, so a hidden handle is never a live hit-target\n * sitting invisibly over the row's left edge. Keyed on `group/cp-row`, which\n * every row box that can hang a handle carries.\n */\nexport const HANDLE_REVEAL =\n \"opacity-0 pointer-events-none \" +\n \"group-hover/cp-row:opacity-100 group-hover/cp-row:pointer-events-auto \" +\n \"group-focus-within/cp-row:opacity-100 group-focus-within/cp-row:pointer-events-auto\";\n","import { createContext, useContext } from \"react\";\nimport type React from \"react\";\n\nconst LabelMarkContext = createContext(null);\n\n/**\n * A mark every row label inside `children` draws after its text — `Row`,\n * `Setting`, `Block` and `Group` alike, through their one label cell\n * (`HintedLabelCell`). For a HOST that renders rows it did not write (a slot's\n * contributions) and must say something about all of them — that a row is\n * developer-only — without each contributor remembering to.\n *\n * The provider renders no box: a panel's rows are its grid's direct subgrids,\n * so a wrapper element around a contributed row would break its rails.\n */\nexport function ControlPanelLabelMark({\n mark,\n children,\n}: {\n mark: React.ReactNode;\n children: React.ReactNode;\n}) {\n return {children};\n}\n\n/** The mark the nearest {@link ControlPanelLabelMark} asks labels to draw. */\nexport function useLabelMark(): React.ReactNode {\n return useContext(LabelMarkContext);\n}\n","import {\n cn,\n WithTooltip,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport { Stack } from \"@plugins/primitives/plugins/css/plugins/spacing/web\";\nimport type React from \"react\";\nimport { useLabelMark } from \"./label-mark\";\n\nexport interface HintedLabelCellProps {\n /**\n * The prose. Undefined is the ordinary case — the cell is then exactly the\n * plain `` it has always been, with no tooltip machinery around it.\n */\n hint?: string;\n /**\n * A VISIBLE second line under the label — muted prose that reserves its own\n * height (it wraps, never truncates). The deliberate opposite of `hint`, for a\n * choice whose meaning must be read before it is picked (a page's kind). See\n * `ControlPanelRowCommon.description`.\n */\n description?: string;\n /**\n * A second line naming WHICH thing the row is (an email): muted, one line,\n * truncating. See `ControlPanelRowCommon.detail`.\n */\n detail?: string;\n /** The label wraps rather than truncates (`ControlPanelRowCommon.wrap`). */\n wrap?: boolean;\n /** From the host's own `useId()`, so the row can point at it. */\n descriptionId: string;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A row's LABEL cell, with `hint` wired into it.\n *\n * `hint` is prose as a TOOLTIP, never a second line. A second line is the one\n * change that breaks invariant #2 in every panel at once, and a muted pseudo-row\n * breaks invariant #1's meaning — a row that is not a control still opening the\n * rails. So the hint costs no height and reserves no track: it adds a leading\n * glyph to nothing, a trailing cell to nothing, and a column to nothing.\n *\n * It lands in the LABEL cell rather than around the row for that last reason. A\n * `WithTooltip` around the row's HOST would have to merge props and a ref into\n * whichever of the three elements the row inferred, and the label cell is\n * already the widest box in the row — `minmax(0, 1fr)` — so it is what the\n * pointer is over anyway.\n *\n * Two readings of one string, which is deliberate: sighted users get the\n * tooltip, and assistive tech gets an `aria-describedby` pointing at a zero-box\n * `sr-only` sibling inside the cell. `sr-only` is `position: absolute`, so the\n * description generates no box in the grid and cannot widen the label track or\n * push the row onto a second line. A tooltip alone would be invisible on touch\n * and unreachable by a screen reader; a description alone would be invisible to\n * everyone else.\n *\n * That sibling is `aria-hidden`, and it has to be: it lives INSIDE the row, so\n * without it the row's accessible NAME would be its label followed by its whole\n * hint — the same defect the icon cell is hidden for one file over. A node that\n * is *directly referenced* by `aria-describedby` still contributes its text to\n * the description even when hidden (accname §2A), so the description survives\n * and only the name is spared.\n */\nexport function HintedLabelCell({\n hint,\n description,\n detail,\n wrap,\n descriptionId,\n className,\n children,\n}: HintedLabelCellProps) {\n const mark = useLabelMark();\n const second =\n description !== undefined\n ? { text: description, wraps: true }\n : detail !== undefined\n ? { text: detail, wraps: false }\n : null;\n if (second !== null) {\n return (\n \n {children}\n {mark != null ? <> {mark} : null}\n \n );\n }\n const cell = (\n \n {children}\n {mark != null ? <> {mark} : null}\n {hint ? (\n \n {hint}\n \n ) : null}\n \n );\n if (!hint) return cell;\n return {cell};\n}\n\n/**\n * The label cell of a row that carries a visible `description`: the label on\n * its own line (still truncating), the description under it as muted caption\n * prose that WRAPS. A flow container, so it resets the row's single-line\n * contract for the description only; the label keeps its own `truncate`.\n *\n * The description is the row's accessible description by `aria-describedby`\n * (the host points at `descriptionId`) and is `aria-hidden` inside the cell, so\n * the row's NAME stays its label alone — the same split `hint` makes.\n *\n * The label is a TITLE over its prose — medium weight in the strong text tier —\n * so the choice reads first and its meaning second.\n *\n * The cell pads itself vertically so two lines sit inside the row's box with\n * the breathing room a one-line row gets from `--cp-row-h`; the row's\n * `min-height` then grows to fit rather than clipping.\n */\nfunction DescribedLabelCell({\n description,\n wraps,\n descriptionId,\n className,\n children,\n}: {\n description: string;\n /** Prose wraps (a long token breaks rather than spill out of the cell);\n * a `detail` identifier truncates on its one line. */\n wraps: boolean;\n descriptionId: string;\n className?: string;\n children: React.ReactNode;\n}) {\n return (\n \n \n {children}\n \n \n {description}\n \n \n );\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\nimport type React from \"react\";\n\n// The stack's contract — entry, state, the context and its reader — apart from\n// the stack COMPONENT, because a row reads it too (`ControlPanel.Row push`) and\n// the stack component renders a row (its back header): one file each way would\n// be an import cycle.\n\nexport interface PanelStackEntry {\n /** Identity of this level — a re-push with the same key replaces it. */\n key: string;\n /** Shown in the back header, so the user knows what popping returns to. */\n title: string;\n render: () => React.ReactNode;\n}\n\n/**\n * The stack's state, for the one host that has to own it from OUTSIDE the\n * stack: `ControlPanelPopover`, which resets it once the panel has closed.\n */\nexport interface PanelStackState {\n entries: readonly PanelStackEntry[];\n push: (entry: PanelStackEntry) => void;\n pop: () => void;\n close: (key: string) => void;\n reset: () => void;\n}\n\nexport function usePanelStackState(): PanelStackState {\n const [entries, setEntries] = useState([]);\n const push = useCallback((entry: PanelStackEntry) => {\n setEntries((prev) =>\n prev.at(-1)?.key === entry.key ? prev : [...prev, entry],\n );\n }, []);\n const pop = useCallback(() => setEntries((prev) => prev.slice(0, -1)), []);\n const close = useCallback((key: string) => {\n setEntries((prev) => {\n const index = prev.findIndex((entry) => entry.key === key);\n return index === -1 ? prev : prev.slice(0, index);\n });\n }, []);\n const reset = useCallback(() => setEntries([]), []);\n return useMemo(\n () => ({ entries, push, pop, close, reset }),\n [entries, push, pop, close, reset],\n );\n}\n\nexport interface PanelStackApi {\n /** 0 while the root panel is showing. */\n depth: number;\n push: (entry: PanelStackEntry) => void;\n pop: () => void;\n /**\n * Removes the level pushed under `key` and every level above it; a no-op when\n * it is not on the stack. For a pusher whose subject just went away — a\n * `Group` unmounting because its list item was removed — so the user is not\n * left on a page about something that no longer exists.\n */\n close: (key: string) => void;\n /** Back to the root in one step — for a host closing and reopening the panel. */\n reset: () => void;\n}\n\nexport const PanelStackContext = createContext(null);\n\n/**\n * The panel stack a contribution pushes onto.\n *\n * It is published through CONTEXT rather than passed down because the third\n * consumer needs it that way: custom-columns' per-field editor is a nested\n * contribution rendered inside someone else's section, and has no prop path back\n * to whichever chrome is hosting the panel. Throws when there is no stack, which\n * is the honest answer — a component that pushes a sub-panel cannot render\n * correctly in a host that has nowhere to push it, and a silent no-op would show\n * as a dead click.\n */\nexport function usePanelStack(): PanelStackApi {\n const api = useContext(PanelStackContext);\n if (!api) {\n throw new Error(\n \"usePanelStack() requires a ancestor. Panels rendered \" +\n \"through ControlPanelPopover already have one.\",\n );\n }\n return api;\n}\n","import {\n cn,\n MENU_ROW_CHECKED,\n MENU_ROW_PAINT,\n MENU_VALUE,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport { CheckboxIndicator } from \"@plugins/primitives/plugins/css/plugins/selection-indicator/web\";\nimport { SwitchIndicator } from \"@plugins/primitives/plugins/css/plugins/switch/web\";\nimport {\n RowActions,\n rowActionsAnchor,\n} from \"@plugins/primitives/plugins/row-actions/web\";\nimport type { Activation } from \"@plugins/primitives/plugins/link-gesture/core\";\nimport { linkProps } from \"@plugins/primitives/plugins/link-gesture/web\";\nimport type React from \"react\";\nimport { useCallback, useContext, useId } from \"react\";\n\nimport {\n dragIndicatorIcon,\n HANDLE_REVEAL,\n type ControlPanelHandleProps,\n} from \"./handle\";\nimport { HintedLabelCell } from \"./hint\";\nimport { PanelStackContext, type PanelStackEntry } from \"./stack-context\";\nimport { symbol } from \"@plugins/ui/plugins/icons/core\";\nimport { Icon } from \"@plugins/ui/plugins/icons/web\";\n\nconst checkIcon = symbol(\"check\");\nconst chevronRightIcon = symbol(\"chevron-right\");\n\n/** The three selection languages, one per meaning. There is no fourth. */\nexport type ControlPanelRowSelect = \"check\" | \"radio\" | \"switch\";\nexport type ControlPanelRowTone = \"default\" | \"danger\";\n\n/**\n * How a row explains itself: a tooltip (`hint`) or a visible line\n * (`description`) — one or the other, never both, so a row cannot say the same\n * thing twice.\n */\ntype ControlPanelRowExplanation =\n | {\n /**\n * Explanatory prose as a TOOLTIP, wired by `aria-describedby` — never a\n * second line. A second line is the one change that breaks invariant #2 in\n * every panel at once; a muted pseudo-row breaks invariant #1's meaning (a row\n * that is not a control still opening the rails). See `HintedLabelCell`.\n */\n hint?: string;\n description?: never;\n detail?: never;\n }\n | {\n /**\n * Explanatory prose as a VISIBLE second line under the label — muted, wrapping,\n * and reserving its own height. Opt-in and the one sanctioned exception to\n * invariant #2 (one row height): a row that says what a choice MEANS before it\n * is picked (the page-kind menu, where each kind is a policy) cannot hide that\n * behind a tooltip the user has to discover. Everything else keeps `hint`.\n * A row takes one or the other, never both.\n */\n description: string;\n hint?: never;\n detail?: never;\n }\n | {\n /**\n * WHICH thing the row names, as a second line — an identifier, not prose\n * (an account's email, a file's folder): muted, ONE line, truncating.\n * The row is one two-line unit, so its leading and trailing cells centre\n * on both lines (a person's disc beside their name and email), unlike a\n * `description`, whose cells sit on the title line above prose. It needs\n * no wider panel: it truncates rather than wraps.\n */\n detail: string;\n hint?: never;\n description?: never;\n };\n\ninterface ControlPanelRowCommon {\n /** Shows the drag handle in the gutter track (revealed on row hover/focus). */\n handle?: boolean;\n /** dnd-kit listeners/attributes (and an activator `ref`) for that handle. */\n handleProps?: ControlPanelHandleProps;\n /**\n * Trailing cell content — a count, a type name, a chip. PRESENTATIONAL: it\n * carries no click target of its own. On a row with no `actions` that is a\n * hard contract (the row IS the click target, so an interactive control here\n * would be a nested one); on a row WITH `actions` the interactive trailing\n * content is the action cluster, and this still sits in front of it as plain\n * text — OUTSIDE the selectable region, so a click on it does nothing. A\n * glyph that looks pressable (an eye, a toggle) belongs in `actions` /\n * `restingActions` as a real button, never here. Ignored when\n * `select=\"switch\"`, which owns the cell.\n */\n trailing?: React.ReactNode;\n /**\n * Row actions — a hover-revealed cluster of `IconButton`s at the row's\n * trailing edge, rendered through the `row-actions` primitive.\n *\n * Passing it switches the row to its SECOND construction: the row box becomes\n * a non-interactive `
` and the selectable part becomes an inner subgrid\n * spanning every track but the trailing one, so the action buttons are the\n * click target's SIBLINGS rather than its descendants. See the component doc.\n */\n actions?: React.ReactNode;\n /**\n * Row actions drawn AT REST — the same xs `IconButton`s as `actions`, in\n * their own always-visible cluster at the row's trailing edge, after the\n * hover cluster (so an action keeps its x whether it rests or waits for the\n * hover). For an action whose glyph is also the row's state and must read\n * without a hover: a hidden field's eye-off. Like `actions`, it switches the\n * row to its second construction.\n */\n restingActions?: React.ReactNode;\n tone?: ControlPanelRowTone;\n /** Muted foreground — for a secondary row (\"New field\", \"Add filter\"). */\n muted?: boolean;\n /**\n * The label WRAPS onto as many lines as it needs instead of truncating, and\n * the row grows to hold it — for a setting whose words are the whole of what\n * it says (a switch row's sentence), where an ellipsis would hide the\n * meaning. A sanctioned exception to invariant #2, like `description`.\n */\n wrap?: boolean;\n /**\n * The thing the row names is switched OFF — a hidden field, a paused rule —\n * drawn in the faint ink, clearly apart from its live siblings. Not\n * `disabled`: the row stays live (selecting it is how it comes back on). Not\n * `muted`, which marks a secondary row and is only a shade off the resting ink.\n */\n inactive?: boolean;\n /**\n * Select on PRESS without taking focus — for a panel opened beside a live\n * caret (the page editor's block menu), where a plain click would first\n * blur the editor and move its selection before the row's action runs. The\n * row selects on a primary-button `mousedown` (with `preventDefault`, so\n * focus stays where it is); keyboard activation (Enter / Space) still\n * selects.\n */\n keepFocus?: boolean;\n /**\n * Disables the row's SELECTION — not the whole row.\n *\n * On a row with no `actions` those are the same thing, and it reads as it\n * always has: the row is inert and dimmed. On a row WITH `actions` the\n * dimming and the inertness land on the inner select element only, and the\n * action cluster stays live. That is the honest meaning of the prop, and it is\n * the case that actually occurs: a saved preset whose fields have all left the\n * schema cannot be applied, but it is exactly the preset you want to delete.\n */\n disabled?: boolean;\n className?: string;\n /**\n * Forwarded to the row BOX — the outermost node — in both constructions, for\n * DnD / scroll-into-view. Deliberately not the inner select element on the\n * `actions` path: a ref that changed node the day a row grew an action would\n * hand a dnd transform a box that leaves the trailing cell behind.\n */\n ref?: React.Ref;\n children: React.ReactNode;\n}\n\n/**\n * What activating the row does — at most one of four, so a row cannot both\n * navigate and push. A row that opens an in-app destination is an\n * `activation` (a `LinkTarget`), never an `onSelect` that runs one: only a\n * link gets ⌘/Ctrl- and middle-click (a new browser tab), which a bare\n * callback cannot give. `push` is the panel-stack arm: the row opens a page of\n * the panel it sits in, and says so with a trailing `chevron-right` after its\n * value. Pushing through `push` rather than calling `usePanelStack().push` from\n * `onSelect` is what makes a drill row without its chevron unspellable.\n */\ntype ControlPanelRowTarget =\n | {\n /** Makes the row a `\n );\n }\n return (\n \n {leadingCells}\n {trailingCell}\n
\n );\n}\n\n/**\n * The selectable region of a row that carries `actions` — the element the row's\n * props inferred, rendered INSIDE the row box instead of as it.\n *\n * It is a subgrid, and that is the whole trick: `grid-cols-subgrid` makes the\n * panel's own tracks pass through this element unchanged, so the three cells it\n * holds land on exactly the rails they would as direct children of `cp-row`.\n * There is deliberately NO `gap` here — a subgrid inherits the parent grid's\n * column gap, and restating it is how the leading cells drift off the panel rail.\n *\n * `self-stretch` against the row's `align-items: center`, so the click target\n * fills the row's full `--cp-row-h` height rather than only its content's: an\n * inferred-height target would leave a few dead pixels above and below the label\n * that the un-split construction has never had.\n *\n * Three concrete elements again rather than one `` local, for the reason\n * spelled out at the call site.\n */\nfunction SelectRegion({\n href,\n onSelect,\n press,\n disabled,\n isLink,\n isButton,\n selection,\n described,\n children,\n}: {\n href?: string;\n onSelect?: () => void;\n press: SelectHandlers;\n disabled?: boolean;\n isLink: boolean;\n isButton: boolean;\n selection: SelectionAttrs | undefined;\n described: DescribedAttrs | undefined;\n children: React.ReactNode;\n}) {\n const className = cn(\n \"grid grid-cols-subgrid col-[1/-2] self-stretch items-center min-w-0\",\n // `outline-none`: the row BOX paints the focus (the lit fill, keyed on this\n // node's `:focus-visible`), so the UA outline would be a second indicator.\n \"rounded-panel-row text-left outline-none\",\n // The selection's state is reported HERE, not on the row box, so the\n // shared checked label (colour + weight) is applied here too.\n MENU_ROW_CHECKED,\n \"aria-checked:not-[[role=switch]]:text-strong-foreground\",\n // The disabled treatment, scoped to the selection. `pointer-events-none` as\n // well as the attribute, because a ``/`` region takes no `disabled`\n // — and the row box around it must stay live either way, so its actions\n // still work.\n disabled && \"pointer-events-none opacity-50\",\n );\n // Nominates this node as the one whose focus lights the box, written `=\"\"` like\n // every other marker attribute in the repo — presence is the whole signal.\n const focus = { \"data-focus-ring\": \"\", \"data-cp-select\": \"\" };\n\n if (isLink) {\n return (\n \n {children}\n \n );\n }\n if (isButton) {\n return (\n \n {children}\n \n );\n }\n return (\n \n {children}\n \n );\n}\n\n/**\n * The two attribute bags the row computes once and then hands to whichever\n * element ends up being the selection — the row host on the plain path, the\n * inner region on the actions path. Named types rather than inferred ones so\n * both spreads typecheck as JSX attributes instead of as an index signature.\n */\ntype SelectionAttrs =\n | { role: \"checkbox\" | \"radio\" | \"switch\"; \"aria-checked\": boolean }\n | { \"aria-current\": \"true\" };\ntype DescribedAttrs = { \"aria-describedby\": string };\n\nconst SELECT_ROLE: Record<\n ControlPanelRowSelect,\n \"checkbox\" | \"radio\" | \"switch\"\n> = {\n check: \"checkbox\",\n radio: \"radio\",\n switch: \"switch\",\n};\n\ntype SelectHandlers = {\n onClick?: React.MouseEventHandler;\n onMouseDown?: React.MouseEventHandler;\n onAuxClick?: React.MouseEventHandler;\n};\n\n/**\n * How a button row selects: on click, or — `keepFocus` — on a primary-button\n * press with the default prevented, so focus never leaves wherever it is. A\n * keyboard activation (Enter / Space) arrives as a `click` with `detail === 0`\n * and still selects; a pointer click (`detail > 0`) was already handled on\n * press, so it is not run twice.\n */\nfunction selectHandlers(\n onSelect: (() => void) | undefined,\n keepFocus: boolean | undefined,\n): SelectHandlers {\n if (!keepFocus) return { onClick: onSelect };\n return {\n onMouseDown: (e) => {\n if (e.button !== 0) return;\n e.preventDefault();\n onSelect?.();\n },\n onClick: (e) => {\n if (e.detail === 0) onSelect?.();\n },\n };\n}\n\n/**\n * How a link row selects: `linkProps`' gestures (plain click here, ⌘/Ctrl- and\n * middle-click in a new browser tab), and — `keepFocus` — the primary press run\n * on `mousedown` with the default prevented, as {@link selectHandlers} does;\n * the press carries the modifiers, so ⌘-press still opens elsewhere.\n */\nfunction linkPressHandlers(\n target: Activation,\n keepFocus: boolean | undefined,\n): SelectHandlers {\n if (typeof target === \"function\") return selectHandlers(target, keepFocus);\n const link = linkProps(target);\n if (!keepFocus) return link;\n return {\n onMouseDown: (e) => {\n if (e.button !== 0) return link.onMouseDown(e);\n e.preventDefault();\n link.onClick(e);\n },\n onClick: (e) => {\n if (e.detail === 0) link.onClick(e);\n },\n onAuxClick: link.onAuxClick,\n };\n}\n","import { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport {\n RowActions,\n rowActionsAnchor,\n} from \"@plugins/primitives/plugins/row-actions/web\";\nimport { Text } from \"@plugins/primitives/plugins/css/plugins/text/web\";\nimport type React from \"react\";\nimport { useId } from \"react\";\n\nimport { Icon } from \"@plugins/ui/plugins/icons/web\";\n\nimport {\n dragIndicatorIcon,\n HANDLE_REVEAL,\n type ControlPanelHandleProps,\n} from \"./handle\";\nimport { HintedLabelCell } from \"./hint\";\n\n/**\n * The chrome-gutter stripe. A STATE with two named tones, never a\n * `gutter?: ReactNode` child slot — the slot reopens \"the author draws chrome\",\n * which is the one thing this vocabulary exists to close.\n *\n * It costs no track, changes no rail and lands identically on a `Setting`, a\n * `Block` header and a `Group` header, because it paints in the gap the row's\n * own bleed already leaves between the panel's inner edge and the row's box —\n * a gap that until now nothing painted in at all.\n */\nexport type ControlPanelMark = \"accent\" | \"warning\";\n\n/** `field` takes the panel's field width; `inline` sizes to its own content. */\nexport type ControlPanelFit = \"inline\" | \"field\";\n\ninterface SettingRowProps {\n label: React.ReactNode;\n hint?: string;\n /** The value cell's content. Omitted for a header row that has no control. */\n control?: React.ReactNode;\n fit?: ControlPanelFit;\n status?: React.ReactNode;\n actions?: React.ReactNode;\n mark?: ControlPanelMark;\n /** Shows the drag handle in the gutter (revealed on row hover/focus). */\n handle?: boolean;\n /** dnd-kit listeners/attributes (and an activator `ref`) for that handle. */\n handleProps?: ControlPanelHandleProps;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * The value row's box, shared by `Setting`, `Block`'s header and `Group`'s\n * inline header — because those three ARE one row with different contents, and\n * splitting them into three components is how one shared grid becomes three that\n * drift.\n *\n * The host is ALWAYS a plain `
`. That is not a default, it is the\n * construction: `ControlPanelSettingProps` carries no `onSelect` and no `href`,\n * so the row can never become a click target, and a control nested inside its\n * cells is therefore legal here and only here. Same trade `RuleRow` made — the\n * two row families differ on exactly one honest axis, who is the click target,\n * enforced by disjoint prop sets rather than by review.\n *\n * The trailing cluster composes `RowActions` at `pin={null}`, verbatim the\n * `RuleRow` construction: the track already reserves the space, so the cluster\n * belongs IN FLOW; pinning it would float it over the value cell. It borrows\n * that primitive's reveal coupling — a hidden action is never a live hit-target\n * — and its xs density instead of restating either.\n */\nexport function SettingRow({\n label,\n hint,\n control,\n fit,\n status,\n actions,\n mark,\n handle,\n handleProps,\n disabled,\n className,\n}: SettingRowProps) {\n const hintId = useId();\n return (\n \n {/* The gutter is PADDING on this grid (see `cp-setting`), so the handle is\n hung in it absolutely — at exactly the x a `cp-row`'s gutter cell sits\n at. `data-cp-handle` is the same occupancy mark a `Row` stamps, so the\n panel reserves the gutter for every row in it either way. */}\n {handle ? (\n \n \n \n ) : null}\n \n {label}\n \n {/* The value cell exists only when there IS a control, and marking it is\n what opens the panel's value track — per PANEL, like every other track\n here, so one field row lines every field control in the panel up while\n a header row with no control costs nothing. */}\n {control != null ? (\n \n {control}\n \n ) : null}\n {status != null ? (\n // PRESENTATIONAL, and deliberately NOT hover-revealed: a tier badge that\n // appears on hover is a badge nobody reads. In flow, before the actions.\n \n {status}\n \n ) : null}\n {actions != null ? (\n \n {actions}\n \n ) : null}\n
\n );\n}\n\n/**\n * A line UNDER a row, on the panel's rail — a conflict line, a validation\n * message, an \"Upstream: …\" note. It is NOT a row: it takes no row height,\n * reserves no track, and reaches the rail by being ordinary content, exactly the\n * way `Empty` does.\n */\nexport function SettingNote({ children }: { children: React.ReactNode }) {\n return (\n \n {children}\n \n );\n}\n\n/**\n * Prose under a label — a `Block`'s or a `Group`'s visible description.\n *\n * It sits on the PANEL's rail rather than under the label it explains, and that\n * is deliberate: reaching the text rail would mean applying an inset on top of\n * the region's, which is the double-inset the rail contract exists to make\n * impossible (and which `useRailGuard` reports). In a panel with no icon track —\n * which is every settings panel — the two rails coincide anyway.\n */\nexport function SettingDescription({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n \n {children}\n \n );\n}\n","import type React from \"react\";\n\nimport {\n SettingDescription,\n SettingNote,\n SettingRow,\n type ControlPanelMark,\n} from \"./setting-row\";\n\nexport interface ControlPanelBlockProps {\n label: React.ReactNode;\n hint?: string;\n /** Muted line under the label, above the control. Visible, not a tooltip. */\n description?: React.ReactNode;\n /** The control. Lands on the panel's rail by doing nothing. */\n children: React.ReactNode;\n status?: React.ReactNode;\n actions?: React.ReactNode;\n mark?: ControlPanelMark;\n note?: React.ReactNode;\n className?: string;\n}\n\n/**\n * A CONTROL WIDER THAN A ROW — a textarea, a code box, a chip cluster, a drag\n * editor — with the label that names it.\n *\n * \"Loose content lands on the rail by doing nothing\" is true and it is not\n * enough, because somebody still has to draw the label; if that somebody is a\n * field renderer, every renderer draws its own and the vocabulary is back where\n * it started. This is the member that carries it.\n *\n * `Block` is deliberately NOT a `Section`: it carries no `cp-band`, so a run of\n * blocks is one visual group with no hairline between them — the same reason\n * `RuleList` and `Empty` are not bands.\n *\n * ITS LABEL IS A FIELD LABEL, so it is drawn in a row's label cell — on the TEXT\n * rail, not on the panel's content edge. Invariant #1 says every LABEL starts at\n * one x, and a Block label sits at the same rung as a Setting label and a Row\n * label. A `Section` label names a band, a different rung, and keeps the\n * panel's content edge. In a panel with no icon track the two coincide; in one\n * with icons the band label hangs back by design. Gated by the `block-label-rail`\n * fixture rather than by this paragraph, because it is exactly the kind of thing\n * that drifts silently.\n */\nexport function ControlPanelBlock({\n label,\n hint,\n description,\n children,\n status,\n actions,\n mark,\n note,\n className,\n}: ControlPanelBlockProps) {\n return (\n <>\n \n {description != null ? (\n {description}\n ) : null}\n {children}\n {note != null ? {note} : null}\n \n );\n}\n","import { Button, cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport type React from \"react\";\nimport type { ComponentProps } from \"react\";\n\nimport { symbol } from \"@plugins/ui/plugins/icons/core\";\nimport { Icon } from \"@plugins/ui/plugins/icons/web\";\n\nconst keyboardArrowDownIcon = symbol(\"keyboard-arrow-down\");\n\nexport interface ControlPanelFieldProps extends Omit<\n ComponentProps,\n \"children\" | \"variant\" | \"aspect\" | \"shape\"\n> {\n /** Leading glyph — typically the picked field's type icon. */\n icon?: React.ReactNode;\n /**\n * The current value, as text. Pass `null` when there is none: the field then\n * shows `placeholder` in the muted tone, so \"unset\" reads differently from\n * \"set to something short\".\n */\n label: React.ReactNode;\n placeholder?: string;\n}\n\n/**\n * The ONE control shape inside a rule row — the box a field, an operator or a\n * value is picked from. It fills its grid cell (`w-full`) and truncates its\n * label, so a long value shortens the box's text instead of widening the panel:\n * a builder whose panel grows when you pick a longer value is a builder that\n * resizes as you use it, which is the measurement-driven width this vocabulary\n * exists to remove.\n *\n * It is a `Button variant=\"outline\"`, so it inherits the app's control height,\n * focus ring, hover and disabled treatment rather than re-deriving them. Every\n * remaining prop is forwarded, which is what lets a caller hand it to a\n * `PopoverTrigger` via base-ui's `render` prop.\n */\nexport function ControlPanelField({\n icon,\n label,\n placeholder = \"Select…\",\n className,\n ref,\n ...rest\n}: ControlPanelFieldProps) {\n const empty = label == null || label === \"\";\n return (\n \n \n {icon != null ? (\n \n {icon}\n \n ) : null}\n \n {empty ? placeholder : label}\n \n \n \n \n );\n}\n","import { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web\";\nimport type React from \"react\";\nimport { useEffect, useId, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { ControlPanelSection } from \"./control-panel\";\nimport { ControlPanelRow } from \"./control-panel-row\";\nimport type { ControlPanelHandleProps } from \"./handle\";\nimport { GroupDepthProvider, useControlPanelHost, useGroupDepth } from \"./host\";\nimport { usePanelStack } from \"./stack-context\";\nimport {\n SettingDescription,\n SettingNote,\n SettingRow,\n type ControlPanelMark,\n} from \"./setting-row\";\n\nexport interface ControlPanelGroupProps {\n label: React.ReactNode;\n hint?: string;\n description?: React.ReactNode;\n /**\n * Trailing summary when the group presents as a drill row — \"3 items\", a\n * variant name, a type. Ignored when the host inlines the group, which shows\n * the contents instead of summarizing them.\n */\n summary?: React.ReactNode;\n /** Presentational, in flow — a count, a state chip. */\n status?: React.ReactNode;\n /**\n * Hover-revealed cluster on the group's HEADER — a reset, a remove.\n *\n * Honoured only when the host INLINES, because only then is the header a plain\n * `
` (the same `SettingRow` a `Setting` is built from). Under `push` the\n * group is a drill row, i.e. a `