{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "column-manager",
  "type": "registry:ui",
  "title": "Column Manager",
  "description": "Show, hide and drag columns into order while the table rearranges live.",
  "categories": [
    "tables"
  ],
  "dependencies": [
    "@base-ui/react",
    "@number-flow/react",
    "motion"
  ],
  "registryDependencies": [
    "https://www.stealth.pm/r/tokens.json",
    "https://www.stealth.pm/r/cn.json",
    "https://www.stealth.pm/r/icons.json",
    "https://www.stealth.pm/r/motion.json",
    "https://www.stealth.pm/r/use-controllable-state.json"
  ],
  "files": [
    {
      "path": "components/ui/column-manager.tsx",
      "content": "\"use client\";\nimport { Checkbox } from \"@base-ui/react/checkbox\";\nimport { Popover } from \"@base-ui/react/popover\";\nimport NumberFlow from \"@number-flow/react\";\nimport { motion, Reorder, useDragControls, useReducedMotion } from \"motion/react\";\nimport { useId, useMemo, useState } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { Grip, Lock, Search, X } from \"@/lib/icons\";\nimport { ease, spring } from \"@/lib/motion\";\nimport { useControllableState } from \"@/lib/use-controllable-state\";\n\nexport type ColumnItem = {\n  id: string;\n  label: string;\n  /** Unticked columns stay in the list so they can be brought back. Defaults to true. */\n  visible?: boolean;\n  /** Pinned to the start of the table: always shown, never moved. */\n  locked?: boolean;\n};\n\nconst isVisible = (c: ColumnItem) => c.locked || c.visible !== false;\n\n// Locked columns always lead, in their own order; everything else follows.\nfunction normalize(columns: ColumnItem[]) {\n  return [...columns.filter((c) => c.locked), ...columns.filter((c) => !c.locked)];\n}\n\nconst sameColumns = (a: ColumnItem[], b: ColumnItem[]) =>\n  a.length === b.length && a.every((c, i) => c.id === b[i].id && isVisible(c) === isVisible(b[i]));\n\n/** The visible columns, in order, for rendering the table itself. */\nexport function visibleColumns<T extends ColumnItem>(columns: T[]): T[] {\n  return normalize(columns).filter(isVisible) as T[];\n}\n\n/** Column state on its own: keep it next to the table and hand it to ColumnManager. */\nexport function useColumnManager<T extends ColumnItem>(initial: T[]) {\n  const [columns, setColumns] = useState(initial);\n  const visible = useMemo(() => visibleColumns(columns), [columns]);\n  return { columns, setColumns, visible } as const;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Panel: the list on its own, for a popover, a sheet or a settings page\n * -----------------------------------------------------------------------------------------------*/\n\nexport type ColumnManagerPanelProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"title\"> & {\n  value?: ColumnItem[];\n  defaultValue?: ColumnItem[];\n  onValueChange?: (columns: ColumnItem[]) => void;\n  /** What Reset restores. Defaults to the columns as they were on first render. */\n  resetTo?: ColumnItem[];\n  title?: React.ReactNode;\n  /** Show a filter field once there are this many columns. */\n  searchThreshold?: number;\n};\n\nexport function ColumnManagerPanel({\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  resetTo,\n  title = \"Columns\",\n  searchThreshold = 9,\n  className,\n  ...rest\n}: ColumnManagerPanelProps) {\n  const [raw, setValue] = useControllableState({ value: valueProp, defaultValue: defaultValue ?? [], onChange: onValueChange });\n  const [initial] = useState(raw);\n  const baseline = resetTo ?? initial;\n  const reduce = useReducedMotion();\n  const hintId = useId();\n\n  const columns = useMemo(() => normalize(raw), [raw]);\n  const locked = columns.filter((c) => c.locked);\n  const movable = columns.filter((c) => !c.locked);\n  const shown = columns.filter(isVisible).length;\n\n  const [query, setQuery] = useState(\"\");\n  const [dragging, setDragging] = useState<string | null>(null);\n  // Keyboard reorder: the picked-up column and the order to return to on Escape.\n  const [grabbed, setGrabbed] = useState<{ id: string; from: string[] } | null>(null);\n  const [message, setMessage] = useState(\"\");\n\n  const labelOf = (id: string) => columns.find((c) => c.id === id)?.label ?? id;\n  const positionOf = (ids: string[], id: string) => locked.length + ids.indexOf(id) + 1;\n  const total = columns.length;\n\n  const setOrder = (ids: string[]) => {\n    const byId = new Map(movable.map((c) => [c.id, c]));\n    setValue([...locked, ...ids.map((id) => byId.get(id)!)]);\n  };\n\n  const toggle = (id: string, next: boolean) => {\n    // The last visible column can't go: a table with no columns is a blank box.\n    if (!next && shown <= 1) return;\n    setValue(columns.map((c) => (c.id === id ? { ...c, visible: next } : c)));\n  };\n\n  const ids = movable.map((c) => c.id);\n\n  const move = (id: string, delta: number) => {\n    const from = ids.indexOf(id);\n    const to = Math.max(0, Math.min(ids.length - 1, from + delta));\n    if (from === to) return;\n    const next = [...ids];\n    next.splice(to, 0, next.splice(from, 1)[0]);\n    setOrder(next);\n    setMessage(`${labelOf(id)} moved to position ${positionOf(next, id)} of ${total}.`);\n  };\n\n  const onHandleKey = (e: React.KeyboardEvent, id: string) => {\n    if (e.key === \" \" || e.key === \"Enter\") {\n      e.preventDefault();\n      if (grabbed?.id === id) {\n        setGrabbed(null);\n        setMessage(`${labelOf(id)} dropped at position ${positionOf(ids, id)} of ${total}.`);\n      } else {\n        setGrabbed({ id, from: ids });\n        setMessage(`${labelOf(id)} picked up, position ${positionOf(ids, id)} of ${total}. Arrow keys move it, Space drops it, Escape cancels.`);\n      }\n    } else if (e.key === \"ArrowUp\" || e.key === \"ArrowDown\") {\n      // Alt+arrows move without picking up first.\n      if (grabbed?.id === id || e.altKey) {\n        e.preventDefault();\n        move(id, e.key === \"ArrowUp\" ? -1 : 1);\n      }\n    } else if (e.key === \"Escape\" && grabbed?.id === id) {\n      // Marks the Escape as handled so the popover stays open (see ColumnManager).\n      e.preventDefault();\n      setOrder(grabbed.from);\n      setGrabbed(null);\n      setMessage(`Move canceled. ${labelOf(id)} is back at position ${positionOf(grabbed.from, id)}.`);\n    }\n  };\n\n  const q = query.trim().toLowerCase();\n  const searchable = total >= searchThreshold;\n  const matches = q ? columns.filter((c) => c.label.toLowerCase().includes(q)) : null;\n  const allShown = shown === total;\n  const pristine = sameColumns(columns, normalize(baseline));\n\n  return (\n    <div className={cn(\"flex min-h-0 flex-col text-[13px] text-fg\", className)} {...rest}>\n      <div className=\"flex items-baseline justify-between gap-3 px-3 pb-2 pt-3\">\n        <span className=\"text-[13px] font-medium tracking-[-0.01em]\">{title}</span>\n        <span className=\"text-[12px] text-fg-3 tabular\">\n          <NumberFlow value={shown} className=\"tabular\" /> of {total} shown\n        </span>\n      </div>\n\n      {searchable && (\n        <div className=\"px-2 pb-1.5\">\n          <div className=\"flex h-8 items-center gap-2 rounded-lg border border-line-2 bg-frame px-2 transition-[border-color,box-shadow] duration-150 focus-within:border-fg-4 focus-within:ring-2 focus-within:ring-fg/10\">\n            <Search size={14} className=\"shrink-0 text-fg-4\" />\n            <input\n              value={query}\n              onChange={(e) => setQuery(e.target.value)}\n              onKeyDown={(e) => {\n                // First Escape clears the filter, the next one closes the popover.\n                if (e.key === \"Escape\" && query) {\n                  e.preventDefault();\n                  setQuery(\"\");\n                }\n              }}\n              placeholder=\"Find a column\"\n              aria-label=\"Find a column\"\n              enterKeyHint=\"search\"\n              className=\"min-w-0 flex-1 bg-transparent text-base text-fg outline-none placeholder:text-fg-4 sm:text-[12.5px]\"\n            />\n            {query && (\n              <button\n                type=\"button\"\n                aria-label=\"Clear filter\"\n                onClick={() => setQuery(\"\")}\n                className=\"relative -mr-1 grid size-5 shrink-0 place-items-center rounded text-fg-3 outline-none transition-[color,scale] duration-150 hover:text-fg focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-fg-3 active:scale-[0.9] before:absolute before:-inset-3 before:content-[''] pointer-fine:before:hidden\"\n              >\n                <X size={12} />\n              </button>\n            )}\n          </div>\n        </div>\n      )}\n\n      <motion.div layoutScroll className=\"min-h-0 overflow-y-auto overscroll-contain px-1 pb-1 [scrollbar-width:thin]\">\n        {matches ? (\n          matches.length ? (\n            <ul aria-label={typeof title === \"string\" ? title : \"Columns\"}>\n              {matches.map((c) => (\n                <li key={c.id}>\n                  <Row column={c} onToggle={toggle} lastVisible={shown <= 1 && isVisible(c)} slot={c.locked ? <LockMark /> : null} />\n                </li>\n              ))}\n            </ul>\n          ) : (\n            <div className=\"flex flex-col items-center gap-2 px-3 py-6 text-center\">\n              <p className=\"text-[12.5px] text-fg-3\">\n                No columns match <span className=\"text-fg-2\">“{query.trim()}”</span>\n              </p>\n              <button\n                type=\"button\"\n                onClick={() => setQuery(\"\")}\n                className=\"h-7 rounded-md px-2 text-[12px] font-medium text-fg-2 outline-none transition-[background-color,color,scale] duration-150 hover:bg-hover hover:text-fg focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-fg-3 active:scale-[0.97]\"\n              >\n                Clear filter\n              </button>\n            </div>\n          )\n        ) : (\n          <>\n            {locked.length > 0 && (\n              <ul aria-label=\"Pinned columns\">\n                {locked.map((c) => (\n                  <li key={c.id}>\n                    <Row column={c} onToggle={toggle} lastVisible slot={<LockMark />} />\n                  </li>\n                ))}\n              </ul>\n            )}\n            <Reorder.Group as=\"ul\" axis=\"y\" values={ids} onReorder={setOrder} aria-label={typeof title === \"string\" ? title : \"Columns\"}>\n              {movable.map((c) => (\n                <MovableRow\n                  key={c.id}\n                  column={c}\n                  reduce={!!reduce}\n                  hintId={hintId}\n                  grabbed={grabbed?.id === c.id}\n                  dragging={dragging === c.id}\n                  lastVisible={shown <= 1 && isVisible(c)}\n                  onToggle={toggle}\n                  onKey={onHandleKey}\n                  onBlurHandle={() => {\n                    if (grabbed?.id === c.id) {\n                      setGrabbed(null);\n                      setMessage(`${c.label} dropped at position ${positionOf(ids, c.id)} of ${total}.`);\n                    }\n                  }}\n                  onDragStart={() => setDragging(c.id)}\n                  onDragEnd={() => {\n                    setDragging(null);\n                    setMessage(`${c.label} moved to position ${positionOf(ids, c.id)} of ${total}.`);\n                  }}\n                />\n              ))}\n            </Reorder.Group>\n          </>\n        )}\n      </motion.div>\n\n      <div className=\"flex items-center justify-between gap-2 border-t border-line p-1\">\n        <FooterButton disabled={allShown} onClick={() => setValue(columns.map((c) => ({ ...c, visible: true })))}>\n          Show all\n        </FooterButton>\n        <FooterButton disabled={pristine} onClick={() => setValue(normalize(baseline))}>\n          Reset\n        </FooterButton>\n      </div>\n\n      <span id={hintId} className=\"sr-only\">\n        Press Space to pick up, the arrow keys to move, Space again to drop.\n      </span>\n      <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n        {message}\n      </span>\n    </div>\n  );\n}\n\nfunction FooterButton({ className, ...rest }: React.ComponentProps<\"button\">) {\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        \"h-7 rounded-md px-2 text-[12px] font-medium text-fg-2 outline-none\",\n        \"transition-[background-color,color,opacity,scale] duration-150 hover:bg-hover hover:text-fg active:scale-[0.97] active:duration-75\",\n        \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-offset-0 focus-visible:outline-fg-3\",\n        \"disabled:pointer-events-none disabled:opacity-40\",\n        className,\n      )}\n      {...rest}\n    />\n  );\n}\n\nfunction LockMark() {\n  return (\n    <span className=\"grid h-8 w-6 shrink-0 place-items-center text-fg-4\" title=\"Pinned\">\n      <Lock size={13} />\n      <span className=\"sr-only\">Pinned</span>\n    </span>\n  );\n}\n\ntype RowProps = {\n  column: ColumnItem;\n  onToggle: (id: string, next: boolean) => void;\n  /** The only column left showing: it can't be hidden. */\n  lastVisible: boolean;\n  slot: React.ReactNode;\n};\n\n// The whole row is the checkbox's label, so a press anywhere on the name toggles it.\nfunction Row({ column, onToggle, lastVisible, slot }: RowProps) {\n  const checked = isVisible(column);\n  const disabled = column.locked || lastVisible;\n  return (\n    <div className=\"flex h-8 items-center rounded-lg transition-colors duration-150 hover:bg-hover\">\n      {slot ?? <span className=\"w-6 shrink-0\" />}\n      <label\n        className={cn(\"group/col flex h-full min-w-0 flex-1 select-none items-center gap-2.5 pr-2\", disabled ? \"cursor-default\" : \"cursor-pointer\")}\n        title={column.locked ? undefined : lastVisible ? \"At least one column stays visible\" : undefined}\n      >\n        <Checkbox.Root\n          checked={checked}\n          disabled={disabled}\n          onCheckedChange={(next) => onToggle(column.id, next)}\n          className={cn(\n            \"relative grid size-3.5 shrink-0 place-items-center rounded-[4px] border outline-none\",\n            \"transition-[background-color,border-color,color,scale,opacity] duration-150 ease-out-expo\",\n            \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-fg-3\",\n            \"data-checked:border-fg data-checked:bg-fg data-checked:text-frame\",\n            \"data-unchecked:border-fg-4 data-unchecked:bg-raised\",\n            !disabled && \"motion-safe:active:scale-[0.86] motion-safe:group-active/col:scale-[0.86] group-hover/col:data-unchecked:border-fg-3\",\n            disabled && \"opacity-45\",\n          )}\n        >\n          <Checkbox.Indicator keepMounted render={(props, state) => <span {...props}><Tick on={state.checked} /></span>} />\n        </Checkbox.Root>\n        <span className={cn(\"min-w-0 truncate transition-colors duration-150\", checked ? \"text-fg\" : \"text-fg-3\")}>{column.label}</span>\n      </label>\n    </div>\n  );\n}\n\nfunction Tick({ on }: { on: boolean }) {\n  const reduce = useReducedMotion();\n  return (\n    <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden focusable=\"false\" className=\"size-3.5\">\n      <motion.path\n        d=\"M3.75 8.25 6.75 11.25 12.25 4.75\"\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        initial={false}\n        animate={{ pathLength: on ? 1 : 0, opacity: on ? 1 : 0 }}\n        transition={\n          reduce\n            ? { duration: 0 }\n            : on\n              ? { pathLength: { duration: 0.22, ease: ease.out, delay: 0.02 }, opacity: { duration: 0.04 } }\n              : { pathLength: { duration: 0.1, ease: ease.in }, opacity: { duration: 0.06, delay: 0.04 } }\n        }\n      />\n    </svg>\n  );\n}\n\ntype MovableRowProps = Omit<RowProps, \"slot\"> & {\n  reduce: boolean;\n  hintId: string;\n  grabbed: boolean;\n  dragging: boolean;\n  onKey: (e: React.KeyboardEvent, id: string) => void;\n  onBlurHandle: () => void;\n  onDragStart: () => void;\n  onDragEnd: () => void;\n};\n\nfunction MovableRow({ column, reduce, hintId, grabbed, dragging, onKey, onBlurHandle, onDragStart, onDragEnd, ...row }: MovableRowProps) {\n  const controls = useDragControls();\n  const lifted = grabbed || dragging;\n  return (\n    <Reorder.Item\n      value={column.id}\n      dragListener={false}\n      dragControls={controls}\n      onDragStart={onDragStart}\n      onDragEnd={onDragEnd}\n      // Neighbors make room on the snappy spring; the dropped row settles on it too.\n      transition={reduce ? { duration: 0 } : spring.snappy}\n      whileDrag={reduce ? undefined : { scale: 1.02 }}\n      data-dragging={dragging || undefined}\n      data-grabbed={grabbed || undefined}\n      className={cn(\n        \"relative rounded-lg transition-[background-color,box-shadow] duration-150\",\n        lifted && \"bg-raised shadow-pop ring-1 ring-line-2\",\n        grabbed && \"z-1\",\n      )}\n    >\n      <Row\n        column={column}\n        {...row}\n        slot={\n          <button\n            type=\"button\"\n            aria-label={`Reorder ${column.label}`}\n            aria-describedby={hintId}\n            aria-pressed={grabbed}\n            onPointerDown={(e) => {\n              if (e.button !== 0) return;\n              e.preventDefault();\n              controls.start(e);\n            }}\n            onKeyDown={(e) => onKey(e, column.id)}\n            onBlur={onBlurHandle}\n            className={cn(\n              \"relative grid h-8 w-6 shrink-0 touch-none place-items-center rounded-md outline-none\",\n              \"cursor-grab text-fg-4 transition-[color,scale] duration-150 hover:text-fg-2 active:cursor-grabbing\",\n              \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:-outline-offset-2 focus-visible:outline-fg-3\",\n              lifted && \"text-fg-2\",\n              // A 44px target on touch without widening the drawing.\n              \"before:absolute before:-inset-x-2 before:-inset-y-1.5 before:content-[''] pointer-fine:before:hidden\",\n            )}\n          >\n            <Grip size={14} />\n          </button>\n        }\n      />\n    </Reorder.Item>\n  );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * The trigger and popover\n * -----------------------------------------------------------------------------------------------*/\n\nexport type ColumnManagerProps = Omit<ColumnManagerPanelProps, \"className\"> & {\n  /** Trigger label. Also its accessible name when iconOnly. */\n  label?: string;\n  iconOnly?: boolean;\n  size?: \"sm\" | \"md\";\n  side?: Popover.Positioner.Props[\"side\"];\n  align?: Popover.Positioner.Props[\"align\"];\n  /** Where to portal the popover. Defaults to document.body. */\n  container?: Popover.Portal.Props[\"container\"];\n  className?: string;\n  /** Classes for the popover surface. */\n  popupClassName?: string;\n};\n\nexport function ColumnManager({\n  label = \"Columns\",\n  iconOnly = false,\n  size = \"md\",\n  side = \"bottom\",\n  align = \"end\",\n  container,\n  className,\n  popupClassName,\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  resetTo,\n  ...panel\n}: ColumnManagerProps) {\n  const [value, setValue] = useControllableState({ value: valueProp, defaultValue: defaultValue ?? [], onChange: onValueChange });\n  const [initial] = useState(value);\n  const shown = value.filter(isVisible).length;\n  const hidden = value.length - shown;\n\n  return (\n    <Popover.Root\n      onOpenChange={(open, details) => {\n        // An Escape that already did something inside (canceled a move, cleared the filter) doesn't also close.\n        if (!open && details.reason === \"escape-key\" && details.event.defaultPrevented) details.cancel();\n      }}\n    >\n      <Popover.Trigger\n        aria-label={iconOnly ? `${label}, ${hidden ? `${hidden} hidden` : \"all shown\"}` : undefined}\n        className={cn(\n          \"group/trigger relative inline-flex shrink-0 select-none items-center justify-center font-medium tracking-[-0.005em] outline-none\",\n          \"border border-line-2 bg-raised text-fg shadow-[var(--shadow)] hover:border-fg-4 hover:bg-hover data-popup-open:border-fg-4 data-popup-open:bg-hover\",\n          \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-fg-3\",\n          \"transition-[background-color,border-color,color,scale] duration-150 active:scale-[0.97] active:duration-75\",\n          \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n          size === \"sm\" ? \"h-7 gap-1.5 rounded-md text-[12px]\" : \"h-8 gap-2 rounded-lg text-[12.5px]\",\n          iconOnly ? (size === \"sm\" ? \"w-7\" : \"w-8\") : size === \"sm\" ? \"px-2\" : \"px-2.5\",\n          iconOnly && \"before:absolute before:-inset-2 before:content-[''] pointer-fine:before:hidden\",\n          className,\n        )}\n      >\n        <ColumnsGlyph className=\"shrink-0 text-fg-2 transition-colors duration-150 group-hover/trigger:text-fg group-data-popup-open/trigger:text-fg\" />\n        {!iconOnly && (\n          <>\n            <span>{label}</span>\n            <span className=\"-mr-0.5 flex items-center font-normal text-fg-3 tabular\" aria-label={hidden ? `${hidden} hidden` : \"All shown\"}>\n              <NumberFlow value={shown} className=\"tabular\" aria-hidden />\n              <span aria-hidden>/{value.length}</span>\n            </span>\n          </>\n        )}\n        {iconOnly && hidden > 0 && <span aria-hidden className=\"absolute right-1 top-1 size-1.5 rounded-full bg-fg\" />}\n      </Popover.Trigger>\n      <Popover.Portal container={container}>\n        <Popover.Positioner side={side} align={align} sideOffset={6} collisionPadding={8} className=\"z-(--z-popover)\">\n          <Popover.Popup\n            className={cn(\n              \"flex max-h-[min(26rem,var(--available-height))] w-64 max-w-[var(--available-width)] flex-col rounded-xl border border-line-2 bg-raised shadow-pop outline-none\",\n              // Grows from the trigger and drifts 4px away from it; leaves faster and simpler.\n              \"origin-[var(--transform-origin)] transition-[opacity,scale,translate] duration-200 ease-out-expo\",\n              \"data-starting-style:scale-[0.96] data-starting-style:opacity-0\",\n              \"data-[side=bottom]:data-starting-style:-translate-y-1 data-[side=top]:data-starting-style:translate-y-1\",\n              \"data-ending-style:scale-[0.98] data-ending-style:opacity-0 data-ending-style:duration-150\",\n              \"data-instant:transition-none motion-reduce:data-starting-style:scale-100 motion-reduce:data-starting-style:translate-y-0 motion-reduce:data-ending-style:scale-100\",\n              popupClassName,\n            )}\n          >\n            <Popover.Title className=\"sr-only\">{label}</Popover.Title>\n            <ColumnManagerPanel value={value} onValueChange={setValue} resetTo={resetTo ?? initial} {...panel} />\n          </Popover.Popup>\n        </Popover.Positioner>\n      </Popover.Portal>\n    </Popover.Root>\n  );\n}\n\nfunction ColumnsGlyph({ className }: { className?: string }) {\n  return (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.4} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden focusable=\"false\" className={className}>\n      <rect x=\"2.25\" y=\"2.75\" width=\"11.5\" height=\"10.5\" rx=\"1.75\" />\n      <path d=\"M6.1 2.75v10.5M9.9 2.75v10.5\" />\n    </svg>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "https://www.stealth.pm/ui/column-manager"
}
