@cosmoops/connect-sdk 0.9.0 → 0.16.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 (155) hide show
  1. package/CHANGELOG.md +566 -0
  2. package/README.md +113 -42
  3. package/dist/cjs/cli/doctor.js +3 -3
  4. package/dist/cjs/cli/init.js +3 -3
  5. package/dist/cjs/client/components/access-gate.js +20 -1
  6. package/dist/cjs/client/components/ai-screens.js +73 -0
  7. package/dist/cjs/client/components/ai-widgets.js +37 -0
  8. package/dist/cjs/client/components/app-shell.js +75 -41
  9. package/dist/cjs/client/components/apps-grid.js +3 -2
  10. package/dist/cjs/client/components/ask-ai.js +50 -5
  11. package/dist/cjs/client/components/confirm-dialog.js +40 -0
  12. package/dist/cjs/client/components/cursor-pagination.js +22 -0
  13. package/dist/cjs/client/components/filter-tabs.js +24 -0
  14. package/dist/cjs/client/components/grievance-notice.js +30 -0
  15. package/dist/cjs/client/components/link-button.js +17 -10
  16. package/dist/cjs/client/components/logo.js +4 -3
  17. package/dist/cjs/client/components/nav.js +41 -0
  18. package/dist/cjs/client/components/plan-pricing.js +27 -0
  19. package/dist/cjs/client/components/plans.js +1 -1
  20. package/dist/cjs/client/components/platform-gate.js +48 -0
  21. package/dist/cjs/client/components/shell-context.js +149 -0
  22. package/dist/cjs/client/components/side-nav.js +49 -23
  23. package/dist/cjs/client/components/sidebar.js +127 -0
  24. package/dist/cjs/client/components/subscribe-dialog.js +2 -2
  25. package/dist/cjs/client/components/subscription.js +1 -1
  26. package/dist/cjs/client/components/user-menu.js +96 -37
  27. package/dist/cjs/client/components/workspace-switcher.js +100 -0
  28. package/dist/cjs/client/index.js +72 -2
  29. package/dist/cjs/client/provider.js +57 -2
  30. package/dist/cjs/client/use-checkout.js +9 -5
  31. package/dist/cjs/errors.js +1 -1
  32. package/dist/cjs/index.js +16 -1
  33. package/dist/cjs/mcp/index.js +90 -0
  34. package/dist/cjs/mcp/tools.js +369 -0
  35. package/dist/cjs/next/index.js +2 -2
  36. package/dist/cjs/server/client.js +25 -2
  37. package/dist/cjs/server/handler.js +203 -18
  38. package/dist/cjs/server/http.js +6 -2
  39. package/dist/cjs/server/resources/acting-as.js +16 -0
  40. package/dist/cjs/server/resources/ai.js +22 -2
  41. package/dist/cjs/server/resources/data.js +374 -0
  42. package/dist/cjs/server/resources/identity.js +7 -3
  43. package/dist/cjs/server/resources/kv.js +84 -0
  44. package/dist/cjs/server/resources/platform.js +43 -0
  45. package/dist/cjs/server/resources/sessions.js +1 -1
  46. package/dist/cjs/server/resources/workspaces.js +74 -0
  47. package/dist/cjs/store-schema.js +66 -0
  48. package/dist/cjs/store.js +152 -0
  49. package/dist/cjs/ui/empty-state.js +28 -0
  50. package/dist/cjs/ui/index.js +44 -0
  51. package/dist/cjs/ui/page-header.js +27 -0
  52. package/dist/cjs/ui/page-shell.js +44 -0
  53. package/dist/cjs/ui/section.js +20 -0
  54. package/dist/cjs/ui/setting-row.js +14 -0
  55. package/dist/cjs/ui/status-chip.js +17 -0
  56. package/dist/esm/cli/doctor.js +3 -3
  57. package/dist/esm/cli/init.js +3 -3
  58. package/dist/esm/client/components/access-gate.js +20 -1
  59. package/dist/esm/client/components/ai-screens.js +69 -0
  60. package/dist/esm/client/components/ai-widgets.js +37 -1
  61. package/dist/esm/client/components/app-shell.js +81 -47
  62. package/dist/esm/client/components/apps-grid.js +3 -2
  63. package/dist/esm/client/components/ask-ai.js +51 -6
  64. package/dist/esm/client/components/confirm-dialog.js +37 -0
  65. package/dist/esm/client/components/cursor-pagination.js +19 -0
  66. package/dist/esm/client/components/filter-tabs.js +21 -0
  67. package/dist/esm/client/components/grievance-notice.js +27 -0
  68. package/dist/esm/client/components/link-button.js +17 -10
  69. package/dist/esm/client/components/logo.js +4 -3
  70. package/dist/esm/client/components/nav.js +40 -0
  71. package/dist/esm/client/components/plan-pricing.js +25 -0
  72. package/dist/esm/client/components/plans.js +2 -2
  73. package/dist/esm/client/components/platform-gate.js +45 -0
  74. package/dist/esm/client/components/shell-context.js +141 -0
  75. package/dist/esm/client/components/side-nav.js +48 -21
  76. package/dist/esm/client/components/sidebar.js +120 -0
  77. package/dist/esm/client/components/subscribe-dialog.js +3 -3
  78. package/dist/esm/client/components/subscription.js +2 -2
  79. package/dist/esm/client/components/user-menu.js +95 -38
  80. package/dist/esm/client/components/workspace-switcher.js +97 -0
  81. package/dist/esm/client/index.js +47 -4
  82. package/dist/esm/client/provider.js +53 -2
  83. package/dist/esm/client/use-checkout.js +9 -5
  84. package/dist/esm/errors.js +1 -1
  85. package/dist/esm/index.js +4 -0
  86. package/dist/esm/mcp/index.js +85 -0
  87. package/dist/esm/mcp/tools.js +366 -0
  88. package/dist/esm/next/index.js +2 -2
  89. package/dist/esm/server/client.js +25 -2
  90. package/dist/esm/server/handler.js +203 -18
  91. package/dist/esm/server/http.js +6 -2
  92. package/dist/esm/server/resources/acting-as.js +13 -0
  93. package/dist/esm/server/resources/ai.js +22 -2
  94. package/dist/esm/server/resources/data.js +369 -0
  95. package/dist/esm/server/resources/identity.js +7 -3
  96. package/dist/esm/server/resources/kv.js +80 -0
  97. package/dist/esm/server/resources/platform.js +39 -0
  98. package/dist/esm/server/resources/sessions.js +1 -1
  99. package/dist/esm/server/resources/workspaces.js +70 -0
  100. package/dist/esm/store-schema.js +62 -0
  101. package/dist/esm/store.js +141 -0
  102. package/dist/esm/ui/empty-state.js +25 -0
  103. package/dist/esm/ui/index.js +34 -0
  104. package/dist/esm/ui/page-header.js +24 -0
  105. package/dist/esm/ui/page-shell.js +41 -0
  106. package/dist/esm/ui/section.js +17 -0
  107. package/dist/esm/ui/setting-row.js +11 -0
  108. package/dist/esm/ui/status-chip.js +14 -0
  109. package/dist/types/client/components/ai-screens.d.ts +25 -0
  110. package/dist/types/client/components/ai-widgets.d.ts +34 -1
  111. package/dist/types/client/components/app-shell.d.ts +65 -27
  112. package/dist/types/client/components/apps-grid.d.ts +11 -2
  113. package/dist/types/client/components/ask-ai.d.ts +47 -1
  114. package/dist/types/client/components/confirm-dialog.d.ts +34 -0
  115. package/dist/types/client/components/cursor-pagination.d.ts +27 -0
  116. package/dist/types/client/components/filter-tabs.d.ts +26 -0
  117. package/dist/types/client/components/grievance-notice.d.ts +31 -0
  118. package/dist/types/client/components/link-button.d.ts +32 -9
  119. package/dist/types/client/components/logo.d.ts +18 -2
  120. package/dist/types/client/components/nav.d.ts +24 -0
  121. package/dist/types/client/components/plan-pricing.d.ts +10 -0
  122. package/dist/types/client/components/platform-gate.d.ts +53 -0
  123. package/dist/types/client/components/shell-context.d.ts +116 -0
  124. package/dist/types/client/components/side-nav.d.ts +27 -13
  125. package/dist/types/client/components/sidebar.d.ts +95 -0
  126. package/dist/types/client/components/user-menu.d.ts +68 -22
  127. package/dist/types/client/components/workspace-switcher.d.ts +49 -0
  128. package/dist/types/client/index.d.ts +29 -5
  129. package/dist/types/client/provider.d.ts +109 -6
  130. package/dist/types/client/use-checkout.d.ts +8 -4
  131. package/dist/types/errors.d.ts +1 -1
  132. package/dist/types/index.d.ts +6 -0
  133. package/dist/types/mcp/index.d.ts +74 -0
  134. package/dist/types/mcp/tools.d.ts +107 -0
  135. package/dist/types/next/index.d.ts +1 -1
  136. package/dist/types/server/client.d.ts +25 -2
  137. package/dist/types/server/http.d.ts +8 -2
  138. package/dist/types/server/index.d.ts +4 -0
  139. package/dist/types/server/resources/acting-as.d.ts +11 -0
  140. package/dist/types/server/resources/ai.d.ts +20 -2
  141. package/dist/types/server/resources/data.d.ts +258 -0
  142. package/dist/types/server/resources/kv.d.ts +63 -0
  143. package/dist/types/server/resources/platform.d.ts +32 -0
  144. package/dist/types/server/resources/workspaces.d.ts +55 -0
  145. package/dist/types/store-schema.d.ts +52 -0
  146. package/dist/types/store.d.ts +96 -0
  147. package/dist/types/types.d.ts +369 -16
  148. package/dist/types/ui/empty-state.d.ts +34 -0
  149. package/dist/types/ui/index.d.ts +40 -0
  150. package/dist/types/ui/page-header.d.ts +34 -0
  151. package/dist/types/ui/page-shell.d.ts +39 -0
  152. package/dist/types/ui/section.d.ts +26 -0
  153. package/dist/types/ui/setting-row.d.ts +18 -0
  154. package/dist/types/ui/status-chip.d.ts +36 -0
  155. package/package.json +15 -2
@@ -0,0 +1,34 @@
1
+ import { type ReactNode } from "react";
2
+ export interface ConnectConfirmDialogProps {
3
+ /** The control that opens it. Rendered inside `Modal.Trigger`. */
4
+ trigger: ReactNode;
5
+ title: ReactNode;
6
+ /** What will happen, in the user's words. Name the thing being acted on. */
7
+ description?: ReactNode;
8
+ confirmLabel?: string;
9
+ cancelLabel?: string;
10
+ /** `danger` for anything that destroys data. */
11
+ tone?: "danger" | "primary";
12
+ onConfirm: () => void | Promise<void>;
13
+ }
14
+ /**
15
+ * The confirmation in front of a destructive action.
16
+ *
17
+ * **It closes on confirm, not on response.** Holding the modal open until the
18
+ * server answers makes every delete feel like a form submission, and the error
19
+ * path already has somewhere to go - a toast, or the row reappearing. A dialog
20
+ * that stays open to report failure is right only where the failure is
21
+ * *expected* and correctable in place (a seat cap, a last-admin invariant),
22
+ * and that is a blocking form, not this.
23
+ *
24
+ * `onConfirm` may return a promise. It is not awaited before closing, but a
25
+ * rejection is caught here rather than escaping - an error thrown out of an
26
+ * async transition reaches the nearest error boundary and takes the page down.
27
+ *
28
+ * The anatomy is HeroUI's: `Modal.Backdrop` owns the open state,
29
+ * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. The trigger
30
+ * goes inside `Modal.Trigger`, which *is* a React Aria button - so pass a
31
+ * plain element, never a `<Button>`, or the nested button swallows the press
32
+ * and the dialog silently never opens.
33
+ */
34
+ export declare function ConnectConfirmDialog({ trigger, title, description, confirmLabel, cancelLabel, tone, onConfirm, }: Readonly<ConnectConfirmDialogProps>): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ export interface ConnectCursorPaginationProps {
2
+ /** How many rows the current page is showing. Used for the live-region count. */
3
+ shown: number;
4
+ /** The cursor for the next page, or `null` when this is the last one. */
5
+ nextCursor: string | null;
6
+ /** Whether a previous page exists - the caller owns the cursor history. */
7
+ hasPrevious: boolean;
8
+ onNext: (cursor: string) => void;
9
+ onPrevious: () => void;
10
+ isPending?: boolean;
11
+ /** Singular/plural noun for the count, e.g. `["contact", "contacts"]`. */
12
+ noun?: readonly [string, string];
13
+ }
14
+ /**
15
+ * Next / previous for a cursor-paginated list.
16
+ *
17
+ * Cursor rather than page numbers, and the component shape follows from that:
18
+ * there is no "page 7 of 42" because a keyset-paginated list does not know how
19
+ * many pages there are without a second count query over the same filter -
20
+ * which is a billed index scan for a number nobody acts on. What a user does
21
+ * act on is "more" and "back", and those are exact.
22
+ *
23
+ * The count is announced in a live region so a screen-reader user learns that
24
+ * filtering or paging changed the result set. Without it the table silently
25
+ * swaps its contents.
26
+ */
27
+ export declare function ConnectCursorPagination({ shown, nextCursor, hasPrevious, onNext, onPrevious, isPending, noun, }: Readonly<ConnectCursorPaginationProps>): import("react").JSX.Element;
@@ -0,0 +1,26 @@
1
+ export interface ConnectFilterTabsOption<TValue extends string> {
2
+ value: TValue | null;
3
+ label: string;
4
+ /** Shown as a count beside the label. Omit where a count would need its own query. */
5
+ count?: number;
6
+ }
7
+ export interface ConnectFilterTabsProps<TValue extends string> {
8
+ options: ReadonlyArray<ConnectFilterTabsOption<TValue>>;
9
+ value: TValue | null;
10
+ onChange: (value: TValue | null) => void;
11
+ /** Names the group for assistive technology - "Filter contacts by lifecycle". */
12
+ label: string;
13
+ }
14
+ /**
15
+ * A single-select filter strip - the "All / Open / Won / Lost" row above a
16
+ * list.
17
+ *
18
+ * Real radio semantics rather than a row of buttons: it is a single choice
19
+ * from a closed set, so arrow keys should move between options and the
20
+ * selected one should be announced as selected. A group of `<button>`s gets
21
+ * neither, and that is the usual hand-rolled shape.
22
+ *
23
+ * Scrolls horizontally rather than wrapping on a narrow viewport - a filter
24
+ * strip that wraps to three lines pushes the list itself off the screen.
25
+ */
26
+ export declare function ConnectFilterTabs<TValue extends string>({ options, value, onChange, label, }: Readonly<ConnectFilterTabsProps<TValue>>): import("react").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ConnectGrievanceNoticeProps {
3
+ /**
4
+ * Heading text. `null` renders none, for embedding inside a page that
5
+ * already has one.
6
+ */
7
+ title?: ReactNode;
8
+ /**
9
+ * What this application calls itself in the sentence naming the processor
10
+ * relationship. Omit to leave that sentence out entirely.
11
+ */
12
+ applicationName?: string;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * The DPDP s.13 grievance channel, rendered.
17
+ *
18
+ * Every application on the platform is a Data Processor under one notice, so
19
+ * the wording lives here rather than being retyped per application - a user
20
+ * comparing two applications' privacy pages should not find two different
21
+ * accounts of who their Data Fiduciary is.
22
+ *
23
+ * Renders nothing at all while loading and nothing on failure, rather than a
24
+ * skeleton or an error. That is unusual here and deliberate: this component
25
+ * is almost always inside a larger privacy page that has its own content, and
26
+ * a red error box in the middle of a legal notice reads as the notice itself
27
+ * being broken. The address is also on the public web either way. Pass
28
+ * `useConnectCompanyProfile()` directly to your own layout if you want to
29
+ * handle those states yourself.
30
+ */
31
+ export declare function ConnectGrievanceNotice({ title, applicationName, className, }: Readonly<ConnectGrievanceNoticeProps>): import("react").JSX.Element | null;
@@ -1,20 +1,43 @@
1
1
  import { type ButtonProps } from "@heroui/react";
2
+ import type { ReactElement, ReactNode } from "react";
2
3
  export type ConnectLinkButtonProps = Omit<ButtonProps, "render"> & {
3
4
  href: string;
5
+ /**
6
+ * The host's router link, if it has one.
7
+ *
8
+ * Without it this renders a plain `<a href>`, which navigates under every
9
+ * router but costs a **full document load** - in an App Router app that
10
+ * throws away the client cache and re-runs the whole tree, which is very
11
+ * visible on a button sitting inside a page the user is working in.
12
+ *
13
+ * ```tsx
14
+ * <ConnectLinkButton href="/deals" renderLink={(props) => <Link {...props} />}>
15
+ * Open board
16
+ * </ConnectLinkButton>
17
+ * ```
18
+ *
19
+ * Same shape as `AppShell`'s `renderLink`, so a host declares its router
20
+ * once and passes the same function to both. This package imports nothing
21
+ * from `next` - see the `./next` entry's doc comment for why that is a hard
22
+ * rule - so handing the link component in is the only way it can be a client
23
+ * transition.
24
+ */
25
+ renderLink?: (props: {
26
+ href: string;
27
+ className?: string;
28
+ children: ReactNode;
29
+ }) => ReactElement;
4
30
  };
5
31
  /**
6
- * A HeroUI `Button` that renders a real `<a>`.
32
+ * A HeroUI `Button` that renders a link.
7
33
  *
8
- * A plain anchor, deliberately not a router link: this package imports nothing
9
- * from `next` (see the `./next` entry's doc comment for why that is a hard
10
- * rule), so it cannot know which router - if any - the host app uses. An
11
- * `<a href>` navigates under all of them, and a host that wants client-side
12
- * routing can pass its own element via `render` on the underlying `Button`.
34
+ * HeroUI v3's `Button` has no `as` prop, so `<Button as={Link}>` does not type
35
+ * check and does not work - this is the supported route, via `render`.
13
36
  *
14
37
  * The cast is the same one HeroUI's own "Render Function" example needs:
15
38
  * `render` types its argument as the props of the element `Button` would have
16
- * emitted, which is a `<button>` - including a `ref` typed to
39
+ * emitted, which is a `<button>`, including a `ref` typed to
17
40
  * `HTMLButtonElement`. Spreading that onto an anchor is sound at runtime and
18
- * unrepresentable in the type, which is exactly what a cast is for.
41
+ * unrepresentable in the type, which is what the cast is for.
19
42
  */
20
- export declare function ConnectLinkButton({ href, ...props }: Readonly<ConnectLinkButtonProps>): import("react").JSX.Element;
43
+ export declare function ConnectLinkButton({ href, renderLink, ...props }: Readonly<ConnectLinkButtonProps>): import("react").JSX.Element;
@@ -14,7 +14,23 @@ export interface ConnectLogoProps {
14
14
  * Falls back to the Connect rings when omitted.
15
15
  */
16
16
  icon?: ReactNode;
17
- /** Mark only, no wordmark - for a collapsed sidebar or a favicon-sized slot. */
17
+ /**
18
+ * Which halves of the lockup to draw.
19
+ *
20
+ * - `"full"` - badge and wordmark, the default.
21
+ * - `"mark"` - the badge alone, for a collapsed rail or a favicon-sized slot.
22
+ * - `"wordmark"` - the words alone, for a layout that already draws the
23
+ * badge somewhere fixed. The sidebar header uses this: the badge stays put
24
+ * while the words fade, and rendering a second badge only to hide it with
25
+ * a CSS child selector is how that kind of layout quietly rots.
26
+ */
27
+ variant?: "full" | "mark" | "wordmark";
28
+ /**
29
+ * Mark only, no wordmark.
30
+ *
31
+ * @deprecated Pass `variant="mark"`. Kept because it is the published 0.11
32
+ * API; `variant` wins when both are given.
33
+ */
18
34
  collapsed?: boolean;
19
35
  className?: string;
20
36
  }
@@ -32,4 +48,4 @@ export interface ConnectLogoProps {
32
48
  * as `text-sm`, with the badge, gap and tracking all in proportion. That is
33
49
  * why there is no `size` prop.
34
50
  */
35
- export declare function ConnectLogo({ name, icon, collapsed, className }: ConnectLogoProps): import("react").JSX.Element;
51
+ export declare function ConnectLogo({ name, icon, variant, collapsed, className }: ConnectLogoProps): import("react").JSX.Element;
@@ -33,6 +33,21 @@ export interface ConnectNavItem {
33
33
  * step with this one.
34
34
  */
35
35
  gateExempt?: boolean;
36
+ /**
37
+ * Who this route is for. Omit - the default - and it renders for everyone,
38
+ * which is what every route on an ordinary application is.
39
+ *
40
+ * - `"staff"` shows it only to a Connect platform staff session; your
41
+ * operator console goes here.
42
+ * - `"member"` hides it *from* staff. Reach for it on anything that reads
43
+ * the workspace - billing, plans, usage, team - since a staff session has
44
+ * no workspace and those routes can only fail for it.
45
+ *
46
+ * Filtering here is presentation, not access: it decides which links a
47
+ * person sees, and a link is not a permission. The route itself must still
48
+ * check `staff` on your server - see `<ConnectPlatformGate>`.
49
+ */
50
+ audience?: "staff" | "member";
36
51
  }
37
52
  /** A titled group of items. Untitled groups render as a plain divider-free block. */
38
53
  export interface ConnectNavSection {
@@ -106,5 +121,14 @@ export declare function resolveActivePath(pathname: string, nav: ConnectNav): st
106
121
  * lights up on every app's home route. External items are never active.
107
122
  */
108
123
  export declare function isNavItemActive(item: ConnectNavItem, activeHref: string | null): boolean;
124
+ /**
125
+ * `nav` with every item the current session should not see removed - see
126
+ * `ConnectNavItem.audience`.
127
+ *
128
+ * Applied to nested `items` too, and a parent whose children all disappear is
129
+ * dropped along with them: a section header pointing at nothing reads as a
130
+ * broken link rather than as a hidden one.
131
+ */
132
+ export declare function filterNavForAudience(nav: ConnectNav, isStaff: boolean): ConnectNav;
109
133
  /** Hrefs declared `gateExempt` - the routes `AppShell` renders outside its `ConnectAccessGate`. */
110
134
  export declare function gateExemptPaths(nav: ConnectNav): string[];
@@ -24,6 +24,16 @@ export declare function formatPrice(paise: number): string;
24
24
  * plan reads as an upgrade over every monthly one.
25
25
  */
26
26
  export declare function monthlyOf(price: number, billingInterval: Plan["billingInterval"]): number;
27
+ export declare function intervalLabel(billingInterval: Plan["billingInterval"]): {
28
+ per: string;
29
+ short: string;
30
+ adverb: string;
31
+ };
32
+ /**
33
+ * `/month`, `/year` - and nothing at all for a one-time plan, where a price
34
+ * with no slash is the honest rendering.
35
+ */
36
+ export declare function pricePerLabel(billingInterval: Plan["billingInterval"]): string;
27
37
  export declare function discountLabel(discount: AppliedDiscount): string;
28
38
  /**
29
39
  * The minimum a plan-like object has to expose to be grouped or compared -
@@ -0,0 +1,53 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ConnectPlatformGateProps {
3
+ /**
4
+ * Restrict to these platform roles. Omit to accept any staff session, which
5
+ * is the common case - your application already chose which roles it accepts
6
+ * when a platform admin turned staff access on for it, and repeating that
7
+ * list here is a second copy to keep in sync.
8
+ *
9
+ * Pass one only when your own console has tiers: a `developer` seeing the
10
+ * read-only half of what a `platform_admin` can edit.
11
+ */
12
+ roles?: string[];
13
+ children: ReactNode;
14
+ /**
15
+ * Rendered for an ordinary member session. Defaults to `null` - hiding the
16
+ * subtree, which is what this gate is normally for. An operator console is
17
+ * usually better hidden than explained.
18
+ */
19
+ fallback?: ReactNode;
20
+ /** Shown until the session read settles. Defaults to nothing. */
21
+ loading?: ReactNode;
22
+ }
23
+ /**
24
+ * Renders `children` only for a Connect platform staff session.
25
+ *
26
+ * ```tsx
27
+ * <ConnectPlatformGate>
28
+ * <LinkMasterConsole />
29
+ * </ConnectPlatformGate>
30
+ * ```
31
+ *
32
+ * Staff are Connect's own operators - `platform_admin`, `platform_manager`,
33
+ * `developer` - signing into *your* application. They belong to no workspace,
34
+ * so a staff session carries no `workspaceId`, no subscription and no member
35
+ * role; see `SessionAccess.staff`. Staff access is off by default and turned on
36
+ * per application by a Connect platform admin, so this gate renders nothing
37
+ * until that happens.
38
+ *
39
+ * **This is a UX gate, not a security boundary** - the same caveat every gate
40
+ * in this package carries, and it matters more here than anywhere else,
41
+ * because what is behind this one is usually an operator console. The browser
42
+ * can ignore this component entirely. Every route it reveals must re-check
43
+ * `staff` on your own server:
44
+ *
45
+ * ```ts
46
+ * const { staff, role } = await client.identity.getUser(sessionToken);
47
+ * if (!staff) return forbidden();
48
+ * ```
49
+ *
50
+ * Renders `loading` - not `fallback` - while the session is unknown, so an
51
+ * operator never flickers through the customer layout on the way in.
52
+ */
53
+ export declare function ConnectPlatformGate({ roles, children, fallback, loading, }: ConnectPlatformGateProps): import("react").JSX.Element;
@@ -0,0 +1,116 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ConnectLinkRenderer } from "./nav.js";
3
+ /** A plain anchor - correct anywhere, and a full page load. See `ConnectLinkRenderer`. */
4
+ export declare const defaultRenderLink: ConnectLinkRenderer;
5
+ /**
6
+ * The rail's two widths, in pixels, and the one place they are written down.
7
+ *
8
+ * They are numbers rather than Tailwind classes because the rail animates
9
+ * between them - `motion` interpolates a number and cannot interpolate
10
+ * `w-16` into `w-64`.
11
+ *
12
+ * **The pairing is load-bearing, not arbitrary.** Every row in the sidebar is
13
+ * laid out so its glyph sits exactly `RAIL_GUTTER + ROW_GUTTER + 10px` from
14
+ * the rail's left edge - 32px, which is also `RAIL_COLLAPSED / 2`. So the
15
+ * collapsed rail centres every icon *without a single centring rule*, and
16
+ * nothing moves horizontally while the width animates. Change either width
17
+ * and that identity breaks: the icons drift sideways during the transition,
18
+ * which is the exact stutter this geometry exists to avoid.
19
+ */
20
+ export declare const RAIL_COLLAPSED = 64;
21
+ export declare const RAIL_EXPANDED = 264;
22
+ /** Horizontal padding inside the rail, on every region. Tailwind `px-3`. */
23
+ export declare const RAIL_GUTTER = 12;
24
+ /**
25
+ * The row shape every sidebar control shares - nav items, the workspace tile,
26
+ * the launcher, the user button.
27
+ *
28
+ * `overflow-hidden` + `whitespace-nowrap` is what makes the collapse smooth:
29
+ * the label is *clipped* by the shrinking rail rather than re-wrapped or
30
+ * re-measured, so a width animation costs no reflow. Fading the label with
31
+ * `SidebarLabel` on top of that hides the clip itself.
32
+ */
33
+ export declare const SIDEBAR_ROW = "group/row relative flex h-10 w-full shrink-0 items-center justify-start gap-3 overflow-hidden whitespace-nowrap rounded-lg pe-2 text-sm font-normal transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus";
34
+ /**
35
+ * Leading inset for a row whose glyph is 20px wide (a nav icon, the launcher).
36
+ * `10 + 10 = 20`, and `RAIL_GUTTER + 20 = 32`. See `RAIL_COLLAPSED`.
37
+ */
38
+ export declare const SIDEBAR_ROW_ICON = "ps-[10px]";
39
+ /**
40
+ * Leading inset for a row whose glyph is 32px wide (the avatar, the workspace
41
+ * tile). `4 + 16 = 20`, landing on the same 32px centre as `SIDEBAR_ROW_ICON`.
42
+ */
43
+ export declare const SIDEBAR_ROW_TILE = "ps-[4px]";
44
+ /**
45
+ * The class list for one sidebar row, in one place.
46
+ *
47
+ * Three different elements need it - a plain `<button>` (`ConnectSidebarRow`),
48
+ * a rendered link (`ConnectSidebarNav`), and a HeroUI `<Button>` acting as a
49
+ * `Modal`/`Dropdown` trigger (the account row, the launcher, the workspace
50
+ * tile). They cannot share a component, because a React Aria overlay has to
51
+ * own its own trigger element - so they share this instead. A second copy of
52
+ * the string is a row that drifts off the 32px centre and takes the collapse
53
+ * animation's smoothness with it.
54
+ *
55
+ * On the HeroUI trigger: these are all Tailwind *utilities*, and HeroUI's
56
+ * `.button` is a component class, so the utility layer wins - which is what
57
+ * lets a `w-fit`, centred, `px-`-padded button take this shape at all.
58
+ */
59
+ export declare function sidebarRowClass(options?: Readonly<{
60
+ /** The glyph is 32px (an avatar, a workspace tile) rather than 20px. */
61
+ tile?: boolean;
62
+ /** Paint the active treatment. */
63
+ active?: boolean;
64
+ className?: string;
65
+ }>): string;
66
+ export interface ConnectShellContextValue {
67
+ /** Icon-only rail. Always `false` inside the mobile drawer - see `AppShell`. */
68
+ collapsed: boolean;
69
+ /** Whether a collapse control should be offered at all. */
70
+ collapsible: boolean;
71
+ setCollapsed: (collapsed: boolean) => void;
72
+ renderLink: ConnectLinkRenderer;
73
+ /** The host router's current path, if it supplied one. */
74
+ activePath?: string;
75
+ /** Called after any sidebar link is pressed - the mobile drawer closes itself with this. */
76
+ onNavigate?: () => void;
77
+ }
78
+ /**
79
+ * Shared state for every part of the shell, so `ConnectSidebarNav`,
80
+ * `ConnectWorkspaceSwitcher` and `ConnectUserButton` all know whether the rail
81
+ * is collapsed without the host threading a prop through three layers.
82
+ *
83
+ * This is the piece that makes the shell *composable* rather than only
84
+ * configurable: the parts are exported and can be rearranged, and they still
85
+ * agree about collapse, routing and the mobile drawer because they read one
86
+ * context instead of one another's props.
87
+ */
88
+ export declare function ConnectShellProvider({ value, children, }: Readonly<{
89
+ value: ConnectShellContextValue;
90
+ children: ReactNode;
91
+ }>): import("react").JSX.Element;
92
+ /**
93
+ * The shell state a sidebar part is rendering inside.
94
+ *
95
+ * Falls back to "expanded, not collapsible, plain anchors" rather than
96
+ * throwing, because every part is also exported for use *outside* a shell -
97
+ * a host dropping `ConnectSidebarNav` into a layout it already owns should
98
+ * not have to mount a provider to get a nav column.
99
+ */
100
+ export declare function useConnectShell(): ConnectShellContextValue;
101
+ /**
102
+ * A sidebar row's text, faded out as the rail collapses.
103
+ *
104
+ * Opacity only - never width, height or margin. The rail's own width
105
+ * animation already moves the clip edge past this text; animating the text's
106
+ * box as well would make two things fight over the same pixels and is what
107
+ * makes a collapsing sidebar look like it stutters.
108
+ *
109
+ * Asymmetric timing on purpose. Collapsing, the label leaves *first* (no
110
+ * delay, quick) so the rail never appears to squeeze text; expanding, it
111
+ * arrives *after* the rail has opened enough to hold it.
112
+ */
113
+ export declare function SidebarLabel({ children, className, }: Readonly<{
114
+ children: ReactNode;
115
+ className?: string;
116
+ }>): import("react").JSX.Element;
@@ -1,19 +1,20 @@
1
+ import { defaultRenderLink } from "./shell-context.js";
1
2
  import { type ConnectLinkRenderer, type ConnectNav } from "./nav.js";
2
- /** A plain anchor - correct anywhere, and a full page load. See `ConnectLinkRenderer`. */
3
- export declare const defaultRenderLink: ConnectLinkRenderer;
4
- export interface ConnectSideNavProps {
3
+ export { defaultRenderLink };
4
+ export interface ConnectSidebarNavProps {
5
5
  /** Items, sections, or a mix - see `ConnectNav`. */
6
6
  nav: ConnectNav;
7
7
  /**
8
- * The current path. Pass your router's own (`usePathname()` in Next) -
9
- * without it the nav reads `window.location` and patches `history` to hear
10
- * about client-side navigations, which works but is guesswork the router
11
- * never has to do. See `useActivePath`.
8
+ * The current path. Pass your router's own (`usePathname()` in Next).
9
+ * Inherited from `AppShell` when omitted; without either, the nav
10
+ * reads `window.location` and patches `history` to hear about client-side
11
+ * navigations - which works, but is guesswork the router never has to do.
12
+ * See `useActivePath`.
12
13
  */
13
14
  activePath?: string;
14
- /** See `ConnectLinkRenderer`. Defaults to a plain `<a>`. */
15
+ /** See `ConnectLinkRenderer`. Inherited from the shell; defaults to a plain `<a>`. */
15
16
  renderLink?: ConnectLinkRenderer;
16
- /** Icon-only rail. Labels move into tooltips and children are hidden. */
17
+ /** Icon-only rail. Inherited from the shell when omitted. */
17
18
  collapsed?: boolean;
18
19
  /** Called after any nav link is pressed - the mobile drawer closes itself with this. */
19
20
  onNavigate?: () => void;
@@ -24,8 +25,21 @@ export interface ConnectSideNavProps {
24
25
  * children, and the active highlight.
25
26
  *
26
27
  * `AppShell` renders this inside its rail and its mobile drawer, and it
27
- * is exported for a host that wants the shell's navigation without its
28
- * chrome: the same items, the same active resolution, dropped into a layout
29
- * you already have.
28
+ * is exported for a host that wants the shell's navigation without its chrome:
29
+ * the same items, the same active resolution, dropped into a layout you
30
+ * already have.
31
+ *
32
+ * Every prop is optional except `nav`, because inside a shell they are all
33
+ * inherited from it. Passing one wins over what the shell says - which is how
34
+ * a second nav column (see `secondaryNav`) can share the rail's collapse state
35
+ * while keeping its own items.
36
+ */
37
+ export declare function ConnectSidebarNav({ nav, activePath, renderLink, collapsed, onNavigate, className, }: ConnectSidebarNavProps): import("react").JSX.Element;
38
+ /**
39
+ * @deprecated Renamed to `ConnectSidebarNav` in 0.12 - the rail is a
40
+ * `ConnectSidebar` now, and its pieces are named after it. Identical
41
+ * behaviour; this alias will be removed in a later minor.
30
42
  */
31
- export declare function ConnectSideNav({ nav, activePath, renderLink, collapsed, onNavigate, className, }: ConnectSideNavProps): import("react").JSX.Element;
43
+ export declare const ConnectSideNav: typeof ConnectSidebarNav;
44
+ /** @deprecated Renamed to `ConnectSidebarNavProps`. */
45
+ export type ConnectSideNavProps = ConnectSidebarNavProps;
@@ -0,0 +1,95 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ export interface ConnectSidebarProps {
3
+ /**
4
+ * The rail's contents, top to bottom. `AppShell` composes the default
5
+ * set; pass your own to rearrange them.
6
+ */
7
+ children: ReactNode;
8
+ /** Accessible name for the `<aside>`. Defaults to `"Sidebar"`. */
9
+ label?: string;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * The application rail: a fixed-width column that animates between an
14
+ * icon-only 64px and a labelled 264px.
15
+ *
16
+ * **Why width, and nothing else, animates.** Every row inside puts its glyph
17
+ * on the same 32px centre in both states (see `RAIL_COLLAPSED`), and every
18
+ * label is clipped rather than re-measured (see `SIDEBAR_ROW`). So the only
19
+ * property in flight is this element's width: no child moves, no text
20
+ * re-wraps, and there is no per-frame layout pass on the rows. That is why the
21
+ * collapse looks smooth rather than merely fast.
22
+ *
23
+ * Hidden below `md`. The mobile drawer renders the same children with
24
+ * `collapsed: false`, because a 64px icon rail on a phone is a worse drawer,
25
+ * not a smaller one.
26
+ */
27
+ export declare function ConnectSidebar({ children, label, className }: ConnectSidebarProps): import("react").JSX.Element;
28
+ export interface ConnectSidebarHeaderProps {
29
+ /** This application's name, in the `cosmo·ops VOICE` lockup. See `ConnectLogo`. */
30
+ name: string;
31
+ /** This application's glyph, drawn on the CosmoOps badge. */
32
+ icon?: ReactNode;
33
+ /** Where the lockup links when the rail is expanded. Defaults to `/`. */
34
+ homeHref?: string;
35
+ }
36
+ /**
37
+ * The brand row, and the rail's own expand/collapse control.
38
+ *
39
+ * **Two states, one DOM.** The badge is mounted once and never re-created; the
40
+ * wordmark fades beside it and the collapse button fades at the trailing edge.
41
+ * Swapping which *element* renders the brand would remount it halfway through
42
+ * the width animation and flicker.
43
+ *
44
+ * Collapsed, hovering or focusing the badge swaps it for the expand glyph, so
45
+ * the rail needs no permanent control eating a row. Expanded, the control sits
46
+ * at the header's trailing edge, where it can be aimed at without covering the
47
+ * brand. The swap is pure CSS on a `group` - hovering re-renders nothing,
48
+ * which is what keeps it instant.
49
+ *
50
+ * The trade this makes, deliberately: **collapsed, the lockup is no longer a
51
+ * link home.** It has become the expand control, and one target that navigates
52
+ * on click but expands on click-after-hover would be worse than either. Put
53
+ * home in the nav; every application already does.
54
+ */
55
+ export declare function ConnectSidebarHeader({ name, icon, homeHref }: ConnectSidebarHeaderProps): import("react").JSX.Element;
56
+ /**
57
+ * The rail's scrolling region, for the primary navigation. Everything else in
58
+ * the sidebar is `shrink-0`, so this is the only part that ever gives way.
59
+ */
60
+ export declare function ConnectSidebarBody({ children }: Readonly<{
61
+ children: ReactNode;
62
+ }>): import("react").JSX.Element;
63
+ /**
64
+ * A pinned region at the foot of the rail - the secondary nav, the launcher,
65
+ * the account row.
66
+ *
67
+ * `separated` draws the hairline above it. The account row wants one; a
68
+ * secondary nav sitting directly on top of the account row does not, or the
69
+ * rail ends in two rules a few pixels apart.
70
+ */
71
+ export declare function ConnectSidebarFooter({ children, separated, }: Readonly<{
72
+ children: ReactNode;
73
+ separated?: boolean;
74
+ }>): import("react").JSX.Element;
75
+ export type ConnectSidebarRowProps = Readonly<{
76
+ /** The glyph. 20px by default; 32px when `tile`. */
77
+ icon: ReactNode;
78
+ /** The row's text, and - when it is a string - its accessible name. */
79
+ label: ReactNode;
80
+ /** Overrides the accessible name. Required when `label` is not a string. */
81
+ "aria-label"?: string;
82
+ /** The glyph is 32px (an avatar, a workspace tile) rather than 20px. */
83
+ tile?: boolean;
84
+ /** Trailing content, e.g. a chevron. Clipped with the label while collapsed. */
85
+ trailing?: ReactNode;
86
+ }> & Omit<ComponentPropsWithoutRef<"button">, "aria-label">;
87
+ /**
88
+ * A pressable row in the rail that is not a nav item - the launcher, the
89
+ * account button, anything a host adds to the footer.
90
+ *
91
+ * Exported so a host's own row lines up with the SDK's. It applies the same
92
+ * 32px glyph centre and the same label fade, which is what stops a partner's
93
+ * "Help" row drifting sideways while every row above it holds still.
94
+ */
95
+ export declare function ConnectSidebarRow({ icon, label, tile, trailing, className, "aria-label": ariaLabel, ...props }: ConnectSidebarRowProps): import("react").JSX.Element;