@weasel-js/core 1.11.0 → 1.11.1

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 (365) hide show
  1. package/CHANGELOG.md +427 -0
  2. package/README.md +1 -1
  3. package/dist/animation/index.js +3 -3
  4. package/dist/animation/rig/bindRig.js +1 -1
  5. package/dist/animation/rig/rigNode.js.map +1 -1
  6. package/dist/barrel/canvas.js +7 -3
  7. package/dist/barrel/interactions.js +5 -4
  8. package/dist/barrel/paths.js +3 -3
  9. package/dist/barrel/renderer.js +3 -2
  10. package/dist/barrel/routing.js +6 -11
  11. package/dist/barrel/scene.js +8 -5
  12. package/dist/barrel/viewport.js +4 -3
  13. package/dist/canvas/Canvas.js +3 -1
  14. package/dist/canvas/Canvas.js.map +1 -1
  15. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +17 -3
  16. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  17. package/dist/canvas/SceneCanvas/builtinToolSets.js +30 -0
  18. package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -0
  19. package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -0
  20. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  21. package/dist/canvas/SceneCanvas/coordTrace.js +5 -16
  22. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -1
  23. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +5 -0
  24. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  25. package/dist/canvas/SceneCanvas/features.js +45 -5
  26. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  27. package/dist/canvas/SceneCanvas/props.js +1 -11
  28. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  29. package/dist/canvas/SceneCanvas/toolRegistry.js +8 -4
  30. package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -11
  32. package/dist/canvas/SceneCanvas/useActionsPropResolver.js.map +1 -1
  33. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  34. package/dist/canvas/SceneCanvas/useBuiltinTools.js +2 -9
  35. package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +93 -0
  37. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/useCanvasSelection.js +7 -2
  39. package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/useCanvasTools.js +8 -8
  41. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/useNodeKinds.js +1 -1
  43. package/dist/canvas/SceneCanvas/useNodeKinds.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +13 -2
  45. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  46. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -3
  47. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  48. package/dist/canvas/SceneCanvas/useSceneChrome.js +6 -7
  49. package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
  50. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +5 -3
  51. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  52. package/dist/canvas/SceneCanvas/useScenePicking.js +2 -3
  53. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  54. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +2 -1
  55. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  56. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +3 -4
  57. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  58. package/dist/canvas/SceneCanvas.js +16 -14
  59. package/dist/canvas/SceneCanvas.js.map +1 -1
  60. package/dist/canvas/SurfaceCanvas.js +5 -22
  61. package/dist/canvas/SurfaceCanvas.js.map +1 -1
  62. package/dist/canvas/canvasRenderer.js +9 -5
  63. package/dist/canvas/canvasRenderer.js.map +1 -1
  64. package/dist/canvas/contextAttributes.js +14 -0
  65. package/dist/canvas/contextAttributes.js.map +1 -0
  66. package/dist/canvas/deps/editAnchors.js +9 -113
  67. package/dist/canvas/deps/editAnchors.js.map +1 -1
  68. package/dist/canvas/deps/insert.js +4 -2
  69. package/dist/canvas/deps/insert.js.map +1 -1
  70. package/dist/canvas/deps/pathEditCommits.js +193 -0
  71. package/dist/canvas/deps/pathEditCommits.js.map +1 -0
  72. package/dist/canvas/deps/snap.js +12 -9
  73. package/dist/canvas/deps/snap.js.map +1 -1
  74. package/dist/canvas/deps/transformFollowers.js +20 -0
  75. package/dist/canvas/deps/transformFollowers.js.map +1 -0
  76. package/dist/canvas/derivedPath.js +20 -1
  77. package/dist/canvas/derivedPath.js.map +1 -1
  78. package/dist/canvas/drawnBounds.js +29 -3
  79. package/dist/canvas/drawnBounds.js.map +1 -1
  80. package/dist/canvas/nodeShapes/derivedPainter.js +9 -1
  81. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  82. package/dist/canvas/nodeShapes/pathPainter.js +3 -2
  83. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  84. package/dist/canvas/nodeShapes/shapePainter.js +1 -1
  85. package/dist/canvas/overlayRoleStrokes.js +12 -0
  86. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  87. package/dist/canvas/pathOverlay.js +27 -7
  88. package/dist/canvas/pathOverlay.js.map +1 -1
  89. package/dist/canvas/renderDebugSnapshot.js +1 -0
  90. package/dist/canvas/renderDebugSnapshot.js.map +1 -1
  91. package/dist/canvas/renderSceneToPixels.js +6 -5
  92. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  93. package/dist/canvas/sceneAdapter.js.map +1 -1
  94. package/dist/canvas/useCanvasPaint.js +2 -1
  95. package/dist/canvas/useCanvasPaint.js.map +1 -1
  96. package/dist/canvas/useInFlightBounds.js +52 -0
  97. package/dist/canvas/useInFlightBounds.js.map +1 -0
  98. package/dist/canvas/useViewHelpers.js +7 -9
  99. package/dist/canvas/useViewHelpers.js.map +1 -1
  100. package/dist/clipboard.d.ts +3 -2
  101. package/dist/{clone-DZDrfIlU.d.ts → clone-C3e0OTuF.d.ts} +8 -21
  102. package/dist/clone.d.ts +4 -3
  103. package/dist/core/geometry/autoPoseDescriptor.js +1 -1
  104. package/dist/core/ops/setDependsOn.js +37 -0
  105. package/dist/core/ops/setDependsOn.js.map +1 -0
  106. package/dist/core/ops/writtenBy.js +53 -0
  107. package/dist/core/ops/writtenBy.js.map +1 -0
  108. package/dist/core/pages/fitViewToPage.js +12 -1
  109. package/dist/core/pages/fitViewToPage.js.map +1 -1
  110. package/dist/core/pages/pageEdits.js +164 -0
  111. package/dist/core/pages/pageEdits.js.map +1 -0
  112. package/dist/core/pages/pages.js +30 -1
  113. package/dist/core/pages/pages.js.map +1 -1
  114. package/dist/core/scene/NodeRouting.js +8 -3
  115. package/dist/core/scene/NodeRouting.js.map +1 -1
  116. package/dist/core/scene/arrivalWindow.js.map +1 -1
  117. package/dist/core/scene/carryDescendants.js +51 -0
  118. package/dist/core/scene/carryDescendants.js.map +1 -0
  119. package/dist/core/scene/docValues.js +57 -0
  120. package/dist/core/scene/docValues.js.map +1 -0
  121. package/dist/core/scene/editPass.js +111 -0
  122. package/dist/core/scene/editPass.js.map +1 -0
  123. package/dist/core/scene/effectivePose.js +7 -1
  124. package/dist/core/scene/effectivePose.js.map +1 -1
  125. package/dist/core/scene/fieldEdits.js +4 -1
  126. package/dist/core/scene/fieldEdits.js.map +1 -1
  127. package/dist/core/scene/historyFacade.js +1 -0
  128. package/dist/core/scene/historyFacade.js.map +1 -1
  129. package/dist/core/scene/layoutPass.js +28 -3
  130. package/dist/core/scene/layoutPass.js.map +1 -1
  131. package/dist/core/scene/recorder.js +28 -19
  132. package/dist/core/scene/recorder.js.map +1 -1
  133. package/dist/core/scene/scene.js +15 -9
  134. package/dist/core/scene/scene.js.map +1 -1
  135. package/dist/core/scene/sceneChanges.js +13 -0
  136. package/dist/core/scene/sceneChanges.js.map +1 -0
  137. package/dist/core/scene/sceneEdits.js +8 -1
  138. package/dist/core/scene/sceneEdits.js.map +1 -1
  139. package/dist/core/scene/sceneMutators.js +1 -0
  140. package/dist/core/scene/sceneMutators.js.map +1 -1
  141. package/dist/core/scene/sceneRecording.js +20 -3
  142. package/dist/core/scene/sceneRecording.js.map +1 -1
  143. package/dist/core/scene/sceneSelection.js +4 -1
  144. package/dist/core/scene/sceneSelection.js.map +1 -1
  145. package/dist/core/scene/sceneState.js +11 -5
  146. package/dist/core/scene/sceneState.js.map +1 -1
  147. package/dist/core/scene/serialization.js +5 -3
  148. package/dist/core/scene/serialization.js.map +1 -1
  149. package/dist/core/scene/types.js.map +1 -1
  150. package/dist/core/scene/unitScopes.js +19 -1
  151. package/dist/core/scene/unitScopes.js.map +1 -1
  152. package/dist/core/selection/usePublishSceneSelection.js +6 -12
  153. package/dist/core/selection/usePublishSceneSelection.js.map +1 -1
  154. package/dist/core/shapeKinds.js +25 -11
  155. package/dist/core/shapeKinds.js.map +1 -1
  156. package/dist/{cornerHandles-Camw_yeu.d.ts → cornerHandles-BHuoMs1o.d.ts} +1 -1
  157. package/dist/debug/createDebugOverlayLayer.js +16 -111
  158. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  159. package/dist/debug/createDebugSink.js +2 -1
  160. package/dist/debug/createDebugSink.js.map +1 -1
  161. package/dist/debug/defaultTheme.js +4 -2
  162. package/dist/debug/defaultTheme.js.map +1 -1
  163. package/dist/debug/features.js +17 -0
  164. package/dist/debug/features.js.map +1 -0
  165. package/dist/debug/overlayPanels.js +117 -0
  166. package/dist/debug/overlayPanels.js.map +1 -0
  167. package/dist/debug/parseDebugFlags.js +4 -13
  168. package/dist/debug/parseDebugFlags.js.map +1 -1
  169. package/dist/{depSchema-Cgfu_G9s.d.ts → depSchema-BpZNOv7q.d.ts} +114 -892
  170. package/dist/devtools/handlers.js +112 -0
  171. package/dist/devtools/handlers.js.map +1 -0
  172. package/dist/devtools/history.js +71 -0
  173. package/dist/devtools/history.js.map +1 -0
  174. package/dist/devtools/hook.js +74 -0
  175. package/dist/devtools/hook.js.map +1 -0
  176. package/dist/devtools/label.js +25 -0
  177. package/dist/devtools/label.js.map +1 -0
  178. package/dist/devtools/notices.js +58 -0
  179. package/dist/devtools/notices.js.map +1 -0
  180. package/dist/devtools/protocol.js +7 -0
  181. package/dist/devtools/protocol.js.map +1 -0
  182. package/dist/devtools/registry.js +90 -0
  183. package/dist/devtools/registry.js.map +1 -0
  184. package/dist/devtools/root.js +80 -0
  185. package/dist/devtools/root.js.map +1 -0
  186. package/dist/devtools/serialize.js +86 -0
  187. package/dist/devtools/serialize.js.map +1 -0
  188. package/dist/devtools/session.js +111 -0
  189. package/dist/devtools/session.js.map +1 -0
  190. package/dist/devtools/transport.js +67 -0
  191. package/dist/devtools/transport.js.map +1 -0
  192. package/dist/devtools/useDevtoolsRoot.js +81 -0
  193. package/dist/devtools/useDevtoolsRoot.js.map +1 -0
  194. package/dist/devtools-protocol.d.ts +435 -0
  195. package/dist/devtools-protocol.js +7 -0
  196. package/dist/devtools.d.ts +8 -0
  197. package/dist/devtools.js +12 -0
  198. package/dist/devtools.js.map +1 -0
  199. package/dist/features/chrome-caps/useHoverTracking.js +16 -10
  200. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  201. package/dist/features/ingestion/contentHandlers.js +25 -2
  202. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  203. package/dist/features/measure/measureCommands.js +7 -5
  204. package/dist/features/measure/measureCommands.js.map +1 -1
  205. package/dist/features/paths/index.js +2 -2
  206. package/dist/features/paths/pathEditExtension.js.map +1 -1
  207. package/dist/features/paths/pathSnap/pathSnapSource.js +5 -5
  208. package/dist/features/paths/pathSnap/pathSnapSource.js.map +1 -1
  209. package/dist/features/paths/pathSnap/sceneIndex.js +10 -3
  210. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  211. package/dist/features/selection/selectionPose.js +4 -1
  212. package/dist/features/selection/selectionPose.js.map +1 -1
  213. package/dist/features/snap/gather.js +9 -1
  214. package/dist/features/snap/gather.js.map +1 -1
  215. package/dist/features/snap/lazySession.js +17 -0
  216. package/dist/features/snap/lazySession.js.map +1 -0
  217. package/dist/features/snap/marks.js.map +1 -1
  218. package/dist/features/snap/probes2d.js +27 -19
  219. package/dist/features/snap/probes2d.js.map +1 -1
  220. package/dist/features/snap/readoutLayer.js.map +1 -1
  221. package/dist/features/snap/roles.js +2 -1
  222. package/dist/features/snap/roles.js.map +1 -1
  223. package/dist/features/snap/rotationSource.js +25 -8
  224. package/dist/features/snap/rotationSource.js.map +1 -1
  225. package/dist/features/snap/sceneSession2d.js +6 -4
  226. package/dist/features/snap/sceneSession2d.js.map +1 -1
  227. package/dist/features/snap/session2d.js +37 -23
  228. package/dist/features/snap/session2d.js.map +1 -1
  229. package/dist/features/snap/spanSnap.js +5 -2
  230. package/dist/features/snap/spanSnap.js.map +1 -1
  231. package/dist/icons.table.js +1 -1
  232. package/dist/icons.table.js.map +1 -1
  233. package/dist/icons.table.json +0 -10
  234. package/dist/index.d.ts +701 -696
  235. package/dist/index.js +35 -29
  236. package/dist/interactions/actions/align/action.js +6 -4
  237. package/dist/interactions/actions/align/action.js.map +1 -1
  238. package/dist/interactions/actions/align/align.js.map +1 -1
  239. package/dist/interactions/actions/booleans/action.js +7 -1
  240. package/dist/interactions/actions/booleans/action.js.map +1 -1
  241. package/dist/interactions/actions/clone/action.js +1 -1
  242. package/dist/interactions/actions/clone/behaviors.js +4 -4
  243. package/dist/interactions/actions/clone/behaviors.js.map +1 -1
  244. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  245. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  246. package/dist/interactions/actions/defaults/createPaintAction.js +1 -1
  247. package/dist/interactions/actions/defaults/delete.js +48 -39
  248. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  249. package/dist/interactions/actions/defaults/duplicate.js +1 -1
  250. package/dist/interactions/actions/defaults/group.js +2 -2
  251. package/dist/interactions/actions/defaults/ingest.js +7 -5
  252. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  253. package/dist/interactions/actions/defaults/nudge.js +13 -38
  254. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  255. package/dist/interactions/actions/distribute/action.js +6 -4
  256. package/dist/interactions/actions/distribute/action.js.map +1 -1
  257. package/dist/interactions/actions/drawnBoxes.js +2 -1
  258. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  259. package/dist/interactions/actions/edit-anchors/action.js +3 -6
  260. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  261. package/dist/interactions/actions/edit-anchors/preview.js +41 -8
  262. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  263. package/dist/interactions/actions/flip/action.js +6 -4
  264. package/dist/interactions/actions/flip/action.js.map +1 -1
  265. package/dist/interactions/actions/gestureLifecycle.js +5 -2
  266. package/dist/interactions/actions/gestureLifecycle.js.map +1 -1
  267. package/dist/interactions/actions/insert/action.js +1 -2
  268. package/dist/interactions/actions/insert/action.js.map +1 -1
  269. package/dist/interactions/actions/insert/commit.js +1 -1
  270. package/dist/interactions/actions/insert/parent.js +11 -0
  271. package/dist/interactions/actions/insert/parent.js.map +1 -0
  272. package/dist/interactions/actions/insert/placeAction.js +7 -2
  273. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  274. package/dist/interactions/actions/insert/session.js +5 -1
  275. package/dist/interactions/actions/insert/session.js.map +1 -1
  276. package/dist/interactions/actions/move/action.js +8 -4
  277. package/dist/interactions/actions/move/action.js.map +1 -1
  278. package/dist/interactions/actions/move/behaviors/layoutDrop.js +0 -1
  279. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  280. package/dist/interactions/actions/move/behaviors/snapToContainer.js +1 -1
  281. package/dist/interactions/actions/move/capture.js +1 -1
  282. package/dist/interactions/actions/move/commit/index.js +0 -2
  283. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  284. package/dist/interactions/actions/move/commit/layoutDrop.js +2 -2
  285. package/dist/interactions/actions/move/commit/layoutRelease.js +2 -2
  286. package/dist/interactions/actions/move/commit/reparent.js +11 -5
  287. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  288. package/dist/interactions/actions/move/frame.js +4 -10
  289. package/dist/interactions/actions/move/frame.js.map +1 -1
  290. package/dist/interactions/actions/move/landedDescendants.js +20 -17
  291. package/dist/interactions/actions/move/landedDescendants.js.map +1 -1
  292. package/dist/interactions/actions/move/layout/container.js +2 -1
  293. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  294. package/dist/interactions/actions/move/layout/dragged.js +1 -1
  295. package/dist/interactions/actions/move/layout/dropPass.js +18 -1
  296. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  297. package/dist/interactions/actions/move/layout/exits.js +5 -5
  298. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  299. package/dist/interactions/actions/move/session.js +14 -3
  300. package/dist/interactions/actions/move/session.js.map +1 -1
  301. package/dist/interactions/actions/move/translate.js +8 -37
  302. package/dist/interactions/actions/move/translate.js.map +1 -1
  303. package/dist/interactions/actions/moveNodes.js +87 -0
  304. package/dist/interactions/actions/moveNodes.js.map +1 -0
  305. package/dist/interactions/actions/planeInput.js.map +1 -1
  306. package/dist/interactions/actions/resize/action.js +83 -72
  307. package/dist/interactions/actions/resize/action.js.map +1 -1
  308. package/dist/interactions/actions/resize/commit.js +2 -1
  309. package/dist/interactions/actions/resize/commit.js.map +1 -1
  310. package/dist/interactions/actions/resize/session.js +2 -0
  311. package/dist/interactions/actions/resize/session.js.map +1 -1
  312. package/dist/interactions/actions/resolvePreviews.js +3 -3
  313. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  314. package/dist/interactions/actions/rotate/action.js +7 -4
  315. package/dist/interactions/actions/rotate/action.js.map +1 -1
  316. package/dist/interactions/actions/rotate/commit.js +2 -1
  317. package/dist/interactions/actions/rotate/commit.js.map +1 -1
  318. package/dist/interactions/actions/rotate/session.js +2 -0
  319. package/dist/interactions/actions/rotate/session.js.map +1 -1
  320. package/dist/interactions/actions/selectionSubtree.js +13 -2
  321. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  322. package/dist/interactions/actions/testUtils.js.map +1 -1
  323. package/dist/interactions/actions/transform-again/action.js +5 -6
  324. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  325. package/dist/interactions/actions/transformFinish.js +106 -0
  326. package/dist/interactions/actions/transformFinish.js.map +1 -0
  327. package/dist/kitSettingsSchema.js +0 -4
  328. package/dist/kitSettingsSchema.js.map +1 -1
  329. package/dist/move.d.ts +5 -4
  330. package/dist/{options-p9GZMXbY.d.ts → options-DBxEryYq.d.ts} +1 -1
  331. package/dist/resize.d.ts +4 -3
  332. package/dist/routing.d.ts +1 -1
  333. package/dist/routing.js +2 -2
  334. package/dist/test-hook/createTestHook.js +1 -1
  335. package/dist/test-hook/createTestHook.js.map +1 -1
  336. package/dist/test-hook/useTestHook.js +2 -1
  337. package/dist/test-hook/useTestHook.js.map +1 -1
  338. package/dist/test-seams.d.ts +9 -5
  339. package/dist/tools/builtin/pen/penHandle.js +3 -5
  340. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  341. package/dist/tools/index.js +26 -0
  342. package/dist/{types-CHVXGfYm.d.ts → types-7puAuVD2.d.ts} +3 -262
  343. package/dist/types-BN9hbdMI.d.ts +184 -0
  344. package/dist/types-CniVctmk.d.ts +949 -0
  345. package/dist/types-oFeQ5VjV.d.ts +264 -0
  346. package/dist/version.js +1 -1
  347. package/package.json +35 -25
  348. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js +0 -65
  349. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js.map +0 -1
  350. package/dist/tools/builtin/bone/boneActions.js +0 -156
  351. package/dist/tools/builtin/bone/boneActions.js.map +0 -1
  352. package/dist/tools/builtin/bone/boneOverlay.js +0 -71
  353. package/dist/tools/builtin/bone/boneOverlay.js.map +0 -1
  354. package/dist/tools/builtin/bone/placedRig.js +0 -28
  355. package/dist/tools/builtin/bone/placedRig.js.map +0 -1
  356. package/dist/tools/builtin/bone/rigEditState.js +0 -30
  357. package/dist/tools/builtin/bone/rigEditState.js.map +0 -1
  358. package/dist/tools/builtin/bone/useBoneTool.js +0 -125
  359. package/dist/tools/builtin/bone/useBoneTool.js.map +0 -1
  360. package/dist/tools/builtin/weight/useWeightTool.js +0 -101
  361. package/dist/tools/builtin/weight/useWeightTool.js.map +0 -1
  362. package/dist/tools/builtin/weight/weightActions.js +0 -140
  363. package/dist/tools/builtin/weight/weightActions.js.map +0 -1
  364. package/dist/tools/builtin/weight/weightOverlay.js +0 -78
  365. package/dist/tools/builtin/weight/weightOverlay.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"createDebugOverlayLayer.js","names":["rectPath"],"sources":["../../src/debug/createDebugOverlayLayer.ts"],"sourcesContent":["import type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { View } from 'core/viewport/view';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\nimport { rotatePoint } from 'interactions/actions/rotate/geometry';\nimport { PATH_L, PATH_M, PATH_Z, type PolygonPath } from '@weasel-js/geom';\nimport { textCommandFromRuns } from '@weasel-js/renderer';\nimport type {\n DebugConfig,\n DebugSnapshot,\n DebugStroke,\n DebugStrokes,\n DebugTheme,\n FrameStats,\n RecordedViewport,\n} from './types';\nimport { DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME } from './defaultTheme';\n\n/** What `createDebugOverlayLayer` paints from: a sink to snapshot and the features to draw. */\nexport interface CreateDebugOverlayLayerOpts {\n sink: { snapshot(): DebugSnapshot };\n config: DebugConfig;\n}\n\nexport const DEBUG_OVERLAY_ID = 'debug-overlay';\nconst FPS_WINDOW = 60;\n\n/**\n * Screen-space `RenderLayer` that paints the sink's snapshot. Appended at\n * the top of the Canvas's layer stack when `debug` is enabled. World-space\n * coords in the snapshot are projected through `view` here, since the\n * layer itself runs at identity transform.\n */\nexport function createDebugOverlayLayer({\n sink,\n config,\n}: CreateDebugOverlayLayerOpts): RenderLayer<unknown> {\n // Timestamps of the last FPS_WINDOW draws, kept for the life of the layer.\n const fpsHistory: number[] = [];\n return {\n id: DEBUG_OVERLAY_ID,\n label: 'Debug overlay',\n space: 'screen',\n alwaysOn: true,\n draw: (_data, view, dims) => {\n if (config.fps) {\n fpsHistory.push(typeof performance !== 'undefined' ? performance.now() : Date.now());\n if (fpsHistory.length > FPS_WINDOW) fpsHistory.shift();\n } else if (fpsHistory.length > 0) {\n fpsHistory.length = 0;\n }\n return buildDebugOverlayCommands(sink.snapshot(), config, view, dims, fpsHistory);\n },\n };\n}\n\n/**\n * The debug overlay's draw commands for one snapshot, in screen space (CSS\n * pixels of a `dims`-sized canvas looking through `view`). What the overlay\n * layer paints each frame, and what `renderDebugSnapshot` rasterizes.\n *\n * `frameTimes` are `performance.now()` stamps of recent paints, oldest first;\n * the frame panel derives its rate from them and shows none without two.\n */\nexport function buildDebugOverlayCommands(\n snapshot: DebugSnapshot,\n config: DebugConfig,\n view: View,\n dims: Dims,\n frameTimes: readonly number[] = [],\n): DrawCommand[] {\n const theme: DebugTheme = { ...DEFAULT_DEBUG_THEME, ...(config.theme ?? {}) };\n const strokes: DebugStrokes = { ...DEFAULT_DEBUG_STROKES, ...(config.strokes ?? {}) };\n const s = snapshot;\n const t = viewToTransform(view);\n const out: DrawCommand[] = [];\n\n if (config.viewport && s.viewport) emitViewport(out, s.viewport, view, dims, theme, strokes);\n if (config.hitboxes) emitHitboxes(out, s, view, t, theme, strokes);\n if (config.bounds) emitBounds(out, s, view, t, theme, strokes);\n if (config.handles) emitHandles(out, s, t, theme, strokes);\n if (config.origins) emitOrigins(out, s, t, theme);\n if (config.snap) emitSnap(out, s, t, theme, strokes);\n if (config.ids) emitIds(out, s, t, theme);\n if (config.layers) emitLayersPanel(out, s, dims, theme);\n if (config.fps) emitFramePanel(out, frameTimes, s.frame, theme);\n return out;\n}\n\n// --- emitters (screen-space) ---\n\nfunction approxCircleScreen(cx: number, cy: number, r: number, segments = 24): PolygonPath {\n return approxEllipseScreen(cx, cy, r, r, segments);\n}\n\nfunction approxEllipseScreen(cx: number, cy: number, rx: number, ry: number, segments = 24): PolygonPath {\n // Commands: M + (segments-1) L + Z. Coords: (segments) × 2 — pairs for\n // the moveTo and the (segments-1) lineTos. PATH_Z consumes no coords.\n const cmds = new Uint8Array(segments + 1);\n const coords = new Float32Array(segments * 2);\n cmds[0] = PATH_M;\n coords[0] = cx + rx;\n coords[1] = cy;\n for (let i = 1; i < segments; i++) {\n cmds[i] = PATH_L;\n const theta = (i / segments) * Math.PI * 2;\n coords[i * 2] = cx + rx * Math.cos(theta);\n coords[i * 2 + 1] = cy + ry * Math.sin(theta);\n }\n cmds[segments] = PATH_Z;\n return { kind: 'polygon', commands: cmds, coords, fillRule: 'nonzero' };\n}\n\n/** A world rect turned `rotation` about its center, as the screen quad it\n * lands on. */\nfunction rotatedRectScreen(\n r: { x: number; y: number; width: number; height: number; rotation: number },\n t: ReturnType<typeof viewToTransform>,\n): PolygonPath {\n const cx = r.x + r.width / 2, cy = r.y + r.height / 2;\n const coords = new Float32Array(8);\n const corners = [[r.x, r.y], [r.x + r.width, r.y], [r.x + r.width, r.y + r.height], [r.x, r.y + r.height]];\n corners.forEach(([x, y], i) => {\n const w = rotatePoint(x, y, cx, cy, r.rotation);\n const [sx, sy] = worldToScreen(w.x, w.y, t);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n });\n return {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),\n coords,\n fillRule: 'nonzero',\n };\n}\n\nfunction rectPath(x: number, y: number, w: number, h: number): { kind: 'rect'; x: number; y: number; width: number; height: number } {\n return { kind: 'rect', x, y, width: w, height: h };\n}\n\n/** A `DebugStroke` + color as the renderer's stroke shape. `dash` is dropped\n * when empty rather than passed through, so a solid line stays solid. */\nfunction strokeOf(s: DebugStroke, color: string) {\n return {\n paint: { kind: 'solid' as const, color },\n width: s.width,\n ...(s.dash && s.dash.length > 0 ? { dash: [...s.dash] } : {}),\n };\n}\n\nfunction emitHitboxes(\n out: DrawCommand[],\n s: DebugSnapshot,\n view: View,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const fill = { kind: 'solid' as const, color: theme.hitboxFill };\n const stroke = strokeOf(strokes.hitbox, theme.hitboxStroke);\n for (const h of s.hitboxes) {\n if (h.shape.kind === 'rect' && h.shape.rotation) {\n const path = rotatedRectScreen({ ...h.shape, rotation: h.shape.rotation }, t);\n out.push({ kind: 'path', path, fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'rect') {\n const [sx, sy] = worldToScreen(h.shape.x, h.shape.y, t);\n const sw = h.shape.width * view.scale.x;\n const sh = h.shape.height * view.scale.y;\n out.push({ kind: 'path', path: rectPath(sx, sy, sw, sh), fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'circle') {\n const [cx, cy] = worldToScreen(h.shape.cx, h.shape.cy, t);\n const rx = h.shape.r * Math.abs(view.scale.x);\n const ry = h.shape.r * Math.abs(view.scale.y);\n out.push({ kind: 'path', path: approxEllipseScreen(cx, cy, rx, ry), fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'polygon' && h.shape.points.length > 2) {\n const pts = h.shape.points;\n const coords = new Float32Array(pts.length * 2);\n pts.forEach((p, i) => {\n const [sx, sy] = worldToScreen(p.x, p.y, t);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n });\n const commands = new Uint8Array(pts.length + 1).fill(PATH_L);\n commands[0] = PATH_M;\n commands[pts.length] = PATH_Z;\n out.push({ kind: 'path', path: { kind: 'polygon', commands, coords, fillRule: 'nonzero' }, fill: { paint: fill }, stroke });\n }\n // 'path' kind: v1 punt — matches 2D behavior.\n }\n}\n\nfunction emitBounds(\n out: DrawCommand[],\n s: DebugSnapshot,\n view: View,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const stroke = strokeOf(strokes.bounds, theme.bounds);\n for (const b of s.bounds) {\n const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);\n const sw = b.bounds.width * view.scale.x;\n const sh = b.bounds.height * view.scale.y;\n out.push({ kind: 'path', path: rectPath(sx, sy, sw, sh), stroke });\n }\n}\n\nfunction emitHandles(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const stroke = strokeOf(strokes.handle, theme.handle);\n for (const h of s.handles) {\n const [cx, cy] = worldToScreen(h.position.x, h.position.y, t);\n const horiz: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([cx - 4, cy, cx + 4, cy]),\n fillRule: 'nonzero',\n };\n const vert: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([cx, cy - 4, cx, cy + 4]),\n fillRule: 'nonzero',\n };\n out.push({ kind: 'path', path: horiz, stroke });\n out.push({ kind: 'path', path: vert, stroke });\n }\n}\n\nfunction emitOrigins(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n): void {\n const fill = { kind: 'solid' as const, color: theme.origin };\n for (const o of s.origins) {\n const [cx, cy] = worldToScreen(o.point.x, o.point.y, t);\n out.push({ kind: 'path', path: approxCircleScreen(cx, cy, 3), fill: { paint: fill } });\n }\n}\n\nfunction emitSnap(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const color = theme.snap;\n for (const c of s.snap) {\n const [cx, cy] = worldToScreen(c.point.x, c.point.y, t);\n const cmd: PathDrawCommand = {\n kind: 'path',\n path: approxCircleScreen(cx, cy, 4),\n ...(c.accepted\n ? { fill: { paint: { kind: 'solid' as const, color } } }\n : { stroke: strokeOf(strokes.snap, color) }),\n };\n out.push(cmd);\n }\n}\n\nfunction emitIds(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n): void {\n // Pulls from the bounds stream — every consumer that records bounds\n // (selection overlay's handle math, the kit's per-node debug shim) gets\n // an id label for free.\n for (const b of s.bounds) {\n const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);\n out.push(textCommandFromRuns(\n sx + 2,\n sy + 11,\n [{ text: b.id, fill: { paint: { kind: 'solid', color: theme.idText } } }],\n { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 10 },\n ));\n }\n}\n\nfunction emitLayersPanel(\n out: DrawCommand[],\n s: DebugSnapshot,\n dims: Dims,\n theme: DebugTheme,\n): void {\n if (s.layers.length === 0) return;\n const lineH = 14;\n const padX = 6;\n const padY = 4;\n const lines = s.layers.map((l) => `[${l.index}] ${l.id} (${l.space})`);\n // Approximate width: 11px font, monospaced — assume each glyph ≈6.6px.\n // 2D path uses ctx.measureText; we don't have a ctx here, so fall back\n // to a character-count estimate. Pixel-level accuracy is not load-bearing\n // for a debug panel; right-edge anchoring stays correct.\n const charW = 6.6;\n let maxW = 0;\n for (const line of lines) maxW = Math.max(maxW, line.length * charW);\n const boxW = maxW + padX * 2;\n const boxH = lines.length * lineH + padY * 2;\n const x = dims.width - boxW - 8;\n const y = 8;\n out.push({\n kind: 'path',\n path: rectPath(x, y, boxW, boxH),\n fill: { paint: { kind: 'solid', color: theme.layerTextBg } },\n });\n for (let i = 0; i < lines.length; i++) {\n out.push(textCommandFromRuns(\n x + padX,\n y + padY + i * lineH,\n [{ text: lines[i], fill: { paint: { kind: 'solid', color: theme.layerText } } }],\n { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 11 },\n ));\n }\n}\n\nconst MONO = { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 11 };\nconst MONO_CHAR_W = 6.6;\nconst LINE_H = 14;\n\n/** A dark box holding monospace lines, at `(x, y)`; `y < 0` anchors its bottom\n * that far above `bottom`. Width is estimated from the character count — there\n * is no text measurer here, and a debug panel does not need one. */\nfunction emitTextPanel(\n out: DrawCommand[],\n lines: readonly string[],\n x: number,\n y: number,\n color: string,\n bg: string,\n): void {\n const padX = 6;\n const padY = 4;\n let maxLen = 0;\n for (const l of lines) maxLen = Math.max(maxLen, l.length);\n out.push({\n kind: 'path',\n path: rectPath(x, y, maxLen * MONO_CHAR_W + padX * 2, lines.length * LINE_H + padY * 2),\n fill: { paint: { kind: 'solid', color: bg } },\n });\n for (let i = 0; i < lines.length; i++) {\n out.push(textCommandFromRuns(\n x + padX,\n y + padY + i * LINE_H,\n [{ text: lines[i], fill: { paint: { kind: 'solid', color } } }],\n MONO,\n ));\n }\n}\n\n/** Fixed-width number, right-aligned, `decimals` places: `%{width}.{decimals}f`. */\nfunction fixed(v: number, width: number, decimals: number): string {\n return v.toFixed(decimals).padStart(width);\n}\n\nfunction emitFramePanel(\n out: DrawCommand[],\n history: readonly number[],\n frame: FrameStats | null,\n theme: DebugTheme,\n): void {\n const span = history.length >= 2 ? history[history.length - 1] - history[0] : 0;\n const interval = span > 0 ? span / (history.length - 1) : null;\n // Columns: label, draw-call count, value, unit — every row the same width,\n // so decimals line up down the panel.\n const rows: [label: string, count: number | null, value: number | null, unit: string][] = [\n ['fps', null, interval ? 1000 / interval : null, ' '],\n ['frame', null, interval, 'ms'],\n ];\n if (frame) {\n rows.push(['paint', frame.drawCalls, frame.paintMs, 'ms']);\n for (const l of frame.layers) rows.push([l.id, l.drawCalls, l.ms, 'ms']);\n }\n let labelW = 0;\n for (const [label] of rows) labelW = Math.max(labelW, label.length);\n const line = (label: string, count: string, value: string, unit: string) =>\n `${label.padEnd(labelW)} ${count.padStart(5)} ${value.padStart(7)} ${unit}`;\n const lines = rows.map(([label, count, value, unit]) =>\n line(label, count === null ? '' : String(count), value === null ? '—' : fixed(value, 7, 2), unit));\n if (frame) lines.splice(2, 0, line('', 'draws', 'cpu', ' '));\n emitTextPanel(out, lines, 8, 8, theme.fpsText, theme.fpsTextBg);\n}\n\nfunction emitViewport(\n out: DrawCommand[],\n rec: RecordedViewport,\n view: View,\n dims: Dims,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const t = viewToTransform(view);\n const stroke = strokeOf(strokes.viewport, theme.viewport);\n // Where the viewport the gesture started from sits in the current camera.\n const { from, to } = rec;\n const [fx, fy] = worldToScreen(from.x, from.y, t);\n out.push({\n kind: 'path',\n path: rectPath(fx, fy, (dims.width / from.scale.x) * view.scale.x, (dims.height / from.scale.y) * view.scale.y),\n stroke,\n });\n if (rec.anchor) {\n const [ax0, ay0] = worldToScreen(rec.anchor.x, rec.anchor.y, viewToTransform(from));\n const [ax1, ay1] = worldToScreen(rec.anchor.x, rec.anchor.y, t);\n out.push({ kind: 'path', path: approxCircleScreen(ax0, ay0, 2.5), fill: { paint: { kind: 'solid', color: theme.viewport } } });\n if (Math.hypot(ax1 - ax0, ay1 - ay0) > 0.5) {\n out.push({ kind: 'path', path: segment(ax0, ay0, ax1, ay1), stroke });\n }\n out.push({ kind: 'path', path: approxCircleScreen(ax1, ay1, 6), stroke: strokeOf({ width: strokes.viewport.width }, theme.viewport) });\n }\n const text = rec.kind === 'pan'\n ? `pan Δ ${signed((from.x - to.x) * to.scale.x)} ${signed((from.y - to.y) * to.scale.y)} px`\n : `zoom ×${(to.scale.x / from.scale.x).toFixed(3)} ${from.scale.x.toFixed(3)} → ${to.scale.x.toFixed(3)}`;\n emitTextPanel(out, [text], 8, dims.height - LINE_H - 16, theme.viewport, theme.layerTextBg);\n}\n\nfunction signed(v: number): string {\n const r = Math.abs(v) < 0.05 ? 0 : v;\n return `${r >= 0 ? '+' : '-'}${Math.abs(r).toFixed(1)}`.padStart(8);\n}\n\nfunction segment(x0: number, y0: number, x1: number, y1: number): PolygonPath {\n return {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([x0, y0, x1, y1]),\n fillRule: 'nonzero',\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"createDebugOverlayLayer.js","names":["rectPath"],"sources":["../../src/debug/createDebugOverlayLayer.ts"],"sourcesContent":["import type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { View } from 'core/viewport/view';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\nimport { rotatePoint } from 'interactions/actions/rotate/geometry';\nimport { PATH_L, PATH_M, PATH_Z, type PolygonPath } from '@weasel-js/geom';\nimport { textCommandFromRuns } from '@weasel-js/renderer';\nimport type {\n DebugConfig,\n DebugSnapshot,\n DebugStroke,\n DebugStrokes,\n DebugTheme,\n RecordedViewport,\n} from './types';\nimport { DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME } from './defaultTheme';\nimport { emitFramePanel, emitLayersPanel, emitTextPanel, LINE_H, rectPath } from './overlayPanels';\n\n/** What `createDebugOverlayLayer` paints from: a sink to snapshot and the features to draw. */\nexport interface CreateDebugOverlayLayerOpts {\n sink: { snapshot(): DebugSnapshot };\n config: DebugConfig;\n}\n\nexport const DEBUG_OVERLAY_ID = 'debug-overlay';\nconst FPS_WINDOW = 60;\n\n/**\n * Screen-space `RenderLayer` that paints the sink's snapshot. Appended at\n * the top of the Canvas's layer stack when `debug` is enabled. World-space\n * coords in the snapshot are projected through `view` here, since the\n * layer itself runs at identity transform.\n */\nexport function createDebugOverlayLayer({\n sink,\n config,\n}: CreateDebugOverlayLayerOpts): RenderLayer<unknown> {\n // Timestamps of the last FPS_WINDOW draws, kept for the life of the layer.\n const fpsHistory: number[] = [];\n return {\n id: DEBUG_OVERLAY_ID,\n label: 'Debug overlay',\n space: 'screen',\n alwaysOn: true,\n draw: (_data, view, dims) => {\n if (config.fps) {\n fpsHistory.push(typeof performance !== 'undefined' ? performance.now() : Date.now());\n if (fpsHistory.length > FPS_WINDOW) fpsHistory.shift();\n } else if (fpsHistory.length > 0) {\n fpsHistory.length = 0;\n }\n return buildDebugOverlayCommands(sink.snapshot(), config, view, dims, fpsHistory);\n },\n };\n}\n\n/**\n * The debug overlay's draw commands for one snapshot, in screen space (CSS\n * pixels of a `dims`-sized canvas looking through `view`). What the overlay\n * layer paints each frame, and what `renderDebugSnapshot` rasterizes.\n *\n * `frameTimes` are `performance.now()` stamps of recent paints, oldest first;\n * the frame panel derives its rate from them and shows none without two.\n */\nexport function buildDebugOverlayCommands(\n snapshot: DebugSnapshot,\n config: DebugConfig,\n view: View,\n dims: Dims,\n frameTimes: readonly number[] = [],\n): DrawCommand[] {\n const theme: DebugTheme = { ...DEFAULT_DEBUG_THEME, ...(config.theme ?? {}) };\n const strokes: DebugStrokes = { ...DEFAULT_DEBUG_STROKES, ...(config.strokes ?? {}) };\n const s = snapshot;\n const t = viewToTransform(view);\n const out: DrawCommand[] = [];\n\n if (config.viewport && s.viewport) emitViewport(out, s.viewport, view, dims, theme, strokes);\n if (config.hitboxes) emitHitboxes(out, s, view, t, theme, strokes);\n if (config.bounds) emitBounds(out, s, view, t, theme, strokes);\n if (config.highlight?.length) emitHighlight(out, s, new Set(config.highlight), view, t, theme, strokes);\n if (config.handles) emitHandles(out, s, t, theme, strokes);\n if (config.origins) emitOrigins(out, s, t, theme);\n if (config.snap) emitSnap(out, s, t, theme, strokes);\n if (config.ids) emitIds(out, s, t, theme);\n if (config.layers) emitLayersPanel(out, s, dims, theme);\n if (config.fps) emitFramePanel(out, frameTimes, s.frame, theme);\n return out;\n}\n\n// --- emitters (screen-space) ---\n\nfunction approxCircleScreen(cx: number, cy: number, r: number, segments = 24): PolygonPath {\n return approxEllipseScreen(cx, cy, r, r, segments);\n}\n\nfunction approxEllipseScreen(cx: number, cy: number, rx: number, ry: number, segments = 24): PolygonPath {\n // Commands: M + (segments-1) L + Z. Coords: (segments) × 2 — pairs for\n // the moveTo and the (segments-1) lineTos. PATH_Z consumes no coords.\n const cmds = new Uint8Array(segments + 1);\n const coords = new Float32Array(segments * 2);\n cmds[0] = PATH_M;\n coords[0] = cx + rx;\n coords[1] = cy;\n for (let i = 1; i < segments; i++) {\n cmds[i] = PATH_L;\n const theta = (i / segments) * Math.PI * 2;\n coords[i * 2] = cx + rx * Math.cos(theta);\n coords[i * 2 + 1] = cy + ry * Math.sin(theta);\n }\n cmds[segments] = PATH_Z;\n return { kind: 'polygon', commands: cmds, coords, fillRule: 'nonzero' };\n}\n\n/** A world rect turned `rotation` about its center, as the screen quad it\n * lands on. */\nfunction rotatedRectScreen(\n r: { x: number; y: number; width: number; height: number; rotation: number },\n t: ReturnType<typeof viewToTransform>,\n): PolygonPath {\n const cx = r.x + r.width / 2, cy = r.y + r.height / 2;\n const coords = new Float32Array(8);\n const corners = [[r.x, r.y], [r.x + r.width, r.y], [r.x + r.width, r.y + r.height], [r.x, r.y + r.height]];\n corners.forEach(([x, y], i) => {\n const w = rotatePoint(x, y, cx, cy, r.rotation);\n const [sx, sy] = worldToScreen(w.x, w.y, t);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n });\n return {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),\n coords,\n fillRule: 'nonzero',\n };\n}\n\nfunction strokeOf(s: DebugStroke, color: string) {\n return {\n paint: { kind: 'solid' as const, color },\n width: s.width,\n ...(s.dash && s.dash.length > 0 ? { dash: [...s.dash] } : {}),\n };\n}\n\nfunction emitHitboxes(\n out: DrawCommand[],\n s: DebugSnapshot,\n view: View,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const fill = { kind: 'solid' as const, color: theme.hitboxFill };\n const stroke = strokeOf(strokes.hitbox, theme.hitboxStroke);\n for (const h of s.hitboxes) {\n if (h.shape.kind === 'rect' && h.shape.rotation) {\n const path = rotatedRectScreen({ ...h.shape, rotation: h.shape.rotation }, t);\n out.push({ kind: 'path', path, fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'rect') {\n const [sx, sy] = worldToScreen(h.shape.x, h.shape.y, t);\n const sw = h.shape.width * view.scale.x;\n const sh = h.shape.height * view.scale.y;\n out.push({ kind: 'path', path: rectPath(sx, sy, sw, sh), fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'circle') {\n const [cx, cy] = worldToScreen(h.shape.cx, h.shape.cy, t);\n const rx = h.shape.r * Math.abs(view.scale.x);\n const ry = h.shape.r * Math.abs(view.scale.y);\n out.push({ kind: 'path', path: approxEllipseScreen(cx, cy, rx, ry), fill: { paint: fill }, stroke });\n } else if (h.shape.kind === 'polygon' && h.shape.points.length > 2) {\n const pts = h.shape.points;\n const coords = new Float32Array(pts.length * 2);\n pts.forEach((p, i) => {\n const [sx, sy] = worldToScreen(p.x, p.y, t);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n });\n const commands = new Uint8Array(pts.length + 1).fill(PATH_L);\n commands[0] = PATH_M;\n commands[pts.length] = PATH_Z;\n out.push({ kind: 'path', path: { kind: 'polygon', commands, coords, fillRule: 'nonzero' }, fill: { paint: fill }, stroke });\n }\n // 'path' kind: v1 punt — matches 2D behavior.\n }\n}\n\nfunction emitBounds(\n out: DrawCommand[],\n s: DebugSnapshot,\n view: View,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const stroke = strokeOf(strokes.bounds, theme.bounds);\n for (const b of s.bounds) {\n const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);\n const sw = b.bounds.width * view.scale.x;\n const sh = b.bounds.height * view.scale.y;\n out.push({ kind: 'path', path: rectPath(sx, sy, sw, sh), stroke });\n }\n}\n\nfunction emitHighlight(\n out: DrawCommand[],\n s: DebugSnapshot,\n ids: ReadonlySet<string>,\n view: View,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const stroke = strokeOf(strokes.highlight, theme.highlight);\n for (const b of s.bounds) {\n if (!ids.has(b.id)) continue;\n const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);\n out.push({ kind: 'path', path: rectPath(sx, sy, b.bounds.width * view.scale.x, b.bounds.height * view.scale.y), stroke });\n }\n}\n\nfunction emitHandles(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const stroke = strokeOf(strokes.handle, theme.handle);\n for (const h of s.handles) {\n const [cx, cy] = worldToScreen(h.position.x, h.position.y, t);\n const horiz: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([cx - 4, cy, cx + 4, cy]),\n fillRule: 'nonzero',\n };\n const vert: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([cx, cy - 4, cx, cy + 4]),\n fillRule: 'nonzero',\n };\n out.push({ kind: 'path', path: horiz, stroke });\n out.push({ kind: 'path', path: vert, stroke });\n }\n}\n\nfunction emitOrigins(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n): void {\n const fill = { kind: 'solid' as const, color: theme.origin };\n for (const o of s.origins) {\n const [cx, cy] = worldToScreen(o.point.x, o.point.y, t);\n out.push({ kind: 'path', path: approxCircleScreen(cx, cy, 3), fill: { paint: fill } });\n }\n}\n\nfunction emitSnap(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const color = theme.snap;\n for (const c of s.snap) {\n const [cx, cy] = worldToScreen(c.point.x, c.point.y, t);\n const cmd: PathDrawCommand = {\n kind: 'path',\n path: approxCircleScreen(cx, cy, 4),\n ...(c.accepted\n ? { fill: { paint: { kind: 'solid' as const, color } } }\n : { stroke: strokeOf(strokes.snap, color) }),\n };\n out.push(cmd);\n }\n}\n\nfunction emitIds(\n out: DrawCommand[],\n s: DebugSnapshot,\n t: ReturnType<typeof viewToTransform>,\n theme: DebugTheme,\n): void {\n // Pulls from the bounds stream — every consumer that records bounds\n // (selection overlay's handle math, the kit's per-node debug shim) gets\n // an id label for free.\n for (const b of s.bounds) {\n const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);\n out.push(textCommandFromRuns(\n sx + 2,\n sy + 11,\n [{ text: b.id, fill: { paint: { kind: 'solid', color: theme.idText } } }],\n { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 10 },\n ));\n }\n}\n\nfunction emitViewport(\n out: DrawCommand[],\n rec: RecordedViewport,\n view: View,\n dims: Dims,\n theme: DebugTheme,\n strokes: DebugStrokes,\n): void {\n const t = viewToTransform(view);\n const stroke = strokeOf(strokes.viewport, theme.viewport);\n // Where the viewport the gesture started from sits in the current camera.\n const { from, to } = rec;\n const [fx, fy] = worldToScreen(from.x, from.y, t);\n out.push({\n kind: 'path',\n path: rectPath(fx, fy, (dims.width / from.scale.x) * view.scale.x, (dims.height / from.scale.y) * view.scale.y),\n stroke,\n });\n if (rec.anchor) {\n const [ax0, ay0] = worldToScreen(rec.anchor.x, rec.anchor.y, viewToTransform(from));\n const [ax1, ay1] = worldToScreen(rec.anchor.x, rec.anchor.y, t);\n out.push({ kind: 'path', path: approxCircleScreen(ax0, ay0, 2.5), fill: { paint: { kind: 'solid', color: theme.viewport } } });\n if (Math.hypot(ax1 - ax0, ay1 - ay0) > 0.5) {\n out.push({ kind: 'path', path: segment(ax0, ay0, ax1, ay1), stroke });\n }\n out.push({ kind: 'path', path: approxCircleScreen(ax1, ay1, 6), stroke: strokeOf({ width: strokes.viewport.width }, theme.viewport) });\n }\n const text = rec.kind === 'pan'\n ? `pan Δ ${signed((from.x - to.x) * to.scale.x)} ${signed((from.y - to.y) * to.scale.y)} px`\n : `zoom ×${(to.scale.x / from.scale.x).toFixed(3)} ${from.scale.x.toFixed(3)} → ${to.scale.x.toFixed(3)}`;\n emitTextPanel(out, [text], 8, dims.height - LINE_H - 16, theme.viewport, theme.layerTextBg);\n}\n\nfunction signed(v: number): string {\n const r = Math.abs(v) < 0.05 ? 0 : v;\n return `${r >= 0 ? '+' : '-'}${Math.abs(r).toFixed(1)}`.padStart(8);\n}\n\nfunction segment(x0: number, y0: number, x1: number, y1: number): PolygonPath {\n return {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([x0, y0, x1, y1]),\n fillRule: 'nonzero',\n };\n}\n"],"mappings":"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"}
@@ -19,6 +19,7 @@ function createDebugSink(config) {
19
19
  viewport: null,
20
20
  frame: null
21
21
  };
22
+ const highlighted = new Set(config.highlight ?? []);
22
23
  return {
23
24
  recordHitbox(id, kind, shape) {
24
25
  if (!config.hitboxes) return;
@@ -37,7 +38,7 @@ function createDebugSink(config) {
37
38
  });
38
39
  },
39
40
  recordBounds(id, bounds) {
40
- if (!config.bounds) return;
41
+ if (!config.bounds && !highlighted.has(id)) return;
41
42
  snap.bounds.push({
42
43
  id,
43
44
  bounds
@@ -1 +1 @@
1
- {"version":3,"file":"createDebugSink.js","names":[],"sources":["../../src/debug/createDebugSink.ts"],"sourcesContent":["import type {\n CanvasDebugSink,\n DebugConfig,\n DebugSnapshot,\n HandleKind,\n HitShape,\n} from './types';\n\n/**\n * Build a sink that stores recorded primitives in arrays keyed by feature.\n * A `recordX` call no-ops when its flag in `config` is off, so callers can\n * record unconditionally.\n *\n * `beginFrame()` clears every per-frame array. Snap, the latest viewport\n * record, and the last frame's stats survive across frames. `snapshot()`\n * returns live references — no copy.\n */\nexport function createDebugSink(config: DebugConfig): CanvasDebugSink {\n const snap: DebugSnapshot = {\n hitboxes: [],\n handles: [],\n bounds: [],\n origins: [],\n snap: [],\n layers: [],\n viewport: null,\n frame: null,\n };\n return {\n recordHitbox(id, kind, shape: HitShape) {\n if (!config.hitboxes) return;\n snap.hitboxes.push({ id, kind, shape });\n },\n recordHandle(id, position, kind: HandleKind) {\n if (!config.handles) return;\n snap.handles.push({ id, position, kind });\n },\n recordBounds(id, bounds) {\n if (!config.bounds) return;\n snap.bounds.push({ id, bounds });\n },\n recordOrigin(id, point) {\n if (!config.origins) return;\n snap.origins.push({ id, point });\n },\n recordSnapCandidate(point, accepted) {\n if (!config.snap) return;\n snap.snap.push({ point, accepted });\n },\n recordLayer(id, label, space, index) {\n if (!config.layers) return;\n snap.layers.push({ id, label, space, index });\n },\n recordViewport(kind, from, to, anchor) {\n if (!config.viewport) return;\n snap.viewport = anchor ? { kind, from, to, anchor } : { kind, from, to };\n },\n recordFrame(stats) {\n if (!config.fps) return;\n snap.frame = stats;\n },\n beginFrame() {\n snap.hitboxes.length = 0;\n snap.handles.length = 0;\n snap.bounds.length = 0;\n snap.origins.length = 0;\n snap.layers.length = 0;\n },\n clearSnap() {\n snap.snap.length = 0;\n },\n snapshot() {\n return snap;\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,gBAAgB,QAAsC;CACpE,MAAM,OAAsB;EAC1B,UAAU,CAAC;EACX,SAAS,CAAC;EACV,QAAQ,CAAC;EACT,SAAS,CAAC;EACV,MAAM,CAAC;EACP,QAAQ,CAAC;EACT,UAAU;EACV,OAAO;CACT;CACA,OAAO;EACL,aAAa,IAAI,MAAM,OAAiB;GACtC,IAAI,CAAC,OAAO,UAAU;GACtB,KAAK,SAAS,KAAK;IAAE;IAAI;IAAM;GAAM,CAAC;EACxC;EACA,aAAa,IAAI,UAAU,MAAkB;GAC3C,IAAI,CAAC,OAAO,SAAS;GACrB,KAAK,QAAQ,KAAK;IAAE;IAAI;IAAU;GAAK,CAAC;EAC1C;EACA,aAAa,IAAI,QAAQ;GACvB,IAAI,CAAC,OAAO,QAAQ;GACpB,KAAK,OAAO,KAAK;IAAE;IAAI;GAAO,CAAC;EACjC;EACA,aAAa,IAAI,OAAO;GACtB,IAAI,CAAC,OAAO,SAAS;GACrB,KAAK,QAAQ,KAAK;IAAE;IAAI;GAAM,CAAC;EACjC;EACA,oBAAoB,OAAO,UAAU;GACnC,IAAI,CAAC,OAAO,MAAM;GAClB,KAAK,KAAK,KAAK;IAAE;IAAO;GAAS,CAAC;EACpC;EACA,YAAY,IAAI,OAAO,OAAO,OAAO;GACnC,IAAI,CAAC,OAAO,QAAQ;GACpB,KAAK,OAAO,KAAK;IAAE;IAAI;IAAO;IAAO;GAAM,CAAC;EAC9C;EACA,eAAe,MAAM,MAAM,IAAI,QAAQ;GACrC,IAAI,CAAC,OAAO,UAAU;GACtB,KAAK,WAAW,SAAS;IAAE;IAAM;IAAM;IAAI;GAAO,IAAI;IAAE;IAAM;IAAM;GAAG;EACzE;EACA,YAAY,OAAO;GACjB,IAAI,CAAC,OAAO,KAAK;GACjB,KAAK,QAAQ;EACf;EACA,aAAa;GACX,KAAK,SAAS,SAAS;GACvB,KAAK,QAAQ,SAAS;GACtB,KAAK,OAAO,SAAS;GACrB,KAAK,QAAQ,SAAS;GACtB,KAAK,OAAO,SAAS;EACvB;EACA,YAAY;GACV,KAAK,KAAK,SAAS;EACrB;EACA,WAAW;GACT,OAAO;EACT;CACF;AACF"}
1
+ {"version":3,"file":"createDebugSink.js","names":[],"sources":["../../src/debug/createDebugSink.ts"],"sourcesContent":["import type {\n CanvasDebugSink,\n DebugConfig,\n DebugSnapshot,\n HandleKind,\n HitShape,\n} from './types';\n\n/**\n * Build a sink that stores recorded primitives in arrays keyed by feature.\n * A `recordX` call no-ops when its flag in `config` is off, so callers can\n * record unconditionally.\n *\n * `beginFrame()` clears every per-frame array. Snap, the latest viewport\n * record, and the last frame's stats survive across frames. `snapshot()`\n * returns live references — no copy.\n */\nexport function createDebugSink(config: DebugConfig): CanvasDebugSink {\n const snap: DebugSnapshot = {\n hitboxes: [],\n handles: [],\n bounds: [],\n origins: [],\n snap: [],\n layers: [],\n viewport: null,\n frame: null,\n };\n const highlighted = new Set(config.highlight ?? []);\n return {\n recordHitbox(id, kind, shape: HitShape) {\n if (!config.hitboxes) return;\n snap.hitboxes.push({ id, kind, shape });\n },\n recordHandle(id, position, kind: HandleKind) {\n if (!config.handles) return;\n snap.handles.push({ id, position, kind });\n },\n recordBounds(id, bounds) {\n if (!config.bounds && !highlighted.has(id)) return;\n snap.bounds.push({ id, bounds });\n },\n recordOrigin(id, point) {\n if (!config.origins) return;\n snap.origins.push({ id, point });\n },\n recordSnapCandidate(point, accepted) {\n if (!config.snap) return;\n snap.snap.push({ point, accepted });\n },\n recordLayer(id, label, space, index) {\n if (!config.layers) return;\n snap.layers.push({ id, label, space, index });\n },\n recordViewport(kind, from, to, anchor) {\n if (!config.viewport) return;\n snap.viewport = anchor ? { kind, from, to, anchor } : { kind, from, to };\n },\n recordFrame(stats) {\n if (!config.fps) return;\n snap.frame = stats;\n },\n beginFrame() {\n snap.hitboxes.length = 0;\n snap.handles.length = 0;\n snap.bounds.length = 0;\n snap.origins.length = 0;\n snap.layers.length = 0;\n },\n clearSnap() {\n snap.snap.length = 0;\n },\n snapshot() {\n return snap;\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,gBAAgB,QAAsC;CACpE,MAAM,OAAsB;EAC1B,UAAU,CAAC;EACX,SAAS,CAAC;EACV,QAAQ,CAAC;EACT,SAAS,CAAC;EACV,MAAM,CAAC;EACP,QAAQ,CAAC;EACT,UAAU;EACV,OAAO;CACT;CACA,MAAM,cAAc,IAAI,IAAI,OAAO,aAAa,CAAC,CAAC;CAClD,OAAO;EACL,aAAa,IAAI,MAAM,OAAiB;GACtC,IAAI,CAAC,OAAO,UAAU;GACtB,KAAK,SAAS,KAAK;IAAE;IAAI;IAAM;GAAM,CAAC;EACxC;EACA,aAAa,IAAI,UAAU,MAAkB;GAC3C,IAAI,CAAC,OAAO,SAAS;GACrB,KAAK,QAAQ,KAAK;IAAE;IAAI;IAAU;GAAK,CAAC;EAC1C;EACA,aAAa,IAAI,QAAQ;GACvB,IAAI,CAAC,OAAO,UAAU,CAAC,YAAY,IAAI,EAAE,GAAG;GAC5C,KAAK,OAAO,KAAK;IAAE;IAAI;GAAO,CAAC;EACjC;EACA,aAAa,IAAI,OAAO;GACtB,IAAI,CAAC,OAAO,SAAS;GACrB,KAAK,QAAQ,KAAK;IAAE;IAAI;GAAM,CAAC;EACjC;EACA,oBAAoB,OAAO,UAAU;GACnC,IAAI,CAAC,OAAO,MAAM;GAClB,KAAK,KAAK,KAAK;IAAE;IAAO;GAAS,CAAC;EACpC;EACA,YAAY,IAAI,OAAO,OAAO,OAAO;GACnC,IAAI,CAAC,OAAO,QAAQ;GACpB,KAAK,OAAO,KAAK;IAAE;IAAI;IAAO;IAAO;GAAM,CAAC;EAC9C;EACA,eAAe,MAAM,MAAM,IAAI,QAAQ;GACrC,IAAI,CAAC,OAAO,UAAU;GACtB,KAAK,WAAW,SAAS;IAAE;IAAM;IAAM;IAAI;GAAO,IAAI;IAAE;IAAM;IAAM;GAAG;EACzE;EACA,YAAY,OAAO;GACjB,IAAI,CAAC,OAAO,KAAK;GACjB,KAAK,QAAQ;EACf;EACA,aAAa;GACX,KAAK,SAAS,SAAS;GACvB,KAAK,QAAQ,SAAS;GACtB,KAAK,OAAO,SAAS;GACrB,KAAK,QAAQ,SAAS;GACtB,KAAK,OAAO,SAAS;EACvB;EACA,YAAY;GACV,KAAK,KAAK,SAAS;EACrB;EACA,WAAW;GACT,OAAO;EACT;CACF;AACF"}
@@ -16,7 +16,8 @@ var DEFAULT_DEBUG_THEME = {
16
16
  idText: "#ffeb3b",
17
17
  fpsText: "#e0e0e0",
18
18
  fpsTextBg: "rgba(0, 0, 0, 0.6)",
19
- viewport: "#b388ff"
19
+ viewport: "#b388ff",
20
+ highlight: "#ff4081"
20
21
  };
21
22
  /**
22
23
  * Default line widths and dashes. Hairline everywhere so the overlay never
@@ -35,7 +36,8 @@ var DEFAULT_DEBUG_STROKES = {
35
36
  viewport: {
36
37
  width: 1,
37
38
  dash: [6, 4]
38
- }
39
+ },
40
+ highlight: { width: 2 }
39
41
  };
40
42
 
41
43
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"defaultTheme.js","names":[],"sources":["../../src/debug/defaultTheme.ts"],"sourcesContent":["import type { DebugStrokes, DebugTheme } from './types';\n\n/**\n * Default colors per the spec's Visual Style section. High contrast against\n * the demo's dark backdrop. Override by passing `theme: { ... }` on\n * `DebugConfig`.\n */\nexport const DEFAULT_DEBUG_THEME: DebugTheme = {\n hitboxFill: 'rgba(255, 0, 255, 0.25)',\n hitboxStroke: '#ff00ff',\n handle: '#00e5ff',\n bounds: '#ffeb3b',\n origin: '#69f0ae',\n snap: '#ffa726',\n layerText: '#e0e0e0',\n layerTextBg: 'rgba(0, 0, 0, 0.6)',\n idText: '#ffeb3b',\n fpsText: '#e0e0e0',\n fpsTextBg: 'rgba(0, 0, 0, 0.6)',\n viewport: '#b388ff',\n};\n\n/**\n * Default line widths and dashes. Hairline everywhere so the overlay never\n * hides the geometry it is describing; the dash on hitboxes is what\n * distinguishes them from the solid bounds box at the same rect.\n * Override by passing `strokes: { ... }` on `DebugConfig`.\n */\nexport const DEFAULT_DEBUG_STROKES: DebugStrokes = {\n hitbox: { width: 1, dash: [2, 2] },\n bounds: { width: 1 },\n handle: { width: 1 },\n snap: { width: 1 },\n viewport: { width: 1, dash: [6, 4] },\n};\n"],"mappings":";;;;;;AAOA,IAAa,sBAAkC;CAC7C,YAAY;CACZ,cAAc;CACd,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,WAAW;CACX,aAAa;CACb,QAAQ;CACR,SAAS;CACT,WAAW;CACX,UAAU;AACZ;;;;;;;AAQA,IAAa,wBAAsC;CACjD,QAAQ;EAAE,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CACjC,QAAQ,EAAE,OAAO,EAAE;CACnB,QAAQ,EAAE,OAAO,EAAE;CACnB,MAAM,EAAE,OAAO,EAAE;CACjB,UAAU;EAAE,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;AACrC"}
1
+ {"version":3,"file":"defaultTheme.js","names":[],"sources":["../../src/debug/defaultTheme.ts"],"sourcesContent":["import type { DebugStrokes, DebugTheme } from './types';\n\n/**\n * Default colors per the spec's Visual Style section. High contrast against\n * the demo's dark backdrop. Override by passing `theme: { ... }` on\n * `DebugConfig`.\n */\nexport const DEFAULT_DEBUG_THEME: DebugTheme = {\n hitboxFill: 'rgba(255, 0, 255, 0.25)',\n hitboxStroke: '#ff00ff',\n handle: '#00e5ff',\n bounds: '#ffeb3b',\n origin: '#69f0ae',\n snap: '#ffa726',\n layerText: '#e0e0e0',\n layerTextBg: 'rgba(0, 0, 0, 0.6)',\n idText: '#ffeb3b',\n fpsText: '#e0e0e0',\n fpsTextBg: 'rgba(0, 0, 0, 0.6)',\n viewport: '#b388ff',\n highlight: '#ff4081',\n};\n\n/**\n * Default line widths and dashes. Hairline everywhere so the overlay never\n * hides the geometry it is describing; the dash on hitboxes is what\n * distinguishes them from the solid bounds box at the same rect.\n * Override by passing `strokes: { ... }` on `DebugConfig`.\n */\nexport const DEFAULT_DEBUG_STROKES: DebugStrokes = {\n hitbox: { width: 1, dash: [2, 2] },\n bounds: { width: 1 },\n handle: { width: 1 },\n snap: { width: 1 },\n viewport: { width: 1, dash: [6, 4] },\n highlight: { width: 2 },\n};\n"],"mappings":";;;;;;AAOA,IAAa,sBAAkC;CAC7C,YAAY;CACZ,cAAc;CACd,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,WAAW;CACX,aAAa;CACb,QAAQ;CACR,SAAS;CACT,WAAW;CACX,UAAU;CACV,WAAW;AACb;;;;;;;AAQA,IAAa,wBAAsC;CACjD,QAAQ;EAAE,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CACjC,QAAQ,EAAE,OAAO,EAAE;CACnB,QAAQ,EAAE,OAAO,EAAE;CACnB,MAAM,EAAE,OAAO,EAAE;CACjB,UAAU;EAAE,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CACnC,WAAW,EAAE,OAAO,EAAE;AACxB"}
@@ -0,0 +1,17 @@
1
+ //#region src/debug/features.ts
2
+ /** Every debug-overlay feature a `DebugConfig` flag turns on, in the order a list of them shows. */
3
+ var DEBUG_FEATURES = [
4
+ "hitboxes",
5
+ "handles",
6
+ "bounds",
7
+ "origins",
8
+ "snap",
9
+ "layers",
10
+ "ids",
11
+ "fps",
12
+ "viewport"
13
+ ];
14
+
15
+ //#endregion
16
+ export { DEBUG_FEATURES };
17
+ //# sourceMappingURL=features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.js","names":[],"sources":["../../src/debug/features.ts"],"sourcesContent":["/** Every debug-overlay feature a `DebugConfig` flag turns on, in the order a list of them shows. */\nexport const DEBUG_FEATURES = [\n 'hitboxes', 'handles', 'bounds', 'origins', 'snap', 'layers', 'ids', 'fps', 'viewport',\n] as const;\n"],"mappings":";;AACA,IAAa,iBAAiB;CAC5B;CAAY;CAAW;CAAU;CAAW;CAAQ;CAAU;CAAO;CAAO;AAC9E"}
@@ -0,0 +1,117 @@
1
+ import { textCommandFromRuns } from "@weasel-js/renderer";
2
+
3
+ //#region src/debug/overlayPanels.ts
4
+ function rectPath(x, y, w, h) {
5
+ return {
6
+ kind: "rect",
7
+ x,
8
+ y,
9
+ width: w,
10
+ height: h
11
+ };
12
+ }
13
+ /** A `DebugStroke` + color as the renderer's stroke shape. `dash` is dropped
14
+ * when empty rather than passed through, so a solid line stays solid. */
15
+ function emitLayersPanel(out, s, dims, theme) {
16
+ if (s.layers.length === 0) return;
17
+ const lineH = 14;
18
+ const padX = 6;
19
+ const lines = s.layers.map((l) => `[${l.index}] ${l.id} (${l.space})`);
20
+ const charW = 6.6;
21
+ let maxW = 0;
22
+ for (const line of lines) maxW = Math.max(maxW, line.length * charW);
23
+ const boxW = maxW + 12;
24
+ const boxH = lines.length * lineH + 8;
25
+ const x = dims.width - boxW - 8;
26
+ out.push({
27
+ kind: "path",
28
+ path: rectPath(x, 8, boxW, boxH),
29
+ fill: { paint: {
30
+ kind: "solid",
31
+ color: theme.layerTextBg
32
+ } }
33
+ });
34
+ for (let i = 0; i < lines.length; i++) out.push(textCommandFromRuns(x + padX, 12 + i * lineH, [{
35
+ text: lines[i],
36
+ fill: { paint: {
37
+ kind: "solid",
38
+ color: theme.layerText
39
+ } }
40
+ }], {
41
+ fontFamily: "ui-monospace, Menlo, monospace",
42
+ fontSize: 11
43
+ }));
44
+ }
45
+ var MONO = {
46
+ fontFamily: "ui-monospace, Menlo, monospace",
47
+ fontSize: 11
48
+ };
49
+ var MONO_CHAR_W = 6.6;
50
+ var LINE_H = 14;
51
+ /** A dark box holding monospace lines, at `(x, y)`; `y < 0` anchors its bottom
52
+ * that far above `bottom`. Width is estimated from the character count — there
53
+ * is no text measurer here, and a debug panel does not need one. */
54
+ function emitTextPanel(out, lines, x, y, color, bg) {
55
+ const padX = 6;
56
+ const padY = 4;
57
+ let maxLen = 0;
58
+ for (const l of lines) maxLen = Math.max(maxLen, l.length);
59
+ out.push({
60
+ kind: "path",
61
+ path: rectPath(x, y, maxLen * MONO_CHAR_W + 12, lines.length * LINE_H + 8),
62
+ fill: { paint: {
63
+ kind: "solid",
64
+ color: bg
65
+ } }
66
+ });
67
+ for (let i = 0; i < lines.length; i++) out.push(textCommandFromRuns(x + padX, y + padY + i * LINE_H, [{
68
+ text: lines[i],
69
+ fill: { paint: {
70
+ kind: "solid",
71
+ color
72
+ } }
73
+ }], MONO));
74
+ }
75
+ /** Fixed-width number, right-aligned, `decimals` places: `%{width}.{decimals}f`. */
76
+ function fixed(v, width, decimals) {
77
+ return v.toFixed(decimals).padStart(width);
78
+ }
79
+ function emitFramePanel(out, history, frame, theme) {
80
+ const span = history.length >= 2 ? history[history.length - 1] - history[0] : 0;
81
+ const interval = span > 0 ? span / (history.length - 1) : null;
82
+ const rows = [[
83
+ "fps",
84
+ null,
85
+ interval ? 1e3 / interval : null,
86
+ " "
87
+ ], [
88
+ "frame",
89
+ null,
90
+ interval,
91
+ "ms"
92
+ ]];
93
+ if (frame) {
94
+ rows.push([
95
+ "paint",
96
+ frame.drawCalls,
97
+ frame.paintMs,
98
+ "ms"
99
+ ]);
100
+ for (const l of frame.layers) rows.push([
101
+ l.id,
102
+ l.drawCalls,
103
+ l.ms,
104
+ "ms"
105
+ ]);
106
+ }
107
+ let labelW = 0;
108
+ for (const [label] of rows) labelW = Math.max(labelW, label.length);
109
+ const line = (label, count, value, unit) => `${label.padEnd(labelW)} ${count.padStart(5)} ${value.padStart(7)} ${unit}`;
110
+ const lines = rows.map(([label, count, value, unit]) => line(label, count === null ? "" : String(count), value === null ? "—" : fixed(value, 7, 2), unit));
111
+ if (frame) lines.splice(2, 0, line("", "draws", "cpu", " "));
112
+ emitTextPanel(out, lines, 8, 8, theme.fpsText, theme.fpsTextBg);
113
+ }
114
+
115
+ //#endregion
116
+ export { LINE_H, emitFramePanel, emitLayersPanel, emitTextPanel, rectPath };
117
+ //# sourceMappingURL=overlayPanels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayPanels.js","names":[],"sources":["../../src/debug/overlayPanels.ts"],"sourcesContent":["/** The debug overlay's text panels — the layer list and the frame stats — and the rect path every box is drawn with. */\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { textCommandFromRuns } from '@weasel-js/renderer';\nimport type { Dims } from 'core/layers/render';\nimport type { DebugSnapshot, DebugTheme, FrameStats } from './types';\n\nexport function rectPath(x: number, y: number, w: number, h: number): { kind: 'rect'; x: number; y: number; width: number; height: number } {\n return { kind: 'rect', x, y, width: w, height: h };\n}\n\n/** A `DebugStroke` + color as the renderer's stroke shape. `dash` is dropped\n * when empty rather than passed through, so a solid line stays solid. */\nexport function emitLayersPanel(\n out: DrawCommand[],\n s: DebugSnapshot,\n dims: Dims,\n theme: DebugTheme,\n): void {\n if (s.layers.length === 0) return;\n const lineH = 14;\n const padX = 6;\n const padY = 4;\n const lines = s.layers.map((l) => `[${l.index}] ${l.id} (${l.space})`);\n // Approximate width: 11px font, monospaced — assume each glyph ≈6.6px.\n // 2D path uses ctx.measureText; we don't have a ctx here, so fall back\n // to a character-count estimate. Pixel-level accuracy is not load-bearing\n // for a debug panel; right-edge anchoring stays correct.\n const charW = 6.6;\n let maxW = 0;\n for (const line of lines) maxW = Math.max(maxW, line.length * charW);\n const boxW = maxW + padX * 2;\n const boxH = lines.length * lineH + padY * 2;\n const x = dims.width - boxW - 8;\n const y = 8;\n out.push({\n kind: 'path',\n path: rectPath(x, y, boxW, boxH),\n fill: { paint: { kind: 'solid', color: theme.layerTextBg } },\n });\n for (let i = 0; i < lines.length; i++) {\n out.push(textCommandFromRuns(\n x + padX,\n y + padY + i * lineH,\n [{ text: lines[i], fill: { paint: { kind: 'solid', color: theme.layerText } } }],\n { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 11 },\n ));\n }\n}\n\nconst MONO = { fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 11 };\nconst MONO_CHAR_W = 6.6;\nexport const LINE_H = 14;\n\n/** A dark box holding monospace lines, at `(x, y)`; `y < 0` anchors its bottom\n * that far above `bottom`. Width is estimated from the character count — there\n * is no text measurer here, and a debug panel does not need one. */\nexport function emitTextPanel(\n out: DrawCommand[],\n lines: readonly string[],\n x: number,\n y: number,\n color: string,\n bg: string,\n): void {\n const padX = 6;\n const padY = 4;\n let maxLen = 0;\n for (const l of lines) maxLen = Math.max(maxLen, l.length);\n out.push({\n kind: 'path',\n path: rectPath(x, y, maxLen * MONO_CHAR_W + padX * 2, lines.length * LINE_H + padY * 2),\n fill: { paint: { kind: 'solid', color: bg } },\n });\n for (let i = 0; i < lines.length; i++) {\n out.push(textCommandFromRuns(\n x + padX,\n y + padY + i * LINE_H,\n [{ text: lines[i], fill: { paint: { kind: 'solid', color } } }],\n MONO,\n ));\n }\n}\n\n/** Fixed-width number, right-aligned, `decimals` places: `%{width}.{decimals}f`. */\nfunction fixed(v: number, width: number, decimals: number): string {\n return v.toFixed(decimals).padStart(width);\n}\n\nexport function emitFramePanel(\n out: DrawCommand[],\n history: readonly number[],\n frame: FrameStats | null,\n theme: DebugTheme,\n): void {\n const span = history.length >= 2 ? history[history.length - 1] - history[0] : 0;\n const interval = span > 0 ? span / (history.length - 1) : null;\n // Columns: label, draw-call count, value, unit — every row the same width,\n // so decimals line up down the panel.\n const rows: [label: string, count: number | null, value: number | null, unit: string][] = [\n ['fps', null, interval ? 1000 / interval : null, ' '],\n ['frame', null, interval, 'ms'],\n ];\n if (frame) {\n rows.push(['paint', frame.drawCalls, frame.paintMs, 'ms']);\n for (const l of frame.layers) rows.push([l.id, l.drawCalls, l.ms, 'ms']);\n }\n let labelW = 0;\n for (const [label] of rows) labelW = Math.max(labelW, label.length);\n const line = (label: string, count: string, value: string, unit: string) =>\n `${label.padEnd(labelW)} ${count.padStart(5)} ${value.padStart(7)} ${unit}`;\n const lines = rows.map(([label, count, value, unit]) =>\n line(label, count === null ? '' : String(count), value === null ? '—' : fixed(value, 7, 2), unit));\n if (frame) lines.splice(2, 0, line('', 'draws', 'cpu', ' '));\n emitTextPanel(out, lines, 8, 8, theme.fpsText, theme.fpsTextBg);\n}\n"],"mappings":"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"}
@@ -1,15 +1,6 @@
1
+ import { DEBUG_FEATURES } from "./features.js";
2
+
1
3
  //#region src/debug/parseDebugFlags.ts
2
- var ALL_FEATURES = [
3
- "hitboxes",
4
- "handles",
5
- "bounds",
6
- "origins",
7
- "snap",
8
- "layers",
9
- "ids",
10
- "fps",
11
- "viewport"
12
- ];
13
4
  /**
14
5
  * Parse a URL query string (with or without leading `?`) for a `debug` param.
15
6
  *
@@ -27,11 +18,11 @@ function parseDebugFlags(search) {
27
18
  if (tokens.length === 0) return null;
28
19
  if (tokens.includes("all")) {
29
20
  const out = {};
30
- for (const f of ALL_FEATURES) out[f] = true;
21
+ for (const f of DEBUG_FEATURES) out[f] = true;
31
22
  return out;
32
23
  }
33
24
  const out = {};
34
- for (const t of tokens) if (ALL_FEATURES.includes(t)) out[t] = true;
25
+ for (const t of tokens) if (DEBUG_FEATURES.includes(t)) out[t] = true;
35
26
  return Object.keys(out).length > 0 ? out : null;
36
27
  }
37
28
 
@@ -1 +1 @@
1
- {"version":3,"file":"parseDebugFlags.js","names":[],"sources":["../../src/debug/parseDebugFlags.ts"],"sourcesContent":["import type { DebugConfig, DebugFeature } from './types';\n\nconst ALL_FEATURES: DebugFeature[] = [\n 'hitboxes', 'handles', 'bounds', 'origins', 'snap', 'layers', 'ids', 'fps', 'viewport',\n];\n\n/**\n * Parse a URL query string (with or without leading `?`) for a `debug` param.\n *\n * - `?debug=hitboxes` → `{ hitboxes: true }`\n * - `?debug=bounds,origins` → `{ bounds: true, origins: true }`\n * - `?debug=all` → every feature enabled\n * - missing/empty/unknown-only → `null`\n */\nexport function parseDebugFlags(search: string): DebugConfig | null {\n const trimmed = search.startsWith('?') ? search.slice(1) : search;\n if (trimmed.length === 0) return null;\n const params = new URLSearchParams(trimmed);\n const raw = params.get('debug');\n if (!raw) return null;\n const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);\n if (tokens.length === 0) return null;\n if (tokens.includes('all')) {\n const out: DebugConfig = {};\n for (const f of ALL_FEATURES) out[f] = true;\n return out;\n }\n const out: DebugConfig = {};\n for (const t of tokens) {\n if ((ALL_FEATURES as string[]).includes(t)) out[t as DebugFeature] = true;\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n"],"mappings":";AAEA,IAAM,eAA+B;CACnC;CAAY;CAAW;CAAU;CAAW;CAAQ;CAAU;CAAO;CAAO;AAC9E;;;;;;;;;AAUA,SAAgB,gBAAgB,QAAoC;CAClE,MAAM,UAAU,OAAO,WAAW,GAAG,IAAI,OAAO,MAAM,CAAC,IAAI;CAC3D,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,MAAM,MAAM,IADO,gBAAgB,OACvB,CAAA,CAAO,IAAI,OAAO;CAC9B,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;CACjE,IAAI,OAAO,WAAW,GAAG,OAAO;CAChC,IAAI,OAAO,SAAS,KAAK,GAAG;EAC1B,MAAM,MAAmB,CAAC;EAC1B,KAAK,MAAM,KAAK,cAAc,IAAI,KAAK;EACvC,OAAO;CACT;CACA,MAAM,MAAmB,CAAC;CAC1B,KAAK,MAAM,KAAK,QACd,IAAK,aAA0B,SAAS,CAAC,GAAG,IAAI,KAAqB;CAEvE,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM;AAC7C"}
1
+ {"version":3,"file":"parseDebugFlags.js","names":[],"sources":["../../src/debug/parseDebugFlags.ts"],"sourcesContent":["import type { DebugConfig, DebugFeature } from './types';\nimport { DEBUG_FEATURES } from './features';\n\n/**\n * Parse a URL query string (with or without leading `?`) for a `debug` param.\n *\n * - `?debug=hitboxes` → `{ hitboxes: true }`\n * - `?debug=bounds,origins` → `{ bounds: true, origins: true }`\n * - `?debug=all` → every feature enabled\n * - missing/empty/unknown-only → `null`\n */\nexport function parseDebugFlags(search: string): DebugConfig | null {\n const trimmed = search.startsWith('?') ? search.slice(1) : search;\n if (trimmed.length === 0) return null;\n const params = new URLSearchParams(trimmed);\n const raw = params.get('debug');\n if (!raw) return null;\n const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);\n if (tokens.length === 0) return null;\n if (tokens.includes('all')) {\n const out: DebugConfig = {};\n for (const f of DEBUG_FEATURES) out[f] = true;\n return out;\n }\n const out: DebugConfig = {};\n for (const t of tokens) {\n if ((DEBUG_FEATURES as readonly string[]).includes(t)) out[t as DebugFeature] = true;\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,gBAAgB,QAAoC;CAClE,MAAM,UAAU,OAAO,WAAW,GAAG,IAAI,OAAO,MAAM,CAAC,IAAI;CAC3D,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,MAAM,MAAM,IADO,gBAAgB,OACvB,CAAA,CAAO,IAAI,OAAO;CAC9B,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;CACjE,IAAI,OAAO,WAAW,GAAG,OAAO;CAChC,IAAI,OAAO,SAAS,KAAK,GAAG;EAC1B,MAAM,MAAmB,CAAC;EAC1B,KAAK,MAAM,KAAK,gBAAgB,IAAI,KAAK;EACzC,OAAO;CACT;CACA,MAAM,MAAmB,CAAC;CAC1B,KAAK,MAAM,KAAK,QACd,IAAK,eAAqC,SAAS,CAAC,GAAG,IAAI,KAAqB;CAElF,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM;AAC7C"}