tempest-react-sdk 0.59.0 → 0.60.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 CHANGED
@@ -208,13 +208,33 @@ The styles ship hashed under the `tempest_` namespace — they do **not** collid
208
208
  **Importing less.** The JavaScript you import is tree-shaken; the CSS is not, so `styles.css` carries all ~150 components whether you mount thirteen or all of them. Pay for what you use by taking the foundation plus the sheets you need:
209
209
 
210
210
  ```ts
211
- import "tempest-react-sdk/styles/core.css"; // reset + tokensalways required
211
+ import "tempest-react-sdk/styles/core.css"; // tokens + global reset see below
212
212
  import "tempest-react-sdk/styles/Button.css";
213
213
  import "tempest-react-sdk/styles/forms.css"; // or a whole family
214
214
  ```
215
215
 
216
216
  Measured in a real Vite app mounting twelve components: **236.71 kB raw / 35.38 kB gzip** with `styles.css`, **38.94 kB / 7.70 kB** with `core.css` plus those twelve — 78% less. See [Styles](https://mauriciobenjamin700.github.io/tempest-react-sdk/styles/) for the full list of groups.
217
217
 
218
+ **Letting the build keep that list.** `tempestStyles()` reads the imports your source already writes and resolves `styles/auto.css` to exactly the sheets your app can reach — including the ones it reaches _transitively_, which is where a hand-kept list goes wrong (`<DataTable>` alone needs six, `<AIChat>` seven):
219
+
220
+ ```ts
221
+ // vite.config.ts
222
+ import { tempestStyles } from "tempest-react-sdk/vite";
223
+ export default defineConfig({ plugins: [react(), tempestStyles()] });
224
+
225
+ // src/main.tsx — one line, forever
226
+ import "tempest-react-sdk/styles/auto.css";
227
+ ```
228
+
229
+ **If your app owns its own layout,** do not reach for `core.css`: 44 of its 46 selectors dress markup you own (`html`, `body`, `#root`, `button`, `table`, `:where(ul, ol)[class]`). Dropping it is not the fix either — the components are written _against_ that reset, so without it they lose their box model, not their polish. Take the foundation in two pieces instead, which is also what `tempestStyles()` emits by default:
230
+
231
+ ```ts
232
+ import "tempest-react-sdk/styles/tokens.css"; // the 373 --tempest-* tokens
233
+ import "tempest-react-sdk/styles/scoped.css"; // the reset, confined to :where([class*="tempest_"])
234
+ ```
235
+
236
+ **3.03 kB brotli** against `core.css`'s 3.23 kB — near enough the same bytes, which is the point: what you drop is not weight, it is the SDK reaching outside its own components.
237
+
218
238
  **Rebranding.** The `--tempest-*` tokens are the only theming API, and `createTheme` writes them for you — `primary` alone yields the ten-step scale for **both** color schemes (dark inverts the ramp), plus the status families, the radius scale and the focus ring:
219
239
 
220
240
  ```ts
@@ -0,0 +1,4 @@
1
+ /* Generated by scripts/split-css.mjs — do not edit. */
2
+ /* Without `tempestStyles()` this is the complete sheet: the plugin narrows
3
+ it to the components the app imports. Costs bytes, never correctness. */
4
+ @import "../styles.css";
@@ -0,0 +1,33 @@
1
+ /* Generated by scripts/split-css.mjs — do not edit. */
2
+ *,:before,:after{box-sizing:border-box}
3
+ html{-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;tab-size:4}
4
+ :where(html,body,#root){height:100%}
5
+ body{background-color:var(--tempest-bg);color:var(--tempest-text);margin:0}
6
+ img,svg,video,canvas,audio,iframe,embed,object{max-width:100%;display:block}
7
+ img,video{height:auto}
8
+ :where(button,a,label,summary)>svg:only-child{margin-inline:auto}
9
+ button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit;color:inherit;letter-spacing:inherit}
10
+ button{cursor:pointer;background:0 0;border:0;padding:0}
11
+ button:disabled{cursor:not-allowed}
12
+ button::-moz-focus-inner{border:0;padding:0}
13
+ textarea{resize:vertical}
14
+ input[type=search]::-webkit-search-decoration{-webkit-appearance:none}
15
+ input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}
16
+ input[type=search]::-webkit-search-results-button{-webkit-appearance:none}
17
+ input[type=search]::-webkit-search-results-decoration{-webkit-appearance:none}
18
+ table{border-collapse:collapse;border-spacing:0}
19
+ :where(ul,ol)[class]{margin:0;padding:0;list-style:none}
20
+ :focus-visible{outline:var(--tempest-focus-ring-width,3px) solid var(--tempest-focus-ring-color,#0066ff59);outline-offset:var(--tempest-focus-ring-offset,2px)}
21
+ @media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}
22
+ @media (width<=767.98px){.tempest-hide-mobile{display:none!important}}
23
+ @media (width>=768px) and (width<=1023.98px){.tempest-hide-tablet{display:none!important}}
24
+ @media (width>=1024px){.tempest-hide-desktop{display:none!important}}
25
+ @media (width>=768px){.tempest-show-only-mobile{display:none!important}}
26
+ @media (width<=767.98px),(width>=1024px){.tempest-show-only-tablet{display:none!important}}
27
+ @media (width<=1023.98px){.tempest-show-only-desktop{display:none!important}}
28
+ @media (pointer:fine){.tempest-show-only-touch{display:none!important}}
29
+ @media (pointer:coarse){.tempest-hide-touch{display:none!important}}
30
+ @media print{.tempest-hide-print{display:none!important}}
31
+ @media screen{.tempest-show-only-print{display:none!important}}
32
+ @media print{*,:before,:after{box-shadow:none!important;text-shadow:none!important;color:#000!important;background-color:#0000!important}body{font-size:11pt;line-height:1.4;color:#000!important;background:#fff!important}.tempest-hide-print,[role=dialog],[aria-modal=true],[role=tooltip],[role=status][aria-live]{display:none!important}table,figure,pre,[class*=card]{page-break-inside:avoid;break-inside:avoid}a[href]:after{content:" (" attr(href) ")";font-size:90%;color:#555!important}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}h1,h2,h3,h4,h5,h6{page-break-after:avoid;break-after:avoid-page;color:#000!important}}
33
+ /*$vite$:1*/
@@ -0,0 +1,496 @@
1
+ {
2
+ "note": "Generated by scripts/gen-style-manifest.mjs — do not edit.",
3
+ "core": "core.css",
4
+ "components": {
5
+ "AIChat": [
6
+ "AIChat.css",
7
+ "CodeBlock.css",
8
+ "Collapsible.css",
9
+ "CopyButton.css",
10
+ "EmptyState.css",
11
+ "Markdown.css",
12
+ "VisuallyHidden.css"
13
+ ],
14
+ "AIChatComposer": [
15
+ "AIChat.css"
16
+ ],
17
+ "AIChatTurn": [
18
+ "AIChat.css",
19
+ "CodeBlock.css",
20
+ "Collapsible.css",
21
+ "CopyButton.css",
22
+ "Markdown.css",
23
+ "VisuallyHidden.css"
24
+ ],
25
+ "Accordion": [
26
+ "Accordion.css"
27
+ ],
28
+ "Alert": [
29
+ "Alert.css"
30
+ ],
31
+ "AppBar": [
32
+ "AppBar.css"
33
+ ],
34
+ "AppShell": [
35
+ "AppShell.css"
36
+ ],
37
+ "AspectRatio": [
38
+ "AspectRatio.css"
39
+ ],
40
+ "AudioPlayer": [
41
+ "AudioPlayer.css"
42
+ ],
43
+ "AudioRecorder": [
44
+ "AudioPlayer.css",
45
+ "AudioRecorder.css"
46
+ ],
47
+ "Avatar": [
48
+ "Avatar.css"
49
+ ],
50
+ "AvatarGroup": [
51
+ "Avatar.css",
52
+ "AvatarGroup.css"
53
+ ],
54
+ "Badge": [
55
+ "Badge.css"
56
+ ],
57
+ "Banner": [
58
+ "Banner.css"
59
+ ],
60
+ "BarList": [
61
+ "BarList.css"
62
+ ],
63
+ "BarcodeScanner": [
64
+ "BarcodeScanner.css"
65
+ ],
66
+ "BottomNavigation": [
67
+ "BottomNavigation.css"
68
+ ],
69
+ "BottomSheet": [
70
+ "BottomSheet.css"
71
+ ],
72
+ "Breadcrumbs": [
73
+ "Breadcrumbs.css"
74
+ ],
75
+ "Button": [
76
+ "Button.css"
77
+ ],
78
+ "CEPInput": [
79
+ "Input.css"
80
+ ],
81
+ "CNPJInput": [
82
+ "Input.css"
83
+ ],
84
+ "CPFInput": [
85
+ "Input.css"
86
+ ],
87
+ "Calendar": [
88
+ "Calendar.css"
89
+ ],
90
+ "Card": [
91
+ "Card.css"
92
+ ],
93
+ "Carousel": [
94
+ "Carousel.css"
95
+ ],
96
+ "Center": [
97
+ "Center.css"
98
+ ],
99
+ "Chat": [
100
+ "Chat.css",
101
+ "EmptyState.css",
102
+ "VisuallyHidden.css"
103
+ ],
104
+ "ChatComposer": [
105
+ "Chat.css"
106
+ ],
107
+ "Checkbox": [
108
+ "Checkbox.css"
109
+ ],
110
+ "ChipInput": [
111
+ "ChipInput.css"
112
+ ],
113
+ "CodeBlock": [
114
+ "CodeBlock.css",
115
+ "CopyButton.css"
116
+ ],
117
+ "Collapsible": [
118
+ "Collapsible.css"
119
+ ],
120
+ "Combobox": [
121
+ "Combobox.css"
122
+ ],
123
+ "Command": [
124
+ "Command.css"
125
+ ],
126
+ "ConfirmDialog": [
127
+ "Button.css",
128
+ "Modal.css"
129
+ ],
130
+ "Container": [
131
+ "Layout.css"
132
+ ],
133
+ "ContextMenu": [
134
+ "ContextMenu.css"
135
+ ],
136
+ "Controller": [
137
+ "Input.css"
138
+ ],
139
+ "CopyButton": [
140
+ "CopyButton.css"
141
+ ],
142
+ "DataList": [
143
+ "DataList.css"
144
+ ],
145
+ "DataTable": [
146
+ "DataTable.css",
147
+ "Pagination.css",
148
+ "SearchBar.css",
149
+ "Skeleton.css",
150
+ "Table.css",
151
+ "VisuallyHidden.css"
152
+ ],
153
+ "DatePicker": [
154
+ "Input.css"
155
+ ],
156
+ "DateRangePicker": [
157
+ "DateRangePicker.css"
158
+ ],
159
+ "DescriptionList": [
160
+ "DescriptionList.css"
161
+ ],
162
+ "Divider": [
163
+ "Divider.css"
164
+ ],
165
+ "Drawer": [
166
+ "Drawer.css"
167
+ ],
168
+ "DropdownMenu": [
169
+ "DropdownMenu.css"
170
+ ],
171
+ "Dropzone": [
172
+ "Dropzone.css"
173
+ ],
174
+ "EmptyState": [
175
+ "EmptyState.css"
176
+ ],
177
+ "ErrorState": [
178
+ "Button.css",
179
+ "ErrorState.css"
180
+ ],
181
+ "ErrorText": [
182
+ "ErrorText.css"
183
+ ],
184
+ "FileUpload": [
185
+ "FileUpload.css"
186
+ ],
187
+ "FilterBar": [
188
+ "FilterBar.css",
189
+ "VisuallyHidden.css"
190
+ ],
191
+ "FloatingActionButton": [
192
+ "FloatingActionButton.css"
193
+ ],
194
+ "Form": [
195
+ "Form.css"
196
+ ],
197
+ "FormActions": [
198
+ "Form.css"
199
+ ],
200
+ "FormProvider": [
201
+ "Input.css"
202
+ ],
203
+ "FormRow": [
204
+ "Form.css"
205
+ ],
206
+ "FormSection": [
207
+ "Form.css"
208
+ ],
209
+ "Grid": [
210
+ "Layout.css"
211
+ ],
212
+ "HoverCard": [
213
+ "HoverCard.css"
214
+ ],
215
+ "Image": [
216
+ "Image.css"
217
+ ],
218
+ "ImageCropper": [
219
+ "ImageCropper.css"
220
+ ],
221
+ "Input": [
222
+ "Input.css"
223
+ ],
224
+ "InstallBanner": [
225
+ "Button.css",
226
+ "InstallBanner.css"
227
+ ],
228
+ "InstallButton": [
229
+ "Button.css"
230
+ ],
231
+ "Kanban": [
232
+ "Kanban.css"
233
+ ],
234
+ "Kbd": [
235
+ "Kbd.css"
236
+ ],
237
+ "Label": [
238
+ "Label.css"
239
+ ],
240
+ "Lightbox": [
241
+ "Lightbox.css"
242
+ ],
243
+ "ListTile": [
244
+ "ListTile.css"
245
+ ],
246
+ "Markdown": [
247
+ "CodeBlock.css",
248
+ "CopyButton.css",
249
+ "Markdown.css"
250
+ ],
251
+ "Masonry": [
252
+ "Masonry.css"
253
+ ],
254
+ "Menubar": [
255
+ "Menubar.css"
256
+ ],
257
+ "Modal": [
258
+ "Modal.css"
259
+ ],
260
+ "ModalsProvider": [
261
+ "Button.css",
262
+ "Modal.css",
263
+ "ModalsManager.css"
264
+ ],
265
+ "MoneyInput": [
266
+ "Input.css"
267
+ ],
268
+ "MultiSelect": [
269
+ "MultiSelect.css"
270
+ ],
271
+ "NProgressBar": [
272
+ "NProgress.css"
273
+ ],
274
+ "Navbar": [
275
+ "Navbar.css"
276
+ ],
277
+ "NavigationMenu": [
278
+ "NavigationMenu.css"
279
+ ],
280
+ "NavigationRail": [
281
+ "NavigationRail.css"
282
+ ],
283
+ "NotificationCenter": [
284
+ "Badge.css",
285
+ "EmptyState.css",
286
+ "NotificationCenter.css"
287
+ ],
288
+ "OfflineIndicator": [
289
+ "OfflineIndicator.css"
290
+ ],
291
+ "Page": [
292
+ "Page.css"
293
+ ],
294
+ "Pagination": [
295
+ "Pagination.css"
296
+ ],
297
+ "PasswordInput": [
298
+ "PasswordInput.css"
299
+ ],
300
+ "PhoneInput": [
301
+ "Input.css"
302
+ ],
303
+ "PinInput": [
304
+ "PinInput.css"
305
+ ],
306
+ "Popover": [
307
+ "Popover.css"
308
+ ],
309
+ "Progress": [
310
+ "Progress.css"
311
+ ],
312
+ "QRCode": [
313
+ "QRCode.css"
314
+ ],
315
+ "Radio": [
316
+ "Radio.css"
317
+ ],
318
+ "RadioGroup": [
319
+ "Radio.css"
320
+ ],
321
+ "RangeSlider": [
322
+ "RangeSlider.css"
323
+ ],
324
+ "RatingStars": [
325
+ "RatingStars.css"
326
+ ],
327
+ "RefreshIndicator": [
328
+ "RefreshIndicator.css",
329
+ "Spinner.css"
330
+ ],
331
+ "Resizable": [
332
+ "Resizable.css"
333
+ ],
334
+ "SafeArea": [
335
+ "SafeArea.css"
336
+ ],
337
+ "Scheduler": [
338
+ "Scheduler.css"
339
+ ],
340
+ "ScrollArea": [
341
+ "ScrollArea.css"
342
+ ],
343
+ "SearchBar": [
344
+ "SearchBar.css"
345
+ ],
346
+ "SegmentedControl": [
347
+ "SegmentedControl.css"
348
+ ],
349
+ "Select": [
350
+ "Select.css"
351
+ ],
352
+ "Sidebar": [
353
+ "Sidebar.css"
354
+ ],
355
+ "SignaturePad": [
356
+ "Button.css",
357
+ "SignaturePad.css"
358
+ ],
359
+ "Skeleton": [
360
+ "Skeleton.css"
361
+ ],
362
+ "Slider": [
363
+ "Slider.css"
364
+ ],
365
+ "Spacer": [
366
+ "Spacer.css"
367
+ ],
368
+ "Sparkline": [
369
+ "Sparkline.css"
370
+ ],
371
+ "Spinner": [
372
+ "Spinner.css"
373
+ ],
374
+ "Stack": [
375
+ "Layout.css"
376
+ ],
377
+ "Stat": [
378
+ "Stat.css"
379
+ ],
380
+ "Stepper": [
381
+ "Stepper.css"
382
+ ],
383
+ "StepperInput": [
384
+ "StepperInput.css"
385
+ ],
386
+ "Switch": [
387
+ "Switch.css"
388
+ ],
389
+ "SyncStatusBadge": [
390
+ "SyncStatusBadge.css",
391
+ "VisuallyHidden.css"
392
+ ],
393
+ "Table": [
394
+ "Table.css"
395
+ ],
396
+ "Tabs": [
397
+ "Tabs.css"
398
+ ],
399
+ "Tag": [
400
+ "Tag.css"
401
+ ],
402
+ "Textarea": [
403
+ "Textarea.css"
404
+ ],
405
+ "TimePicker": [
406
+ "TimePicker.css"
407
+ ],
408
+ "Timeline": [
409
+ "Timeline.css"
410
+ ],
411
+ "ToastProvider": [
412
+ "Toast.css"
413
+ ],
414
+ "Toggle": [
415
+ "Toggle.css"
416
+ ],
417
+ "ToggleGroup": [
418
+ "ToggleGroup.css"
419
+ ],
420
+ "ToggleGroupItem": [
421
+ "ToggleGroup.css"
422
+ ],
423
+ "Tooltip": [
424
+ "Tooltip.css"
425
+ ],
426
+ "Tour": [
427
+ "Tour.css"
428
+ ],
429
+ "TrajectoryMap": [
430
+ "TrajectoryMap.css"
431
+ ],
432
+ "Transfer": [
433
+ "Checkbox.css",
434
+ "Transfer.css",
435
+ "VisuallyHidden.css"
436
+ ],
437
+ "TreeView": [
438
+ "TreeView.css"
439
+ ],
440
+ "TruncateText": [
441
+ "TruncateText.css"
442
+ ],
443
+ "UpdatePrompt": [
444
+ "UpdatePrompt.css"
445
+ ],
446
+ "VideoPlayer": [
447
+ "VideoPlayer.css"
448
+ ],
449
+ "VirtualList": [
450
+ "VirtualList.css"
451
+ ],
452
+ "VirtualTable": [
453
+ "VirtualTable.css"
454
+ ],
455
+ "VisuallyHidden": [
456
+ "VisuallyHidden.css"
457
+ ],
458
+ "Wizard": [
459
+ "Button.css",
460
+ "Stepper.css",
461
+ "Wizard.css"
462
+ ],
463
+ "applyKanbanMove": [
464
+ "Kanban.css"
465
+ ],
466
+ "estimatePasswordStrength": [
467
+ "PasswordInput.css"
468
+ ],
469
+ "nprogress": [
470
+ "NProgress.css"
471
+ ],
472
+ "useFieldArray": [
473
+ "Input.css"
474
+ ],
475
+ "useForm": [
476
+ "Input.css"
477
+ ],
478
+ "useFormContext": [
479
+ "Input.css"
480
+ ],
481
+ "useFormState": [
482
+ "Input.css"
483
+ ],
484
+ "useModals": [
485
+ "Button.css",
486
+ "Modal.css",
487
+ "ModalsManager.css"
488
+ ],
489
+ "useToast": [
490
+ "Toast.css"
491
+ ],
492
+ "useWatch": [
493
+ "Input.css"
494
+ ]
495
+ }
496
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Scoped reset — generated by scripts/gen-scoped-reset.mjs. Do not edit.
3
+ *
4
+ * The rules of `reset.css` that dress the elements a Tempest component renders,
5
+ * republished under `:where([class*="tempest_"])` so they reach inside a component
6
+ * and nowhere else. Import this instead of `reset.css` when the app owns its own
7
+ * document styling: the components keep the box model, button and control
8
+ * normalisation they are written against, and `html`, `body` and `#root` are left
9
+ * to the app.
10
+ *
11
+ * `:where()` contributes no specificity, so every selector here weighs what it
12
+ * weighed globally.
13
+ */
14
+
15
+ :where([class*="tempest_"]),
16
+ :where([class*="tempest_"]) *,
17
+ :where([class*="tempest_"]) *::before,
18
+ :where([class*="tempest_"]) *::after {
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ :where([class*="tempest_"]) img,
23
+ img:where([class*="tempest_"]),
24
+ :where([class*="tempest_"]) svg,
25
+ svg:where([class*="tempest_"]),
26
+ :where([class*="tempest_"]) video,
27
+ video:where([class*="tempest_"]),
28
+ :where([class*="tempest_"]) canvas,
29
+ canvas:where([class*="tempest_"]),
30
+ :where([class*="tempest_"]) audio,
31
+ audio:where([class*="tempest_"]),
32
+ :where([class*="tempest_"]) iframe,
33
+ iframe:where([class*="tempest_"]),
34
+ :where([class*="tempest_"]) embed,
35
+ embed:where([class*="tempest_"]),
36
+ :where([class*="tempest_"]) object,
37
+ object:where([class*="tempest_"]) {
38
+ display: block;
39
+ max-width: 100%;
40
+ }
41
+
42
+ :where([class*="tempest_"]) img,
43
+ img:where([class*="tempest_"]),
44
+ :where([class*="tempest_"]) video,
45
+ video:where([class*="tempest_"]) {
46
+ height: auto;
47
+ }
48
+
49
+ :where([class*="tempest_"]) :where(button, a, label, summary) > svg:only-child {
50
+ margin-inline: auto;
51
+ }
52
+
53
+ :where([class*="tempest_"]) button,
54
+ button:where([class*="tempest_"]),
55
+ :where([class*="tempest_"]) input,
56
+ input:where([class*="tempest_"]),
57
+ :where([class*="tempest_"]) select,
58
+ select:where([class*="tempest_"]),
59
+ :where([class*="tempest_"]) textarea,
60
+ textarea:where([class*="tempest_"]) {
61
+ font-family: inherit;
62
+ font-size: inherit;
63
+ line-height: inherit;
64
+ color: inherit;
65
+ letter-spacing: inherit;
66
+ }
67
+
68
+ :where([class*="tempest_"]) button,
69
+ button:where([class*="tempest_"]) {
70
+ cursor: pointer;
71
+ background: none;
72
+ border: 0;
73
+ padding: 0;
74
+ }
75
+
76
+ :where([class*="tempest_"]) button:disabled,
77
+ button:disabled:where([class*="tempest_"]) {
78
+ cursor: not-allowed;
79
+ }
80
+
81
+ :where([class*="tempest_"]) button::-moz-focus-inner,
82
+ button:where([class*="tempest_"])::-moz-focus-inner {
83
+ border: 0;
84
+ padding: 0;
85
+ }
86
+
87
+ :where([class*="tempest_"]) textarea,
88
+ textarea:where([class*="tempest_"]) {
89
+ resize: vertical;
90
+ }
91
+
92
+ :where([class*="tempest_"]) input[type="search"]::-webkit-search-decoration,
93
+ input[type="search"]:where([class*="tempest_"])::-webkit-search-decoration,
94
+ :where([class*="tempest_"]) input[type="search"]::-webkit-search-cancel-button,
95
+ input[type="search"]:where([class*="tempest_"])::-webkit-search-cancel-button,
96
+ :where([class*="tempest_"]) input[type="search"]::-webkit-search-results-button,
97
+ input[type="search"]:where([class*="tempest_"])::-webkit-search-results-button,
98
+ :where([class*="tempest_"]) input[type="search"]::-webkit-search-results-decoration,
99
+ input[type="search"]:where([class*="tempest_"])::-webkit-search-results-decoration {
100
+ -webkit-appearance: none;
101
+ }
102
+
103
+ :where([class*="tempest_"]) table,
104
+ table:where([class*="tempest_"]) {
105
+ border-collapse: collapse;
106
+ border-spacing: 0;
107
+ }
108
+
109
+ :where([class*="tempest_"]) :where(ul, ol)[class] {
110
+ list-style: none;
111
+ margin: 0;
112
+ padding: 0;
113
+ }
114
+
115
+ :where([class*="tempest_"]) :focus-visible,
116
+ :focus-visible:where([class*="tempest_"]) {
117
+ outline: var(--tempest-focus-ring-width, 3px) solid
118
+ var(--tempest-focus-ring-color, rgba(0, 102, 255, 0.35));
119
+ outline-offset: var(--tempest-focus-ring-offset, 2px);
120
+ }
121
+
122
+ @media (prefers-reduced-motion: reduce) {
123
+ :where([class*="tempest_"]) *,
124
+ :where([class*="tempest_"]) *::before,
125
+ :where([class*="tempest_"]) *::after {
126
+ animation-duration: 0.01ms !important;
127
+ animation-iteration-count: 1 !important;
128
+ transition-duration: 0.01ms !important;
129
+ scroll-behavior: auto !important;
130
+ }
131
+ }
@@ -0,0 +1,10 @@
1
+ /* Generated by scripts/split-css.mjs — do not edit. */
2
+ :root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--tempest-primary-50:#eef4ff;--tempest-primary-100:#d9e6ff;--tempest-primary-200:#b3ccff;--tempest-primary-300:#80aaff;--tempest-primary-400:#4d85ff;--tempest-primary-500:#06f;--tempest-primary-600:#0052cc;--tempest-primary-700:#003d99;--tempest-primary-800:#002e75;--tempest-primary-900:#001f4d;--tempest-primary:var(--tempest-primary-500);--tempest-primary-hover:var(--tempest-primary-600);--tempest-primary-active:var(--tempest-primary-700);--tempest-primary-soft:var(--tempest-primary-50);--tempest-primary-soft-hover:var(--tempest-primary-100);--tempest-primary-foreground:#fff;--tempest-danger-on-solid:#fff;--tempest-info-on-solid:#fff;--tempest-success-on-solid:#1f0606;--tempest-warning-on-solid:#1f0606;--tempest-neutral-on-solid:#fff;--tempest-primary-on-soft:var(--tempest-primary-600);--tempest-gray-50:#f8f9fb;--tempest-gray-100:#f1f3f6;--tempest-gray-200:#e4e7ec;--tempest-gray-300:#d0d5dd;--tempest-gray-400:#98a2b3;--tempest-gray-500:#667085;--tempest-gray-600:#475467;--tempest-gray-700:#344054;--tempest-gray-800:#1d2939;--tempest-gray-900:#101828;--tempest-bg:#fff;--tempest-surface:var(--tempest-gray-50);--tempest-surface-2:var(--tempest-gray-100);--tempest-surface-3:var(--tempest-gray-200);--tempest-border:var(--tempest-gray-200);--tempest-border-strong:var(--tempest-gray-300);--tempest-text:var(--tempest-gray-900);--tempest-text-muted:var(--tempest-gray-600);--tempest-text-subtle:var(--tempest-gray-500);--tempest-text-on-primary:#fff;--tempest-success:#15803d;--tempest-success-fg:#14532d;--tempest-success-bg:#dcfce7;--tempest-success-border:#86efac;--tempest-success-solid:#16a34a;--tempest-warning:#b45309;--tempest-warning-fg:#78350f;--tempest-warning-bg:#fef3c7;--tempest-warning-border:#fcd34d;--tempest-warning-solid:#d97706;--tempest-danger:#b91c1c;--tempest-danger-fg:#7f1d1d;--tempest-danger-bg:#fee2e2;--tempest-danger-border:#fca5a5;--tempest-danger-solid:#dc2626;--tempest-danger-hover:#991b1b;--tempest-info:#1d4ed8;--tempest-info-fg:#1e3a8a;--tempest-info-bg:#dbeafe;--tempest-info-border:#93c5fd;--tempest-info-solid:#2563eb;--tempest-focus-ring-color:#0066ff59;--tempest-focus-ring-width:3px;--tempest-focus-ring-offset:2px;--tempest-radius-xs:2px;--tempest-radius-sm:4px;--tempest-radius-md:8px;--tempest-radius-lg:12px;--tempest-radius-xl:16px;--tempest-radius-2xl:24px;--tempest-radius-full:9999px;--tempest-shadow-xs:0 1px 2px #0f172a0a;--tempest-shadow-sm:0 1px 3px #0f172a14, 0 1px 2px #0f172a0a;--tempest-shadow-md:0 4px 12px #0f172a14, 0 2px 4px #0f172a0a;--tempest-shadow-lg:0 12px 32px #0f172a1f, 0 4px 8px #0f172a0f;--tempest-shadow-xl:0 24px 48px #0f172a2e, 0 8px 16px #0f172a14;--tempest-shadow-inner:inset 0 2px 4px #0f172a0f;--tempest-space-0:0px;--tempest-space-1:4px;--tempest-space-2:8px;--tempest-space-3:12px;--tempest-space-4:16px;--tempest-space-5:20px;--tempest-space-6:24px;--tempest-space-7:28px;--tempest-space-8:32px;--tempest-space-10:40px;--tempest-space-12:48px;--tempest-space-16:64px;--tempest-space-20:80px;--tempest-space-24:96px;--tempest-font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif;--tempest-font-mono:ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;--tempest-font-display:var(--tempest-font-sans);--tempest-z-base:0;--tempest-z-raised:10;--tempest-z-dropdown:1000;--tempest-z-sticky:1020;--tempest-z-overlay:1050;--tempest-z-modal:1100;--tempest-z-popover:1150;--tempest-z-toast:1200;--tempest-z-tooltip:1300;--tempest-bp-xs:480px;--tempest-bp-sm:640px;--tempest-bp-md:768px;--tempest-bp-lg:1024px;--tempest-bp-xl:1280px;--tempest-bp-2xl:1536px;--tempest-chart-1:#2563eb;--tempest-chart-2:#16a34a;--tempest-chart-3:#f59e0b;--tempest-chart-4:#7c3aed;--tempest-chart-5:#ec4899;--tempest-chart-6:#06b6d4;--tempest-chart-7:#ea580c;--tempest-chart-8:#0f766e;--tempest-chart-sequential-1:#dde8fe;--tempest-chart-sequential-2:#acc8fe;--tempest-chart-sequential-3:#7aa7ff;--tempest-chart-sequential-4:#4983fc;--tempest-chart-sequential-5:#3667ce;--tempest-chart-sequential-6:#2b4e96;--tempest-chart-sequential-7:#25375a;--tempest-chart-diverging-1:#0f3ca1;--tempest-chart-diverging-2:#416eca;--tempest-chart-diverging-3:#7ba0e7;--tempest-chart-diverging-4:#bdd2f9;--tempest-chart-diverging-5:#e4e7ec;--tempest-chart-diverging-6:#f6c3bc;--tempest-chart-diverging-7:#de847a;--tempest-chart-diverging-8:#ba473f;--tempest-chart-diverging-9:#870f11;--tempest-chart-grid:var(--tempest-gray-200);--tempest-chart-axis:var(--tempest-gray-500);--tempest-code-comment:#5f666e;--tempest-code-punctuation:#5d6671;--tempest-code-string:#327243;--tempest-code-number:#9d5035;--tempest-code-keyword:#625baf;--tempest-code-literal:#9f4c4f;--tempest-code-function:#1a6c94;--tempest-code-tag:#974d72;--tempest-code-attribute:#127171;--tempest-code-property:#127171;--tempest-safe-area-top:env(safe-area-inset-top,0px);--tempest-safe-area-right:env(safe-area-inset-right,0px);--tempest-safe-area-bottom:env(safe-area-inset-bottom,0px);--tempest-safe-area-left:env(safe-area-inset-left,0px)}
3
+ [data-tempest-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--tempest-primary-50:#0a1730;--tempest-primary-100:#0f2245;--tempest-primary-200:#133066;--tempest-primary-300:#1a4399;--tempest-primary-400:#2563eb;--tempest-primary-500:#3b82f6;--tempest-primary-600:#60a5fa;--tempest-primary-700:#93c5fd;--tempest-primary-800:#bfdbfe;--tempest-primary-900:#dbeafe;--tempest-primary:var(--tempest-primary-500);--tempest-primary-hover:var(--tempest-primary-600);--tempest-primary-active:var(--tempest-primary-700);--tempest-primary-soft:var(--tempest-primary-100);--tempest-primary-soft-hover:var(--tempest-primary-200);--tempest-primary-on-soft:var(--tempest-primary-700);--tempest-bg:#0b0d12;--tempest-surface:#14171f;--tempest-surface-2:#1d2230;--tempest-surface-3:#262d3f;--tempest-border:#2a3142;--tempest-border-strong:#3a4258;--tempest-text:#f1f3f8;--tempest-text-muted:#aab2c0;--tempest-text-subtle:#7b849a;--tempest-primary-foreground:#1f0606;--tempest-text-on-primary:var(--tempest-primary-foreground);--tempest-danger-on-solid:#1f0606;--tempest-info-on-solid:#1f0606;--tempest-success-on-solid:#1f0606;--tempest-warning-on-solid:#1f0606;--tempest-neutral-on-solid:#fff;--tempest-success-fg:#4ade80;--tempest-success-bg:#052e16;--tempest-success-border:#166534;--tempest-success-solid:#22c55e;--tempest-success:#4ade80;--tempest-warning-fg:#fbbf24;--tempest-warning-bg:#2c1c05;--tempest-warning-border:#78350f;--tempest-warning-solid:#f59e0b;--tempest-warning:#fbbf24;--tempest-danger-fg:#f87171;--tempest-danger-bg:#2c0a0a;--tempest-danger-border:#7f1d1d;--tempest-danger-solid:#ef4444;--tempest-danger:#f87171;--tempest-danger-hover:#f87171;--tempest-info-fg:#60a5fa;--tempest-info-bg:#0a1730;--tempest-info-border:#1e3a8a;--tempest-info-solid:#3b82f6;--tempest-info:#60a5fa;--tempest-focus-ring-color:#60a5fa73;--tempest-shadow-xs:0 1px 2px #0000004d;--tempest-shadow-sm:0 1px 3px #0006, 0 1px 2px #0003;--tempest-shadow-md:0 4px 12px #0006, 0 2px 4px #0003;--tempest-shadow-lg:0 12px 32px #00000080, 0 4px 8px #0000004d;--tempest-shadow-xl:0 24px 48px #0009, 0 8px 16px #0000004d;--tempest-chart-1:#60a5fa;--tempest-chart-2:#4ade80;--tempest-chart-3:#fbbf24;--tempest-chart-4:#a78bfa;--tempest-chart-5:#f472b6;--tempest-chart-6:#22d3ee;--tempest-chart-7:#fb923c;--tempest-chart-8:#2dd4bf;--tempest-chart-sequential-1:#172849;--tempest-chart-sequential-2:#1b3c83;--tempest-chart-sequential-3:#2453b9;--tempest-chart-sequential-4:#376fe5;--tempest-chart-sequential-5:#5b90fb;--tempest-chart-sequential-6:#8cb2fa;--tempest-chart-sequential-7:#bad2fe;--tempest-chart-diverging-1:#1543a8;--tempest-chart-diverging-2:#416eca;--tempest-chart-diverging-3:#7599e0;--tempest-chart-diverging-4:#b0c5ec;--tempest-chart-diverging-5:#262d3f;--tempest-chart-diverging-6:#e8b6af;--tempest-chart-diverging-7:#d77e74;--tempest-chart-diverging-8:#ba473f;--tempest-chart-diverging-9:#94020c;--tempest-chart-grid:var(--tempest-gray-300);--tempest-chart-axis:var(--tempest-gray-600);--tempest-code-comment:#838b93;--tempest-code-punctuation:#818b95;--tempest-code-string:#589866;--tempest-code-number:#c57558;--tempest-code-keyword:#8580d8;--tempest-code-literal:#c87172;--tempest-code-function:#2893c6;--tempest-code-tag:#c07197;--tempest-code-attribute:#349897;--tempest-code-property:#349897}
4
+ :root{--tempest-text-2xs:10px;--tempest-text-xs:12px;--tempest-text-sm:13px;--tempest-text-base:14px;--tempest-text-md:15px;--tempest-text-lg:16px;--tempest-text-xl:18px;--tempest-text-2xl:20px;--tempest-text-3xl:24px;--tempest-text-4xl:30px;--tempest-text-5xl:36px;--tempest-text-6xl:48px;--tempest-leading-none:1;--tempest-leading-tight:1.2;--tempest-leading-snug:1.35;--tempest-leading-normal:1.5;--tempest-leading-relaxed:1.65;--tempest-leading-loose:1.9;--tempest-weight-regular:400;--tempest-weight-medium:500;--tempest-weight-semibold:600;--tempest-weight-bold:700;--tempest-weight-extrabold:800;--tempest-tracking-tight:-.02em;--tempest-tracking-normal:0;--tempest-tracking-wide:.025em;--tempest-tracking-wider:.05em;--tempest-tracking-widest:.1em;--tempest-text-fluid-sm:clamp(13px, 12px + .2vw, 15px);--tempest-text-fluid-base:clamp(14px, 13px + .3vw, 16px);--tempest-text-fluid-lg:clamp(16px, 14px + .5vw, 20px);--tempest-text-fluid-xl:clamp(18px, 15px + .8vw, 24px);--tempest-text-fluid-2xl:clamp(20px, 16px + 1.2vw, 30px);--tempest-text-fluid-3xl:clamp(24px, 18px + 2vw, 40px);--tempest-text-fluid-4xl:clamp(28px, 20px + 3vw, 56px);--tempest-text-fluid-5xl:clamp(32px, 24px + 4vw, 72px);--tempest-text-fluid-6xl:clamp(40px, 28px + 5vw, 96px);--tempest-duration-instant:0s;--tempest-duration-fast:.12s;--tempest-duration-base:.18s;--tempest-duration-slow:.28s;--tempest-duration-slower:.42s;--tempest-ease-linear:linear;--tempest-ease-in:cubic-bezier(.4, 0, 1, 1);--tempest-ease-out:cubic-bezier(0, 0, .2, 1);--tempest-ease-in-out:cubic-bezier(.4, 0, .2, 1);--tempest-ease-emphasized:cubic-bezier(.2, 0, 0, 1);--tempest-ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--tempest-transition-color:color var(--tempest-duration-fast) var(--tempest-ease-out), background-color var(--tempest-duration-fast) var(--tempest-ease-out), border-color var(--tempest-duration-fast) var(--tempest-ease-out);--tempest-transition-shadow:box-shadow var(--tempest-duration-base) var(--tempest-ease-out);--tempest-transition-transform:transform var(--tempest-duration-fast) var(--tempest-ease-out);--tempest-transition-base:color var(--tempest-duration-fast) var(--tempest-ease-out), background-color var(--tempest-duration-fast) var(--tempest-ease-out), border-color var(--tempest-duration-fast) var(--tempest-ease-out), box-shadow var(--tempest-duration-base) var(--tempest-ease-out), transform var(--tempest-duration-fast) var(--tempest-ease-out), opacity var(--tempest-duration-fast) var(--tempest-ease-out)}
5
+ @media (prefers-reduced-motion:reduce){:root{--tempest-duration-fast:.01ms;--tempest-duration-base:.01ms;--tempest-duration-slow:.01ms;--tempest-duration-slower:.01ms}}
6
+ :root,[data-tempest-density=comfortable]{--tempest-control-height-xs:24px;--tempest-control-height-sm:32px;--tempest-control-height-md:40px;--tempest-control-height-lg:48px;--tempest-control-height-xl:56px;--tempest-control-padding-xs:var(--tempest-space-2);--tempest-control-padding-sm:var(--tempest-space-3);--tempest-control-padding-md:var(--tempest-space-4);--tempest-control-padding-lg:var(--tempest-space-5);--tempest-control-padding-xl:var(--tempest-space-6);--tempest-control-font-xs:var(--tempest-text-xs);--tempest-control-font-sm:var(--tempest-text-sm);--tempest-control-font-md:var(--tempest-text-base);--tempest-control-font-lg:var(--tempest-text-lg);--tempest-control-font-xl:var(--tempest-text-xl);--tempest-control-radius:var(--tempest-radius-md);--tempest-control-gap:var(--tempest-space-2)}
7
+ [data-tempest-density=compact]{--tempest-control-height-xs:20px;--tempest-control-height-sm:28px;--tempest-control-height-md:34px;--tempest-control-height-lg:40px;--tempest-control-height-xl:48px;--tempest-control-padding-xs:var(--tempest-space-1);--tempest-control-padding-sm:var(--tempest-space-2);--tempest-control-padding-md:var(--tempest-space-3);--tempest-control-padding-lg:var(--tempest-space-4);--tempest-control-padding-xl:var(--tempest-space-5);--tempest-control-font-xs:var(--tempest-text-2xs);--tempest-control-font-sm:var(--tempest-text-xs);--tempest-control-font-md:var(--tempest-text-sm);--tempest-control-font-lg:var(--tempest-text-base);--tempest-control-font-xl:var(--tempest-text-md);--tempest-control-radius:var(--tempest-radius-sm);--tempest-control-gap:var(--tempest-space-1)}
8
+ [data-tempest-density=touch]{--tempest-control-height-xs:32px;--tempest-control-height-sm:40px;--tempest-control-height-md:44px;--tempest-control-height-lg:52px;--tempest-control-height-xl:60px;--tempest-control-padding-xs:var(--tempest-space-2);--tempest-control-padding-sm:var(--tempest-space-3);--tempest-control-padding-md:var(--tempest-space-4);--tempest-control-padding-lg:var(--tempest-space-5);--tempest-control-padding-xl:var(--tempest-space-6);--tempest-control-font-xs:var(--tempest-text-xs);--tempest-control-font-sm:var(--tempest-text-sm);--tempest-control-font-md:var(--tempest-text-base);--tempest-control-font-lg:var(--tempest-text-lg);--tempest-control-font-xl:var(--tempest-text-xl);--tempest-control-radius:var(--tempest-radius-md);--tempest-control-gap:var(--tempest-space-2)}
9
+ [data-tempest-density=spacious]{--tempest-control-height-xs:28px;--tempest-control-height-sm:36px;--tempest-control-height-md:44px;--tempest-control-height-lg:52px;--tempest-control-height-xl:64px;--tempest-control-padding-xs:var(--tempest-space-3);--tempest-control-padding-sm:var(--tempest-space-4);--tempest-control-padding-md:var(--tempest-space-5);--tempest-control-padding-lg:var(--tempest-space-6);--tempest-control-padding-xl:var(--tempest-space-7);--tempest-control-font-xs:var(--tempest-text-xs);--tempest-control-font-sm:var(--tempest-text-base);--tempest-control-font-md:var(--tempest-text-md);--tempest-control-font-lg:var(--tempest-text-xl);--tempest-control-font-xl:var(--tempest-text-2xl);--tempest-control-radius:var(--tempest-radius-lg);--tempest-control-gap:var(--tempest-space-3)}
10
+ @media (pointer:coarse){:root:not([data-tempest-density=compact]){--tempest-control-height-xs:32px;--tempest-control-height-sm:40px;--tempest-control-height-md:44px}}
@@ -0,0 +1,5 @@
1
+ let e=require("node:path"),t=require("node:fs/promises"),n=require("node:fs"),r=require("node:url");var i=`tempest-react-sdk/styles/auto.css`,a=`\0tempest-styles/auto.css`,o=/^tempest-react-sdk(\/(charts|editor|br|icons))?$/,s=/\.[cm]?[jt]sx?$/,c=new Set([`node_modules`,`dist`,`build`,`coverage`]);function l(e){let t=new Set;for(let n of e.matchAll(/\bimport\s+(type\s+)?([^;'"]*?)\s+from\s*["']([^"']+)["']/g)){let[,e,r,i]=n;if(!o.test(i??``)||e)continue;if(/^\s*\*\s+as\s/.test(r??``))return null;let a=/\{([^}]*)\}/.exec(r??``);if(a)for(let e of(a[1]??``).split(`,`)){let n=e.trim();if(!n||n.startsWith(`type `))continue;let r=n.split(/\s+as\s+/)[0]?.trim();r&&t.add(r)}}return[...t]}function u(e,t,n=`scoped`){let r=`/* Generated by tempestStyles() — the CSS this app actually uses. */
2
+ `,i=n===`global`?[t.core]:n===`scoped`?[`tokens.css`,`scoped.css`]:[`tokens.css`];if(e===null)return`${r}${(n===`global`?[`../styles.css`]:[...i,`../styles.css`]).map(e=>`@import "tempest-react-sdk/styles/${e}";`).join(`
3
+ `)}\n`;let a=new Set;for(let n of e)for(let e of t.components[n]??[])a.add(e);return`${r}${[...i,...[...a].sort()].map(e=>`@import "tempest-react-sdk/styles/${e}";`).join(`
4
+ `)}\n`}async function d(n,r){let i=[],a;try{a=await(0,t.readdir)(n,{withFileTypes:!0})}catch{return i}for(let t of a)if(t.isDirectory()){if(r.has(t.name)||t.name.startsWith(`.`))continue;i.push(...await d((0,e.join)(n,t.name),r))}else t.isFile()&&s.test(t.name)&&i.push((0,e.join)(n,t.name));return i}function f(t){let i=(0,e.join)(`tempest-react-sdk`,`dist`,`styles`,`manifest.json`),a=[],o=(0,e.resolve)(t);for(;;){a.push((0,e.join)(o,`node_modules`,i));let t=(0,e.dirname)(o);if(t===o)break;o=t}a.push((0,e.join)((0,e.dirname)((0,r.fileURLToPath)({}.url)),`..`,`styles`,`manifest.json`));for(let e of a)try{return JSON.parse((0,n.readFileSync)(e,`utf8`))}catch{continue}throw Error(`tempestStyles(): tempest-react-sdk/dist/styles/manifest.json not found — the installed SDK predates it. Upgrade, or drop the plugin and import tempest-react-sdk/styles.css.`)}function p(n={}){let{dir:r=`src`,include:i=[],skipDirs:o,reset:s=`scoped`}=n,p=new Set(o??c),m=process.cwd(),h=[...i],g=async()=>{let n=new Set(i);for(let i of await d((0,e.resolve)(m,r),p)){let e;try{e=await(0,t.readFile)(i,`utf8`)}catch{continue}let r=l(e);if(r===null){h=null;return}for(let e of r)n.add(e)}h=[...n]};return{name:`tempest-styles`,enforce:`pre`,configResolved(e){m=e.root??m},async buildStart(){await g()},resolveId(e){return e===`tempest-react-sdk/styles/auto.css`?a:null},load(e){return e===a?u(h,f(m),s):null}}}exports.TEMPEST_STYLES_ID=i,exports.buildStyleEntry=u,exports.scanStyleImports=l,exports.tempestStyles=p;
5
+ //# sourceMappingURL=tempest-styles.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tempest-styles.cjs","names":[],"sources":["../../src/vite/tempest-styles.ts"],"sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** The stylesheet id an app imports to get exactly the CSS it uses. */\nexport const TEMPEST_STYLES_ID = \"tempest-react-sdk/styles/auto.css\";\n\nconst RESOLVED_ID = \"\\0tempest-styles/auto.css\";\n\n/** Package subpaths whose exports are components with stylesheets. */\nconst SDK_SPECIFIER = /^tempest-react-sdk(\\/(charts|editor|br|icons))?$/;\n\nconst SOURCE_FILE = /\\.[cm]?[jt]sx?$/;\nconst SKIP_DIRS = new Set([\"node_modules\", \"dist\", \"build\", \"coverage\"]);\n\n/**\n * How much of the reset an app takes.\n *\n * The components are written against the reset — `.tempest_button` assumes\n * `button { background: none; border: 0; padding: 0 }`, every width assumes\n * `box-sizing: border-box` — so `\"none\"` is not \"unstyled document, styled\n * components\": it is components missing their box model. It exists for the app\n * that already ships an equivalent reset of its own.\n */\nexport type ResetMode = \"scoped\" | \"global\" | \"none\";\n\n/** Shape of `dist/styles/manifest.json`. */\nexport interface StyleManifest {\n /** The foundation sheet — tokens and reset together, the pre-split default. */\n core: string;\n /** Public export name → the stylesheets it needs, transitive closure included. */\n components: Record<string, readonly string[]>;\n}\n\nexport interface TempestStylesOptions {\n /** Directory to scan, relative to the Vite root. Default: `\"src\"`. */\n dir?: string;\n /**\n * Which reset to emit alongside the tokens. Default: `\"scoped\"`.\n *\n * `\"scoped\"` confines the reset to `:where([class*=\"tempest_\"])`, so components\n * keep the normalisation they are written against and the app keeps `html`,\n * `body` and `#root`. `\"global\"` is the pre-split behaviour, correct when the\n * SDK owns the whole page. `\"none\"` emits tokens only.\n */\n reset?: ResetMode;\n /**\n * Extra export names to style even when the scan cannot see them — a component\n * reached only through a namespace import, or rendered by a sibling package.\n */\n include?: readonly string[];\n /** Directory names to skip. Default: `node_modules`, `dist`, `build`, `coverage`. */\n skipDirs?: readonly string[];\n}\n\n/**\n * Collect the SDK export names a source file imports.\n *\n * Reads the import specifier rather than the JSX, because a bare `<Button>` in the\n * markup says nothing about where it came from — the app may well define its own.\n * Type-only bindings are dropped: they are erased before any component renders, so\n * paying for their CSS would be paying for nothing.\n *\n * @param code - File contents.\n * @returns The imported names, or `null` when the file namespace-imports the SDK\n * and the set therefore cannot be known.\n */\nexport function scanStyleImports(code: string): string[] | null {\n const names = new Set<string>();\n\n for (const match of code.matchAll(\n /\\bimport\\s+(type\\s+)?([^;'\"]*?)\\s+from\\s*[\"']([^\"']+)[\"']/g,\n )) {\n const [, typeOnly, clause, specifier] = match;\n if (!SDK_SPECIFIER.test(specifier ?? \"\")) continue;\n if (typeOnly) continue;\n if (/^\\s*\\*\\s+as\\s/.test(clause ?? \"\")) return null;\n\n const braces = /\\{([^}]*)\\}/.exec(clause ?? \"\");\n if (!braces) continue;\n for (const part of (braces[1] ?? \"\").split(\",\")) {\n const binding = part.trim();\n if (!binding || binding.startsWith(\"type \")) continue;\n const name = binding.split(/\\s+as\\s+/)[0]?.trim();\n if (name) names.add(name);\n }\n }\n return [...names];\n}\n\n/**\n * Build the stylesheet for a set of export names.\n *\n * Emits `@import` statements rather than inlined rules so Vite's own CSS pipeline\n * resolves, deduplicates and minifies them — the same treatment a hand-written\n * list of imports gets, which is what this replaces.\n *\n * @param names - Export names the app imports, or `null` to take everything.\n * @param manifest - The published style manifest.\n * @returns CSS source.\n */\nexport function buildStyleEntry(\n names: readonly string[] | null,\n manifest: StyleManifest,\n reset: ResetMode = \"scoped\",\n): string {\n const banner = \"/* Generated by tempestStyles() — the CSS this app actually uses. */\\n\";\n const foundation =\n reset === \"global\"\n ? [manifest.core]\n : reset === \"scoped\"\n ? [\"tokens.css\", \"scoped.css\"]\n : [\"tokens.css\"];\n\n if (names === null) {\n const whole = reset === \"global\" ? [\"../styles.css\"] : [...foundation, \"../styles.css\"];\n return `${banner}${whole.map((sheet) => `@import \"tempest-react-sdk/styles/${sheet}\";`).join(\"\\n\")}\\n`;\n }\n\n const sheets = new Set<string>();\n for (const name of names) {\n for (const sheet of manifest.components[name] ?? []) sheets.add(sheet);\n }\n const lines = [...foundation, ...[...sheets].sort()].map(\n (sheet) => `@import \"tempest-react-sdk/styles/${sheet}\";`,\n );\n return `${banner}${lines.join(\"\\n\")}\\n`;\n}\n\n/**\n * Walk a directory and collect every source file path.\n *\n * @param dir - Absolute directory to walk.\n * @param skip - Directory names to skip.\n * @returns Absolute file paths.\n */\nasync function collectSourceFiles(dir: string, skip: ReadonlySet<string>): Promise<string[]> {\n const out: string[] = [];\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (skip.has(entry.name) || entry.name.startsWith(\".\")) continue;\n out.push(...(await collectSourceFiles(join(dir, entry.name), skip)));\n } else if (entry.isFile() && SOURCE_FILE.test(entry.name)) {\n out.push(join(dir, entry.name));\n }\n }\n return out;\n}\n\n/**\n * Read the style manifest the installed package ships.\n *\n * Resolved by walking the filesystem rather than through `createRequire`, which\n * does not survive the trip: Vite bundles `vite.config.ts` into a temporary module\n * before running it, and in that bundle the `node:module` namespace import comes\n * back without the function — `(0, a.createRequire) is not a function`, at load\n * time, inside the build. The same rewrite is why `import.meta.url` is only the\n * fallback here and the app's `node_modules` is tried first.\n *\n * @param root - The Vite root, where the walk for `node_modules` starts.\n * @returns The manifest.\n * @throws If no installed copy carries one — an SDK older than the manifest.\n */\nfunction readManifest(root: string): StyleManifest {\n const relative = join(\"tempest-react-sdk\", \"dist\", \"styles\", \"manifest.json\");\n const candidates: string[] = [];\n\n let dir = resolve(root);\n for (;;) {\n candidates.push(join(dir, \"node_modules\", relative));\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n candidates.push(join(dirname(fileURLToPath(import.meta.url)), \"..\", \"styles\", \"manifest.json\"));\n\n for (const candidate of candidates) {\n try {\n return JSON.parse(readFileSync(candidate, \"utf8\")) as StyleManifest;\n } catch {\n continue;\n }\n }\n throw new Error(\n \"tempestStyles(): tempest-react-sdk/dist/styles/manifest.json not found — \" +\n \"the installed SDK predates it. Upgrade, or drop the plugin and import \" +\n \"tempest-react-sdk/styles.css.\",\n );\n}\n\n/**\n * Import only the SDK stylesheets your app can actually reach.\n *\n * `styles.css` carries every component the SDK ships — measured at 236.71 kB raw\n * against the 38.94 kB an app mounting twelve of them can reach. The per-component\n * sheets under `styles/` have always closed that gap, but by hand: the app lists\n * them and keeps the list honest as it grows. This plugin makes the list the build's\n * problem, resolving it from the imports the source already writes.\n *\n * The closure matters more than the scan. A component pays for the CSS of every SDK\n * component it renders internally, so `<DataTable>` alone needs six sheets and\n * `<AIChat>` seven — a hand-kept list of one sheet per component the app names is\n * wrong in a way that only shows up as an unstyled child.\n *\n * The reset is the other half, and the half that breaks apps. `styles.css` claims\n * `html`, `body` and `#root`; an app with its own layout that imports it loses its\n * document surface, and an app that drops the import loses the box model its\n * components are written against — components render unstyled, which is what the\n * \"just remove the import\" fix actually costs. The default `reset: \"scoped\"` is the\n * third option: the same normalisation, confined to\n * `:where([class*=\"tempest_\"])`, so it reaches inside a Tempest component and\n * nowhere else.\n *\n * `tempest-react-sdk/styles/auto.css` is a real file, so it resolves without the\n * plugin too — to the complete sheet. Dropping the plugin costs bytes, never\n * correctness.\n *\n * @param options - Scan configuration.\n * @returns The Vite plugin.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestStyles } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestStyles()] });\n *\n * @example\n * // src/main.tsx — the one import, instead of a maintained list\n * import \"tempest-react-sdk/styles/auto.css\";\n */\nexport function tempestStyles(options: TempestStylesOptions = {}): TempestVitePlugin {\n const { dir = \"src\", include = [], skipDirs, reset = \"scoped\" } = options;\n const skip = new Set<string>(skipDirs ?? SKIP_DIRS);\n let root = process.cwd();\n let names: string[] | null = [...include];\n\n /**\n * Rescan the source tree, keeping the explicitly included names.\n *\n * @tempest-limits empty-catch — the scan races the editor and the file system:\n * a file listed a moment ago can be renamed, deleted, or held by another process\n * by the time it is read. An unreadable file contributes no names and the build\n * continues; failing the whole scan would break `vite dev` over a temp file that\n * no longer exists.\n */\n const rescan = async (): Promise<void> => {\n const found = new Set<string>(include);\n for (const file of await collectSourceFiles(resolve(root, dir), skip)) {\n let code: string;\n try {\n code = await readFile(file, \"utf8\");\n } catch {\n continue;\n }\n const scanned = scanStyleImports(code);\n if (scanned === null) {\n names = null;\n return;\n }\n for (const name of scanned) found.add(name);\n }\n names = [...found];\n };\n\n return {\n name: \"tempest-styles\",\n enforce: \"pre\",\n\n configResolved(config: { root?: string }) {\n root = config.root ?? root;\n },\n\n async buildStart() {\n await rescan();\n },\n\n resolveId(id: string) {\n return id === TEMPEST_STYLES_ID ? RESOLVED_ID : null;\n },\n\n load(id: string) {\n return id === RESOLVED_ID ? buildStyleEntry(names, readManifest(root), reset) : null;\n },\n };\n}\n"],"mappings":"oGAQA,IAAa,EAAoB,oCAE3B,EAAc,4BAGd,EAAgB,mDAEhB,EAAc,kBACd,EAAY,IAAI,IAAI,CAAC,eAAgB,OAAQ,QAAS,UAAU,CAAC,EAsDvE,SAAgB,EAAiB,EAA+B,CAC5D,IAAM,EAAQ,IAAI,IAElB,IAAK,IAAM,KAAS,EAAK,SACrB,4DACJ,EAAG,CACC,GAAM,EAAG,EAAU,EAAQ,GAAa,EAExC,GADI,CAAC,EAAc,KAAK,GAAa,EAAE,GACnC,EAAU,SACd,GAAI,gBAAgB,KAAK,GAAU,EAAE,EAAG,OAAO,KAE/C,IAAM,EAAS,cAAc,KAAK,GAAU,EAAE,EACzC,KACL,IAAK,IAAM,KAAS,EAAO,IAAM,GAAA,CAAI,MAAM,GAAG,EAAG,CAC7C,IAAM,EAAU,EAAK,KAAK,EAC1B,GAAI,CAAC,GAAW,EAAQ,WAAW,OAAO,EAAG,SAC7C,IAAM,EAAO,EAAQ,MAAM,UAAU,CAAC,CAAC,EAAE,EAAE,KAAK,EAC5C,GAAM,EAAM,IAAI,CAAI,CAC5B,CACJ,CACA,MAAO,CAAC,GAAG,CAAK,CACpB,CAaA,SAAgB,EACZ,EACA,EACA,EAAmB,SACb,CACN,IAAM,EAAS;EACT,EACF,IAAU,SACJ,CAAC,EAAS,IAAI,EACd,IAAU,SACR,CAAC,aAAc,YAAY,EAC3B,CAAC,YAAY,EAEzB,GAAI,IAAU,KAEV,MAAO,GAAG,KADI,IAAU,SAAW,CAAC,eAAe,EAAI,CAAC,GAAG,EAAY,eAAe,EAAA,CAC7D,IAAK,GAAU,qCAAqC,EAAM,GAAG,CAAC,CAAC,KAAK;CAAI,EAAE,IAGvG,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EACf,IAAK,IAAM,KAAS,EAAS,WAAW,IAAS,CAAC,EAAG,EAAO,IAAI,CAAK,EAKzE,MAAO,GAAG,IAHI,CAAC,GAAG,EAAY,GAAG,CAAC,GAAG,CAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAChD,GAAU,qCAAqC,EAAM,GAEvC,CAAA,CAAM,KAAK;CAAI,EAAE,GACxC,CASA,eAAe,EAAmB,EAAa,EAA8C,CACzF,IAAM,EAAgB,CAAC,EACnB,EACJ,GAAI,CACA,EAAU,MAAA,EAAM,EAAA,QAAA,CAAQ,EAAK,CAAE,cAAe,EAAK,CAAC,CACxD,MAAQ,CACJ,OAAO,CACX,CACA,IAAK,IAAM,KAAS,EAChB,GAAI,EAAM,YAAY,EAAG,CACrB,GAAI,EAAK,IAAI,EAAM,IAAI,GAAK,EAAM,KAAK,WAAW,GAAG,EAAG,SACxD,EAAI,KAAK,GAAI,MAAM,GAAA,EAAmB,EAAA,KAAA,CAAK,EAAK,EAAM,IAAI,EAAG,CAAI,CAAE,CACvE,MAAW,EAAM,OAAO,GAAK,EAAY,KAAK,EAAM,IAAI,GACpD,EAAI,MAAA,EAAK,EAAA,KAAA,CAAK,EAAK,EAAM,IAAI,CAAC,EAGtC,OAAO,CACX,CAgBA,SAAS,EAAa,EAA6B,CAC/C,IAAM,GAAA,EAAW,EAAA,KAAA,CAAK,oBAAqB,OAAQ,SAAU,eAAe,EACtE,EAAuB,CAAC,EAE1B,GAAA,EAAM,EAAA,QAAA,CAAQ,CAAI,EACtB,OAAS,CACL,EAAW,MAAA,EAAK,EAAA,KAAA,CAAK,EAAK,eAAgB,CAAQ,CAAC,EACnD,IAAM,GAAA,EAAS,EAAA,QAAA,CAAQ,CAAG,EAC1B,GAAI,IAAW,EAAK,MACpB,EAAM,CACV,CACA,EAAW,MAAA,EAAK,EAAA,KAAA,EAAA,EAAK,EAAA,QAAA,EAAA,EAAQ,EAAA,cAAA,CAAA,CAAA,EAA0B,GAAG,CAAC,EAAG,KAAM,SAAU,eAAe,CAAC,EAE9F,IAAK,IAAM,KAAa,EACpB,GAAI,CACA,OAAO,KAAK,OAAA,EAAM,EAAA,aAAA,CAAa,EAAW,MAAM,CAAC,CACrD,MAAQ,CACJ,QACJ,CAEJ,MAAU,MACN,8KAGJ,CACJ,CA0CA,SAAgB,EAAc,EAAgC,CAAC,EAAsB,CACjF,GAAM,CAAE,MAAM,MAAO,UAAU,CAAC,EAAG,WAAU,QAAQ,UAAa,EAC5D,EAAO,IAAI,IAAY,GAAY,CAAS,EAC9C,EAAO,QAAQ,IAAI,EACnB,EAAyB,CAAC,GAAG,CAAO,EAWlC,EAAS,SAA2B,CACtC,IAAM,EAAQ,IAAI,IAAY,CAAO,EACrC,IAAK,IAAM,KAAQ,MAAM,GAAA,EAAmB,EAAA,QAAA,CAAQ,EAAM,CAAG,EAAG,CAAI,EAAG,CACnE,IAAI,EACJ,GAAI,CACA,EAAO,MAAA,EAAM,EAAA,SAAA,CAAS,EAAM,MAAM,CACtC,MAAQ,CACJ,QACJ,CACA,IAAM,EAAU,EAAiB,CAAI,EACrC,GAAI,IAAY,KAAM,CAClB,EAAQ,KACR,MACJ,CACA,IAAK,IAAM,KAAQ,EAAS,EAAM,IAAI,CAAI,CAC9C,CACA,EAAQ,CAAC,GAAG,CAAK,CACrB,EAEA,MAAO,CACH,KAAM,iBACN,QAAS,MAET,eAAe,EAA2B,CACtC,EAAO,EAAO,MAAQ,CAC1B,EAEA,MAAM,YAAa,CACf,MAAM,EAAO,CACjB,EAEA,UAAU,EAAY,CAClB,OAAO,IAAA,oCAA2B,EAAc,IACpD,EAEA,KAAK,EAAY,CACb,OAAO,IAAO,EAAc,EAAgB,EAAO,EAAa,CAAI,EAAG,CAAK,EAAI,IACpF,CACJ,CACJ"}
@@ -0,0 +1,103 @@
1
+ import { dirname as e, join as t, resolve as n } from "node:path";
2
+ import { readFile as r, readdir as i } from "node:fs/promises";
3
+ import { readFileSync as a } from "node:fs";
4
+ import { fileURLToPath as o } from "node:url";
5
+ //#region src/vite/tempest-styles.ts
6
+ var s = "tempest-react-sdk/styles/auto.css", c = "\0tempest-styles/auto.css", l = /^tempest-react-sdk(\/(charts|editor|br|icons))?$/, u = /\.[cm]?[jt]sx?$/, d = /* @__PURE__ */ new Set([
7
+ "node_modules",
8
+ "dist",
9
+ "build",
10
+ "coverage"
11
+ ]);
12
+ function f(e) {
13
+ let t = /* @__PURE__ */ new Set();
14
+ for (let n of e.matchAll(/\bimport\s+(type\s+)?([^;'"]*?)\s+from\s*["']([^"']+)["']/g)) {
15
+ let [, e, r, i] = n;
16
+ if (!l.test(i ?? "") || e) continue;
17
+ if (/^\s*\*\s+as\s/.test(r ?? "")) return null;
18
+ let a = /\{([^}]*)\}/.exec(r ?? "");
19
+ if (a) for (let e of (a[1] ?? "").split(",")) {
20
+ let n = e.trim();
21
+ if (!n || n.startsWith("type ")) continue;
22
+ let r = n.split(/\s+as\s+/)[0]?.trim();
23
+ r && t.add(r);
24
+ }
25
+ }
26
+ return [...t];
27
+ }
28
+ function p(e, t, n = "scoped") {
29
+ let r = "/* Generated by tempestStyles() — the CSS this app actually uses. */\n", i = n === "global" ? [t.core] : n === "scoped" ? ["tokens.css", "scoped.css"] : ["tokens.css"];
30
+ if (e === null) return `${r}${(n === "global" ? ["../styles.css"] : [...i, "../styles.css"]).map((e) => `@import "tempest-react-sdk/styles/${e}";`).join("\n")}\n`;
31
+ let a = /* @__PURE__ */ new Set();
32
+ for (let n of e) for (let e of t.components[n] ?? []) a.add(e);
33
+ return `${r}${[...i, ...[...a].sort()].map((e) => `@import "tempest-react-sdk/styles/${e}";`).join("\n")}\n`;
34
+ }
35
+ async function m(e, n) {
36
+ let r = [], a;
37
+ try {
38
+ a = await i(e, { withFileTypes: !0 });
39
+ } catch {
40
+ return r;
41
+ }
42
+ for (let i of a) if (i.isDirectory()) {
43
+ if (n.has(i.name) || i.name.startsWith(".")) continue;
44
+ r.push(...await m(t(e, i.name), n));
45
+ } else i.isFile() && u.test(i.name) && r.push(t(e, i.name));
46
+ return r;
47
+ }
48
+ function h(r) {
49
+ let i = t("tempest-react-sdk", "dist", "styles", "manifest.json"), s = [], c = n(r);
50
+ for (;;) {
51
+ s.push(t(c, "node_modules", i));
52
+ let n = e(c);
53
+ if (n === c) break;
54
+ c = n;
55
+ }
56
+ s.push(t(e(o(import.meta.url)), "..", "styles", "manifest.json"));
57
+ for (let e of s) try {
58
+ return JSON.parse(a(e, "utf8"));
59
+ } catch {
60
+ continue;
61
+ }
62
+ throw Error("tempestStyles(): tempest-react-sdk/dist/styles/manifest.json not found — the installed SDK predates it. Upgrade, or drop the plugin and import tempest-react-sdk/styles.css.");
63
+ }
64
+ function g(e = {}) {
65
+ let { dir: t = "src", include: i = [], skipDirs: a, reset: o = "scoped" } = e, s = new Set(a ?? d), l = process.cwd(), u = [...i], g = async () => {
66
+ let e = new Set(i);
67
+ for (let i of await m(n(l, t), s)) {
68
+ let t;
69
+ try {
70
+ t = await r(i, "utf8");
71
+ } catch {
72
+ continue;
73
+ }
74
+ let n = f(t);
75
+ if (n === null) {
76
+ u = null;
77
+ return;
78
+ }
79
+ for (let t of n) e.add(t);
80
+ }
81
+ u = [...e];
82
+ };
83
+ return {
84
+ name: "tempest-styles",
85
+ enforce: "pre",
86
+ configResolved(e) {
87
+ l = e.root ?? l;
88
+ },
89
+ async buildStart() {
90
+ await g();
91
+ },
92
+ resolveId(e) {
93
+ return e === "tempest-react-sdk/styles/auto.css" ? c : null;
94
+ },
95
+ load(e) {
96
+ return e === c ? p(u, h(l), o) : null;
97
+ }
98
+ };
99
+ }
100
+ //#endregion
101
+ export { s as TEMPEST_STYLES_ID, p as buildStyleEntry, f as scanStyleImports, g as tempestStyles };
102
+
103
+ //# sourceMappingURL=tempest-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tempest-styles.js","names":[],"sources":["../../src/vite/tempest-styles.ts"],"sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** The stylesheet id an app imports to get exactly the CSS it uses. */\nexport const TEMPEST_STYLES_ID = \"tempest-react-sdk/styles/auto.css\";\n\nconst RESOLVED_ID = \"\\0tempest-styles/auto.css\";\n\n/** Package subpaths whose exports are components with stylesheets. */\nconst SDK_SPECIFIER = /^tempest-react-sdk(\\/(charts|editor|br|icons))?$/;\n\nconst SOURCE_FILE = /\\.[cm]?[jt]sx?$/;\nconst SKIP_DIRS = new Set([\"node_modules\", \"dist\", \"build\", \"coverage\"]);\n\n/**\n * How much of the reset an app takes.\n *\n * The components are written against the reset — `.tempest_button` assumes\n * `button { background: none; border: 0; padding: 0 }`, every width assumes\n * `box-sizing: border-box` — so `\"none\"` is not \"unstyled document, styled\n * components\": it is components missing their box model. It exists for the app\n * that already ships an equivalent reset of its own.\n */\nexport type ResetMode = \"scoped\" | \"global\" | \"none\";\n\n/** Shape of `dist/styles/manifest.json`. */\nexport interface StyleManifest {\n /** The foundation sheet — tokens and reset together, the pre-split default. */\n core: string;\n /** Public export name → the stylesheets it needs, transitive closure included. */\n components: Record<string, readonly string[]>;\n}\n\nexport interface TempestStylesOptions {\n /** Directory to scan, relative to the Vite root. Default: `\"src\"`. */\n dir?: string;\n /**\n * Which reset to emit alongside the tokens. Default: `\"scoped\"`.\n *\n * `\"scoped\"` confines the reset to `:where([class*=\"tempest_\"])`, so components\n * keep the normalisation they are written against and the app keeps `html`,\n * `body` and `#root`. `\"global\"` is the pre-split behaviour, correct when the\n * SDK owns the whole page. `\"none\"` emits tokens only.\n */\n reset?: ResetMode;\n /**\n * Extra export names to style even when the scan cannot see them — a component\n * reached only through a namespace import, or rendered by a sibling package.\n */\n include?: readonly string[];\n /** Directory names to skip. Default: `node_modules`, `dist`, `build`, `coverage`. */\n skipDirs?: readonly string[];\n}\n\n/**\n * Collect the SDK export names a source file imports.\n *\n * Reads the import specifier rather than the JSX, because a bare `<Button>` in the\n * markup says nothing about where it came from — the app may well define its own.\n * Type-only bindings are dropped: they are erased before any component renders, so\n * paying for their CSS would be paying for nothing.\n *\n * @param code - File contents.\n * @returns The imported names, or `null` when the file namespace-imports the SDK\n * and the set therefore cannot be known.\n */\nexport function scanStyleImports(code: string): string[] | null {\n const names = new Set<string>();\n\n for (const match of code.matchAll(\n /\\bimport\\s+(type\\s+)?([^;'\"]*?)\\s+from\\s*[\"']([^\"']+)[\"']/g,\n )) {\n const [, typeOnly, clause, specifier] = match;\n if (!SDK_SPECIFIER.test(specifier ?? \"\")) continue;\n if (typeOnly) continue;\n if (/^\\s*\\*\\s+as\\s/.test(clause ?? \"\")) return null;\n\n const braces = /\\{([^}]*)\\}/.exec(clause ?? \"\");\n if (!braces) continue;\n for (const part of (braces[1] ?? \"\").split(\",\")) {\n const binding = part.trim();\n if (!binding || binding.startsWith(\"type \")) continue;\n const name = binding.split(/\\s+as\\s+/)[0]?.trim();\n if (name) names.add(name);\n }\n }\n return [...names];\n}\n\n/**\n * Build the stylesheet for a set of export names.\n *\n * Emits `@import` statements rather than inlined rules so Vite's own CSS pipeline\n * resolves, deduplicates and minifies them — the same treatment a hand-written\n * list of imports gets, which is what this replaces.\n *\n * @param names - Export names the app imports, or `null` to take everything.\n * @param manifest - The published style manifest.\n * @returns CSS source.\n */\nexport function buildStyleEntry(\n names: readonly string[] | null,\n manifest: StyleManifest,\n reset: ResetMode = \"scoped\",\n): string {\n const banner = \"/* Generated by tempestStyles() — the CSS this app actually uses. */\\n\";\n const foundation =\n reset === \"global\"\n ? [manifest.core]\n : reset === \"scoped\"\n ? [\"tokens.css\", \"scoped.css\"]\n : [\"tokens.css\"];\n\n if (names === null) {\n const whole = reset === \"global\" ? [\"../styles.css\"] : [...foundation, \"../styles.css\"];\n return `${banner}${whole.map((sheet) => `@import \"tempest-react-sdk/styles/${sheet}\";`).join(\"\\n\")}\\n`;\n }\n\n const sheets = new Set<string>();\n for (const name of names) {\n for (const sheet of manifest.components[name] ?? []) sheets.add(sheet);\n }\n const lines = [...foundation, ...[...sheets].sort()].map(\n (sheet) => `@import \"tempest-react-sdk/styles/${sheet}\";`,\n );\n return `${banner}${lines.join(\"\\n\")}\\n`;\n}\n\n/**\n * Walk a directory and collect every source file path.\n *\n * @param dir - Absolute directory to walk.\n * @param skip - Directory names to skip.\n * @returns Absolute file paths.\n */\nasync function collectSourceFiles(dir: string, skip: ReadonlySet<string>): Promise<string[]> {\n const out: string[] = [];\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (skip.has(entry.name) || entry.name.startsWith(\".\")) continue;\n out.push(...(await collectSourceFiles(join(dir, entry.name), skip)));\n } else if (entry.isFile() && SOURCE_FILE.test(entry.name)) {\n out.push(join(dir, entry.name));\n }\n }\n return out;\n}\n\n/**\n * Read the style manifest the installed package ships.\n *\n * Resolved by walking the filesystem rather than through `createRequire`, which\n * does not survive the trip: Vite bundles `vite.config.ts` into a temporary module\n * before running it, and in that bundle the `node:module` namespace import comes\n * back without the function — `(0, a.createRequire) is not a function`, at load\n * time, inside the build. The same rewrite is why `import.meta.url` is only the\n * fallback here and the app's `node_modules` is tried first.\n *\n * @param root - The Vite root, where the walk for `node_modules` starts.\n * @returns The manifest.\n * @throws If no installed copy carries one — an SDK older than the manifest.\n */\nfunction readManifest(root: string): StyleManifest {\n const relative = join(\"tempest-react-sdk\", \"dist\", \"styles\", \"manifest.json\");\n const candidates: string[] = [];\n\n let dir = resolve(root);\n for (;;) {\n candidates.push(join(dir, \"node_modules\", relative));\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n candidates.push(join(dirname(fileURLToPath(import.meta.url)), \"..\", \"styles\", \"manifest.json\"));\n\n for (const candidate of candidates) {\n try {\n return JSON.parse(readFileSync(candidate, \"utf8\")) as StyleManifest;\n } catch {\n continue;\n }\n }\n throw new Error(\n \"tempestStyles(): tempest-react-sdk/dist/styles/manifest.json not found — \" +\n \"the installed SDK predates it. Upgrade, or drop the plugin and import \" +\n \"tempest-react-sdk/styles.css.\",\n );\n}\n\n/**\n * Import only the SDK stylesheets your app can actually reach.\n *\n * `styles.css` carries every component the SDK ships — measured at 236.71 kB raw\n * against the 38.94 kB an app mounting twelve of them can reach. The per-component\n * sheets under `styles/` have always closed that gap, but by hand: the app lists\n * them and keeps the list honest as it grows. This plugin makes the list the build's\n * problem, resolving it from the imports the source already writes.\n *\n * The closure matters more than the scan. A component pays for the CSS of every SDK\n * component it renders internally, so `<DataTable>` alone needs six sheets and\n * `<AIChat>` seven — a hand-kept list of one sheet per component the app names is\n * wrong in a way that only shows up as an unstyled child.\n *\n * The reset is the other half, and the half that breaks apps. `styles.css` claims\n * `html`, `body` and `#root`; an app with its own layout that imports it loses its\n * document surface, and an app that drops the import loses the box model its\n * components are written against — components render unstyled, which is what the\n * \"just remove the import\" fix actually costs. The default `reset: \"scoped\"` is the\n * third option: the same normalisation, confined to\n * `:where([class*=\"tempest_\"])`, so it reaches inside a Tempest component and\n * nowhere else.\n *\n * `tempest-react-sdk/styles/auto.css` is a real file, so it resolves without the\n * plugin too — to the complete sheet. Dropping the plugin costs bytes, never\n * correctness.\n *\n * @param options - Scan configuration.\n * @returns The Vite plugin.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestStyles } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestStyles()] });\n *\n * @example\n * // src/main.tsx — the one import, instead of a maintained list\n * import \"tempest-react-sdk/styles/auto.css\";\n */\nexport function tempestStyles(options: TempestStylesOptions = {}): TempestVitePlugin {\n const { dir = \"src\", include = [], skipDirs, reset = \"scoped\" } = options;\n const skip = new Set<string>(skipDirs ?? SKIP_DIRS);\n let root = process.cwd();\n let names: string[] | null = [...include];\n\n /**\n * Rescan the source tree, keeping the explicitly included names.\n *\n * @tempest-limits empty-catch — the scan races the editor and the file system:\n * a file listed a moment ago can be renamed, deleted, or held by another process\n * by the time it is read. An unreadable file contributes no names and the build\n * continues; failing the whole scan would break `vite dev` over a temp file that\n * no longer exists.\n */\n const rescan = async (): Promise<void> => {\n const found = new Set<string>(include);\n for (const file of await collectSourceFiles(resolve(root, dir), skip)) {\n let code: string;\n try {\n code = await readFile(file, \"utf8\");\n } catch {\n continue;\n }\n const scanned = scanStyleImports(code);\n if (scanned === null) {\n names = null;\n return;\n }\n for (const name of scanned) found.add(name);\n }\n names = [...found];\n };\n\n return {\n name: \"tempest-styles\",\n enforce: \"pre\",\n\n configResolved(config: { root?: string }) {\n root = config.root ?? root;\n },\n\n async buildStart() {\n await rescan();\n },\n\n resolveId(id: string) {\n return id === TEMPEST_STYLES_ID ? RESOLVED_ID : null;\n },\n\n load(id: string) {\n return id === RESOLVED_ID ? buildStyleEntry(names, readManifest(root), reset) : null;\n },\n };\n}\n"],"mappings":"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"}
package/dist/vite.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./vite/tempest-icons.cjs"),t=require("./vite/create-vite-config.cjs"),n=require("./vite/tempest-pwa-manifest.cjs"),r=require("./vite/tempest-pwa-icons.cjs"),i=require("./vite/tempest-pwa-dev-sw.cjs");exports.createViteConfig=t.createViteConfig,exports.tempestIcons=e.tempestIcons,exports.tempestPwaDevSw=i.tempestPwaDevSw,exports.tempestPwaIcons=r.tempestPwaIcons,exports.tempestPwaManifest=n.tempestPwaManifest;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./vite/tempest-icons.cjs"),t=require("./vite/create-vite-config.cjs"),n=require("./vite/tempest-pwa-manifest.cjs"),r=require("./vite/tempest-pwa-icons.cjs"),i=require("./vite/tempest-pwa-dev-sw.cjs"),a=require("./vite/tempest-styles.cjs");exports.createViteConfig=t.createViteConfig,exports.tempestIcons=e.tempestIcons,exports.tempestPwaDevSw=i.tempestPwaDevSw,exports.tempestPwaIcons=r.tempestPwaIcons,exports.tempestPwaManifest=n.tempestPwaManifest,exports.tempestStyles=a.tempestStyles;
package/dist/vite.d.ts CHANGED
@@ -69,6 +69,25 @@ export declare interface CreateViteConfigOptions {
69
69
  */
70
70
  export declare type ProxyEntry = string | Record<string, unknown>;
71
71
 
72
+ /**
73
+ * How much of the reset an app takes.
74
+ *
75
+ * The components are written against the reset — `.tempest_button` assumes
76
+ * `button { background: none; border: 0; padding: 0 }`, every width assumes
77
+ * `box-sizing: border-box` — so `"none"` is not "unstyled document, styled
78
+ * components": it is components missing their box model. It exists for the app
79
+ * that already ships an equivalent reset of its own.
80
+ */
81
+ export declare type ResetMode = "scoped" | "global" | "none";
82
+
83
+ /** Shape of `dist/styles/manifest.json`. */
84
+ export declare interface StyleManifest {
85
+ /** The foundation sheet — tokens and reset together, the pre-split default. */
86
+ core: string;
87
+ /** Public export name → the stylesheets it needs, transitive closure included. */
88
+ components: Record<string, readonly string[]>;
89
+ }
90
+
72
91
  /**
73
92
  * Generate a static icon registry from the slugs your source mentions.
74
93
  *
@@ -250,6 +269,69 @@ export declare interface TempestPwaManifestOptions {
250
269
  appShell?: string | false;
251
270
  }
252
271
 
272
+ /**
273
+ * Import only the SDK stylesheets your app can actually reach.
274
+ *
275
+ * `styles.css` carries every component the SDK ships — measured at 236.71 kB raw
276
+ * against the 38.94 kB an app mounting twelve of them can reach. The per-component
277
+ * sheets under `styles/` have always closed that gap, but by hand: the app lists
278
+ * them and keeps the list honest as it grows. This plugin makes the list the build's
279
+ * problem, resolving it from the imports the source already writes.
280
+ *
281
+ * The closure matters more than the scan. A component pays for the CSS of every SDK
282
+ * component it renders internally, so `<DataTable>` alone needs six sheets and
283
+ * `<AIChat>` seven — a hand-kept list of one sheet per component the app names is
284
+ * wrong in a way that only shows up as an unstyled child.
285
+ *
286
+ * The reset is the other half, and the half that breaks apps. `styles.css` claims
287
+ * `html`, `body` and `#root`; an app with its own layout that imports it loses its
288
+ * document surface, and an app that drops the import loses the box model its
289
+ * components are written against — components render unstyled, which is what the
290
+ * "just remove the import" fix actually costs. The default `reset: "scoped"` is the
291
+ * third option: the same normalisation, confined to
292
+ * `:where([class*="tempest_"])`, so it reaches inside a Tempest component and
293
+ * nowhere else.
294
+ *
295
+ * `tempest-react-sdk/styles/auto.css` is a real file, so it resolves without the
296
+ * plugin too — to the complete sheet. Dropping the plugin costs bytes, never
297
+ * correctness.
298
+ *
299
+ * @param options - Scan configuration.
300
+ * @returns The Vite plugin.
301
+ *
302
+ * @example
303
+ * // vite.config.ts
304
+ * import { createViteConfig, tempestStyles } from "tempest-react-sdk/vite";
305
+ *
306
+ * export default createViteConfig({ plugins: [tempestStyles()] });
307
+ *
308
+ * @example
309
+ * // src/main.tsx — the one import, instead of a maintained list
310
+ * import "tempest-react-sdk/styles/auto.css";
311
+ */
312
+ export declare function tempestStyles(options?: TempestStylesOptions): TempestVitePlugin;
313
+
314
+ export declare interface TempestStylesOptions {
315
+ /** Directory to scan, relative to the Vite root. Default: `"src"`. */
316
+ dir?: string;
317
+ /**
318
+ * Which reset to emit alongside the tokens. Default: `"scoped"`.
319
+ *
320
+ * `"scoped"` confines the reset to `:where([class*="tempest_"])`, so components
321
+ * keep the normalisation they are written against and the app keeps `html`,
322
+ * `body` and `#root`. `"global"` is the pre-split behaviour, correct when the
323
+ * SDK owns the whole page. `"none"` emits tokens only.
324
+ */
325
+ reset?: ResetMode;
326
+ /**
327
+ * Extra export names to style even when the scan cannot see them — a component
328
+ * reached only through a namespace import, or rendered by a sibling package.
329
+ */
330
+ include?: readonly string[];
331
+ /** Directory names to skip. Default: `node_modules`, `dist`, `build`, `coverage`. */
332
+ skipDirs?: readonly string[];
333
+ }
334
+
253
335
  /**
254
336
  * The resulting Vite config object. Typed loosely so the SDK's published
255
337
  * declarations stay free of `vite`'s internal types; assign it straight to a
package/dist/vite.js CHANGED
@@ -3,4 +3,5 @@ import { createViteConfig as t } from "./vite/create-vite-config.js";
3
3
  import { tempestPwaManifest as n } from "./vite/tempest-pwa-manifest.js";
4
4
  import { tempestPwaIcons as r } from "./vite/tempest-pwa-icons.js";
5
5
  import { tempestPwaDevSw as i } from "./vite/tempest-pwa-dev-sw.js";
6
- export { t as createViteConfig, e as tempestIcons, i as tempestPwaDevSw, r as tempestPwaIcons, n as tempestPwaManifest };
6
+ import { tempestStyles as a } from "./vite/tempest-styles.js";
7
+ export { t as createViteConfig, e as tempestIcons, i as tempestPwaDevSw, r as tempestPwaIcons, n as tempestPwaManifest, a as tempestStyles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tempest-react-sdk",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "description": "SDK público da Tempest com componentes, hooks e integrações para projetos React.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -102,12 +102,13 @@
102
102
  },
103
103
  "./styles.css": "./dist/styles.css",
104
104
  "./styles/*.css": "./dist/styles/*.css",
105
+ "./styles/manifest.json": "./dist/styles/manifest.json",
105
106
  "./package.json": "./package.json",
106
107
  "./utilities.css": "./dist/utilities.css"
107
108
  },
108
109
  "scripts": {
109
110
  "dev": "vite build --watch",
110
- "build": "tsc -b tsconfig.build.json && node --max-old-space-size=6144 ./node_modules/vite/bin/vite.js build && node scripts/copy-css-assets.mjs && node scripts/split-css.mjs",
111
+ "build": "node scripts/gen-scoped-reset.mjs && tsc -b tsconfig.build.json && node --max-old-space-size=6144 ./node_modules/vite/bin/vite.js build && node scripts/split-css.mjs && node scripts/gen-style-manifest.mjs && node scripts/copy-css-assets.mjs",
111
112
  "postbuild": "node scripts/check-dist-guards.mjs",
112
113
  "typecheck": "tsc -b tsconfig.json --noEmit",
113
114
  "lint": "eslint .",