cursedbelt 1.0.0 → 2.3.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 (533) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/core/category/index.d.ts.map +1 -1
  3. package/dist/core/category/index.js +2 -1
  4. package/dist/core/category/index.js.map +1 -1
  5. package/dist/core/category/types.js +1 -1
  6. package/dist/core/category/types.js.map +1 -1
  7. package/dist/core/file-tree/fileTreeModel.d.ts +49 -0
  8. package/dist/core/file-tree/fileTreeModel.d.ts.map +1 -1
  9. package/dist/core/file-tree/fileTreeModel.js +84 -0
  10. package/dist/core/file-tree/fileTreeModel.js.map +1 -1
  11. package/dist/core/layout/index.js +1 -1
  12. package/dist/core/layout/index.js.map +1 -1
  13. package/dist/core/media/index.js +1 -1
  14. package/dist/core/media/index.js.map +1 -1
  15. package/dist/core/media/types.js +1 -1
  16. package/dist/core/media/types.js.map +1 -1
  17. package/dist/core/media/uploadLimits.js +1 -1
  18. package/dist/core/media/uploadLimits.js.map +1 -1
  19. package/dist/core/sharing/model.d.ts +1 -1
  20. package/dist/ctgr/index.js +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/react/activity-bar/ActivityBar.d.ts +17 -0
  24. package/dist/react/activity-bar/ActivityBar.d.ts.map +1 -1
  25. package/dist/react/activity-bar/ActivityBar.js.map +1 -1
  26. package/dist/react/analytics/index.d.ts +1 -1
  27. package/dist/react/analytics/index.js +1 -1
  28. package/dist/react/chat/MessageComposer.d.ts.map +1 -1
  29. package/dist/react/chat/MessageComposer.js +5 -4
  30. package/dist/react/chat/MessageComposer.js.map +1 -1
  31. package/dist/react/components/Button.d.ts.map +1 -1
  32. package/dist/react/components/Button.js +3 -2
  33. package/dist/react/components/Button.js.map +1 -1
  34. package/dist/react/components/Checkbox.d.ts +19 -3
  35. package/dist/react/components/Checkbox.d.ts.map +1 -1
  36. package/dist/react/components/Checkbox.js +29 -12
  37. package/dist/react/components/Checkbox.js.map +1 -1
  38. package/dist/react/components/CodeArea.d.ts.map +1 -1
  39. package/dist/react/components/CodeArea.js +2 -1
  40. package/dist/react/components/CodeArea.js.map +1 -1
  41. package/dist/react/components/Collapsible.d.ts.map +1 -1
  42. package/dist/react/components/Collapsible.js +2 -1
  43. package/dist/react/components/Collapsible.js.map +1 -1
  44. package/dist/react/components/ComboboxField.d.ts.map +1 -1
  45. package/dist/react/components/ComboboxField.js +3 -2
  46. package/dist/react/components/ComboboxField.js.map +1 -1
  47. package/dist/react/components/CopyButton.d.ts.map +1 -1
  48. package/dist/react/components/CopyButton.js +2 -1
  49. package/dist/react/components/CopyButton.js.map +1 -1
  50. package/dist/react/components/DataTable/ColumnChecklist.d.ts.map +1 -1
  51. package/dist/react/components/DataTable/ColumnChecklist.js +2 -1
  52. package/dist/react/components/DataTable/ColumnChecklist.js.map +1 -1
  53. package/dist/react/components/DataTable/Pagination.d.ts.map +1 -1
  54. package/dist/react/components/DataTable/Pagination.js +2 -1
  55. package/dist/react/components/DataTable/Pagination.js.map +1 -1
  56. package/dist/react/components/DataTable/SortHeader.d.ts.map +1 -1
  57. package/dist/react/components/DataTable/SortHeader.js +2 -1
  58. package/dist/react/components/DataTable/SortHeader.js.map +1 -1
  59. package/dist/react/components/DataTable/TableFilterBuilder.d.ts +61 -0
  60. package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -0
  61. package/dist/react/components/DataTable/{FilterBuilder.js → TableFilterBuilder.js} +26 -3
  62. package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -0
  63. package/dist/react/components/DataTable/Toolbar.d.ts +1 -1
  64. package/dist/react/components/DataTable/Toolbar.js +3 -3
  65. package/dist/react/components/DataTable/Toolbar.js.map +1 -1
  66. package/dist/react/components/DataTable/cells/LinkCell.d.ts.map +1 -1
  67. package/dist/react/components/DataTable/cells/LinkCell.js +2 -1
  68. package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
  69. package/dist/react/components/DataTable/index.d.ts +1 -1
  70. package/dist/react/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/react/components/DataTable/index.js +4 -1
  72. package/dist/react/components/DataTable/index.js.map +1 -1
  73. package/dist/react/components/DragHandle.d.ts.map +1 -1
  74. package/dist/react/components/DragHandle.js +2 -1
  75. package/dist/react/components/DragHandle.js.map +1 -1
  76. package/dist/react/components/EmojiField.d.ts.map +1 -1
  77. package/dist/react/components/EmojiField.js +2 -1
  78. package/dist/react/components/EmojiField.js.map +1 -1
  79. package/dist/react/components/EventTimeline.d.ts +3 -2
  80. package/dist/react/components/EventTimeline.d.ts.map +1 -1
  81. package/dist/react/components/EventTimeline.js +3 -2
  82. package/dist/react/components/EventTimeline.js.map +1 -1
  83. package/dist/react/components/IconButton.d.ts +1 -1
  84. package/dist/react/components/IconButton.js +1 -1
  85. package/dist/react/components/InfoHint.d.ts.map +1 -1
  86. package/dist/react/components/InfoHint.js +5 -4
  87. package/dist/react/components/InfoHint.js.map +1 -1
  88. package/dist/react/components/InheritBadge.js +1 -1
  89. package/dist/react/components/InheritBadge.js.map +1 -1
  90. package/dist/react/components/InlineEditableText.d.ts.map +1 -1
  91. package/dist/react/components/InlineEditableText.js +2 -1
  92. package/dist/react/components/InlineEditableText.js.map +1 -1
  93. package/dist/react/components/MeterList.d.ts.map +1 -1
  94. package/dist/react/components/MeterList.js +2 -1
  95. package/dist/react/components/MeterList.js.map +1 -1
  96. package/dist/react/components/PathText.d.ts +2 -2
  97. package/dist/react/components/PathText.d.ts.map +1 -1
  98. package/dist/react/components/PathText.js +2 -1
  99. package/dist/react/components/PathText.js.map +1 -1
  100. package/dist/react/components/RatingField.d.ts.map +1 -1
  101. package/dist/react/components/RatingField.js +2 -1
  102. package/dist/react/components/RatingField.js.map +1 -1
  103. package/dist/react/components/SecretInput.js +3 -2
  104. package/dist/react/components/SecretInput.js.map +1 -1
  105. package/dist/react/components/SegmentedControl.d.ts.map +1 -1
  106. package/dist/react/components/SegmentedControl.js +2 -1
  107. package/dist/react/components/SegmentedControl.js.map +1 -1
  108. package/dist/react/components/StarRating.d.ts.map +1 -1
  109. package/dist/react/components/StarRating.js +9 -5
  110. package/dist/react/components/StarRating.js.map +1 -1
  111. package/dist/react/components/StatStrip.d.ts.map +1 -1
  112. package/dist/react/components/StatStrip.js +2 -1
  113. package/dist/react/components/StatStrip.js.map +1 -1
  114. package/dist/react/components/StatTile.d.ts.map +1 -1
  115. package/dist/react/components/StatTile.js +3 -2
  116. package/dist/react/components/StatTile.js.map +1 -1
  117. package/dist/react/components/Tabs.d.ts.map +1 -1
  118. package/dist/react/components/Tabs.js +2 -1
  119. package/dist/react/components/Tabs.js.map +1 -1
  120. package/dist/react/components/TruncatedText.d.ts +2 -1
  121. package/dist/react/components/TruncatedText.d.ts.map +1 -1
  122. package/dist/react/components/TruncatedText.js +2 -1
  123. package/dist/react/components/TruncatedText.js.map +1 -1
  124. package/dist/react/components/calendar/MonthGrid.d.ts.map +1 -1
  125. package/dist/react/components/calendar/MonthGrid.js +2 -1
  126. package/dist/react/components/calendar/MonthGrid.js.map +1 -1
  127. package/dist/react/components/calendar/WeekView.d.ts.map +1 -1
  128. package/dist/react/components/calendar/WeekView.js +2 -1
  129. package/dist/react/components/calendar/WeekView.js.map +1 -1
  130. package/dist/react/components/revealOnHover.d.ts.map +1 -1
  131. package/dist/react/components/revealOnHover.js +2 -1
  132. package/dist/react/components/revealOnHover.js.map +1 -1
  133. package/dist/react/file-tree/FileTree.d.ts +50 -1
  134. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  135. package/dist/react/file-tree/FileTree.js +234 -6
  136. package/dist/react/file-tree/FileTree.js.map +1 -1
  137. package/dist/react/file-tree/fileTree.css +52 -0
  138. package/dist/react/filter-rail/FilterRail.d.ts.map +1 -1
  139. package/dist/react/filter-rail/FilterRail.js +5 -4
  140. package/dist/react/filter-rail/FilterRail.js.map +1 -1
  141. package/dist/react/folder-tree/FolderTreeRail.d.ts.map +1 -1
  142. package/dist/react/folder-tree/FolderTreeRail.js +2 -1
  143. package/dist/react/folder-tree/FolderTreeRail.js.map +1 -1
  144. package/dist/react/index.d.ts +5 -1
  145. package/dist/react/index.d.ts.map +1 -1
  146. package/dist/react/index.js +5 -1
  147. package/dist/react/index.js.map +1 -1
  148. package/dist/react/layoutEngine/index.js +1 -1
  149. package/dist/react/layoutEngine/index.js.map +1 -1
  150. package/dist/react/lib/field.d.ts.map +1 -1
  151. package/dist/react/lib/field.js +3 -2
  152. package/dist/react/lib/field.js.map +1 -1
  153. package/dist/react/lib/floatingStyles.d.ts.map +1 -1
  154. package/dist/react/lib/floatingStyles.js +2 -1
  155. package/dist/react/lib/floatingStyles.js.map +1 -1
  156. package/dist/react/lib/tapTarget.d.ts +96 -0
  157. package/dist/react/lib/tapTarget.d.ts.map +1 -0
  158. package/dist/react/lib/tapTarget.js +96 -0
  159. package/dist/react/lib/tapTarget.js.map +1 -0
  160. package/dist/react/media/VideoTrimmer.d.ts +1 -1
  161. package/dist/react/media/VideoTrimmer.js +1 -1
  162. package/dist/react/media/denseGallery.css +3 -3
  163. package/dist/react/media/ffmpegWasm.js +2 -2
  164. package/dist/react/media/ffmpegWasm.js.map +1 -1
  165. package/dist/react/media/videoPlayer.css +42 -0
  166. package/dist/react/namespace/NamespaceTargetCapsule.d.ts +2 -2
  167. package/dist/react/namespace/NamespaceTargetCapsule.d.ts.map +1 -1
  168. package/dist/react/nav/BreadcrumbTrail.d.ts.map +1 -1
  169. package/dist/react/nav/BreadcrumbTrail.js +4 -3
  170. package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
  171. package/dist/react/nav/HubTileGrid.d.ts.map +1 -1
  172. package/dist/react/nav/HubTileGrid.js +2 -1
  173. package/dist/react/nav/HubTileGrid.js.map +1 -1
  174. package/dist/react/nav/SideNav.d.ts.map +1 -1
  175. package/dist/react/nav/SideNav.js +4 -3
  176. package/dist/react/nav/SideNav.js.map +1 -1
  177. package/dist/react/nav/SideNavCustomize.d.ts.map +1 -1
  178. package/dist/react/nav/SideNavCustomize.js +2 -1
  179. package/dist/react/nav/SideNavCustomize.js.map +1 -1
  180. package/dist/react/palette/CommandPalette.d.ts.map +1 -1
  181. package/dist/react/palette/CommandPalette.js +3 -2
  182. package/dist/react/palette/CommandPalette.js.map +1 -1
  183. package/dist/server/drivers/pooledControlStores.pg.test.js +1 -0
  184. package/dist/server/drivers/pooledControlStores.pg.test.js.map +1 -1
  185. package/dist/server/drivers/pooledIdentityStore.pg.test.js +1 -0
  186. package/dist/server/drivers/pooledIdentityStore.pg.test.js.map +1 -1
  187. package/dist/server/drivers/retentionPolicy.pg.test.js +1 -0
  188. package/dist/server/drivers/retentionPolicy.pg.test.js.map +1 -1
  189. package/dist/server/guard/argon2Binding.d.ts +1 -1
  190. package/dist/server/guard/argon2Binding.js +1 -1
  191. package/dist/server/incidents/store.d.ts +3 -3
  192. package/dist/server/index.js +1 -1
  193. package/dist/server/index.js.map +1 -1
  194. package/dist/server/master-lock/masterLock.d.ts +1 -1
  195. package/dist/server/media-bun/index.js +1 -1
  196. package/dist/server/media-bun/index.js.map +1 -1
  197. package/dist/server/paths/appDataHome.d.ts +20 -0
  198. package/dist/server/paths/appDataHome.d.ts.map +1 -0
  199. package/dist/server/paths/appDataHome.js +69 -0
  200. package/dist/server/paths/appDataHome.js.map +1 -0
  201. package/dist/server/retention/archive.d.ts +3 -4
  202. package/dist/server/retention/archive.d.ts.map +1 -1
  203. package/dist/server/retention/archive.js +20 -15
  204. package/dist/server/retention/archive.js.map +1 -1
  205. package/dist/server/retention/index.d.ts +1 -1
  206. package/dist/server/retention/index.js +1 -1
  207. package/dist/server/retention/policy.d.ts +2 -2
  208. package/dist/server/retention/policy.d.ts.map +1 -1
  209. package/dist/server/retention/policy.js +1 -1
  210. package/dist/server/retention/store.d.ts.map +1 -1
  211. package/dist/server/retention/store.js +27 -20
  212. package/dist/server/retention/store.js.map +1 -1
  213. package/dist/server/runtime/localHost.d.ts +10 -5
  214. package/dist/server/runtime/localHost.d.ts.map +1 -1
  215. package/dist/server/runtime/localHost.js +12 -8
  216. package/dist/server/runtime/localHost.js.map +1 -1
  217. package/dist/server/runtime/meterRollup.pg.test.js +1 -0
  218. package/dist/server/runtime/meterRollup.pg.test.js.map +1 -1
  219. package/dist/server/runtime/recipe.d.ts +4 -4
  220. package/dist/server/runtime/retentionJob.pg.test.js +1 -0
  221. package/dist/server/runtime/retentionJob.pg.test.js.map +1 -1
  222. package/dist/server/search/pgSearchProvider.pg.test.js +1 -0
  223. package/dist/server/search/pgSearchProvider.pg.test.js.map +1 -1
  224. package/dist/shared/errors.d.ts +1 -1
  225. package/dist/shared/errors.js +1 -1
  226. package/dist/styles-static.css +391 -0
  227. package/dist/styles.css +27 -0
  228. package/package.json +19 -167
  229. package/scripts/generateStaticStyles.ts +122 -0
  230. package/scripts/publicSurface.ts +455 -0
  231. package/scripts/testTmpRoot.ts +34 -5
  232. package/src/client/index.ts +1 -1
  233. package/src/core/category/index.ts +2 -1
  234. package/src/core/category/types.ts +1 -1
  235. package/src/core/file-tree/fileTreeModel.spec.ts +134 -0
  236. package/src/core/file-tree/fileTreeModel.ts +104 -0
  237. package/src/core/layout/index.ts +1 -1
  238. package/src/core/media/index.ts +1 -1
  239. package/src/core/media/types.ts +1 -1
  240. package/src/core/media/uploadLimits.ts +1 -1
  241. package/src/core/sharing/model.ts +1 -1
  242. package/src/ctgr/index.ts +1 -1
  243. package/src/declaredDepsAreImported.spec.ts +87 -0
  244. package/src/demoFixture.spec.ts +133 -0
  245. package/src/index.ts +1 -1
  246. package/src/lockfileIsTracked.spec.ts +50 -0
  247. package/src/namedSubpathsResolve.spec.ts +119 -0
  248. package/src/publicSurface.spec.ts +72 -0
  249. package/src/react/activity-bar/ActivityBar.tsx +17 -0
  250. package/src/react/analytics/index.ts +1 -1
  251. package/src/react/chat/MessageComposer.tsx +5 -4
  252. package/src/react/components/Button.tsx +3 -2
  253. package/src/react/components/Checkbox.spec.tsx +51 -0
  254. package/src/react/components/Checkbox.tsx +68 -26
  255. package/src/react/components/CodeArea.tsx +2 -1
  256. package/src/react/components/CodeChip.spec.tsx +2 -2
  257. package/src/react/components/Collapsible.tsx +2 -1
  258. package/src/react/components/ComboboxField.tsx +3 -2
  259. package/src/react/components/CopyButton.tsx +2 -1
  260. package/src/react/components/CopyableText.spec.tsx +1 -1
  261. package/src/react/components/DataTable/ColumnChecklist.tsx +2 -1
  262. package/src/react/components/DataTable/Pagination.tsx +2 -1
  263. package/src/react/components/DataTable/SortHeader.tsx +2 -1
  264. package/src/react/components/DataTable/{FilterBuilder.tsx → TableFilterBuilder.tsx} +33 -5
  265. package/src/react/components/DataTable/Toolbar.tsx +3 -3
  266. package/src/react/components/DataTable/cells/LinkCell.tsx +2 -1
  267. package/src/react/components/DataTable/index.ts +8 -1
  268. package/src/react/components/DragHandle.tsx +2 -1
  269. package/src/react/components/EmojiField.tsx +2 -1
  270. package/src/react/components/EventTimeline.tsx +3 -2
  271. package/src/react/components/FileLocation.spec.tsx +1 -1
  272. package/src/react/components/IconButton.tsx +1 -1
  273. package/src/react/components/InfoHint.tsx +5 -4
  274. package/src/react/components/InheritBadge.tsx +1 -1
  275. package/src/react/components/InlineEditableText.tsx +2 -1
  276. package/src/react/components/MeterList.tsx +2 -1
  277. package/src/react/components/PathText.tsx +4 -3
  278. package/src/react/components/RatingField.tsx +2 -1
  279. package/src/react/components/SecretInput.tsx +3 -3
  280. package/src/react/components/SegmentedControl.tsx +2 -2
  281. package/src/react/components/StarRating.spec.tsx +2 -2
  282. package/src/react/components/StarRating.tsx +9 -5
  283. package/src/react/components/StatStrip.tsx +2 -1
  284. package/src/react/components/StatTile.tsx +3 -3
  285. package/src/react/components/Tabs.spec.tsx +3 -2
  286. package/src/react/components/Tabs.tsx +2 -1
  287. package/src/react/components/TruncatedText.tsx +2 -1
  288. package/src/react/components/calendar/MonthGrid.spec.tsx +2 -1
  289. package/src/react/components/calendar/MonthGrid.tsx +2 -1
  290. package/src/react/components/calendar/WeekView.tsx +2 -1
  291. package/src/react/components/revealOnHover.ts +3 -2
  292. package/src/react/file-tree/FileTree.spec.tsx +248 -0
  293. package/src/react/file-tree/FileTree.tsx +304 -4
  294. package/src/react/file-tree/fileTree.css +52 -0
  295. package/src/react/file-tree/fileTreeGeometry.fixture.json +151 -0
  296. package/src/react/file-tree/fileTreeGeometry.spec.ts +33 -52
  297. package/src/react/filter-rail/FilterRail.spec.tsx +2 -1
  298. package/src/react/filter-rail/FilterRail.tsx +5 -4
  299. package/src/react/folder-tree/FolderTreeRail.tsx +2 -1
  300. package/src/react/index.ts +13 -3
  301. package/src/react/layoutEngine/index.ts +1 -1
  302. package/src/react/layoutEngine/types.ts +1 -1
  303. package/src/react/lib/field.ts +4 -2
  304. package/src/react/lib/floatingStyles.spec.ts +2 -1
  305. package/src/react/lib/floatingStyles.ts +3 -1
  306. package/src/react/lib/tapTarget.ts +100 -0
  307. package/src/react/media/MaskPainter.tsx +2 -2
  308. package/src/react/media/VideoTrimmer.tsx +1 -1
  309. package/src/react/media/denseGallery.css +3 -3
  310. package/src/react/media/ffmpegWasm.ts +2 -2
  311. package/src/react/media/videoPlayer.css +42 -0
  312. package/src/react/namespace/NamespaceTargetCapsule.tsx +2 -2
  313. package/src/react/nav/BreadcrumbTrail.tsx +4 -3
  314. package/src/react/nav/HubTileGrid.tsx +2 -1
  315. package/src/react/nav/SideNav.tsx +4 -3
  316. package/src/react/nav/SideNavCustomize.tsx +2 -1
  317. package/src/react/palette/CommandPalette.tsx +3 -2
  318. package/src/server/ctgr.ts +1 -1
  319. package/src/server/drivers/pooledControlStores.pg.test.ts +1 -0
  320. package/src/server/drivers/pooledIdentityStore.pg.test.ts +1 -0
  321. package/src/server/drivers/retentionPolicy.pg.test.ts +1 -0
  322. package/src/server/guard/argon2Binding.ts +1 -1
  323. package/src/server/incidents/store.ts +3 -3
  324. package/src/server/index.ts +1 -1
  325. package/src/server/master-lock/masterLock.spec.ts +18 -2
  326. package/src/server/master-lock/masterLock.ts +1 -1
  327. package/src/server/media-bun/image.spec.ts +1 -0
  328. package/src/server/media-bun/index.ts +1 -1
  329. package/src/server/media-bun/video.spec.ts +1 -0
  330. package/src/server/paths/appDataHome.spec.ts +87 -0
  331. package/src/server/paths/appDataHome.ts +84 -0
  332. package/src/server/retention/archive.spec.ts +26 -4
  333. package/src/server/retention/archive.ts +20 -15
  334. package/src/server/retention/index.ts +1 -1
  335. package/src/server/retention/policy.ts +2 -2
  336. package/src/server/retention/store.spec.ts +38 -10
  337. package/src/server/retention/store.ts +26 -19
  338. package/src/server/runtime/localHost.spec.ts +22 -3
  339. package/src/server/runtime/localHost.ts +12 -9
  340. package/src/server/runtime/meterRollup.pg.test.ts +1 -0
  341. package/src/server/runtime/recipe.ts +4 -4
  342. package/src/server/runtime/retentionJob.pg.test.ts +1 -0
  343. package/src/server/search/pgSearchProvider.pg.test.ts +1 -0
  344. package/src/server/storage/binaryServerProcessing.integration.spec.ts +6 -2
  345. package/src/server/storage/chunked.integration.spec.ts +5 -2
  346. package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +5 -2
  347. package/src/server/storage/files/adapter.photo.display.spec.ts +1 -0
  348. package/src/server/storage/imageThumbnail.spec.ts +1 -0
  349. package/src/server/storage/transcode.integration.spec.ts +5 -2
  350. package/src/server/storage/videoProcessor.posterOnly.spec.ts +1 -0
  351. package/src/shared/errors.ts +1 -1
  352. package/src/skippedTestsAreAnswered.spec.ts +215 -0
  353. package/src/styles-static.css +391 -0
  354. package/src/styles.css +27 -0
  355. package/src/stylesStaticMatches.spec.ts +134 -0
  356. package/src/tapTargetStylesheet.spec.ts +150 -0
  357. package/src/testTmpRoot.spec.ts +102 -0
  358. package/dist/core/duplicates/index.d.ts +0 -116
  359. package/dist/core/duplicates/index.d.ts.map +0 -1
  360. package/dist/core/duplicates/index.js +0 -178
  361. package/dist/core/duplicates/index.js.map +0 -1
  362. package/dist/core/namespace/helpers.d.ts +0 -14
  363. package/dist/core/namespace/helpers.d.ts.map +0 -1
  364. package/dist/core/namespace/helpers.js +0 -29
  365. package/dist/core/namespace/helpers.js.map +0 -1
  366. package/dist/core/namespace/index.d.ts +0 -4
  367. package/dist/core/namespace/index.d.ts.map +0 -1
  368. package/dist/core/namespace/index.js +0 -6
  369. package/dist/core/namespace/index.js.map +0 -1
  370. package/dist/core/namespace/memoryStore.d.ts +0 -19
  371. package/dist/core/namespace/memoryStore.d.ts.map +0 -1
  372. package/dist/core/namespace/memoryStore.js +0 -70
  373. package/dist/core/namespace/memoryStore.js.map +0 -1
  374. package/dist/core/namespace/types.d.ts +0 -44
  375. package/dist/core/namespace/types.d.ts.map +0 -1
  376. package/dist/core/namespace/types.js +0 -8
  377. package/dist/core/namespace/types.js.map +0 -1
  378. package/dist/game/core/DisposeRegistry.d.ts +0 -42
  379. package/dist/game/core/DisposeRegistry.d.ts.map +0 -1
  380. package/dist/game/core/DisposeRegistry.js +0 -67
  381. package/dist/game/core/DisposeRegistry.js.map +0 -1
  382. package/dist/game/core/drawBudget.d.ts +0 -26
  383. package/dist/game/core/drawBudget.d.ts.map +0 -1
  384. package/dist/game/core/drawBudget.js +0 -55
  385. package/dist/game/core/drawBudget.js.map +0 -1
  386. package/dist/game/core/fixedTimestep.d.ts +0 -38
  387. package/dist/game/core/fixedTimestep.d.ts.map +0 -1
  388. package/dist/game/core/fixedTimestep.js +0 -47
  389. package/dist/game/core/fixedTimestep.js.map +0 -1
  390. package/dist/game/core/index.d.ts +0 -9
  391. package/dist/game/core/index.d.ts.map +0 -1
  392. package/dist/game/core/index.js +0 -8
  393. package/dist/game/core/index.js.map +0 -1
  394. package/dist/game/core/inputCollector.d.ts +0 -82
  395. package/dist/game/core/inputCollector.d.ts.map +0 -1
  396. package/dist/game/core/inputCollector.js +0 -176
  397. package/dist/game/core/inputCollector.js.map +0 -1
  398. package/dist/game/core/resizeCanvas.d.ts +0 -19
  399. package/dist/game/core/resizeCanvas.d.ts.map +0 -1
  400. package/dist/game/core/resizeCanvas.js +0 -29
  401. package/dist/game/core/resizeCanvas.js.map +0 -1
  402. package/dist/game/react/index.d.ts +0 -3
  403. package/dist/game/react/index.d.ts.map +0 -1
  404. package/dist/game/react/index.js +0 -5
  405. package/dist/game/react/index.js.map +0 -1
  406. package/dist/game/react/useGameEngine.d.ts +0 -64
  407. package/dist/game/react/useGameEngine.d.ts.map +0 -1
  408. package/dist/game/react/useGameEngine.js +0 -78
  409. package/dist/game/react/useGameEngine.js.map +0 -1
  410. package/dist/react/canvas/CanvasNavHUD.d.ts +0 -64
  411. package/dist/react/canvas/CanvasNavHUD.d.ts.map +0 -1
  412. package/dist/react/canvas/CanvasNavHUD.js +0 -58
  413. package/dist/react/canvas/CanvasNavHUD.js.map +0 -1
  414. package/dist/react/canvas/fitLabel.d.ts +0 -53
  415. package/dist/react/canvas/fitLabel.d.ts.map +0 -1
  416. package/dist/react/canvas/fitLabel.js +0 -104
  417. package/dist/react/canvas/fitLabel.js.map +0 -1
  418. package/dist/react/canvas/index.d.ts +0 -4
  419. package/dist/react/canvas/index.d.ts.map +0 -1
  420. package/dist/react/canvas/index.js +0 -4
  421. package/dist/react/canvas/index.js.map +0 -1
  422. package/dist/react/canvas/lod.d.ts +0 -40
  423. package/dist/react/canvas/lod.d.ts.map +0 -1
  424. package/dist/react/canvas/lod.js +0 -37
  425. package/dist/react/canvas/lod.js.map +0 -1
  426. package/dist/react/components/DataTable/FilterBuilder.d.ts +0 -38
  427. package/dist/react/components/DataTable/FilterBuilder.d.ts.map +0 -1
  428. package/dist/react/components/DataTable/FilterBuilder.js.map +0 -1
  429. package/dist/react/seo/HeadProvider.d.ts +0 -14
  430. package/dist/react/seo/HeadProvider.d.ts.map +0 -1
  431. package/dist/react/seo/HeadProvider.js +0 -44
  432. package/dist/react/seo/HeadProvider.js.map +0 -1
  433. package/dist/react/seo/applyHeadToDocument.d.ts +0 -3
  434. package/dist/react/seo/applyHeadToDocument.d.ts.map +0 -1
  435. package/dist/react/seo/applyHeadToDocument.js +0 -66
  436. package/dist/react/seo/applyHeadToDocument.js.map +0 -1
  437. package/dist/react/seo/index.d.ts +0 -3
  438. package/dist/react/seo/index.d.ts.map +0 -1
  439. package/dist/react/seo/index.js +0 -7
  440. package/dist/react/seo/index.js.map +0 -1
  441. package/dist/react/timeline/ClipView.d.ts +0 -29
  442. package/dist/react/timeline/ClipView.d.ts.map +0 -1
  443. package/dist/react/timeline/ClipView.js +0 -53
  444. package/dist/react/timeline/ClipView.js.map +0 -1
  445. package/dist/react/timeline/Playhead.d.ts +0 -16
  446. package/dist/react/timeline/Playhead.d.ts.map +0 -1
  447. package/dist/react/timeline/Playhead.js +0 -26
  448. package/dist/react/timeline/Playhead.js.map +0 -1
  449. package/dist/react/timeline/TimeRuler.d.ts +0 -21
  450. package/dist/react/timeline/TimeRuler.d.ts.map +0 -1
  451. package/dist/react/timeline/TimeRuler.js +0 -31
  452. package/dist/react/timeline/TimeRuler.js.map +0 -1
  453. package/dist/react/timeline/TimelineSurface.d.ts +0 -29
  454. package/dist/react/timeline/TimelineSurface.d.ts.map +0 -1
  455. package/dist/react/timeline/TimelineSurface.js +0 -56
  456. package/dist/react/timeline/TimelineSurface.js.map +0 -1
  457. package/dist/react/timeline/TrackLane.d.ts +0 -31
  458. package/dist/react/timeline/TrackLane.d.ts.map +0 -1
  459. package/dist/react/timeline/TrackLane.js +0 -32
  460. package/dist/react/timeline/TrackLane.js.map +0 -1
  461. package/dist/react/timeline/index.d.ts +0 -6
  462. package/dist/react/timeline/index.d.ts.map +0 -1
  463. package/dist/react/timeline/index.js +0 -12
  464. package/dist/react/timeline/index.js.map +0 -1
  465. package/dist/server/private-media/index.d.ts +0 -7
  466. package/dist/server/private-media/index.d.ts.map +0 -1
  467. package/dist/server/private-media/index.js +0 -13
  468. package/dist/server/private-media/index.js.map +0 -1
  469. package/dist/server/seo/index.d.ts +0 -3
  470. package/dist/server/seo/index.d.ts.map +0 -1
  471. package/dist/server/seo/index.js +0 -6
  472. package/dist/server/seo/index.js.map +0 -1
  473. package/dist/server/seo/mountSeoRoutes.d.ts +0 -9
  474. package/dist/server/seo/mountSeoRoutes.d.ts.map +0 -1
  475. package/dist/server/seo/mountSeoRoutes.js +0 -19
  476. package/dist/server/seo/mountSeoRoutes.js.map +0 -1
  477. package/dist/server/seo/serveHeadInjectedIndex.d.ts +0 -9
  478. package/dist/server/seo/serveHeadInjectedIndex.d.ts.map +0 -1
  479. package/dist/server/seo/serveHeadInjectedIndex.js +0 -20
  480. package/dist/server/seo/serveHeadInjectedIndex.js.map +0 -1
  481. package/dist/upload.d.ts +0 -148
  482. package/dist/upload.d.ts.map +0 -1
  483. package/dist/upload.js +0 -159
  484. package/dist/upload.js.map +0 -1
  485. package/dist/verify/runtime/index.d.ts +0 -16
  486. package/dist/verify/runtime/index.d.ts.map +0 -1
  487. package/dist/verify/runtime/index.js +0 -16
  488. package/dist/verify/runtime/index.js.map +0 -1
  489. package/scripts/demoCheck.ts +0 -42
  490. package/src/core/duplicates/index.spec.ts +0 -116
  491. package/src/core/duplicates/index.ts +0 -203
  492. package/src/core/namespace/helpers.ts +0 -31
  493. package/src/core/namespace/index.ts +0 -12
  494. package/src/core/namespace/memoryStore.ts +0 -77
  495. package/src/core/namespace/namespace.spec.ts +0 -117
  496. package/src/core/namespace/types.ts +0 -53
  497. package/src/game/core/DisposeRegistry.spec.ts +0 -53
  498. package/src/game/core/DisposeRegistry.ts +0 -70
  499. package/src/game/core/drawBudget.spec.ts +0 -51
  500. package/src/game/core/drawBudget.ts +0 -75
  501. package/src/game/core/fixedTimestep.spec.ts +0 -91
  502. package/src/game/core/fixedTimestep.ts +0 -79
  503. package/src/game/core/index.ts +0 -16
  504. package/src/game/core/inputCollector.spec.ts +0 -29
  505. package/src/game/core/inputCollector.ts +0 -245
  506. package/src/game/core/resizeCanvas.ts +0 -31
  507. package/src/game/react/index.ts +0 -10
  508. package/src/game/react/useGameEngine.ts +0 -125
  509. package/src/react/canvas/CanvasNavHUD.spec.tsx +0 -62
  510. package/src/react/canvas/CanvasNavHUD.tsx +0 -210
  511. package/src/react/canvas/fitLabel.spec.ts +0 -82
  512. package/src/react/canvas/fitLabel.ts +0 -150
  513. package/src/react/canvas/index.ts +0 -15
  514. package/src/react/canvas/lod.ts +0 -53
  515. package/src/react/seo/HeadProvider.tsx +0 -67
  516. package/src/react/seo/applyHeadToDocument.ts +0 -73
  517. package/src/react/seo/index.ts +0 -12
  518. package/src/react/seo/seo.spec.tsx +0 -101
  519. package/src/react/timeline/ClipView.tsx +0 -146
  520. package/src/react/timeline/Playhead.tsx +0 -63
  521. package/src/react/timeline/TimeRuler.tsx +0 -85
  522. package/src/react/timeline/TimelineSurface.tsx +0 -157
  523. package/src/react/timeline/TrackLane.tsx +0 -87
  524. package/src/react/timeline/index.ts +0 -12
  525. package/src/react/timeline/timeline.spec.tsx +0 -175
  526. package/src/server/private-media/index.ts +0 -45
  527. package/src/server/seo/index.ts +0 -8
  528. package/src/server/seo/mountSeoRoutes.ts +0 -37
  529. package/src/server/seo/seo.spec.ts +0 -128
  530. package/src/server/seo/serveHeadInjectedIndex.ts +0 -36
  531. package/src/upload.spec.ts +0 -300
  532. package/src/upload.ts +0 -288
  533. package/src/verify/runtime/index.ts +0 -64
@@ -360,6 +360,110 @@ export function resolveDrop({
360
360
  });
361
361
  }
362
362
 
363
+ export interface KeyboardMoveOptions {
364
+ nodes: readonly FileTreeNode[];
365
+ /** The rows on screen — siblings are read off these, which is every sibling by construction. */
366
+ rows: readonly FileTreeRow[];
367
+ /** The row the keyboard is on. */
368
+ row: FileTreeRow;
369
+ /** `in` demotes into the nearest folder above it; `out` promotes to its grandparent. */
370
+ direction: 'in' | 'out';
371
+ lockedReason?: string;
372
+ canDrop?: (dragId: string | null, target: FileTreeDrop) => string | null;
373
+ }
374
+
375
+ /**
376
+ * Where ⌥→ / ⌥← would put the focused row — the keyboard's half of the drag.
377
+ *
378
+ * 🔴 It exists because a gesture with no keyboard route is a gesture half the people using
379
+ * it cannot perform, and because TOUCH has no drag here at all: a sidebar row is a small
380
+ * target on a surface whose one constant gesture is the flick, so the pointer drag gives
381
+ * that up and this is what is left. `cursedesk`'s tree has carried it since it was written;
382
+ * this one shipped with the drag alone.
383
+ *
384
+ * The two directions are the only two a keyboard can mean unambiguously. **→ demotes into
385
+ * the nearest folder ABOVE it among its own siblings** — the one place a demotion can go
386
+ * without asking which of several folders was meant — and **← promotes out to the folder its
387
+ * own folder is in**, which is `root` at depth 1.
388
+ *
389
+ * 🔴 Every refusal the DRAG honours is honoured here, from the same fields: `acceptsDrop`,
390
+ * the consumer's `canDrop`, and the descendant rule. A keyboard route that can reach a
391
+ * destination the pointer refuses is a second, weaker opinion about what is legal — and it
392
+ * is the one nobody looks at, because the pointer is what gets tested by hand.
393
+ *
394
+ * There is deliberately no ⌥↑/⌥↓ here. Those would move a row among its siblings, which is
395
+ * only meaningful when the consumer stores an order — and when it does, the sibling before
396
+ * and after are already reachable as `before`/`after` drops. Offering them unconditionally
397
+ * would write a position nothing reads and then draw the row back where it started.
398
+ */
399
+ export function resolveKeyboardMove({
400
+ nodes,
401
+ rows,
402
+ row,
403
+ direction,
404
+ lockedReason = 'That folder is locked',
405
+ canDrop,
406
+ }: KeyboardMoveOptions): DropOffer {
407
+ const vetted = (target: FileTreeDrop): DropOffer => {
408
+ const reason = canDrop?.(row.node.id, target);
409
+ return reason
410
+ ? { target: null, refused: true, reason }
411
+ : { target, refused: false, reason: null };
412
+ };
413
+
414
+ if (direction === 'out') {
415
+ if (row.parentId === null)
416
+ return { target: null, refused: true, reason: 'It is already at the top level' };
417
+ const grandparentId = parentIdOf(nodes, row.parentId);
418
+ if (grandparentId === null) return vetted({ kind: 'root' });
419
+ const grandparent = findFileTreeNode(nodes, grandparentId);
420
+ if (grandparent?.acceptsDrop === false)
421
+ return { target: null, refused: true, reason: lockedReason };
422
+ return vetted({ kind: 'into', parentId: grandparentId });
423
+ }
424
+
425
+ // A row's siblings are all on screen whenever the row itself is — a shut folder hides its
426
+ // CHILDREN, never its peers — so the visible rows are the whole level.
427
+ const level = rows.filter((entry) => entry.parentId === row.parentId);
428
+ const above = level
429
+ .filter((entry) => entry.index < row.index && entry.node.kind === 'folder')
430
+ .at(-1);
431
+ if (!above)
432
+ return { target: null, refused: true, reason: 'There is no folder above it to move into' };
433
+ if (above.node.acceptsDrop === false)
434
+ return { target: null, refused: true, reason: lockedReason };
435
+ // Belt and braces: a folder cannot go inside itself, and `above` is a sibling rather than
436
+ // a descendant — but the rule is the tree's, not this function's, and stating it once here
437
+ // is cheaper than discovering the day someone changes what `above` means.
438
+ if (descendantIdsOf(nodes, row.node.id).has(above.node.id))
439
+ return { target: null, refused: true, reason: 'A folder cannot go inside itself' };
440
+ return vetted({ kind: 'into', parentId: above.node.id });
441
+ }
442
+
443
+ /**
444
+ * Where a drop would put the thing being carried, in words — for the chip under the cursor
445
+ * and for the screen reader that cannot see it.
446
+ *
447
+ * 🔴 A drag that says only WHAT is held answers half the question. Owner, of the tree this
448
+ * one was ported from: *"dragging an item from one place to another does not have any
449
+ * indication that it is working until you drop it somewhere new and see it move."* The chip
450
+ * following the cursor answers "it is working"; this answers "and it is going there", which
451
+ * is what stops a wrong drop before the pointer is lifted rather than after.
452
+ *
453
+ * Empty for no target — a refusal has its own words and this must never carry a second "no".
454
+ */
455
+ export function describeDrop(nodes: readonly FileTreeNode[], drop: FileTreeDrop | null): string {
456
+ if (!drop) return '';
457
+ if (drop.kind === 'root') return 'To the top level';
458
+ if (drop.kind === 'into') {
459
+ const folder = findFileTreeNode(nodes, drop.parentId);
460
+ return folder ? `Into ${folder.name}` : 'Into this folder';
461
+ }
462
+ const sibling = findFileTreeNode(nodes, drop.siblingId);
463
+ const where = drop.kind === 'before' ? 'Above' : 'Below';
464
+ return sibling ? `${where} ${sibling.name}` : `${where} this row`;
465
+ }
466
+
363
467
  /**
364
468
  * The next row whose name starts with `query`, searching from `fromIndex` and wrapping.
365
469
  *
@@ -1,4 +1,4 @@
1
- // cursedbelt/layout — re-export of `cwip/layout`, the single source of truth for the
1
+ // src/core/layout — re-export of `cwip/layout`, the single source of truth for the
2
2
  // framework-agnostic layout/widget engine core (v2 node model + idempotent migration,
3
3
  // pure treeOps, aggregates, container-query grid/style class maps, binding resolution,
4
4
  // undo/redo history, wire version). cursedbelt's copy was absorbed into cwip
@@ -1,5 +1,5 @@
1
1
  // cursedbelt/media-core — pure, dependency-free media contract + utilities shared by
2
- // the Bun server adapter (`cursedbelt/server/media`), the React components
2
+ // the Bun server adapter (`src/server/media-bun`), the React components
3
3
  // (`cursedbelt/react/media`), and any consuming app/test. No React, no Bun, no npm
4
4
  // dep — the same module loads identically everywhere.
5
5
 
@@ -1,5 +1,5 @@
1
1
  // cursedbelt media-core — the pure, dependency-free contract shared by the Bun
2
- // server adapter (`cursedbelt/server/media`), the React components
2
+ // server adapter (`src/server/media-bun`), the React components
3
3
  // (`cursedbelt/react/media`), and any app/test. ZERO npm deps: types + pure
4
4
  // functions only, so the exact same module loads identically in Bun, the browser,
5
5
  // and a unit test.
@@ -1,6 +1,6 @@
1
1
  // Upload size limits — the ONE source of truth shared by the upload client
2
2
  // (`cursedbelt/react/media` fileUploadClient) and the server file routes
3
- // (`cursedbelt/server/storage/files`). Pure constants, no deps, so both the browser
3
+ // (`cursedbelt/server/storage`). Pure constants, no deps, so both the browser
4
4
  // and Bun sides import the same numbers and can never drift.
5
5
  //
6
6
  // The law: NO single HTTP request may carry more than MAX_SINGLE_UPLOAD_BYTES of body.
@@ -74,7 +74,7 @@ export interface ShareResource {
74
74
  }
75
75
 
76
76
  /** One grant. `grantedAt` is an ISO-8601 string, matching the fleet's audit
77
- * vocabulary (`cursedbelt/server/audit`) rather than the epoch-millis apps use
77
+ * vocabulary (the audit module behind `cursedbelt/server`) rather than the epoch-millis apps use
78
78
  * for their own rows — an audit trail is read by humans far more often than it is
79
79
  * sorted by a machine. */
80
80
  export interface ShareGrant {
package/src/ctgr/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * cursedbelt/ctgr — the isomorphic ctgr codec (browser + Bun, no runtime deps). The
2
+ * src/ctgr — the isomorphic ctgr codec, re-exported from `cursedbelt/server`; (browser + Bun, no runtime deps). The
3
3
  * Hono transport binding (`unGETify` + `buildRoutes`) lives in `cursedbelt/server`;
4
4
  * the browser guard + request converter live in `cursedbelt/client`.
5
5
  *
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import pkg from '../package.json';
3
+
4
+ /**
5
+ * Every declared dependency must be reachable from an import (2026-09-13).
6
+ *
7
+ * `dependencies` is not advice — a consumer installs all of it to render one
8
+ * button. `peerDependencies` is a promise that installing the peer lights a
9
+ * feature up. Both rot silently: a component gets deleted, its package stays
10
+ * declared, and nothing anywhere reds. At the time this was written the manifest
11
+ * carried three runtime deps and five optional peers that no file imported.
12
+ *
13
+ * 🔴 The reason this matches an IMPORT SPECIFIER and scans EVERY file type,
14
+ * rather than grepping .ts for the bare name:
15
+ *
16
+ * - A .ts-only grep says `tw-animate-css` is dead. It is not — it is pulled in
17
+ * from CSS (`@import` in src/styles.css), and deleting it would have shipped
18
+ * a package whose stylesheet cannot resolve. That is the exact mistake this
19
+ * session made on its first pass.
20
+ * - A bare-name grep says everything is alive, because names like `diff` and
21
+ * `marked` appear in prose in hundreds of files. A gate that cannot fail is
22
+ * not a gate.
23
+ *
24
+ * devDependencies are deliberately NOT checked: several are installed only so
25
+ * this repo can typecheck and test an OPTIONAL peer's integration, so "nothing
26
+ * imports it by name" is the expected state there, not a defect.
27
+ */
28
+ const ROOTS = ['src', 'scripts'] as const;
29
+
30
+ // Reading a font or a screenshot as text costs time and can never match.
31
+ const BINARY = /\.(png|jpe?g|gif|webp|avif|ico|mp4|webm|mp3|wav|pdf|zip|gz|woff2?|ttf|otf|eot|wasm)$/i;
32
+
33
+ /**
34
+ * `from 'x'` · `import 'x/y.css'` · `import('x')` · `require('x')` · `@import "x"`,
35
+ * with or without a subpath. Anchored on the specifier quote so a name mentioned
36
+ * in prose or in a comment does not count as a use.
37
+ */
38
+ const RE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
39
+
40
+ const importsPackage = (name: string): RegExp =>
41
+ new RegExp(`(?:from|import|require|@import)\\s*\\(?\\s*['"]${name.replace(RE_SPECIAL, '\\$&')}(?:/[^'"]*)?['"]`);
42
+
43
+ // 🔴 Bun.Glob + Bun.file, NOT node:fs — this package's test preload virtualizes
44
+ // node:fs, so a readdirSync/readFileSync scan reads MOCK data and passes against
45
+ // files it never opened. Same trap as publishShape.spec.ts; see its header.
46
+ let cache: Promise<string[]> | undefined;
47
+ const sources = (): Promise<string[]> => {
48
+ cache ??= (async () => {
49
+ const repo = `${import.meta.dir}/..`;
50
+ const texts: string[] = [];
51
+ for (const root of ROOTS) {
52
+ const dir = `${repo}/${root}`;
53
+ for (const rel of new Bun.Glob('**/*').scanSync({ cwd: dir, onlyFiles: true })) {
54
+ if (BINARY.test(rel)) continue;
55
+ texts.push(await Bun.file(`${dir}/${rel}`).text());
56
+ }
57
+ }
58
+ return texts;
59
+ })();
60
+ return cache;
61
+ };
62
+
63
+ const unimported = async (block: Record<string, string> | undefined): Promise<string[]> => {
64
+ const texts = await sources();
65
+ return Object.keys(block ?? {}).filter((name) => {
66
+ const re = importsPackage(name);
67
+ return !texts.some((t) => re.test(t));
68
+ });
69
+ };
70
+
71
+ describe('declared dependencies are imported', () => {
72
+ it('has scanned a real source tree', async () => {
73
+ // Guards the whole file: if the glob or the fs mock ever returns nothing,
74
+ // every assertion below passes vacuously and the gate quietly dies.
75
+ expect((await sources()).length).toBeGreaterThan(100);
76
+ });
77
+
78
+ for (const field of ['dependencies', 'peerDependencies'] as const) {
79
+ it(`${field} names nothing the code never imports`, async () => {
80
+ const dead = await unimported((pkg as Record<string, unknown>)[field] as Record<string, string>);
81
+ expect(
82
+ dead,
83
+ `declared in ${field} but imported nowhere in ${ROOTS.join('/')} — import it or drop it:\n ${dead.join('\n ')}`,
84
+ ).toEqual([]);
85
+ });
86
+ }
87
+ });
@@ -0,0 +1,133 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import {
5
+ DEMO_COMMAND,
6
+ DEMO_DEV_SCRIPT,
7
+ DEMO_PACKAGE_DIR,
8
+ DEMO_PORT,
9
+ DEMO_ROOT,
10
+ DEMO_SERVE_SCRIPT,
11
+ DEMO_URL,
12
+ } from '../demo/devServer';
13
+ import { DEMO_TSCONFIG, derivePaths, readDemoTsconfig, renderPathsBlock } from '../demo/generatePaths';
14
+ import pkg from '../package.json';
15
+
16
+ /**
17
+ * 🔴 The e2e suite must never be able to sit green while it drives nothing.
18
+ *
19
+ * For the whole life of this repo, `e2e/playwright.config.ts` pointed its `webServer`
20
+ * at `cursedbelt/demo` — a directory that did not exist, because the kitchen-sink demo
21
+ * was never carried across from `cursed-core` when the package was extracted. All 18
22
+ * specs failed at web-server startup, and NOTHING noticed: `verify` was
23
+ * `paths && typecheck && build && test`, and `bun test` only runs `src`. So 2,400 lines
24
+ * of real assertions — including the `DESIGN.md` §5 44×44 touch-target floor, which the
25
+ * `touch` Playwright project exists specifically to prove and which no unit test can
26
+ * check, because `(pointer: coarse)` is about the input DEVICE — read as coverage the
27
+ * repo did not have.
28
+ *
29
+ * `bun run verify` now ends in `bun run e2e`, which is the real proof. These assertions
30
+ * are the cheap half: they fail in milliseconds naming the exact missing path instead of
31
+ * after a 120s web-server timeout, and they would have caught the original defect on the
32
+ * day the package was extracted.
33
+ *
34
+ * Note the blunt case: if `demo/` is removed OUTRIGHT this file cannot even load
35
+ * (`Cannot find module '../demo/devServer'`) and `bun test` fails on it. That is the
36
+ * intended red — the imports above are the coupling, not an accident of layout.
37
+ */
38
+ const repoRoot = resolve(import.meta.dir, '..');
39
+ const e2eDir = resolve(repoRoot, 'e2e');
40
+
41
+ describe('the e2e fixture exists', () => {
42
+ it('the demo directory Playwright starts is real', () => {
43
+ expect(existsSync(DEMO_ROOT), `demo root does not exist: ${DEMO_ROOT}`).toBe(true);
44
+ expect(existsSync(resolve(DEMO_ROOT, 'vite.config.ts')), 'demo/ has no vite.config.ts').toBe(true);
45
+ expect(DEMO_PACKAGE_DIR, 'the demo must be started from this package').toBe(repoRoot);
46
+ });
47
+
48
+ it('the webServer command names a script this package defines', () => {
49
+ // A renamed or deleted script is the other way this config goes quietly dead —
50
+ // the suite would fail at startup with a shell error, not a diff.
51
+ const scripts = Object.keys(pkg.scripts);
52
+ expect(DEMO_COMMAND).toBe(`bun run ${DEMO_SERVE_SCRIPT}`);
53
+ for (const script of [DEMO_DEV_SCRIPT, DEMO_SERVE_SCRIPT]) {
54
+ expect(scripts, `package.json has no "${script}" script`).toContain(script);
55
+ }
56
+ expect(pkg.scripts.demo, 'the demo script must start vite on the demo config').toInclude('demo/vite.config.ts');
57
+ // Playwright drives the BUILT bundle, not the dev server — see demo/devServer.ts.
58
+ expect(pkg.scripts['demo:serve'], 'demo:serve must build before it previews').toInclude('demo:build');
59
+ expect(pkg.scripts['demo:serve']).toInclude('vite preview');
60
+ });
61
+
62
+ it('both servers serve the port the specs are pointed at', () => {
63
+ // vite silently walks to the next free port unless `strictPort` is set, which is
64
+ // the same outage wearing a costume: a server that starts perfectly well and a
65
+ // suite that times out against the port nobody is listening on.
66
+ const viteConfig = readFileSync(resolve(DEMO_ROOT, 'vite.config.ts'), 'utf8');
67
+ expect(DEMO_URL).toBe(`http://localhost:${DEMO_PORT}`);
68
+ // Once for `server` (dev), once for `preview` (what e2e drives).
69
+ expect(viteConfig.match(/port: DEMO_PORT/g) ?? [], 'both server and preview must pin the port').toHaveLength(2);
70
+ expect(viteConfig.match(/strictPort: true/g) ?? [], 'both must set strictPort').toHaveLength(2);
71
+ });
72
+
73
+ it('every page the specs navigate to is a real demo entry point', () => {
74
+ const specs = readdirSync(e2eDir).filter((f) => f.endsWith('.spec.ts'));
75
+ expect(specs.length, 'e2e/ holds no specs — delete the config and these assertions too').toBeGreaterThan(0);
76
+
77
+ const missing: string[] = [];
78
+ for (const spec of specs) {
79
+ const text = readFileSync(resolve(e2eDir, spec), 'utf8');
80
+ for (const [, url] of text.matchAll(/goto\('([^']+)'\)/g)) {
81
+ // '/' is index.html; '/table-layout.html' is that file in demo/.
82
+ const file = url === '/' ? 'index.html' : url.replace(/^\//, '');
83
+ if (!existsSync(resolve(DEMO_ROOT, file))) missing.push(`${spec} → ${url}`);
84
+ }
85
+ }
86
+ expect(missing, `specs navigate to pages demo/ does not serve:\n ${missing.join('\n ')}`).toEqual([]);
87
+ });
88
+
89
+ it('a built demo still emits every fixture page', () => {
90
+ // vite's dev server hands out any .html under its root, but a build only emits
91
+ // what `rollupOptions.input` lists — so a fixture page can exist, pass the
92
+ // assertion above, and vanish from the bundle the suite actually drives.
93
+ const viteConfig = readFileSync(resolve(DEMO_ROOT, 'vite.config.ts'), 'utf8');
94
+ const unlisted = readdirSync(DEMO_ROOT)
95
+ .filter((f) => f.endsWith('.html'))
96
+ .filter((page) => !viteConfig.includes(`'${page}'`));
97
+ expect(unlisted, `demo/vite.config.ts's rollup input omits:\n ${unlisted.join('\n ')}`).toEqual([]);
98
+ });
99
+
100
+ it('verify actually runs the suite', () => {
101
+ // Everything above proves the fixture resolves. Only this proves anybody looks:
102
+ // without it, `e2e` drops back out of the gate and this whole file becomes a
103
+ // comment about a suite nothing runs.
104
+ expect(pkg.scripts.verify, '`verify` must end in the e2e suite').toInclude('bun run e2e');
105
+ expect(pkg.scripts.e2e).toInclude('e2e/playwright.config.ts');
106
+ });
107
+ });
108
+
109
+ /**
110
+ * The demo imports cursedbelt by package specifier, and nothing installs
111
+ * `node_modules/cursedbelt` for it to resolve against. `demo/vite.config.ts` derives its
112
+ * aliases from the `exports` map at startup; tsc cannot, so `demo/tsconfig.json` carries
113
+ * a generated `paths` block. This keeps the generated half honest — see
114
+ * `demo/generatePaths.ts` for why neither resolver is allowed a hand-written list.
115
+ */
116
+ describe("the demo's tsconfig paths track the export map", () => {
117
+ it('matches what the exports map derives', () => {
118
+ expect(readDemoTsconfig().compilerOptions.paths, 'stale — run `bun run demo:paths`').toEqual(derivePaths());
119
+ });
120
+
121
+ it('is rendered in the exact form `bun run demo:paths` writes', () => {
122
+ // Compare TEXT too: an equal-but-reformatted block would make the writer report a
123
+ // diff forever, which is how a generated file stops being regenerated.
124
+ expect(readFileSync(DEMO_TSCONFIG, 'utf8')).toInclude(renderPathsBlock(derivePaths()));
125
+ });
126
+
127
+ it('names only source files that exist', () => {
128
+ const missing = Object.entries(derivePaths())
129
+ .filter(([, [target]]) => !existsSync(resolve(DEMO_ROOT, target as string)))
130
+ .map(([specifier, [target]]) => `${specifier} → ${target}`);
131
+ expect(missing, `demo/tsconfig.json paths point at missing files:\n ${missing.join('\n ')}`).toEqual([]);
132
+ });
133
+ });
package/src/index.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  * `bun run gate` at the repo root fails when `dist` is older than `src`.
19
19
  */
20
20
  // cursedbelt core (framework-agnostic utilities). The React UI kit lives in
21
- // `cursedbelt/react`; the isomorphic ctgr codec in `cursedbelt/ctgr`.
21
+ // `cursedbelt/react`; the isomorphic ctgr codec in `cursedbelt/server`.
22
22
  export const VERSION = '0.0.0';
23
23
 
24
24
  // Color helpers — framework-agnostic, pure over hex strings. `contrastText` is the
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 🔴 Rule 2 of this generation: `bun install --frozen-lockfile` must pass on a
3
+ * CLEAN CHECKOUT — not merely in the tree an agent happens to be sitting in.
4
+ *
5
+ * ── What this cost, 2026-09-13 ──────────────────────────────────────────────
6
+ * `.gitignore` carried a `bun.lock` stanza with a comment explaining it was
7
+ * temporary: the only lockfile that could exist at the time recorded a
8
+ * proof-only local pack of `cwip`, and committing that would have reintroduced
9
+ * the `file:` dependency the package had just removed. The reasoning was sound
10
+ * and the expiry condition — "until cwip is on the registry" — was real. It was
11
+ * also written in a comment, which nothing can evaluate. `cwip@3.0.2` went to
12
+ * the registry and the stanza stayed, so rule 2 was true of every working tree
13
+ * and false of every fresh clone: exactly the case the rule exists for, and the
14
+ * one nobody hits until they are the person doing the clone.
15
+ *
16
+ * A temporary ignore needs a check that notices when it stopped being true.
17
+ * This is that check. `git check-ignore` is the whole mechanism, because the
18
+ * failure is not "the lockfile is missing" — it is "git has been told to throw
19
+ * it away", which is invisible in a tree that already has one.
20
+ *
21
+ * Deliberately NOT `git ls-files bun.lock`: a regenerated lockfile is untracked
22
+ * until it is staged, and a check that reds on work-in-progress teaches people
23
+ * to skip it. Ignored-vs-not is the property that actually regresses.
24
+ */
25
+ import { describe, expect, test } from 'bun:test'
26
+ import { spawnSync } from 'node:child_process'
27
+ import { existsSync } from 'node:fs'
28
+ import { fileURLToPath } from 'node:url'
29
+
30
+ const repoRoot = fileURLToPath(new URL('..', import.meta.url))
31
+
32
+ describe('bun.lock is part of the checkout', () => {
33
+ test('the lockfile exists', () => {
34
+ expect(
35
+ existsSync(new URL('../bun.lock', import.meta.url)),
36
+ 'no bun.lock — regenerate it with `bun install`, or `bun install --frozen-lockfile` cannot pass for anyone cloning this',
37
+ ).toBe(true)
38
+ })
39
+
40
+ test('git is not told to ignore it', () => {
41
+ const git = spawnSync('git', ['check-ignore', '-q', 'bun.lock'], { cwd: repoRoot })
42
+ if (git.error) return // no git on this host: nothing to assert about ignore rules
43
+
44
+ // exit 0 = the path IS ignored; 1 = it is not; anything else = git could not tell us.
45
+ expect(
46
+ git.status,
47
+ 'bun.lock is gitignored, so a fresh clone has no lockfile and `bun install --frozen-lockfile` cannot pass there. Delete the stanza from .gitignore rather than the assertion.',
48
+ ).not.toBe(0)
49
+ })
50
+ })
@@ -0,0 +1,119 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import pkg from '../package.json';
3
+
4
+ /**
5
+ * Every `cursedbelt/<subpath>` this repo NAMES must be one it actually exports
6
+ * (2026-09-13).
7
+ *
8
+ * Removing the 23 subpaths nothing referenced left 19 sentences in `src` and
9
+ * `docs` still advertising them — `cursedbelt/ctgr`, `cursedbelt/server/media`,
10
+ * `cursedbelt/tokens` and friends, each one a copy-pasteable import that now
11
+ * throws ERR_PACKAGE_PATH_NOT_EXPORTED. Nothing could see them, because a
12
+ * subpath named in prose is invisible to `tsc` (it is a comment), invisible to
13
+ * Biome (it is a string), and invisible to `check-paths.ts` (it is not a
14
+ * filesystem path). The only reason those 19 were found is that one agent
15
+ * grepped for them by hand while deleting the exports.
16
+ *
17
+ * That is the drift this file exists to make impossible: the manifest and the
18
+ * prose describing it can no longer disagree without something going red.
19
+ *
20
+ * 🔴 Bun.Glob + Bun.file, NOT node:fs — this package's test preload virtualizes
21
+ * node:fs, so a readdirSync/readFileSync scan reads MOCK data and passes against
22
+ * files it never opened. Same trap as publishShape.spec.ts; see its header.
23
+ *
24
+ * Escape hatch: a line that must name a removed subpath (a changelog entry, a
25
+ * migration note) carries `subpath-check:ignore` and is skipped.
26
+ */
27
+ const ROOTS = ['src', 'scripts', 'docs'] as const;
28
+
29
+ const BINARY = /\.(png|jpe?g|gif|webp|avif|ico|mp4|webm|mp3|wav|pdf|zip|gz|woff2?|ttf|otf|eot|wasm)$/i;
30
+
31
+ const IGNORE = 'subpath-check:ignore';
32
+
33
+ /**
34
+ * `cursedbelt/<subpath>` wherever it appears — code, comment or markdown. The
35
+ * trailing class stops `cursedbelt/react` from matching inside
36
+ * `cursedbelt/react/timeline`, so a removed deep subpath cannot hide behind a
37
+ * live shallow one.
38
+ *
39
+ * The leading `(?<![\w/.-])` is what separates an IMPORT SPECIFIER from a REPO
40
+ * PATH: `packages/cursedbelt/src/...` and `../cursedbelt/scripts/...` name files
41
+ * on disk (check-paths.ts' job), not subpaths, and flagging them would make this
42
+ * gate unpassable and therefore useless.
43
+ */
44
+ const NAMED = /(?<![\w/.-])cursedbelt(\/[a-z0-9][a-z0-9._/-]*[a-z0-9])/gi;
45
+
46
+ /** First segment is a directory in THIS repo ⇒ the mention is a file path. */
47
+ const REPO_DIRS = /^\/(src|scripts|dist|docs|e2e|demo|node_modules|releases)(\/|$)/;
48
+
49
+ /**
50
+ * Ends in a file extension ⇒ a file path, not a subpath — except the three CSS
51
+ * entries, which genuinely ARE exports (`cursedbelt/styles.css`).
52
+ */
53
+ const FILE_EXT = /\.(?!css$)[a-z0-9]+$/i;
54
+
55
+ const exported = new Set(Object.keys(pkg.exports).map((k) => k.replace(/^\./, '')));
56
+
57
+ // This file and the task/report prose quoting the removal are allowed to name them.
58
+ const SELF = 'namedSubpathsResolve.spec.ts';
59
+
60
+ type Offence = { file: string; line: number; subpath: string; text: string };
61
+
62
+ let cache: Promise<Offence[]> | undefined;
63
+ const offences = (): Promise<Offence[]> => {
64
+ cache ??= (async () => {
65
+ const repo = `${import.meta.dir}/..`;
66
+ const found: Offence[] = [];
67
+ for (const root of ROOTS) {
68
+ const dir = `${repo}/${root}`;
69
+ for (const rel of new Bun.Glob('**/*').scanSync({ cwd: dir, onlyFiles: true })) {
70
+ if (BINARY.test(rel) || rel.endsWith(SELF)) continue;
71
+ const text = await Bun.file(`${dir}/${rel}`).text();
72
+ const lines = text.split('\n');
73
+ for (let i = 0; i < lines.length; i++) {
74
+ const line = lines[i] ?? '';
75
+ if (line.includes(IGNORE)) continue;
76
+ for (const m of line.matchAll(NAMED)) {
77
+ const subpath = m[1] ?? '';
78
+ if (exported.has(subpath)) continue;
79
+ if (REPO_DIRS.test(subpath) || FILE_EXT.test(subpath)) continue;
80
+ found.push({ file: `${root}/${rel}`, line: i + 1, subpath, text: line.trim() });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ return found;
86
+ })();
87
+ return cache;
88
+ };
89
+
90
+ describe('subpaths named in this repo resolve', () => {
91
+ it('has scanned a real source tree', async () => {
92
+ // Guards the whole file: if the glob or the fs mock ever returns nothing,
93
+ // the assertion below passes vacuously and the gate quietly dies.
94
+ const repo = `${import.meta.dir}/..`;
95
+ let seen = 0;
96
+ for (const root of ROOTS) {
97
+ for (const _ of new Bun.Glob('**/*').scanSync({ cwd: `${repo}/${root}`, onlyFiles: true })) seen++;
98
+ }
99
+ expect(seen).toBeGreaterThan(100);
100
+ });
101
+
102
+ it('knows what the manifest exports', () => {
103
+ // If `exports` is ever read wrong, every offence below silently becomes one.
104
+ expect(exported.has('')).toBe(true); // the "." root entry
105
+ expect(exported.has('/react')).toBe(true);
106
+ expect(exported.has('/game')).toBe(false); // removed 2026-09-13
107
+ });
108
+
109
+ it('names no subpath package.json does not export', async () => {
110
+ const dead = await offences();
111
+ expect(
112
+ dead,
113
+ `these name a cursedbelt subpath that is not in package.json exports — a consumer who\n` +
114
+ `copies one gets ERR_PACKAGE_PATH_NOT_EXPORTED. Point at the surviving subpath, or at\n` +
115
+ `the repo path, or mark the line \`${IGNORE}\`:\n` +
116
+ dead.map((d) => ` ${d.file}:${d.line} cursedbelt${d.subpath}\n ${d.text}`).join('\n'),
117
+ ).toEqual([]);
118
+ });
119
+ });
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { run } from '../scripts/publicSurface';
3
+
4
+ /**
5
+ * The public-surface ratchet, wired into `bun run verify` at its cheapest correct
6
+ * home: `verify` already ends in `bun test src`, so this needs no new script and
7
+ * cannot be a script somebody forgot to chain. The whole measurement is ~1.3s
8
+ * against a 4m41s suite.
9
+ *
10
+ * WHY it exists, WHAT a symbol count includes and excludes, why `bin` targets and
11
+ * the dependency list are deliberately NOT counted here, and what `--prune` may
12
+ * and may not do are all in the header of `scripts/publicSurface.ts` — one check,
13
+ * one baseline (`publicSurface.baseline`), one place that explains itself. This
14
+ * file is only the wiring.
15
+ *
16
+ * 🔴 Verified failing, 2026-09-13, before it was trusted — every branch, not just
17
+ * the easy one. Each probe was applied, observed, and removed again:
18
+ *
19
+ * · `export const ratchetProbe = 1` appended to `src/core/palette/index.ts`
20
+ * → this file's "ceiling" test red, and the CLI:
21
+ * "2 subpath(s) that gained exported symbols · ./react — baseline allows 1061,
22
+ * exports 1062 now · ./palette — baseline allows 10, exports 11 now".
23
+ * TWO, because the `./react` barrel re-exports the palette — which is the
24
+ * transitive `export *` walk this file's header promises, seen working.
25
+ * · a `"./probe"` subpath added to package.json `exports`
26
+ * → "1 export subpath(s) the baseline does not know · ./probe — NEW subpath, 10 exported symbol(s)"
27
+ * · `searchInitials` removed from `src/core/palette/index.ts`, no prune
28
+ * → "2 baseline entr(y|ies) that no longer match `exports` · ./palette — baseline
29
+ * says 10, exports 9 · ./react — baseline says 1061, exports 1060"
30
+ * · a `5 ./gone` line added to the baseline by hand
31
+ * → same failure, "./gone — baseline says 5, the subpath is gone". An entry
32
+ * matching nothing is the half that stops a deleted module leaving an allowance.
33
+ * · `--prune` run against the grown surface
34
+ * → "refuses while the surface has GROWN — it may only lower the baseline", exit 1
35
+ * · `--prune` run against the shrunk surface
36
+ * → "pruned to 85 subpath(s), 0 removed, 3806 symbols", exit 0 — `./palette`
37
+ * 10 → 9 and `./react` 1061 → 1060 in the file, which is the burn-down path
38
+ *
39
+ * A ratchet nobody has seen red is a ratchet nobody knows is wired up.
40
+ */
41
+ const result = await run();
42
+
43
+ describe('public surface', () => {
44
+ it('has measured a real surface', () => {
45
+ // Guards the three assertions below: if `exports` were ever read as empty,
46
+ // every one of them passes vacuously and the ratchet quietly dies.
47
+ expect(result.surface.counts.size).toBeGreaterThan(50);
48
+ });
49
+
50
+ it('publishes no export subpath the baseline does not know', () => {
51
+ expect(
52
+ result.fresh,
53
+ `new public subpath(s) — a permanent promise. Add the line to publicSurface.baseline and say why:\n ${result.fresh.join('\n ')}`,
54
+ ).toEqual([]);
55
+ });
56
+
57
+ it('exports no more symbols per subpath than the baseline allows', () => {
58
+ expect(
59
+ result.grown,
60
+ `a baseline entry is a ceiling, not an amnesty:\n ${result.grown.join('\n ')}`,
61
+ ).toEqual([]);
62
+ });
63
+
64
+ it('has no baseline entry that outlived the surface it recorded', () => {
65
+ // The half that makes it a RATCHET: a deleted module may not leave an
66
+ // allowance behind for the next one to grow into.
67
+ expect(
68
+ result.stale,
69
+ `surface came off and the ratchet was not tightened — run \`bun scripts/publicSurface.ts --prune\`:\n ${result.stale.join('\n ')}`,
70
+ ).toEqual([]);
71
+ });
72
+ });
@@ -32,6 +32,23 @@
32
32
  * brighter glyph — the same language the file tree's selected row uses, so a person learns
33
33
  * it once.
34
34
  *
35
+ * ── 🔴 The graduation verdict, 2026-09-13: THIS one, unchanged ──────────────────────────
36
+ * `cursedesk` has an `ActivityBar.tsx` of its own and the pair was compared to decide which
37
+ * should be the shared one. Nothing moved here, and the reason is that they are not two
38
+ * versions of one component: this is a RAIL that takes {@link ActivityBarProps.top} and
39
+ * {@link ActivityBarProps.bottom} and draws whatever is in them; the app's is one particular
40
+ * seven-button composition — Explorer, Search, ⌘P, the command menu, full screen, theme,
41
+ * settings — hard-wired to that app's `primitives.tsx` and `shortcuts.ts`, with every prop
42
+ * (`searchOpen`, `onSwitcher`, `shortcutPrefs`, `commandUnavailable`) naming one of them.
43
+ *
44
+ * A design system cannot hold that: `onSwitcher` is a door onto a file finder and
45
+ * `shortcutPrefs` is a preferences table, neither of which a rail has any business knowing.
46
+ * So the app's copy is deleted rather than moved, and its seven buttons become seven
47
+ * `ActivityBarItem`s built where the app's state already lives. The one thing it has that
48
+ * this does not — a command menu rendered by the rail itself — is already expressible:
49
+ * {@link ActivityBarItem.render} takes the consumer's own popover, and
50
+ * {@link ActivityBarItem.hasMenu}/{@link ActivityBarItem.menuOpen} say so to a screen reader.
51
+ *
35
52
  * ── Plain CSS, deliberately ─────────────────────────────────────────────────────────────
36
53
  * `activityBar.css` under the `cbab-` prefix, the same convention (and the same reason) as
37
54
  * `fileTree.css` and `denseGallery.css`: this mounts inside apps whose Tailwind build cannot