@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/debug/flag.js
CHANGED
|
@@ -1,41 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
function n() {
|
|
4
|
-
let t = {
|
|
5
|
-
all: !1,
|
|
6
|
-
set: /* @__PURE__ */ new Set()
|
|
7
|
-
};
|
|
8
|
-
if (typeof localStorage > "u") return t;
|
|
9
|
-
try {
|
|
10
|
-
for (let t of e) {
|
|
11
|
-
let e = localStorage.getItem(t);
|
|
12
|
-
if (e != null && e !== "") return e === "1" || e === "true" || e === "*" ? {
|
|
13
|
-
all: !0,
|
|
14
|
-
set: /* @__PURE__ */ new Set()
|
|
15
|
-
} : {
|
|
16
|
-
all: !1,
|
|
17
|
-
set: new Set(e.split(/[\s,]+/).filter(Boolean))
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
return t;
|
|
21
|
-
} catch {
|
|
22
|
-
return t;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function r() {
|
|
26
|
-
return t === null && (t = n()), t;
|
|
27
|
-
}
|
|
28
|
-
function i(e) {
|
|
29
|
-
let t = r();
|
|
30
|
-
return t.all ? !0 : e === void 0 ? t.set.size > 0 : t.set.has(e);
|
|
31
|
-
}
|
|
32
|
-
function a(e, ...t) {
|
|
33
|
-
i(e) && console.debug(`[${e}]`, ...t);
|
|
34
|
-
}
|
|
35
|
-
function o(e, ...t) {
|
|
36
|
-
i(e) && console.warn(`[${e}]`, ...t);
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
export { a as dlog, o as dwarn, i as isDebugEnabled };
|
|
40
|
-
|
|
41
|
-
//# sourceMappingURL=flag.js.map
|
|
1
|
+
import { dlog as e, dwarn as t, isDebugEnabled as n } from "@weasel-js/routing";
|
|
2
|
+
export { e as dlog, t as dwarn, n as isDebugEnabled };
|
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useLatest as
|
|
1
|
+
import { useCallback as e, useEffect as t, useMemo as n, useRef as r } from "react";
|
|
2
|
+
import { useLatest as i } from "@weasel-js/react";
|
|
3
3
|
//#region src/features/chrome-caps/useHoverTracking.ts
|
|
4
|
-
function
|
|
5
|
-
let {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (!e) return;
|
|
4
|
+
function a(a) {
|
|
5
|
+
let { nodeAtClientPoint: o, enabled: s = !0 } = a, c = r(null), l = i(o), u = r(null), d = r(s), f = r(null), p = e(() => {
|
|
6
|
+
f.current?.(), f.current = null, c.current = null;
|
|
7
|
+
let e = u.current;
|
|
8
|
+
if (!e || !d.current) return;
|
|
10
9
|
let t = (e) => {
|
|
11
|
-
|
|
10
|
+
c.current = l.current(e.clientX, e.clientY)?.id ?? null;
|
|
12
11
|
}, n = () => {
|
|
13
|
-
|
|
12
|
+
c.current = null;
|
|
14
13
|
};
|
|
15
|
-
|
|
14
|
+
e.addEventListener("pointermove", t), e.addEventListener("pointerleave", n), f.current = () => {
|
|
16
15
|
e.removeEventListener("pointermove", t), e.removeEventListener("pointerleave", n);
|
|
17
16
|
};
|
|
18
|
-
}, [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
}, [l]);
|
|
18
|
+
t(() => (d.current = s, p(), () => {
|
|
19
|
+
f.current?.(), f.current = null, c.current = null;
|
|
20
|
+
}), [s, p]);
|
|
21
|
+
let m = e((e) => {
|
|
22
|
+
u.current !== e && (u.current = e, p());
|
|
23
|
+
}, [p]), h = e(() => c.current, []);
|
|
24
|
+
return n(() => ({
|
|
25
|
+
getHover: h,
|
|
26
|
+
canvasRef: m
|
|
27
|
+
}), [h, m]);
|
|
23
28
|
}
|
|
24
29
|
//#endregion
|
|
25
|
-
export {
|
|
30
|
+
export { a as useHoverTracking };
|
|
26
31
|
|
|
27
32
|
//# sourceMappingURL=useHoverTracking.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHoverTracking.js","names":["useHoverTracking","args","hoverRef","useRef","nodeAtRef","useLatest","nodeAtClientPoint","
|
|
1
|
+
{"version":3,"file":"useHoverTracking.js","names":["useHoverTracking","args","hoverRef","useRef","nodeAtRef","useLatest","nodeAtClientPoint","elementRef","enabledRef","enabled","detachRef","sync","useCallback","el","onMove","onLeave","canvasRef","element","getHover","useMemo"],"sources":["../../../src/features/chrome-caps/useHoverTracking.ts"],"sourcesContent":["/**\n * useHoverTracking — last-hovered NodeId for chrome-caps rules.\n *\n * Listens for `pointermove` on the element handed to the returned\n * `canvasRef`; on each move it runs the supplied `nodeAtClientPoint` and\n * caches the resulting id on a ref. Cleared on `pointerleave` (pointer left\n * the canvas → nothing is hovered).\n *\n * `getHover` is a stable getter — call once per frame from `buildChromeCtx`.\n *\n * No re-render: the ref updates silently, and the next render that\n * reads it picks up the fresh value. Chrome-caps rules are evaluated\n * during paint, so the hover state is always read fresh.\n */\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from '../../core/scene/types';\n\n/** Options for `useHoverTracking`. */\nexport interface UseHoverTrackingArgs {\n /** Topmost id under a client point, or null. One lookup rather than a\n * client→world thunk beside a world-space picker: a screen-pixel pick\n * tolerance converts against the camera the point resolved to, and two\n * thunks are two chances to disagree about which view that is. */\n nodeAtClientPoint: (clientX: number, clientY: number) => { id: NodeId } | null;\n enabled?: boolean;\n}\n\n/** What `useHoverTracking` returns. Both members keep their identity. */\nexport interface HoverTracking {\n /** The node the pointer is over, or null. */\n getHover: () => NodeId | null;\n /** Hand it the element to track, or null to stop: a callback ref, so an\n * element that mounts after the hook is tracked from the moment it arrives. */\n canvasRef: (element: HTMLElement | null) => void;\n}\n\n/** Track which node the pointer is over. Returns a getter rather than state,\n * so per-frame code can read the current value without the hover re-rendering\n * the component on every pointer move. */\nexport function useHoverTracking(args: UseHoverTrackingArgs): HoverTracking {\n const { nodeAtClientPoint, enabled = true } = args;\n const hoverRef = useRef<NodeId | null>(null);\n const nodeAtRef = useLatest(nodeAtClientPoint);\n const elementRef = useRef<HTMLElement | null>(null);\n const enabledRef = useRef(enabled);\n const detachRef = useRef<(() => void) | null>(null);\n\n const sync = useCallback(() => {\n detachRef.current?.();\n detachRef.current = null;\n hoverRef.current = null;\n const el = elementRef.current;\n if (!el || !enabledRef.current) return;\n const onMove = (e: PointerEvent) => {\n hoverRef.current = nodeAtRef.current(e.clientX, e.clientY)?.id ?? null;\n };\n const onLeave = () => { hoverRef.current = null; };\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerleave', onLeave);\n detachRef.current = () => {\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerleave', onLeave);\n };\n }, [nodeAtRef]);\n\n useEffect(() => {\n enabledRef.current = enabled;\n sync();\n return () => {\n detachRef.current?.();\n detachRef.current = null;\n hoverRef.current = null;\n };\n }, [enabled, sync]);\n\n const canvasRef = useCallback((element: HTMLElement | null) => {\n if (elementRef.current === element) return;\n elementRef.current = element;\n sync();\n }, [sync]);\n const getHover = useCallback(() => hoverRef.current, []);\n\n return useMemo(() => ({ getHover, canvasRef }), [getHover, canvasRef]);\n}\n"],"mappings":";;;AAyCA,SAAgBA,EAAiB,GAA2C;CAC1E,IAAM,EAAE,sBAAmB,aAAU,OAASC,GACxCC,IAAWC,EAAsB,IAAI,GACrCC,IAAYC,EAAUC,CAAiB,GACvCC,IAAaJ,EAA2B,IAAI,GAC5CK,IAAaL,EAAOM,CAAO,GAC3BC,IAAYP,EAA4B,IAAI,GAE5CQ,IAAOC,QAAkB;EAG7B,AAFAF,EAAU,UAAU,GACpB,EAAU,UAAU,MACpB,EAAS,UAAU;EACnB,IAAMG,IAAKN,EAAW;EACtB,IAAI,CAACM,KAAM,CAACL,EAAW,SAAS;EAChC,IAAMM,KAAU,MAAoB;GAClC,EAAS,UAAUV,EAAU,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM;EACpE,GACMW,UAAgB;GAAE,EAAS,UAAU;EAAM;EAGjD,AAFAF,EAAG,iBAAiB,eAAeC,CAAM,GACzCD,EAAG,iBAAiB,gBAAgBE,CAAO,GAC3C,EAAU,gBAAgB;GAExB,AADAF,EAAG,oBAAoB,eAAeC,CAAM,GAC5CD,EAAG,oBAAoB,gBAAgBE,CAAO;EAChD;CACF,GAAG,CAACX,CAAS,CAAC;CAEd,SACE,EAAW,UAAUK,GACrBE,EAAK,SACQ;EAGX,AAFAD,EAAU,UAAU,GACpB,EAAU,UAAU,MACpB,EAAS,UAAU;CACrB,IACC,CAACD,GAASE,CAAI,CAAC;CAElB,IAAMK,IAAYJ,GAAa,MAAgC;EAC7D,AAAIL,EAAW,YAAYU,MAC3B,EAAW,UAAUA,GACrBN,EAAK;CACP,GAAG,CAACA,CAAI,CAAC,GACHO,IAAWN,QAAkBV,EAAS,SAAS,CAAC,CAAC;CAEvD,OAAOiB,SAAe;EAAE;EAAU;CAAU,IAAI,CAACD,GAAUF,CAAS,CAAC;AACvE"}
|
|
@@ -2,11 +2,10 @@ import { subscribeToSources as e } from "../../core/layers/render.js";
|
|
|
2
2
|
//#region src/features/focus/gateLayer.ts
|
|
3
3
|
function t(t) {
|
|
4
4
|
let { layer: n, visible: r } = t, i = {
|
|
5
|
-
|
|
6
|
-
label: n.label,
|
|
5
|
+
...n,
|
|
7
6
|
draw: (e, t, i) => r() ? n.draw(e, t, i) : []
|
|
8
|
-
};
|
|
9
|
-
return
|
|
7
|
+
}, { deps: a, hitTest: o } = n;
|
|
8
|
+
return a && (i.deps = (e, t, n) => [r(), ...a(e, t, n)]), o && (i.hitTest = (...e) => r() ? o(...e) : null), n.subscribe && (i.subscribe = e([n])), i;
|
|
10
9
|
}
|
|
11
10
|
//#endregion
|
|
12
11
|
export { t as gateLayer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateLayer.js","names":["gateLayer","opts","wrapped","layer","visible","data","view","dims","subscribeToSources"],"sources":["../../../src/features/focus/gateLayer.ts"],"sourcesContent":["/**\n * `RenderLayer` wrapper that conditionally emits draw commands based on a\n * predicate. Most common use: hide selection-overlay handles when the\n * canvas isn't focused.\n *\n * The predicate is a getter (`() => boolean`), not a static value, because\n * RenderLayers are constructed once but their `draw` runs every frame —\n * a static value would lock in whatever the predicate returned at\n * construction time.\n */\nimport { subscribeToSources, type RenderLayer } from 'core/layers/render';\n\n/** Options for `gateLayer`. */\nexport interface GateLayerOptions<TData> {\n /** The wrapped layer.
|
|
1
|
+
{"version":3,"file":"gateLayer.js","names":["gateLayer","opts","wrapped","layer","visible","data","view","dims","deps","hitTest","args","subscribeToSources"],"sources":["../../../src/features/focus/gateLayer.ts"],"sourcesContent":["/**\n * `RenderLayer` wrapper that conditionally emits draw commands based on a\n * predicate. Most common use: hide selection-overlay handles when the\n * canvas isn't focused.\n *\n * The predicate is a getter (`() => boolean`), not a static value, because\n * RenderLayers are constructed once but their `draw` runs every frame —\n * a static value would lock in whatever the predicate returned at\n * construction time.\n */\nimport { subscribeToSources, type RenderLayer } from 'core/layers/render';\n\n/** Options for `gateLayer`. */\nexport interface GateLayerOptions<TData> {\n /** The wrapped layer. Everything but `draw`, `hitTest`, and `deps` flows\n * through unchanged. */\n layer: RenderLayer<TData>;\n /** Returns `true` to render the layer this frame, `false` to skip. */\n visible: () => boolean;\n}\n\n/** Wrap a render layer so it only draws, and is only hit, while `visible()`\n * is true. The predicate is consulted per frame, which is how chrome follows\n * canvas focus without the layer knowing anything about focus. */\nexport function gateLayer<TData>(opts: GateLayerOptions<TData>): RenderLayer<TData> {\n const { layer, visible } = opts;\n const wrapped: RenderLayer<TData> = {\n ...layer,\n draw: (data, view, dims) => (visible() ? layer.draw(data, view, dims) : []),\n };\n const { deps, hitTest } = layer;\n if (deps) wrapped.deps = (data, view, dims) => [visible(), ...deps(data, view, dims)];\n if (hitTest) wrapped.hitTest = (...args) => (visible() ? hitTest(...args) : null);\n if (layer.subscribe) wrapped.subscribe = subscribeToSources([layer]);\n return wrapped;\n}\n"],"mappings":";;AAwBA,SAAgBA,EAAiB,GAAmD;CAClF,IAAM,EAAE,UAAO,eAAYC,GACrBC,IAA8B;EAClC,GAAGC;EACH,OAAO,GAAM,GAAM,MAAUC,EAAQ,IAAID,EAAM,KAAKE,GAAMC,GAAMC,CAAI,IAAI,CAAC;CAC3E,GACM,EAAE,SAAM,eAAYJ;CAI1B,OAHIK,MAAM,EAAQ,QAAQ,GAAM,GAAM,MAAS,CAACJ,EAAQ,GAAG,GAAGI,EAAKH,GAAMC,GAAMC,CAAI,CAAC,IAChFE,MAAS,EAAQ,WAAW,GAAGC,MAAUN,EAAQ,IAAIK,EAAQ,GAAGC,CAAI,IAAI,OACxEP,EAAM,cAAW,EAAQ,YAAYQ,EAAmB,CAACR,CAAK,CAAC,IAC5DD;AACT"}
|
|
@@ -39,7 +39,8 @@ function g(e) {
|
|
|
39
39
|
height: n.height
|
|
40
40
|
},
|
|
41
41
|
scale: 1,
|
|
42
|
-
failed: /* @__PURE__ */ new Set()
|
|
42
|
+
failed: /* @__PURE__ */ new Set(),
|
|
43
|
+
floor: 0
|
|
43
44
|
} } : {}
|
|
44
45
|
}), h());
|
|
45
46
|
}, () => {
|
|
@@ -57,18 +58,20 @@ function v(e, n, i, a) {
|
|
|
57
58
|
let s = _(i.natural, a);
|
|
58
59
|
if (s === void 0) return;
|
|
59
60
|
let c = s > i.scale;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
c && i.shrunkTo === i.scale && (i.floor = Math.max(i.floor, s));
|
|
62
|
+
let l = c ? s : Math.max(s, i.floor);
|
|
63
|
+
if (!(!c && l * r > i.scale)) {
|
|
64
|
+
for (let e of i.failed) if (l === e || c && e > i.scale && l >= e) return;
|
|
65
|
+
i.pending = l, m(e, {
|
|
66
|
+
width: Math.max(1, Math.round(i.natural.width * l)),
|
|
67
|
+
height: Math.max(1, Math.round(i.natural.height * l))
|
|
65
68
|
}).then((r) => {
|
|
66
69
|
if (o.get(e) !== n) return;
|
|
67
70
|
i.pending = void 0;
|
|
68
71
|
let a = n.bitmap;
|
|
69
|
-
n.bitmap = r, i.scale =
|
|
72
|
+
n.bitmap = r, i.shrunkTo = c ? void 0 : l, i.scale = l, a && a !== r && t(a), h();
|
|
70
73
|
}, () => {
|
|
71
|
-
o.get(e) === n && (i.pending = void 0, i.failed.add(
|
|
74
|
+
o.get(e) === n && (i.pending = void 0, i.failed.add(l));
|
|
72
75
|
});
|
|
73
76
|
}
|
|
74
77
|
}
|
|
@@ -84,11 +87,18 @@ function b(e) {
|
|
|
84
87
|
return o.get(e)?.status ?? "idle";
|
|
85
88
|
}
|
|
86
89
|
function x(e) {
|
|
90
|
+
let n = o.get(e);
|
|
91
|
+
o.delete(e), c.delete(e), n?.bitmap && t(n.bitmap);
|
|
92
|
+
}
|
|
93
|
+
function S(e) {
|
|
94
|
+
o.get(e)?.status === "error" && (g(e), h());
|
|
95
|
+
}
|
|
96
|
+
function C(e) {
|
|
87
97
|
return s.add(e), () => {
|
|
88
98
|
s.delete(e);
|
|
89
99
|
};
|
|
90
100
|
}
|
|
91
101
|
//#endregion
|
|
92
|
-
export { y as getImageBitmap, b as imageStatus, l as isVectorImageSrc, x as subscribeImageReady };
|
|
102
|
+
export { y as getImageBitmap, b as imageStatus, l as isVectorImageSrc, x as releaseImage, S as retryImage, C as subscribeImageReady };
|
|
93
103
|
|
|
94
104
|
//# sourceMappingURL=imageCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageCache.js","names":["MAX_VECTOR_RASTER_PIXELS","SHRINK_SLACK","FALLBACK_WIDTH","FALLBACK_HEIGHT","cache","subscribers","decodedVectors","isVectorImageSrc","src","requireDom","decodeImage","img","decodeVector","decoded","rasterizeVector","width","size","height","canvas","c2d","loader","notify","cb","startLoad","entry","bitmap","rasterScaleFor","need","drawnAt","natural","side","maxImageTextureSide","cap","maybeReraster","v","target","grow","f","old","releaseImageSource","getImageBitmap","imageStatus","subscribeImageReady"],"sources":["../../../src/features/images/imageCache.ts"],"sourcesContent":["/**\n * imageCache — the kit's per-`src` image loader + cache.\n *\n * The shape-painter chain (`kit:image` in `NodeShape.ts`) is synchronous, but\n * decoding an image is async. This module bridges the two: `getImageBitmap` is\n * a synchronous read that returns the decoded `ImageBitmap` when ready, and\n * otherwise returns `undefined` and lazily kicks off a de-duped async load.\n * When a load resolves (or errors), every `subscribeImageReady` listener fires\n * — `<SceneCanvas>` subscribes and calls `requestRedraw()`, so the painter\n * re-runs and emits the now-ready bitmap.\n *\n * `src` is any browser-loadable image string: a remote URL, a `blob:` URL, or\n * a `data:image/…;base64,…` URI (the embedded-bytes path). The decoded bitmap\n * lives only here, keyed by `src` — never in node `data` — so `scene.toJSON()`\n * round-trips the (serializable) `src` string untouched.\n */\n\nimport { maxImageTextureSide, releaseImageSource } from 'renderer/cache/GLImageCache';\n\n/** Public shape of an image node's `data`. `src` round-trips through\n * serialization; the decoded bitmap is held in this cache, not on the node. */\nexport interface ImageNodeData {\n image: {\n src: string;\n opacity?: number;\n /** The part of the bitmap drawn into the pose rect, as fractions of the\n * bitmap's width and height from its top-left. Omitted draws the whole\n * bitmap. Fractions, so the data means the same thing before the bitmap\n * has decoded and after `src` is swapped for another resolution. */\n source?: { x: number; y: number; width: number; height: number };\n /** Mirror the drawn region within the pose rect. The rect does not move. */\n flipX?: boolean;\n flipY?: boolean;\n };\n}\n\n/** Where an image is in its load: never requested, in flight, decoded, or\n * failed. Renderers paint a placeholder for anything but `'ready'`. */\nexport type ImageStatus = 'idle' | 'loading' | 'ready' | 'error';\n\n/** A size in device pixels. */\nexport interface ImageRasterSize {\n width: number;\n height: number;\n}\n\n/** A vector source's raster bookkeeping. `scale` is the current raster's size\n * over the natural one, always a power of two unless a cap cut it short. */\ninterface VectorRaster {\n natural: ImageRasterSize;\n scale: number;\n pending?: number;\n failed: Set<number>;\n}\n\ninterface Entry {\n status: ImageStatus;\n bitmap?: ImageBitmap;\n vector?: VectorRaster;\n}\n\n/** `size` is absent for the first load, which decodes at natural size. */\ntype Loader = (src: string, size?: ImageRasterSize) => Promise<ImageBitmap>;\n\n/** One vector raster may hold at most 64MB of RGBA. */\nconst MAX_VECTOR_RASTER_PIXELS = 4096 * 4096;\n/** A raster is only shrunk once it is this many times larger than needed, so\n * zooming back and forth across a bucket edge does not re-raster each way. */\nconst SHRINK_SLACK = 4;\n// CSS default object size, as `kit:svg` measures a viewBox-only SVG.\nconst FALLBACK_WIDTH = 300;\nconst FALLBACK_HEIGHT = 150;\n\nconst cache = new Map<string, Entry>();\nconst subscribers = new Set<() => void>();\nconst decodedVectors = new Map<string, Promise<HTMLImageElement>>();\n\n/** True when `src` names SVG — a `data:image/svg+xml` URI, or a URL whose path\n * ends in `.svg`/`.svgz`. Such sources re-rasterize at the size they are drawn\n * at; everything else decodes once. A `blob:` URL carries no type and counts\n * as raster. */\nexport function isVectorImageSrc(src: string): boolean {\n if (/^data:image\\/svg\\+xml[;,]/i.test(src)) return true;\n if (/^(data|blob):/i.test(src)) return false;\n return /\\.svgz?$/i.test(src.replace(/[?#].*$/, ''));\n}\n\nfunction requireDom(): void {\n if (typeof Image === 'undefined' || typeof createImageBitmap === 'undefined') {\n throw new Error('weasel imageCache: no DOM image loader in this environment');\n }\n}\n\nasync function decodeImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n await img.decode();\n return img;\n}\n\nfunction decodeVector(src: string): Promise<HTMLImageElement> {\n let decoded = decodedVectors.get(src);\n if (!decoded) {\n decoded = decodeImage(src);\n decodedVectors.set(src, decoded);\n decoded.catch(() => decodedVectors.delete(src));\n }\n return decoded;\n}\n\n// `createImageBitmap` rejects SVG blobs and resamples an SVG `<img>` rather\n// than re-drawing it, so vectors go through a canvas `drawImage` at full size.\nasync function rasterizeVector(src: string, size?: ImageRasterSize): Promise<ImageBitmap> {\n const img = await decodeVector(src);\n const width = size?.width ?? (img.naturalWidth || FALLBACK_WIDTH);\n const height = size?.height ?? (img.naturalHeight || FALLBACK_HEIGHT);\n const canvas: OffscreenCanvas | HTMLCanvasElement = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(width, height)\n : Object.assign(document.createElement('canvas'), { width, height });\n const c2d = canvas.getContext('2d') as CanvasDrawImage | null;\n if (!c2d) throw new Error('weasel imageCache: no 2D context to rasterize an SVG');\n c2d.drawImage(img, 0, 0, width, height);\n return createImageBitmap(canvas);\n}\n\nconst defaultLoader: Loader = async (src, size) => {\n requireDom();\n if (isVectorImageSrc(src)) return rasterizeVector(src, size);\n return createImageBitmap(await decodeImage(src));\n};\n\nlet loader: Loader = defaultLoader;\n\nfunction notify(): void {\n for (const cb of subscribers) cb();\n}\n\nfunction startLoad(src: string): void {\n const entry: Entry = { status: 'loading' };\n cache.set(src, entry);\n loader(src).then(\n (bitmap) => {\n // The entry may have been replaced/cleared mid-flight (reset, etc.);\n // only commit if this src is still the one we started loading.\n if (cache.get(src) !== entry) return;\n cache.set(src, {\n status: 'ready',\n bitmap,\n ...(isVectorImageSrc(src)\n ? { vector: { natural: { width: bitmap.width, height: bitmap.height }, scale: 1, failed: new Set<number>() } }\n : {}),\n });\n notify();\n },\n () => {\n // Cache the error so a broken `src` doesn't reload-storm.\n if (cache.get(src) !== entry) return;\n cache.set(src, { status: 'error' });\n notify();\n },\n );\n}\n\n/** The raster scale `drawnAt` calls for: the next power of two at or above the\n * natural-to-drawn ratio, capped by texture size and the pixel ceiling. */\nfunction rasterScaleFor(natural: ImageRasterSize, drawnAt: ImageRasterSize): number | undefined {\n const need = Math.max(drawnAt.width / natural.width, drawnAt.height / natural.height);\n if (!(need > 0) || !Number.isFinite(need)) return undefined;\n const side = maxImageTextureSide();\n const cap = Math.min(\n side / natural.width,\n side / natural.height,\n Math.sqrt(MAX_VECTOR_RASTER_PIXELS / (natural.width * natural.height)),\n );\n return Math.min(2 ** Math.ceil(Math.log2(need)), cap);\n}\n\nfunction maybeReraster(src: string, entry: Entry, v: VectorRaster, drawnAt: ImageRasterSize): void {\n if (v.pending !== undefined) return;\n const target = rasterScaleFor(v.natural, drawnAt);\n if (target === undefined) return;\n const grow = target > v.scale;\n if (!grow && target * SHRINK_SLACK > v.scale) return;\n for (const f of v.failed) if (target === f || (grow && f > v.scale && target >= f)) return;\n\n v.pending = target;\n const size = {\n width: Math.max(1, Math.round(v.natural.width * target)),\n height: Math.max(1, Math.round(v.natural.height * target)),\n };\n loader(src, size).then(\n (bitmap) => {\n if (cache.get(src) !== entry) return;\n v.pending = undefined;\n const old = entry.bitmap;\n entry.bitmap = bitmap;\n v.scale = target;\n if (old && old !== bitmap) releaseImageSource(old);\n notify();\n },\n () => {\n // The previous raster keeps painting; only this scale is given up on.\n if (cache.get(src) !== entry) return;\n v.pending = undefined;\n v.failed.add(target);\n },\n );\n}\n\n/** Synchronous read. Returns the decoded bitmap when ready; otherwise returns\n * `undefined` and lazily starts a de-duped async load for `src`.\n *\n * `drawnAt` is the size in device pixels the image is about to cover. A\n * vector source ({@link isVectorImageSrc}) uses it to re-rasterize in\n * power-of-two steps — the current raster is returned until the sharper one\n * is ready. Raster sources ignore it. */\nexport function getImageBitmap(src: string, drawnAt?: ImageRasterSize): ImageBitmap | undefined {\n const entry = cache.get(src);\n if (!entry) {\n startLoad(src);\n return undefined;\n }\n if (drawnAt && entry.vector) maybeReraster(src, entry, entry.vector, drawnAt);\n return entry.bitmap;\n}\n\n/** Current load status for `src` (`'idle'` if never requested). */\nexport function imageStatus(src: string): ImageStatus {\n return cache.get(src)?.status ?? 'idle';\n}\n\n/** Subscribe to load-resolution events (fires after any `src` resolves or\n * errors). Returns an unsubscribe. */\nexport function subscribeImageReady(cb: () => void): () => void {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n}\n\n/** @internal test seam — override the async loader so unit tests don't need\n * a real DOM image. Not part of the public barrel. */\nexport function __setImageLoaderForTests(fn: Loader): void {\n loader = fn;\n}\n\n/** @internal test seam — clear cache + subscribers and restore the default\n * loader. Not part of the public barrel. */\nexport function _resetImageCacheForTests(): void {\n cache.clear();\n subscribers.clear();\n decodedVectors.clear();\n loader = defaultLoader;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"imageCache.js","names":["MAX_VECTOR_RASTER_PIXELS","SHRINK_SLACK","FALLBACK_WIDTH","FALLBACK_HEIGHT","cache","subscribers","decodedVectors","isVectorImageSrc","src","requireDom","decodeImage","img","decodeVector","decoded","rasterizeVector","width","size","height","canvas","c2d","loader","notify","cb","startLoad","entry","bitmap","rasterScaleFor","need","drawnAt","natural","side","maxImageTextureSide","cap","maybeReraster","v","wanted","grow","target","f","old","releaseImageSource","getImageBitmap","imageStatus","releaseImage","retryImage","subscribeImageReady"],"sources":["../../../src/features/images/imageCache.ts"],"sourcesContent":["/**\n * imageCache — the kit's per-`src` image loader + cache.\n *\n * The shape-painter chain (`kit:image` in `NodeShape.ts`) is synchronous, but\n * decoding an image is async. This module bridges the two: `getImageBitmap` is\n * a synchronous read that returns the decoded `ImageBitmap` when ready, and\n * otherwise returns `undefined` and lazily kicks off a de-duped async load.\n * When a load resolves (or errors), every `subscribeImageReady` listener fires\n * — `<SceneCanvas>` subscribes and calls `requestRedraw()`, so the painter\n * re-runs and emits the now-ready bitmap.\n *\n * `src` is any browser-loadable image string: a remote URL, a `blob:` URL, or\n * a `data:image/…;base64,…` URI (the embedded-bytes path). The decoded bitmap\n * lives only here, keyed by `src` — never in node `data` — so `scene.toJSON()`\n * round-trips the (serializable) `src` string untouched.\n */\n\nimport { maxImageTextureSide, releaseImageSource } from 'renderer/cache/GLImageCache';\n\n/** Public shape of an image node's `data`. `src` round-trips through\n * serialization; the decoded bitmap is held in this cache, not on the node. */\nexport interface ImageNodeData {\n image: {\n src: string;\n opacity?: number;\n /** The part of the bitmap drawn into the pose rect, as fractions of the\n * bitmap's width and height from its top-left. Omitted draws the whole\n * bitmap. Fractions, so the data means the same thing before the bitmap\n * has decoded and after `src` is swapped for another resolution. */\n source?: { x: number; y: number; width: number; height: number };\n /** Mirror the drawn region within the pose rect. The rect does not move. */\n flipX?: boolean;\n flipY?: boolean;\n };\n}\n\n/** Where an image is in its load: never requested, in flight, decoded, or\n * failed. Renderers paint a placeholder for anything but `'ready'`. */\nexport type ImageStatus = 'idle' | 'loading' | 'ready' | 'error';\n\n/** A size in device pixels. */\nexport interface ImageRasterSize {\n width: number;\n height: number;\n}\n\n/** A vector source's raster bookkeeping. `scale` is the current raster's size\n * over the natural one, always a power of two unless a cap cut it short. */\ninterface VectorRaster {\n natural: ImageRasterSize;\n scale: number;\n pending?: number;\n failed: Set<number>;\n /** The scale the last shrink left, until something asks for more. */\n shrunkTo?: number;\n /** The largest scale a shrunk raster was asked to grow back to. One raster\n * serves every size a source is drawn at, so that request may be a second\n * consumer the shrink starved; shrinking below it again would loop. */\n floor: number;\n}\n\ninterface Entry {\n status: ImageStatus;\n bitmap?: ImageBitmap;\n vector?: VectorRaster;\n}\n\n/** `size` is absent for the first load, which decodes at natural size. */\ntype Loader = (src: string, size?: ImageRasterSize) => Promise<ImageBitmap>;\n\n/** One vector raster may hold at most 64MB of RGBA. */\nconst MAX_VECTOR_RASTER_PIXELS = 4096 * 4096;\n/** A raster is only shrunk once it is this many times larger than needed, so\n * zooming back and forth across a bucket edge does not re-raster each way. */\nconst SHRINK_SLACK = 4;\n// CSS default object size, as `kit:svg` measures a viewBox-only SVG.\nconst FALLBACK_WIDTH = 300;\nconst FALLBACK_HEIGHT = 150;\n\nconst cache = new Map<string, Entry>();\nconst subscribers = new Set<() => void>();\nconst decodedVectors = new Map<string, Promise<HTMLImageElement>>();\n\n/** True when `src` names SVG — a `data:image/svg+xml` URI, or a URL whose path\n * ends in `.svg`/`.svgz`. Such sources re-rasterize at the size they are drawn\n * at; everything else decodes once. A `blob:` URL carries no type and counts\n * as raster. */\nexport function isVectorImageSrc(src: string): boolean {\n if (/^data:image\\/svg\\+xml[;,]/i.test(src)) return true;\n if (/^(data|blob):/i.test(src)) return false;\n return /\\.svgz?$/i.test(src.replace(/[?#].*$/, ''));\n}\n\nfunction requireDom(): void {\n if (typeof Image === 'undefined' || typeof createImageBitmap === 'undefined') {\n throw new Error('weasel imageCache: no DOM image loader in this environment');\n }\n}\n\nasync function decodeImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n await img.decode();\n return img;\n}\n\nfunction decodeVector(src: string): Promise<HTMLImageElement> {\n let decoded = decodedVectors.get(src);\n if (!decoded) {\n decoded = decodeImage(src);\n decodedVectors.set(src, decoded);\n decoded.catch(() => decodedVectors.delete(src));\n }\n return decoded;\n}\n\n// `createImageBitmap` rejects SVG blobs and resamples an SVG `<img>` rather\n// than re-drawing it, so vectors go through a canvas `drawImage` at full size.\nasync function rasterizeVector(src: string, size?: ImageRasterSize): Promise<ImageBitmap> {\n const img = await decodeVector(src);\n const width = size?.width ?? (img.naturalWidth || FALLBACK_WIDTH);\n const height = size?.height ?? (img.naturalHeight || FALLBACK_HEIGHT);\n const canvas: OffscreenCanvas | HTMLCanvasElement = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(width, height)\n : Object.assign(document.createElement('canvas'), { width, height });\n const c2d = canvas.getContext('2d') as CanvasDrawImage | null;\n if (!c2d) throw new Error('weasel imageCache: no 2D context to rasterize an SVG');\n c2d.drawImage(img, 0, 0, width, height);\n return createImageBitmap(canvas);\n}\n\nconst defaultLoader: Loader = async (src, size) => {\n requireDom();\n if (isVectorImageSrc(src)) return rasterizeVector(src, size);\n return createImageBitmap(await decodeImage(src));\n};\n\nlet loader: Loader = defaultLoader;\n\nfunction notify(): void {\n for (const cb of subscribers) cb();\n}\n\nfunction startLoad(src: string): void {\n const entry: Entry = { status: 'loading' };\n cache.set(src, entry);\n loader(src).then(\n (bitmap) => {\n // The entry may have been replaced/cleared mid-flight (reset, etc.);\n // only commit if this src is still the one we started loading.\n if (cache.get(src) !== entry) return;\n cache.set(src, {\n status: 'ready',\n bitmap,\n ...(isVectorImageSrc(src)\n ? { vector: { natural: { width: bitmap.width, height: bitmap.height }, scale: 1, failed: new Set<number>(), floor: 0 } }\n : {}),\n });\n notify();\n },\n () => {\n // Cache the error so a broken `src` doesn't reload-storm.\n if (cache.get(src) !== entry) return;\n cache.set(src, { status: 'error' });\n notify();\n },\n );\n}\n\n/** The raster scale `drawnAt` calls for: the next power of two at or above the\n * natural-to-drawn ratio, capped by texture size and the pixel ceiling. */\nfunction rasterScaleFor(natural: ImageRasterSize, drawnAt: ImageRasterSize): number | undefined {\n const need = Math.max(drawnAt.width / natural.width, drawnAt.height / natural.height);\n if (!(need > 0) || !Number.isFinite(need)) return undefined;\n const side = maxImageTextureSide();\n const cap = Math.min(\n side / natural.width,\n side / natural.height,\n Math.sqrt(MAX_VECTOR_RASTER_PIXELS / (natural.width * natural.height)),\n );\n return Math.min(2 ** Math.ceil(Math.log2(need)), cap);\n}\n\nfunction maybeReraster(src: string, entry: Entry, v: VectorRaster, drawnAt: ImageRasterSize): void {\n if (v.pending !== undefined) return;\n const wanted = rasterScaleFor(v.natural, drawnAt);\n if (wanted === undefined) return;\n const grow = wanted > v.scale;\n if (grow && v.shrunkTo === v.scale) v.floor = Math.max(v.floor, wanted);\n const target = grow ? wanted : Math.max(wanted, v.floor);\n if (!grow && target * SHRINK_SLACK > v.scale) return;\n for (const f of v.failed) if (target === f || (grow && f > v.scale && target >= f)) return;\n\n v.pending = target;\n const size = {\n width: Math.max(1, Math.round(v.natural.width * target)),\n height: Math.max(1, Math.round(v.natural.height * target)),\n };\n loader(src, size).then(\n (bitmap) => {\n if (cache.get(src) !== entry) return;\n v.pending = undefined;\n const old = entry.bitmap;\n entry.bitmap = bitmap;\n v.shrunkTo = grow ? undefined : target;\n v.scale = target;\n if (old && old !== bitmap) releaseImageSource(old);\n notify();\n },\n () => {\n // The previous raster keeps painting; only this scale is given up on.\n if (cache.get(src) !== entry) return;\n v.pending = undefined;\n v.failed.add(target);\n },\n );\n}\n\n/** Synchronous read. Returns the decoded bitmap when ready; otherwise returns\n * `undefined` and lazily starts a de-duped async load for `src`.\n *\n * `drawnAt` is the size in device pixels the image is about to cover. A\n * vector source ({@link isVectorImageSrc}) uses it to re-rasterize in\n * power-of-two steps — the current raster is returned until the sharper one\n * is ready. Raster sources ignore it. */\nexport function getImageBitmap(src: string, drawnAt?: ImageRasterSize): ImageBitmap | undefined {\n const entry = cache.get(src);\n if (!entry) {\n startLoad(src);\n return undefined;\n }\n if (drawnAt && entry.vector) maybeReraster(src, entry, entry.vector, drawnAt);\n return entry.bitmap;\n}\n\n/** Current load status for `src` (`'idle'` if never requested). */\nexport function imageStatus(src: string): ImageStatus {\n return cache.get(src)?.status ?? 'idle';\n}\n\n/**\n * Forget `src`: drop its decoded bitmap, its decoded SVG, and the texture every\n * live renderer holds for it, and abandon a load in flight. The cache is keyed\n * by `src` across every scene on the page, so it cannot know when the last use\n * is gone; whoever does know calls this. A later {@link getImageBitmap} for the\n * same `src` loads it again.\n */\nexport function releaseImage(src: string): void {\n const entry = cache.get(src);\n cache.delete(src);\n decodedVectors.delete(src);\n if (entry?.bitmap) releaseImageSource(entry.bitmap);\n}\n\n/** Load a `src` whose last load failed again. Does nothing for a `src` in any\n * other state; one that failed otherwise stays `'error'` for the page's life. */\nexport function retryImage(src: string): void {\n if (cache.get(src)?.status !== 'error') return;\n startLoad(src);\n notify();\n}\n\n/** Subscribe to load-resolution events (fires after any `src` resolves or\n * errors). Returns an unsubscribe. */\nexport function subscribeImageReady(cb: () => void): () => void {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n}\n\n/** @internal test seam — override the async loader so unit tests don't need\n * a real DOM image. Not part of the public barrel. */\nexport function __setImageLoaderForTests(fn: Loader): void {\n loader = fn;\n}\n\n/** @internal test seam — clear cache + subscribers and restore the default\n * loader. Not part of the public barrel. */\nexport function _resetImageCacheForTests(): void {\n cache.clear();\n subscribers.clear();\n decodedVectors.clear();\n loader = defaultLoader;\n}\n"],"mappings":"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"}
|
|
@@ -8,10 +8,13 @@ function r(e) {
|
|
|
8
8
|
t >= 0 && n.splice(t, 1);
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
function i() {
|
|
12
|
-
return [...
|
|
11
|
+
function i(e) {
|
|
12
|
+
return [...e].sort((e, t) => (t.priority ?? 0) - (e.priority ?? 0));
|
|
13
13
|
}
|
|
14
|
-
function a(
|
|
14
|
+
function a() {
|
|
15
|
+
return i(n);
|
|
16
|
+
}
|
|
17
|
+
function o(e, n) {
|
|
15
18
|
let { match: r } = e;
|
|
16
19
|
if (typeof r == "function") try {
|
|
17
20
|
return r(n);
|
|
@@ -20,18 +23,18 @@ function a(e, n) {
|
|
|
20
23
|
}
|
|
21
24
|
return (Array.isArray(r) ? r : [r]).some((e) => t(n.mime, e));
|
|
22
25
|
}
|
|
23
|
-
async function
|
|
24
|
-
let
|
|
25
|
-
for (let e of
|
|
26
|
-
if (
|
|
27
|
-
let t =
|
|
28
|
-
t.length !== 0 && (
|
|
26
|
+
async function s(t, n, r = {}) {
|
|
27
|
+
let s = t, c = [], l = r.handlers ? i(r.handlers) : a();
|
|
28
|
+
for (let e of l) {
|
|
29
|
+
if (s.length === 0) break;
|
|
30
|
+
let t = s.filter((t) => o(e, t));
|
|
31
|
+
t.length !== 0 && (s = s.filter((e) => !t.includes(e)), c.push(Promise.resolve().then(() => e.handle(t, n)).catch((t) => {
|
|
29
32
|
console.warn(`weasel ingest: handler "${e.id}" threw`, t);
|
|
30
33
|
})));
|
|
31
34
|
}
|
|
32
|
-
|
|
35
|
+
s.length > 0 && e("ingest", `no content handler matched ${s.length} item(s):`, s.map((e) => e.mime).join(", ")), await Promise.all(c);
|
|
33
36
|
}
|
|
34
37
|
//#endregion
|
|
35
|
-
export {
|
|
38
|
+
export { a as getContentHandlers, r as registerContentHandler, s as runIngest };
|
|
36
39
|
|
|
37
40
|
//# sourceMappingURL=contentHandlers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentHandlers.js","names":["HANDLERS","registerContentHandler","entry","i","
|
|
1
|
+
{"version":3,"file":"contentHandlers.js","names":["HANDLERS","registerContentHandler","entry","i","byPriority","handlers","b","a","getContentHandlers","entryMatches","match","item","err","mimeMatchesGlob","g","runIngest","remaining","items","runs","opts","mine","it","ctx","dwarn"],"sources":["../../../src/features/ingestion/contentHandlers.ts"],"sourcesContent":["/**\n * Content-handler registry — the \"registry out\" half of external-content\n * ingestion (spec: docs/superpowers/specs/2026-07-03-content-ingestion-design.md).\n *\n * All three arrival paths (OS drop, clipboard paste, file picker /\n * `CanvasExtensionApi.ingest`) converge here: the `ingest` action calls\n * `runIngest(items, ctx)`, which partitions the items across registered\n * handlers in priority order — the first matching handler takes every item\n * it matches; leftovers continue down the list. Same per-trait registry\n * idiom as `NodeShape` / `NodeRouting`. The `clipboard.pasteEvent` action\n * calls it too, with `RunIngestOptions.handlers` naming the one handler it\n * accepts.\n *\n * MIME matching delegates to `mimeMatchesGlob` from `@weasel-js/gestures` —\n * the SAME matcher the binding-level `types` filter uses, so a binding that\n * matched can never disagree with the registry about what a glob means.\n */\nimport { mimeMatchesGlob } from '@weasel-js/gestures';\nimport { dwarn } from '../../debug';\nimport type { Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport type { ClipboardIngestCtx, InsertDep, SvgIngestOptions } from 'interactions/actions/depSchema';\nimport type { ActionDeps } from '@weasel-js/routing';\nimport type { IngestItem } from '@weasel-js/routing';\n\n/** Context handed to every content handler for one ingest event. */\nexport interface IngestCtx {\n /** World-space arrival point (drop / pointed imperative ingest); `null`\n * for paste and point-less calls — handlers pick their own policy\n * (the kit image handler centers on the viewport). */\n point: { x: number; y: number } | null;\n /** Visible canvas area in world coordinates. */\n viewportWorldRect(): { x: number; y: number; width: number; height: number };\n /** The kit insert dep — id/layer/undoable-op supplied; the canonical way\n * for a handler to mint a node (`insert.commit(bounds, { kind, ... })`). */\n insert: InsertDep;\n /** Raw op commit for handlers that build their own ops. */\n applyOps(ops: Op[], label?: string): void;\n scene: Scene<unknown, string, unknown>;\n selection: SelectionApi;\n /** Consumer file→src resolver (SceneCanvas `ingestion.resolveSrc`).\n * When absent, the kit image handler embeds as a `data:` URI. */\n resolveSrc?: (file: File) => Promise<string>;\n /** Kit SVG-handler options (SceneCanvas `ingestion.svg`) — e.g.\n * `{ unpack: unpackSvgFiles }` (from `@weasel-js/svg`) to parse SVG files\n * into scene nodes. */\n svg?: SvgIngestOptions;\n /** Clipboard-paste seam — present when the hosting `SceneCanvas` supplied\n * an adapter with `commitPaste`. `reviver` comes from\n * `SceneCanvasProps.ingestion.clipboard`. Absent ⇒ the kit weasel-JSON\n * handler declines inert (dwarn, nothing ingested) — its matched items\n * were already consumed at match time, so they do NOT fall through;\n * only match-level misses flow on to other handlers. */\n clipboard?: ClipboardIngestCtx;\n /** Set to `true` by the kit weasel-JSON handler when it successfully\n * pastes a payload in this event. The `ctx` object is shared across all\n * handlers in one `runIngest` call, and higher-priority handlers' `handle`\n * bodies run (synchronously) before lower ones — so `kit:svg`'s\n * `text/plain` SVG fallback reads this to decline the SVG flavor of a copy\n * whose canonical weasel-JSON flavor already ingested (avoids a\n * double-paste when both flavors ride one clipboard event). */\n consumedWeaselPayload?: boolean;\n /** Full action-deps bag, for consumer handlers that need more. */\n deps: ActionDeps;\n}\n\n/** A handler for content arriving by paste, drop or file picker. Handlers are\n * matched by MIME glob or predicate and run highest-priority first; the kit's\n * own register at a low priority so a consumer's handler wins by default. */\nexport interface ContentHandlerEntry {\n /** Stable identifier — used for unregistration and debugging\n * (`'kit:image'`, `'app:csv'`). */\n id: string;\n /** MIME glob(s) (`'image/*'`, `'text/csv'`) or an item predicate. */\n match: string | string[] | ((item: IngestItem) => boolean);\n /** Higher runs earlier. Kit defaults register at -100 so any consumer\n * handler (default 0) beats them. */\n priority?: number;\n handle(items: IngestItem[], ctx: IngestCtx): void | Promise<void>;\n}\n\nconst HANDLERS: ContentHandlerEntry[] = [];\n\n/** Register a content handler. Returns a disposer that removes it. */\nexport function registerContentHandler(entry: ContentHandlerEntry): () => void {\n HANDLERS.push(entry);\n return () => {\n const i = HANDLERS.indexOf(entry);\n if (i >= 0) HANDLERS.splice(i, 1);\n };\n}\n\nfunction byPriority(handlers: readonly ContentHandlerEntry[]): ContentHandlerEntry[] {\n return [...handlers].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n}\n\n/** Snapshot of registered handlers, priority-ordered (stable within ties —\n * registration order; Array.prototype.sort is stable per ES2019). */\nexport function getContentHandlers(): readonly ContentHandlerEntry[] {\n return byPriority(HANDLERS);\n}\n\n/** Options for {@link runIngest}. */\nexport interface RunIngestOptions {\n /**\n * The handlers to route through, in place of the registry. The registry is\n * shared by every canvas on the page, so this is how one arrival path\n * accepts a narrower set of content than the page has handlers for: the\n * `clipboard.pasteEvent` action passes the kit's node-payload handler alone.\n * Ordered by `priority` like registered handlers; an item none of them\n * matches is ignored.\n */\n handlers?: readonly ContentHandlerEntry[];\n}\n\nfunction entryMatches(entry: ContentHandlerEntry, item: IngestItem): boolean {\n const { match } = entry;\n if (typeof match === 'function') {\n try {\n return match(item);\n } catch (err) {\n console.warn(`weasel ingest: handler \"${entry.id}\" match predicate threw`, err);\n return false;\n }\n }\n const globs = Array.isArray(match) ? match : [match];\n return globs.some((g) => mimeMatchesGlob(item.mime, g));\n}\n\n/**\n * Route one ingest event's items through the registry. Handlers are queued\n * as microtasks in priority order and their async work then overlaps (each\n * already owns a disjoint item set); a throwing or rejecting handler\n * `console.warn`s and doesn't block the others. The queue order is a real\n * invariant: a higher-priority handler's `handle` body runs before lower\n * ones start, so ctx-flag coordination (e.g. `consumedWeaselPayload`)\n * works — but only while the writing handler stays synchronous up to the\n * write and the reading handler reads before its first `await`.\n * Unmatched items are ignored with a debug-gated `dwarn('ingest', ...)`.\n */\nexport async function runIngest(\n items: IngestItem[],\n ctx: IngestCtx,\n opts: RunIngestOptions = {},\n): Promise<void> {\n let remaining = items;\n const runs: Promise<void>[] = [];\n const handlers = opts.handlers ? byPriority(opts.handlers) : getContentHandlers();\n\n for (const entry of handlers) {\n if (remaining.length === 0) break;\n const mine = remaining.filter((it) => entryMatches(entry, it));\n if (mine.length === 0) continue;\n remaining = remaining.filter((it) => !mine.includes(it));\n runs.push(\n Promise.resolve()\n .then(() => entry.handle(mine, ctx))\n .catch((err) => {\n console.warn(`weasel ingest: handler \"${entry.id}\" threw`, err);\n }),\n );\n }\n\n if (remaining.length > 0) {\n dwarn(\n 'ingest',\n `no content handler matched ${remaining.length} item(s):`,\n remaining.map((it) => it.mime).join(', '),\n );\n }\n\n await Promise.all(runs);\n}\n\n/** @internal test seam — clear the registry. Not part of the public barrel. */\nexport function _resetContentHandlersForTests(): void {\n HANDLERS.length = 0;\n}\n"],"mappings":";;;AAkFA,IAAMA,IAAkC,CAAC;AAGzC,SAAgBC,EAAuB,GAAwC;CAE7E,OADAD,EAAS,KAAKE,CAAK,SACN;EACX,IAAMC,IAAIH,EAAS,QAAQE,CAAK;EAChC,AAAIC,KAAK,KAAGH,EAAS,OAAOG,GAAG,CAAC;CAClC;AACF;AAEA,SAASC,EAAW,GAAiE;CACnF,OAAO,CAAC,GAAGC,CAAQ,CAAC,CAAC,MAAM,GAAG,OAAOC,EAAE,YAAY,MAAMC,EAAE,YAAY,EAAE;AAC3E;AAIA,SAAgBC,IAAqD;CACnE,OAAOJ,EAAWJ,CAAQ;AAC5B;AAeA,SAASS,EAAa,GAA4B,GAA2B;CAC3E,IAAM,EAAE,aAAUP;CAClB,IAAI,OAAOQ,KAAU,YACnB,IAAI;EACF,OAAOA,EAAMC,CAAI;CACnB,SAASC,GAAK;EAEZ,OADA,QAAQ,KAAK,2BAA2BV,EAAM,GAAG,0BAA0BU,CAAG,GACvE;CACT;CAGF,QADc,MAAM,QAAQF,CAAK,IAAIA,IAAQ,CAACA,CAAK,EAAA,CACtC,MAAM,MAAMG,EAAgBF,EAAK,MAAMG,CAAC,CAAC;AACxD;AAaA,eAAsBC,EACpB,GACA,GACA,IAAyB,CAAC,GACX;CACf,IAAIC,IAAYC,GACVC,IAAwB,CAAC,GACzBb,IAAWc,EAAK,WAAWf,EAAWe,EAAK,QAAQ,IAAIX,EAAmB;CAEhF,KAAK,IAAMN,KAASG,GAAU;EAC5B,IAAIW,EAAU,WAAW,GAAG;EAC5B,IAAMI,IAAOJ,EAAU,QAAQ,MAAOP,EAAaP,GAAOmB,CAAE,CAAC;EAC7D,AAAID,EAAK,WAAW,MACpB,IAAYJ,EAAU,QAAQ,MAAO,CAACI,EAAK,SAASC,CAAE,CAAC,GACvDH,EAAK,KACH,QAAQ,QAAQ,CAAC,CACd,WAAWhB,EAAM,OAAOkB,GAAME,CAAG,CAAC,CAAC,CACnC,OAAO,MAAQ;GACd,QAAQ,KAAK,2BAA2BpB,EAAM,GAAG,UAAUU,CAAG;EAChE,CAAC,CACL;CACF;CAUA,AARII,EAAU,SAAS,KACrBO,EACE,UACA,8BAA8BP,EAAU,OAAO,YAC/CA,EAAU,KAAK,MAAOK,EAAG,IAAI,CAAC,CAAC,KAAK,IAAI,CAC1C,GAGF,MAAM,QAAQ,IAAIH,CAAI;AACxB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { registerContentHandler as e } from "./contentHandlers.js";
|
|
2
1
|
import "./imageHandler.js";
|
|
3
2
|
import "./svgHandler.js";
|
|
4
3
|
import "./weaselJsonHandler.js";
|
|
4
|
+
import { registerContentHandler as e } from "./contentHandlers.js";
|
|
5
5
|
import { acquireKitContentHandlers as t } from "./registerKitHandlers.js";
|
|
6
6
|
import { openFilePicker as n } from "./openFilePicker.js";
|
|
7
7
|
import { itemsFromFiles as r } from "@weasel-js/routing";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { kitImageHandler as e } from "./imageHandler.js";
|
|
2
|
+
import { kitSvgHandler as t } from "./svgHandler.js";
|
|
3
|
+
import { kitWeaselJsonHandler as n } from "./weaselJsonHandler.js";
|
|
4
|
+
import { registerContentHandler as r } from "./contentHandlers.js";
|
|
5
5
|
//#region src/features/ingestion/registerKitHandlers.ts
|
|
6
6
|
var i = 0, a = null;
|
|
7
7
|
function o() {
|
|
8
8
|
if (i++, i === 1) {
|
|
9
9
|
let i = [
|
|
10
|
+
e,
|
|
10
11
|
t,
|
|
11
|
-
n
|
|
12
|
-
|
|
13
|
-
].map(e);
|
|
12
|
+
n
|
|
13
|
+
].map(r);
|
|
14
14
|
a = () => {
|
|
15
15
|
for (let e of i) e();
|
|
16
16
|
};
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
import { dwarn as e } from "../../debug/flag.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { materializePaste as t } from "../../interactions/actions/clipboard/materializePaste.js";
|
|
3
|
+
import { WEASEL_CLIPBOARD_MIME as n, WEASEL_CLIPBOARD_MIME_WEB as r, extractWeaselClipboardFromSvg as i, parseWeaselClipboardText as a, sniffWeaselClipboardText as o } from "../../interactions/actions/clipboard/wireFormat.js";
|
|
4
|
+
import { sniffSvgText as s } from "./svgHandler.js";
|
|
5
5
|
//#region src/features/ingestion/weaselJsonHandler.ts
|
|
6
|
-
var c = (e) => e.includes("\"weaselClipboard\"") &&
|
|
6
|
+
var c = (e) => e.includes("\"weaselClipboard\"") && s(e) && i(e) !== null, l = {
|
|
7
7
|
id: "kit:weasel-json",
|
|
8
8
|
priority: -50,
|
|
9
|
-
match: (e) => e.kind === "string" && (e.mime ===
|
|
10
|
-
handle(
|
|
11
|
-
let
|
|
12
|
-
if (!
|
|
9
|
+
match: (e) => e.kind === "string" && (e.mime === n || e.mime === r || e.mime === "text/plain" && (o(e.text) || c(e.text))),
|
|
10
|
+
handle(n, r) {
|
|
11
|
+
let o = r.clipboard;
|
|
12
|
+
if (!o) {
|
|
13
13
|
e("clipboard", "weasel payload arrived but ctx.clipboard is not wired (ingestion.clipboard disabled or absent) — declining");
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
if (!
|
|
16
|
+
if (!o.adapter.commitPaste) {
|
|
17
17
|
e("clipboard", "weasel payload arrived but the clipboard adapter lacks commitPaste — declining");
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
-
let c =
|
|
20
|
+
let c = n.find((e) => e.kind === "string");
|
|
21
21
|
if (!c || c.kind !== "string") return;
|
|
22
|
-
let l =
|
|
22
|
+
let l = s(c.text) ? i(c.text) : c.text;
|
|
23
23
|
if (l === null) {
|
|
24
24
|
e("clipboard", "weasel payload vanished from the SVG metadata between match and handle — declining");
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
let u =
|
|
27
|
+
let u = a(l, o.reviver);
|
|
28
28
|
if (u === null) {
|
|
29
29
|
e("clipboard", "weasel payload unusable — declining (see prior warning if the parse threw)");
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
let d = { items: u }, f =
|
|
33
|
-
|
|
32
|
+
let d = { items: u }, f = t(o.adapter, d, {
|
|
33
|
+
currentSelection: r.selection.get(),
|
|
34
|
+
onOmitted: o.onOmitted
|
|
35
|
+
});
|
|
36
|
+
f && (r.applyOps(f.ops, "Paste"), r.selection.set(f.newIds), r.consumedWeaselPayload = !0);
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weaselJsonHandler.js","names":["isMetadataBearingSvg","text","sniffSvgText","extractWeaselClipboardFromSvg","kitWeaselJsonHandler","item","WEASEL_CLIPBOARD_MIME","WEASEL_CLIPBOARD_MIME_WEB","sniffWeaselClipboardText","clipboard","ctx","items","it","nodes","parseWeaselClipboardText","snapshot","result","materializePaste"],"sources":["../../../src/features/ingestion/weaselJsonHandler.ts"],"sourcesContent":["/**\n * The kit's weasel-JSON clipboard content handler.\n *\n * Consumes `application/x-weasel-clipboard+json` payloads (either MIME\n * spelling — Chromium's async-clipboard API round-trips custom formats with\n * a `web ` prefix — plus `text/plain` that sniffs as the wire format, plus\n * `text/plain` SVG carrying the wire format embedded in a `<metadata>`\n * element: Chromium's DOM paste event exposes ONLY `text/plain`, and draw\n * puts metadata-bearing SVG there so draw→draw paste keeps full JSON\n * fidelity) and replays them as a paste through the hosting canvas's\n * adapter: parse →\n * `commitPaste` (fresh ids, cascade offset) → one undoable batch of\n * `InsertOp`s + a selection op. Mirrors `useClipboardOps.paste`'s op\n * construction exactly so OS-arriving pastes and in-memory pastes are\n * indistinguishable in history.\n *\n * Declines (leaving the event un-acted-on) when `ctx.clipboard` is absent\n * (consumer disabled it, or the adapter lacks `commitPaste`) or the payload\n * fails to parse. Note `runIngest` consumes items at *match* time, so\n * \"leave it for other handlers\" is the sniff's job: non-weasel `text/plain`\n * never matches and flows on to lower-priority handlers untouched.\n */\nimport type { NodeId } from 'core/scene/types';\nimport type { ClipboardSnapshot } from 'core/adapters/types';\nimport { materializePaste } from 'interactions/actions/clipboard/materializePaste';\nimport {\n WEASEL_CLIPBOARD_MIME,\n WEASEL_CLIPBOARD_MIME_WEB,\n sniffWeaselClipboardText,\n parseWeaselClipboardText,\n extractWeaselClipboardFromSvg,\n} from 'interactions/actions/clipboard/wireFormat';\nimport { dwarn } from '../../debug';\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\n// Direct module import (not the ./index barrel) to avoid a cycle — the\n// barrel re-exports this handler.\nimport { sniffSvgText } from './svgHandler';\n\n/** text/plain SVG whose `<metadata>` carries a verified weasel payload —\n * the draw→draw DOM-paste flavor (Chromium exposes only text/plain there).\n * The `\"weaselClipboard\"` substring guard keeps match cheap on arbitrary\n * external SVG pastes before the extraction regex runs. */\nconst isMetadataBearingSvg = (text: string): boolean =>\n text.includes('\"weaselClipboard\"')\n && sniffSvgText(text)\n && extractWeaselClipboardFromSvg(text) !== null;\n\nexport const kitWeaselJsonHandler: ContentHandlerEntry = {\n id: 'kit:weasel-json',\n // Above the other kit handlers (-100/-90) so weasel payloads never fall\n // through to the SVG/text paths; still below consumer handlers (default 0).\n priority: -50,\n match: (item) =>\n item.kind === 'string'\n && (item.mime === WEASEL_CLIPBOARD_MIME\n || item.mime === WEASEL_CLIPBOARD_MIME_WEB\n || (item.mime === 'text/plain'\n && (sniffWeaselClipboardText(item.text) || isMetadataBearingSvg(item.text)))),\n handle(items, ctx: IngestCtx) {\n const clipboard = ctx.clipboard;\n if (!clipboard) {\n dwarn('clipboard', 'weasel payload arrived but ctx.clipboard is not wired (ingestion.clipboard disabled or absent) — declining');\n return;\n }\n if (!clipboard.adapter.commitPaste) {\n dwarn('clipboard', 'weasel payload arrived but the clipboard adapter lacks commitPaste — declining');\n return;\n }\n const item = items.find((it) => it.kind === 'string');\n if (!item || item.kind !== 'string') return;\n // The metadata-bearing-SVG flavor: unwrap the embedded payload before\n // parsing. Extraction was already verified at match time, so a null here\n // is the same inert-decline class as a corrupt custom-MIME payload.\n const text = sniffSvgText(item.text)\n ? extractWeaselClipboardFromSvg(item.text)\n : item.text;\n if (text === null) {\n dwarn('clipboard', 'weasel payload vanished from the SVG metadata between match and handle — declining');\n return;\n }\n const nodes = parseWeaselClipboardText(text, clipboard.reviver);\n if (nodes === null) {\n dwarn('clipboard', 'weasel payload unusable — declining (see prior warning if the parse threw)');\n return;\n }\n // Shared paste recipe (offset → commitPaste → InsertOps + selection op)\n // so OS-arriving pastes match useClipboardOps.paste exactly. `ctx.point`\n // is null for paste, so no dropPoint is threaded — the cascade offset is\n // the placement.\n const snapshot: ClipboardSnapshot = { items: nodes };\n const result = materializePaste(clipboard.adapter, snapshot, {\n currentSelection: ctx.selection.get(),\n });\n if (!result) return;\n ctx.applyOps(result.ops, 'Paste');\n ctx.selection.set(result.newIds as NodeId[]);\n // Flag the shared event ctx so kit:svg's text/plain fallback declines the\n // SVG flavor of this same copy (draw writes weasel-JSON + SVG together).\n // WARNING: do not add an `await` earlier in this handle — the flag must\n // be written before lower-priority handlers' microtasks run.\n ctx.consumedWeaselPayload = true;\n },\n};\n"],"mappings":";;;;;AA0CA,IAAMA,KAAwB,MAC5BC,EAAK,SAAS,qBAAmB,KAC9BC,EAAaD,CAAI,KACjBE,EAA8BF,CAAI,MAAM,MAEhCG,IAA4C;CACvD,IAAI;CAGJ,UAAU;CACV,QAAQ,MACNC,EAAK,SAAS,aACVA,EAAK,SAASC,KACbD,EAAK,SAASE,KACbF,EAAK,SAAS,iBACZG,EAAyBH,EAAK,IAAI,KAAKL,EAAqBK,EAAK,IAAI;CAC/E,OAAO,GAAO,GAAgB;EAC5B,IAAMI,IAAYC,EAAI;EACtB,IAAI,CAACD,GAAW;GACd,EAAM,aAAa,4GAA4G;GAC/H;EACF;EACA,IAAI,CAACA,EAAU,QAAQ,aAAa;GAClC,EAAM,aAAa,gFAAgF;GACnG;EACF;EACA,IAAMJ,IAAOM,EAAM,MAAM,MAAOC,EAAG,SAAS,QAAQ;EACpD,IAAI,CAACP,KAAQA,EAAK,SAAS,UAAU;EAIrC,IAAMJ,IAAOC,EAAaG,EAAK,IAAI,IAC/BF,EAA8BE,EAAK,IAAI,IACvCA,EAAK;EACT,IAAIJ,MAAS,MAAM;GACjB,EAAM,aAAa,oFAAoF;GACvG;EACF;EACA,IAAMY,IAAQC,EAAyBb,GAAMQ,EAAU,OAAO;EAC9D,IAAII,MAAU,MAAM;GAClB,EAAM,aAAa,4EAA4E;GAC/F;EACF;EAKA,IAAME,IAA8B,EAAE,OAAOF,EAAM,GAC7CG,IAASC,EAAiBR,EAAU,SAASM,GAAU,
|
|
1
|
+
{"version":3,"file":"weaselJsonHandler.js","names":["isMetadataBearingSvg","text","sniffSvgText","extractWeaselClipboardFromSvg","kitWeaselJsonHandler","item","WEASEL_CLIPBOARD_MIME","WEASEL_CLIPBOARD_MIME_WEB","sniffWeaselClipboardText","clipboard","ctx","items","it","nodes","parseWeaselClipboardText","snapshot","result","materializePaste"],"sources":["../../../src/features/ingestion/weaselJsonHandler.ts"],"sourcesContent":["/**\n * The kit's weasel-JSON clipboard content handler.\n *\n * Consumes `application/x-weasel-clipboard+json` payloads (either MIME\n * spelling — Chromium's async-clipboard API round-trips custom formats with\n * a `web ` prefix — plus `text/plain` that sniffs as the wire format, plus\n * `text/plain` SVG carrying the wire format embedded in a `<metadata>`\n * element: Chromium's DOM paste event exposes ONLY `text/plain`, and draw\n * puts metadata-bearing SVG there so draw→draw paste keeps full JSON\n * fidelity) and replays them as a paste through the hosting canvas's\n * adapter: parse →\n * `commitPaste` (fresh ids, cascade offset) → one undoable batch of\n * `InsertOp`s + a selection op. Mirrors `useClipboardOps.paste`'s op\n * construction exactly so OS-arriving pastes and in-memory pastes are\n * indistinguishable in history.\n *\n * Declines (leaving the event un-acted-on) when `ctx.clipboard` is absent\n * (consumer disabled it, or the adapter lacks `commitPaste`) or the payload\n * fails to parse. Note `runIngest` consumes items at *match* time, so\n * \"leave it for other handlers\" is the sniff's job: non-weasel `text/plain`\n * never matches and flows on to lower-priority handlers untouched.\n */\nimport type { NodeId } from 'core/scene/types';\nimport type { ClipboardSnapshot } from 'core/adapters/types';\nimport { materializePaste } from 'interactions/actions/clipboard/materializePaste';\nimport {\n WEASEL_CLIPBOARD_MIME,\n WEASEL_CLIPBOARD_MIME_WEB,\n sniffWeaselClipboardText,\n parseWeaselClipboardText,\n extractWeaselClipboardFromSvg,\n} from 'interactions/actions/clipboard/wireFormat';\nimport { dwarn } from '../../debug';\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\n// Direct module import (not the ./index barrel) to avoid a cycle — the\n// barrel re-exports this handler.\nimport { sniffSvgText } from './svgHandler';\n\n/** text/plain SVG whose `<metadata>` carries a verified weasel payload —\n * the draw→draw DOM-paste flavor (Chromium exposes only text/plain there).\n * The `\"weaselClipboard\"` substring guard keeps match cheap on arbitrary\n * external SVG pastes before the extraction regex runs. */\nconst isMetadataBearingSvg = (text: string): boolean =>\n text.includes('\"weaselClipboard\"')\n && sniffSvgText(text)\n && extractWeaselClipboardFromSvg(text) !== null;\n\nexport const kitWeaselJsonHandler: ContentHandlerEntry = {\n id: 'kit:weasel-json',\n // Above the other kit handlers (-100/-90) so weasel payloads never fall\n // through to the SVG/text paths; still below consumer handlers (default 0).\n priority: -50,\n match: (item) =>\n item.kind === 'string'\n && (item.mime === WEASEL_CLIPBOARD_MIME\n || item.mime === WEASEL_CLIPBOARD_MIME_WEB\n || (item.mime === 'text/plain'\n && (sniffWeaselClipboardText(item.text) || isMetadataBearingSvg(item.text)))),\n handle(items, ctx: IngestCtx) {\n const clipboard = ctx.clipboard;\n if (!clipboard) {\n dwarn('clipboard', 'weasel payload arrived but ctx.clipboard is not wired (ingestion.clipboard disabled or absent) — declining');\n return;\n }\n if (!clipboard.adapter.commitPaste) {\n dwarn('clipboard', 'weasel payload arrived but the clipboard adapter lacks commitPaste — declining');\n return;\n }\n const item = items.find((it) => it.kind === 'string');\n if (!item || item.kind !== 'string') return;\n // The metadata-bearing-SVG flavor: unwrap the embedded payload before\n // parsing. Extraction was already verified at match time, so a null here\n // is the same inert-decline class as a corrupt custom-MIME payload.\n const text = sniffSvgText(item.text)\n ? extractWeaselClipboardFromSvg(item.text)\n : item.text;\n if (text === null) {\n dwarn('clipboard', 'weasel payload vanished from the SVG metadata between match and handle — declining');\n return;\n }\n const nodes = parseWeaselClipboardText(text, clipboard.reviver);\n if (nodes === null) {\n dwarn('clipboard', 'weasel payload unusable — declining (see prior warning if the parse threw)');\n return;\n }\n // Shared paste recipe (offset → commitPaste → InsertOps + selection op)\n // so OS-arriving pastes match useClipboardOps.paste exactly. `ctx.point`\n // is null for paste, so no dropPoint is threaded — the cascade offset is\n // the placement.\n const snapshot: ClipboardSnapshot = { items: nodes };\n const result = materializePaste(clipboard.adapter, snapshot, {\n currentSelection: ctx.selection.get(),\n onOmitted: clipboard.onOmitted,\n });\n if (!result) return;\n ctx.applyOps(result.ops, 'Paste');\n ctx.selection.set(result.newIds as NodeId[]);\n // Flag the shared event ctx so kit:svg's text/plain fallback declines the\n // SVG flavor of this same copy (draw writes weasel-JSON + SVG together).\n // WARNING: do not add an `await` earlier in this handle — the flag must\n // be written before lower-priority handlers' microtasks run.\n ctx.consumedWeaselPayload = true;\n },\n};\n"],"mappings":";;;;;AA0CA,IAAMA,KAAwB,MAC5BC,EAAK,SAAS,qBAAmB,KAC9BC,EAAaD,CAAI,KACjBE,EAA8BF,CAAI,MAAM,MAEhCG,IAA4C;CACvD,IAAI;CAGJ,UAAU;CACV,QAAQ,MACNC,EAAK,SAAS,aACVA,EAAK,SAASC,KACbD,EAAK,SAASE,KACbF,EAAK,SAAS,iBACZG,EAAyBH,EAAK,IAAI,KAAKL,EAAqBK,EAAK,IAAI;CAC/E,OAAO,GAAO,GAAgB;EAC5B,IAAMI,IAAYC,EAAI;EACtB,IAAI,CAACD,GAAW;GACd,EAAM,aAAa,4GAA4G;GAC/H;EACF;EACA,IAAI,CAACA,EAAU,QAAQ,aAAa;GAClC,EAAM,aAAa,gFAAgF;GACnG;EACF;EACA,IAAMJ,IAAOM,EAAM,MAAM,MAAOC,EAAG,SAAS,QAAQ;EACpD,IAAI,CAACP,KAAQA,EAAK,SAAS,UAAU;EAIrC,IAAMJ,IAAOC,EAAaG,EAAK,IAAI,IAC/BF,EAA8BE,EAAK,IAAI,IACvCA,EAAK;EACT,IAAIJ,MAAS,MAAM;GACjB,EAAM,aAAa,oFAAoF;GACvG;EACF;EACA,IAAMY,IAAQC,EAAyBb,GAAMQ,EAAU,OAAO;EAC9D,IAAII,MAAU,MAAM;GAClB,EAAM,aAAa,4EAA4E;GAC/F;EACF;EAKA,IAAME,IAA8B,EAAE,OAAOF,EAAM,GAC7CG,IAASC,EAAiBR,EAAU,SAASM,GAAU;GAC3D,kBAAkBL,EAAI,UAAU,IAAI;GACpC,WAAWD,EAAU;EACvB,CAAC;EACD,AAAKO,MACLN,EAAI,SAASM,EAAO,KAAK,OAAO,GAChCN,EAAI,UAAU,IAAIM,EAAO,MAAkB,GAK3C,EAAI,wBAAwB;CAC9B;AACF"}
|