@av-pi-studio/ui-library 0.0.0-stage → 0.0.115

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +137 -2
  3. package/dist/brand/config.d.ts +125 -0
  4. package/dist/brand/index.d.ts +3 -0
  5. package/dist/brand/index.js +6 -0
  6. package/dist/brand/theme-injection.d.ts +9 -0
  7. package/dist/breakpoints.d.ts +34 -0
  8. package/dist/breakpoints.js +32 -0
  9. package/dist/chunks/theme-injection-BHAAhBYz.js +103 -0
  10. package/dist/chunks/token-guard-B85Ysv3w.js +813 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.js +1049 -0
  13. package/dist/kv-store.d.ts +3 -0
  14. package/dist/primitives/Announcer.d.ts +13 -0
  15. package/dist/primitives/Avatar.d.ts +15 -0
  16. package/dist/primitives/Button.d.ts +18 -0
  17. package/dist/primitives/Checkbox.d.ts +11 -0
  18. package/dist/primitives/Dialog.d.ts +58 -0
  19. package/dist/primitives/Divider.d.ts +10 -0
  20. package/dist/primitives/EmptyState.d.ts +11 -0
  21. package/dist/primitives/Icon.d.ts +20 -0
  22. package/dist/primitives/IconButton.d.ts +25 -0
  23. package/dist/primitives/Menu.d.ts +50 -0
  24. package/dist/primitives/Panel.d.ts +9 -0
  25. package/dist/primitives/Popover.d.ts +25 -0
  26. package/dist/primitives/ResizeHandle.d.ts +20 -0
  27. package/dist/primitives/ScreenTitle.d.ts +15 -0
  28. package/dist/primitives/ScrollArea.d.ts +11 -0
  29. package/dist/primitives/Select.d.ts +25 -0
  30. package/dist/primitives/Shortcut.d.ts +15 -0
  31. package/dist/primitives/Spinner.d.ts +14 -0
  32. package/dist/primitives/StatusBadge.d.ts +13 -0
  33. package/dist/primitives/StatusDot.d.ts +17 -0
  34. package/dist/primitives/Surface.d.ts +13 -0
  35. package/dist/primitives/Switch.d.ts +14 -0
  36. package/dist/primitives/TextInput.d.ts +14 -0
  37. package/dist/primitives/ToastViewport.d.ts +17 -0
  38. package/dist/primitives/helpers.d.ts +47 -0
  39. package/dist/primitives/index.d.ts +24 -0
  40. package/dist/primitives/useHover.d.ts +16 -0
  41. package/dist/reset.css +43 -0
  42. package/dist/stores/announcer-store.d.ts +37 -0
  43. package/dist/stores/toast-store.d.ts +37 -0
  44. package/dist/styles.css +1 -0
  45. package/dist/theme/ThemeBoundary.d.ts +38 -0
  46. package/dist/theme/appearance-store.d.ts +37 -0
  47. package/dist/theme/color-utils.d.ts +13 -0
  48. package/dist/theme/colors.d.ts +111 -0
  49. package/dist/theme/css-bridge.d.ts +21 -0
  50. package/dist/theme/dev-warnings.d.ts +23 -0
  51. package/dist/theme/index.d.ts +10 -0
  52. package/dist/theme/index.js +39 -0
  53. package/dist/theme/palette.d.ts +19 -0
  54. package/dist/theme/theme.d.ts +47 -0
  55. package/dist/theme/token-guard.d.ts +20 -0
  56. package/dist/theme/tokens.d.ts +105 -0
  57. package/dist/theme/variants.d.ts +12 -0
  58. package/dist/ui/avatar.d.ts +11 -0
  59. package/dist/ui/button.d.ts +34 -0
  60. package/dist/ui/combobox.d.ts +55 -0
  61. package/dist/ui/index.d.ts +10 -0
  62. package/dist/ui/index.js +406 -0
  63. package/dist/ui/option-groups.d.ts +36 -0
  64. package/dist/ui/screen-header.d.ts +23 -0
  65. package/dist/ui/shortcut.d.ts +13 -0
  66. package/dist/ui/status-badge.d.ts +15 -0
  67. package/dist/ui/status-dot.d.ts +20 -0
  68. package/dist/ui/toast.d.ts +60 -0
  69. package/package.json +68 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ Breaking changes get a dated entry here. Versions move in lockstep with the other
4
+ `@av-pi-studio/*` packages (0.0.x).
5
+
6
+ ## 2026-11-14 — 0.0.115 (initial release)
7
+
8
+ Extracted from `@av-pi-studio/web-client`.
9
+
10
+ ### Public surface
11
+
12
+ - `@av-pi-studio/ui-library` — components (`Button`, `IconButton`, `Dialog`, `Menu*`, `Popover*`,
13
+ `Panel`, `Select`, `Switch`, `Checkbox`, `TextInput`, `TextArea`, `Icon`, `Spinner`, `StatusDot`,
14
+ `StatusBadge`, `Avatar`, `EmptyState`, …), `ThemeBoundary` (`store`, `storageKey` default
15
+ `pi-studio-appearance`, `brandConfig`), `useAppearance`, `ToastViewport`, `Announcer`,
16
+ `useToastStore`, `speak`, `localStorageKvStore`.
17
+ - `/theme` (incl. `findTokenProblems`), `/ui`, `/brand`, `/breakpoints` subpath entries.
18
+ - CSS entries: `/styles.css` (tokens + component styles, `piui-` class prefix) and `/reset.css`.
19
+ Import library CSS before your own.
20
+ - Peer dependencies: `react`, `react-dom` ^19. Targets Vite web SPAs and Electron renderers
21
+ (no SSR, no React Native). Dev-only console warnings rely on the consumer's bundler folding
22
+ `process.env.NODE_ENV`.
23
+ - One `ThemeBoundary` per document; one copy of the library per app (toast/announcer stores are
24
+ module singletons).
package/README.md CHANGED
@@ -1,3 +1,138 @@
1
- # Temporary Holding Version
1
+ # @av-pi-studio/ui-library
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Reusable React UI primitives, theme system and global styles for Pi-Studio client apps.
4
+ Targets Vite web SPAs and Electron renderers. No SSR, no React Native.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm i @av-pi-studio/ui-library react react-dom
10
+ ```
11
+
12
+ Peer dependencies: `react` and `react-dom` `^19`. Radix (dialog, dropdown-menu, popover),
13
+ `lucide-react`, `clsx`, `zustand` and `zod` are regular dependencies.
14
+
15
+ ## Quickstart
16
+
17
+ Import the library CSS **before your own CSS**. Library class names carry the `piui-` prefix and
18
+ design tokens are `--pi-*` CSS variables. `styles.css` is required; `reset.css` is an optional
19
+ global reset.
20
+
21
+ <!-- smoke:main.tsx -->
22
+ ```tsx
23
+ import "@av-pi-studio/ui-library/reset.css";
24
+ import "@av-pi-studio/ui-library/styles.css";
25
+
26
+ import { StrictMode } from "react";
27
+ import { createRoot } from "react-dom/client";
28
+ import {
29
+ Announcer,
30
+ Button,
31
+ Switch,
32
+ ThemeBoundary,
33
+ ToastViewport,
34
+ useToastStore,
35
+ } from "@av-pi-studio/ui-library";
36
+
37
+ function App() {
38
+ const show = useToastStore((s) => s.show);
39
+ return (
40
+ <main style={{ padding: 16, display: "grid", gap: 12, justifyItems: "start" }}>
41
+ <Button id="smoke-button" onClick={() => show("Hello from ui-library")}>
42
+ Show toast
43
+ </Button>
44
+ <Switch checked onCheckedChange={() => {}} aria-label="Example switch" />
45
+ </main>
46
+ );
47
+ }
48
+
49
+ createRoot(document.getElementById("root")!).render(
50
+ <StrictMode>
51
+ <ThemeBoundary>
52
+ <App />
53
+ <ToastViewport />
54
+ <Announcer />
55
+ </ThemeBoundary>
56
+ </StrictMode>,
57
+ );
58
+ ```
59
+ <!-- /smoke:main.tsx -->
60
+
61
+ - `ThemeBoundary` applies the theme (light/dark/system) to the document and provides
62
+ `useAppearance()`. Props (all optional):
63
+ - `storageKey` — key the appearance JSON is stored under (default `pi-studio-appearance`).
64
+ - `store` — a custom key-value store instead of `localStorage` (see `localStorageKvStore`).
65
+ - `brandConfig` — e.g. `{ colors: { accent: "#7c3aed" } }` to re-theme the accent.
66
+ - `ToastViewport` and `Announcer` are **not** mounted automatically; mount them once, inside
67
+ `ThemeBoundary`. Fire toasts with `useToastStore.getState().show("text")` (also `copied`,
68
+ `error`) and screen-reader announcements with `speak()`.
69
+
70
+ ### Switching the theme
71
+
72
+ `useAppearanceControls()` (root entry, under `ThemeBoundary`) returns `{ setMode, updateSettings }`;
73
+ changes are applied to the document and persisted. `useAppearance()` reads the current state.
74
+
75
+ ```tsx
76
+ import { Button, THEME_NAMES, useAppearance, useAppearanceControls } from "@av-pi-studio/ui-library";
77
+
78
+ function ThemeSwitcher() {
79
+ const { mode } = useAppearance();
80
+ const { setMode } = useAppearanceControls();
81
+ return (
82
+ <>
83
+ {(["system", ...THEME_NAMES] as const).map((name) => (
84
+ <Button key={name} variant={name === mode ? "default" : "outline"} onClick={() => setMode(name)}>
85
+ {name}
86
+ </Button>
87
+ ))}
88
+ </>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ## Components
94
+
95
+ `Button`, `IconButton`, `Dialog`, `Menu*`, `Popover*`, `Panel`, `Surface`, `Select`, `Combobox`,
96
+ `Switch`, `Checkbox`, `TextInput`, `TextArea`, `Icon`, `Spinner`, `StatusDot`, `StatusBadge`,
97
+ `Avatar`, `EmptyState`, `ScrollArea`, `Divider`, `ResizeHandle`, `ShortcutHint`. Types ship
98
+ alongside (`ButtonProps`, ...).
99
+
100
+ `Menu*` is half of a menu: `MenuContent`, `MenuItem`, `MenuGroup`, `MenuSeparator` and
101
+ `MenuCursorTrigger` ship, but `Root`/`Trigger` do not — install `@radix-ui/react-dropdown-menu`
102
+ yourself and import `DropdownMenu.Root`/`.Trigger` from it.
103
+
104
+ ## Entry points
105
+
106
+ Only these resolve (the `exports` map); deep imports such as `.../src/...` or `.../dist/...` are
107
+ rejected.
108
+
109
+ | Specifier | Contents |
110
+ | --- | --- |
111
+ | `@av-pi-studio/ui-library` | Everything: components, `ThemeBoundary`, hooks, stores, and the subpaths below |
112
+ | `.../theme` | Theme/appearance logic and token helpers (`findTokenProblems`), React-free |
113
+ | `.../ui` | Pure UI logic (button/toast helpers), React-free |
114
+ | `.../brand` | `BrandConfig` schema and accent derivation, React-free |
115
+ | `.../breakpoints` | Breakpoint constants, React-free |
116
+ | `.../styles.css` | Required global styles and `--pi-*` tokens |
117
+ | `.../reset.css` | Optional global reset |
118
+
119
+ ## Constraints
120
+
121
+ - One `ThemeBoundary` per document.
122
+ - One copy of the package per app: the toast and announcer stores are module singletons.
123
+ - React `^19`.
124
+ - A bundler (Vite, etc.) is required: dev-only warnings read `process.env.NODE_ENV`, which the
125
+ bundler folds. Loading the ESM unbundled would throw `ReferenceError: process` in dev.
126
+ - No SSR, no React Native.
127
+
128
+ ## Dev-only warnings
129
+
130
+ In development builds the library logs a console warning when `styles.css` is not loaded, when a
131
+ second `ThemeBoundary` mounts, or when `brandConfig` is invalid. They are stripped from
132
+ production bundles.
133
+
134
+ ## Verifying a release
135
+
136
+ `bash packages/ui-library/scripts/pack-smoke.sh` packs the library, installs the tarball into a
137
+ scratch Vite app outside the repo using the Quickstart snippet above, and checks types, build,
138
+ `exports` and CSS. `KEEP_DIR=1` keeps the scratch dir.
@@ -0,0 +1,125 @@
1
+ import { z } from "zod";
2
+ export declare const BrandConfigSchema: z.ZodObject<{
3
+ productName: z.ZodOptional<z.ZodString>;
4
+ shortName: z.ZodOptional<z.ZodString>;
5
+ tagline: z.ZodOptional<z.ZodString>;
6
+ colors: z.ZodObject<{
7
+ accent: z.ZodEffects<z.ZodString, string, string>;
8
+ accentBright: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
9
+ accentForeground: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
10
+ swatch: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ accent: string;
13
+ accentBright?: string | undefined;
14
+ accentForeground?: string | undefined;
15
+ swatch?: string | undefined;
16
+ }, {
17
+ accent: string;
18
+ accentBright?: string | undefined;
19
+ accentForeground?: string | undefined;
20
+ swatch?: string | undefined;
21
+ }>;
22
+ assets: z.ZodOptional<z.ZodObject<{
23
+ logoLight: z.ZodString;
24
+ logoDark: z.ZodString;
25
+ logoMark: z.ZodOptional<z.ZodString>;
26
+ icon: z.ZodString;
27
+ splash: z.ZodOptional<z.ZodString>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ logoLight: string;
30
+ logoDark: string;
31
+ logoMark?: string | undefined;
32
+ icon: string;
33
+ splash?: string | undefined;
34
+ }, {
35
+ logoLight: string;
36
+ logoDark: string;
37
+ logoMark?: string | undefined;
38
+ icon: string;
39
+ splash?: string | undefined;
40
+ }>>;
41
+ links: z.ZodOptional<z.ZodObject<{
42
+ docs: z.ZodOptional<z.ZodString>;
43
+ support: z.ZodOptional<z.ZodString>;
44
+ issues: z.ZodOptional<z.ZodString>;
45
+ website: z.ZodOptional<z.ZodString>;
46
+ }, "strip", z.ZodTypeAny, {
47
+ docs?: string | undefined;
48
+ support?: string | undefined;
49
+ issues?: string | undefined;
50
+ website?: string | undefined;
51
+ }, {
52
+ docs?: string | undefined;
53
+ support?: string | undefined;
54
+ issues?: string | undefined;
55
+ website?: string | undefined;
56
+ }>>;
57
+ legal: z.ZodOptional<z.ZodObject<{
58
+ copyright: z.ZodOptional<z.ZodString>;
59
+ companyName: z.ZodOptional<z.ZodString>;
60
+ }, "strip", z.ZodTypeAny, {
61
+ copyright?: string | undefined;
62
+ companyName?: string | undefined;
63
+ }, {
64
+ copyright?: string | undefined;
65
+ companyName?: string | undefined;
66
+ }>>;
67
+ }, "strip", z.ZodTypeAny, {
68
+ productName?: string | undefined;
69
+ shortName?: string | undefined;
70
+ tagline?: string | undefined;
71
+ colors: {
72
+ accent: string;
73
+ accentBright?: string | undefined;
74
+ accentForeground?: string | undefined;
75
+ swatch?: string | undefined;
76
+ };
77
+ assets?: {
78
+ logoLight: string;
79
+ logoDark: string;
80
+ logoMark?: string | undefined;
81
+ icon: string;
82
+ splash?: string | undefined;
83
+ } | undefined;
84
+ links?: {
85
+ docs?: string | undefined;
86
+ support?: string | undefined;
87
+ issues?: string | undefined;
88
+ website?: string | undefined;
89
+ } | undefined;
90
+ legal?: {
91
+ copyright?: string | undefined;
92
+ companyName?: string | undefined;
93
+ } | undefined;
94
+ }, {
95
+ productName?: string | undefined;
96
+ shortName?: string | undefined;
97
+ tagline?: string | undefined;
98
+ colors: {
99
+ accent: string;
100
+ accentBright?: string | undefined;
101
+ accentForeground?: string | undefined;
102
+ swatch?: string | undefined;
103
+ };
104
+ assets?: {
105
+ logoLight: string;
106
+ logoDark: string;
107
+ logoMark?: string | undefined;
108
+ icon: string;
109
+ splash?: string | undefined;
110
+ } | undefined;
111
+ links?: {
112
+ docs?: string | undefined;
113
+ support?: string | undefined;
114
+ issues?: string | undefined;
115
+ website?: string | undefined;
116
+ } | undefined;
117
+ legal?: {
118
+ copyright?: string | undefined;
119
+ companyName?: string | undefined;
120
+ } | undefined;
121
+ }>;
122
+ export type BrandConfig = z.infer<typeof BrandConfigSchema>;
123
+ export type ResolvedBrandColors = Required<Pick<BrandConfig["colors"], "accent" | "accentBright" | "accentForeground" | "swatch">>;
124
+ export declare function resolveAccentColors(colors: BrandConfig["colors"]): ResolvedBrandColors;
125
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from "./config.js";
2
+ export * from "./theme-injection.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { B as a, i as c, r as n } from "../chunks/theme-injection-BHAAhBYz.js";
2
+ export {
3
+ a as BrandConfigSchema,
4
+ c as injectBrandAccent,
5
+ n as resolveAccentColors
6
+ };
@@ -0,0 +1,9 @@
1
+ import { type ResolvedBrandColors } from "./config.js";
2
+ import { type Theme } from "../theme/theme.js";
3
+ /**
4
+ * Build a branded copy of a theme by replacing only the accent family tokens
5
+ * and swatch. All other tokens (surfaces, semantic status, syntax, terminal)
6
+ * remain unchanged — only the accent family is brand-overridable in this version.
7
+ */
8
+ export declare function injectBrandAccent(theme: Theme, resolved: ResolvedBrandColors): Theme;
9
+ //# sourceMappingURL=theme-injection.d.ts.map
@@ -0,0 +1,34 @@
1
+ export declare const breakpoints: {
2
+ readonly xs: 0;
3
+ readonly sm: 576;
4
+ readonly md: 768;
5
+ readonly lg: 992;
6
+ readonly xl: 1200;
7
+ };
8
+ export type Breakpoint = keyof typeof breakpoints;
9
+ /** Returns the active breakpoint name for a given container/window width. */
10
+ export declare function getBreakpoint(width: number): Breakpoint;
11
+ /**
12
+ * Compact form factor = xs or sm (phone-class layout).
13
+ * This is the single source of truth for "phone-class layout" —
14
+ * never use Platform.OS as a layout proxy.
15
+ */
16
+ export declare function isCompactFormFactor(width: number): boolean;
17
+ export declare const HEADER_INNER_HEIGHT = 48;
18
+ export declare const HEADER_INNER_HEIGHT_MOBILE = 56;
19
+ export declare const HEADER_TOP_PADDING_MOBILE = 8;
20
+ export declare const WORKSPACE_SECONDARY_HEADER_HEIGHT = 36;
21
+ export declare const FOOTER_HEIGHT = 75;
22
+ export declare const MAX_CONTENT_WIDTH = 820;
23
+ export declare const COMPACT_FORM_FACTOR_WIDTH = 500;
24
+ export declare const WINDOW_CHROME: {
25
+ readonly macOS: {
26
+ readonly width: 78;
27
+ readonly height: 45;
28
+ };
29
+ readonly windowsLinux: {
30
+ readonly width: 140;
31
+ readonly height: 48;
32
+ };
33
+ };
34
+ //# sourceMappingURL=breakpoints.d.ts.map
@@ -0,0 +1,32 @@
1
+ const n = {
2
+ xs: 0,
3
+ sm: 576,
4
+ md: 768,
5
+ lg: 992,
6
+ xl: 1200
7
+ };
8
+ function s(t) {
9
+ return t >= n.xl ? "xl" : t >= n.lg ? "lg" : t >= n.md ? "md" : t >= n.sm ? "sm" : "xs";
10
+ }
11
+ function o(t) {
12
+ const E = s(t);
13
+ return E === "xs" || E === "sm";
14
+ }
15
+ const _ = 48, r = 56, c = 8, H = 36, O = 75, e = 820, i = 500, I = {
16
+ macOS: { width: 78, height: 45 },
17
+ // traffic-light reserve
18
+ windowsLinux: { width: 140, height: 48 }
19
+ };
20
+ export {
21
+ i as COMPACT_FORM_FACTOR_WIDTH,
22
+ O as FOOTER_HEIGHT,
23
+ _ as HEADER_INNER_HEIGHT,
24
+ r as HEADER_INNER_HEIGHT_MOBILE,
25
+ c as HEADER_TOP_PADDING_MOBILE,
26
+ e as MAX_CONTENT_WIDTH,
27
+ I as WINDOW_CHROME,
28
+ H as WORKSPACE_SECONDARY_HEADER_HEIGHT,
29
+ n as breakpoints,
30
+ s as getBreakpoint,
31
+ o as isCompactFormFactor
32
+ };
@@ -0,0 +1,103 @@
1
+ import { z as o } from "zod";
2
+ function l(n) {
3
+ const t = n.trim().replace(/^#/, ""), a = t.length === 3 ? t.split("").map((r) => r + r).join("") : t;
4
+ if (a.length !== 6 || /[^0-9a-fA-F]/.test(a))
5
+ throw new Error(`Invalid hex color: ${n}`);
6
+ return {
7
+ r: parseInt(a.slice(0, 2), 16),
8
+ g: parseInt(a.slice(2, 4), 16),
9
+ b: parseInt(a.slice(4, 6), 16)
10
+ };
11
+ }
12
+ function h(n) {
13
+ return Math.max(0, Math.min(255, Math.round(n)));
14
+ }
15
+ function u({ r: n, g: t, b: a }) {
16
+ const r = (c) => h(c).toString(16).padStart(2, "0");
17
+ return `#${r(n)}${r(t)}${r(a)}`;
18
+ }
19
+ function p(n) {
20
+ const { r: t, g: a, b: r } = l(n), c = (e) => {
21
+ const g = e / 255;
22
+ return g <= 0.03928 ? g / 12.92 : ((g + 0.055) / 1.055) ** 2.4;
23
+ };
24
+ return 0.2126 * c(t) + 0.7152 * c(a) + 0.0722 * c(r);
25
+ }
26
+ function m(n, t = "#18181b", a = "#ffffff") {
27
+ return p(n) > 0.45 ? t : a;
28
+ }
29
+ function b(n, t) {
30
+ const { r: a, g: r, b: c } = l(n), e = Math.max(0, Math.min(1, t));
31
+ return u({
32
+ r: a + (255 - a) * e,
33
+ g: r + (255 - r) * e,
34
+ b: c + (255 - c) * e
35
+ });
36
+ }
37
+ function F(n, t) {
38
+ const { r: a, g: r, b: c } = l(n), e = Math.max(0, Math.min(1, t));
39
+ return u({ r: a * (1 - e), g: r * (1 - e), b: c * (1 - e) });
40
+ }
41
+ function i(n) {
42
+ const t = n.trim().replace(/^#/, "");
43
+ return (t.length === 3 || t.length === 6) && !/[^0-9a-fA-F]/.test(t);
44
+ }
45
+ const s = o.string().min(1, "Asset path must not be empty"), f = o.object({
46
+ accent: o.string().refine(i, { message: "accent must be a valid #rrggbb hex color" }),
47
+ accentBright: o.string().refine(i, { message: "accentBright must be a valid #rrggbb hex color" }).optional(),
48
+ accentForeground: o.string().refine(i, { message: "accentForeground must be a valid #rrggbb hex color" }).optional(),
49
+ swatch: o.string().refine(i, { message: "swatch must be a valid #rrggbb hex color" }).optional()
50
+ }), d = o.object({
51
+ logoLight: s,
52
+ logoDark: s,
53
+ logoMark: s.optional(),
54
+ icon: s,
55
+ splash: s.optional()
56
+ }), B = o.object({
57
+ docs: o.string().url().optional(),
58
+ support: o.string().url().optional(),
59
+ issues: o.string().url().optional(),
60
+ website: o.string().url().optional()
61
+ }).optional(), x = o.object({
62
+ copyright: o.string().optional(),
63
+ companyName: o.string().optional()
64
+ }).optional(), M = o.object({
65
+ productName: o.string().min(1, "productName must not be empty").optional(),
66
+ shortName: o.string().optional(),
67
+ tagline: o.string().optional(),
68
+ colors: f,
69
+ assets: d.optional(),
70
+ links: B,
71
+ legal: x
72
+ });
73
+ function S(n) {
74
+ const t = n.accent, a = n.accentBright ?? b(t, 0.35), r = n.accentForeground ?? m(t), c = n.swatch ?? t;
75
+ return { accent: t, accentBright: a, accentForeground: r, swatch: c };
76
+ }
77
+ function j(n, t) {
78
+ return {
79
+ ...n,
80
+ colors: {
81
+ ...n.colors,
82
+ accent: t.accent,
83
+ accentBright: t.accentBright,
84
+ accentForeground: t.accentForeground,
85
+ // Legacy aliases that mirror accent.
86
+ primary: t.accent,
87
+ ring: t.accent
88
+ },
89
+ swatch: t.swatch
90
+ };
91
+ }
92
+ export {
93
+ M as B,
94
+ i as a,
95
+ p as b,
96
+ m as c,
97
+ F as d,
98
+ u as e,
99
+ l as h,
100
+ j as i,
101
+ b as l,
102
+ S as r
103
+ };