@loworbitstudio/visor 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.json +13 -1
- package/dist/registry.json +52 -2
- package/dist/visor-manifest.json +171 -2
- package/package.json +1 -1
package/dist/CHANGELOG.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.4.0",
|
|
3
|
-
"generated_at": "2026-06-
|
|
3
|
+
"generated_at": "2026-06-12T05:08:25.423Z",
|
|
4
4
|
"components": {
|
|
5
5
|
"accessibility-specimen": {
|
|
6
6
|
"changeType": "current",
|
|
@@ -230,6 +230,12 @@
|
|
|
230
230
|
"breakingChange": false,
|
|
231
231
|
"migrationNote": null
|
|
232
232
|
},
|
|
233
|
+
"error-placard": {
|
|
234
|
+
"changeType": "current",
|
|
235
|
+
"files": [],
|
|
236
|
+
"breakingChange": false,
|
|
237
|
+
"migrationNote": null
|
|
238
|
+
},
|
|
233
239
|
"field": {
|
|
234
240
|
"changeType": "current",
|
|
235
241
|
"files": [],
|
|
@@ -674,6 +680,12 @@
|
|
|
674
680
|
"breakingChange": false,
|
|
675
681
|
"migrationNote": null
|
|
676
682
|
},
|
|
683
|
+
"toast-card": {
|
|
684
|
+
"changeType": "current",
|
|
685
|
+
"files": [],
|
|
686
|
+
"breakingChange": false,
|
|
687
|
+
"migrationNote": null
|
|
688
|
+
},
|
|
677
689
|
"toggle-group": {
|
|
678
690
|
"changeType": "current",
|
|
679
691
|
"files": [],
|
package/dist/registry.json
CHANGED
|
@@ -468,7 +468,7 @@
|
|
|
468
468
|
{
|
|
469
469
|
"path": "components/ui/avatar/avatar.tsx",
|
|
470
470
|
"type": "registry:ui",
|
|
471
|
-
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./avatar.module.css\"\nimport stackStyles from \"./avatar-stack.module.css\"\n\nexport interface AvatarProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> {\n size?: \"sm\" | \"default\" | \"lg\"\n}\n\nconst Avatar = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Root>,\n AvatarProps\n>(({ className, size = \"default\", ...props }, ref) => {\n return (\n <AvatarPrimitive.Root\n ref={ref}\n data-slot=\"avatar\"\n data-size={size}\n className={cn(\n styles.avatar,\n size === \"sm\" && styles.avatarSm,\n size === \"lg\" && styles.avatarLg,\n className\n )}\n {...props}\n />\n )\n})\nAvatar.displayName = \"Avatar\"\n\nconst AvatarImage = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Image>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Image\n ref={ref}\n data-slot=\"avatar-image\"\n className={cn(styles.avatarImage, className)}\n {...props}\n />\n )\n})\nAvatarImage.displayName = \"AvatarImage\"\n\nconst AvatarFallback = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Fallback>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Fallback\n ref={ref}\n data-slot=\"avatar-fallback\"\n className={cn(styles.avatarFallback, className)}\n {...props}\n />\n )\n})\nAvatarFallback.displayName = \"AvatarFallback\"\n\nexport interface AvatarStackProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"role\" | \"aria-label\"> {\n /**\n *
|
|
471
|
+
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./avatar.module.css\"\nimport stackStyles from \"./avatar-stack.module.css\"\n\n// ─── AvatarStack item type ───────────────────────────────────────────────────\n\n/**\n * Rich item form for AvatarStack. A plain string or `undefined` is also\n * accepted (backward-compatible shorthand for an image-src-only item).\n */\nexport interface AvatarStackItem {\n /** Initials rendered as fallback (and primary) content — e.g. \"AR\". */\n initials?: React.ReactNode\n /** Optional image source. When present, the image covers the disc. */\n src?: string\n /** Accessible label for the disc — e.g. the member name. */\n alt?: string\n /**\n * Per-avatar style escape hatch — carries the gradient `background` + text\n * `color` for the editorial gradient discs (see `getMemberAvatarStyle`).\n */\n style?: React.CSSProperties\n}\n\nexport interface AvatarProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> {\n size?: \"sm\" | \"default\" | \"lg\"\n}\n\nconst Avatar = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Root>,\n AvatarProps\n>(({ className, size = \"default\", ...props }, ref) => {\n return (\n <AvatarPrimitive.Root\n ref={ref}\n data-slot=\"avatar\"\n data-size={size}\n className={cn(\n styles.avatar,\n size === \"sm\" && styles.avatarSm,\n size === \"lg\" && styles.avatarLg,\n className\n )}\n {...props}\n />\n )\n})\nAvatar.displayName = \"Avatar\"\n\nconst AvatarImage = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Image>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Image\n ref={ref}\n data-slot=\"avatar-image\"\n className={cn(styles.avatarImage, className)}\n {...props}\n />\n )\n})\nAvatarImage.displayName = \"AvatarImage\"\n\nconst AvatarFallback = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Fallback>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Fallback\n ref={ref}\n data-slot=\"avatar-fallback\"\n className={cn(styles.avatarFallback, className)}\n {...props}\n />\n )\n})\nAvatarFallback.displayName = \"AvatarFallback\"\n\nexport interface AvatarStackProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"role\" | \"aria-label\"> {\n /**\n * Avatars to render, in display order. Each entry is either:\n * - A plain image URL string (backward-compatible shorthand)\n * - `undefined` — renders the `·` fallback disc, useful for server-truncated lists\n * - An `AvatarStackItem` object with `initials`, `src`, `alt`, and/or `style`\n */\n avatars: (string | undefined | AvatarStackItem)[]\n /**\n * Total member count. May exceed `avatars.length` when the caller has\n * server-truncated the avatar URLs and only knows the count. The overflow\n * indicator is computed against this value.\n */\n total: number\n /**\n * Maximum number of avatar slots rendered before the `+N` overflow\n * indicator. Defaults to `6`.\n */\n max?: number\n /**\n * Explicit \"+N\" override. When provided, this value is used verbatim\n * instead of the value derived from `total - visible.length`. Useful\n * when the caller has a pre-computed overflow count.\n */\n overflowCount?: number\n /** Avatar size. Defaults to `\"sm\"`. */\n size?: \"sm\" | \"default\" | \"lg\"\n /**\n * Accessible label override. Defaults to ``${total} members``.\n */\n label?: string\n}\n\n/** Normalize a raw avatar entry to an `AvatarStackItem`. */\nfunction toItem(entry: string | undefined | AvatarStackItem): AvatarStackItem {\n if (entry === undefined || entry === null) return {}\n if (typeof entry === \"string\") return { src: entry, alt: \"\" }\n return entry\n}\n\nconst AvatarStack = React.forwardRef<HTMLDivElement, AvatarStackProps>(\n function AvatarStack(\n {\n avatars,\n total,\n max = 6,\n overflowCount,\n size = \"sm\",\n label,\n className,\n ...rest\n },\n ref,\n ) {\n const visible = avatars.slice(0, max)\n const derivedOverflow = Math.max(0, total - visible.length)\n const overflow = overflowCount ?? derivedOverflow\n const ariaLabel = label ?? `${total} members`\n\n return (\n <div\n ref={ref}\n role=\"img\"\n aria-label={ariaLabel}\n data-slot=\"avatar-stack\"\n data-size={size}\n className={cn(stackStyles.root, className)}\n {...rest}\n >\n {visible.map((entry, index) => {\n const item = toItem(entry)\n return (\n <Avatar\n key={index}\n size={size}\n className={stackStyles.avatar}\n style={item.style}\n data-stack-item=\"\"\n >\n {item.src ? (\n <AvatarImage src={item.src} alt={item.alt ?? \"\"} />\n ) : item.initials != null ? (\n <AvatarFallback className={stackStyles.initialsDisc}>\n {item.initials}\n </AvatarFallback>\n ) : (\n <AvatarFallback>·</AvatarFallback>\n )}\n </Avatar>\n )\n })}\n {overflow > 0 ? (\n <Avatar\n size={size}\n className={stackStyles.avatar}\n data-stack-overflow=\"\"\n >\n <AvatarFallback>+{overflow}</AvatarFallback>\n </Avatar>\n ) : null}\n </div>\n )\n },\n)\n\nAvatarStack.displayName = \"AvatarStack\"\n\nexport { Avatar, AvatarImage, AvatarFallback, AvatarStack }\n"
|
|
472
472
|
},
|
|
473
473
|
{
|
|
474
474
|
"path": "components/ui/avatar/avatar.module.css",
|
|
@@ -478,7 +478,7 @@
|
|
|
478
478
|
{
|
|
479
479
|
"path": "components/ui/avatar/avatar-stack.module.css",
|
|
480
480
|
"type": "registry:ui",
|
|
481
|
-
"content": "/* AvatarStack root */\n.root {\n display: inline-flex;\n flex-direction: row;\n align-items: center;\n isolation: isolate;\n}\n\n/* Each avatar gets a ring matching the parent surface so adjacent avatars\n read as separate circles. Avatar's own `overflow: hidden` clips inner\n shadows, so the ring is projected outward via box-shadow. */\n.avatar {\n box-shadow: 0 0 0 var(--stroke-width-medium, 2px) var(--surface-default, #ffffff);\n}\n\n/* Overlap every avatar except the first. Later siblings stack on top of\n earlier ones because `isolation: isolate` establishes a single stacking\n context and DOM order wins inside it. */\n.avatar:not(:first-child) {\n margin-inline-start: calc(-1 * var(--spacing-2, 0.5rem));\n}\n"
|
|
481
|
+
"content": "/* AvatarStack root */\n.root {\n display: inline-flex;\n flex-direction: row;\n align-items: center;\n isolation: isolate;\n}\n\n/* Each avatar gets a ring matching the parent surface so adjacent avatars\n read as separate circles. Avatar's own `overflow: hidden` clips inner\n shadows, so the ring is projected outward via box-shadow. */\n.avatar {\n box-shadow: 0 0 0 var(--stroke-width-medium, 2px) var(--surface-default, #ffffff);\n}\n\n/* Overlap every avatar except the first. Later siblings stack on top of\n earlier ones because `isolation: isolate` establishes a single stacking\n context and DOM order wins inside it. */\n.avatar:not(:first-child) {\n margin-inline-start: calc(-1 * var(--spacing-2, 0.5rem));\n}\n\n/* When an AvatarStackItem carries a per-item `style` with gradient\n background + color, the fallback must be transparent so the gradient\n on the parent Avatar shows through. Only applied to the initials\n fallback inside a stack disc — plain Avatar usage is unaffected. */\n.initialsDisc {\n background-color: transparent;\n color: inherit;\n}\n"
|
|
482
482
|
}
|
|
483
483
|
]
|
|
484
484
|
},
|
|
@@ -1024,6 +1024,32 @@
|
|
|
1024
1024
|
}
|
|
1025
1025
|
]
|
|
1026
1026
|
},
|
|
1027
|
+
{
|
|
1028
|
+
"name": "toast-card",
|
|
1029
|
+
"type": "registry:ui",
|
|
1030
|
+
"description": "A static, server-renderable notification card for editorial display of toast anatomy. Use in state galleries — not for imperative portal notifications.",
|
|
1031
|
+
"category": "feedback",
|
|
1032
|
+
"dependencies": [
|
|
1033
|
+
"class-variance-authority",
|
|
1034
|
+
"@phosphor-icons/react",
|
|
1035
|
+
"@loworbitstudio/visor-core"
|
|
1036
|
+
],
|
|
1037
|
+
"registryDependencies": [
|
|
1038
|
+
"utils"
|
|
1039
|
+
],
|
|
1040
|
+
"files": [
|
|
1041
|
+
{
|
|
1042
|
+
"path": "components/ui/toast-card/toast-card.tsx",
|
|
1043
|
+
"type": "registry:ui",
|
|
1044
|
+
"content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport {\n CheckCircle,\n WarningCircle,\n Info,\n Warning,\n X,\n} from \"@phosphor-icons/react/dist/ssr\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./toast-card.module.css\"\n\n/* Variant → filled icon-badge color. A circular FILLED badge whose\n background is the semantic token and whose glyph reads in --primary-text.\n The whole card is declarative + server-renderable (no Sonner), so a fixed\n stack of these can be statically composed (see ToastCardStack). */\nconst toastCardVariants = cva(styles.base, {\n variants: {\n variant: {\n success: styles.variantSuccess,\n error: styles.variantError,\n info: styles.variantInfo,\n warning: styles.variantWarning,\n },\n },\n defaultVariants: {\n variant: \"info\",\n },\n})\n\ntype ToastCardVariant = NonNullable<VariantProps<typeof toastCardVariants>[\"variant\"]>\n\n/* Default glyph per variant — matches the golden success/error/info stack.\n Overridable via the `icon` prop for bespoke notifications. */\nconst variantIcon: Record<ToastCardVariant, React.ElementType> = {\n success: CheckCircle,\n error: WarningCircle,\n info: Info,\n warning: Warning,\n}\n\nexport interface ToastCardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">,\n VariantProps<typeof toastCardVariants> {\n /** Bold one-line headline. Required. */\n title: React.ReactNode\n /** Muted supporting line beneath the title. */\n body?: React.ReactNode\n /** Optional primary-colored action affordance (link/button text). */\n action?: React.ReactNode\n /** Handler for the action affordance. When omitted with `action`, the\n * action renders as static text (server-safe). */\n onAction?: React.MouseEventHandler<HTMLButtonElement>\n /** Dismiss handler. When provided, renders the muted X control. */\n onDismiss?: React.MouseEventHandler<HTMLButtonElement>\n /** Override the leading glyph. Defaults to the per-variant icon. */\n icon?: React.ReactNode\n}\n\nconst ToastCard = React.forwardRef<HTMLDivElement, ToastCardProps>(\n (\n {\n className,\n variant,\n title,\n body,\n action,\n onAction,\n onDismiss,\n icon,\n ...props\n },\n ref\n ) => {\n const resolvedVariant: ToastCardVariant = variant ?? \"info\"\n const Glyph = variantIcon[resolvedVariant]\n\n return (\n <div\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"toast-card\"\n data-variant={resolvedVariant}\n className={cn(toastCardVariants({ variant }), className)}\n {...props}\n >\n <span\n data-slot=\"toast-card-icon\"\n className={styles.icon}\n aria-hidden=\"true\"\n >\n {icon ?? <Glyph weight=\"fill\" />}\n </span>\n <div data-slot=\"toast-card-content\" className={styles.content}>\n <div data-slot=\"toast-card-title\" className={styles.title}>\n {title}\n </div>\n {body ? (\n <div data-slot=\"toast-card-body\" className={styles.body}>\n {body}\n </div>\n ) : null}\n {action ? (\n <button\n type=\"button\"\n data-slot=\"toast-card-action\"\n className={styles.action}\n onClick={onAction}\n >\n {action}\n </button>\n ) : null}\n </div>\n {onDismiss ? (\n <button\n type=\"button\"\n data-slot=\"toast-card-close\"\n className={styles.close}\n aria-label=\"Dismiss notification\"\n onClick={onDismiss}\n >\n <X weight=\"bold\" />\n </button>\n ) : null}\n </div>\n )\n }\n)\nToastCard.displayName = \"ToastCard\"\n\nexport interface ToastCardStackProps\n extends React.HTMLAttributes<HTMLDivElement> {\n /** Distance from the top of the viewport. Defaults to --spacing-6. */\n top?: string\n /** Vertical gap between stacked cards. Defaults to --spacing-3. */\n gap?: string\n}\n\n/* Fixed top-right column for a vertical stack of ToastCards. Declarative +\n server-renderable: the Feedback screen pins three of these without any\n client toast runtime. Offsets are CSS-var driven so consumers can retune\n without forking the module. */\nconst ToastCardStack = React.forwardRef<HTMLDivElement, ToastCardStackProps>(\n ({ className, top, gap, style, children, ...props }, ref) => {\n const stackStyle = {\n ...(top ? { [\"--toast-card-stack-top\" as string]: top } : {}),\n ...(gap ? { [\"--toast-card-stack-gap\" as string]: gap } : {}),\n ...style,\n } as React.CSSProperties\n\n return (\n <div\n ref={ref}\n data-slot=\"toast-card-stack\"\n className={cn(styles.stack, className)}\n style={stackStyle}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\nToastCardStack.displayName = \"ToastCardStack\"\n\nexport { ToastCard, ToastCardStack, toastCardVariants }\n"
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
"path": "components/ui/toast-card/toast-card.module.css",
|
|
1048
|
+
"type": "registry:ui",
|
|
1049
|
+
"content": "/* ToastCard — a declarative (non-Sonner) notification card that can be\n * server-rendered in a fixed stack. Theme-agnostic: every value is a\n * design-language token with a safe fallback, so the card looks right on\n * all themes without any hardcoded brand color.\n *\n * Note: use ToastCard for static editorial rendering of notification anatomy\n * (state galleries, docs). For imperative portal notifications use Toast. */\n\n/* Card surface — elevated card on a soft shadow, leading icon + content +\n optional close, aligned to the top so multi-line bodies read cleanly. */\n.base {\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-3, 0.75rem);\n min-width: 320px;\n padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);\n background-color: var(--surface-elev, var(--surface-card, #ffffff));\n border-radius: var(--radius-md, 0.5rem);\n box-shadow: var(--shadow-lg);\n color: var(--text-primary, #111827);\n}\n\n/* Circular FILLED icon badge — fixed square collapses to a perfect circle.\n Background is set per-variant; the glyph inherits currentColor. */\n.icon {\n flex-shrink: 0;\n width: var(--toast-card-icon-size, 1.5rem);\n height: var(--toast-card-icon-size, 1.5rem);\n margin-top: calc(var(--spacing-1, 0.25rem) / 4);\n border-radius: var(--radius-full, 9999px);\n display: grid;\n place-items: center;\n}\n\n.icon svg {\n width: var(--toast-card-icon-glyph, 0.875rem);\n height: var(--toast-card-icon-glyph, 0.875rem);\n}\n\n/* Variants — filled badge color + glyph color. */\n.variantSuccess .icon {\n background-color: var(--success, #22c55e);\n color: var(--primary-text, var(--text-inverse, #ffffff));\n}\n\n.variantError .icon {\n background-color: var(--destructive, #ef4444);\n color: var(--color-white, #ffffff);\n}\n\n.variantInfo .icon {\n background-color: var(--accent, #0ea5e9);\n color: var(--primary-text, var(--text-inverse, #ffffff));\n}\n\n.variantWarning .icon {\n background-color: var(--warning, #f59e0b);\n color: var(--primary-text, var(--text-inverse, #ffffff));\n}\n\n/* Content column — title, optional muted body, optional action. */\n.content {\n flex: 1;\n min-width: 0;\n}\n\n.title {\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-semibold, 600);\n line-height: 1.3;\n color: var(--text-primary, #111827);\n}\n\n.body {\n margin-top: calc(var(--spacing-1, 0.25rem) / 2);\n font-size: var(--toast-card-body-font-size, 0.8125rem);\n line-height: 1.4;\n color: var(--text-tertiary, #6b7280);\n}\n\n/* Primary-colored action affordance — borderless text button that reads as a\n link. Server-safe: renders as static styled text when no handler is wired. */\n.action {\n display: inline-block;\n margin-top: calc(var(--spacing-1, 0.25rem) * 1.5);\n padding: 0;\n border: none;\n background: transparent;\n font-size: var(--toast-card-body-font-size, 0.8125rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--primary, currentColor);\n cursor: pointer;\n}\n\n.action:hover {\n text-decoration: underline;\n}\n\n.action:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n border-radius: var(--radius-sm, 0.25rem);\n}\n\n/* Muted dismiss control — transparent square, darkens on hover. */\n.close {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: calc(var(--spacing-1, 0.25rem) / 2);\n border: none;\n background: transparent;\n color: var(--text-muted, var(--text-tertiary, #9ca3af));\n cursor: pointer;\n transition: color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.close:hover {\n color: var(--text-primary, #111827);\n}\n\n.close:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n border-radius: var(--radius-sm, 0.25rem);\n}\n\n.close svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* Fixed top-right column for a vertical stack of cards. Top offset + gap are\n CSS-var driven (set via ToastCardStack props) so consumers retune without\n forking. Defaults match the overlay toast-region pattern. */\n.stack {\n position: fixed;\n top: var(--toast-card-stack-top, var(--spacing-6, 1.5rem));\n right: var(--toast-card-stack-right, var(--spacing-6, 1.5rem));\n z-index: var(--toast-card-stack-z, 60);\n display: flex;\n flex-direction: column;\n gap: var(--toast-card-stack-gap, var(--spacing-3, 0.75rem));\n max-width: 380px;\n}\n"
|
|
1050
|
+
}
|
|
1051
|
+
]
|
|
1052
|
+
},
|
|
1027
1053
|
{
|
|
1028
1054
|
"name": "table",
|
|
1029
1055
|
"type": "registry:ui",
|
|
@@ -2393,6 +2419,30 @@
|
|
|
2393
2419
|
}
|
|
2394
2420
|
]
|
|
2395
2421
|
},
|
|
2422
|
+
{
|
|
2423
|
+
"name": "error-placard",
|
|
2424
|
+
"type": "registry:ui",
|
|
2425
|
+
"description": "Inline failed-load placard with a destructive-tinted icon chip, title, body, and optional right-aligned recovery actions. Distinct from Alert (passive semantic callout) and Banner (full-width page bar).",
|
|
2426
|
+
"category": "feedback",
|
|
2427
|
+
"dependencies": [
|
|
2428
|
+
"@loworbitstudio/visor-core"
|
|
2429
|
+
],
|
|
2430
|
+
"registryDependencies": [
|
|
2431
|
+
"utils"
|
|
2432
|
+
],
|
|
2433
|
+
"files": [
|
|
2434
|
+
{
|
|
2435
|
+
"path": "components/ui/error-placard/error-placard.tsx",
|
|
2436
|
+
"type": "registry:ui",
|
|
2437
|
+
"content": "import * as React from \"react\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./error-placard.module.css\"\n\nexport interface ErrorPlacardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Leading visual — typically a Phosphor icon. Rendered in a destructive-tinted circular chip. */\n icon: React.ReactNode\n /** Short direct statement of the failure. Required. */\n title: React.ReactNode\n /** 1-2 sentence explanation or recovery guidance. Required. */\n body: React.ReactNode\n /** Optional right-aligned action cluster — typically one or more Buttons. */\n actions?: React.ReactNode\n}\n\n/**\n * ErrorPlacard — an inline failed-load placard.\n *\n * A destructive-tinted horizontal card: a circular destructive icon chip on the\n * left, title + body in the middle, and an optional right-aligned action cluster.\n * The surface is a destructive color-mix on the card with an inset destructive\n * ring. Theme-agnostic — reads entirely from design-language tokens.\n */\nconst ErrorPlacard = React.forwardRef<HTMLDivElement, ErrorPlacardProps>(\n ({ className, icon, title, body, actions, ...props }, ref) => {\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-slot=\"error-placard\"\n className={cn(styles.base, className)}\n {...props}\n >\n <div\n data-slot=\"error-placard-icon\"\n className={styles.icon}\n aria-hidden=\"true\"\n >\n {icon}\n </div>\n\n <div data-slot=\"error-placard-body\" className={styles.content}>\n <p data-slot=\"error-placard-title\" className={styles.title}>\n {title}\n </p>\n <p data-slot=\"error-placard-message\" className={styles.message}>\n {body}\n </p>\n </div>\n\n {actions ? (\n <div\n data-slot=\"error-placard-actions\"\n className={styles.actions}\n >\n {actions}\n </div>\n ) : null}\n </div>\n )\n }\n)\nErrorPlacard.displayName = \"ErrorPlacard\"\n\nexport { ErrorPlacard }\n"
|
|
2438
|
+
},
|
|
2439
|
+
{
|
|
2440
|
+
"path": "components/ui/error-placard/error-placard.module.css",
|
|
2441
|
+
"type": "registry:ui",
|
|
2442
|
+
"content": "/* ErrorPlacard — inline failed-load placard.\n Destructive-tinted horizontal card: circular icon chip, title + body, optional\n right-aligned action cluster. Theme-agnostic — all values from design tokens. */\n.base {\n display: flex;\n align-items: center;\n gap: var(--spacing-5);\n padding: var(--spacing-8);\n border-radius: var(--radius-lg);\n /* Destructive color-mix on the card surface */\n background: color-mix(in srgb, var(--destructive) 8%, var(--surface-card));\n /* Inset destructive ring */\n box-shadow: inset 0 0 0 1px\n color-mix(in srgb, var(--destructive) 30%, transparent);\n}\n\n/* Circular destructive icon chip on the left */\n.icon {\n flex-shrink: 0;\n display: grid;\n place-items: center;\n width: 44px;\n height: 44px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--destructive) 20%, transparent);\n color: var(--destructive);\n font-size: 22px;\n line-height: 1;\n}\n\n/* Title + body column in the middle */\n.content {\n min-width: 0;\n}\n\n.title {\n margin: 0;\n font-size: var(--font-size-base);\n font-weight: 600;\n}\n\n.message {\n margin: calc(var(--spacing-1, 0.25rem) / 2) 0 0;\n font-size: 13px;\n color: var(--text-tertiary);\n}\n\n/* Right-aligned action cluster slot */\n.actions {\n margin-left: auto;\n display: flex;\n gap: var(--spacing-2);\n}\n"
|
|
2443
|
+
}
|
|
2444
|
+
]
|
|
2445
|
+
},
|
|
2396
2446
|
{
|
|
2397
2447
|
"name": "filter-bar",
|
|
2398
2448
|
"type": "registry:ui",
|
package/dist/visor-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.4.0",
|
|
3
|
-
"generated_at": "2026-06-
|
|
3
|
+
"generated_at": "2026-06-12T05:08:25.418Z",
|
|
4
4
|
"components": {
|
|
5
5
|
"accessibility-specimen": {
|
|
6
6
|
"category": "specimen",
|
|
@@ -311,7 +311,7 @@
|
|
|
311
311
|
},
|
|
312
312
|
{
|
|
313
313
|
"name": "AvatarStack",
|
|
314
|
-
"description": "Compound primitive for overlapping avatar groups with a `+N` overflow indicator.
|
|
314
|
+
"description": "Compound primitive for overlapping avatar groups with a `+N` overflow indicator. Each entry in `avatars` is a plain image-URL string, `undefined` (renders a placeholder disc), or an `AvatarStackItem` object with `initials`, `src`, `alt`, and an optional `style` escape hatch for per-disc gradient backgrounds. Accepts a `total` count (supports server-truncated lists), a `max` cap (default 6), an explicit `overflowCount` override, a `size` pass-through (`\"sm\" | \"default\" | \"lg\"`, default `\"sm\"`), and an optional accessible `label` override. Renders `role=\"img\"` with `data-slot=\"avatar-stack\"`.\n"
|
|
315
315
|
}
|
|
316
316
|
],
|
|
317
317
|
"dependencies": [
|
|
@@ -3525,6 +3525,62 @@
|
|
|
3525
3525
|
],
|
|
3526
3526
|
"example": "<EmptyState\n icon={<FolderOpen />}\n heading=\"No profiles yet\"\n description=\"Create your first profile to get started.\"\n action={<Button>New profile</Button>}\n secondaryAction={<Button variant=\"outline\">Learn more</Button>}\n/>\n"
|
|
3527
3527
|
},
|
|
3528
|
+
"error-placard": {
|
|
3529
|
+
"category": "feedback",
|
|
3530
|
+
"description": "Inline failed-load placard with a destructive icon chip, title, body, and optional recovery actions.",
|
|
3531
|
+
"when_to_use": [
|
|
3532
|
+
"A specific section or data panel failed to load and requires user action to recover",
|
|
3533
|
+
"Displaying a named error with both context and a retry or dismiss option in-place",
|
|
3534
|
+
"Inline error states within cards, list items, or content panels (not page-level)"
|
|
3535
|
+
],
|
|
3536
|
+
"when_not_to_use": [
|
|
3537
|
+
"Passive informational notices or warnings (use Alert)",
|
|
3538
|
+
"Site-wide or page-level announcements (use Banner)",
|
|
3539
|
+
"Empty states where no data exists yet (use EmptyState)",
|
|
3540
|
+
"Ephemeral transient notifications (use Toast)"
|
|
3541
|
+
],
|
|
3542
|
+
"props": [
|
|
3543
|
+
{
|
|
3544
|
+
"name": "icon",
|
|
3545
|
+
"type": "ReactNode",
|
|
3546
|
+
"required": true,
|
|
3547
|
+
"description": "Leading visual rendered in a destructive-tinted circular chip. Typically a Phosphor icon at 22px."
|
|
3548
|
+
},
|
|
3549
|
+
{
|
|
3550
|
+
"name": "title",
|
|
3551
|
+
"type": "ReactNode",
|
|
3552
|
+
"required": true,
|
|
3553
|
+
"description": "Short direct statement of the failure."
|
|
3554
|
+
},
|
|
3555
|
+
{
|
|
3556
|
+
"name": "body",
|
|
3557
|
+
"type": "ReactNode",
|
|
3558
|
+
"required": true,
|
|
3559
|
+
"description": "One or two sentences of explanation or recovery guidance."
|
|
3560
|
+
},
|
|
3561
|
+
{
|
|
3562
|
+
"name": "actions",
|
|
3563
|
+
"type": "ReactNode",
|
|
3564
|
+
"required": false,
|
|
3565
|
+
"description": "Optional right-aligned action cluster — typically one or more Buttons (retry, dismiss, contact support)."
|
|
3566
|
+
}
|
|
3567
|
+
],
|
|
3568
|
+
"dependencies": [
|
|
3569
|
+
"@loworbitstudio/visor-core"
|
|
3570
|
+
],
|
|
3571
|
+
"tokens_used": [
|
|
3572
|
+
"--destructive",
|
|
3573
|
+
"--font-size-base",
|
|
3574
|
+
"--radius-lg",
|
|
3575
|
+
"--spacing-1",
|
|
3576
|
+
"--spacing-2",
|
|
3577
|
+
"--spacing-5",
|
|
3578
|
+
"--spacing-8",
|
|
3579
|
+
"--surface-card",
|
|
3580
|
+
"--text-tertiary"
|
|
3581
|
+
],
|
|
3582
|
+
"example": "import { WarningCircle } from '@phosphor-icons/react';\n\n<ErrorPlacard\n icon={<WarningCircle weight=\"fill\" />}\n title=\"Could not load members\"\n body=\"The request timed out. Check your connection and try again.\"\n actions={\n <>\n <Button variant=\"ghost\" size=\"sm\">Dismiss</Button>\n <Button variant=\"outline\" size=\"sm\">Retry</Button>\n </>\n }\n/>\n"
|
|
3583
|
+
},
|
|
3528
3584
|
"field": {
|
|
3529
3585
|
"category": "form",
|
|
3530
3586
|
"description": "A form field wrapper composing label, description, and error components.",
|
|
@@ -8778,6 +8834,117 @@
|
|
|
8778
8834
|
],
|
|
8779
8835
|
"example": "import { Toaster, toast } from \"@/components/ui/toast\"\n\n{/* Place once in your layout */}\n<Toaster />\n\n{/* Call imperatively */}\ntoast.success(\"Changes saved\")\ntoast.error(\"Something went wrong\")\n"
|
|
8780
8836
|
},
|
|
8837
|
+
"toast-card": {
|
|
8838
|
+
"category": "feedback",
|
|
8839
|
+
"description": "A static, server-renderable notification card for editorial display of toast anatomy. Use in state galleries and design documentation — not for imperative portal notifications.",
|
|
8840
|
+
"when_to_use": [
|
|
8841
|
+
"State gallery screens showing notification anatomy in-flow",
|
|
8842
|
+
"Design documentation illustrating toast variants",
|
|
8843
|
+
"Static editorial rendering of notification cards without portal or timer machinery",
|
|
8844
|
+
"Layouts that need declarative, server-rendered notification cards"
|
|
8845
|
+
],
|
|
8846
|
+
"when_not_to_use": [
|
|
8847
|
+
"Imperative portal notifications that auto-dismiss (use Toast with Sonner instead)",
|
|
8848
|
+
"Notifications triggered by user actions at runtime (use Toast)",
|
|
8849
|
+
"Full-width announcement bars (use Banner)",
|
|
8850
|
+
"Inline contextual callouts within content (use Alert)"
|
|
8851
|
+
],
|
|
8852
|
+
"variants": {
|
|
8853
|
+
"variant": [
|
|
8854
|
+
"success",
|
|
8855
|
+
"error",
|
|
8856
|
+
"info",
|
|
8857
|
+
"warning"
|
|
8858
|
+
]
|
|
8859
|
+
},
|
|
8860
|
+
"props": [
|
|
8861
|
+
{
|
|
8862
|
+
"name": "variant",
|
|
8863
|
+
"type": "\"success\" | \"error\" | \"info\" | \"warning\"",
|
|
8864
|
+
"default": "\"info\"",
|
|
8865
|
+
"description": "Intent variant controlling the icon badge color and default glyph"
|
|
8866
|
+
},
|
|
8867
|
+
{
|
|
8868
|
+
"name": "title",
|
|
8869
|
+
"type": "React.ReactNode",
|
|
8870
|
+
"description": "Bold one-line headline. Required."
|
|
8871
|
+
},
|
|
8872
|
+
{
|
|
8873
|
+
"name": "body",
|
|
8874
|
+
"type": "React.ReactNode",
|
|
8875
|
+
"description": "Muted supporting line beneath the title. Optional."
|
|
8876
|
+
},
|
|
8877
|
+
{
|
|
8878
|
+
"name": "action",
|
|
8879
|
+
"type": "React.ReactNode",
|
|
8880
|
+
"description": "Primary-colored action affordance text. Optional. Renders as a button when onAction is provided, otherwise static text."
|
|
8881
|
+
},
|
|
8882
|
+
{
|
|
8883
|
+
"name": "onAction",
|
|
8884
|
+
"type": "React.MouseEventHandler<HTMLButtonElement>",
|
|
8885
|
+
"description": "Handler for the action affordance. When omitted with action, renders action as static text."
|
|
8886
|
+
},
|
|
8887
|
+
{
|
|
8888
|
+
"name": "onDismiss",
|
|
8889
|
+
"type": "React.MouseEventHandler<HTMLButtonElement>",
|
|
8890
|
+
"description": "Dismiss handler. When provided, renders a muted X dismiss control."
|
|
8891
|
+
},
|
|
8892
|
+
{
|
|
8893
|
+
"name": "icon",
|
|
8894
|
+
"type": "React.ReactNode",
|
|
8895
|
+
"description": "Override the leading glyph. Defaults to the per-variant Phosphor icon (CheckCircle / WarningCircle / Info / Warning)."
|
|
8896
|
+
}
|
|
8897
|
+
],
|
|
8898
|
+
"sub_components": [
|
|
8899
|
+
{
|
|
8900
|
+
"name": "ToastCardStack",
|
|
8901
|
+
"description": "Fixed top-right stacking container for multiple ToastCards. Declarative and server-renderable. Accepts `top` and `gap` string props that map to CSS custom properties for consumer retuning without forking. Renders `data-slot=\"toast-card-stack\"`.\n"
|
|
8902
|
+
}
|
|
8903
|
+
],
|
|
8904
|
+
"dependencies": [
|
|
8905
|
+
"class-variance-authority",
|
|
8906
|
+
"@phosphor-icons/react"
|
|
8907
|
+
],
|
|
8908
|
+
"tokens_used": [
|
|
8909
|
+
"--accent",
|
|
8910
|
+
"--border-focus",
|
|
8911
|
+
"--color-white",
|
|
8912
|
+
"--destructive",
|
|
8913
|
+
"--focus-ring-offset",
|
|
8914
|
+
"--focus-ring-width",
|
|
8915
|
+
"--font-size-sm",
|
|
8916
|
+
"--font-weight-medium",
|
|
8917
|
+
"--font-weight-semibold",
|
|
8918
|
+
"--motion-duration-150",
|
|
8919
|
+
"--motion-easing-default",
|
|
8920
|
+
"--primary",
|
|
8921
|
+
"--primary-text",
|
|
8922
|
+
"--radius-full",
|
|
8923
|
+
"--radius-md",
|
|
8924
|
+
"--radius-sm",
|
|
8925
|
+
"--shadow-lg",
|
|
8926
|
+
"--spacing-1",
|
|
8927
|
+
"--spacing-3",
|
|
8928
|
+
"--spacing-4",
|
|
8929
|
+
"--spacing-6",
|
|
8930
|
+
"--success",
|
|
8931
|
+
"--surface-card",
|
|
8932
|
+
"--surface-elev",
|
|
8933
|
+
"--text-inverse",
|
|
8934
|
+
"--text-muted",
|
|
8935
|
+
"--text-primary",
|
|
8936
|
+
"--text-tertiary",
|
|
8937
|
+
"--toast-card-body-font-size",
|
|
8938
|
+
"--toast-card-icon-glyph",
|
|
8939
|
+
"--toast-card-icon-size",
|
|
8940
|
+
"--toast-card-stack-gap",
|
|
8941
|
+
"--toast-card-stack-right",
|
|
8942
|
+
"--toast-card-stack-top",
|
|
8943
|
+
"--toast-card-stack-z",
|
|
8944
|
+
"--warning"
|
|
8945
|
+
],
|
|
8946
|
+
"example": "<ToastCard variant=\"success\" title=\"Changes saved\" body=\"Your profile has been updated.\" />\n\n<ToastCard\n variant=\"error\"\n title=\"Upload failed\"\n body=\"The file exceeds the 10MB limit.\"\n action=\"Try again\"\n onAction={() => {}}\n onDismiss={() => {}}\n/>\n"
|
|
8947
|
+
},
|
|
8781
8948
|
"toggle-group": {
|
|
8782
8949
|
"category": "form",
|
|
8783
8950
|
"description": "A toggle group component with single-select and multi-select modes using CVA variants.",
|
|
@@ -11223,10 +11390,12 @@
|
|
|
11223
11390
|
"alert",
|
|
11224
11391
|
"banner",
|
|
11225
11392
|
"challenge-card",
|
|
11393
|
+
"error-placard",
|
|
11226
11394
|
"popover",
|
|
11227
11395
|
"progress",
|
|
11228
11396
|
"skeleton",
|
|
11229
11397
|
"toast",
|
|
11398
|
+
"toast-card",
|
|
11230
11399
|
"tooltip"
|
|
11231
11400
|
],
|
|
11232
11401
|
"layout": [
|