cursedbelt 3.0.2 → 4.0.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/dist/react/components/ListToolbar.d.ts +94 -0
- package/dist/react/components/ListToolbar.d.ts.map +1 -0
- package/dist/react/components/ListToolbar.js +66 -0
- package/dist/react/components/ListToolbar.js.map +1 -0
- package/dist/react/file-tree/FileTree.d.ts +282 -3
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +799 -131
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +151 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts +187 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts.map +1 -0
- package/dist/react/file-tree/fileTreeSelection.js +264 -0
- package/dist/react/file-tree/fileTreeSelection.js.map +1 -0
- package/dist/react/file-tree/index.d.ts +1 -0
- package/dist/react/file-tree/index.d.ts.map +1 -1
- package/dist/react/file-tree/index.js +8 -0
- package/dist/react/file-tree/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/form.d.ts +3 -0
- package/dist/react/lib/form.d.ts.map +1 -1
- package/dist/react/lib/form.js +43 -5
- package/dist/react/lib/form.js.map +1 -1
- package/dist/react/media/DenseGalleryShell.d.ts +19 -3
- package/dist/react/media/DenseGalleryShell.d.ts.map +1 -1
- package/dist/react/media/DenseGalleryShell.js +55 -15
- package/dist/react/media/DenseGalleryShell.js.map +1 -1
- package/dist/react/media/FocusViewer.d.ts.map +1 -1
- package/dist/react/media/FocusViewer.js +14 -14
- package/dist/react/media/FocusViewer.js.map +1 -1
- package/dist/react/media/FrameGrabber.d.ts +23 -0
- package/dist/react/media/FrameGrabber.d.ts.map +1 -1
- package/dist/react/media/FrameGrabber.js +67 -1
- package/dist/react/media/FrameGrabber.js.map +1 -1
- package/dist/react/media/MediaResize.d.ts +51 -19
- package/dist/react/media/MediaResize.d.ts.map +1 -1
- package/dist/react/media/MediaResize.js +20 -13
- package/dist/react/media/MediaResize.js.map +1 -1
- package/dist/react/media/VideoChapterEditor.d.ts +67 -0
- package/dist/react/media/VideoChapterEditor.d.ts.map +1 -0
- package/dist/react/media/VideoChapterEditor.js +148 -0
- package/dist/react/media/VideoChapterEditor.js.map +1 -0
- package/dist/react/media/VideoPlayer.d.ts +46 -4
- package/dist/react/media/VideoPlayer.d.ts.map +1 -1
- package/dist/react/media/VideoPlayer.js +124 -11
- package/dist/react/media/VideoPlayer.js.map +1 -1
- package/dist/react/media/VideoTurner.d.ts +15 -2
- package/dist/react/media/VideoTurner.d.ts.map +1 -1
- package/dist/react/media/VideoTurner.js +29 -2
- package/dist/react/media/VideoTurner.js.map +1 -1
- package/dist/react/media/denseGallery.css +104 -46
- package/dist/react/media/hlsSource.d.ts +57 -0
- package/dist/react/media/hlsSource.d.ts.map +1 -1
- package/dist/react/media/hlsSource.js +177 -1
- package/dist/react/media/hlsSource.js.map +1 -1
- package/dist/react/media/index.d.ts +2 -0
- package/dist/react/media/index.d.ts.map +1 -1
- package/dist/react/media/index.js +6 -0
- package/dist/react/media/index.js.map +1 -1
- package/dist/react/media/mediaResize.css +127 -54
- package/dist/react/media/pictureEditor.css +52 -6
- package/dist/react/media/videoChapterEditor.css +203 -0
- package/dist/react/media/videoChapters.d.ts +117 -0
- package/dist/react/media/videoChapters.d.ts.map +1 -0
- package/dist/react/media/videoChapters.js +95 -0
- package/dist/react/media/videoChapters.js.map +1 -0
- package/dist/react/media-gallery/GalleryTable.d.ts +52 -1
- package/dist/react/media-gallery/GalleryTable.d.ts.map +1 -1
- package/dist/react/media-gallery/GalleryTable.js +40 -9
- package/dist/react/media-gallery/GalleryTable.js.map +1 -1
- package/dist/react/media-gallery/MediaGallery.d.ts +107 -4
- package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
- package/dist/react/media-gallery/MediaGallery.js +484 -104
- package/dist/react/media-gallery/MediaGallery.js.map +1 -1
- package/dist/react/media-gallery/MediaMetaEditor.d.ts +48 -0
- package/dist/react/media-gallery/MediaMetaEditor.d.ts.map +1 -0
- package/dist/react/media-gallery/MediaMetaEditor.js +66 -0
- package/dist/react/media-gallery/MediaMetaEditor.js.map +1 -0
- package/dist/react/media-gallery/index.d.ts +2 -0
- package/dist/react/media-gallery/index.d.ts.map +1 -1
- package/dist/react/media-gallery/index.js +5 -0
- package/dist/react/media-gallery/index.js.map +1 -1
- package/dist/react/media-gallery/mediaGallery.css +180 -4
- package/dist/react/media-gallery/mediaMeta.d.ts +62 -0
- package/dist/react/media-gallery/mediaMeta.d.ts.map +1 -0
- package/dist/react/media-gallery/mediaMeta.js +56 -0
- package/dist/react/media-gallery/mediaMeta.js.map +1 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts +77 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts.map +1 -1
- package/dist/react/media-gallery/playbackPreferences.js +42 -0
- package/dist/react/media-gallery/playbackPreferences.js.map +1 -1
- package/dist/react/media-gallery/types.d.ts +34 -0
- package/dist/react/media-gallery/types.d.ts.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts +29 -0
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -0
- package/dist/styles-areas/activity-bar.css +30 -0
- package/dist/styles-areas/analytics.css +672 -0
- package/dist/styles-areas/auth.css +154 -0
- package/dist/styles-areas/calendar.css +416 -0
- package/dist/styles-areas/chart-table.css +41 -0
- package/dist/styles-areas/charts.css +89 -0
- package/dist/styles-areas/chat.css +364 -0
- package/dist/styles-areas/clipboard.css +26 -0
- package/dist/styles-areas/code-editor.css +130 -0
- package/dist/styles-areas/companion-link.css +70 -0
- package/dist/styles-areas/core.css +2503 -0
- package/dist/styles-areas/dashboard-grid.css +84 -0
- package/dist/styles-areas/data-table.css +657 -0
- package/dist/styles-areas/deep-link.css +26 -0
- package/dist/styles-areas/diff-viewer.css +30 -0
- package/dist/styles-areas/disk-usage.css +262 -0
- package/dist/styles-areas/emoji.css +154 -0
- package/dist/styles-areas/fields.css +582 -0
- package/dist/styles-areas/file-tree.css +33 -0
- package/dist/styles-areas/filter-rail.css +235 -0
- package/dist/styles-areas/folder-tree.css +219 -0
- package/dist/styles-areas/keep-awake.css +26 -0
- package/dist/styles-areas/layout-engine.css +239 -0
- package/dist/styles-areas/markdown.css +87 -0
- package/dist/styles-areas/master-detail.css +239 -0
- package/dist/styles-areas/master-lock.css +26 -0
- package/dist/styles-areas/media-gallery.css +351 -0
- package/dist/styles-areas/media.css +658 -0
- package/dist/styles-areas/nav.css +488 -0
- package/dist/styles-areas/notifications.css +245 -0
- package/dist/styles-areas/overlays.css +133 -0
- package/dist/styles-areas/palette.css +140 -0
- package/dist/styles-areas/pdf-viewer.css +47 -0
- package/dist/styles-areas/problem-detail.css +97 -0
- package/dist/styles-areas/rich-text.css +167 -0
- package/dist/styles-areas/sharing.css +153 -0
- package/dist/styles-areas/spreadsheet.css +37 -0
- package/dist/styles-areas/stats.css +281 -0
- package/dist/styles-areas/test-report.css +133 -0
- package/dist/styles-areas/virtual.css +30 -0
- package/dist/styles-areas/wizard.css +381 -0
- package/dist/styles-areas/workbench.css +543 -0
- package/dist/styles-areas/workbook-viewer.css +44 -0
- package/dist/styles-static.css +34 -23
- package/dist/styles.css +34 -23
- package/package.json +111 -31
- package/scripts/checkDistExports.ts +67 -0
- package/scripts/cssRules.ts +125 -0
- package/scripts/fixtureAppCss.ts +344 -0
- package/scripts/generateAreaStyles.ts +253 -0
- package/scripts/guardrailsEnforce.spec.ts +62 -0
- package/scripts/guardrailsEnforce.ts +67 -3
- package/scripts/styleAreas.ts +585 -0
- package/scripts/verify.ts +5 -0
- package/src/barrelsReachNoOptionalPeer.spec.ts +117 -9
- package/src/docsMatchTheSplit.spec.ts +98 -0
- package/src/fixtureAppCss.spec.ts +177 -0
- package/src/namedSubpathsResolve.spec.ts +35 -0
- package/src/publishShape.spec.ts +67 -0
- package/src/react/components/ListToolbar.spec.tsx +172 -0
- package/src/react/components/ListToolbar.tsx +151 -0
- package/src/react/file-tree/FileTree.spec.tsx +1009 -0
- package/src/react/file-tree/FileTree.tsx +1459 -309
- package/src/react/file-tree/fileTree.css +151 -0
- package/src/react/file-tree/fileTreeSelection.spec.ts +327 -0
- package/src/react/file-tree/fileTreeSelection.ts +321 -0
- package/src/react/file-tree/index.ts +21 -0
- package/src/react/index.ts +1 -0
- package/src/react/lib/form.ts +51 -8
- package/src/react/media/DenseGalleryShell.tsx +108 -36
- package/src/react/media/FocusViewer.tsx +15 -8
- package/src/react/media/FrameGrabber.spec.tsx +175 -0
- package/src/react/media/FrameGrabber.tsx +67 -0
- package/src/react/media/MediaResize.spec.tsx +84 -14
- package/src/react/media/MediaResize.tsx +51 -19
- package/src/react/media/VideoChapterEditor.tsx +428 -0
- package/src/react/media/VideoPlayer.spec.tsx +111 -1
- package/src/react/media/VideoPlayer.tsx +158 -11
- package/src/react/media/VideoTurner.spec.tsx +67 -0
- package/src/react/media/VideoTurner.tsx +70 -15
- package/src/react/media/denseGallery.css +104 -46
- package/src/react/media/hlsSource.recovery.spec.ts +213 -0
- package/src/react/media/hlsSource.spec.ts +8 -0
- package/src/react/media/hlsSource.ts +189 -1
- package/src/react/media/index.ts +14 -0
- package/src/react/media/mediaResize.css +127 -54
- package/src/react/media/pictureEditor.css +52 -6
- package/src/react/media/videoChapterEditor.css +203 -0
- package/src/react/media/videoChapters.spec.ts +176 -0
- package/src/react/media/videoChapters.ts +181 -0
- package/src/react/media-gallery/GalleryTable.tsx +93 -3
- package/src/react/media-gallery/MediaGallery.spec.tsx +1156 -10
- package/src/react/media-gallery/MediaGallery.tsx +813 -95
- package/src/react/media-gallery/MediaMetaEditor.tsx +237 -0
- package/src/react/media-gallery/galleryTable.spec.ts +41 -1
- package/src/react/media-gallery/index.ts +7 -0
- package/src/react/media-gallery/mediaGallery.css +180 -4
- package/src/react/media-gallery/mediaMeta.spec.ts +95 -0
- package/src/react/media-gallery/mediaMeta.ts +101 -0
- package/src/react/media-gallery/playbackPreferences.spec.ts +172 -0
- package/src/react/media-gallery/playbackPreferences.ts +100 -0
- package/src/react/media-gallery/types.ts +34 -0
- package/src/shippedFilesAreTracked.spec.ts +69 -0
- package/src/styles-areas/activity-bar.css +30 -0
- package/src/styles-areas/analytics.css +672 -0
- package/src/styles-areas/auth.css +154 -0
- package/src/styles-areas/calendar.css +416 -0
- package/src/styles-areas/chart-table.css +41 -0
- package/src/styles-areas/charts.css +89 -0
- package/src/styles-areas/chat.css +364 -0
- package/src/styles-areas/clipboard.css +26 -0
- package/src/styles-areas/code-editor.css +130 -0
- package/src/styles-areas/companion-link.css +70 -0
- package/src/styles-areas/core.css +2503 -0
- package/src/styles-areas/dashboard-grid.css +84 -0
- package/src/styles-areas/data-table.css +657 -0
- package/src/styles-areas/deep-link.css +26 -0
- package/src/styles-areas/diff-viewer.css +30 -0
- package/src/styles-areas/disk-usage.css +262 -0
- package/src/styles-areas/emoji.css +154 -0
- package/src/styles-areas/fields.css +582 -0
- package/src/styles-areas/file-tree.css +33 -0
- package/src/styles-areas/filter-rail.css +235 -0
- package/src/styles-areas/folder-tree.css +219 -0
- package/src/styles-areas/keep-awake.css +26 -0
- package/src/styles-areas/layout-engine.css +239 -0
- package/src/styles-areas/markdown.css +87 -0
- package/src/styles-areas/master-detail.css +239 -0
- package/src/styles-areas/master-lock.css +26 -0
- package/src/styles-areas/media-gallery.css +351 -0
- package/src/styles-areas/media.css +658 -0
- package/src/styles-areas/nav.css +488 -0
- package/src/styles-areas/notifications.css +245 -0
- package/src/styles-areas/overlays.css +133 -0
- package/src/styles-areas/palette.css +140 -0
- package/src/styles-areas/pdf-viewer.css +47 -0
- package/src/styles-areas/problem-detail.css +97 -0
- package/src/styles-areas/rich-text.css +167 -0
- package/src/styles-areas/sharing.css +153 -0
- package/src/styles-areas/spreadsheet.css +37 -0
- package/src/styles-areas/stats.css +281 -0
- package/src/styles-areas/test-report.css +133 -0
- package/src/styles-areas/virtual.css +30 -0
- package/src/styles-areas/wizard.css +381 -0
- package/src/styles-areas/workbench.css +543 -0
- package/src/styles-areas/workbook-viewer.css +44 -0
- package/src/styles-static.css +34 -23
- package/src/styles.css +34 -23
- package/src/stylesAreas.spec.ts +247 -0
- package/src/stylesUtilitiesMatches.spec.ts +6 -77
- package/src/testFilesRunInParallel.spec.ts +79 -0
- package/src/typecheckCachesAreSeparate.spec.ts +128 -0
- package/src/verifyGraph.spec.ts +6 -0
package/dist/styles.css
CHANGED
|
@@ -6,29 +6,32 @@
|
|
|
6
6
|
* This `@source` makes Tailwind v4 scan cursedbelt's source files so the utility
|
|
7
7
|
* classes used by its components are generated in the app's stylesheet.
|
|
8
8
|
*
|
|
9
|
-
* ──
|
|
9
|
+
* ── Three ways in, and which one an app wants ────────────────────────────────
|
|
10
10
|
* `@source "."` is the expensive half: it points Tailwind's JIT at cursedbelt's
|
|
11
11
|
* ENTIRE source tree, so the app emits the union of the whole design system
|
|
12
12
|
* whether it renders three components or eighty, and its own build walks 827
|
|
13
13
|
* files it does not own. Everything else in this file is CSS the components
|
|
14
14
|
* genuinely need and no app can do without.
|
|
15
15
|
*
|
|
16
|
-
* A CSS `@import` takes the whole file, so the split is
|
|
16
|
+
* A CSS `@import` takes the whole file, so the split is more subpaths:
|
|
17
17
|
*
|
|
18
|
-
* cursedbelt/styles.css
|
|
19
|
-
* cursedbelt/styles-static.css
|
|
20
|
-
* cursedbelt/styles-utilities.css
|
|
21
|
-
*
|
|
18
|
+
* cursedbelt/styles.css this file — the real CSS + the `@source` scan
|
|
19
|
+
* cursedbelt/styles-static.css the same file MINUS the `@source` line
|
|
20
|
+
* cursedbelt/styles-utilities.css the utilities that `@source` line would have
|
|
21
|
+
* produced, precompiled at cursedbelt's build
|
|
22
|
+
* cursedbelt/styles-areas/<area>.css the same utilities, split by component family
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* `styles-static.css` is never usable alone — on its own it gets the tooltip
|
|
25
|
+
* pointer-events fix, the Progress keyframes, the lightbox bridge, the
|
|
26
|
+
* ambient/glass layer and `tw-animate-css`, and emits none of cursedbelt's
|
|
27
|
+
* utility classes, i.e. an unstyled app. It pairs with EITHER of the other two:
|
|
27
28
|
*
|
|
28
|
-
* @import "tailwindcss";
|
|
29
|
-
* @import "cursedbelt/styles-static.css";
|
|
30
|
-
* @import "cursedbelt/styles-utilities.css";
|
|
31
|
-
* @import "cursedbelt/theme.css";
|
|
29
|
+
* @import "tailwindcss"; @import "tailwindcss";
|
|
30
|
+
* @import "cursedbelt/styles-static.css"; @import "cursedbelt/styles-static.css";
|
|
31
|
+
* @import "cursedbelt/styles-utilities.css"; @import "cursedbelt/styles-areas/core.css";
|
|
32
|
+
* @import "cursedbelt/theme.css"; @import "cursedbelt/styles-areas/data-table.css";
|
|
33
|
+
* @import "cursedbelt/theme.css";
|
|
34
|
+
* the whole design system what this app renders
|
|
32
35
|
*
|
|
33
36
|
* 🔴 Do NOT hand-list cursedbelt's component files to get the utilities back.
|
|
34
37
|
* Tailwind v4 resolves every `@source` to a DIRECTORY and scans it recursively —
|
|
@@ -38,22 +41,30 @@
|
|
|
38
41
|
* into this package's source tree is also a `file:` dependency wearing a
|
|
39
42
|
* stylesheet's clothes, and it buys nothing: it is the thing the previous
|
|
40
43
|
* generation's 29-line-per-app arrangement is remembered for, and those 29 lines
|
|
41
|
-
* were never file-level scoping either.
|
|
44
|
+
* were never file-level scoping either. Three apps hand-maintained such a list
|
|
45
|
+
* and one of them ran to 173 lines; all three were deleted, because every one of
|
|
46
|
+
* them was a directory list wearing a costume.
|
|
42
47
|
*
|
|
43
|
-
*
|
|
44
|
-
* rules within 54 bytes
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
+
* `styles-utilities.css` is not SMALLER than this file — measured, both routes
|
|
49
|
+
* emit the same rules within 54 bytes; the union is the union however it is
|
|
50
|
+
* assembled. What it buys is a build that scans nothing of another package's
|
|
51
|
+
* source tree. 🔴 What makes a consumer smaller is the AREAS, and only the
|
|
52
|
+
* package could build them: Tailwind cannot aim a `@source` at anything finer
|
|
53
|
+
* than a directory, but `@tailwindcss/oxide` can be handed a FILE LIST, which is
|
|
54
|
+
* what `scripts/generateAreaStyles.ts` does. Measured on the fixture consumer
|
|
55
|
+
* `demo/fixtures/lean-app`, the area route emits 30% less CSS than the union, and
|
|
56
|
+
* `src/fixtureAppCss.spec.ts` is the ratchet that stops it climbing back. The
|
|
57
|
+
* area map, and a sentence on what each area is for, is `scripts/styleAreas.ts`.
|
|
48
58
|
*
|
|
49
59
|
* 🔴 This header is in BOTH of the first two files, because one is generated from
|
|
50
60
|
* the other and a test holds them byte-identical apart from the `@source` line.
|
|
51
61
|
* Always edit `src/styles.css`; never `src/styles-static.css`, which
|
|
52
62
|
* `scripts/generateStaticStyles.ts` rewrites on every `bun run build` and
|
|
53
63
|
* `src/stylesStaticMatches.spec.ts` fails on when it is stale. Editing this file
|
|
54
|
-
* invalidates `src/styles-utilities.css`
|
|
55
|
-
*
|
|
56
|
-
* and `src/stylesUtilitiesMatches.spec.ts`
|
|
64
|
+
* invalidates `src/styles-utilities.css` and every `src/styles-areas/*.css` too —
|
|
65
|
+
* all of them are compiled against the static variant, `bun run styles` rewrites
|
|
66
|
+
* them in the same build step, and `src/stylesUtilitiesMatches.spec.ts` /
|
|
67
|
+
* `src/stylesAreas.spec.ts` are what fail when they are stale.
|
|
57
68
|
*/
|
|
58
69
|
|
|
59
70
|
/* Animation utilities (animate-in/out, fade/zoom/slide-in-from-*) consumed by the
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "The React design system of the cursedbelt split
|
|
6
|
+
"description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
|
|
7
7
|
"cursed": {
|
|
8
8
|
"schemaVersion": 1,
|
|
9
9
|
"app": {
|
|
@@ -20,30 +20,35 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"typecheck": "bun run typecheck:root && bun run typecheck:specs",
|
|
23
|
+
"typecheck": "bun run typecheck:root && bun run typecheck:specs && bun run typecheck:scripts",
|
|
24
24
|
"typecheck:root": "tsc --noEmit",
|
|
25
25
|
"typecheck:specs": "tsc --noEmit --project tsconfig.specs.json",
|
|
26
|
+
"typecheck:scripts": "tsc --noEmit --project tsconfig.scripts.json",
|
|
26
27
|
"demo:check": "tsc --noEmit --project demo/tsconfig.json",
|
|
27
28
|
"demo:paths": "bun run demo/generatePaths.ts --write",
|
|
28
29
|
"guardrails:report": "bun run scripts/guardrailsEnforce.ts no-raw-action-button,no-raw-overflow,no-raw-inputs,no-hardcoded-colors,no-raw-modal-footer,no-unguarded-editing-modal,control-height-tokens,no-raw-zindex,no-raw-data-table,no-tailwind-palette,no-raw-title-attr,micro-typography-floor,choice-needs-empty-state,no-select-as-action-menu,no-native-dialog,no-fast-tooltip --scope cursedbelt src/react src/styles.css",
|
|
29
30
|
"format": "biome format --write .",
|
|
30
|
-
"styles": "bun run styles:static && bun run styles:utilities",
|
|
31
|
+
"styles": "bun run styles:static && bun run styles:utilities && bun run styles:areas",
|
|
31
32
|
"styles:static": "bun run scripts/generateStaticStyles.ts",
|
|
32
33
|
"styles:utilities": "bun run scripts/generateUtilityStyles.ts",
|
|
33
|
-
"test": "NODE_ENV=development bun test src",
|
|
34
|
+
"test": "NODE_ENV=development bun test src --parallel=6",
|
|
34
35
|
"demo": "vite --config demo/vite.config.ts",
|
|
35
36
|
"demo:build": "vite build --config demo/vite.config.ts",
|
|
36
37
|
"demo:serve": "bun run demo:build && vite preview --config demo/vite.config.ts",
|
|
37
|
-
"build": "rm -rf dist.next dist.prev && bun run scripts/generateStaticStyles.ts && bun run scripts/generateUtilityStyles.ts && tsc --project tsconfig.build.json --outDir dist.next && CURSEDBELT_DIST_DIR=dist.next bun run scripts/copyDistAssets.ts && { [ -d dist ] && mv dist dist.prev || true; } && mv dist.next dist && rm -rf dist.prev",
|
|
38
|
+
"build": "rm -rf dist.next dist.prev && bun run scripts/generateStaticStyles.ts && bun run scripts/generateUtilityStyles.ts && bun run scripts/generateAreaStyles.ts && tsc --project tsconfig.build.json --outDir dist.next && tsc --project tsconfig.scripts.build.json --outDir dist.next/scripts && CURSEDBELT_DIST_DIR=dist.next bun run scripts/copyDistAssets.ts && CURSEDBELT_DIST_DIR=dist.next bun run scripts/checkDistExports.ts &&{ [ -d dist ] && mv dist dist.prev || true; } && mv dist.next dist && rm -rf dist.prev",
|
|
38
39
|
"e2e": "playwright test --config e2e/playwright.config.ts",
|
|
39
40
|
"e2e:filter-rail": "playwright test --config e2e/playwright.config.ts filter-rail.spec.ts",
|
|
40
41
|
"e2e:table-layout": "playwright test --config e2e/playwright.config.ts table-layout.spec.ts",
|
|
41
42
|
"e2e:stage-layout": "playwright test --config e2e/playwright.config.ts stage-layout.spec.ts",
|
|
43
|
+
"e2e:dialog-reach": "playwright test --config e2e/playwright.config.ts dialog-reach.spec.ts",
|
|
44
|
+
"e2e:video-turn": "playwright test --config e2e/playwright.config.ts video-turn.spec.ts",
|
|
45
|
+
"e2e:resize-grip": "playwright test --config e2e/playwright.config.ts resize-grip-rail.spec.ts",
|
|
42
46
|
"e2e:touch": "playwright test --config e2e/playwright.config.ts --project=touch",
|
|
43
47
|
"done:serial": "bun run typecheck && bun run lint && bun run test && bun run build",
|
|
44
48
|
"paths": "bun ../../tools/check-paths.ts .",
|
|
45
49
|
"verify": "bun run scripts/verify.ts",
|
|
46
|
-
"prepublishOnly": "bun run verify"
|
|
50
|
+
"prepublishOnly": "bun run verify",
|
|
51
|
+
"styles:areas": "bun run scripts/generateAreaStyles.ts"
|
|
47
52
|
},
|
|
48
53
|
"exports": {
|
|
49
54
|
".": {
|
|
@@ -53,7 +58,7 @@
|
|
|
53
58
|
"import": "./dist/index.js"
|
|
54
59
|
},
|
|
55
60
|
"./guardrails": {
|
|
56
|
-
"types": "./scripts/guardrailsEnforce.ts",
|
|
61
|
+
"types": "./dist/scripts/guardrailsEnforce.d.ts",
|
|
57
62
|
"bun": "./scripts/guardrailsEnforce.ts",
|
|
58
63
|
"source": "./scripts/guardrailsEnforce.ts",
|
|
59
64
|
"default": "./scripts/guardrailsEnforce.ts"
|
|
@@ -317,6 +322,49 @@
|
|
|
317
322
|
"import": "./dist/react/workbook-viewer/index.js"
|
|
318
323
|
},
|
|
319
324
|
"./styles-static.css": "./src/styles-static.css",
|
|
325
|
+
"./styles-areas/activity-bar.css": "./src/styles-areas/activity-bar.css",
|
|
326
|
+
"./styles-areas/analytics.css": "./src/styles-areas/analytics.css",
|
|
327
|
+
"./styles-areas/auth.css": "./src/styles-areas/auth.css",
|
|
328
|
+
"./styles-areas/calendar.css": "./src/styles-areas/calendar.css",
|
|
329
|
+
"./styles-areas/chart-table.css": "./src/styles-areas/chart-table.css",
|
|
330
|
+
"./styles-areas/charts.css": "./src/styles-areas/charts.css",
|
|
331
|
+
"./styles-areas/chat.css": "./src/styles-areas/chat.css",
|
|
332
|
+
"./styles-areas/clipboard.css": "./src/styles-areas/clipboard.css",
|
|
333
|
+
"./styles-areas/code-editor.css": "./src/styles-areas/code-editor.css",
|
|
334
|
+
"./styles-areas/companion-link.css": "./src/styles-areas/companion-link.css",
|
|
335
|
+
"./styles-areas/core.css": "./src/styles-areas/core.css",
|
|
336
|
+
"./styles-areas/dashboard-grid.css": "./src/styles-areas/dashboard-grid.css",
|
|
337
|
+
"./styles-areas/data-table.css": "./src/styles-areas/data-table.css",
|
|
338
|
+
"./styles-areas/deep-link.css": "./src/styles-areas/deep-link.css",
|
|
339
|
+
"./styles-areas/diff-viewer.css": "./src/styles-areas/diff-viewer.css",
|
|
340
|
+
"./styles-areas/disk-usage.css": "./src/styles-areas/disk-usage.css",
|
|
341
|
+
"./styles-areas/emoji.css": "./src/styles-areas/emoji.css",
|
|
342
|
+
"./styles-areas/fields.css": "./src/styles-areas/fields.css",
|
|
343
|
+
"./styles-areas/file-tree.css": "./src/styles-areas/file-tree.css",
|
|
344
|
+
"./styles-areas/filter-rail.css": "./src/styles-areas/filter-rail.css",
|
|
345
|
+
"./styles-areas/folder-tree.css": "./src/styles-areas/folder-tree.css",
|
|
346
|
+
"./styles-areas/keep-awake.css": "./src/styles-areas/keep-awake.css",
|
|
347
|
+
"./styles-areas/layout-engine.css": "./src/styles-areas/layout-engine.css",
|
|
348
|
+
"./styles-areas/markdown.css": "./src/styles-areas/markdown.css",
|
|
349
|
+
"./styles-areas/master-detail.css": "./src/styles-areas/master-detail.css",
|
|
350
|
+
"./styles-areas/master-lock.css": "./src/styles-areas/master-lock.css",
|
|
351
|
+
"./styles-areas/media.css": "./src/styles-areas/media.css",
|
|
352
|
+
"./styles-areas/media-gallery.css": "./src/styles-areas/media-gallery.css",
|
|
353
|
+
"./styles-areas/nav.css": "./src/styles-areas/nav.css",
|
|
354
|
+
"./styles-areas/notifications.css": "./src/styles-areas/notifications.css",
|
|
355
|
+
"./styles-areas/overlays.css": "./src/styles-areas/overlays.css",
|
|
356
|
+
"./styles-areas/palette.css": "./src/styles-areas/palette.css",
|
|
357
|
+
"./styles-areas/pdf-viewer.css": "./src/styles-areas/pdf-viewer.css",
|
|
358
|
+
"./styles-areas/problem-detail.css": "./src/styles-areas/problem-detail.css",
|
|
359
|
+
"./styles-areas/rich-text.css": "./src/styles-areas/rich-text.css",
|
|
360
|
+
"./styles-areas/sharing.css": "./src/styles-areas/sharing.css",
|
|
361
|
+
"./styles-areas/spreadsheet.css": "./src/styles-areas/spreadsheet.css",
|
|
362
|
+
"./styles-areas/stats.css": "./src/styles-areas/stats.css",
|
|
363
|
+
"./styles-areas/test-report.css": "./src/styles-areas/test-report.css",
|
|
364
|
+
"./styles-areas/virtual.css": "./src/styles-areas/virtual.css",
|
|
365
|
+
"./styles-areas/wizard.css": "./src/styles-areas/wizard.css",
|
|
366
|
+
"./styles-areas/workbench.css": "./src/styles-areas/workbench.css",
|
|
367
|
+
"./styles-areas/workbook-viewer.css": "./src/styles-areas/workbook-viewer.css",
|
|
320
368
|
"./styles-utilities.css": "./src/styles-utilities.css",
|
|
321
369
|
"./styles.css": "./src/styles.css",
|
|
322
370
|
"./testing": {
|
|
@@ -350,10 +398,14 @@
|
|
|
350
398
|
"@codemirror/legacy-modes": ">=6",
|
|
351
399
|
"@codemirror/state": ">=6",
|
|
352
400
|
"@codemirror/view": ">=6",
|
|
353
|
-
"@
|
|
401
|
+
"@emoji-mart/data": ">=1",
|
|
402
|
+
"@emoji-mart/react": ">=1",
|
|
354
403
|
"@ffmpeg/ffmpeg": ">=0.12",
|
|
355
404
|
"@ffmpeg/util": ">=0.12",
|
|
405
|
+
"@git-diff-view/react": ">=0.1.6",
|
|
356
406
|
"@glideapps/glide-data-grid": ">=6",
|
|
407
|
+
"@hookform/resolvers": ">=5",
|
|
408
|
+
"@lezer/highlight": ">=1",
|
|
357
409
|
"@tanstack/react-query": ">=5",
|
|
358
410
|
"@testing-library/react": ">=16",
|
|
359
411
|
"@tiptap/core": ">=3",
|
|
@@ -366,17 +418,21 @@
|
|
|
366
418
|
"@tiptap/react": ">=3",
|
|
367
419
|
"@tiptap/starter-kit": ">=3",
|
|
368
420
|
"@vidstack/react": ">=1",
|
|
421
|
+
"diff": ">=9",
|
|
422
|
+
"emoji-mart": ">=5",
|
|
369
423
|
"hls.js": ">=1",
|
|
370
424
|
"lowlight": ">=3",
|
|
425
|
+
"marked": ">=14",
|
|
426
|
+
"pdfjs-dist": ">=4",
|
|
371
427
|
"react": "^19",
|
|
372
428
|
"react-dom": "^19",
|
|
429
|
+
"react-grid-layout": "2.2.3",
|
|
373
430
|
"react-photo-album": ">=3",
|
|
374
431
|
"recharts": ">=3",
|
|
375
432
|
"tiptap-markdown": ">=0.9",
|
|
433
|
+
"xlsx": ">=0.20",
|
|
376
434
|
"yet-another-react-lightbox": ">=3",
|
|
377
|
-
"zod": "^4.4.0"
|
|
378
|
-
"pdfjs-dist": ">=4",
|
|
379
|
-
"xlsx": ">=0.20"
|
|
435
|
+
"zod": "^4.4.0"
|
|
380
436
|
},
|
|
381
437
|
"peerDependenciesMeta": {
|
|
382
438
|
"@codemirror/commands": {
|
|
@@ -406,7 +462,10 @@
|
|
|
406
462
|
"@codemirror/view": {
|
|
407
463
|
"optional": true
|
|
408
464
|
},
|
|
409
|
-
"@
|
|
465
|
+
"@emoji-mart/data": {
|
|
466
|
+
"optional": true
|
|
467
|
+
},
|
|
468
|
+
"@emoji-mart/react": {
|
|
410
469
|
"optional": true
|
|
411
470
|
},
|
|
412
471
|
"@ffmpeg/ffmpeg": {
|
|
@@ -415,9 +474,18 @@
|
|
|
415
474
|
"@ffmpeg/util": {
|
|
416
475
|
"optional": true
|
|
417
476
|
},
|
|
477
|
+
"@git-diff-view/react": {
|
|
478
|
+
"optional": true
|
|
479
|
+
},
|
|
418
480
|
"@glideapps/glide-data-grid": {
|
|
419
481
|
"optional": true
|
|
420
482
|
},
|
|
483
|
+
"@hookform/resolvers": {
|
|
484
|
+
"optional": true
|
|
485
|
+
},
|
|
486
|
+
"@lezer/highlight": {
|
|
487
|
+
"optional": true
|
|
488
|
+
},
|
|
421
489
|
"@testing-library/react": {
|
|
422
490
|
"optional": true
|
|
423
491
|
},
|
|
@@ -451,29 +519,41 @@
|
|
|
451
519
|
"@vidstack/react": {
|
|
452
520
|
"optional": true
|
|
453
521
|
},
|
|
522
|
+
"diff": {
|
|
523
|
+
"optional": true
|
|
524
|
+
},
|
|
525
|
+
"emoji-mart": {
|
|
526
|
+
"optional": true
|
|
527
|
+
},
|
|
454
528
|
"hls.js": {
|
|
455
529
|
"optional": true
|
|
456
530
|
},
|
|
457
531
|
"lowlight": {
|
|
458
532
|
"optional": true
|
|
459
533
|
},
|
|
460
|
-
"
|
|
534
|
+
"marked": {
|
|
461
535
|
"optional": true
|
|
462
536
|
},
|
|
463
|
-
"
|
|
537
|
+
"pdfjs-dist": {
|
|
464
538
|
"optional": true
|
|
465
539
|
},
|
|
466
|
-
"
|
|
540
|
+
"react-grid-layout": {
|
|
467
541
|
"optional": true
|
|
468
542
|
},
|
|
469
|
-
"
|
|
543
|
+
"react-photo-album": {
|
|
470
544
|
"optional": true
|
|
471
545
|
},
|
|
472
|
-
"
|
|
546
|
+
"recharts": {
|
|
547
|
+
"optional": true
|
|
548
|
+
},
|
|
549
|
+
"tiptap-markdown": {
|
|
473
550
|
"optional": true
|
|
474
551
|
},
|
|
475
552
|
"xlsx": {
|
|
476
553
|
"optional": true
|
|
554
|
+
},
|
|
555
|
+
"yet-another-react-lightbox": {
|
|
556
|
+
"optional": true
|
|
477
557
|
}
|
|
478
558
|
},
|
|
479
559
|
"devDependencies": {
|
|
@@ -490,12 +570,16 @@
|
|
|
490
570
|
"@codemirror/search": "^6.7.1",
|
|
491
571
|
"@codemirror/state": "^6.7.0",
|
|
492
572
|
"@codemirror/view": "^6.43.4",
|
|
573
|
+
"@emoji-mart/data": "^1.2.1",
|
|
574
|
+
"@emoji-mart/react": "^1.1.1",
|
|
493
575
|
"@ffmpeg/ffmpeg": "^0.12.15",
|
|
494
576
|
"@ffmpeg/util": "^0.12.2",
|
|
577
|
+
"@git-diff-view/react": "^0.1.6",
|
|
495
578
|
"@glideapps/glide-data-grid": "^6.0.3",
|
|
496
579
|
"@happy-dom/global-registrator": "^20.10.6",
|
|
580
|
+
"@hookform/resolvers": "^5.4.0",
|
|
497
581
|
"@lezer/highlight": "^1.2.3",
|
|
498
|
-
"@playwright/test": "
|
|
582
|
+
"@playwright/test": "1.62.1",
|
|
499
583
|
"@tailwindcss/node": "^4.3.1",
|
|
500
584
|
"@tailwindcss/oxide": "^4.3.1",
|
|
501
585
|
"@tailwindcss/vite": "^4.3.1",
|
|
@@ -520,12 +604,17 @@
|
|
|
520
604
|
"@types/react-dom": "^19.2.3",
|
|
521
605
|
"@vidstack/react": "1.15.6",
|
|
522
606
|
"@vitejs/plugin-react": "^5.2.0",
|
|
607
|
+
"cursedbelt-server": "^1.0.1",
|
|
608
|
+
"diff": "^9.0.0",
|
|
609
|
+
"emoji-mart": "^5.6.0",
|
|
523
610
|
"hls.js": "^1.5.0",
|
|
524
611
|
"hono": "4.12.28",
|
|
525
612
|
"lowlight": "^3.3.0",
|
|
613
|
+
"marked": "^14.0.0",
|
|
526
614
|
"pdfjs-dist": "^4.10.38",
|
|
527
615
|
"react": "19.2.7",
|
|
528
616
|
"react-dom": "19.2.7",
|
|
617
|
+
"react-grid-layout": "2.2.3",
|
|
529
618
|
"react-photo-album": "^3.6.0",
|
|
530
619
|
"recharts": "^3.8.1",
|
|
531
620
|
"tailwindcss": "^4.3.1",
|
|
@@ -534,8 +623,7 @@
|
|
|
534
623
|
"vite": "^7.3.6",
|
|
535
624
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
536
625
|
"yet-another-react-lightbox": "^3.32.0",
|
|
537
|
-
"zod": "4.4.3"
|
|
538
|
-
"cursedbelt-server": "^1.0.1"
|
|
626
|
+
"zod": "4.4.3"
|
|
539
627
|
},
|
|
540
628
|
"dependencies": {
|
|
541
629
|
"@atlaskit/pragmatic-drag-and-drop": "^2.0.1",
|
|
@@ -543,11 +631,7 @@
|
|
|
543
631
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^2.0.0",
|
|
544
632
|
"@base-ui/react": "^1.6.0",
|
|
545
633
|
"@daypicker/react": "^10.0.1",
|
|
546
|
-
"@emoji-mart/data": "^1.2.1",
|
|
547
|
-
"@emoji-mart/react": "^1.1.1",
|
|
548
634
|
"@floating-ui/react": "^0.27.19",
|
|
549
|
-
"@git-diff-view/react": "^0.1.6",
|
|
550
|
-
"@hookform/resolvers": "^5.4.0",
|
|
551
635
|
"@radix-ui/react-accordion": "^1.2.14",
|
|
552
636
|
"@radix-ui/react-checkbox": "^1.3.5",
|
|
553
637
|
"@radix-ui/react-collapsible": "^1.1.14",
|
|
@@ -568,19 +652,15 @@
|
|
|
568
652
|
"class-variance-authority": "^0.7.1",
|
|
569
653
|
"clsx": "^2.1.1",
|
|
570
654
|
"cmdk": "^1.1.1",
|
|
655
|
+
"cursedbelt-core": "^1.1.0",
|
|
571
656
|
"cwip": "^3.0.2",
|
|
572
|
-
"diff": "^9.0.0",
|
|
573
|
-
"emoji-mart": "^5.6.0",
|
|
574
657
|
"lucide-react": "^1.21.0",
|
|
575
|
-
"marked": "^14.0.0",
|
|
576
|
-
"react-grid-layout": "2.2.3",
|
|
577
658
|
"react-hook-form": "^7.80.0",
|
|
578
659
|
"react-resizable-panels": "^4.12.0",
|
|
579
660
|
"sonner": "^2.0.7",
|
|
580
661
|
"tailwind-merge": "^3.6.0",
|
|
581
662
|
"tw-animate-css": "^1.4.0",
|
|
582
|
-
"zustand": "^5.0.14"
|
|
583
|
-
"cursedbelt-core": "^1.1.0"
|
|
663
|
+
"zustand": "^5.0.14"
|
|
584
664
|
},
|
|
585
665
|
"publishConfig": {
|
|
586
666
|
"access": "public"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Every `exports` target that points into `dist/` must EXIST in the build that just ran.
|
|
4
|
+
*
|
|
5
|
+
* ── Why this is a build step and not a spec ────────────────────────────────────────
|
|
6
|
+
* A missing target does not throw. Node and tsc walk the conditions in order and take the
|
|
7
|
+
* first one that matches an ACTIVE condition — but a `types` (or `import`) target whose
|
|
8
|
+
* file is absent makes the resolver fall through to the next condition, which for this
|
|
9
|
+
* package is `bun`/`source`/`default`: the raw `.ts`. So an export that stops being built
|
|
10
|
+
* silently reverts to the exact defect task 172 fixed — a consumer's `tsc` compiling our
|
|
11
|
+
* source again — and every test in this repo stays green while it happens.
|
|
12
|
+
*
|
|
13
|
+
* It has to run inside `build`, against `CURSEDBELT_DIST_DIR`, because `dist/` is the one
|
|
14
|
+
* thing no spec here may read: `build` runs BESIDE `test` and `e2e` in the verify graph
|
|
15
|
+
* (`src/verifyGraph.spec.ts` asserts no spec opens `dist/`), so a spec looking at it would
|
|
16
|
+
* be reading a directory another stage is in the middle of swapping.
|
|
17
|
+
*
|
|
18
|
+
* Declarations are checked the same as runtime files on purpose — a `.d.ts` that was not
|
|
19
|
+
* emitted is not a smaller problem than a `.js` that was not emitted; it is a quieter one.
|
|
20
|
+
*/
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
|
|
23
|
+
const root = path.resolve(import.meta.dir, "..");
|
|
24
|
+
const outDir = process.env.CURSEDBELT_DIST_DIR || "dist";
|
|
25
|
+
|
|
26
|
+
const pkg = (await Bun.file(path.join(root, "package.json")).json()) as {
|
|
27
|
+
exports: Record<string, string | Record<string, string>>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** `[subpath, condition, target]` for every export target inside `dist/`. */
|
|
31
|
+
const distTargets: [string, string, string][] = [];
|
|
32
|
+
for (const [subpath, entry] of Object.entries(pkg.exports)) {
|
|
33
|
+
const conditions = typeof entry === "string" ? { default: entry } : entry;
|
|
34
|
+
for (const [condition, target] of Object.entries(conditions)) {
|
|
35
|
+
if (typeof target === "string" && target.startsWith("./dist/")) {
|
|
36
|
+
distTargets.push([subpath, condition, target]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// A manifest this script cannot read is a manifest it cannot check; passing silently on
|
|
42
|
+
// zero targets would make the whole step decorative.
|
|
43
|
+
if (distTargets.length === 0) {
|
|
44
|
+
console.error("🔴 no exports target resolves into dist/ — checkDistExports is checking nothing.");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const missing: string[] = [];
|
|
49
|
+
for (const [subpath, condition, target] of distTargets) {
|
|
50
|
+
// `./dist/react/index.js` → `<outDir>/react/index.js`, so a staged build (dist.next)
|
|
51
|
+
// is checked where it actually is rather than where it will be after the swap.
|
|
52
|
+
const staged = path.join(root, outDir, target.slice("./dist/".length));
|
|
53
|
+
if (!(await Bun.file(staged).exists())) {
|
|
54
|
+
missing.push(`${subpath} [${condition}] → ${target} (not emitted at ${outDir}/${target.slice(7)})`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (missing.length > 0) {
|
|
59
|
+
console.error(
|
|
60
|
+
`\n🔴 the build did not emit ${missing.length} file(s) package.json exports promise:\n ${missing.join("\n ")}\n\n` +
|
|
61
|
+
"A consumer does not get an error for these — the resolver falls through to the next\n" +
|
|
62
|
+
"condition, which is our raw `.ts`, and the consumer's tsc compiles our source instead.\n",
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`✅ all ${distTargets.length} dist export target(s) present in ${outDir}`);
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare two compiled stylesheets by the RULES they emit, not by their bytes.
|
|
3
|
+
*
|
|
4
|
+
* Every split this package has made to its CSS — `styles-static.css`,
|
|
5
|
+
* `styles-utilities.css`, and now `styles-areas/*.css` — can fail in exactly one
|
|
6
|
+
* way that matters: the new route emits FEWER rules than the old one, so some
|
|
7
|
+
* component renders unstyled in the apps that adopted it. That failure is
|
|
8
|
+
* invisible to `tsc` (it is CSS), to Biome (nothing is wrong with the file), and
|
|
9
|
+
* to a byte count read in the only direction anybody reads it, because a smaller
|
|
10
|
+
* stylesheet is what the whole exercise was asked for.
|
|
11
|
+
*
|
|
12
|
+
* So the specs compare rule sets. These helpers were written inside
|
|
13
|
+
* `src/stylesUtilitiesMatches.spec.ts` and live here now because
|
|
14
|
+
* `src/stylesAreas.spec.ts` needs the same comparison against the same two
|
|
15
|
+
* normalisations — a second copy of a CSS flattener is a second copy that can
|
|
16
|
+
* drift from the assertions relying on it.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Flatten a stylesheet into `at-rule ‖ … ‖ selector` → the set of declarations in
|
|
21
|
+
* that block, merging blocks that repeat (both routes emit `@keyframes spin`
|
|
22
|
+
* twice; a consumer's browser does not care, and neither should a comparison).
|
|
23
|
+
*
|
|
24
|
+
* A hand-rolled walk rather than a CSS parser because the only input is Tailwind's
|
|
25
|
+
* own output: well-formed, and the one construct that could confuse brace counting
|
|
26
|
+
* — a `{` inside a string or a comment — is skipped explicitly.
|
|
27
|
+
*/
|
|
28
|
+
export const flatten = (css: string): Map<string, Set<string>> => {
|
|
29
|
+
const out = new Map<string, Set<string>>();
|
|
30
|
+
const stack: string[] = [];
|
|
31
|
+
const open: string[][] = [[]];
|
|
32
|
+
let buf = '';
|
|
33
|
+
const push = (raw: string): void => {
|
|
34
|
+
const decl = raw.trim();
|
|
35
|
+
if (decl) open[open.length - 1]?.push(decl);
|
|
36
|
+
};
|
|
37
|
+
for (let i = 0; i < css.length; i++) {
|
|
38
|
+
const ch = css[i];
|
|
39
|
+
if (ch === '/' && css[i + 1] === '*') {
|
|
40
|
+
i = css.indexOf('*/', i) + 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (ch === '"' || ch === "'") {
|
|
44
|
+
const end = css.indexOf(ch, i + 1);
|
|
45
|
+
buf += css.slice(i, end + 1);
|
|
46
|
+
i = end;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (ch === '{') {
|
|
50
|
+
stack.push(buf.trim().replace(/\s+/g, ' '));
|
|
51
|
+
buf = '';
|
|
52
|
+
open.push([]);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (ch === '}') {
|
|
56
|
+
push(buf);
|
|
57
|
+
buf = '';
|
|
58
|
+
const key = stack.join(' ‖ ');
|
|
59
|
+
const set = out.get(key) ?? new Set<string>();
|
|
60
|
+
for (const decl of open.pop() ?? []) set.add(decl);
|
|
61
|
+
out.set(key, set);
|
|
62
|
+
stack.pop();
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// At depth 0 a `;` ends a statement at-rule (`@layer a, b;`, `@import …`),
|
|
66
|
+
// which carries no declarations and must not stick to the next selector.
|
|
67
|
+
if (ch === ';') {
|
|
68
|
+
if (stack.length) push(buf);
|
|
69
|
+
buf = '';
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
buf += ch;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** `var(--x, <anything>)` → `var(--x)`, so a fallback reads as the same declaration. */
|
|
78
|
+
export const dropFallbacks = (decl: string): string => decl.replace(/var\((\s*--[\w-]+)[^)]*\)/g, 'var($1)');
|
|
79
|
+
|
|
80
|
+
export const CUSTOM_PROPERTY = /^(--[\w-]+)\s*:/;
|
|
81
|
+
|
|
82
|
+
/** Every custom property a stylesheet actually reads through `var()`. */
|
|
83
|
+
export const referenced = (css: string): Set<string> => {
|
|
84
|
+
const out = new Set<string>();
|
|
85
|
+
for (const m of css.matchAll(/var\(\s*(--[\w-]+)/g)) out.add(m[1] as string);
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Every custom property a stylesheet declares, including `@property` registrations. */
|
|
90
|
+
export const declared = (css: string): Set<string> => {
|
|
91
|
+
const out = new Set<string>();
|
|
92
|
+
for (const m of css.matchAll(/(?:^|[;{\s])(--[\w-]+)\s*:/g)) out.add(m[1] as string);
|
|
93
|
+
for (const m of css.matchAll(/@property\s+(--[\w-]+)/g)) out.add(m[1] as string);
|
|
94
|
+
return out;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Rules and declarations `before` emits that `after` does not — the whole point.
|
|
99
|
+
*
|
|
100
|
+
* `ignoreProperty` is how a caller excuses the `@theme inline` alias declarations
|
|
101
|
+
* that one route writes into `:root` and nothing ever reads; everything else is a
|
|
102
|
+
* real difference.
|
|
103
|
+
*/
|
|
104
|
+
export const lostRules = (
|
|
105
|
+
before: Map<string, Set<string>>,
|
|
106
|
+
after: Map<string, Set<string>>,
|
|
107
|
+
ignoreProperty: (prop: string) => boolean = () => false,
|
|
108
|
+
): { rules: string[]; declarations: string[] } => {
|
|
109
|
+
const rules: string[] = [];
|
|
110
|
+
const declarations: string[] = [];
|
|
111
|
+
for (const [key, decls] of before) {
|
|
112
|
+
const mine = after.get(key);
|
|
113
|
+
if (!mine) {
|
|
114
|
+
rules.push(key);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const theirs = new Set([...mine].map(dropFallbacks));
|
|
118
|
+
for (const decl of decls) {
|
|
119
|
+
const prop = CUSTOM_PROPERTY.exec(decl)?.[1];
|
|
120
|
+
if (prop && ignoreProperty(prop)) continue;
|
|
121
|
+
if (!theirs.has(dropFallbacks(decl))) declarations.push(`${key} → ${decl}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { rules, declarations };
|
|
125
|
+
};
|