@pollinations/ui 0.0.2 → 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +38 -71
  2. package/dist/{Chip-SM52sUbZ.d.cts → Chip-CQCq34kS.d.cts} +1 -1
  3. package/dist/{Chip-C5impfYd.d.ts → Chip-Cj4xG7Te.d.ts} +1 -1
  4. package/dist/app-user-menu/sdk.cjs +497 -0
  5. package/dist/app-user-menu/sdk.cjs.map +1 -0
  6. package/dist/app-user-menu/sdk.d.cts +18 -0
  7. package/dist/app-user-menu/sdk.d.ts +18 -0
  8. package/dist/app-user-menu/sdk.js +469 -0
  9. package/dist/app-user-menu/sdk.js.map +1 -0
  10. package/dist/app.css +94 -3
  11. package/dist/auth/index.cjs +77 -62
  12. package/dist/auth/index.cjs.map +1 -1
  13. package/dist/auth/index.js +77 -62
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/gen/index.cjs +477 -0
  16. package/dist/gen/index.cjs.map +1 -0
  17. package/dist/gen/index.d.cts +35 -0
  18. package/dist/gen/index.d.ts +35 -0
  19. package/dist/gen/index.js +451 -0
  20. package/dist/gen/index.js.map +1 -0
  21. package/dist/index.cjs +1875 -761
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +294 -109
  24. package/dist/index.d.ts +294 -109
  25. package/dist/index.js +1889 -798
  26. package/dist/index.js.map +1 -1
  27. package/dist/styles.css +1 -1
  28. package/dist/theme-jRPPBmld.d.cts +20 -0
  29. package/dist/theme-jRPPBmld.d.ts +20 -0
  30. package/dist/wallet/index.cjs +17 -8
  31. package/dist/wallet/index.cjs.map +1 -1
  32. package/dist/wallet/index.d.cts +2 -2
  33. package/dist/wallet/index.d.ts +2 -2
  34. package/dist/wallet/index.js +17 -8
  35. package/dist/wallet/index.js.map +1 -1
  36. package/package.json +15 -18
  37. package/dist/Button-Bn8pGg6N.d.ts +0 -26
  38. package/dist/Button-CWUBCiIc.d.cts +0 -26
  39. package/dist/auth/sdk.cjs +0 -220
  40. package/dist/auth/sdk.cjs.map +0 -1
  41. package/dist/auth/sdk.d.cts +0 -53
  42. package/dist/auth/sdk.d.ts +0 -53
  43. package/dist/auth/sdk.js +0 -187
  44. package/dist/auth/sdk.js.map +0 -1
  45. package/dist/modality/index.cjs +0 -83
  46. package/dist/modality/index.cjs.map +0 -1
  47. package/dist/modality/index.d.cts +0 -25
  48. package/dist/modality/index.d.ts +0 -25
  49. package/dist/modality/index.js +0 -56
  50. package/dist/modality/index.js.map +0 -1
  51. package/dist/theme-VMRlhqST.d.cts +0 -9
  52. package/dist/theme-VMRlhqST.d.ts +0 -9
  53. package/dist/wallet/sdk.cjs +0 -206
  54. package/dist/wallet/sdk.cjs.map +0 -1
  55. package/dist/wallet/sdk.d.cts +0 -33
  56. package/dist/wallet/sdk.d.ts +0 -33
  57. package/dist/wallet/sdk.js +0 -175
  58. package/dist/wallet/sdk.js.map +0 -1
  59. package/dist/wallet.css +0 -73
package/README.md CHANGED
@@ -1,65 +1,39 @@
1
1
  # @pollinations/ui
2
2
 
3
- Internal UI primitives for Pollinations apps. SDK-backed subpaths consume auth
4
- state from [`@pollinations/sdk/react`](../sdk/README.md#react-auth-provider);
5
- core primitives and display recipes stay SDK-free.
3
+ Internal UI primitives, compositions, and modules for Pollinations apps.
4
+ SDK-backed subpaths consume auth state from
5
+ [`@pollinations/sdk/react`](../sdk/README.md#react-auth-provider); core
6
+ primitives and display recipes stay SDK-free.
6
7
 
7
8
  ## Install
8
9
 
10
+ > [!WARNING]
11
+ > **The `alpha` line (`0.1.0-alpha.x`) is unstable and breakage-prone.** It ships the in-progress design-system rebuild (new `--polli-color-*` token system, new primitives, renamed size props `sm|md|lg`) and its API may change between alpha versions without notice. The stable `latest` line is `0.0.2`. Opt into the alpha deliberately, and pin an exact version.
12
+
9
13
  ```bash
14
+ # stable (recommended)
10
15
  npm install @pollinations/ui
16
+
17
+ # alpha (in-progress rebuild — pin exactly)
18
+ npm install @pollinations/ui@alpha
11
19
  ```
12
20
 
13
- Install `@pollinations/sdk` too when using `@pollinations/ui/*/sdk`
14
- subpaths.
21
+ Install `@pollinations/sdk` too when using the
22
+ `@pollinations/ui/app-user-menu/sdk` subpath.
15
23
 
16
24
  ## Usage
17
25
 
18
26
  ```tsx
19
27
  import "@pollinations/ui/styles.css";
20
- import { PolliProvider, useAccountKeyUsage } from "@pollinations/sdk/react";
28
+ import { PolliProvider } from "@pollinations/sdk/react";
21
29
  import { Surface } from "@pollinations/ui";
22
- import {
23
- LoginButton,
24
- LogoutButton,
25
- UserAvatar,
26
- UserName,
27
- WhenLoggedIn,
28
- WhenLoggedOut,
29
- } from "@pollinations/ui/auth/sdk";
30
- import {
31
- Balance,
32
- KeyBudget,
33
- KeyExpiry,
34
- KeyModels,
35
- KeyPrefix,
36
- } from "@pollinations/ui/wallet/sdk";
37
-
38
- function RecentRequests() {
39
- const { data: usage } = useAccountKeyUsage({ days: 7, limit: 5 });
40
- return <span>{usage?.usage.length ?? 0} recent requests</span>;
41
- }
30
+ import { AppUserMenu } from "@pollinations/ui/app-user-menu/sdk";
42
31
 
43
32
  export function App() {
44
33
  return (
45
34
  <PolliProvider appKey="pk_your_publishable_key" permissions={["profile"]}>
46
35
  <Surface data-theme="amber">
47
- <WhenLoggedOut>
48
- <LoginButton theme="amber">
49
- Log in with Pollinations
50
- </LoginButton>
51
- </WhenLoggedOut>
52
- <WhenLoggedIn>
53
- <UserAvatar size="md" />
54
- <UserName />
55
- <Balance />
56
- <KeyPrefix />
57
- <KeyBudget />
58
- <KeyExpiry />
59
- <KeyModels />
60
- <RecentRequests />
61
- <LogoutButton theme="amber">Log out</LogoutButton>
62
- </WhenLoggedIn>
36
+ <AppUserMenu dashboardHref="https://enter.pollinations.ai" />
63
37
  </Surface>
64
38
  </PolliProvider>
65
39
  );
@@ -87,11 +61,8 @@ The SVG sources use `currentColor`. Apps control the rendered color by inlining
87
61
  them or using them as masks. Root-level favicon, PWA icon, and SEO files stay
88
62
  app-owned.
89
63
 
90
- Wallet-specific colors and utilities live in a separate stylesheet:
91
-
92
- ```css
93
- @import "@pollinations/ui/wallet.css";
94
- ```
64
+ Wallet colors and utilities are bundled into the main stylesheet
65
+ (`@pollinations/ui/styles.css`) — no separate import needed.
95
66
 
96
67
  ## What's exported
97
68
 
@@ -100,30 +71,25 @@ Wallet-specific colors and utilities live in a separate stylesheet:
100
71
  - `@pollinations/ui/auth` exports SDK-free auth modal pieces:
101
72
  `AuthModal`, `AuthModalHeader`, `AuthModalLoading`, `AuthInfoCard`, and
102
73
  `ErrorBanner`.
103
- - `@pollinations/ui/auth/sdk` exports identity/session components that read
104
- from the surrounding `<PolliProvider>`:
105
- - **null when not logged in (or before data loads):** `LogoutButton`,
106
- `UserAvatar`, `UserEmail`, `UserName`, `WhenLoggedIn`.
107
- - **shown only when logged out:** `LoginButton`, `WhenLoggedOut`.
108
-
109
- These are intentionally bare wrappers around `useAuth*` hooks. They render
110
- the data and nothing else — no default copy, no default theme, no default
111
- intent. The app composes layout, copy, and color.
112
74
  - `@pollinations/ui/wallet` exports SDK-free wallet-specific display helpers
113
75
  and recipes: `formatPollen`, `PaidChip`, `TierChip`, `WalletDot`,
114
76
  `WalletBalanceCard`, `PAID_BALANCE_CHART_COLOR`, and
115
77
  `TIER_BALANCE_CHART_COLOR`.
116
- - `@pollinations/ui/wallet/sdk` exports SDK-backed wallet components:
117
- `Balance`, `KeyBudget`, `KeyExpiry`, `KeyModels`, and `KeyPrefix`.
118
- - `@pollinations/ui/modality` exports model-modality color recipes.
78
+ - `@pollinations/ui/app-user-menu/sdk` exports the SDK-backed app account
79
+ menu module.
80
+ - `@pollinations/ui/gen` exports the generation UI — `ModelSelector`,
81
+ category labels, and the model-modality theme mapping.
119
82
  - `@pollinations/ui/assets/*` exports canonical Pollinations source SVGs:
120
83
  `logo.svg` and `logo-wordmark.svg`.
121
- - **Design primitives** — `Alert`, `Button`, `ButtonGroup`, `Chip`, `ChevronIcon`,
122
- `Collapsible`, `CopyButton`, `Dialog`, `Dropdown`, `ExternalLinkButton`,
123
- `IconButton`, `InfoTip`, `Input`, `MultiSelect`, `PeriodPicker`,
124
- `ScrollArea`, `Section`, `Slider`, `StatCard`, `Surface`, `Switch`,
125
- `Table`, `TableBody`, `TableCell`, `TableHead`, `TableHeaderCell`,
126
- `TableRow`, `TabButton`, `Tooltip`.
84
+ - **Design primitives** — `Button`, `ButtonGroup`, `Chip`, `ChevronIcon`,
85
+ `Dialog`, `DialogTitle`, `Dropdown`, `DropdownItem`, `Field`, `Heading`,
86
+ `IconButton`, `InlineLink`, `Input`, `ScrollArea`, `Slider`, `Surface`,
87
+ `Switch`, `TabButton`, `Table`, `TableBody`, `TableCell`, `TableHead`,
88
+ `TableHeaderCell`, `TableRow`, `Text`, `Textarea`, `Tooltip`.
89
+ - **Design compositions** — `Alert`, `CodeBlock`, `Collapsible`,
90
+ `CopyButton`, `ExternalLinkButton`, `FileUpload`, `InfoTip`, `LinkCard`,
91
+ `Markdown`, `MediaPlaceholder`, `MultiSelect`, `NavItem`, `PeriodPicker`,
92
+ `Prose`, `Section`, `StatCard`.
127
93
  - **Helpers** — `cn`, `useScrollLock`, `currentPeriod`,
128
94
  `getPeriodBucketKeys`, `periodBucketKeyToDate`.
129
95
  - **Theme** — `themes` (runtime array of theme names), `ThemeName` (type).
@@ -135,16 +101,17 @@ from `@pollinations/sdk/react` (`useAccountKeyUsage`, `useAccountKey`,
135
101
  ## Source Layout
136
102
 
137
103
  - `src/primitives/*` contains generic, SDK-free building blocks.
104
+ - `src/compositions/*` contains SDK-free recipes that compose primitives.
138
105
  - `src/modules/*` contains package-owned recipes with domain assumptions
139
- such as auth, wallet, and modality.
106
+ such as auth, wallet, app-user-menu, and gen.
140
107
  - Public subpath exports (`@pollinations/ui/auth`,
141
- `@pollinations/ui/auth/sdk`, `@pollinations/ui/wallet`,
142
- `@pollinations/ui/wallet/sdk`, `@pollinations/ui/modality`) are built
143
- directly from those modules.
108
+ `@pollinations/ui/wallet`, `@pollinations/ui/gen`,
109
+ `@pollinations/ui/app-user-menu/sdk`) are built directly from those source
110
+ layers.
144
111
 
145
112
  ## Theming
146
113
 
147
- Set `data-theme="amber" | "blue" | "pink" | "teal" | "violet" | "green"`
114
+ Set `data-theme="amber" | "blue" | "pink" | "coral" | "teal" | "violet" | "emerald"`
148
115
  on any ancestor (or per-component via the `theme` prop on Button-family
149
116
  components) to switch the cascade. Theme variables are defined in
150
117
  `styles.css` — import it once at your app entry.
@@ -185,7 +152,7 @@ with host app tokens.
185
152
  | `--polli-text-base` | Base text size. |
186
153
  | `--polli-color-surface-white` | Translucent white surface. |
187
154
 
188
- Wallet tokens are public when `@pollinations/ui/wallet.css` is imported:
155
+ Wallet tokens are public (bundled into `@pollinations/ui/styles.css`):
189
156
 
190
157
  | Token | Purpose |
191
158
  | ----------------------------- | --------------------------------------------- |
@@ -1,5 +1,5 @@
1
1
  import { FC, ComponentPropsWithoutRef } from 'react';
2
- import { T as ThemeName } from './theme-VMRlhqST.cjs';
2
+ import { T as ThemeName } from './theme-jRPPBmld.cjs';
3
3
 
4
4
  /** Semantic chip labels for status and metadata badges. */
5
5
  type ChipIntent = "news" | "alpha" | "neutral" | "success" | "warning" | "danger";
@@ -1,5 +1,5 @@
1
1
  import { FC, ComponentPropsWithoutRef } from 'react';
2
- import { T as ThemeName } from './theme-VMRlhqST.js';
2
+ import { T as ThemeName } from './theme-jRPPBmld.js';
3
3
 
4
4
  /** Semantic chip labels for status and metadata badges. */
5
5
  type ChipIntent = "news" | "alpha" | "neutral" | "success" | "warning" | "danger";
@@ -0,0 +1,497 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/modules/app-user-menu/sdk.ts
21
+ var sdk_exports = {};
22
+ __export(sdk_exports, {
23
+ AppUserMenu: () => AppUserMenu,
24
+ isEmbeddedContext: () => isEmbeddedContext
25
+ });
26
+ module.exports = __toCommonJS(sdk_exports);
27
+
28
+ // src/modules/app-user-menu/AppUserMenu.tsx
29
+ var import_react10 = require("@pollinations/sdk/react");
30
+
31
+ // src/lib/cn.ts
32
+ var import_clsx = require("clsx");
33
+ var import_tailwind_merge = require("tailwind-merge");
34
+ var twMerge = (0, import_tailwind_merge.extendTailwindMerge)({ prefix: "polli" });
35
+ function cn(...inputs) {
36
+ return twMerge((0, import_clsx.clsx)(...inputs));
37
+ }
38
+
39
+ // src/primitives/ChevronIcon.tsx
40
+ var import_jsx_runtime = require("react/jsx-runtime");
41
+ var ChevronIcon = ({ expanded, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42
+ "svg",
43
+ {
44
+ "aria-hidden": "true",
45
+ width: "12",
46
+ height: "12",
47
+ viewBox: "0 0 12 12",
48
+ className: cn(
49
+ "polli:shrink-0 polli:transition-transform polli:duration-200 polli:ease-out",
50
+ expanded && "polli:rotate-180",
51
+ className
52
+ ),
53
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
54
+ "path",
55
+ {
56
+ d: "M3 4.5 6 7.5l3-3",
57
+ fill: "none",
58
+ stroke: "currentColor",
59
+ strokeLinecap: "round",
60
+ strokeLinejoin: "round",
61
+ strokeWidth: "1.5"
62
+ }
63
+ )
64
+ }
65
+ );
66
+
67
+ // src/primitives/Dropdown.tsx
68
+ var import_popover = require("@ark-ui/react/popover");
69
+ var import_portal = require("@ark-ui/react/portal");
70
+ var import_react = require("react");
71
+ var import_jsx_runtime2 = require("react/jsx-runtime");
72
+ var DEFAULT_PANEL = "polli:rounded-lg polli:border polli:border-theme-border polli:bg-theme-bg-pale polli:shadow-lg";
73
+ var Dropdown = ({
74
+ theme,
75
+ trigger,
76
+ children,
77
+ align = "start",
78
+ open: openProp,
79
+ onOpenChange,
80
+ className
81
+ }) => {
82
+ const [internalOpen, setInternalOpen] = (0, import_react.useState)(false);
83
+ const isControlled = openProp !== void 0;
84
+ const open = isControlled ? openProp : internalOpen;
85
+ const setOpen = (next) => {
86
+ if (!isControlled) setInternalOpen(next);
87
+ onOpenChange?.(next);
88
+ };
89
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
90
+ import_popover.Popover.Root,
91
+ {
92
+ open,
93
+ onOpenChange: (details) => setOpen(details.open),
94
+ positioning: {
95
+ placement: align === "end" ? "bottom-end" : "bottom-start"
96
+ },
97
+ children: [
98
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_popover.Popover.Trigger, { asChild: true, children: trigger(open) }),
99
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_portal.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_popover.Popover.Positioner, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
100
+ import_popover.Popover.Content,
101
+ {
102
+ "data-theme": theme,
103
+ className: cn(
104
+ "polli:z-50 polli:overflow-hidden polli:focus:outline-none",
105
+ DEFAULT_PANEL,
106
+ className
107
+ ),
108
+ children: typeof children === "function" ? children(() => setOpen(false)) : children
109
+ }
110
+ ) }) })
111
+ ]
112
+ }
113
+ );
114
+ };
115
+
116
+ // src/primitives/DropdownItem.tsx
117
+ var import_jsx_runtime3 = require("react/jsx-runtime");
118
+ function DropdownItem({
119
+ as,
120
+ className,
121
+ children,
122
+ ...rest
123
+ }) {
124
+ const Component = as || "button";
125
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
126
+ Component,
127
+ {
128
+ ...Component === "button" ? { type: "button" } : void 0,
129
+ ...rest,
130
+ className: cn(
131
+ "polli-control polli:flex polli:w-full polli:cursor-pointer polli:items-center polli:gap-2 polli:rounded-lg polli:bg-transparent polli:px-3 polli:py-2 polli:text-left polli:text-sm polli:font-medium polli:text-theme-text-base polli:no-underline polli:transition-colors polli:hover:bg-theme-bg-hover polli:focus-visible:bg-theme-bg-hover",
132
+ className
133
+ ),
134
+ children
135
+ }
136
+ );
137
+ }
138
+
139
+ // src/modules/auth/LoginButton.tsx
140
+ var import_react2 = require("@pollinations/sdk/react");
141
+
142
+ // src/primitives/Button.tsx
143
+ var import_jsx_runtime4 = require("react/jsx-runtime");
144
+ var sizes = {
145
+ sm: "polli:px-2 polli:pt-0.5 polli:pb-1",
146
+ md: "polli:px-4 polli:pt-1.5 polli:pb-2",
147
+ lg: "polli:px-6 polli:py-3"
148
+ };
149
+ var themeClasses = "polli:bg-theme-bg-active polli:text-theme-text-base polli:hover:bg-theme-bg-hover polli:transition-colors";
150
+ var intentClasses = {
151
+ danger: "polli:bg-intent-danger-bg-light polli:text-intent-danger-text polli:hover:bg-intent-danger-bg-hover polli:transition-colors"
152
+ };
153
+ var buttonClasses = ({
154
+ theme: _theme,
155
+ intent,
156
+ size,
157
+ className,
158
+ disabled
159
+ }) => {
160
+ const colorClasses = intent ? intentClasses[intent] : themeClasses;
161
+ return cn(
162
+ "polli-control polli:inline-flex polli:items-center polli:justify-center polli:rounded-full polli:self-center polli:font-medium polli:leading-normal polli:box-border",
163
+ disabled ? "polli:opacity-50 polli:cursor-not-allowed" : "polli:hover:filter polli:hover:brightness-105 polli:cursor-pointer",
164
+ colorClasses,
165
+ sizes[size || "md"],
166
+ className
167
+ );
168
+ };
169
+ function Button({
170
+ as,
171
+ children,
172
+ theme,
173
+ intent,
174
+ size,
175
+ className,
176
+ disabled,
177
+ ...buttonProps
178
+ }) {
179
+ const Component = as || "button";
180
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
181
+ Component,
182
+ {
183
+ "data-theme": intent ? void 0 : theme,
184
+ "data-intent": intent,
185
+ className: buttonClasses({
186
+ theme,
187
+ intent,
188
+ size,
189
+ className,
190
+ disabled
191
+ }),
192
+ disabled,
193
+ ...buttonProps,
194
+ children
195
+ }
196
+ );
197
+ }
198
+
199
+ // src/modules/auth/LoginButton.tsx
200
+ var import_jsx_runtime5 = require("react/jsx-runtime");
201
+ function LoginButton({ children, ...buttonProps }) {
202
+ const { isLoggedIn } = (0, import_react2.useAuthState)();
203
+ const { login } = (0, import_react2.useAuthActions)();
204
+ if (isLoggedIn) return null;
205
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { ...buttonProps, onClick: () => login(), children });
206
+ }
207
+
208
+ // src/modules/auth/UserAvatar.tsx
209
+ var import_react3 = require("@pollinations/sdk/react");
210
+ var import_jsx_runtime6 = require("react/jsx-runtime");
211
+ var sizeClass = {
212
+ sm: "polli:w-6 polli:h-6 polli:text-xs",
213
+ md: "polli:w-9 polli:h-9 polli:text-sm",
214
+ lg: "polli:w-12 polli:h-12 polli:text-base"
215
+ };
216
+ function initials(name) {
217
+ const parts = name.trim().split(/\s+/).filter(Boolean);
218
+ if (parts.length === 0) return null;
219
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase() || null;
220
+ const first = parts[0][0] ?? "";
221
+ const last = parts[parts.length - 1][0] ?? "";
222
+ return (first + last).toUpperCase() || null;
223
+ }
224
+ function UserAvatar({ size = "md", className }) {
225
+ const { isLoggedIn } = (0, import_react3.useAuthState)();
226
+ const { data: profile } = (0, import_react3.useAccountProfile)({ enabled: isLoggedIn });
227
+ if (!isLoggedIn) return null;
228
+ const base = cn(
229
+ "polli:inline-flex polli:items-center polli:justify-center polli:rounded-full polli:overflow-hidden polli:bg-theme-bg-pale polli:text-theme-text-strong polli:font-semibold polli:select-none",
230
+ sizeClass[size],
231
+ className
232
+ );
233
+ if (profile?.image) {
234
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
235
+ "img",
236
+ {
237
+ "data-polli": "user-avatar",
238
+ src: profile.image,
239
+ alt: profile.name ?? "",
240
+ className: base
241
+ }
242
+ );
243
+ }
244
+ const label = profile?.name ? initials(profile.name) : null;
245
+ if (!label) return null;
246
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
247
+ "div",
248
+ {
249
+ "data-polli": "user-avatar",
250
+ role: "img",
251
+ "aria-label": profile?.name ?? void 0,
252
+ className: base,
253
+ children: label
254
+ }
255
+ );
256
+ }
257
+
258
+ // src/modules/auth/UserName.tsx
259
+ var import_react4 = require("@pollinations/sdk/react");
260
+ var import_jsx_runtime7 = require("react/jsx-runtime");
261
+ function UserName({ className }) {
262
+ const { isLoggedIn } = (0, import_react4.useAuthState)();
263
+ const { data: profile } = (0, import_react4.useAccountProfile)({ enabled: isLoggedIn });
264
+ if (!isLoggedIn || !profile) return null;
265
+ const display = profile.name || profile.githubUsername;
266
+ if (!display) return null;
267
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-polli": "user-name", className, children: display });
268
+ }
269
+
270
+ // src/modules/auth/WhenLoggedIn.tsx
271
+ var import_react5 = require("@pollinations/sdk/react");
272
+ var import_react6 = require("react");
273
+ var import_jsx_runtime8 = require("react/jsx-runtime");
274
+ function WhenLoggedIn({ children }) {
275
+ const { isLoggedIn } = (0, import_react5.useAuthState)();
276
+ if (!isLoggedIn) return null;
277
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react6.Fragment, { children });
278
+ }
279
+
280
+ // src/modules/auth/WhenLoggedOut.tsx
281
+ var import_react7 = require("@pollinations/sdk/react");
282
+ var import_react8 = require("react");
283
+ var import_jsx_runtime9 = require("react/jsx-runtime");
284
+ function WhenLoggedOut({ children }) {
285
+ const { isLoggedIn } = (0, import_react7.useAuthState)();
286
+ if (isLoggedIn) return null;
287
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children });
288
+ }
289
+
290
+ // src/modules/wallet/Balance.tsx
291
+ var import_react9 = require("@pollinations/sdk/react");
292
+
293
+ // src/primitives/Chip.tsx
294
+ var import_jsx_runtime10 = require("react/jsx-runtime");
295
+ var chipSizes = {
296
+ sm: "polli:px-2 polli:py-0.5 polli:text-xs",
297
+ md: "polli:px-2.5 polli:py-0.5 polli:text-sm",
298
+ lg: "polli:px-3 polli:py-1 polli:text-sm"
299
+ };
300
+ var intentClasses2 = {
301
+ news: "polli:bg-intent-news-bg-light polli:text-intent-news-text",
302
+ alpha: "polli:bg-intent-alpha-bg-light polli:text-intent-alpha-text",
303
+ neutral: "polli:bg-ink-100/80 polli:text-ink-900",
304
+ success: "polli:bg-intent-success-bg-light polli:text-intent-success-text",
305
+ warning: "polli:bg-intent-warning-bg-light polli:text-intent-warning-text",
306
+ danger: "polli:bg-intent-danger-bg-light polli:text-intent-danger-text"
307
+ };
308
+ var Chip = ({
309
+ theme,
310
+ intent,
311
+ size = "md",
312
+ className,
313
+ children,
314
+ ...rest
315
+ }) => {
316
+ const intentClass = intent ? intentClasses2[intent] : "polli:bg-theme-bg-active polli:text-theme-text-strong";
317
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
318
+ "span",
319
+ {
320
+ ...rest,
321
+ "data-theme": theme,
322
+ className: cn(
323
+ "polli:inline-flex polli:shrink-0 polli:items-center polli:gap-1 polli:rounded-lg polli:font-medium polli:leading-normal",
324
+ chipSizes[size],
325
+ intentClass,
326
+ className
327
+ ),
328
+ children
329
+ }
330
+ );
331
+ };
332
+
333
+ // src/modules/wallet/format-pollen.ts
334
+ var BUDGET = 5;
335
+ var MAX_DECIMALS = 4;
336
+ var COMPACT_THRESHOLD = 1e5;
337
+ function floorTo(value, decimals) {
338
+ const factor = 10 ** decimals;
339
+ return Math.floor(value * factor) / factor;
340
+ }
341
+ function formatCompact(value) {
342
+ const abs = Math.abs(value);
343
+ const sign = value < 0 ? "-" : "";
344
+ const [unit, scale] = abs >= 1e9 ? ["B", 1e9] : abs >= 1e6 ? ["M", 1e6] : ["K", 1e3];
345
+ const ratio = abs / scale;
346
+ const intDigits = Math.max(1, Math.floor(Math.log10(ratio)) + 1);
347
+ const decimals = Math.max(0, 3 - intDigits);
348
+ const floored = floorTo(ratio, decimals);
349
+ return `${sign}${Number(floored.toFixed(decimals)).toString()}${unit}`;
350
+ }
351
+ function formatPollen(value) {
352
+ if (!Number.isFinite(value) || value === 0) return "0";
353
+ const abs = Math.abs(value);
354
+ if (abs >= COMPACT_THRESHOLD) return formatCompact(value);
355
+ const intDigits = abs >= 1 ? Math.floor(Math.log10(abs)) + 1 : 1;
356
+ const decimals = Math.min(MAX_DECIMALS, Math.max(0, BUDGET - intDigits));
357
+ const floored = floorTo(value, decimals);
358
+ if (floored === 0) return "0";
359
+ return Number(floored.toFixed(decimals)).toString();
360
+ }
361
+
362
+ // src/modules/wallet/Balance.tsx
363
+ var import_jsx_runtime11 = require("react/jsx-runtime");
364
+ function Balance({ className } = {}) {
365
+ const { isLoggedIn } = (0, import_react9.useAuthState)();
366
+ const { data: balance } = (0, import_react9.useAccountBalance)({ enabled: isLoggedIn });
367
+ if (!isLoggedIn || balance == null) return null;
368
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
369
+ Chip,
370
+ {
371
+ "data-polli": "balance",
372
+ className: cn("polli:tabular-nums", className),
373
+ children: [
374
+ formatPollen(balance.balance),
375
+ " pollen"
376
+ ]
377
+ }
378
+ );
379
+ }
380
+
381
+ // src/modules/app-user-menu/AppUserMenu.tsx
382
+ var import_jsx_runtime12 = require("react/jsx-runtime");
383
+ var defaultLabels = {
384
+ authorize: "Authorize app",
385
+ appUserMenu: "App user menu",
386
+ topUpAccount: "Top up account",
387
+ logout: "Log out from this app"
388
+ };
389
+ function isEmbeddedContext(embedQueryParam = "embed") {
390
+ if (typeof window === "undefined") return false;
391
+ const search = new URLSearchParams(window.location.search);
392
+ if (search.get(embedQueryParam) === "1") return true;
393
+ try {
394
+ return window.self !== window.top;
395
+ } catch {
396
+ return true;
397
+ }
398
+ }
399
+ function AppUserMenu({
400
+ dashboardHref,
401
+ labels: labelOverrides,
402
+ hiddenWhenEmbedded = false,
403
+ embedQueryParam = "embed"
404
+ }) {
405
+ if (hiddenWhenEmbedded && isEmbeddedContext(embedQueryParam)) return null;
406
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
407
+ AppUserMenuContent,
408
+ {
409
+ dashboardHref,
410
+ labels: labelOverrides
411
+ }
412
+ );
413
+ }
414
+ function AppUserMenuContent({
415
+ dashboardHref,
416
+ labels: labelOverrides
417
+ }) {
418
+ const labels = { ...defaultLabels, ...labelOverrides };
419
+ const { logout } = (0, import_react10.useAuthActions)();
420
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-theme": "amber", className: "polli:flex polli:justify-end", children: [
421
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WhenLoggedOut, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LoginButton, { theme: "amber", children: labels.authorize }) }),
422
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WhenLoggedIn, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
423
+ Dropdown,
424
+ {
425
+ theme: "amber",
426
+ align: "end",
427
+ className: "polli:w-64 polli:p-1",
428
+ trigger: (open) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
429
+ "button",
430
+ {
431
+ type: "button",
432
+ "data-theme": "amber",
433
+ "aria-label": labels.appUserMenu,
434
+ className: "polli-control polli:flex polli:min-w-0 polli:items-center polli:gap-2 polli:rounded-full polli:bg-theme-bg-active polli:py-1 polli:pl-1 polli:pr-3 polli:text-theme-text-base polli:shadow-sm polli:transition-colors polli:hover:bg-theme-bg-hover",
435
+ children: [
436
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
437
+ UserAvatar,
438
+ {
439
+ size: "md",
440
+ className: "polli:h-8 polli:w-8"
441
+ }
442
+ ),
443
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "polli:flex polli:min-w-0 polli:flex-col polli:items-start polli:leading-tight", children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UserName, { className: "polli:max-w-32 polli:truncate polli:text-sm polli:font-semibold" }),
445
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Balance, { className: "polli:bg-transparent polli:px-0 polli:py-0 polli:text-xs polli:text-theme-text-base" })
446
+ ] }),
447
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
448
+ ChevronIcon,
449
+ {
450
+ expanded: open,
451
+ className: "polli:h-4 polli:w-4 polli:text-theme-text-base"
452
+ }
453
+ )
454
+ ]
455
+ }
456
+ ),
457
+ children: (close) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
458
+ "div",
459
+ {
460
+ "data-theme": "amber",
461
+ className: "polli:flex polli:flex-col",
462
+ children: [
463
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
464
+ DropdownItem,
465
+ {
466
+ as: "a",
467
+ href: dashboardHref,
468
+ target: "_blank",
469
+ rel: "noopener noreferrer",
470
+ onClick: close,
471
+ children: labels.topUpAccount
472
+ }
473
+ ),
474
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
475
+ DropdownItem,
476
+ {
477
+ type: "button",
478
+ onClick: () => {
479
+ close();
480
+ logout();
481
+ },
482
+ children: labels.logout
483
+ }
484
+ )
485
+ ]
486
+ }
487
+ )
488
+ }
489
+ ) })
490
+ ] });
491
+ }
492
+ // Annotate the CommonJS export names for ESM import in node:
493
+ 0 && (module.exports = {
494
+ AppUserMenu,
495
+ isEmbeddedContext
496
+ });
497
+ //# sourceMappingURL=sdk.cjs.map