@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
package/dist/input/keyState.js
CHANGED
|
@@ -1,76 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEmitter as e } from "@weasel-js/registry";
|
|
2
|
+
import { isEditableTarget as t } from "@weasel-js/routing";
|
|
2
3
|
//#region src/input/keyState.ts
|
|
3
|
-
var
|
|
4
|
+
var n = {
|
|
4
5
|
shift: ["ShiftLeft", "ShiftRight"],
|
|
5
6
|
ctrl: ["ControlLeft", "ControlRight"],
|
|
6
7
|
alt: ["AltLeft", "AltRight"],
|
|
7
8
|
meta: ["MetaLeft", "MetaRight"]
|
|
8
|
-
},
|
|
9
|
-
function
|
|
10
|
-
let
|
|
9
|
+
}, r = new Set(Object.values(n).flat()), i = (e) => e.length === 1 ? e.toLowerCase() : e, a = (e) => typeof e == "string" ? [e] : e;
|
|
10
|
+
function o() {
|
|
11
|
+
let o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set(), l = {
|
|
11
12
|
shift: !1,
|
|
12
13
|
ctrl: !1,
|
|
13
14
|
alt: !1,
|
|
14
15
|
meta: !1
|
|
15
|
-
}, l =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}, p = (e) => {
|
|
21
|
-
let n = !1, r = {
|
|
16
|
+
}, u = { ...l }, d = e(), f = d.emit, p = (e) => (s.delete(e), o.delete(e)), m = () => {
|
|
17
|
+
let e = o.size > 0 || c.size > 0 || l.shift || l.ctrl || l.alt || l.meta;
|
|
18
|
+
o.clear(), s.clear(), c.clear(), l.shift = l.ctrl = l.alt = l.meta = !1, e && f();
|
|
19
|
+
}, h = (e) => {
|
|
20
|
+
let t = !1, r = {
|
|
22
21
|
shift: e.shiftKey,
|
|
23
22
|
ctrl: e.ctrlKey,
|
|
24
23
|
alt: e.altKey,
|
|
25
24
|
meta: e.metaKey
|
|
26
25
|
};
|
|
27
|
-
for (let e of Object.keys(
|
|
28
|
-
return
|
|
26
|
+
for (let e of Object.keys(n)) if (l[e] !== r[e] && (l[e] = r[e], t = !0), !r[e]) for (let r of n[e]) t = p(r) || t;
|
|
27
|
+
return t;
|
|
29
28
|
};
|
|
30
29
|
return {
|
|
31
|
-
isDown: (e) =>
|
|
30
|
+
isDown: (e) => a(e).some((e) => s.has(e)),
|
|
32
31
|
isKeyDown: (e) => {
|
|
33
|
-
let t = new Set(
|
|
34
|
-
for (let e of
|
|
32
|
+
let t = new Set(a(e).map(i));
|
|
33
|
+
for (let e of o.values()) if (t.has(e)) return !0;
|
|
35
34
|
return !1;
|
|
36
35
|
},
|
|
37
36
|
take: (e) => {
|
|
38
37
|
let t = !1;
|
|
39
|
-
for (let n of
|
|
38
|
+
for (let n of a(e)) t = c.delete(n) || t;
|
|
40
39
|
return t;
|
|
41
40
|
},
|
|
42
|
-
codes: () =>
|
|
43
|
-
modifiers: () => ({ ...
|
|
44
|
-
subscribe:
|
|
45
|
-
release:
|
|
46
|
-
attach: (
|
|
47
|
-
let l =
|
|
48
|
-
let n =
|
|
49
|
-
if (l &&
|
|
50
|
-
|
|
51
|
-
let
|
|
52
|
-
|
|
41
|
+
codes: () => s,
|
|
42
|
+
modifiers: () => ((u.shift !== l.shift || u.ctrl !== l.ctrl || u.alt !== l.alt || u.meta !== l.meta) && (u = { ...l }), u),
|
|
43
|
+
subscribe: d.subscribe,
|
|
44
|
+
release: m,
|
|
45
|
+
attach: (e, a = {}) => {
|
|
46
|
+
let l = a.skipInEditable ?? !0, u = new Set(a.preventDefault ?? []), d = (e) => {
|
|
47
|
+
let n = e;
|
|
48
|
+
if (l && t(n.target)) return;
|
|
49
|
+
u.has(n.code) && n.preventDefault();
|
|
50
|
+
let r = h(n);
|
|
51
|
+
o.has(n.code) || (o.set(n.code, i(n.key)), s.add(n.code), r = !0), !n.repeat && !c.has(n.code) && (c.add(n.code), r = !0), r && f();
|
|
53
52
|
}, g = (e) => {
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
let i =
|
|
57
|
-
if (i =
|
|
58
|
-
i &&
|
|
53
|
+
let t = e;
|
|
54
|
+
u.has(t.code) && t.preventDefault();
|
|
55
|
+
let i = h(t);
|
|
56
|
+
if (i = p(t.code) || i, n.meta.includes(t.code)) for (let e of [...s]) r.has(e) || (i = p(e) || i);
|
|
57
|
+
i && f();
|
|
59
58
|
}, _ = () => {
|
|
60
|
-
document.hidden &&
|
|
61
|
-
}, v = (
|
|
62
|
-
let
|
|
63
|
-
(!(
|
|
59
|
+
document.hidden && m();
|
|
60
|
+
}, v = (t) => {
|
|
61
|
+
let n = t.relatedTarget;
|
|
62
|
+
(!(n instanceof Node) || !e.contains(n)) && m();
|
|
64
63
|
};
|
|
65
|
-
|
|
66
|
-
let y =
|
|
67
|
-
return y &&
|
|
68
|
-
|
|
64
|
+
e.addEventListener("keydown", d), e.addEventListener("keyup", g), window.addEventListener("blur", m), document.addEventListener("visibilitychange", _);
|
|
65
|
+
let y = e !== window;
|
|
66
|
+
return y && e.addEventListener("focusout", v), () => {
|
|
67
|
+
e.removeEventListener("keydown", d), e.removeEventListener("keyup", g), window.removeEventListener("blur", m), document.removeEventListener("visibilitychange", _), y && e.removeEventListener("focusout", v), m();
|
|
69
68
|
};
|
|
70
69
|
}
|
|
71
70
|
};
|
|
72
71
|
}
|
|
73
72
|
//#endregion
|
|
74
|
-
export {
|
|
73
|
+
export { o as createKeyState };
|
|
75
74
|
|
|
76
75
|
//# sourceMappingURL=keyState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyState.js","names":["MODIFIER_CODES","MODIFIER_SET","fold","key","list","v","createKeyState","down","codeSet","presses","mods","listeners","notify","l","remove","code","release","changed","reconcile","flags","m","c","want","k","hit","listener","skipInEditable","options","owned","onKeyDown","e","event","isEditableTarget","onKeyUp","onVisibility","onFocusOut","next","target","isElement"],"sources":["../../src/input/keyState.ts"],"sourcesContent":["/**\n * Which physical keys are down right now — the poll a game loop or character\n * controller reads each step. This is raw input state, not routing: it sees\n * every key whether or not a binding claims it, and a binding claiming a key\n * does not hide it from here.\n */\n\nimport { isEditableTarget } from '@weasel-js/routing';\n\n/** Which modifier keys are held, on either side of the keyboard. */\nexport interface KeyModifiers {\n shift: boolean;\n ctrl: boolean;\n alt: boolean;\n meta: boolean;\n}\n\n/** Options for {@link KeyState.attach}. */\nexport interface KeyStateAttachOptions {\n /** Ignore presses whose target is an input, textarea or contenteditable\n * element. Their releases are always taken. Default `true`. */\n skipInEditable?: boolean;\n /** `KeyboardEvent.code`s whose keydown and keyup have their default\n * prevented — the keys a game owns, so Space and the arrows stop scrolling\n * the page. */\n preventDefault?: readonly string[];\n}\n\n/** A poll of which physical keys are down, fed by {@link KeyState.attach}. */\nexport interface KeyState {\n /** Whether any of these physical keys, by `KeyboardEvent.code`\n * (`'KeyA'`, `'Space'`, `'ArrowLeft'`), is down. Codes name positions, so\n * WASD stays in place on any layout. */\n isDown(code: string | readonly string[]): boolean;\n /** Whether a key down produced any of these `KeyboardEvent.key` values when\n * it was pressed. Single characters compare case-insensitively. */\n isKeyDown(key: string | readonly string[]): boolean;\n /** Whether any of these codes was freshly pressed since it was last taken;\n * clears them. Autorepeat is not a press. A press is taken once, so two\n * readers of the same code see it once between them. */\n take(code: string | readonly string[]): boolean;\n /** Every code down. The set is live — copy it to keep it. */\n codes(): ReadonlySet<string>;\n modifiers(): KeyModifiers;\n /** Called on every change of what is down. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void;\n /** Mark everything up and drop untaken presses. */\n release(): void;\n /**\n * Listen on `target` — `window`, or an element whose subtree has focus.\n * Everything is released when the keyups can no longer arrive: the window\n * blurs, the document hides, focus leaves an element target, or Meta is\n * released (macOS delivers no keyup for a key released while Cmd is held).\n * Returns the detach, which also releases.\n */\n attach(target: Window | HTMLElement, options?: KeyStateAttachOptions): () => void;\n}\n\nconst MODIFIER_CODES: Record<keyof KeyModifiers, readonly string[]> = {\n shift: ['ShiftLeft', 'ShiftRight'],\n ctrl: ['ControlLeft', 'ControlRight'],\n alt: ['AltLeft', 'AltRight'],\n meta: ['MetaLeft', 'MetaRight'],\n};\nconst MODIFIER_SET = new Set(Object.values(MODIFIER_CODES).flat());\n\nconst fold = (key: string): string => (key.length === 1 ? key.toLowerCase() : key);\nconst list = (v: string | readonly string[]): readonly string[] => (typeof v === 'string' ? [v] : v);\n\n/** A new {@link KeyState} with nothing down. It hears nothing until attached;\n * in React, `useKeyState` attaches one for a component's lifetime. */\nexport function createKeyState(): KeyState {\n // code → the folded key it produced when pressed\n const down = new Map<string, string>();\n const codeSet = new Set<string>();\n const presses = new Set<string>();\n const mods: KeyModifiers = { shift: false, ctrl: false, alt: false, meta: false };\n const listeners = new Set<() => void>();\n\n const notify = () => {\n for (const l of [...listeners]) l();\n };\n\n const remove = (code: string): boolean => {\n codeSet.delete(code);\n return down.delete(code);\n };\n\n const release = () => {\n const changed = down.size > 0 || presses.size > 0 || mods.shift || mods.ctrl || mods.alt || mods.meta;\n down.clear();\n codeSet.clear();\n presses.clear();\n mods.shift = mods.ctrl = mods.alt = mods.meta = false;\n if (changed) notify();\n };\n\n // A modifier's keyup can land in another window (alt-tab, cmd-tab), so trust\n // the flags every event carries over the keys we saw go down.\n const reconcile = (e: KeyboardEvent): boolean => {\n let changed = false;\n const flags: KeyModifiers = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey };\n for (const m of Object.keys(MODIFIER_CODES) as Array<keyof KeyModifiers>) {\n if (mods[m] !== flags[m]) {\n mods[m] = flags[m];\n changed = true;\n }\n if (!flags[m]) for (const code of MODIFIER_CODES[m]) changed = remove(code) || changed;\n }\n return changed;\n };\n\n const state: KeyState = {\n isDown: (code) => list(code).some((c) => codeSet.has(c)),\n isKeyDown: (key) => {\n const want = new Set(list(key).map(fold));\n for (const k of down.values()) if (want.has(k)) return true;\n return false;\n },\n take: (code) => {\n let hit = false;\n for (const c of list(code)) hit = presses.delete(c) || hit;\n return hit;\n },\n codes: () => codeSet,\n modifiers: () => ({ ...mods }),\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n release,\n attach: (target, options = {}) => {\n const skipInEditable = options.skipInEditable ?? true;\n const owned = new Set(options.preventDefault ?? []);\n\n const onKeyDown = (event: Event) => {\n const e = event as KeyboardEvent;\n if (skipInEditable && isEditableTarget(e.target)) return;\n if (owned.has(e.code)) e.preventDefault();\n let changed = reconcile(e);\n if (!down.has(e.code)) {\n down.set(e.code, fold(e.key));\n codeSet.add(e.code);\n changed = true;\n }\n if (!e.repeat && !presses.has(e.code)) {\n presses.add(e.code);\n changed = true;\n }\n if (changed) notify();\n };\n\n const onKeyUp = (event: Event) => {\n const e = event as KeyboardEvent;\n if (owned.has(e.code)) e.preventDefault();\n let changed = reconcile(e);\n changed = remove(e.code) || changed;\n if (MODIFIER_CODES.meta.includes(e.code)) {\n for (const code of [...codeSet]) if (!MODIFIER_SET.has(code)) changed = remove(code) || changed;\n }\n if (changed) notify();\n };\n\n const onVisibility = () => {\n if (document.hidden) release();\n };\n\n const onFocusOut = (event: Event) => {\n const next = (event as FocusEvent).relatedTarget;\n if (!(next instanceof Node) || !(target as HTMLElement).contains(next)) release();\n };\n\n target.addEventListener('keydown', onKeyDown);\n target.addEventListener('keyup', onKeyUp);\n window.addEventListener('blur', release);\n document.addEventListener('visibilitychange', onVisibility);\n const isElement = target !== window;\n if (isElement) target.addEventListener('focusout', onFocusOut);\n\n return () => {\n target.removeEventListener('keydown', onKeyDown);\n target.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('blur', release);\n document.removeEventListener('visibilitychange', onVisibility);\n if (isElement) target.removeEventListener('focusout', onFocusOut);\n release();\n };\n },\n };\n return state;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keyState.js","names":["MODIFIER_CODES","MODIFIER_SET","fold","key","list","v","createKeyState","down","codeSet","presses","mods","held","listeners","createEmitter","notify","remove","code","release","changed","reconcile","flags","m","c","want","k","hit","skipInEditable","options","owned","onKeyDown","e","event","isEditableTarget","onKeyUp","onVisibility","onFocusOut","next","target","isElement"],"sources":["../../src/input/keyState.ts"],"sourcesContent":["/**\n * Which physical keys are down right now — the poll a game loop or character\n * controller reads each step. This is raw input state, not routing: it sees\n * every key whether or not a binding claims it, and a binding claiming a key\n * does not hide it from here.\n */\n\nimport { createEmitter } from '@weasel-js/registry';\nimport { isEditableTarget } from '@weasel-js/routing';\n\n/** Which modifier keys are held, on either side of the keyboard. */\nexport interface KeyModifiers {\n shift: boolean;\n ctrl: boolean;\n alt: boolean;\n meta: boolean;\n}\n\n/** Options for {@link KeyState.attach}. */\nexport interface KeyStateAttachOptions {\n /** Ignore presses whose target is an input, textarea or contenteditable\n * element. Their releases are always taken. Default `true`. */\n skipInEditable?: boolean;\n /** `KeyboardEvent.code`s whose keydown and keyup have their default\n * prevented — the keys a game owns, so Space and the arrows stop scrolling\n * the page. */\n preventDefault?: readonly string[];\n}\n\n/** A poll of which physical keys are down, fed by {@link KeyState.attach}. */\nexport interface KeyState {\n /** Whether any of these physical keys, by `KeyboardEvent.code`\n * (`'KeyA'`, `'Space'`, `'ArrowLeft'`), is down. Codes name positions, so\n * WASD stays in place on any layout. */\n isDown(code: string | readonly string[]): boolean;\n /** Whether a key down produced any of these `KeyboardEvent.key` values when\n * it was pressed. Single characters compare case-insensitively. */\n isKeyDown(key: string | readonly string[]): boolean;\n /** Whether any of these codes was freshly pressed since it was last taken;\n * clears them. Autorepeat is not a press. A press is taken once, so two\n * readers of the same code see it once between them. */\n take(code: string | readonly string[]): boolean;\n /** Every code down. The set is live — copy it to keep it. */\n codes(): ReadonlySet<string>;\n /** The modifiers held. The same object until one changes, so it serves as\n * a `useSyncExternalStore` snapshot. */\n modifiers(): KeyModifiers;\n /** Called on every change of what is down. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void;\n /** Mark everything up and drop untaken presses. */\n release(): void;\n /**\n * Listen on `target` — `window`, or an element whose subtree has focus.\n * Everything is released when the keyups can no longer arrive: the window\n * blurs, the document hides, focus leaves an element target, or Meta is\n * released (macOS delivers no keyup for a key released while Cmd is held).\n * Returns the detach, which also releases.\n */\n attach(target: Window | HTMLElement, options?: KeyStateAttachOptions): () => void;\n}\n\nconst MODIFIER_CODES: Record<keyof KeyModifiers, readonly string[]> = {\n shift: ['ShiftLeft', 'ShiftRight'],\n ctrl: ['ControlLeft', 'ControlRight'],\n alt: ['AltLeft', 'AltRight'],\n meta: ['MetaLeft', 'MetaRight'],\n};\nconst MODIFIER_SET = new Set(Object.values(MODIFIER_CODES).flat());\n\nconst fold = (key: string): string => (key.length === 1 ? key.toLowerCase() : key);\nconst list = (v: string | readonly string[]): readonly string[] => (typeof v === 'string' ? [v] : v);\n\n/** A new {@link KeyState} with nothing down. It hears nothing until attached;\n * in React, `useKeyState` attaches one for a component's lifetime. */\nexport function createKeyState(): KeyState {\n // code → the folded key it produced when pressed\n const down = new Map<string, string>();\n const codeSet = new Set<string>();\n const presses = new Set<string>();\n const mods: KeyModifiers = { shift: false, ctrl: false, alt: false, meta: false };\n let held: KeyModifiers = { ...mods };\n const listeners = createEmitter();\n const notify = listeners.emit;\n\n const remove = (code: string): boolean => {\n codeSet.delete(code);\n return down.delete(code);\n };\n\n const release = () => {\n const changed = down.size > 0 || presses.size > 0 || mods.shift || mods.ctrl || mods.alt || mods.meta;\n down.clear();\n codeSet.clear();\n presses.clear();\n mods.shift = mods.ctrl = mods.alt = mods.meta = false;\n if (changed) notify();\n };\n\n // A modifier's keyup can land in another window (alt-tab, cmd-tab), so trust\n // the flags every event carries over the keys we saw go down.\n const reconcile = (e: KeyboardEvent): boolean => {\n let changed = false;\n const flags: KeyModifiers = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey };\n for (const m of Object.keys(MODIFIER_CODES) as Array<keyof KeyModifiers>) {\n if (mods[m] !== flags[m]) {\n mods[m] = flags[m];\n changed = true;\n }\n if (!flags[m]) for (const code of MODIFIER_CODES[m]) changed = remove(code) || changed;\n }\n return changed;\n };\n\n const state: KeyState = {\n isDown: (code) => list(code).some((c) => codeSet.has(c)),\n isKeyDown: (key) => {\n const want = new Set(list(key).map(fold));\n for (const k of down.values()) if (want.has(k)) return true;\n return false;\n },\n take: (code) => {\n let hit = false;\n for (const c of list(code)) hit = presses.delete(c) || hit;\n return hit;\n },\n codes: () => codeSet,\n modifiers: () => {\n if (held.shift !== mods.shift || held.ctrl !== mods.ctrl || held.alt !== mods.alt || held.meta !== mods.meta) {\n held = { ...mods };\n }\n return held;\n },\n subscribe: listeners.subscribe,\n release,\n attach: (target, options = {}) => {\n const skipInEditable = options.skipInEditable ?? true;\n const owned = new Set(options.preventDefault ?? []);\n\n const onKeyDown = (event: Event) => {\n const e = event as KeyboardEvent;\n if (skipInEditable && isEditableTarget(e.target)) return;\n if (owned.has(e.code)) e.preventDefault();\n let changed = reconcile(e);\n if (!down.has(e.code)) {\n down.set(e.code, fold(e.key));\n codeSet.add(e.code);\n changed = true;\n }\n if (!e.repeat && !presses.has(e.code)) {\n presses.add(e.code);\n changed = true;\n }\n if (changed) notify();\n };\n\n const onKeyUp = (event: Event) => {\n const e = event as KeyboardEvent;\n if (owned.has(e.code)) e.preventDefault();\n let changed = reconcile(e);\n changed = remove(e.code) || changed;\n if (MODIFIER_CODES.meta.includes(e.code)) {\n for (const code of [...codeSet]) if (!MODIFIER_SET.has(code)) changed = remove(code) || changed;\n }\n if (changed) notify();\n };\n\n const onVisibility = () => {\n if (document.hidden) release();\n };\n\n const onFocusOut = (event: Event) => {\n const next = (event as FocusEvent).relatedTarget;\n if (!(next instanceof Node) || !(target as HTMLElement).contains(next)) release();\n };\n\n target.addEventListener('keydown', onKeyDown);\n target.addEventListener('keyup', onKeyUp);\n window.addEventListener('blur', release);\n document.addEventListener('visibilitychange', onVisibility);\n const isElement = target !== window;\n if (isElement) target.addEventListener('focusout', onFocusOut);\n\n return () => {\n target.removeEventListener('keydown', onKeyDown);\n target.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('blur', release);\n document.removeEventListener('visibilitychange', onVisibility);\n if (isElement) target.removeEventListener('focusout', onFocusOut);\n release();\n };\n },\n };\n return state;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useKeyState as e } from "./useKeyState.js";
|
|
2
|
+
import { useSyncExternalStore as t } from "react";
|
|
3
|
+
//#region src/input/useKeyModifiers.ts
|
|
4
|
+
function n(n = {}) {
|
|
5
|
+
let r = e(n);
|
|
6
|
+
return t(r.subscribe, r.modifiers, r.modifiers);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { n as useKeyModifiers };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=useKeyModifiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKeyModifiers.js","names":["useKeyModifiers","keys","useKeyState","options","useSyncExternalStore"],"sources":["../../src/input/useKeyModifiers.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\nimport type { KeyModifiers } from './keyState';\nimport { useKeyState, type UseKeyStateOptions } from './useKeyState';\n\n/**\n * Which modifier keys are held, re-rendering the component when that changes.\n * For chrome that draws the modifiers; a frame loop polls\n * {@link useKeyState} instead. The object is the same one between changes.\n */\nexport function useKeyModifiers(options: UseKeyStateOptions = {}): KeyModifiers {\n const keys = useKeyState(options);\n return useSyncExternalStore(keys.subscribe, keys.modifiers, keys.modifiers);\n}\n"],"mappings":";;;AASA,SAAgBA,EAAgB,IAA8B,CAAC,GAAiB;CAC9E,IAAMC,IAAOC,EAAYC,CAAO;CAChC,OAAOC,EAAqBH,EAAK,WAAWA,EAAK,WAAWA,EAAK,SAAS;AAC5E"}
|
|
@@ -2,9 +2,9 @@ import { IDENTITY_POSE_COMPOSITION as e } from "../../../core/scene/composeFrame
|
|
|
2
2
|
import { createTransformOp as t } from "../../../core/ops/transform.js";
|
|
3
3
|
import { unionAABB as n } from "../../../core/geometry/unionBounds.js";
|
|
4
4
|
import { RECT_POSE_DESCRIPTOR as r, translatePoseViaDescriptor as i, visualBoundsViaDescriptor as a } from "../../../core/geometry/poseDescriptor.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { usePointerContext as o } from "../../../features/pointer/PointerContext.js";
|
|
6
|
+
import { poseFrame as s } from "../poseFrame.js";
|
|
7
|
+
import { dispatchApplyBatch as c } from "../../../core/applyOps.js";
|
|
8
8
|
import { resolveSpatialReference as l } from "../spatialReference.js";
|
|
9
9
|
import { useCallback as u } from "react";
|
|
10
10
|
import { useLatest as d } from "@weasel-js/react";
|
|
@@ -41,12 +41,12 @@ function p(e, t, n) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
function m(n, l = {}) {
|
|
44
|
-
let m = d(n), h = d(l), g =
|
|
45
|
-
return { align: u((n,
|
|
46
|
-
let l = m.current, u = h.current, d = l.getSelection(), g = u.geometry ?? r, v =
|
|
44
|
+
let m = d(n), h = d(l), g = o(), _ = d(g);
|
|
45
|
+
return { align: u((n, o) => {
|
|
46
|
+
let l = m.current, u = h.current, d = l.getSelection(), g = u.geometry ?? r, v = s({
|
|
47
47
|
getPose: (e) => l.getPose(e),
|
|
48
48
|
getParent: (e) => l.getParent?.(e) ?? null
|
|
49
|
-
}, u.composition ?? e), y = d.map((e) => v.world(e)), b = y.map((e) => a(e, g)), x = f(b,
|
|
49
|
+
}, u.composition ?? e), y = d.map((e) => v.world(e)), b = y.map((e) => a(e, g)), x = f(b, o, {
|
|
50
50
|
pointer: () => {
|
|
51
51
|
let e = _.current?.get();
|
|
52
52
|
return e ? {
|
|
@@ -68,7 +68,7 @@ function m(n, l = {}) {
|
|
|
68
68
|
to: v.local(d[e], o)
|
|
69
69
|
}));
|
|
70
70
|
}
|
|
71
|
-
S.length !== 0 &&
|
|
71
|
+
S.length !== 0 && c(l, S, u.label ?? "Align");
|
|
72
72
|
}, [
|
|
73
73
|
m,
|
|
74
74
|
h,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dwarn as e } from "../../../debug/flag.js";
|
|
2
2
|
import { usePointerContext as t } from "../../../features/pointer/PointerContext.js";
|
|
3
|
-
import {
|
|
3
|
+
import { dispatchApplyBatch as n } from "../../../core/applyOps.js";
|
|
4
4
|
import { materializePaste as r } from "./materializePaste.js";
|
|
5
5
|
import { WEASEL_CLIPBOARD_MIME as i, buildWeaselClipboardText as a } from "./wireFormat.js";
|
|
6
6
|
import { useCallback as o, useRef as s } from "react";
|
|
@@ -8,39 +8,41 @@ import { useLatest as c } from "@weasel-js/react";
|
|
|
8
8
|
//#region src/interactions/actions/clipboard/clipboardOps.ts
|
|
9
9
|
var l = { items: [] };
|
|
10
10
|
function u(i, a) {
|
|
11
|
-
let { getSelection: u, onPaste: f,
|
|
11
|
+
let { getSelection: u, onPaste: f, onPasteOmitted: m, pasteLabel: h = "Paste", getDropPoint: g, produceFlavors: _, jsonReplacer: v } = a, y = t(), b = g ?? y?.getDropPoint, x = s(l), S = c(i), C = c({
|
|
12
12
|
getSelection: u,
|
|
13
13
|
onPaste: f,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
onPasteOmitted: m,
|
|
15
|
+
pasteLabel: h,
|
|
16
|
+
getDropPoint: b,
|
|
17
|
+
produceFlavors: _,
|
|
18
|
+
jsonReplacer: v
|
|
18
19
|
});
|
|
19
20
|
return {
|
|
20
21
|
copy: o(() => {
|
|
21
|
-
let
|
|
22
|
-
if (
|
|
23
|
-
let
|
|
24
|
-
if (!
|
|
25
|
-
|
|
22
|
+
let t = C.current.getSelection();
|
|
23
|
+
if (t.length === 0) return;
|
|
24
|
+
let n = S.current.snapshotSelection;
|
|
25
|
+
if (!n) return;
|
|
26
|
+
x.current = n(t);
|
|
26
27
|
let r = null;
|
|
27
28
|
try {
|
|
28
|
-
r =
|
|
29
|
-
} catch (
|
|
30
|
-
|
|
29
|
+
r = C.current.produceFlavors?.(x.current) ?? d(x.current, C.current.jsonReplacer);
|
|
30
|
+
} catch (t) {
|
|
31
|
+
e("clipboard", `flavor producer threw — skipping OS write: ${String(t)}`);
|
|
31
32
|
}
|
|
32
33
|
r && p(r);
|
|
33
|
-
}, [
|
|
34
|
+
}, [S, C]),
|
|
34
35
|
paste: o(() => {
|
|
35
|
-
let
|
|
36
|
-
if (
|
|
37
|
-
let
|
|
38
|
-
currentSelection:
|
|
39
|
-
dropPoint:
|
|
36
|
+
let e = x.current;
|
|
37
|
+
if (e.items.length === 0) return;
|
|
38
|
+
let t = S.current, i = r(t, e, {
|
|
39
|
+
currentSelection: C.current.getSelection(),
|
|
40
|
+
dropPoint: C.current.getDropPoint?.(),
|
|
41
|
+
onOmitted: C.current.onPasteOmitted
|
|
40
42
|
});
|
|
41
|
-
i && (
|
|
42
|
-
}, [
|
|
43
|
-
isEmpty: o(() =>
|
|
43
|
+
i && (n(t, i.ops, C.current.pasteLabel), x.current = { items: i.created }, C.current.onPaste?.(i.newIds));
|
|
44
|
+
}, [S, C]),
|
|
45
|
+
isEmpty: o(() => x.current.items.length === 0, [])
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
48
|
function d(e, t) {
|
|
@@ -55,13 +57,13 @@ var f = /* @__PURE__ */ new Set([
|
|
|
55
57
|
"text/html",
|
|
56
58
|
"image/png"
|
|
57
59
|
]);
|
|
58
|
-
async function p(
|
|
59
|
-
let
|
|
60
|
-
if (
|
|
60
|
+
async function p(t) {
|
|
61
|
+
let n = Object.entries(t);
|
|
62
|
+
if (n.length === 0) return;
|
|
61
63
|
let r = typeof navigator < "u" ? navigator.clipboard : void 0;
|
|
62
64
|
if (!r?.write || typeof ClipboardItem > "u") return;
|
|
63
65
|
let i = /* @__PURE__ */ new Set();
|
|
64
|
-
for (let [e,
|
|
66
|
+
for (let [e, t] of n) typeof t != "string" && t.catch(() => {
|
|
65
67
|
i.add(e);
|
|
66
68
|
});
|
|
67
69
|
let a = (e) => f.has(e) ? e : `web ${e}`, o = (e) => new ClipboardItem(Object.fromEntries(e.map(([e, t]) => {
|
|
@@ -79,14 +81,14 @@ async function p(e) {
|
|
|
79
81
|
t = n;
|
|
80
82
|
}
|
|
81
83
|
};
|
|
82
|
-
if (await s(
|
|
83
|
-
let c =
|
|
84
|
+
if (await s(n) === null) return;
|
|
85
|
+
let c = n.filter(([e]) => f.has(e) && !i.has(e));
|
|
84
86
|
if (c.length === 0) {
|
|
85
|
-
|
|
87
|
+
e("clipboard", "OS write failed; no standard flavors to fall back to");
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
90
|
let l = await s(c);
|
|
89
|
-
l !== null &&
|
|
91
|
+
l !== null && e("clipboard", `OS clipboard write failed twice — copy stays in-memory only: ${String(l)}`);
|
|
90
92
|
}
|
|
91
93
|
//#endregion
|
|
92
94
|
export { u as useClipboardOps, p as writeOsClipboard };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboardOps.js","names":["EMPTY","useClipboardOps","options","pointerCtx","usePointerContext","effectiveGetDropPoint","getDropPoint","clipboardRef","useRef","adapterRef","useLatest","adapter","optsRef","useCallback","ids","snap","flavors","defaultFlavors","err","writeOsClipboard","cb","a","result","materializePaste","dispatchApplyBatch","text","buildWeaselClipboardText","snapshot","replacer","WEASEL_CLIPBOARD_MIME","WELL_KNOWN_CLIPBOARD_MIMES","entries","rejected","mime","wireMime","toItem","fs","wire","blob","t","promised","writeLive","live","next","standard","dwarn"],"sources":["../../../../src/interactions/actions/clipboard/clipboardOps.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from 'core/scene/types';\nimport { dispatchApplyBatch } from 'core/applyOps';\nimport type { InsertAdapter } from 'core/adapters/types';\nimport type { ClipboardSnapshot } from './types';\nimport { usePointerContext } from 'features/pointer/PointerContext';\nimport { dwarn } from 'debug/flag';\nimport { materializePaste } from './materializePaste';\nimport { WEASEL_CLIPBOARD_MIME, buildWeaselClipboardText } from './wireFormat';\n\ntype Replacer = (key: string, value: unknown) => unknown;\n\n/** Options for `useClipboardOps`. */\nexport interface UseClipboardOpsOptions {\n /** How the hook reads \"current selection\" for copy. The kit doesn't assume\n * a global selection store; each consumer wires this. */\n getSelection: () => NodeId[];\n /** Called after a successful paste with the ids of the newly inserted objects. */\n onPaste?: (newIds: NodeId[]) => void;\n /** Label for the history entry produced by paste. Default 'Paste'. */\n pasteLabel?: string;\n /** Pulled once per paste() call. When non-null, threaded to commitPaste\n * as `ctx.dropPoint`; adapters typically use it as the cluster origin\n * (ignoring `offset`). When null/undefined, the hook falls back to the\n * existing cascade-offset behavior. */\n getDropPoint?: () => { worldX: number; worldY: number } | null;\n /** Produce the OS-clipboard flavor map for a copied snapshot. Keys are MIME\n * types, values the serialized payload. The kit default emits\n * `application/x-weasel-clipboard+json` plus `text/plain` carrying the\n * same JSON. Apps override to add richer flavors (e.g. real SVG) or to\n * replace the text flavor. Return an empty object to skip the OS write\n * entirely. A value may be a promise — an SVG flavor waiting on\n * `warmSvg`, say: the write is still issued inside the copy gesture and\n * the clipboard takes the text when it resolves. The producer itself runs\n * synchronously, so it reads the selection as it stood at the copy. */\n produceFlavors?: (snapshot: ClipboardSnapshot) => ClipboardFlavors;\n /** Replacer for the kit-default JSON flavor (typed arrays etc.). Ignored\n * when `produceFlavors` is supplied. */\n jsonReplacer?: Replacer;\n}\n\n/** MIME type to payload, as `produceFlavors` returns it. */\nexport type ClipboardFlavors = Record<string, string | Promise<string>>;\n\n/** Return shape of `useClipboardOps`: imperative `copy`, `paste`, and `isEmpty` functions. */\nexport interface UseClipboardOpsReturn {\n copy(): void;\n paste(): void;\n isEmpty(): boolean;\n}\n\nconst EMPTY: ClipboardSnapshot = { items: [] };\n\n/** In-memory copy/paste of selections via `InsertAdapter.snapshotSelection` / `commitPaste`. */\nexport function useClipboardOps<TNode extends { id: string }>(\n adapter: InsertAdapter<TNode>,\n options: UseClipboardOpsOptions,\n): UseClipboardOpsReturn {\n const { getSelection, onPaste, pasteLabel = 'Paste', getDropPoint, produceFlavors, jsonReplacer } = options;\n // Fall back to the ambient pointer context (auto-published by SceneCanvas)\n // when the caller didn't supply an explicit `getDropPoint`. Null context\n // (no provider in scope) and no caller-supplied thunk together mean\n // \"no drop point\" — paste uses the cascade offset instead.\n const pointerCtx = usePointerContext();\n const effectiveGetDropPoint = getDropPoint ?? pointerCtx?.getDropPoint;\n const clipboardRef = useRef<ClipboardSnapshot>(EMPTY);\n // Keep callbacks stable across renders.\n const adapterRef = useLatest(adapter);\n const optsRef = useLatest({\n getSelection, onPaste, pasteLabel, getDropPoint: effectiveGetDropPoint, produceFlavors, jsonReplacer,\n });\n\n const copy = useCallback(() => {\n const ids = optsRef.current.getSelection();\n if (ids.length === 0) return;\n const snap = adapterRef.current.snapshotSelection;\n if (!snap) return;\n clipboardRef.current = snap(ids);\n // Best-effort OS write. Never blocks or throws — the in-memory ref is\n // already set, and OS clipboard support varies (custom formats are\n // Chromium-only via the \"web \" prefix; jsdom/non-secure contexts have\n // no API at all). The flavor producer itself is untrusted consumer code\n // (`produceFlavors`), so guard its call too — a throwing producer must\n // not break `copy()`, it just skips the OS write.\n let flavors: ClipboardFlavors | null = null;\n try {\n flavors = optsRef.current.produceFlavors?.(clipboardRef.current)\n ?? defaultFlavors(clipboardRef.current, optsRef.current.jsonReplacer);\n } catch (err) {\n dwarn('clipboard', `flavor producer threw — skipping OS write: ${String(err)}`);\n }\n if (flavors) void writeOsClipboard(flavors);\n }, [adapterRef, optsRef]);\n\n const paste = useCallback(() => {\n const cb = clipboardRef.current;\n if (cb.items.length === 0) return;\n const a = adapterRef.current;\n const result = materializePaste(a, cb, {\n currentSelection: optsRef.current.getSelection(),\n dropPoint: optsRef.current.getDropPoint?.(),\n });\n if (!result) return;\n dispatchApplyBatch(a, result.ops, optsRef.current.pasteLabel);\n // Use the just-created objects as the next cascade source. Re-snapshotting\n // via `adapter.snapshotSelection(newIds)` would read adapter state, but\n // React-state adapters haven't flushed yet — `itemsRef.current` still\n // points at the pre-insert array, so the snapshot would come back empty\n // and the next paste would no-op.\n clipboardRef.current = { items: result.created };\n optsRef.current.onPaste?.(result.newIds as NodeId[]);\n }, [adapterRef, optsRef]);\n\n const isEmpty = useCallback(() => clipboardRef.current.items.length === 0, []);\n\n return { copy, paste, isEmpty };\n}\n\nfunction defaultFlavors(snapshot: ClipboardSnapshot, replacer?: Replacer): ClipboardFlavors {\n const text = buildWeaselClipboardText(snapshot.items, replacer);\n return { [WEASEL_CLIPBOARD_MIME]: text, 'text/plain': text };\n}\n\n/** MIME types the async Clipboard API accepts unprefixed. Every other type\n * (including our own custom MIME, and app-supplied flavors like\n * `image/svg+xml`) must carry Chromium's `web ` prefix or `ClipboardItem`'s\n * constructor throws. */\nconst WELL_KNOWN_CLIPBOARD_MIMES = new Set(['text/plain', 'text/html', 'image/png']);\n\n/** Degradation ladder: full map (every non-well-known MIME web-prefixed) →\n * the same minus any promised flavor that rejected → well-known-only → give\n * up with a dwarn. Chromium writes nothing when one flavor rejects, so a\n * rejection costs only that flavor, never the clipboard's previous content\n * half-overwritten.\n * @internal test seam — exported so unit tests can await the fire-and-forget\n * write directly; tests should still exercise this at least once through\n * the public `copy()` (see clipboardOps.test.tsx). */\nexport async function writeOsClipboard(flavors: ClipboardFlavors): Promise<void> {\n const entries = Object.entries(flavors);\n if (entries.length === 0) return;\n const cb = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (!cb?.write || typeof ClipboardItem === 'undefined') return;\n const rejected = new Set<string>();\n for (const [mime, text] of entries) {\n if (typeof text !== 'string') text.catch(() => { rejected.add(mime); });\n }\n const wireMime = (mime: string) => (WELL_KNOWN_CLIPBOARD_MIMES.has(mime) ? mime : `web ${mime}`);\n const toItem = (fs: Flavor[]) => new ClipboardItem(Object.fromEntries(\n fs.map(([mime, text]) => {\n const wire = wireMime(mime);\n const blob = (t: string) => new Blob([t], { type: wire });\n if (typeof text === 'string') return [wire, blob(text)];\n const promised = text.then(blob);\n // Chromium leaves a ClipboardItem's rejected promise unhandled.\n promised.catch(() => {});\n return [wire, promised];\n }),\n ));\n // Writes `fs`, dropping flavors whose promise rejected and retrying the\n // rest. Returns the last error, or null once a write lands.\n const writeLive = async (fs: Flavor[]): Promise<unknown> => {\n let live = fs;\n for (;;) {\n try {\n await cb.write([toItem(live)]);\n return null;\n } catch (err) {\n const next = live.filter(([mime]) => !rejected.has(mime));\n if (next.length === live.length || next.length === 0) return err;\n live = next;\n }\n }\n };\n if (await writeLive(entries) === null) return;\n const standard = entries.filter(([mime]) => WELL_KNOWN_CLIPBOARD_MIMES.has(mime) && !rejected.has(mime));\n if (standard.length === 0) { dwarn('clipboard', 'OS write failed; no standard flavors to fall back to'); return; }\n const err = await writeLive(standard);\n if (err !== null) dwarn('clipboard', `OS clipboard write failed twice — copy stays in-memory only: ${String(err)}`);\n}\n\ntype Flavor = [mime: string, text: string | Promise<string>];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clipboardOps.js","names":["EMPTY","useClipboardOps","options","pointerCtx","usePointerContext","effectiveGetDropPoint","getDropPoint","clipboardRef","useRef","adapterRef","useLatest","adapter","optsRef","useCallback","ids","snap","flavors","defaultFlavors","err","writeOsClipboard","cb","a","result","materializePaste","dispatchApplyBatch","text","buildWeaselClipboardText","snapshot","replacer","WEASEL_CLIPBOARD_MIME","WELL_KNOWN_CLIPBOARD_MIMES","entries","rejected","mime","wireMime","toItem","fs","wire","blob","t","promised","writeLive","live","next","standard","dwarn"],"sources":["../../../../src/interactions/actions/clipboard/clipboardOps.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from 'core/scene/types';\nimport { dispatchApplyBatch } from 'core/applyOps';\nimport type { InsertAdapter, PasteOmission } from 'core/adapters/types';\nimport type { ClipboardSnapshot } from './types';\nimport { usePointerContext } from 'features/pointer/PointerContext';\nimport { dwarn } from 'debug/flag';\nimport { materializePaste } from './materializePaste';\nimport { WEASEL_CLIPBOARD_MIME, buildWeaselClipboardText } from './wireFormat';\n\ntype Replacer = (key: string, value: unknown) => unknown;\n\n/** Options for `useClipboardOps`. */\nexport interface UseClipboardOpsOptions {\n /** How the hook reads \"current selection\" for copy. The kit doesn't assume\n * a global selection store; each consumer wires this. */\n getSelection: () => NodeId[];\n /** Called after a successful paste with the ids of the newly inserted objects. */\n onPaste?: (newIds: NodeId[]) => void;\n /** Called when a paste left copied nodes out, with each one and the reason\n * — an edge whose endpoints were not copied with it, say. Also called for\n * a paste that made nothing. */\n onPasteOmitted?: (omitted: PasteOmission[]) => void;\n /** Label for the history entry produced by paste. Default 'Paste'. */\n pasteLabel?: string;\n /** Pulled once per paste() call. When non-null, threaded to commitPaste\n * as `ctx.dropPoint`; adapters typically use it as the cluster origin\n * (ignoring `offset`). When null/undefined, the hook falls back to the\n * existing cascade-offset behavior. */\n getDropPoint?: () => { worldX: number; worldY: number } | null;\n /** Produce the OS-clipboard flavor map for a copied snapshot. Keys are MIME\n * types, values the serialized payload. The kit default emits\n * `application/x-weasel-clipboard+json` plus `text/plain` carrying the\n * same JSON. Apps override to add richer flavors (e.g. real SVG) or to\n * replace the text flavor. Return an empty object to skip the OS write\n * entirely. A value may be a promise — an SVG flavor waiting on\n * `warmSvg`, say: the write is still issued inside the copy gesture and\n * the clipboard takes the text when it resolves. The producer itself runs\n * synchronously, so it reads the selection as it stood at the copy. */\n produceFlavors?: (snapshot: ClipboardSnapshot) => ClipboardFlavors;\n /** Replacer for the kit-default JSON flavor (typed arrays etc.). Ignored\n * when `produceFlavors` is supplied. */\n jsonReplacer?: Replacer;\n}\n\n/** MIME type to payload, as `produceFlavors` returns it. */\nexport type ClipboardFlavors = Record<string, string | Promise<string>>;\n\n/** Return shape of `useClipboardOps`: imperative `copy`, `paste`, and `isEmpty` functions. */\nexport interface UseClipboardOpsReturn {\n copy(): void;\n paste(): void;\n isEmpty(): boolean;\n}\n\nconst EMPTY: ClipboardSnapshot = { items: [] };\n\n/** In-memory copy/paste of selections via `InsertAdapter.snapshotSelection` / `commitPaste`. */\nexport function useClipboardOps<TNode extends { id: string }>(\n adapter: InsertAdapter<TNode>,\n options: UseClipboardOpsOptions,\n): UseClipboardOpsReturn {\n const {\n getSelection, onPaste, onPasteOmitted, pasteLabel = 'Paste', getDropPoint, produceFlavors, jsonReplacer,\n } = options;\n // Fall back to the ambient pointer context (auto-published by SceneCanvas)\n // when the caller didn't supply an explicit `getDropPoint`. Null context\n // (no provider in scope) and no caller-supplied thunk together mean\n // \"no drop point\" — paste uses the cascade offset instead.\n const pointerCtx = usePointerContext();\n const effectiveGetDropPoint = getDropPoint ?? pointerCtx?.getDropPoint;\n const clipboardRef = useRef<ClipboardSnapshot>(EMPTY);\n // Keep callbacks stable across renders.\n const adapterRef = useLatest(adapter);\n const optsRef = useLatest({\n getSelection, onPaste, onPasteOmitted, pasteLabel, getDropPoint: effectiveGetDropPoint, produceFlavors,\n jsonReplacer,\n });\n\n const copy = useCallback(() => {\n const ids = optsRef.current.getSelection();\n if (ids.length === 0) return;\n const snap = adapterRef.current.snapshotSelection;\n if (!snap) return;\n clipboardRef.current = snap(ids);\n // Best-effort OS write. Never blocks or throws — the in-memory ref is\n // already set, and OS clipboard support varies (custom formats are\n // Chromium-only via the \"web \" prefix; jsdom/non-secure contexts have\n // no API at all). The flavor producer itself is untrusted consumer code\n // (`produceFlavors`), so guard its call too — a throwing producer must\n // not break `copy()`, it just skips the OS write.\n let flavors: ClipboardFlavors | null = null;\n try {\n flavors = optsRef.current.produceFlavors?.(clipboardRef.current)\n ?? defaultFlavors(clipboardRef.current, optsRef.current.jsonReplacer);\n } catch (err) {\n dwarn('clipboard', `flavor producer threw — skipping OS write: ${String(err)}`);\n }\n if (flavors) void writeOsClipboard(flavors);\n }, [adapterRef, optsRef]);\n\n const paste = useCallback(() => {\n const cb = clipboardRef.current;\n if (cb.items.length === 0) return;\n const a = adapterRef.current;\n const result = materializePaste(a, cb, {\n currentSelection: optsRef.current.getSelection(),\n dropPoint: optsRef.current.getDropPoint?.(),\n onOmitted: optsRef.current.onPasteOmitted,\n });\n if (!result) return;\n dispatchApplyBatch(a, result.ops, optsRef.current.pasteLabel);\n // Use the just-created objects as the next cascade source. Re-snapshotting\n // via `adapter.snapshotSelection(newIds)` would read adapter state, but\n // React-state adapters haven't flushed yet — `itemsRef.current` still\n // points at the pre-insert array, so the snapshot would come back empty\n // and the next paste would no-op.\n clipboardRef.current = { items: result.created };\n optsRef.current.onPaste?.(result.newIds as NodeId[]);\n }, [adapterRef, optsRef]);\n\n const isEmpty = useCallback(() => clipboardRef.current.items.length === 0, []);\n\n return { copy, paste, isEmpty };\n}\n\nfunction defaultFlavors(snapshot: ClipboardSnapshot, replacer?: Replacer): ClipboardFlavors {\n const text = buildWeaselClipboardText(snapshot.items, replacer);\n return { [WEASEL_CLIPBOARD_MIME]: text, 'text/plain': text };\n}\n\n/** MIME types the async Clipboard API accepts unprefixed. Every other type\n * (including our own custom MIME, and app-supplied flavors like\n * `image/svg+xml`) must carry Chromium's `web ` prefix or `ClipboardItem`'s\n * constructor throws. */\nconst WELL_KNOWN_CLIPBOARD_MIMES = new Set(['text/plain', 'text/html', 'image/png']);\n\n/** Degradation ladder: full map (every non-well-known MIME web-prefixed) →\n * the same minus any promised flavor that rejected → well-known-only → give\n * up with a dwarn. Chromium writes nothing when one flavor rejects, so a\n * rejection costs only that flavor, never the clipboard's previous content\n * half-overwritten.\n * @internal test seam — exported so unit tests can await the fire-and-forget\n * write directly; tests should still exercise this at least once through\n * the public `copy()` (see clipboardOps.test.tsx). */\nexport async function writeOsClipboard(flavors: ClipboardFlavors): Promise<void> {\n const entries = Object.entries(flavors);\n if (entries.length === 0) return;\n const cb = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (!cb?.write || typeof ClipboardItem === 'undefined') return;\n const rejected = new Set<string>();\n for (const [mime, text] of entries) {\n if (typeof text !== 'string') text.catch(() => { rejected.add(mime); });\n }\n const wireMime = (mime: string) => (WELL_KNOWN_CLIPBOARD_MIMES.has(mime) ? mime : `web ${mime}`);\n const toItem = (fs: Flavor[]) => new ClipboardItem(Object.fromEntries(\n fs.map(([mime, text]) => {\n const wire = wireMime(mime);\n const blob = (t: string) => new Blob([t], { type: wire });\n if (typeof text === 'string') return [wire, blob(text)];\n const promised = text.then(blob);\n // Chromium leaves a ClipboardItem's rejected promise unhandled.\n promised.catch(() => {});\n return [wire, promised];\n }),\n ));\n // Writes `fs`, dropping flavors whose promise rejected and retrying the\n // rest. Returns the last error, or null once a write lands.\n const writeLive = async (fs: Flavor[]): Promise<unknown> => {\n let live = fs;\n for (;;) {\n try {\n await cb.write([toItem(live)]);\n return null;\n } catch (err) {\n const next = live.filter(([mime]) => !rejected.has(mime));\n if (next.length === live.length || next.length === 0) return err;\n live = next;\n }\n }\n };\n if (await writeLive(entries) === null) return;\n const standard = entries.filter(([mime]) => WELL_KNOWN_CLIPBOARD_MIMES.has(mime) && !rejected.has(mime));\n if (standard.length === 0) { dwarn('clipboard', 'OS write failed; no standard flavors to fall back to'); return; }\n const err = await writeLive(standard);\n if (err !== null) dwarn('clipboard', `OS clipboard write failed twice — copy stays in-memory only: ${String(err)}`);\n}\n\ntype Flavor = [mime: string, text: string | Promise<string>];\n"],"mappings":"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"}
|
|
@@ -7,16 +7,19 @@ function n(n, r, i) {
|
|
|
7
7
|
let o = n.getPasteOffset?.(r) ?? {
|
|
8
8
|
dx: 0,
|
|
9
9
|
dy: 0
|
|
10
|
-
}, s =
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
}, s = [], c = {
|
|
11
|
+
...i.dropPoint == null ? {} : { dropPoint: i.dropPoint },
|
|
12
|
+
...i.onOmitted ? { onOmit: (e) => s.push(e) } : {}
|
|
13
|
+
}, l = a.call(n, r, o, Object.keys(c).length > 0 ? c : void 0);
|
|
14
|
+
if (s.length > 0 && i.onOmitted?.(s), l.length === 0) return null;
|
|
15
|
+
let u = l.map((e) => e.id);
|
|
13
16
|
return {
|
|
14
|
-
ops: [...
|
|
17
|
+
ops: [...l.map((t) => e({ node: t })), t({
|
|
15
18
|
from: i.currentSelection,
|
|
16
|
-
to:
|
|
19
|
+
to: u
|
|
17
20
|
})],
|
|
18
|
-
newIds:
|
|
19
|
-
created:
|
|
21
|
+
newIds: u,
|
|
22
|
+
created: l
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materializePaste.js","names":["materializePaste","commitPaste","adapter","offset","snapshot","ctx","opts","created","newIds","o","createInsertOp","createSetSelectionOp"],"sources":["../../../../src/interactions/actions/clipboard/materializePaste.ts"],"sourcesContent":["/**\n * Shared paste materialization — the one place the paste recipe lives:\n * offset policy → `commitPaste` → the op batch (InsertOps parents-first +\n * a SetSelectionOp). Both `useClipboardOps.paste` (in-memory paste) and the\n * kit weasel-JSON content handler (OS-clipboard paste) dispatch exactly this\n * batch, so the two arrival paths stay indistinguishable in history.\n *\n * Pure: no dispatching here — callers own how the ops are applied\n * (`dispatchApplyBatch` vs `IngestCtx.applyOps`) and any post-paste\n * bookkeeping (the hook's cascade re-snapshot, the handler's selection set).\n */\nimport { createInsertOp } from 'core/ops/create';\nimport { createSetSelectionOp } from 'core/ops/select';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId } from 'core/scene/types';\nimport type { ClipboardSnapshot, InsertAdapter } from 'core/adapters/types';\n\n/** Shared paste materialization: offset policy → commitPaste → the op batch\n * both the in-memory paste and the OS-paste handler dispatch. Returns null\n * when the adapter can't paste or nothing was created. */\nexport function materializePaste<TNode extends { id: string }>(\n adapter: InsertAdapter<TNode>,\n snapshot: ClipboardSnapshot,\n opts: {\n currentSelection: string[];\n dropPoint?: { worldX: number; worldY: number } | null;\n },\n): { ops: Op[]; newIds: string[]; created: TNode[] } | null {\n const commitPaste = adapter.commitPaste;\n if (!commitPaste) return null;\n const offset = adapter.getPasteOffset?.(snapshot) ?? { dx: 0, dy: 0 };\n const ctx = opts.dropPoint != null ? { dropPoint: opts.dropPoint } :
|
|
1
|
+
{"version":3,"file":"materializePaste.js","names":["materializePaste","commitPaste","adapter","offset","snapshot","omitted","ctx","opts","omission","created","newIds","o","createInsertOp","createSetSelectionOp"],"sources":["../../../../src/interactions/actions/clipboard/materializePaste.ts"],"sourcesContent":["/**\n * Shared paste materialization — the one place the paste recipe lives:\n * offset policy → `commitPaste` → the op batch (InsertOps parents-first +\n * a SetSelectionOp). Both `useClipboardOps.paste` (in-memory paste) and the\n * kit weasel-JSON content handler (OS-clipboard paste) dispatch exactly this\n * batch, so the two arrival paths stay indistinguishable in history.\n *\n * Pure: no dispatching here — callers own how the ops are applied\n * (`dispatchApplyBatch` vs `IngestCtx.applyOps`) and any post-paste\n * bookkeeping (the hook's cascade re-snapshot, the handler's selection set).\n */\nimport { createInsertOp } from 'core/ops/create';\nimport { createSetSelectionOp } from 'core/ops/select';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId } from 'core/scene/types';\nimport type { ClipboardSnapshot, InsertAdapter, PasteCtx, PasteOmission } from 'core/adapters/types';\n\n/** Shared paste materialization: offset policy → commitPaste → the op batch\n * both the in-memory paste and the OS-paste handler dispatch. Returns null\n * when the adapter can't paste or nothing was created. */\nexport function materializePaste<TNode extends { id: string }>(\n adapter: InsertAdapter<TNode>,\n snapshot: ClipboardSnapshot,\n opts: {\n currentSelection: string[];\n dropPoint?: { worldX: number; worldY: number } | null;\n /** Told what the paste left out and why, when it left anything out —\n * including a paste that made nothing. */\n onOmitted?: (omitted: PasteOmission[]) => void;\n },\n): { ops: Op[]; newIds: string[]; created: TNode[] } | null {\n const commitPaste = adapter.commitPaste;\n if (!commitPaste) return null;\n const offset = adapter.getPasteOffset?.(snapshot) ?? { dx: 0, dy: 0 };\n const omitted: PasteOmission[] = [];\n const ctx: PasteCtx = {\n ...(opts.dropPoint != null ? { dropPoint: opts.dropPoint } : {}),\n ...(opts.onOmitted ? { onOmit: (omission: PasteOmission) => omitted.push(omission) } : {}),\n };\n const created = commitPaste.call(\n adapter, snapshot, offset, Object.keys(ctx).length > 0 ? ctx : undefined,\n );\n if (omitted.length > 0) opts.onOmitted?.(omitted);\n if (created.length === 0) return null;\n const newIds = created.map((o) => o.id);\n const ops: Op[] = [\n ...created.map((o) => createInsertOp({ node: o })),\n createSetSelectionOp({ from: opts.currentSelection as NodeId[], to: newIds as NodeId[] }),\n ];\n return { ops, newIds, created };\n}\n"],"mappings":";;;AAoBA,SAAgBA,EACd,GACA,GACA,GAO0D;CAC1D,IAAMC,IAAcC,EAAQ;CAC5B,IAAI,CAACD,GAAa,OAAO;CACzB,IAAME,IAASD,EAAQ,iBAAiBE,CAAQ,KAAK;EAAE,IAAI;EAAG,IAAI;CAAE,GAC9DC,IAA2B,CAAC,GAC5BC,IAAgB;EACpB,GAAIC,EAAK,aAAa,OAAuC,CAAC,IAAjC,EAAE,WAAWA,EAAK,UAAU;EACzD,GAAIA,EAAK,YAAY,EAAE,SAAS,MAA4BF,EAAQ,KAAKG,CAAQ,EAAE,IAAI,CAAC;CAC1F,GACMC,IAAUR,EAAY,KAC1BC,GAASE,GAAUD,GAAQ,OAAO,KAAKG,CAAG,CAAC,CAAC,SAAS,IAAIA,IAAM,KAAA,CACjE;CAEA,IADID,EAAQ,SAAS,KAAGE,EAAK,YAAYF,CAAO,GAC5CI,EAAQ,WAAW,GAAG,OAAO;CACjC,IAAMC,IAASD,EAAQ,KAAK,MAAME,EAAE,EAAE;CAKtC,OAAO;EAAE,KAAA,CAHP,GAAGF,EAAQ,KAAK,MAAMG,EAAe,EAAE,MAAMD,EAAE,CAAC,CAAC,GACjDE,EAAqB;GAAE,MAAMN,EAAK;GAA8B,IAAIG;EAAmB,CAAC,CAEjF;EAAK;EAAQ;CAAQ;AAChC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wireFormat.js","names":["WEASEL_CLIPBOARD_MIME","WEASEL_CLIPBOARD_MIME_WEB","buildWeaselClipboardText","items","replacer","sniffWeaselClipboardText","text","parsed","SVG_OPEN_TAG","embedWeaselMetadataInSvg","open","svgText","dwarn","cdata","payloadText","at","extractWeaselClipboardFromSvg","block","inner","payload","m","parseWeaselClipboardText","reviver","err"],"sources":["../../../../src/interactions/actions/clipboard/wireFormat.ts"],"sourcesContent":["import { dwarn } from 'debug/flag';\n\n/** MIME type the kit writes its full-fidelity clipboard payload under, so a\n * copy/paste between two weasel canvases round-trips scene nodes rather than\n * the SVG or text flavors other apps read. */\nexport const WEASEL_CLIPBOARD_MIME = 'application/x-weasel-clipboard+json';\n/** Chromium's async-clipboard spelling for custom formats. */\nexport const WEASEL_CLIPBOARD_MIME_WEB = `web ${WEASEL_CLIPBOARD_MIME}`;\n\ninterface WeaselClipboardPayload { weaselClipboard: 1; nodes: unknown[] }\n\ntype Replacer = (key: string, value: unknown) => unknown;\ntype Reviver = (key: string, value: unknown) => unknown;\n\n/** Serialize snapshot items into the versioned wire text.\n *\n *
|
|
1
|
+
{"version":3,"file":"wireFormat.js","names":["WEASEL_CLIPBOARD_MIME","WEASEL_CLIPBOARD_MIME_WEB","buildWeaselClipboardText","items","replacer","sniffWeaselClipboardText","text","parsed","SVG_OPEN_TAG","embedWeaselMetadataInSvg","open","svgText","dwarn","cdata","payloadText","at","extractWeaselClipboardFromSvg","block","inner","payload","m","parseWeaselClipboardText","reviver","err"],"sources":["../../../../src/interactions/actions/clipboard/wireFormat.ts"],"sourcesContent":["import { dwarn } from 'debug/flag';\n\n/** MIME type the kit writes its full-fidelity clipboard payload under, so a\n * copy/paste between two weasel canvases round-trips scene nodes rather than\n * the SVG or text flavors other apps read. */\nexport const WEASEL_CLIPBOARD_MIME = 'application/x-weasel-clipboard+json';\n/** Chromium's async-clipboard spelling for custom formats. */\nexport const WEASEL_CLIPBOARD_MIME_WEB = `web ${WEASEL_CLIPBOARD_MIME}`;\n\ninterface WeaselClipboardPayload { weaselClipboard: 1; nodes: unknown[] }\n\ntype Replacer = (key: string, value: unknown) => unknown;\ntype Reviver = (key: string, value: unknown) => unknown;\n\n/** Serialize snapshot items into the versioned wire text.\n *\n * JSON drops a function-valued field (a container's `clipFromPose`, a\n * node's `derivePath`). A scene adapter's snapshot also carries each one's\n * `SceneRegistry` key (`clipFromPoseKey`, `derivePathKey`, …), which does\n * cross, and its paste restores the function from the pasting scene's\n * registry. A function that is not registered does not survive. */\nexport function buildWeaselClipboardText(items: unknown[], replacer?: Replacer): string {\n const payload: WeaselClipboardPayload = { weaselClipboard: 1, nodes: items };\n return JSON.stringify(payload, replacer as Parameters<typeof JSON.stringify>[1]);\n}\n\n/** Cheap check: could this text be a weasel clipboard payload? Substring\n * pre-check, then a guarded parse + marker/version test. */\nexport function sniffWeaselClipboardText(text: string): boolean {\n if (!text.includes('\"weaselClipboard\"')) return false;\n try {\n const parsed = JSON.parse(text) as Partial<WeaselClipboardPayload> | null;\n return !!parsed && parsed.weaselClipboard === 1;\n } catch {\n return false;\n }\n}\n\n// Opening `<svg ...>` tag of a serialized document. `[^>]*` is safe for OUR\n// serializer's output (attribute values are XML-escaped, so no raw `>` can\n// appear inside the tag) — see embedWeaselMetadataInSvg's contract note.\nconst SVG_OPEN_TAG = /<svg(?:\\s[^>]*)?>/i;\n\n/**\n * Embed a weasel clipboard payload in an SVG document as a `<metadata>`\n * child of the root — legal SVG 1.1, ignored by external tools — so one\n * `text/plain` flavor serves both audiences: external tools parse the SVG,\n * weasel extracts the JSON for full fidelity on draw→draw DOM paste.\n *\n * The payload rides in CDATA so JSON's `<` / `&` need no entity escaping; a\n * literal `]]>` inside the payload is split across CDATA sections\n * (`]]]]><![CDATA[>`), which {@link extractWeaselClipboardFromSvg} rejoins.\n *\n * Intended for OUR serializer's output only (`serializeSvg` — single\n * `<svg ...>` open tag, XML-escaped attributes). When no open tag is found\n * the input is returned unchanged with a debug warning.\n */\nexport function embedWeaselMetadataInSvg(svgText: string, payloadText: string): string {\n const open = SVG_OPEN_TAG.exec(svgText);\n if (!open) {\n dwarn('clipboard', 'embedWeaselMetadataInSvg: no <svg ...> open tag found — returning input unchanged');\n return svgText;\n }\n const cdata = `<![CDATA[${payloadText.replaceAll(']]>', ']]]]><![CDATA[>')}]]>`;\n const at = open.index + open[0].length;\n return `${svgText.slice(0, at)}<metadata>${cdata}</metadata>${svgText.slice(at)}`;\n}\n\n/**\n * Recover an embedded weasel clipboard payload from SVG text: read the first\n * `<metadata>` block, rejoin its CDATA sections (concatenation restores the\n * `]]>` split by {@link embedWeaselMetadataInSvg}; plain non-CDATA content\n * passes through as-is), and return the payload text iff it sniffs as the\n * weasel wire format — else null (external/non-weasel metadata declines).\n *\n * A text scan, not an XML parse — the payload is small and ours.\n */\nexport function extractWeaselClipboardFromSvg(svgText: string): string | null {\n const block = /<metadata(?:\\s[^>]*)?>([\\s\\S]*?)<\\/metadata>/i.exec(svgText);\n if (!block) return null;\n const inner = block[1];\n const payload = inner.includes('<![CDATA[')\n ? Array.from(inner.matchAll(/<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>/g), (m) => m[1]).join('')\n : inner;\n return sniffWeaselClipboardText(payload) ? payload : null;\n}\n\n/** Parse wire text to snapshot items, or null when malformed/mismatched\n * (callers decline to the next content handler). */\nexport function parseWeaselClipboardText(text: string, reviver?: Reviver): unknown[] | null {\n try {\n const parsed = JSON.parse(text, reviver as Parameters<typeof JSON.parse>[1]) as\n Partial<WeaselClipboardPayload> | null;\n if (!parsed || parsed.weaselClipboard !== 1 || !Array.isArray(parsed.nodes)) return null;\n return parsed.nodes;\n } catch (err) {\n dwarn('clipboard', `weasel payload failed to parse: ${String(err)}`);\n return null;\n }\n}\n"],"mappings":";;AAKA,IAAaA,IAAwB,uCAExBC,IAA4B,OAAOD;AAchD,SAAgBE,EAAyB,GAAkB,GAA6B;CAEtF,OAAO,KAAK,UAAU;EADoB,iBAAiB;EAAG,OAAOC;CAC/C,GAASC,CAAgD;AACjF;AAIA,SAAgBC,EAAyB,GAAuB;CAC9D,IAAI,CAACC,EAAK,SAAS,qBAAmB,GAAG,OAAO;CAChD,IAAI;EACF,IAAMC,IAAS,KAAK,MAAMD,CAAI;EAC9B,OAAO,CAAC,CAACC,KAAUA,EAAO,oBAAoB;CAChD,QAAQ;EACN,OAAO;CACT;AACF;AAKA,IAAMC,IAAe;AAgBrB,SAAgBC,EAAyB,GAAiB,GAA6B;CACrF,IAAMC,IAAOF,EAAa,KAAKG,CAAO;CACtC,IAAI,CAACD,GAEH,OADAE,EAAM,aAAa,mFAAmF,GAC/FD;CAET,IAAME,IAAQ,YAAYC,EAAY,WAAW,OAAO,iBAAiB,EAAE,MACrEC,IAAKL,EAAK,QAAQA,EAAK,EAAE,CAAC;CAChC,OAAO,GAAGC,EAAQ,MAAM,GAAGI,CAAE,EAAE,YAAYF,EAAM,aAAaF,EAAQ,MAAMI,CAAE;AAChF;AAWA,SAAgBC,EAA8B,GAAgC;CAC5E,IAAMC,IAAQ,gDAAgD,KAAKN,CAAO;CAC1E,IAAI,CAACM,GAAO,OAAO;CACnB,IAAMC,IAAQD,EAAM,IACdE,IAAUD,EAAM,SAAS,WAAW,IACtC,MAAM,KAAKA,EAAM,SAAS,6BAA6B,IAAI,MAAME,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAC9EF;CACJ,OAAOb,EAAyBc,CAAO,IAAIA,IAAU;AACvD;AAIA,SAAgBE,EAAyB,GAAc,GAAqC;CAC1F,IAAI;EACF,IAAMd,IAAS,KAAK,MAAMD,GAAMgB,CAA2C;EAG3E,OADI,CAACf,KAAUA,EAAO,oBAAoB,KAAK,CAAC,MAAM,QAAQA,EAAO,KAAK,IAAU,OAC7EA,EAAO;CAChB,SAASgB,GAAK;EAEZ,OADAX,EAAM,aAAa,mCAAmC,OAAOW,CAAG,GAAG,GAC5D;CACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloneByAltDrag.js","names":["cloneByAltDrag","mods","ctx","snap","pose","created","newIds","o","from","createInsertOp","createSetSelectionOp"],"sources":["../../../../../src/interactions/actions/clone/behaviors/cloneByAltDrag.ts"],"sourcesContent":["import { createInsertOp } from 'core/ops/create';\nimport { createSetSelectionOp } from 'core/ops/select';\nimport type { NodeId } from 'core/scene/types';\nimport type { CloneBehavior } from '../../../gestures/types';\n\n/** Clone-on-alt-drag behavior
|
|
1
|
+
{"version":3,"file":"cloneByAltDrag.js","names":["cloneByAltDrag","mods","ctx","snap","pose","created","newIds","o","from","createInsertOp","createSetSelectionOp"],"sources":["../../../../../src/interactions/actions/clone/behaviors/cloneByAltDrag.ts"],"sourcesContent":["import { createInsertOp } from 'core/ops/create';\nimport { createSetSelectionOp } from 'core/ops/select';\nimport type { NodeId } from 'core/scene/types';\nimport type { CloneBehavior } from '../../../gestures/types';\n\n/** Clone-on-alt-drag behavior; activates when Alt/Option is held at drag start.\n * `cloneAction` runs no clone behaviors, so nothing in the kit calls this. */\nexport function cloneByAltDrag(): CloneBehavior {\n return {\n id: 'cloneByAltDrag',\n activates: (mods) => mods.alt === true,\n onEnd(pose, ctx) {\n if (!ctx.adapter.snapshotSelection || !ctx.adapter.commitPaste) return [];\n const snap = ctx.adapter.snapshotSelection(pose.ids);\n const created = ctx.adapter.commitPaste(snap, pose.offset, {\n dropPoint: { worldX: pose.worldX, worldY: pose.worldY },\n });\n if (created.length === 0) return [];\n const newIds = created.map((o: { id: string }) => o.id);\n const from = ctx.adapter.getSelection?.() ?? [];\n return [\n ...created.map((o) => createInsertOp({ node: o })),\n createSetSelectionOp({ from: from as NodeId[], to: newIds as NodeId[] }),\n ];\n },\n };\n}\n"],"mappings":";;;AAOA,SAAgBA,IAAgC;CAC9C,OAAO;EACL,IAAI;EACJ,YAAY,MAASC,EAAK,QAAQ;EAClC,MAAM,GAAM,GAAK;GACf,IAAI,CAACC,EAAI,QAAQ,qBAAqB,CAACA,EAAI,QAAQ,aAAa,OAAO,CAAC;GACxE,IAAMC,IAAOD,EAAI,QAAQ,kBAAkBE,EAAK,GAAG,GAC7CC,IAAUH,EAAI,QAAQ,YAAYC,GAAMC,EAAK,QAAQ,EACzD,WAAW;IAAE,QAAQA,EAAK;IAAQ,QAAQA,EAAK;GAAO,EACxD,CAAC;GACD,IAAIC,EAAQ,WAAW,GAAG,OAAO,CAAC;GAClC,IAAMC,IAASD,EAAQ,KAAK,MAAsBE,EAAE,EAAE,GAChDC,IAAON,EAAI,QAAQ,eAAe,KAAK,CAAC;GAC9C,OAAO,CACL,GAAGG,EAAQ,KAAK,MAAMI,EAAe,EAAE,MAAMF,EAAE,CAAC,CAAC,GACjDG,EAAqB;IAAQ;IAAkB,IAAIJ;GAAmB,CAAC,CACzE;EACF;CACF;AACF"}
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import { documentPose as e } from "../../core/scene/effectivePose.js";
|
|
2
2
|
import { asNodeId as t } from "../../core/scene/types.js";
|
|
3
3
|
import { fnFieldsOfNode as n } from "../../core/scene/nodeFnFields.js";
|
|
4
|
+
import { pickability as r } from "../../core/scene/pickability.js";
|
|
4
5
|
//#region src/interactions/actions/defaultCommitAdapter.ts
|
|
5
|
-
function
|
|
6
|
+
function i(i, a) {
|
|
6
7
|
return {
|
|
7
|
-
getNode: (e) =>
|
|
8
|
-
getNodes: () => [...
|
|
9
|
-
getPose: (n) => e(
|
|
10
|
-
getSelection: () => [...
|
|
8
|
+
getNode: (e) => i.get(t(e)),
|
|
9
|
+
getNodes: () => [...i.renderOrderNodes()],
|
|
10
|
+
getPose: (n) => e(i, i.get(t(n))),
|
|
11
|
+
getSelection: () => [...a ? a.getSelection() : i.getSelection()],
|
|
11
12
|
setSelection: (e) => {
|
|
12
|
-
|
|
13
|
+
a ? a.setSelection(e.map(t)) : i.setSelection(e.map(t));
|
|
13
14
|
},
|
|
14
|
-
getParent: (e) =>
|
|
15
|
-
getRemovalClosure: (e) => [...
|
|
16
|
-
getChildren: (e) => e === null ? [...
|
|
15
|
+
getParent: (e) => i.get(t(e))?.parent ?? null,
|
|
16
|
+
getRemovalClosure: (e) => [...i.removalClosure(e.map(t))],
|
|
17
|
+
getChildren: (e) => e === null ? [...i.roots] : [...i.childrenOf(t(e))],
|
|
17
18
|
setChildOrder: (e, n) => {
|
|
18
|
-
for (let
|
|
19
|
+
for (let r = 0; r < n.length; r++) (e === null ? i.roots : i.childrenOf(t(e)))[r] !== n[r] && i.reorder(t(n[r]), r);
|
|
19
20
|
},
|
|
20
|
-
setPose: (e, n) =>
|
|
21
|
-
setParent: (e, n) =>
|
|
22
|
-
setData: (e, n) =>
|
|
23
|
-
setLayer: (e, n) =>
|
|
24
|
-
setLayout: (e, n) =>
|
|
25
|
-
removeNode: (e) =>
|
|
26
|
-
insertNode: (e, t) =>
|
|
21
|
+
setPose: (e, n) => i.setPose(t(e), n),
|
|
22
|
+
setParent: (e, n) => i.move(t(e), n == null ? null : t(n)),
|
|
23
|
+
setData: (e, n) => i.update(t(e), { data: n }),
|
|
24
|
+
setLayer: (e, n) => i.setLayer(t(e), n),
|
|
25
|
+
setLayout: (e, n) => i.setLayout(t(e), n),
|
|
26
|
+
removeNode: (e) => i.remove(t(e)),
|
|
27
|
+
insertNode: (e, t) => i.add({
|
|
27
28
|
kind: e.kind,
|
|
28
29
|
layer: e.layer,
|
|
29
30
|
pose: e.pose,
|
|
@@ -32,11 +33,12 @@ function r(r, i) {
|
|
|
32
33
|
...e.parent == null ? {} : { parent: e.parent },
|
|
33
34
|
...t === void 0 ? {} : { index: t },
|
|
34
35
|
...e.dependsOn === void 0 ? {} : { dependsOn: e.dependsOn },
|
|
36
|
+
...r(e),
|
|
35
37
|
...n(e)
|
|
36
38
|
})
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
//#endregion
|
|
40
|
-
export {
|
|
42
|
+
export { i as defaultCommitAdapter };
|
|
41
43
|
|
|
42
44
|
//# sourceMappingURL=defaultCommitAdapter.js.map
|