@blocksdiy/blocks-client-sdk 1.24.1 → 1.25.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.
- package/README.md +32 -0
- package/dist/Navigation.d.ts +40 -0
- package/dist/Navigation.d.ts.map +1 -0
- package/dist/Navigation.js +66 -0
- package/dist/ReactClientSdk.d.ts +1 -0
- package/dist/ReactClientSdk.d.ts.map +1 -1
- package/dist/ReactClientSdk.jsx +1 -0
- package/dist/app/AppRoutes.d.ts +8 -0
- package/dist/app/AppRoutes.d.ts.map +1 -0
- package/dist/app/AppRoutes.jsx +222 -0
- package/dist/app/BlocksApp.d.ts +22 -0
- package/dist/app/BlocksApp.d.ts.map +1 -0
- package/dist/app/BlocksApp.jsx +73 -0
- package/dist/app/SdkProvider.d.ts +6 -0
- package/dist/app/SdkProvider.d.ts.map +1 -0
- package/dist/app/SdkProvider.jsx +45 -0
- package/dist/app/app-shape/bundle-shape.d.ts +54 -0
- package/dist/app/app-shape/bundle-shape.d.ts.map +1 -0
- package/dist/app/app-shape/bundle-shape.js +118 -0
- package/dist/app/app-shape/route-tree.d.ts +33 -0
- package/dist/app/app-shape/route-tree.d.ts.map +1 -0
- package/dist/app/app-shape/route-tree.jsx +137 -0
- package/dist/app/app-shape/use-app-shape.d.ts +13 -0
- package/dist/app/app-shape/use-app-shape.d.ts.map +1 -0
- package/dist/app/app-shape/use-app-shape.js +13 -0
- package/dist/app/dev-server/bootstrap.d.ts +8 -0
- package/dist/app/dev-server/bootstrap.d.ts.map +1 -0
- package/dist/app/dev-server/bootstrap.js +43 -0
- package/dist/app/dev-server/fonts.d.ts +8 -0
- package/dist/app/dev-server/fonts.d.ts.map +1 -0
- package/dist/app/dev-server/fonts.js +67 -0
- package/dist/app/hooks/use-scroll-to-top-on-page-change.d.ts +17 -0
- package/dist/app/hooks/use-scroll-to-top-on-page-change.d.ts.map +1 -0
- package/dist/app/hooks/use-scroll-to-top-on-page-change.js +55 -0
- package/dist/app/host.d.ts +106 -0
- package/dist/app/host.d.ts.map +1 -0
- package/dist/app/host.js +67 -0
- package/dist/app/index.d.ts +14 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +14 -0
- package/dist/app/sign-in/Login.d.ts +8 -0
- package/dist/app/sign-in/Login.d.ts.map +1 -0
- package/dist/app/sign-in/Login.jsx +194 -0
- package/dist/app/sign-in/api.d.ts +13 -0
- package/dist/app/sign-in/api.d.ts.map +1 -0
- package/dist/app/sign-in/api.js +47 -0
- package/dist/app/sign-in/auth-url.d.ts +7 -0
- package/dist/app/sign-in/auth-url.d.ts.map +1 -0
- package/dist/app/sign-in/auth-url.js +6 -0
- package/dist/app/types.d.ts +36 -0
- package/dist/app/types.d.ts.map +1 -0
- package/dist/app/types.js +1 -0
- package/dist/app/ui/icons.d.ts +12 -0
- package/dist/app/ui/icons.d.ts.map +1 -0
- package/dist/app/ui/icons.jsx +32 -0
- package/dist/app/ui/ui-styles.d.ts +15 -0
- package/dist/app/ui/ui-styles.d.ts.map +1 -0
- package/dist/app/ui/ui-styles.js +458 -0
- package/dist/dev-config.d.ts +15 -0
- package/dist/dev-config.d.ts.map +1 -0
- package/dist/dev-config.js +9 -0
- package/package.json +23 -7
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The UI the platform adds to an app (the standalone login, the 404 page, the loading screen):
|
|
3
|
+
* shadcn's components as the app's own registry styles them (radix-nova) and Tailwind 4 draws
|
|
4
|
+
* them, written out as CSS. Neither the SDK nor app-render has Tailwind, and an app's Tailwind only
|
|
5
|
+
* generates the classes in its own files, so these can't be utility classes.
|
|
6
|
+
*
|
|
7
|
+
* Unlayered, so the app's CSS (in Tailwind's cascade layers) never restyles it. Every rule is
|
|
8
|
+
* scoped to `.blocks-ui`, except two that touch nothing of the app's: the login page's
|
|
9
|
+
* `body:has(...)` margin, and the dark fallbacks, which set only this UI's own `--ui-*` properties.
|
|
10
|
+
*/
|
|
11
|
+
const STYLE_ID = "blocks-ui-styles";
|
|
12
|
+
const APP_TOKENS_STYLE_ID = "blocks-ui-app-tokens";
|
|
13
|
+
/**
|
|
14
|
+
* The app's shadcn color tokens this UI reads, with shadcn's light and dark defaults. Each is read
|
|
15
|
+
* through a typed copy (`--ui-<token>`, registered as an inherited `<color>`): when the app's
|
|
16
|
+
* token isn't a full color (an older app's bare HSL numbers, or the host's placeholder before the
|
|
17
|
+
* bundle's CSS arrives), the copy inherits the default of the current mode instead of leaving the
|
|
18
|
+
* UI unstyled. Copies, never the tokens themselves: typing `--primary` would break the apps that
|
|
19
|
+
* wrap it in `hsl()`.
|
|
20
|
+
*/
|
|
21
|
+
const COLOR_TOKENS = {
|
|
22
|
+
background: { light: "#ffffff", dark: "#0a0a0a" },
|
|
23
|
+
foreground: { light: "#0a0a0a", dark: "#fafafa" },
|
|
24
|
+
card: { light: "#ffffff", dark: "#171717" },
|
|
25
|
+
"card-foreground": { light: "#0a0a0a", dark: "#fafafa" },
|
|
26
|
+
primary: { light: "#171717", dark: "#e5e5e5" },
|
|
27
|
+
"primary-foreground": { light: "#fafafa", dark: "#171717" },
|
|
28
|
+
muted: { light: "#f5f5f5", dark: "#262626" },
|
|
29
|
+
"muted-foreground": { light: "#737373", dark: "#a1a1a1" },
|
|
30
|
+
destructive: { light: "#e7000b", dark: "#ff6467" },
|
|
31
|
+
border: { light: "#e5e5e5", dark: "#282828" },
|
|
32
|
+
input: { light: "#e5e5e5", dark: "#343434" },
|
|
33
|
+
ring: { light: "#a1a1a1", dark: "#737373" },
|
|
34
|
+
};
|
|
35
|
+
/** Every app token this UI reads: the colors, and the ones it reads as they are. */
|
|
36
|
+
const APP_TOKENS = new Set([...Object.keys(COLOR_TOKENS), "radius", "font-sans", "font-heading"]);
|
|
37
|
+
const colorTokens = Object.entries(COLOR_TOKENS);
|
|
38
|
+
const tokenRegistrations = colorTokens
|
|
39
|
+
.map(([token, { light }]) => `@property --ui-${token} { syntax: "<color>"; inherits: true; initial-value: ${light}; }`)
|
|
40
|
+
.join("\n");
|
|
41
|
+
const darkFallbacks = colorTokens.map(([token, { dark }]) => ` --ui-${token}: ${dark};`).join("\n");
|
|
42
|
+
const tokenCopies = colorTokens.map(([token]) => ` --ui-${token}: var(--${token});`).join("\n");
|
|
43
|
+
/** Tailwind 4's `transition-*` timing. */
|
|
44
|
+
const EASE = "150ms cubic-bezier(0.4, 0, 0.2, 1)";
|
|
45
|
+
/** Tailwind 4's opacity modifier (`bg-primary/80`). */
|
|
46
|
+
const alpha = (token, percent) => `color-mix(in oklab, var(--ui-${token}) ${percent}%, transparent)`;
|
|
47
|
+
/** The radius scale of an app's `index.css` (`rounded-lg` is `--radius`). */
|
|
48
|
+
const RADIUS = "var(--radius, 0.625rem)";
|
|
49
|
+
const UI_STYLES = `
|
|
50
|
+
${tokenRegistrations}
|
|
51
|
+
|
|
52
|
+
.dark {
|
|
53
|
+
${darkFallbacks}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
body:has(.ui-login-page) {
|
|
57
|
+
margin: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.blocks-ui,
|
|
61
|
+
.blocks-ui *,
|
|
62
|
+
.blocks-ui *::before,
|
|
63
|
+
.blocks-ui *::after {
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.blocks-ui {
|
|
68
|
+
${tokenCopies}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Card */
|
|
72
|
+
.blocks-ui .ui-card {
|
|
73
|
+
--ui-card-spacing: 1rem;
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
gap: var(--ui-card-spacing);
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
overflow-wrap: anywhere;
|
|
79
|
+
border-radius: calc(${RADIUS} * 1.4);
|
|
80
|
+
background: var(--ui-card);
|
|
81
|
+
padding-block: var(--ui-card-spacing);
|
|
82
|
+
font-size: 0.875rem;
|
|
83
|
+
line-height: 1.25rem;
|
|
84
|
+
color: var(--ui-card-foreground);
|
|
85
|
+
box-shadow: 0 0 0 1px ${alpha("foreground", 10)};
|
|
86
|
+
}
|
|
87
|
+
.blocks-ui .ui-card-header {
|
|
88
|
+
display: grid;
|
|
89
|
+
gap: 0.25rem;
|
|
90
|
+
padding-inline: var(--ui-card-spacing);
|
|
91
|
+
text-align: center;
|
|
92
|
+
}
|
|
93
|
+
.blocks-ui .ui-card-title {
|
|
94
|
+
margin: 0;
|
|
95
|
+
font-family: var(--font-heading, inherit);
|
|
96
|
+
font-size: 1.25rem;
|
|
97
|
+
line-height: 1.375;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
}
|
|
100
|
+
.blocks-ui .ui-card-description {
|
|
101
|
+
margin: 0;
|
|
102
|
+
color: var(--ui-muted-foreground);
|
|
103
|
+
}
|
|
104
|
+
.blocks-ui .ui-card-content {
|
|
105
|
+
display: grid;
|
|
106
|
+
gap: 1.25rem;
|
|
107
|
+
padding-inline: var(--ui-card-spacing);
|
|
108
|
+
}
|
|
109
|
+
.blocks-ui .ui-strong {
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
color: var(--ui-foreground);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Icon tile (shadcn's empty-state media) */
|
|
115
|
+
.blocks-ui .ui-icon-tile {
|
|
116
|
+
display: flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
justify-content: center;
|
|
119
|
+
width: 2.5rem;
|
|
120
|
+
height: 2.5rem;
|
|
121
|
+
margin: 0 auto 0.5rem;
|
|
122
|
+
flex-shrink: 0;
|
|
123
|
+
border-radius: calc(${RADIUS} * 1.8);
|
|
124
|
+
background: var(--ui-muted);
|
|
125
|
+
color: var(--ui-foreground);
|
|
126
|
+
}
|
|
127
|
+
.blocks-ui .ui-icon-tile svg {
|
|
128
|
+
width: 1.5rem;
|
|
129
|
+
height: 1.5rem;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Form */
|
|
133
|
+
.blocks-ui .ui-form,
|
|
134
|
+
.blocks-ui .ui-actions {
|
|
135
|
+
display: grid;
|
|
136
|
+
gap: 1rem;
|
|
137
|
+
margin: 0;
|
|
138
|
+
}
|
|
139
|
+
.blocks-ui .ui-actions {
|
|
140
|
+
gap: 0.5rem;
|
|
141
|
+
}
|
|
142
|
+
.blocks-ui .ui-field {
|
|
143
|
+
display: grid;
|
|
144
|
+
gap: 0.5rem;
|
|
145
|
+
}
|
|
146
|
+
.blocks-ui .ui-label {
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
gap: 0.5rem;
|
|
150
|
+
font-size: 0.875rem;
|
|
151
|
+
line-height: 1;
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
user-select: none;
|
|
154
|
+
}
|
|
155
|
+
.blocks-ui .ui-input {
|
|
156
|
+
width: 100%;
|
|
157
|
+
min-width: 0;
|
|
158
|
+
height: 2rem;
|
|
159
|
+
margin: 0;
|
|
160
|
+
padding: 0.25rem 0.625rem;
|
|
161
|
+
border: 1px solid var(--ui-input);
|
|
162
|
+
border-radius: ${RADIUS};
|
|
163
|
+
background: transparent;
|
|
164
|
+
color: inherit;
|
|
165
|
+
font: inherit;
|
|
166
|
+
font-size: 1rem;
|
|
167
|
+
line-height: 1.5rem;
|
|
168
|
+
outline: none;
|
|
169
|
+
transition:
|
|
170
|
+
color ${EASE},
|
|
171
|
+
background-color ${EASE},
|
|
172
|
+
border-color ${EASE};
|
|
173
|
+
}
|
|
174
|
+
.blocks-ui .ui-input::placeholder {
|
|
175
|
+
color: var(--ui-muted-foreground);
|
|
176
|
+
}
|
|
177
|
+
.blocks-ui .ui-input:focus-visible {
|
|
178
|
+
border-color: var(--ui-ring);
|
|
179
|
+
box-shadow: 0 0 0 3px ${alpha("ring", 50)};
|
|
180
|
+
}
|
|
181
|
+
.blocks-ui .ui-input:disabled {
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
cursor: not-allowed;
|
|
184
|
+
background: ${alpha("input", 50)};
|
|
185
|
+
opacity: 0.5;
|
|
186
|
+
}
|
|
187
|
+
.dark .blocks-ui .ui-input {
|
|
188
|
+
background: ${alpha("input", 30)};
|
|
189
|
+
}
|
|
190
|
+
.dark .blocks-ui .ui-input:disabled {
|
|
191
|
+
background: ${alpha("input", 80)};
|
|
192
|
+
}
|
|
193
|
+
@media (min-width: 768px) {
|
|
194
|
+
.blocks-ui .ui-input {
|
|
195
|
+
font-size: 0.875rem;
|
|
196
|
+
line-height: 1.25rem;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Button */
|
|
201
|
+
.blocks-ui .ui-button {
|
|
202
|
+
display: inline-flex;
|
|
203
|
+
flex-shrink: 0;
|
|
204
|
+
align-items: center;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
gap: 0.375rem;
|
|
207
|
+
height: 2rem;
|
|
208
|
+
margin: 0;
|
|
209
|
+
padding: 0 0.625rem;
|
|
210
|
+
border: 1px solid transparent;
|
|
211
|
+
border-radius: ${RADIUS};
|
|
212
|
+
background-color: transparent;
|
|
213
|
+
background-clip: padding-box;
|
|
214
|
+
color: inherit;
|
|
215
|
+
font: inherit;
|
|
216
|
+
font-size: 0.875rem;
|
|
217
|
+
line-height: 1.25rem;
|
|
218
|
+
font-weight: 500;
|
|
219
|
+
white-space: nowrap;
|
|
220
|
+
text-decoration: none;
|
|
221
|
+
cursor: pointer;
|
|
222
|
+
outline: none;
|
|
223
|
+
user-select: none;
|
|
224
|
+
transition: all ${EASE};
|
|
225
|
+
}
|
|
226
|
+
.blocks-ui .ui-button svg {
|
|
227
|
+
width: 1rem;
|
|
228
|
+
height: 1rem;
|
|
229
|
+
flex-shrink: 0;
|
|
230
|
+
pointer-events: none;
|
|
231
|
+
}
|
|
232
|
+
.blocks-ui .ui-button:focus-visible {
|
|
233
|
+
border-color: var(--ui-ring);
|
|
234
|
+
box-shadow: 0 0 0 3px ${alpha("ring", 50)};
|
|
235
|
+
}
|
|
236
|
+
.blocks-ui .ui-button:active:not([aria-haspopup]) {
|
|
237
|
+
translate: 0 1px;
|
|
238
|
+
}
|
|
239
|
+
.blocks-ui .ui-button:disabled {
|
|
240
|
+
pointer-events: none;
|
|
241
|
+
opacity: 0.5;
|
|
242
|
+
}
|
|
243
|
+
.blocks-ui .ui-button-full {
|
|
244
|
+
width: 100%;
|
|
245
|
+
}
|
|
246
|
+
.blocks-ui .ui-button-default {
|
|
247
|
+
background-color: var(--ui-primary);
|
|
248
|
+
color: var(--ui-primary-foreground);
|
|
249
|
+
}
|
|
250
|
+
.blocks-ui .ui-button-outline {
|
|
251
|
+
border-color: var(--ui-border);
|
|
252
|
+
background-color: var(--ui-background);
|
|
253
|
+
}
|
|
254
|
+
.dark .blocks-ui .ui-button-outline {
|
|
255
|
+
border-color: var(--ui-input);
|
|
256
|
+
background-color: ${alpha("input", 30)};
|
|
257
|
+
}
|
|
258
|
+
@media (hover: hover) {
|
|
259
|
+
.blocks-ui .ui-button-default:hover {
|
|
260
|
+
background-color: ${alpha("primary", 80)};
|
|
261
|
+
}
|
|
262
|
+
.blocks-ui .ui-button-outline:hover {
|
|
263
|
+
background-color: var(--ui-muted);
|
|
264
|
+
color: var(--ui-foreground);
|
|
265
|
+
}
|
|
266
|
+
.dark .blocks-ui .ui-button-outline:hover {
|
|
267
|
+
background-color: ${alpha("input", 50)};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* Separator with a label */
|
|
272
|
+
.blocks-ui .ui-separator-text {
|
|
273
|
+
position: relative;
|
|
274
|
+
text-align: center;
|
|
275
|
+
font-size: 0.875rem;
|
|
276
|
+
line-height: 1.25rem;
|
|
277
|
+
}
|
|
278
|
+
.blocks-ui .ui-separator-text::after {
|
|
279
|
+
content: "";
|
|
280
|
+
position: absolute;
|
|
281
|
+
inset: 50% 0 auto;
|
|
282
|
+
border-top: 1px solid var(--ui-border);
|
|
283
|
+
}
|
|
284
|
+
.blocks-ui .ui-separator-text span {
|
|
285
|
+
position: relative;
|
|
286
|
+
z-index: 1;
|
|
287
|
+
padding: 0 0.5rem;
|
|
288
|
+
background: var(--ui-card);
|
|
289
|
+
color: var(--ui-muted-foreground);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Alert (destructive) */
|
|
293
|
+
.blocks-ui .ui-alert {
|
|
294
|
+
display: grid;
|
|
295
|
+
grid-template-columns: auto 1fr;
|
|
296
|
+
gap: 0.125rem 0.5rem;
|
|
297
|
+
width: 100%;
|
|
298
|
+
padding: 0.5rem 0.625rem;
|
|
299
|
+
border: 1px solid var(--ui-border);
|
|
300
|
+
border-radius: ${RADIUS};
|
|
301
|
+
background: var(--ui-card);
|
|
302
|
+
color: var(--ui-destructive);
|
|
303
|
+
font-size: 0.875rem;
|
|
304
|
+
line-height: 1.25rem;
|
|
305
|
+
text-align: start;
|
|
306
|
+
}
|
|
307
|
+
.blocks-ui .ui-alert svg {
|
|
308
|
+
grid-row: span 2;
|
|
309
|
+
width: 1rem;
|
|
310
|
+
height: 1rem;
|
|
311
|
+
translate: 0 0.125rem;
|
|
312
|
+
}
|
|
313
|
+
.blocks-ui .ui-alert-description {
|
|
314
|
+
color: ${alpha("destructive", 90)};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Spinner */
|
|
318
|
+
.blocks-ui .ui-spinner {
|
|
319
|
+
width: 1rem;
|
|
320
|
+
height: 1rem;
|
|
321
|
+
animation: blocks-ui-spin 1s linear infinite;
|
|
322
|
+
}
|
|
323
|
+
@keyframes blocks-ui-spin {
|
|
324
|
+
to {
|
|
325
|
+
transform: rotate(360deg);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* The login's page: a page of its own, so it sets the type the app's CSS would. */
|
|
330
|
+
.blocks-ui.ui-login-page {
|
|
331
|
+
display: flex;
|
|
332
|
+
min-height: 100vh;
|
|
333
|
+
min-height: 100svh;
|
|
334
|
+
flex-direction: column;
|
|
335
|
+
align-items: center;
|
|
336
|
+
justify-content: center;
|
|
337
|
+
gap: 1.5rem;
|
|
338
|
+
padding: 1.5rem;
|
|
339
|
+
background: var(--ui-muted);
|
|
340
|
+
color: var(--ui-foreground);
|
|
341
|
+
font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji");
|
|
342
|
+
line-height: 1.5;
|
|
343
|
+
-webkit-font-smoothing: antialiased;
|
|
344
|
+
}
|
|
345
|
+
.blocks-ui .ui-login {
|
|
346
|
+
display: flex;
|
|
347
|
+
width: 100%;
|
|
348
|
+
max-width: 24rem;
|
|
349
|
+
flex-direction: column;
|
|
350
|
+
gap: 1.5rem;
|
|
351
|
+
}
|
|
352
|
+
@media (min-width: 768px) {
|
|
353
|
+
.blocks-ui.ui-login-page {
|
|
354
|
+
padding: 2.5rem;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* Empty state (the 404 page), inside the app's layout and type */
|
|
359
|
+
.blocks-ui.ui-empty {
|
|
360
|
+
display: flex;
|
|
361
|
+
width: 100%;
|
|
362
|
+
min-width: 0;
|
|
363
|
+
min-height: 50vh;
|
|
364
|
+
flex: 1;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
gap: 1rem;
|
|
369
|
+
padding: 3rem;
|
|
370
|
+
text-align: center;
|
|
371
|
+
text-wrap: balance;
|
|
372
|
+
}
|
|
373
|
+
.blocks-ui .ui-empty-header {
|
|
374
|
+
display: flex;
|
|
375
|
+
max-width: 24rem;
|
|
376
|
+
flex-direction: column;
|
|
377
|
+
align-items: center;
|
|
378
|
+
gap: 0.5rem;
|
|
379
|
+
}
|
|
380
|
+
.blocks-ui .ui-empty-title {
|
|
381
|
+
margin: 0;
|
|
382
|
+
font-family: var(--font-heading, inherit);
|
|
383
|
+
font-size: 1.125rem;
|
|
384
|
+
line-height: 1.75rem;
|
|
385
|
+
font-weight: 500;
|
|
386
|
+
letter-spacing: -0.025em;
|
|
387
|
+
}
|
|
388
|
+
.blocks-ui .ui-empty-description {
|
|
389
|
+
margin: 0;
|
|
390
|
+
color: var(--ui-muted-foreground);
|
|
391
|
+
font-size: 0.875rem;
|
|
392
|
+
line-height: 1.625;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Loading and quiet notes */
|
|
396
|
+
.blocks-ui.ui-loading {
|
|
397
|
+
display: flex;
|
|
398
|
+
min-height: 100vh;
|
|
399
|
+
min-height: 100svh;
|
|
400
|
+
align-items: center;
|
|
401
|
+
justify-content: center;
|
|
402
|
+
gap: 0.5rem;
|
|
403
|
+
}
|
|
404
|
+
.blocks-ui.ui-loading {
|
|
405
|
+
color: var(--ui-muted-foreground);
|
|
406
|
+
font-size: 0.875rem;
|
|
407
|
+
line-height: 1.25rem;
|
|
408
|
+
}
|
|
409
|
+
`;
|
|
410
|
+
/** Adds the stylesheet once; every component that renders this UI calls it before it lays out. */
|
|
411
|
+
export const ensureUiStyles = () => {
|
|
412
|
+
if (document.getElementById(STYLE_ID)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const style = document.createElement("style");
|
|
416
|
+
style.id = STYLE_ID;
|
|
417
|
+
style.textContent = UI_STYLES;
|
|
418
|
+
document.head.appendChild(style);
|
|
419
|
+
};
|
|
420
|
+
/** How often `char` occurs in `value`. */
|
|
421
|
+
const count = (value, char) => value.split(char).length - 1;
|
|
422
|
+
/**
|
|
423
|
+
* A token this UI reads, with a value that stays inside its declaration: no braces, semicolons,
|
|
424
|
+
* angle brackets, escapes, `!important` or comment markers, and its quotes and parentheses closed.
|
|
425
|
+
*/
|
|
426
|
+
const isSafeToken = ([name, value]) => APP_TOKENS.has(name) &&
|
|
427
|
+
typeof value === "string" &&
|
|
428
|
+
value.length > 0 &&
|
|
429
|
+
value.length <= 200 &&
|
|
430
|
+
!/[{};<>\\!]|\/\*/.test(value) &&
|
|
431
|
+
count(value, '"') % 2 === 0 &&
|
|
432
|
+
count(value, "'") % 2 === 0 &&
|
|
433
|
+
count(value, "(") === count(value, ")");
|
|
434
|
+
const declarations = (tokens) => Object.entries(tokens ?? {})
|
|
435
|
+
.filter(isSafeToken)
|
|
436
|
+
.map(([name, value]) => `--${name}: ${value};`)
|
|
437
|
+
.join(" ");
|
|
438
|
+
/**
|
|
439
|
+
* The app's own tokens, on the login only. A signed-out visitor's page has no app CSS (the bundle
|
|
440
|
+
* loads after sign-in), so the platform's public app data carries the tokens of the app's
|
|
441
|
+
* `index.css`, and they are set on the login's page: light, and under the host's `dark` class.
|
|
442
|
+
* Checked again here, since it is network data written into a stylesheet.
|
|
443
|
+
*/
|
|
444
|
+
export const applyLoginAppTokens = (theme) => {
|
|
445
|
+
const light = declarations(theme?.variables);
|
|
446
|
+
const dark = declarations(theme?.darkVariables);
|
|
447
|
+
let style = document.getElementById(APP_TOKENS_STYLE_ID);
|
|
448
|
+
if (!light && !dark) {
|
|
449
|
+
style?.remove();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (!style) {
|
|
453
|
+
style = document.createElement("style");
|
|
454
|
+
style.id = APP_TOKENS_STYLE_ID;
|
|
455
|
+
document.head.appendChild(style);
|
|
456
|
+
}
|
|
457
|
+
style.textContent = `.ui-login-page { ${light} }\n.dark .ui-login-page { ${dark} }`;
|
|
458
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the boilerplate's `index.html` declares for the dev server (Vite fills the `VITE_*`
|
|
3
|
+
* placeholders): the platform origin the login posts to, and the app this workspace builds.
|
|
4
|
+
*/
|
|
5
|
+
export interface BlocksDevConfig {
|
|
6
|
+
apiHost?: string;
|
|
7
|
+
appId?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const devConfig: () => BlocksDevConfig | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Where the signed-in token is kept: per app on the dev server, as the builder's sandbox seeds it,
|
|
12
|
+
* and `token` under app-render.
|
|
13
|
+
*/
|
|
14
|
+
export declare const tokenStorageKey: () => string;
|
|
15
|
+
//# sourceMappingURL=dev-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-config.d.ts","sourceRoot":"","sources":["../src/dev-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,SAAS,mCAAmF,CAAC;AAE1G;;;GAGG;AACH,eAAO,MAAM,eAAe,cAG3B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const devConfig = () => window.__BLOCKS_DEV__;
|
|
2
|
+
/**
|
|
3
|
+
* Where the signed-in token is kept: per app on the dev server, as the builder's sandbox seeds it,
|
|
4
|
+
* and `token` under app-render.
|
|
5
|
+
*/
|
|
6
|
+
export const tokenStorageKey = () => {
|
|
7
|
+
const appId = devConfig()?.appId;
|
|
8
|
+
return appId ? `token-${appId}` : "token";
|
|
9
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocksdiy/blocks-client-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Blocks client sdk",
|
|
6
6
|
"keywords": [],
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"import": "./dist/Page.js",
|
|
29
29
|
"require": "./dist/Page.js",
|
|
30
30
|
"types": "./dist/Page.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./app": {
|
|
33
|
+
"import": "./dist/app/index.js",
|
|
34
|
+
"require": "./dist/app/index.js",
|
|
35
|
+
"types": "./dist/app/index.d.ts"
|
|
31
36
|
}
|
|
32
37
|
},
|
|
33
38
|
"directories": {
|
|
@@ -46,25 +51,36 @@
|
|
|
46
51
|
]
|
|
47
52
|
},
|
|
48
53
|
"dependencies": {
|
|
49
|
-
"@blocksdiy/blocks-client-api": "1.30.
|
|
50
|
-
"@blocksdiy/react-common": "1.62.
|
|
51
|
-
"@tanstack/react-query": "^5.101.0"
|
|
54
|
+
"@blocksdiy/blocks-client-api": "1.30.1",
|
|
55
|
+
"@blocksdiy/react-common": "1.62.1",
|
|
56
|
+
"@tanstack/react-query": "^5.101.0",
|
|
57
|
+
"react-error-boundary": "^6.1.6"
|
|
52
58
|
},
|
|
53
59
|
"devDependencies": {
|
|
54
60
|
"@blockscom/eslint-config": "4.6.4",
|
|
61
|
+
"jsdom": "^26.1.0",
|
|
62
|
+
"react-dom": "^19.2.8",
|
|
63
|
+
"react-router": "^8.4.0",
|
|
55
64
|
"tsc-alias": "^1.8.10",
|
|
56
|
-
"typescript": "^5.9.3"
|
|
65
|
+
"typescript": "^5.9.3",
|
|
66
|
+
"vitest": "^4.1.11"
|
|
57
67
|
},
|
|
58
68
|
"engines": {
|
|
59
69
|
"node": ">=24.22.1"
|
|
60
70
|
},
|
|
61
71
|
"peerDependencies": {
|
|
62
|
-
"react": ">=
|
|
72
|
+
"react": ">=19",
|
|
73
|
+
"react-router": ">=7"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"react-router": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
63
79
|
},
|
|
64
80
|
"scripts": {
|
|
65
81
|
"clean": "rm -rf ./dist/ && rm -rf tsconfig.tsbuildinfo && rm -rf node_modules",
|
|
66
82
|
"build": "rm -rf dist && tsc && tsc-alias && echo $(date +%s) > .build-sentinel.json",
|
|
67
|
-
"test": "jest --passWithNoTests --config ./test/jest.json",
|
|
83
|
+
"test": "jest --passWithNoTests --config ./test/jest.json && vitest run",
|
|
68
84
|
"lint": "pnpm eslint src/",
|
|
69
85
|
"lint-fix": "pnpm eslint src/ --fix"
|
|
70
86
|
}
|