@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.
- package/dist/avatar.cjs +7 -6
- package/dist/avatar.js +7 -5
- package/dist/badge.cjs +33 -35
- package/dist/badge.js +33 -35
- package/dist/bank.d.cts +3 -3
- package/dist/bank.d.ts +3 -3
- package/dist/checkbox.cjs +4 -4
- package/dist/checkbox.d.cts +3 -2
- package/dist/checkbox.d.ts +3 -2
- package/dist/checkbox.js +5 -4
- package/dist/chrome.cjs +960 -0
- package/dist/chrome.d.cts +93 -0
- package/dist/chrome.d.ts +93 -0
- package/dist/chrome.js +931 -0
- package/dist/drawer.cjs +86 -20
- package/dist/drawer.js +85 -19
- package/dist/expiration-selector.cjs +3 -2
- package/dist/expiration-selector.js +3 -2
- package/dist/greeks-display.cjs +6 -6
- package/dist/greeks-display.js +6 -6
- package/dist/gui.cjs +13 -0
- package/dist/gui.d.cts +2 -0
- package/dist/gui.d.ts +2 -0
- package/dist/gui.js +2 -0
- package/dist/heading.cjs +4 -2
- package/dist/heading.js +4 -2
- package/dist/iam.cjs +216 -0
- package/dist/iam.d.cts +65 -0
- package/dist/iam.d.ts +65 -0
- package/dist/iam.js +211 -0
- package/dist/icons.cjs +13 -0
- package/dist/icons.d.cts +1 -0
- package/dist/icons.d.ts +1 -0
- package/dist/icons.js +2 -0
- package/dist/identity.cjs +430 -0
- package/dist/identity.d.cts +64 -0
- package/dist/identity.d.ts +64 -0
- package/dist/identity.js +408 -0
- package/dist/index.cjs +1610 -836
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1587 -831
- package/dist/input.cjs +3 -1
- package/dist/input.d.cts +3 -5
- package/dist/input.d.ts +3 -5
- package/dist/input.js +3 -1
- package/dist/lux.config.cjs +23 -2
- package/dist/lux.config.d.cts +28 -10
- package/dist/lux.config.d.ts +28 -10
- package/dist/lux.config.js +23 -2
- package/dist/menu.cjs +4 -4
- package/dist/menu.js +4 -4
- package/dist/next.cjs +132 -0
- package/dist/next.d.cts +32 -0
- package/dist/next.d.ts +32 -0
- package/dist/next.js +129 -0
- package/dist/option-chain.cjs +1 -1
- package/dist/option-chain.js +1 -1
- package/dist/option-position.cjs +2 -1
- package/dist/option-position.js +2 -1
- package/dist/pnl-diagram.cjs +8 -6
- package/dist/pnl-diagram.js +8 -6
- package/dist/progress.cjs +7 -6
- package/dist/progress.js +7 -5
- package/dist/provider.cjs +146 -4
- package/dist/provider.d.cts +15 -3
- package/dist/provider.d.ts +15 -3
- package/dist/provider.js +146 -5
- package/dist/radio.cjs +9 -7
- package/dist/radio.js +10 -7
- package/dist/separator.cjs +3 -1
- package/dist/separator.js +3 -1
- package/dist/slider.cjs +28 -16
- package/dist/slider.js +28 -15
- package/dist/strategy-builder.cjs +2 -1
- package/dist/strategy-builder.js +2 -1
- package/dist/switch.cjs +11 -12
- package/dist/switch.js +12 -12
- package/dist/tag.cjs +34 -36
- package/dist/tag.js +33 -35
- package/dist/textarea.cjs +3 -1
- package/dist/textarea.js +3 -1
- package/dist/tooltip.cjs +28 -30
- package/dist/tooltip.js +27 -29
- package/dist/use-narrow.cjs +46 -0
- package/dist/use-narrow.d.cts +12 -0
- package/dist/use-narrow.d.ts +12 -0
- package/dist/use-narrow.js +20 -0
- package/dist/vite.cjs +109 -0
- package/dist/vite.d.cts +29 -0
- package/dist/vite.d.ts +29 -0
- package/dist/vite.js +107 -0
- package/dist/white-label.cjs +142 -0
- package/dist/white-label.d.cts +59 -0
- package/dist/white-label.d.ts +59 -0
- package/dist/white-label.js +134 -0
- package/package.json +85 -19
- package/src/avatar.tsx +22 -16
- package/src/checkbox.tsx +15 -10
- package/src/chrome.tsx +459 -0
- package/src/drawer.tsx +83 -10
- package/src/engine.ts +130 -0
- package/src/expiration-selector.tsx +3 -2
- package/src/greeks-display.tsx +9 -6
- package/src/gui.ts +17 -0
- package/src/heading.tsx +12 -8
- package/src/iam.ts +170 -0
- package/src/icons.ts +18 -0
- package/src/identity.tsx +318 -0
- package/src/index.ts +44 -1
- package/src/input.tsx +13 -9
- package/src/lux.config.ts +16 -15
- package/src/menu.tsx +4 -4
- package/src/next.ts +89 -0
- package/src/option-chain.tsx +1 -1
- package/src/option-position.tsx +2 -1
- package/src/pnl-diagram.tsx +11 -6
- package/src/progress.tsx +15 -8
- package/src/provider.tsx +48 -7
- package/src/radio.tsx +15 -11
- package/src/separator.tsx +8 -3
- package/src/slider.tsx +35 -19
- package/src/strategy-builder.tsx +3 -2
- package/src/switch.tsx +17 -16
- package/src/textarea.tsx +8 -4
- package/src/tooltip.tsx +15 -24
- package/src/use-narrow.ts +40 -0
- package/src/vite.ts +80 -0
- package/src/white-label.ts +254 -0
- package/tokens.css +613 -319
- 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 };
|