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.
Files changed (250) hide show
  1. package/dist/react/components/ListToolbar.d.ts +94 -0
  2. package/dist/react/components/ListToolbar.d.ts.map +1 -0
  3. package/dist/react/components/ListToolbar.js +66 -0
  4. package/dist/react/components/ListToolbar.js.map +1 -0
  5. package/dist/react/file-tree/FileTree.d.ts +282 -3
  6. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  7. package/dist/react/file-tree/FileTree.js +799 -131
  8. package/dist/react/file-tree/FileTree.js.map +1 -1
  9. package/dist/react/file-tree/fileTree.css +151 -0
  10. package/dist/react/file-tree/fileTreeSelection.d.ts +187 -0
  11. package/dist/react/file-tree/fileTreeSelection.d.ts.map +1 -0
  12. package/dist/react/file-tree/fileTreeSelection.js +264 -0
  13. package/dist/react/file-tree/fileTreeSelection.js.map +1 -0
  14. package/dist/react/file-tree/index.d.ts +1 -0
  15. package/dist/react/file-tree/index.d.ts.map +1 -1
  16. package/dist/react/file-tree/index.js +8 -0
  17. package/dist/react/file-tree/index.js.map +1 -1
  18. package/dist/react/index.d.ts +1 -0
  19. package/dist/react/index.d.ts.map +1 -1
  20. package/dist/react/index.js +1 -0
  21. package/dist/react/index.js.map +1 -1
  22. package/dist/react/lib/form.d.ts +3 -0
  23. package/dist/react/lib/form.d.ts.map +1 -1
  24. package/dist/react/lib/form.js +43 -5
  25. package/dist/react/lib/form.js.map +1 -1
  26. package/dist/react/media/DenseGalleryShell.d.ts +19 -3
  27. package/dist/react/media/DenseGalleryShell.d.ts.map +1 -1
  28. package/dist/react/media/DenseGalleryShell.js +55 -15
  29. package/dist/react/media/DenseGalleryShell.js.map +1 -1
  30. package/dist/react/media/FocusViewer.d.ts.map +1 -1
  31. package/dist/react/media/FocusViewer.js +14 -14
  32. package/dist/react/media/FocusViewer.js.map +1 -1
  33. package/dist/react/media/FrameGrabber.d.ts +23 -0
  34. package/dist/react/media/FrameGrabber.d.ts.map +1 -1
  35. package/dist/react/media/FrameGrabber.js +67 -1
  36. package/dist/react/media/FrameGrabber.js.map +1 -1
  37. package/dist/react/media/MediaResize.d.ts +51 -19
  38. package/dist/react/media/MediaResize.d.ts.map +1 -1
  39. package/dist/react/media/MediaResize.js +20 -13
  40. package/dist/react/media/MediaResize.js.map +1 -1
  41. package/dist/react/media/VideoChapterEditor.d.ts +67 -0
  42. package/dist/react/media/VideoChapterEditor.d.ts.map +1 -0
  43. package/dist/react/media/VideoChapterEditor.js +148 -0
  44. package/dist/react/media/VideoChapterEditor.js.map +1 -0
  45. package/dist/react/media/VideoPlayer.d.ts +46 -4
  46. package/dist/react/media/VideoPlayer.d.ts.map +1 -1
  47. package/dist/react/media/VideoPlayer.js +124 -11
  48. package/dist/react/media/VideoPlayer.js.map +1 -1
  49. package/dist/react/media/VideoTurner.d.ts +15 -2
  50. package/dist/react/media/VideoTurner.d.ts.map +1 -1
  51. package/dist/react/media/VideoTurner.js +29 -2
  52. package/dist/react/media/VideoTurner.js.map +1 -1
  53. package/dist/react/media/denseGallery.css +104 -46
  54. package/dist/react/media/hlsSource.d.ts +57 -0
  55. package/dist/react/media/hlsSource.d.ts.map +1 -1
  56. package/dist/react/media/hlsSource.js +177 -1
  57. package/dist/react/media/hlsSource.js.map +1 -1
  58. package/dist/react/media/index.d.ts +2 -0
  59. package/dist/react/media/index.d.ts.map +1 -1
  60. package/dist/react/media/index.js +6 -0
  61. package/dist/react/media/index.js.map +1 -1
  62. package/dist/react/media/mediaResize.css +127 -54
  63. package/dist/react/media/pictureEditor.css +52 -6
  64. package/dist/react/media/videoChapterEditor.css +203 -0
  65. package/dist/react/media/videoChapters.d.ts +117 -0
  66. package/dist/react/media/videoChapters.d.ts.map +1 -0
  67. package/dist/react/media/videoChapters.js +95 -0
  68. package/dist/react/media/videoChapters.js.map +1 -0
  69. package/dist/react/media-gallery/GalleryTable.d.ts +52 -1
  70. package/dist/react/media-gallery/GalleryTable.d.ts.map +1 -1
  71. package/dist/react/media-gallery/GalleryTable.js +40 -9
  72. package/dist/react/media-gallery/GalleryTable.js.map +1 -1
  73. package/dist/react/media-gallery/MediaGallery.d.ts +107 -4
  74. package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
  75. package/dist/react/media-gallery/MediaGallery.js +484 -104
  76. package/dist/react/media-gallery/MediaGallery.js.map +1 -1
  77. package/dist/react/media-gallery/MediaMetaEditor.d.ts +48 -0
  78. package/dist/react/media-gallery/MediaMetaEditor.d.ts.map +1 -0
  79. package/dist/react/media-gallery/MediaMetaEditor.js +66 -0
  80. package/dist/react/media-gallery/MediaMetaEditor.js.map +1 -0
  81. package/dist/react/media-gallery/index.d.ts +2 -0
  82. package/dist/react/media-gallery/index.d.ts.map +1 -1
  83. package/dist/react/media-gallery/index.js +5 -0
  84. package/dist/react/media-gallery/index.js.map +1 -1
  85. package/dist/react/media-gallery/mediaGallery.css +180 -4
  86. package/dist/react/media-gallery/mediaMeta.d.ts +62 -0
  87. package/dist/react/media-gallery/mediaMeta.d.ts.map +1 -0
  88. package/dist/react/media-gallery/mediaMeta.js +56 -0
  89. package/dist/react/media-gallery/mediaMeta.js.map +1 -0
  90. package/dist/react/media-gallery/playbackPreferences.d.ts +77 -0
  91. package/dist/react/media-gallery/playbackPreferences.d.ts.map +1 -1
  92. package/dist/react/media-gallery/playbackPreferences.js +42 -0
  93. package/dist/react/media-gallery/playbackPreferences.js.map +1 -1
  94. package/dist/react/media-gallery/types.d.ts +34 -0
  95. package/dist/react/media-gallery/types.d.ts.map +1 -1
  96. package/dist/scripts/guardrailsEnforce.d.ts +29 -0
  97. package/dist/scripts/guardrailsEnforce.d.ts.map +1 -0
  98. package/dist/styles-areas/activity-bar.css +30 -0
  99. package/dist/styles-areas/analytics.css +672 -0
  100. package/dist/styles-areas/auth.css +154 -0
  101. package/dist/styles-areas/calendar.css +416 -0
  102. package/dist/styles-areas/chart-table.css +41 -0
  103. package/dist/styles-areas/charts.css +89 -0
  104. package/dist/styles-areas/chat.css +364 -0
  105. package/dist/styles-areas/clipboard.css +26 -0
  106. package/dist/styles-areas/code-editor.css +130 -0
  107. package/dist/styles-areas/companion-link.css +70 -0
  108. package/dist/styles-areas/core.css +2503 -0
  109. package/dist/styles-areas/dashboard-grid.css +84 -0
  110. package/dist/styles-areas/data-table.css +657 -0
  111. package/dist/styles-areas/deep-link.css +26 -0
  112. package/dist/styles-areas/diff-viewer.css +30 -0
  113. package/dist/styles-areas/disk-usage.css +262 -0
  114. package/dist/styles-areas/emoji.css +154 -0
  115. package/dist/styles-areas/fields.css +582 -0
  116. package/dist/styles-areas/file-tree.css +33 -0
  117. package/dist/styles-areas/filter-rail.css +235 -0
  118. package/dist/styles-areas/folder-tree.css +219 -0
  119. package/dist/styles-areas/keep-awake.css +26 -0
  120. package/dist/styles-areas/layout-engine.css +239 -0
  121. package/dist/styles-areas/markdown.css +87 -0
  122. package/dist/styles-areas/master-detail.css +239 -0
  123. package/dist/styles-areas/master-lock.css +26 -0
  124. package/dist/styles-areas/media-gallery.css +351 -0
  125. package/dist/styles-areas/media.css +658 -0
  126. package/dist/styles-areas/nav.css +488 -0
  127. package/dist/styles-areas/notifications.css +245 -0
  128. package/dist/styles-areas/overlays.css +133 -0
  129. package/dist/styles-areas/palette.css +140 -0
  130. package/dist/styles-areas/pdf-viewer.css +47 -0
  131. package/dist/styles-areas/problem-detail.css +97 -0
  132. package/dist/styles-areas/rich-text.css +167 -0
  133. package/dist/styles-areas/sharing.css +153 -0
  134. package/dist/styles-areas/spreadsheet.css +37 -0
  135. package/dist/styles-areas/stats.css +281 -0
  136. package/dist/styles-areas/test-report.css +133 -0
  137. package/dist/styles-areas/virtual.css +30 -0
  138. package/dist/styles-areas/wizard.css +381 -0
  139. package/dist/styles-areas/workbench.css +543 -0
  140. package/dist/styles-areas/workbook-viewer.css +44 -0
  141. package/dist/styles-static.css +34 -23
  142. package/dist/styles.css +34 -23
  143. package/package.json +111 -31
  144. package/scripts/checkDistExports.ts +67 -0
  145. package/scripts/cssRules.ts +125 -0
  146. package/scripts/fixtureAppCss.ts +344 -0
  147. package/scripts/generateAreaStyles.ts +253 -0
  148. package/scripts/guardrailsEnforce.spec.ts +62 -0
  149. package/scripts/guardrailsEnforce.ts +67 -3
  150. package/scripts/styleAreas.ts +585 -0
  151. package/scripts/verify.ts +5 -0
  152. package/src/barrelsReachNoOptionalPeer.spec.ts +117 -9
  153. package/src/docsMatchTheSplit.spec.ts +98 -0
  154. package/src/fixtureAppCss.spec.ts +177 -0
  155. package/src/namedSubpathsResolve.spec.ts +35 -0
  156. package/src/publishShape.spec.ts +67 -0
  157. package/src/react/components/ListToolbar.spec.tsx +172 -0
  158. package/src/react/components/ListToolbar.tsx +151 -0
  159. package/src/react/file-tree/FileTree.spec.tsx +1009 -0
  160. package/src/react/file-tree/FileTree.tsx +1459 -309
  161. package/src/react/file-tree/fileTree.css +151 -0
  162. package/src/react/file-tree/fileTreeSelection.spec.ts +327 -0
  163. package/src/react/file-tree/fileTreeSelection.ts +321 -0
  164. package/src/react/file-tree/index.ts +21 -0
  165. package/src/react/index.ts +1 -0
  166. package/src/react/lib/form.ts +51 -8
  167. package/src/react/media/DenseGalleryShell.tsx +108 -36
  168. package/src/react/media/FocusViewer.tsx +15 -8
  169. package/src/react/media/FrameGrabber.spec.tsx +175 -0
  170. package/src/react/media/FrameGrabber.tsx +67 -0
  171. package/src/react/media/MediaResize.spec.tsx +84 -14
  172. package/src/react/media/MediaResize.tsx +51 -19
  173. package/src/react/media/VideoChapterEditor.tsx +428 -0
  174. package/src/react/media/VideoPlayer.spec.tsx +111 -1
  175. package/src/react/media/VideoPlayer.tsx +158 -11
  176. package/src/react/media/VideoTurner.spec.tsx +67 -0
  177. package/src/react/media/VideoTurner.tsx +70 -15
  178. package/src/react/media/denseGallery.css +104 -46
  179. package/src/react/media/hlsSource.recovery.spec.ts +213 -0
  180. package/src/react/media/hlsSource.spec.ts +8 -0
  181. package/src/react/media/hlsSource.ts +189 -1
  182. package/src/react/media/index.ts +14 -0
  183. package/src/react/media/mediaResize.css +127 -54
  184. package/src/react/media/pictureEditor.css +52 -6
  185. package/src/react/media/videoChapterEditor.css +203 -0
  186. package/src/react/media/videoChapters.spec.ts +176 -0
  187. package/src/react/media/videoChapters.ts +181 -0
  188. package/src/react/media-gallery/GalleryTable.tsx +93 -3
  189. package/src/react/media-gallery/MediaGallery.spec.tsx +1156 -10
  190. package/src/react/media-gallery/MediaGallery.tsx +813 -95
  191. package/src/react/media-gallery/MediaMetaEditor.tsx +237 -0
  192. package/src/react/media-gallery/galleryTable.spec.ts +41 -1
  193. package/src/react/media-gallery/index.ts +7 -0
  194. package/src/react/media-gallery/mediaGallery.css +180 -4
  195. package/src/react/media-gallery/mediaMeta.spec.ts +95 -0
  196. package/src/react/media-gallery/mediaMeta.ts +101 -0
  197. package/src/react/media-gallery/playbackPreferences.spec.ts +172 -0
  198. package/src/react/media-gallery/playbackPreferences.ts +100 -0
  199. package/src/react/media-gallery/types.ts +34 -0
  200. package/src/shippedFilesAreTracked.spec.ts +69 -0
  201. package/src/styles-areas/activity-bar.css +30 -0
  202. package/src/styles-areas/analytics.css +672 -0
  203. package/src/styles-areas/auth.css +154 -0
  204. package/src/styles-areas/calendar.css +416 -0
  205. package/src/styles-areas/chart-table.css +41 -0
  206. package/src/styles-areas/charts.css +89 -0
  207. package/src/styles-areas/chat.css +364 -0
  208. package/src/styles-areas/clipboard.css +26 -0
  209. package/src/styles-areas/code-editor.css +130 -0
  210. package/src/styles-areas/companion-link.css +70 -0
  211. package/src/styles-areas/core.css +2503 -0
  212. package/src/styles-areas/dashboard-grid.css +84 -0
  213. package/src/styles-areas/data-table.css +657 -0
  214. package/src/styles-areas/deep-link.css +26 -0
  215. package/src/styles-areas/diff-viewer.css +30 -0
  216. package/src/styles-areas/disk-usage.css +262 -0
  217. package/src/styles-areas/emoji.css +154 -0
  218. package/src/styles-areas/fields.css +582 -0
  219. package/src/styles-areas/file-tree.css +33 -0
  220. package/src/styles-areas/filter-rail.css +235 -0
  221. package/src/styles-areas/folder-tree.css +219 -0
  222. package/src/styles-areas/keep-awake.css +26 -0
  223. package/src/styles-areas/layout-engine.css +239 -0
  224. package/src/styles-areas/markdown.css +87 -0
  225. package/src/styles-areas/master-detail.css +239 -0
  226. package/src/styles-areas/master-lock.css +26 -0
  227. package/src/styles-areas/media-gallery.css +351 -0
  228. package/src/styles-areas/media.css +658 -0
  229. package/src/styles-areas/nav.css +488 -0
  230. package/src/styles-areas/notifications.css +245 -0
  231. package/src/styles-areas/overlays.css +133 -0
  232. package/src/styles-areas/palette.css +140 -0
  233. package/src/styles-areas/pdf-viewer.css +47 -0
  234. package/src/styles-areas/problem-detail.css +97 -0
  235. package/src/styles-areas/rich-text.css +167 -0
  236. package/src/styles-areas/sharing.css +153 -0
  237. package/src/styles-areas/spreadsheet.css +37 -0
  238. package/src/styles-areas/stats.css +281 -0
  239. package/src/styles-areas/test-report.css +133 -0
  240. package/src/styles-areas/virtual.css +30 -0
  241. package/src/styles-areas/wizard.css +381 -0
  242. package/src/styles-areas/workbench.css +543 -0
  243. package/src/styles-areas/workbook-viewer.css +44 -0
  244. package/src/styles-static.css +34 -23
  245. package/src/styles.css +34 -23
  246. package/src/stylesAreas.spec.ts +247 -0
  247. package/src/stylesUtilitiesMatches.spec.ts +6 -77
  248. package/src/testFilesRunInParallel.spec.ts +79 -0
  249. package/src/typecheckCachesAreSeparate.spec.ts +128 -0
  250. 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
- * ── Two ways in, and which one an app wants ──────────────────────────────────
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 two more subpaths:
16
+ * A CSS `@import` takes the whole file, so the split is more subpaths:
17
17
  *
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
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
- * The second two are a PAIR and neither is usable alone — `styles-static.css` on
24
- * its own gets the tooltip pointer-events fix, the Progress keyframes, the
25
- * lightbox bridge, the ambient/glass layer and `tw-animate-css`, and emits none
26
- * of cursedbelt's utility classes, i.e. an unstyled app:
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
- * The pair is not SMALLER than this file — measured, both routes emit the same
44
- * rules within 54 bytes. What it buys is a build that scans nothing of another
45
- * package's source tree, and a `styles-static.css` that finally has a correct
46
- * use. The measurements, and what would actually shrink a consumer, are in the
47
- * header of `scripts/generateUtilityStyles.ts`.
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` too — it is compiled against the static
55
- * variant, `scripts/generateUtilityStyles.ts` rewrites it in the same build step,
56
- * and `src/stylesUtilitiesMatches.spec.ts` is what fails when it is stale.
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.0.2",
3
+ "version": "4.0.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
- "description": "The React design system of the cursedbelt split \u2014 components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
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
- "@lezer/highlight": ">=1",
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
- "@lezer/highlight": {
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
- "react-photo-album": {
534
+ "marked": {
461
535
  "optional": true
462
536
  },
463
- "recharts": {
537
+ "pdfjs-dist": {
464
538
  "optional": true
465
539
  },
466
- "tiptap-markdown": {
540
+ "react-grid-layout": {
467
541
  "optional": true
468
542
  },
469
- "yet-another-react-lightbox": {
543
+ "react-photo-album": {
470
544
  "optional": true
471
545
  },
472
- "pdfjs-dist": {
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": "^1.61.0",
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
+ };