@lessly/ui 0.4.0 → 0.5.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/README.md CHANGED
@@ -6,20 +6,25 @@ Module Federation host provides to federation extension remotes at runtime, and
6
6
  it is published to the public npm registry (npmjs.com).
7
7
 
8
8
  > **Contract:** the package name (`@lessly/ui`), its exports (`.`,
9
- > `./tailwind-preset`, `./styles.css`), and its React 19 peer dependencies are a
10
- > cross-project contract. Do not change them without coordinating the workspace
11
- > host and every federation remote.
9
+ > `./tailwind-preset`, `./styles.css`), and its peer dependencies (React 19,
10
+ > react-router 7, optional `tailwindcss >=3.4`) are a cross-project contract. Do
11
+ > not change them without coordinating the workspace host and every federation
12
+ > remote.
12
13
 
13
14
  ## What's inside
14
15
 
15
- - **Components:** Button, Card, Dialog, DropdownMenu, Input, Label, SectionIntro,
16
- Select, Switch.
16
+ - **Components:** shadcn/radix primitives themed by Lessly tokens (Button,
17
+ Dialog, Select, Tabs, …) plus layout: `Grid` / `Col` (the adaptive 4 / 8 / 12
18
+ column grid) and `GridOverlay` (a design-aid overlay). The full catalog lives
19
+ in Storybook at [ui.lessly.com](https://ui.lessly.com).
17
20
  - **Theme:** `useTheme` hook + `ThemeToggle` (toggles the `light` class on
18
21
  `<html>`; `:root` is dark, `.light` is light).
19
- - **Tokens:** `tailwind-preset` (spacing, colors, typography, radii, …) and
20
- `styles.css` (CSS variables, fonts, `.light` overrides). Token values are
21
- generated upstream in `lessly-hub/design-system` and vendored here do not
22
- hand-edit them.
22
+ - **Tokens:** the `tailwind-preset` re-exports the token vocabulary from
23
+ [`@lessly/tokens`](https://www.npmjs.com/package/@lessly/tokens) (generated
24
+ upstream in `lessly-hub/design-system`) with `darkMode: 'class'` added, and
25
+ `styles.css` bundles the package's `theme.css` (CSS variables, `.light`
26
+ overrides, typography classes) plus this library's fonts and base layer.
27
+ Token values are never edited here — they change by bumping `@lessly/tokens`.
23
28
 
24
29
  ## Install
25
30
 
@@ -37,13 +42,25 @@ import { Button, useTheme } from '@lessly/ui';
37
42
  import '@lessly/ui/styles.css';
38
43
  ```
39
44
 
40
- Your app's Tailwind config must use the preset so token utilities resolve:
45
+ Your app's Tailwind config must use the preset so token utilities resolve. The
46
+ preset needs Tailwind CSS >= 3.4 (declared as an optional peer dependency —
47
+ only required if you consume the preset):
41
48
 
42
49
  ```ts
43
50
  import { lesslyPreset } from '@lessly/ui/tailwind-preset';
44
- export default { presets: [lesslyPreset], content: ['./src/**/*.{ts,tsx}'] };
51
+ export default {
52
+ presets: [lesslyPreset],
53
+ // Scan the library too — its components (Grid, Col, …) use literal token
54
+ // utilities (`md:col-span-5`, `gap-gutter`) that Tailwind must see to generate.
55
+ content: ['./src/**/*.{ts,tsx}', './node_modules/@lessly/ui/dist/**/*.js'],
56
+ };
45
57
  ```
46
58
 
59
+ CSS variables use the upstream unprefixed names (`--bg-primary`,
60
+ `--text-primary`, …). The old `--color-*` names still resolve through a
61
+ deprecated alias layer in `styles.css` and will be removed in the next major —
62
+ migrate any direct `var(--color-*)` reads.
63
+
47
64
  ## Storybook
48
65
 
49
66
  ```bash
@@ -65,7 +82,7 @@ npm package published to npmjs always contains the package, never the Storybook
65
82
 
66
83
  | Script | Description |
67
84
  | --- | --- |
68
- | `pnpm build` | tsup package build → `dist/` (+ copies `styles.css`) — the npm package published to npmjs |
85
+ | `pnpm build` | tsup package build → `dist/` (+ assembles `styles.css` from the @lessly/tokens theme, deprecated `--color-*` aliases, ui-local pieces, and the animation layer) — the npm package published to npmjs |
69
86
  | `pnpm build-storybook` | Storybook static site → `storybook-static/` — what the static-service pipeline deploys to `ui.lessly.com` |
70
87
  | `pnpm storybook` | Storybook dev server on `http://localhost:6006` |
71
88
  | `pnpm test` | Vitest |
package/dist/index.d.ts CHANGED
@@ -848,6 +848,73 @@ declare function AppShell({ sidebar, topBar, children, className }: AppShellProp
848
848
  /** Alias of AppShell — reads better in app router files. */
849
849
  declare const AuthenticatedLayout: typeof AppShell;
850
850
 
851
+ /**
852
+ * Grid — the adaptive 4 / 8 / 12 column container. Column count switches at the
853
+ * token breakpoints (4 below md, 8 at md/768, 12 at xl/1280). Gutter, side
854
+ * margins and the max-width are tokenised: `gap-gutter`, `px-grid-*` and
855
+ * `max-w-container` are var-backed by @lessly/tokens theme.css (--grid-*).
856
+ * Place <Col> children inside.
857
+ *
858
+ * The grid owns the container — it centres and caps content at the container
859
+ * token (1440) by default. Set `fluid` for a full-bleed row that keeps the
860
+ * gutter and side margins but drops the max-width cap.
861
+ *
862
+ * Note: the token utilities are custom theme keys tailwind-merge doesn't
863
+ * classify, so a `className` like `px-6` or `gap-2` does NOT replace them —
864
+ * both classes are emitted and CSS source order decides. Don't override the
865
+ * grid's spacing via className; use `fluid` or wrap the grid instead.
866
+ */
867
+ interface GridProps extends React$1.HTMLAttributes<HTMLDivElement> {
868
+ /** Drop the max-width cap for a full-bleed row (gutter + margins stay). */
869
+ fluid?: boolean;
870
+ /** Render as the child element via Radix Slot instead of a div. */
871
+ asChild?: boolean;
872
+ }
873
+ declare const Grid: React$1.ForwardRefExoticComponent<GridProps & React$1.RefAttributes<HTMLDivElement>>;
874
+
875
+ /**
876
+ * Col — a child of <Grid>. Spans columns of the adaptive 4 / 8 / 12 track using
877
+ * literal `col-span-*` / `col-start-*` classes. Pure CSS: no context, no
878
+ * measurement.
879
+ *
880
+ * Spans and offsets are expressed as literal Tailwind classes (never CSS
881
+ * variables), so the values are visible to Tailwind's content scanner. The
882
+ * lookup maps below hold every class as a verbatim string for that reason — do
883
+ * NOT rebuild them with template literals (`col-span-${n}`), which the JIT would
884
+ * never see and so would never generate.
885
+ */
886
+ /** Constant across tiers (number) or set per breakpoint ({ base, md, xl }). */
887
+ type Responsive = number | {
888
+ base?: number;
889
+ md?: number;
890
+ xl?: number;
891
+ };
892
+ interface ColProps extends React$1.HTMLAttributes<HTMLDivElement> {
893
+ /**
894
+ * Columns to span. Number = same on every tier, clamped to each tier's column
895
+ * count (with a dev warning past a tier's max — express full width as
896
+ * `{ base: 4, md: 8, xl: 12 }` instead of `12`); object = per breakpoint.
897
+ */
898
+ span?: Responsive;
899
+ /**
900
+ * Leading empty columns (→ col-start). Number or per-breakpoint object.
901
+ * Clamped per tier (with a dev warning) so offset + span stay inside the
902
+ * tier's track instead of forcing an implicit column.
903
+ */
904
+ offset?: Responsive;
905
+ /** Render as the child element via Radix Slot instead of a div. */
906
+ asChild?: boolean;
907
+ }
908
+ declare const Col: React$1.ForwardRefExoticComponent<ColProps & React$1.RefAttributes<HTMLDivElement>>;
909
+
910
+ interface GridOverlayProps extends React$1.HTMLAttributes<HTMLDivElement> {
911
+ /** Number of columns to paint (e.g. 4, 8 or 12). Fixed, viewport-independent. */
912
+ columns: number;
913
+ /** Also paint the baseline rhythm (horizontal lines) behind the columns. */
914
+ baseline?: boolean;
915
+ }
916
+ declare const GridOverlay: React$1.ForwardRefExoticComponent<GridOverlayProps & React$1.RefAttributes<HTMLDivElement>>;
917
+
851
918
  declare const segmentChipVariants: (props?: ({
852
919
  kind?: "granted" | "own" | "pending" | "available" | null | undefined;
853
920
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -864,7 +931,7 @@ declare const SegmentChip: React$1.ForwardRefExoticComponent<SegmentChipProps &
864
931
  type ConnectionScope = 'org' | 'org/product' | 'product';
865
932
  type ConnectionAuth = 'app' | 'oauth' | 'token';
866
933
  type ConnectionStatus = 'connected' | 'error' | 'disconnected';
867
- interface ConnectionCardProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, 'children'> {
934
+ interface ConnectionCardProps extends React$1.HTMLAttributes<HTMLDivElement> {
868
935
  /** Display name, e.g. "GitHub — apliteni". */
869
936
  name: string;
870
937
  scope: ConnectionScope;
@@ -885,6 +952,13 @@ interface ConnectionCardProps extends Omit<React$1.HTMLAttributes<HTMLDivElement
885
952
  * a warning notice is shown, e.g. "Full org access — all repositories."
886
953
  */
887
954
  access?: React$1.ReactNode;
955
+ /**
956
+ * Drawer content rendered below the card body — the org surface's accordion
957
+ * drawer in the integration prototype (the per-product grants roster, inline
958
+ * requests, "+ Grant to a product"). The caller owns expand/collapse: pass
959
+ * children only while open. Omitted = the flat card, unchanged.
960
+ */
961
+ children?: React$1.ReactNode;
888
962
  }
889
963
  declare const ConnectionCard: React$1.ForwardRefExoticComponent<ConnectionCardProps & React$1.RefAttributes<HTMLDivElement>>;
890
964
 
@@ -912,4 +986,4 @@ interface RequestRowProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, '
912
986
  }
913
987
  declare const RequestRow: React$1.ForwardRefExoticComponent<RequestRowProps & React$1.RefAttributes<HTMLDivElement>>;
914
988
 
915
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, type AppShellProps, AppSidebar, type AppSidebarProps, AspectRatio, AuthenticatedLayout, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, type CarouselContextProps, CarouselItem, CarouselNext, type CarouselOptions, type CarouselPlugin, CarouselPrevious, type CarouselProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, type ConnectionAuth, ConnectionCard, type ConnectionCardProps, type ConnectionScope, type ConnectionStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, DatePicker, type DatePickerProps, type DeltaDirection, type DeltaTone, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DocsLink, type DocsLinkProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExtensionIcon, type ExtensionIconProps, ExtensionLink, type ExtensionLinkProps, type ExtensionLinkUiMode, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HoverCard, HoverCardContent, HoverCardTrigger, InlineError, type InlineErrorProps, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, MenuDivider, MenuPopup, type MenuPopupProps, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, type NavItem, type NavLinkComponent, NavRow, type NavRowProps, type NavRowVariant, NavSectionLabel, type NavSectionLabelProps, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RequestRow, type RequestRowProps, type RequestState, type RequestSurface, type ResolvedTheme, ScrollArea, ScrollBar, SectionIntro, type SectionIntroProps, SegmentChip, type SegmentChipProps, Select, type SelectOption, type SelectProps, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarNav, SidebarNavLink, type SidebarNavLinkProps, type SidebarNavProps, Skeleton, Slider, StatCard, type StatCardProps, StatusDot, type StatusDotProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, type Theme, ThemeToggle, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseSidebarOptions, type UseSidebarResult, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuUser, alertVariants, badgeVariants, buttonVariants, cn, isKnownExtensionIcon, navigationMenuTriggerStyle, segmentChipVariants, statusDotVariants, toggleVariants, useCarousel, useFormField, useIsMobile, useSidebar, useTheme };
989
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, type AppShellProps, AppSidebar, type AppSidebarProps, AspectRatio, AuthenticatedLayout, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, type CarouselContextProps, CarouselItem, CarouselNext, type CarouselOptions, type CarouselPlugin, CarouselPrevious, type CarouselProps, Checkbox, Col, type ColProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, type ConnectionAuth, ConnectionCard, type ConnectionCardProps, type ConnectionScope, type ConnectionStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, DatePicker, type DatePickerProps, type DeltaDirection, type DeltaTone, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DocsLink, type DocsLinkProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExtensionIcon, type ExtensionIconProps, ExtensionLink, type ExtensionLinkProps, type ExtensionLinkUiMode, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Grid, GridOverlay, type GridOverlayProps, type GridProps, HoverCard, HoverCardContent, HoverCardTrigger, InlineError, type InlineErrorProps, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, MenuDivider, MenuPopup, type MenuPopupProps, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, type NavItem, type NavLinkComponent, NavRow, type NavRowProps, type NavRowVariant, NavSectionLabel, type NavSectionLabelProps, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RequestRow, type RequestRowProps, type RequestState, type RequestSurface, type ResolvedTheme, type Responsive, ScrollArea, ScrollBar, SectionIntro, type SectionIntroProps, SegmentChip, type SegmentChipProps, Select, type SelectOption, type SelectProps, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarNav, SidebarNavLink, type SidebarNavLinkProps, type SidebarNavProps, Skeleton, Slider, StatCard, type StatCardProps, StatusDot, type StatusDotProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, type Theme, ThemeToggle, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseSidebarOptions, type UseSidebarResult, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuUser, alertVariants, badgeVariants, buttonVariants, cn, isKnownExtensionIcon, navigationMenuTriggerStyle, segmentChipVariants, statusDotVariants, toggleVariants, useCarousel, useFormField, useIsMobile, useSidebar, useTheme };
package/dist/index.js CHANGED
@@ -3069,17 +3069,203 @@ function AppShell({ sidebar, topBar, children, className }) {
3069
3069
  }
3070
3070
  var AuthenticatedLayout = AppShell;
3071
3071
 
3072
- // src/components/segment-chip.tsx
3072
+ // src/components/grid.tsx
3073
3073
  import * as React51 from "react";
3074
+ import { Slot as Slot5 } from "@radix-ui/react-slot";
3075
+ import { jsx as jsx61 } from "react/jsx-runtime";
3076
+ var Grid = React51.forwardRef(
3077
+ ({ fluid = false, asChild = false, className, ...props }, ref) => {
3078
+ const Comp = asChild ? Slot5 : "div";
3079
+ return /* @__PURE__ */ jsx61(
3080
+ Comp,
3081
+ {
3082
+ ref,
3083
+ className: cn(
3084
+ "grid grid-cols-4 md:grid-cols-8 xl:grid-cols-12",
3085
+ "gap-gutter px-grid-sm md:px-grid-md xl:px-grid-xl",
3086
+ !fluid && "mx-auto max-w-container",
3087
+ className
3088
+ ),
3089
+ ...props
3090
+ }
3091
+ );
3092
+ }
3093
+ );
3094
+ Grid.displayName = "Grid";
3095
+
3096
+ // src/components/col.tsx
3097
+ import * as React52 from "react";
3098
+ import { Slot as Slot6 } from "@radix-ui/react-slot";
3099
+ import { jsx as jsx62 } from "react/jsx-runtime";
3100
+ var TIER_MAX = { base: 4, md: 8, xl: 12 };
3101
+ var SPAN = {
3102
+ base: { 1: "col-span-1", 2: "col-span-2", 3: "col-span-3", 4: "col-span-4" },
3103
+ md: {
3104
+ 1: "md:col-span-1",
3105
+ 2: "md:col-span-2",
3106
+ 3: "md:col-span-3",
3107
+ 4: "md:col-span-4",
3108
+ 5: "md:col-span-5",
3109
+ 6: "md:col-span-6",
3110
+ 7: "md:col-span-7",
3111
+ 8: "md:col-span-8"
3112
+ },
3113
+ xl: {
3114
+ 1: "xl:col-span-1",
3115
+ 2: "xl:col-span-2",
3116
+ 3: "xl:col-span-3",
3117
+ 4: "xl:col-span-4",
3118
+ 5: "xl:col-span-5",
3119
+ 6: "xl:col-span-6",
3120
+ 7: "xl:col-span-7",
3121
+ 8: "xl:col-span-8",
3122
+ 9: "xl:col-span-9",
3123
+ 10: "xl:col-span-10",
3124
+ 11: "xl:col-span-11",
3125
+ 12: "xl:col-span-12"
3126
+ }
3127
+ };
3128
+ var START = {
3129
+ base: { 1: "col-start-1", 2: "col-start-2", 3: "col-start-3", 4: "col-start-4" },
3130
+ md: {
3131
+ 1: "md:col-start-1",
3132
+ 2: "md:col-start-2",
3133
+ 3: "md:col-start-3",
3134
+ 4: "md:col-start-4",
3135
+ 5: "md:col-start-5",
3136
+ 6: "md:col-start-6",
3137
+ 7: "md:col-start-7",
3138
+ 8: "md:col-start-8"
3139
+ },
3140
+ xl: {
3141
+ 1: "xl:col-start-1",
3142
+ 2: "xl:col-start-2",
3143
+ 3: "xl:col-start-3",
3144
+ 4: "xl:col-start-4",
3145
+ 5: "xl:col-start-5",
3146
+ 6: "xl:col-start-6",
3147
+ 7: "xl:col-start-7",
3148
+ 8: "xl:col-start-8",
3149
+ 9: "xl:col-start-9",
3150
+ 10: "xl:col-start-10",
3151
+ 11: "xl:col-start-11",
3152
+ 12: "xl:col-start-12"
3153
+ }
3154
+ };
3155
+ var TIERS = ["base", "md", "xl"];
3156
+ function tiersOf(value) {
3157
+ return typeof value === "number" ? TIERS : Object.keys(value);
3158
+ }
3159
+ function resolveTiers(value, kind) {
3160
+ const out = {};
3161
+ if (value == null) return out;
3162
+ for (const tier of tiersOf(value)) {
3163
+ if (!(tier in TIER_MAX)) {
3164
+ if (process.env.NODE_ENV !== "production") {
3165
+ console.warn(`[Col] unknown breakpoint "${tier}" (grid tiers are base/md/xl); skipped.`);
3166
+ }
3167
+ continue;
3168
+ }
3169
+ const requested = typeof value === "number" ? value : value[tier];
3170
+ if (requested == null) continue;
3171
+ const index = kind === "offset" ? requested + 1 : requested;
3172
+ if (!Number.isFinite(index)) {
3173
+ if (process.env.NODE_ENV !== "production") {
3174
+ console.warn(`[Col] ${kind} ${requested} is not a finite number at the "${tier}" tier; skipped.`);
3175
+ }
3176
+ continue;
3177
+ }
3178
+ const rounded = Math.round(index);
3179
+ const clamped = Math.min(Math.max(1, rounded), TIER_MAX[tier]);
3180
+ if (process.env.NODE_ENV !== "production" && (rounded < 1 || rounded > TIER_MAX[tier])) {
3181
+ console.warn(
3182
+ `[Col] ${kind} ${requested} is out of range at the "${tier}" tier (${TIER_MAX[tier]} columns); clamped.`
3183
+ );
3184
+ }
3185
+ out[tier] = clamped;
3186
+ }
3187
+ return out;
3188
+ }
3189
+ function responsiveClasses(span, offset) {
3190
+ const spans = resolveTiers(span, "span");
3191
+ const starts = resolveTiers(offset, "offset");
3192
+ const spanClasses = [];
3193
+ const startClasses = [];
3194
+ let effectiveSpan = 1;
3195
+ let requestedStart;
3196
+ let emittedStart;
3197
+ for (const tier of TIERS) {
3198
+ const tierSpan = spans[tier];
3199
+ if (tierSpan != null) {
3200
+ effectiveSpan = tierSpan;
3201
+ spanClasses.push(SPAN[tier][tierSpan]);
3202
+ }
3203
+ if (starts[tier] != null) requestedStart = starts[tier];
3204
+ if (requestedStart == null) continue;
3205
+ const lastFittingStart = TIER_MAX[tier] - effectiveSpan + 1;
3206
+ const tierStart = Math.min(requestedStart, lastFittingStart);
3207
+ if (tierStart < requestedStart && process.env.NODE_ENV !== "production") {
3208
+ console.warn(
3209
+ `[Col] span ${effectiveSpan} + offset ${requestedStart - 1} spill past the "${tier}" tier (${TIER_MAX[tier]} columns); offset clamped to ${tierStart - 1}.`
3210
+ );
3211
+ }
3212
+ if (starts[tier] != null || tierStart !== emittedStart) {
3213
+ emittedStart = tierStart;
3214
+ startClasses.push(START[tier][tierStart]);
3215
+ }
3216
+ }
3217
+ return cn(...spanClasses, ...startClasses);
3218
+ }
3219
+ var Col = React52.forwardRef(
3220
+ ({ span, offset, asChild = false, className, ...props }, ref) => {
3221
+ const Comp = asChild ? Slot6 : "div";
3222
+ return /* @__PURE__ */ jsx62(Comp, { ref, className: cn(responsiveClasses(span, offset), className), ...props });
3223
+ }
3224
+ );
3225
+ Col.displayName = "Col";
3226
+
3227
+ // src/components/grid-overlay.tsx
3228
+ import * as React53 from "react";
3229
+ import { jsx as jsx63 } from "react/jsx-runtime";
3230
+ var BASELINE = "var(--grid-gutter) / 2";
3231
+ var GridOverlay = React53.forwardRef(
3232
+ ({ columns, baseline = false, className, style, ...props }, ref) => {
3233
+ const count = Number.isFinite(columns) ? Math.max(1, Math.floor(columns)) : 1;
3234
+ return /* @__PURE__ */ jsx63(
3235
+ "div",
3236
+ {
3237
+ ref,
3238
+ "aria-hidden": true,
3239
+ className: cn("pointer-events-none absolute inset-0", className),
3240
+ style: {
3241
+ display: "grid",
3242
+ gridTemplateColumns: `repeat(${count}, minmax(0, 1fr))`,
3243
+ gridTemplateRows: "1fr",
3244
+ gap: "var(--grid-gutter)",
3245
+ ...baseline && {
3246
+ backgroundImage: `repeating-linear-gradient(to bottom, transparent 0, transparent calc(${BASELINE} - 1px), var(--border-subtle) calc(${BASELINE} - 1px), var(--border-subtle) calc(${BASELINE}))`
3247
+ },
3248
+ ...style
3249
+ },
3250
+ ...props,
3251
+ children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx63("span", { style: { backgroundColor: "var(--bg-brand)", opacity: 0.1 } }, i))
3252
+ }
3253
+ );
3254
+ }
3255
+ );
3256
+ GridOverlay.displayName = "GridOverlay";
3257
+
3258
+ // src/components/segment-chip.tsx
3259
+ import * as React54 from "react";
3074
3260
  import { cva as cva9 } from "class-variance-authority";
3075
3261
  import { Link2 as Link22, KeyRound, Clock, Plus } from "lucide-react";
3076
- import { jsx as jsx61, jsxs as jsxs31 } from "react/jsx-runtime";
3262
+ import { jsx as jsx64, jsxs as jsxs31 } from "react/jsx-runtime";
3077
3263
  var segmentChipVariants = cva9(
3078
3264
  "inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1 text-sm font-medium leading-tight",
3079
3265
  {
3080
3266
  variants: {
3081
3267
  kind: {
3082
- granted: "border-dashed border-teal-800 bg-bg-teal-subtle text-teal-300",
3268
+ granted: "border-dashed border-text-teal/40 bg-bg-teal-subtle text-text-teal",
3083
3269
  own: "border-border-warning bg-bg-warning-subtle text-text-warning",
3084
3270
  pending: "border-dashed border-border-default bg-bg-secondary text-text-tertiary",
3085
3271
  available: "border-border-subtle bg-transparent text-text-secondary"
@@ -3094,11 +3280,11 @@ var kindIcons = {
3094
3280
  pending: Clock,
3095
3281
  available: Plus
3096
3282
  };
3097
- var SegmentChip = React51.forwardRef(
3283
+ var SegmentChip = React54.forwardRef(
3098
3284
  ({ kind, resource, icon, hideIcon = false, className, ...props }, ref) => {
3099
3285
  const Icon = icon ?? kindIcons[kind ?? "granted"];
3100
3286
  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" }),
3287
+ !hideIcon && /* @__PURE__ */ jsx64(Icon, { className: "size-3.5 shrink-0 opacity-90", "aria-hidden": "true" }),
3102
3288
  resource
3103
3289
  ] });
3104
3290
  }
@@ -3106,9 +3292,9 @@ var SegmentChip = React51.forwardRef(
3106
3292
  SegmentChip.displayName = "SegmentChip";
3107
3293
 
3108
3294
  // src/components/connection-card.tsx
3109
- import * as React52 from "react";
3295
+ import * as React55 from "react";
3110
3296
  import { AlertTriangle } from "lucide-react";
3111
- import { Fragment as Fragment3, jsx as jsx62, jsxs as jsxs32 } from "react/jsx-runtime";
3297
+ import { Fragment as Fragment3, jsx as jsx65, jsxs as jsxs32 } from "react/jsx-runtime";
3112
3298
  var authLabels = { app: "App", oauth: "OAuth", token: "token" };
3113
3299
  var statusMeta = {
3114
3300
  connected: { label: "Connected", badge: "success", dot: "success" },
@@ -3118,13 +3304,13 @@ var statusMeta = {
3118
3304
  function monogram(name) {
3119
3305
  return name.replace(/[^a-zA-Z0-9 ]/g, " ").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
3120
3306
  }
3121
- var ConnectionCard = React52.forwardRef(
3122
- ({ name, scope, auth, status, glyph, statusLabel, grantedCount, action, access, className, ...props }, ref) => {
3307
+ var ConnectionCard = React55.forwardRef(
3308
+ ({ name, scope, auth, status, glyph, statusLabel, grantedCount, action, access, children, className, ...props }, ref) => {
3123
3309
  const meta = statusMeta[status];
3124
3310
  const connected = status !== "disconnected";
3125
3311
  return /* @__PURE__ */ jsxs32(Card, { ref, className: cn(status === "disconnected" && "border-dashed border-border-default", className), ...props, children: [
3126
3312
  /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-3 p-4", children: [
3127
- /* @__PURE__ */ jsx62(
3313
+ /* @__PURE__ */ jsx65(
3128
3314
  "div",
3129
3315
  {
3130
3316
  "aria-hidden": "true",
@@ -3133,14 +3319,14 @@ var ConnectionCard = React52.forwardRef(
3133
3319
  }
3134
3320
  ),
3135
3321
  /* @__PURE__ */ jsxs32("div", { className: "min-w-0", children: [
3136
- /* @__PURE__ */ jsx62("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3322
+ /* @__PURE__ */ jsx65("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3137
3323
  /* @__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] })
3324
+ /* @__PURE__ */ jsx65(Badge, { variant: "secondary", children: scope }),
3325
+ /* @__PURE__ */ jsx65(Badge, { variant: "secondary", children: authLabels[auth] })
3140
3326
  ] })
3141
3327
  ] }),
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" }),
3328
+ /* @__PURE__ */ jsx65("div", { className: "ml-auto shrink-0", children: connected ? /* @__PURE__ */ jsxs32(Badge, { variant: meta.badge, className: "gap-1.5", children: [
3329
+ /* @__PURE__ */ jsx65(StatusDot, { status: meta.dot, size: "sm" }),
3144
3330
  statusLabel ?? meta.label
3145
3331
  ] }) : action })
3146
3332
  ] }),
@@ -3150,49 +3336,50 @@ var ConnectionCard = React52.forwardRef(
3150
3336
  role: "note",
3151
3337
  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
3338
  children: [
3153
- /* @__PURE__ */ jsx62(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0", "aria-hidden": "true" }),
3154
- /* @__PURE__ */ jsx62("span", { children: access })
3339
+ /* @__PURE__ */ jsx65(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0", "aria-hidden": "true" }),
3340
+ /* @__PURE__ */ jsx65("span", { children: access })
3155
3341
  ]
3156
3342
  }
3157
3343
  ),
3158
3344
  (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: [
3345
+ /* @__PURE__ */ jsx65("span", { className: "text-sm text-text-secondary", children: grantedCount !== void 0 ? /* @__PURE__ */ jsxs32(Fragment3, { children: [
3160
3346
  "Granted to ",
3161
- /* @__PURE__ */ jsx62("span", { className: "font-semibold text-text-primary", children: grantedCount }),
3347
+ /* @__PURE__ */ jsx65("span", { className: "font-semibold text-text-primary", children: grantedCount }),
3162
3348
  " ",
3163
3349
  grantedCount === 1 ? "product" : "products"
3164
- ] }) : /* @__PURE__ */ jsx62("span", { className: "text-text-tertiary", children: "Not granted to any product yet" }) }),
3350
+ ] }) : /* @__PURE__ */ jsx65("span", { className: "text-text-tertiary", children: "Not granted to any product yet" }) }),
3165
3351
  connected && action
3166
- ] })
3352
+ ] }),
3353
+ children && /* @__PURE__ */ jsx65("div", { "data-slot": "drawer", className: "border-t border-border-subtle", children })
3167
3354
  ] });
3168
3355
  }
3169
3356
  );
3170
3357
  ConnectionCard.displayName = "ConnectionCard";
3171
3358
 
3172
3359
  // src/components/request-row.tsx
3173
- import * as React53 from "react";
3174
- import { Fragment as Fragment4, jsx as jsx63, jsxs as jsxs33 } from "react/jsx-runtime";
3360
+ import * as React56 from "react";
3361
+ import { Fragment as Fragment4, jsx as jsx66, jsxs as jsxs33 } from "react/jsx-runtime";
3175
3362
  function monogram2(name) {
3176
3363
  return name.replace(/[^a-zA-Z0-9 ]/g, " ").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
3177
3364
  }
3178
- var RequestRow = React53.forwardRef(
3365
+ var RequestRow = React56.forwardRef(
3179
3366
  ({ surface, name, state: state2, glyph, subtitle, grantedResource, onApprove, onDeny, onRequest, action, className, ...props }, ref) => {
3180
3367
  let controls = action;
3181
3368
  if (controls === void 0) {
3182
3369
  if (surface === "org" && state2 === "pending") {
3183
3370
  controls = /* @__PURE__ */ jsxs33(Fragment4, { children: [
3184
- /* @__PURE__ */ jsx63(Button, { variant: "outline", size: "sm", onClick: onDeny, children: "Deny" }),
3185
- /* @__PURE__ */ jsx63(Button, { size: "sm", onClick: onApprove, children: "Approve" })
3371
+ /* @__PURE__ */ jsx66(Button, { variant: "outline", size: "sm", onClick: onDeny, children: "Deny" }),
3372
+ /* @__PURE__ */ jsx66(Button, { size: "sm", onClick: onApprove, children: "Approve" })
3186
3373
  ] });
3187
3374
  } else if (state2 === "available") {
3188
- controls = /* @__PURE__ */ jsx63(Button, { size: "sm", onClick: onRequest, children: "Request" });
3375
+ controls = /* @__PURE__ */ jsx66(Button, { size: "sm", onClick: onRequest, children: "Request" });
3189
3376
  } else if (state2 === "pending") {
3190
3377
  controls = /* @__PURE__ */ jsxs33(Badge, { variant: "secondary", className: "gap-1.5", children: [
3191
- /* @__PURE__ */ jsx63(StatusDot, { status: "neutral", size: "sm" }),
3378
+ /* @__PURE__ */ jsx66(StatusDot, { status: "neutral", size: "sm" }),
3192
3379
  "Pending"
3193
3380
  ] });
3194
3381
  } else if (state2 === "granted") {
3195
- controls = /* @__PURE__ */ jsx63(SegmentChip, { kind: "granted", resource: grantedResource ?? "a segment" });
3382
+ controls = /* @__PURE__ */ jsx66(SegmentChip, { kind: "granted", resource: grantedResource ?? "a segment" });
3196
3383
  }
3197
3384
  }
3198
3385
  return /* @__PURE__ */ jsxs33(
@@ -3205,7 +3392,7 @@ var RequestRow = React53.forwardRef(
3205
3392
  ),
3206
3393
  ...props,
3207
3394
  children: [
3208
- /* @__PURE__ */ jsx63(
3395
+ /* @__PURE__ */ jsx66(
3209
3396
  "div",
3210
3397
  {
3211
3398
  "aria-hidden": "true",
@@ -3214,10 +3401,10 @@ var RequestRow = React53.forwardRef(
3214
3401
  }
3215
3402
  ),
3216
3403
  /* @__PURE__ */ jsxs33("div", { className: "min-w-0", children: [
3217
- /* @__PURE__ */ jsx63("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3218
- subtitle && /* @__PURE__ */ jsx63("p", { className: "truncate text-xs text-text-tertiary", children: subtitle })
3404
+ /* @__PURE__ */ jsx66("p", { className: "truncate text-sm font-semibold text-text-primary", children: name }),
3405
+ subtitle && /* @__PURE__ */ jsx66("p", { className: "truncate text-xs text-text-tertiary", children: subtitle })
3219
3406
  ] }),
3220
- /* @__PURE__ */ jsx63("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: controls })
3407
+ /* @__PURE__ */ jsx66("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: controls })
3221
3408
  ]
3222
3409
  }
3223
3410
  );
@@ -3272,6 +3459,7 @@ export {
3272
3459
  CarouselNext,
3273
3460
  CarouselPrevious,
3274
3461
  Checkbox,
3462
+ Col,
3275
3463
  Collapsible,
3276
3464
  CollapsibleContent2 as CollapsibleContent,
3277
3465
  CollapsibleTrigger2 as CollapsibleTrigger,
@@ -3349,6 +3537,8 @@ export {
3349
3537
  FormItem,
3350
3538
  FormLabel,
3351
3539
  FormMessage,
3540
+ Grid,
3541
+ GridOverlay,
3352
3542
  HoverCard,
3353
3543
  HoverCardContent,
3354
3544
  HoverCardTrigger,