@loworbitstudio/visor 1.35.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.4.0",
3
- "generated_at": "2026-10-04T07:08:34.776Z",
3
+ "generated_at": "2026-10-04T23:46:57.986Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "changeType": "current",
package/dist/index.js CHANGED
@@ -9446,6 +9446,41 @@ var FIXTURES = {
9446
9446
  "file-upload": {
9447
9447
  default: { export: "FileUpload", interactiveTarget: '[tabindex="0"]', props: `{}` }
9448
9448
  },
9449
+ // VI-698: the edged Alert, then `appearance="soft"` in each tone. `soft` is
9450
+ // Blacklight's door refusal; `soft-actions` is Animal's merge-dialog block.
9451
+ alert: {
9452
+ default: {
9453
+ export: "Alert",
9454
+ props: `{ variant: "destructive", style: { width: 420 }, children: [React.createElement(__mod.AlertTitle, { key: "t" }, "Error"), React.createElement(__mod.AlertDescription, { key: "d" }, "Your session has expired. Please log in again.")] }`
9455
+ },
9456
+ soft: {
9457
+ export: "Alert",
9458
+ props: `{ appearance: "soft", variant: "destructive", style: { width: 420 }, children: React.createElement(__mod.AlertDescription, null, React.createElement(__mod.AlertLead, null, "That email isn't on the list."), " Ask your host for an invite, or try another address.") }`
9459
+ },
9460
+ "soft-warning": {
9461
+ export: "Alert",
9462
+ props: `{ appearance: "soft", variant: "warning", style: { width: 420 }, children: React.createElement(__mod.AlertDescription, null, React.createElement(__mod.AlertLead, null, "Unsaved changes."), " Leave now and they are lost.") }`
9463
+ },
9464
+ "soft-success": {
9465
+ export: "Alert",
9466
+ props: `{ appearance: "soft", variant: "success", style: { width: 420 }, children: React.createElement(__mod.AlertDescription, null, "Check your inbox for a sign-in code.") }`
9467
+ },
9468
+ "soft-note": {
9469
+ export: "Alert",
9470
+ props: `{ appearance: "soft", style: { width: 420 }, children: React.createElement(__mod.AlertDescription, null, "Only the venue owner can change payout details.") }`
9471
+ },
9472
+ "soft-actions": {
9473
+ export: "Alert",
9474
+ with: ["button"],
9475
+ props: `{ appearance: "soft", variant: "destructive", style: { width: 420 }, children: [React.createElement(__mod.AlertDescription, { key: "d" }, React.createElement(__mod.AlertLead, null, "This can't be undone."), " The two profiles merge into one, and their bookings, payouts and notes move with it."), React.createElement(__mod.AlertActions, { key: "a" }, [React.createElement(__with["button"].Button, { key: "c", type: "button", variant: "ghost", size: "sm" }, "Cancel"), React.createElement(__with["button"].Button, { key: "m", type: "button", variant: "destructive", size: "sm" }, "Merge profiles")])] }`
9476
+ },
9477
+ // The same block with its text in a paragraph, as MDX wraps multi-line
9478
+ // children: the paragraph's margins must not push the text off the icon.
9479
+ "soft-paragraph": {
9480
+ export: "Alert",
9481
+ props: `{ appearance: "soft", variant: "warning", style: { width: 420 }, children: React.createElement(__mod.AlertDescription, null, React.createElement("p", null, React.createElement(__mod.AlertLead, null, "Unsaved changes."), " Leave now and they are lost.")) }`
9482
+ }
9483
+ },
9449
9484
  "empty-state": {
9450
9485
  default: {
9451
9486
  export: "EmptyState",
@@ -9636,10 +9671,15 @@ ${opts.bundleJs}
9636
9671
  </body>
9637
9672
  </html>`;
9638
9673
  }
9639
- function buildEntrySource(componentFile, fixture, exportName) {
9674
+ function buildEntrySource(componentFile, fixture, exportName, withFiles = {}) {
9675
+ const withNames = Object.keys(withFiles);
9676
+ const withImports = withNames.map((name, i) => `import * as __with_${i} from ${JSON.stringify(withFiles[name])};
9677
+ `).join("");
9678
+ const withMap = withNames.map((name, i) => `${JSON.stringify(name)}: __with_${i}`).join(", ");
9640
9679
  return `import * as React from "react";
9641
9680
  import { createRoot } from "react-dom/client";
9642
9681
  import * as __mod from ${JSON.stringify(componentFile)};
9682
+ ${withImports}var __with = { ${withMap} };
9643
9683
 
9644
9684
  function __resolveComponent(mod, preferred) {
9645
9685
  if (preferred && mod[preferred]) return mod[preferred];
@@ -9722,6 +9762,17 @@ async function renderCommand(component, cwd, options) {
9722
9762
  );
9723
9763
  }
9724
9764
  const exportName = fixture.export ?? pascalCase(component);
9765
+ const withFiles = {};
9766
+ for (const name of fixture.with ?? []) {
9767
+ const file = resolveComponentFile(cwd, name);
9768
+ if (!file) {
9769
+ fail3(
9770
+ "COMPONENT_NOT_FOUND",
9771
+ `Fixture "${fixtureName}" composes "${name}", which was not found under components/ui/ or blocks/.`
9772
+ );
9773
+ }
9774
+ withFiles[name] = file;
9775
+ }
9725
9776
  const esbuild = await loadOptional("esbuild");
9726
9777
  const playwright = await loadOptional("playwright");
9727
9778
  const missing = [];
@@ -9734,7 +9785,7 @@ async function renderCommand(component, cwd, options) {
9734
9785
  let componentCss = "";
9735
9786
  let bundleJs = "";
9736
9787
  try {
9737
- const entrySource = buildEntrySource(componentFile, fixture, exportName);
9788
+ const entrySource = buildEntrySource(componentFile, fixture, exportName, withFiles);
9738
9789
  const result = await esbuild.build({
9739
9790
  stdin: {
9740
9791
  contents: entrySource,
@@ -46,7 +46,7 @@
46
46
  {
47
47
  "path": "components/ui/heading/heading.module.css",
48
48
  "type": "registry:ui",
49
- "content": "/* Heading base styles */\n.base {\n font-family: var(--font-heading, ui-sans-serif, system-ui, sans-serif);\n color: var(--text-primary, #111827);\n line-height: var(--line-height-tight, 1.25);\n margin: 0;\n}\n\n/* Sizes */\n.sizeXs {\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n.sizeSm {\n font-size: var(--font-size-base, 1rem);\n}\n\n.sizeMd {\n font-size: var(--font-size-lg, 1.125rem);\n}\n\n.sizeLg {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeXl {\n font-size: var(--font-size-2xl, 1.5rem);\n}\n\n.size2xl {\n font-size: var(--font-size-4xl, 2.25rem);\n}\n\n/* Weights */\n\n/*\n * VI-639: the theme's own heading weight. `--font-weight-heading` carries what\n * this theme says headings weigh; `--font-weight-semibold` is a step on the\n * named ramp and no longer doubles as that channel. Falls back through semibold\n * so a consumer on an older tokens package keeps the previous resolution.\n */\n.weightHeading {\n font-weight: var(--font-weight-heading, var(--font-weight-semibold, 600));\n}\n\n.weightNormal {\n font-weight: var(--font-weight-normal, 400);\n}\n\n.weightMedium {\n font-weight: var(--font-weight-medium, 500);\n}\n\n.weightSemibold {\n font-weight: var(--font-weight-semibold, 600);\n}\n\n.weightBold {\n font-weight: var(--font-weight-bold, 700);\n}\n"
49
+ "content": "/* Heading base styles */\n.base {\n font-family: var(--font-heading, ui-sans-serif, system-ui, sans-serif);\n color: var(--text-primary, #111827);\n line-height: var(--line-height-tight, 1.25);\n /* VI-687: theme-set heading casing; inherit (the CSS default) when unset. */\n text-transform: var(--font-heading-text-transform, inherit);\n margin: 0;\n}\n\n/* Sizes */\n.sizeXs {\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n.sizeSm {\n font-size: var(--font-size-base, 1rem);\n}\n\n.sizeMd {\n font-size: var(--font-size-lg, 1.125rem);\n}\n\n.sizeLg {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeXl {\n font-size: var(--font-size-2xl, 1.5rem);\n}\n\n.size2xl {\n font-size: var(--font-size-4xl, 2.25rem);\n}\n\n/* Weights */\n\n/*\n * VI-639: the theme's own heading weight. `--font-weight-heading` carries what\n * this theme says headings weigh; `--font-weight-semibold` is a step on the\n * named ramp and no longer doubles as that channel. Falls back through semibold\n * so a consumer on an older tokens package keeps the previous resolution.\n */\n.weightHeading {\n font-weight: var(--font-weight-heading, var(--font-weight-semibold, 600));\n}\n\n.weightNormal {\n font-weight: var(--font-weight-normal, 400);\n}\n\n.weightMedium {\n font-weight: var(--font-weight-medium, 500);\n}\n\n.weightSemibold {\n font-weight: var(--font-weight-semibold, 600);\n}\n\n.weightBold {\n font-weight: var(--font-weight-bold, 700);\n}\n"
50
50
  }
51
51
  ]
52
52
  },
@@ -322,7 +322,7 @@
322
322
  {
323
323
  "path": "components/ui/select/select.tsx",
324
324
  "type": "registry:ui",
325
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { CaretDownIcon, CaretUpIcon, CheckIcon } from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./select.module.css\"\n\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"md\" | \"lg\"\n /**\n * Visual variant of the closed trigger. `\"default\"` keeps the 1px border on\n * the standard control surface. `\"borderless\"` drops the border and sits on\n * `--field-control-bg`, matching a borderless Input inside an in-dialog form\n * context (one that sets `--field-control-bg: var(--surface-subtle)`).\n * Opt-in — default is unchanged.\n */\n variant?: \"default\" | \"borderless\"\n }\n>(({ className, size = \"md\", variant = \"default\", children, ...props }, ref) => {\n const sizeClass = {\n sm: styles.triggerSizeSm,\n md: styles.triggerSizeMd,\n lg: styles.triggerSizeLg,\n }[size]\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n data-variant={variant}\n className={cn(\n styles.trigger,\n sizeClass,\n variant === \"borderless\" && styles.triggerBorderless,\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <CaretDownIcon className={styles.triggerIcon} />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(styles.content, className)}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n styles.viewport,\n position === \"popper\" && styles.viewportPopper\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n )\n})\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(styles.label, className)}\n ref={ref}\n {...props}\n />\n )\n})\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(styles.item, className)}\n ref={ref}\n {...props}\n >\n <span className={styles.itemIndicatorWrapper}>\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className={styles.itemIndicatorIcon} />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n})\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(styles.separator, className)}\n ref={ref}\n {...props}\n />\n )\n})\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(styles.scrollButton, className)}\n ref={ref}\n {...props}\n >\n <CaretUpIcon className={styles.scrollButtonIcon} />\n </SelectPrimitive.ScrollUpButton>\n )\n})\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(styles.scrollButton, className)}\n ref={ref}\n {...props}\n >\n <CaretDownIcon className={styles.scrollButtonIcon} />\n </SelectPrimitive.ScrollDownButton>\n )\n})\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"
325
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { CaretDownIcon, CaretUpIcon, CheckIcon } from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./select.module.css\"\n\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"md\" | \"lg\"\n /**\n * Visual variant of the closed trigger. `\"default\"` keeps the 1px border on\n * the standard control surface. `\"borderless\"` drops the border and sits on\n * `--field-control-bg`, matching a borderless Input inside an in-dialog form\n * context (one that sets `--field-control-bg: var(--surface-subtle)`).\n * Opt-in — default is unchanged.\n */\n variant?: \"default\" | \"borderless\"\n }\n>(({ className, size = \"md\", variant = \"default\", children, ...props }, ref) => {\n const sizeClass = {\n sm: styles.triggerSizeSm,\n md: styles.triggerSizeMd,\n lg: styles.triggerSizeLg,\n }[size]\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n data-variant={variant}\n className={cn(\n styles.trigger,\n sizeClass,\n variant === \"borderless\" && styles.triggerBorderless,\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <CaretDownIcon className={styles.triggerIcon} />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\ninterface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {\n /**\n * Element the menu portals into. Defaults to `document.body`. Pass the root\n * of a class-scoped theme so the menu inherits that theme's tokens.\n */\n container?: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Portal>[\"container\"]\n}\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(({ className, children, position = \"popper\", container, ...props }, ref) => {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(styles.content, className)}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n styles.viewport,\n position === \"popper\" && styles.viewportPopper\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n )\n})\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(styles.label, className)}\n ref={ref}\n {...props}\n />\n )\n})\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(styles.item, className)}\n ref={ref}\n {...props}\n >\n <span className={styles.itemIndicatorWrapper}>\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className={styles.itemIndicatorIcon} />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n})\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(styles.separator, className)}\n ref={ref}\n {...props}\n />\n )\n})\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(styles.scrollButton, className)}\n ref={ref}\n {...props}\n >\n <CaretUpIcon className={styles.scrollButtonIcon} />\n </SelectPrimitive.ScrollUpButton>\n )\n})\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(styles.scrollButton, className)}\n ref={ref}\n {...props}\n >\n <CaretDownIcon className={styles.scrollButtonIcon} />\n </SelectPrimitive.ScrollDownButton>\n )\n})\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\nexport type { SelectContentProps }\n"
326
326
  },
327
327
  {
328
328
  "path": "components/ui/select/select.module.css",
@@ -353,7 +353,7 @@
353
353
  {
354
354
  "path": "components/ui/switch/switch.module.css",
355
355
  "type": "registry:ui",
356
- "content": "/* Switch root styles */\n.root {\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n border-radius: 9999px;\n /* VI-655: the border only holds box geometry. The track's resting edge is an\n inset outline on the shared --control-edge-* tokens; it is transparent\n unless a theme sets --switch-edge-color, and --control-edge-width: 0\n removes it with every other control's edge. */\n border: 1px solid transparent;\n background-color: var(--switch-track-bg, var(--border-default, #e5e7eb));\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), outline-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), box-shadow var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: var(--control-edge-width, 1px) var(--control-edge-style, solid) var(--switch-edge-color, transparent);\n outline-offset: calc(-1 * var(--control-edge-width, 1px));\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]) {\n background-color: var(--border-strong, #d1d5db);\n }\n}\n\n/* Focus and invalid draw at --control-state-edge-width, which the edge switch\n does not touch. */\n.root:focus-visible {\n outline: var(--control-state-edge-width, 1px) solid var(--border-focus, #111827);\n outline-offset: calc(-1 * var(--control-state-edge-width, 1px));\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-focus, #111827) 15%, transparent);\n}\n\n.root:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.root[aria-invalid=\"true\"] {\n outline: var(--control-state-edge-width, 1px) solid var(--border-error, #ef4444);\n outline-offset: calc(-1 * var(--control-state-edge-width, 1px));\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-error, #ef4444) 15%, transparent);\n}\n\n.root[data-state=\"checked\"] {\n background-color: var(--interactive-primary-bg, var(--primary, #111827));\n}\n\n/* Size variants\n The default size takes its dimensions from the theme (VI-656):\n --switch-track-width / -height, and --switch-knob-inset, the gap between the\n knob and the track's outer edge on every side. The private properties below\n exist only while --switch-knob-inset is set; unset, they are invalid, so the\n knob falls back to its shipped 1rem size and position and the track keeps\n whatever inline padding it had (revert-layer). Set, the track's inline\n padding goes to 0 so the inset alone places the knob. The 1px is the\n geometry border (see .root), which the inset is measured across. */\n.sizeDefault {\n height: var(--switch-track-height, 1.15rem);\n width: var(--switch-track-width, 2rem);\n --_switch-pad: calc(var(--switch-knob-inset) * 0);\n --_switch-knob-size: calc(var(--switch-track-height, 1.15rem) - 2 * var(--switch-knob-inset));\n --_switch-knob-start: calc(var(--switch-knob-inset) - 1px);\n}\n\n/* In visor-base so an unset inset leaves the track's padding to whatever reset\n or browser default applies, and a set inset zeroes it only where no consumer\n rule speaks. */\n@layer visor-base {\n .sizeDefault {\n padding-inline: var(--_switch-pad, revert-layer);\n }\n}\n\n.sizeSm {\n height: 0.875rem;\n width: 1.5rem;\n}\n\n/* Thumb */\n.thumb {\n display: block;\n border-radius: 9999px;\n background-color: var(--surface-page, #ffffff);\n box-shadow: var(--shadow-sm);\n transition: transform var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n pointer-events: none;\n}\n\n/* Thumb size — derived from parent via CSS nesting */\n.sizeDefault .thumb {\n width: var(--_switch-knob-size, 1rem);\n height: var(--_switch-knob-size, 1rem);\n}\n\n.sizeSm .thumb {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n.root[data-state=\"unchecked\"] .thumb {\n transform: translateX(0);\n}\n\n.sizeDefault[data-state=\"unchecked\"] .thumb {\n transform: translateX(var(--_switch-knob-start, 0));\n}\n\n/* The knob's right edge sits the same distance from the track's right edge as\n its left edge does from the left: flush inside the 1px geometry border by\n default (2rem - 1rem - 2px, the shipped 100% - 2px), or the inset when set. */\n.sizeDefault[data-state=\"checked\"] .thumb {\n transform: translateX(calc(var(--switch-track-width, 2rem) - 100% - 2px - var(--_switch-knob-start, 0px)));\n}\n\n.sizeSm[data-state=\"checked\"] .thumb {\n transform: translateX(calc(100% - 2px));\n}\n"
356
+ "content": "/* Switch root styles */\n.root {\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n border-radius: 9999px;\n /* VI-655: the border only holds box geometry. The track's resting edge is an\n inset outline on the shared --control-edge-* tokens; it is transparent\n unless a theme sets --switch-edge-color, and --control-edge-width: 0\n removes it with every other control's edge. */\n border: 1px solid transparent;\n background-color: var(--switch-track-bg, var(--border-default, #e5e7eb));\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), outline-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), box-shadow var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: var(--control-edge-width, 1px) var(--control-edge-style, solid) var(--switch-edge-color, transparent);\n outline-offset: calc(-1 * var(--control-edge-width, 1px));\n cursor: pointer;\n}\n\n/* VI-693: hover reads its own keys, so a borderless theme (border-strong set\n transparent) keeps a visible track under the pointer. Unset, both states\n repaint with --border-strong, as they always have. The checked rule is one\n class more specific, so it wins over the unchecked one. */\n@media (hover: hover) {\n .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]) {\n background-color: var(--switch-track-hover-bg, var(--border-strong, #d1d5db));\n }\n\n .root[data-state=\"checked\"]:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]) {\n background-color: var(--switch-track-hover-bg-checked, var(--border-strong, #d1d5db));\n }\n}\n\n/* Focus and invalid draw at --control-state-edge-width, which the edge switch\n does not touch. */\n.root:focus-visible {\n outline: var(--control-state-edge-width, 1px) solid var(--border-focus, #111827);\n outline-offset: calc(-1 * var(--control-state-edge-width, 1px));\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-focus, #111827) 15%, transparent);\n}\n\n.root:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.root[aria-invalid=\"true\"] {\n outline: var(--control-state-edge-width, 1px) solid var(--border-error, #ef4444);\n outline-offset: calc(-1 * var(--control-state-edge-width, 1px));\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-error, #ef4444) 15%, transparent);\n}\n\n.root[data-state=\"checked\"] {\n background-color: var(--interactive-primary-bg, var(--primary, #111827));\n}\n\n/* Size variants\n The default size takes its dimensions from the theme (VI-656):\n --switch-track-width / -height, and --switch-knob-inset, the gap between the\n knob and the track's outer edge on every side. The private properties below\n exist only while --switch-knob-inset is set; unset, they are invalid, so the\n knob falls back to its shipped 1rem size and position and the track keeps\n whatever inline padding it had (revert-layer). Set, the track's inline\n padding goes to 0 so the inset alone places the knob. The 1px is the\n geometry border (see .root), which the inset is measured across. */\n.sizeDefault {\n height: var(--switch-track-height, 1.15rem);\n width: var(--switch-track-width, 2rem);\n --_switch-pad: calc(var(--switch-knob-inset) * 0);\n --_switch-knob-size: calc(var(--switch-track-height, 1.15rem) - 2 * var(--switch-knob-inset));\n --_switch-knob-start: calc(var(--switch-knob-inset) - 1px);\n}\n\n/* In visor-base so an unset inset leaves the track's padding to whatever reset\n or browser default applies, and a set inset zeroes it only where no consumer\n rule speaks. */\n@layer visor-base {\n .sizeDefault {\n padding-inline: var(--_switch-pad, revert-layer);\n }\n}\n\n.sizeSm {\n height: 0.875rem;\n width: 1.5rem;\n}\n\n/* Thumb */\n.thumb {\n display: block;\n border-radius: 9999px;\n background-color: var(--switch-knob-bg, var(--surface-page, #ffffff));\n box-shadow: var(--shadow-sm);\n transition: transform var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n pointer-events: none;\n}\n\n/* Thumb size — derived from parent via CSS nesting */\n.sizeDefault .thumb {\n width: var(--_switch-knob-size, 1rem);\n height: var(--_switch-knob-size, 1rem);\n}\n\n.sizeSm .thumb {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n/* VI-693: the knob has a key per state. A dark theme's page colour is a black\n knob, which an unchecked track cannot hold at 3:1 (WCAG 1.4.11). */\n.root[data-state=\"checked\"] .thumb {\n background-color: var(--switch-knob-bg-checked, var(--surface-page, #ffffff));\n}\n\n.root[data-state=\"unchecked\"] .thumb {\n transform: translateX(0);\n}\n\n.sizeDefault[data-state=\"unchecked\"] .thumb {\n transform: translateX(var(--_switch-knob-start, 0));\n}\n\n/* The knob's right edge sits the same distance from the track's right edge as\n its left edge does from the left: flush inside the 1px geometry border by\n default (2rem - 1rem - 2px, the shipped 100% - 2px), or the inset when set. */\n.sizeDefault[data-state=\"checked\"] .thumb {\n transform: translateX(calc(var(--switch-track-width, 2rem) - 100% - 2px - var(--_switch-knob-start, 0px)));\n}\n\n.sizeSm[data-state=\"checked\"] .thumb {\n transform: translateX(calc(100% - 2px));\n}\n"
357
357
  }
358
358
  ]
359
359
  },
@@ -690,10 +690,11 @@
690
690
  {
691
691
  "name": "alert",
692
692
  "type": "registry:ui",
693
- "description": "An alert component with title, description, and right-aligned actions sub-components, supporting default, destructive, success, and warning variants.",
693
+ "description": "An alert component with title, description, lead, and right-aligned actions sub-components, supporting default, destructive, success, and warning variants, and a soft appearance with a tone icon for inline refusals under a control.",
694
694
  "category": "feedback",
695
695
  "dependencies": [
696
696
  "class-variance-authority",
697
+ "@phosphor-icons/react",
697
698
  "@loworbitstudio/visor-core"
698
699
  ],
699
700
  "registryDependencies": [
@@ -703,12 +704,12 @@
703
704
  {
704
705
  "path": "components/ui/alert/alert.tsx",
705
706
  "type": "registry:ui",
706
- "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./alert.module.css\"\n\nconst alertVariants = cva(styles.base, {\n variants: {\n variant: {\n default: styles.variantDefault,\n destructive: styles.variantDestructive,\n success: styles.variantSuccess,\n warning: styles.variantWarning,\n info: styles.variantInfo,\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n})\n\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({ className, variant, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert\"\n role=\"alert\"\n className={cn(alertVariants({ variant }), className)}\n {...props}\n />\n )\n }\n)\nAlert.displayName = \"Alert\"\n\nconst AlertTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-title\"\n className={cn(styles.title, className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = \"AlertTitle\"\n\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-description\"\n className={cn(styles.description, className)}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = \"AlertDescription\"\n\nconst AlertActions = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-actions\"\n className={cn(styles.actions, className)}\n {...props}\n />\n )\n }\n)\nAlertActions.displayName = \"AlertActions\"\n\nexport { Alert, AlertTitle, AlertDescription, AlertActions, alertVariants }\n"
707
+ "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport type { IconProps } from \"@phosphor-icons/react\"\nimport { CheckCircle, Info, Warning, WarningOctagon } from \"@phosphor-icons/react/dist/ssr\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./alert.module.css\"\n\n/*\n * Two appearances share one component (VI-698):\n *\n * - `default` — the edged callout every current Alert draws. Its class list is\n * unchanged: base + one variant class.\n * - `soft` — the inline alert Animal Booking (AN-724) and Blacklight (BL-1204)\n * each built locally: a soft tinted fill with no edge and no shadow, a 32px\n * outline icon in the tone's colour, and the body in secondary ink. Under\n * `soft` the edged variant classes are not applied at all, so their border,\n * shadow and tone-tinted ink cannot leak in.\n */\nconst alertVariants = cva(styles.base, {\n variants: {\n variant: {\n default: \"\",\n destructive: \"\",\n success: \"\",\n warning: \"\",\n info: \"\",\n },\n appearance: {\n default: \"\",\n soft: styles.soft,\n },\n },\n compoundVariants: [\n { appearance: \"default\", variant: \"default\", class: styles.variantDefault },\n { appearance: \"default\", variant: \"destructive\", class: styles.variantDestructive },\n { appearance: \"default\", variant: \"success\", class: styles.variantSuccess },\n { appearance: \"default\", variant: \"warning\", class: styles.variantWarning },\n { appearance: \"default\", variant: \"info\", class: styles.variantInfo },\n // `info` has no soft surface yet (no `--surface-info-soft`), so it falls\n // back to the neutral note.\n { appearance: \"soft\", variant: [\"default\", \"info\"], class: styles.softNote },\n { appearance: \"soft\", variant: \"destructive\", class: styles.softDestructive },\n { appearance: \"soft\", variant: \"success\", class: styles.softSuccess },\n { appearance: \"soft\", variant: \"warning\", class: styles.softWarning },\n ],\n defaultVariants: {\n variant: \"default\",\n appearance: \"default\",\n },\n})\n\ntype AlertVariant = NonNullable<VariantProps<typeof alertVariants>[\"variant\"]>\n\n/** Each soft tone's default icon. Danger is an octagon so it differs from a\n * warning by shape as well as colour (WCAG 1.4.1). */\nconst SOFT_ICON: Record<AlertVariant, React.ElementType<IconProps>> = {\n default: Info,\n info: Info,\n destructive: WarningOctagon,\n warning: Warning,\n success: CheckCircle,\n}\n\n/** The soft icon size for every tone but the neutral note. Matches the\n * `--alert-icon-size` fallback; the CSS token governs the rendered size. */\nexport const ALERT_ICON_SIZE = 32\n/** The neutral note is usually one line, where 32px reads heavy. Matches the\n * `--alert-icon-size-note` fallback. */\nexport const ALERT_ICON_SIZE_NOTE = 24\n\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /**\n * `soft` only: replaces the tone's default icon. Drawn at the tone's size and\n * hidden from assistive tech; its weight stays the caller's.\n */\n icon?: React.ReactElement<IconProps>\n}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({ className, variant, appearance, icon, role, children, ...props }, ref) => {\n const tone: AlertVariant = variant ?? \"default\"\n\n if (appearance !== \"soft\") {\n return (\n <div\n ref={ref}\n data-slot=\"alert\"\n role={role ?? \"alert\"}\n className={cn(alertVariants({ variant }), className)}\n {...props}\n >\n {children}\n </div>\n )\n }\n\n const isNote = tone === \"default\" || tone === \"info\"\n const DefaultIcon = SOFT_ICON[tone]\n const glyph = icon ?? <DefaultIcon />\n\n return (\n <div\n ref={ref}\n data-slot=\"alert\"\n data-appearance=\"soft\"\n role={role ?? (tone === \"destructive\" ? \"alert\" : \"status\")}\n className={cn(alertVariants({ variant, appearance }), className)}\n {...props}\n >\n <span data-slot=\"alert-icon\" className={styles.icon}>\n {React.cloneElement(glyph, {\n size: isNote ? ALERT_ICON_SIZE_NOTE : ALERT_ICON_SIZE,\n \"aria-hidden\": true,\n })}\n </span>\n <div data-slot=\"alert-content\" className={styles.content}>\n {children}\n </div>\n </div>\n )\n }\n)\nAlert.displayName = \"Alert\"\n\nconst AlertTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-title\"\n className={cn(styles.title, className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = \"AlertTitle\"\n\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-description\"\n className={cn(styles.description, className)}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = \"AlertDescription\"\n\n/**\n * A bold lead sentence, set inline at the start of an `AlertDescription`, in\n * primary ink: `<AlertDescription><AlertLead>Can't sign in.</AlertLead> That\n * email isn't on the list.</AlertDescription>`.\n */\nconst AlertLead = React.forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement>>(\n ({ className, ...props }, ref) => {\n return (\n <strong\n ref={ref}\n data-slot=\"alert-lead\"\n className={cn(styles.lead, className)}\n {...props}\n />\n )\n }\n)\nAlertLead.displayName = \"AlertLead\"\n\nconst AlertActions = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"alert-actions\"\n className={cn(styles.actions, className)}\n {...props}\n />\n )\n }\n)\nAlertActions.displayName = \"AlertActions\"\n\nexport { Alert, AlertTitle, AlertDescription, AlertLead, AlertActions, alertVariants }\n"
707
708
  },
708
709
  {
709
710
  "path": "components/ui/alert/alert.module.css",
710
711
  "type": "registry:ui",
711
- "content": "/* Alert base */\n.base {\n position: relative;\n display: grid;\n width: 100%;\n gap: calc(var(--spacing-1, 0.25rem) / 2);\n border-radius: var(--radius-lg, 0.5rem);\n box-shadow: var(--shadow-sm);\n padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);\n font-size: var(--font-size-sm, 0.875rem);\n text-align: left;\n}\n\n/* Variants */\n.variantDefault {\n background-color: var(--surface-card, transparent);\n color: var(--text-primary, currentColor);\n}\n\n.variantDestructive {\n background-color: var(--surface-error-subtle, transparent);\n color: var(--text-error, currentColor);\n border: 1px solid var(--border-error, currentColor);\n}\n\n.variantSuccess {\n background-color: var(--surface-success-subtle, transparent);\n color: var(--text-success, currentColor);\n border: 1px solid var(--border-success, currentColor);\n}\n\n.variantWarning {\n background-color: var(--surface-warning-subtle, transparent);\n color: var(--text-warning, currentColor);\n border: 1px solid var(--border-warning, currentColor);\n}\n\n.variantInfo {\n background-color: var(--surface-info-subtle, transparent);\n color: var(--text-info, currentColor);\n border: 1px solid var(--border-info, currentColor);\n}\n\n/* AlertTitle */\n.title {\n font-weight: var(--font-weight-semibold, 600);\n line-height: 1.5;\n color: inherit;\n}\n\n/* AlertDescription */\n.description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, currentColor);\n line-height: 1.5;\n}\n\n/* AlertActions — right-aligned, gap-aware row for inline action buttons */\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--spacing-2, 0.5rem);\n flex-wrap: wrap;\n margin-top: var(--spacing-2, 0.5rem);\n}\n\n.variantDestructive .description {\n color: color-mix(in srgb, var(--text-error, currentColor) 90%, transparent);\n}\n\n.variantSuccess .description {\n color: color-mix(in srgb, var(--text-success, currentColor) 90%, transparent);\n}\n\n.variantWarning .description {\n color: color-mix(in srgb, var(--text-warning, currentColor) 90%, transparent);\n}\n\n.variantInfo .description {\n color: color-mix(in srgb, var(--text-info, currentColor) 90%, transparent);\n}\n"
712
+ "content": "/* Alert base */\n.base {\n position: relative;\n display: grid;\n width: 100%;\n gap: calc(var(--spacing-1, 0.25rem) / 2);\n border-radius: var(--radius-lg, 0.5rem);\n box-shadow: var(--shadow-sm);\n padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);\n font-size: var(--font-size-sm, 0.875rem);\n text-align: left;\n}\n\n/* Variants */\n.variantDefault {\n background-color: var(--surface-card, transparent);\n color: var(--text-primary, currentColor);\n}\n\n.variantDestructive {\n background-color: var(--surface-error-subtle, transparent);\n color: var(--text-error, currentColor);\n border: 1px solid var(--border-error, currentColor);\n}\n\n.variantSuccess {\n background-color: var(--surface-success-subtle, transparent);\n color: var(--text-success, currentColor);\n border: 1px solid var(--border-success, currentColor);\n}\n\n.variantWarning {\n background-color: var(--surface-warning-subtle, transparent);\n color: var(--text-warning, currentColor);\n border: 1px solid var(--border-warning, currentColor);\n}\n\n.variantInfo {\n background-color: var(--surface-info-subtle, transparent);\n color: var(--text-info, currentColor);\n border: 1px solid var(--border-info, currentColor);\n}\n\n/* AlertTitle */\n.title {\n font-weight: var(--font-weight-semibold, 600);\n line-height: 1.5;\n color: inherit;\n}\n\n/* AlertDescription */\n.description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, currentColor);\n line-height: 1.5;\n}\n\n/* AlertActions — right-aligned, gap-aware row for inline action buttons */\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--spacing-2, 0.5rem);\n flex-wrap: wrap;\n margin-top: var(--spacing-2, 0.5rem);\n}\n\n.variantDestructive .description {\n color: color-mix(in srgb, var(--text-error, currentColor) 90%, transparent);\n}\n\n.variantSuccess .description {\n color: color-mix(in srgb, var(--text-success, currentColor) 90%, transparent);\n}\n\n.variantWarning .description {\n color: color-mix(in srgb, var(--text-warning, currentColor) 90%, transparent);\n}\n\n.variantInfo .description {\n color: color-mix(in srgb, var(--text-info, currentColor) 90%, transparent);\n}\n\n/* AlertLead — a bold lead sentence set inline at the start of the description */\n.lead {\n color: var(--text-primary, currentColor);\n font-weight: var(--alert-lead-font-weight, var(--font-weight-bold, 700));\n}\n\n/* ─────────────────────────────────────────────────────────────────────────────\n appearance=\"soft\" (VI-698) — the inline alert Animal Booking (AN-724) and\n Blacklight (BL-1204) each built locally. A soft tinted fill with no edge and\n no shadow, an unchipped outline icon in the tone's colour, and the body in\n secondary ink: the icon and the fill carry the tone, so a refusal reads calm.\n The edged variant classes are never applied under `soft`, so nothing here\n has to out-specify them. Sizes are `--alert-*` component tokens a theme binds\n in its components block, under alert.\n ───────────────────────────────────────────────────────────────────────────── */\n.soft {\n display: flex;\n align-items: flex-start;\n gap: var(--alert-gap, var(--spacing-3, 0.75rem));\n /* 12px 14px — the approved builds' inset; 14px is spacing-3 plus half a step. */\n padding: var(--alert-soft-padding, var(--spacing-3, 0.75rem) calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2));\n border-radius: var(--alert-radius, var(--radius-lg, 0.5rem));\n box-shadow: none;\n font-size: var(--alert-font-size, var(--font-size-sm, 0.875rem));\n line-height: var(--line-height-normal, 1.5);\n color: var(--text-secondary, currentColor);\n}\n\n.softDestructive {\n background-color: var(--surface-error-soft, var(--surface-error-subtle, transparent));\n}\n\n.softWarning {\n background-color: var(--surface-warning-soft, var(--surface-warning-subtle, transparent));\n}\n\n.softSuccess {\n background-color: var(--surface-success-soft, var(--surface-success-subtle, transparent));\n}\n\n/* The neutral note: a faint wash of the ink colour — white on a dark card, a\n faint grey on a light one. 5% is the approved builds' white wash at 5% alpha,\n mixed toward the ink instead of white so it also reads in light mode. */\n.softNote {\n background-color: color-mix(in srgb, var(--text-primary, #111827) 5%, transparent);\n}\n\n/* The icon: outline, unchipped, in the tone's colour. The SVG's own width is\n the token's fallback; this governs the rendered size so a theme can bind it. */\n.icon {\n flex: none;\n display: inline-flex;\n}\n\n.icon > svg {\n width: var(--alert-icon-size, 2rem);\n height: var(--alert-icon-size, 2rem);\n}\n\n.softNote .icon > svg {\n width: var(--alert-icon-size-note, 1.5rem);\n height: var(--alert-icon-size-note, 1.5rem);\n}\n\n.softDestructive .icon {\n color: var(--text-error, currentColor);\n}\n\n.softWarning .icon {\n color: var(--text-warning, currentColor);\n}\n\n.softSuccess .icon {\n color: var(--text-success, currentColor);\n}\n\n.softNote .icon {\n color: var(--text-secondary, currentColor);\n}\n\n/* At least the icon's height and centred against it, so a one-line message\n sits level with the icon while a longer one starts at its top. */\n.content {\n flex: 1;\n min-width: 0;\n min-height: var(--alert-icon-size, 2rem);\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: calc(var(--spacing-1, 0.25rem) / 2);\n}\n\n.softNote .content {\n min-height: var(--alert-icon-size-note, 1.5rem);\n}\n\n/* Trim the outer margins of whatever sits at the content's edges. MDX wraps\n multi-line children in a paragraph, and prose styles (and the browser\n default) give it a top and bottom margin, which pushes the text below the\n icon and pads the card. Two classes out-specify a prose paragraph rule, so\n this holds inside documentation and marketing prose. Margins between\n paragraphs are kept. */\n.content > :first-child,\n.soft .description > :first-child {\n margin-top: 0;\n}\n\n.content > :last-child,\n.soft .description > :last-child {\n margin-bottom: 0;\n}\n\n.soft .title {\n color: var(--text-primary, currentColor);\n}\n\n.soft .description {\n font-size: inherit;\n line-height: inherit;\n color: var(--text-secondary, currentColor);\n}\n"
712
713
  }
713
714
  ]
714
715
  },
@@ -2904,7 +2905,7 @@
2904
2905
  {
2905
2906
  "path": "components/ui/page-header/page-header.module.css",
2906
2907
  "type": "registry:ui",
2907
- "content": "/* Page Header base: container query wrapper.\n\n VI-625: `--page-header-title-size` / `-title-family` used to be *declared*\n here. An element-level declaration outranks anything a theme emits into a\n cascade layer on :root, so the documented \"themes can set these\" was never\n actually true — the component shadowed the theme. Both use sites already\n carry the identical fallback inline, so dropping the declarations is\n value-preserving (`var(--x, 3.5rem)` with no --x resolves to 3.5rem, exactly\n as it did when --x was declared as 3.5rem) and makes the hooks genuinely\n theme-bindable. `--page-header-title-leading` was already unset for the same\n reason: the title falls back to its per-rule default line-height (tight for\n the standard title, `1` for the marquee scale); the `leading` prop sets it. */\n.base {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--page-header-gap, var(--spacing-4, 1rem));\n container-type: inline-size;\n container-name: page-header;\n color: var(--page-header-text-color, var(--text-primary, #111827));\n}\n\n/* Size variants control vertical rhythm */\n.sizeSm {\n gap: var(--spacing-3, 0.75rem);\n}\n\n.sizeMd {\n gap: var(--spacing-4, 1rem);\n}\n\n.sizeLg {\n gap: var(--spacing-5, 1.25rem);\n}\n\n/* Breadcrumb slot sits above the title row */\n.breadcrumb {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n/* Main row: text block on the left, actions on the right */\n.row {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--spacing-6, 1.5rem);\n min-width: 0;\n}\n\n/* When a leading media/identity slot is present, the row forms an identity\n lockup: the media sits before the text stack a tight gap from it. Scoped to\n `data-has-leading` so the default (text|actions) row is untouched for\n consumers that don't pass `leading`. The expanding text block keeps actions\n pinned right via the inherited `justify-content: space-between`. */\n.row[data-has-leading] {\n align-items: center;\n gap: var(--page-header-leading-gap, var(--spacing-3, 0.75rem));\n}\n\n/* Leading media/identity slot: sits before the text block, vertically\n centered against the title/description stack, never shrinks. (Reverts the\n VI-539 top-align, which assumed a different slot — the blessed admin\n identity lockup centers the media. Superseded per the admin-editorial\n reconcile plan.) */\n.leading {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n/* Text block: eyebrow, title, description stacked */\n.text {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.sizeSm .text {\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeLg .text {\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Eyebrow: small uppercase label above the title */\n.eyebrow {\n font-size: var(--page-header-eyebrow-font-size, var(--font-size-xs, 0.75rem));\n font-weight: var(--page-header-eyebrow-font-weight, var(--font-weight-semibold, 600));\n letter-spacing: var(--page-header-eyebrow-letter-spacing, var(--letter-spacing-wide, 0.05em));\n text-transform: var(--page-header-eyebrow-text-transform, uppercase);\n color: var(--page-header-eyebrow-color, var(--text-tertiary, #6b7280));\n line-height: var(--line-height-tight, 1.25);\n}\n\n/* Title */\n.title {\n margin: 0;\n font-family: var(--font-family-heading, inherit);\n font-size: var(--page-header-title-font-size, var(--font-size-2xl, 1.5rem));\n font-weight: var(--page-header-title-font-weight, var(--font-weight-semibold, 600));\n line-height: var(--page-header-title-leading, var(--line-height-tight, 1.2));\n color: var(--page-header-title-color, var(--text-primary, #111827));\n letter-spacing: var(--page-header-title-letter-spacing, var(--letter-spacing-tight, -0.01em));\n}\n\n.sizeSm .title {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeLg .title {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n/* Title typography overrides (orthogonal to size variants).\n `data-title-size=\"marquee\"` pulls the title up to a hero scale via the\n `--page-header-title-size` custom property. Raw-string consumers pass an\n inline `--page-header-title-size` and rely on the same rule. */\n.title[data-title-size=\"marquee\"] {\n font-size: var(--page-header-title-size, 3.5rem);\n line-height: var(--page-header-title-leading, 1);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n/* `data-title-family=\"display\"` switches to the display family with a\n graceful fallback to the heading family when no display font is themed.\n VI-625: `--admin-ui-marquee-family` sits in the chain so the admin-ui\n pattern's marquee role drives the hero title when a theme binds it. */\n.title[data-title-family=\"display\"] {\n font-family: var(--page-header-title-family, var(--admin-ui-marquee-family, var(--font-display, var(--font-family-heading, inherit))));\n}\n\n/* Description */\n.description {\n margin: 0;\n font-size: var(--page-header-description-font-size, var(--font-size-sm, 0.875rem));\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--page-header-description-color, var(--text-secondary, #6b7280));\n max-width: 65ch;\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Actions slot: cluster on the right */\n.actions {\n display: flex;\n align-items: center;\n gap: var(--page-header-actions-gap, var(--spacing-2, 0.5rem));\n flex: 0 0 auto;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n\n/* Container query: stack to column below a small container width */\n@container page-header (max-width: 480px) {\n .row {\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n }\n\n .actions {\n width: 100%;\n justify-content: flex-start;\n }\n}\n"
2908
+ "content": "/* Page Header base: container query wrapper.\n\n VI-625: `--page-header-title-size` / `-title-family` used to be *declared*\n here. An element-level declaration outranks anything a theme emits into a\n cascade layer on :root, so the documented \"themes can set these\" was never\n actually true — the component shadowed the theme. Both use sites already\n carry the identical fallback inline, so dropping the declarations is\n value-preserving (`var(--x, 3.5rem)` with no --x resolves to 3.5rem, exactly\n as it did when --x was declared as 3.5rem) and makes the hooks genuinely\n theme-bindable. `--page-header-title-leading` was already unset for the same\n reason: the title falls back to its per-rule default line-height (tight for\n the standard title, `1` for the marquee scale); the `leading` prop sets it. */\n.base {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--page-header-gap, var(--spacing-4, 1rem));\n container-type: inline-size;\n container-name: page-header;\n color: var(--page-header-text-color, var(--text-primary, #111827));\n}\n\n/* Size variants control vertical rhythm */\n.sizeSm {\n gap: var(--spacing-3, 0.75rem);\n}\n\n.sizeMd {\n gap: var(--spacing-4, 1rem);\n}\n\n.sizeLg {\n gap: var(--spacing-5, 1.25rem);\n}\n\n/* Breadcrumb slot sits above the title row */\n.breadcrumb {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n/* Main row: text block on the left, actions on the right */\n.row {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--spacing-6, 1.5rem);\n min-width: 0;\n}\n\n/* When a leading media/identity slot is present, the row forms an identity\n lockup: the media sits before the text stack a tight gap from it. Scoped to\n `data-has-leading` so the default (text|actions) row is untouched for\n consumers that don't pass `leading`. The expanding text block keeps actions\n pinned right via the inherited `justify-content: space-between`. */\n.row[data-has-leading] {\n align-items: center;\n gap: var(--page-header-leading-gap, var(--spacing-3, 0.75rem));\n}\n\n/* Leading media/identity slot: sits before the text block, vertically\n centered against the title/description stack, never shrinks. (Reverts the\n VI-539 top-align, which assumed a different slot — the blessed admin\n identity lockup centers the media. Superseded per the admin-editorial\n reconcile plan.) */\n.leading {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n/* Text block: eyebrow, title, description stacked */\n.text {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.sizeSm .text {\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeLg .text {\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Eyebrow: small uppercase label above the title */\n.eyebrow {\n font-size: var(--page-header-eyebrow-font-size, var(--font-size-xs, 0.75rem));\n font-weight: var(--page-header-eyebrow-font-weight, var(--font-weight-semibold, 600));\n letter-spacing: var(--page-header-eyebrow-letter-spacing, var(--letter-spacing-wide, 0.05em));\n text-transform: var(--page-header-eyebrow-text-transform, uppercase);\n color: var(--page-header-eyebrow-color, var(--text-tertiary, #6b7280));\n line-height: var(--line-height-tight, 1.25);\n}\n\n/* Title */\n.title {\n margin: 0;\n font-family: var(--font-family-heading, inherit);\n font-size: var(--page-header-title-font-size, var(--font-size-2xl, 1.5rem));\n font-weight: var(--page-header-title-font-weight, var(--font-weight-semibold, 600));\n line-height: var(--page-header-title-leading, var(--line-height-tight, 1.2));\n color: var(--page-header-title-color, var(--text-primary, #111827));\n letter-spacing: var(--page-header-title-letter-spacing, var(--letter-spacing-tight, -0.01em));\n}\n\n.sizeSm .title {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeLg .title {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n/* Title typography overrides (orthogonal to size variants).\n `data-title-size=\"marquee\"` pulls the title up to a hero scale via the\n `--page-header-title-size` custom property. Raw-string consumers pass an\n inline `--page-header-title-size` and rely on the same rule. */\n.title[data-title-size=\"marquee\"] {\n font-size: var(--page-header-title-size, 3.5rem);\n line-height: var(--page-header-title-leading, 1);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n/* `data-title-family=\"display\"` switches to the display family with a\n graceful fallback to the heading family when no display font is themed.\n VI-625: `--admin-ui-marquee-family` sits in the chain so the admin-ui\n pattern's marquee role drives the hero title when a theme binds it. */\n.title[data-title-family=\"display\"] {\n font-family: var(--page-header-title-family, var(--admin-ui-marquee-family, var(--font-display, var(--font-family-heading, inherit))));\n /* VI-687: theme-set display casing; inherit when unset. */\n text-transform: var(--font-display-text-transform, inherit);\n}\n\n/* Description */\n.description {\n margin: 0;\n font-size: var(--page-header-description-font-size, var(--font-size-sm, 0.875rem));\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--page-header-description-color, var(--text-secondary, #6b7280));\n max-width: 65ch;\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Actions slot: cluster on the right */\n.actions {\n display: flex;\n align-items: center;\n gap: var(--page-header-actions-gap, var(--spacing-2, 0.5rem));\n flex: 0 0 auto;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n\n/* Container query: stack to column below a small container width */\n@container page-header (max-width: 480px) {\n .row {\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n }\n\n .actions {\n width: 100%;\n justify-content: flex-start;\n }\n}\n"
2908
2909
  }
2909
2910
  ]
2910
2911
  },
@@ -2977,7 +2978,7 @@
2977
2978
  {
2978
2979
  "path": "components/ui/section-intro/section-intro.module.css",
2979
2980
  "type": "registry:ui",
2980
- "content": "/* SectionIntro — marketing eyebrow + display heading + optional lede\n *\n * The eyebrow color is intentionally driven through a CSS custom property\n * (`--section-intro-eyebrow-color`) so that consumers can override it at\n * any ancestor level and have it track a live-rewritten brand accent var\n * (for example a keyed `--color-acid` that swaps between artist palette entries).\n * Default fallback chain: --section-intro-eyebrow-color → --accent-default\n * → --interactive-primary-bg → #111827 (Tailwind Gray-900).\n *\n * Alignment is controlled by data-align on the root (set by the `align`\n * prop). Both \"left\" and \"center\" are defined here.\n */\n\n.root {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4, 1rem);\n width: 100%;\n min-width: 0;\n}\n\n/* ── Alignment variants ─────────────────────────────────────────────────── */\n\n.root[data-align=\"left\"] {\n align-items: flex-start;\n text-align: left;\n}\n\n.root[data-align=\"center\"] {\n align-items: center;\n text-align: center;\n}\n\n/* ── Eyebrow ────────────────────────────────────────────────────────────── */\n\n.eyebrow {\n margin: 0;\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: var(--font-size-xs, 0.6875rem);\n font-weight: var(--font-weight-medium, 500);\n letter-spacing: var(--letter-spacing-widest, 0.2em);\n text-transform: uppercase;\n line-height: var(--line-height-tight, 1.25);\n /*\n * Live-rewriteable eyebrow color. Override --section-intro-eyebrow-color\n * at any ancestor (for example on the <section> that contains this component) to\n * track a keyed brand accent that changes at runtime.\n */\n color: var(\n --section-intro-eyebrow-color,\n var(--accent-default, var(--interactive-primary-bg, #111827))\n );\n}\n\n/* ── Heading ────────────────────────────────────────────────────────────── */\n\n.heading {\n margin: 0;\n font-family: var(--font-display, var(--font-family-heading, inherit));\n font-size: clamp(2.5rem, 6vw, 4rem);\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--line-height-none, 1.05);\n letter-spacing: var(--letter-spacing-tight, -0.02em);\n color: var(--text-primary, #111827);\n text-wrap: balance;\n max-width: 18ch;\n}\n\n.root[data-align=\"center\"] .heading {\n max-width: 22ch;\n}\n\n/* ── Lede ───────────────────────────────────────────────────────────────── */\n\n.lede {\n margin: 0;\n font-size: var(--font-size-base, 1rem);\n line-height: var(--line-height-relaxed, 1.6);\n font-weight: var(--font-weight-light, 300);\n color: var(--text-secondary, #6b7280);\n max-width: 54ch;\n}\n\n.root[data-align=\"center\"] .lede {\n max-width: 60ch;\n}\n"
2981
+ "content": "/* SectionIntro — marketing eyebrow + display heading + optional lede\n *\n * The eyebrow color is intentionally driven through a CSS custom property\n * (`--section-intro-eyebrow-color`) so that consumers can override it at\n * any ancestor level and have it track a live-rewritten brand accent var\n * (for example a keyed `--color-acid` that swaps between artist palette entries).\n * Default fallback chain: --section-intro-eyebrow-color → --accent-default\n * → --interactive-primary-bg → #111827 (Tailwind Gray-900).\n *\n * Alignment is controlled by data-align on the root (set by the `align`\n * prop). Both \"left\" and \"center\" are defined here.\n */\n\n.root {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4, 1rem);\n width: 100%;\n min-width: 0;\n}\n\n/* ── Alignment variants ─────────────────────────────────────────────────── */\n\n.root[data-align=\"left\"] {\n align-items: flex-start;\n text-align: left;\n}\n\n.root[data-align=\"center\"] {\n align-items: center;\n text-align: center;\n}\n\n/* ── Eyebrow ────────────────────────────────────────────────────────────── */\n\n.eyebrow {\n margin: 0;\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: var(--font-size-xs, 0.6875rem);\n font-weight: var(--font-weight-medium, 500);\n letter-spacing: var(--letter-spacing-widest, 0.2em);\n text-transform: uppercase;\n line-height: var(--line-height-tight, 1.25);\n /*\n * Live-rewriteable eyebrow color. Override --section-intro-eyebrow-color\n * at any ancestor (for example on the <section> that contains this component) to\n * track a keyed brand accent that changes at runtime.\n */\n color: var(\n --section-intro-eyebrow-color,\n var(--accent-default, var(--interactive-primary-bg, #111827))\n );\n}\n\n/* ── Heading ────────────────────────────────────────────────────────────── */\n\n.heading {\n margin: 0;\n font-family: var(--font-display, var(--font-family-heading, inherit));\n /* VI-687: theme-set display casing; inherit when unset. */\n text-transform: var(--font-display-text-transform, inherit);\n font-size: clamp(2.5rem, 6vw, 4rem);\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--line-height-none, 1.05);\n letter-spacing: var(--letter-spacing-tight, -0.02em);\n color: var(--text-primary, #111827);\n text-wrap: balance;\n max-width: 18ch;\n}\n\n.root[data-align=\"center\"] .heading {\n max-width: 22ch;\n}\n\n/* ── Lede ───────────────────────────────────────────────────────────────── */\n\n.lede {\n margin: 0;\n font-size: var(--font-size-base, 1rem);\n line-height: var(--line-height-relaxed, 1.6);\n font-weight: var(--font-weight-light, 300);\n color: var(--text-secondary, #6b7280);\n max-width: 54ch;\n}\n\n.root[data-align=\"center\"] .lede {\n max-width: 60ch;\n}\n"
2981
2982
  }
2982
2983
  ]
2983
2984
  },
@@ -5393,7 +5394,7 @@
5393
5394
  {
5394
5395
  "path": "components/devtools/source-inspector/visor-component-names.generated.ts",
5395
5396
  "type": "registry:devtool",
5396
- "content": "// THIS FILE IS GENERATED BY scripts/generate-visor-component-names.ts.\n// Do not edit by hand. Re-run `npm run generate:component-names` after\n// adding, removing, or renaming a Visor component.\n//\n// Source of truth: registry/registry-{ui,blocks,deck,visual,devtools}.ts\n// Used by: components/devtools/source-inspector/* (VI-311)\n\nexport const VISOR_COMPONENT_NAMES: ReadonlySet<string> = new Set([\n \"AccessibilitySection\",\n \"AccessibilitySlide\",\n \"AccessibilitySpecimen\",\n \"Accordion\",\n \"AccordionContent\",\n \"AccordionItem\",\n \"AccordionTrigger\",\n \"ActionRow\",\n \"ActionRowList\",\n \"ActivityFeed\",\n \"ActivityFeedContext\",\n \"ActivityFeedItem\",\n \"ActivityFeedRoot\",\n \"AdminDashboard\",\n \"AdminDetail\",\n \"AdminDetailDrawer\",\n \"AdminListPage\",\n \"AdminListPageInner\",\n \"AdminSettingsPage\",\n \"AdminShell\",\n \"AdminTabbedEditor\",\n \"AdminWizard\",\n \"Alert\",\n \"AlertActions\",\n \"AlertDescription\",\n \"AlertTitle\",\n \"AmbientGlow\",\n \"Avatar\",\n \"AvatarFallback\",\n \"AvatarImage\",\n \"AvatarStack\",\n \"Badge\",\n \"Banner\",\n \"BannerAction\",\n \"BannerDescription\",\n \"BannerTitle\",\n \"BentoGrid\",\n \"BentoTile\",\n \"BentoTileBody\",\n \"BentoTileDescription\",\n \"BentoTileFigure\",\n \"BentoTileHeadline\",\n \"BentoTileMedia\",\n \"BentoTileMeta\",\n \"BentoTileTitle\",\n \"BottomNav\",\n \"Box\",\n \"Breadcrumb\",\n \"BreadcrumbEllipsis\",\n \"BreadcrumbItem\",\n \"BreadcrumbLink\",\n \"BreadcrumbList\",\n \"BreadcrumbPage\",\n \"BreadcrumbSeparator\",\n \"BrowserFrame\",\n \"BulkActionBar\",\n \"Button\",\n \"ButtonSpecimenSection\",\n \"ButtonSpecimenSlide\",\n \"Calendar\",\n \"Capture\",\n \"Card\",\n \"CardContent\",\n \"CardDescription\",\n \"CardFooter\",\n \"CardGrid\",\n \"CardHeader\",\n \"CardLift\",\n \"CardTitle\",\n \"Carousel\",\n \"CarouselContent\",\n \"CarouselContext\",\n \"CarouselGallery\",\n \"CarouselItem\",\n \"CarouselNext\",\n \"CarouselPrevious\",\n \"ChallengeCard\",\n \"ChallengeCardAction\",\n \"ChallengeCardActions\",\n \"ChallengeCardBody\",\n \"ChallengeCardGate\",\n \"ChallengeCardHeader\",\n \"ChartContainer\",\n \"ChartContext\",\n \"ChartLegend\",\n \"ChartLegendContent\",\n \"ChartStyle\",\n \"ChartTooltip\",\n \"ChartTooltipContent\",\n \"Checkbox\",\n \"CheckGroup\",\n \"CheckRow\",\n \"Chip\",\n \"ChipGroup\",\n \"ChipGroupContext\",\n \"ChipGroupItem\",\n \"ChoiceChip\",\n \"ChromeButton\",\n \"ClosingSlide\",\n \"CodeBlock\",\n \"Collapsible\",\n \"CollapsibleContent\",\n \"CollapsibleTrigger\",\n \"ColorBar\",\n \"ColorPaletteSection\",\n \"ColorPicker\",\n \"ColorPickerSurface\",\n \"ColorSwatch\",\n \"ColorSwatchGrid\",\n \"Column\",\n \"Combobox\",\n \"ComboboxContent\",\n \"ComboboxContext\",\n \"ComboboxEmpty\",\n \"ComboboxGroup\",\n \"ComboboxInput\",\n \"ComboboxItem\",\n \"ComboboxSeparator\",\n \"Command\",\n \"CommandDialog\",\n \"CommandEmpty\",\n \"CommandGroup\",\n \"CommandInput\",\n \"CommandItem\",\n \"CommandList\",\n \"CommandLoading\",\n \"CommandSeparator\",\n \"CommandShortcut\",\n \"ComponentShowcaseContent\",\n \"ComponentShowcaseSection\",\n \"ComponentShowcaseSlide\",\n \"Composer\",\n \"ComposerContext\",\n \"ComposerField\",\n \"ComposerSend\",\n \"ComposerSpacer\",\n \"ComposerToolbar\",\n \"ComposerToolButton\",\n \"ConceptSlide\",\n \"ConfigurationPanel\",\n \"ConfirmDialog\",\n \"ConflictBanner\",\n \"Container\",\n \"ContextMenu\",\n \"ContextMenuCheckboxItem\",\n \"ContextMenuContent\",\n \"ContextMenuGroup\",\n \"ContextMenuItem\",\n \"ContextMenuLabel\",\n \"ContextMenuPortal\",\n \"ContextMenuRadioGroup\",\n \"ContextMenuRadioItem\",\n \"ContextMenuSeparator\",\n \"ContextMenuShortcut\",\n \"ContextMenuSub\",\n \"ContextMenuSubContent\",\n \"ContextMenuSubTrigger\",\n \"ContextMenuTrigger\",\n \"Controls\",\n \"CtaSection\",\n \"DataTable\",\n \"DataTableInner\",\n \"DatePicker\",\n \"DateRangePicker\",\n \"DeckContext\",\n \"DeckFooter\",\n \"DeckLayout\",\n \"DeckProvider\",\n \"DeckRenderer\",\n \"DesignSystemDeck\",\n \"DesignSystemSpecimen\",\n \"Dialog\",\n \"DialogBack\",\n \"DialogClose\",\n \"DialogContent\",\n \"DialogContext\",\n \"DialogDescription\",\n \"DialogField\",\n \"DialogFieldControl\",\n \"DialogFieldLabel\",\n \"DialogFooter\",\n \"DialogForm\",\n \"DialogFormBody\",\n \"DialogFormClose\",\n \"DialogFormContent\",\n \"DialogFormDescription\",\n \"DialogFormFooter\",\n \"DialogFormHeader\",\n \"DialogFormTitle\",\n \"DialogFormTrigger\",\n \"DialogHeader\",\n \"DialogOverlay\",\n \"DialogPortal\",\n \"DialogScrim\",\n \"DialogTitle\",\n \"DialogTrigger\",\n \"DocFrame\",\n \"DocFrameBrand\",\n \"DocNav\",\n \"DocNavGroup\",\n \"DocNavPill\",\n \"DotNav\",\n \"DropdownMenu\",\n \"DropdownMenuCheckboxItem\",\n \"DropdownMenuContent\",\n \"DropdownMenuGroup\",\n \"DropdownMenuItem\",\n \"DropdownMenuLabel\",\n \"DropdownMenuPortal\",\n \"DropdownMenuRadioGroup\",\n \"DropdownMenuRadioItem\",\n \"DropdownMenuSeparator\",\n \"DropdownMenuShortcut\",\n \"DropdownMenuSub\",\n \"DropdownMenuSubContent\",\n \"DropdownMenuSubTrigger\",\n \"DropdownMenuTrigger\",\n \"EditableBlock\",\n \"ElevationCard\",\n \"ElevationSlide\",\n \"EmptyState\",\n \"ErrorPlacard\",\n \"ExportMenu\",\n \"FeaturesGrid\",\n \"FidelityMirror\",\n \"Field\",\n \"FieldDescription\",\n \"FieldError\",\n \"FieldLabel\",\n \"Fieldset\",\n \"FieldsetLegend\",\n \"FileUpload\",\n \"FilterBar\",\n \"FilterChip\",\n \"FontShowcase\",\n \"FontShowcaseGrid\",\n \"Footer\",\n \"FooterSection\",\n \"Form\",\n \"FormError\",\n \"FormErrorDescription\",\n \"FormErrorTitle\",\n \"FormField\",\n \"FormSpecimenSection\",\n \"FormSpecimenSlide\",\n \"FullscreenOverlay\",\n \"FullscreenOverlayContent\",\n \"FullscreenOverlayTrigger\",\n \"GrainOverlay\",\n \"Grid\",\n \"Header\",\n \"Heading\",\n \"HeroGlow\",\n \"HeroSection\",\n \"HeroSlide\",\n \"HoverCard\",\n \"HoverCardContent\",\n \"HoverCardTrigger\",\n \"IconGrid\",\n \"IconGridSection\",\n \"IconSizeRow\",\n \"IconsSlide\",\n \"Image\",\n \"InfographicBar\",\n \"Inline\",\n \"InlineEdit\",\n \"Input\",\n \"Kbd\",\n \"KeyValueList\",\n \"Label\",\n \"Landing\",\n \"Lightbox\",\n \"LightboxContent\",\n \"LightboxContext\",\n \"LightboxTrigger\",\n \"LoginForm\",\n \"Marquee\",\n \"MarqueeBandRenderer\",\n \"MatrixCell\",\n \"MatrixTable\",\n \"MatrixTableInner\",\n \"Menubar\",\n \"MenubarCheckboxItem\",\n \"MenubarContent\",\n \"MenubarGroup\",\n \"MenubarItem\",\n \"MenubarLabel\",\n \"MenubarMenu\",\n \"MenubarRadioGroup\",\n \"MenubarRadioItem\",\n \"MenubarSeparator\",\n \"MenubarShortcut\",\n \"MenubarSub\",\n \"MenubarSubContent\",\n \"MenubarSubTrigger\",\n \"MenubarTrigger\",\n \"MonthCalendar\",\n \"MotionDuration\",\n \"MotionDurationSection\",\n \"MotionEasing\",\n \"MotionEasingSection\",\n \"MotionSlide\",\n \"NameRoster\",\n \"NameRosterItem\",\n \"Navbar\",\n \"NavbarBrand\",\n \"NavbarContent\",\n \"NavbarItem\",\n \"NavbarLink\",\n \"NavbarToggle\",\n \"NumberInput\",\n \"OfflineBanner\",\n \"OpacityBar\",\n \"OpacitySlide\",\n \"OTPInput\",\n \"PageHeader\",\n \"Pagination\",\n \"PaginationContent\",\n \"PaginationEllipsis\",\n \"PaginationItem\",\n \"PaginationLink\",\n \"PaginationNext\",\n \"PaginationPrevious\",\n \"PasswordInput\",\n \"PhoneInput\",\n \"PlatformChip\",\n \"Popover\",\n \"PopoverAnchor\",\n \"PopoverContent\",\n \"PopoverFooter\",\n \"PopoverSelectionItem\",\n \"PopoverSelectionLabel\",\n \"PopoverSelectionList\",\n \"PopoverTrigger\",\n \"PositionPicker\",\n \"PricingSection\",\n \"ProfileMenu\",\n \"Progress\",\n \"PrototypeReview\",\n \"QuickActions\",\n \"RadioGroup\",\n \"RadioGroupItem\",\n \"RadioGroupVariantContext\",\n \"RadiusScale\",\n \"RadiusSection\",\n \"RadiusSlide\",\n \"RecordSection\",\n \"RightRailList\",\n \"SaveStatus\",\n \"ScoreIndicator\",\n \"ScrollArea\",\n \"ScrollBar\",\n \"SearchInput\",\n \"SectionHeader\",\n \"SectionIntro\",\n \"SectionNav\",\n \"SectionNavItem\",\n \"SegmentedControl\",\n \"SegmentedProgress\",\n \"Select\",\n \"SelectContent\",\n \"SelectGroup\",\n \"SelectionListContext\",\n \"SelectItem\",\n \"SelectLabel\",\n \"SelectScrollDownButton\",\n \"SelectScrollUpButton\",\n \"SelectSeparator\",\n \"SelectTrigger\",\n \"SelectValue\",\n \"SemanticColorGrid\",\n \"SemanticColorItem\",\n \"SemanticTokensSlide\",\n \"SensitivePanel\",\n \"Separator\",\n \"SessionTimeout\",\n \"ShadowSection\",\n \"Sheet\",\n \"SheetClose\",\n \"SheetContent\",\n \"SheetContext\",\n \"SheetDescription\",\n \"SheetFooter\",\n \"SheetHeader\",\n \"SheetOverlay\",\n \"SheetPortal\",\n \"SheetScrim\",\n \"SheetTitle\",\n \"SheetTrigger\",\n \"Sidebar\",\n \"SidebarContent\",\n \"SidebarContext\",\n \"SidebarFooter\",\n \"SidebarGroup\",\n \"SidebarGroupAction\",\n \"SidebarGroupContent\",\n \"SidebarGroupLabel\",\n \"SidebarHeader\",\n \"SidebarInset\",\n \"SidebarMenu\",\n \"SidebarMenuAction\",\n \"SidebarMenuBadge\",\n \"SidebarMenuButton\",\n \"SidebarMenuItem\",\n \"SidebarMenuSub\",\n \"SidebarMenuSubButton\",\n \"SidebarMenuSubItem\",\n \"SidebarProvider\",\n \"SidebarRail\",\n \"SidebarScopeContext\",\n \"SidebarSeparator\",\n \"SidebarTrigger\",\n \"Skeleton\",\n \"SkeletonDetail\",\n \"SkeletonList\",\n \"SkeletonTable\",\n \"Slide\",\n \"SlideHeader\",\n \"Slider\",\n \"SliderControl\",\n \"SlideThemeContext\",\n \"SlideThemeProvider\",\n \"SlowNetworkBar\",\n \"SourceInspector\",\n \"SourceInspectorContext\",\n \"SourceInspectorDevImpl\",\n \"SourceInspectorProvider\",\n \"SourceInspectorRunner\",\n \"SourceInspectorToggle\",\n \"SpacingScale\",\n \"SpacingSection\",\n \"SpacingSlide\",\n \"Sparkline\",\n \"SpecimenCard\",\n \"SpecimenCardFooter\",\n \"Sphere\",\n \"SpherePlayground\",\n \"Spinner\",\n \"SpinnerOrb\",\n \"Stack\",\n \"Stage\",\n \"StatCard\",\n \"StatHero\",\n \"StationSpectrum\",\n \"StatusBadge\",\n \"StatusColorsSlide\",\n \"StatusDot\",\n \"Stepper\",\n \"StepperContext\",\n \"StepperDescription\",\n \"StepperItem\",\n \"StepperSeparator\",\n \"StepperTitle\",\n \"StepperTrigger\",\n \"StepsSection\",\n \"StructuredPrompt\",\n \"StructuredPromptBody\",\n \"StructuredPromptHeader\",\n \"StructuredPromptHint\",\n \"StructuredPromptSlot\",\n \"SuccessLiveRegion\",\n \"SurfaceRow\",\n \"SurfaceScaleStack\",\n \"SurfaceSection\",\n \"Switch\",\n \"Table\",\n \"TableBody\",\n \"TableCaption\",\n \"TableCell\",\n \"TableFooter\",\n \"TableHead\",\n \"TableHeader\",\n \"TableRow\",\n \"Tabs\",\n \"TabsContent\",\n \"TabsList\",\n \"TabsTrigger\",\n \"TagInput\",\n \"TestimonialAttribution\",\n \"TestimonialSection\",\n \"Text\",\n \"Textarea\",\n \"ThemeArchitectureSlide\",\n \"ThemeColorsSlide\",\n \"ThemeSwitcher\",\n \"ThinkingOrb\",\n \"Timeline\",\n \"TimelineContent\",\n \"TimelineDescription\",\n \"TimelineIcon\",\n \"TimelineItem\",\n \"TimelineTimestamp\",\n \"TimelineTitle\",\n \"TimePicker\",\n \"TinInput\",\n \"TitleSlide\",\n \"ToastCard\",\n \"ToastCardStack\",\n \"Toaster\",\n \"TOCSlide\",\n \"ToggleButton\",\n \"ToggleDevImpl\",\n \"ToggleGroup\",\n \"ToggleGroupContext\",\n \"ToggleGroupItem\",\n \"Tooltip\",\n \"TooltipContent\",\n \"TooltipProvider\",\n \"TooltipTrigger\",\n \"TypeBodySlide\",\n \"TypeDisplaySlide\",\n \"TypeScaleStack\",\n \"TypeSpecimen\",\n \"TypographySection\",\n \"Vignette\",\n \"WorkspaceSwitcher\",\n])\n"
5397
+ "content": "// THIS FILE IS GENERATED BY scripts/generate-visor-component-names.ts.\n// Do not edit by hand. Re-run `npm run generate:component-names` after\n// adding, removing, or renaming a Visor component.\n//\n// Source of truth: registry/registry-{ui,blocks,deck,visual,devtools}.ts\n// Used by: components/devtools/source-inspector/* (VI-311)\n\nexport const VISOR_COMPONENT_NAMES: ReadonlySet<string> = new Set([\n \"AccessibilitySection\",\n \"AccessibilitySlide\",\n \"AccessibilitySpecimen\",\n \"Accordion\",\n \"AccordionContent\",\n \"AccordionItem\",\n \"AccordionTrigger\",\n \"ActionRow\",\n \"ActionRowList\",\n \"ActivityFeed\",\n \"ActivityFeedContext\",\n \"ActivityFeedItem\",\n \"ActivityFeedRoot\",\n \"AdminDashboard\",\n \"AdminDetail\",\n \"AdminDetailDrawer\",\n \"AdminListPage\",\n \"AdminListPageInner\",\n \"AdminSettingsPage\",\n \"AdminShell\",\n \"AdminTabbedEditor\",\n \"AdminWizard\",\n \"Alert\",\n \"AlertActions\",\n \"AlertDescription\",\n \"AlertLead\",\n \"AlertTitle\",\n \"AmbientGlow\",\n \"Avatar\",\n \"AvatarFallback\",\n \"AvatarImage\",\n \"AvatarStack\",\n \"Badge\",\n \"Banner\",\n \"BannerAction\",\n \"BannerDescription\",\n \"BannerTitle\",\n \"BentoGrid\",\n \"BentoTile\",\n \"BentoTileBody\",\n \"BentoTileDescription\",\n \"BentoTileFigure\",\n \"BentoTileHeadline\",\n \"BentoTileMedia\",\n \"BentoTileMeta\",\n \"BentoTileTitle\",\n \"BottomNav\",\n \"Box\",\n \"Breadcrumb\",\n \"BreadcrumbEllipsis\",\n \"BreadcrumbItem\",\n \"BreadcrumbLink\",\n \"BreadcrumbList\",\n \"BreadcrumbPage\",\n \"BreadcrumbSeparator\",\n \"BrowserFrame\",\n \"BulkActionBar\",\n \"Button\",\n \"ButtonSpecimenSection\",\n \"ButtonSpecimenSlide\",\n \"Calendar\",\n \"Capture\",\n \"Card\",\n \"CardContent\",\n \"CardDescription\",\n \"CardFooter\",\n \"CardGrid\",\n \"CardHeader\",\n \"CardLift\",\n \"CardTitle\",\n \"Carousel\",\n \"CarouselContent\",\n \"CarouselContext\",\n \"CarouselGallery\",\n \"CarouselItem\",\n \"CarouselNext\",\n \"CarouselPrevious\",\n \"ChallengeCard\",\n \"ChallengeCardAction\",\n \"ChallengeCardActions\",\n \"ChallengeCardBody\",\n \"ChallengeCardGate\",\n \"ChallengeCardHeader\",\n \"ChartContainer\",\n \"ChartContext\",\n \"ChartLegend\",\n \"ChartLegendContent\",\n \"ChartStyle\",\n \"ChartTooltip\",\n \"ChartTooltipContent\",\n \"Checkbox\",\n \"CheckGroup\",\n \"CheckRow\",\n \"Chip\",\n \"ChipGroup\",\n \"ChipGroupContext\",\n \"ChipGroupItem\",\n \"ChoiceChip\",\n \"ChromeButton\",\n \"ClosingSlide\",\n \"CodeBlock\",\n \"Collapsible\",\n \"CollapsibleContent\",\n \"CollapsibleTrigger\",\n \"ColorBar\",\n \"ColorPaletteSection\",\n \"ColorPicker\",\n \"ColorPickerSurface\",\n \"ColorSwatch\",\n \"ColorSwatchGrid\",\n \"Column\",\n \"Combobox\",\n \"ComboboxContent\",\n \"ComboboxContext\",\n \"ComboboxEmpty\",\n \"ComboboxGroup\",\n \"ComboboxInput\",\n \"ComboboxItem\",\n \"ComboboxSeparator\",\n \"Command\",\n \"CommandDialog\",\n \"CommandEmpty\",\n \"CommandGroup\",\n \"CommandInput\",\n \"CommandItem\",\n \"CommandList\",\n \"CommandLoading\",\n \"CommandSeparator\",\n \"CommandShortcut\",\n \"ComponentShowcaseContent\",\n \"ComponentShowcaseSection\",\n \"ComponentShowcaseSlide\",\n \"Composer\",\n \"ComposerContext\",\n \"ComposerField\",\n \"ComposerSend\",\n \"ComposerSpacer\",\n \"ComposerToolbar\",\n \"ComposerToolButton\",\n \"ConceptSlide\",\n \"ConfigurationPanel\",\n \"ConfirmDialog\",\n \"ConflictBanner\",\n \"Container\",\n \"ContextMenu\",\n \"ContextMenuCheckboxItem\",\n \"ContextMenuContent\",\n \"ContextMenuGroup\",\n \"ContextMenuItem\",\n \"ContextMenuLabel\",\n \"ContextMenuPortal\",\n \"ContextMenuRadioGroup\",\n \"ContextMenuRadioItem\",\n \"ContextMenuSeparator\",\n \"ContextMenuShortcut\",\n \"ContextMenuSub\",\n \"ContextMenuSubContent\",\n \"ContextMenuSubTrigger\",\n \"ContextMenuTrigger\",\n \"Controls\",\n \"CtaSection\",\n \"DataTable\",\n \"DataTableInner\",\n \"DatePicker\",\n \"DateRangePicker\",\n \"DeckContext\",\n \"DeckFooter\",\n \"DeckLayout\",\n \"DeckProvider\",\n \"DeckRenderer\",\n \"DesignSystemDeck\",\n \"DesignSystemSpecimen\",\n \"Dialog\",\n \"DialogBack\",\n \"DialogClose\",\n \"DialogContent\",\n \"DialogContext\",\n \"DialogDescription\",\n \"DialogField\",\n \"DialogFieldControl\",\n \"DialogFieldLabel\",\n \"DialogFooter\",\n \"DialogForm\",\n \"DialogFormBody\",\n \"DialogFormClose\",\n \"DialogFormContent\",\n \"DialogFormDescription\",\n \"DialogFormFooter\",\n \"DialogFormHeader\",\n \"DialogFormTitle\",\n \"DialogFormTrigger\",\n \"DialogHeader\",\n \"DialogOverlay\",\n \"DialogPortal\",\n \"DialogScrim\",\n \"DialogTitle\",\n \"DialogTrigger\",\n \"DocFrame\",\n \"DocFrameBrand\",\n \"DocNav\",\n \"DocNavGroup\",\n \"DocNavPill\",\n \"DotNav\",\n \"DropdownMenu\",\n \"DropdownMenuCheckboxItem\",\n \"DropdownMenuContent\",\n \"DropdownMenuGroup\",\n \"DropdownMenuItem\",\n \"DropdownMenuLabel\",\n \"DropdownMenuPortal\",\n \"DropdownMenuRadioGroup\",\n \"DropdownMenuRadioItem\",\n \"DropdownMenuSeparator\",\n \"DropdownMenuShortcut\",\n \"DropdownMenuSub\",\n \"DropdownMenuSubContent\",\n \"DropdownMenuSubTrigger\",\n \"DropdownMenuTrigger\",\n \"EditableBlock\",\n \"ElevationCard\",\n \"ElevationSlide\",\n \"EmptyState\",\n \"ErrorPlacard\",\n \"ExportMenu\",\n \"FeaturesGrid\",\n \"FidelityMirror\",\n \"Field\",\n \"FieldDescription\",\n \"FieldError\",\n \"FieldLabel\",\n \"Fieldset\",\n \"FieldsetLegend\",\n \"FileUpload\",\n \"FilterBar\",\n \"FilterChip\",\n \"FontShowcase\",\n \"FontShowcaseGrid\",\n \"Footer\",\n \"FooterSection\",\n \"Form\",\n \"FormError\",\n \"FormErrorDescription\",\n \"FormErrorTitle\",\n \"FormField\",\n \"FormSpecimenSection\",\n \"FormSpecimenSlide\",\n \"FullscreenOverlay\",\n \"FullscreenOverlayContent\",\n \"FullscreenOverlayTrigger\",\n \"GrainOverlay\",\n \"Grid\",\n \"Header\",\n \"Heading\",\n \"HeroGlow\",\n \"HeroSection\",\n \"HeroSlide\",\n \"HoverCard\",\n \"HoverCardContent\",\n \"HoverCardTrigger\",\n \"IconGrid\",\n \"IconGridSection\",\n \"IconSizeRow\",\n \"IconsSlide\",\n \"Image\",\n \"InfographicBar\",\n \"Inline\",\n \"InlineEdit\",\n \"Input\",\n \"Kbd\",\n \"KeyValueList\",\n \"Label\",\n \"Landing\",\n \"Lightbox\",\n \"LightboxContent\",\n \"LightboxContext\",\n \"LightboxTrigger\",\n \"LoginForm\",\n \"Marquee\",\n \"MarqueeBandRenderer\",\n \"MatrixCell\",\n \"MatrixTable\",\n \"MatrixTableInner\",\n \"Menubar\",\n \"MenubarCheckboxItem\",\n \"MenubarContent\",\n \"MenubarGroup\",\n \"MenubarItem\",\n \"MenubarLabel\",\n \"MenubarMenu\",\n \"MenubarRadioGroup\",\n \"MenubarRadioItem\",\n \"MenubarSeparator\",\n \"MenubarShortcut\",\n \"MenubarSub\",\n \"MenubarSubContent\",\n \"MenubarSubTrigger\",\n \"MenubarTrigger\",\n \"MonthCalendar\",\n \"MotionDuration\",\n \"MotionDurationSection\",\n \"MotionEasing\",\n \"MotionEasingSection\",\n \"MotionSlide\",\n \"NameRoster\",\n \"NameRosterItem\",\n \"Navbar\",\n \"NavbarBrand\",\n \"NavbarContent\",\n \"NavbarItem\",\n \"NavbarLink\",\n \"NavbarToggle\",\n \"NumberInput\",\n \"OfflineBanner\",\n \"OpacityBar\",\n \"OpacitySlide\",\n \"OTPInput\",\n \"PageHeader\",\n \"Pagination\",\n \"PaginationContent\",\n \"PaginationEllipsis\",\n \"PaginationItem\",\n \"PaginationLink\",\n \"PaginationNext\",\n \"PaginationPrevious\",\n \"PasswordInput\",\n \"PhoneInput\",\n \"PlatformChip\",\n \"Popover\",\n \"PopoverAnchor\",\n \"PopoverContent\",\n \"PopoverFooter\",\n \"PopoverSelectionItem\",\n \"PopoverSelectionLabel\",\n \"PopoverSelectionList\",\n \"PopoverTrigger\",\n \"PositionPicker\",\n \"PricingSection\",\n \"ProfileMenu\",\n \"Progress\",\n \"PrototypeReview\",\n \"QuickActions\",\n \"RadioGroup\",\n \"RadioGroupItem\",\n \"RadioGroupVariantContext\",\n \"RadiusScale\",\n \"RadiusSection\",\n \"RadiusSlide\",\n \"RecordSection\",\n \"RightRailList\",\n \"SaveStatus\",\n \"ScoreIndicator\",\n \"ScrollArea\",\n \"ScrollBar\",\n \"SearchInput\",\n \"SectionHeader\",\n \"SectionIntro\",\n \"SectionNav\",\n \"SectionNavItem\",\n \"SegmentedControl\",\n \"SegmentedProgress\",\n \"Select\",\n \"SelectContent\",\n \"SelectGroup\",\n \"SelectionListContext\",\n \"SelectItem\",\n \"SelectLabel\",\n \"SelectScrollDownButton\",\n \"SelectScrollUpButton\",\n \"SelectSeparator\",\n \"SelectTrigger\",\n \"SelectValue\",\n \"SemanticColorGrid\",\n \"SemanticColorItem\",\n \"SemanticTokensSlide\",\n \"SensitivePanel\",\n \"Separator\",\n \"SessionTimeout\",\n \"ShadowSection\",\n \"Sheet\",\n \"SheetClose\",\n \"SheetContent\",\n \"SheetContext\",\n \"SheetDescription\",\n \"SheetFooter\",\n \"SheetHeader\",\n \"SheetOverlay\",\n \"SheetPortal\",\n \"SheetScrim\",\n \"SheetTitle\",\n \"SheetTrigger\",\n \"Sidebar\",\n \"SidebarContent\",\n \"SidebarContext\",\n \"SidebarFooter\",\n \"SidebarGroup\",\n \"SidebarGroupAction\",\n \"SidebarGroupContent\",\n \"SidebarGroupLabel\",\n \"SidebarHeader\",\n \"SidebarInset\",\n \"SidebarMenu\",\n \"SidebarMenuAction\",\n \"SidebarMenuBadge\",\n \"SidebarMenuButton\",\n \"SidebarMenuItem\",\n \"SidebarMenuSub\",\n \"SidebarMenuSubButton\",\n \"SidebarMenuSubItem\",\n \"SidebarProvider\",\n \"SidebarRail\",\n \"SidebarScopeContext\",\n \"SidebarSeparator\",\n \"SidebarTrigger\",\n \"Skeleton\",\n \"SkeletonDetail\",\n \"SkeletonList\",\n \"SkeletonTable\",\n \"Slide\",\n \"SlideHeader\",\n \"Slider\",\n \"SliderControl\",\n \"SlideThemeContext\",\n \"SlideThemeProvider\",\n \"SlowNetworkBar\",\n \"SourceInspector\",\n \"SourceInspectorContext\",\n \"SourceInspectorDevImpl\",\n \"SourceInspectorProvider\",\n \"SourceInspectorRunner\",\n \"SourceInspectorToggle\",\n \"SpacingScale\",\n \"SpacingSection\",\n \"SpacingSlide\",\n \"Sparkline\",\n \"SpecimenCard\",\n \"SpecimenCardFooter\",\n \"Sphere\",\n \"SpherePlayground\",\n \"Spinner\",\n \"SpinnerOrb\",\n \"Stack\",\n \"Stage\",\n \"StatCard\",\n \"StatHero\",\n \"StationSpectrum\",\n \"StatusBadge\",\n \"StatusColorsSlide\",\n \"StatusDot\",\n \"Stepper\",\n \"StepperContext\",\n \"StepperDescription\",\n \"StepperItem\",\n \"StepperSeparator\",\n \"StepperTitle\",\n \"StepperTrigger\",\n \"StepsSection\",\n \"StructuredPrompt\",\n \"StructuredPromptBody\",\n \"StructuredPromptHeader\",\n \"StructuredPromptHint\",\n \"StructuredPromptSlot\",\n \"SuccessLiveRegion\",\n \"SurfaceRow\",\n \"SurfaceScaleStack\",\n \"SurfaceSection\",\n \"Switch\",\n \"Table\",\n \"TableBody\",\n \"TableCaption\",\n \"TableCell\",\n \"TableFooter\",\n \"TableHead\",\n \"TableHeader\",\n \"TableRow\",\n \"Tabs\",\n \"TabsContent\",\n \"TabsList\",\n \"TabsTrigger\",\n \"TagInput\",\n \"TestimonialAttribution\",\n \"TestimonialSection\",\n \"Text\",\n \"Textarea\",\n \"ThemeArchitectureSlide\",\n \"ThemeColorsSlide\",\n \"ThemeSwitcher\",\n \"ThinkingOrb\",\n \"Timeline\",\n \"TimelineContent\",\n \"TimelineDescription\",\n \"TimelineIcon\",\n \"TimelineItem\",\n \"TimelineTimestamp\",\n \"TimelineTitle\",\n \"TimePicker\",\n \"TinInput\",\n \"TitleSlide\",\n \"ToastCard\",\n \"ToastCardStack\",\n \"Toaster\",\n \"TOCSlide\",\n \"ToggleButton\",\n \"ToggleDevImpl\",\n \"ToggleGroup\",\n \"ToggleGroupContext\",\n \"ToggleGroupItem\",\n \"Tooltip\",\n \"TooltipContent\",\n \"TooltipProvider\",\n \"TooltipTrigger\",\n \"TypeBodySlide\",\n \"TypeDisplaySlide\",\n \"TypeScaleStack\",\n \"TypeSpecimen\",\n \"TypographySection\",\n \"Vignette\",\n \"WorkspaceSwitcher\",\n])\n"
5397
5398
  }
5398
5399
  ]
5399
5400
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.4.0",
3
- "generated_at": "2026-10-04T07:08:34.766Z",
3
+ "generated_at": "2026-10-04T23:46:57.980Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "category": "specimen",
@@ -318,12 +318,14 @@
318
318
  "Displaying a persistent warning or error within page content",
319
319
  "Showing success confirmation that should remain visible",
320
320
  "Informational callouts in documentation or onboarding flows",
321
- "Form-level error summaries above or below a form"
321
+ "Form-level error summaries above or below a form",
322
+ "A refusal or notice directly under a control, or a consequence in a dialog (\"This can't be undone\") — use appearance=\"soft\""
322
323
  ],
323
324
  "when_not_to_use": [
324
325
  "Ephemeral notifications that auto-dismiss (use Toast)",
325
326
  "Full-width announcements at the top of a page (use Banner)",
326
- "Inline field-level validation messages (use Field error state)",
327
+ "A field's own validation hint tied to one input via aria-describedby (use FieldError); a refusal of the whole submit under the control uses appearance=\"soft\"",
328
+ "A failed load inside a card or panel that needs a retry (use ErrorPlacard)",
327
329
  "Messages requiring user action to dismiss (use Dialog)"
328
330
  ],
329
331
  "variants": {
@@ -333,6 +335,10 @@
333
335
  "success",
334
336
  "warning",
335
337
  "info"
338
+ ],
339
+ "appearance": [
340
+ "default",
341
+ "soft"
336
342
  ]
337
343
  },
338
344
  "props": [
@@ -340,6 +346,18 @@
340
346
  "name": "variant",
341
347
  "type": "'default' | 'destructive' | 'success' | 'warning' | 'info'",
342
348
  "default": "default"
349
+ },
350
+ {
351
+ "name": "appearance",
352
+ "type": "'default' | 'soft'",
353
+ "default": "default",
354
+ "description": "soft draws a tinted fill with no edge or shadow, a tone icon and the body in secondary ink. Under soft, info falls back to the neutral default note, and role defaults to alert for destructive and status for the rest (an explicit role still wins).\n"
355
+ },
356
+ {
357
+ "name": "icon",
358
+ "type": "ReactElement<IconProps>",
359
+ "required": false,
360
+ "description": "soft only. Replaces the tone's default icon (WarningOctagon, Warning, CheckCircle, Info). Drawn at --alert-icon-size (32px; 24px for the neutral note) and hidden from assistive tech.\n"
343
361
  }
344
362
  ],
345
363
  "sub_components": [
@@ -351,21 +369,35 @@
351
369
  "name": "AlertDescription",
352
370
  "description": "Secondary descriptive text below the title."
353
371
  },
372
+ {
373
+ "name": "AlertLead",
374
+ "description": "Bold lead sentence in primary ink, set inline at the start of AlertDescription."
375
+ },
354
376
  {
355
377
  "name": "AlertActions",
356
378
  "description": "Right-aligned, gap-aware row for inline action buttons (e.g. retry/dismiss)."
357
379
  }
358
380
  ],
359
381
  "dependencies": [
360
- "class-variance-authority"
382
+ "class-variance-authority",
383
+ "@phosphor-icons/react"
361
384
  ],
362
385
  "tokens_used": [
386
+ "--alert-font-size",
387
+ "--alert-gap",
388
+ "--alert-icon-size",
389
+ "--alert-icon-size-note",
390
+ "--alert-lead-font-weight",
391
+ "--alert-radius",
392
+ "--alert-soft-padding",
363
393
  "--border-error",
364
394
  "--border-info",
365
395
  "--border-success",
366
396
  "--border-warning",
367
397
  "--font-size-sm",
398
+ "--font-weight-bold",
368
399
  "--font-weight-semibold",
400
+ "--line-height-normal",
369
401
  "--radius-lg",
370
402
  "--shadow-sm",
371
403
  "--spacing-1",
@@ -373,9 +405,12 @@
373
405
  "--spacing-3",
374
406
  "--spacing-4",
375
407
  "--surface-card",
408
+ "--surface-error-soft",
376
409
  "--surface-error-subtle",
377
410
  "--surface-info-subtle",
411
+ "--surface-success-soft",
378
412
  "--surface-success-subtle",
413
+ "--surface-warning-soft",
379
414
  "--surface-warning-subtle",
380
415
  "--text-error",
381
416
  "--text-info",
@@ -384,7 +419,7 @@
384
419
  "--text-success",
385
420
  "--text-warning"
386
421
  ],
387
- "example": "<Alert variant=\"destructive\">\n <AlertTitle>Error</AlertTitle>\n <AlertDescription>Your session has expired. Please log in again.</AlertDescription>\n</Alert>\n"
422
+ "example": "<Alert variant=\"destructive\">\n <AlertTitle>Error</AlertTitle>\n <AlertDescription>Your session has expired. Please log in again.</AlertDescription>\n</Alert>\n\n<Alert appearance=\"soft\" variant=\"destructive\">\n <AlertDescription>\n <AlertLead>That email isn't on the list.</AlertLead> Ask your host for an invite, or try another address.\n </AlertDescription>\n</Alert>\n"
388
423
  },
389
424
  "avatar": {
390
425
  "category": "data-display",
@@ -5314,6 +5349,7 @@
5314
5349
  ],
5315
5350
  "tokens_used": [
5316
5351
  "--font-heading",
5352
+ "--font-heading-text-transform",
5317
5353
  "--font-size-2xl",
5318
5354
  "--font-size-4xl",
5319
5355
  "--font-size-base",
@@ -6979,6 +7015,7 @@
6979
7015
  "tokens_used": [
6980
7016
  "--admin-ui-marquee-family",
6981
7017
  "--font-display",
7018
+ "--font-display-text-transform",
6982
7019
  "--font-family-heading",
6983
7020
  "--font-size-2xl",
6984
7021
  "--font-size-3xl",
@@ -8070,6 +8107,7 @@
8070
8107
  "tokens_used": [
8071
8108
  "--accent-default",
8072
8109
  "--font-display",
8110
+ "--font-display-text-transform",
8073
8111
  "--font-family-heading",
8074
8112
  "--font-mono",
8075
8113
  "--font-size-base",
@@ -8368,6 +8406,12 @@
8368
8406
  "name": "placeholder",
8369
8407
  "type": "string",
8370
8408
  "description": "Shown via SelectValue when no value is selected"
8409
+ },
8410
+ {
8411
+ "name": "container",
8412
+ "type": "HTMLElement | null",
8413
+ "default": "document.body",
8414
+ "description": "SelectContent only. Element the menu portals into. Pass the root of a class-scoped theme so the menu reads that theme's tokens instead of the page's.\n"
8371
8415
  }
8372
8416
  ],
8373
8417
  "sub_components": [
@@ -8381,7 +8425,7 @@
8381
8425
  },
8382
8426
  {
8383
8427
  "name": "SelectContent",
8384
- "description": "The dropdown container with scroll buttons and portal positioning."
8428
+ "description": "The dropdown container with scroll buttons and portal positioning (document.body unless `container` is set)."
8385
8429
  },
8386
8430
  {
8387
8431
  "name": "SelectItem",
@@ -10300,9 +10344,13 @@
10300
10344
  "--shadow-sm",
10301
10345
  "--surface-page",
10302
10346
  "--switch-edge-color",
10347
+ "--switch-knob-bg",
10348
+ "--switch-knob-bg-checked",
10303
10349
  "--switch-knob-inset",
10304
10350
  "--switch-track-bg",
10305
10351
  "--switch-track-height",
10352
+ "--switch-track-hover-bg",
10353
+ "--switch-track-hover-bg-checked",
10306
10354
  "--switch-track-width"
10307
10355
  ],
10308
10356
  "example": "<div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <Switch id=\"notifications\" />\n <Label htmlFor=\"notifications\">Enable notifications</Label>\n</div>\n"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loworbitstudio/visor",
3
- "version": "1.35.0",
3
+ "version": "1.37.0",
4
4
  "description": "CLI for the Visor design system — add components, hooks, and utilities to your project.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@aws-sdk/client-s3": "^3.1021.0",
48
48
  "@babel/parser": "^7.26.0",
49
- "@loworbitstudio/visor-theme-engine": "^0.28.0",
49
+ "@loworbitstudio/visor-theme-engine": "^0.30.0",
50
50
  "commander": "^13.1.0",
51
51
  "diff": "^8.0.4",
52
52
  "picocolors": "^1.1.1",