@luxfi/ui 7.4.0 → 7.4.3

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 (131) hide show
  1. package/dist/avatar.cjs +7 -6
  2. package/dist/avatar.js +7 -5
  3. package/dist/badge.cjs +33 -35
  4. package/dist/badge.js +33 -35
  5. package/dist/bank.d.cts +3 -3
  6. package/dist/bank.d.ts +3 -3
  7. package/dist/checkbox.cjs +4 -4
  8. package/dist/checkbox.d.cts +3 -2
  9. package/dist/checkbox.d.ts +3 -2
  10. package/dist/checkbox.js +5 -4
  11. package/dist/chrome.cjs +960 -0
  12. package/dist/chrome.d.cts +93 -0
  13. package/dist/chrome.d.ts +93 -0
  14. package/dist/chrome.js +931 -0
  15. package/dist/drawer.cjs +86 -20
  16. package/dist/drawer.js +85 -19
  17. package/dist/expiration-selector.cjs +3 -2
  18. package/dist/expiration-selector.js +3 -2
  19. package/dist/greeks-display.cjs +6 -6
  20. package/dist/greeks-display.js +6 -6
  21. package/dist/gui.cjs +13 -0
  22. package/dist/gui.d.cts +2 -0
  23. package/dist/gui.d.ts +2 -0
  24. package/dist/gui.js +2 -0
  25. package/dist/heading.cjs +4 -2
  26. package/dist/heading.js +4 -2
  27. package/dist/iam.cjs +216 -0
  28. package/dist/iam.d.cts +65 -0
  29. package/dist/iam.d.ts +65 -0
  30. package/dist/iam.js +211 -0
  31. package/dist/icons.cjs +13 -0
  32. package/dist/icons.d.cts +1 -0
  33. package/dist/icons.d.ts +1 -0
  34. package/dist/icons.js +2 -0
  35. package/dist/identity.cjs +430 -0
  36. package/dist/identity.d.cts +64 -0
  37. package/dist/identity.d.ts +64 -0
  38. package/dist/identity.js +408 -0
  39. package/dist/index.cjs +1610 -836
  40. package/dist/index.d.cts +7 -3
  41. package/dist/index.d.ts +7 -3
  42. package/dist/index.js +1587 -831
  43. package/dist/input.cjs +3 -1
  44. package/dist/input.d.cts +3 -5
  45. package/dist/input.d.ts +3 -5
  46. package/dist/input.js +3 -1
  47. package/dist/lux.config.cjs +23 -2
  48. package/dist/lux.config.d.cts +28 -10
  49. package/dist/lux.config.d.ts +28 -10
  50. package/dist/lux.config.js +23 -2
  51. package/dist/menu.cjs +4 -4
  52. package/dist/menu.js +4 -4
  53. package/dist/next.cjs +132 -0
  54. package/dist/next.d.cts +32 -0
  55. package/dist/next.d.ts +32 -0
  56. package/dist/next.js +129 -0
  57. package/dist/option-chain.cjs +1 -1
  58. package/dist/option-chain.js +1 -1
  59. package/dist/option-position.cjs +2 -1
  60. package/dist/option-position.js +2 -1
  61. package/dist/pnl-diagram.cjs +8 -6
  62. package/dist/pnl-diagram.js +8 -6
  63. package/dist/progress.cjs +7 -6
  64. package/dist/progress.js +7 -5
  65. package/dist/provider.cjs +146 -4
  66. package/dist/provider.d.cts +15 -3
  67. package/dist/provider.d.ts +15 -3
  68. package/dist/provider.js +146 -5
  69. package/dist/radio.cjs +9 -7
  70. package/dist/radio.js +10 -7
  71. package/dist/separator.cjs +3 -1
  72. package/dist/separator.js +3 -1
  73. package/dist/slider.cjs +28 -16
  74. package/dist/slider.js +28 -15
  75. package/dist/strategy-builder.cjs +2 -1
  76. package/dist/strategy-builder.js +2 -1
  77. package/dist/switch.cjs +11 -12
  78. package/dist/switch.js +12 -12
  79. package/dist/tag.cjs +34 -36
  80. package/dist/tag.js +33 -35
  81. package/dist/textarea.cjs +3 -1
  82. package/dist/textarea.js +3 -1
  83. package/dist/tooltip.cjs +28 -30
  84. package/dist/tooltip.js +27 -29
  85. package/dist/use-narrow.cjs +46 -0
  86. package/dist/use-narrow.d.cts +12 -0
  87. package/dist/use-narrow.d.ts +12 -0
  88. package/dist/use-narrow.js +20 -0
  89. package/dist/vite.cjs +109 -0
  90. package/dist/vite.d.cts +29 -0
  91. package/dist/vite.d.ts +29 -0
  92. package/dist/vite.js +107 -0
  93. package/dist/white-label.cjs +142 -0
  94. package/dist/white-label.d.cts +59 -0
  95. package/dist/white-label.d.ts +59 -0
  96. package/dist/white-label.js +134 -0
  97. package/package.json +85 -19
  98. package/src/avatar.tsx +22 -16
  99. package/src/checkbox.tsx +15 -10
  100. package/src/chrome.tsx +459 -0
  101. package/src/drawer.tsx +83 -10
  102. package/src/engine.ts +130 -0
  103. package/src/expiration-selector.tsx +3 -2
  104. package/src/greeks-display.tsx +9 -6
  105. package/src/gui.ts +17 -0
  106. package/src/heading.tsx +12 -8
  107. package/src/iam.ts +170 -0
  108. package/src/icons.ts +18 -0
  109. package/src/identity.tsx +318 -0
  110. package/src/index.ts +44 -1
  111. package/src/input.tsx +13 -9
  112. package/src/lux.config.ts +16 -15
  113. package/src/menu.tsx +4 -4
  114. package/src/next.ts +89 -0
  115. package/src/option-chain.tsx +1 -1
  116. package/src/option-position.tsx +2 -1
  117. package/src/pnl-diagram.tsx +11 -6
  118. package/src/progress.tsx +15 -8
  119. package/src/provider.tsx +48 -7
  120. package/src/radio.tsx +15 -11
  121. package/src/separator.tsx +8 -3
  122. package/src/slider.tsx +35 -19
  123. package/src/strategy-builder.tsx +3 -2
  124. package/src/switch.tsx +17 -16
  125. package/src/textarea.tsx +8 -4
  126. package/src/tooltip.tsx +15 -24
  127. package/src/use-narrow.ts +40 -0
  128. package/src/vite.ts +80 -0
  129. package/src/white-label.ts +254 -0
  130. package/tokens.css +613 -319
  131. package/src/tokens.css +0 -438
@@ -0,0 +1,254 @@
1
+ // White-label resolution — BY HOSTNAME, never per deployment.
2
+ //
3
+ // One image, many brands. Brand, gui theme row, IAM client id and OIDC issuer
4
+ // are all a pure function of the Host header, so the same container serves
5
+ // lux.cloud, zoo.network, hanzo.ai and pars.id without a rebuild, an env var or
6
+ // a per-deployment override. Nothing here reads process.env.
7
+ //
8
+ // This module is VALUES ONLY (no React, no DOM) so it resolves identically in
9
+ // three places that must agree:
10
+ // - the Next/SSR server, from `req.headers.host`
11
+ // - the browser, from `location.host`
12
+ // - an edge/middleware rewrite, from the incoming URL
13
+ //
14
+ // Logos, SEO copy and marketing palettes live in @luxfi/brand. This is the
15
+ // runtime identity the UI layer needs and nothing more.
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Branding = VALUE, not place.
19
+ // The ONE spot brand hex is allowed to live (project rule: never hardcode hex
20
+ // in components). It lives HERE, beside the identity table it colours, rather
21
+ // than in ./lux.config — that module builds the gui engine, and an identity
22
+ // value that drags a Tamagui config behind it cannot be read by a server
23
+ // component, a route handler or the `@luxfi/ui/iam` client. lux.config imports
24
+ // these; nothing imports lux.config to get them.
25
+ // ---------------------------------------------------------------------------
26
+ export const LUX_BRAND = {
27
+ lux: '#7000FF',
28
+ zoo: '#6C5EFB',
29
+ hanzo: '#EA580C',
30
+ pars: '#1C3879',
31
+ // bootno.de is its own white label with its own IdP (id.bootno.de) — accent
32
+ // taken from the bootnode console's own brand default, not invented here.
33
+ bootnode: '#3B82F6',
34
+ } as const;
35
+
36
+ export const PARS_GOLD = '#C8A45C';
37
+
38
+ export type LuxBrand = keyof typeof LUX_BRAND;
39
+
40
+ export type Org = LuxBrand;
41
+
42
+ export interface OrgIdentity {
43
+ /** Org id — also the gui child-theme suffix (`dark_lux`, `dark_zoo`, …). */
44
+ readonly org: Org;
45
+ /** Display name for chrome (header, title, sign-in copy). */
46
+ readonly name: string;
47
+ /** Canonical apex domain. */
48
+ readonly domain: string;
49
+ /** IAM/OIDC host. The issuer is `https://` + this. */
50
+ readonly iamDomain: string;
51
+ /** Brand accent hex — the same value the gui child-theme row is built from. */
52
+ readonly accent: string;
53
+ /** Foreground used on top of the accent. */
54
+ readonly accentForeground: string;
55
+ }
56
+
57
+ // The ONE table. Every hostname in the fleet resolves into one of these rows.
58
+ const ORGS: Readonly<Record<Org, OrgIdentity>> = {
59
+ lux: {
60
+ org: 'lux',
61
+ name: 'Lux',
62
+ domain: 'lux.network',
63
+ iamDomain: 'lux.id',
64
+ accent: LUX_BRAND.lux,
65
+ accentForeground: '#FFFFFF',
66
+ },
67
+ zoo: {
68
+ org: 'zoo',
69
+ name: 'Zoo',
70
+ domain: 'zoo.ngo',
71
+ // Zoo's issuer is `zoolabs.id` — the one row where it is not `<org>.id`,
72
+ // and the one every hand-rolled branding map gets wrong. `id.zoo.network`
73
+ // ANSWERS, which is why it keeps being copied around, but it is an alias:
74
+ // its own discovery document advertises
75
+ // issuer https://zoolabs.id
76
+ // authorization_endpoint https://zoolabs.id/v1/iam/oauth/authorize
77
+ // jwks_uri https://zoolabs.id/v1/iam/.well-known/jwks
78
+ // so a client configured on the alias validates `iss` against a string the
79
+ // IdP never emits and rejects every token it is given — the exact shape of
80
+ // the lux.id outage. `zoo.id` is not an IdP at all (no discovery document).
81
+ iamDomain: 'zoolabs.id',
82
+ accent: LUX_BRAND.zoo,
83
+ accentForeground: '#FFFFFF',
84
+ },
85
+ bootnode: {
86
+ org: 'bootnode',
87
+ name: 'Bootnode',
88
+ domain: 'bootno.de',
89
+ iamDomain: 'id.bootno.de',
90
+ accent: LUX_BRAND.lux,
91
+ accentForeground: '#FFFFFF',
92
+ },
93
+ hanzo: {
94
+ org: 'hanzo',
95
+ name: 'Hanzo',
96
+ domain: 'hanzo.ai',
97
+ iamDomain: 'hanzo.id',
98
+ accent: LUX_BRAND.hanzo,
99
+ accentForeground: '#FFFFFF',
100
+ },
101
+ pars: {
102
+ org: 'pars',
103
+ name: 'Pars',
104
+ domain: 'pars.id',
105
+ iamDomain: 'pars.id',
106
+ accent: LUX_BRAND.pars,
107
+ accentForeground: PARS_GOLD,
108
+ },
109
+ };
110
+
111
+ // Registrable domains that belong to each org. Matched against the hostname
112
+ // SUFFIX on a label boundary, so `mpc.lux.cloud` and `lux.cloud` both hit `lux`
113
+ // while `notlux.cloud` does not.
114
+ //
115
+ // The third slot PINS the registered app slug for every host under the domain.
116
+ // Most brands register one client per app and the slug is derivable from the
117
+ // hostname — `lux.cloud` → `cloud` → `lux-cloud`, `mpc.lux.network` → `lux-mpc`,
118
+ // both verified 302 against the live authorize endpoint. Some register ONE
119
+ // client for the whole brand, and then the derivation is a guess that 400s:
120
+ // `bootno.de` is `bootnode-platform`, and Pars is `pars-app` on every host
121
+ // (`pars-cloud` answers 400 where `pars-app` answers 302). A client id is a
122
+ // registration, not a guess — so where it is not derivable it is written down.
123
+ const DOMAIN_ORG: ReadonlyArray<readonly [string, Org, string?]> = [
124
+ [ 'lux.network', 'lux' ],
125
+ [ 'lux.cloud', 'lux' ],
126
+ [ 'lux.id', 'lux' ],
127
+ [ 'lux.finance', 'lux' ],
128
+ [ 'lux.market', 'lux' ],
129
+ [ 'lux.exchange', 'lux' ],
130
+ [ 'zoo.ngo', 'zoo' ],
131
+ [ 'zoo.network', 'zoo' ],
132
+ [ 'zoo.cloud', 'zoo' ],
133
+ [ 'zoolabs.id', 'zoo' ],
134
+ [ 'hanzo.ai', 'hanzo' ],
135
+ [ 'hanzo.cloud', 'hanzo' ],
136
+ [ 'hanzo.id', 'hanzo' ],
137
+ [ 'pars.id', 'pars', 'app' ],
138
+ [ 'pars.network', 'pars', 'app' ],
139
+ [ 'parsdao.org', 'pars', 'app' ],
140
+ [ 'bootno.de', 'bootnode', 'platform' ],
141
+ ];
142
+
143
+ export const DEFAULT_ORG: Org = 'lux';
144
+
145
+ export interface WhiteLabel extends OrgIdentity {
146
+ /** Hostname the identity was resolved from, port stripped, lowercased. */
147
+ readonly host: string;
148
+ /** App slug taken from the hostname — `mpc.lux.cloud` → `mpc`. */
149
+ readonly app: string;
150
+ /** gui child-theme row to hand GuiProvider — `dark_lux`, `dark_zoo`, … */
151
+ readonly theme: string;
152
+ /** OIDC issuer — `https://lux.id`. */
153
+ readonly issuer: string;
154
+ /** IAM client id, `<org>-<app>` per the fleet naming scheme — `lux-mpc`. */
155
+ readonly clientId: string;
156
+ }
157
+
158
+ /** Strip the port and any trailing dot, lowercase. `LUX.cloud:3000` → `lux.cloud`. */
159
+ function normalizeHost(host: string): string {
160
+ const noPort = host.trim().toLowerCase().split(':')[0] ?? '';
161
+ return noPort.replace(/\.$/, '');
162
+ }
163
+
164
+ /** true when `host` is `domain` or a subdomain of it (label boundary respected). */
165
+ function isUnder(host: string, domain: string): boolean {
166
+ return host === domain || host.endsWith(`.${ domain }`);
167
+ }
168
+
169
+ /** The registrable domain this host sits under, or undefined for unknown hosts. */
170
+ function matchDomain(host: string): readonly [string, Org, string?] | undefined {
171
+ // Longest domain first so `zoo.network` wins over a hypothetical `network`.
172
+ let best: readonly [string, Org, string?] | undefined;
173
+ for (const entry of DOMAIN_ORG) {
174
+ if (isUnder(host, entry[0]) && (!best || entry[0].length > best[0].length)) {
175
+ best = entry;
176
+ }
177
+ }
178
+ return best;
179
+ }
180
+
181
+ /**
182
+ * App slug for the IAM client id.
183
+ *
184
+ * `mpc.lux.cloud` → `mpc` (leftmost label)
185
+ * `lux.cloud` → `cloud` (apex: the service name, not the org)
186
+ * `www.lux.network`→ `network` (www is chrome, not an app)
187
+ * `bootno.de` → `platform` (pinned: a registration, not a guess)
188
+ * `localhost` → `local`
189
+ */
190
+ function appSlug(host: string, domain: string, pinned?: string): string {
191
+ if (pinned) return pinned;
192
+ const sub = host === domain ? '' : host.slice(0, -(domain.length + 1));
193
+ const leftmost = sub.split('.')[0] ?? '';
194
+ if (leftmost && leftmost !== 'www') return leftmost;
195
+ // Apex (or www) — the domain's own service label.
196
+ const label = domain.split('.')[0] ?? '';
197
+ const tld = domain.split('.').slice(1).join('.');
198
+ // `lux.network` → `network`, `lux.cloud` → `cloud`, `pars.id` → `id`.
199
+ return tld.split('.')[0] || label;
200
+ }
201
+
202
+ /** Loopback / LAN / *.local hosts a developer actually types. */
203
+ function isLocal(host: string): boolean {
204
+ return (
205
+ host === 'localhost' ||
206
+ host.endsWith('.localhost') ||
207
+ host === '127.0.0.1' ||
208
+ host === '0.0.0.0' ||
209
+ host === '[::1]' ||
210
+ host.endsWith('.local')
211
+ );
212
+ }
213
+
214
+ /**
215
+ * Resolve the full white-label identity from a Host header value.
216
+ *
217
+ * Pure and total: an unknown or missing host falls back to the Lux row rather
218
+ * than throwing, so a preview URL or a health probe never blanks the UI.
219
+ *
220
+ * resolveWhiteLabel('mpc.lux.network') // org lux, clientId lux-mpc, issuer https://lux.id
221
+ * resolveWhiteLabel('zoo.cloud') // org zoo, clientId zoo-cloud, issuer https://zoolabs.id
222
+ * resolveWhiteLabel('cloud.pars.network') // org pars, clientId pars-app, issuer https://pars.id
223
+ */
224
+ export function resolveWhiteLabel(host?: string | null): WhiteLabel {
225
+ const h = normalizeHost(host ?? '');
226
+ const match = h ? matchDomain(h) : undefined;
227
+ const org = match ? match[1] : DEFAULT_ORG;
228
+ const identity = ORGS[org];
229
+ const app = match ? appSlug(h, match[0], match[2]) : (h && isLocal(h) ? 'local' : 'app');
230
+ return {
231
+ ...identity,
232
+ host: h,
233
+ app,
234
+ theme: `dark_${ org }`,
235
+ issuer: `https://${ identity.iamDomain }`,
236
+ clientId: `${ org }-${ app }`,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * The current host in a browser, or '' during SSR / prerender.
242
+ * Server code must pass `req.headers.host` to resolveWhiteLabel explicitly —
243
+ * there is no ambient request here, and guessing would bake in a brand.
244
+ */
245
+ export function currentHost(): string {
246
+ return typeof window === 'undefined' ? '' : window.location.host;
247
+ }
248
+
249
+ /** Identity for the org id itself, for the rare surface that is not host-routed. */
250
+ export function orgIdentity(org: Org): OrgIdentity {
251
+ return ORGS[org];
252
+ }
253
+
254
+ export { ORGS as orgs };