cursedbelt 1.0.0 → 2.5.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 (537) 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 +413 -0
  227. package/dist/styles-utilities.css +5074 -0
  228. package/dist/styles.css +49 -0
  229. package/package.json +30 -168
  230. package/scripts/generateStaticStyles.ts +122 -0
  231. package/scripts/generateUtilityStyles.ts +215 -0
  232. package/scripts/publicSurface.ts +455 -0
  233. package/scripts/testTmpRoot.ts +34 -5
  234. package/src/client/index.ts +1 -1
  235. package/src/core/category/index.ts +2 -1
  236. package/src/core/category/types.ts +1 -1
  237. package/src/core/file-tree/fileTreeModel.spec.ts +134 -0
  238. package/src/core/file-tree/fileTreeModel.ts +104 -0
  239. package/src/core/layout/index.ts +1 -1
  240. package/src/core/media/index.ts +1 -1
  241. package/src/core/media/types.ts +1 -1
  242. package/src/core/media/uploadLimits.ts +1 -1
  243. package/src/core/sharing/model.ts +1 -1
  244. package/src/ctgr/index.ts +1 -1
  245. package/src/declaredDepsAreImported.spec.ts +87 -0
  246. package/src/demoFixture.spec.ts +133 -0
  247. package/src/index.ts +1 -1
  248. package/src/lockfileIsTracked.spec.ts +50 -0
  249. package/src/namedSubpathsResolve.spec.ts +119 -0
  250. package/src/publicSurface.spec.ts +72 -0
  251. package/src/react/activity-bar/ActivityBar.tsx +17 -0
  252. package/src/react/analytics/index.ts +1 -1
  253. package/src/react/chat/MessageComposer.tsx +5 -4
  254. package/src/react/components/Button.tsx +3 -2
  255. package/src/react/components/Checkbox.spec.tsx +51 -0
  256. package/src/react/components/Checkbox.tsx +68 -26
  257. package/src/react/components/CodeArea.tsx +2 -1
  258. package/src/react/components/CodeChip.spec.tsx +2 -2
  259. package/src/react/components/Collapsible.tsx +2 -1
  260. package/src/react/components/ComboboxField.tsx +3 -2
  261. package/src/react/components/CopyButton.tsx +2 -1
  262. package/src/react/components/CopyableText.spec.tsx +1 -1
  263. package/src/react/components/DataTable/ColumnChecklist.tsx +2 -1
  264. package/src/react/components/DataTable/Pagination.tsx +2 -1
  265. package/src/react/components/DataTable/SortHeader.tsx +2 -1
  266. package/src/react/components/DataTable/{FilterBuilder.tsx → TableFilterBuilder.tsx} +33 -5
  267. package/src/react/components/DataTable/Toolbar.tsx +3 -3
  268. package/src/react/components/DataTable/cells/LinkCell.tsx +2 -1
  269. package/src/react/components/DataTable/index.ts +8 -1
  270. package/src/react/components/DragHandle.tsx +2 -1
  271. package/src/react/components/EmojiField.tsx +2 -1
  272. package/src/react/components/EventTimeline.tsx +3 -2
  273. package/src/react/components/FileLocation.spec.tsx +1 -1
  274. package/src/react/components/IconButton.tsx +1 -1
  275. package/src/react/components/InfoHint.tsx +5 -4
  276. package/src/react/components/InheritBadge.tsx +1 -1
  277. package/src/react/components/InlineEditableText.tsx +2 -1
  278. package/src/react/components/MeterList.tsx +2 -1
  279. package/src/react/components/PathText.tsx +4 -3
  280. package/src/react/components/RatingField.tsx +2 -1
  281. package/src/react/components/SecretInput.tsx +3 -3
  282. package/src/react/components/SegmentedControl.tsx +2 -2
  283. package/src/react/components/StarRating.spec.tsx +2 -2
  284. package/src/react/components/StarRating.tsx +9 -5
  285. package/src/react/components/StatStrip.tsx +2 -1
  286. package/src/react/components/StatTile.tsx +3 -3
  287. package/src/react/components/Tabs.spec.tsx +3 -2
  288. package/src/react/components/Tabs.tsx +2 -1
  289. package/src/react/components/TruncatedText.tsx +2 -1
  290. package/src/react/components/calendar/MonthGrid.spec.tsx +2 -1
  291. package/src/react/components/calendar/MonthGrid.tsx +2 -1
  292. package/src/react/components/calendar/WeekView.tsx +2 -1
  293. package/src/react/components/revealOnHover.ts +3 -2
  294. package/src/react/file-tree/FileTree.spec.tsx +248 -0
  295. package/src/react/file-tree/FileTree.tsx +304 -4
  296. package/src/react/file-tree/fileTree.css +52 -0
  297. package/src/react/file-tree/fileTreeGeometry.fixture.json +151 -0
  298. package/src/react/file-tree/fileTreeGeometry.spec.ts +33 -52
  299. package/src/react/filter-rail/FilterRail.spec.tsx +2 -1
  300. package/src/react/filter-rail/FilterRail.tsx +5 -4
  301. package/src/react/folder-tree/FolderTreeRail.tsx +2 -1
  302. package/src/react/index.ts +13 -3
  303. package/src/react/layoutEngine/index.ts +1 -1
  304. package/src/react/layoutEngine/types.ts +1 -1
  305. package/src/react/lib/field.ts +4 -2
  306. package/src/react/lib/floatingStyles.spec.ts +2 -1
  307. package/src/react/lib/floatingStyles.ts +3 -1
  308. package/src/react/lib/tapTarget.ts +100 -0
  309. package/src/react/media/MaskPainter.tsx +2 -2
  310. package/src/react/media/VideoTrimmer.tsx +1 -1
  311. package/src/react/media/denseGallery.css +3 -3
  312. package/src/react/media/ffmpegWasm.ts +2 -2
  313. package/src/react/media/videoPlayer.css +42 -0
  314. package/src/react/namespace/NamespaceTargetCapsule.tsx +2 -2
  315. package/src/react/nav/BreadcrumbTrail.tsx +4 -3
  316. package/src/react/nav/HubTileGrid.tsx +2 -1
  317. package/src/react/nav/SideNav.tsx +4 -3
  318. package/src/react/nav/SideNavCustomize.tsx +2 -1
  319. package/src/react/palette/CommandPalette.tsx +3 -2
  320. package/src/server/ctgr.ts +1 -1
  321. package/src/server/drivers/pooledControlStores.pg.test.ts +1 -0
  322. package/src/server/drivers/pooledIdentityStore.pg.test.ts +1 -0
  323. package/src/server/drivers/retentionPolicy.pg.test.ts +1 -0
  324. package/src/server/guard/argon2Binding.ts +1 -1
  325. package/src/server/incidents/store.ts +3 -3
  326. package/src/server/index.ts +1 -1
  327. package/src/server/master-lock/masterLock.spec.ts +18 -2
  328. package/src/server/master-lock/masterLock.ts +1 -1
  329. package/src/server/media-bun/image.spec.ts +1 -0
  330. package/src/server/media-bun/index.ts +1 -1
  331. package/src/server/media-bun/video.spec.ts +1 -0
  332. package/src/server/paths/appDataHome.spec.ts +87 -0
  333. package/src/server/paths/appDataHome.ts +84 -0
  334. package/src/server/retention/archive.spec.ts +26 -4
  335. package/src/server/retention/archive.ts +20 -15
  336. package/src/server/retention/index.ts +1 -1
  337. package/src/server/retention/policy.ts +2 -2
  338. package/src/server/retention/store.spec.ts +38 -10
  339. package/src/server/retention/store.ts +26 -19
  340. package/src/server/runtime/localHost.spec.ts +22 -3
  341. package/src/server/runtime/localHost.ts +12 -9
  342. package/src/server/runtime/meterRollup.pg.test.ts +1 -0
  343. package/src/server/runtime/recipe.ts +4 -4
  344. package/src/server/runtime/retentionJob.pg.test.ts +1 -0
  345. package/src/server/search/pgSearchProvider.pg.test.ts +1 -0
  346. package/src/server/storage/binaryServerProcessing.integration.spec.ts +6 -2
  347. package/src/server/storage/chunked.integration.spec.ts +5 -2
  348. package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +5 -2
  349. package/src/server/storage/files/adapter.photo.display.spec.ts +1 -0
  350. package/src/server/storage/imageThumbnail.spec.ts +1 -0
  351. package/src/server/storage/transcode.integration.spec.ts +5 -2
  352. package/src/server/storage/videoProcessor.posterOnly.spec.ts +1 -0
  353. package/src/shared/errors.ts +1 -1
  354. package/src/skippedTestsAreAnswered.spec.ts +215 -0
  355. package/src/styles-static.css +413 -0
  356. package/src/styles-utilities.css +5074 -0
  357. package/src/styles.css +49 -0
  358. package/src/stylesStaticMatches.spec.ts +134 -0
  359. package/src/stylesUtilitiesMatches.spec.ts +314 -0
  360. package/src/tapTargetStylesheet.spec.ts +150 -0
  361. package/src/testTmpRoot.spec.ts +102 -0
  362. package/dist/core/duplicates/index.d.ts +0 -116
  363. package/dist/core/duplicates/index.d.ts.map +0 -1
  364. package/dist/core/duplicates/index.js +0 -178
  365. package/dist/core/duplicates/index.js.map +0 -1
  366. package/dist/core/namespace/helpers.d.ts +0 -14
  367. package/dist/core/namespace/helpers.d.ts.map +0 -1
  368. package/dist/core/namespace/helpers.js +0 -29
  369. package/dist/core/namespace/helpers.js.map +0 -1
  370. package/dist/core/namespace/index.d.ts +0 -4
  371. package/dist/core/namespace/index.d.ts.map +0 -1
  372. package/dist/core/namespace/index.js +0 -6
  373. package/dist/core/namespace/index.js.map +0 -1
  374. package/dist/core/namespace/memoryStore.d.ts +0 -19
  375. package/dist/core/namespace/memoryStore.d.ts.map +0 -1
  376. package/dist/core/namespace/memoryStore.js +0 -70
  377. package/dist/core/namespace/memoryStore.js.map +0 -1
  378. package/dist/core/namespace/types.d.ts +0 -44
  379. package/dist/core/namespace/types.d.ts.map +0 -1
  380. package/dist/core/namespace/types.js +0 -8
  381. package/dist/core/namespace/types.js.map +0 -1
  382. package/dist/game/core/DisposeRegistry.d.ts +0 -42
  383. package/dist/game/core/DisposeRegistry.d.ts.map +0 -1
  384. package/dist/game/core/DisposeRegistry.js +0 -67
  385. package/dist/game/core/DisposeRegistry.js.map +0 -1
  386. package/dist/game/core/drawBudget.d.ts +0 -26
  387. package/dist/game/core/drawBudget.d.ts.map +0 -1
  388. package/dist/game/core/drawBudget.js +0 -55
  389. package/dist/game/core/drawBudget.js.map +0 -1
  390. package/dist/game/core/fixedTimestep.d.ts +0 -38
  391. package/dist/game/core/fixedTimestep.d.ts.map +0 -1
  392. package/dist/game/core/fixedTimestep.js +0 -47
  393. package/dist/game/core/fixedTimestep.js.map +0 -1
  394. package/dist/game/core/index.d.ts +0 -9
  395. package/dist/game/core/index.d.ts.map +0 -1
  396. package/dist/game/core/index.js +0 -8
  397. package/dist/game/core/index.js.map +0 -1
  398. package/dist/game/core/inputCollector.d.ts +0 -82
  399. package/dist/game/core/inputCollector.d.ts.map +0 -1
  400. package/dist/game/core/inputCollector.js +0 -176
  401. package/dist/game/core/inputCollector.js.map +0 -1
  402. package/dist/game/core/resizeCanvas.d.ts +0 -19
  403. package/dist/game/core/resizeCanvas.d.ts.map +0 -1
  404. package/dist/game/core/resizeCanvas.js +0 -29
  405. package/dist/game/core/resizeCanvas.js.map +0 -1
  406. package/dist/game/react/index.d.ts +0 -3
  407. package/dist/game/react/index.d.ts.map +0 -1
  408. package/dist/game/react/index.js +0 -5
  409. package/dist/game/react/index.js.map +0 -1
  410. package/dist/game/react/useGameEngine.d.ts +0 -64
  411. package/dist/game/react/useGameEngine.d.ts.map +0 -1
  412. package/dist/game/react/useGameEngine.js +0 -78
  413. package/dist/game/react/useGameEngine.js.map +0 -1
  414. package/dist/react/canvas/CanvasNavHUD.d.ts +0 -64
  415. package/dist/react/canvas/CanvasNavHUD.d.ts.map +0 -1
  416. package/dist/react/canvas/CanvasNavHUD.js +0 -58
  417. package/dist/react/canvas/CanvasNavHUD.js.map +0 -1
  418. package/dist/react/canvas/fitLabel.d.ts +0 -53
  419. package/dist/react/canvas/fitLabel.d.ts.map +0 -1
  420. package/dist/react/canvas/fitLabel.js +0 -104
  421. package/dist/react/canvas/fitLabel.js.map +0 -1
  422. package/dist/react/canvas/index.d.ts +0 -4
  423. package/dist/react/canvas/index.d.ts.map +0 -1
  424. package/dist/react/canvas/index.js +0 -4
  425. package/dist/react/canvas/index.js.map +0 -1
  426. package/dist/react/canvas/lod.d.ts +0 -40
  427. package/dist/react/canvas/lod.d.ts.map +0 -1
  428. package/dist/react/canvas/lod.js +0 -37
  429. package/dist/react/canvas/lod.js.map +0 -1
  430. package/dist/react/components/DataTable/FilterBuilder.d.ts +0 -38
  431. package/dist/react/components/DataTable/FilterBuilder.d.ts.map +0 -1
  432. package/dist/react/components/DataTable/FilterBuilder.js.map +0 -1
  433. package/dist/react/seo/HeadProvider.d.ts +0 -14
  434. package/dist/react/seo/HeadProvider.d.ts.map +0 -1
  435. package/dist/react/seo/HeadProvider.js +0 -44
  436. package/dist/react/seo/HeadProvider.js.map +0 -1
  437. package/dist/react/seo/applyHeadToDocument.d.ts +0 -3
  438. package/dist/react/seo/applyHeadToDocument.d.ts.map +0 -1
  439. package/dist/react/seo/applyHeadToDocument.js +0 -66
  440. package/dist/react/seo/applyHeadToDocument.js.map +0 -1
  441. package/dist/react/seo/index.d.ts +0 -3
  442. package/dist/react/seo/index.d.ts.map +0 -1
  443. package/dist/react/seo/index.js +0 -7
  444. package/dist/react/seo/index.js.map +0 -1
  445. package/dist/react/timeline/ClipView.d.ts +0 -29
  446. package/dist/react/timeline/ClipView.d.ts.map +0 -1
  447. package/dist/react/timeline/ClipView.js +0 -53
  448. package/dist/react/timeline/ClipView.js.map +0 -1
  449. package/dist/react/timeline/Playhead.d.ts +0 -16
  450. package/dist/react/timeline/Playhead.d.ts.map +0 -1
  451. package/dist/react/timeline/Playhead.js +0 -26
  452. package/dist/react/timeline/Playhead.js.map +0 -1
  453. package/dist/react/timeline/TimeRuler.d.ts +0 -21
  454. package/dist/react/timeline/TimeRuler.d.ts.map +0 -1
  455. package/dist/react/timeline/TimeRuler.js +0 -31
  456. package/dist/react/timeline/TimeRuler.js.map +0 -1
  457. package/dist/react/timeline/TimelineSurface.d.ts +0 -29
  458. package/dist/react/timeline/TimelineSurface.d.ts.map +0 -1
  459. package/dist/react/timeline/TimelineSurface.js +0 -56
  460. package/dist/react/timeline/TimelineSurface.js.map +0 -1
  461. package/dist/react/timeline/TrackLane.d.ts +0 -31
  462. package/dist/react/timeline/TrackLane.d.ts.map +0 -1
  463. package/dist/react/timeline/TrackLane.js +0 -32
  464. package/dist/react/timeline/TrackLane.js.map +0 -1
  465. package/dist/react/timeline/index.d.ts +0 -6
  466. package/dist/react/timeline/index.d.ts.map +0 -1
  467. package/dist/react/timeline/index.js +0 -12
  468. package/dist/react/timeline/index.js.map +0 -1
  469. package/dist/server/private-media/index.d.ts +0 -7
  470. package/dist/server/private-media/index.d.ts.map +0 -1
  471. package/dist/server/private-media/index.js +0 -13
  472. package/dist/server/private-media/index.js.map +0 -1
  473. package/dist/server/seo/index.d.ts +0 -3
  474. package/dist/server/seo/index.d.ts.map +0 -1
  475. package/dist/server/seo/index.js +0 -6
  476. package/dist/server/seo/index.js.map +0 -1
  477. package/dist/server/seo/mountSeoRoutes.d.ts +0 -9
  478. package/dist/server/seo/mountSeoRoutes.d.ts.map +0 -1
  479. package/dist/server/seo/mountSeoRoutes.js +0 -19
  480. package/dist/server/seo/mountSeoRoutes.js.map +0 -1
  481. package/dist/server/seo/serveHeadInjectedIndex.d.ts +0 -9
  482. package/dist/server/seo/serveHeadInjectedIndex.d.ts.map +0 -1
  483. package/dist/server/seo/serveHeadInjectedIndex.js +0 -20
  484. package/dist/server/seo/serveHeadInjectedIndex.js.map +0 -1
  485. package/dist/upload.d.ts +0 -148
  486. package/dist/upload.d.ts.map +0 -1
  487. package/dist/upload.js +0 -159
  488. package/dist/upload.js.map +0 -1
  489. package/dist/verify/runtime/index.d.ts +0 -16
  490. package/dist/verify/runtime/index.d.ts.map +0 -1
  491. package/dist/verify/runtime/index.js +0 -16
  492. package/dist/verify/runtime/index.js.map +0 -1
  493. package/scripts/demoCheck.ts +0 -42
  494. package/src/core/duplicates/index.spec.ts +0 -116
  495. package/src/core/duplicates/index.ts +0 -203
  496. package/src/core/namespace/helpers.ts +0 -31
  497. package/src/core/namespace/index.ts +0 -12
  498. package/src/core/namespace/memoryStore.ts +0 -77
  499. package/src/core/namespace/namespace.spec.ts +0 -117
  500. package/src/core/namespace/types.ts +0 -53
  501. package/src/game/core/DisposeRegistry.spec.ts +0 -53
  502. package/src/game/core/DisposeRegistry.ts +0 -70
  503. package/src/game/core/drawBudget.spec.ts +0 -51
  504. package/src/game/core/drawBudget.ts +0 -75
  505. package/src/game/core/fixedTimestep.spec.ts +0 -91
  506. package/src/game/core/fixedTimestep.ts +0 -79
  507. package/src/game/core/index.ts +0 -16
  508. package/src/game/core/inputCollector.spec.ts +0 -29
  509. package/src/game/core/inputCollector.ts +0 -245
  510. package/src/game/core/resizeCanvas.ts +0 -31
  511. package/src/game/react/index.ts +0 -10
  512. package/src/game/react/useGameEngine.ts +0 -125
  513. package/src/react/canvas/CanvasNavHUD.spec.tsx +0 -62
  514. package/src/react/canvas/CanvasNavHUD.tsx +0 -210
  515. package/src/react/canvas/fitLabel.spec.ts +0 -82
  516. package/src/react/canvas/fitLabel.ts +0 -150
  517. package/src/react/canvas/index.ts +0 -15
  518. package/src/react/canvas/lod.ts +0 -53
  519. package/src/react/seo/HeadProvider.tsx +0 -67
  520. package/src/react/seo/applyHeadToDocument.ts +0 -73
  521. package/src/react/seo/index.ts +0 -12
  522. package/src/react/seo/seo.spec.tsx +0 -101
  523. package/src/react/timeline/ClipView.tsx +0 -146
  524. package/src/react/timeline/Playhead.tsx +0 -63
  525. package/src/react/timeline/TimeRuler.tsx +0 -85
  526. package/src/react/timeline/TimelineSurface.tsx +0 -157
  527. package/src/react/timeline/TrackLane.tsx +0 -87
  528. package/src/react/timeline/index.ts +0 -12
  529. package/src/react/timeline/timeline.spec.tsx +0 -175
  530. package/src/server/private-media/index.ts +0 -45
  531. package/src/server/seo/index.ts +0 -8
  532. package/src/server/seo/mountSeoRoutes.ts +0 -37
  533. package/src/server/seo/seo.spec.ts +0 -128
  534. package/src/server/seo/serveHeadInjectedIndex.ts +0 -36
  535. package/src/upload.spec.ts +0 -300
  536. package/src/upload.ts +0 -288
  537. package/src/verify/runtime/index.ts +0 -64
@@ -1,10 +0,0 @@
1
- // cursedbelt/game/react — the browser-only React host hook. Reads `document`/`window`;
2
- // never import this subpath from a Bun server file. The engine-agnostic core (no React,
3
- // Bun-safe) lives in `cursedbelt/game`.
4
-
5
- export type {
6
- GameFactory,
7
- GameHandle,
8
- UseGameEngineResult,
9
- } from './useGameEngine';
10
- export { useGameEngine } from './useGameEngine';
@@ -1,125 +0,0 @@
1
- import { type DependencyList, type RefObject, useEffect, useRef, useState } from 'react';
2
- import { DisposeRegistry } from '../core/DisposeRegistry';
3
-
4
- /**
5
- * The handle a {@link GameFactory} returns. The host hook owns its lifecycle:
6
- * `dispose` runs on true unmount; `pause`/`resume` run on tab hide/show.
7
- */
8
- export interface GameHandle {
9
- /** Tear down the engine. Called exactly once, on true unmount. */
10
- dispose(): void;
11
- /** Optional: called when the tab is hidden (Page Visibility). Cancel your rAF here. */
12
- pause?(): void;
13
- /** Optional: called when the tab becomes visible again. Restart your rAF here. */
14
- resume?(): void;
15
- }
16
-
17
- /**
18
- * Creates the engine for a freshly-mounted canvas. Receives a {@link DisposeRegistry}
19
- * for RAII cleanup — register every listener / rAF id / resource you allocate.
20
- *
21
- * MUST be referentially stable: define it at module level or wrap it in `useCallback`.
22
- * (It's read through a ref, so an unstable identity won't restart the engine — but a
23
- * stale closure could capture old props. Prefer reading live values through refs.)
24
- *
25
- * @returns the handle, or a promise of it (async setup — e.g. loading WASM — is fine).
26
- */
27
- export type GameFactory<THandle extends GameHandle> = (
28
- canvas: HTMLCanvasElement,
29
- registry: DisposeRegistry,
30
- ) => THandle | Promise<THandle>;
31
-
32
- export interface UseGameEngineResult<THandle extends GameHandle> {
33
- /** Attach to your `<canvas ref={canvasRef} />`. */
34
- canvasRef: RefObject<HTMLCanvasElement | null>;
35
- /**
36
- * The live handle, available after mount (`null` during SSR/init). This is a ref
37
- * value, NOT reactive — gate UI that needs it on {@link UseGameEngineResult.isReady}.
38
- */
39
- handle: THandle | null;
40
- /** `true` once the factory has resolved and the engine is running. */
41
- isReady: boolean;
42
- }
43
-
44
- /**
45
- * Stable game-engine host hook — the architectural fix for the "game froze, can't
46
- * click out" bug.
47
- *
48
- * The bug: a game's lifecycle `useEffect` listed a context-derived value (typically
49
- * `authRequest` / `saveNow` from `AuthContext`) in its dep array. When that value's
50
- * identity churned on an unrelated re-render, React tore down and re-ran the effect —
51
- * disposing the *live* engine mid-session. A guarded setup (`if (engine) return`) then
52
- * refused to recreate it, leaving a dead canvas with no way out.
53
- *
54
- * The fix, by construction: the `factory` is held in a ref and is **never** an effect
55
- * dependency. The engine starts once on mount and is disposed once on true unmount.
56
- * AuthContext can re-render as much as it likes — the engine lifecycle is fully
57
- * decoupled from React's render cycle. Only the explicit `deps` (true restart triggers
58
- * like a room id or game mode) re-create the engine.
59
- *
60
- * Notes:
61
- * - **Canvas sizing / DPR is game-specific** and intentionally NOT handled here. Call
62
- * `resizeCanvasToDisplaySize(canvas)` (from `cursedbelt/game`) in your render loop.
63
- * - **Page Visibility**: the hook calls `handle.pause?()` on hide and `handle.resume?()`
64
- * on show. Cancel your rAF in `pause` and restart it in `resume`; pair with
65
- * `createFixedTimestep`'s `maxCatchup` guard so the post-resume delta can't spiral.
66
- *
67
- * @param factory Stable engine constructor (module-level or `useCallback`).
68
- * @param deps True restart triggers (keep small + stable). Default `[]` = mount-once.
69
- */
70
- export function useGameEngine<THandle extends GameHandle>(
71
- factory: GameFactory<THandle>,
72
- deps: DependencyList = [],
73
- ): UseGameEngineResult<THandle> {
74
- const canvasRef = useRef<HTMLCanvasElement | null>(null);
75
- const factoryRef = useRef(factory); // ← intentionally never an effect dependency
76
- const handleRef = useRef<THandle | null>(null);
77
- const [isReady, setIsReady] = useState(false);
78
-
79
- // Keep the factory ref fresh on every render WITHOUT re-running the engine effect.
80
- // (Same pattern as a `useEvent` / event-handler ref.)
81
- useEffect(() => {
82
- factoryRef.current = factory;
83
- });
84
-
85
- // NOTE (the freeze fix): `factory` is intentionally NOT a dependency of this effect —
86
- // it is read through the stable `factoryRef`, so AuthContext churn (or any re-render)
87
- // can never tear down and restart the live engine. Only `deps` (true restart triggers
88
- // like a room id) re-create it. The exhaustive-deps lint has nothing to flag precisely
89
- // because `factory` is never referenced directly here.
90
- useEffect(() => {
91
- const canvas = canvasRef.current;
92
- if (!canvas) return;
93
-
94
- const registry = new DisposeRegistry();
95
- let active = true;
96
-
97
- Promise.resolve(factoryRef.current(canvas, registry)).then((handle) => {
98
- if (!active) {
99
- // Unmounted before the (possibly async) factory resolved — dispose immediately.
100
- handle.dispose();
101
- return;
102
- }
103
- handleRef.current = handle;
104
- setIsReady(true);
105
-
106
- const onVisibility = () => {
107
- if (document.hidden) handle.pause?.();
108
- else handle.resume?.();
109
- };
110
- document.addEventListener('visibilitychange', onVisibility);
111
- registry.add(() => document.removeEventListener('visibilitychange', onVisibility));
112
- });
113
-
114
- return () => {
115
- active = false;
116
- registry.disposeAll();
117
- handleRef.current?.dispose();
118
- handleRef.current = null;
119
- setIsReady(false);
120
- };
121
- // biome-ignore lint/correctness/useExhaustiveDependencies: `deps` is this hook's own pass-through dependency-list parameter (same shape as useEffect's own deps arg), inherently caller-supplied rather than an inline literal; `factory` is intentionally excluded per the freeze-fix note above.
122
- }, deps);
123
-
124
- return { canvasRef, handle: handleRef.current, isReady };
125
- }
@@ -1,62 +0,0 @@
1
- import { afterEach, describe, expect, test } from 'bun:test';
2
- import { cleanup, fireEvent, render } from '@testing-library/react';
3
- import { CanvasNavHUD } from './CanvasNavHUD';
4
-
5
- afterEach(cleanup);
6
-
7
- const WORLD = { x: 0, y: 0, width: 1000, height: 500 };
8
-
9
- describe('CanvasNavHUD — DESIGN.md §7 names three controls', () => {
10
- test('fit, centre-on-focus and the radar are all present', () => {
11
- const { getByLabelText } = render(
12
- <CanvasNavHUD
13
- onFit={() => {}}
14
- onCenterFocus={() => {}}
15
- focusLabel='Ada Lovelace'
16
- world={WORLD}
17
- viewport={{ x: 100, y: 50, width: 200, height: 100 }}
18
- />,
19
- );
20
- expect(getByLabelText('Fit to canvas')).toBeTruthy();
21
- // The hint names WHAT it centres on, not "centre".
22
- expect(getByLabelText('Centre on Ada Lovelace')).toBeTruthy();
23
- expect(getByLabelText('Radar')).toBeTruthy();
24
- });
25
-
26
- test('the radar draws the viewport rectangle in world coordinates', () => {
27
- const { container } = render(
28
- <CanvasNavHUD
29
- onFit={() => {}}
30
- world={WORLD}
31
- viewport={{ x: 500, y: 250, width: 250, height: 125 }}
32
- />,
33
- );
34
- const rect = container.querySelector('[data-cb-hud-viewport]');
35
- // 132×88 radar; half-way across the world is half-way across the radar.
36
- expect(rect?.getAttribute('x')).toBe('66');
37
- expect(rect?.getAttribute('y')).toBe('44');
38
- expect(rect?.getAttribute('width')).toBe('33');
39
- });
40
-
41
- test('a canvas with no focus node simply omits that control', () => {
42
- const { queryByLabelText } = render(<CanvasNavHUD onFit={() => {}} world={WORLD} />);
43
- expect(queryByLabelText(/Centre on/)).toBeNull();
44
- });
45
-
46
- test('the zoom cluster clamps to its range and reports a percentage', () => {
47
- const seen: number[] = [];
48
- const { getByLabelText, getByText } = render(
49
- <CanvasNavHUD onFit={() => {}} zoom={0.1} zoomRange={[0.1, 4]} onZoomChange={(z) => seen.push(z)} />,
50
- );
51
- expect(getByText('10%')).toBeTruthy();
52
- // At the floor, zooming out further is not offered rather than silently ignored.
53
- expect(getByLabelText('Zoom out').hasAttribute('disabled')).toBe(true);
54
- fireEvent.click(getByLabelText('Zoom in'));
55
- expect(seen).toEqual([0.125]);
56
- });
57
-
58
- test('without world bounds there is no radar to draw', () => {
59
- const { queryByLabelText } = render(<CanvasNavHUD onFit={() => {}} />);
60
- expect(queryByLabelText('Radar')).toBeNull();
61
- });
62
- });
@@ -1,210 +0,0 @@
1
- import { Crosshair, Maximize2, Minus, Plus } from 'lucide-react';
2
- import { type PointerEvent as ReactPointerEvent, useCallback, useRef } from 'react';
3
- import { IconButton } from '../components/IconButton';
4
- import { Surface } from '../components/Surface';
5
- import { cn } from '../lib/cn';
6
-
7
- export interface CanvasRect {
8
- x: number;
9
- y: number;
10
- width: number;
11
- height: number;
12
- }
13
-
14
- export interface CanvasMinimapPoint {
15
- x: number;
16
- y: number;
17
- /** A `--tone-*`/`--chart-*` CSS value. Defaults to the muted foreground. */
18
- color?: string;
19
- /** The focus node is drawn larger and on top. */
20
- focus?: boolean;
21
- }
22
-
23
- export interface CanvasNavHUDProps {
24
- /** "Fit to canvas size" — frame the whole graph. Required: it is the escape
25
- * hatch from being lost, and a canvas without one is the reported defect. */
26
- onFit: () => void;
27
- /** "Centre on the active focus node". Omit only if the canvas has no focus. */
28
- onCenterFocus?: () => void;
29
- /** Named in the centre control's hint, so it says WHAT it will centre on. */
30
- focusLabel?: string;
31
- /** Current zoom (1 = 100%). Renders the zoom cluster when `onZoomChange` is set. */
32
- zoom?: number;
33
- onZoomChange?: (zoom: number) => void;
34
- /** Zoom bounds for the +/- controls. Default 0.1 – 4. */
35
- zoomRange?: [number, number];
36
- /** The whole graph's bounds, in world units. */
37
- world?: CanvasRect;
38
- /** What the user can currently see, in the same units. */
39
- viewport?: CanvasRect;
40
- /** Optional node dots for the radar. Keep it cheap — sample, don't map 10k nodes. */
41
- points?: CanvasMinimapPoint[];
42
- /** Click/drag the radar to jump. Receives world coordinates. */
43
- onMinimapJump?: (point: { x: number; y: number }) => void;
44
- /** Which corner the HUD floats in. Default `bottom-right`. */
45
- corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
46
- className?: string;
47
- }
48
-
49
- const CORNER_CLASS: Record<NonNullable<CanvasNavHUDProps['corner']>, string> = {
50
- 'top-left': 'top-3 left-3',
51
- 'top-right': 'top-3 right-3',
52
- 'bottom-left': 'bottom-3 left-3',
53
- 'bottom-right': 'bottom-3 right-3',
54
- };
55
-
56
- const MINIMAP_W = 132;
57
- const MINIMAP_H = 88;
58
-
59
- /**
60
- * The floating navigation HUD every infinite canvas must have.
61
- *
62
- * `DESIGN.md` §7: *"Every infinite panning or zooming canvas must include an
63
- * explicit floating Navigation HUD. The HUD must pack three essential controls: a
64
- * 'Fit to Canvas Size' trigger, a 'Center Canvas on Active Focus Node' tracking
65
- * action, and a low-opacity Radar Mini-Map positioned in a viewport corner
66
- * indicating active coordinate bounds."*
67
- *
68
- * Nothing shared implemented any of the three — `HudPill` is a pill, not a HUD —
69
- * so the family tree, the atlas, the orch lanes and fractals each had a different
70
- * partial answer or none. This is the one implementation.
71
- *
72
- * It is deliberately presentational: it owns no camera. The canvas keeps its own
73
- * transform and passes `world`/`viewport` in the SAME units, which is the only
74
- * contract the radar needs and the reason this works for an SVG tree and a WebGL
75
- * field alike. The controls sit in a `Surface` at `--z-sticky`, above the canvas
76
- * and below every overlay (`DESIGN.md` §5's containment rule), and every one of
77
- * them is a real 44×44 target on a coarse pointer via `IconButton`.
78
- */
79
- export function CanvasNavHUD({
80
- onFit,
81
- onCenterFocus,
82
- focusLabel,
83
- zoom,
84
- onZoomChange,
85
- zoomRange = [0.1, 4],
86
- world,
87
- viewport,
88
- points,
89
- onMinimapJump,
90
- corner = 'bottom-right',
91
- className,
92
- }: CanvasNavHUDProps) {
93
- const svgRef = useRef<SVGSVGElement | null>(null);
94
- const [minZoom, maxZoom] = zoomRange;
95
-
96
- const jump = useCallback(
97
- (e: ReactPointerEvent<SVGSVGElement>) => {
98
- if (!onMinimapJump || !world) return;
99
- const box = svgRef.current?.getBoundingClientRect();
100
- if (!box || box.width === 0 || box.height === 0) return;
101
- const fx = (e.clientX - box.left) / box.width;
102
- const fy = (e.clientY - box.top) / box.height;
103
- onMinimapJump({ x: world.x + fx * world.width, y: world.y + fy * world.height });
104
- },
105
- [onMinimapJump, world],
106
- );
107
-
108
- const showRadar = Boolean(world && world.width > 0 && world.height > 0);
109
- const scaleX = showRadar ? MINIMAP_W / (world as CanvasRect).width : 0;
110
- const scaleY = showRadar ? MINIMAP_H / (world as CanvasRect).height : 0;
111
- const toX = (x: number) => (x - (world as CanvasRect).x) * scaleX;
112
- const toY = (y: number) => (y - (world as CanvasRect).y) * scaleY;
113
-
114
- return (
115
- <Surface
116
- level='overlay'
117
- pad='none'
118
- className={cn(
119
- 'pointer-events-auto absolute z-[var(--z-sticky)] flex flex-col gap-2 rounded-lg p-2',
120
- // Low-opacity by default so it never competes with the graph, full presence
121
- // the moment the pointer or the keyboard is anywhere near it.
122
- 'opacity-70 transition-opacity duration-[var(--duration-fast)] hover:opacity-100 focus-within:opacity-100',
123
- CORNER_CLASS[corner],
124
- className,
125
- )}
126
- data-cb-canvas-hud=''
127
- >
128
- <div className='flex items-center gap-1'>
129
- <IconButton size='sm' label='Fit to canvas' onClick={onFit}>
130
- <Maximize2 />
131
- </IconButton>
132
- {onCenterFocus ? (
133
- <IconButton
134
- size='sm'
135
- label={focusLabel ? `Centre on ${focusLabel}` : 'Centre on the focused item'}
136
- onClick={onCenterFocus}
137
- >
138
- <Crosshair />
139
- </IconButton>
140
- ) : null}
141
- {onZoomChange ? (
142
- <>
143
- <IconButton
144
- size='sm'
145
- label='Zoom out'
146
- disabled={zoom !== undefined && zoom <= minZoom}
147
- onClick={() => onZoomChange(Math.max(minZoom, (zoom ?? 1) / 1.25))}
148
- >
149
- <Minus />
150
- </IconButton>
151
- <span className='min-w-10 text-center text-caption tabular-nums text-muted-foreground'>
152
- {Math.round((zoom ?? 1) * 100)}%
153
- </span>
154
- <IconButton
155
- size='sm'
156
- label='Zoom in'
157
- disabled={zoom !== undefined && zoom >= maxZoom}
158
- onClick={() => onZoomChange(Math.min(maxZoom, (zoom ?? 1) * 1.25))}
159
- >
160
- <Plus />
161
- </IconButton>
162
- </>
163
- ) : null}
164
- </div>
165
-
166
- {showRadar ? (
167
- <svg
168
- ref={svgRef}
169
- role={onMinimapJump ? 'button' : 'img'}
170
- aria-label={onMinimapJump ? 'Radar — click to jump' : 'Radar'}
171
- tabIndex={onMinimapJump ? 0 : undefined}
172
- width={MINIMAP_W}
173
- height={MINIMAP_H}
174
- viewBox={`0 0 ${MINIMAP_W} ${MINIMAP_H}`}
175
- onPointerDown={jump}
176
- className={cn(
177
- 'rounded border border-border bg-surface-sunken',
178
- onMinimapJump && 'cursor-pointer',
179
- )}
180
- >
181
- <title>Radar</title>
182
- {(points ?? []).map((p, i) => (
183
- <circle
184
- // biome-ignore lint/suspicious/noArrayIndexKey: sampled dots have no stable identity
185
- key={i}
186
- cx={toX(p.x)}
187
- cy={toY(p.y)}
188
- r={p.focus ? 2.5 : 1.4}
189
- fill={p.color ?? 'var(--muted-foreground)'}
190
- opacity={p.focus ? 1 : 0.55}
191
- />
192
- ))}
193
- {viewport ? (
194
- <rect
195
- x={toX(viewport.x)}
196
- y={toY(viewport.y)}
197
- width={Math.max(2, viewport.width * scaleX)}
198
- height={Math.max(2, viewport.height * scaleY)}
199
- fill='var(--accent)'
200
- fillOpacity={0.12}
201
- stroke='var(--accent)'
202
- strokeWidth={1}
203
- data-cb-hud-viewport=''
204
- />
205
- ) : null}
206
- </svg>
207
- ) : null}
208
- </Surface>
209
- );
210
- }
@@ -1,82 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { fitLabel } from './fitLabel';
3
- import { lodForNode, lodForZoom, lodShowsDetail, lodShowsText } from './lod';
4
-
5
- // A deterministic measurer: 6px per character at fontSize 12, scaling linearly.
6
- const measure = (t: string, size: number) => t.length * size * 0.5;
7
-
8
- describe('fitLabel — DESIGN.md §5, in the order the clause states', () => {
9
- test('a name that fits is left alone', () => {
10
- const fit = fitLabel('Ada Lovelace', { width: 200, fontSize: 12, measure });
11
- expect(fit.lines).toEqual(['Ada Lovelace']);
12
- expect(fit.strategy).toBe('as-is');
13
- expect(fit.overflow).toBe(false);
14
- });
15
-
16
- test('middle names go before anything else does', () => {
17
- // "Mary Anne Beth Sullivan" is 23 chars = 138px; "Mary Sullivan" is 13 = 78px.
18
- const fit = fitLabel('Mary Anne Beth Sullivan', { width: 90, fontSize: 12, measure });
19
- expect(fit.lines).toEqual(['Mary Sullivan']);
20
- expect(fit.strategy).toBe('stripped');
21
- });
22
-
23
- test('then a clean second line, never a mid-word cut', () => {
24
- const fit = fitLabel('Bartholomew Wickersham', { width: 80, fontSize: 12, measure });
25
- expect(fit.lines.length).toBe(2);
26
- expect(fit.lines.join(' ')).toBe('Bartholomew Wickersham');
27
- // The load-bearing property: nothing is chopped.
28
- for (const line of fit.lines) expect(line.endsWith('…')).toBe(false);
29
- });
30
-
31
- test('then the type scales down, in whole pixels', () => {
32
- // 11 chars: 66px at 12, 55px at 10 — so it lands on 10 and stops there.
33
- const fit = fitLabel('Bartholomew', { width: 58, fontSize: 12, measure });
34
- expect(fit.strategy).toBe('scaled');
35
- expect(Number.isInteger(fit.fontSize)).toBe(true);
36
- expect(fit.fontSize).toBeLessThan(12);
37
- expect(measure(fit.lines[0] as string, fit.fontSize)).toBeLessThanOrEqual(58);
38
- });
39
-
40
- test('the last rung is a first name and a last initial', () => {
41
- // "Alexander Wickersham" is 20 chars and will not scale into 62px at the 10px
42
- // floor; "Alexander W." is 12 chars = 60px, which does.
43
- const fit = fitLabel('Alexander Wickersham', { width: 62, fontSize: 12, measure, maxLines: 1 });
44
- expect(fit.lines).toEqual(['Alexander W.']);
45
- expect(fit.strategy).toBe('initials');
46
- });
47
-
48
- test('a box too small for even that reports overflow, so the caller draws a glyph', () => {
49
- const fit = fitLabel('Bartholomew Wickersham', { width: 4, fontSize: 12, measure });
50
- expect(fit.overflow).toBe(true);
51
- expect(fit.strategy).toBe('overflow');
52
- });
53
-
54
- test('empty and zero-width inputs do not throw', () => {
55
- expect(fitLabel('', { width: 100, fontSize: 12 }).lines).toEqual(['']);
56
- expect(fitLabel('x', { width: 0, fontSize: 12 }).overflow).toBe(true);
57
- });
58
- });
59
-
60
- describe('lodForZoom — DESIGN.md §7 draws the line at 50%', () => {
61
- test('full detail at 100%, names only in between, glyphs below half', () => {
62
- expect(lodForZoom(1)).toBe('full');
63
- expect(lodForZoom(0.6)).toBe('compact');
64
- expect(lodForZoom(0.49)).toBe('glyph');
65
- });
66
-
67
- test('the 50% boundary itself still shows the name', () => {
68
- expect(lodForZoom(0.5)).toBe('compact');
69
- expect(lodShowsText(lodForZoom(0.5))).toBe(true);
70
- expect(lodShowsDetail(lodForZoom(0.5))).toBe(false);
71
- });
72
-
73
- test('a hovered or focused node keeps its label at any zoom', () => {
74
- // "until zoomed in OR HOVERED over" — the half that keeps a glyph field usable.
75
- expect(lodForNode(0.1, false)).toBe('glyph');
76
- expect(lodForNode(0.1, true)).toBe('full');
77
- });
78
-
79
- test('a nonsense zoom degrades to full detail rather than to nothing', () => {
80
- expect(lodForZoom(Number.NaN)).toBe('full');
81
- });
82
- });
@@ -1,150 +0,0 @@
1
- /**
2
- * Fitting a human name into a node box without a harsh cut.
3
- *
4
- * `DESIGN.md` §5: *"Node diagrams, map elements, or tree visualizations displaying
5
- * user identities must never truncate names with harsh, broken text string cuts.
6
- * Use responsive font scaling, wrap long strings onto a clean second line, or
7
- * optimize middle-initial stripping to keep names human-readable."*
8
- *
9
- * That is three strategies in a stated order, and every canvas in the fleet was
10
- * improvising one of them (family's `TreeCanvas` does its own; the atlas does
11
- * none). This is the shared ladder, as a pure function with no DOM, so a `<text>`
12
- * in SVG, a 2D canvas and an absolutely-positioned `<div>` all fit labels the same
13
- * way and it can be tested without a browser.
14
- *
15
- * @see lodForZoom — below ~50% zoom the answer is not a smaller label, it is a glyph.
16
- */
17
-
18
- export type FitStrategy = 'as-is' | 'stripped' | 'wrapped' | 'scaled' | 'initials' | 'overflow';
19
-
20
- export interface FitLabelOptions {
21
- /** Usable width of the node box, in px. */
22
- width: number;
23
- /** The size the label would like to be, in px. */
24
- fontSize: number;
25
- /** How many lines the box can hold. Default 2 — the "clean second line". */
26
- maxLines?: number;
27
- /** Never scale below this. Default 10px, which is `DESIGN.md` §4's floor minus
28
- * the two px a canvas label is allowed as a non-body-text glyph label. */
29
- minFontSize?: number;
30
- /**
31
- * Width of `text` at `fontSize`. The default is a linear approximation
32
- * (0.52em average advance, which is close for a UI sans at these sizes); a
33
- * caller holding a real `CanvasRenderingContext2D` should pass `measureText`
34
- * so the fit is exact.
35
- */
36
- measure?: (text: string, fontSize: number) => number;
37
- }
38
-
39
- export interface FittedLabel {
40
- /** One entry per rendered line. Never empty. */
41
- lines: string[];
42
- /** The size to actually draw at. */
43
- fontSize: number;
44
- /** What the ladder had to do — surfaced so a caller can offer the full name in a
45
- * hint exactly when something was dropped, rather than always or never. */
46
- strategy: FitStrategy;
47
- /** True when even the last rung does not fit. Draw a glyph, not an ellipsis. */
48
- overflow: boolean;
49
- }
50
-
51
- const defaultMeasure = (text: string, fontSize: number) => text.length * fontSize * 0.52;
52
-
53
- /** `Mary Anne Beth Sullivan` → `Mary Sullivan`; drops middles, keeps first + last. */
54
- function stripMiddles(parts: string[]): string[] {
55
- if (parts.length <= 2) return parts;
56
- return [parts[0] as string, parts[parts.length - 1] as string];
57
- }
58
-
59
- /** `Mary Sullivan` → `Mary S.` — the last rung before giving up on text entirely. */
60
- function toInitialForm(parts: string[]): string {
61
- if (parts.length === 0) return '';
62
- if (parts.length === 1) return parts[0] as string;
63
- const first = parts[0] as string;
64
- const last = parts[parts.length - 1] as string;
65
- return `${first} ${last.charAt(0).toUpperCase()}.`;
66
- }
67
-
68
- /** Greedy word wrap into at most `maxLines`; returns null if it does not fit. */
69
- function wrap(
70
- words: string[],
71
- width: number,
72
- fontSize: number,
73
- maxLines: number,
74
- measure: (t: string, s: number) => number,
75
- ): string[] | null {
76
- const lines: string[] = [];
77
- let current = '';
78
- for (const word of words) {
79
- const candidate = current ? `${current} ${word}` : word;
80
- if (measure(candidate, fontSize) <= width || !current) {
81
- current = candidate;
82
- } else {
83
- lines.push(current);
84
- current = word;
85
- if (lines.length >= maxLines) return null;
86
- }
87
- }
88
- if (current) lines.push(current);
89
- if (lines.length === 0 || lines.length > maxLines) return null;
90
- return lines.every((l) => measure(l, fontSize) <= width) ? lines : null;
91
- }
92
-
93
- /**
94
- * Fit `text` into `width`, in `DESIGN.md` §5's stated order: as-is → drop middle
95
- * names → wrap onto a second line → scale the type down → first name + last
96
- * initial → give up and tell the caller to draw a glyph.
97
- */
98
- export function fitLabel(text: string, options: FitLabelOptions): FittedLabel {
99
- const {
100
- width,
101
- fontSize,
102
- maxLines = 2,
103
- minFontSize = 10,
104
- measure = defaultMeasure,
105
- } = options;
106
- const clean = String(text ?? '').trim().replace(/\s+/g, ' ');
107
- if (!clean) return { lines: [''], fontSize, strategy: 'as-is', overflow: false };
108
- if (width <= 0) return { lines: [clean], fontSize, strategy: 'overflow', overflow: true };
109
-
110
- if (measure(clean, fontSize) <= width) {
111
- return { lines: [clean], fontSize, strategy: 'as-is', overflow: false };
112
- }
113
-
114
- const parts = clean.split(' ');
115
- const stripped = stripMiddles(parts).join(' ');
116
- if (stripped !== clean && measure(stripped, fontSize) <= width) {
117
- return { lines: [stripped], fontSize, strategy: 'stripped', overflow: false };
118
- }
119
-
120
- if (maxLines > 1) {
121
- const wrapped = wrap(stripped.split(' '), width, fontSize, maxLines, measure);
122
- if (wrapped) {
123
- return {
124
- lines: wrapped,
125
- fontSize,
126
- strategy: stripped === clean ? 'wrapped' : 'stripped',
127
- overflow: false,
128
- };
129
- }
130
- }
131
-
132
- // Scale down in whole pixels — a fractional ladder produces sizes that differ
133
- // between two adjacent nodes by less than the eye can see, for no extra fit.
134
- for (let size = fontSize - 1; size >= minFontSize; size--) {
135
- const wrapped =
136
- maxLines > 1
137
- ? wrap(stripped.split(' '), width, size, maxLines, measure)
138
- : measure(stripped, size) <= width
139
- ? [stripped]
140
- : null;
141
- if (wrapped) return { lines: wrapped, fontSize: size, strategy: 'scaled', overflow: false };
142
- }
143
-
144
- const initials = toInitialForm(parts);
145
- if (measure(initials, minFontSize) <= width) {
146
- return { lines: [initials], fontSize: minFontSize, strategy: 'initials', overflow: false };
147
- }
148
-
149
- return { lines: [initials], fontSize: minFontSize, strategy: 'overflow', overflow: true };
150
- }
@@ -1,15 +0,0 @@
1
- export {
2
- CanvasNavHUD,
3
- type CanvasMinimapPoint,
4
- type CanvasNavHUDProps,
5
- type CanvasRect,
6
- } from './CanvasNavHUD';
7
- export { fitLabel, type FitLabelOptions, type FittedLabel, type FitStrategy } from './fitLabel';
8
- export {
9
- type CanvasLod,
10
- lodForNode,
11
- lodForZoom,
12
- lodShowsDetail,
13
- lodShowsText,
14
- type LodThresholds,
15
- } from './lod';