@weasel-js/core 1.9.2 → 1.9.4

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 (542) hide show
  1. package/CHANGELOG.md +393 -0
  2. package/README.md +2 -2
  3. package/dist/WeaselProvider.js +5 -5
  4. package/dist/affordances/composeAffordanceLayer.js +10 -20
  5. package/dist/affordances/composeAffordanceLayer.js.map +1 -1
  6. package/dist/affordances/cornerResize.js +25 -17
  7. package/dist/affordances/cornerResize.js.map +1 -1
  8. package/dist/affordances/hitAffordanceRegions.js +27 -20
  9. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  10. package/dist/affordances/rotationHandle.js +18 -18
  11. package/dist/affordances/rotationHandle.js.map +1 -1
  12. package/dist/animation/behaviors/momentum.js +93 -55
  13. package/dist/animation/behaviors/momentum.js.map +1 -1
  14. package/dist/animation/loop.js +10 -12
  15. package/dist/animation/loop.js.map +1 -1
  16. package/dist/animation/reflow.js +8 -8
  17. package/dist/animation/reflow.js.map +1 -1
  18. package/dist/animation/stagger.js +64 -56
  19. package/dist/animation/stagger.js.map +1 -1
  20. package/dist/animation/timeline/booking.js +62 -54
  21. package/dist/animation/timeline/booking.js.map +1 -1
  22. package/dist/animation/timeline/createTimeline.js +85 -76
  23. package/dist/animation/timeline/createTimeline.js.map +1 -1
  24. package/dist/animation/timeline/sampleTrack.js +13 -5
  25. package/dist/animation/timeline/sampleTrack.js.map +1 -1
  26. package/dist/animation/useAnimator.js +45 -35
  27. package/dist/animation/useAnimator.js.map +1 -1
  28. package/dist/animation/wrappers/animateLifecycle.js +31 -24
  29. package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
  30. package/dist/animation/wrappers/animateOnSetPose.js +28 -26
  31. package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
  32. package/dist/animation/wrappers/instantPose.js +13 -0
  33. package/dist/animation/wrappers/instantPose.js.map +1 -0
  34. package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
  35. package/dist/barrel/chrome.js +16 -0
  36. package/dist/barrel/input.js +19 -0
  37. package/dist/barrel/interactions.js +42 -0
  38. package/dist/barrel/paint.js +18 -0
  39. package/dist/barrel/paths.js +19 -0
  40. package/dist/barrel/renderer.js +20 -0
  41. package/dist/barrel/routing.js +51 -0
  42. package/dist/barrel/text.js +18 -0
  43. package/dist/barrel/viewport.js +52 -0
  44. package/dist/builtins-DPcdghtB.d.ts +1223 -0
  45. package/dist/canvas/Canvas.js +363 -448
  46. package/dist/canvas/Canvas.js.map +1 -1
  47. package/dist/canvas/CanvasView.js +164 -165
  48. package/dist/canvas/CanvasView.js.map +1 -1
  49. package/dist/canvas/CursorCoordsHud.js +37 -32
  50. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  51. package/dist/canvas/DrawCanvas.js +49 -43
  52. package/dist/canvas/DrawCanvas.js.map +1 -1
  53. package/dist/canvas/MinimapCanvas.js +26 -25
  54. package/dist/canvas/MinimapCanvas.js.map +1 -1
  55. package/dist/canvas/NodeShape.js +59 -377
  56. package/dist/canvas/NodeShape.js.map +1 -1
  57. package/dist/canvas/PickHud.js +50 -47
  58. package/dist/canvas/PickHud.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
  60. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
  61. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
  62. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
  63. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
  64. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
  65. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
  66. package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
  67. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
  68. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +146 -92
  69. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  70. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
  71. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  72. package/dist/canvas/SceneCanvas/features.js +1 -0
  73. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  74. package/dist/canvas/SceneCanvas/isDev.js +12 -0
  75. package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
  76. package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
  77. package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
  78. package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
  79. package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
  80. package/dist/canvas/SceneCanvas/props.js +38 -0
  81. package/dist/canvas/SceneCanvas/props.js.map +1 -0
  82. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
  83. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  84. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
  85. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
  86. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  87. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
  88. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  89. package/dist/canvas/SceneCanvas.js +534 -788
  90. package/dist/canvas/SceneCanvas.js.map +1 -1
  91. package/dist/canvas/SceneViewCanvas.js +45 -43
  92. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  93. package/dist/canvas/buildSceneLayer.js +91 -0
  94. package/dist/canvas/buildSceneLayer.js.map +1 -0
  95. package/dist/canvas/buildSceneTree.js +16 -19
  96. package/dist/canvas/buildSceneTree.js.map +1 -1
  97. package/dist/canvas/canvasRenderer.js +68 -52
  98. package/dist/canvas/canvasRenderer.js.map +1 -1
  99. package/dist/canvas/deps/changes.js +13 -0
  100. package/dist/canvas/deps/changes.js.map +1 -0
  101. package/dist/canvas/deps/editAnchors.js +28 -28
  102. package/dist/canvas/deps/insert.js +119 -91
  103. package/dist/canvas/deps/insert.js.map +1 -1
  104. package/dist/canvas/deps/slice.js +46 -45
  105. package/dist/canvas/deps/slice.js.map +1 -1
  106. package/dist/canvas/deps/textEdit.js +10 -9
  107. package/dist/canvas/deps/textEdit.js.map +1 -1
  108. package/dist/canvas/derivedPath.js +46 -23
  109. package/dist/canvas/derivedPath.js.map +1 -1
  110. package/dist/canvas/getNodeAtPoint.js +7 -7
  111. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  112. package/dist/canvas/hud.module.js +1 -1
  113. package/dist/canvas/hud.module.js.map +1 -1
  114. package/dist/canvas/kitRequirements.js +2 -2
  115. package/dist/canvas/kitRequirements.js.map +1 -1
  116. package/dist/canvas/layerOrder.js +13 -6
  117. package/dist/canvas/layerOrder.js.map +1 -1
  118. package/dist/canvas/minimapMath.js +12 -10
  119. package/dist/canvas/minimapMath.js.map +1 -1
  120. package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
  121. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
  122. package/dist/canvas/nodeShapes/imagePainter.js +87 -0
  123. package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
  124. package/dist/canvas/nodeShapes/memo.js +9 -0
  125. package/dist/canvas/nodeShapes/memo.js.map +1 -0
  126. package/dist/canvas/nodeShapes/nodePaint.js +57 -0
  127. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
  128. package/dist/canvas/nodeShapes/pathPainter.js +38 -0
  129. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
  130. package/dist/canvas/nodeShapes/poseBox.js +20 -0
  131. package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
  132. package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
  133. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
  134. package/dist/canvas/nodeShapes/shapePainter.js +72 -0
  135. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
  136. package/dist/canvas/nodeShapes/textPainter.js +55 -0
  137. package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
  138. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  139. package/dist/canvas/paintCull.js +10 -11
  140. package/dist/canvas/paintCull.js.map +1 -1
  141. package/dist/canvas/pickWalk.js +52 -63
  142. package/dist/canvas/pickWalk.js.map +1 -1
  143. package/dist/canvas/poseRotation.js +11 -23
  144. package/dist/canvas/poseRotation.js.map +1 -1
  145. package/dist/canvas/renderDebugSnapshot.js +5 -5
  146. package/dist/canvas/renderSceneToPixels.js +3 -1
  147. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  148. package/dist/canvas/sceneAdapter.js +66 -148
  149. package/dist/canvas/sceneAdapter.js.map +1 -1
  150. package/dist/canvas/sceneClipboard.js +112 -0
  151. package/dist/canvas/sceneClipboard.js.map +1 -0
  152. package/dist/canvas/sceneSlotWiring.js +4 -4
  153. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  154. package/dist/canvas/sceneViewRender.js +39 -40
  155. package/dist/canvas/sceneViewRender.js.map +1 -1
  156. package/dist/canvas/useCanvasRenderer.js +14 -6
  157. package/dist/canvas/useCanvasRenderer.js.map +1 -1
  158. package/dist/canvas/useCanvasView.js +58 -0
  159. package/dist/canvas/useCanvasView.js.map +1 -0
  160. package/dist/canvas/useFrameLoop.js +35 -26
  161. package/dist/canvas/useFrameLoop.js.map +1 -1
  162. package/dist/canvas/useSceneLayerList.js +21 -0
  163. package/dist/canvas/useSceneLayerList.js.map +1 -0
  164. package/dist/clipboard.d.ts +10 -6
  165. package/dist/clone.d.ts +4 -3
  166. package/dist/core/adapters/arrayAdapter.js +48 -39
  167. package/dist/core/adapters/arrayAdapter.js.map +1 -1
  168. package/dist/core/device/index.js +5 -4
  169. package/dist/core/device/useDeviceProfile.js +38 -44
  170. package/dist/core/device/useDeviceProfile.js.map +1 -1
  171. package/dist/core/device/watchDevicePixelRatio.js +17 -0
  172. package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
  173. package/dist/core/fillInPoseFrame.js.map +1 -1
  174. package/dist/core/geometry/poseDescriptor.js +21 -19
  175. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  176. package/dist/core/geometry/poseFrame.js +44 -0
  177. package/dist/core/geometry/poseFrame.js.map +1 -0
  178. package/dist/core/geometry/poseRotation.js.map +1 -1
  179. package/dist/core/geometry/regionHit.js +9 -10
  180. package/dist/core/geometry/regionHit.js.map +1 -1
  181. package/dist/core/geometry/unionBounds.js +31 -29
  182. package/dist/core/geometry/unionBounds.js.map +1 -1
  183. package/dist/core/isPlainObject.js +2 -11
  184. package/dist/core/ops/arrange.js +1 -0
  185. package/dist/core/ops/arrange.js.map +1 -1
  186. package/dist/core/ops/create.js +10 -5
  187. package/dist/core/ops/create.js.map +1 -1
  188. package/dist/core/ops/delete.js +12 -24
  189. package/dist/core/ops/delete.js.map +1 -1
  190. package/dist/core/ops/reorder/index.js +43 -40
  191. package/dist/core/ops/reorder/index.js.map +1 -1
  192. package/dist/core/ops/reorder/setChildOrder.js +34 -0
  193. package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
  194. package/dist/core/paintKinds.js +5 -5
  195. package/dist/core/paintKinds.js.map +1 -1
  196. package/dist/core/scene/NodeProperties.js.map +1 -1
  197. package/dist/core/scene/appliedLog.js +37 -0
  198. package/dist/core/scene/appliedLog.js.map +1 -0
  199. package/dist/core/scene/arrivalWindow.js +101 -0
  200. package/dist/core/scene/arrivalWindow.js.map +1 -0
  201. package/dist/core/scene/derivedPath.js.map +1 -1
  202. package/dist/core/scene/historyFacade.js +84 -0
  203. package/dist/core/scene/historyFacade.js.map +1 -0
  204. package/dist/core/scene/kitOps.js +173 -0
  205. package/dist/core/scene/kitOps.js.map +1 -0
  206. package/dist/core/scene/layerStack.js +38 -0
  207. package/dist/core/scene/layerStack.js.map +1 -0
  208. package/dist/core/scene/lockGuard.js +37 -0
  209. package/dist/core/scene/lockGuard.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +5 -6
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +45 -19
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +126 -0
  215. package/dist/core/scene/nodeStore.js.map +1 -0
  216. package/dist/core/scene/pickability.js +6 -0
  217. package/dist/core/scene/pickability.js.map +1 -0
  218. package/dist/core/scene/poseFeed.js +38 -38
  219. package/dist/core/scene/poseFeed.js.map +1 -1
  220. package/dist/core/scene/poseOverrides.js +34 -38
  221. package/dist/core/scene/poseOverrides.js.map +1 -1
  222. package/dist/core/scene/renderOrder.js +84 -0
  223. package/dist/core/scene/renderOrder.js.map +1 -0
  224. package/dist/core/scene/scene.js +289 -866
  225. package/dist/core/scene/scene.js.map +1 -1
  226. package/dist/core/scene/serialization.js +68 -0
  227. package/dist/core/scene/serialization.js.map +1 -0
  228. package/dist/core/scene/treeOrder.js +13 -0
  229. package/dist/core/scene/treeOrder.js.map +1 -0
  230. package/dist/core/scene/types.js.map +1 -1
  231. package/dist/core/viewport/meanScale.js +1 -1
  232. package/dist/core/viewport/meanScale.js.map +1 -1
  233. package/dist/core/viewport/view.js +12 -12
  234. package/dist/core/viewport/view.js.map +1 -1
  235. package/dist/core/viewport/zoomBounds.js +10 -8
  236. package/dist/core/viewport/zoomBounds.js.map +1 -1
  237. package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
  238. package/dist/debug/createDebugOverlayLayer.js +14 -14
  239. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  240. package/dist/debug/flag.js +2 -41
  241. package/dist/features/chrome-caps/useHoverTracking.js +22 -17
  242. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  243. package/dist/features/focus/gateLayer.js +3 -4
  244. package/dist/features/focus/gateLayer.js.map +1 -1
  245. package/dist/features/images/imageCache.js +19 -9
  246. package/dist/features/images/imageCache.js.map +1 -1
  247. package/dist/features/ingestion/contentHandlers.js +14 -11
  248. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  249. package/dist/features/ingestion/index.js +1 -1
  250. package/dist/features/ingestion/registerKitHandlers.js +7 -7
  251. package/dist/features/ingestion/weaselJsonHandler.js +17 -14
  252. package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
  253. package/dist/features/meshPaint/lazyKind.js +201 -35
  254. package/dist/features/meshPaint/lazyKind.js.map +1 -1
  255. package/dist/features/meshPaint/meshPaint.js +82 -79
  256. package/dist/features/meshPaint/meshPaint.js.map +1 -1
  257. package/dist/features/minimap/createMinimapContribution.js +42 -42
  258. package/dist/features/minimap/layers.js +44 -62
  259. package/dist/features/minimap/layers.js.map +1 -1
  260. package/dist/features/parallax/createParallaxPlane.js +10 -14
  261. package/dist/features/parallax/createParallaxPlane.js.map +1 -1
  262. package/dist/features/paths/anchorEdits.js +19 -8
  263. package/dist/features/paths/anchorEdits.js.map +1 -1
  264. package/dist/features/paths/anchors.js +31 -26
  265. package/dist/features/paths/anchors.js.map +1 -1
  266. package/dist/features/paths/booleans.js +2 -2
  267. package/dist/features/paths/index.js +13 -11
  268. package/dist/features/paths/markerCommands.js +37 -36
  269. package/dist/features/paths/markerCommands.js.map +1 -1
  270. package/dist/features/paths/pathInWorld.js +19 -19
  271. package/dist/features/paths/pathInWorld.js.map +1 -1
  272. package/dist/features/paths/pathLayer.js.map +1 -1
  273. package/dist/features/paths/strokeToPath.js +40 -0
  274. package/dist/features/paths/strokeToPath.js.map +1 -0
  275. package/dist/features/paths/tessellate/stroke.js.map +1 -1
  276. package/dist/features/patterns/commandPaint.js +18 -0
  277. package/dist/features/patterns/commandPaint.js.map +1 -0
  278. package/dist/features/patterns/index.js +5 -17
  279. package/dist/features/patterns/index.js.map +1 -1
  280. package/dist/features/patterns/rasterizeTile.js +17 -0
  281. package/dist/features/patterns/rasterizeTile.js.map +1 -0
  282. package/dist/features/patterns/resolveSpec.js +52 -44
  283. package/dist/features/patterns/resolveSpec.js.map +1 -1
  284. package/dist/features/poseRun/usePoseRun.js +8 -3
  285. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  286. package/dist/features/selection/SelectionContext.js +5 -0
  287. package/dist/features/selection/SelectionContext.js.map +1 -1
  288. package/dist/features/selection/overlay.js +18 -18
  289. package/dist/features/selection/overlay.js.map +1 -1
  290. package/dist/features/text/domOffsets.js +82 -0
  291. package/dist/features/text/domOffsets.js.map +1 -0
  292. package/dist/features/text/domRuns.js +103 -131
  293. package/dist/features/text/domRuns.js.map +1 -1
  294. package/dist/features/text/hitTest.js +5 -2
  295. package/dist/features/text/hitTest.js.map +1 -1
  296. package/dist/features/text/isTextData.js +8 -0
  297. package/dist/features/text/isTextData.js.map +1 -0
  298. package/dist/features/text/overlayDirection.js +15 -0
  299. package/dist/features/text/overlayDirection.js.map +1 -0
  300. package/dist/features/text/runs/rangeStyle.js +32 -34
  301. package/dist/features/text/runs/rangeStyle.js.map +1 -1
  302. package/dist/features/text/textCommand.js +34 -33
  303. package/dist/features/text/textCommand.js.map +1 -1
  304. package/dist/features/text/textToPath.js +40 -40
  305. package/dist/features/text/textToPath.js.map +1 -1
  306. package/dist/features/text/useSceneTextEdit.js +76 -82
  307. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  308. package/dist/features/text/useTextEdit.js +184 -160
  309. package/dist/features/text/useTextEdit.js.map +1 -1
  310. package/dist/features/tiling/createTiledLayer.js +24 -22
  311. package/dist/features/tiling/createTiledLayer.js.map +1 -1
  312. package/dist/index.d.ts +11010 -7700
  313. package/dist/index.js +316 -309
  314. package/dist/input/keyState.js +42 -43
  315. package/dist/input/keyState.js.map +1 -1
  316. package/dist/input/useKeyModifiers.js +11 -0
  317. package/dist/input/useKeyModifiers.js.map +1 -0
  318. package/dist/interactions/actions/align/align.js +8 -8
  319. package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
  320. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  321. package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
  322. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  323. package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
  324. package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
  325. package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
  326. package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
  327. package/dist/interactions/actions/defaults/align.js +9 -10
  328. package/dist/interactions/actions/defaults/align.js.map +1 -1
  329. package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
  330. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  331. package/dist/interactions/actions/defaults/areaSelect.js +35 -38
  332. package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
  333. package/dist/interactions/actions/defaults/clipboard.js +26 -10
  334. package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
  335. package/dist/interactions/actions/defaults/clone.js +44 -60
  336. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  337. package/dist/interactions/actions/defaults/copySubtree.js +40 -0
  338. package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
  339. package/dist/interactions/actions/defaults/distribute.js +14 -13
  340. package/dist/interactions/actions/defaults/distribute.js.map +1 -1
  341. package/dist/interactions/actions/defaults/duplicate.js +17 -57
  342. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  343. package/dist/interactions/actions/defaults/editAnchors.js +18 -18
  344. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  345. package/dist/interactions/actions/defaults/flip.js +52 -46
  346. package/dist/interactions/actions/defaults/flip.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +43 -48
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +35 -33
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insert.js +50 -51
  352. package/dist/interactions/actions/defaults/insert.js.map +1 -1
  353. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
  354. package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
  355. package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
  356. package/dist/interactions/actions/defaults/move.js +267 -249
  357. package/dist/interactions/actions/defaults/move.js.map +1 -1
  358. package/dist/interactions/actions/defaults/nudge.js +40 -68
  359. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  360. package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
  361. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
  362. package/dist/interactions/actions/defaults/reorder.js +2 -0
  363. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  364. package/dist/interactions/actions/defaults/resize.js +104 -98
  365. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  366. package/dist/interactions/actions/defaults/rotate.js +111 -110
  367. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  368. package/dist/interactions/actions/defaults/setFill.js +7 -7
  369. package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
  370. package/dist/interactions/actions/defaults/setStroke.js +12 -12
  371. package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
  372. package/dist/interactions/actions/defaults/sliceOps.js +19 -19
  373. package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
  374. package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
  375. package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
  376. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  377. package/dist/interactions/actions/flip/helpers.js +30 -17
  378. package/dist/interactions/actions/flip/helpers.js.map +1 -1
  379. package/dist/interactions/actions/hitSelection.js +8 -0
  380. package/dist/interactions/actions/hitSelection.js.map +1 -0
  381. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
  382. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
  383. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
  384. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
  385. package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
  386. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  387. package/dist/interactions/actions/move/gestureAdapter.js +32 -15
  388. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  389. package/dist/interactions/actions/move/landedDescendants.js +33 -0
  390. package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
  391. package/dist/interactions/actions/outlines/createOutlines.js +18 -18
  392. package/dist/interactions/actions/poseDescriptorDep.js +16 -4
  393. package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
  394. package/dist/interactions/actions/poseFrame.js +11 -6
  395. package/dist/interactions/actions/poseFrame.js.map +1 -1
  396. package/dist/interactions/actions/previewOverrides.js +5 -5
  397. package/dist/interactions/actions/previewOverrides.js.map +1 -1
  398. package/dist/interactions/actions/selectionSubtree.js +69 -0
  399. package/dist/interactions/actions/selectionSubtree.js.map +1 -0
  400. package/dist/interactions/actions/useStandardActions.js +66 -62
  401. package/dist/interactions/actions/useStandardActions.js.map +1 -1
  402. package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
  403. package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
  404. package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
  405. package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
  406. package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
  407. package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
  408. package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
  409. package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
  410. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
  411. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  412. package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
  413. package/dist/math.d.ts +2 -3
  414. package/dist/move.d.ts +12 -11
  415. package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
  416. package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
  417. package/dist/renderer/DrawCommand.js.map +1 -1
  418. package/dist/renderer/WeaselRenderer.js +75 -73
  419. package/dist/renderer/WeaselRenderer.js.map +1 -1
  420. package/dist/renderer/cache/GLImageCache.js +15 -5
  421. package/dist/renderer/cache/GLImageCache.js.map +1 -1
  422. package/dist/renderer/cache/GLMeshCache.js +23 -23
  423. package/dist/renderer/cache/GLMeshCache.js.map +1 -1
  424. package/dist/renderer/cache/GLTextureCache.js +18 -1
  425. package/dist/renderer/cache/GLTextureCache.js.map +1 -1
  426. package/dist/renderer/cache/cache.js.map +1 -1
  427. package/dist/renderer/cache/markerCommandCache.js.map +1 -1
  428. package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
  429. package/dist/renderer/cullDrawCommands.js +11 -11
  430. package/dist/renderer/draw/batch.js +133 -0
  431. package/dist/renderer/draw/batch.js.map +1 -0
  432. package/dist/renderer/draw/clip.js +89 -0
  433. package/dist/renderer/draw/clip.js.map +1 -0
  434. package/dist/renderer/draw/customShader.js +230 -0
  435. package/dist/renderer/draw/customShader.js.map +1 -0
  436. package/dist/renderer/draw/drawTriangles.js +8 -0
  437. package/dist/renderer/draw/drawTriangles.js.map +1 -0
  438. package/dist/renderer/draw/effects.js +28 -0
  439. package/dist/renderer/draw/effects.js.map +1 -0
  440. package/dist/renderer/draw/flushBatch.js +35 -0
  441. package/dist/renderer/draw/flushBatch.js.map +1 -0
  442. package/dist/renderer/draw/gradient.js +90 -0
  443. package/dist/renderer/draw/gradient.js.map +1 -0
  444. package/dist/renderer/draw/image.js +38 -0
  445. package/dist/renderer/draw/image.js.map +1 -0
  446. package/dist/renderer/draw/mesh.js +16 -0
  447. package/dist/renderer/draw/mesh.js.map +1 -0
  448. package/dist/renderer/draw/paintBind.js +70 -0
  449. package/dist/renderer/draw/paintBind.js.map +1 -0
  450. package/dist/renderer/draw/pathPaint.js +142 -0
  451. package/dist/renderer/draw/pathPaint.js.map +1 -0
  452. package/dist/renderer/draw/scratchPass.js +83 -0
  453. package/dist/renderer/draw/scratchPass.js.map +1 -0
  454. package/dist/renderer/draw/stencil.js +28 -0
  455. package/dist/renderer/draw/stencil.js.map +1 -0
  456. package/dist/renderer/draw/text.js +167 -0
  457. package/dist/renderer/draw/text.js.map +1 -0
  458. package/dist/renderer/draw/uniforms.js +77 -0
  459. package/dist/renderer/draw/uniforms.js.map +1 -0
  460. package/dist/renderer/draw.js +60 -1116
  461. package/dist/renderer/draw.js.map +1 -1
  462. package/dist/renderer/effects/EffectTargets.js +3 -0
  463. package/dist/renderer/effects/EffectTargets.js.map +1 -1
  464. package/dist/renderer/effects/composite.js +7 -5
  465. package/dist/renderer/effects/composite.js.map +1 -1
  466. package/dist/renderer/index.js +18 -17
  467. package/dist/renderer/math/color.js +63 -83
  468. package/dist/renderer/math/color.js.map +1 -1
  469. package/dist/renderer/math/mat3.js +15 -11
  470. package/dist/renderer/math/mat3.js.map +1 -1
  471. package/dist/renderer/math/viewToMat3.js +1 -1
  472. package/dist/renderer/math/viewToMat3.js.map +1 -1
  473. package/dist/renderer/shaders/ShaderProgram.js +13 -13
  474. package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
  475. package/dist/renderer/shaders/glyphPaint.js +3 -3
  476. package/dist/renderer/shaders/glyphPaint.js.map +1 -1
  477. package/dist/renderer/shaders/gradFill.js +2 -1
  478. package/dist/renderer/shaders/gradFill.js.map +1 -1
  479. package/dist/renderer/shaders/pathFill.js +2 -1
  480. package/dist/renderer/shaders/pathFill.js.map +1 -1
  481. package/dist/renderer/shaders/patternFill.js +2 -1
  482. package/dist/renderer/shaders/patternFill.js.map +1 -1
  483. package/dist/renderer/shaders/registerProgram.js.map +1 -1
  484. package/dist/renderer/strokeLengths.js.map +1 -1
  485. package/dist/renderer/textures/registerTexture.js +25 -5
  486. package/dist/renderer/textures/registerTexture.js.map +1 -1
  487. package/dist/renderer.d.ts +16 -15
  488. package/dist/renderer.js +17 -16
  489. package/dist/resize.d.ts +5 -6
  490. package/dist/resize.js +5 -5
  491. package/dist/style.css +1 -1
  492. package/dist/test-hook/install.js +12 -6
  493. package/dist/test-hook/install.js.map +1 -1
  494. package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
  495. package/dist/test-seams.d.ts +2 -2
  496. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  497. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  498. package/dist/tools/builtin/hand/useHandTool.js +1 -0
  499. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  500. package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
  501. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  502. package/dist/tools/builtin/line/useLineTool.js +1 -0
  503. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  504. package/dist/tools/builtin/pen/usePenTool.js +153 -103
  505. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  506. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  507. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  508. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
  509. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  510. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  511. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  512. package/dist/tools/builtin/select/selectionContributions.js +24 -23
  513. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  514. package/dist/tools/builtin/select/useSelectTool.js +51 -48
  515. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  516. package/dist/tools/builtin/shared/pointerFollower.js +17 -0
  517. package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
  518. package/dist/tools/builtin/slice/useSliceTool.js +49 -54
  519. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  520. package/dist/tools/builtin/star/useStarTool.js +22 -16
  521. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  522. package/dist/tools/builtin/text/useTextTool.js +13 -2
  523. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  524. package/dist/tools/useKeybindings.js +14 -30
  525. package/dist/tools/useKeybindings.js.map +1 -1
  526. package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
  527. package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
  528. package/dist/util/color.js +3 -3
  529. package/dist/util/color.js.map +1 -1
  530. package/dist/util/paint.js.map +1 -1
  531. package/dist/version.js +1 -1
  532. package/package.json +20 -18
  533. package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
  534. package/dist/builtins-C-VgUlWI.d.ts +0 -4514
  535. package/dist/core/isPlainObject.js.map +0 -1
  536. package/dist/debug/flag.js.map +0 -1
  537. package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
  538. package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
  539. package/dist/renderer/shaders/imageFill.js +0 -14
  540. package/dist/renderer/shaders/imageFill.js.map +0 -1
  541. package/dist/tools/prefs.js +0 -47
  542. package/dist/tools/prefs.js.map +0 -1
@@ -1,34 +1,35 @@
1
- import { cssFontFamily as e } from "@weasel-js/font";
2
- import { DEFAULT_TEXT_STYLE as t, SMALL_CAPS_SCALE as n, isHardLineBreak as r, numericWeight as i, smallCapsScaleFor as a, smallCapsText as o, transformRunTexts as s } from "@weasel-js/text";
1
+ import { BLOCK_TAGS as e, charOffsetToDomPosition as t, domPositionToCharOffset as n } from "./domOffsets.js";
2
+ import { cssFontFamily as r } from "@weasel-js/font";
3
+ import { DEFAULT_TEXT_STYLE as i, SMALL_CAPS_SCALE as a, isHardLineBreak as o, numericWeight as s, smallCapsScaleFor as c, smallCapsText as l, transformRunTexts as u } from "@weasel-js/text";
3
4
  //#region src/features/text/domRuns.ts
4
- function c(e) {
5
+ function d(e) {
5
6
  return e && "color" in e ? e.color : null;
6
7
  }
7
- var l = /* @__PURE__ */ new Set([
8
+ var f = "data-fill", p = /* @__PURE__ */ new Set([
8
9
  "none",
9
10
  "uppercase",
10
11
  "lowercase",
11
12
  "capitalize"
12
- ]), u = {
13
+ ]), m = {
13
14
  bold: !1,
14
15
  italic: !1,
15
16
  underline: !1,
16
17
  strikethrough: !1,
17
18
  overline: !1
18
19
  };
19
- function d(e) {
20
+ function h(e) {
20
21
  let t = parseFloat(e);
21
22
  if (!Number.isFinite(t)) return;
22
23
  let n = /^-?[\d.]+([a-z%]+)$/i.exec(e.trim())?.[1]?.toLowerCase();
23
24
  return n && n !== "px" && console.warn(`weasel domRuns: letter-spacing "${e}" uses unit "${n}", which is not converted; treated as ${t} world units`), t;
24
25
  }
25
- var f = (e) => e.hasAttribute("data-small-caps");
26
- function p(e) {
26
+ var g = (e) => e.hasAttribute("data-small-caps");
27
+ function _(e) {
27
28
  let t = e.style.fontVariant || e.style.getPropertyValue("font-variant-caps");
28
29
  return t === "small-caps" || t === "normal" ? t : void 0;
29
30
  }
30
- function m(e, t) {
31
- if (f(e)) return t;
31
+ function v(e, t) {
32
+ if (g(e)) return t;
32
33
  let n = { ...t }, r = e.tagName;
33
34
  if ((r === "B" || r === "STRONG") && (n.bold = !0, n.fontWeight = void 0), (r === "I" || r === "EM") && (n.italic = !0), r === "U" && (n.underline = !0), (r === "S" || r === "STRIKE" || r === "DEL") && (n.strikethrough = !0), r === "SUP" && (n.script = "super"), r === "SUB" && (n.script = "sub"), e instanceof HTMLElement) {
34
35
  let t = e.style.fontWeight, r = Number(e.getAttribute("data-font-weight") ?? t);
@@ -54,148 +55,150 @@ function m(e, t) {
54
55
  Number.isFinite(e) && (n.baselineShift = e);
55
56
  }
56
57
  let c = e.style.textTransform;
57
- l.has(c) && (n.textTransform = c);
58
- let u = p(e);
59
- u && (n.fontVariantCaps = u);
60
- let f = e.getAttribute("data-font-family") ?? e.style.fontFamily;
61
- f && (n.fontFamily = f), e.style.color && (n.color = e.style.color);
58
+ p.has(c) && (n.textTransform = c);
59
+ let l = _(e);
60
+ l && (n.fontVariantCaps = l);
61
+ let u = e.getAttribute("data-font-family") ?? e.style.fontFamily;
62
+ u && (n.fontFamily = u);
63
+ let d = e.getAttribute(f);
64
+ d == null ? e.style.color && (n.color = e.style.color, n.fillJson = void 0) : (n.fillJson = d, n.color = void 0);
62
65
  let m = e.style.letterSpacing;
63
66
  if (m === "normal") n.letterSpacing = void 0;
64
67
  else if (m) {
65
- let e = d(m);
68
+ let e = h(m);
66
69
  e != null && (n.letterSpacing = e);
67
70
  }
68
71
  }
69
72
  return n;
70
73
  }
71
- function h(e, t) {
72
- return e.bold === t.bold && e.italic === t.italic && e.underline === t.underline && e.strikethrough === t.strikethrough && e.overline === t.overline && e.fontWeight === t.fontWeight && e.fontSize === t.fontSize && e.fontFamily === t.fontFamily && e.color === t.color && e.letterSpacing === t.letterSpacing && e.script === t.script && e.baselineShift === t.baselineShift && e.fontScale === t.fontScale && e.textTransform === t.textTransform && e.fontVariantCaps === t.fontVariantCaps;
74
+ function y(e, t) {
75
+ return e.bold === t.bold && e.italic === t.italic && e.underline === t.underline && e.strikethrough === t.strikethrough && e.overline === t.overline && e.fontWeight === t.fontWeight && e.fontSize === t.fontSize && e.fontFamily === t.fontFamily && e.color === t.color && e.fillJson === t.fillJson && e.letterSpacing === t.letterSpacing && e.script === t.script && e.baselineShift === t.baselineShift && e.fontScale === t.fontScale && e.textTransform === t.textTransform && e.fontVariantCaps === t.fontVariantCaps;
73
76
  }
74
- function g(e, t) {
77
+ function b(e, t) {
75
78
  let n = { text: e };
76
- return t.bold && (n.bold = !0), t.italic && (n.italic = !0), t.fontWeight != null && (n.fontWeight = t.fontWeight), t.fontSize != null && (n.fontSize = t.fontSize), t.fontFamily != null && (n.fontFamily = t.fontFamily), t.color != null && (n.fill = {
79
+ return t.bold && (n.bold = !0), t.italic && (n.italic = !0), t.fontWeight != null && (n.fontWeight = t.fontWeight), t.fontSize != null && (n.fontSize = t.fontSize), t.fontFamily != null && (n.fontFamily = t.fontFamily), t.fillJson === void 0 ? t.color != null && (n.fill = {
77
80
  fill: "solid",
78
81
  color: t.color
79
- }), t.letterSpacing != null && (n.letterSpacing = t.letterSpacing), t.underline && (n.underline = !0), t.strikethrough && (n.strikethrough = !0), t.overline && (n.overline = !0), t.script != null && (n.script = t.script), t.baselineShift != null && (n.baselineShift = t.baselineShift), t.fontScale != null && (n.fontScale = t.fontScale), t.textTransform != null && (n.textTransform = t.textTransform), t.fontVariantCaps != null && (n.fontVariantCaps = t.fontVariantCaps), n;
82
+ }) : n.fill = JSON.parse(t.fillJson), t.letterSpacing != null && (n.letterSpacing = t.letterSpacing), t.underline && (n.underline = !0), t.strikethrough && (n.strikethrough = !0), t.overline && (n.overline = !0), t.script != null && (n.script = t.script), t.baselineShift != null && (n.baselineShift = t.baselineShift), t.fontScale != null && (n.fontScale = t.fontScale), t.textTransform != null && (n.textTransform = t.textTransform), t.fontVariantCaps != null && (n.fontVariantCaps = t.fontVariantCaps), n;
80
83
  }
81
- var _ = "data-break";
82
- function v(e) {
83
- let t = e.textContent ?? "", n = parseInt(e.getAttribute(_) ?? "", 16);
84
+ var x = "data-break";
85
+ function S(e) {
86
+ let t = e.textContent ?? "", n = parseInt(e.getAttribute(x) ?? "", 16);
84
87
  return t.startsWith("\n") && Number.isFinite(n) ? String.fromCharCode(n) + t.slice(1) : t;
85
88
  }
86
- function y(e, t) {
89
+ function C(e, t) {
87
90
  let n = 0;
88
- for (let i = 0; i < t.length; i++) {
89
- let a = t.charCodeAt(i);
90
- if (a === 10 || !r(a) || a === 13 && t.charCodeAt(i + 1) === 10) continue;
91
- i > n && e.appendChild(document.createTextNode(t.slice(n, i)));
92
- let o = document.createElement("span");
93
- o.setAttribute(_, a.toString(16)), o.textContent = "\n", e.appendChild(o), n = i + 1;
91
+ for (let r = 0; r < t.length; r++) {
92
+ let i = t.charCodeAt(r);
93
+ if (i === 10 || !o(i) || i === 13 && t.charCodeAt(r + 1) === 10) continue;
94
+ r > n && e.appendChild(document.createTextNode(t.slice(n, r)));
95
+ let a = document.createElement("span");
96
+ a.setAttribute(x, i.toString(16)), a.textContent = "\n", e.appendChild(a), n = r + 1;
94
97
  }
95
98
  n < t.length && e.appendChild(document.createTextNode(t.slice(n)));
96
99
  }
97
- function b(e) {
98
- let t = [];
99
- function n(e, r) {
100
- if (e.nodeType === Node.TEXT_NODE) {
101
- let n = e.data;
102
- n.length > 0 && t.push({
103
- text: n,
104
- style: r
100
+ function w(t) {
101
+ let n = [];
102
+ function r(t, i) {
103
+ if (t.nodeType === Node.TEXT_NODE) {
104
+ let e = t.data;
105
+ e.length > 0 && n.push({
106
+ text: e,
107
+ style: i
105
108
  });
106
109
  return;
107
110
  }
108
- if (e.nodeType !== Node.ELEMENT_NODE) return;
109
- let i = e;
110
- if (i.tagName === "BR") {
111
- t.push({
111
+ if (t.nodeType !== Node.ELEMENT_NODE) return;
112
+ let a = t;
113
+ if (a.tagName === "BR") {
114
+ n.push({
112
115
  text: "\n",
113
- style: r
116
+ style: i
114
117
  });
115
118
  return;
116
119
  }
117
- if (i.hasAttribute(_)) {
118
- let e = v(i);
119
- e.length > 0 && t.push({
120
+ if (a.hasAttribute(x)) {
121
+ let e = S(a);
122
+ e.length > 0 && n.push({
120
123
  text: e,
121
- style: r
124
+ style: i
122
125
  });
123
126
  return;
124
127
  }
125
- i.tagName === "DIV" && t.length > 0 && t.push({
128
+ e.has(a.tagName) && n.length > 0 && n.push({
126
129
  text: "\n",
127
- style: r
130
+ style: i
128
131
  });
129
- let a = m(i, r);
130
- for (let e of Array.from(i.childNodes)) n(e, a);
132
+ let o = v(a, i);
133
+ for (let e of Array.from(a.childNodes)) r(e, o);
131
134
  }
132
- for (let t of Array.from(e.childNodes)) n(t, u);
133
- let r = [], i = 0;
134
- for (; i < t.length;) {
135
- let e = i + 1;
136
- for (; e < t.length && h(t[i].style, t[e].style);) e++;
137
- let n = t.slice(i, e).map((e) => e.text).join("");
138
- r.push(g(n, t[i].style)), i = e;
135
+ for (let e of Array.from(t.childNodes)) r(e, m);
136
+ let i = [], a = 0;
137
+ for (; a < n.length;) {
138
+ let e = a + 1;
139
+ for (; e < n.length && y(n[a].style, n[e].style);) e++;
140
+ let t = n.slice(a, e).map((e) => e.text).join("");
141
+ i.push(b(t, n[a].style)), a = e;
139
142
  }
140
- return r;
143
+ return i;
141
144
  }
142
- var x = "--weasel-small-caps";
143
- function S(e, t) {
144
- let [n] = s([e], [t]), r = o(n.text, n.srcMap), i = Array(e.length).fill(!1);
145
+ var T = "--weasel-small-caps";
146
+ function E(e, t) {
147
+ let [n] = u([e], [t]), r = l(n.text, n.srcMap), i = Array(e.length).fill(!1);
145
148
  r.small?.forEach((e, t) => {
146
149
  e && (i[r.srcMap ? r.srcMap.starts[t] : t] = !0);
147
150
  });
148
- let a = [], c = 0;
151
+ let a = [], o = 0;
149
152
  for (let t of e) {
150
153
  let e = a[a.length - 1];
151
- e && e.small === i[c] ? e.text += t : a.push({
154
+ e && e.small === i[o] ? e.text += t : a.push({
152
155
  text: t,
153
- small: i[c]
154
- }), c += t.length;
156
+ small: i[o]
157
+ }), o += t.length;
155
158
  }
156
159
  return a;
157
160
  }
158
- function C(e, t, r) {
161
+ function D(e, t, n) {
159
162
  e.replaceChildren();
160
- for (let i of S(t, r)) {
161
- if (!i.small) {
162
- y(e, i.text);
163
+ for (let r of E(t, n)) {
164
+ if (!r.small) {
165
+ C(e, r.text);
163
166
  continue;
164
167
  }
165
168
  let t = document.createElement("span");
166
- t.setAttribute("data-small-caps", ""), t.style.fontSize = `calc(var(${x}, ${n}) * 1em)`, t.style.textTransform = "uppercase", t.textContent = i.text, e.appendChild(t);
169
+ t.setAttribute("data-small-caps", ""), t.style.fontSize = `calc(var(${T}, ${a}) * 1em)`, t.style.textTransform = "uppercase", t.textContent = r.text, e.appendChild(t);
167
170
  }
168
171
  }
169
- function w(n, r, o = t) {
170
- r.replaceChildren();
171
- for (let t of n) {
172
- let n = document.createElement("span");
173
- if (n.setAttribute("data-run", ""), (t.fontVariantCaps ?? o.fontVariantCaps) === "small-caps") {
174
- let e = a(t.fontFamily ?? o.fontFamily, t.fontWeight ?? (t.bold ? 700 : i(o.fontWeight)), t.italic ? "italic" : o.fontStyle);
175
- n.style.setProperty(x, String(e)), C(n, t.text, t.textTransform ?? o.textTransform);
176
- } else y(n, t.text);
177
- t.fontVariantCaps != null && (n.style.fontVariant = t.fontVariantCaps), t.fontWeight == null ? t.bold && (n.style.fontWeight = "700") : (n.style.fontWeight = String(t.fontWeight), n.setAttribute("data-font-weight", String(t.fontWeight))), t.italic && (n.style.fontStyle = "italic"), t.fontSize == null ? t.fontScale != null && (n.style.fontSize = `${t.fontScale * 100}%`) : n.style.fontSize = `${t.fontSize}px`, t.fontFamily != null && (n.style.fontFamily = e(t.fontFamily, {
178
- weight: t.fontWeight ?? (t.bold ? 700 : void 0),
179
- style: t.italic ? "italic" : void 0
180
- }), n.setAttribute("data-font-family", t.fontFamily));
181
- let s = c(t.fill);
182
- s != null && (n.style.color = s);
183
- let l = [];
184
- t.underline && l.push("underline"), t.strikethrough && l.push("line-through"), t.overline && l.push("overline"), l.length > 0 && (n.style.textDecoration = l.join(" ")), t.script != null && (n.style.verticalAlign = t.script), t.baselineShift != null && n.setAttribute("data-baseline-shift", String(t.baselineShift)), t.letterSpacing != null && (n.style.letterSpacing = `${t.letterSpacing}px`), t.textTransform != null && (n.style.textTransform = t.textTransform), r.appendChild(n);
172
+ function O(e, t, n = i) {
173
+ t.replaceChildren();
174
+ for (let i of e) {
175
+ let e = document.createElement("span");
176
+ if (e.setAttribute("data-run", ""), (i.fontVariantCaps ?? n.fontVariantCaps) === "small-caps") {
177
+ let t = c(i.fontFamily ?? n.fontFamily, i.fontWeight ?? (i.bold ? 700 : s(n.fontWeight)), i.italic ? "italic" : n.fontStyle);
178
+ e.style.setProperty(T, String(t)), D(e, i.text, i.textTransform ?? n.textTransform);
179
+ } else C(e, i.text);
180
+ i.fontVariantCaps != null && (e.style.fontVariant = i.fontVariantCaps), i.fontWeight == null ? i.bold && (e.style.fontWeight = "700") : (e.style.fontWeight = String(i.fontWeight), e.setAttribute("data-font-weight", String(i.fontWeight))), i.italic && (e.style.fontStyle = "italic"), i.fontSize == null ? i.fontScale != null && (e.style.fontSize = `${i.fontScale * 100}%`) : e.style.fontSize = `${i.fontSize}px`, i.fontFamily != null && (e.style.fontFamily = r(i.fontFamily, {
181
+ weight: i.fontWeight ?? (i.bold ? 700 : void 0),
182
+ style: i.italic ? "italic" : void 0
183
+ }), e.setAttribute("data-font-family", i.fontFamily));
184
+ let a = d(i.fill);
185
+ a == null ? i.fill !== void 0 && e.setAttribute(f, JSON.stringify(i.fill)) : e.style.color = a, i.fill === null && (e.style.color = "#000");
186
+ let o = [];
187
+ i.underline && o.push("underline"), i.strikethrough && o.push("line-through"), i.overline && o.push("overline"), o.length > 0 && (e.style.textDecoration = o.join(" ")), i.script != null && (e.style.verticalAlign = i.script), i.baselineShift != null && e.setAttribute("data-baseline-shift", String(i.baselineShift)), i.letterSpacing != null && (e.style.letterSpacing = `${i.letterSpacing}px`), i.textTransform != null && (e.style.textTransform = i.textTransform), t.appendChild(e);
185
188
  }
186
189
  }
187
- function T(e) {
190
+ function k(e) {
188
191
  let t = "";
189
- for (let n of e.childNodes) n.nodeType === Node.TEXT_NODE ? t += n.data : n instanceof Element && (t += n.hasAttribute(_) ? v(n) : T(n));
192
+ for (let n of e.childNodes) n.nodeType === Node.TEXT_NODE ? t += n.data : n instanceof Element && (t += n.hasAttribute(x) ? S(n) : k(n));
190
193
  return t;
191
194
  }
192
- function E(e, t) {
193
- let n = S(T(e), t), r = [];
195
+ function A(e, t) {
196
+ let n = E(k(e), t), r = [];
194
197
  for (let t of e.childNodes) {
195
198
  let e, n;
196
199
  if (t.nodeType === Node.TEXT_NODE) e = !1, n = t.data;
197
- else if (t instanceof HTMLElement && t.hasAttribute(_)) e = !1, n = v(t);
198
- else if (t instanceof HTMLElement && f(t) && t.children.length === 0) e = !0, n = t.textContent ?? "";
200
+ else if (t instanceof HTMLElement && t.hasAttribute(x)) e = !1, n = S(t);
201
+ else if (t instanceof HTMLElement && g(t) && t.children.length === 0) e = !0, n = t.textContent ?? "";
199
202
  else return !1;
200
203
  if (n.length === 0) continue;
201
204
  let i = r[r.length - 1];
@@ -206,14 +209,14 @@ function E(e, t) {
206
209
  }
207
210
  return n.length === r.length && n.every((e, t) => e.text === r[t].text && e.small === r[t].small);
208
211
  }
209
- function D(e) {
212
+ function j(e) {
210
213
  let t = !1, n = (e, r, i) => {
211
- let a = p(e) ?? r, o = e.style.textTransform, s = l.has(o) ? o : i;
214
+ let a = _(e) ?? r, o = e.style.textTransform, s = p.has(o) ? o : i;
212
215
  if (e.hasAttribute("data-run")) {
213
- if (a === "small-caps") E(e, s) || (C(e, T(e), s), t = !0);
216
+ if (a === "small-caps") A(e, s) || (D(e, k(e), s), t = !0);
214
217
  else if (e.querySelector("[data-small-caps]")) {
215
- let n = T(e);
216
- e.replaceChildren(), y(e, n), t = !0;
218
+ let n = k(e);
219
+ e.replaceChildren(), C(e, n), t = !0;
217
220
  }
218
221
  return;
219
222
  }
@@ -221,38 +224,7 @@ function D(e) {
221
224
  };
222
225
  return n(e, "normal", "none"), t;
223
226
  }
224
- function O(e, t) {
225
- let n = t, r = document.createTreeWalker(e, NodeFilter.SHOW_TEXT), i = null, a = r.nextNode();
226
- for (; a;) {
227
- let e = a.data.length;
228
- if (n <= e) return {
229
- node: a,
230
- offset: n
231
- };
232
- n -= e, i = a, a = r.nextNode();
233
- }
234
- return i ? {
235
- node: i,
236
- offset: i.data.length
237
- } : {
238
- node: e,
239
- offset: 0
240
- };
241
- }
242
- function k(e, t, n) {
243
- let r = 0, i = document.createTreeWalker(e, NodeFilter.SHOW_TEXT), a = i.nextNode();
244
- if (t.nodeType === Node.TEXT_NODE) {
245
- for (; a;) {
246
- if (a === t) return r + n;
247
- r += a.data.length, a = i.nextNode();
248
- }
249
- return r;
250
- }
251
- let o = document.createRange();
252
- for (o.setStart(t, Math.max(0, Math.min(n, t.childNodes.length))), o.collapse(!0); a;) o.comparePoint(a, 0) < 0 && (r += a.data.length), a = i.nextNode();
253
- return r;
254
- }
255
227
  //#endregion
256
- export { x as SMALL_CAPS_SCALE_PROPERTY, y as appendOverlayText, O as charOffsetToDomPosition, k as domPositionToCharOffset, b as domToRuns, D as normalizeSmallCaps, w as runsToDom };
228
+ export { T as SMALL_CAPS_SCALE_PROPERTY, C as appendOverlayText, t as charOffsetToDomPosition, n as domPositionToCharOffset, w as domToRuns, j as normalizeSmallCaps, O as runsToDom };
257
229
 
258
230
  //# sourceMappingURL=domRuns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"domRuns.js","names":["solidColor","p","TRANSFORMS","EMPTY_STYLE","parseLetterSpacing","n","raw","unit","isSmallCapsPiece","el","cssFontVariant","v","styleStateFromElement","parent","next","tag","fw","ownWeight","fs","td","size","va","rawShift","shift","tt","fv","family","ls","px","styleEquals","a","b","toRun","run","style","BREAK_ATTR","restoreBreak","text","code","appendOverlayText","from","c","isHardLineBreak","span","domToRuns","fragments","visit","node","restored","nextStyle","child","runs","j","merged","f","SMALL_CAPS_SCALE_PROPERTY","smallCapsPieces","shown","transformRunTexts","transform","caps","smallCapsText","small","s","i","out","at","ch","last","writeSmallCaps","piece","SMALL_CAPS_SCALE","runsToDom","DEFAULT_TEXT_STYLE","base","scale","smallCapsScaleFor","numericWeight","cssFontFamily","color","decorations","sourceText","piecesMatch","want","have","normalizeSmallCaps","changed","variant","t","charOffsetToDomPosition","remaining","offset","walker","len","domPositionToCharOffset","total","cur","point"],"sources":["../../../src/features/text/domRuns.ts"],"sourcesContent":["/**\n * Pure DOM ↔ `StyledRun[]` serializers for the contenteditable overlay\n * used by `useTextEdit`. The overlay's children are a flat sequence of\n * `<span data-run>` elements, each carrying one run's text and inline\n * styles. Newlines inside a run are literal `\\n` characters; the overlay\n * has `white-space: pre-wrap` so they render as line breaks. The browser\n * breaks at no other UAX #14 hard break, so each of those is written as a\n * `<span data-break>` holding a `\\n` in its place — one character for one,\n * which keeps DOM offsets equal to source offsets.\n *\n * A small-caps run is the one exception to one text node per span: its\n * lowercase letters sit in `<span data-small-caps>` pieces, uppercased and\n * sized at the scale the canvas draws them with, since a browser's own\n * synthesis uses a fixed factor of its own. The pieces are presentation\n * only — `domToRuns` reads straight through them.\n */\n\nimport type { FillStyle } from '@weasel-js/paint';\nimport {\n DEFAULT_TEXT_STYLE, isHardLineBreak, numericWeight, smallCapsScaleFor, smallCapsText, transformRunTexts,\n SMALL_CAPS_SCALE,\n type FontVariantCaps, type ResolvedTextStyle, type StyledRun, type TextTransform,\n} from '@weasel-js/text';\nimport { cssFontFamily } from '@weasel-js/font';\n\nfunction solidColor(p: FillStyle | undefined): string | null {\n if (!p) return null;\n if ('color' in p) return p.color;\n return null;\n}\n\ninterface StyleState {\n bold: boolean;\n italic: boolean;\n underline: boolean;\n strikethrough: boolean;\n overline: boolean;\n fontWeight?: number;\n fontSize?: number;\n fontFamily?: string;\n color?: string;\n letterSpacing?: number;\n script?: 'super' | 'sub';\n baselineShift?: number;\n fontScale?: number;\n textTransform?: TextTransform;\n fontVariantCaps?: FontVariantCaps;\n}\n\nconst TRANSFORMS: ReadonlySet<string> = new Set(['none', 'uppercase', 'lowercase', 'capitalize']);\n\nconst EMPTY_STYLE: StyleState = {\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n overline: false,\n};\n\n/**\n * Parse a CSS `letter-spacing` value into world units. `runsToDom` only ever\n * writes `px`, but paste is a live path into the contenteditable and CSSOM\n * keeps whatever unit the pasted markup used. Any other unit is still\n * numerically coerced — `parseFloat` reads the leading digits — but flagged,\n * since e.g. `0.1em` silently becomes `0.1` world units, off by a factor of\n * the font size. Mirrors `parseLetterSpacing` in the SVG reader.\n */\nfunction parseLetterSpacing(raw: string): number | undefined {\n const n = parseFloat(raw);\n if (!Number.isFinite(n)) return undefined;\n const unit = /^-?[\\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();\n if (unit && unit !== 'px') {\n console.warn(\n `weasel domRuns: letter-spacing \"${raw}\" uses unit \"${unit}\", which is not converted; treated as ${n} world units`,\n );\n }\n return n;\n}\n\n/** A small-caps piece is presentation `runsToDom` split a run into, not styling. */\nconst isSmallCapsPiece = (el: Element): boolean => el.hasAttribute('data-small-caps');\n\n/** The element's own `font-variant`, from the shorthand or the caps longhand\n * pasted markup may carry alone. */\nfunction cssFontVariant(el: HTMLElement): FontVariantCaps | undefined {\n const v = el.style.fontVariant || el.style.getPropertyValue('font-variant-caps');\n return v === 'small-caps' || v === 'normal' ? v : undefined;\n}\n\nfunction styleStateFromElement(el: Element, parent: StyleState): StyleState {\n if (isSmallCapsPiece(el)) return parent;\n const next: StyleState = { ...parent };\n const tag = el.tagName;\n if (tag === 'B' || tag === 'STRONG') {\n next.bold = true;\n next.fontWeight = undefined;\n }\n if (tag === 'I' || tag === 'EM') next.italic = true;\n // `runsToDom` never emits these, but a browser contenteditable can (Cmd+U\n // runs the native `underline` command, which produces `<u>` in Chrome), so\n // read them for the same reason `<b>`/`<i>` are read.\n if (tag === 'U') next.underline = true;\n if (tag === 'S' || tag === 'STRIKE' || tag === 'DEL') next.strikethrough = true;\n if (tag === 'SUP') next.script = 'super';\n if (tag === 'SUB') next.script = 'sub';\n if (el instanceof HTMLElement) {\n // 700 and 400 are the bold flag on and off, which is what pasted markup\n // means by them. A run's own weight is written with an attribute beside\n // the CSS, so a stored 400 or 700 comes back as the weight it was.\n const fw = el.style.fontWeight;\n const ownWeight = Number(el.getAttribute('data-font-weight') ?? fw);\n if (el.hasAttribute('data-font-weight') || !['', '700', 'bold', '400', 'normal'].includes(fw)) {\n if (Number.isFinite(ownWeight) && ownWeight > 0) {\n next.fontWeight = ownWeight;\n next.bold = false;\n }\n } else if (fw !== '') {\n next.bold = fw === '700' || fw === 'bold';\n next.fontWeight = undefined;\n }\n const fs = el.style.fontStyle;\n if (fs === 'italic') next.italic = true;\n if (fs === 'normal') next.italic = false;\n // Decoration accumulates down the tree — unlike `font-weight`/`font-style`\n // above, `text-decoration` does not inherit, it *propagates*, and a\n // descendant cannot cancel what an ancestor drew. So OR the flags in\n // rather than overwriting: `<u><span style=\"text-decoration: line-through\">`\n // renders both lines, and that is a reachable shape here, since `runsToDom`\n // emits the span and an unintercepted Cmd+U supplies the `<u>`. An explicit\n // `none` contributes no tokens and cancels nothing.\n //\n // A declaration block carrying only the longhand serializes the shorthand\n // as `''` (the shorthand needs the full set), and browsers and pasted HTML\n // both produce that, so fall back to `text-decoration-line`.\n const td = el.style.textDecoration || el.style.textDecorationLine;\n if (td && !td.includes('none')) {\n next.underline ||= td.includes('underline');\n next.strikethrough ||= td.includes('line-through');\n next.overline ||= td.includes('overline');\n }\n // A percentage is `fontScale`'s own spelling — CSS resolves it against the\n // parent, which is exactly what the field means. Any other unit is an\n // absolute size.\n if (el.style.fontSize) {\n const size = parseFloat(el.style.fontSize);\n if (Number.isFinite(size)) {\n if (el.style.fontSize.trim().endsWith('%')) next.fontScale = size / 100;\n else next.fontSize = size;\n }\n }\n const va = el.style.verticalAlign;\n if (va === 'super' || va === 'sub') next.script = va === 'super' ? 'super' : 'sub';\n if (va === 'baseline') next.script = undefined;\n const rawShift = el.getAttribute('data-baseline-shift');\n if (rawShift != null) {\n const shift = parseFloat(rawShift);\n if (Number.isFinite(shift)) next.baselineShift = shift;\n }\n const tt = el.style.textTransform;\n if (TRANSFORMS.has(tt)) next.textTransform = tt as TextTransform;\n const fv = cssFontVariant(el);\n if (fv) next.fontVariantCaps = fv;\n // The attribute holds the run's family; the CSS holds the face it maps to.\n const family = el.getAttribute('data-font-family') ?? el.style.fontFamily;\n if (family) next.fontFamily = family;\n if (el.style.color) next.color = el.style.color;\n const ls = el.style.letterSpacing;\n if (ls === 'normal') {\n next.letterSpacing = undefined;\n } else if (ls) {\n const px = parseLetterSpacing(ls);\n if (px != null) next.letterSpacing = px;\n }\n }\n return next;\n}\n\nfunction styleEquals(a: StyleState, b: StyleState): boolean {\n return (\n a.bold === b.bold &&\n a.italic === b.italic &&\n a.underline === b.underline &&\n a.strikethrough === b.strikethrough &&\n a.overline === b.overline &&\n a.fontWeight === b.fontWeight &&\n a.fontSize === b.fontSize &&\n a.fontFamily === b.fontFamily &&\n a.color === b.color &&\n a.letterSpacing === b.letterSpacing &&\n a.script === b.script &&\n a.baselineShift === b.baselineShift &&\n a.fontScale === b.fontScale &&\n a.textTransform === b.textTransform &&\n a.fontVariantCaps === b.fontVariantCaps\n );\n}\n\nfunction toRun(text: string, style: StyleState): StyledRun {\n const run: StyledRun = { text };\n if (style.bold) run.bold = true;\n if (style.italic) run.italic = true;\n if (style.fontWeight != null) run.fontWeight = style.fontWeight;\n if (style.fontSize != null) run.fontSize = style.fontSize;\n if (style.fontFamily != null) run.fontFamily = style.fontFamily;\n if (style.color != null) run.fill = { fill: 'solid', color: style.color };\n // `letterSpacing: 0` is a meaningful override (it cancels node-level\n // tracking for this run), so test for presence, not truthiness.\n if (style.letterSpacing != null) run.letterSpacing = style.letterSpacing;\n // Run-level flags are additive over the node style — a run cannot un-set\n // one — so an undecorated run gets an absent key, never `false`.\n if (style.underline) run.underline = true;\n if (style.strikethrough) run.strikethrough = true;\n if (style.overline) run.overline = true;\n if (style.script != null) run.script = style.script;\n // Both are meaningful at values that test falsy — a 0 shift cancels an\n // inherited script's rise — so test for presence, as `letterSpacing` does.\n if (style.baselineShift != null) run.baselineShift = style.baselineShift;\n if (style.fontScale != null) run.fontScale = style.fontScale;\n if (style.textTransform != null) run.textTransform = style.textTransform;\n if (style.fontVariantCaps != null) run.fontVariantCaps = style.fontVariantCaps;\n return run;\n}\n\nconst BREAK_ATTR = 'data-break';\n\n/** A break stand-in's source text: its hard break, and anything typed into it after. */\nfunction restoreBreak(el: Element): string {\n const text = el.textContent ?? '';\n const code = parseInt(el.getAttribute(BREAK_ATTR) ?? '', 16);\n return text.startsWith('\\n') && Number.isFinite(code) ? String.fromCharCode(code) + text.slice(1) : text;\n}\n\n/**\n * Append `text` to `parent` as the overlay sets it: a hard break the browser\n * would not break at becomes a `<span data-break>` holding a `\\n`, carrying\n * the original's code as hex for `domToRuns` to restore. A CR that opens a\n * CRLF is left as it is — the LF breaks, and the CR draws nothing.\n */\nexport function appendOverlayText(parent: Node, text: string): void {\n let from = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i);\n if (c === 10 || !isHardLineBreak(c) || (c === 13 && text.charCodeAt(i + 1) === 10)) continue;\n if (i > from) parent.appendChild(document.createTextNode(text.slice(from, i)));\n const span = document.createElement('span');\n span.setAttribute(BREAK_ATTR, c.toString(16));\n span.textContent = '\\n';\n parent.appendChild(span);\n from = i + 1;\n }\n if (from < text.length) parent.appendChild(document.createTextNode(text.slice(from)));\n}\n\n/** Walk an overlay tree and emit a coalesced `StyledRun[]`. */\nexport function domToRuns(parent: HTMLElement): StyledRun[] {\n const fragments: Array<{ text: string; style: StyleState }> = [];\n\n function visit(node: Node, style: StyleState): void {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = (node as Text).data;\n if (text.length > 0) fragments.push({ text, style });\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as Element;\n if (el.tagName === 'BR') {\n fragments.push({ text: '\\n', style });\n return;\n }\n if (el.hasAttribute(BREAK_ATTR)) {\n const restored = restoreBreak(el);\n if (restored.length > 0) fragments.push({ text: restored, style });\n return;\n }\n if (el.tagName === 'DIV' && fragments.length > 0) {\n fragments.push({ text: '\\n', style });\n }\n const nextStyle = styleStateFromElement(el, style);\n for (const child of Array.from(el.childNodes)) {\n visit(child, nextStyle);\n }\n }\n\n for (const child of Array.from(parent.childNodes)) {\n visit(child, EMPTY_STYLE);\n }\n\n // Coalesce adjacent fragments with identical style.\n const runs: StyledRun[] = [];\n let i = 0;\n while (i < fragments.length) {\n let j = i + 1;\n while (j < fragments.length && styleEquals(fragments[i].style, fragments[j].style)) {\n j++;\n }\n const merged = fragments.slice(i, j).map((f) => f.text).join('');\n runs.push(toRun(merged, fragments[i].style));\n i = j;\n }\n return runs;\n}\n\n/** What a run inherits from its node, for the overlay's small caps: which\n * runs have it, and the face and transform that decide their pieces. */\nexport type OverlayRunBase = Pick<\n ResolvedTextStyle, 'fontFamily' | 'fontWeight' | 'fontStyle' | 'textTransform' | 'fontVariantCaps'\n>;\n\n/** The custom property carrying a small-caps run's scale down to its pieces. */\nexport const SMALL_CAPS_SCALE_PROPERTY = '--weasel-small-caps';\n\n/** A run's source text cut where small caps changes size, per `smallCapsText`\n * over the text as `transform` draws it. */\nfunction smallCapsPieces(text: string, transform: TextTransform): Array<{ text: string; small: boolean }> {\n const [shown] = transformRunTexts([text], [transform]);\n const caps = smallCapsText(shown.text, shown.srcMap);\n const small = new Array<boolean>(text.length).fill(false);\n caps.small?.forEach((s, i) => {\n if (s) small[caps.srcMap ? caps.srcMap.starts[i] : i] = true;\n });\n const out: Array<{ text: string; small: boolean }> = [];\n let at = 0;\n for (const ch of text) {\n const last = out[out.length - 1];\n if (last && last.small === small[at]) last.text += ch;\n else out.push({ text: ch, small: small[at] });\n at += ch.length;\n }\n return out;\n}\n\n/** Replace `span`'s children with `text`, its small-caps letters in pieces. */\nfunction writeSmallCaps(span: HTMLElement, text: string, transform: TextTransform): void {\n span.replaceChildren();\n for (const p of smallCapsPieces(text, transform)) {\n if (!p.small) { appendOverlayText(span, p.text); continue; }\n const piece = document.createElement('span');\n piece.setAttribute('data-small-caps', '');\n piece.style.fontSize = `calc(var(${SMALL_CAPS_SCALE_PROPERTY}, ${SMALL_CAPS_SCALE}) * 1em)`;\n piece.style.textTransform = 'uppercase';\n piece.textContent = p.text;\n span.appendChild(piece);\n }\n}\n\n/**\n * Build a flat sequence of `<span data-run>` children from `runs`, replacing\n * any existing children of `parent`. `base` is the node's style, which a run\n * inheriting small caps needs to be split by; without it the kit defaults\n * stand in.\n */\nexport function runsToDom(\n runs: readonly StyledRun[],\n parent: HTMLElement,\n base: OverlayRunBase = DEFAULT_TEXT_STYLE,\n): void {\n parent.replaceChildren();\n for (const run of runs) {\n const span = document.createElement('span');\n span.setAttribute('data-run', '');\n if ((run.fontVariantCaps ?? base.fontVariantCaps) === 'small-caps') {\n const scale = smallCapsScaleFor(\n run.fontFamily ?? base.fontFamily,\n run.fontWeight ?? (run.bold ? 700 : numericWeight(base.fontWeight)),\n run.italic ? 'italic' : base.fontStyle,\n );\n span.style.setProperty(SMALL_CAPS_SCALE_PROPERTY, String(scale));\n writeSmallCaps(span, run.text, run.textTransform ?? base.textTransform);\n } else {\n appendOverlayText(span, run.text);\n }\n // CSS as well as the pieces: it is what pasted markup says, and it gives a\n // letter typed outside a piece a small cap until the next normalize.\n if (run.fontVariantCaps != null) span.style.fontVariant = run.fontVariantCaps;\n if (run.fontWeight != null) {\n span.style.fontWeight = String(run.fontWeight);\n span.setAttribute('data-font-weight', String(run.fontWeight));\n } else if (run.bold) span.style.fontWeight = '700';\n if (run.italic) span.style.fontStyle = 'italic';\n // An absolute size wins over a relative one, as it does in `resolveRuns`.\n if (run.fontSize != null) span.style.fontSize = `${run.fontSize}px`;\n else if (run.fontScale != null) span.style.fontSize = `${run.fontScale * 100}%`;\n if (run.fontFamily != null) {\n span.style.fontFamily = cssFontFamily(run.fontFamily, {\n weight: run.fontWeight ?? (run.bold ? 700 : undefined),\n style: run.italic ? 'italic' : undefined,\n });\n span.setAttribute('data-font-family', run.fontFamily);\n }\n const color = solidColor(run.fill);\n if (color != null) span.style.color = color;\n // Decoration goes on the span as an inline style rather than `<u>`/`<s>`\n // wrappers: one representation for `domToRuns` to unwrap, and one element\n // per run so the caret-offset walkers stay flat.\n const decorations: string[] = [];\n if (run.underline) decorations.push('underline');\n if (run.strikethrough) decorations.push('line-through');\n if (run.overline) decorations.push('overline');\n if (decorations.length > 0) span.style.textDecoration = decorations.join(' ');\n if (run.script != null) span.style.verticalAlign = run.script;\n // The one run field with no CSS spelling: `vertical-align` takes a length\n // or a percentage of the *line height*, and this is a fraction of the\n // parent's font size. An attribute keeps the round-trip exact; the canvas\n // underneath is what actually draws the run at its offset.\n if (run.baselineShift != null) {\n span.setAttribute('data-baseline-shift', String(run.baselineShift));\n }\n // World units — the overlay's own node-level tracking is screen-scaled,\n // but a run override is stored as-is so the round-trip is lossless. CSS\n // `letter-spacing` is inherited and a child declaration *replaces* the\n // inherited value, so this composes with the overlay's rather than adding.\n if (run.letterSpacing != null) span.style.letterSpacing = `${run.letterSpacing}px`;\n // CSS draws the transform, so the span's text stays the source and the\n // caret-offset walkers below need no map.\n if (run.textTransform != null) span.style.textTransform = run.textTransform;\n parent.appendChild(span);\n }\n}\n\n/** `el`'s text as `domToRuns` reads it: each break stand-in restored. */\nfunction sourceText(el: Element): string {\n let out = '';\n for (const n of el.childNodes) {\n if (n.nodeType === Node.TEXT_NODE) out += (n as Text).data;\n else if (n instanceof Element) out += n.hasAttribute(BREAK_ATTR) ? restoreBreak(n) : sourceText(n);\n }\n return out;\n}\n\n/** Whether `span`'s children are the pieces `writeSmallCaps` would write. */\nfunction piecesMatch(span: HTMLElement, transform: TextTransform): boolean {\n const want = smallCapsPieces(sourceText(span), transform);\n const have: Array<{ text: string; small: boolean }> = [];\n for (const n of span.childNodes) {\n let small: boolean;\n let text: string;\n if (n.nodeType === Node.TEXT_NODE) { small = false; text = (n as Text).data; }\n else if (n instanceof HTMLElement && n.hasAttribute(BREAK_ATTR)) { small = false; text = restoreBreak(n); }\n else if (n instanceof HTMLElement && isSmallCapsPiece(n) && n.children.length === 0) { small = true; text = n.textContent ?? ''; }\n else return false;\n if (text.length === 0) continue;\n const last = have[have.length - 1];\n if (last && last.small === small) last.text += text;\n else have.push({ text, small });\n }\n return want.length === have.length\n && want.every((p, i) => p.text === have[i].text && p.small === have[i].small);\n}\n\n/**\n * Put every run span's small-caps pieces back where its text now needs them —\n * typing lands a letter in whichever piece holds the caret, not the one its\n * case belongs in. Returns whether anything was rewritten, which moves the\n * DOM selection: the caller restores it by character offset.\n */\nexport function normalizeSmallCaps(parent: HTMLElement): boolean {\n let changed = false;\n const visit = (el: HTMLElement, variant: FontVariantCaps, transform: TextTransform): void => {\n const v = cssFontVariant(el) ?? variant;\n const tt = el.style.textTransform;\n const t = TRANSFORMS.has(tt) ? (tt as TextTransform) : transform;\n if (el.hasAttribute('data-run')) {\n if (v === 'small-caps') {\n if (!piecesMatch(el, t)) { writeSmallCaps(el, sourceText(el), t); changed = true; }\n } else if (el.querySelector('[data-small-caps]')) {\n const text = sourceText(el);\n el.replaceChildren();\n appendOverlayText(el, text);\n changed = true;\n }\n return;\n }\n for (const child of el.children) if (child instanceof HTMLElement) visit(child, v, t);\n };\n visit(parent, 'normal', 'none');\n return changed;\n}\n\n/**\n * Walk the overlay's text nodes in document order and find the text node\n * + offset that corresponds to plain-text character position `offset`.\n * Offsets beyond the total text length clamp to the end of the last text\n * node. Returns `{ node: parent, offset: 0 }` when there are no text nodes.\n */\nexport function charOffsetToDomPosition(\n parent: HTMLElement,\n offset: number,\n): { node: Node; offset: number } | null {\n let remaining = offset;\n const walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);\n let last: Text | null = null;\n let node = walker.nextNode() as Text | null;\n while (node) {\n const len = node.data.length;\n if (remaining <= len) {\n return { node, offset: remaining };\n }\n remaining -= len;\n last = node;\n node = walker.nextNode() as Text | null;\n }\n if (last) return { node: last, offset: last.data.length };\n return { node: parent, offset: 0 };\n}\n\n/**\n * Inverse of `charOffsetToDomPosition`. Walks text nodes in document order;\n * sums the lengths of every text node preceding `node` and adds `offset`.\n *\n * When `node` is an element the DOM offset indexes *child nodes*, not\n * characters, so it can't simply be added — `(overlay, 0)` is the start of\n * the text and `(overlay, childNodes.length)` its end. That shape is not\n * exotic: `Range.selectNodeContents`, which `useTextEdit` uses for its\n * select-all caret, produces exactly it. So resolve an element position by\n * counting the text that precedes the boundary point. A text node can never\n * *contain* that point (the container isn't one), so each is wholly before\n * or wholly after it.\n */\nexport function domPositionToCharOffset(\n parent: HTMLElement,\n node: Node,\n offset: number,\n): number {\n let total = 0;\n const walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);\n let cur = walker.nextNode() as Text | null;\n if (node.nodeType === Node.TEXT_NODE) {\n while (cur) {\n if (cur === node) return total + offset;\n total += cur.data.length;\n cur = walker.nextNode() as Text | null;\n }\n return total;\n }\n const point = document.createRange();\n point.setStart(node, Math.max(0, Math.min(offset, node.childNodes.length)));\n point.collapse(true);\n while (cur) {\n if (point.comparePoint(cur, 0) < 0) total += cur.data.length;\n cur = walker.nextNode() as Text | null;\n }\n return total;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"domRuns.js","names":["solidColor","p","FILL_ATTR","TRANSFORMS","EMPTY_STYLE","parseLetterSpacing","n","raw","unit","isSmallCapsPiece","el","cssFontVariant","v","styleStateFromElement","parent","next","tag","fw","ownWeight","fs","td","size","va","rawShift","shift","tt","fv","family","fillJson","ls","px","styleEquals","a","b","toRun","run","style","BREAK_ATTR","restoreBreak","text","code","appendOverlayText","from","i","c","isHardLineBreak","span","domToRuns","fragments","visit","node","restored","BLOCK_TAGS","nextStyle","child","runs","j","merged","f","SMALL_CAPS_SCALE_PROPERTY","smallCapsPieces","shown","transformRunTexts","transform","caps","smallCapsText","small","s","out","at","ch","last","writeSmallCaps","piece","SMALL_CAPS_SCALE","runsToDom","DEFAULT_TEXT_STYLE","base","scale","smallCapsScaleFor","numericWeight","cssFontFamily","color","decorations","sourceText","piecesMatch","want","have","normalizeSmallCaps","changed","variant","t","charOffsetToDomPosition","domPositionToCharOffset"],"sources":["../../../src/features/text/domRuns.ts"],"sourcesContent":["/**\n * Pure DOM ↔ `StyledRun[]` serializers for the contenteditable overlay\n * used by `useTextEdit`. The overlay's children are a flat sequence of\n * `<span data-run>` elements, each carrying one run's text and inline\n * styles. Newlines inside a run are literal `\\n` characters; the overlay\n * has `white-space: pre-wrap` so they render as line breaks. The browser\n * breaks at no other UAX #14 hard break, so each of those is written as a\n * `<span data-break>` holding a `\\n` in its place — one character for one,\n * which keeps DOM offsets equal to source offsets.\n *\n * A small-caps run is the one exception to one text node per span: its\n * lowercase letters sit in `<span data-small-caps>` pieces, uppercased and\n * sized at the scale the canvas draws them with, since a browser's own\n * synthesis uses a fixed factor of its own. The pieces are presentation\n * only — `domToRuns` reads straight through them.\n */\n\nimport type { FillStyle } from '@weasel-js/paint';\nimport {\n DEFAULT_TEXT_STYLE, isHardLineBreak, numericWeight, smallCapsScaleFor, smallCapsText, transformRunTexts,\n SMALL_CAPS_SCALE,\n type FontVariantCaps, type ResolvedTextStyle, type StyledRun, type TextTransform,\n} from '@weasel-js/text';\nimport { cssFontFamily } from '@weasel-js/font';\nimport { BLOCK_TAGS } from './domOffsets';\n\nexport { charOffsetToDomPosition, domPositionToCharOffset } from './domOffsets';\n\nfunction solidColor(p: FillStyle | null | undefined): string | null {\n if (!p) return null;\n if ('color' in p) return p.color;\n return null;\n}\n\n/** Holds, as JSON, a run fill its span's CSS color cannot say: a gradient, a\n * pattern, or `null` for no fill. */\nconst FILL_ATTR = 'data-fill';\n\ninterface StyleState {\n bold: boolean;\n italic: boolean;\n underline: boolean;\n strikethrough: boolean;\n overline: boolean;\n fontWeight?: number;\n fontSize?: number;\n fontFamily?: string;\n color?: string;\n /** `FILL_ATTR`'s value, which wins over `color`. */\n fillJson?: string;\n letterSpacing?: number;\n script?: 'super' | 'sub';\n baselineShift?: number;\n fontScale?: number;\n textTransform?: TextTransform;\n fontVariantCaps?: FontVariantCaps;\n}\n\nconst TRANSFORMS: ReadonlySet<string> = new Set(['none', 'uppercase', 'lowercase', 'capitalize']);\n\nconst EMPTY_STYLE: StyleState = {\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n overline: false,\n};\n\n/**\n * Parse a CSS `letter-spacing` value into world units. `runsToDom` only ever\n * writes `px`, but paste is a live path into the contenteditable and CSSOM\n * keeps whatever unit the pasted markup used. Any other unit is still\n * numerically coerced — `parseFloat` reads the leading digits — but flagged,\n * since e.g. `0.1em` silently becomes `0.1` world units, off by a factor of\n * the font size. Mirrors `parseLetterSpacing` in the SVG reader.\n */\nfunction parseLetterSpacing(raw: string): number | undefined {\n const n = parseFloat(raw);\n if (!Number.isFinite(n)) return undefined;\n const unit = /^-?[\\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();\n if (unit && unit !== 'px') {\n console.warn(\n `weasel domRuns: letter-spacing \"${raw}\" uses unit \"${unit}\", which is not converted; treated as ${n} world units`,\n );\n }\n return n;\n}\n\n/** A small-caps piece is presentation `runsToDom` split a run into, not styling. */\nconst isSmallCapsPiece = (el: Element): boolean => el.hasAttribute('data-small-caps');\n\n/** The element's own `font-variant`, from the shorthand or the caps longhand\n * pasted markup may carry alone. */\nfunction cssFontVariant(el: HTMLElement): FontVariantCaps | undefined {\n const v = el.style.fontVariant || el.style.getPropertyValue('font-variant-caps');\n return v === 'small-caps' || v === 'normal' ? v : undefined;\n}\n\nfunction styleStateFromElement(el: Element, parent: StyleState): StyleState {\n if (isSmallCapsPiece(el)) return parent;\n const next: StyleState = { ...parent };\n const tag = el.tagName;\n if (tag === 'B' || tag === 'STRONG') {\n next.bold = true;\n next.fontWeight = undefined;\n }\n if (tag === 'I' || tag === 'EM') next.italic = true;\n // `runsToDom` never emits these, but a browser contenteditable can (Cmd+U\n // runs the native `underline` command, which produces `<u>` in Chrome), so\n // read them for the same reason `<b>`/`<i>` are read.\n if (tag === 'U') next.underline = true;\n if (tag === 'S' || tag === 'STRIKE' || tag === 'DEL') next.strikethrough = true;\n if (tag === 'SUP') next.script = 'super';\n if (tag === 'SUB') next.script = 'sub';\n if (el instanceof HTMLElement) {\n // 700 and 400 are the bold flag on and off, which is what pasted markup\n // means by them. A run's own weight is written with an attribute beside\n // the CSS, so a stored 400 or 700 comes back as the weight it was.\n const fw = el.style.fontWeight;\n const ownWeight = Number(el.getAttribute('data-font-weight') ?? fw);\n if (el.hasAttribute('data-font-weight') || !['', '700', 'bold', '400', 'normal'].includes(fw)) {\n if (Number.isFinite(ownWeight) && ownWeight > 0) {\n next.fontWeight = ownWeight;\n next.bold = false;\n }\n } else if (fw !== '') {\n next.bold = fw === '700' || fw === 'bold';\n next.fontWeight = undefined;\n }\n const fs = el.style.fontStyle;\n if (fs === 'italic') next.italic = true;\n if (fs === 'normal') next.italic = false;\n // Decoration accumulates down the tree — unlike `font-weight`/`font-style`\n // above, `text-decoration` does not inherit, it *propagates*, and a\n // descendant cannot cancel what an ancestor drew. So OR the flags in\n // rather than overwriting: `<u><span style=\"text-decoration: line-through\">`\n // renders both lines, and that is a reachable shape here, since `runsToDom`\n // emits the span and an unintercepted Cmd+U supplies the `<u>`. An explicit\n // `none` contributes no tokens and cancels nothing.\n //\n // A declaration block carrying only the longhand serializes the shorthand\n // as `''` (the shorthand needs the full set), and browsers and pasted HTML\n // both produce that, so fall back to `text-decoration-line`.\n const td = el.style.textDecoration || el.style.textDecorationLine;\n if (td && !td.includes('none')) {\n next.underline ||= td.includes('underline');\n next.strikethrough ||= td.includes('line-through');\n next.overline ||= td.includes('overline');\n }\n // A percentage is `fontScale`'s own spelling — CSS resolves it against the\n // parent, which is exactly what the field means. Any other unit is an\n // absolute size.\n if (el.style.fontSize) {\n const size = parseFloat(el.style.fontSize);\n if (Number.isFinite(size)) {\n if (el.style.fontSize.trim().endsWith('%')) next.fontScale = size / 100;\n else next.fontSize = size;\n }\n }\n const va = el.style.verticalAlign;\n if (va === 'super' || va === 'sub') next.script = va === 'super' ? 'super' : 'sub';\n if (va === 'baseline') next.script = undefined;\n const rawShift = el.getAttribute('data-baseline-shift');\n if (rawShift != null) {\n const shift = parseFloat(rawShift);\n if (Number.isFinite(shift)) next.baselineShift = shift;\n }\n const tt = el.style.textTransform;\n if (TRANSFORMS.has(tt)) next.textTransform = tt as TextTransform;\n const fv = cssFontVariant(el);\n if (fv) next.fontVariantCaps = fv;\n // The attribute holds the run's family; the CSS holds the face it maps to.\n const family = el.getAttribute('data-font-family') ?? el.style.fontFamily;\n if (family) next.fontFamily = family;\n const fillJson = el.getAttribute(FILL_ATTR);\n if (fillJson != null) {\n next.fillJson = fillJson;\n next.color = undefined;\n } else if (el.style.color) {\n next.color = el.style.color;\n next.fillJson = undefined;\n }\n const ls = el.style.letterSpacing;\n if (ls === 'normal') {\n next.letterSpacing = undefined;\n } else if (ls) {\n const px = parseLetterSpacing(ls);\n if (px != null) next.letterSpacing = px;\n }\n }\n return next;\n}\n\nfunction styleEquals(a: StyleState, b: StyleState): boolean {\n return (\n a.bold === b.bold &&\n a.italic === b.italic &&\n a.underline === b.underline &&\n a.strikethrough === b.strikethrough &&\n a.overline === b.overline &&\n a.fontWeight === b.fontWeight &&\n a.fontSize === b.fontSize &&\n a.fontFamily === b.fontFamily &&\n a.color === b.color &&\n a.fillJson === b.fillJson &&\n a.letterSpacing === b.letterSpacing &&\n a.script === b.script &&\n a.baselineShift === b.baselineShift &&\n a.fontScale === b.fontScale &&\n a.textTransform === b.textTransform &&\n a.fontVariantCaps === b.fontVariantCaps\n );\n}\n\nfunction toRun(text: string, style: StyleState): StyledRun {\n const run: StyledRun = { text };\n if (style.bold) run.bold = true;\n if (style.italic) run.italic = true;\n if (style.fontWeight != null) run.fontWeight = style.fontWeight;\n if (style.fontSize != null) run.fontSize = style.fontSize;\n if (style.fontFamily != null) run.fontFamily = style.fontFamily;\n if (style.fillJson !== undefined) run.fill = JSON.parse(style.fillJson) as FillStyle | null;\n else if (style.color != null) run.fill = { fill: 'solid', color: style.color };\n // `letterSpacing: 0` is a meaningful override (it cancels node-level\n // tracking for this run), so test for presence, not truthiness.\n if (style.letterSpacing != null) run.letterSpacing = style.letterSpacing;\n // Run-level flags are additive over the node style — a run cannot un-set\n // one — so an undecorated run gets an absent key, never `false`.\n if (style.underline) run.underline = true;\n if (style.strikethrough) run.strikethrough = true;\n if (style.overline) run.overline = true;\n if (style.script != null) run.script = style.script;\n // Both are meaningful at values that test falsy — a 0 shift cancels an\n // inherited script's rise — so test for presence, as `letterSpacing` does.\n if (style.baselineShift != null) run.baselineShift = style.baselineShift;\n if (style.fontScale != null) run.fontScale = style.fontScale;\n if (style.textTransform != null) run.textTransform = style.textTransform;\n if (style.fontVariantCaps != null) run.fontVariantCaps = style.fontVariantCaps;\n return run;\n}\n\nconst BREAK_ATTR = 'data-break';\n\n/** A break stand-in's source text: its hard break, and anything typed into it after. */\nfunction restoreBreak(el: Element): string {\n const text = el.textContent ?? '';\n const code = parseInt(el.getAttribute(BREAK_ATTR) ?? '', 16);\n return text.startsWith('\\n') && Number.isFinite(code) ? String.fromCharCode(code) + text.slice(1) : text;\n}\n\n/**\n * Append `text` to `parent` as the overlay sets it: a hard break the browser\n * would not break at becomes a `<span data-break>` holding a `\\n`, carrying\n * the original's code as hex for `domToRuns` to restore. A CR that opens a\n * CRLF is left as it is — the LF breaks, and the CR draws nothing.\n */\nexport function appendOverlayText(parent: Node, text: string): void {\n let from = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i);\n if (c === 10 || !isHardLineBreak(c) || (c === 13 && text.charCodeAt(i + 1) === 10)) continue;\n if (i > from) parent.appendChild(document.createTextNode(text.slice(from, i)));\n const span = document.createElement('span');\n span.setAttribute(BREAK_ATTR, c.toString(16));\n span.textContent = '\\n';\n parent.appendChild(span);\n from = i + 1;\n }\n if (from < text.length) parent.appendChild(document.createTextNode(text.slice(from)));\n}\n\n/** Walk an overlay tree and emit a coalesced `StyledRun[]`. */\nexport function domToRuns(parent: HTMLElement): StyledRun[] {\n const fragments: Array<{ text: string; style: StyleState }> = [];\n\n function visit(node: Node, style: StyleState): void {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = (node as Text).data;\n if (text.length > 0) fragments.push({ text, style });\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as Element;\n if (el.tagName === 'BR') {\n fragments.push({ text: '\\n', style });\n return;\n }\n if (el.hasAttribute(BREAK_ATTR)) {\n const restored = restoreBreak(el);\n if (restored.length > 0) fragments.push({ text: restored, style });\n return;\n }\n if (BLOCK_TAGS.has(el.tagName) && fragments.length > 0) {\n fragments.push({ text: '\\n', style });\n }\n const nextStyle = styleStateFromElement(el, style);\n for (const child of Array.from(el.childNodes)) {\n visit(child, nextStyle);\n }\n }\n\n for (const child of Array.from(parent.childNodes)) {\n visit(child, EMPTY_STYLE);\n }\n\n // Coalesce adjacent fragments with identical style.\n const runs: StyledRun[] = [];\n let i = 0;\n while (i < fragments.length) {\n let j = i + 1;\n while (j < fragments.length && styleEquals(fragments[i].style, fragments[j].style)) {\n j++;\n }\n const merged = fragments.slice(i, j).map((f) => f.text).join('');\n runs.push(toRun(merged, fragments[i].style));\n i = j;\n }\n return runs;\n}\n\n/** What a run inherits from its node, for the overlay's small caps: which\n * runs have it, and the face and transform that decide their pieces. */\nexport type OverlayRunBase = Pick<\n ResolvedTextStyle, 'fontFamily' | 'fontWeight' | 'fontStyle' | 'textTransform' | 'fontVariantCaps'\n>;\n\n/** The custom property carrying a small-caps run's scale down to its pieces. */\nexport const SMALL_CAPS_SCALE_PROPERTY = '--weasel-small-caps';\n\n/** A run's source text cut where small caps changes size, per `smallCapsText`\n * over the text as `transform` draws it. */\nfunction smallCapsPieces(text: string, transform: TextTransform): Array<{ text: string; small: boolean }> {\n const [shown] = transformRunTexts([text], [transform]);\n const caps = smallCapsText(shown.text, shown.srcMap);\n const small = new Array<boolean>(text.length).fill(false);\n caps.small?.forEach((s, i) => {\n if (s) small[caps.srcMap ? caps.srcMap.starts[i] : i] = true;\n });\n const out: Array<{ text: string; small: boolean }> = [];\n let at = 0;\n for (const ch of text) {\n const last = out[out.length - 1];\n if (last && last.small === small[at]) last.text += ch;\n else out.push({ text: ch, small: small[at] });\n at += ch.length;\n }\n return out;\n}\n\n/** Replace `span`'s children with `text`, its small-caps letters in pieces. */\nfunction writeSmallCaps(span: HTMLElement, text: string, transform: TextTransform): void {\n span.replaceChildren();\n for (const p of smallCapsPieces(text, transform)) {\n if (!p.small) { appendOverlayText(span, p.text); continue; }\n const piece = document.createElement('span');\n piece.setAttribute('data-small-caps', '');\n piece.style.fontSize = `calc(var(${SMALL_CAPS_SCALE_PROPERTY}, ${SMALL_CAPS_SCALE}) * 1em)`;\n piece.style.textTransform = 'uppercase';\n piece.textContent = p.text;\n span.appendChild(piece);\n }\n}\n\n/**\n * Build a flat sequence of `<span data-run>` children from `runs`, replacing\n * any existing children of `parent`. `base` is the node's style, which a run\n * inheriting small caps needs to be split by; without it the kit defaults\n * stand in.\n */\nexport function runsToDom(\n runs: readonly StyledRun[],\n parent: HTMLElement,\n base: OverlayRunBase = DEFAULT_TEXT_STYLE,\n): void {\n parent.replaceChildren();\n for (const run of runs) {\n const span = document.createElement('span');\n span.setAttribute('data-run', '');\n if ((run.fontVariantCaps ?? base.fontVariantCaps) === 'small-caps') {\n const scale = smallCapsScaleFor(\n run.fontFamily ?? base.fontFamily,\n run.fontWeight ?? (run.bold ? 700 : numericWeight(base.fontWeight)),\n run.italic ? 'italic' : base.fontStyle,\n );\n span.style.setProperty(SMALL_CAPS_SCALE_PROPERTY, String(scale));\n writeSmallCaps(span, run.text, run.textTransform ?? base.textTransform);\n } else {\n appendOverlayText(span, run.text);\n }\n // CSS as well as the pieces: it is what pasted markup says, and it gives a\n // letter typed outside a piece a small cap until the next normalize.\n if (run.fontVariantCaps != null) span.style.fontVariant = run.fontVariantCaps;\n if (run.fontWeight != null) {\n span.style.fontWeight = String(run.fontWeight);\n span.setAttribute('data-font-weight', String(run.fontWeight));\n } else if (run.bold) span.style.fontWeight = '700';\n if (run.italic) span.style.fontStyle = 'italic';\n // An absolute size wins over a relative one, as it does in `resolveRuns`.\n if (run.fontSize != null) span.style.fontSize = `${run.fontSize}px`;\n else if (run.fontScale != null) span.style.fontSize = `${run.fontScale * 100}%`;\n if (run.fontFamily != null) {\n span.style.fontFamily = cssFontFamily(run.fontFamily, {\n weight: run.fontWeight ?? (run.bold ? 700 : undefined),\n style: run.italic ? 'italic' : undefined,\n });\n span.setAttribute('data-font-family', run.fontFamily);\n }\n const color = solidColor(run.fill);\n if (color != null) span.style.color = color;\n else if (run.fill !== undefined) span.setAttribute(FILL_ATTR, JSON.stringify(run.fill));\n // Black, as an unfilled node edits in: no CSS color leaves it legible.\n if (run.fill === null) span.style.color = '#000';\n // Decoration goes on the span as an inline style rather than `<u>`/`<s>`\n // wrappers: one representation for `domToRuns` to unwrap, and one element\n // per run so the caret-offset walkers stay flat.\n const decorations: string[] = [];\n if (run.underline) decorations.push('underline');\n if (run.strikethrough) decorations.push('line-through');\n if (run.overline) decorations.push('overline');\n if (decorations.length > 0) span.style.textDecoration = decorations.join(' ');\n if (run.script != null) span.style.verticalAlign = run.script;\n // The one run field with no CSS spelling: `vertical-align` takes a length\n // or a percentage of the *line height*, and this is a fraction of the\n // parent's font size. An attribute keeps the round-trip exact; the canvas\n // underneath is what actually draws the run at its offset.\n if (run.baselineShift != null) {\n span.setAttribute('data-baseline-shift', String(run.baselineShift));\n }\n // World units — the overlay's own node-level tracking is screen-scaled,\n // but a run override is stored as-is so the round-trip is lossless. CSS\n // `letter-spacing` is inherited and a child declaration *replaces* the\n // inherited value, so this composes with the overlay's rather than adding.\n if (run.letterSpacing != null) span.style.letterSpacing = `${run.letterSpacing}px`;\n // CSS draws the transform, so the span's text stays the source and the\n // caret-offset walkers below need no map.\n if (run.textTransform != null) span.style.textTransform = run.textTransform;\n parent.appendChild(span);\n }\n}\n\n/** `el`'s text as `domToRuns` reads it: each break stand-in restored. */\nfunction sourceText(el: Element): string {\n let out = '';\n for (const n of el.childNodes) {\n if (n.nodeType === Node.TEXT_NODE) out += (n as Text).data;\n else if (n instanceof Element) out += n.hasAttribute(BREAK_ATTR) ? restoreBreak(n) : sourceText(n);\n }\n return out;\n}\n\n/** Whether `span`'s children are the pieces `writeSmallCaps` would write. */\nfunction piecesMatch(span: HTMLElement, transform: TextTransform): boolean {\n const want = smallCapsPieces(sourceText(span), transform);\n const have: Array<{ text: string; small: boolean }> = [];\n for (const n of span.childNodes) {\n let small: boolean;\n let text: string;\n if (n.nodeType === Node.TEXT_NODE) { small = false; text = (n as Text).data; }\n else if (n instanceof HTMLElement && n.hasAttribute(BREAK_ATTR)) { small = false; text = restoreBreak(n); }\n else if (n instanceof HTMLElement && isSmallCapsPiece(n) && n.children.length === 0) { small = true; text = n.textContent ?? ''; }\n else return false;\n if (text.length === 0) continue;\n const last = have[have.length - 1];\n if (last && last.small === small) last.text += text;\n else have.push({ text, small });\n }\n return want.length === have.length\n && want.every((p, i) => p.text === have[i].text && p.small === have[i].small);\n}\n\n/**\n * Put every run span's small-caps pieces back where its text now needs them —\n * typing lands a letter in whichever piece holds the caret, not the one its\n * case belongs in. Returns whether anything was rewritten, which moves the\n * DOM selection: the caller restores it by character offset.\n */\nexport function normalizeSmallCaps(parent: HTMLElement): boolean {\n let changed = false;\n const visit = (el: HTMLElement, variant: FontVariantCaps, transform: TextTransform): void => {\n const v = cssFontVariant(el) ?? variant;\n const tt = el.style.textTransform;\n const t = TRANSFORMS.has(tt) ? (tt as TextTransform) : transform;\n if (el.hasAttribute('data-run')) {\n if (v === 'small-caps') {\n if (!piecesMatch(el, t)) { writeSmallCaps(el, sourceText(el), t); changed = true; }\n } else if (el.querySelector('[data-small-caps]')) {\n const text = sourceText(el);\n el.replaceChildren();\n appendOverlayText(el, text);\n changed = true;\n }\n return;\n }\n for (const child of el.children) if (child instanceof HTMLElement) visit(child, v, t);\n };\n visit(parent, 'normal', 'none');\n return changed;\n}\n"],"mappings":"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"}
@@ -4,12 +4,15 @@ function t(e) {
4
4
  let t = [];
5
5
  for (let n of e) {
6
6
  let e = t[t.length - 1];
7
- if (e && e.srcIndex === n.srcIndex && e.srcEnd === n.srcEnd) {
7
+ if (e && (!e.clusterEnd || e.srcIndex === n.srcIndex && e.srcEnd === n.srcEnd)) {
8
8
  let r = Math.min(e.x, n.x), i = Math.max(e.x + e.advance, n.x + n.advance);
9
9
  t[t.length - 1] = {
10
10
  ...e,
11
11
  x: r,
12
- advance: i - r
12
+ advance: i - r,
13
+ srcIndex: Math.min(e.srcIndex, n.srcIndex),
14
+ srcEnd: Math.max(e.srcEnd, n.srcEnd),
15
+ clusterEnd: n.clusterEnd
13
16
  };
14
17
  } else t.push(n);
15
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hitTest.js","names":["sourceClusters","out","c","cells","prev","x0","x1","pointInTextPose","p","opts","x","pose","y","caretIndexAt","dx","dy","layoutTextPose","lines","laid","last","line","candidate","visual","i","a","b","trailing"],"sources":["../../../src/features/text/hitTest.ts"],"sourcesContent":["/**\n * Hit-testing for text nodes.\n *\n * `pointInTextPose` does a coarse pose-rect test — suitable for click-to-edit\n * where the pose rect is the authoritative bounding box (selection outline,\n * drag target).\n *\n * `caretIndexAt` does the finer test: given a world-space (x, y) inside the\n * pose, returns the corresponding character offset so the consumer can place\n * the caret on click. It reads the caret stops off `layoutTextPose`, the\n * layout the painters draw and `textLineBoxes` picks against, so the caret\n * cannot land on a different line — or between different glyphs — than the\n * one under the pointer.\n */\n\nimport { layoutTextPose } from '@weasel-js/text';\nimport type { LaidOutCell, TextPose } from '@weasel-js/text';\n\n/**\n * Merge neighboring cells drawn from one source character — the two `S`s an\n * uppercased `ß` becomes — into one cell spanning both, so no caret stop falls\n * between them.\n */\nfunction sourceClusters(cells: readonly LaidOutCell[]): LaidOutCell[] {\n const out: LaidOutCell[] = [];\n for (const c of cells) {\n const prev = out[out.length - 1];\n if (prev && prev.srcIndex === c.srcIndex && prev.srcEnd === c.srcEnd) {\n const x0 = Math.min(prev.x, c.x);\n const x1 = Math.max(prev.x + prev.advance, c.x + c.advance);\n out[out.length - 1] = { ...prev, x: x0, advance: x1 - x0 };\n } else {\n out.push(c);\n }\n }\n return out;\n}\n\n/** Options for `pointInTextPose`. */\nexport interface PointInTextPoseOpts {\n /** Extra padding (world units) added to the rect on all sides. Default 0. */\n padding?: number;\n}\n\n/** Coarse pose-rect hit-test for a text node — suitable for click-to-edit dispatch. */\nexport function pointInTextPose(\n x: number,\n y: number,\n pose: TextPose,\n opts: PointInTextPoseOpts = {},\n): boolean {\n const p = opts.padding ?? 0;\n return (\n x >= pose.x - p &&\n x <= pose.x + pose.width + p &&\n y >= pose.y - p &&\n y <= pose.y + pose.height + p\n );\n}\n\n/**\n * Map a world-space point inside `pose` to a character offset into the pose's\n * text (0..length). Clicks above the first line clamp to 0; clicks below the\n * last line clamp to the end. Within a line, the caret lands between two\n * glyphs at whichever side of the advance cell's midpoint `x` falls on — the\n * standard \"snap caret to nearest character boundary\" rule.\n *\n * Honors `pose.runs` (a mixed-size line snaps on the cells each run actually\n * produced) and `pose.verticalAlign`. The offset is into the runs'\n * concatenated text, which `TextPose` requires to equal `pose.text`.\n */\nexport function caretIndexAt(x: number, y: number, pose: TextPose): number {\n const { laid, x: dx, y: dy } = layoutTextPose(pose);\n const lines = laid.lines;\n if (lines.length === 0) return 0;\n\n const last = lines[lines.length - 1];\n if (y < dy + lines[0].y0) return lines[0].cells[0]?.srcIndex ?? lines[0].srcEnd;\n if (y >= dy + last.y1) return last.srcEnd;\n\n let line = last;\n for (const candidate of lines) {\n if (y < dy + candidate.y1) { line = candidate; break; }\n }\n\n // Cells are in logical order and their x values need not ascend, so the\n // sweep has to be in visual order and each cell's own extent is what it is\n // tested against — the next cell along is not its right edge.\n if (line.cells.length === 0) return line.srcEnd;\n const cells = sourceClusters(line.cells);\n const visual = cells.map((_, i) => i).sort((a, b) => cells[a].x - cells[b].x);\n\n for (const i of visual) {\n const c = cells[i];\n if (x >= dx + c.x + c.advance / 2) continue;\n // A right-to-left cell reads the other way, so its visually-leading half\n // is the character's logical end.\n return c.level % 2 === 1 ? c.srcEnd : c.srcIndex;\n }\n const trailing = cells[visual[visual.length - 1]];\n return trailing.level % 2 === 1 ? trailing.srcIndex : trailing.srcEnd;\n}\n"],"mappings":";;AAuBA,SAASA,EAAe,GAA8C;CACpE,IAAMC,IAAqB,CAAC;CAC5B,KAAK,IAAMC,KAAKC,GAAO;EACrB,IAAMC,IAAOH,EAAIA,EAAI,SAAS;EAC9B,IAAIG,KAAQA,EAAK,aAAaF,EAAE,YAAYE,EAAK,WAAWF,EAAE,QAAQ;GACpE,IAAMG,IAAK,KAAK,IAAID,EAAK,GAAGF,EAAE,CAAC,GACzBI,IAAK,KAAK,IAAIF,EAAK,IAAIA,EAAK,SAASF,EAAE,IAAIA,EAAE,OAAO;GAC1D,EAAID,EAAI,SAAS,KAAK;IAAE,GAAGG;IAAM,GAAGC;IAAI,SAASC,IAAKD;GAAG;EAC3D,OACE,EAAI,KAAKH,CAAC;CAEd;CACA,OAAOD;AACT;AASA,SAAgBM,EACd,GACA,GACA,GACA,IAA4B,CAAC,GACpB;CACT,IAAMC,IAAIC,EAAK,WAAW;CAC1B,OACEC,KAAKC,EAAK,IAAIH,KACdE,KAAKC,EAAK,IAAIA,EAAK,QAAQH,KAC3BI,KAAKD,EAAK,IAAIH,KACdI,KAAKD,EAAK,IAAIA,EAAK,SAASH;AAEhC;AAaA,SAAgBK,EAAa,GAAW,GAAW,GAAwB;CACzE,IAAM,EAAE,SAAM,GAAGC,GAAI,GAAGC,MAAOC,EAAeL,CAAI,GAC5CM,IAAQC,EAAK;CACnB,IAAID,EAAM,WAAW,GAAG,OAAO;CAE/B,IAAME,IAAOF,EAAMA,EAAM,SAAS;CAClC,IAAIL,IAAIG,IAAKE,EAAM,EAAE,CAAC,IAAI,OAAOA,EAAM,EAAE,CAAC,MAAM,EAAE,EAAE,YAAYA,EAAM,EAAE,CAAC;CACzE,IAAIL,KAAKG,IAAKI,EAAK,IAAI,OAAOA,EAAK;CAEnC,IAAIC,IAAOD;CACX,KAAK,IAAME,KAAaJ,GACtB,IAAIL,IAAIG,IAAKM,EAAU,IAAI;EAAE,IAAOA;EAAW;CAAO;CAMxD,IAAID,EAAK,MAAM,WAAW,GAAG,OAAOA,EAAK;CACzC,IAAMjB,IAAQH,EAAeoB,EAAK,KAAK,GACjCE,IAASnB,EAAM,KAAK,GAAG,MAAMoB,CAAC,CAAC,CAAC,MAAM,GAAG,MAAMpB,EAAMqB,EAAE,CAAC,IAAIrB,EAAMsB,EAAE,CAAC,CAAC;CAE5E,KAAK,IAAMF,KAAKD,GAAQ;EACtB,IAAMpB,IAAIC,EAAMoB;EAChB,IAAI,OAAKT,IAAKZ,EAAE,IAAIA,EAAE,UAAU,IAGhC,OAAOA,EAAE,QAAQ,KAAM,IAAIA,EAAE,SAASA,EAAE;CAC1C;CACA,IAAMwB,IAAWvB,EAAMmB,EAAOA,EAAO,SAAS;CAC9C,OAAOI,EAAS,QAAQ,KAAM,IAAIA,EAAS,WAAWA,EAAS;AACjE"}
1
+ {"version":3,"file":"hitTest.js","names":["caretCells","out","c","cells","prev","x0","x1","pointInTextPose","p","opts","x","pose","y","caretIndexAt","dx","dy","layoutTextPose","lines","laid","last","line","candidate","visual","i","a","b","trailing"],"sources":["../../../src/features/text/hitTest.ts"],"sourcesContent":["/**\n * Hit-testing for text nodes.\n *\n * `pointInTextPose` does a coarse pose-rect test — suitable for click-to-edit\n * where the pose rect is the authoritative bounding box (selection outline,\n * drag target).\n *\n * `caretIndexAt` does the finer test: given a world-space (x, y) inside the\n * pose, returns the corresponding character offset so the consumer can place\n * the caret on click. It reads the caret stops off `layoutTextPose`, the\n * layout the painters draw and `textLineBoxes` picks against, so the caret\n * cannot land on a different line — or between different glyphs — than the\n * one under the pointer.\n */\n\nimport { layoutTextPose } from '@weasel-js/text';\nimport type { LaidOutCell, TextPose } from '@weasel-js/text';\n\n/**\n * Merge neighboring cells a caret cannot stop between into one cell spanning\n * them: the cells of one grapheme cluster (a letter and its combining mark,\n * the parts of a joined emoji), and cells drawn from one source character\n * (the two `S`s an uppercased `ß` becomes).\n */\nfunction caretCells(cells: readonly LaidOutCell[]): LaidOutCell[] {\n const out: LaidOutCell[] = [];\n for (const c of cells) {\n const prev = out[out.length - 1];\n if (prev && (!prev.clusterEnd || (prev.srcIndex === c.srcIndex && prev.srcEnd === c.srcEnd))) {\n const x0 = Math.min(prev.x, c.x);\n const x1 = Math.max(prev.x + prev.advance, c.x + c.advance);\n out[out.length - 1] = {\n ...prev, x: x0, advance: x1 - x0,\n srcIndex: Math.min(prev.srcIndex, c.srcIndex), srcEnd: Math.max(prev.srcEnd, c.srcEnd),\n clusterEnd: c.clusterEnd,\n };\n } else {\n out.push(c);\n }\n }\n return out;\n}\n\n/** Options for `pointInTextPose`. */\nexport interface PointInTextPoseOpts {\n /** Extra padding (world units) added to the rect on all sides. Default 0. */\n padding?: number;\n}\n\n/** Coarse pose-rect hit-test for a text node — suitable for click-to-edit dispatch. */\nexport function pointInTextPose(\n x: number,\n y: number,\n pose: TextPose,\n opts: PointInTextPoseOpts = {},\n): boolean {\n const p = opts.padding ?? 0;\n return (\n x >= pose.x - p &&\n x <= pose.x + pose.width + p &&\n y >= pose.y - p &&\n y <= pose.y + pose.height + p\n );\n}\n\n/**\n * Map a world-space point inside `pose` to a character offset into the pose's\n * text (0..length). Clicks above the first line clamp to 0; clicks below the\n * last line clamp to the end. Within a line, the caret lands between two\n * glyphs at whichever side of the advance cell's midpoint `x` falls on — the\n * standard \"snap caret to nearest character boundary\" rule.\n *\n * Honors `pose.runs` (a mixed-size line snaps on the cells each run actually\n * produced) and `pose.verticalAlign`. The offset is into the runs'\n * concatenated text, which `TextPose` requires to equal `pose.text`.\n */\nexport function caretIndexAt(x: number, y: number, pose: TextPose): number {\n const { laid, x: dx, y: dy } = layoutTextPose(pose);\n const lines = laid.lines;\n if (lines.length === 0) return 0;\n\n const last = lines[lines.length - 1];\n if (y < dy + lines[0].y0) return lines[0].cells[0]?.srcIndex ?? lines[0].srcEnd;\n if (y >= dy + last.y1) return last.srcEnd;\n\n let line = last;\n for (const candidate of lines) {\n if (y < dy + candidate.y1) { line = candidate; break; }\n }\n\n // Cells are in logical order and their x values need not ascend, so the\n // sweep has to be in visual order and each cell's own extent is what it is\n // tested against — the next cell along is not its right edge.\n if (line.cells.length === 0) return line.srcEnd;\n const cells = caretCells(line.cells);\n const visual = cells.map((_, i) => i).sort((a, b) => cells[a].x - cells[b].x);\n\n for (const i of visual) {\n const c = cells[i];\n if (x >= dx + c.x + c.advance / 2) continue;\n // A right-to-left cell reads the other way, so its visually-leading half\n // is the character's logical end.\n return c.level % 2 === 1 ? c.srcEnd : c.srcIndex;\n }\n const trailing = cells[visual[visual.length - 1]];\n return trailing.level % 2 === 1 ? trailing.srcIndex : trailing.srcEnd;\n}\n"],"mappings":";;AAwBA,SAASA,EAAW,GAA8C;CAChE,IAAMC,IAAqB,CAAC;CAC5B,KAAK,IAAMC,KAAKC,GAAO;EACrB,IAAMC,IAAOH,EAAIA,EAAI,SAAS;EAC9B,IAAIG,MAAS,CAACA,EAAK,cAAeA,EAAK,aAAaF,EAAE,YAAYE,EAAK,WAAWF,EAAE,SAAU;GAC5F,IAAMG,IAAK,KAAK,IAAID,EAAK,GAAGF,EAAE,CAAC,GACzBI,IAAK,KAAK,IAAIF,EAAK,IAAIA,EAAK,SAASF,EAAE,IAAIA,EAAE,OAAO;GAC1D,EAAID,EAAI,SAAS,KAAK;IACpB,GAAGG;IAAM,GAAGC;IAAI,SAASC,IAAKD;IAC9B,UAAU,KAAK,IAAID,EAAK,UAAUF,EAAE,QAAQ;IAAG,QAAQ,KAAK,IAAIE,EAAK,QAAQF,EAAE,MAAM;IACrF,YAAYA,EAAE;GAChB;EACF,OACE,EAAI,KAAKA,CAAC;CAEd;CACA,OAAOD;AACT;AASA,SAAgBM,EACd,GACA,GACA,GACA,IAA4B,CAAC,GACpB;CACT,IAAMC,IAAIC,EAAK,WAAW;CAC1B,OACEC,KAAKC,EAAK,IAAIH,KACdE,KAAKC,EAAK,IAAIA,EAAK,QAAQH,KAC3BI,KAAKD,EAAK,IAAIH,KACdI,KAAKD,EAAK,IAAIA,EAAK,SAASH;AAEhC;AAaA,SAAgBK,EAAa,GAAW,GAAW,GAAwB;CACzE,IAAM,EAAE,SAAM,GAAGC,GAAI,GAAGC,MAAOC,EAAeL,CAAI,GAC5CM,IAAQC,EAAK;CACnB,IAAID,EAAM,WAAW,GAAG,OAAO;CAE/B,IAAME,IAAOF,EAAMA,EAAM,SAAS;CAClC,IAAIL,IAAIG,IAAKE,EAAM,EAAE,CAAC,IAAI,OAAOA,EAAM,EAAE,CAAC,MAAM,EAAE,EAAE,YAAYA,EAAM,EAAE,CAAC;CACzE,IAAIL,KAAKG,IAAKI,EAAK,IAAI,OAAOA,EAAK;CAEnC,IAAIC,IAAOD;CACX,KAAK,IAAME,KAAaJ,GACtB,IAAIL,IAAIG,IAAKM,EAAU,IAAI;EAAE,IAAOA;EAAW;CAAO;CAMxD,IAAID,EAAK,MAAM,WAAW,GAAG,OAAOA,EAAK;CACzC,IAAMjB,IAAQH,EAAWoB,EAAK,KAAK,GAC7BE,IAASnB,EAAM,KAAK,GAAG,MAAMoB,CAAC,CAAC,CAAC,MAAM,GAAG,MAAMpB,EAAMqB,EAAE,CAAC,IAAIrB,EAAMsB,EAAE,CAAC,CAAC;CAE5E,KAAK,IAAMF,KAAKD,GAAQ;EACtB,IAAMpB,IAAIC,EAAMoB;EAChB,IAAI,OAAKT,IAAKZ,EAAE,IAAIA,EAAE,UAAU,IAGhC,OAAOA,EAAE,QAAQ,KAAM,IAAIA,EAAE,SAASA,EAAE;CAC1C;CACA,IAAMwB,IAAWvB,EAAMmB,EAAOA,EAAO,SAAS;CAC9C,OAAOI,EAAS,QAAQ,KAAM,IAAIA,EAAS,WAAWA,EAAS;AACjE"}
@@ -0,0 +1,8 @@
1
+ //#region src/features/text/isTextData.ts
2
+ function e(e) {
3
+ return typeof e == "object" && !!e && typeof e.text == "string";
4
+ }
5
+ //#endregion
6
+ export { e as isTextData };
7
+
8
+ //# sourceMappingURL=isTextData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isTextData.js","names":["isTextData","data"],"sources":["../../../src/features/text/isTextData.ts"],"sourcesContent":["/**\n * The kit's one answer to \"is this node text?\": its data carries a string\n * `text`. Routing, the `textEdit` dep, and `useSceneTextEdit` all ask it.\n */\nexport function isTextData(data: unknown): data is { text: string } {\n return typeof data === 'object' && data !== null\n && typeof (data as { text?: unknown }).text === 'string';\n}\n"],"mappings":";AAIA,SAAgBA,EAAW,GAAyC;CAClE,OAAO,OAAOC,KAAS,cAAYA,KAC9B,OAAQA,EAA4B,QAAS;AACpD"}
@@ -0,0 +1,15 @@
1
+ import { paragraphDirection as e, resolveAlign as t } from "@weasel-js/text";
2
+ //#region src/features/text/overlayDirection.ts
3
+ function n(e, n) {
4
+ let r = n.direction === "auto";
5
+ e.style.direction = r ? "" : n.direction, e.style.setProperty("unicode-bidi", r ? "plaintext" : ""), e.style.setProperty("tab-size", String(n.tabSize));
6
+ let i = n.align === "justify", a = n.direction === "auto" ? i ? "start" : n.align : t(n.align, n.direction);
7
+ e.style.textAlign = i ? "justify" : a, e.style.setProperty("text-align-last", i ? a : "");
8
+ }
9
+ function r(n, r) {
10
+ return t(r.align, e(n.textContent ?? "", r.direction));
11
+ }
12
+ //#endregion
13
+ export { n as applyOverlayDirection, r as overlayAlignEdge };
14
+
15
+ //# sourceMappingURL=overlayDirection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayDirection.js","names":["applyOverlayDirection","auto","style","el","justify","edge","resolveAlign","overlayAlignEdge","paragraphDirection"],"sources":["../../../src/features/text/overlayDirection.ts"],"sourcesContent":["/**\n * The edit overlay's reading direction, alignment, and tab stops, set so the\n * browser resolves them the way `layoutRuns` does for the canvas.\n */\nimport { paragraphDirection, resolveAlign, type ResolvedTextStyle } from '@weasel-js/text';\n\n/**\n * A stated direction goes on the overlay as CSS `direction`. `auto` becomes\n * `unicode-bidi: plaintext`, CSS's spelling of \"each paragraph reads the way\n * its first strong character does\", under which `start` and `end` follow each\n * paragraph too, so those are left for the browser to resolve.\n */\nexport function applyOverlayDirection(el: HTMLElement, style: ResolvedTextStyle): void {\n const auto = style.direction === 'auto';\n el.style.direction = auto ? '' : style.direction;\n el.style.setProperty('unicode-bidi', auto ? 'plaintext' : '');\n el.style.setProperty('tab-size', String(style.tabSize));\n const justify = style.align === 'justify';\n const edge = style.direction === 'auto'\n ? (justify ? 'start' : style.align)\n : resolveAlign(style.align, style.direction);\n el.style.textAlign = justify ? 'justify' : edge;\n // Pinned, so a justified paragraph's last line sits where the canvas puts it.\n el.style.setProperty('text-align-last', justify ? edge : '');\n}\n\n/** The absolute edge the overlay's text is set from, by its first paragraph. */\nexport function overlayAlignEdge(el: HTMLElement, style: ResolvedTextStyle): 'left' | 'center' | 'right' {\n return resolveAlign(style.align, paragraphDirection(el.textContent ?? '', style.direction));\n}\n"],"mappings":";;AAYA,SAAgBA,EAAsB,GAAiB,GAAgC;CACrF,IAAMC,IAAOC,EAAM,cAAc;CAGjC,AAFA,EAAG,MAAM,YAAYD,IAAO,KAAKC,EAAM,WACvCC,EAAG,MAAM,YAAY,gBAAgBF,IAAO,cAAc,EAAE,GAC5DE,EAAG,MAAM,YAAY,YAAY,OAAOD,EAAM,OAAO,CAAC;CACtD,IAAME,IAAUF,EAAM,UAAU,WAC1BG,IAAOH,EAAM,cAAc,SAC5BE,IAAU,UAAUF,EAAM,QAC3BI,EAAaJ,EAAM,OAAOA,EAAM,SAAS;CAG7C,AAFA,EAAG,MAAM,YAAYE,IAAU,YAAYC,GAE3CF,EAAG,MAAM,YAAY,mBAAmBC,IAAUC,IAAO,EAAE;AAC7D;AAGA,SAAgBE,EAAiB,GAAiB,GAAuD;CACvG,OAAOD,EAAaJ,EAAM,OAAOM,EAAmBL,EAAG,eAAe,IAAID,EAAM,SAAS,CAAC;AAC5F"}