@weasel-js/core 1.9.3 → 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.
- package/CHANGELOG.md +356 -0
- package/README.md +2 -2
- package/dist/WeaselProvider.js +5 -5
- package/dist/affordances/composeAffordanceLayer.js +10 -20
- package/dist/affordances/composeAffordanceLayer.js.map +1 -1
- package/dist/affordances/cornerResize.js +25 -17
- package/dist/affordances/cornerResize.js.map +1 -1
- package/dist/affordances/hitAffordanceRegions.js +27 -20
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/rotationHandle.js +18 -18
- package/dist/affordances/rotationHandle.js.map +1 -1
- package/dist/animation/behaviors/momentum.js +93 -55
- package/dist/animation/behaviors/momentum.js.map +1 -1
- package/dist/animation/loop.js +10 -12
- package/dist/animation/loop.js.map +1 -1
- package/dist/animation/reflow.js +8 -8
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/stagger.js +64 -56
- package/dist/animation/stagger.js.map +1 -1
- package/dist/animation/timeline/booking.js +62 -54
- package/dist/animation/timeline/booking.js.map +1 -1
- package/dist/animation/timeline/createTimeline.js +85 -76
- package/dist/animation/timeline/createTimeline.js.map +1 -1
- package/dist/animation/timeline/sampleTrack.js +13 -5
- package/dist/animation/timeline/sampleTrack.js.map +1 -1
- package/dist/animation/useAnimator.js +45 -35
- package/dist/animation/useAnimator.js.map +1 -1
- package/dist/animation/wrappers/animateLifecycle.js +31 -24
- package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
- package/dist/animation/wrappers/animateOnSetPose.js +28 -26
- package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
- package/dist/animation/wrappers/instantPose.js +13 -0
- package/dist/animation/wrappers/instantPose.js.map +1 -0
- package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
- package/dist/barrel/chrome.js +16 -0
- package/dist/barrel/input.js +19 -0
- package/dist/barrel/interactions.js +42 -0
- package/dist/barrel/paint.js +18 -0
- package/dist/barrel/paths.js +19 -0
- package/dist/barrel/renderer.js +20 -0
- package/dist/barrel/routing.js +51 -0
- package/dist/barrel/text.js +18 -0
- package/dist/barrel/viewport.js +52 -0
- package/dist/builtins-DPcdghtB.d.ts +1223 -0
- package/dist/canvas/Canvas.js +363 -448
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +164 -165
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/CursorCoordsHud.js +37 -32
- package/dist/canvas/CursorCoordsHud.js.map +1 -1
- package/dist/canvas/DrawCanvas.js +49 -43
- package/dist/canvas/DrawCanvas.js.map +1 -1
- package/dist/canvas/MinimapCanvas.js +26 -25
- package/dist/canvas/MinimapCanvas.js.map +1 -1
- package/dist/canvas/NodeShape.js +59 -377
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/PickHud.js +50 -47
- package/dist/canvas/PickHud.js.map +1 -1
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
- package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +112 -70
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +1 -0
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/isDev.js +12 -0
- package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
- package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
- package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
- package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
- package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
- package/dist/canvas/SceneCanvas/props.js +38 -0
- package/dist/canvas/SceneCanvas/props.js.map +1 -0
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +534 -788
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +45 -43
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/buildSceneLayer.js +91 -0
- package/dist/canvas/buildSceneLayer.js.map +1 -0
- package/dist/canvas/buildSceneTree.js +16 -19
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/canvasRenderer.js +68 -52
- package/dist/canvas/canvasRenderer.js.map +1 -1
- package/dist/canvas/deps/changes.js +13 -0
- package/dist/canvas/deps/changes.js.map +1 -0
- package/dist/canvas/deps/editAnchors.js +28 -28
- package/dist/canvas/deps/insert.js +119 -91
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/slice.js +46 -45
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/deps/textEdit.js +10 -9
- package/dist/canvas/deps/textEdit.js.map +1 -1
- package/dist/canvas/derivedPath.js +46 -23
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/getNodeAtPoint.js +7 -7
- package/dist/canvas/getNodeAtPoint.js.map +1 -1
- package/dist/canvas/hud.module.js +1 -1
- package/dist/canvas/hud.module.js.map +1 -1
- package/dist/canvas/kitRequirements.js +2 -2
- package/dist/canvas/kitRequirements.js.map +1 -1
- package/dist/canvas/layerOrder.js +13 -6
- package/dist/canvas/layerOrder.js.map +1 -1
- package/dist/canvas/minimapMath.js +12 -10
- package/dist/canvas/minimapMath.js.map +1 -1
- package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/imagePainter.js +87 -0
- package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/memo.js +9 -0
- package/dist/canvas/nodeShapes/memo.js.map +1 -0
- package/dist/canvas/nodeShapes/nodePaint.js +57 -0
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
- package/dist/canvas/nodeShapes/pathPainter.js +38 -0
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/poseBox.js +20 -0
- package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/shapePainter.js +72 -0
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/textPainter.js +55 -0
- package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/paintCull.js +10 -11
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickWalk.js +52 -63
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseRotation.js +11 -23
- package/dist/canvas/poseRotation.js.map +1 -1
- package/dist/canvas/renderDebugSnapshot.js +5 -5
- package/dist/canvas/renderSceneToPixels.js +3 -1
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +66 -148
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +112 -0
- package/dist/canvas/sceneClipboard.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +4 -4
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +39 -40
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/useCanvasRenderer.js +14 -6
- package/dist/canvas/useCanvasRenderer.js.map +1 -1
- package/dist/canvas/useCanvasView.js +58 -0
- package/dist/canvas/useCanvasView.js.map +1 -0
- package/dist/canvas/useFrameLoop.js +35 -26
- package/dist/canvas/useFrameLoop.js.map +1 -1
- package/dist/canvas/useSceneLayerList.js +21 -0
- package/dist/canvas/useSceneLayerList.js.map +1 -0
- package/dist/clipboard.d.ts +10 -6
- package/dist/clone.d.ts +4 -3
- package/dist/core/adapters/arrayAdapter.js +48 -39
- package/dist/core/adapters/arrayAdapter.js.map +1 -1
- package/dist/core/device/index.js +5 -4
- package/dist/core/device/useDeviceProfile.js +38 -44
- package/dist/core/device/useDeviceProfile.js.map +1 -1
- package/dist/core/device/watchDevicePixelRatio.js +17 -0
- package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
- package/dist/core/fillInPoseFrame.js.map +1 -1
- package/dist/core/geometry/poseDescriptor.js +21 -19
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/poseFrame.js +44 -0
- package/dist/core/geometry/poseFrame.js.map +1 -0
- package/dist/core/geometry/poseRotation.js.map +1 -1
- package/dist/core/geometry/regionHit.js +9 -10
- package/dist/core/geometry/regionHit.js.map +1 -1
- package/dist/core/geometry/unionBounds.js +31 -29
- package/dist/core/geometry/unionBounds.js.map +1 -1
- package/dist/core/isPlainObject.js +2 -11
- package/dist/core/ops/arrange.js +1 -0
- package/dist/core/ops/arrange.js.map +1 -1
- package/dist/core/ops/create.js +10 -5
- package/dist/core/ops/create.js.map +1 -1
- package/dist/core/ops/delete.js +12 -24
- package/dist/core/ops/delete.js.map +1 -1
- package/dist/core/ops/reorder/index.js +43 -40
- package/dist/core/ops/reorder/index.js.map +1 -1
- package/dist/core/ops/reorder/setChildOrder.js +34 -0
- package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
- package/dist/core/paintKinds.js +5 -5
- package/dist/core/paintKinds.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/appliedLog.js +37 -0
- package/dist/core/scene/appliedLog.js.map +1 -0
- package/dist/core/scene/arrivalWindow.js +101 -0
- package/dist/core/scene/arrivalWindow.js.map +1 -0
- package/dist/core/scene/derivedPath.js.map +1 -1
- package/dist/core/scene/historyFacade.js +84 -0
- package/dist/core/scene/historyFacade.js.map +1 -0
- package/dist/core/scene/kitOps.js +173 -0
- package/dist/core/scene/kitOps.js.map +1 -0
- package/dist/core/scene/layerStack.js +38 -0
- package/dist/core/scene/layerStack.js.map +1 -0
- package/dist/core/scene/lockGuard.js +37 -0
- package/dist/core/scene/lockGuard.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +5 -6
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +45 -19
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +126 -0
- package/dist/core/scene/nodeStore.js.map +1 -0
- package/dist/core/scene/pickability.js +6 -0
- package/dist/core/scene/pickability.js.map +1 -0
- package/dist/core/scene/poseFeed.js +38 -38
- package/dist/core/scene/poseFeed.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +34 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/renderOrder.js +84 -0
- package/dist/core/scene/renderOrder.js.map +1 -0
- package/dist/core/scene/scene.js +289 -866
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/serialization.js +68 -0
- package/dist/core/scene/serialization.js.map +1 -0
- package/dist/core/scene/treeOrder.js +13 -0
- package/dist/core/scene/treeOrder.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/viewport/meanScale.js +1 -1
- package/dist/core/viewport/meanScale.js.map +1 -1
- package/dist/core/viewport/view.js +12 -12
- package/dist/core/viewport/view.js.map +1 -1
- package/dist/core/viewport/zoomBounds.js +10 -8
- package/dist/core/viewport/zoomBounds.js.map +1 -1
- package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
- package/dist/debug/createDebugOverlayLayer.js +14 -14
- package/dist/debug/createDebugOverlayLayer.js.map +1 -1
- package/dist/debug/flag.js +2 -41
- package/dist/features/chrome-caps/useHoverTracking.js +22 -17
- package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
- package/dist/features/focus/gateLayer.js +3 -4
- package/dist/features/focus/gateLayer.js.map +1 -1
- package/dist/features/images/imageCache.js +19 -9
- package/dist/features/images/imageCache.js.map +1 -1
- package/dist/features/ingestion/contentHandlers.js +14 -11
- package/dist/features/ingestion/contentHandlers.js.map +1 -1
- package/dist/features/ingestion/index.js +1 -1
- package/dist/features/ingestion/registerKitHandlers.js +7 -7
- package/dist/features/ingestion/weaselJsonHandler.js +17 -14
- package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
- package/dist/features/meshPaint/lazyKind.js +201 -35
- package/dist/features/meshPaint/lazyKind.js.map +1 -1
- package/dist/features/meshPaint/meshPaint.js +82 -79
- package/dist/features/meshPaint/meshPaint.js.map +1 -1
- package/dist/features/minimap/createMinimapContribution.js +42 -42
- package/dist/features/minimap/layers.js +44 -62
- package/dist/features/minimap/layers.js.map +1 -1
- package/dist/features/parallax/createParallaxPlane.js +10 -14
- package/dist/features/parallax/createParallaxPlane.js.map +1 -1
- package/dist/features/paths/anchorEdits.js +19 -8
- package/dist/features/paths/anchorEdits.js.map +1 -1
- package/dist/features/paths/anchors.js +31 -26
- package/dist/features/paths/anchors.js.map +1 -1
- package/dist/features/paths/booleans.js +2 -2
- package/dist/features/paths/index.js +13 -11
- package/dist/features/paths/markerCommands.js +37 -36
- package/dist/features/paths/markerCommands.js.map +1 -1
- package/dist/features/paths/pathInWorld.js +19 -19
- package/dist/features/paths/pathInWorld.js.map +1 -1
- package/dist/features/paths/pathLayer.js.map +1 -1
- package/dist/features/paths/strokeToPath.js +40 -0
- package/dist/features/paths/strokeToPath.js.map +1 -0
- package/dist/features/paths/tessellate/stroke.js.map +1 -1
- package/dist/features/patterns/commandPaint.js +18 -0
- package/dist/features/patterns/commandPaint.js.map +1 -0
- package/dist/features/patterns/index.js +5 -17
- package/dist/features/patterns/index.js.map +1 -1
- package/dist/features/patterns/rasterizeTile.js +17 -0
- package/dist/features/patterns/rasterizeTile.js.map +1 -0
- package/dist/features/patterns/resolveSpec.js +52 -44
- package/dist/features/patterns/resolveSpec.js.map +1 -1
- package/dist/features/poseRun/usePoseRun.js +8 -3
- package/dist/features/poseRun/usePoseRun.js.map +1 -1
- package/dist/features/selection/SelectionContext.js +5 -0
- package/dist/features/selection/SelectionContext.js.map +1 -1
- package/dist/features/selection/overlay.js +18 -18
- package/dist/features/selection/overlay.js.map +1 -1
- package/dist/features/text/domOffsets.js +82 -0
- package/dist/features/text/domOffsets.js.map +1 -0
- package/dist/features/text/domRuns.js +103 -131
- package/dist/features/text/domRuns.js.map +1 -1
- package/dist/features/text/hitTest.js +5 -2
- package/dist/features/text/hitTest.js.map +1 -1
- package/dist/features/text/isTextData.js +8 -0
- package/dist/features/text/isTextData.js.map +1 -0
- package/dist/features/text/overlayDirection.js +15 -0
- package/dist/features/text/overlayDirection.js.map +1 -0
- package/dist/features/text/runs/rangeStyle.js +32 -34
- package/dist/features/text/runs/rangeStyle.js.map +1 -1
- package/dist/features/text/textCommand.js +34 -33
- package/dist/features/text/textCommand.js.map +1 -1
- package/dist/features/text/textToPath.js +40 -40
- package/dist/features/text/textToPath.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +76 -82
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/text/useTextEdit.js +184 -160
- package/dist/features/text/useTextEdit.js.map +1 -1
- package/dist/features/tiling/createTiledLayer.js +24 -22
- package/dist/features/tiling/createTiledLayer.js.map +1 -1
- package/dist/index.d.ts +11033 -7749
- package/dist/index.js +316 -309
- package/dist/input/keyState.js +42 -43
- package/dist/input/keyState.js.map +1 -1
- package/dist/input/useKeyModifiers.js +11 -0
- package/dist/input/useKeyModifiers.js.map +1 -0
- package/dist/interactions/actions/align/align.js +8 -8
- package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
- package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
- package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
- package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
- package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
- package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
- package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
- package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
- package/dist/interactions/actions/defaults/align.js +9 -10
- package/dist/interactions/actions/defaults/align.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/areaSelect.js +35 -38
- package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/clipboard.js +26 -10
- package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
- package/dist/interactions/actions/defaults/clone.js +44 -60
- package/dist/interactions/actions/defaults/clone.js.map +1 -1
- package/dist/interactions/actions/defaults/copySubtree.js +40 -0
- package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
- package/dist/interactions/actions/defaults/distribute.js +14 -13
- package/dist/interactions/actions/defaults/distribute.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +17 -57
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/editAnchors.js +18 -18
- package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/flip.js +52 -46
- package/dist/interactions/actions/defaults/flip.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +43 -48
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +35 -33
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insert.js +50 -51
- package/dist/interactions/actions/defaults/insert.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
- package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
- package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/move.js +267 -249
- package/dist/interactions/actions/defaults/move.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +40 -68
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
- package/dist/interactions/actions/defaults/reorder.js +2 -0
- package/dist/interactions/actions/defaults/reorder.js.map +1 -1
- package/dist/interactions/actions/defaults/resize.js +104 -98
- package/dist/interactions/actions/defaults/resize.js.map +1 -1
- package/dist/interactions/actions/defaults/rotate.js +111 -110
- package/dist/interactions/actions/defaults/rotate.js.map +1 -1
- package/dist/interactions/actions/defaults/setFill.js +7 -7
- package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
- package/dist/interactions/actions/defaults/setStroke.js +12 -12
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
- package/dist/interactions/actions/defaults/sliceOps.js +19 -19
- package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
- package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
- package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
- package/dist/interactions/actions/flip/helpers.js +30 -17
- package/dist/interactions/actions/flip/helpers.js.map +1 -1
- package/dist/interactions/actions/hitSelection.js +8 -0
- package/dist/interactions/actions/hitSelection.js.map +1 -0
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +32 -15
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/landedDescendants.js +33 -0
- package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
- package/dist/interactions/actions/outlines/createOutlines.js +18 -18
- package/dist/interactions/actions/poseDescriptorDep.js +16 -4
- package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
- package/dist/interactions/actions/poseFrame.js +11 -6
- package/dist/interactions/actions/poseFrame.js.map +1 -1
- package/dist/interactions/actions/previewOverrides.js +5 -5
- package/dist/interactions/actions/previewOverrides.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +69 -0
- package/dist/interactions/actions/selectionSubtree.js.map +1 -0
- package/dist/interactions/actions/useStandardActions.js +66 -62
- package/dist/interactions/actions/useStandardActions.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
- package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
- package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
- package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
- package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
- package/dist/math.d.ts +2 -3
- package/dist/move.d.ts +12 -11
- package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
- package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
- package/dist/renderer/DrawCommand.js.map +1 -1
- package/dist/renderer/WeaselRenderer.js +75 -73
- package/dist/renderer/WeaselRenderer.js.map +1 -1
- package/dist/renderer/cache/GLImageCache.js +15 -5
- package/dist/renderer/cache/GLImageCache.js.map +1 -1
- package/dist/renderer/cache/GLMeshCache.js +23 -23
- package/dist/renderer/cache/GLMeshCache.js.map +1 -1
- package/dist/renderer/cache/GLTextureCache.js +18 -1
- package/dist/renderer/cache/GLTextureCache.js.map +1 -1
- package/dist/renderer/cache/cache.js.map +1 -1
- package/dist/renderer/cache/markerCommandCache.js.map +1 -1
- package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
- package/dist/renderer/cullDrawCommands.js +11 -11
- package/dist/renderer/draw/batch.js +133 -0
- package/dist/renderer/draw/batch.js.map +1 -0
- package/dist/renderer/draw/clip.js +89 -0
- package/dist/renderer/draw/clip.js.map +1 -0
- package/dist/renderer/draw/customShader.js +230 -0
- package/dist/renderer/draw/customShader.js.map +1 -0
- package/dist/renderer/draw/drawTriangles.js +8 -0
- package/dist/renderer/draw/drawTriangles.js.map +1 -0
- package/dist/renderer/draw/effects.js +28 -0
- package/dist/renderer/draw/effects.js.map +1 -0
- package/dist/renderer/draw/flushBatch.js +35 -0
- package/dist/renderer/draw/flushBatch.js.map +1 -0
- package/dist/renderer/draw/gradient.js +90 -0
- package/dist/renderer/draw/gradient.js.map +1 -0
- package/dist/renderer/draw/image.js +38 -0
- package/dist/renderer/draw/image.js.map +1 -0
- package/dist/renderer/draw/mesh.js +16 -0
- package/dist/renderer/draw/mesh.js.map +1 -0
- package/dist/renderer/draw/paintBind.js +70 -0
- package/dist/renderer/draw/paintBind.js.map +1 -0
- package/dist/renderer/draw/pathPaint.js +142 -0
- package/dist/renderer/draw/pathPaint.js.map +1 -0
- package/dist/renderer/draw/scratchPass.js +83 -0
- package/dist/renderer/draw/scratchPass.js.map +1 -0
- package/dist/renderer/draw/stencil.js +28 -0
- package/dist/renderer/draw/stencil.js.map +1 -0
- package/dist/renderer/draw/text.js +167 -0
- package/dist/renderer/draw/text.js.map +1 -0
- package/dist/renderer/draw/uniforms.js +77 -0
- package/dist/renderer/draw/uniforms.js.map +1 -0
- package/dist/renderer/draw.js +60 -1116
- package/dist/renderer/draw.js.map +1 -1
- package/dist/renderer/effects/EffectTargets.js +3 -0
- package/dist/renderer/effects/EffectTargets.js.map +1 -1
- package/dist/renderer/effects/composite.js +7 -5
- package/dist/renderer/effects/composite.js.map +1 -1
- package/dist/renderer/index.js +18 -17
- package/dist/renderer/math/color.js +63 -83
- package/dist/renderer/math/color.js.map +1 -1
- package/dist/renderer/math/mat3.js +15 -11
- package/dist/renderer/math/mat3.js.map +1 -1
- package/dist/renderer/math/viewToMat3.js +1 -1
- package/dist/renderer/math/viewToMat3.js.map +1 -1
- package/dist/renderer/shaders/ShaderProgram.js +13 -13
- package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
- package/dist/renderer/shaders/glyphPaint.js +3 -3
- package/dist/renderer/shaders/glyphPaint.js.map +1 -1
- package/dist/renderer/shaders/gradFill.js +2 -1
- package/dist/renderer/shaders/gradFill.js.map +1 -1
- package/dist/renderer/shaders/pathFill.js +2 -1
- package/dist/renderer/shaders/pathFill.js.map +1 -1
- package/dist/renderer/shaders/patternFill.js +2 -1
- package/dist/renderer/shaders/patternFill.js.map +1 -1
- package/dist/renderer/shaders/registerProgram.js.map +1 -1
- package/dist/renderer/strokeLengths.js.map +1 -1
- package/dist/renderer/textures/registerTexture.js +25 -5
- package/dist/renderer/textures/registerTexture.js.map +1 -1
- package/dist/renderer.d.ts +16 -15
- package/dist/renderer.js +17 -16
- package/dist/resize.d.ts +5 -6
- package/dist/resize.js +5 -5
- package/dist/style.css +1 -1
- package/dist/test-hook/install.js +12 -6
- package/dist/test-hook/install.js.map +1 -1
- package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
- package/dist/test-seams.d.ts +2 -2
- package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/hand/useHandTool.js +1 -0
- package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
- package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
- package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js +1 -0
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +153 -103
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
- package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/rect/useRectTool.js +1 -0
- package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +24 -23
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +51 -48
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/shared/pointerFollower.js +17 -0
- package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
- package/dist/tools/builtin/slice/useSliceTool.js +49 -54
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +22 -16
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +1 -0
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/useKeybindings.js +14 -30
- package/dist/tools/useKeybindings.js.map +1 -1
- package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
- package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
- package/dist/util/color.js +3 -3
- package/dist/util/color.js.map +1 -1
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -18
- package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
- package/dist/builtins-C-VgUlWI.d.ts +0 -4514
- package/dist/core/isPlainObject.js.map +0 -1
- package/dist/debug/flag.js.map +0 -1
- package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
- package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
- package/dist/renderer/shaders/imageFill.js +0 -14
- package/dist/renderer/shaders/imageFill.js.map +0 -1
- package/dist/tools/prefs.js +0 -62
- package/dist/tools/prefs.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { fillInPoseFrame as e } from "../../core/fillInPoseFrame.js";
|
|
2
|
+
import { resolveFillPattern as t } from "./resolveSpec.js";
|
|
3
|
+
//#region src/features/patterns/commandPaint.ts
|
|
4
|
+
function n(n, r) {
|
|
5
|
+
return t(e(n, r));
|
|
6
|
+
}
|
|
7
|
+
function r(e, t) {
|
|
8
|
+
if (e.paint == null) return null;
|
|
9
|
+
let r = n(e.paint, t);
|
|
10
|
+
return r === null ? null : r === e.paint ? e : {
|
|
11
|
+
...e,
|
|
12
|
+
paint: r
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { n as commandFill, r as commandStroke };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=commandPaint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandPaint.js","names":["commandFill","resolveFillPattern","fillInPoseFrame","fill","box","commandStroke","stroke","paint"],"sources":["../../../src/features/patterns/commandPaint.ts"],"sourcesContent":["/**\n * A node's paint, resolved into what a draw command takes. One place composes\n * the two resolutions so every painter and layer applies both.\n */\n\nimport type { CommandFill, CommandStroke, FillStyle, Stroke } from '@weasel-js/paint';\nimport { fillInPoseFrame, type FillPoseBox } from '../../core/fillInPoseFrame';\nimport { resolveFillPattern } from './resolveSpec';\n\n/**\n * `fill` as a draw command's fill: `'bounds'` units mapped onto `box`, and a\n * tile spec swapped for its texture. `null` when a spec cannot rasterize.\n */\nexport function commandFill(fill: FillStyle, box: FillPoseBox): CommandFill | null {\n return resolveFillPattern(fillInPoseFrame(fill, box));\n}\n\n/**\n * `stroke` as a draw command's stroke, its paint resolved as\n * {@link commandFill} resolves a fill. `null` when the stroke carries no\n * paint, or its paint cannot be resolved.\n */\nexport function commandStroke(stroke: Stroke, box: FillPoseBox): CommandStroke | null {\n if (stroke.paint == null) return null;\n const paint = commandFill(stroke.paint, box);\n if (paint === null) return null;\n // Same object when nothing changed: the renderer's stroke caches key on it.\n return paint === stroke.paint ? (stroke as CommandStroke) : { ...stroke, paint };\n}\n"],"mappings":";;;AAaA,SAAgBA,EAAY,GAAiB,GAAsC;CACjF,OAAOC,EAAmBC,EAAgBC,GAAMC,CAAG,CAAC;AACtD;AAOA,SAAgBC,EAAc,GAAgB,GAAwC;CACpF,IAAIC,EAAO,SAAS,MAAM,OAAO;CACjC,IAAMC,IAAQP,EAAYM,EAAO,OAAOF,CAAG;CAG3C,OAFIG,MAAU,OAAa,OAEpBA,MAAUD,EAAO,QAASA,IAA2B;EAAE,GAAGA;EAAQ;CAAM;AACjF"}
|
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { registerTexture as e } from "../../renderer/textures/registerTexture.js";
|
|
2
|
+
import { rasterizeTile as t } from "./rasterizeTile.js";
|
|
2
3
|
//#region src/features/patterns/index.ts
|
|
3
|
-
function
|
|
4
|
-
let n;
|
|
5
|
-
|
|
6
|
-
n = new OffscreenCanvas(t.size, t.size);
|
|
7
|
-
} catch {
|
|
8
|
-
if (typeof document > "u") return null;
|
|
9
|
-
let e = document.createElement("canvas");
|
|
10
|
-
e.width = t.size, e.height = t.size, n = e;
|
|
11
|
-
}
|
|
12
|
-
let r = n.getContext("2d");
|
|
13
|
-
if (!r) return null;
|
|
14
|
-
t.draw(r, t.size);
|
|
15
|
-
let i;
|
|
16
|
-
if (typeof n.transferToImageBitmap == "function") i = n.transferToImageBitmap();
|
|
17
|
-
else return null;
|
|
18
|
-
return e(i);
|
|
4
|
+
function n(n) {
|
|
5
|
+
let r = t(n);
|
|
6
|
+
return r ? e(r) : null;
|
|
19
7
|
}
|
|
20
8
|
//#endregion
|
|
21
|
-
export {
|
|
9
|
+
export { n as createTilePattern };
|
|
22
10
|
|
|
23
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["createTilePattern","
|
|
1
|
+
{"version":3,"file":"index.js","names":["createTilePattern","bitmap","rasterizeTile","opts","registerTexture"],"sources":["../../../src/features/patterns/index.ts"],"sourcesContent":["/**\n * Tile-pattern primitive — a small helper for building repeating texture\n * tiles from a draw callback. The result plugs into a `FillStyle` of\n * `{ fill: 'pattern', pattern }` (see `@weasel-js/paint`).\n *\n * GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback\n * to a regular `<canvas>` for environments without `OffscreenCanvas`),\n * converted to an `ImageBitmap`, and registered with `registerTexture()`.\n * The returned `TextureHandle` is what `FillStyle.pattern` accepts.\n *\n * For ready-to-use named patterns (hatch, crosshatch, dots, chunks),\n * see the `@weasel-js/core/patterns-builtin` subpath.\n */\n\nimport { registerTexture } from '../../renderer/textures/registerTexture';\nimport type { TextureHandle } from '../../renderer/textures/registerTexture';\nimport { rasterizeTile } from './rasterizeTile';\n\n/** Options for `createTilePattern`. */\nexport interface TilePatternOpts {\n /** Edge length (in pixels) of the square offscreen tile. */\n size: number;\n /** FillStyle one tile at integer-pixel coordinates `(0, 0) .. (size, size)`. */\n draw: (oc: CanvasRenderingContext2D, size: number) => void;\n}\n\n/**\n * Render a single tile to an offscreen canvas and register it as a GL\n * texture, returning a `TextureHandle` you can wrap in a `FillStyle` of\n * `{ fill: 'pattern', pattern }`. Returns `null` only when an\n * `OffscreenCanvas`/2D context can't be acquired (very rare; same fallback\n * semantics as the prior `CanvasPattern`-based implementation).\n */\nexport function createTilePattern(opts: TilePatternOpts): TextureHandle | null {\n const bitmap = rasterizeTile(opts);\n return bitmap ? registerTexture(bitmap) : null;\n}\n"],"mappings":";;;AAiCA,SAAgBA,EAAkB,GAA6C;CAC7E,IAAMC,IAASC,EAAcC,CAAI;CACjC,OAAOF,IAASG,EAAgBH,CAAM,IAAI;AAC5C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/features/patterns/rasterizeTile.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
let t;
|
|
4
|
+
try {
|
|
5
|
+
t = new OffscreenCanvas(e.size, e.size);
|
|
6
|
+
} catch {
|
|
7
|
+
if (typeof document > "u") return null;
|
|
8
|
+
let n = document.createElement("canvas");
|
|
9
|
+
n.width = e.size, n.height = e.size, t = n;
|
|
10
|
+
}
|
|
11
|
+
let n = t.getContext("2d");
|
|
12
|
+
return !n || (e.draw(n, e.size), typeof t.transferToImageBitmap != "function") ? null : t.transferToImageBitmap();
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { e as rasterizeTile };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=rasterizeTile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rasterizeTile.js","names":["rasterizeTile","off","opts","c","oc"],"sources":["../../../src/features/patterns/rasterizeTile.ts"],"sourcesContent":["import type { TilePatternOpts } from '.';\n\n/** Draw one tile and hand back its pixels, or `null` where no\n * `OffscreenCanvas` 2D context can be had. */\nexport function rasterizeTile(opts: TilePatternOpts): ImageBitmap | null {\n // Prefer OffscreenCanvas; fall back to <canvas> for environments without it.\n let off: OffscreenCanvas | HTMLCanvasElement;\n try {\n off = new OffscreenCanvas(opts.size, opts.size);\n } catch {\n if (typeof document === 'undefined') return null;\n const c = document.createElement('canvas');\n c.width = opts.size;\n c.height = opts.size;\n off = c;\n }\n const oc = off.getContext('2d') as CanvasRenderingContext2D | null;\n if (!oc) return null;\n opts.draw(oc, opts.size);\n\n // Only an OffscreenCanvas can hand over a bitmap synchronously.\n if (typeof (off as OffscreenCanvas).transferToImageBitmap !== 'function') return null;\n return (off as OffscreenCanvas).transferToImageBitmap();\n}\n"],"mappings":";AAIA,SAAgBA,EAAc,GAA2C;CAEvE,IAAIC;CACJ,IAAI;EACF,IAAM,IAAI,gBAAgBC,EAAK,MAAMA,EAAK,IAAI;CAChD,QAAQ;EACN,IAAI,OAAO,WAAa,KAAa,OAAO;EAC5C,IAAMC,IAAI,SAAS,cAAc,QAAQ;EAGzC,AAFA,EAAE,QAAQD,EAAK,MACf,EAAE,SAASA,EAAK,MAChB,IAAMC;CACR;CACA,IAAMC,IAAKH,EAAI,WAAW,IAAI;CAM9B,OALI,CAACG,MACLF,EAAK,KAAKE,GAAIF,EAAK,IAAI,GAGnB,OAAQD,EAAwB,yBAA0B,cAAmB,OACzEA,EAAwB,sBAAsB;AACxD"}
|
|
@@ -1,57 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ownTextureIds as e, registerTextureAs as t, textureRegistry as n, unregisterTexture as r } from "../../renderer/textures/registerTexture.js";
|
|
2
|
+
import { rasterizeTile as i } from "./rasterizeTile.js";
|
|
3
|
+
import { drawTileShapes as a, tileGeometry as o, tileSize as s } from "./tileGeometry.js";
|
|
2
4
|
//#region src/features/patterns/resolveSpec.ts
|
|
3
|
-
var
|
|
4
|
-
function
|
|
5
|
-
let t =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
5
|
+
var c = 256, l = "pattern:", u = /* @__PURE__ */ new Map();
|
|
6
|
+
function d(e) {
|
|
7
|
+
let t = e, n = {};
|
|
8
|
+
for (let e of Object.keys(t).sort()) n[e] = t[e];
|
|
9
|
+
return l + JSON.stringify(n);
|
|
10
|
+
}
|
|
11
|
+
function f(e) {
|
|
12
|
+
let t = d(e), n = u.get(t);
|
|
13
|
+
return n === void 0 ? m(t, e) : (p(t), n);
|
|
14
|
+
}
|
|
15
|
+
function p(e) {
|
|
16
|
+
let t = u.get(e);
|
|
17
|
+
t !== void 0 && (u.delete(e), u.set(e, t));
|
|
18
|
+
}
|
|
19
|
+
function m(e, n) {
|
|
20
|
+
let r = h.has(n.tile) ? i({
|
|
21
|
+
size: s(n),
|
|
22
|
+
draw: (e) => a(e, o(n))
|
|
23
|
+
}) : null, l = r ? t(e, r) : null;
|
|
24
|
+
for (u.delete(e), u.set(e, l); u.size > c;) g(u.keys().next().value);
|
|
25
|
+
return l;
|
|
26
|
+
}
|
|
27
|
+
var h = /* @__PURE__ */ new Set([
|
|
28
|
+
"hatch",
|
|
29
|
+
"crosshatch",
|
|
30
|
+
"dots",
|
|
31
|
+
"chunks"
|
|
32
|
+
]);
|
|
33
|
+
function g(e) {
|
|
34
|
+
u.delete(e);
|
|
35
|
+
let t = n.get(e)?.source;
|
|
36
|
+
r({ id: e }), t && "close" in t && t.close();
|
|
37
|
+
}
|
|
38
|
+
function _(e) {
|
|
39
|
+
let t;
|
|
40
|
+
try {
|
|
41
|
+
t = JSON.parse(e.slice(8));
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
41
44
|
}
|
|
45
|
+
typeof t == "object" && t && "tile" in t && m(e, t);
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
e(l, {
|
|
48
|
+
revive: _,
|
|
49
|
+
touched: p
|
|
50
|
+
});
|
|
51
|
+
function v(e) {
|
|
44
52
|
return "tile" in e;
|
|
45
53
|
}
|
|
46
|
-
function
|
|
47
|
-
if (e.fill !== "pattern" || !
|
|
48
|
-
let t =
|
|
54
|
+
function y(e) {
|
|
55
|
+
if (e.fill !== "pattern" || !v(e.pattern)) return e;
|
|
56
|
+
let t = f(e.pattern);
|
|
49
57
|
return t ? {
|
|
50
58
|
...e,
|
|
51
59
|
pattern: t
|
|
52
60
|
} : null;
|
|
53
61
|
}
|
|
54
62
|
//#endregion
|
|
55
|
-
export { c as isPatternSpec,
|
|
63
|
+
export { c as PATTERN_TILE_CACHE_LIMIT, v as isPatternSpec, y as resolveFillPattern, f as resolvePatternSpec };
|
|
56
64
|
|
|
57
65
|
//# sourceMappingURL=resolveSpec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveSpec.js","names":["cache","
|
|
1
|
+
{"version":3,"file":"resolveSpec.js","names":["PATTERN_TILE_CACHE_LIMIT","ID_PREFIX","cache","specId","bag","spec","sorted","k","resolvePatternSpec","id","hit","build","markUsed","bitmap","TILES","rasterizeTile","tileSize","drawTileShapes","oc","tileGeometry","handle","registerTextureAs","evict","source","textureRegistry","unregisterTexture","revive","ownTextureIds","isPatternSpec","payload","resolveFillPattern","fill"],"sources":["../../../src/features/patterns/resolveSpec.ts"],"sourcesContent":["/**\n * `TilePatternSpec` → `TextureHandle`. The bridge between the serializable\n * half of a pattern paint and the GL texture that paints it.\n *\n * Memoized on the spec's field values, so identical specs across nodes share\n * one texture and a re-render never re-rasterizes. The memo keeps the\n * `PATTERN_TILE_CACHE_LIMIT` tiles most recently resolved or drawn and\n * unregisters the rest. A handle outliving its tile still draws: its id is\n * the spec, and the texture registry asks this module to rebuild it.\n */\n\nimport type { CommandFill, FillStyle, PosedFill, TilePatternSpec } from '@weasel-js/paint';\nimport {\n ownTextureIds, registerTextureAs, textureRegistry, unregisterTexture,\n type TextureHandle,\n} from '../../renderer/textures/registerTexture';\nimport { rasterizeTile } from './rasterizeTile';\nimport { drawTileShapes, tileGeometry, tileSize } from './tileGeometry';\n\n/** How many built-in tiles stay rasterized at once. A scene drawing more\n * distinct specs than this in one frame rebuilds the overflow every frame. */\nexport const PATTERN_TILE_CACHE_LIMIT = 256;\n\nconst ID_PREFIX = 'pattern:';\n\n/** Texture id → handle, least recently used first. */\nconst cache = new Map<string, TextureHandle | null>();\n\n/** Stable across key order — specs arriving from JSON have no guaranteed\n * field order, and two orderings must not rasterize two tiles. */\nfunction specId(spec: TilePatternSpec): string {\n const bag = spec as unknown as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const k of Object.keys(bag).sort()) sorted[k] = bag[k];\n return ID_PREFIX + JSON.stringify(sorted);\n}\n\n/** @internal Test helper — do not call from product code. */\nexport function _resetPatternSpecCacheForTests(): void {\n cache.clear();\n}\n\n/**\n * Build (or fetch) the texture for a spec. Returns `null` when the tile\n * can't be rasterized — no `OffscreenCanvas`, same as `createTilePattern`.\n */\nexport function resolvePatternSpec(spec: TilePatternSpec): TextureHandle | null {\n const id = specId(spec);\n const hit = cache.get(id);\n if (hit !== undefined) {\n markUsed(id);\n return hit;\n }\n return build(id, spec);\n}\n\nfunction markUsed(id: string): void {\n const hit = cache.get(id);\n if (hit === undefined) return;\n cache.delete(id);\n cache.set(id, hit);\n}\n\nfunction build(id: string, spec: TilePatternSpec): TextureHandle | null {\n const bitmap = TILES.has(spec.tile)\n ? rasterizeTile({\n size: tileSize(spec),\n draw: (oc) => drawTileShapes(oc, tileGeometry(spec)),\n })\n : null;\n const handle = bitmap ? registerTextureAs(id, bitmap) : null;\n cache.delete(id);\n cache.set(id, handle);\n while (cache.size > PATTERN_TILE_CACHE_LIMIT) evict(cache.keys().next().value!);\n return handle;\n}\n\nconst TILES: ReadonlySet<string> = new Set<TilePatternSpec['tile']>([\n 'hatch', 'crosshatch', 'dots', 'chunks',\n]);\n\nfunction evict(id: string): void {\n cache.delete(id);\n const source = textureRegistry.get(id)?.source;\n unregisterTexture({ id });\n if (source && 'close' in source) source.close();\n}\n\nfunction revive(id: string): void {\n let spec: unknown;\n try {\n spec = JSON.parse(id.slice(ID_PREFIX.length));\n } catch {\n return;\n }\n if (typeof spec !== 'object' || spec === null || !('tile' in spec)) return;\n build(id, spec as TilePatternSpec);\n}\n\nownTextureIds(ID_PREFIX, { revive, touched: markUsed });\n\n/** Whether a pattern payload is a spec rather than an already-built handle. */\nexport function isPatternSpec(\n payload: TextureHandle | TilePatternSpec,\n): payload is TilePatternSpec {\n return 'tile' in payload;\n}\n\n/**\n * Swap a pattern paint's spec payload for the texture that paints it,\n * leaving every other fill untouched. The built-in painters run this\n * alongside `fillInPoseFrame`; a consumer emitting its own draw commands\n * runs it at the same place it runs that one.\n *\n * A spec that can't rasterize drops the fill rather than painting garbage.\n */\nexport function resolveFillPattern(fill: PosedFill): CommandFill | null;\nexport function resolveFillPattern(fill: FillStyle): FillStyle | null;\nexport function resolveFillPattern(fill: FillStyle): FillStyle | null {\n if (fill.fill !== 'pattern') return fill;\n if (!isPatternSpec(fill.pattern)) return fill;\n const handle = resolvePatternSpec(fill.pattern);\n return handle ? { ...fill, pattern: handle } : null;\n}\n"],"mappings":";;;;AAqBA,IAAaA,IAA2B,KAElCC,IAAY,YAGZC,oBAAQ,IAAI,IAAkC;AAIpD,SAASC,EAAO,GAA+B;CAC7C,IAAMC,IAAMC,GACNC,IAAkC,CAAC;CACzC,KAAK,IAAMC,KAAK,OAAO,KAAKH,CAAG,CAAC,CAAC,KAAK,GAAG,EAAOG,KAAKH,EAAIG;CACzD,OAAON,IAAY,KAAK,UAAUK,CAAM;AAC1C;AAWA,SAAgBE,EAAmB,GAA6C;CAC9E,IAAMC,IAAKN,EAAOE,CAAI,GAChBK,IAAMR,EAAM,IAAIO,CAAE;CAKxB,OAJIC,MAAQ,KAAA,IAILC,EAAMF,GAAIJ,CAAI,KAHnBO,EAASH,CAAE,GACJC;AAGX;AAEA,SAASE,EAAS,GAAkB;CAClC,IAAMF,IAAMR,EAAM,IAAIO,CAAE;CACxB,AAAIC,MAAQ,KAAA,MACZR,EAAM,OAAOO,CAAE,GACfP,EAAM,IAAIO,GAAIC,CAAG;AACnB;AAEA,SAASC,EAAM,GAAY,GAA6C;CACtE,IAAME,IAASC,EAAM,IAAIT,EAAK,IAAI,IAC9BU,EAAc;EACd,MAAMC,EAASX,CAAI;EACnB,OAAO,MAAOY,EAAeC,GAAIC,EAAad,CAAI,CAAC;CACrD,CAAC,IACC,MACEe,IAASP,IAASQ,EAAkBZ,GAAII,CAAM,IAAI;CAGxD,KAFAX,EAAM,OAAOO,CAAE,GACfP,EAAM,IAAIO,GAAIW,CAAM,GACblB,EAAM,OAAOF,IAA0B,EAAME,EAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM;CAC9E,OAAOkB;AACT;AAEA,IAAMN,oBAA6B,IAAI,IAA6B;CAClE;CAAS;CAAc;CAAQ;AACjC,CAAC;AAED,SAASQ,EAAM,GAAkB;CAC/B,EAAM,OAAOb,CAAE;CACf,IAAMc,IAASC,EAAgB,IAAIf,CAAE,CAAC,EAAE;CAExC,AADAgB,EAAkB,EAAE,MAAG,CAAC,GACpBF,KAAU,WAAWA,KAAQA,EAAO,MAAM;AAChD;AAEA,SAASG,EAAO,GAAkB;CAChC,IAAIrB;CACJ,IAAI;EACF,IAAO,KAAK,MAAMI,EAAG,MAAM,CAAgB,CAAC;CAC9C,QAAQ;EACN;CACF;CACA,AAAI,OAAOJ,KAAS,YAAYA,KAAmB,UAAUA,KAC7DM,EAAMF,GAAIJ,CAAuB;AACnC;AAEAsB,EAAc1B,GAAW;CAAE;CAAQ,SAASW;AAAS,CAAC;AAGtD,SAAgBgB,EACd,GAC4B;CAC5B,OAAO,UAAUC;AACnB;AAYA,SAAgBC,EAAmB,GAAmC;CAEpE,IADIC,EAAK,SAAS,aACd,CAACH,EAAcG,EAAK,OAAO,GAAG,OAAOA;CACzC,IAAMX,IAASZ,EAAmBuB,EAAK,OAAO;CAC9C,OAAOX,IAAS;EAAE,GAAGW;EAAM,SAASX;CAAO,IAAI;AACjD"}
|
|
@@ -26,9 +26,14 @@ function l(l) {
|
|
|
26
26
|
return t ?? c;
|
|
27
27
|
}, v = () => {
|
|
28
28
|
let { previews: e } = d.current, n = u.current.scene, r = 0;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
try {
|
|
30
|
+
e.size > 0 && n.batch(u.current.label ?? "Layout", () => {
|
|
31
|
+
for (let [t, i] of e) n.get(t) === void 0 || n.isLocked(t) || (n.setPose(t, i), r++);
|
|
32
|
+
});
|
|
33
|
+
} finally {
|
|
34
|
+
t(g()), e.clear();
|
|
35
|
+
}
|
|
36
|
+
u.current.onCommit?.(r);
|
|
32
37
|
}, y = () => {
|
|
33
38
|
f.current = !1, h.cancel();
|
|
34
39
|
}, b = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePoseRun.js","names":["EMPTY","usePoseRun","optsRef","useLatest","opts","state","useRef","running","frame","mounted","loop","useVisibleRaf","syncState","useCallback","pinnedNow","pinned","id","overrides","entry","commit","scene","moved","previews","pose","dropPreviewOverrides","halt","tick","poses","syncPreviewOverrides","done","useEffect","useMemo"],"sources":["../../../src/features/poseRun/usePoseRun.ts"],"sourcesContent":["/**\n * A pose run: poses published per frame, committed once.\n *\n * The frames go to `scene.overrides` — the ephemeral table a drag publishes to,\n * which `effectivePose`, the derived-geometry lookup and the pick source\n * already read. So an edge follows a node the run is moving with nothing added,\n * and the whole run lands as one undo entry when it settles.\n *\n * It knows nothing about layout. Each frame it asks `step` for poses and\n * whether it is finished; a relaxation, an eased transition to a target, and a\n * scrubbed timeline are all the same shape to it.\n *\n * **A node another gesture owns is pinned, not driven.** The run publishes\n * entries it holds by reference, so an override it did not put there belongs to\n * somebody else — a drag, a resize. It never writes that id, never commits it,\n * and hands it to `step` as pinned so the producer can hold its neighbors\n * against it.\n */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { useVisibleRaf } from '../../scheduling/useVisibleRaf';\nimport {\n dropPreviewOverrides,\n syncPreviewOverrides,\n} from '../../interactions/actions/previewOverrides';\nimport type { NodeId, Scene } from '../../core/scene/types';\n\n/** What a producer is told before it is asked for a frame. */\nexport interface PoseRunCtx<TPose> {\n /** Nodes another gesture is moving, and the pose each is drawn at. The run\n * will not write these, so a producer holds them fixed. */\n pinned: ReadonlyMap<NodeId, TPose>;\n /** Frames this run has produced, starting at 0. */\n frame: number;\n}\n\n/** One frame of a run. */\nexport interface PoseRunStep<TPose> {\n /** Where the nodes it drives are this frame — the whole picture, not a\n * delta. An id the frame omits stops being published, which is how a node\n * leaves the run's hands: a producer that drops a grabbed box and its rows\n * from one frame has released them by the next. */\n poses: Iterable<readonly [NodeId, TPose]>;\n /** Whether the run is finished. The frame is published either way, and a\n * finished run commits. */\n done: boolean;\n}\n\n/** Options for `usePoseRun`. Read fresh each frame, so they may change mid-run. */\nexport interface UsePoseRunOptions<TPose> {\n scene: Scene<unknown, string, TPose>;\n /** Called once per frame while running, for that frame's poses. */\n step: (ctx: PoseRunCtx<TPose>) => PoseRunStep<TPose>;\n /** The undo entry's name. Default `'Layout'`. */\n label?: string;\n /** How many nodes the commit wrote. Fires after the ops land. */\n onCommit?: (moved: number) => void;\n /** Clock injection, for tests. Defaults to `requestAnimationFrame`. */\n requestFrame?: (cb: (t: number) => void) => number;\n cancelFrame?: (handle: number) => void;\n}\n\n/** Controls for a pose run. Stable for the life of the component. */\nexport interface PoseRun {\n /** Begin a new run, counting frames from 0. Idempotent while running. */\n start(): void;\n /** Finish now: commit what is published, then drop the overrides. */\n stop(): void;\n /** Abandon: drop the overrides and write nothing, like a canceled drag. */\n cancel(): void;\n isRunning(): boolean;\n}\n\nconst EMPTY: ReadonlyMap<NodeId, unknown> = new Map<NodeId, unknown>();\n\n/**\n * Drive node poses frame by frame through `scene.overrides`, committing them as\n * one undo entry when `step` reports done or `stop` is called. Idle until\n * `start`; unmounting abandons the run without writing.\n */\nexport function usePoseRun<TPose>(opts: UsePoseRunOptions<TPose>): PoseRun {\n const optsRef = useLatest(opts);\n\n /** This frame's poses, and the entries published for them. Held by\n * reference so a frame mutates in place rather than re-notifying per node. */\n const state = useRef({\n previews: new Map<NodeId, unknown>(),\n overrideEntries: new Map<NodeId, { pose: unknown }>(),\n });\n const running = useRef(false);\n const frame = useRef(0);\n const mounted = useRef(true);\n\n // The gate defaults an absent clock to `requestAnimationFrame` itself, so\n // these pass straight through rather than being defaulted twice.\n const loop = useVisibleRaf(() => { tick(); }, {\n requestFrame: opts.requestFrame,\n cancelFrame: opts.cancelFrame,\n });\n\n /** The scene as the previous frame left it, for `syncPreviewOverrides`. */\n const syncState = useCallback((): {\n scene: Scene<unknown, string, unknown>;\n previews: Map<NodeId, unknown>;\n overrideEntries: Map<NodeId, { pose: unknown }>;\n } => ({\n scene: optsRef.current.scene,\n previews: state.current.previews,\n overrideEntries: state.current.overrideEntries,\n }), [optsRef]);\n\n /** Overrides somebody else published, and the pose each carries. */\n const pinnedNow = (): ReadonlyMap<NodeId, TPose> => {\n const { overrides } = optsRef.current.scene;\n let pinned: Map<NodeId, TPose> | undefined;\n for (const id of overrides.ids()) {\n const entry = overrides.get(id);\n if (entry === undefined || entry === state.current.overrideEntries.get(id)) continue;\n if (entry.pose === undefined) continue;\n (pinned ??= new Map()).set(id, entry.pose);\n }\n return pinned ?? (EMPTY as ReadonlyMap<NodeId, TPose>);\n };\n\n const commit = (): void => {\n const { previews } = state.current;\n const scene = optsRef.current.scene;\n let moved = 0;\n if (previews.size > 0) {\n scene.batch(optsRef.current.label ?? 'Layout', () => {\n for (const [id, pose] of previews) {\n if (scene.get(id) === undefined) continue;\n scene.setPose(id, pose as TPose);\n moved++;\n }\n });\n }\n // After the ops land: dropping first leaves a window in which the scene\n // answers with the pre-run pose.\n dropPreviewOverrides(syncState());\n previews.clear();\n optsRef.current.onCommit?.(moved);\n };\n\n const halt = (): void => {\n running.current = false;\n loop.cancel();\n };\n\n const tick = (): void => {\n if (!mounted.current || !running.current) return;\n const pinned = pinnedNow();\n const { poses, done } = optsRef.current.step({ pinned, frame: frame.current });\n frame.current++;\n // Each frame replaces the last: an id this one omits — a node just\n // grabbed, or a row under one — stops being published, rather than being\n // held at a pose from before the press while the gesture carries it\n // somewhere else.\n state.current.previews.clear();\n for (const [id, pose] of poses) {\n if (pinned.has(id)) continue;\n state.current.previews.set(id, pose);\n }\n syncPreviewOverrides(syncState());\n if (done) {\n halt();\n commit();\n return;\n }\n loop.request();\n };\n\n useEffect(() => {\n mounted.current = true;\n const { previews } = state.current;\n return () => {\n mounted.current = false;\n loop.cancel();\n // Teardown is not a decision: an unmounted run abandons its frames\n // rather than writing them.\n dropPreviewOverrides(syncState());\n previews.clear();\n };\n }, [loop, syncState]);\n\n return useMemo<PoseRun>(() => ({\n start() {\n if (running.current || !mounted.current) return;\n running.current = true;\n frame.current = 0;\n loop.request();\n },\n stop() {\n if (!running.current) return;\n halt();\n commit();\n },\n cancel() {\n halt();\n dropPreviewOverrides(syncState());\n state.current.previews.clear();\n },\n isRunning: () => running.current,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }), [loop]);\n}\n"],"mappings":";;;;;AAyEA,IAAMA,oBAAsC,IAAI,IAAqB;AAOrE,SAAgBC,EAAkB,GAAyC;CACzE,IAAMC,IAAUC,EAAUC,CAAI,GAIxBC,IAAQC,EAAO;EACnB,0BAAU,IAAI,IAAqB;EACnC,iCAAiB,IAAI,IAA+B;CACtD,CAAC,GACKC,IAAUD,EAAO,EAAK,GACtBE,IAAQF,EAAO,CAAC,GAChBG,IAAUH,EAAO,EAAI,GAIrBI,IAAOC,QAAoB;EAAE,EAAK;CAAG,GAAG;EAC5C,cAAcP,EAAK;EACnB,aAAaA,EAAK;CACpB,CAAC,GAGKQ,IAAYC,SAIZ;EACJ,OAAOX,EAAQ,QAAQ;EACvB,UAAUG,EAAM,QAAQ;EACxB,iBAAiBA,EAAM,QAAQ;CACjC,IAAI,CAACH,CAAO,CAAC,GAGPY,UAA8C;EAClD,IAAM,EAAE,iBAAcZ,EAAQ,QAAQ,OAClCa;EACJ,KAAK,IAAMC,KAAMC,EAAU,IAAI,GAAG;GAChC,IAAMC,IAAQD,EAAU,IAAID,CAAE;GAC9B,AAAIE,MAAU,KAAA,KAAaA,MAAUb,EAAM,QAAQ,gBAAgB,IAAIW,CAAE,KACrEE,EAAM,SAAS,KAAA,MAClB,sBAAW,IAAI,IAAI,EAAA,CAAG,IAAIF,GAAIE,EAAM,IAAI;EAC3C;EACA,OAAOH,KAAWf;CACpB,GAEMmB,UAAqB;EACzB,IAAM,EAAE,gBAAad,EAAM,SACrBe,IAAQlB,EAAQ,QAAQ,OAC1BmB,IAAQ;EAcZ,AAbIC,EAAS,OAAO,KAClBF,EAAM,MAAMlB,EAAQ,QAAQ,SAAS,gBAAgB;GACnD,KAAK,IAAM,CAACc,GAAIO,MAASD,GACvB,AAAIF,EAAM,IAAIJ,CAAE,MAAM,KAAA,MACtBI,EAAM,QAAQJ,GAAIO,CAAa,GAC/B;EAEJ,CAAC,GAIHC,EAAqBZ,EAAU,CAAC,GAChCU,EAAS,MAAM,GACfpB,EAAQ,QAAQ,WAAWmB,CAAK;CAClC,GAEMI,UAAmB;EAEvB,AADA,EAAQ,UAAU,IAClBf,EAAK,OAAO;CACd,GAEMgB,UAAmB;EACvB,IAAI,CAACjB,EAAQ,WAAW,CAACF,EAAQ,SAAS;EAC1C,IAAMQ,IAASD,EAAU,GACnB,EAAE,UAAO,YAASZ,EAAQ,QAAQ,KAAK;GAAE;GAAQ,OAAOM,EAAM;EAAQ,CAAC;EAM7E,AALA,EAAM,WAKNH,EAAM,QAAQ,SAAS,MAAM;EAC7B,KAAK,IAAM,CAACW,GAAIO,MAASI,GACvB,AAAIZ,EAAO,IAAIC,CAAE,KACjBX,EAAM,QAAQ,SAAS,IAAIW,GAAIO,CAAI;EAGrC,IADAK,EAAqBhB,EAAU,CAAC,GAC5BiB,GAAM;GAER,AADAJ,EAAK,GACLN,EAAO;GACP;EACF;EACA,EAAK,QAAQ;CACf;CAeA,OAbAW,QAAgB;EACd,EAAQ,UAAU;EAClB,IAAM,EAAE,gBAAazB,EAAM;EAC3B,aAAa;GAMX,AALA,EAAQ,UAAU,IAClBK,EAAK,OAAO,GAGZc,EAAqBZ,EAAU,CAAC,GAChCU,EAAS,MAAM;EACjB;CACF,GAAG,CAACZ,GAAME,CAAS,CAAC,GAEbmB,SAAwB;EAC7B,QAAQ;GACN,AAAI,GAAQ,WAAYtB,EAAQ,YAChC,EAAQ,UAAU,IAClB,EAAM,UAAU,GAChBC,EAAK,QAAQ;EACf;EACA,OAAO;GACL,AAAKH,EAAQ,YACbkB,EAAK,GACLN,EAAO;EACT;EACA,SAAS;GAGP,AAFAM,EAAK,GACLD,EAAqBZ,EAAU,CAAC,GAChCP,EAAM,QAAQ,SAAS,MAAM;EAC/B;EACA,iBAAiBE,EAAQ;CAE3B,IAAI,CAACG,CAAI,CAAC;AACZ"}
|
|
1
|
+
{"version":3,"file":"usePoseRun.js","names":["EMPTY","usePoseRun","optsRef","useLatest","opts","state","useRef","running","frame","mounted","loop","useVisibleRaf","syncState","useCallback","pinnedNow","pinned","id","overrides","entry","commit","scene","moved","previews","pose","dropPreviewOverrides","halt","tick","poses","syncPreviewOverrides","done","useEffect","useMemo"],"sources":["../../../src/features/poseRun/usePoseRun.ts"],"sourcesContent":["/**\n * A pose run: poses published per frame, committed once.\n *\n * The frames go to `scene.overrides` — the ephemeral table a drag publishes to,\n * which `effectivePose`, the derived-geometry lookup and the pick source\n * already read. So an edge follows a node the run is moving with nothing added,\n * and the whole run lands as one undo entry when it settles.\n *\n * It knows nothing about layout. Each frame it asks `step` for poses and\n * whether it is finished; a relaxation, an eased transition to a target, and a\n * scrubbed timeline are all the same shape to it.\n *\n * **A node another gesture owns is pinned, not driven.** The run publishes\n * entries it holds by reference, so an override it did not put there belongs to\n * somebody else — a drag, a resize. It never writes that id, never commits it,\n * and hands it to `step` as pinned so the producer can hold its neighbors\n * against it.\n */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { useVisibleRaf } from '../../scheduling/useVisibleRaf';\nimport {\n dropPreviewOverrides,\n syncPreviewOverrides,\n} from '../../interactions/actions/previewOverrides';\nimport type { NodeId, Scene } from '../../core/scene/types';\n\n/** What a producer is told before it is asked for a frame. */\nexport interface PoseRunCtx<TPose> {\n /** Nodes another gesture is moving, and the pose each is drawn at. The run\n * will not write these, so a producer holds them fixed. */\n pinned: ReadonlyMap<NodeId, TPose>;\n /** Frames this run has produced, starting at 0. */\n frame: number;\n}\n\n/** One frame of a run. */\nexport interface PoseRunStep<TPose> {\n /** Where the nodes it drives are this frame — the whole picture, not a\n * delta. An id the frame omits stops being published, which is how a node\n * leaves the run's hands: a producer that drops a grabbed box and its rows\n * from one frame has released them by the next. */\n poses: Iterable<readonly [NodeId, TPose]>;\n /** Whether the run is finished. The frame is published either way, and a\n * finished run commits. */\n done: boolean;\n}\n\n/** Options for `usePoseRun`. Read fresh each frame, so they may change mid-run. */\nexport interface UsePoseRunOptions<TPose> {\n scene: Scene<unknown, string, TPose>;\n /** Called once per frame while running, for that frame's poses. */\n step: (ctx: PoseRunCtx<TPose>) => PoseRunStep<TPose>;\n /** The undo entry's name. Default `'Layout'`. */\n label?: string;\n /** How many nodes the commit wrote. Fires after the ops land. */\n onCommit?: (moved: number) => void;\n /** Clock injection, for tests. Defaults to `requestAnimationFrame`. */\n requestFrame?: (cb: (t: number) => void) => number;\n cancelFrame?: (handle: number) => void;\n}\n\n/** Controls for a pose run. Stable for the life of the component. */\nexport interface PoseRun {\n /** Begin a new run, counting frames from 0. Idempotent while running. */\n start(): void;\n /** Finish now: commit what is published, then drop the overrides. */\n stop(): void;\n /** Abandon: drop the overrides and write nothing, like a canceled drag. */\n cancel(): void;\n isRunning(): boolean;\n}\n\nconst EMPTY: ReadonlyMap<NodeId, unknown> = new Map<NodeId, unknown>();\n\n/**\n * Drive node poses frame by frame through `scene.overrides`, committing them as\n * one undo entry when `step` reports done or `stop` is called. Idle until\n * `start`; unmounting abandons the run without writing.\n */\nexport function usePoseRun<TPose>(opts: UsePoseRunOptions<TPose>): PoseRun {\n const optsRef = useLatest(opts);\n\n /** This frame's poses, and the entries published for them. Held by\n * reference so a frame mutates in place rather than re-notifying per node. */\n const state = useRef({\n previews: new Map<NodeId, unknown>(),\n overrideEntries: new Map<NodeId, { pose: unknown }>(),\n });\n const running = useRef(false);\n const frame = useRef(0);\n const mounted = useRef(true);\n\n // The gate defaults an absent clock to `requestAnimationFrame` itself, so\n // these pass straight through rather than being defaulted twice.\n const loop = useVisibleRaf(() => { tick(); }, {\n requestFrame: opts.requestFrame,\n cancelFrame: opts.cancelFrame,\n });\n\n /** The scene as the previous frame left it, for `syncPreviewOverrides`. */\n const syncState = useCallback((): {\n scene: Scene<unknown, string, unknown>;\n previews: Map<NodeId, unknown>;\n overrideEntries: Map<NodeId, { pose: unknown }>;\n } => ({\n scene: optsRef.current.scene,\n previews: state.current.previews,\n overrideEntries: state.current.overrideEntries,\n }), [optsRef]);\n\n /** Overrides somebody else published, and the pose each carries. */\n const pinnedNow = (): ReadonlyMap<NodeId, TPose> => {\n const { overrides } = optsRef.current.scene;\n let pinned: Map<NodeId, TPose> | undefined;\n for (const id of overrides.ids()) {\n const entry = overrides.get(id);\n if (entry === undefined || entry === state.current.overrideEntries.get(id)) continue;\n if (entry.pose === undefined) continue;\n (pinned ??= new Map()).set(id, entry.pose);\n }\n return pinned ?? (EMPTY as ReadonlyMap<NodeId, TPose>);\n };\n\n const commit = (): void => {\n const { previews } = state.current;\n const scene = optsRef.current.scene;\n let moved = 0;\n try {\n if (previews.size > 0) {\n scene.batch(optsRef.current.label ?? 'Layout', () => {\n for (const [id, pose] of previews) {\n if (scene.get(id) === undefined || scene.isLocked(id)) continue;\n scene.setPose(id, pose as TPose);\n moved++;\n }\n });\n }\n } finally {\n // After the ops land: dropping first leaves a window in which the scene\n // answers with the pre-run pose.\n dropPreviewOverrides(syncState());\n previews.clear();\n }\n optsRef.current.onCommit?.(moved);\n };\n\n const halt = (): void => {\n running.current = false;\n loop.cancel();\n };\n\n const tick = (): void => {\n if (!mounted.current || !running.current) return;\n const pinned = pinnedNow();\n const { poses, done } = optsRef.current.step({ pinned, frame: frame.current });\n frame.current++;\n // Each frame replaces the last: an id this one omits — a node just\n // grabbed, or a row under one — stops being published, rather than being\n // held at a pose from before the press while the gesture carries it\n // somewhere else.\n state.current.previews.clear();\n for (const [id, pose] of poses) {\n if (pinned.has(id)) continue;\n state.current.previews.set(id, pose);\n }\n syncPreviewOverrides(syncState());\n if (done) {\n halt();\n commit();\n return;\n }\n loop.request();\n };\n\n useEffect(() => {\n mounted.current = true;\n const { previews } = state.current;\n return () => {\n mounted.current = false;\n loop.cancel();\n // Teardown is not a decision: an unmounted run abandons its frames\n // rather than writing them.\n dropPreviewOverrides(syncState());\n previews.clear();\n };\n }, [loop, syncState]);\n\n return useMemo<PoseRun>(() => ({\n start() {\n if (running.current || !mounted.current) return;\n running.current = true;\n frame.current = 0;\n loop.request();\n },\n stop() {\n if (!running.current) return;\n halt();\n commit();\n },\n cancel() {\n halt();\n dropPreviewOverrides(syncState());\n state.current.previews.clear();\n },\n isRunning: () => running.current,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }), [loop]);\n}\n"],"mappings":";;;;;AAyEA,IAAMA,oBAAsC,IAAI,IAAqB;AAOrE,SAAgBC,EAAkB,GAAyC;CACzE,IAAMC,IAAUC,EAAUC,CAAI,GAIxBC,IAAQC,EAAO;EACnB,0BAAU,IAAI,IAAqB;EACnC,iCAAiB,IAAI,IAA+B;CACtD,CAAC,GACKC,IAAUD,EAAO,EAAK,GACtBE,IAAQF,EAAO,CAAC,GAChBG,IAAUH,EAAO,EAAI,GAIrBI,IAAOC,QAAoB;EAAE,EAAK;CAAG,GAAG;EAC5C,cAAcP,EAAK;EACnB,aAAaA,EAAK;CACpB,CAAC,GAGKQ,IAAYC,SAIZ;EACJ,OAAOX,EAAQ,QAAQ;EACvB,UAAUG,EAAM,QAAQ;EACxB,iBAAiBA,EAAM,QAAQ;CACjC,IAAI,CAACH,CAAO,CAAC,GAGPY,UAA8C;EAClD,IAAM,EAAE,iBAAcZ,EAAQ,QAAQ,OAClCa;EACJ,KAAK,IAAMC,KAAMC,EAAU,IAAI,GAAG;GAChC,IAAMC,IAAQD,EAAU,IAAID,CAAE;GAC9B,AAAIE,MAAU,KAAA,KAAaA,MAAUb,EAAM,QAAQ,gBAAgB,IAAIW,CAAE,KACrEE,EAAM,SAAS,KAAA,MAClB,sBAAW,IAAI,IAAI,EAAA,CAAG,IAAIF,GAAIE,EAAM,IAAI;EAC3C;EACA,OAAOH,KAAWf;CACpB,GAEMmB,UAAqB;EACzB,IAAM,EAAE,gBAAad,EAAM,SACrBe,IAAQlB,EAAQ,QAAQ,OAC1BmB,IAAQ;EACZ,IAAI;GACF,AAAIC,EAAS,OAAO,KAClBF,EAAM,MAAMlB,EAAQ,QAAQ,SAAS,gBAAgB;IACnD,KAAK,IAAM,CAACc,GAAIO,MAASD,GACvB,AAAIF,EAAM,IAAIJ,CAAE,MAAM,KAAA,KAAaI,EAAM,SAASJ,CAAE,MACpDI,EAAM,QAAQJ,GAAIO,CAAa,GAC/B;GAEJ,CAAC;EAEL,UAAU;GAIR,AADAC,EAAqBZ,EAAU,CAAC,GAChCU,EAAS,MAAM;EACjB;EACA,EAAQ,QAAQ,WAAWD,CAAK;CAClC,GAEMI,UAAmB;EAEvB,AADA,EAAQ,UAAU,IAClBf,EAAK,OAAO;CACd,GAEMgB,UAAmB;EACvB,IAAI,CAACjB,EAAQ,WAAW,CAACF,EAAQ,SAAS;EAC1C,IAAMQ,IAASD,EAAU,GACnB,EAAE,UAAO,YAASZ,EAAQ,QAAQ,KAAK;GAAE;GAAQ,OAAOM,EAAM;EAAQ,CAAC;EAM7E,AALA,EAAM,WAKNH,EAAM,QAAQ,SAAS,MAAM;EAC7B,KAAK,IAAM,CAACW,GAAIO,MAASI,GACvB,AAAIZ,EAAO,IAAIC,CAAE,KACjBX,EAAM,QAAQ,SAAS,IAAIW,GAAIO,CAAI;EAGrC,IADAK,EAAqBhB,EAAU,CAAC,GAC5BiB,GAAM;GAER,AADAJ,EAAK,GACLN,EAAO;GACP;EACF;EACA,EAAK,QAAQ;CACf;CAeA,OAbAW,QAAgB;EACd,EAAQ,UAAU;EAClB,IAAM,EAAE,gBAAazB,EAAM;EAC3B,aAAa;GAMX,AALA,EAAQ,UAAU,IAClBK,EAAK,OAAO,GAGZc,EAAqBZ,EAAU,CAAC,GAChCU,EAAS,MAAM;EACjB;CACF,GAAG,CAACZ,GAAME,CAAS,CAAC,GAEbmB,SAAwB;EAC7B,QAAQ;GACN,AAAI,GAAQ,WAAYtB,EAAQ,YAChC,EAAQ,UAAU,IAClB,EAAM,UAAU,GAChBC,EAAK,QAAQ;EACf;EACA,OAAO;GACL,AAAKH,EAAQ,YACbkB,EAAK,GACLN,EAAO;EACT;EACA,SAAS;GAGP,AAFAM,EAAK,GACLD,EAAqBZ,EAAU,CAAC,GAChCP,EAAM,QAAQ,SAAS,MAAM;EAC/B;EACA,iBAAiBE,EAAQ;CAE3B,IAAI,CAACG,CAAI,CAAC;AACZ"}
|
|
@@ -39,6 +39,11 @@ function m(e, t) {
|
|
|
39
39
|
a,
|
|
40
40
|
o
|
|
41
41
|
]);
|
|
42
|
+
let d = s(i), f = s(c);
|
|
43
|
+
r(() => () => {
|
|
44
|
+
let e = d.current;
|
|
45
|
+
e && JSON.stringify([e.selection, e.kinds]) === f.current && e.publishSelection([]);
|
|
46
|
+
}, [d, f]);
|
|
42
47
|
}
|
|
43
48
|
//#endregion
|
|
44
49
|
export { d as SelectionContextProvider, p as SelectionContextProviderIfRoot, m as usePublishSelection, f as useSelectionContext };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectionContext.js","names":["_Fragment","SelectionContext","createContext","SelectionContextProvider","state","setState","useState","lastSerializedRef","useRef","publishSelection","useCallback","next","ids","kinds","value","useMemo","useSelectionContext","useContext","SelectionContextProviderIfRoot","Fragment","usePublishSelection","ctx","idsRef","useLatest","kindsRef","serialized","publish"],"sources":["../../../src/features/selection/SelectionContext.tsx"],"sourcesContent":["/**\n * @experimental\n * SelectionContext — a tiny ambient context that publishes \"what's currently\n * selected\" so non-canvas UI (command palette, status bar, breadcrumbs,\n * undo-label generation) can read it without threading a selection prop\n * through the tree.\n *\n * **Single-slot semantics (v1):** the context holds one selection array.\n * Whichever canvas (or other publisher) calls `publishSelection` most\n * recently owns the slot. For single-canvas pages — the typical case —\n * this Just Works. Multi-canvas pages get last-writer-wins, which is\n * approximately \"most recently rendered.\" A v2 could become focus-aware\n * (canvas claims the slot on focus, releases on blur), but that's deferred\n * until a real multi-canvas use case lands.\n *\n * `<SceneCanvas>` calls `publishSelection` automatically when wrapped in a\n * `<SelectionContext>`. Bare-`<Canvas>` consumers opt in by calling\n * `useSelectionContext().publishSelection(ids)` themselves.\n */\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from 'core/scene/types';\n\n/** @experimental */\nexport interface SelectionContextValue {\n /** The most-recently-published selection. Empty array when no canvas\n * has published or when the active canvas's selection is empty. */\n readonly selection: readonly NodeId[];\n /** Optional per-id kind label, parallel to `selection`. Publishers that\n * know their domain populate it (e.g. `'rectangle'`, `'path'`, `'group'`)\n * so consumers can render type-aware copy. Sparse entries (`undefined`)\n * are allowed; consumers should treat the kinds array as best-effort.\n *\n * See `docs/TODO.md` (Tier 1.5 → \"Typed scene-object references\") for the\n * longer-term direction toward a proper id+kind value type. */\n readonly kinds?: readonly (string | undefined)[];\n /** Publish the calling canvas's selection. Idempotent — calling with\n * the same array (by content equality) does not trigger a re-render\n * of the consumer tree. */\n publishSelection(ids: readonly NodeId[], kinds?: readonly (string | undefined)[]): void;\n}\n\nconst SelectionContext = createContext<SelectionContextValue | null>(null);\n\n/**\n * @experimental\n * Wrap the part of the React tree that should share a selection context.\n * Usually placed at the demo / app root, alongside `<ActionsProvider>`.\n */\nexport function SelectionContextProvider({ children }: { children: ReactNode }): ReactNode {\n const [state, setState] = useState<{\n selection: readonly NodeId[];\n kinds: readonly (string | undefined)[] | undefined;\n }>({ selection: [], kinds: undefined });\n const lastSerializedRef = useRef<string>('');\n\n const publishSelection = useCallback((\n ids: readonly NodeId[],\n kinds?: readonly (string | undefined)[],\n ): void => {\n // Cheap content equality via JSON serialization over both ids and kinds.\n // Selection arrays are typically small (≤ a few dozen entries); the cost\n // is dwarfed by avoiding a re-render when nothing changed.\n const next = JSON.stringify([ids, kinds]);\n if (next === lastSerializedRef.current) return;\n lastSerializedRef.current = next;\n setState({ selection: ids, kinds });\n }, []);\n\n const value = useMemo<SelectionContextValue>(\n () => ({\n selection: state.selection,\n ...(state.kinds !== undefined ? { kinds: state.kinds } : {}),\n publishSelection,\n }),\n [state, publishSelection],\n );\n\n return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;\n}\n\n/**\n * @experimental\n * Read the current selection-context value. Returns `null` when no\n * `<SelectionContextProvider>` is in scope; consumers should fall back\n * gracefully (e.g. command palette hides the \"N selected\" header).\n */\nexport function useSelectionContext(): SelectionContextValue | null {\n return useContext(SelectionContext);\n}\n\n/**\n * @experimental\n * Conditional `<SelectionContextProvider>` wrapper. Mounts a provider only\n * when no parent provider is in scope — otherwise renders children unwrapped\n * so callers (e.g. `<WeaselProvider>`) defer to the host's existing scope.\n * Mirrors `ActionsProviderIfRoot` / `DepRegistryProviderIfRoot`.\n */\nexport function SelectionContextProviderIfRoot({\n children,\n}: {\n children: ReactNode;\n}): ReactNode {\n const parent = useContext(SelectionContext);\n if (parent !== null) return <>{children}</>;\n return <SelectionContextProvider>{children}</SelectionContextProvider>;\n}\n\n/**\n * @experimental\n * Convenience hook for canvas components: publishes the supplied `ids`\n * into the surrounding `<SelectionContextProvider>` whenever the\n * content of `ids` changes. No-op when there's no provider in scope.\n *\n * Used by `<SceneCanvas>` internally to auto-publish; bare-Canvas\n * consumers call `useSelectionContext()` and `publishSelection` directly.\n */\nexport function usePublishSelection(\n ids: readonly NodeId[],\n kinds?: readonly (string | undefined)[],\n): void {\n const ctx = useContext(SelectionContext);\n // Track latest values via refs so the effect's deps array can use a stable\n // serialization — avoids re-firing on identity-only changes.\n const idsRef = useLatest(ids);\n const kindsRef = useLatest(kinds);\n const serialized = JSON.stringify([ids, kinds]);\n // Depend on `publishSelection` rather than `ctx`: the provider mints a new\n // context value on every publish, so keying on `ctx` refires this effect for\n // every publisher whenever any one of them publishes.\n const publish = ctx?.publishSelection;\n useEffect(() => {\n if (!publish) return;\n publish(idsRef.current, kindsRef.current);\n }, [publish, serialized, idsRef, kindsRef]);\n}\n"],"mappings":";;;;AAmDA,IAAMC,IAAmBC,EAA4C,IAAI;AAOzE,SAAgBC,EAAyB,EAAE,eAAgD;CACzF,IAAM,CAACC,GAAOC,KAAYC,EAGvB;EAAE,WAAW,CAAC;EAAG,OAAO,KAAA;CAAU,CAAC,GAChCC,IAAoBC,EAAe,EAAE,GAErCC,IAAmBC,GACvB,GACA,MACS;EAIT,IAAMC,IAAO,KAAK,UAAU,CAACC,GAAKC,CAAK,CAAC;EACxC,AAAIF,MAASJ,EAAkB,YAC/B,EAAkB,UAAUI,GAC5BN,EAAS;GAAE,WAAWO;GAAK;EAAM,CAAC;CACpC,GAAG,CAAC,CAAC,GAECE,IAAQC,SACL;EACL,WAAWX,EAAM;EACjB,GAAIA,EAAM,UAAU,KAAA,IAAqC,CAAC,IAA1B,EAAE,OAAOA,EAAM,MAAM;EACrD;CACF,IACA,CAACA,GAAOK,CAAgB,CAC1B;CAEA,OAAO,kBAACR,EAAiB,UAAlB;EAAkC;EAAQ;CAAoC,CAAA;AACvF;AAQA,SAAgBe,IAAoD;CAClE,OAAOC,EAAWhB,CAAgB;AACpC;AASA,SAAgBiB,EAA+B,EAC7C,eAGY;CAGZ,OAFeD,EAAWhB,CACtB,MAAW,OACR,kBAACE,GAAD,EAA2B,YAAmC,CAAA,IADzC,kBAAAH,GAAA,EAAG,YAAW,CAAA;AAE5C;AAWA,SAAgBoB,EACd,GACA,GACM;CACN,IAAMC,IAAMJ,EAAWhB,CAAgB,GAGjCqB,IAASC,EAAUX,CAAG,GACtBY,IAAWD,EAAUV,CAAK,GAC1BY,IAAa,KAAK,UAAU,CAACb,GAAKC,CAAK,CAAC,GAIxCa,IAAUL,GAAK;CACrB,QAAgB;EACd,AAAKK,KACLA,EAAQJ,EAAO,SAASE,EAAS,OAAO;CAC1C,GAAG;EAACE;EAASD;EAAYH;EAAQE;CAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"SelectionContext.js","names":["_Fragment","SelectionContext","createContext","SelectionContextProvider","state","setState","useState","lastSerializedRef","useRef","publishSelection","useCallback","next","ids","kinds","value","useMemo","useSelectionContext","useContext","SelectionContextProviderIfRoot","Fragment","usePublishSelection","ctx","idsRef","useLatest","kindsRef","serialized","publish","ctxRef","serializedRef","slot"],"sources":["../../../src/features/selection/SelectionContext.tsx"],"sourcesContent":["/**\n * @experimental\n * SelectionContext — a tiny ambient context that publishes \"what's currently\n * selected\" so non-canvas UI (command palette, status bar, breadcrumbs,\n * undo-label generation) can read it without threading a selection prop\n * through the tree.\n *\n * **Single-slot semantics (v1):** the context holds one selection array.\n * Whichever canvas (or other publisher) calls `publishSelection` most\n * recently owns the slot. For single-canvas pages — the typical case —\n * this Just Works. Multi-canvas pages get last-writer-wins, which is\n * approximately \"most recently rendered.\" A v2 could become focus-aware\n * (canvas claims the slot on focus, releases on blur), but that's deferred\n * until a real multi-canvas use case lands.\n *\n * `<SceneCanvas>` calls `publishSelection` automatically when wrapped in a\n * `<SelectionContext>`. Bare-`<Canvas>` consumers opt in by calling\n * `useSelectionContext().publishSelection(ids)` themselves.\n */\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from 'core/scene/types';\n\n/** @experimental */\nexport interface SelectionContextValue {\n /** The most-recently-published selection. Empty array when no canvas\n * has published or when the active canvas's selection is empty. */\n readonly selection: readonly NodeId[];\n /** Optional per-id kind label, parallel to `selection`. Publishers that\n * know their domain populate it (e.g. `'rectangle'`, `'path'`, `'group'`)\n * so consumers can render type-aware copy. Sparse entries (`undefined`)\n * are allowed; consumers should treat the kinds array as best-effort.\n *\n * See `docs/TODO.md` (Tier 1.5 → \"Typed scene-object references\") for the\n * longer-term direction toward a proper id+kind value type. */\n readonly kinds?: readonly (string | undefined)[];\n /** Publish the calling canvas's selection. Idempotent — calling with\n * the same array (by content equality) does not trigger a re-render\n * of the consumer tree. */\n publishSelection(ids: readonly NodeId[], kinds?: readonly (string | undefined)[]): void;\n}\n\nconst SelectionContext = createContext<SelectionContextValue | null>(null);\n\n/**\n * @experimental\n * Wrap the part of the React tree that should share a selection context.\n * Usually placed at the demo / app root, alongside `<ActionsProvider>`.\n */\nexport function SelectionContextProvider({ children }: { children: ReactNode }): ReactNode {\n const [state, setState] = useState<{\n selection: readonly NodeId[];\n kinds: readonly (string | undefined)[] | undefined;\n }>({ selection: [], kinds: undefined });\n const lastSerializedRef = useRef<string>('');\n\n const publishSelection = useCallback((\n ids: readonly NodeId[],\n kinds?: readonly (string | undefined)[],\n ): void => {\n // Cheap content equality via JSON serialization over both ids and kinds.\n // Selection arrays are typically small (≤ a few dozen entries); the cost\n // is dwarfed by avoiding a re-render when nothing changed.\n const next = JSON.stringify([ids, kinds]);\n if (next === lastSerializedRef.current) return;\n lastSerializedRef.current = next;\n setState({ selection: ids, kinds });\n }, []);\n\n const value = useMemo<SelectionContextValue>(\n () => ({\n selection: state.selection,\n ...(state.kinds !== undefined ? { kinds: state.kinds } : {}),\n publishSelection,\n }),\n [state, publishSelection],\n );\n\n return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;\n}\n\n/**\n * @experimental\n * Read the current selection-context value. Returns `null` when no\n * `<SelectionContextProvider>` is in scope; consumers should fall back\n * gracefully (e.g. command palette hides the \"N selected\" header).\n */\nexport function useSelectionContext(): SelectionContextValue | null {\n return useContext(SelectionContext);\n}\n\n/**\n * @experimental\n * Conditional `<SelectionContextProvider>` wrapper. Mounts a provider only\n * when no parent provider is in scope — otherwise renders children unwrapped\n * so callers (e.g. `<WeaselProvider>`) defer to the host's existing scope.\n * Mirrors `ActionsProviderIfRoot` / `DepRegistryProviderIfRoot`.\n */\nexport function SelectionContextProviderIfRoot({\n children,\n}: {\n children: ReactNode;\n}): ReactNode {\n const parent = useContext(SelectionContext);\n if (parent !== null) return <>{children}</>;\n return <SelectionContextProvider>{children}</SelectionContextProvider>;\n}\n\n/**\n * @experimental\n * Convenience hook for canvas components: publishes the supplied `ids`\n * into the surrounding `<SelectionContextProvider>` whenever the\n * content of `ids` changes. No-op when there's no provider in scope.\n *\n * Used by `<SceneCanvas>` internally to auto-publish; bare-Canvas\n * consumers call `useSelectionContext()` and `publishSelection` directly.\n */\nexport function usePublishSelection(\n ids: readonly NodeId[],\n kinds?: readonly (string | undefined)[],\n): void {\n const ctx = useContext(SelectionContext);\n // Track latest values via refs so the effect's deps array can use a stable\n // serialization — avoids re-firing on identity-only changes.\n const idsRef = useLatest(ids);\n const kindsRef = useLatest(kinds);\n const serialized = JSON.stringify([ids, kinds]);\n // Depend on `publishSelection` rather than `ctx`: the provider mints a new\n // context value on every publish, so keying on `ctx` refires this effect for\n // every publisher whenever any one of them publishes.\n const publish = ctx?.publishSelection;\n useEffect(() => {\n if (!publish) return;\n publish(idsRef.current, kindsRef.current);\n }, [publish, serialized, idsRef, kindsRef]);\n // A publisher that goes away takes its selection with it, unless another\n // has written the slot since.\n const ctxRef = useLatest(ctx);\n const serializedRef = useLatest(serialized);\n useEffect(() => () => {\n const slot = ctxRef.current;\n if (slot && JSON.stringify([slot.selection, slot.kinds]) === serializedRef.current) {\n slot.publishSelection([]);\n }\n }, [ctxRef, serializedRef]);\n}\n"],"mappings":";;;;AAmDA,IAAMC,IAAmBC,EAA4C,IAAI;AAOzE,SAAgBC,EAAyB,EAAE,eAAgD;CACzF,IAAM,CAACC,GAAOC,KAAYC,EAGvB;EAAE,WAAW,CAAC;EAAG,OAAO,KAAA;CAAU,CAAC,GAChCC,IAAoBC,EAAe,EAAE,GAErCC,IAAmBC,GACvB,GACA,MACS;EAIT,IAAMC,IAAO,KAAK,UAAU,CAACC,GAAKC,CAAK,CAAC;EACxC,AAAIF,MAASJ,EAAkB,YAC/B,EAAkB,UAAUI,GAC5BN,EAAS;GAAE,WAAWO;GAAK;EAAM,CAAC;CACpC,GAAG,CAAC,CAAC,GAECE,IAAQC,SACL;EACL,WAAWX,EAAM;EACjB,GAAIA,EAAM,UAAU,KAAA,IAAqC,CAAC,IAA1B,EAAE,OAAOA,EAAM,MAAM;EACrD;CACF,IACA,CAACA,GAAOK,CAAgB,CAC1B;CAEA,OAAO,kBAACR,EAAiB,UAAlB;EAAkC;EAAQ;CAAoC,CAAA;AACvF;AAQA,SAAgBe,IAAoD;CAClE,OAAOC,EAAWhB,CAAgB;AACpC;AASA,SAAgBiB,EAA+B,EAC7C,eAGY;CAGZ,OAFeD,EAAWhB,CACtB,MAAW,OACR,kBAACE,GAAD,EAA2B,YAAmC,CAAA,IADzC,kBAAAH,GAAA,EAAG,YAAW,CAAA;AAE5C;AAWA,SAAgBoB,EACd,GACA,GACM;CACN,IAAMC,IAAMJ,EAAWhB,CAAgB,GAGjCqB,IAASC,EAAUX,CAAG,GACtBY,IAAWD,EAAUV,CAAK,GAC1BY,IAAa,KAAK,UAAU,CAACb,GAAKC,CAAK,CAAC,GAIxCa,IAAUL,GAAK;CACrB,QAAgB;EACd,AAAKK,KACLA,EAAQJ,EAAO,SAASE,EAAS,OAAO;CAC1C,GAAG;EAACE;EAASD;EAAYH;EAAQE;CAAQ,CAAC;CAG1C,IAAMG,IAASJ,EAAUF,CAAG,GACtBO,IAAgBL,EAAUE,CAAU;CAC1C,cAAsB;EACpB,IAAMI,IAAOF,EAAO;EACpB,AAAIE,KAAQ,KAAK,UAAU,CAACA,EAAK,WAAWA,EAAK,KAAK,CAAC,MAAMD,EAAc,WACzEC,EAAK,iBAAiB,CAAC,CAAC;CAE5B,GAAG,CAACF,GAAQC,CAAa,CAAC;AAC5B"}
|
|
@@ -8,8 +8,8 @@ import { HANDLE_BASE_PX as o } from "../../core/device/targets.js";
|
|
|
8
8
|
import { unionAABB as s } from "../../core/geometry/unionBounds.js";
|
|
9
9
|
import { visualBoundsViaDescriptor as c } from "../../core/geometry/poseDescriptor.js";
|
|
10
10
|
import { AUTO_POSE_DESCRIPTOR as l } from "../../interactions/actions/resize/autoPoseDescriptor.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { MULTI_RESIZE_TARGET_ID as u } from "../../core/selection/selectionTarget.js";
|
|
12
|
+
import { rectCorners as d, rotatePoint as f } from "../../interactions/actions/rotate/geometry.js";
|
|
13
13
|
import { DEFAULT_ROTATION_HANDLE_DISTANCE as p, rotationHandle as m } from "../../interactions/actions/rotate/handle.js";
|
|
14
14
|
import { alignedStrokeRect as h } from "@weasel-js/paint";
|
|
15
15
|
import { PATH_L as g, PATH_M as _, PATH_Z as v } from "@weasel-js/geom";
|
|
@@ -230,15 +230,15 @@ function M(e, n, i, a, o) {
|
|
|
230
230
|
return s;
|
|
231
231
|
}
|
|
232
232
|
function N(e, t, r, a) {
|
|
233
|
-
let o = n(r), s =
|
|
234
|
-
let n =
|
|
233
|
+
let o = n(r), s = d(e).map((e) => {
|
|
234
|
+
let n = f(e.x, e.y, t.cx, t.cy, t.rotation), [r, a] = i(n.x, n.y, o);
|
|
235
235
|
return {
|
|
236
236
|
x: r,
|
|
237
237
|
y: a
|
|
238
238
|
};
|
|
239
239
|
});
|
|
240
240
|
if (a === 0) return s;
|
|
241
|
-
let [c, l] = i(t.cx, t.cy, o),
|
|
241
|
+
let [c, l] = i(t.cx, t.cy, o), u = (e, t) => {
|
|
242
242
|
let n = -(t.y - e.y), r = t.x - e.x, i = Math.hypot(n, r) || 1;
|
|
243
243
|
n /= i, r /= i;
|
|
244
244
|
let a = (e.x + t.x) / 2 - c, o = (e.y + t.y) / 2 - l;
|
|
@@ -251,7 +251,7 @@ function N(e, t, r, a) {
|
|
|
251
251
|
};
|
|
252
252
|
};
|
|
253
253
|
return s.map((e, t) => {
|
|
254
|
-
let n =
|
|
254
|
+
let n = u(s[(t + 3) % 4], e), r = u(e, s[(t + 1) % 4]), i = a / (1 + n.x * r.x + n.y * r.y);
|
|
255
255
|
return {
|
|
256
256
|
x: e.x + (n.x + r.x) * i,
|
|
257
257
|
y: e.y + (n.y + r.y) * i
|
|
@@ -259,18 +259,18 @@ function N(e, t, r, a) {
|
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
261
|
function P(e, o, s, c, l) {
|
|
262
|
-
let u = [],
|
|
262
|
+
let u = [], d = s.size / 2, p = s.outline.align ?? "center", m = t(s.outline.width ?? 1, 1);
|
|
263
263
|
for (let t of e) {
|
|
264
264
|
let e = o(t);
|
|
265
265
|
if (!e) continue;
|
|
266
266
|
let g = r(e), _ = n(l), v = g ? a(g.rotation, l.scale) : 0;
|
|
267
267
|
for (let t of c(e)) {
|
|
268
|
-
let e = g ?
|
|
268
|
+
let e = g ? f(t.x, t.y, g.cx, g.cy, g.rotation) : t, [n, r] = i(e.x, e.y, _), a = {
|
|
269
269
|
x: n,
|
|
270
270
|
y: r
|
|
271
271
|
}, o = {
|
|
272
|
-
x: a.x -
|
|
273
|
-
y: a.y -
|
|
272
|
+
x: a.x - d,
|
|
273
|
+
y: a.y - d,
|
|
274
274
|
width: s.size,
|
|
275
275
|
height: s.size
|
|
276
276
|
}, c = h(o, p, m), l = {
|
|
@@ -369,26 +369,26 @@ function F(r, a, o, s) {
|
|
|
369
369
|
}];
|
|
370
370
|
}
|
|
371
371
|
function I(e, t) {
|
|
372
|
-
let { stroke: n, pad: r } = O(e.outline), i = t.handles && e.handles !== !1 ? k(e.handles || void 0) : null, a = e.handlesOf ?? D, o = e.poseDescriptor ?? l, s = e.getPose, c = s ? S(s, o, e.getChildren, e.isContainer) : null,
|
|
372
|
+
let { stroke: n, pad: r } = O(e.outline), i = t.handles && e.handles !== !1 ? k(e.handles || void 0) : null, a = e.handlesOf ?? D, o = e.poseDescriptor ?? l, s = e.getPose, c = s ? S(s, o, e.getChildren, e.isContainer) : null, d = t.handles ? L(e)?.distancePx ?? null : null;
|
|
373
373
|
return {
|
|
374
374
|
id: t.id,
|
|
375
375
|
label: t.label,
|
|
376
376
|
space: "screen",
|
|
377
377
|
draw: (o, s) => {
|
|
378
|
-
let l = U(o),
|
|
379
|
-
if (
|
|
380
|
-
let p = e.getSuppressedIds?.(), m = p && p.size > 0 ?
|
|
378
|
+
let l = U(o), f = e.getSelection ? e.getSelection() : H(l);
|
|
379
|
+
if (f.length === 0) return [];
|
|
380
|
+
let p = e.getSuppressedIds?.(), m = p && p.size > 0 ? f.filter((e) => !p.has(e)) : f;
|
|
381
381
|
if (m.length === 0) return [];
|
|
382
|
-
let h = V(o), g = t.outline && (!h || h("selection.outline")), _ = !h || h("selection.resize-handles"), v = !h || h("selection.rotation-handle"), y = c ?? ((e) => l?.boundsOf(e) ?? null), b = (e) => e ===
|
|
382
|
+
let h = V(o), g = t.outline && (!h || h("selection.outline")), _ = !h || h("selection.resize-handles"), v = !h || h("selection.rotation-handle"), y = c ?? ((e) => l?.boundsOf(e) ?? null), b = (e) => e === u && l?.unionBounds != null ? l.unionBounds : y(e), x = [];
|
|
383
383
|
if (g) {
|
|
384
384
|
let t = e.getOutlineIds ? e.getOutlineIds() : e.getSelection ? m : l?.selection ?? m, i = p && p.size > 0 ? t.filter((e) => !p.has(e)) : t;
|
|
385
385
|
for (let e of M(i, b, n, r, s)) x.push(e);
|
|
386
386
|
}
|
|
387
387
|
if (!i) return x;
|
|
388
388
|
if (_) for (let e of P(m, b, i, a, s)) x.push(e);
|
|
389
|
-
if (v &&
|
|
389
|
+
if (v && d !== null) for (let e of m) {
|
|
390
390
|
let t = b(e);
|
|
391
|
-
if (t) for (let e of F(t, i,
|
|
391
|
+
if (t) for (let e of F(t, i, d, s)) x.push(e);
|
|
392
392
|
}
|
|
393
393
|
return x;
|
|
394
394
|
}
|
|
@@ -430,7 +430,7 @@ function V(e) {
|
|
|
430
430
|
return typeof t?.getIsVisible == "function" ? t.getIsVisible() : null;
|
|
431
431
|
}
|
|
432
432
|
function H(e) {
|
|
433
|
-
return e ? e.multiActive ? [
|
|
433
|
+
return e ? e.multiActive ? [u] : e.selection : [];
|
|
434
434
|
}
|
|
435
435
|
function U(e) {
|
|
436
436
|
let t = e;
|