@lessly/ui 0.4.1 → 0.6.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/index.js CHANGED
@@ -11,7 +11,7 @@ import { Slot } from "@radix-ui/react-slot";
11
11
  import { cva } from "class-variance-authority";
12
12
  import { jsx } from "react/jsx-runtime";
13
13
  var buttonVariants = cva(
14
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus disabled:pointer-events-none disabled:opacity-50",
14
+ "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus disabled:pointer-events-none disabled:opacity-50",
15
15
  {
16
16
  variants: {
17
17
  variant: {
@@ -2618,8 +2618,9 @@ function initialsOf(name) {
2618
2618
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
2619
2619
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
2620
2620
  }
2621
- function UserMenu({ user, items, collapsed = false, align = "end" }) {
2622
- const avatar = /* @__PURE__ */ jsxs25(Avatar, { className: collapsed ? "size-9" : "size-8", children: [
2621
+ function UserMenu({ user, items, collapsed = false, align = "start" }) {
2622
+ const secondLine = user.subtitle ?? user.email;
2623
+ const avatar = /* @__PURE__ */ jsxs25(Avatar, { className: collapsed ? "size-9" : "size-7", children: [
2623
2624
  user.avatarUrl && /* @__PURE__ */ jsx52(AvatarImage, { src: user.avatarUrl, alt: user.name }),
2624
2625
  /* @__PURE__ */ jsx52(AvatarFallback, { className: "text-xs", children: initialsOf(user.name) })
2625
2626
  ] });
@@ -2635,14 +2636,14 @@ function UserMenu({ user, items, collapsed = false, align = "end" }) {
2635
2636
  "button",
2636
2637
  {
2637
2638
  type: "button",
2638
- className: "flex w-full items-center gap-2.5 rounded-xl px-2.5 py-1.5 text-left transition-colors hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
2639
+ className: "flex w-full items-center gap-2.5 rounded-xl px-1.5 py-[5px] text-left transition-colors hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
2639
2640
  children: [
2640
2641
  avatar,
2641
- /* @__PURE__ */ jsxs25("span", { className: "min-w-0 flex-1", children: [
2642
- /* @__PURE__ */ jsx52("span", { className: "block truncate text-sm font-medium text-text-primary", children: user.name }),
2643
- user.email && /* @__PURE__ */ jsx52("span", { className: "block truncate text-xs text-text-tertiary", children: user.email })
2642
+ /* @__PURE__ */ jsxs25("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
2643
+ /* @__PURE__ */ jsx52("span", { className: "truncate text-sm leading-[normal] font-medium text-text-primary", children: user.name }),
2644
+ secondLine && /* @__PURE__ */ jsx52("span", { className: "truncate text-xs leading-[normal] text-text-tertiary", children: secondLine })
2644
2645
  ] }),
2645
- /* @__PURE__ */ jsx52(ChevronsUpDown, { className: "size-4 shrink-0 text-icon-secondary", "aria-hidden": "true" })
2646
+ /* @__PURE__ */ jsx52(ChevronsUpDown, { size: 14, "aria-hidden": "true", className: "shrink-0 text-text-tertiary" })
2646
2647
  ]
2647
2648
  }
2648
2649
  );
@@ -2661,7 +2662,7 @@ function UserMenu({ user, items, collapsed = false, align = "end" }) {
2661
2662
  children: [
2662
2663
  /* @__PURE__ */ jsxs25(DropdownMenuLabel, { className: "px-2 pt-1.5 pb-1 text-xs font-medium text-text-tertiary", children: [
2663
2664
  /* @__PURE__ */ jsx52("span", { className: "block truncate text-sm font-medium text-text-primary", children: user.name }),
2664
- user.email && /* @__PURE__ */ jsx52("span", { className: "block truncate text-xs font-normal text-text-tertiary", children: user.email })
2665
+ secondLine && /* @__PURE__ */ jsx52("span", { className: "block truncate text-xs font-normal text-text-tertiary", children: secondLine })
2665
2666
  ] }),
2666
2667
  /* @__PURE__ */ jsx52(DropdownMenuSeparator, { className: "mx-0 shrink-0 bg-border-subtle" }),
2667
2668
  items.map((item, i) => {
@@ -2938,8 +2939,8 @@ function SidebarNav({
2938
2939
  "nav",
2939
2940
  {
2940
2941
  className: cn(
2941
- "flex flex-col gap-0.5",
2942
- collapsed ? "items-center px-1" : "px-3",
2942
+ "flex flex-col gap-2",
2943
+ collapsed && "items-center",
2943
2944
  className
2944
2945
  ),
2945
2946
  children: items.map((item) => {
@@ -2964,13 +2965,27 @@ function SidebarNav({
2964
2965
 
2965
2966
  // src/components/app-sidebar.tsx
2966
2967
  import { PanelLeft } from "lucide-react";
2967
- import { jsx as jsx59, jsxs as jsxs29 } from "react/jsx-runtime";
2968
+ import { Fragment as Fragment3, jsx as jsx59, jsxs as jsxs29 } from "react/jsx-runtime";
2969
+ function Divider({ collapsed, testId, className }) {
2970
+ return /* @__PURE__ */ jsx59(
2971
+ "div",
2972
+ {
2973
+ "data-testid": testId,
2974
+ className: cn(
2975
+ "h-px shrink-0 bg-border-subtle",
2976
+ collapsed ? "ml-[6px] w-[40px]" : "ml-[14px] w-[220px]",
2977
+ className
2978
+ )
2979
+ }
2980
+ );
2981
+ }
2968
2982
  function AppSidebar({
2969
2983
  items,
2970
2984
  logo,
2971
2985
  logoCollapsed,
2972
2986
  header,
2973
2987
  footer,
2988
+ collapsible = false,
2974
2989
  collapsed: controlled,
2975
2990
  defaultCollapsed,
2976
2991
  onCollapsedChange,
@@ -2984,18 +2999,49 @@ function AppSidebar({
2984
2999
  defaultCollapsed,
2985
3000
  onCollapsedChange
2986
3001
  });
3002
+ const pin = collapsible ? /* @__PURE__ */ jsx59(
3003
+ "button",
3004
+ {
3005
+ type: "button",
3006
+ onClick: toggle,
3007
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
3008
+ "aria-expanded": !collapsed,
3009
+ className: "flex size-6 shrink-0 items-center justify-center rounded-md text-text-secondary transition-colors hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
3010
+ children: /* @__PURE__ */ jsx59(PanelLeft, { className: "size-4", "aria-hidden": "true" })
3011
+ }
3012
+ ) : null;
2987
3013
  return /* @__PURE__ */ jsxs29(
2988
3014
  "aside",
2989
3015
  {
2990
3016
  "data-collapsed": collapsed,
2991
3017
  className: cn(
2992
- "flex h-full flex-col overflow-hidden rounded-2xl border border-border-subtle bg-bg-surface transition-[width] duration-200 ease-in-out",
2993
- collapsed ? "w-[52px]" : "w-[220px]",
3018
+ "flex h-full shrink-0 flex-col overflow-hidden transition-[width] duration-200 ease-in-out",
3019
+ collapsed ? "w-[52px]" : "w-[248px]",
2994
3020
  className
2995
3021
  ),
2996
3022
  children: [
2997
- collapsed ? /* @__PURE__ */ jsx59("div", { className: "flex justify-center border-b border-border-subtle p-2", children: header ?? /* @__PURE__ */ jsxs29("div", { className: "group relative flex size-8 items-center justify-center", children: [
2998
- /* @__PURE__ */ jsx59("span", { className: "pointer-events-none transition-opacity group-hover:opacity-0 group-focus-within:opacity-0", children: logoCollapsed ?? logo }),
3023
+ /* @__PURE__ */ jsx59("div", { className: cn("flex items-center gap-2", collapsed ? "justify-center px-1 pt-2" : "px-3 pt-[13px]"), children: collapsed ? collapsible ? logoCollapsed ?? logo ? (
3024
+ /* The collapsed rail (52px) can't fit the logo mark beside the pin side by side —
3025
+ so the pin overlays the mark instead, revealed on hover/focus, same as pre-flat-rail. */
3026
+ /* @__PURE__ */ jsxs29("div", { className: "group relative flex size-8 items-center justify-center", children: [
3027
+ /* @__PURE__ */ jsx59("span", { className: "pointer-events-none transition-opacity group-hover:opacity-0 group-focus-within:opacity-0", children: logoCollapsed ?? logo }),
3028
+ /* @__PURE__ */ jsx59(
3029
+ "button",
3030
+ {
3031
+ type: "button",
3032
+ onClick: toggle,
3033
+ "aria-label": "Expand sidebar",
3034
+ "aria-expanded": false,
3035
+ className: "absolute inset-0 flex items-center justify-center rounded-md text-text-secondary opacity-0 transition-opacity hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-border-focus group-hover:opacity-100 group-focus-within:opacity-100",
3036
+ children: /* @__PURE__ */ jsx59(PanelLeft, { className: "size-4", "aria-hidden": "true" })
3037
+ }
3038
+ )
3039
+ ] })
3040
+ ) : (
3041
+ /* No mark to hide behind (no `logoCollapsed`/`logo` given — `header` doesn't
3042
+ count, it's expanded-only). Hover-revealing the pin over nothing would leave
3043
+ an invisible 32px box as the only way back to expanded, so render it plain
3044
+ and fully visible instead. */
2999
3045
  /* @__PURE__ */ jsx59(
3000
3046
  "button",
3001
3047
  {
@@ -3003,77 +3049,290 @@ function AppSidebar({
3003
3049
  onClick: toggle,
3004
3050
  "aria-label": "Expand sidebar",
3005
3051
  "aria-expanded": false,
3006
- className: "absolute inset-0 flex items-center justify-center rounded-md text-text-secondary opacity-0 transition-opacity hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-border-focus group-hover:opacity-100 group-focus-within:opacity-100",
3052
+ className: "flex size-8 shrink-0 items-center justify-center rounded-md text-text-secondary transition-colors hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
3007
3053
  children: /* @__PURE__ */ jsx59(PanelLeft, { className: "size-4", "aria-hidden": "true" })
3008
3054
  }
3009
3055
  )
3010
- ] }) }) : /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between border-b border-border-subtle p-3", children: [
3056
+ ) : logoCollapsed ?? logo : /* @__PURE__ */ jsxs29(Fragment3, { children: [
3011
3057
  header ?? logo,
3012
- /* @__PURE__ */ jsx59(
3013
- "button",
3014
- {
3015
- type: "button",
3016
- onClick: toggle,
3017
- "aria-label": "Collapse sidebar",
3018
- "aria-expanded": true,
3019
- className: "flex size-6 items-center justify-center rounded-md text-text-secondary transition-colors hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
3020
- children: /* @__PURE__ */ jsx59(PanelLeft, { className: "size-4", "aria-hidden": "true" })
3021
- }
3022
- )
3023
- ] }),
3024
- /* @__PURE__ */ jsx59("div", { className: "flex-1 overflow-y-auto overflow-x-hidden pt-3 pb-2", children: /* @__PURE__ */ jsx59(SidebarNav, { items, collapsed, basePath, LinkComponent, isItemActive }) }),
3025
- footer && /* @__PURE__ */ jsx59("div", { className: cn("border-t border-border-subtle", collapsed ? "px-1 py-2" : "px-3 py-3"), children: footer })
3058
+ pin
3059
+ ] }) }),
3060
+ /* @__PURE__ */ jsx59(Divider, { collapsed, testId: "sidebar-divider-header", className: "mt-3" }),
3061
+ /* @__PURE__ */ jsx59(
3062
+ "div",
3063
+ {
3064
+ "data-testid": "sidebar-scroll",
3065
+ className: cn(
3066
+ "min-h-0 flex-1 overflow-y-auto overflow-x-hidden pt-3 pb-2",
3067
+ collapsed ? "px-1" : "px-3"
3068
+ ),
3069
+ children: /* @__PURE__ */ jsx59(SidebarNav, { items, collapsed, basePath, LinkComponent, isItemActive })
3070
+ }
3071
+ ),
3072
+ footer && /* @__PURE__ */ jsxs29(Fragment3, { children: [
3073
+ /* @__PURE__ */ jsx59(Divider, { collapsed, testId: "sidebar-divider-account" }),
3074
+ /* @__PURE__ */ jsx59("div", { className: cn("py-2", collapsed ? "px-1" : "px-3"), children: footer })
3075
+ ] })
3026
3076
  ]
3027
3077
  }
3028
3078
  );
3029
3079
  }
3030
3080
 
3031
- // src/components/app-shell.tsx
3081
+ // src/components/sidebar-product-header.tsx
3032
3082
  import * as React50 from "react";
3083
+ import { Slot as Slot5 } from "@radix-ui/react-slot";
3084
+ import { ChevronsUpDown as ChevronsUpDown2 } from "lucide-react";
3085
+ import { Fragment as Fragment4, jsx as jsx60, jsxs as jsxs30 } from "react/jsx-runtime";
3086
+ var SidebarProductHeader = React50.forwardRef(
3087
+ function SidebarProductHeader2({ name, icon, trailing, asChild = false, className, children, ...props }, ref) {
3088
+ const adornment = trailing === void 0 ? /* @__PURE__ */ jsx60(ChevronsUpDown2, { size: 14, "aria-hidden": "true", className: "shrink-0 text-text-tertiary" }) : trailing;
3089
+ const content = /* @__PURE__ */ jsxs30(Fragment4, { children: [
3090
+ icon && /* @__PURE__ */ jsx60("span", { className: "flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-md", "aria-hidden": "true", children: icon }),
3091
+ /* @__PURE__ */ jsx60("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-text-primary", children: name }),
3092
+ adornment && /* @__PURE__ */ jsx60("span", { className: "flex shrink-0 items-center", children: adornment })
3093
+ ] });
3094
+ const classes = cn("flex w-full items-center gap-2.5 rounded-xl px-1.5 py-0 text-left", className);
3095
+ if (asChild) {
3096
+ const child = React50.Children.only(children);
3097
+ return /* @__PURE__ */ jsx60(Slot5, { ref, className: classes, ...props, children: React50.cloneElement(child, void 0, content) });
3098
+ }
3099
+ return /* @__PURE__ */ jsx60("button", { ref, type: "button", className: classes, ...props, children: content });
3100
+ }
3101
+ );
3102
+ SidebarProductHeader.displayName = "SidebarProductHeader";
3103
+
3104
+ // src/components/app-shell.tsx
3105
+ import * as React51 from "react";
3033
3106
  import { Menu as Menu2 } from "lucide-react";
3034
3107
  import { useLocation as useLocation2 } from "react-router";
3035
- import { jsx as jsx60, jsxs as jsxs30 } from "react/jsx-runtime";
3108
+ import { jsx as jsx61, jsxs as jsxs31 } from "react/jsx-runtime";
3036
3109
  function expandedSidebar(sidebar) {
3037
- return React50.isValidElement(sidebar) ? React50.cloneElement(sidebar, { collapsed: false }) : sidebar;
3110
+ return React51.isValidElement(sidebar) ? React51.cloneElement(sidebar, { collapsed: false }) : sidebar;
3038
3111
  }
3039
3112
  function AppShell({ sidebar, topBar, children, className }) {
3040
3113
  const { pathname } = useLocation2();
3041
- const [mobileOpen, setMobileOpen] = React50.useState(false);
3042
- React50.useEffect(() => {
3114
+ const [mobileOpen, setMobileOpen] = React51.useState(false);
3115
+ React51.useEffect(() => {
3043
3116
  setMobileOpen(false);
3044
3117
  }, [pathname]);
3045
3118
  const isMobile = useIsMobile();
3046
3119
  if (isMobile) {
3047
- return /* @__PURE__ */ jsx60(TooltipProvider, { children: /* @__PURE__ */ jsxs30("div", { className: cn("flex min-h-screen flex-col bg-bg-sunken", className), children: [
3048
- /* @__PURE__ */ jsxs30("header", { className: "flex shrink-0 items-center justify-between border-b border-border-subtle bg-bg-surface px-4 py-3", children: [
3049
- /* @__PURE__ */ jsxs30(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: [
3050
- /* @__PURE__ */ jsx60(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx60(Button, { variant: "ghost", size: "icon", className: "size-8 text-text-secondary", "aria-label": "Open navigation", children: /* @__PURE__ */ jsx60(Menu2, { className: "size-5" }) }) }),
3051
- /* @__PURE__ */ jsxs30(SheetContent, { side: "left", className: "w-[240px] bg-bg-surface p-0", children: [
3052
- /* @__PURE__ */ jsx60(SheetTitle, { className: "sr-only", children: "Navigation" }),
3053
- /* @__PURE__ */ jsx60(SheetDescription, { className: "sr-only", children: "Application navigation menu" }),
3120
+ return /* @__PURE__ */ jsx61(TooltipProvider, { children: /* @__PURE__ */ jsxs31("div", { className: cn("flex min-h-screen flex-col bg-bg-surface", className), children: [
3121
+ /* @__PURE__ */ jsxs31("header", { className: "flex shrink-0 items-center justify-between border-b border-border-subtle bg-bg-surface px-4 py-3", children: [
3122
+ /* @__PURE__ */ jsxs31(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: [
3123
+ /* @__PURE__ */ jsx61(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx61(Button, { variant: "ghost", size: "icon", className: "size-8 text-text-secondary", "aria-label": "Open navigation", children: /* @__PURE__ */ jsx61(Menu2, { className: "size-5" }) }) }),
3124
+ /* @__PURE__ */ jsxs31(SheetContent, { side: "left", className: "w-[248px] bg-bg-surface p-0", children: [
3125
+ /* @__PURE__ */ jsx61(SheetTitle, { className: "sr-only", children: "Navigation" }),
3126
+ /* @__PURE__ */ jsx61(SheetDescription, { className: "sr-only", children: "Application navigation menu" }),
3054
3127
  expandedSidebar(sidebar)
3055
3128
  ] })
3056
3129
  ] }),
3057
- /* @__PURE__ */ jsx60("div", { className: "flex items-center gap-2", children: topBar })
3130
+ /* @__PURE__ */ jsx61("div", { className: "flex items-center gap-2", children: topBar })
3058
3131
  ] }),
3059
- /* @__PURE__ */ jsx60("main", { className: "flex-1 overflow-auto p-4", children })
3132
+ /* @__PURE__ */ jsx61("main", { className: "flex-1 overflow-auto", children })
3060
3133
  ] }) });
3061
3134
  }
3062
- return /* @__PURE__ */ jsx60(TooltipProvider, { children: /* @__PURE__ */ jsxs30("div", { className: cn("flex min-h-screen gap-2 bg-bg-sunken p-4", className), children: [
3063
- /* @__PURE__ */ jsx60("div", { className: "sticky top-4 h-[calc(100vh-32px)] shrink-0", children: sidebar }),
3064
- /* @__PURE__ */ jsxs30("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl border border-border-subtle bg-bg-surface", children: [
3065
- /* @__PURE__ */ jsx60("div", { className: "flex min-h-[52px] shrink-0 items-center justify-end gap-2 border-b border-border-subtle px-5", children: topBar }),
3066
- /* @__PURE__ */ jsx60("main", { className: "flex-1 overflow-auto px-7 py-8", children })
3135
+ return /* @__PURE__ */ jsx61(TooltipProvider, { children: /* @__PURE__ */ jsxs31("div", { className: cn("flex min-h-screen gap-3 bg-bg-surface p-4", className), children: [
3136
+ /* @__PURE__ */ jsx61("div", { className: "sticky top-4 h-[calc(100vh-32px)] shrink-0", children: sidebar }),
3137
+ /* @__PURE__ */ jsxs31("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden", children: [
3138
+ topBar && /* @__PURE__ */ jsx61("div", { className: "flex shrink-0 items-center justify-end gap-2", children: topBar }),
3139
+ /* @__PURE__ */ jsx61("main", { className: "flex-1 overflow-y-auto", children })
3067
3140
  ] })
3068
3141
  ] }) });
3069
3142
  }
3070
3143
  var AuthenticatedLayout = AppShell;
3071
3144
 
3145
+ // src/components/grid.tsx
3146
+ import * as React52 from "react";
3147
+ import { Slot as Slot6 } from "@radix-ui/react-slot";
3148
+ import { jsx as jsx62 } from "react/jsx-runtime";
3149
+ var Grid = React52.forwardRef(
3150
+ ({ fluid = false, asChild = false, className, ...props }, ref) => {
3151
+ const Comp = asChild ? Slot6 : "div";
3152
+ return /* @__PURE__ */ jsx62(
3153
+ Comp,
3154
+ {
3155
+ ref,
3156
+ className: cn(
3157
+ "grid grid-cols-4 md:grid-cols-8 xl:grid-cols-12",
3158
+ "gap-gutter px-grid-sm md:px-grid-md xl:px-grid-xl",
3159
+ !fluid && "mx-auto max-w-container",
3160
+ className
3161
+ ),
3162
+ ...props
3163
+ }
3164
+ );
3165
+ }
3166
+ );
3167
+ Grid.displayName = "Grid";
3168
+
3169
+ // src/components/col.tsx
3170
+ import * as React53 from "react";
3171
+ import { Slot as Slot7 } from "@radix-ui/react-slot";
3172
+ import { jsx as jsx63 } from "react/jsx-runtime";
3173
+ var TIER_MAX = { base: 4, md: 8, xl: 12 };
3174
+ var SPAN = {
3175
+ base: { 1: "col-span-1", 2: "col-span-2", 3: "col-span-3", 4: "col-span-4" },
3176
+ md: {
3177
+ 1: "md:col-span-1",
3178
+ 2: "md:col-span-2",
3179
+ 3: "md:col-span-3",
3180
+ 4: "md:col-span-4",
3181
+ 5: "md:col-span-5",
3182
+ 6: "md:col-span-6",
3183
+ 7: "md:col-span-7",
3184
+ 8: "md:col-span-8"
3185
+ },
3186
+ xl: {
3187
+ 1: "xl:col-span-1",
3188
+ 2: "xl:col-span-2",
3189
+ 3: "xl:col-span-3",
3190
+ 4: "xl:col-span-4",
3191
+ 5: "xl:col-span-5",
3192
+ 6: "xl:col-span-6",
3193
+ 7: "xl:col-span-7",
3194
+ 8: "xl:col-span-8",
3195
+ 9: "xl:col-span-9",
3196
+ 10: "xl:col-span-10",
3197
+ 11: "xl:col-span-11",
3198
+ 12: "xl:col-span-12"
3199
+ }
3200
+ };
3201
+ var START = {
3202
+ base: { 1: "col-start-1", 2: "col-start-2", 3: "col-start-3", 4: "col-start-4" },
3203
+ md: {
3204
+ 1: "md:col-start-1",
3205
+ 2: "md:col-start-2",
3206
+ 3: "md:col-start-3",
3207
+ 4: "md:col-start-4",
3208
+ 5: "md:col-start-5",
3209
+ 6: "md:col-start-6",
3210
+ 7: "md:col-start-7",
3211
+ 8: "md:col-start-8"
3212
+ },
3213
+ xl: {
3214
+ 1: "xl:col-start-1",
3215
+ 2: "xl:col-start-2",
3216
+ 3: "xl:col-start-3",
3217
+ 4: "xl:col-start-4",
3218
+ 5: "xl:col-start-5",
3219
+ 6: "xl:col-start-6",
3220
+ 7: "xl:col-start-7",
3221
+ 8: "xl:col-start-8",
3222
+ 9: "xl:col-start-9",
3223
+ 10: "xl:col-start-10",
3224
+ 11: "xl:col-start-11",
3225
+ 12: "xl:col-start-12"
3226
+ }
3227
+ };
3228
+ var TIERS = ["base", "md", "xl"];
3229
+ function tiersOf(value) {
3230
+ return typeof value === "number" ? TIERS : Object.keys(value);
3231
+ }
3232
+ function resolveTiers(value, kind) {
3233
+ const out = {};
3234
+ if (value == null) return out;
3235
+ for (const tier of tiersOf(value)) {
3236
+ if (!(tier in TIER_MAX)) {
3237
+ if (process.env.NODE_ENV !== "production") {
3238
+ console.warn(`[Col] unknown breakpoint "${tier}" (grid tiers are base/md/xl); skipped.`);
3239
+ }
3240
+ continue;
3241
+ }
3242
+ const requested = typeof value === "number" ? value : value[tier];
3243
+ if (requested == null) continue;
3244
+ const index = kind === "offset" ? requested + 1 : requested;
3245
+ if (!Number.isFinite(index)) {
3246
+ if (process.env.NODE_ENV !== "production") {
3247
+ console.warn(`[Col] ${kind} ${requested} is not a finite number at the "${tier}" tier; skipped.`);
3248
+ }
3249
+ continue;
3250
+ }
3251
+ const rounded = Math.round(index);
3252
+ const clamped = Math.min(Math.max(1, rounded), TIER_MAX[tier]);
3253
+ if (process.env.NODE_ENV !== "production" && (rounded < 1 || rounded > TIER_MAX[tier])) {
3254
+ console.warn(
3255
+ `[Col] ${kind} ${requested} is out of range at the "${tier}" tier (${TIER_MAX[tier]} columns); clamped.`
3256
+ );
3257
+ }
3258
+ out[tier] = clamped;
3259
+ }
3260
+ return out;
3261
+ }
3262
+ function responsiveClasses(span, offset) {
3263
+ const spans = resolveTiers(span, "span");
3264
+ const starts = resolveTiers(offset, "offset");
3265
+ const spanClasses = [];
3266
+ const startClasses = [];
3267
+ let effectiveSpan = 1;
3268
+ let requestedStart;
3269
+ let emittedStart;
3270
+ for (const tier of TIERS) {
3271
+ const tierSpan = spans[tier];
3272
+ if (tierSpan != null) {
3273
+ effectiveSpan = tierSpan;
3274
+ spanClasses.push(SPAN[tier][tierSpan]);
3275
+ }
3276
+ if (starts[tier] != null) requestedStart = starts[tier];
3277
+ if (requestedStart == null) continue;
3278
+ const lastFittingStart = TIER_MAX[tier] - effectiveSpan + 1;
3279
+ const tierStart = Math.min(requestedStart, lastFittingStart);
3280
+ if (tierStart < requestedStart && process.env.NODE_ENV !== "production") {
3281
+ console.warn(
3282
+ `[Col] span ${effectiveSpan} + offset ${requestedStart - 1} spill past the "${tier}" tier (${TIER_MAX[tier]} columns); offset clamped to ${tierStart - 1}.`
3283
+ );
3284
+ }
3285
+ if (starts[tier] != null || tierStart !== emittedStart) {
3286
+ emittedStart = tierStart;
3287
+ startClasses.push(START[tier][tierStart]);
3288
+ }
3289
+ }
3290
+ return cn(...spanClasses, ...startClasses);
3291
+ }
3292
+ var Col = React53.forwardRef(
3293
+ ({ span, offset, asChild = false, className, ...props }, ref) => {
3294
+ const Comp = asChild ? Slot7 : "div";
3295
+ return /* @__PURE__ */ jsx63(Comp, { ref, className: cn(responsiveClasses(span, offset), className), ...props });
3296
+ }
3297
+ );
3298
+ Col.displayName = "Col";
3299
+
3300
+ // src/components/grid-overlay.tsx
3301
+ import * as React54 from "react";
3302
+ import { jsx as jsx64 } from "react/jsx-runtime";
3303
+ var BASELINE = "var(--grid-gutter) / 2";
3304
+ var GridOverlay = React54.forwardRef(
3305
+ ({ columns, baseline = false, className, style, ...props }, ref) => {
3306
+ const count = Number.isFinite(columns) ? Math.max(1, Math.floor(columns)) : 1;
3307
+ return /* @__PURE__ */ jsx64(
3308
+ "div",
3309
+ {
3310
+ ref,
3311
+ "aria-hidden": true,
3312
+ className: cn("pointer-events-none absolute inset-0", className),
3313
+ style: {
3314
+ display: "grid",
3315
+ gridTemplateColumns: `repeat(${count}, minmax(0, 1fr))`,
3316
+ gridTemplateRows: "1fr",
3317
+ gap: "var(--grid-gutter)",
3318
+ ...baseline && {
3319
+ backgroundImage: `repeating-linear-gradient(to bottom, transparent 0, transparent calc(${BASELINE} - 1px), var(--border-subtle) calc(${BASELINE} - 1px), var(--border-subtle) calc(${BASELINE}))`
3320
+ },
3321
+ ...style
3322
+ },
3323
+ ...props,
3324
+ children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx64("span", { style: { backgroundColor: "var(--bg-brand)", opacity: 0.1 } }, i))
3325
+ }
3326
+ );
3327
+ }
3328
+ );
3329
+ GridOverlay.displayName = "GridOverlay";
3330
+
3072
3331
  // src/components/segment-chip.tsx
3073
- import * as React51 from "react";
3332
+ import * as React55 from "react";
3074
3333
  import { cva as cva9 } from "class-variance-authority";
3075
3334
  import { Link2 as Link22, KeyRound, Clock, Plus } from "lucide-react";
3076
- import { jsx as jsx61, jsxs as jsxs31 } from "react/jsx-runtime";
3335
+ import { jsx as jsx65, jsxs as jsxs32 } from "react/jsx-runtime";
3077
3336
  var segmentChipVariants = cva9(
3078
3337
  "inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1 text-sm font-medium leading-tight",
3079
3338
  {
@@ -3094,11 +3353,11 @@ var kindIcons = {
3094
3353
  pending: Clock,
3095
3354
  available: Plus
3096
3355
  };
3097
- var SegmentChip = React51.forwardRef(
3356
+ var SegmentChip = React55.forwardRef(
3098
3357
  ({ kind, resource, icon, hideIcon = false, className, ...props }, ref) => {
3099
3358
  const Icon = icon ?? kindIcons[kind ?? "granted"];
3100
- return /* @__PURE__ */ jsxs31("span", { ref, className: cn(segmentChipVariants({ kind }), className), ...props, children: [
3101
- !hideIcon && /* @__PURE__ */ jsx61(Icon, { className: "size-3.5 shrink-0 opacity-90", "aria-hidden": "true" }),
3359
+ return /* @__PURE__ */ jsxs32("span", { ref, className: cn(segmentChipVariants({ kind }), className), ...props, children: [
3360
+ !hideIcon && /* @__PURE__ */ jsx65(Icon, { className: "size-3.5 shrink-0 opacity-90", "aria-hidden": "true" }),
3102
3361
  resource
3103
3362
  ] });
3104
3363
  }
@@ -3106,9 +3365,9 @@ var SegmentChip = React51.forwardRef(
3106
3365
  SegmentChip.displayName = "SegmentChip";
3107
3366
 
3108
3367
  // src/components/connection-card.tsx
3109
- import * as React52 from "react";
3368
+ import * as React56 from "react";
3110
3369
  import { AlertTriangle } from "lucide-react";
3111
- import { Fragment as Fragment3, jsx as jsx62, jsxs as jsxs32 } from "react/jsx-runtime";
3370
+ import { Fragment as Fragment5, jsx as jsx66, jsxs as jsxs33 } from "react/jsx-runtime";
3112
3371
  var authLabels = { app: "App", oauth: "OAuth", token: "token" };
3113
3372
  var statusMeta = {
3114
3373
  connected: { label: "Connected", badge: "success", dot: "success" },
@@ -3118,13 +3377,13 @@ var statusMeta = {
3118
3377
  function monogram(name) {
3119
3378
  return name.replace(/[^a-zA-Z0-9 ]/g, " ").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
3120
3379
  }
3121
- var ConnectionCard = React52.forwardRef(
3380
+ var ConnectionCard = React56.forwardRef(
3122
3381
  ({ name, scope, auth, status, glyph, statusLabel, grantedCount, action, access, children, className, ...props }, ref) => {
3123
3382
  const meta = statusMeta[status];
3124
3383
  const connected = status !== "disconnected";
3125
- return /* @__PURE__ */ jsxs32(Card, { ref, className: cn(status === "disconnected" && "border-dashed border-border-default", className), ...props, children: [
3126
- /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-3 p-4", children: [
3127
- /* @__PURE__ */ jsx62(
3384
+ return /* @__PURE__ */ jsxs33(Card, { ref, className: cn(status === "disconnected" && "border-dashed border-border-default", className), ...props, children: [
3385
+ /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-3 p-4", children: [
3386
+ /* @__PURE__ */ jsx66(
3128
3387
  "div",
3129
3388
  {
3130
3389
  "aria-hidden": "true",
@@ -3132,71 +3391,71 @@ var ConnectionCard = React52.forwardRef(
3132
3391
  children: glyph ?? monogram(name)
3133
3392
  }
3134
3393
  ),
3135
- /* @__PURE__ */ jsxs32("div", { className: "min-w-0", children: [
3136
- /* @__PURE__ */ jsx62("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3137
- /* @__PURE__ */ jsxs32("div", { className: "mt-0.5 flex flex-wrap gap-1", children: [
3138
- /* @__PURE__ */ jsx62(Badge, { variant: "secondary", children: scope }),
3139
- /* @__PURE__ */ jsx62(Badge, { variant: "secondary", children: authLabels[auth] })
3394
+ /* @__PURE__ */ jsxs33("div", { className: "min-w-0", children: [
3395
+ /* @__PURE__ */ jsx66("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3396
+ /* @__PURE__ */ jsxs33("div", { className: "mt-0.5 flex flex-wrap gap-1", children: [
3397
+ /* @__PURE__ */ jsx66(Badge, { variant: "secondary", children: scope }),
3398
+ /* @__PURE__ */ jsx66(Badge, { variant: "secondary", children: authLabels[auth] })
3140
3399
  ] })
3141
3400
  ] }),
3142
- /* @__PURE__ */ jsx62("div", { className: "ml-auto shrink-0", children: connected ? /* @__PURE__ */ jsxs32(Badge, { variant: meta.badge, className: "gap-1.5", children: [
3143
- /* @__PURE__ */ jsx62(StatusDot, { status: meta.dot, size: "sm" }),
3401
+ /* @__PURE__ */ jsx66("div", { className: "ml-auto shrink-0", children: connected ? /* @__PURE__ */ jsxs33(Badge, { variant: meta.badge, className: "gap-1.5", children: [
3402
+ /* @__PURE__ */ jsx66(StatusDot, { status: meta.dot, size: "sm" }),
3144
3403
  statusLabel ?? meta.label
3145
3404
  ] }) : action })
3146
3405
  ] }),
3147
- access && /* @__PURE__ */ jsxs32(
3406
+ access && /* @__PURE__ */ jsxs33(
3148
3407
  "div",
3149
3408
  {
3150
3409
  role: "note",
3151
3410
  className: "flex items-start gap-2 border-t border-border-subtle bg-bg-warning-subtle px-4 py-2.5 text-xs font-medium text-text-warning",
3152
3411
  children: [
3153
- /* @__PURE__ */ jsx62(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0", "aria-hidden": "true" }),
3154
- /* @__PURE__ */ jsx62("span", { children: access })
3412
+ /* @__PURE__ */ jsx66(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0", "aria-hidden": "true" }),
3413
+ /* @__PURE__ */ jsx66("span", { children: access })
3155
3414
  ]
3156
3415
  }
3157
3416
  ),
3158
- (grantedCount !== void 0 || connected && action) && /* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between border-t border-border-subtle px-4 py-3", children: [
3159
- /* @__PURE__ */ jsx62("span", { className: "text-sm text-text-secondary", children: grantedCount !== void 0 ? /* @__PURE__ */ jsxs32(Fragment3, { children: [
3417
+ (grantedCount !== void 0 || connected && action) && /* @__PURE__ */ jsxs33("div", { className: "flex items-center justify-between border-t border-border-subtle px-4 py-3", children: [
3418
+ /* @__PURE__ */ jsx66("span", { className: "text-sm text-text-secondary", children: grantedCount !== void 0 ? /* @__PURE__ */ jsxs33(Fragment5, { children: [
3160
3419
  "Granted to ",
3161
- /* @__PURE__ */ jsx62("span", { className: "font-semibold text-text-primary", children: grantedCount }),
3420
+ /* @__PURE__ */ jsx66("span", { className: "font-semibold text-text-primary", children: grantedCount }),
3162
3421
  " ",
3163
3422
  grantedCount === 1 ? "product" : "products"
3164
- ] }) : /* @__PURE__ */ jsx62("span", { className: "text-text-tertiary", children: "Not granted to any product yet" }) }),
3423
+ ] }) : /* @__PURE__ */ jsx66("span", { className: "text-text-tertiary", children: "Not granted to any product yet" }) }),
3165
3424
  connected && action
3166
3425
  ] }),
3167
- children && /* @__PURE__ */ jsx62("div", { "data-slot": "drawer", className: "border-t border-border-subtle", children })
3426
+ children && /* @__PURE__ */ jsx66("div", { "data-slot": "drawer", className: "border-t border-border-subtle", children })
3168
3427
  ] });
3169
3428
  }
3170
3429
  );
3171
3430
  ConnectionCard.displayName = "ConnectionCard";
3172
3431
 
3173
3432
  // src/components/request-row.tsx
3174
- import * as React53 from "react";
3175
- import { Fragment as Fragment4, jsx as jsx63, jsxs as jsxs33 } from "react/jsx-runtime";
3433
+ import * as React57 from "react";
3434
+ import { Fragment as Fragment6, jsx as jsx67, jsxs as jsxs34 } from "react/jsx-runtime";
3176
3435
  function monogram2(name) {
3177
3436
  return name.replace(/[^a-zA-Z0-9 ]/g, " ").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
3178
3437
  }
3179
- var RequestRow = React53.forwardRef(
3438
+ var RequestRow = React57.forwardRef(
3180
3439
  ({ surface, name, state: state2, glyph, subtitle, grantedResource, onApprove, onDeny, onRequest, action, className, ...props }, ref) => {
3181
3440
  let controls = action;
3182
3441
  if (controls === void 0) {
3183
3442
  if (surface === "org" && state2 === "pending") {
3184
- controls = /* @__PURE__ */ jsxs33(Fragment4, { children: [
3185
- /* @__PURE__ */ jsx63(Button, { variant: "outline", size: "sm", onClick: onDeny, children: "Deny" }),
3186
- /* @__PURE__ */ jsx63(Button, { size: "sm", onClick: onApprove, children: "Approve" })
3443
+ controls = /* @__PURE__ */ jsxs34(Fragment6, { children: [
3444
+ /* @__PURE__ */ jsx67(Button, { variant: "outline", size: "sm", onClick: onDeny, children: "Deny" }),
3445
+ /* @__PURE__ */ jsx67(Button, { size: "sm", onClick: onApprove, children: "Approve" })
3187
3446
  ] });
3188
3447
  } else if (state2 === "available") {
3189
- controls = /* @__PURE__ */ jsx63(Button, { size: "sm", onClick: onRequest, children: "Request" });
3448
+ controls = /* @__PURE__ */ jsx67(Button, { size: "sm", onClick: onRequest, children: "Request" });
3190
3449
  } else if (state2 === "pending") {
3191
- controls = /* @__PURE__ */ jsxs33(Badge, { variant: "secondary", className: "gap-1.5", children: [
3192
- /* @__PURE__ */ jsx63(StatusDot, { status: "neutral", size: "sm" }),
3450
+ controls = /* @__PURE__ */ jsxs34(Badge, { variant: "secondary", className: "gap-1.5", children: [
3451
+ /* @__PURE__ */ jsx67(StatusDot, { status: "neutral", size: "sm" }),
3193
3452
  "Pending"
3194
3453
  ] });
3195
3454
  } else if (state2 === "granted") {
3196
- controls = /* @__PURE__ */ jsx63(SegmentChip, { kind: "granted", resource: grantedResource ?? "a segment" });
3455
+ controls = /* @__PURE__ */ jsx67(SegmentChip, { kind: "granted", resource: grantedResource ?? "a segment" });
3197
3456
  }
3198
3457
  }
3199
- return /* @__PURE__ */ jsxs33(
3458
+ return /* @__PURE__ */ jsxs34(
3200
3459
  "div",
3201
3460
  {
3202
3461
  ref,
@@ -3206,7 +3465,7 @@ var RequestRow = React53.forwardRef(
3206
3465
  ),
3207
3466
  ...props,
3208
3467
  children: [
3209
- /* @__PURE__ */ jsx63(
3468
+ /* @__PURE__ */ jsx67(
3210
3469
  "div",
3211
3470
  {
3212
3471
  "aria-hidden": "true",
@@ -3214,11 +3473,11 @@ var RequestRow = React53.forwardRef(
3214
3473
  children: glyph ?? monogram2(name)
3215
3474
  }
3216
3475
  ),
3217
- /* @__PURE__ */ jsxs33("div", { className: "min-w-0", children: [
3218
- /* @__PURE__ */ jsx63("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3219
- subtitle && /* @__PURE__ */ jsx63("p", { className: "truncate text-xs text-text-tertiary", children: subtitle })
3476
+ /* @__PURE__ */ jsxs34("div", { className: "min-w-0", children: [
3477
+ /* @__PURE__ */ jsx67("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3478
+ subtitle && /* @__PURE__ */ jsx67("p", { className: "truncate text-xs text-text-tertiary", children: subtitle })
3220
3479
  ] }),
3221
- /* @__PURE__ */ jsx63("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: controls })
3480
+ /* @__PURE__ */ jsx67("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: controls })
3222
3481
  ]
3223
3482
  }
3224
3483
  );
@@ -3273,6 +3532,7 @@ export {
3273
3532
  CarouselNext,
3274
3533
  CarouselPrevious,
3275
3534
  Checkbox,
3535
+ Col,
3276
3536
  Collapsible,
3277
3537
  CollapsibleContent2 as CollapsibleContent,
3278
3538
  CollapsibleTrigger2 as CollapsibleTrigger,
@@ -3350,6 +3610,8 @@ export {
3350
3610
  FormItem,
3351
3611
  FormLabel,
3352
3612
  FormMessage,
3613
+ Grid,
3614
+ GridOverlay,
3353
3615
  HoverCard,
3354
3616
  HoverCardContent,
3355
3617
  HoverCardTrigger,
@@ -3421,6 +3683,7 @@ export {
3421
3683
  SheetTrigger,
3422
3684
  SidebarNav,
3423
3685
  SidebarNavLink,
3686
+ SidebarProductHeader,
3424
3687
  Skeleton,
3425
3688
  Slider,
3426
3689
  StatCard,