@weasel-js/core 1.14.2 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +251 -0
- package/dist/WeaselProvider.js +4 -5
- package/dist/affordances/composeAffordanceLayer.js +1 -2
- package/dist/affordances/cornerResize.js +4 -5
- package/dist/affordances/hitAffordanceRegions.js +1 -2
- package/dist/affordances/pathAnchors.js +1 -3
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/affordances/rotateCursor.js +1 -2
- package/dist/affordances/rotationHandle.js +2 -3
- package/dist/animation/behaviors/coast.js +1 -2
- package/dist/animation/behaviors/coastByWrite.js +2 -3
- package/dist/animation/behaviors/coastOnTargets.js +2 -3
- package/dist/animation/behaviors/momentum.js +1 -2
- package/dist/animation/clipTargets.js +1 -2
- package/dist/animation/departures.js +2 -3
- package/dist/animation/poseHelpers.js +2 -3
- package/dist/animation/poseTarget.js +2 -3
- package/dist/animation/poseTransition.js +1 -2
- package/dist/animation/reflow.js +2 -3
- package/dist/animation/rewind.js +1 -2
- package/dist/animation/rig/bindRig.js +2 -3
- package/dist/animation/rig/rigNode.js +1 -2
- package/dist/animation/rig/rigSeat.js +1 -2
- package/dist/animation/rig/skinBinding.js +1 -2
- package/dist/animation/rig/useRig.js +2 -3
- package/dist/animation/wrappers/animateLifecycle.js +1 -2
- package/dist/animation/wrappers/animateOnSetPose.js +1 -2
- package/dist/animation/wrappers/instantPose.js +1 -2
- package/dist/animation/writtenPoseTarget.js +1 -2
- package/dist/barrel/interactions.js +2 -1
- package/dist/barrel/paths.js +1 -1
- package/dist/barrel/renderer.js +2 -2
- package/dist/barrel/routing.js +6 -4
- package/dist/barrel/scene.js +2 -2
- package/dist/barrel/text.js +5 -3
- package/dist/canvas/Canvas.js +10 -7
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +6 -7
- package/dist/canvas/CursorCoordsHud.js +2 -3
- package/dist/canvas/DrawCanvas.js +8 -5
- package/dist/canvas/DrawCanvas.js.map +1 -1
- package/dist/canvas/GestureReason.js +1 -2
- package/dist/canvas/GestureReason.module.js +1 -2
- package/dist/canvas/LeanSceneCanvas.js +2 -3
- package/dist/canvas/ModalityHud.js +2 -3
- package/dist/canvas/NodeShape.js +3 -4
- package/dist/canvas/PickHud.js +2 -3
- package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -2
- package/dist/canvas/SceneCanvas/CanvasKitProviders.js +3 -4
- package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -2
- package/dist/canvas/SceneCanvas/DevHuds.js +1 -2
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -2
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +8 -5
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +1 -2
- package/dist/canvas/SceneCanvas/brushOverlay.js +1 -2
- package/dist/canvas/SceneCanvas/builtinToolSets.js +7 -5
- package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -2
- package/dist/canvas/SceneCanvas/contributionFrameHooks.js +1 -2
- package/dist/canvas/SceneCanvas/coordTrace.js +1 -2
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +4 -5
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +1 -2
- package/dist/canvas/SceneCanvas/defaultTextEditor.js +4 -5
- package/dist/canvas/SceneCanvas/describePresets.js +1 -2
- package/dist/canvas/SceneCanvas/dispatcherGestureBounds.js +1 -2
- package/dist/canvas/SceneCanvas/features.js +1 -2
- package/dist/canvas/SceneCanvas/insertPreviewCommands.js +1 -2
- package/dist/canvas/SceneCanvas/mergeLayers.js +1 -2
- package/dist/canvas/SceneCanvas/namedPresets.js +5 -6
- package/dist/canvas/SceneCanvas/poseGeometry.js +1 -2
- package/dist/canvas/SceneCanvas/poseRegistrars.js +1 -2
- package/dist/canvas/SceneCanvas/presets/arrange.js +2 -3
- package/dist/canvas/SceneCanvas/presets/baseline.js +1 -2
- package/dist/canvas/SceneCanvas/presets/chrome.js +1 -2
- package/dist/canvas/SceneCanvas/presets/composites.js +1 -2
- package/dist/canvas/SceneCanvas/presets/edit.js +4 -5
- package/dist/canvas/SceneCanvas/presets/ingest.js +1 -2
- package/dist/canvas/SceneCanvas/presets/paths.js +9 -10
- package/dist/canvas/SceneCanvas/presets/paths.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/select.js +1 -2
- package/dist/canvas/SceneCanvas/presets/toolDriven.js +2 -3
- package/dist/canvas/SceneCanvas/presets/tools.js +1 -2
- package/dist/canvas/SceneCanvas/presets/transform.js +2 -3
- package/dist/canvas/SceneCanvas/presets/view.js +2 -3
- package/dist/canvas/SceneCanvas/props.js +1 -2
- package/dist/canvas/SceneCanvas/scenePointQuery.js +2 -3
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +2 -3
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/toolRegistry.js +2 -3
- package/dist/canvas/SceneCanvas/useActionTunings.js +1 -2
- package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -2
- package/dist/canvas/SceneCanvas/useBakedScene.js +2 -3
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +7 -8
- package/dist/canvas/SceneCanvas/useBuiltinTools.js +3 -2
- package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasCamera.js +2 -3
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -2
- package/dist/canvas/SceneCanvas/useCanvasDispatcher.js +1 -2
- package/dist/canvas/SceneCanvas/useCanvasSelection.js +8 -6
- package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +2 -3
- package/dist/canvas/SceneCanvas/useChromeAffordances.js +2 -3
- package/dist/canvas/SceneCanvas/useContributionRoles.js +3 -2
- package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js +4 -5
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +2 -3
- package/dist/canvas/SceneCanvas/useHeldModifiers.js +1 -2
- package/dist/canvas/SceneCanvas/useMagnificationCheck.js +41 -0
- package/dist/canvas/SceneCanvas/useMagnificationCheck.js.map +1 -0
- package/dist/canvas/SceneCanvas/useNodeAtPoint.js +1 -2
- package/dist/canvas/SceneCanvas/useNodeKinds.js +2 -3
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +10 -11
- package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +1 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +6 -7
- package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js +1 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -2
- package/dist/canvas/SceneCanvas/useSceneChrome.js +22 -11
- package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +6 -7
- package/dist/canvas/SceneCanvas/useScenePicking.js +5 -6
- package/dist/canvas/SceneCanvas/useSceneRedrawSources.js +1 -2
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +7 -8
- package/dist/canvas/SceneCanvas/useSceneSlotLayers.js +2 -3
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +28 -17
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js +2 -3
- package/dist/canvas/SceneCanvas/useSurfaceRouting.js +1 -2
- package/dist/canvas/SceneCanvas/useSurfaceViewInputs.js +1 -2
- package/dist/canvas/SceneCanvas/useToolKeybindings.js +1 -2
- package/dist/canvas/SceneCanvas/useViewportActions.js +3 -4
- package/dist/canvas/SceneCanvas/worldPointAt.js +1 -2
- package/dist/canvas/SceneCanvas.js +22 -20
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +7 -4
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/SurfaceCanvas.js +1 -2
- package/dist/canvas/ViewMarker.js +1 -2
- package/dist/canvas/accentColor.js +1 -2
- package/dist/canvas/adapterDerivedPath.js +2 -3
- package/dist/canvas/adapterPickSource.js +1 -2
- package/dist/canvas/affordanceAt.js +2 -4
- package/dist/canvas/affordanceAt.js.map +1 -1
- package/dist/canvas/buildSceneLayer.js +4 -5
- package/dist/canvas/buildSceneTree.js +4 -5
- package/dist/canvas/canvasRenderer.js +1 -2
- package/dist/canvas/colorOverrides.js +1 -2
- package/dist/canvas/contextAttributes.js +1 -2
- package/dist/canvas/copiesAbove.js +1 -2
- package/dist/canvas/defaultDrawOne.js +1 -2
- package/dist/canvas/deps/areaSelect.js +5 -3
- package/dist/canvas/deps/areaSelect.js.map +1 -1
- package/dist/canvas/deps/booleans.js +5 -6
- package/dist/canvas/deps/changes.js +1 -2
- package/dist/canvas/deps/chromeRules.js +36 -0
- package/dist/canvas/deps/chromeRules.js.map +1 -0
- package/dist/canvas/deps/debug.js +1 -2
- package/dist/canvas/deps/dispatchSources.js +1 -2
- package/dist/canvas/deps/dispatcher.js +1 -2
- package/dist/canvas/deps/editAnchors.js +3 -10
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +1 -2
- package/dist/canvas/deps/geometryProjection.js +1 -2
- package/dist/canvas/deps/hitTestArea.js +6 -5
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/ingestion.js +1 -2
- package/dist/canvas/deps/insert.js +3 -4
- package/dist/canvas/deps/insertSite.js +3 -4
- package/dist/canvas/deps/kitSettings.js +1 -2
- package/dist/canvas/deps/lassoSelect.js +5 -3
- package/dist/canvas/deps/lassoSelect.js.map +1 -1
- package/dist/canvas/deps/layout.js +1 -2
- package/dist/canvas/deps/modeDefaultTool.js +22 -0
- package/dist/canvas/deps/modeDefaultTool.js.map +1 -0
- package/dist/canvas/deps/nodeAtPoint.js +1 -2
- package/dist/canvas/deps/pathEditCommits.js +3 -4
- package/dist/canvas/deps/pathLeaf.js +1 -2
- package/dist/canvas/deps/pathLeafDeps.js +2 -3
- package/dist/canvas/deps/poseDescriptor.js +1 -2
- package/dist/canvas/deps/previewPaths.js +1 -2
- package/dist/canvas/deps/resizePolicy.js +1 -2
- package/dist/canvas/deps/selectionSnapshot.js +22 -0
- package/dist/canvas/deps/selectionSnapshot.js.map +1 -0
- package/dist/canvas/deps/slice.js +5 -6
- package/dist/canvas/deps/snap.js +4 -4
- package/dist/canvas/deps/snap.js.map +1 -1
- package/dist/canvas/deps/surface.js +1 -2
- package/dist/canvas/deps/textEdit.js +1 -2
- package/dist/canvas/deps/transformFollowers.js +1 -2
- package/dist/canvas/deps/view.js +1 -2
- package/dist/canvas/derivedPath.js +6 -7
- package/dist/canvas/doubleClickTarget.js +1 -2
- package/dist/canvas/drawEnvelope.js +1 -2
- package/dist/canvas/drawnBounds.js +4 -5
- package/dist/canvas/getNodeAtPoint.js +1 -2
- package/dist/canvas/hostAnchor.js +1 -2
- package/dist/canvas/hud.module.js +1 -2
- package/dist/canvas/inheritedPaints.js +1 -2
- package/dist/canvas/insertPreviewExtent.js +1 -2
- package/dist/canvas/kitRequirements.js +1 -2
- package/dist/canvas/layerHover.js +1 -2
- package/dist/canvas/layerOrder.js +1 -2
- package/dist/canvas/layerSlots.js +1 -2
- package/dist/canvas/mountedCanvases.js +1 -2
- package/dist/canvas/nodeShapes/derivedPainter.js +2 -3
- package/dist/canvas/nodeShapes/imagePainter.js +1 -2
- package/dist/canvas/nodeShapes/measurePainter.js +2 -3
- package/dist/canvas/nodeShapes/memo.js +1 -2
- package/dist/canvas/nodeShapes/nodePaint.js +1 -2
- package/dist/canvas/nodeShapes/pathPainter.js +3 -4
- package/dist/canvas/nodeShapes/poseBox.js +1 -2
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +1 -2
- package/dist/canvas/nodeShapes/shapePainter.js +2 -3
- package/dist/canvas/nodeShapes/strokedBounds.js +1 -2
- package/dist/canvas/nodeShapes/textPainter.js +1 -2
- package/dist/canvas/overChildren.js +1 -2
- package/dist/canvas/overlayRoleStrokes.js +1 -2
- package/dist/canvas/ownClip.js +1 -2
- package/dist/canvas/paintCull.js +1 -2
- package/dist/canvas/paintedCursorLayer.js +1 -2
- package/dist/canvas/pathOverlay.js +1 -2
- package/dist/canvas/pickIndex/nodeBroadBox.js +3 -4
- package/dist/canvas/pickIndex/scenePickIndex.js +3 -4
- package/dist/canvas/pickReach.js +13 -10
- package/dist/canvas/pickReach.js.map +1 -1
- package/dist/canvas/pickWalk.js +9 -8
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/planeClips.js +1 -2
- package/dist/canvas/poseRotation.js +1 -2
- package/dist/canvas/publishPreviews.js +1 -2
- package/dist/canvas/rasterReadback.js +1 -2
- package/dist/canvas/rasterToPng.js +1 -2
- package/dist/canvas/renderDebugSnapshot.js +1 -2
- package/dist/canvas/renderNeeds.js +1 -2
- package/dist/canvas/renderSceneToPixels.js +5 -6
- package/dist/canvas/sceneAdapter.js +37 -27
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +19 -7
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/sceneLayerPaint.js +1 -2
- package/dist/canvas/scenePickReads.js +1 -2
- package/dist/canvas/scenePickSource.js +7 -8
- package/dist/canvas/scenePickSource.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +2 -3
- package/dist/canvas/sceneViewRender.js +5 -6
- package/dist/canvas/shownNodes.js +1 -2
- package/dist/canvas/slopsDebugLayer.js +1 -2
- package/dist/canvas/surfaceContribution.js +1 -2
- package/dist/canvas/useCanvasElements.js +1 -2
- package/dist/canvas/useCanvasHandle.js +1 -2
- package/dist/canvas/useCanvasPaint.js +2 -3
- package/dist/canvas/useCanvasRenderer.js +1 -2
- package/dist/canvas/useCanvasSurface.js +22 -5
- package/dist/canvas/useCanvasSurface.js.map +1 -1
- package/dist/canvas/useCanvasView.js +2 -3
- package/dist/canvas/useClientToWorld.js +1 -2
- package/dist/canvas/useExtraLayers.js +1 -2
- package/dist/canvas/useFrameLoop.js +7 -9
- package/dist/canvas/useFrameLoop.js.map +1 -1
- package/dist/canvas/useHitObserver.js +1 -2
- package/dist/canvas/useHostAnchor.js +1 -2
- package/dist/canvas/useInFlightBounds.js +6 -6
- package/dist/canvas/useInFlightBounds.js.map +1 -1
- package/dist/canvas/useLateContentRedraw.js +1 -2
- package/dist/canvas/useLayerStack.js +2 -3
- package/dist/canvas/usePaintedCursor.js +1 -2
- package/dist/canvas/useRedrawOn.js +1 -2
- package/dist/canvas/useSceneAdapter.js +1 -2
- package/dist/canvas/useSceneLayerList.js +1 -2
- package/dist/canvas/useSurfaceInput.js +1 -2
- package/dist/canvas/useSurfaceSize.js +5 -5
- package/dist/canvas/useSurfaceSize.js.map +1 -1
- package/dist/canvas/useToolCursor.js +1 -2
- package/dist/canvas/useViewAim.js +2 -3
- package/dist/canvas/useViewHelpers.js +5 -4
- package/dist/canvas/useViewHelpers.js.map +1 -1
- package/dist/canvas/useViewLayerGate.js +1 -2
- package/dist/canvas/useViewNesting.js +1 -2
- package/dist/canvas/useViewRegistrySurface.js +1 -2
- package/dist/canvas/viewInputs.js +1 -2
- package/dist/canvas/viewMarkers.js +1 -2
- package/dist/canvas/viewRegistry.js +1 -2
- package/dist/canvas/viewSnap.js +1 -2
- package/dist/canvas/watchElementMove.js +1 -2
- package/dist/canvas/wrapNodeOutput.js +1 -2
- package/dist/core/adapters/testing/rootsTree.js +1 -2
- package/dist/core/applyOps.js +1 -2
- package/dist/core/device/targets.js +1 -2
- package/dist/core/device/useDeviceProfile.js +1 -2
- package/dist/core/device/useTargetBasePx.js +1 -2
- package/dist/core/device/watchDevicePixelRatio.js +1 -2
- package/dist/core/geometry/autoPoseDescriptor.js +2 -3
- package/dist/core/geometry/booleanOfPaths.js +1 -2
- package/dist/core/geometry/inkExtents.js +1 -2
- package/dist/core/geometry/pathPoseDescriptor.js +1 -2
- package/dist/core/geometry/poseDescriptor.js +1 -2
- package/dist/core/geometry/regionHit.js +36 -18
- package/dist/core/geometry/regionHit.js.map +1 -1
- package/dist/core/geometry/targetFrame.js +1 -2
- package/dist/core/geometry/unionBounds.js +1 -2
- package/dist/core/layers/backgroundFill.js +1 -2
- package/dist/core/layers/children.js +1 -2
- package/dist/core/layers/gateLayer.js +1 -2
- package/dist/core/layers/layerGroup.js +1 -2
- package/dist/core/layers/redrawSource.js +1 -2
- package/dist/core/layers/render.js +1 -2
- package/dist/core/layers/retain.js +1 -2
- package/dist/core/ops/arrange.js +1 -2
- package/dist/core/ops/create.js +2 -3
- package/dist/core/ops/createHistory.js +2 -3
- package/dist/core/ops/delete.js +2 -3
- package/dist/core/ops/nodeFlags.js +1 -2
- package/dist/core/ops/registry.js +1 -2
- package/dist/core/ops/reorder/algorithms.js +1 -2
- package/dist/core/ops/reorder/index.js +1 -2
- package/dist/core/ops/reorder/setChildOrder.js +1 -2
- package/dist/core/ops/reparent.js +1 -2
- package/dist/core/ops/select.js +1 -2
- package/dist/core/ops/setData.js +1 -2
- package/dist/core/ops/setDependsOn.js +1 -2
- package/dist/core/ops/setLayer.js +1 -2
- package/dist/core/ops/setLayout.js +1 -2
- package/dist/core/ops/setPath.js +1 -2
- package/dist/core/ops/setText.js +1 -2
- package/dist/core/ops/slot.js +1 -2
- package/dist/core/ops/transform.js +1 -2
- package/dist/core/ops/writtenBy.js +1 -2
- package/dist/core/pages/fitViewToPage.js +1 -2
- package/dist/core/pages/pageEdits.js +3 -4
- package/dist/core/pages/pages.js +1 -2
- package/dist/core/pages/pages.js.map +1 -1
- package/dist/core/pages/pagesLayer.js +1 -2
- package/dist/core/paint/meshKind.js +1 -2
- package/dist/core/paint/meshKindLazy.js +9 -18
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/paint/paintKindEntry.js +1 -2
- package/dist/core/paint/paintKinds.js +1 -2
- package/dist/core/paint/registerMesh.js +1 -2
- package/dist/core/scene/NodeProperties.js +1 -2
- package/dist/core/scene/NodeRouting.js +1 -2
- package/dist/core/scene/annotation.js +1 -2
- package/dist/core/scene/appliedLog.js +1 -2
- package/dist/core/scene/arrivalWindow.js +2 -3
- package/dist/core/scene/arrivals.js +1 -2
- package/dist/core/scene/booleanGroup.js +1 -2
- package/dist/core/scene/booleanOfChildren.js +1 -2
- package/dist/core/scene/boundOps.js +1 -2
- package/dist/core/scene/carryDescendants.js +2 -3
- package/dist/core/scene/clipNothing.js +1 -2
- package/dist/core/scene/composeFrame.js +1 -2
- package/dist/core/scene/composePose.js +1 -2
- package/dist/core/scene/declaredFieldSetters.js +1 -2
- package/dist/core/scene/depFrame.js +1 -2
- package/dist/core/scene/depMemo.js +1 -2
- package/dist/core/scene/dependents.js +1 -2
- package/dist/core/scene/derivedParts.js +1 -2
- package/dist/core/scene/derivedPath.js +2 -3
- package/dist/core/scene/docValues.js +1 -2
- package/dist/core/scene/documentOf.js +1 -2
- package/dist/core/scene/editMode.js +5 -3
- package/dist/core/scene/editMode.js.map +1 -1
- package/dist/core/scene/editPass.js +1 -2
- package/dist/core/scene/effectivePose.js +3 -4
- package/dist/core/scene/fieldEdits.js +1 -2
- package/dist/core/scene/fontReads.js +1 -2
- package/dist/core/scene/historyFacade.js +8 -52
- package/dist/core/scene/historyFacade.js.map +1 -1
- package/dist/core/scene/historyForm.js +1 -2
- package/dist/core/scene/inheritedPaint.js +1 -2
- package/dist/core/scene/kitOps.js +16 -15
- package/dist/core/scene/kitOps.js.map +1 -1
- package/dist/core/scene/kitRegistry.js +2 -3
- package/dist/core/scene/layerOps.js +1 -2
- package/dist/core/scene/layerStack.js +1 -2
- package/dist/core/scene/layoutPass.js +4 -5
- package/dist/core/scene/lockGuard.js +1 -2
- package/dist/core/scene/minSize.js +1 -2
- package/dist/core/scene/nestedHit.js +1 -2
- package/dist/core/scene/nodeAsShown.js +1 -2
- package/dist/core/scene/nodeFlags.js +1 -2
- package/dist/core/scene/nodeFnFields.js +1 -2
- package/dist/core/scene/nodeGates.js +3 -4
- package/dist/core/scene/nodeGates.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +1 -2
- package/dist/core/scene/nodeStore.js +2 -3
- package/dist/core/scene/notifier.js +1 -2
- package/dist/core/scene/opTable.js +9 -10
- package/dist/core/scene/opTable.js.map +1 -1
- package/dist/core/scene/outerOps.js +3 -4
- package/dist/core/scene/outerOps.js.map +1 -1
- package/dist/core/scene/pathEffects/blend.js +1 -2
- package/dist/core/scene/pathEffects/derive.js +2 -3
- package/dist/core/scene/pathEffects/effects.js +1 -2
- package/dist/core/scene/pathEffects/foundHidden.js +1 -2
- package/dist/core/scene/pathEffects/textAlong.js +1 -2
- package/dist/core/scene/pathEffects/textAlongBands.js +1 -2
- package/dist/core/scene/plyStacks.js +1 -2
- package/dist/core/scene/poseFeed.js +1 -2
- package/dist/core/scene/poseHold.js +1 -2
- package/dist/core/scene/poseOverrides.js +1 -2
- package/dist/core/scene/posePlies.js +1 -2
- package/dist/core/scene/poseRemovals.js +1 -2
- package/dist/core/scene/poseSnapshot.js +1 -2
- package/dist/core/scene/poseWrites.js +17 -17
- package/dist/core/scene/poseWrites.js.map +1 -1
- package/dist/core/scene/reconcileSpecs.js +26 -3
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/recorder.js +1 -2
- package/dist/core/scene/renderOrder.js +1 -2
- package/dist/core/scene/sameValue.js +1 -2
- package/dist/core/scene/scene.js +11 -12
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneChanges.js +1 -2
- package/dist/core/scene/sceneEdits.js +1 -2
- package/dist/core/scene/sceneJournals.js +77 -0
- package/dist/core/scene/sceneJournals.js.map +1 -0
- package/dist/core/scene/sceneMutators.js +2 -3
- package/dist/core/scene/scenePoseAdapter.js +1 -2
- package/dist/core/scene/sceneRecording.js +4 -4
- package/dist/core/scene/sceneRecording.js.map +1 -1
- package/dist/core/scene/sceneSelection.js +8 -5
- package/dist/core/scene/sceneSelection.js.map +1 -1
- package/dist/core/scene/sceneState.js +3 -4
- package/dist/core/scene/scrollOffsets.js +1 -2
- package/dist/core/scene/serialization.js +2 -3
- package/dist/core/scene/staleReplay.js +1 -2
- package/dist/core/scene/statedFields.js +1 -2
- package/dist/core/scene/strategyView.js +1 -2
- package/dist/core/scene/treeEdits.js +2 -3
- package/dist/core/scene/treeOrder.js +1 -2
- package/dist/core/scene/treeReads.js +1 -2
- package/dist/core/scene/treeShape.js +1 -2
- package/dist/core/scene/types.js +1 -2
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/unionOfChildren.js +1 -2
- package/dist/core/scene/unitScopes.js +20 -4
- package/dist/core/scene/unitScopes.js.map +1 -1
- package/dist/core/scene/useScene.js +2 -3
- package/dist/core/scene/worldSave.js +2 -3
- package/dist/core/selection/chromeState.js +1 -2
- package/dist/core/selection/outermostSelection.js +56 -0
- package/dist/core/selection/outermostSelection.js.map +1 -0
- package/dist/core/selection/readOnlySelection.js +1 -2
- package/dist/core/selection/selectionTarget.js +1 -2
- package/dist/core/selection/usePublishSceneSelection.js +1 -2
- package/dist/core/selection/useSelection.js +1 -2
- package/dist/core/shapeKinds.js +1 -2
- package/dist/core/stableStringify.js +1 -2
- package/dist/core/stylus/stylus.js +1 -2
- package/dist/core/stylus/usePointerStylus.js +1 -2
- package/dist/core/usePaintKinds.js +1 -2
- package/dist/core/viewport/clampView.js +1 -2
- package/dist/core/viewport/clientToWorld.js +1 -2
- package/dist/core/viewport/createVelocityTracker.js +1 -2
- package/dist/core/viewport/fitToBounds.js +1 -2
- package/dist/core/viewport/fitViewToBounds.js +1 -2
- package/dist/core/viewport/interpolateView.js +1 -2
- package/dist/core/viewport/parallax.js +1 -2
- package/dist/core/viewport/sceneNodeClientRect.js +1 -2
- package/dist/core/viewport/screenSlop.js +1 -2
- package/dist/core/viewport/useAutoCenter.js +1 -2
- package/dist/core/viewport/useCanvasSize.js +48 -28
- package/dist/core/viewport/useCanvasSize.js.map +1 -1
- package/dist/core/viewport/useDecayLoop.js +1 -2
- package/dist/core/viewport/useNodeOverlayFrame.js +1 -2
- package/dist/core/viewport/useVelocityTracker.js +1 -2
- package/dist/core/viewport/useViewAnimation.js +3 -4
- package/dist/core/viewport/view.js +1 -2
- package/dist/core/viewport/viewTransform.js +1 -2
- package/dist/core/viewport/viewportWorldRect.js +1 -2
- package/dist/core/viewport/wheelHandler.js +1 -2
- package/dist/core/viewport/zoomAt.js +2 -3
- package/dist/core/viewport/zoomBounds.js +1 -2
- package/dist/debug/createDebugOverlayLayer.js +1 -2
- package/dist/debug/createDebugSink.js +1 -2
- package/dist/debug/defaultTheme.js +1 -2
- package/dist/debug/features.js +1 -2
- package/dist/debug/frameStats.js +1 -2
- package/dist/debug/overlayPanels.js +1 -2
- package/dist/debug/parseDebugFlags.js +1 -2
- package/dist/debug/useDebugSink.js +2 -3
- package/dist/{depSchema-DD9H89gX.d.ts → depSchema-C7a_OMt-.d.ts} +101 -65
- package/dist/devtools/handlers.js +1 -2
- package/dist/devtools/history.js +1 -2
- package/dist/devtools/hook.js +1 -2
- package/dist/devtools/label.js +1 -2
- package/dist/devtools/notices.js +1 -2
- package/dist/devtools/protocol.js +1 -2
- package/dist/devtools/registry.js +1 -2
- package/dist/devtools/root.js +1 -2
- package/dist/devtools/serialize.js +1 -2
- package/dist/devtools/session.js +2 -3
- package/dist/devtools/transport.js +1 -2
- package/dist/devtools/useDevtoolsRoot.js +1 -2
- package/dist/devtools-protocol.d.ts +1 -1
- package/dist/devtools-protocol.js +2 -2
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +1 -2
- package/dist/domains/chrome-caps/buildChromeCtx.js +1 -2
- package/dist/domains/chrome-caps/conditions.js +1 -2
- package/dist/domains/chrome-caps/defaults.js +1 -2
- package/dist/domains/chrome-caps/resolve.js +3 -3
- package/dist/domains/chrome-caps/resolve.js.map +1 -1
- package/dist/domains/chrome-caps/useChromeCaps.js +5 -4
- package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -1
- package/dist/domains/chrome-caps/useHoverTracking.js +1 -2
- package/dist/domains/ingestion/contentHandlers.js +1 -2
- package/dist/domains/ingestion/ghost.js +2 -3
- package/dist/domains/ingestion/imageHandler.js +1 -2
- package/dist/domains/ingestion/kitHandlers.js +2 -3
- package/dist/domains/ingestion/openFilePicker.js +1 -2
- package/dist/domains/ingestion/placement.js +1 -2
- package/dist/domains/ingestion/svgHandler.js +1 -2
- package/dist/domains/ingestion/textHandler.js +1 -2
- package/dist/domains/ingestion/useCanvasIngestion.js +1 -2
- package/dist/domains/ingestion/weaselJsonHandler.js +2 -3
- package/dist/domains/longPress/longPressFeedback.js +1 -2
- package/dist/domains/measure/actionIds.js +1 -2
- package/dist/domains/measure/measureCommands.js +6 -7
- package/dist/domains/measure/measureContribution.js +3 -4
- package/dist/domains/measure/measureEdits.js +3 -4
- package/dist/domains/measure/measureGestures.js +2 -3
- package/dist/domains/measure/measureHit.js +3 -4
- package/dist/domains/measure/measureLayer.js +3 -4
- package/dist/domains/measure/measurement.js +1 -2
- package/dist/domains/measure/measurements.js +1 -2
- package/dist/domains/modes/modeLayers.js +1 -2
- package/dist/domains/modes/modeTriggers.js +1 -2
- package/dist/domains/pathEffects/effectSources.js +2 -3
- package/dist/domains/pathEffects/index.js +1 -2
- package/dist/domains/pathEffects/readSource.js +3 -4
- package/dist/domains/pathEffects/textItems.js +1 -2
- package/dist/domains/pathEffects/useEffectSourcesLayer.js +3 -4
- package/dist/domains/paths/anchorData.js +1 -2
- package/dist/domains/paths/anchorMarkers.js +1 -2
- package/dist/domains/paths/editablePath.js +3 -4
- package/dist/domains/paths/originProjection.js +1 -2
- package/dist/domains/paths/pathEditExtension.js +1 -2
- package/dist/domains/paths/pathEditState.js +1 -26
- package/dist/domains/paths/pathEditState.js.map +1 -1
- package/dist/domains/paths/pathEditingOverlayLayer.js +2 -3
- package/dist/domains/paths/pathLayer.js +1 -2
- package/dist/domains/paths/pathRepresentation.js +1 -2
- package/dist/domains/paths/pathSnap/glyphs.js +1 -2
- package/dist/domains/paths/pathSnap/pathSnapSource.js +1 -2
- package/dist/domains/paths/pathSnap/sceneIndex.js +1 -2
- package/dist/domains/paths/pathSnap/snapGeometry.js +2 -3
- package/dist/domains/paths/penPreviewLayer.js +1 -2
- package/dist/domains/paths/shapeGenerator.js +1 -2
- package/dist/domains/paths/strokeOutline.js +2 -3
- package/dist/domains/paths/useAnchorFields.js +1 -2
- package/dist/domains/paths/usePathEdit.js +2 -3
- package/dist/domains/poseRun/usePoseRun.js +1 -2
- package/dist/domains/selection/enteredGroupFrame.js +1 -2
- package/dist/domains/selection/enteredGroupScope.js +1 -2
- package/dist/domains/selection/enteredOperands.js +1 -2
- package/dist/domains/selection/handleCommands.js +2 -3
- package/dist/domains/selection/overlay.js +6 -7
- package/dist/domains/selection/selectionPose.js +2 -3
- package/dist/domains/snap/angle2d.js +1 -2
- package/dist/domains/snap/debugCandidates.js +1 -2
- package/dist/domains/snap/gather.js +1 -2
- package/dist/domains/snap/inputSubspace.js +2 -3
- package/dist/domains/snap/lazySession.js +1 -2
- package/dist/domains/snap/marks.js +1 -2
- package/dist/domains/snap/outcomeStore.js +1 -2
- package/dist/domains/snap/pointSnap.js +1 -2
- package/dist/domains/snap/probes2d.js +6 -2
- package/dist/domains/snap/probes2d.js.map +1 -1
- package/dist/domains/snap/readoutDraw.js +1 -2
- package/dist/domains/snap/readoutLayer.js +1 -2
- package/dist/domains/snap/registry.js +1 -2
- package/dist/domains/snap/resizeBox.js +1 -2
- package/dist/domains/snap/roles.js +1 -2
- package/dist/domains/snap/rotationSource.js +1 -2
- package/dist/domains/snap/sceneReads.js +13 -4
- package/dist/domains/snap/sceneReads.js.map +1 -1
- package/dist/domains/snap/sceneSession2d.js +6 -5
- package/dist/domains/snap/sceneSession2d.js.map +1 -1
- package/dist/domains/snap/screen.js +2 -3
- package/dist/domains/snap/session2d.js +4 -5
- package/dist/domains/snap/spanSnap.js +1 -2
- package/dist/domains/snap/stepAngles.js +1 -2
- package/dist/domains/text/editingDrawOne.js +1 -2
- package/dist/domains/text/hostTextEditors.js +1 -2
- package/dist/domains/text/index.js +2 -2
- package/dist/domains/text/sceneTextDrafts.js +1 -2
- package/dist/domains/text/textHitAt.js +4 -5
- package/dist/domains/text/textLayer.js +12 -2
- package/dist/domains/text/textLayer.js.map +1 -1
- package/dist/domains/text/useSceneTextEdit.js +4 -5
- package/dist/domains/viewports/aim.js +3 -4
- package/dist/domains/viewports/viewMarker.js +1 -2
- package/dist/domains/viewports/viewMarkerDrag.js +1 -2
- package/dist/domains/viewports/viewResolver.js +1 -2
- package/dist/domains/viewports/viewportLayer.js +1 -2
- package/dist/iconRegistry/IconRegistryProvider.js +1 -2
- package/dist/iconRegistry/useIconGlyph.js +1 -2
- package/dist/iconRegistry/useIconVariant.js +1 -2
- package/dist/icons.table.js +2 -3
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +5 -0
- package/dist/icons2.table.js +1 -2
- package/dist/index.d.ts +160 -64
- package/dist/index.js +14 -9
- package/dist/input/keyState.js +1 -2
- package/dist/input/useKeyModifiers.js +1 -2
- package/dist/input/useKeyState.js +1 -2
- package/dist/interactions/actions/admitsChild.js +1 -2
- package/dist/interactions/actions/align/action.js +6 -7
- package/dist/interactions/actions/align/align.js +6 -7
- package/dist/interactions/actions/booleans/action.js +5 -4
- package/dist/interactions/actions/booleans/action.js.map +1 -1
- package/dist/interactions/actions/booleans/booleanGroupAction.js +2 -3
- package/dist/interactions/actions/booleans/booleans.js +1 -2
- package/dist/interactions/actions/booleans/commit.js +4 -5
- package/dist/interactions/actions/booleans/custom.js +2 -3
- package/dist/interactions/actions/booleans/keymap.js +1 -2
- package/dist/interactions/actions/booleans/operands.js +1 -2
- package/dist/interactions/actions/booleans/pieces.js +1 -2
- package/dist/interactions/actions/booleans/placement.js +1 -2
- package/dist/interactions/actions/booleans/useBooleans.js +1 -2
- package/dist/interactions/actions/booleans/useBooleansAdapter.js +1 -2
- package/dist/interactions/actions/clipboard/action.js +2 -3
- package/dist/interactions/actions/clipboard/clipboardOps.js +2 -3
- package/dist/interactions/actions/clipboard/materializePaste.js +1 -2
- package/dist/interactions/actions/clipboard/wireFormat.js +1 -2
- package/dist/interactions/actions/clone/action.js +7 -8
- package/dist/interactions/actions/clone/behaviors.js +4 -5
- package/dist/interactions/actions/defaultCommitAdapter.js +2 -3
- package/dist/interactions/actions/defaults/anchorEditing.js +6 -10
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorFields.js +1 -2
- package/dist/interactions/actions/defaults/anchorHandles.js +2 -3
- package/dist/interactions/actions/defaults/anchorJoin.js +2 -3
- package/dist/interactions/actions/defaults/areaSelect.js +6 -6
- package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +4 -6
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/cancelGesture.js +1 -2
- package/dist/interactions/actions/defaults/clearSelection.js +1 -2
- package/dist/interactions/actions/defaults/containerScroll.js +4 -5
- package/dist/interactions/actions/defaults/contextMenu.js +6 -7
- package/dist/interactions/actions/defaults/copySubtree.js +2 -3
- package/dist/interactions/actions/defaults/createPaintAction.js +3 -4
- package/dist/interactions/actions/defaults/delete.js +2 -3
- package/dist/interactions/actions/defaults/duplicate.js +5 -6
- package/dist/interactions/actions/defaults/enterGroup.js +2 -3
- package/dist/interactions/actions/defaults/enterPathEdit.js +1 -2
- package/dist/interactions/actions/defaults/enterTextEdit.js +1 -2
- package/dist/interactions/actions/defaults/escape.js +1 -2
- package/dist/interactions/actions/defaults/exitPathEdit.js +18 -3
- package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +6 -7
- package/dist/interactions/actions/defaults/ingest.js +3 -4
- package/dist/interactions/actions/defaults/insertPathAnchor.js +2 -3
- package/dist/interactions/actions/defaults/nudge.js +3 -4
- package/dist/interactions/actions/defaults/nudgeSteps.js +2 -3
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
- package/dist/interactions/actions/defaults/pathEditMarks.js +1 -2
- package/dist/interactions/actions/defaults/pinchZoom.js +3 -4
- package/dist/interactions/actions/defaults/reorder.js +2 -3
- package/dist/interactions/actions/defaults/requiresSelection.js +1 -2
- package/dist/interactions/actions/defaults/segmentUnder.js +1 -2
- package/dist/interactions/actions/defaults/selectAll.js +1 -2
- package/dist/interactions/actions/defaults/selectDeep.js +1 -2
- package/dist/interactions/actions/defaults/setFill.js +1 -2
- package/dist/interactions/actions/defaults/setFillOpacity.js +1 -2
- package/dist/interactions/actions/defaults/setStroke.js +1 -2
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -2
- package/dist/interactions/actions/defaults/slice.js +1 -2
- package/dist/interactions/actions/defaults/sliceOps.js +1 -2
- package/dist/interactions/actions/defaults/toolActivate.js +1 -2
- package/dist/interactions/actions/defaults/toolResetToDefault.js +1 -2
- package/dist/interactions/actions/defaults/undoRedo.js +1 -2
- package/dist/interactions/actions/defaults/viewportDragPan.js +1 -2
- package/dist/interactions/actions/defaults/viewportWheelPan.js +1 -2
- package/dist/interactions/actions/defaults/viewportZoom.js +3 -4
- package/dist/interactions/actions/distribute/action.js +4 -5
- package/dist/interactions/actions/distribute/distribute.js +5 -6
- package/dist/interactions/actions/distribute/plan.js +1 -2
- package/dist/interactions/actions/drawnBoxes.js +4 -5
- package/dist/interactions/actions/dropVerdict.js +1 -2
- package/dist/interactions/actions/edit-anchors/action.js +7 -9
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/extensionDrags.js +1 -2
- package/dist/interactions/actions/edit-anchors/geometry.js +1 -2
- package/dist/interactions/actions/edit-anchors/handleDrag.js +1 -2
- package/dist/interactions/actions/edit-anchors/handles.js +1 -2
- package/dist/interactions/actions/edit-anchors/params.js +1 -2
- package/dist/interactions/actions/edit-anchors/preview.js +1 -2
- package/dist/interactions/actions/edit-anchors/symmetry.js +1 -2
- package/dist/interactions/actions/flip/action.js +8 -9
- package/dist/interactions/actions/flip/helpers.js +1 -2
- package/dist/interactions/actions/geometryProjection.js +1 -2
- package/dist/interactions/actions/gestureFrames.js +3 -4
- package/dist/interactions/actions/gestureLifecycle.js +1 -2
- package/dist/interactions/actions/hitSelection.js +7 -3
- package/dist/interactions/actions/hitSelection.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +27 -9
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/commit.js +2 -3
- package/dist/interactions/actions/insert/extras.js +1 -2
- package/dist/interactions/actions/insert/frame.js +1 -2
- package/dist/interactions/actions/insert/geometry.js +1 -2
- package/dist/interactions/actions/insert/params.js +42 -0
- package/dist/interactions/actions/insert/params.js.map +1 -0
- package/dist/interactions/actions/insert/parent.js +1 -2
- package/dist/interactions/actions/insert/placeAction.js +5 -6
- package/dist/interactions/actions/insert/repositionAction.js +1 -2
- package/dist/interactions/actions/insert/rotateAction.js +1 -2
- package/dist/interactions/actions/insert/session.js +5 -6
- package/dist/interactions/actions/insert/shape.js +2 -3
- package/dist/interactions/actions/lasso-select/action.js +9 -9
- package/dist/interactions/actions/lasso-select/action.js.map +1 -1
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +2 -3
- package/dist/interactions/actions/magnification.js +37 -0
- package/dist/interactions/actions/magnification.js.map +1 -0
- package/dist/interactions/actions/move/action.js +2 -3
- package/dist/interactions/actions/move/behaviors/layoutDrop.js +2 -3
- package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +2 -3
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +1 -2
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +2 -3
- package/dist/interactions/actions/move/capture.js +6 -7
- package/dist/interactions/actions/move/commit/index.js +2 -3
- package/dist/interactions/actions/move/commit/layoutDrop.js +4 -5
- package/dist/interactions/actions/move/commit/layoutRelease.js +7 -8
- package/dist/interactions/actions/move/commit/rebase.js +2 -3
- package/dist/interactions/actions/move/commit/reparent.js +5 -6
- package/dist/interactions/actions/move/delta.js +2 -3
- package/dist/interactions/actions/move/frame.js +2 -3
- package/dist/interactions/actions/move/gestureAdapter.js +1 -2
- package/dist/interactions/actions/move/landedDescendants.js +2 -3
- package/dist/interactions/actions/move/layout/container.js +3 -4
- package/dist/interactions/actions/move/layout/dragged.js +2 -3
- package/dist/interactions/actions/move/layout/dropPass.js +5 -6
- package/dist/interactions/actions/move/layout/exits.js +2 -3
- package/dist/interactions/actions/move/layout/glide.js +2 -3
- package/dist/interactions/actions/move/layout/view.js +1 -2
- package/dist/interactions/actions/move/refusal.js +2 -3
- package/dist/interactions/actions/move/session.js +4 -5
- package/dist/interactions/actions/move/translate.js +2 -3
- package/dist/interactions/actions/moveNodes.js +5 -6
- package/dist/interactions/actions/notices.js +16 -0
- package/dist/interactions/actions/notices.js.map +1 -0
- package/dist/interactions/actions/outlines/createOutlines.js +9 -8
- package/dist/interactions/actions/outlines/createOutlines.js.map +1 -1
- package/dist/interactions/actions/outlines/createOutlinesAction.js +14 -10
- package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
- package/dist/interactions/actions/outlines/outlineStroke.js +2 -3
- package/dist/interactions/actions/outlines/outlineStrokeAction.js +5 -5
- package/dist/interactions/actions/outlines/outlineStrokeAction.js.map +1 -1
- package/dist/interactions/actions/outlines/replaceWithPaths.js +5 -6
- package/dist/interactions/actions/outlines/strokeRegion.js +1 -2
- package/dist/interactions/actions/outlines/useCreateOutlinesAdapter.js +1 -2
- package/dist/interactions/actions/pathCommands/cutJoin.js +3 -4
- package/dist/interactions/actions/pathCommands/livePathEdit.js +3 -4
- package/dist/interactions/actions/pathCommands/pathNodes.js +2 -3
- package/dist/interactions/actions/pathCommands/simplifyOffset.js +2 -3
- package/dist/interactions/actions/pathGestureTags.js +1 -2
- package/dist/interactions/actions/planeInput.js +2 -3
- package/dist/interactions/actions/pointerState.js +1 -2
- package/dist/interactions/actions/poseDescriptorDep.js +1 -2
- package/dist/interactions/actions/poseFrame.js +3 -4
- package/dist/interactions/actions/resize/action.js +3 -4
- package/dist/interactions/actions/resize/aspect.js +1 -2
- package/dist/interactions/actions/resize/behaviors/clampMinSize.js +1 -2
- package/dist/interactions/actions/resize/boxFrame.js +1 -2
- package/dist/interactions/actions/resize/capture.js +7 -8
- package/dist/interactions/actions/resize/commit.js +3 -4
- package/dist/interactions/actions/resize/cornerHandles.js +1 -2
- package/dist/interactions/actions/resize/frame.js +7 -8
- package/dist/interactions/actions/resize/held.js +2 -3
- package/dist/interactions/actions/resize/minimum.js +1 -2
- package/dist/interactions/actions/resize/replay.js +1 -2
- package/dist/interactions/actions/resize/ruleBounds.js +1 -2
- package/dist/interactions/actions/resize/session.js +9 -10
- package/dist/interactions/actions/resolveOverlays.js +1 -2
- package/dist/interactions/actions/resolvePreviews.js +3 -4
- package/dist/interactions/actions/rotate/action.js +5 -6
- package/dist/interactions/actions/rotate/commit.js +2 -3
- package/dist/interactions/actions/rotate/frame.js +3 -4
- package/dist/interactions/actions/rotate/geometry.js +1 -2
- package/dist/interactions/actions/rotate/handle.js +2 -3
- package/dist/interactions/actions/rotate/replay.js +2 -3
- package/dist/interactions/actions/rotate/session.js +10 -11
- package/dist/interactions/actions/selectionSubtree.js +7 -22
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/spatialReference.js +1 -2
- package/dist/interactions/actions/testUtils.js +1 -9
- package/dist/interactions/actions/testUtils.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +5 -6
- package/dist/interactions/actions/transform-again/memory.js +1 -2
- package/dist/interactions/actions/transformFinish.js +1 -2
- package/dist/interactions/actions/useActionSet.js +1 -2
- package/dist/interactions/actions/useNotices.js +12 -0
- package/dist/interactions/actions/useNotices.js.map +1 -0
- package/dist/interactions/actions/useStandardActions.js +1 -2
- package/dist/interactions/gestures/dragGesture/dragGesture.js +1 -2
- package/dist/interactions/gestures/dragGesture/dragPointOf.js +1 -2
- package/dist/interactions/gestures/dragRadial/dragRadial.js +2 -3
- package/dist/interactions/gestures/dragRect/dragRect.js +2 -3
- package/dist/interactions/gestures/handleDrag/handleDrag.js +1 -2
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js +1 -2
- package/dist/interactions/gestures/shared/originProjection.js +1 -2
- package/dist/interactions/gestures/shared/screenTolerance.js +1 -2
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +1 -2
- package/dist/kitSettingsSchema.js +4 -5
- package/dist/test-hook/createTestHook.js +1 -2
- package/dist/test-hook/install.js +1 -2
- package/dist/test-hook/useTestHook.js +1 -2
- package/dist/test-seams.d.ts +7 -14
- package/dist/test-seams.js +2 -2
- package/dist/tools/builtin/anchors/anchorsTool.js +30 -0
- package/dist/tools/builtin/anchors/anchorsTool.js.map +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js +2 -3
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js +1 -2
- package/dist/tools/builtin/hand/dragPanContribution.js +1 -2
- package/dist/tools/builtin/hand/useHandTool.js +1 -2
- package/dist/tools/builtin/image/useImageTool.js +1 -2
- package/dist/tools/builtin/lasso/useLassoTool.js +1 -2
- package/dist/tools/builtin/line/useLineTool.js +2 -3
- package/dist/tools/builtin/measure/useMeasureTool.js +2 -3
- package/dist/tools/builtin/pen/penHandle.js +1 -2
- package/dist/tools/builtin/pen/usePenTool.js +6 -7
- package/dist/tools/builtin/pencil/usePencilTool.js +1 -2
- package/dist/tools/builtin/pickTopMostHit.js +1 -2
- package/dist/tools/builtin/polygon/usePolygonTool.js +2 -3
- package/dist/tools/builtin/pressTarget.js +1 -2
- package/dist/tools/builtin/rect/useRectTool.js +2 -3
- package/dist/tools/builtin/select/adapterPointQuery.js +3 -4
- package/dist/tools/builtin/select/collapseDeferred.js +1 -2
- package/dist/tools/builtin/select/groupEntry.js +2 -3
- package/dist/tools/builtin/select/pressDecision.js +3 -4
- package/dist/tools/builtin/select/pressDecision.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +1 -2
- package/dist/tools/builtin/select/useSelectTool.js +5 -6
- package/dist/tools/builtin/shared/insertToolOptions.js +1 -2
- package/dist/tools/builtin/shared/pointerFollower.js +1 -2
- package/dist/tools/builtin/slice/useSliceTool.js +5 -6
- package/dist/tools/builtin/star/useStarTool.js +2 -3
- package/dist/tools/builtin/sticky/stickyNode.js +1 -2
- package/dist/tools/builtin/sticky/useStickyTool.js +2 -3
- package/dist/tools/builtin/text/useTextTool.js +1 -2
- package/dist/tools/index.js +4 -2
- package/dist/tools/overlayBinding.js +1 -2
- package/dist/tools/toolOptions.js +60 -0
- package/dist/tools/toolOptions.js.map +1 -0
- package/dist/tools/useKeybindings.js +1 -2
- package/dist/{types-DW14eu9B.d.ts → types-BUvUXZe5.d.ts} +3 -3
- package/dist/util/color.js +1 -2
- package/dist/util/paint.js +1 -2
- package/dist/version.js +2 -3
- package/package.json +27 -27
- package/dist/interactions/actions/defaults/pathEditTools.js +0 -60
- package/dist/interactions/actions/defaults/pathEditTools.js.map +0 -1
package/dist/canvas/pickWalk.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { ownClipOf } from "./ownClip.js";
|
|
1
2
|
import { inheritedPaintOf, inheritsPaint } from "../core/scene/inheritedPaint.js";
|
|
2
3
|
import { clipCarrier } from "./planeClips.js";
|
|
3
|
-
import { ownClipOf } from "./ownClip.js";
|
|
4
4
|
import { reaches } from "./pickReach.js";
|
|
5
5
|
import { scenePickSource, scenePlaneOf } from "./scenePickSource.js";
|
|
6
6
|
import { adapterPickSource } from "./adapterPickSource.js";
|
|
7
7
|
import { invert } from "@weasel-js/geom";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// src/canvas/pickWalk.ts
|
|
10
10
|
/**
|
|
11
11
|
* The one hit-test walk. Everything that decides whether a node *can* be hit lives here; a
|
|
12
12
|
* query supplies only the shape test and the clip predicate for its own region.
|
|
@@ -62,6 +62,12 @@ function pickWalk(src, q) {
|
|
|
62
62
|
}
|
|
63
63
|
return found;
|
|
64
64
|
};
|
|
65
|
+
const lookups = {
|
|
66
|
+
clipOf,
|
|
67
|
+
copiesOf,
|
|
68
|
+
carry
|
|
69
|
+
};
|
|
70
|
+
const parentsOf = (node) => src.parentsOf(node);
|
|
65
71
|
const order = src.order();
|
|
66
72
|
const copying = order.some((n) => typeof n.copiesFromPose === "function");
|
|
67
73
|
const visit = (q.broad !== void 0 ? src.candidates?.((layer) => {
|
|
@@ -80,16 +86,11 @@ function pickWalk(src, q) {
|
|
|
80
86
|
const pose = src.poseOf(node);
|
|
81
87
|
const derived = src.derivedPathOf?.(node);
|
|
82
88
|
const inherited = inheritsPaint(node) ? inheritedPaintOf(node, src.parentsOf(node)) : void 0;
|
|
83
|
-
if (!reaches(node, pose, derived, inherited, query,
|
|
84
|
-
clipOf,
|
|
85
|
-
copiesOf,
|
|
86
|
-
carry
|
|
87
|
-
})) continue;
|
|
89
|
+
if (!reaches(node, pose, derived, inherited, query, parentsOf, copying, lookups)) continue;
|
|
88
90
|
out.push(node.id);
|
|
89
91
|
}
|
|
90
92
|
return out;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
//#endregion
|
|
94
95
|
export { adapterPickSource, pickWalk, scenePickSource, scenePlaneOf };
|
|
95
96
|
//# sourceMappingURL=pickWalk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pickWalk.js","names":[],"sources":["../../src/canvas/pickWalk.ts"],"sourcesContent":["/**\n * The one hit-test walk. Everything that decides whether a node *can* be hit lives here; a\n * query supplies only the shape test and the clip predicate for its own region.\n *\n * The gates, in the order the walk applies them:\n * 1. groups, when the query does not want them\n * 2. the node is painted at all — not hidden in the scene (`nodeGates`),\n * and on a layer the asking view's `layerVisibility` / `layerOrder` paints\n * 3. painted alpha is above zero\n * 4. the node is not locked, unless the query asks for locked nodes\n * 5. the query's own shape test\n * 6. every ancestor clip admits the query region\n *\n * 2, 3 and 6 are all \"the renderer did not put this on screen\". A pick path\n * that skips one answers for a node the user cannot see. 4 is the other axis:\n * a locked node is on screen and still out of reach.\n */\n\nimport { invert, type Affine, type Path } from '@weasel-js/geom';\nimport type { CopiesFromPoseFn } from 'core/scene/types';\nimport { ownClipOf, type ClipBearing } from './ownClip';\nimport { inheritedPaintOf, inheritsPaint, type InheritedPaint } from 'core/scene/inheritedPaint';\nimport { rectToPlane, type PlaneMap } from 'core/viewport/parallax';\nimport { clipCarrier } from './planeClips';\nimport { reaches } from './pickReach';\n\n/** The subset of a node the walk itself reads. Deliberately structural: a\n * bare adapter's node satisfies it as readily as a `SceneNode`. */\nexport interface PickCandidate<TPose> extends ClipBearing<TPose> {\n id: string;\n kind?: string;\n layer?: string;\n pose: TPose;\n /** `false` makes the node transparent to the walk — see `SceneNode.pickable`. */\n pickable?: boolean;\n /** Read for an `'inherit'` fill or stroke, which takes its containers'. */\n data?: unknown;\n copiesFromPose?: CopiesFromPoseFn<TPose>;\n}\n\n/**\n * Everything the walk reads that a **view** owns.\n *\n * A layer must not hold state a view owns, so none of these are closed over\n * at construction — each is asked per walk, of the view being dispatched to.\n */\nexport interface PickSource<TPose> {\n /** Candidates in paint order, back-to-front. Containers included; the\n * query decides whether they can be hit. */\n order(): readonly PickCandidate<TPose>[];\n /** The pose the renderer draws — override-aware. Not `node.pose`, which is\n * how the render and pick paths came to disagree about where a node is. */\n poseOf(node: PickCandidate<TPose>): TPose;\n /** Ancestors, innermost first. Only walked for candidates that already\n * passed their own shape test. */\n parentsOf(node: PickCandidate<TPose>): readonly PickCandidate<TPose>[];\n /** Painted alpha for the asking view: the view's `alphaFor` times any\n * per-node override alpha. Omit when the source has no alpha concept. */\n alphaOf?(id: string): number;\n /** Whether this layer reaches the screen in the asking view. Omit for a\n * source with no layers. */\n layerIsPainted?(layer: string): boolean;\n /** Whether the node is locked — painted, but not something a pointer can\n * take. Omit for a source with no lock concept. */\n isLocked?(node: PickCandidate<TPose>): boolean;\n /** Whether the source hides the node, so nothing of it is on screen. Omit\n * for a source that hides nothing. */\n isHidden?(node: PickCandidate<TPose>): boolean;\n /** The path a node computes from its dependencies, or null for none. */\n derivedPathOf?(node: PickCandidate<TPose>): Path | null;\n /** How the asking view's world maps into a parallax layer's, or null for a\n * layer that moves with the camera. Omit for a source with no planes. */\n planeOf?(layer: string): PlaneMap | null;\n /**\n * The candidates a query with these broad boxes can hit, back-to-front: a\n * subset of `order()` that drops only nodes no query inside the box could\n * hit. `broadFor(layer)` is the box for that layer's query: `null` for a\n * layer it cannot reach, `undefined` for one with no box, which makes this\n * answer `null` and the walk visit `order()`. Omit for a source with no index.\n */\n candidates?(\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n ): readonly PickCandidate<TPose>[] | null;\n}\n\n/** The box a query cannot hit outside of, for a source's index to look up. */\nexport interface PickBroad {\n /** World box, already grown by the query's own slop. */\n box: { x: number; y: number; width: number; height: number };\n /** Screen px per world unit at the asking view (least axis); grows the box\n * by the largest `outsetPx` the index holds. Omit for a query whose test\n * reads no `outsetPx`. */\n pxPerWorld?: number;\n}\n\n/** `broad` carried into a plane's world, as `inPlane` carries the query. */\nexport function broadInPlane(broad: PickBroad, m: PlaneMap): PickBroad {\n const box = rectToPlane(m, broad.box);\n if (broad.pxPerWorld === undefined) return { box };\n // A plane's pixel is the same on screen, so its world unit holds fewer of them.\n const k = Math.max(Math.abs(m.scale.x), Math.abs(m.scale.y));\n return { box, pxPerWorld: broad.pxPerWorld / k };\n}\n\n/** One hit-test question: what region, and what counts as covering it. */\nexport interface PickQuery<TPose> {\n /** Default `true`. The area walks exclude groups so a marquee does not\n * return a group and its members both. */\n includeGroups?: boolean;\n /** Default `false`. Every selecting or editing query passes over locked\n * nodes; one that only samples what is painted can take them back. */\n includeLocked?: boolean;\n /** Where the query can hit at all, for a source with an index. `inPlane`\n * carries it with the query; without it the walk visits every node. */\n broad?: PickBroad;\n /** Does this node, drawn at this pose, cover the query region? `derived`\n * is the node's resolved path when it has one — a derived node's pose is a\n * placeholder, so a region test that reads only the pose answers for the\n * wrong box. `inherited` is what its `'inherit'` fields take from its\n * containers, for the ink it is tested against; absent when it inherits\n * nothing. */\n hits(node: PickCandidate<TPose>, pose: TPose, derived?: Path | null, inherited?: InheritedPaint): boolean;\n /**\n * Does an ancestor's clip leave anything of `node` for this query to hit?\n *\n * A point query asks point-in-clip and ignores the node. An area query has\n * to ask about the node as well: the clip and the area can overlap where\n * the node is not, and the node and the clip can overlap where the area is\n * not, and only the two terms together reject both.\n */\n clipAdmits(\n clip: Path,\n node: PickCandidate<TPose>,\n pose: TPose,\n derived?: Path | null,\n ): boolean;\n /** The same question asked in a parallax plane's world: the region carried\n * through `map`, and any screen-pixel slop re-derived at the plane's scale.\n * A query without it cannot reach nodes on a plane — it would test them\n * where their poses sit rather than where they are painted. */\n inPlane?(map: PlaneMap): PickQuery<TPose>;\n /** The same question carried by `m`, an affine from world into the source\n * frame of a copy a container draws. A query without it cannot reach\n * copies — see `GroupNode.copiesFromPose`. */\n inCopy?(m: Affine): PickQuery<TPose>;\n}\n\n/**\n * Collect every candidate the query hits, back-to-front — the last element is\n * the topmost, which is the order `pickTopMostHit` expects.\n */\nexport function pickWalk<TPose>(\n src: PickSource<TPose>,\n q: PickQuery<TPose>,\n): string[] {\n const includeGroups = q.includeGroups ?? true;\n const isLocked = q.includeLocked === true ? undefined : src.isLocked;\n const out: string[] = [];\n\n // Resolving a container's clip is the expensive half, so the *path* is memoized per\n // container for the walk. The predicate re-runs per candidate: caching its answer per\n // container would answer the wrong question for an area query.\n const clipPath = new Map<string, Path | null>();\n // A clip is in its container's plane; the query is in the candidate's.\n const carry = clipCarrier(src.planeOf ?? null);\n\n const clipOf = (ancestor: PickCandidate<TPose>): Path | null => {\n const cached = clipPath.get(ancestor.id);\n if (cached !== undefined) return cached;\n const resolved = ownClipOf(\n ancestor, src.poseOf(ancestor), () => src.derivedPathOf?.(ancestor),\n );\n clipPath.set(ancestor.id, resolved);\n return resolved;\n };\n\n const copyMaps = new Map<string, readonly Affine[]>();\n const copiesOf = (ancestor: PickCandidate<TPose>): readonly Affine[] => {\n let found = copyMaps.get(ancestor.id);\n if (found === undefined) {\n const fn = ancestor.copiesFromPose;\n const copies = fn ? fn(src.poseOf(ancestor), ancestor as never) ?? [] : [];\n found = copies.map(invert).filter((m): m is Affine => m !== null);\n copyMaps.set(ancestor.id, found);\n }\n return found;\n };\n\n // One reprojected query per plane per walk; `null` marks a plane this\n // query cannot reach.\n const planeQueries = new Map<string, PickQuery<TPose> | null>();\n const queryFor = (layer: string | undefined): PickQuery<TPose> | null => {\n if (layer === undefined || src.planeOf === undefined) return q;\n let found = planeQueries.get(layer);\n if (found === undefined) {\n const map = src.planeOf(layer);\n found = map === null ? q : (q.inPlane?.(map) ?? null);\n planeQueries.set(layer, found);\n }\n return found;\n };\n\n const order = src.order();\n const copying = order.some((n) => typeof n.copiesFromPose === 'function');\n const visit = (q.broad !== undefined ? src.candidates?.((layer) => {\n const query = queryFor(layer);\n return query === null ? null : query.broad;\n }) : null) ?? order;\n for (const node of visit) {\n if (!includeGroups && node.kind === 'group') continue;\n // Transparent to picking: the click belongs to whatever is behind it,\n // which for a label inside a box is the box.\n if (node.pickable === false) continue;\n if (src.isHidden?.(node) === true) continue;\n if (node.layer !== undefined && src.layerIsPainted?.(node.layer) === false) continue;\n if (src.alphaOf !== undefined && src.alphaOf(node.id) <= 0) continue;\n if (isLocked !== undefined && isLocked(node)) continue;\n\n const query = queryFor(node.layer);\n if (query === null) continue;\n const pose = src.poseOf(node);\n const derived = src.derivedPathOf?.(node);\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, src.parentsOf(node)) : undefined;\n if (!reaches(node, pose, derived, inherited, query, () => src.parentsOf(node), copying, { clipOf, copiesOf, carry })) continue;\n out.push(node.id);\n }\n\n return out;\n}\n\n/**\n * The two answers a pick needs from the view it is asked for rather than from\n * the data behind it: how that view paints a node, and whether it paints a\n * layer at all. A scene and a bare adapter take the same pair.\n */\nexport interface ViewPickGates {\n /** Painted alpha: the view's `alphaFor` times any per-node override alpha.\n * A node painted at alpha 0 cannot be hit. */\n alphaOf?: (id: string) => number;\n /** Whether a node's `layer` reaches the screen in the asking view. */\n layerIsPainted?: (layer: string) => boolean;\n}\n\n/** The gates a bare adapter takes from its view, plus how that view's camera\n * maps into each parallax layer — see `scenePlaneOf`. */\nexport interface AdapterPickGates extends ViewPickGates {\n planeOf?: (layer: string) => PlaneMap | null;\n}\n\nexport { scenePickSource, scenePlaneOf, type ScenePickSourceOptions } from './scenePickSource';\nexport { adapterPickSource } from './adapterPickSource';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuJA,SAAgB,SACd,KACA,GACU;CACV,MAAM,gBAAgB,EAAE,iBAAiB;CACzC,MAAM,WAAW,EAAE,kBAAkB,OAAO,SAAY,IAAI;CAC5D,MAAM,MAAgB,CAAC;CAKvB,MAAM,2BAAW,IAAI,IAAyB;CAE9C,MAAM,QAAQ,YAAY,IAAI,WAAW,IAAI;CAE7C,MAAM,UAAU,aAAgD;EAC9D,MAAM,SAAS,SAAS,IAAI,SAAS,EAAE;EACvC,IAAI,WAAW,QAAW,OAAO;EACjC,MAAM,WAAW,UACf,UAAU,IAAI,OAAO,QAAQ,SAAS,IAAI,gBAAgB,QAAQ,CACpE;EACA,SAAS,IAAI,SAAS,IAAI,QAAQ;EAClC,OAAO;CACT;CAEA,MAAM,2BAAW,IAAI,IAA+B;CACpD,MAAM,YAAY,aAAsD;EACtE,IAAI,QAAQ,SAAS,IAAI,SAAS,EAAE;EACpC,IAAI,UAAU,QAAW;GACvB,MAAM,KAAK,SAAS;GAEpB,SADe,KAAK,GAAG,IAAI,OAAO,QAAQ,GAAG,QAAiB,KAAK,CAAC,IAAI,CAAC,EACjE,CAAO,IAAI,MAAM,CAAC,CAAC,QAAQ,MAAmB,MAAM,IAAI;GAChE,SAAS,IAAI,SAAS,IAAI,KAAK;EACjC;EACA,OAAO;CACT;CAIA,MAAM,+BAAe,IAAI,IAAqC;CAC9D,MAAM,YAAY,UAAuD;EACvE,IAAI,UAAU,UAAa,IAAI,YAAY,QAAW,OAAO;EAC7D,IAAI,QAAQ,aAAa,IAAI,KAAK;EAClC,IAAI,UAAU,QAAW;GACvB,MAAM,MAAM,IAAI,QAAQ,KAAK;GAC7B,QAAQ,QAAQ,OAAO,IAAK,EAAE,UAAU,GAAG,KAAK;GAChD,aAAa,IAAI,OAAO,KAAK;EAC/B;EACA,OAAO;CACT;CAEA,MAAM,QAAQ,IAAI,MAAM;CACxB,MAAM,UAAU,MAAM,MAAM,MAAM,OAAO,EAAE,mBAAmB,UAAU;CACxE,MAAM,SAAS,EAAE,UAAU,SAAY,IAAI,cAAc,UAAU;EACjE,MAAM,QAAQ,SAAS,KAAK;EAC5B,OAAO,UAAU,OAAO,OAAO,MAAM;CACvC,CAAC,IAAI,SAAS;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,iBAAiB,KAAK,SAAS,SAAS;EAG7C,IAAI,KAAK,aAAa,OAAO;EAC7B,IAAI,IAAI,WAAW,IAAI,MAAM,MAAM;EACnC,IAAI,KAAK,UAAU,UAAa,IAAI,iBAAiB,KAAK,KAAK,MAAM,OAAO;EAC5E,IAAI,IAAI,YAAY,UAAa,IAAI,QAAQ,KAAK,EAAE,KAAK,GAAG;EAC5D,IAAI,aAAa,UAAa,SAAS,IAAI,GAAG;EAE9C,MAAM,QAAQ,SAAS,KAAK,KAAK;EACjC,IAAI,UAAU,MAAM;EACpB,MAAM,OAAO,IAAI,OAAO,IAAI;EAC5B,MAAM,UAAU,IAAI,gBAAgB,IAAI;EACxC,MAAM,YAAY,cAAc,IAAI,IAAI,iBAAiB,MAAM,IAAI,UAAU,IAAI,CAAC,IAAI;EACtF,IAAI,CAAC,QAAQ,MAAM,MAAM,SAAS,WAAW,aAAa,IAAI,UAAU,IAAI,GAAG,SAAS;GAAE;GAAQ;GAAU;EAAM,CAAC,GAAG;EACtH,IAAI,KAAK,KAAK,EAAE;CAClB;CAEA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"pickWalk.js","names":[],"sources":["../../src/canvas/pickWalk.ts"],"sourcesContent":["/**\n * The one hit-test walk. Everything that decides whether a node *can* be hit lives here; a\n * query supplies only the shape test and the clip predicate for its own region.\n *\n * The gates, in the order the walk applies them:\n * 1. groups, when the query does not want them\n * 2. the node is painted at all — not hidden in the scene (`nodeGates`),\n * and on a layer the asking view's `layerVisibility` / `layerOrder` paints\n * 3. painted alpha is above zero\n * 4. the node is not locked, unless the query asks for locked nodes\n * 5. the query's own shape test\n * 6. every ancestor clip admits the query region\n *\n * 2, 3 and 6 are all \"the renderer did not put this on screen\". A pick path\n * that skips one answers for a node the user cannot see. 4 is the other axis:\n * a locked node is on screen and still out of reach.\n */\n\nimport { invert, type Affine, type Path } from '@weasel-js/geom';\nimport type { CopiesFromPoseFn } from 'core/scene/types';\nimport { ownClipOf, type ClipBearing } from './ownClip';\nimport { inheritedPaintOf, inheritsPaint, type InheritedPaint } from 'core/scene/inheritedPaint';\nimport { rectToPlane, type PlaneMap } from 'core/viewport/parallax';\nimport { clipCarrier } from './planeClips';\nimport { reaches } from './pickReach';\n\n/** The subset of a node the walk itself reads. Deliberately structural: a\n * bare adapter's node satisfies it as readily as a `SceneNode`. */\nexport interface PickCandidate<TPose> extends ClipBearing<TPose> {\n id: string;\n kind?: string;\n layer?: string;\n pose: TPose;\n /** `false` makes the node transparent to the walk — see `SceneNode.pickable`. */\n pickable?: boolean;\n /** Read for an `'inherit'` fill or stroke, which takes its containers'. */\n data?: unknown;\n copiesFromPose?: CopiesFromPoseFn<TPose>;\n}\n\n/**\n * Everything the walk reads that a **view** owns.\n *\n * A layer must not hold state a view owns, so none of these are closed over\n * at construction — each is asked per walk, of the view being dispatched to.\n */\nexport interface PickSource<TPose> {\n /** Candidates in paint order, back-to-front. Containers included; the\n * query decides whether they can be hit. */\n order(): readonly PickCandidate<TPose>[];\n /** The pose the renderer draws — override-aware. Not `node.pose`, which is\n * how the render and pick paths came to disagree about where a node is. */\n poseOf(node: PickCandidate<TPose>): TPose;\n /** Ancestors, innermost first. Only walked for candidates that already\n * passed their own shape test. */\n parentsOf(node: PickCandidate<TPose>): readonly PickCandidate<TPose>[];\n /** Painted alpha for the asking view: the view's `alphaFor` times any\n * per-node override alpha. Omit when the source has no alpha concept. */\n alphaOf?(id: string): number;\n /** Whether this layer reaches the screen in the asking view. Omit for a\n * source with no layers. */\n layerIsPainted?(layer: string): boolean;\n /** Whether the node is locked — painted, but not something a pointer can\n * take. Omit for a source with no lock concept. */\n isLocked?(node: PickCandidate<TPose>): boolean;\n /** Whether the source hides the node, so nothing of it is on screen. Omit\n * for a source that hides nothing. */\n isHidden?(node: PickCandidate<TPose>): boolean;\n /** The path a node computes from its dependencies, or null for none. */\n derivedPathOf?(node: PickCandidate<TPose>): Path | null;\n /** How the asking view's world maps into a parallax layer's, or null for a\n * layer that moves with the camera. Omit for a source with no planes. */\n planeOf?(layer: string): PlaneMap | null;\n /**\n * The candidates a query with these broad boxes can hit, back-to-front: a\n * subset of `order()` that drops only nodes no query inside the box could\n * hit. `broadFor(layer)` is the box for that layer's query: `null` for a\n * layer it cannot reach, `undefined` for one with no box, which makes this\n * answer `null` and the walk visit `order()`. Omit for a source with no index.\n */\n candidates?(\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n ): readonly PickCandidate<TPose>[] | null;\n}\n\n/** The box a query cannot hit outside of, for a source's index to look up. */\nexport interface PickBroad {\n /** World box, already grown by the query's own slop. */\n box: { x: number; y: number; width: number; height: number };\n /** Screen px per world unit at the asking view (least axis); grows the box\n * by the largest `outsetPx` the index holds. Omit for a query whose test\n * reads no `outsetPx`. */\n pxPerWorld?: number;\n}\n\n/** `broad` carried into a plane's world, as `inPlane` carries the query. */\nexport function broadInPlane(broad: PickBroad, m: PlaneMap): PickBroad {\n const box = rectToPlane(m, broad.box);\n if (broad.pxPerWorld === undefined) return { box };\n // A plane's pixel is the same on screen, so its world unit holds fewer of them.\n const k = Math.max(Math.abs(m.scale.x), Math.abs(m.scale.y));\n return { box, pxPerWorld: broad.pxPerWorld / k };\n}\n\n/** One hit-test question: what region, and what counts as covering it. */\nexport interface PickQuery<TPose> {\n /** Default `true`. The area walks exclude groups so a marquee does not\n * return a group and its members both. */\n includeGroups?: boolean;\n /** Default `false`. Every selecting or editing query passes over locked\n * nodes; one that only samples what is painted can take them back. */\n includeLocked?: boolean;\n /** Where the query can hit at all, for a source with an index. `inPlane`\n * carries it with the query; without it the walk visits every node. */\n broad?: PickBroad;\n /** Does this node, drawn at this pose, cover the query region? `derived`\n * is the node's resolved path when it has one — a derived node's pose is a\n * placeholder, so a region test that reads only the pose answers for the\n * wrong box. `inherited` is what its `'inherit'` fields take from its\n * containers, for the ink it is tested against; absent when it inherits\n * nothing. */\n hits(node: PickCandidate<TPose>, pose: TPose, derived?: Path | null, inherited?: InheritedPaint): boolean;\n /**\n * Does an ancestor's clip leave anything of `node` for this query to hit?\n *\n * A point query asks point-in-clip and ignores the node. An area query has\n * to ask about the node as well: the clip and the area can overlap where\n * the node is not, and the node and the clip can overlap where the area is\n * not, and only the two terms together reject both.\n */\n clipAdmits(\n clip: Path,\n node: PickCandidate<TPose>,\n pose: TPose,\n derived?: Path | null,\n ): boolean;\n /** The same question asked in a parallax plane's world: the region carried\n * through `map`, and any screen-pixel slop re-derived at the plane's scale.\n * A query without it cannot reach nodes on a plane — it would test them\n * where their poses sit rather than where they are painted. */\n inPlane?(map: PlaneMap): PickQuery<TPose>;\n /** The same question carried by `m`, an affine from world into the source\n * frame of a copy a container draws. A query without it cannot reach\n * copies — see `GroupNode.copiesFromPose`. */\n inCopy?(m: Affine): PickQuery<TPose>;\n}\n\n/**\n * Collect every candidate the query hits, back-to-front — the last element is\n * the topmost, which is the order `pickTopMostHit` expects.\n */\nexport function pickWalk<TPose>(\n src: PickSource<TPose>,\n q: PickQuery<TPose>,\n): string[] {\n const includeGroups = q.includeGroups ?? true;\n const isLocked = q.includeLocked === true ? undefined : src.isLocked;\n const out: string[] = [];\n\n // Resolving a container's clip is the expensive half, so the *path* is memoized per\n // container for the walk. The predicate re-runs per candidate: caching its answer per\n // container would answer the wrong question for an area query.\n const clipPath = new Map<string, Path | null>();\n // A clip is in its container's plane; the query is in the candidate's.\n const carry = clipCarrier(src.planeOf ?? null);\n\n const clipOf = (ancestor: PickCandidate<TPose>): Path | null => {\n const cached = clipPath.get(ancestor.id);\n if (cached !== undefined) return cached;\n const resolved = ownClipOf(\n ancestor, src.poseOf(ancestor), () => src.derivedPathOf?.(ancestor),\n );\n clipPath.set(ancestor.id, resolved);\n return resolved;\n };\n\n const copyMaps = new Map<string, readonly Affine[]>();\n const copiesOf = (ancestor: PickCandidate<TPose>): readonly Affine[] => {\n let found = copyMaps.get(ancestor.id);\n if (found === undefined) {\n const fn = ancestor.copiesFromPose;\n const copies = fn ? fn(src.poseOf(ancestor), ancestor as never) ?? [] : [];\n found = copies.map(invert).filter((m): m is Affine => m !== null);\n copyMaps.set(ancestor.id, found);\n }\n return found;\n };\n\n // One reprojected query per plane per walk; `null` marks a plane this\n // query cannot reach.\n const planeQueries = new Map<string, PickQuery<TPose> | null>();\n const queryFor = (layer: string | undefined): PickQuery<TPose> | null => {\n if (layer === undefined || src.planeOf === undefined) return q;\n let found = planeQueries.get(layer);\n if (found === undefined) {\n const map = src.planeOf(layer);\n found = map === null ? q : (q.inPlane?.(map) ?? null);\n planeQueries.set(layer, found);\n }\n return found;\n };\n\n const lookups = { clipOf, copiesOf, carry };\n const parentsOf = (node: PickCandidate<TPose>) => src.parentsOf(node);\n const order = src.order();\n const copying = order.some((n) => typeof n.copiesFromPose === 'function');\n const visit = (q.broad !== undefined ? src.candidates?.((layer) => {\n const query = queryFor(layer);\n return query === null ? null : query.broad;\n }) : null) ?? order;\n for (const node of visit) {\n if (!includeGroups && node.kind === 'group') continue;\n // Transparent to picking: the click belongs to whatever is behind it,\n // which for a label inside a box is the box.\n if (node.pickable === false) continue;\n if (src.isHidden?.(node) === true) continue;\n if (node.layer !== undefined && src.layerIsPainted?.(node.layer) === false) continue;\n if (src.alphaOf !== undefined && src.alphaOf(node.id) <= 0) continue;\n if (isLocked !== undefined && isLocked(node)) continue;\n\n const query = queryFor(node.layer);\n if (query === null) continue;\n const pose = src.poseOf(node);\n const derived = src.derivedPathOf?.(node);\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, src.parentsOf(node)) : undefined;\n if (!reaches(node, pose, derived, inherited, query, parentsOf, copying, lookups)) continue;\n out.push(node.id);\n }\n\n return out;\n}\n\n/**\n * The two answers a pick needs from the view it is asked for rather than from\n * the data behind it: how that view paints a node, and whether it paints a\n * layer at all. A scene and a bare adapter take the same pair.\n */\nexport interface ViewPickGates {\n /** Painted alpha: the view's `alphaFor` times any per-node override alpha.\n * A node painted at alpha 0 cannot be hit. */\n alphaOf?: (id: string) => number;\n /** Whether a node's `layer` reaches the screen in the asking view. */\n layerIsPainted?: (layer: string) => boolean;\n}\n\n/** The gates a bare adapter takes from its view, plus how that view's camera\n * maps into each parallax layer — see `scenePlaneOf`. */\nexport interface AdapterPickGates extends ViewPickGates {\n planeOf?: (layer: string) => PlaneMap | null;\n}\n\nexport { scenePickSource, scenePlaneOf, type ScenePickSourceOptions } from './scenePickSource';\nexport { adapterPickSource } from './adapterPickSource';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuJA,SAAgB,SACd,KACA,GACU;CACV,MAAM,gBAAgB,EAAE,iBAAiB;CACzC,MAAM,WAAW,EAAE,kBAAkB,OAAO,SAAY,IAAI;CAC5D,MAAM,MAAgB,CAAC;CAKvB,MAAM,2BAAW,IAAI,IAAyB;CAE9C,MAAM,QAAQ,YAAY,IAAI,WAAW,IAAI;CAE7C,MAAM,UAAU,aAAgD;EAC9D,MAAM,SAAS,SAAS,IAAI,SAAS,EAAE;EACvC,IAAI,WAAW,QAAW,OAAO;EACjC,MAAM,WAAW,UACf,UAAU,IAAI,OAAO,QAAQ,SAAS,IAAI,gBAAgB,QAAQ,CACpE;EACA,SAAS,IAAI,SAAS,IAAI,QAAQ;EAClC,OAAO;CACT;CAEA,MAAM,2BAAW,IAAI,IAA+B;CACpD,MAAM,YAAY,aAAsD;EACtE,IAAI,QAAQ,SAAS,IAAI,SAAS,EAAE;EACpC,IAAI,UAAU,QAAW;GACvB,MAAM,KAAK,SAAS;GAEpB,SADe,KAAK,GAAG,IAAI,OAAO,QAAQ,GAAG,QAAiB,KAAK,CAAC,IAAI,CAAC,EACjE,CAAO,IAAI,MAAM,CAAC,CAAC,QAAQ,MAAmB,MAAM,IAAI;GAChE,SAAS,IAAI,SAAS,IAAI,KAAK;EACjC;EACA,OAAO;CACT;CAIA,MAAM,+BAAe,IAAI,IAAqC;CAC9D,MAAM,YAAY,UAAuD;EACvE,IAAI,UAAU,UAAa,IAAI,YAAY,QAAW,OAAO;EAC7D,IAAI,QAAQ,aAAa,IAAI,KAAK;EAClC,IAAI,UAAU,QAAW;GACvB,MAAM,MAAM,IAAI,QAAQ,KAAK;GAC7B,QAAQ,QAAQ,OAAO,IAAK,EAAE,UAAU,GAAG,KAAK;GAChD,aAAa,IAAI,OAAO,KAAK;EAC/B;EACA,OAAO;CACT;CAEA,MAAM,UAAU;EAAE;EAAQ;EAAU;CAAM;CAC1C,MAAM,aAAa,SAA+B,IAAI,UAAU,IAAI;CACpE,MAAM,QAAQ,IAAI,MAAM;CACxB,MAAM,UAAU,MAAM,MAAM,MAAM,OAAO,EAAE,mBAAmB,UAAU;CACxE,MAAM,SAAS,EAAE,UAAU,SAAY,IAAI,cAAc,UAAU;EACjE,MAAM,QAAQ,SAAS,KAAK;EAC5B,OAAO,UAAU,OAAO,OAAO,MAAM;CACvC,CAAC,IAAI,SAAS;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,iBAAiB,KAAK,SAAS,SAAS;EAG7C,IAAI,KAAK,aAAa,OAAO;EAC7B,IAAI,IAAI,WAAW,IAAI,MAAM,MAAM;EACnC,IAAI,KAAK,UAAU,UAAa,IAAI,iBAAiB,KAAK,KAAK,MAAM,OAAO;EAC5E,IAAI,IAAI,YAAY,UAAa,IAAI,QAAQ,KAAK,EAAE,KAAK,GAAG;EAC5D,IAAI,aAAa,UAAa,SAAS,IAAI,GAAG;EAE9C,MAAM,QAAQ,SAAS,KAAK,KAAK;EACjC,IAAI,UAAU,MAAM;EACpB,MAAM,OAAO,IAAI,OAAO,IAAI;EAC5B,MAAM,UAAU,IAAI,gBAAgB,IAAI;EACxC,MAAM,YAAY,cAAc,IAAI,IAAI,iBAAiB,MAAM,IAAI,UAAU,IAAI,CAAC,IAAI;EACtF,IAAI,CAAC,QAAQ,MAAM,MAAM,SAAS,WAAW,OAAO,WAAW,SAAS,OAAO,GAAG;EAClF,IAAI,KAAK,KAAK,EAAE;CAClB;CAEA,OAAO;AACT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { planeMatrix, planeToPlane } from "../core/viewport/parallax.js";
|
|
2
2
|
import { mat3, transformPath } from "@weasel-js/geom";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/canvas/planeClips.ts
|
|
5
5
|
/**
|
|
6
6
|
* Carries a container's clip into the world a descendant is drawn or picked
|
|
7
7
|
* in. A clip is built from the container's pose, so it is in the container's
|
|
@@ -41,6 +41,5 @@ function pathFromPlane(path, m) {
|
|
|
41
41
|
return back === null ? path : transformPath(path, planeMatrix(back));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
//#endregion
|
|
45
44
|
export { clipCarrier, pathFromPlane, planeTransform };
|
|
46
45
|
//# sourceMappingURL=planeClips.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mat3, poseFrameOf, rotateAboutPoint } from "@weasel-js/geom";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/canvas/poseRotation.ts
|
|
4
4
|
/** Rotation by `rotation` about `(cx, cy)` as the render-tree's column-major
|
|
5
5
|
* 3×3 affine. */
|
|
6
6
|
function rotationMatrixAbout(cx, cy, rotation) {
|
|
@@ -26,6 +26,5 @@ function wrapWithPoseRotation(cmds, pose) {
|
|
|
26
26
|
}];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
//#endregion
|
|
30
29
|
export { rotateAroundAABBCenter, rotationMatrixAbout, wrapWithPoseRotation };
|
|
31
30
|
//# sourceMappingURL=poseRotation.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isPlainObject } from "@weasel-js/registry";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/canvas/publishPreviews.ts
|
|
4
4
|
/**
|
|
5
5
|
* `publishPreviews` — what in-flight handles propose, published to the scene.
|
|
6
6
|
*
|
|
@@ -92,6 +92,5 @@ function collect(handle, frame, exists, dataFor) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//#endregion
|
|
96
95
|
export { publishPreviews };
|
|
97
96
|
//# sourceMappingURL=publishPreviews.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/canvas/rasterReadback.ts
|
|
2
2
|
/** A GL readback (bottom-up, premultiplied RGBA) as image bytes: top-down
|
|
3
3
|
* rows of straight RGBA. */
|
|
4
4
|
function straightTopDown(raw, width, height) {
|
|
@@ -25,6 +25,5 @@ function unpremultiply(data) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
//#endregion
|
|
29
28
|
export { straightTopDown };
|
|
30
29
|
//# sourceMappingURL=rasterReadback.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/canvas/rasterToPng.ts
|
|
2
2
|
/** Encode a raster as a PNG `Blob` — for download, upload, or the clipboard.
|
|
3
3
|
* Needs a 2D canvas: `OffscreenCanvas` where there is one, else the DOM's. */
|
|
4
4
|
async function rasterToPng(image) {
|
|
@@ -22,6 +22,5 @@ async function rasterToPng(image) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
//#endregion
|
|
26
25
|
export { rasterToPng };
|
|
27
26
|
//# sourceMappingURL=rasterToPng.js.map
|
|
@@ -2,7 +2,7 @@ import { renderSceneToPixels } from "./renderSceneToPixels.js";
|
|
|
2
2
|
import { buildDebugOverlayCommands } from "../debug/createDebugOverlayLayer.js";
|
|
3
3
|
import { mat3 } from "@weasel-js/geom";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// src/canvas/renderDebugSnapshot.ts
|
|
6
6
|
/**
|
|
7
7
|
* `renderDebugSnapshot` — the canvas as its debug overlay showed it, scene and
|
|
8
8
|
* overlay in one raster, for attaching to a bug report.
|
|
@@ -45,6 +45,5 @@ function debugSnapshotArgs(args) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
//#endregion
|
|
49
48
|
export { debugSnapshotArgs, renderDebugSnapshot };
|
|
50
49
|
//# sourceMappingURL=renderDebugSnapshot.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/canvas/renderNeeds.ts
|
|
2
2
|
/**
|
|
3
3
|
* The fonts and paint kinds `commands` draw with, read off the commands
|
|
4
4
|
* themselves: text runs, path fills and strokes, run fills and strokes, and
|
|
@@ -35,6 +35,5 @@ function renderNeeds(commands) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//#endregion
|
|
39
38
|
export { renderNeeds };
|
|
40
39
|
//# sourceMappingURL=renderNeeds.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { sceneLayerKey } from "./sceneLayerPaint.js";
|
|
2
|
-
import { CANVAS_CONTEXT_ATTRIBUTES } from "./contextAttributes.js";
|
|
3
|
-
import { annotationLayerIds } from "../core/scene/annotation.js";
|
|
4
|
-
import { defaultDrawOne } from "./defaultDrawOne.js";
|
|
5
1
|
import { buildSceneViewCommands } from "./sceneViewRender.js";
|
|
2
|
+
import { defaultDrawOne } from "./defaultDrawOne.js";
|
|
6
3
|
import { renderNeeds } from "./renderNeeds.js";
|
|
7
4
|
import { straightTopDown } from "./rasterReadback.js";
|
|
5
|
+
import { annotationLayerIds } from "../core/scene/annotation.js";
|
|
6
|
+
import { sceneLayerKey } from "./sceneLayerPaint.js";
|
|
7
|
+
import { CANVAS_CONTEXT_ATTRIBUTES } from "./contextAttributes.js";
|
|
8
8
|
import { WeaselRenderer, isPaintKindKnown, warmPaintKinds } from "@weasel-js/renderer";
|
|
9
9
|
import { viewToMat3 as viewToMat3$1 } from "@weasel-js/geom";
|
|
10
10
|
import { warmFonts } from "@weasel-js/font";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// src/canvas/renderSceneToPixels.ts
|
|
13
13
|
/**
|
|
14
14
|
* `renderSceneToPixels` — headless rasterization of a scene-space rect to raw RGBA pixels at an
|
|
15
15
|
* explicit, per-axis scale. It drives the same WebGL2 pipeline (`WeaselRenderer` +
|
|
@@ -237,6 +237,5 @@ function renderSceneToPixels(args) {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
//#endregion
|
|
241
240
|
export { createRasterSession, planPixelRender, renderSceneToPixels, warmRender };
|
|
242
241
|
//# sourceMappingURL=renderSceneToPixels.js.map
|
|
@@ -1,18 +1,48 @@
|
|
|
1
|
-
import { composeWorldPose, composesFrames, frameAtOrAbove, memoizeCompose } from "../core/scene/composeFrame.js";
|
|
2
1
|
import { definesFrame, effectivePose } from "../core/scene/effectivePose.js";
|
|
3
|
-
import {
|
|
4
|
-
import { scrolledFrames } from "../core/scene/scrollOffsets.js";
|
|
5
|
-
import { AUTO_POSE_DESCRIPTOR } from "../core/geometry/autoPoseDescriptor.js";
|
|
2
|
+
import { sceneMutators } from "../core/scene/sceneMutators.js";
|
|
6
3
|
import { asNodeId } from "../core/scene/types.js";
|
|
7
4
|
import { nodeGates } from "../core/scene/nodeGates.js";
|
|
8
|
-
import { sceneMutators } from "../core/scene/sceneMutators.js";
|
|
9
5
|
import { applyOpsTo } from "../core/applyOps.js";
|
|
10
6
|
import { hitTestArea, hitTestLassoPolygon, regionPickOptions } from "./deps/hitTestArea.js";
|
|
7
|
+
import { poseDescriptorForNode, translatePoseViaDescriptor, visualBoundsViaDescriptor } from "../core/geometry/poseDescriptor.js";
|
|
8
|
+
import { AUTO_POSE_DESCRIPTOR } from "../core/geometry/autoPoseDescriptor.js";
|
|
9
|
+
import { composeChildFrame, composeWorldPose, composesFrames, frameAtOrAbove, memoizeCompose } from "../core/scene/composeFrame.js";
|
|
10
|
+
import { scrolledFrames } from "../core/scene/scrollOffsets.js";
|
|
11
11
|
import { insertSite } from "./deps/insertSite.js";
|
|
12
12
|
import { scenePoseTarget } from "../animation/poseTarget.js";
|
|
13
13
|
import { pasteSceneSnapshot, snapshotSceneSelection } from "./sceneClipboard.js";
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// src/canvas/sceneAdapter.ts
|
|
16
|
+
/** A scene adapter's pose reads: the stored pose, the world pose, and the fold between them. */
|
|
17
|
+
function scenePoseReads(scene) {
|
|
18
|
+
const composes = composesFrames(scene.composition);
|
|
19
|
+
const compose = memoizeCompose(scene.composition.compose);
|
|
20
|
+
const childFrame = composes ? scrolledFrames(scene) : void 0;
|
|
21
|
+
const reads = {
|
|
22
|
+
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
|
|
23
|
+
getPose(id) {
|
|
24
|
+
const n = scene.get(asNodeId(id));
|
|
25
|
+
if (!n) throw new Error(`sceneToAdapter: unknown node "${id}"`);
|
|
26
|
+
return effectivePose(scene, n);
|
|
27
|
+
},
|
|
28
|
+
definesFrame(id) {
|
|
29
|
+
const n = scene.get(asNodeId(id));
|
|
30
|
+
return n === void 0 || definesFrame(n);
|
|
31
|
+
},
|
|
32
|
+
...childFrame ? { childFrame } : {}
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
getPose: reads.getPose,
|
|
36
|
+
definesFrame: reads.definesFrame,
|
|
37
|
+
getWorldPose: (id) => composes ? composeWorldPose(reads, id, compose) : reads.getPose(id),
|
|
38
|
+
worldOfStored(id, stored) {
|
|
39
|
+
const frame = composes ? frameAtOrAbove(reads, reads.getParent(id)) : null;
|
|
40
|
+
return frame === null ? stored : compose(composeChildFrame(reads, frame, compose), stored);
|
|
41
|
+
},
|
|
42
|
+
...composes ? { composePose: compose } : {},
|
|
43
|
+
...childFrame ? { childFrame } : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
16
46
|
/** Derive the adapter for a scene. Reads go straight to the scene; writes are
|
|
17
47
|
* ops applied through its history, so anything done through the adapter is
|
|
18
48
|
* undoable. A container's pose is a frame its children are stored in, so the
|
|
@@ -24,10 +54,6 @@ function sceneToAdapter(scene, options = {}) {
|
|
|
24
54
|
const d = options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
25
55
|
const poseBounds = (p, node) => visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));
|
|
26
56
|
const translate = (p, dx, dy, node) => translatePoseViaDescriptor(p, dx, dy, node === void 0 ? d : poseDescriptorForNode(d, node));
|
|
27
|
-
const composition = scene.composition;
|
|
28
|
-
const composes = composesFrames(composition);
|
|
29
|
-
const compose = memoizeCompose(composition.compose);
|
|
30
|
-
const childFrame = composes ? scrolledFrames(scene) : void 0;
|
|
31
57
|
const pickOpts = {};
|
|
32
58
|
const pickOptsFor = (view) => view ? {
|
|
33
59
|
...pickOpts,
|
|
@@ -47,22 +73,7 @@ function sceneToAdapter(scene, options = {}) {
|
|
|
47
73
|
const gates = nodeGates(scene);
|
|
48
74
|
return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));
|
|
49
75
|
},
|
|
50
|
-
|
|
51
|
-
const nid = asNodeId(id);
|
|
52
|
-
const n = scene.get(nid);
|
|
53
|
-
if (!n) throw new Error(`sceneToAdapter: unknown node "${id}"`);
|
|
54
|
-
return effectivePose(scene, n);
|
|
55
|
-
},
|
|
56
|
-
definesFrame(id) {
|
|
57
|
-
const n = scene.get(asNodeId(id));
|
|
58
|
-
return n === void 0 || definesFrame(n);
|
|
59
|
-
},
|
|
60
|
-
getWorldPose(id) {
|
|
61
|
-
if (!composes) return adapter.getPose(id);
|
|
62
|
-
return composeWorldPose(adapter, id, compose);
|
|
63
|
-
},
|
|
64
|
-
...composes ? { composePose: compose } : {},
|
|
65
|
-
...childFrame ? { childFrame } : {},
|
|
76
|
+
...scenePoseReads(scene),
|
|
66
77
|
poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),
|
|
67
78
|
getLayers: () => scene.layers.map((l) => ({
|
|
68
79
|
id: l.id,
|
|
@@ -112,6 +123,5 @@ function sceneToAdapter(scene, options = {}) {
|
|
|
112
123
|
return adapter;
|
|
113
124
|
}
|
|
114
125
|
|
|
115
|
-
//#endregion
|
|
116
126
|
export { sceneToAdapter };
|
|
117
127
|
//# sourceMappingURL=sceneAdapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composesFrames, composeWorldPose, frameAtOrAbove } from 'core/scene/composePose';\nimport { memoizeCompose } from 'core/scene/composeFrame';\nimport { scrolledFrames } from 'core/scene/scrollOffsets';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n /** The frame a container gives its children, scrolled by the scene's\n * `scroll` offset — see `PoseAdapter.childFrame`. Present only when the\n * composition can scroll. */\n childFrame?(id: string, world: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable. A container's pose is a frame its children are stored in, so the\n * one pose written moves its contents. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node));\n\n const composition = scene.composition;\n const composes = composesFrames(composition);\n const compose = memoizeCompose(composition.compose);\n const childFrame = composes ? scrolledFrames(scene) : undefined;\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n getPose(id) {\n const nid = asNodeId(id);\n const n = scene.get(nid);\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n getWorldPose(id) {\n if (!composes) return adapter.getPose(id);\n return composeWorldPose(adapter, id, compose);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: compose } : {}),\n ...(childFrame ? { childFrame } : {}),\n poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composeChildFrame, composesFrames, composeWorldPose, frameAtOrAbove, type PoseAdapter } from 'core/scene/composePose';\nimport { memoizeCompose } from 'core/scene/composeFrame';\nimport { scrolledFrames } from 'core/scene/scrollOffsets';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** A scene adapter's pose reads: the stored pose, the world pose, and the fold between them. */\nfunction scenePoseReads<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>) {\n const composes = composesFrames(scene.composition);\n const compose = memoizeCompose(scene.composition.compose);\n const childFrame = composes ? scrolledFrames(scene) : undefined;\n const reads: PoseAdapter<TPose> & { definesFrame(id: string): boolean } = {\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n getPose(id) {\n const n = scene.get(asNodeId(id));\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n ...(childFrame ? { childFrame } : {}),\n };\n return {\n getPose: reads.getPose,\n definesFrame: reads.definesFrame,\n getWorldPose: (id: string): TPose => (composes ? composeWorldPose(reads, id, compose) : reads.getPose(id)),\n worldOfStored(id: string, stored: TPose): TPose {\n const frame = composes ? frameAtOrAbove(reads, reads.getParent(id)) : null;\n return frame === null ? stored : compose(composeChildFrame(reads, frame, compose), stored);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: compose } : {}),\n ...(childFrame ? { childFrame } : {}),\n };\n}\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Where `id` is drawn if it stores `stored`: `stored` folded into the\n * frame of its parent's world pose. */\n worldOfStored(id: string, stored: TPose): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n /** The frame a container gives its children, scrolled by the scene's\n * `scroll` offset — see `PoseAdapter.childFrame`. Present only when the\n * composition can scroll. */\n childFrame?(id: string, world: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable. A container's pose is a frame its children are stored in, so the\n * one pose written moves its contents. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node));\n\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n ...scenePoseReads(scene),\n poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
import { asNodeId } from "../core/scene/types.js";
|
|
2
|
+
import { hasChildren } from "../core/scene/treeShape.js";
|
|
1
3
|
import { sourcesOf } from "../core/scene/dependents.js";
|
|
2
4
|
import { definesFrame } from "../core/scene/effectivePose.js";
|
|
3
|
-
import { asNodeId } from "../core/scene/types.js";
|
|
4
5
|
import { NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf } from "../core/scene/nodeFnFields.js";
|
|
5
6
|
import { statedFields } from "../core/scene/statedFields.js";
|
|
6
|
-
import {
|
|
7
|
+
import { holderOf, outermost } from "@weasel-js/select";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
// src/canvas/sceneClipboard.ts
|
|
10
|
+
/**
|
|
11
|
+
* The scene adapter's clipboard seam: what a copy captures and what a paste
|
|
12
|
+
* mints from it.
|
|
13
|
+
*
|
|
14
|
+
* `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never
|
|
15
|
+
* touches the scene. Insertion happens through the caller's InsertOps (one per
|
|
16
|
+
* created node, parents before children).
|
|
17
|
+
*
|
|
18
|
+
* A snapshot item carries its node's function fields twice: live, for a paste
|
|
19
|
+
* in the same session, and as the scene's registry keys, for a snapshot that
|
|
20
|
+
* crossed JSON on the way back.
|
|
21
|
+
*/
|
|
9
22
|
function copyField(value) {
|
|
10
23
|
if (Array.isArray(value)) return [...value];
|
|
11
24
|
if (value !== null && typeof value === "object") return { ...value };
|
|
@@ -42,7 +55,8 @@ function snapshotSceneSelection(env, ids) {
|
|
|
42
55
|
});
|
|
43
56
|
if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
|
|
44
57
|
};
|
|
45
|
-
const
|
|
58
|
+
const parentOf = (id) => scene.get(asNodeId(id))?.parent;
|
|
59
|
+
const covered = (id) => holderOf(idSet, id, parentOf) !== null;
|
|
46
60
|
const carrying = /* @__PURE__ */ new Set();
|
|
47
61
|
const carrySources = (id) => {
|
|
48
62
|
if (carrying.has(id)) return;
|
|
@@ -56,8 +70,7 @@ function snapshotSceneSelection(env, ids) {
|
|
|
56
70
|
}
|
|
57
71
|
if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);
|
|
58
72
|
};
|
|
59
|
-
for (const id of ids) {
|
|
60
|
-
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
73
|
+
for (const id of outermost(ids, parentOf)) {
|
|
61
74
|
carrySources(id);
|
|
62
75
|
capture(id, true);
|
|
63
76
|
}
|
|
@@ -176,6 +189,5 @@ function pasteSceneSnapshot(env, clipboard, offset, ctx) {
|
|
|
176
189
|
}));
|
|
177
190
|
}
|
|
178
191
|
|
|
179
|
-
//#endregion
|
|
180
192
|
export { pasteSceneSnapshot, snapshotSceneSelection };
|
|
181
193
|
//# sourceMappingURL=sceneClipboard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n const covered = (id: string): boolean =>\n idSet.has(id) || scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a));\n // A hidden node cannot be selected, so one that a copied node derives from\n // comes with it, ahead of it: without it the paste would leave the copy out.\n const carrying = new Set<string>();\n const carrySources = (id: string): void => {\n if (carrying.has(id)) return;\n carrying.add(id);\n const n = scene.get(asNodeId(id));\n if (!n) return;\n for (const source of sourcesOf(n)) {\n if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;\n carrySources(source);\n capture(source, true);\n }\n if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);\n };\n for (const id of ids) {\n // Dedupe: an id whose ancestor is also selected is already covered\n // by that ancestor's subtree walk.\n if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;\n carrySources(id);\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // A node inside a pasted frame rides it, and offsetting it as well would\n // move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.x - minX;\n dy = ctx.dropPoint.y - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport { holderOf, outermost } from '@weasel-js/select';\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n const parentOf = (id: string) => scene.get(asNodeId(id))?.parent;\n const covered = (id: string): boolean => holderOf(idSet, id, parentOf) !== null;\n // A hidden node cannot be selected, so one that a copied node derives from\n // comes with it, ahead of it: without it the paste would leave the copy out.\n const carrying = new Set<string>();\n const carrySources = (id: string): void => {\n if (carrying.has(id)) return;\n carrying.add(id);\n const n = scene.get(asNodeId(id));\n if (!n) return;\n for (const source of sourcesOf(n)) {\n if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;\n carrySources(source);\n capture(source, true);\n }\n if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);\n };\n // An id an ancestor holds comes with that ancestor's subtree walk.\n for (const id of outermost(ids, parentOf)) {\n carrySources(id);\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // A node inside a pasted frame rides it, and offsetting it as well would\n // move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.x - minX;\n dy = ctx.dropPoint.y - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { paintedLayers } from "../core/layers/render.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/canvas/sceneLayerPaint.ts
|
|
4
4
|
/**
|
|
5
5
|
* Which scene layers a view paints.
|
|
6
6
|
*
|
|
@@ -61,6 +61,5 @@ function paintedSceneLayers(layers, view) {
|
|
|
61
61
|
return (layerOrder ? layerOrder.map((key) => layers.find((l) => sceneLayerKey(l.id) === key)).filter((l) => l !== void 0) : layers).filter((l) => layerVisibility?.[sceneLayerKey(l.id)] !== false);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
//#endregion
|
|
65
64
|
export { createSceneLayerGate, paintedSceneLayers, sceneLayerKey, viewPaintedLayers };
|
|
66
65
|
//# sourceMappingURL=sceneLayerPaint.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { asNodeId } from "../core/scene/types.js";
|
|
2
2
|
import { sceneDerivedPathOf, showsComputedPath } from "./derivedPath.js";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/canvas/scenePickReads.ts
|
|
5
5
|
/** Shared, never mutated: most scenes are flat and every node returns it. */
|
|
6
6
|
var EMPTY_PARENTS = [];
|
|
7
7
|
/** `(node) => its ancestors, innermost first`, stopping at a cycle or a missing parent. */
|
|
@@ -32,6 +32,5 @@ function sceneShownPathOf(scene) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
//#endregion
|
|
36
35
|
export { sceneParentsOf, sceneShownPathOf };
|
|
37
36
|
//# sourceMappingURL=scenePickReads.js.map
|