@forwardreach/saas-ui 0.4.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +7 -0
  3. package/dist/components/audit-log.d.ts +79 -0
  4. package/dist/components/audit-log.js +24 -0
  5. package/dist/components/auth-shell.d.ts +25 -0
  6. package/dist/components/auth-shell.js +30 -0
  7. package/dist/components/avatar.d.ts +31 -0
  8. package/dist/components/avatar.js +56 -0
  9. package/dist/components/badge.d.ts +9 -0
  10. package/dist/components/badge.js +23 -0
  11. package/dist/components/brand-icons.d.ts +14 -0
  12. package/dist/components/brand-icons.js +23 -0
  13. package/dist/components/button.d.ts +16 -0
  14. package/dist/components/button.js +37 -0
  15. package/dist/components/confirm-dialog.d.ts +18 -0
  16. package/dist/components/confirm-dialog.js +39 -0
  17. package/dist/components/copy-field.d.ts +22 -0
  18. package/dist/components/copy-field.js +53 -0
  19. package/dist/components/dialog.d.ts +14 -0
  20. package/dist/components/dialog.js +24 -0
  21. package/dist/components/dropdown-menu.d.ts +23 -0
  22. package/dist/components/dropdown-menu.js +30 -0
  23. package/dist/components/empty-state.d.ts +9 -0
  24. package/dist/components/empty-state.js +7 -0
  25. package/dist/components/filter-chip.d.ts +11 -0
  26. package/dist/components/filter-chip.js +13 -0
  27. package/dist/components/getting-started.d.ts +31 -0
  28. package/dist/components/getting-started.js +17 -0
  29. package/dist/components/index.d.ts +29 -0
  30. package/dist/components/index.js +29 -0
  31. package/dist/components/inline-notice.d.ts +27 -0
  32. package/dist/components/inline-notice.js +47 -0
  33. package/dist/components/inline-rename-input.d.ts +11 -0
  34. package/dist/components/inline-rename-input.js +57 -0
  35. package/dist/components/input.d.ts +4 -0
  36. package/dist/components/input.js +5 -0
  37. package/dist/components/list-shell.d.ts +17 -0
  38. package/dist/components/list-shell.js +13 -0
  39. package/dist/components/login.d.ts +39 -0
  40. package/dist/components/login.js +50 -0
  41. package/dist/components/page-header.d.ts +10 -0
  42. package/dist/components/page-header.js +6 -0
  43. package/dist/components/request-access.d.ts +25 -0
  44. package/dist/components/request-access.js +40 -0
  45. package/dist/components/scroll-area.d.ts +4 -0
  46. package/dist/components/scroll-area.js +8 -0
  47. package/dist/components/search-input.d.ts +7 -0
  48. package/dist/components/search-input.js +11 -0
  49. package/dist/components/separator.d.ts +3 -0
  50. package/dist/components/separator.js +6 -0
  51. package/dist/components/settings-section.d.ts +18 -0
  52. package/dist/components/settings-section.js +10 -0
  53. package/dist/components/skeleton.d.ts +4 -0
  54. package/dist/components/skeleton.js +6 -0
  55. package/dist/components/status-pill.d.ts +7 -0
  56. package/dist/components/status-pill.js +7 -0
  57. package/dist/components/tabs.d.ts +6 -0
  58. package/dist/components/tabs.js +11 -0
  59. package/dist/components/textarea.d.ts +4 -0
  60. package/dist/components/textarea.js +5 -0
  61. package/dist/components/toolbar.d.ts +7 -0
  62. package/dist/components/toolbar.js +9 -0
  63. package/dist/components/tooltip.d.ts +6 -0
  64. package/dist/components/tooltip.js +9 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.js +2 -0
  67. package/dist/styles/index.css +71 -0
  68. package/dist/utils/cn.d.ts +2 -0
  69. package/dist/utils/cn.js +5 -0
  70. package/dist/utils/email.d.ts +2 -0
  71. package/dist/utils/email.js +5 -0
  72. package/dist/utils/flow-runner.d.ts +18 -0
  73. package/dist/utils/flow-runner.js +39 -0
  74. package/dist/utils/index.d.ts +3 -0
  75. package/dist/utils/index.js +3 -0
  76. package/dist/utils/initials.d.ts +10 -0
  77. package/dist/utils/initials.js +27 -0
  78. package/dist/utils/time.d.ts +5 -0
  79. package/dist/utils/time.js +46 -0
  80. package/package.json +84 -0
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ import { X } from "lucide-react";
4
+ import * as React from "react";
5
+ import { cn } from "../utils/cn.js";
6
+ import { IconButton } from "./button.js";
7
+ export const Dialog = DialogPrimitive.Root;
8
+ export const DialogTrigger = DialogPrimitive.Trigger;
9
+ export const DialogPortal = DialogPrimitive.Portal;
10
+ export const DialogClose = DialogPrimitive.Close;
11
+ export const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-[color:var(--ssui-overlay)]", className), ...props })));
12
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
+ export const DialogContent = React.forwardRef(({ children, className, showClose = true, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--ssui-radius-lg)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-elevated)] p-6 text-[color:var(--ssui-text)] shadow-[var(--ssui-shadow-md)] focus:outline-none", className), ...props, children: [children, showClose ? (_jsx(DialogPrimitive.Close, { asChild: true, children: _jsx(IconButton, { className: "absolute right-3 top-3", label: "Close", size: "icon-sm", variant: "ghost", children: _jsx(X, {}) }) })) : null] })] })));
14
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
15
+ export function DialogHeader({ className, ...props }) {
16
+ return (_jsx("div", { className: cn("flex flex-col gap-1.5 text-left", className), ...props }));
17
+ }
18
+ export function DialogFooter({ className, ...props }) {
19
+ return (_jsx("div", { className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props }));
20
+ }
21
+ export const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn("text-lg font-semibold text-[color:var(--ssui-text)]", className), ...props })));
22
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
23
+ export const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn("text-sm text-[color:var(--ssui-text-muted)]", className), ...props })));
24
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
@@ -0,0 +1,23 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import * as React from "react";
3
+ export declare const DropdownMenu: React.FC<DropdownMenuPrimitive.DropdownMenuProps>;
4
+ export declare const DropdownMenuTrigger: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
+ export declare const DropdownMenuGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuGroupProps & React.RefAttributes<HTMLDivElement>>;
6
+ export declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPortalProps>;
7
+ export declare const DropdownMenuSub: React.FC<DropdownMenuPrimitive.DropdownMenuSubProps>;
8
+ export declare const DropdownMenuRadioGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
9
+ export declare const DropdownMenuSubTrigger: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubTriggerProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
10
+ inset?: boolean;
11
+ } & React.RefAttributes<HTMLDivElement>>;
12
+ export declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ export declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
14
+ export declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
15
+ inset?: boolean;
16
+ } & React.RefAttributes<HTMLDivElement>>;
17
+ export declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
+ export declare const DropdownMenuRadioItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
19
+ export declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
20
+ inset?: boolean;
21
+ } & React.RefAttributes<HTMLDivElement>>;
22
+ export declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
23
+ export declare function DropdownMenuShortcut({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
+ import { Check, ChevronRight, Circle } from "lucide-react";
4
+ import * as React from "react";
5
+ import { cn } from "../utils/cn.js";
6
+ export const DropdownMenu = DropdownMenuPrimitive.Root;
7
+ export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
8
+ export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
9
+ export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
10
+ export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
11
+ export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
12
+ export const DropdownMenuSubTrigger = React.forwardRef(({ children, className, inset, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.SubTrigger, { ref: ref, className: cn("flex cursor-default select-none items-center rounded-[var(--ssui-radius-sm)] px-2 py-1.5 text-sm text-[color:var(--ssui-text)] outline-none focus:bg-[color:var(--ssui-surface-muted)] data-[state=open]:bg-[color:var(--ssui-surface-muted)]", inset && "pl-8", className), ...props, children: [children, _jsx(ChevronRight, { className: "ml-auto size-4" })] })));
13
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
14
+ export const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.SubContent, { ref: ref, className: cn("z-50 min-w-40 overflow-hidden rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-elevated)] p-1 text-[color:var(--ssui-text)] shadow-[var(--ssui-shadow-md)]", className), ...props })));
15
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
16
+ export const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 6, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Portal, { children: _jsx(DropdownMenuPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 min-w-44 overflow-hidden rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-elevated)] p-1 text-[color:var(--ssui-text)] shadow-[var(--ssui-shadow-md)]", className), ...props }) })));
17
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
18
+ export const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Item, { ref: ref, className: cn("relative flex cursor-default select-none items-center gap-2 rounded-[var(--ssui-radius-sm)] px-2 py-1.5 text-sm outline-none transition-colors focus:bg-[color:var(--ssui-surface-muted)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50", inset && "pl-8", className), ...props })));
19
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
20
+ export const DropdownMenuCheckboxItem = React.forwardRef(({ children, className, checked, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.CheckboxItem, { ref: ref, checked: checked, className: cn("relative flex cursor-default select-none items-center rounded-[var(--ssui-radius-sm)] py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-[color:var(--ssui-surface-muted)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Check, { className: "size-4" }) }) }), children] })));
21
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
22
+ export const DropdownMenuRadioItem = React.forwardRef(({ children, className, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.RadioItem, { ref: ref, className: cn("relative flex cursor-default select-none items-center rounded-[var(--ssui-radius-sm)] py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-[color:var(--ssui-surface-muted)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Circle, { className: "size-2 fill-current" }) }) }), children] })));
23
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
24
+ export const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Label, { ref: ref, className: cn("px-2 py-1.5 text-xs font-medium text-[color:var(--ssui-text-muted)]", inset && "pl-8", className), ...props })));
25
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
26
+ export const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Separator, { ref: ref, className: cn("-mx-1 my-1 h-px bg-[color:var(--ssui-border)]", className), ...props })));
27
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
28
+ export function DropdownMenuShortcut({ className, ...props }) {
29
+ return (_jsx("span", { className: cn("ml-auto text-xs text-[color:var(--ssui-text-subtle)]", className), ...props }));
30
+ }
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ export interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
3
+ actions?: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ message?: React.ReactNode;
7
+ title?: React.ReactNode;
8
+ }
9
+ export declare function EmptyState({ actions, className, description, icon, message, title, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ export function EmptyState({ actions, className, description, icon, message, title, ...props }) {
5
+ const heading = title ?? message;
6
+ return (_jsxs("div", { className: cn("flex flex-col items-center justify-center gap-3 rounded-[var(--ssui-radius-lg)] border border-dashed border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface)] px-6 py-10 text-center", className), ...props, children: [icon ? (_jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-[color:var(--ssui-surface-muted)] text-[color:var(--ssui-text-muted)]", "aria-hidden": "true", children: icon })) : null, heading ? (_jsx("h3", { className: "max-w-md text-base font-semibold text-[color:var(--ssui-text)]", children: heading })) : null, description ? (_jsx("div", { className: "max-w-lg text-sm text-[color:var(--ssui-text-muted)]", children: description })) : null, actions ? _jsx("div", { className: "flex flex-wrap justify-center gap-2", children: actions }) : null] }));
7
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ export interface FilterChipProps extends React.HTMLAttributes<HTMLSpanElement> {
3
+ onPressedChange?: (pressed: boolean) => void;
4
+ onRemove?: () => void;
5
+ pressed?: boolean;
6
+ removeLabel?: string;
7
+ }
8
+ export declare function FilterChip({ children, className, onPressedChange, onRemove, pressed, removeLabel, ...props }: FilterChipProps): import("react/jsx-runtime").JSX.Element;
9
+ export interface FilterChipGroupProps extends React.HTMLAttributes<HTMLDivElement> {
10
+ }
11
+ export declare function FilterChipGroup({ className, ...props }: FilterChipGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { X } from "lucide-react";
3
+ import * as React from "react";
4
+ import { cn } from "../utils/cn.js";
5
+ export function FilterChip({ children, className, onPressedChange, onRemove, pressed = false, removeLabel = "Remove filter", ...props }) {
6
+ const label = (_jsx("span", { className: "inline-flex h-full min-w-0 items-center truncate px-3 text-sm font-medium leading-none", children: children }));
7
+ return (_jsxs("span", { className: cn("inline-flex h-8 max-w-full items-center overflow-hidden rounded-full border text-[color:var(--ssui-text)]", pressed
8
+ ? "border-[color:var(--ssui-primary)] bg-[color:var(--ssui-status-info-bg)]"
9
+ : "border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface)]", className), ...props, children: [onPressedChange ? (_jsx("button", { "aria-pressed": pressed, className: "flex h-full min-w-0 items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--ssui-focus-ring)]", onClick: () => onPressedChange(!pressed), type: "button", children: label })) : (label), onRemove ? (_jsx("button", { "aria-label": removeLabel, className: "mr-1 flex size-6 items-center justify-center rounded-full text-[color:var(--ssui-text-muted)] outline-none hover:bg-[color:var(--ssui-overlay-hover)] focus-visible:ring-2 focus-visible:ring-[color:var(--ssui-focus-ring)]", onClick: onRemove, type: "button", children: _jsx(X, { className: "size-3.5" }) })) : null] }));
10
+ }
11
+ export function FilterChipGroup({ className, ...props }) {
12
+ return _jsx("div", { className: cn("flex flex-wrap gap-2", className), ...props });
13
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from "react";
2
+ export interface Step {
3
+ /** Short step heading. */
4
+ title: React.ReactNode;
5
+ /** Optional rich description or supporting content for the step. */
6
+ description?: React.ReactNode;
7
+ }
8
+ export interface StepListProps extends Omit<React.OlHTMLAttributes<HTMLOListElement>, "children"> {
9
+ /** Ordered steps rendered as a numbered list. */
10
+ steps: Step[];
11
+ }
12
+ /**
13
+ * Ordered, numbered step list for doc-forward onboarding. Renders a semantic
14
+ * `<ol>` with numbered markers, a step title, and optional rich description.
15
+ */
16
+ export declare function StepList({ steps, className, ...props }: StepListProps): import("react/jsx-runtime").JSX.Element;
17
+ export interface GettingStartedProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
18
+ /** Optional intro heading above the steps. */
19
+ title?: React.ReactNode;
20
+ /** Optional intro copy above the steps. */
21
+ description?: React.ReactNode;
22
+ /** Ordered onboarding steps. */
23
+ steps: Step[];
24
+ /** Optional content rendered below the steps (notes, links, warnings). */
25
+ children?: React.ReactNode;
26
+ }
27
+ /**
28
+ * Doc-forward getting-started block: optional intro copy, a numbered
29
+ * {@link StepList}, and optional trailing content.
30
+ */
31
+ export declare function GettingStarted({ title, description, steps, children, className, ...props }: GettingStartedProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ /**
5
+ * Ordered, numbered step list for doc-forward onboarding. Renders a semantic
6
+ * `<ol>` with numbered markers, a step title, and optional rich description.
7
+ */
8
+ export function StepList({ steps, className, ...props }) {
9
+ return (_jsx("ol", { className: cn("space-y-4", className), ...props, children: steps.map((step, index) => (_jsxs("li", { className: "flex gap-3", children: [_jsx("span", { "aria-hidden": "true", className: "flex size-6 shrink-0 items-center justify-center rounded-full bg-[color:var(--ssui-surface-muted)] text-xs font-semibold text-[color:var(--ssui-text)]", children: index + 1 }), _jsxs("div", { className: "min-w-0 space-y-1 pt-0.5", children: [_jsx("div", { className: "text-sm font-medium text-[color:var(--ssui-text)]", children: step.title }), step.description ? (_jsx("div", { className: "text-sm text-[color:var(--ssui-text-muted)]", children: step.description })) : null] })] }, index))) }));
10
+ }
11
+ /**
12
+ * Doc-forward getting-started block: optional intro copy, a numbered
13
+ * {@link StepList}, and optional trailing content.
14
+ */
15
+ export function GettingStarted({ title, description, steps, children, className, ...props }) {
16
+ return (_jsxs("div", { className: cn("space-y-4", className), ...props, children: [title || description ? (_jsxs("div", { className: "space-y-1", children: [title ? (_jsx("div", { className: "text-sm font-semibold text-[color:var(--ssui-text)]", children: title })) : null, description ? (_jsx("div", { className: "text-sm text-[color:var(--ssui-text-muted)]", children: description })) : null] })) : null, _jsx(StepList, { steps: steps }), children ? _jsx("div", { children: children }) : null] }));
17
+ }
@@ -0,0 +1,29 @@
1
+ export * from "./audit-log.js";
2
+ export * from "./avatar.js";
3
+ export * from "./badge.js";
4
+ export * from "./brand-icons.js";
5
+ export * from "./button.js";
6
+ export * from "./confirm-dialog.js";
7
+ export * from "./copy-field.js";
8
+ export * from "./dialog.js";
9
+ export * from "./dropdown-menu.js";
10
+ export * from "./empty-state.js";
11
+ export * from "./filter-chip.js";
12
+ export * from "./getting-started.js";
13
+ export * from "./inline-notice.js";
14
+ export * from "./inline-rename-input.js";
15
+ export * from "./input.js";
16
+ export * from "./list-shell.js";
17
+ export * from "./login.js";
18
+ export * from "./page-header.js";
19
+ export * from "./request-access.js";
20
+ export * from "./scroll-area.js";
21
+ export * from "./search-input.js";
22
+ export * from "./separator.js";
23
+ export * from "./settings-section.js";
24
+ export * from "./skeleton.js";
25
+ export * from "./status-pill.js";
26
+ export * from "./tabs.js";
27
+ export * from "./textarea.js";
28
+ export * from "./toolbar.js";
29
+ export * from "./tooltip.js";
@@ -0,0 +1,29 @@
1
+ export * from "./audit-log.js";
2
+ export * from "./avatar.js";
3
+ export * from "./badge.js";
4
+ export * from "./brand-icons.js";
5
+ export * from "./button.js";
6
+ export * from "./confirm-dialog.js";
7
+ export * from "./copy-field.js";
8
+ export * from "./dialog.js";
9
+ export * from "./dropdown-menu.js";
10
+ export * from "./empty-state.js";
11
+ export * from "./filter-chip.js";
12
+ export * from "./getting-started.js";
13
+ export * from "./inline-notice.js";
14
+ export * from "./inline-rename-input.js";
15
+ export * from "./input.js";
16
+ export * from "./list-shell.js";
17
+ export * from "./login.js";
18
+ export * from "./page-header.js";
19
+ export * from "./request-access.js";
20
+ export * from "./scroll-area.js";
21
+ export * from "./search-input.js";
22
+ export * from "./separator.js";
23
+ export * from "./settings-section.js";
24
+ export * from "./skeleton.js";
25
+ export * from "./status-pill.js";
26
+ export * from "./tabs.js";
27
+ export * from "./textarea.js";
28
+ export * from "./toolbar.js";
29
+ export * from "./tooltip.js";
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ export type InlineNoticeVariant = "info" | "success" | "warning" | "danger";
3
+ export interface InlineNoticeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
4
+ actions?: React.ReactNode;
5
+ /**
6
+ * Where action buttons render relative to the content.
7
+ * - "block" (default): stacked below the content, full width.
8
+ * - "inline": grouped with the dismiss control on the right on >=sm screens,
9
+ * stacking below the content on narrow screens (a more compact layout).
10
+ */
11
+ actionsPlacement?: "inline" | "block";
12
+ autoDismissMs?: number;
13
+ dismissLabel?: string;
14
+ hideIcon?: boolean;
15
+ icon?: React.ReactNode;
16
+ onDismiss?: () => void;
17
+ /**
18
+ * Externally-controlled visibility. When provided, the component renders from
19
+ * `open` and never hides itself: dismiss and auto-dismiss only invoke
20
+ * `onDismiss`, leaving mount/unmount to the consumer. Omit for the default
21
+ * uncontrolled (self-hiding) behavior.
22
+ */
23
+ open?: boolean;
24
+ title?: React.ReactNode;
25
+ variant?: InlineNoticeVariant;
26
+ }
27
+ export declare function InlineNotice({ actions, actionsPlacement, autoDismissMs, children, className, dismissLabel, hideIcon, icon, onDismiss, open, title, variant, ...props }: InlineNoticeProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AlertTriangle, CheckCircle2, Info, X, XCircle } from "lucide-react";
3
+ import * as React from "react";
4
+ import { cn } from "../utils/cn.js";
5
+ import { IconButton } from "./button.js";
6
+ const noticeStyles = {
7
+ info: "border-[color:var(--ssui-notice-info-border)] bg-[color:var(--ssui-notice-info-bg)] text-[color:var(--ssui-notice-info-text)]",
8
+ success: "border-[color:var(--ssui-notice-success-border)] bg-[color:var(--ssui-notice-success-bg)] text-[color:var(--ssui-notice-success-text)]",
9
+ warning: "border-[color:var(--ssui-notice-warning-border)] bg-[color:var(--ssui-notice-warning-bg)] text-[color:var(--ssui-notice-warning-text)]",
10
+ danger: "border-[color:var(--ssui-notice-danger-border)] bg-[color:var(--ssui-notice-danger-bg)] text-[color:var(--ssui-notice-danger-text)]"
11
+ };
12
+ const noticeIcons = {
13
+ info: Info,
14
+ success: CheckCircle2,
15
+ warning: AlertTriangle,
16
+ danger: XCircle
17
+ };
18
+ export function InlineNotice({ actions, actionsPlacement = "block", autoDismissMs, children, className, dismissLabel = "Dismiss notice", hideIcon = false, icon, onDismiss, open, title, variant = "info", ...props }) {
19
+ const isControlled = open !== undefined;
20
+ const [visible, setVisible] = React.useState(true);
21
+ const isVisible = isControlled ? open : visible;
22
+ const Icon = noticeIcons[variant];
23
+ React.useEffect(() => {
24
+ if (autoDismissMs === undefined || !isVisible)
25
+ return;
26
+ const timer = window.setTimeout(() => {
27
+ if (!isControlled)
28
+ setVisible(false);
29
+ onDismiss?.();
30
+ }, autoDismissMs);
31
+ return () => window.clearTimeout(timer);
32
+ }, [autoDismissMs, isControlled, isVisible, onDismiss]);
33
+ if (!isVisible)
34
+ return null;
35
+ const role = variant === "danger" || variant === "warning" ? "alert" : "status";
36
+ const iconNode = hideIcon ? null : (_jsx("div", { className: "mt-0.5 shrink-0", "aria-hidden": "true", children: icon ?? _jsx(Icon, { className: "size-4" }) }));
37
+ const contentNode = (_jsxs("div", { className: "space-y-1", children: [title ? _jsx("div", { className: "font-medium", children: title }) : null, children ? _jsx("div", { children: children }) : null] }));
38
+ const dismissNode = onDismiss ? (_jsx(IconButton, { className: "-mr-1 -mt-1 text-current hover:bg-[color:var(--ssui-overlay-hover)]", label: dismissLabel, onClick: () => {
39
+ if (!isControlled)
40
+ setVisible(false);
41
+ onDismiss();
42
+ }, size: "icon-sm", type: "button", variant: "ghost", children: _jsx(X, {}) })) : null;
43
+ if (actionsPlacement === "inline") {
44
+ return (_jsxs("div", { role: role, className: cn("flex flex-col gap-3 rounded-[var(--ssui-radius)] border p-3 text-sm sm:flex-row sm:items-start sm:justify-between", noticeStyles[variant], className), ...props, children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [iconNode, _jsx("div", { className: "min-w-0 flex-1", children: contentNode })] }), actions || dismissNode ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2 self-start sm:shrink-0 sm:flex-nowrap", children: [actions, dismissNode] })) : null] }));
45
+ }
46
+ return (_jsxs("div", { role: role, className: cn("flex gap-3 rounded-[var(--ssui-radius)] border p-3 text-sm", noticeStyles[variant], className), ...props, children: [iconNode, _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [contentNode, actions ? _jsx("div", { className: "flex flex-wrap gap-2", children: actions }) : null] }), dismissNode] }));
47
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ import { type InputProps } from "./input.js";
3
+ export interface InlineRenameInputProps extends Omit<InputProps, "defaultValue" | "onCancel" | "onSubmit" | "value"> {
4
+ initialValue?: string;
5
+ onCancel?: () => void;
6
+ onSubmit: (value: string) => void;
7
+ selectOnMount?: boolean;
8
+ submitOnBlur?: boolean;
9
+ value?: string;
10
+ }
11
+ export declare const InlineRenameInput: React.ForwardRefExoticComponent<InlineRenameInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Input } from "./input.js";
4
+ export const InlineRenameInput = React.forwardRef(({ initialValue = "", onCancel, onBlur, onChange, onSubmit, onKeyDown, selectOnMount = true, submitOnBlur = true, value, ...props }, forwardedRef) => {
5
+ const [draft, setDraft] = React.useState(value ?? initialValue);
6
+ const inputRef = React.useRef(null);
7
+ const skippedBlurRef = React.useRef(false);
8
+ React.useImperativeHandle(forwardedRef, () => inputRef.current);
9
+ React.useEffect(() => {
10
+ if (value !== undefined) {
11
+ setDraft(value);
12
+ }
13
+ }, [value]);
14
+ React.useEffect(() => {
15
+ const input = inputRef.current;
16
+ if (!input)
17
+ return;
18
+ input.focus();
19
+ if (selectOnMount) {
20
+ input.select();
21
+ }
22
+ }, [selectOnMount]);
23
+ const commit = React.useCallback(() => {
24
+ skippedBlurRef.current = true;
25
+ onSubmit(draft);
26
+ }, [draft, onSubmit]);
27
+ return (_jsx(Input, { ref: inputRef, value: draft, onBlur: (event) => {
28
+ onBlur?.(event);
29
+ if (skippedBlurRef.current) {
30
+ skippedBlurRef.current = false;
31
+ return;
32
+ }
33
+ if (submitOnBlur) {
34
+ onSubmit(draft);
35
+ }
36
+ }, onChange: (event) => {
37
+ setDraft(event.target.value);
38
+ onChange?.(event);
39
+ }, onKeyDown: (event) => {
40
+ onKeyDown?.(event);
41
+ if (event.defaultPrevented || event.nativeEvent.isComposing)
42
+ return;
43
+ if (event.key === "Enter") {
44
+ event.preventDefault();
45
+ commit();
46
+ event.currentTarget.blur();
47
+ }
48
+ if (event.key === "Escape") {
49
+ event.preventDefault();
50
+ skippedBlurRef.current = true;
51
+ setDraft(value ?? initialValue);
52
+ onCancel?.();
53
+ event.currentTarget.blur();
54
+ }
55
+ }, ...props }));
56
+ });
57
+ InlineRenameInput.displayName = "InlineRenameInput";
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
3
+ }
4
+ export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ export const Input = React.forwardRef(({ className, type = "text", ...props }, ref) => (_jsx("input", { ref: ref, type: type, className: cn("flex h-10 w-full rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface)] px-3 py-2 text-sm text-[color:var(--ssui-text)] shadow-sm transition-colors placeholder:text-[color:var(--ssui-text-subtle)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--ssui-focus-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[color:var(--ssui-bg)] disabled:cursor-not-allowed disabled:bg-[color:var(--ssui-surface-muted)] disabled:opacity-70", className), ...props })));
5
+ Input.displayName = "Input";
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export interface ListShellProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ empty?: React.ReactNode;
4
+ footer?: React.ReactNode;
5
+ header?: React.ReactNode;
6
+ toolbar?: React.ReactNode;
7
+ }
8
+ export declare function ListShell({ children, className, empty, footer, header, toolbar, ...props }: ListShellProps): import("react/jsx-runtime").JSX.Element;
9
+ export interface ListRowProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
10
+ actions?: React.ReactNode;
11
+ asChild?: boolean;
12
+ description?: React.ReactNode;
13
+ leading?: React.ReactNode;
14
+ metadata?: React.ReactNode;
15
+ title?: React.ReactNode;
16
+ }
17
+ export declare const ListRow: React.ForwardRefExoticComponent<ListRowProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import * as React from "react";
4
+ import { cn } from "../utils/cn.js";
5
+ export function ListShell({ children, className, empty, footer, header, toolbar, ...props }) {
6
+ const hasContent = React.Children.count(children) > 0;
7
+ return (_jsxs("section", { className: cn("overflow-hidden rounded-[var(--ssui-radius-lg)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface)]", className), ...props, children: [header ? (_jsx("div", { className: "border-b border-[color:var(--ssui-border)] px-4 py-3", children: header })) : null, toolbar ? (_jsx("div", { className: "border-b border-[color:var(--ssui-border)] px-4 py-3", children: toolbar })) : null, hasContent ? (_jsx("div", { className: "divide-y divide-[color:var(--ssui-border)]", children: children })) : (empty), footer ? (_jsx("div", { className: "border-t border-[color:var(--ssui-border)] px-4 py-3", children: footer })) : null] }));
8
+ }
9
+ export const ListRow = React.forwardRef(({ actions, asChild = false, children, className, description, leading, metadata, title, ...props }, ref) => {
10
+ const Comp = asChild ? Slot : "div";
11
+ return (_jsxs(Comp, { ref: ref, className: cn("flex min-h-14 items-center gap-3 px-4 py-3 text-sm text-[color:var(--ssui-text)]", className), ...props, children: [leading ? _jsx("div", { className: "shrink-0", children: leading }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [title ? _jsx("div", { className: "truncate font-medium", children: title }) : null, description ? (_jsx("div", { className: "truncate text-[color:var(--ssui-text-muted)]", children: description })) : null, children] }), metadata ? (_jsx("div", { className: "hidden shrink-0 text-[color:var(--ssui-text-muted)] sm:block", children: metadata })) : null, actions ? _jsx("div", { className: "shrink-0", children: actions }) : null] }));
12
+ });
13
+ ListRow.displayName = "ListRow";
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+ export interface LoginProvider {
3
+ /** Selects the built-in brand icon (`google`, `microsoft`, `github`); unknown ids render label-only. */
4
+ id: string;
5
+ label: React.ReactNode;
6
+ /** Rejections surface the Error's message next to this provider's button. */
7
+ signIn: () => Promise<void>;
8
+ }
9
+ export interface LoginMagicLinkConfig {
10
+ defaultEmail?: string;
11
+ /** Rejections surface the Error's message; on resolve the form swaps to the sent confirmation. */
12
+ sendLink: (email: string) => Promise<void>;
13
+ sentMessage?: (email: string) => React.ReactNode;
14
+ submitLabel?: React.ReactNode;
15
+ }
16
+ export interface LoginDevLoginConfig {
17
+ label?: React.ReactNode;
18
+ open: () => Promise<void>;
19
+ }
20
+ export interface LoginRequestAccessConfig {
21
+ /** Consumer-supplied link element (e.g. the app router's `<Link>`), so the package stays framework-independent. */
22
+ link: React.ReactNode;
23
+ prompt?: React.ReactNode;
24
+ }
25
+ export interface LoginProps {
26
+ description?: React.ReactNode;
27
+ devLogin?: LoginDevLoginConfig;
28
+ /** Rare escape hatch rendered below the request-access line. */
29
+ footer?: React.ReactNode;
30
+ logo?: React.ReactNode;
31
+ magicLink?: LoginMagicLinkConfig;
32
+ providers?: LoginProvider[];
33
+ /** Initial error shown in the provider section, e.g. from an OAuth `?error=` redirect. */
34
+ providersError?: React.ReactNode;
35
+ requestAccess?: LoginRequestAccessConfig;
36
+ /** Visually hidden when a logo is provided; the wordmark carries the branding. */
37
+ title: React.ReactNode;
38
+ }
39
+ export declare function Login({ description, devLogin, footer, logo, magicLink, providers, providersError, requestAccess, title }: LoginProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { FlaskConical, Mail } from "lucide-react";
3
+ import * as React from "react";
4
+ import { isPlausibleEmail } from "../utils/email.js";
5
+ import { useFlowRunner } from "../utils/flow-runner.js";
6
+ import { AuthFeedback, AuthHeader, AuthLinkLine, AuthShell, AuthSpinner } from "./auth-shell.js";
7
+ import { brandIconsByProviderId } from "./brand-icons.js";
8
+ import { Button } from "./button.js";
9
+ import { Input } from "./input.js";
10
+ const MAGIC_LINK_FLOW = "magic-link";
11
+ const DEV_LOGIN_FLOW = "dev-login";
12
+ export function Login({ description, devLogin, footer, logo, magicLink, providers = [], providersError, requestAccess, title }) {
13
+ const flows = useFlowRunner();
14
+ const emailInputId = React.useId();
15
+ const [email, setEmail] = React.useState(() => magicLink?.defaultEmail ?? "");
16
+ const [sentEmail, setSentEmail] = React.useState(null);
17
+ const [interacted, setInteracted] = React.useState(false);
18
+ const trimmedEmail = email.trim();
19
+ const emailReady = isPlausibleEmail(trimmedEmail);
20
+ const hasDivider = providers.length > 0 && Boolean(magicLink);
21
+ function startFlow(key, action) {
22
+ setInteracted(true);
23
+ return flows.run(key, action);
24
+ }
25
+ async function handleMagicLinkSubmit(event) {
26
+ if (!magicLink)
27
+ return;
28
+ event.preventDefault();
29
+ if (flows.busy || !emailReady)
30
+ return;
31
+ const sent = await startFlow(MAGIC_LINK_FLOW, () => magicLink.sendLink(trimmedEmail));
32
+ if (sent)
33
+ setSentEmail(trimmedEmail);
34
+ }
35
+ return (_jsxs(AuthShell, { children: [_jsx(AuthHeader, { description: description, logo: logo, srOnlyTitleWithLogo: true, title: title }), _jsxs("div", { className: "space-y-5", children: [providers.length > 0 || providersError ? (_jsxs("section", { "aria-label": "Sign-in options", className: "space-y-2", children: [providers.map((provider) => {
36
+ const flowKey = `provider:${provider.id}`;
37
+ const active = flows.busyKey === flowKey;
38
+ const BrandIcon = brandIconsByProviderId[provider.id];
39
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Button, { "aria-busy": active || undefined, className: "w-full justify-center", disabled: flows.busy, onClick: () => {
40
+ void startFlow(flowKey, provider.signIn);
41
+ }, type: "button", variant: "secondary", children: [active ? (_jsx(AuthSpinner, {})) : BrandIcon ? (_jsx(BrandIcon, {})) : null, provider.label] }), _jsx(AuthFeedback, { tone: "error", children: flows.errorFor(flowKey) })] }, provider.id));
42
+ }), !interacted ? (_jsx(AuthFeedback, { tone: "error", children: providersError })) : null] })) : null, hasDivider ? (_jsxs("div", { "aria-hidden": "true", className: "flex items-center gap-3 text-xs text-[color:var(--ssui-text-subtle)]", children: [_jsx("span", { className: "h-px flex-1 bg-[color:var(--ssui-border)]" }), _jsx("span", { children: "or" }), _jsx("span", { className: "h-px flex-1 bg-[color:var(--ssui-border)]" })] })) : null, magicLink ? (_jsxs("section", { "aria-label": "Email sign-in", className: "space-y-2", children: [sentEmail === null ? (_jsxs("form", { "aria-busy": flows.busyKey === MAGIC_LINK_FLOW || undefined, "aria-label": "Magic link sign-in", className: "space-y-2", onSubmit: handleMagicLinkSubmit, children: [_jsx("label", { className: "block", htmlFor: emailInputId, children: _jsxs("span", { className: "mb-1.5 flex items-center gap-1.5 text-xs font-medium text-[color:var(--ssui-text-muted)]", children: [_jsx(Mail, { "aria-hidden": "true", className: "size-3.5" }), "Email"] }) }), _jsx(Input, { autoComplete: "email", disabled: flows.busy, id: emailInputId, inputMode: "email", name: "email", onChange: (event) => setEmail(event.target.value), placeholder: "you@example.com", type: "email", value: email }), _jsxs(Button, { "aria-busy": flows.busyKey === MAGIC_LINK_FLOW || undefined, className: "w-full justify-center", disabled: flows.busy || !emailReady, type: "submit", variant: "primary", children: [flows.busyKey === MAGIC_LINK_FLOW ? _jsx(AuthSpinner, {}) : _jsx(Mail, {}), magicLink.submitLabel ?? "Continue with email link"] })] })) : (_jsxs("div", { className: "space-y-3", children: [_jsx(AuthFeedback, { tone: "status", children: magicLink.sentMessage?.(sentEmail) ?? (_jsxs(_Fragment, { children: ["We sent a sign-in link to", " ", _jsx("span", { className: "font-medium text-[color:var(--ssui-text)]", children: sentEmail }), ". Check your inbox."] })) }), _jsxs(Button, { "aria-busy": flows.busyKey === MAGIC_LINK_FLOW || undefined, className: "w-full justify-center", disabled: flows.busy, onClick: () => {
43
+ void startFlow(MAGIC_LINK_FLOW, () => magicLink.sendLink(sentEmail));
44
+ }, type: "button", variant: "secondary", children: [flows.busyKey === MAGIC_LINK_FLOW ? _jsx(AuthSpinner, {}) : null, "Resend link"] }), _jsx(Button, { className: "w-full justify-center", disabled: flows.busy, onClick: () => {
45
+ setSentEmail(null);
46
+ flows.clearError();
47
+ }, type: "button", variant: "ghost", children: "Use a different email" })] })), _jsx(AuthFeedback, { tone: "error", children: flows.errorFor(MAGIC_LINK_FLOW) })] })) : null] }), devLogin ? (_jsxs("section", { className: "mt-5 space-y-2 border-t border-dashed border-[color:var(--ssui-border)] pt-4", children: [_jsxs(Button, { "aria-busy": flows.busyKey === DEV_LOGIN_FLOW || undefined, className: "w-full justify-center border-dashed bg-transparent text-[color:var(--ssui-text-muted)] hover:text-[color:var(--ssui-text)]", disabled: flows.busy, onClick: () => {
48
+ void startFlow(DEV_LOGIN_FLOW, devLogin.open);
49
+ }, type: "button", variant: "outline", children: [flows.busyKey === DEV_LOGIN_FLOW ? _jsx(AuthSpinner, {}) : _jsx(FlaskConical, {}), devLogin.label ?? "Open dev workspace"] }), _jsx(AuthFeedback, { tone: "error", children: flows.errorFor(DEV_LOGIN_FLOW) })] })) : null, requestAccess || footer ? (_jsxs("div", { className: "mt-6 space-y-2", children: [requestAccess ? (_jsx(AuthLinkLine, { link: requestAccess.link, prompt: requestAccess.prompt ?? "Need access?" })) : null, footer ? (_jsx("div", { className: "text-sm text-[color:var(--ssui-text-muted)]", children: footer })) : null] })) : null] }));
50
+ }
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
3
+ actions?: React.ReactNode;
4
+ breadcrumbs?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ eyebrow?: React.ReactNode;
7
+ metadata?: React.ReactNode;
8
+ title: React.ReactNode;
9
+ }
10
+ export declare function PageHeader({ actions, breadcrumbs, className, description, eyebrow, metadata, title, ...props }: PageHeaderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ export function PageHeader({ actions, breadcrumbs, className, description, eyebrow, metadata, title, ...props }) {
5
+ return (_jsxs("header", { className: cn("flex flex-col gap-4 border-b border-[color:var(--ssui-border)] pb-4 sm:flex-row sm:items-end sm:justify-between", className), ...props, children: [_jsxs("div", { className: "min-w-0 space-y-2", children: [breadcrumbs ? _jsx("div", { children: breadcrumbs }) : null, eyebrow ? (_jsx("div", { className: "text-xs font-medium uppercase text-[color:var(--ssui-text-muted)]", children: eyebrow })) : null, _jsxs("div", { className: "space-y-1", children: [_jsx("h1", { className: "truncate text-2xl font-semibold text-[color:var(--ssui-text)]", children: title }), description ? (_jsx("div", { className: "max-w-3xl text-sm text-[color:var(--ssui-text-muted)]", children: description })) : null] }), metadata ? _jsx("div", { className: "flex flex-wrap gap-2", children: metadata }) : null] }), actions ? _jsx("div", { className: "flex shrink-0 flex-wrap gap-2", children: actions }) : null] }));
6
+ }
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ export interface RequestAccessRequestConfig {
3
+ /** Rejections surface the Error's message inside the request card. */
4
+ submit: (email: string) => Promise<void>;
5
+ successMessage?: React.ReactNode;
6
+ }
7
+ export interface RequestAccessCodeConfig {
8
+ /** Rejections surface the Error's message inside the access-code card. */
9
+ submit: (code: string) => Promise<void>;
10
+ }
11
+ export interface RequestAccessSignInConfig {
12
+ /** Consumer-supplied link element (e.g. the app router's `<Link>`), so the package stays framework-independent. */
13
+ link: React.ReactNode;
14
+ prompt?: React.ReactNode;
15
+ }
16
+ export interface RequestAccessProps {
17
+ /** Optional access-code card; omitted entirely when not provided. */
18
+ accessCode?: RequestAccessCodeConfig;
19
+ description?: React.ReactNode;
20
+ logo?: React.ReactNode;
21
+ request: RequestAccessRequestConfig;
22
+ signIn?: RequestAccessSignInConfig;
23
+ title?: React.ReactNode;
24
+ }
25
+ export declare function RequestAccess({ accessCode, description, logo, request, signIn, title }: RequestAccessProps): import("react/jsx-runtime").JSX.Element;