@photon-ai/pho-ui 0.6.0 → 0.7.1

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.
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import { t } from "./cn-7uGUzCOM.js";
3
+ import { t as l } from "./resolve-variant-CcD8iG5l.js";
4
+ import "react";
5
+ import { jsx as i } from "react/jsx-runtime";
6
+ import { Avatar as r } from "@base-ui/react/avatar";
7
+ var n = {
8
+ xs: {
9
+ classes: "size-6 text-[0.625rem]",
10
+ description: "Extra small — inline chips and rails"
11
+ },
12
+ sm: {
13
+ classes: "size-8 text-xs",
14
+ description: "Small — dense lists"
15
+ },
16
+ md: {
17
+ classes: "size-10 text-sm",
18
+ description: "Medium — the default"
19
+ },
20
+ lg: {
21
+ classes: "size-12 text-md",
22
+ description: "Large — profile headers"
23
+ }
24
+ };
25
+ function o({ className: e, size: s = "md", ...a }) {
26
+ return /* @__PURE__ */ i(r.Root, {
27
+ className: t("squircle bg-pho-tertiary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none", l(n, s), e),
28
+ ...a
29
+ });
30
+ }
31
+ function c({ className: e, ...s }) {
32
+ return /* @__PURE__ */ i(r.Image, {
33
+ className: t("size-full object-cover", e),
34
+ ...s
35
+ });
36
+ }
37
+ function m({ className: e, ...s }) {
38
+ return /* @__PURE__ */ i(r.Fallback, {
39
+ className: t("flex size-full items-center justify-center", e),
40
+ ...s
41
+ });
42
+ }
43
+ var v = {
44
+ Root: o,
45
+ Image: c,
46
+ Fallback: m
47
+ };
48
+ export {
49
+ v as n,
50
+ n as t
51
+ };
52
+
53
+ //# sourceMappingURL=avatar-Bb4RZQIa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar-Bb4RZQIa.js","names":[],"sources":["../../src/components/avatar/avatar.tsx"],"sourcesContent":["import { type ComponentProps, type ElementType } from \"react\";\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\n/** Size axis — four sizes, no `xl` (Geist-reductive). `md` is default. */\nexport const AVATAR_SIZES = {\n xs: {\n classes: \"size-6 text-[0.625rem]\",\n description: \"Extra small — inline chips and rails\",\n },\n sm: { classes: \"size-8 text-xs\", description: \"Small — dense lists\" },\n md: { classes: \"size-10 text-sm\", description: \"Medium — the default\" },\n lg: { classes: \"size-12 text-md\", description: \"Large — profile headers\" },\n} satisfies VariantMap;\n\nexport type AvatarSize = keyof typeof AVATAR_SIZES;\n\ninterface RootProps extends StyledProps<typeof BaseAvatar.Root> {\n /** Size variant. @default \"md\" */\n size?: AvatarSize;\n}\n\nfunction Root({ className, size = \"md\", ...props }: RootProps) {\n return (\n <BaseAvatar.Root\n className={cn(\n \"squircle bg-pho-tertiary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none\",\n resolveVariant(AVATAR_SIZES, size),\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Image({ className, ...props }: StyledProps<typeof BaseAvatar.Image>) {\n return (\n <BaseAvatar.Image\n className={cn(\"size-full object-cover\", className)}\n {...props}\n />\n );\n}\n\nfunction Fallback({\n className,\n ...props\n}: StyledProps<typeof BaseAvatar.Fallback>) {\n return (\n <BaseAvatar.Fallback\n className={cn(\"flex size-full items-center justify-center\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Avatar built on Base UI `Avatar`. Compose `Avatar.Image` with an\n * `Avatar.Fallback` (initials or icon) that shows while the image loads or if\n * it fails. Set `size` on `Avatar.Root`.\n */\nexport const Avatar = { Root, Image, Fallback };\n"],"mappings":";;;;;;AAcA,IAAa,IAAe;AAAA,EAC1B,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IAAE,SAAS;AAAA,IAAkB,aAAa;AAAA,EAAsB;AAAA,EACpE,IAAI;AAAA,IAAE,SAAS;AAAA,IAAmB,aAAa;AAAA,EAAuB;AAAA,EACtE,IAAI;AAAA,IAAE,SAAS;AAAA,IAAmB,aAAa;AAAA,EAA0B;AAC3E;AASA,SAAS,EAAK,EAAE,WAAA,GAAW,MAAA,IAAO,MAAM,GAAG,EAAA,GAAoB;AAC7D,SACE,gBAAA,EAAC,EAAW,MAAZ;AAAA,IACE,WAAW,EACT,2KACA,EAAe,GAAc,CAAI,GACjC,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAAG,0BAA0B,CAAS;AAAA,IACjD,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAS,EAChB,WAAA,GACA,GAAG,EAAA,GACuC;AAC1C,SACE,gBAAA,EAAC,EAAW,UAAZ;AAAA,IACE,WAAW,EAAG,8CAA8C,CAAS;AAAA,IACrE,GAAI;AAAA,EACL,CAAA;AAEL;AAOA,IAAa,IAAS;AAAA,EAAE,MAAA;AAAA,EAAM,OAAA;AAAA,EAAO,UAAA;AAAS"}
@@ -0,0 +1,164 @@
1
+ "use client";
2
+ import { t as o } from "./cn-7uGUzCOM.js";
3
+ import { n as g, r as v } from "./link-provider-9TFNyDqO.js";
4
+ import { l as x } from "./button-CQ4cZvkC.js";
5
+ import { t as h } from "./createReactComponent-BqCmnqfX.js";
6
+ import { n as l } from "./avatar-Bb4RZQIa.js";
7
+ import { isValidElement as y } from "react";
8
+ import { Fragment as b, jsx as t, jsxs as a } from "react/jsx-runtime";
9
+ var N = [["path", {
10
+ d: "M17 7l-10 10",
11
+ key: "svg-0"
12
+ }], ["path", {
13
+ d: "M8 7l9 0l0 9",
14
+ key: "svg-1"
15
+ }]], I = h("outline", "arrow-up-right", "ArrowUpRight", N), k = [
16
+ ["path", {
17
+ d: "M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
18
+ key: "svg-0"
19
+ }],
20
+ ["path", {
21
+ d: "M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
22
+ key: "svg-1"
23
+ }],
24
+ ["path", {
25
+ d: "M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
26
+ key: "svg-2"
27
+ }]
28
+ ], w = h("outline", "dots", "Dots", k), R = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r px-4 py-5", _ = "outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1", A = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-fg-pho-primary", E = o("text-pho-secondary *:data-icon:text-fg-pho-secondary", "hover:bg-pho-ghost-hover hover:text-pho-primary hover:*:data-icon:text-fg-pho-primary");
29
+ function C({ className: r, ...e }) {
30
+ return /* @__PURE__ */ t("aside", {
31
+ className: o(R, r),
32
+ ...e
33
+ });
34
+ }
35
+ function M({ className: r, ...e }) {
36
+ return /* @__PURE__ */ t("div", {
37
+ className: o("mb-6 flex items-center gap-2.5 px-2", r),
38
+ ...e
39
+ });
40
+ }
41
+ function T({ className: r, ...e }) {
42
+ return /* @__PURE__ */ t("nav", {
43
+ className: o("flex flex-1 flex-col gap-6 overflow-y-auto", r),
44
+ ...e
45
+ });
46
+ }
47
+ function D({ className: r, ...e }) {
48
+ return /* @__PURE__ */ t("div", {
49
+ className: r,
50
+ ...e
51
+ });
52
+ }
53
+ function B({ className: r, ...e }) {
54
+ return /* @__PURE__ */ t("div", {
55
+ className: o("text-pho-secondary mb-2 px-2 text-xs font-medium", r),
56
+ ...e
57
+ });
58
+ }
59
+ function z({ className: r, columns: e, ...n }) {
60
+ return /* @__PURE__ */ t("ul", {
61
+ className: o("m-0 list-none p-0 *:m-0", e ? "grid grid-cols-2 gap-x-2 gap-y-0.5" : "flex flex-col gap-0.5", r),
62
+ ...n
63
+ });
64
+ }
65
+ function F({ className: r, ...e }) {
66
+ return /* @__PURE__ */ t("div", {
67
+ className: o("mt-4 flex flex-col gap-0.5", r),
68
+ ...e
69
+ });
70
+ }
71
+ function p(r) {
72
+ if (r == null || r === !1) return null;
73
+ let e = null;
74
+ return v(r) ? e = /* @__PURE__ */ t(r, {}) : y(r) && (e = r), e == null ? null : /* @__PURE__ */ t("span", {
75
+ "data-icon": !0,
76
+ className: "pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-4 [&>svg]:shrink-0",
77
+ children: e
78
+ });
79
+ }
80
+ function L({ icon: r, active: e, className: n, children: i, href: s, external: c, ...u }) {
81
+ const f = g(), d = o(_, e ? A : E, n), m = /* @__PURE__ */ t("span", {
82
+ className: "min-w-0 flex-1 truncate",
83
+ children: i
84
+ });
85
+ return s !== void 0 ? /* @__PURE__ */ t("li", { children: /* @__PURE__ */ a(f, {
86
+ href: s,
87
+ "aria-current": e ? "page" : void 0,
88
+ ...c && {
89
+ target: "_blank",
90
+ rel: "noopener noreferrer"
91
+ },
92
+ ...u,
93
+ className: d,
94
+ children: [
95
+ p(r),
96
+ m,
97
+ c && /* @__PURE__ */ a(b, { children: [p(/* @__PURE__ */ t(I, { "aria-hidden": !0 })), /* @__PURE__ */ t("span", {
98
+ className: "sr-only",
99
+ children: "(opens in a new tab)"
100
+ })] })
101
+ ]
102
+ }) }) : /* @__PURE__ */ t("li", { children: /* @__PURE__ */ a("button", {
103
+ type: "button",
104
+ "aria-current": e ? "page" : void 0,
105
+ ...u,
106
+ className: d,
107
+ children: [p(r), m]
108
+ }) });
109
+ }
110
+ var O = "group outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-lg p-2 text-left focus-visible:outline-2 focus-visible:outline-offset-2";
111
+ function S(r) {
112
+ const e = r.split(/\s+/).filter(Boolean);
113
+ return ((e[0]?.[0] ?? r[0] ?? "?") + (e.length >= 2 ? e[e.length - 1]?.[0] ?? "" : "")).toUpperCase();
114
+ }
115
+ function U({ name: r, avatarSrc: e, initials: n, className: i, ...s }) {
116
+ return /* @__PURE__ */ a("button", {
117
+ type: "button",
118
+ className: o(O, i),
119
+ ...s,
120
+ children: [
121
+ /* @__PURE__ */ a(l.Root, {
122
+ size: "xs",
123
+ children: [e && /* @__PURE__ */ t(l.Image, {
124
+ src: e,
125
+ alt: ""
126
+ }), /* @__PURE__ */ t(l.Fallback, { children: n ?? S(r) })]
127
+ }),
128
+ /* @__PURE__ */ t("span", {
129
+ className: "text-pho-primary min-w-0 flex-1 truncate text-xs",
130
+ children: r
131
+ }),
132
+ /* @__PURE__ */ t("span", {
133
+ "aria-hidden": !0,
134
+ className: x({
135
+ variant: "secondary",
136
+ size: "sm",
137
+ shape: "circle",
138
+ className: "group-hover:bg-pho-primary-hover group-hover:text-pho-secondary-hover group-data-[popup-open]:bg-pho-primary-hover group-data-[popup-open]:text-pho-secondary-hover p-1 group-data-[pressed]:scale-[0.97]"
139
+ }),
140
+ children: /* @__PURE__ */ t(w, { className: "size-4" })
141
+ })
142
+ ]
143
+ });
144
+ }
145
+ var J = {
146
+ Root: C,
147
+ Header: M,
148
+ Nav: T,
149
+ Group: D,
150
+ GroupLabel: B,
151
+ List: z,
152
+ Item: L,
153
+ Footer: F,
154
+ AccountRow: U
155
+ };
156
+ export {
157
+ J as a,
158
+ R as i,
159
+ A as n,
160
+ E as r,
161
+ _ as t
162
+ };
163
+
164
+ //# sourceMappingURL=sidebar-CivriTLb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-CivriTLb.js","names":["__iconNode"],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconArrowUpRight.mjs","../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconDots.mjs","../../src/sections/sidebar/sidebar.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M17 7l-10 10\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M8 7l9 0l0 9\", \"key\": \"svg-1\" }]];\nconst IconArrowUpRight = createReactComponent(\"outline\", \"arrow-up-right\", \"ArrowUpRight\", __iconNode);\n\nexport { __iconNode, IconArrowUpRight as default };\n//# sourceMappingURL=IconArrowUpRight.mjs.map\n","/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-2\" }]];\nconst IconDots = createReactComponent(\"outline\", \"dots\", \"Dots\", __iconNode);\n\nexport { __iconNode, IconDots as default };\n//# sourceMappingURL=IconDots.mjs.map\n","import {\n isValidElement,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentProps,\n type FC,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { IconArrowUpRight, IconDots } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Avatar } from \"../../components/avatar\";\nimport { buttonVariants } from \"../../components/button\";\n\n/**\n * Sidebar — Pho's first SECTION: a composed app rail, not a micro component.\n * It encodes the navigation rail used across Photon surfaces (the Pho docs\n * site, the dashboard): a sticky full-height column with a brand header, a\n * scrollable stack of titled nav groups, and a pinned footer that usually\n * carries the account row.\n *\n * Sections stay presentational — no router, no data fetching, no theme\n * plumbing. Links render through the host's `LinkProvider` (same contract as\n * `ButtonLink`), state arrives via props (`active`), and interactive footers\n * compose with other Pho parts (e.g. wrap `Sidebar.AccountRow` in a\n * `Menu.Trigger render={...}` to open an account menu).\n */\n\n/* ------------------------------- class map ------------------------------- */\n\n/** The rail itself — sticky, full-height, hairline right edge. */\nexport const SIDEBAR_ROOT =\n \"border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r px-4 py-5\";\n\n/** A nav row at rest and on hover. Active/inactive inks split below. */\nexport const SIDEBAR_ITEM =\n \"outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1\";\n\n/**\n * Selected row — lifted onto the primary surface with the site hairline.\n * The icon carries the same ink as the label so the pair reads as one unit.\n */\nexport const SIDEBAR_ITEM_ACTIVE =\n \"bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-fg-pho-primary\";\n\n/**\n * Resting row — secondary ink (75% alpha; tertiary at 50% fails contrast for\n * nav labels), ghost wash on hover. The icon tracks the label's ink at every\n * state instead of sitting on a fainter step.\n */\nexport const SIDEBAR_ITEM_INACTIVE = cn(\n \"text-pho-secondary *:data-icon:text-fg-pho-secondary\",\n \"hover:bg-pho-ghost-hover hover:text-pho-primary hover:*:data-icon:text-fg-pho-primary\",\n);\n\n/* --------------------------------- frame --------------------------------- */\n\ninterface RootProps extends ComponentProps<\"aside\"> {\n ref?: Ref<HTMLElement>;\n}\n\n/** The rail — a sticky, full-height `<aside>` with the hairline right edge. */\nfunction Root({ className, ...props }: RootProps) {\n return <aside className={cn(SIDEBAR_ROOT, className)} {...props} />;\n}\n\n/** Brand slot at the top of the rail — logo, wordmark, workspace switcher. */\nfunction Header({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"mb-6 flex items-center gap-2.5 px-2\", className)}\n {...props}\n />\n );\n}\n\n/** The scrollable middle — a stack of `Sidebar.Group`s. */\nfunction Nav({ className, ...props }: ComponentProps<\"nav\">) {\n return (\n <nav\n className={cn(\"flex flex-1 flex-col gap-6 overflow-y-auto\", className)}\n {...props}\n />\n );\n}\n\n/** One titled cluster of nav rows: a `GroupLabel` followed by a `List`. */\nfunction Group({ className, ...props }: ComponentProps<\"div\">) {\n return <div className={className} {...props} />;\n}\n\n/** The cluster's caption — secondary ink; quaternary (30%) is illegible. */\nfunction GroupLabel({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\n \"text-pho-secondary mb-2 px-2 text-xs font-medium\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface ListProps extends ComponentProps<\"ul\"> {\n /** Lay the rows out in two columns — for long, flat registries. */\n columns?: boolean;\n}\n\n/** The rows of a group. `columns` switches to a two-column grid. */\nfunction List({ className, columns, ...props }: ListProps) {\n return (\n <ul\n className={cn(\n // list-none/m-0/p-0 shield the rail from host prose styles.\n \"m-0 list-none p-0 *:m-0\",\n columns\n ? \"grid grid-cols-2 gap-x-2 gap-y-0.5\"\n : \"flex flex-col gap-0.5\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Pinned bottom cluster — docs link, account row, theme toggle. */\nfunction Footer({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div className={cn(\"mt-4 flex flex-col gap-0.5\", className)} {...props} />\n );\n}\n\n/* --------------------------------- item ---------------------------------- */\n\n/** An icon slot — a component (`IconBook`) or element (`<IconBook />`). */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\nfunction renderIcon(icon: Slottable): ReactNode {\n if (icon == null || icon === false) return null;\n let content: ReactNode = null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n content = <Icon />;\n } else if (isValidElement(icon)) {\n content = icon;\n }\n if (content == null) return null;\n return (\n <span\n data-icon\n className=\"pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-4 [&>svg]:shrink-0\"\n >\n {content}\n </span>\n );\n}\n\ninterface ItemBaseProps {\n /** Slot before the label — a component (`IconBook`) or element. */\n icon?: Slottable;\n /** Marks the row as the current destination (adds `aria-current=\"page\"`). */\n active?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface SidebarItemLinkProps\n extends\n ItemBaseProps,\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof ItemBaseProps> {\n /** Destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Leaves the app: opens in a new tab and renders a ↗ cue. */\n external?: boolean;\n}\n\nexport interface SidebarItemButtonProps\n extends\n ItemBaseProps,\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof ItemBaseProps> {\n href?: undefined;\n external?: undefined;\n}\n\nexport type SidebarItemProps = SidebarItemLinkProps | SidebarItemButtonProps;\n\n/**\n * A nav row — a link when `href` is given (rendered through the host's\n * `LinkProvider`), otherwise a button. `active` lifts the row onto the\n * primary surface; `external` opens a new tab and appends a ↗ cue.\n */\nfunction Item({\n icon,\n active,\n className,\n children,\n href,\n external,\n ...rest\n}: SidebarItemProps) {\n const Link = useLinkComponent();\n const rowClass = cn(\n SIDEBAR_ITEM,\n active ? SIDEBAR_ITEM_ACTIVE : SIDEBAR_ITEM_INACTIVE,\n className,\n );\n const label = <span className=\"min-w-0 flex-1 truncate\">{children}</span>;\n\n if (href !== undefined) {\n return (\n <li>\n <Link\n href={href}\n aria-current={active ? \"page\" : undefined}\n {...(external && { target: \"_blank\", rel: \"noopener noreferrer\" })}\n {...(rest as AnchorHTMLAttributes<HTMLAnchorElement>)}\n className={rowClass}\n >\n {renderIcon(icon)}\n {label}\n {external && (\n <>\n {renderIcon(<IconArrowUpRight aria-hidden />)}\n <span className=\"sr-only\">(opens in a new tab)</span>\n </>\n )}\n </Link>\n </li>\n );\n }\n\n return (\n <li>\n <button\n type=\"button\"\n aria-current={active ? \"page\" : undefined}\n {...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}\n className={rowClass}\n >\n {renderIcon(icon)}\n {label}\n </button>\n </li>\n );\n}\n\n/* ------------------------------ account row ------------------------------ */\n\n/**\n * Whole-row account trigger. The row itself stays visually quiet — it only\n * carries the `group` state (hover/press/popup-open) and the focus ring;\n * all visual feedback renders on the ⋯ circle via `group-*` classes.\n */\nconst ACCOUNT_ROW =\n \"group outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-lg p-2 text-left focus-visible:outline-2 focus-visible:outline-offset-2\";\n\n/** Avatar initials: first + last word initial, else the first character. */\nfunction initialsFrom(name: string): string {\n const words = name.split(/\\s+/).filter(Boolean);\n const first = words[0]?.[0] ?? name[0] ?? \"?\";\n const last = words.length >= 2 ? (words[words.length - 1]?.[0] ?? \"\") : \"\";\n return (first + last).toUpperCase();\n}\n\nexport interface SidebarAccountRowProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"children\"\n> {\n /** Display name — also the source for fallback initials. */\n name: string;\n /** Avatar image URL; falls back to initials while loading or on error. */\n avatarSrc?: string;\n /** Override the derived initials. */\n initials?: string;\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * The account chip pinned to the rail's footer — an xs avatar, the name, and\n * a decorative ⋯ circle. The WHOLE row is one button, but the row is only the\n * control — the circle is the indicator: hover, press, and menu-open feedback\n * all render on the ⋯ (a span wearing the stock secondary-circle button\n * classes), driven by the row's `group` state, so it reads as a button\n * without nesting one.\n *\n * It renders a plain `<button>` and forwards every prop, so it slots straight\n * into a menu: `<Menu.Trigger render={<Sidebar.AccountRow name=\"…\" />} />` —\n * Base UI's `data-popup-open` / `data-pressed` states light up the circle.\n */\nfunction AccountRow({\n name,\n avatarSrc,\n initials,\n className,\n ...props\n}: SidebarAccountRowProps) {\n return (\n <button type=\"button\" className={cn(ACCOUNT_ROW, className)} {...props}>\n <Avatar.Root size=\"xs\">\n {avatarSrc && <Avatar.Image src={avatarSrc} alt=\"\" />}\n <Avatar.Fallback>{initials ?? initialsFrom(name)}</Avatar.Fallback>\n </Avatar.Root>\n <span className=\"text-pho-primary min-w-0 flex-1 truncate text-xs\">\n {name}\n </span>\n <span\n aria-hidden\n className={buttonVariants({\n variant: \"secondary\",\n size: \"sm\",\n shape: \"circle\",\n className:\n \"group-hover:bg-pho-primary-hover group-hover:text-pho-secondary-hover group-data-[popup-open]:bg-pho-primary-hover group-data-[popup-open]:text-pho-secondary-hover p-1 group-data-[pressed]:scale-[0.97]\",\n })}\n >\n <IconDots className=\"size-4\" />\n </span>\n </button>\n );\n}\n\n/* -------------------------------- assembly ------------------------------- */\n\n/**\n * The composed sidebar rail. Assemble the frame parts and fill them with\n * rows; wire interactivity by composing other Pho parts around them:\n *\n * ```tsx\n * <Sidebar.Root>\n * <Sidebar.Header>…brand…</Sidebar.Header>\n * <Sidebar.Nav>\n * <Sidebar.Group>\n * <Sidebar.GroupLabel>Pages</Sidebar.GroupLabel>\n * <Sidebar.List>\n * <Sidebar.Item href=\"/\" icon={IconHome} active>\n * Overview\n * </Sidebar.Item>\n * </Sidebar.List>\n * </Sidebar.Group>\n * </Sidebar.Nav>\n * <Sidebar.Footer>\n * <Menu.Root>\n * <Menu.Trigger render={<Sidebar.AccountRow name=\"Ada Lovelace\" />} />\n * …\n * </Menu.Root>\n * </Sidebar.Footer>\n * </Sidebar.Root>\n * ```\n */\nexport const Sidebar = {\n Root,\n Header,\n Nav,\n Group,\n GroupLabel,\n List,\n Item,\n Footer,\n AccountRow,\n};\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -1,49 +1,6 @@
1
1
  "use client";
2
- import { t as s } from "../chunks/cn-7uGUzCOM.js";
3
- import { t as l } from "../chunks/resolve-variant-CcD8iG5l.js";
4
- import "react";
5
- import { jsx as r } from "react/jsx-runtime";
6
- import { Avatar as i } from "@base-ui/react/avatar";
7
- var o = {
8
- sm: {
9
- classes: "size-8 text-xs",
10
- description: "Small — dense lists"
11
- },
12
- md: {
13
- classes: "size-10 text-sm",
14
- description: "Medium — the default"
15
- },
16
- lg: {
17
- classes: "size-12 text-md",
18
- description: "Large — profile headers"
19
- }
20
- };
21
- function n({ className: e, size: t = "md", ...a }) {
22
- return /* @__PURE__ */ r(i.Root, {
23
- className: s("squircle bg-pho-tertiary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none", l(o, t), e),
24
- ...a
25
- });
26
- }
27
- function m({ className: e, ...t }) {
28
- return /* @__PURE__ */ r(i.Image, {
29
- className: s("size-full object-cover", e),
30
- ...t
31
- });
32
- }
33
- function c({ className: e, ...t }) {
34
- return /* @__PURE__ */ r(i.Fallback, {
35
- className: s("flex size-full items-center justify-center", e),
36
- ...t
37
- });
38
- }
39
- var x = {
40
- Root: n,
41
- Image: m,
42
- Fallback: c
43
- };
2
+ import { n as a, t as r } from "../chunks/avatar-Bb4RZQIa.js";
44
3
  export {
45
- o as AVATAR_SIZES,
46
- x as Avatar
4
+ r as AVATAR_SIZES,
5
+ a as Avatar
47
6
  };
48
-
49
- //# sourceMappingURL=avatar.js.map
package/dist/index.js CHANGED
@@ -1,28 +1,34 @@
1
1
  "use client";
2
- import { t as o } from "./chunks/cn-7uGUzCOM.js";
2
+ import { t as a } from "./chunks/cn-7uGUzCOM.js";
3
3
  import { n as s, r, t as n } from "./chunks/link-provider-9TFNyDqO.js";
4
4
  import { t as m } from "./chunks/resolve-variant-CcD8iG5l.js";
5
5
  import "./utils.js";
6
- import { a as p, c as u, i as B, l as T, n as c, o as E, r as S, s as l, t as N, u as _ } from "./chunks/button-CQ4cZvkC.js";
7
- import { t as C } from "./chunks/input-DbJpdu6v.js";
8
- import { t as L } from "./chunks/close-button-CNhn8z0w.js";
9
- import { t as U } from "./chunks/dialog-7oOe-eaO.js";
6
+ import { a as T, c as f, i as B, l as I, n as p, o as S, r as u, s as _, t as A, u as c } from "./chunks/button-CQ4cZvkC.js";
7
+ import { a as C, i as N, n as O, r as V, t as l } from "./chunks/sidebar-CivriTLb.js";
8
+ import { t as k } from "./chunks/input-DbJpdu6v.js";
9
+ import { t as U } from "./chunks/close-button-CNhn8z0w.js";
10
+ import { t as b } from "./chunks/dialog-7oOe-eaO.js";
10
11
  export {
11
- N as BUTTON_SHAPES,
12
- c as BUTTON_SIZES,
13
- S as BUTTON_VARIANTS,
12
+ A as BUTTON_SHAPES,
13
+ p as BUTTON_SIZES,
14
+ u as BUTTON_VARIANTS,
14
15
  B as Button,
15
- p as ButtonLink,
16
- L as CloseButton,
17
- U as Dialog,
18
- E as EFFECT_BUTTON_SIZES,
19
- l as EffectButton,
20
- u as EffectButtonLink,
21
- C as Input,
16
+ T as ButtonLink,
17
+ U as CloseButton,
18
+ b as Dialog,
19
+ S as EFFECT_BUTTON_SIZES,
20
+ _ as EffectButton,
21
+ f as EffectButtonLink,
22
+ k as Input,
22
23
  n as LinkProvider,
23
- T as buttonVariants,
24
- o as cn,
25
- _ as effectButtonVariants,
24
+ l as SIDEBAR_ITEM,
25
+ O as SIDEBAR_ITEM_ACTIVE,
26
+ V as SIDEBAR_ITEM_INACTIVE,
27
+ N as SIDEBAR_ROOT,
28
+ C as Sidebar,
29
+ I as buttonVariants,
30
+ a as cn,
31
+ c as effectButtonVariants,
26
32
  r as isReactComponent,
27
33
  m as resolveVariant,
28
34
  s as useLinkComponent
@@ -42,10 +42,10 @@ import "./primitives/toolbar.js";
42
42
  import "./primitives/tooltip.js";
43
43
  export * from "@base-ui/react/field";
44
44
  export * from "@base-ui/react/dialog";
45
+ export * from "@base-ui/react/avatar";
45
46
  export * from "@base-ui/react/accordion";
46
47
  export * from "@base-ui/react/alert-dialog";
47
48
  export * from "@base-ui/react/autocomplete";
48
- export * from "@base-ui/react/avatar";
49
49
  export * from "@base-ui/react/button";
50
50
  export * from "@base-ui/react/checkbox";
51
51
  export * from "@base-ui/react/checkbox-group";
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { a as E, i as a, n as s, r as A, t as T } from "../chunks/sidebar-CivriTLb.js";
3
+ export {
4
+ T as SIDEBAR_ITEM,
5
+ s as SIDEBAR_ITEM_ACTIVE,
6
+ A as SIDEBAR_ITEM_INACTIVE,
7
+ a as SIDEBAR_ROOT,
8
+ E as Sidebar
9
+ };
@@ -4,8 +4,12 @@ import { Avatar as BaseAvatar } from '@base-ui/react/avatar';
4
4
  type StyledProps<T extends ElementType> = Omit<ComponentProps<T>, "className"> & {
5
5
  className?: string;
6
6
  };
7
- /** Size axis — three sizes, no `xs`/`xl` (Geist-reductive). `md` is default. */
7
+ /** Size axis — four sizes, no `xl` (Geist-reductive). `md` is default. */
8
8
  export declare const AVATAR_SIZES: {
9
+ xs: {
10
+ classes: string;
11
+ description: string;
12
+ };
9
13
  sm: {
10
14
  classes: string;
11
15
  description: string;
@@ -9,3 +9,4 @@ export * from './utils';
9
9
  export * from './components/button';
10
10
  export * from './components/input';
11
11
  export * from './components/dialog';
12
+ export * from './sections/sidebar';
@@ -0,0 +1 @@
1
+ export { Sidebar, SIDEBAR_ROOT, SIDEBAR_ITEM, SIDEBAR_ITEM_ACTIVE, SIDEBAR_ITEM_INACTIVE, type SidebarItemProps, type SidebarItemLinkProps, type SidebarItemButtonProps, type SidebarAccountRowProps, } from './sidebar';
@@ -0,0 +1,139 @@
1
+ import { AnchorHTMLAttributes, ButtonHTMLAttributes, ComponentProps, FC, ReactNode, Ref } from 'react';
2
+ /**
3
+ * Sidebar — Pho's first SECTION: a composed app rail, not a micro component.
4
+ * It encodes the navigation rail used across Photon surfaces (the Pho docs
5
+ * site, the dashboard): a sticky full-height column with a brand header, a
6
+ * scrollable stack of titled nav groups, and a pinned footer that usually
7
+ * carries the account row.
8
+ *
9
+ * Sections stay presentational — no router, no data fetching, no theme
10
+ * plumbing. Links render through the host's `LinkProvider` (same contract as
11
+ * `ButtonLink`), state arrives via props (`active`), and interactive footers
12
+ * compose with other Pho parts (e.g. wrap `Sidebar.AccountRow` in a
13
+ * `Menu.Trigger render={...}` to open an account menu).
14
+ */
15
+ /** The rail itself — sticky, full-height, hairline right edge. */
16
+ export declare const SIDEBAR_ROOT = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r px-4 py-5";
17
+ /** A nav row at rest and on hover. Active/inactive inks split below. */
18
+ export declare const SIDEBAR_ITEM = "outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
19
+ /**
20
+ * Selected row — lifted onto the primary surface with the site hairline.
21
+ * The icon carries the same ink as the label so the pair reads as one unit.
22
+ */
23
+ export declare const SIDEBAR_ITEM_ACTIVE = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-fg-pho-primary";
24
+ /**
25
+ * Resting row — secondary ink (75% alpha; tertiary at 50% fails contrast for
26
+ * nav labels), ghost wash on hover. The icon tracks the label's ink at every
27
+ * state instead of sitting on a fainter step.
28
+ */
29
+ export declare const SIDEBAR_ITEM_INACTIVE: string;
30
+ interface RootProps extends ComponentProps<"aside"> {
31
+ ref?: Ref<HTMLElement>;
32
+ }
33
+ /** The rail — a sticky, full-height `<aside>` with the hairline right edge. */
34
+ declare function Root({ className, ...props }: RootProps): import("react").JSX.Element;
35
+ /** Brand slot at the top of the rail — logo, wordmark, workspace switcher. */
36
+ declare function Header({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
37
+ /** The scrollable middle — a stack of `Sidebar.Group`s. */
38
+ declare function Nav({ className, ...props }: ComponentProps<"nav">): import("react").JSX.Element;
39
+ /** One titled cluster of nav rows: a `GroupLabel` followed by a `List`. */
40
+ declare function Group({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
41
+ /** The cluster's caption — secondary ink; quaternary (30%) is illegible. */
42
+ declare function GroupLabel({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
43
+ interface ListProps extends ComponentProps<"ul"> {
44
+ /** Lay the rows out in two columns — for long, flat registries. */
45
+ columns?: boolean;
46
+ }
47
+ /** The rows of a group. `columns` switches to a two-column grid. */
48
+ declare function List({ className, columns, ...props }: ListProps): import("react").JSX.Element;
49
+ /** Pinned bottom cluster — docs link, account row, theme toggle. */
50
+ declare function Footer({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
51
+ /** An icon slot — a component (`IconBook`) or element (`<IconBook />`). */
52
+ type Slottable = ReactNode | FC<{
53
+ className?: string;
54
+ }>;
55
+ interface ItemBaseProps {
56
+ /** Slot before the label — a component (`IconBook`) or element. */
57
+ icon?: Slottable;
58
+ /** Marks the row as the current destination (adds `aria-current="page"`). */
59
+ active?: boolean;
60
+ children?: ReactNode;
61
+ className?: string;
62
+ }
63
+ export interface SidebarItemLinkProps extends ItemBaseProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof ItemBaseProps> {
64
+ /** Destination — rendered through the host's `LinkProvider`. */
65
+ href: string;
66
+ /** Leaves the app: opens in a new tab and renders a ↗ cue. */
67
+ external?: boolean;
68
+ }
69
+ export interface SidebarItemButtonProps extends ItemBaseProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof ItemBaseProps> {
70
+ href?: undefined;
71
+ external?: undefined;
72
+ }
73
+ export type SidebarItemProps = SidebarItemLinkProps | SidebarItemButtonProps;
74
+ /**
75
+ * A nav row — a link when `href` is given (rendered through the host's
76
+ * `LinkProvider`), otherwise a button. `active` lifts the row onto the
77
+ * primary surface; `external` opens a new tab and appends a ↗ cue.
78
+ */
79
+ declare function Item({ icon, active, className, children, href, external, ...rest }: SidebarItemProps): import("react").JSX.Element;
80
+ export interface SidebarAccountRowProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
81
+ /** Display name — also the source for fallback initials. */
82
+ name: string;
83
+ /** Avatar image URL; falls back to initials while loading or on error. */
84
+ avatarSrc?: string;
85
+ /** Override the derived initials. */
86
+ initials?: string;
87
+ ref?: Ref<HTMLButtonElement>;
88
+ }
89
+ /**
90
+ * The account chip pinned to the rail's footer — an xs avatar, the name, and
91
+ * a decorative ⋯ circle. The WHOLE row is one button, but the row is only the
92
+ * control — the circle is the indicator: hover, press, and menu-open feedback
93
+ * all render on the ⋯ (a span wearing the stock secondary-circle button
94
+ * classes), driven by the row's `group` state, so it reads as a button
95
+ * without nesting one.
96
+ *
97
+ * It renders a plain `<button>` and forwards every prop, so it slots straight
98
+ * into a menu: `<Menu.Trigger render={<Sidebar.AccountRow name="…" />} />` —
99
+ * Base UI's `data-popup-open` / `data-pressed` states light up the circle.
100
+ */
101
+ declare function AccountRow({ name, avatarSrc, initials, className, ...props }: SidebarAccountRowProps): import("react").JSX.Element;
102
+ /**
103
+ * The composed sidebar rail. Assemble the frame parts and fill them with
104
+ * rows; wire interactivity by composing other Pho parts around them:
105
+ *
106
+ * ```tsx
107
+ * <Sidebar.Root>
108
+ * <Sidebar.Header>…brand…</Sidebar.Header>
109
+ * <Sidebar.Nav>
110
+ * <Sidebar.Group>
111
+ * <Sidebar.GroupLabel>Pages</Sidebar.GroupLabel>
112
+ * <Sidebar.List>
113
+ * <Sidebar.Item href="/" icon={IconHome} active>
114
+ * Overview
115
+ * </Sidebar.Item>
116
+ * </Sidebar.List>
117
+ * </Sidebar.Group>
118
+ * </Sidebar.Nav>
119
+ * <Sidebar.Footer>
120
+ * <Menu.Root>
121
+ * <Menu.Trigger render={<Sidebar.AccountRow name="Ada Lovelace" />} />
122
+ * …
123
+ * </Menu.Root>
124
+ * </Sidebar.Footer>
125
+ * </Sidebar.Root>
126
+ * ```
127
+ */
128
+ export declare const Sidebar: {
129
+ Root: typeof Root;
130
+ Header: typeof Header;
131
+ Nav: typeof Nav;
132
+ Group: typeof Group;
133
+ GroupLabel: typeof GroupLabel;
134
+ List: typeof List;
135
+ Item: typeof Item;
136
+ Footer: typeof Footer;
137
+ AccountRow: typeof AccountRow;
138
+ };
139
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -185,6 +185,10 @@
185
185
  "types": "./dist/src/components/notification/index.d.ts",
186
186
  "import": "./dist/components/notification.js"
187
187
  },
188
+ "./sections/sidebar": {
189
+ "types": "./dist/src/sections/sidebar/index.d.ts",
190
+ "import": "./dist/sections/sidebar.js"
191
+ },
188
192
  "./icons": {
189
193
  "types": "./dist/src/icons/index.d.ts",
190
194
  "import": "./dist/icons.js"
@@ -178,8 +178,10 @@
178
178
  --text-color-fg-pho-error: light-dark(#e01e3d, #ff5c75);
179
179
 
180
180
  /* =============== Semantic: background (bg-pho-*) =============== */
181
- /* App/page canvas — the site's "Primary BG". */
182
- --background-color-pho-page: light-dark(#f5f5f7, #000);
181
+ /* App/page canvas — the dashboard's backdrop (app.photon.codes sign-in).
182
+ Near-white neutral gray, warmer than the old #f5f5f7 (which had a faint
183
+ blue cast) and one step off the #fff card surface. */
184
+ --background-color-pho-page: light-dark(#fafafa, #000);
183
185
  /* Main surface: cards, inputs, panels — the site's "BG/Card". */
184
186
  --background-color-pho-primary: light-dark(#fff, #101010);
185
187
  --background-color-pho-primary-hover: light-dark(#f5f5f7, #1a1a1a);
@@ -1 +0,0 @@
1
- {"version":3,"file":"avatar.js","names":[],"sources":["../../src/components/avatar/avatar.tsx"],"sourcesContent":["import { type ComponentProps, type ElementType } from \"react\";\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\n/** Size axis — three sizes, no `xs`/`xl` (Geist-reductive). `md` is default. */\nexport const AVATAR_SIZES = {\n sm: { classes: \"size-8 text-xs\", description: \"Small — dense lists\" },\n md: { classes: \"size-10 text-sm\", description: \"Medium — the default\" },\n lg: { classes: \"size-12 text-md\", description: \"Large — profile headers\" },\n} satisfies VariantMap;\n\nexport type AvatarSize = keyof typeof AVATAR_SIZES;\n\ninterface RootProps extends StyledProps<typeof BaseAvatar.Root> {\n /** Size variant. @default \"md\" */\n size?: AvatarSize;\n}\n\nfunction Root({ className, size = \"md\", ...props }: RootProps) {\n return (\n <BaseAvatar.Root\n className={cn(\n \"squircle bg-pho-tertiary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none\",\n resolveVariant(AVATAR_SIZES, size),\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Image({ className, ...props }: StyledProps<typeof BaseAvatar.Image>) {\n return (\n <BaseAvatar.Image\n className={cn(\"size-full object-cover\", className)}\n {...props}\n />\n );\n}\n\nfunction Fallback({\n className,\n ...props\n}: StyledProps<typeof BaseAvatar.Fallback>) {\n return (\n <BaseAvatar.Fallback\n className={cn(\"flex size-full items-center justify-center\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Avatar built on Base UI `Avatar`. Compose `Avatar.Image` with an\n * `Avatar.Fallback` (initials or icon) that shows while the image loads or if\n * it fails. Set `size` on `Avatar.Root`.\n */\nexport const Avatar = { Root, Image, Fallback };\n"],"mappings":";;;;;;AAcA,IAAa,IAAe;AAAA,EAC1B,IAAI;AAAA,IAAE,SAAS;AAAA,IAAkB,aAAa;AAAA,EAAsB;AAAA,EACpE,IAAI;AAAA,IAAE,SAAS;AAAA,IAAmB,aAAa;AAAA,EAAuB;AAAA,EACtE,IAAI;AAAA,IAAE,SAAS;AAAA,IAAmB,aAAa;AAAA,EAA0B;AAC3E;AASA,SAAS,EAAK,EAAE,WAAA,GAAW,MAAA,IAAO,MAAM,GAAG,EAAA,GAAoB;AAC7D,SACE,gBAAA,EAAC,EAAW,MAAZ;AAAA,IACE,WAAW,EACT,2KACA,EAAe,GAAc,CAAI,GACjC,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAAG,0BAA0B,CAAS;AAAA,IACjD,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAS,EAChB,WAAA,GACA,GAAG,EAAA,GACuC;AAC1C,SACE,gBAAA,EAAC,EAAW,UAAZ;AAAA,IACE,WAAW,EAAG,8CAA8C,CAAS;AAAA,IACrE,GAAI;AAAA,EACL,CAAA;AAEL;AAOA,IAAa,IAAS;AAAA,EAAE,MAAA;AAAA,EAAM,OAAA;AAAA,EAAO,UAAA;AAAS"}