@weasel-js/core 1.13.0 → 1.14.0
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 +300 -0
- package/README.md +8 -12
- package/dist/affordances/hitAffordanceRegions.js +1 -1
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/animation/clipTargets.js +1 -1
- package/dist/animation/clipTargets.js.map +1 -1
- package/dist/animation/poseTarget.js +5 -32
- package/dist/animation/poseTarget.js.map +1 -1
- package/dist/{autoPoseDescriptor-CZ1eXbzY.d.ts → autoPoseDescriptor-C25QVb0W.d.ts} +1 -1
- package/dist/barrel/canvas.js +8 -8
- package/dist/barrel/chrome.js +9 -9
- package/dist/barrel/input.js +1 -1
- package/dist/barrel/interactions.js +3 -3
- package/dist/barrel/paint.js +2 -2
- package/dist/barrel/paths.js +19 -17
- package/dist/barrel/renderer.js +11 -7
- package/dist/barrel/routing.js +4 -1
- package/dist/barrel/scene.js +16 -12
- package/dist/barrel/text.js +5 -5
- package/dist/barrel/viewport.js +8 -8
- package/dist/canvas/Canvas.js +2 -2
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +20 -14
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/GestureReason.js +64 -0
- package/dist/canvas/GestureReason.js.map +1 -0
- package/dist/canvas/GestureReason.module.js +7 -0
- package/dist/canvas/GestureReason.module.js.map +1 -0
- package/dist/canvas/LeanSceneCanvas.js +3 -1
- package/dist/canvas/LeanSceneCanvas.js.map +1 -1
- package/dist/canvas/NodeShape.js +2 -0
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/PickHud.js +7 -3
- package/dist/canvas/PickHud.js.map +1 -1
- package/dist/canvas/SceneCanvas/CanvasKitProviders.js +1 -1
- package/dist/canvas/SceneCanvas/CanvasKitProviders.js.map +1 -1
- package/dist/canvas/SceneCanvas/DevHuds.js +2 -2
- package/dist/canvas/SceneCanvas/DevHuds.js.map +1 -1
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +4 -2
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +3 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/brushOverlay.js.map +1 -1
- package/dist/canvas/SceneCanvas/builtinToolSets.js +17 -9
- package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +75 -19
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +7 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultTextEditor.js +108 -0
- package/dist/canvas/SceneCanvas/defaultTextEditor.js.map +1 -0
- package/dist/canvas/SceneCanvas/describePresets.js +47 -0
- package/dist/canvas/SceneCanvas/describePresets.js.map +1 -0
- package/dist/canvas/SceneCanvas/features.js +57 -16
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/mergeLayers.js +10 -1
- package/dist/canvas/SceneCanvas/mergeLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/namedPresets.js +52 -24
- package/dist/canvas/SceneCanvas/namedPresets.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/arrange.js +35 -10
- package/dist/canvas/SceneCanvas/presets/arrange.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/baseline.js +9 -0
- package/dist/canvas/SceneCanvas/presets/baseline.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/chrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/composites.js +43 -0
- package/dist/canvas/SceneCanvas/presets/composites.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/edit.js +79 -26
- package/dist/canvas/SceneCanvas/presets/edit.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/ingest.js +1 -0
- package/dist/canvas/SceneCanvas/presets/ingest.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/paths.js +58 -16
- package/dist/canvas/SceneCanvas/presets/paths.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/select.js +4 -2
- package/dist/canvas/SceneCanvas/presets/select.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/tools.js +24 -0
- package/dist/canvas/SceneCanvas/presets/tools.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/transform.js +4 -0
- package/dist/canvas/SceneCanvas/presets/transform.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/view.js +3 -1
- package/dist/canvas/SceneCanvas/presets/view.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +1 -1
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/toolRegistry.js +5 -4
- package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +26 -10
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useContributionRoles.js +15 -2
- package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js +5 -4
- package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +1 -1
- package/dist/canvas/SceneCanvas/useNodeAtPoint.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +9 -8
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js +2 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +4 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneChrome.js +7 -4
- package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +9 -9
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +4 -7
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneRedrawSources.js +2 -1
- package/dist/canvas/SceneCanvas/useSceneRedrawSources.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +6 -6
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -2
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js +1 -1
- package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSurfaceViewInputs.js.map +1 -1
- package/dist/canvas/SceneCanvas/useToolKeybindings.js +2 -2
- package/dist/canvas/SceneCanvas/useToolKeybindings.js.map +1 -1
- package/dist/canvas/SceneCanvas/useViewportActions.js +5 -2
- package/dist/canvas/SceneCanvas/useViewportActions.js.map +1 -1
- package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +22 -11
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +1 -0
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/ViewMarker.js.map +1 -1
- package/dist/canvas/accentColor.js +14 -1
- package/dist/canvas/accentColor.js.map +1 -1
- package/dist/canvas/affordanceAt.js +2 -2
- package/dist/canvas/affordanceAt.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +5 -3
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/deps/editAnchors.js +4 -4
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +14 -5
- package/dist/canvas/deps/enteredGroup.js.map +1 -1
- package/dist/canvas/deps/ingestion.js +5 -1
- package/dist/canvas/deps/ingestion.js.map +1 -1
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/insertSite.js +5 -3
- package/dist/canvas/deps/insertSite.js.map +1 -1
- package/dist/canvas/deps/nodeAtPoint.js +1 -12
- package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +3 -3
- package/dist/canvas/deps/pathEditCommits.js.map +1 -1
- package/dist/canvas/deps/slice.js +16 -2
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/deps/snap.js +5 -5
- package/dist/canvas/deps/snap.js.map +1 -1
- package/dist/canvas/deps/textEdit.js +5 -6
- package/dist/canvas/deps/textEdit.js.map +1 -1
- package/dist/canvas/derivedPath.js +8 -3
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/getNodeAtPoint.js +2 -2
- package/dist/canvas/getNodeAtPoint.js.map +1 -1
- package/dist/canvas/insertPreviewExtent.js.map +1 -1
- package/dist/canvas/layerSlots.js.map +1 -1
- package/dist/canvas/nodeShapes/measurePainter.js +1 -1
- package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
- package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
- package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
- package/dist/canvas/overlayRoleStrokes.js +6 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
- package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +10 -19
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +17 -4
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +3 -2
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasPaint.js +10 -4
- package/dist/canvas/useCanvasPaint.js.map +1 -1
- package/dist/canvas/useHitObserver.js +1 -1
- package/dist/canvas/useHitObserver.js.map +1 -1
- package/dist/canvas/useViewAim.js +1 -1
- package/dist/canvas/useViewAim.js.map +1 -1
- package/dist/canvas/useViewHelpers.js +8 -5
- package/dist/canvas/useViewHelpers.js.map +1 -1
- package/dist/canvas/useViewNesting.js +39 -12
- package/dist/canvas/useViewNesting.js.map +1 -1
- package/dist/canvas/viewInputs.js.map +1 -1
- package/dist/canvas/viewMarkers.js +2 -2
- package/dist/canvas/viewMarkers.js.map +1 -1
- package/dist/canvas/viewRegistry.js +2 -2
- package/dist/canvas/viewRegistry.js.map +1 -1
- package/dist/canvas/viewSnap.js +67 -0
- package/dist/canvas/viewSnap.js.map +1 -0
- package/dist/clipboard.d.ts +6 -12
- package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
- package/dist/clone.d.ts +5 -5
- package/dist/core/geometry/booleanOfPaths.js +23 -0
- package/dist/core/geometry/booleanOfPaths.js.map +1 -0
- package/dist/core/geometry/pathPoseDescriptor.js +12 -9
- package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
- package/dist/core/geometry/poseDescriptor.js +12 -3
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/targetFrame.js +2 -13
- package/dist/core/geometry/targetFrame.js.map +1 -1
- package/dist/core/layers/render.js +2 -1
- package/dist/core/layers/render.js.map +1 -1
- package/dist/core/layers/retain.js +5 -3
- package/dist/core/layers/retain.js.map +1 -1
- package/dist/core/ops/setPath.js +3 -0
- package/dist/core/ops/setPath.js.map +1 -1
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/paint/paintKindEntry.js.map +1 -1
- package/dist/core/paint/paintKinds.js +8 -1
- package/dist/core/paint/paintKinds.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/booleanGroup.js +33 -0
- package/dist/core/scene/booleanGroup.js.map +1 -0
- package/dist/core/scene/booleanOfChildren.js +53 -0
- package/dist/core/scene/booleanOfChildren.js.map +1 -0
- package/dist/core/scene/carryDescendants.js +18 -6
- package/dist/core/scene/carryDescendants.js.map +1 -1
- package/dist/core/scene/composeFrame.js +42 -5
- package/dist/core/scene/composeFrame.js.map +1 -1
- package/dist/core/scene/composePose.js +35 -16
- package/dist/core/scene/composePose.js.map +1 -1
- package/dist/core/scene/depMemo.js +36 -18
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/derivedParts.js +2 -23
- package/dist/core/scene/derivedParts.js.map +1 -1
- package/dist/core/scene/effectivePose.js +10 -0
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/historyForm.js +5 -4
- package/dist/core/scene/historyForm.js.map +1 -1
- package/dist/core/scene/kitOps.js +5 -71
- package/dist/core/scene/kitOps.js.map +1 -1
- package/dist/core/scene/kitRegistry.js +5 -0
- package/dist/core/scene/kitRegistry.js.map +1 -1
- package/dist/core/scene/layerOps.js +82 -0
- package/dist/core/scene/layerOps.js.map +1 -0
- package/dist/core/scene/layoutPass.js +14 -12
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/lockGuard.js +9 -3
- package/dist/core/scene/lockGuard.js.map +1 -1
- package/dist/core/scene/nestedHit.js +5 -5
- package/dist/core/scene/nestedHit.js.map +1 -1
- package/dist/core/scene/nodeFlags.js +8 -2
- package/dist/core/scene/nodeFlags.js.map +1 -1
- package/dist/core/scene/nodeGates.js +31 -2
- package/dist/core/scene/nodeGates.js.map +1 -1
- package/dist/core/scene/pathEffects/effects.js +22 -3
- package/dist/core/scene/pathEffects/effects.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlong.js +16 -3
- package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
- package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
- package/dist/core/scene/reconcileSpecs.js +1 -1
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/scene.js +11 -5
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneEdits.js +5 -2
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/scenePoseAdapter.js +10 -1
- package/dist/core/scene/scenePoseAdapter.js.map +1 -1
- package/dist/core/scene/scrollOffsets.js +93 -0
- package/dist/core/scene/scrollOffsets.js.map +1 -0
- package/dist/core/scene/serialization.js +15 -5
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/statedFields.js +2 -1
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/strategyView.js +35 -0
- package/dist/core/scene/strategyView.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/useScene.js +2 -0
- package/dist/core/scene/useScene.js.map +1 -1
- package/dist/core/scene/worldSave.js +87 -0
- package/dist/core/scene/worldSave.js.map +1 -0
- package/dist/core/selection/selectionTarget.js +1 -1
- package/dist/core/selection/selectionTarget.js.map +1 -1
- package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
- package/dist/{depSchema-CpIQzKWQ.d.ts → depSchema-C3BwxitB.d.ts} +119 -99
- package/dist/devtools-protocol.d.ts +2 -2
- package/dist/devtools.d.ts +2 -2
- package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
- package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
- package/dist/domains/chrome-caps/conditions.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
- package/dist/domains/chrome-caps/defaults.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
- package/dist/domains/chrome-caps/resolve.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
- package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
- package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
- package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
- package/dist/domains/ingestion/contentHandlers.js.map +1 -0
- package/dist/domains/ingestion/ghost.js +56 -0
- package/dist/domains/ingestion/ghost.js.map +1 -0
- package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
- package/dist/domains/ingestion/imageHandler.js.map +1 -0
- package/dist/domains/ingestion/kitHandlers.js +22 -0
- package/dist/domains/ingestion/kitHandlers.js.map +1 -0
- package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
- package/dist/domains/ingestion/openFilePicker.js.map +1 -0
- package/dist/{features → domains}/ingestion/placement.js +1 -1
- package/dist/domains/ingestion/placement.js.map +1 -0
- package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
- package/dist/domains/ingestion/svgHandler.js.map +1 -0
- package/dist/{features → domains}/ingestion/textHandler.js +9 -7
- package/dist/domains/ingestion/textHandler.js.map +1 -0
- package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
- package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
- package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
- package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
- package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
- package/dist/domains/longPress/longPressFeedback.js.map +1 -0
- package/dist/{features → domains}/measure/actionIds.js +1 -1
- package/dist/domains/measure/actionIds.js.map +1 -0
- package/dist/{features → domains}/measure/measureCommands.js +1 -1
- package/dist/domains/measure/measureCommands.js.map +1 -0
- package/dist/{features → domains}/measure/measureContribution.js +1 -1
- package/dist/domains/measure/measureContribution.js.map +1 -0
- package/dist/{features → domains}/measure/measureEdits.js +1 -1
- package/dist/domains/measure/measureEdits.js.map +1 -0
- package/dist/{features → domains}/measure/measureGestures.js +4 -7
- package/dist/domains/measure/measureGestures.js.map +1 -0
- package/dist/{features → domains}/measure/measureHit.js +1 -1
- package/dist/domains/measure/measureHit.js.map +1 -0
- package/dist/{features → domains}/measure/measureLayer.js +1 -1
- package/dist/domains/measure/measureLayer.js.map +1 -0
- package/dist/{features → domains}/measure/measurement.js +1 -1
- package/dist/domains/measure/measurement.js.map +1 -0
- package/dist/{features → domains}/measure/measurements.js +2 -2
- package/dist/domains/measure/measurements.js.map +1 -0
- package/dist/{features → domains}/modes/modeLayers.js +1 -1
- package/dist/domains/modes/modeLayers.js.map +1 -0
- package/dist/{features → domains}/modes/modeTriggers.js +3 -6
- package/dist/domains/modes/modeTriggers.js.map +1 -0
- package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
- package/dist/domains/pathEffects/effectSources.js.map +1 -0
- package/dist/{features → domains}/pathEffects/index.js +1 -1
- package/dist/domains/pathEffects/index.js.map +1 -0
- package/dist/{features → domains}/pathEffects/readSource.js +1 -1
- package/dist/domains/pathEffects/readSource.js.map +1 -0
- package/dist/{features → domains}/pathEffects/textItems.js +11 -2
- package/dist/domains/pathEffects/textItems.js.map +1 -0
- package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
- package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
- package/dist/{features → domains}/paths/anchorData.js +1 -1
- package/dist/domains/paths/anchorData.js.map +1 -0
- package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
- package/dist/domains/paths/anchorMarkers.js.map +1 -0
- package/dist/{features → domains}/paths/editablePath.js +1 -1
- package/dist/domains/paths/editablePath.js.map +1 -0
- package/dist/{features → domains}/paths/index.js +3 -3
- package/dist/{features → domains}/paths/originProjection.js +1 -1
- package/dist/domains/paths/originProjection.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
- package/dist/domains/paths/pathEditExtension.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditState.js +1 -1
- package/dist/domains/paths/pathEditState.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
- package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
- package/dist/{features → domains}/paths/pathLayer.js +1 -1
- package/dist/domains/paths/pathLayer.js.map +1 -0
- package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
- package/dist/domains/paths/pathRepresentation.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
- package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
- package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
- package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
- package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
- package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
- package/dist/domains/paths/penPreviewLayer.js.map +1 -0
- package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
- package/dist/domains/paths/shapeGenerator.js.map +1 -0
- package/dist/{features → domains}/paths/strokeOutline.js +1 -1
- package/dist/domains/paths/strokeOutline.js.map +1 -0
- package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
- package/dist/domains/paths/useAnchorFields.js.map +1 -0
- package/dist/{features → domains}/paths/usePathEdit.js +1 -1
- package/dist/domains/paths/usePathEdit.js.map +1 -0
- package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
- package/dist/domains/poseRun/usePoseRun.js.map +1 -0
- package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
- package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
- package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
- package/dist/domains/selection/enteredGroupScope.js.map +1 -0
- package/dist/domains/selection/enteredOperands.js +29 -0
- package/dist/domains/selection/enteredOperands.js.map +1 -0
- package/dist/{features → domains}/selection/handleCommands.js +1 -1
- package/dist/domains/selection/handleCommands.js.map +1 -0
- package/dist/{features → domains}/selection/overlay.js +1 -1
- package/dist/domains/selection/overlay.js.map +1 -0
- package/dist/{features → domains}/selection/selectionPose.js +1 -1
- package/dist/domains/selection/selectionPose.js.map +1 -0
- package/dist/{features → domains}/snap/angle2d.js +1 -1
- package/dist/domains/snap/angle2d.js.map +1 -0
- package/dist/{features → domains}/snap/debugCandidates.js +1 -1
- package/dist/domains/snap/debugCandidates.js.map +1 -0
- package/dist/{features → domains}/snap/gather.js +1 -1
- package/dist/domains/snap/gather.js.map +1 -0
- package/dist/{features → domains}/snap/inputSubspace.js +1 -1
- package/dist/domains/snap/inputSubspace.js.map +1 -0
- package/dist/{features → domains}/snap/lazySession.js +1 -1
- package/dist/domains/snap/lazySession.js.map +1 -0
- package/dist/{features → domains}/snap/marks.js +1 -1
- package/dist/domains/snap/marks.js.map +1 -0
- package/dist/{features → domains}/snap/outcomeStore.js +1 -1
- package/dist/domains/snap/outcomeStore.js.map +1 -0
- package/dist/{features → domains}/snap/pointSnap.js +1 -1
- package/dist/domains/snap/pointSnap.js.map +1 -0
- package/dist/{features → domains}/snap/probes2d.js +1 -1
- package/dist/domains/snap/probes2d.js.map +1 -0
- package/dist/{features → domains}/snap/readoutDraw.js +1 -1
- package/dist/domains/snap/readoutDraw.js.map +1 -0
- package/dist/{features → domains}/snap/readoutLayer.js +1 -1
- package/dist/domains/snap/readoutLayer.js.map +1 -0
- package/dist/{features → domains}/snap/registry.js +1 -1
- package/dist/domains/snap/registry.js.map +1 -0
- package/dist/{features → domains}/snap/resizeBox.js +1 -1
- package/dist/domains/snap/resizeBox.js.map +1 -0
- package/dist/{features → domains}/snap/roles.js +1 -1
- package/dist/domains/snap/roles.js.map +1 -0
- package/dist/{features → domains}/snap/rotationSource.js +1 -1
- package/dist/domains/snap/rotationSource.js.map +1 -0
- package/dist/{features → domains}/snap/sceneReads.js +24 -3
- package/dist/domains/snap/sceneReads.js.map +1 -0
- package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
- package/dist/domains/snap/sceneSession2d.js.map +1 -0
- package/dist/{features → domains}/snap/screen.js +1 -1
- package/dist/domains/snap/screen.js.map +1 -0
- package/dist/{features → domains}/snap/session2d.js +3 -2
- package/dist/domains/snap/session2d.js.map +1 -0
- package/dist/{features → domains}/snap/spanSnap.js +1 -1
- package/dist/domains/snap/spanSnap.js.map +1 -0
- package/dist/{features → domains}/snap/stepAngles.js +1 -1
- package/dist/domains/snap/stepAngles.js.map +1 -0
- package/dist/{features → domains}/text/editingDrawOne.js +4 -4
- package/dist/domains/text/editingDrawOne.js.map +1 -0
- package/dist/domains/text/hostTextEditors.js +24 -0
- package/dist/domains/text/hostTextEditors.js.map +1 -0
- package/dist/{features → domains}/text/index.js +1 -1
- package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
- package/dist/domains/text/sceneTextDrafts.js.map +1 -0
- package/dist/{features → domains}/text/textHitAt.js +18 -2
- package/dist/domains/text/textHitAt.js.map +1 -0
- package/dist/{features → domains}/text/textLayer.js +1 -1
- package/dist/domains/text/textLayer.js.map +1 -0
- package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
- package/dist/domains/text/useSceneTextEdit.js.map +1 -0
- package/dist/{features → domains}/viewports/aim.js +1 -1
- package/dist/domains/viewports/aim.js.map +1 -0
- package/dist/{features → domains}/viewports/viewMarker.js +23 -5
- package/dist/domains/viewports/viewMarker.js.map +1 -0
- package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
- package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
- package/dist/{features → domains}/viewports/viewResolver.js +1 -1
- package/dist/domains/viewports/viewResolver.js.map +1 -0
- package/dist/{features → domains}/viewports/viewportLayer.js +1 -1
- package/dist/domains/viewports/viewportLayer.js.map +1 -0
- package/dist/icons.table.js +1 -1
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +106 -6
- package/dist/index.d.ts +1441 -1050
- package/dist/index.js +90 -77
- package/dist/interactions/actions/admitsChild.js +6 -2
- package/dist/interactions/actions/admitsChild.js.map +1 -1
- package/dist/interactions/actions/align/align.js +1 -4
- package/dist/interactions/actions/align/align.js.map +1 -1
- package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
- package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
- package/dist/interactions/actions/booleans/keymap.js +52 -0
- package/dist/interactions/actions/booleans/keymap.js.map +1 -0
- package/dist/interactions/actions/booleans/operands.js +1 -1
- package/dist/interactions/actions/booleans/pieces.js +4 -11
- package/dist/interactions/actions/booleans/pieces.js.map +1 -1
- package/dist/interactions/actions/clipboard/action.js +3 -7
- package/dist/interactions/actions/clipboard/action.js.map +1 -1
- package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
- package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
- package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/containerScroll.js +170 -0
- package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
- package/dist/interactions/actions/defaults/contextMenu.js +2 -5
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/delete.js +1 -1
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +1 -3
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterGroup.js +7 -12
- package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +12 -24
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +18 -10
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
- package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
- package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
- package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
- package/dist/interactions/actions/defaults/selectDeep.js +2 -2
- package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
- package/dist/interactions/actions/distribute/distribute.js.map +1 -1
- package/dist/interactions/actions/dropVerdict.js +9 -0
- package/dist/interactions/actions/dropVerdict.js.map +1 -0
- package/dist/interactions/actions/edit-anchors/action.js +2 -2
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
- package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +3 -2
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/geometryProjection.js +8 -3
- package/dist/interactions/actions/geometryProjection.js.map +1 -1
- package/dist/interactions/actions/gestureFrames.js +1 -0
- package/dist/interactions/actions/gestureFrames.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +6 -2
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/frame.js +1 -1
- package/dist/interactions/actions/insert/frame.js.map +1 -1
- package/dist/interactions/actions/insert/placeAction.js +6 -8
- package/dist/interactions/actions/insert/placeAction.js.map +1 -1
- package/dist/interactions/actions/insert/session.js +2 -2
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/action.js +6 -15
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
- package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
- package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/capture.js +4 -8
- package/dist/interactions/actions/move/capture.js.map +1 -1
- package/dist/interactions/actions/move/commit/index.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
- package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
- package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
- package/dist/interactions/actions/move/commit/reparent.js +19 -13
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/delta.js +51 -5
- package/dist/interactions/actions/move/delta.js.map +1 -1
- package/dist/interactions/actions/move/frame.js +8 -4
- package/dist/interactions/actions/move/frame.js.map +1 -1
- package/dist/interactions/actions/move/layout/container.js +19 -9
- package/dist/interactions/actions/move/layout/container.js.map +1 -1
- package/dist/interactions/actions/move/layout/dragged.js +2 -2
- package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
- package/dist/interactions/actions/move/layout/dropPass.js +36 -18
- package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +2 -2
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/layout/glide.js +5 -5
- package/dist/interactions/actions/move/layout/glide.js.map +1 -1
- package/dist/interactions/actions/move/layout/view.js +55 -0
- package/dist/interactions/actions/move/layout/view.js.map +1 -0
- package/dist/interactions/actions/move/refusal.js +52 -0
- package/dist/interactions/actions/move/refusal.js.map +1 -0
- package/dist/interactions/actions/move/session.js +9 -8
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/move/translate.js +10 -4
- package/dist/interactions/actions/move/translate.js.map +1 -1
- package/dist/interactions/actions/moveNodes.js +21 -5
- package/dist/interactions/actions/moveNodes.js.map +1 -1
- package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
- package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
- package/dist/interactions/actions/planeInput.js +14 -13
- package/dist/interactions/actions/planeInput.js.map +1 -1
- package/dist/interactions/actions/poseFrame.js +21 -8
- package/dist/interactions/actions/poseFrame.js.map +1 -1
- package/dist/interactions/actions/resize/action.js +1 -1
- package/dist/interactions/actions/resize/action.js.map +1 -1
- package/dist/interactions/actions/resize/aspect.js +1 -1
- package/dist/interactions/actions/resize/aspect.js.map +1 -1
- package/dist/interactions/actions/resize/commit.js +10 -1
- package/dist/interactions/actions/resize/commit.js.map +1 -1
- package/dist/interactions/actions/resize/frame.js +3 -2
- package/dist/interactions/actions/resize/frame.js.map +1 -1
- package/dist/interactions/actions/resize/held.js +44 -0
- package/dist/interactions/actions/resize/held.js.map +1 -0
- package/dist/interactions/actions/resize/session.js +3 -1
- package/dist/interactions/actions/resize/session.js.map +1 -1
- package/dist/interactions/actions/resolveOverlays.js +9 -0
- package/dist/interactions/actions/resolveOverlays.js.map +1 -1
- package/dist/interactions/actions/resolvePreviews.js +23 -7
- package/dist/interactions/actions/resolvePreviews.js.map +1 -1
- package/dist/interactions/actions/rotate/action.js +1 -1
- package/dist/interactions/actions/rotate/action.js.map +1 -1
- package/dist/interactions/actions/rotate/frame.js +1 -1
- package/dist/interactions/actions/rotate/frame.js.map +1 -1
- package/dist/interactions/actions/rotate/session.js +2 -2
- package/dist/interactions/actions/rotate/session.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +2 -4
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/spatialReference.js +4 -9
- package/dist/interactions/actions/spatialReference.js.map +1 -1
- package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
- package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
- package/dist/kitSettingsSchema.js +2 -2
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
- package/dist/math.d.ts +3 -3
- package/dist/move.d.ts +6 -6
- package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
- package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
- package/dist/resize.d.ts +6 -6
- package/dist/style.css +14 -0
- package/dist/test-seams.d.ts +11 -15
- package/dist/test-seams.js +1 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
- package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
- package/dist/tools/builtin/pen/penHandle.js +2 -2
- package/dist/tools/builtin/pen/penHandle.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +13 -19
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/select/groupEntry.js +1 -1
- package/dist/tools/builtin/select/groupEntry.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +1 -1
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +7 -7
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/slice/useSliceTool.js +5 -8
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +1 -1
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
- package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
- package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
- package/dist/util/color.js +12 -14
- package/dist/util/color.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -24
- package/dist/canvas/poseCascade.js +0 -65
- package/dist/canvas/poseCascade.js.map +0 -1
- package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
- package/dist/features/chrome-caps/conditions.js.map +0 -1
- package/dist/features/chrome-caps/defaults.js.map +0 -1
- package/dist/features/chrome-caps/resolve.js.map +0 -1
- package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
- package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
- package/dist/features/ingestion/contentHandlers.js.map +0 -1
- package/dist/features/ingestion/imageHandler.js.map +0 -1
- package/dist/features/ingestion/openFilePicker.js.map +0 -1
- package/dist/features/ingestion/placement.js.map +0 -1
- package/dist/features/ingestion/registerKitHandlers.js +0 -43
- package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
- package/dist/features/ingestion/svgHandler.js.map +0 -1
- package/dist/features/ingestion/textHandler.js.map +0 -1
- package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
- package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
- package/dist/features/longPress/longPressFeedback.js.map +0 -1
- package/dist/features/measure/actionIds.js.map +0 -1
- package/dist/features/measure/measureCommands.js.map +0 -1
- package/dist/features/measure/measureContribution.js.map +0 -1
- package/dist/features/measure/measureEdits.js.map +0 -1
- package/dist/features/measure/measureGestures.js.map +0 -1
- package/dist/features/measure/measureHit.js.map +0 -1
- package/dist/features/measure/measureLayer.js.map +0 -1
- package/dist/features/measure/measurement.js.map +0 -1
- package/dist/features/measure/measurements.js.map +0 -1
- package/dist/features/modes/modeLayers.js.map +0 -1
- package/dist/features/modes/modeTriggers.js.map +0 -1
- package/dist/features/pathEffects/effectSources.js.map +0 -1
- package/dist/features/pathEffects/index.js.map +0 -1
- package/dist/features/pathEffects/readSource.js.map +0 -1
- package/dist/features/pathEffects/textItems.js.map +0 -1
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
- package/dist/features/paths/anchorData.js.map +0 -1
- package/dist/features/paths/anchorMarkers.js.map +0 -1
- package/dist/features/paths/editablePath.js.map +0 -1
- package/dist/features/paths/originProjection.js.map +0 -1
- package/dist/features/paths/pathEditExtension.js.map +0 -1
- package/dist/features/paths/pathEditState.js.map +0 -1
- package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
- package/dist/features/paths/pathLayer.js.map +0 -1
- package/dist/features/paths/pathRepresentation.js.map +0 -1
- package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
- package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
- package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
- package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
- package/dist/features/paths/penPreviewLayer.js.map +0 -1
- package/dist/features/paths/shapeGenerator.js.map +0 -1
- package/dist/features/paths/strokeOutline.js.map +0 -1
- package/dist/features/paths/useAnchorFields.js.map +0 -1
- package/dist/features/paths/usePathEdit.js.map +0 -1
- package/dist/features/poseRun/usePoseRun.js.map +0 -1
- package/dist/features/selection/enteredGroupFrame.js.map +0 -1
- package/dist/features/selection/enteredGroupScope.js.map +0 -1
- package/dist/features/selection/handleCommands.js.map +0 -1
- package/dist/features/selection/overlay.js.map +0 -1
- package/dist/features/selection/selectionPose.js.map +0 -1
- package/dist/features/snap/angle2d.js.map +0 -1
- package/dist/features/snap/debugCandidates.js.map +0 -1
- package/dist/features/snap/gather.js.map +0 -1
- package/dist/features/snap/inputSubspace.js.map +0 -1
- package/dist/features/snap/lazySession.js.map +0 -1
- package/dist/features/snap/marks.js.map +0 -1
- package/dist/features/snap/outcomeStore.js.map +0 -1
- package/dist/features/snap/pointSnap.js.map +0 -1
- package/dist/features/snap/probes2d.js.map +0 -1
- package/dist/features/snap/readoutDraw.js.map +0 -1
- package/dist/features/snap/readoutLayer.js.map +0 -1
- package/dist/features/snap/registry.js.map +0 -1
- package/dist/features/snap/resizeBox.js.map +0 -1
- package/dist/features/snap/roles.js.map +0 -1
- package/dist/features/snap/rotationSource.js.map +0 -1
- package/dist/features/snap/sceneReads.js.map +0 -1
- package/dist/features/snap/sceneSession2d.js.map +0 -1
- package/dist/features/snap/screen.js.map +0 -1
- package/dist/features/snap/session2d.js.map +0 -1
- package/dist/features/snap/spanSnap.js.map +0 -1
- package/dist/features/snap/stepAngles.js.map +0 -1
- package/dist/features/text/editingDrawOne.js.map +0 -1
- package/dist/features/text/sceneTextDrafts.js.map +0 -1
- package/dist/features/text/textHitAt.js.map +0 -1
- package/dist/features/text/textLayer.js.map +0 -1
- package/dist/features/text/useSceneTextEdit.js.map +0 -1
- package/dist/features/viewports/aim.js.map +0 -1
- package/dist/features/viewports/viewMarker.js.map +0 -1
- package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
- package/dist/features/viewports/viewResolver.js.map +0 -1
- package/dist/features/viewports/viewportLayer.js.map +0 -1
- /package/dist/{features → domains}/chrome-caps/index.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,305 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8e2c8c8: <!-- bump-approved: minor: maintainer — 1.14.0 release requested 2026-10-29 -->
|
|
8
|
+
|
|
9
|
+
Three live path effects of one shape: Roughen, Zigzag, and Pucker & Bloat. geom adds the math as `roughenPath`, `zigzagPath`, and `puckerBloatPath`, each with its options type. Core adds `RoughenEffect`, `ZigzagEffect`, and `PuckerBloatEffect` to `PathEffect`: a path-effect node of one of these kinds has one source, `[shape]`, and paints it changed in its own fill and stroke. path-tools adds `roughenAction` (Alt+Shift+G), `zigzagAction` (Alt+Shift+Z), and `puckerBloatAction` (Alt+Shift+K) to `pathEffectsPreset`, each with a `path-tools:` glyph. Each makes one effect of every selected shape, just over it, and hides the shape while the effect shows it; Edit Path Effect, Expand to Paths, and Release work on them as on the others. This adds API.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- d8fbd3d: `Affordance` takes an optional `regionsAt(state, wx, wy, view)`: the regions a press at that world point can hit, in the order `regions` lists them. The affordance hit test reads it in place of `regions` when it is there, so an affordance with many regions can answer from an index. This adds API.
|
|
14
|
+
|
|
15
|
+
The diagram port layer and edge-label layer use it. A `ParticipantSource` may carry a `generation()`, and `sceneParticipants` gives it one from the scene's version and its overrides' generation, so the port layer hit-tests through an index of the ports' points instead of laying out every participant's ports on each press. The label layer and `labelUnder` index each scene's draggable labels against its version. Both answer exactly what they answered before; a source without a generation is tested the old way.
|
|
16
|
+
- b4cc0d7: `AimSource` takes an optional `subscribe(listener)`. A view marker whose source is an aim handed in by value now repaints when that source notifies; before, it repainted only when something else asked for a redraw.
|
|
17
|
+
- 575e470: A text node's Align bar has Start and End segments of their own, drawn as `core:textAlignStart` and `core:textAlignEnd`. A paragraph aligned to `start` or `end` lights that segment, where it used to light the edge it resolved to under a stated direction and nothing under `auto`, and clicking Start or End writes the reading-order value instead of an edge.
|
|
18
|
+
- 47656e3: Boolean groups can be made, switched and released from the kit's commands. Make Boolean Group (`pathfinder.booleanGroup`, in the `paths` preset and the Pathfinder group) puts the selection under a new union in the frontmost node's place, wearing that node's fill and stroke, and nothing moves on screen; `makeBooleanGroup(scene, ids, op?)` does the same from code. Ungroup now releases a boolean group too, its children taking its place. A boolean group routes as `'boolean'` (`booleanGroupRouting`, now in `inferredNodeRouting`), and its properties carry an Operation control over `data.boolean`. A routing entry's `node` may now carry `consumesChildren`. This adds API.
|
|
19
|
+
- ad58b32: While a boolean group, or a group inside one, is entered, the canvas outlines each of the children it draws only through its result, in the entered-group frame's color, so they can be found without selecting them. `useEnteredGroupFrameLayer` takes the scene for this, and `createEnteredGroupFrameLayer` takes `outlinesOf` and `outlinesChanged`. This adds API.
|
|
20
|
+
- f045da7: A group inside a boolean group is now one operand, the union of everything it shows, where before it counted for nothing. A derivation can read through a group to its contents: `DerivedDep.children` lists a dependency's children as dependencies, in the frame its own pose is in, and a memoized derivation that read them recomputes when one of them changes. This adds API.
|
|
21
|
+
- ac7aa31: Boolean groups: a mark whose shape is the union, intersection, subtraction or exclusion of its children's outlines, recomputed whenever a child moves, changes or is hidden, while the children stay nodes. Add one with `scene.add({ ...booleanGroupFields(), layer, pose, data: { boolean: 'subtract', fill } })` and add its children under it. It paints the result in its own `data.fill` and `data.stroke`, is picked by that ink, moves its children when moved, and saves and loads like any derived node (`BOOLEAN_OF_CHILDREN`, `booleanOfChildren`, `booleanOpOf`). This adds API.
|
|
22
|
+
|
|
23
|
+
It is built on a new node field, `consumesChildren`: the render walk paints none of such a node's children, and a press, region select or select-all reaches one only while that node, or a group inside it, is entered. A node that consumes its children is entered as a group is. `createEnteredGroupScope` and `reachableInGroup` now read the scene's `get` as well as `ancestorsOf`.
|
|
24
|
+
|
|
25
|
+
Hiding or showing a node now recomputes whatever derives from it.
|
|
26
|
+
- a1b06d8: Breaking: the clipboard's paste point is a `WorldPoint` (`{ x, y }`), like the dispatcher's points, in place of `{ worldX, worldY }`. This covers `useClipboardOps`' `paste(at)` and its `getDropPoint` option, `ClipboardDep.paste`, and `PasteCtx.dropPoint`, which an adapter's `commitPaste` receives. Pass `{ x, y }` where you passed `{ worldX: x, worldY: y }`, and read `ctx.dropPoint.x` where you read `ctx.dropPoint.worldX`.
|
|
27
|
+
- 9725598: `LayoutStrategy.commitDrop`'s documentation now says what it does not own: reparenting the dragged nodes, and choosing which container takes the drop, are the move action's.
|
|
28
|
+
- 592b6e5: A container's wheel scroll hands the wheel on at the end of its range. Scrolled as far as it goes in the wheel's direction, `containerScrollAction` passes the wheel to the scrollable container holding it, and past the outermost one to the viewport's wheel pan. Before, a scrollable container kept every wheel over it, even with nowhere left to go.
|
|
29
|
+
|
|
30
|
+
`scrollRange` now measures in the container's own frame, the axes its scroll offset runs along, so a turned container scrolls by its own length rather than its axis-aligned box's, and `scrollableAt` takes a point only over a turned container's own area. Under a composition without `scroll`, or for a container whose pose is not a frame, `scrollRange` answers `null`, since neither can scroll.
|
|
31
|
+
|
|
32
|
+
An immediate invoker's `run` may now return `'pass'`: the dispatcher walks on to the next matching binding as though `enabled()` had declined, and `ActionsRegistry.trigger` answers `false`. This adds API; a `run` returning nothing behaves as before.
|
|
33
|
+
- 41a013b: The wheel scrolls a layout container whose content runs past its bounds, such as `tileGrid({ overflow: 'scroll' })`. `<SceneCanvas>` registers `containerScrollAction` (`container.scroll`) ahead of the wheel pan: over a container whose strategy's `contentExtent` reaches past its bounds, the wheel moves `scene.scroll`'s offset for it, clamped to that extent, and Shift+wheel scrolls it across; anywhere else it declines and the wheel pans the view. `scrollRange` and `scrollableAt` answer how far a container can scroll and which one is under a point. Hiding the overflow is the container's own `clip`. This adds API.
|
|
34
|
+
|
|
35
|
+
A wheel event now carries the world point under the pointer: `WheelEvent` gains optional `x`/`y`, `Action.enabled` is handed it, an immediate invoker's params carry it as `worldX`/`worldY`, and `InvocationCtx.world` holds it.
|
|
36
|
+
- 965e698: Copying or cutting a node that derives from hidden nodes now takes those hidden nodes onto the clipboard with it, still hidden, so a paste is whole. A path effect with `hideSources` is the case that mattered: Cut deleted its hidden sources with it, and the paste then left the effect out as `dependency-not-copied`, so cut and paste lost it. A hidden node cannot be selected, so there was no way to copy one alongside.
|
|
37
|
+
- fda48f1: `FEATURE_PRESETS`, each core preset by the name `<SceneCanvas features>` takes, and `BASELINE_ACTIONS`, the undo and redo actions every canvas registers, are exported, so a page or tool can list what each preset turns on from the presets themselves.
|
|
38
|
+
- 070f3d0: Core's local `{ x: number; y: number }` point types are geom's `Point` now, or `WorldPoint` where the point is the dispatcher's. `DragRectPoint`, `DragRadialPoint`, `PolygonPoint`, `StarPoint` and `LinePoint` are aliases of `WorldPoint`, and `MeasurePoint` of `Point`, in place of interfaces of their own. They have the same shape, so nothing that type-checked before stops.
|
|
39
|
+
- f2188ba: A text node's property panel offers its style's `lang`, as Language under Character.
|
|
40
|
+
- 8b14236: A "may this land here?" check can now refuse with a reason. `DropVerdict` is `true`, `false` (a
|
|
41
|
+
silent no), or `{ refuse: string }`; over a refusal the canvas outlines the target in the
|
|
42
|
+
`refused` overlay role and shows the reason beside the pointer, and a release there does nothing.
|
|
43
|
+
|
|
44
|
+
- **Breaking:** `CanConnect` returns a `DropVerdict` rather than a `boolean`, and so does core's
|
|
45
|
+
`CanDropIntoDep`. A function returning a boolean still fits; code that calls one and treats the
|
|
46
|
+
answer as a boolean must test for `true`.
|
|
47
|
+
- `DiagramView` takes `canReparent(ids, container)`, asked before a drag moves nodes into a
|
|
48
|
+
container or lane they are not in.
|
|
49
|
+
- A `DataContainer` takes `reorder: 'x' | 'y'`: `onReorder` drags its members along that axis and
|
|
50
|
+
Alt with an arrow key steps the selected one, as a group's. **Breaking:** `ReorderChange` names
|
|
51
|
+
the group or container as `parent`, in place of `group`.
|
|
52
|
+
- `DiagramView` takes `onDropItem` and `canDropItem` for items carried in from a list outside the
|
|
53
|
+
canvas, made a source with `dragItemProps(item)`. The report gives the item, what it landed on,
|
|
54
|
+
the container that would hold it, and its index among that container's members.
|
|
55
|
+
- `DiagramViewApi.rectOf` and overlays place a container's members in the world rather than in
|
|
56
|
+
the container's frame, and a stencil drawn into a nested container finds it.
|
|
57
|
+
- Core: move behaviors can publish overlays, routing has a `reason` overlay that `SceneCanvas`
|
|
58
|
+
shows beside the pointer, and a `target` overlay role outlines what a drag would drop into.
|
|
59
|
+
- 61e07e5: An ongoing action, or one binding of it, can declare `dragThresholdPx`: the dispatcher holds a pointer drag until it has traveled that far, then calls `start` with the press's context, and a release short of it never starts the action. `insert` declares 8px (`INSERT_DRAG_THRESHOLD_PX`), so a click that wobbles a few pixels with a shape or stencil tool no longer draws a shape a few pixels wide. `move`'s `dragThresholdPx` option now rides the move bindings' `dragThresholdPx` instead of a `params` entry the action read itself, so a move action invoked directly no longer holds a short drag.
|
|
60
|
+
- 88573be: Core's feature presets are split into small ones, and every name `<SceneCanvas features>` takes is now a preset of its own. `edit` includes `escape`, `contextMenu`, `selectAll`, `duplicate`, `delete`, `group`, `nudge`, `style`, and `clipboard`; `arrange` includes `flip`, `reorder`, `align`, and `distribute`; `paths` includes `anchorKeys`, `pathfinder`, `outlines`, `pathEdit`, and `pathCommands`. Each tool set has a preset (`tools:select`, `tools:view`, `tools:shape`, `tools:draw`, `tools:type`, `tools:page`), and `pick`, `transform`, and `draw` are presets that include others. Any of these names can be passed on its own, so `features={['nudge', 'tools:shape']}` turns on just the arrow keys and the shape tools.
|
|
61
|
+
|
|
62
|
+
This breaks: `COMPOSITE_FEATURES`, `BaseFeature`, and `CompositeFeature` are removed, `NamedPresets` is a plain map of id to preset, and `FEATURE_ACTION_IDS` lists each preset's own actions without those of the presets it includes. `select` now mounts the lasso tool beside the select tool, through `tools:select`.
|
|
63
|
+
|
|
64
|
+
`FeaturePreset` takes an optional `label`, and `describePresets` describes presets as data: each one's own action and tool ids, the presets it includes, the package that ships it, and whether it is an atom, one with a role beyond actions, tools, and includes. This adds API.
|
|
65
|
+
- b142892: Add `attachMenuDismiss` to `@weasel-js/menu`: it closes a menu on a press or wheel outside it, on the window losing focus, and on a resize, attaching to any `Window`-like target and returning a detach. `press: false` leaves out the press listener, and `keepOnBlur` keeps the menu open through a blur. ui's `ContextMenuHost` and hud's `attachMenuHost` both dismiss through it.
|
|
66
|
+
|
|
67
|
+
`normalizeHex` takes an optional form: `'rgb'` drops alpha, `'rgba'` always writes it (`ff` when none was named). `tryNormalizeHex` is the same, returning undefined where `normalizeHex` throws. hud's color row and ui's `ColorField` and `TokenPanel` read hex through them. hud's hex row now accepts `#rgba` shorthand, and `ColorField` shows a malformed `#` value as black rather than passing it to the color input.
|
|
68
|
+
|
|
69
|
+
core's `toHex8`, `getAlpha01`, `withAlpha01` and `mergeAlphaFromPrev` read hex through `tryNormalizeHex` too. A malformed `#` value (`#xyz`, `#12345`) now comes back unchanged instead of expanded into an invalid 8-digit string, and `getAlpha01` reads 1 from one. `toHex8` now lowercases `#RRGGBB` as it already did every other hex form, and ignores surrounding whitespace.
|
|
70
|
+
- 14f8583: `@weasel-js/geom/nd` exports `createSettledCache()`: a value built only once its source holds still. Each `read(key, build)` passes the source's key as a tuple; the first read at a new key answers null, and the second at the same key builds and keeps the value (`reads` moves which read builds it, and `reads: 1` makes it a plain memo). Keys match element by element under `Object.is`. This adds API.
|
|
71
|
+
|
|
72
|
+
Core's pick index, the diagram port and label indexes, and kernel3d's snap box index now use it, in place of three copies of that rule. They build at the same reads as before.
|
|
73
|
+
- e7f4305: `GeometryProjection.transform` takes a third argument, `change: PoseChange`, holding the node's stored pose before the action and the pose the action moved it to. Move, nudge, flip and resize commit pass it. A host whose poses are not planar, such as a `@weasel-js/kernel3d` scene, can now map its data-held geometry from `change`, where the plane affine `m` could not describe a 3D transform. `GeometryProjection` takes an optional pose type parameter, and `PoseChange` is exported. Existing two-argument implementations keep working.
|
|
74
|
+
- c01c5f0: A `FeaturePreset` can carry `layers`: pre-built layers by slot, as `<SceneCanvas layers>` takes them. A slot the canvas's own `layers` names, `null` included, keeps the consumer's entry. This adds API.
|
|
75
|
+
|
|
76
|
+
`@weasel-js/guides` adds `createGridPreset({ spacing, … })`, which draws a grid in the grid slot and snaps gestures to it, so `features={['draw', createGridPreset({ spacing: 20 })]}` replaces building the grid layer and the grid snap source and passing each to its own prop. It takes every `createGridLayer` option, and `snap: false` or `snap: { origin }`.
|
|
77
|
+
- 23bb196: Adds `createGroupEntryContribution` and `useGroupEntryContribution` (with `GROUP_ENTRY_ID` and the `EnterGroupOptions`, `LeaveGroupOptions` and `PressTargetTree` types) to the public API: the double-click-to-enter and Escape-to-leave bindings `<SceneCanvas>` installs, for a host that mounts the dispatcher and tools itself. This adds API.
|
|
78
|
+
- 783756a: A scoped container's grid runs in the container's own frame: from its corner, along its axes, so
|
|
79
|
+
it turns when the container does. `sceneGridRegions` gives each region its `frame` and, without an
|
|
80
|
+
`outlineOf`, the outline the container draws; the grid layer draws a region's lattice in that
|
|
81
|
+
frame, and `pointToGridCell`, `useGridCellHover` and `createCellHighlightLayer` count and draw a
|
|
82
|
+
cell in it. `gridSnapSource` rounds in the same frame, read through the new
|
|
83
|
+
`SnapCollect.frameOf`, which a scene session fills from `sceneBoxFrames`. This changes behavior
|
|
84
|
+
for an unturned container too: its lattice now starts at its corner rather than at the grid's
|
|
85
|
+
origin. A subject spread over two containers still snaps on the world lattice.
|
|
86
|
+
`sceneGridRegions` now takes a `Scene`, and `GridRegionScene` is gone. geom adds `boxFrameOf` and
|
|
87
|
+
`pxExtentInFrame`.
|
|
88
|
+
- 2692828: New package `@weasel-js/text-shaping`: OpenType shaping through HarfBuzz. `await loadShaper()` loads its WebAssembly and resolves with a shaper; `registerShaper(shaper)` (from `@weasel-js/text`, re-exported by core) hands it to every text layout, and `layoutRuns` also takes one as `opts.shaper`. Text set in a face registered with `registerFontOutlines` from TrueType or OpenType bytes is then shaped: Arabic joins, Devanagari forms its conjuncts and places its vowel signs, marks sit where the face's `GPOS` puts them, and ligatures and the face's kerning come from the shaping. Text drawn from an atlas, or with no shaper registered, lays out as before.
|
|
89
|
+
|
|
90
|
+
A shaped code point draws its whole cluster from its own pen, and the cluster's other code points take no width; `PlacedGlyph.shaped` carries what it draws. `fontVariantCaps: 'small-caps'` on a shaped face with an `smcp` feature now draws the face's own small capitals instead of synthesizing them, and `ResolvedRun` gains `fontFeatures` for it. This adds API: `registerShaper`, the `TextShaper`, `ShapedGlyph` and `ShapedCluster` types, `LayoutRunsOpts.shaper`, `ResolvedRun.fontFeatures` and `PlacedGlyph.shaped`.
|
|
91
|
+
- 767713e: `@weasel-js/headless` holds the arrow-key step sizes and a slider's value math, so a DOM slider and a canvas-drawn one step alike. `arrowSteps`, `arrowMove` and `rangeKey` read a key string and Shift: one step, or `COARSE_STEPS` (ten) with Shift or a Page key, and Home and End for a range's ends. `sliderKey` is the value one key moves a thumb to, and `usableStops`, `attract` and `clampOrdered` are the stop and ordering rules `Slider` uses. core's node and anchor nudge and ui's `Slider`, curve, gradient, band and timeline editors all take their steps from it.
|
|
92
|
+
|
|
93
|
+
`ResizeHandle` now moves ten steps with Shift held, not eight, matching every other arrow-key control in weasel.
|
|
94
|
+
- 6a69c27: `holdPreview(action)` makes an action a hold, under `holdPreviewId(action.id)`: bound to a `held(…)` key, in a keymap or anywhere, the press shows the action's `preview`, the release runs it, and a cancel (Escape, through core's cancel-gesture action) takes the preview down and runs nothing. The binding's params reach both. Nothing binds one yet; a Pathfinder key is the next step.
|
|
95
|
+
- b0a55a6: A fill or stroke can now tile a picture: `{ kind: 'image', src, width, height }`, a new pattern paint kind beside the built-in tiles. `src` is anything an `<img>` loads, and a `data:` URI makes the paint carry the picture itself, so it saves, copies and exports with nothing stored beside the document. `width` and `height` are the tile's size in paint units; left out, the picture's pixel size is used. The fill draws nothing until the picture has decoded, then repaints.
|
|
96
|
+
|
|
97
|
+
This adds API. `@weasel-js/paint` exports `ImagePatternPaint`, `TileExtent`, `ResolvablePatternKind` and `isImagePatternPaint`, and `isPatternPaint` now answers true for an image paint. `TexturePaint` takes an optional `width` and `height`, which set its tile size in place of the texture's pixel size. `resolveFillPattern` resolves an image paint to a `texture` paint. Code that switches over every `Paint` kind meets a new member.
|
|
98
|
+
|
|
99
|
+
`@weasel-js/svg` writes an image paint as a `<pattern>` holding one `<image>` that fills the tile, and reads that structure back from any editor's SVG, `xlink:href` included. An image paint with no `width` and `height` cannot be sized as an SVG pattern and is left out with a warning.
|
|
100
|
+
|
|
101
|
+
`PatternPicker` gains an image swatch after the tiles that opens a file picker. The picked file is stored as a `data:` URI, re-encoded down to 1024 px on its longest side if larger (an SVG is kept as it is), and the size switch then sets the tile's longest side to 32, 64, 128 or 256 units. A new `readImage` prop replaces that step for an app that stores images elsewhere. `readImageFile`, `seedImagePattern` and `MAX_STORED_IMAGE_SIDE` are exported.
|
|
102
|
+
- 5b7d0ac: Breaking: an immediate action's params carry the event's point as `world`, a `WorldPoint`, in place of `worldX`/`worldY`, and a click's press point as `press` in place of `pressX`/`pressY`. Read `params.world.x` where you read `params.worldX`, and pass `{ world: { x, y } }` where you passed `{ worldX: x, worldY: y }`: to `trigger`, as a menu's params, and to an invoker called directly. The new exported `ImmediateParams` type names every field the dispatcher puts in the bag; cast `params` to it to read them. `trigger` asks `enabled` at `params.world`. A parallax plane now carries `press` into the plane as it already did the release point. The align and flip actions' `'pointer'` reference reads `world` too. The input events themselves are unchanged: a `ClickEvent` still has `x`/`y` and `pressX`/`pressY`, and `useClipboardOps`' `paste(at)` keeps its `{ worldX, worldY }` shape.
|
|
103
|
+
- 930f2fa: A canvas with the `ingest` feature now turns pasted or dropped plain text into a text node by default: `kitTextHandler` is registered with the other kit handlers, and passing it in `ingestion.handlers` is no longer needed. A string that arrives beside a file is passed over, since there it is the file's name or URL (a browser dragging an image, the OS copying a file); for this `IngestCtx` gains optional `items`, every item the arrival carried.
|
|
104
|
+
|
|
105
|
+
While an OS drag hovers, `ingest` previews it: a placeholder box per file some handler could claim, or one line-shaped box for a drag of text, where the drop would put it, at a size fixed against the viewport. A browser withholds a hovering drag's contents and size, so the box shows where, not what.
|
|
106
|
+
- edc7324: Content handlers passed in `<SceneCanvas ingestion={{ handlers }}>` now run only on that canvas. They used to go into the page-wide registry and run on every mounted canvas. A handler meant for every canvas goes through `registerContentHandler`, which is unchanged. This changes behavior for an app that relied on one canvas's handlers reaching another.
|
|
107
|
+
|
|
108
|
+
The kit's handlers (`kit:image`, `kit:svg`, `kit:weasel-json`, `kit:text`) also belong to each canvas now, and a canvas can turn them off with the new `ingestion.kitHandlers`: `false` drops all of them, and `{ omit: ['kit:text'] }` drops the ones named. The default keeps all. Adds API: the `kitHandlers` option and its `KitHandlersOption` type.
|
|
109
|
+
- e7fca17: `@weasel-js/geom` adds `joinPaths(parts)`, which joins polygon paths into one `'nonzero'` compound path measured from the first part's origin, re-measuring any part that has another. `textToPath`, `textToPathsByPaint` and `textClusters` join their glyphs through it, so text set far from 0 keeps an origin instead of dropping it, and a decoration rule there is measured from one too. A derived path made of several parts (a blend, a repeat) joins through it as well.
|
|
110
|
+
- 1d39cf6: In a 3D move of several roots, a root stored under a container now moves by the same world distance as the first root, as roots stored in world space already did. It used to resolve the drag on its own plane, so it went a different distance and missed a snap target the first root found. A move now hands such a root the lead (`TranslateLead`) expressed in its container's frame, and kernel3d's descriptor applies the lead's change whole rather than flattening it onto the pose's own height.
|
|
111
|
+
- e5ea7e8: A `@weasel-js/kernel3d` move or nudge of several roots moves them as one body. Each root used to turn the screen delta into a world delta on the plane at its own height and from its own place on screen, so roots moved by slightly different amounts and only the first landed on what the snap held it to. `PoseDescriptor.translate` takes an optional fourth argument, `lead: { from, to }`: how the move's first root went under the same delta. Core's move, nudge and `moveNodes` pass it for every root stored in world space, and kernel3d's descriptor moves each root by the lead's world change. `TranslateLead` is exported. A descriptor that ignores the argument behaves as before.
|
|
112
|
+
- 22472a1: A drag into or out of a turned layout container now shows the container to its strategy upright, as the layout pass already did for `childPoses`, `arrive` and `depart`: `getDropTargets`, `reflowPoses`, `commitDrop`, `acceptsDrop` and `releaseDrop` get the container's box in its own frame, with its children and the dragged child taken into that frame, and what they return is taken back to world. A drop preview or commit into a rotated grid or stack now runs along the container's axes. An upright container's hooks see world, as before. A container's `bounds` handed to a drag hook now carry only `x`, `y`, `width` and `height`, where they used to carry the whole pose the descriptor measured.
|
|
113
|
+
- ec6d7d3: The layout pass lays a turned container's children out along the container's own axes. `childPoses`, `arrive` and `depart` now see a turned container upright, in its own frame placed where its unturned box sits, with its children in that same frame; before, they got the container's unturned box beside children in their turned world poses, so a stack or tile grid in a rotated container placed its children off its axes and outside it. A size `arrive` or `depart` asks for grows the container along its own axes. Upright containers are seen in world exactly as before.
|
|
114
|
+
- 80789ff: Scene edits no longer climb a node's ancestors to check for a lock while nothing in the scene is locked, and the climb they do make when something is allocates nothing. Since per-node locks landed, every edit built a fresh pair of gate maps and walked to the root: `setPose` on a node 16 deep took about 2.5 µs, and now takes about 0.5 µs. `isNodeLocked` and `isNodeHidden` take the same allocation-free path.
|
|
115
|
+
- f695cff: `<SceneCanvas>` installs its long-press feedback beside a `tools` takeover too, as it does the view marker drag: the ring shows for a held press whether or not the consumer's own `useTools` lists it. A takeover whose ambient already holds an entry of the same id keeps its own and gets no second one.
|
|
116
|
+
- b1b16ce: A view marker now drags under a `tools` takeover. The surface installs its marker-drag entry beside the takeover's tool set, as it already did its preset bindings, unless the takeover's ambient lists the entry itself. Before, a takeover turned marker dragging off until the consumer added `createViewMarkerContribution()` to its own ambient. `actions={null}` still turns it off.
|
|
117
|
+
- b15704a: `measureAt`, `measureIds` and `currentMeasureId` read the measurement layer's own nodes (`scene.nodesOnLayer`) rather than walking every node in the scene, so a measure hover costs the number of measurements, not the size of the document. Their `scene` argument now needs `nodesOnLayer` in place of `renderOrderNodes`; every `Scene` has both.
|
|
118
|
+
- 81d08f7: `resolveSchemaMenu` now draws the leaves it used to leave out. An `object` is a submenu of its fields, each edit writing the object whole; a `list` is a submenu of its entries (`Cap 1`, `Cap 2`) with rows that add an entry and remove the last within `minItems`/`maxItems`; a `map` is a submenu of its entries named for their keys; a `union` is a submenu of its variants as radio rows, then the fields of the variant it holds; a `field` is a submenu of the schema's value-holding leaves it may name. A `paint` is a color row while it is solid or none, and reads as its kind otherwise. A section among an object's fields adds nothing to their paths. An app-defined leaf is drawn by the new `MenuSources.customRows`, given the row key, the path, and the `values`/`onChange` where the leaf sits (`MenuCustomAt`), and is left out without it. Adds API; a menu over a schema holding these leaves now shows rows it did not before.
|
|
119
|
+
- a29d2df: `PaintInput`'s Inherit segment is a glyph like its neighbors, `core:paintInherit` (a solid parent swatch handing its paint down to an empty child), where it was a text label. Its accessible name is still "Inherit".
|
|
120
|
+
- 4aa9b8b: A node properties schema can name a field the node declares for its children as a leaf key: `clip`, or `units` and paths inside it. `SelectionPanel` reads the field off the node and writes it through `scene.setClip` or `scene.setUnits`, one undo step per edit, so a boolean leaf keyed `clip` with an encoding of `'outline'` switches a group's clip.
|
|
121
|
+
- 39cb8ed: `core:symmetricHandles`, `core:simplifyBrush`, `core:harmonize`, `path-tools:spiro`, `path-tools:spiroSmooth`, `path-tools:spiroG4`, and the warp tools' `path-tools:distort`, `path-tools:envelope` and `path-tools:puppetWarp` are redrawn on the 16px pixel grid at a one-pixel stroke. The spiro S-curves now run level and along the pixel diagonal, so at 1x they no longer render as a gray diagonal. `simplifyBrush` is now a zigzag path that runs straight from inside the brush's ring, and `harmonize` is a circular arc under two equal curvature-comb teeth.
|
|
122
|
+
- a58fb4f: Every reader of a `PolygonPath`'s coords now reads them where its `origin` puts them: hit-testing and marquee/lasso picking, distance, anchors (`pathToAnchors`, anchor editing, corner expansion and restore), the booleans, `pathToD` and SVG export, cubic contours, puppet meshes, pose interpolation, derived parts, path warps, diagram connectors, and skinning. `translatePath` and `rotatePathAround` move the origin rather than the coords, and `anchorsToPath` given `like` writes back from its origin. This adds API: `worldCoords`, `pathPoint`, `originOf`, `addOrigin` and `coordsFrom` read and write coords in a path's own space.
|
|
123
|
+
|
|
124
|
+
Paths far from 0 now get an origin where they are made: past `ORIGIN_THRESHOLD` (16,384), `anchorsToPath` without `like` (the pen tool), `pathFromD`, the booleans, and SVG import measure the path from `originFor`, the rounded center of its points. `PathBuilder.autoOrigin()` and `transformPath`'s `{ autoOrigin: true }` opt a producer in. A path near 0 is built as before.
|
|
125
|
+
- 8e6ed97: A `PolygonPath` can carry an `origin`, the point its `coords` are measured from, in doubles; a `Mesh` built from one carries it too. This adds API. Far from 0 a float32 coordinate loses its fraction (at x = 262,144 it sits on a 1/32 grid, 2 px at 64x); measured from an origin beside the shape it keeps it. `PathBuilder.setOrigin(x, y)` builds such a path from points given in the path's own space.
|
|
126
|
+
|
|
127
|
+
The renderer draws one where its origin puts it, batched or by its own draw, filled, stroked, clipped, and culled. `tessellate`, `tessellateStroke`, `markerHeads`, `strokeToPath`, `transformPath` and `boundsOfPath` honor it, and so does a node's stroked bounds.
|
|
128
|
+
- 18c96c3: `PATHFINDER_KEYMAP` puts the Pathfinder operations on held keys behind the leader Alt+P (`PATHFINDER_LEADER`): U Unite, I Intersect, M Minus Front, X Exclude, D Divide, C Crop, T Trim, R Merge. Holding a key previews the operation on the selection, releasing it runs the operation, and Escape before the release cancels it. The keys work with the leader tapped or held down. Each is a `holdPreview` of the operation, bound under its hold's id (`pathfinder.union.hold`), which is what a host rebinds it by. It carries only the holds, so the operations still come from the `paths` preset; install it with `<SceneCanvas ambient>`. New API.
|
|
129
|
+
- f30c624: **Breaking:** the pick functions take the world point as one `WorldPoint` argument in place of two positional numbers. A picker written `(worldX, worldY) => …` is now `(world) => …`, or `({ x, y }) => …`:
|
|
130
|
+
|
|
131
|
+
- `<SceneCanvas geometry={{ pickEvery }}>` and `<SceneCanvas selectTool={{ pickBest }}>`
|
|
132
|
+
- `useSelectTool`'s `pickEvery` and `pickBest` options
|
|
133
|
+
- `nestedHitTester`'s `pickOutermost` and `pickBest`
|
|
134
|
+
- `PickHud`'s `pickEvery` and `pickBest`
|
|
135
|
+
|
|
136
|
+
`pickBest` keeps its `alt` and `selection` arguments after the point. A 3D host's picker can now hand the point straight to a `nodeAtPoint` dep, which already took one.
|
|
137
|
+
- b06e2b9: Breaking: `PointerWorldPos`, the pointer store's value, is `{ world: WorldPoint; viewId }` in place of `{ worldX, worldY, viewId }`. Read `p.world.x` where you read `p.worldX`, and publish with `store.set({ world: { x, y }, viewId })`. `useClipboardOps`' own `getDropPoint` option keeps its `{ worldX, worldY }` shape. When that option is left out, the paste point read from the pointer store no longer carries the store's `viewId` along with it.
|
|
138
|
+
- 34a364e: Removes public API: `useFrameClock` and its `FrameClock` and `FrameClockOptions` types are gone from `@weasel-js/react`, and from their re-export in `@weasel-js/core`. Use a `@weasel-js/clock` clock in their place: `createClock({ rate: 1 })` for a clock that runs, `createClockRegistry` and `useClockLoop` from `@weasel-js/clock/react` to drive it, `handle.onFrame` as a render layer's `subscribe`, and `handle.clock.elapsed / 1000` for seconds. Rate 0 pauses it. The loop leaves out time with the tab hidden, as `useFrameClock` did, and skips a clock registered with a target while that element is out of the viewport; unlike `useFrameClock`, a running clock is synced whether or not anything subscribes to its frames.
|
|
139
|
+
- 4c0141f: A group carrying `retain` that also has `effects` or `isolate` is now retained: its surface keeps what the group's own buffer composites, effects run, so a still frame runs none of its passes. A retained group inside another group's effects buffer, such as a layer in a `LayerGroup` with effects, now composites into that buffer instead of drawing straight through; it is drawn single-sampled there, as drawing into that buffer is. `drawLayers` now marks a layer with `effects` for retaining under the same rules as any other layer. A retained group inside another retained group is still drawn straight through.
|
|
140
|
+
- f9282f7: A retained world layer now keeps its surface through a pan. `drawLayers` marks an unchanged world layer for retaining while the view only translates, and stops only for a zoom. The renderer composites the kept surface moved when the pan is a whole number of device pixels and what it drew still covers the buffer; past that it draws the surface again with a margin of an eighth of the buffer past each edge, so following pans keep reusing it. A pan by a fraction of a device pixel draws the layer straight through, keeping the surface, until the view holds for a frame. A layer holding a custom shader, a gradient or pattern in screen units (the default), a world-unit paint with no view matrix, a texture paint on text, or a group with effects, isolation or a composite operator is drawn straight through while the view moves instead, since those do not move with it, and drawn again once it holds; so is a layer whose margin the budget has no room for. `RetainedSurfaces.lookup` now returns what to do (`kept`, `redraw` with the window to draw, or `through`) rather than pixels or `null`; `RetainedSurfaces` is internal.
|
|
141
|
+
- 01b103e: `core:retractHandles` and `path-tools:resamplePath` are redrawn on the 16px pixel grid. `retractHandles`'s corner runs on the pixel diagonal and its arrows carry 45-degree heads; `resamplePath`'s anchors are two pixels square on pixel corners, so they no longer blur at 1x.
|
|
142
|
+
- 19e4c11: `<SceneCanvas>` mounts a text editor of its own, so a node the text or sticky tool places can be typed into with no host wiring, and with the `edit` feature a double-click on a text node edits it. The new `textEditor` prop takes `useSceneTextEdit`'s options, or `false` for none. The editor mounts its overlay in the canvas's offset parent, and stands down for a host's own: a `useSceneTextEdit` whose container holds the canvas, or a `textEdit` dep a child registers. `drawOneEditing` takes the `drawOne` to wrap, and `useSceneTextEdit`'s `onDoubleClick` takes a DOM `MouseEvent` as well as React's.
|
|
143
|
+
- a80f8be: A scene composes poses by default. A child's stored pose is now relative to the frame of the nearest container above it that defines one (`RIGID_POSE_COMPOSITION`), so moving a container writes one pose, rotating it rotates its contents, and resizing it leaves them where they draw. A container whose pose derives from its children (`dependsOn: 'children'` with a `derivePose`) is an envelope, not a frame: its children share the frame above it.
|
|
144
|
+
|
|
145
|
+
**This breaks a consumer that stores absolute child poses.** To migrate:
|
|
146
|
+
|
|
147
|
+
- A scene whose `TPose` is not a rect pose must name its composition: `useScene` and `createScene` require `composition` in the type unless `TPose` is a `RectPose`. `IDENTITY_POSE_COMPOSITION` keeps every pose world; under it a container groups its children and never carries them. kernel3d's scenes name it.
|
|
148
|
+
- Saves: `toJSON` writes version 3, whose child poses are local. `sceneFromJSON`, `Scene.loadState`, and `specsFromSerialized` read a version 1 or 2 save as world and make each pose local as it loads, under the scene's composition (`SpecsFromSerializedOptions.composition`). A recorded history from before is not restored: `SCENE_HISTORY_FORM` is 4.
|
|
149
|
+
- Specs or ops you build with world poses for children: put them through `localizeWorldPoses(specs, composition)`, or `localizeInScene(scene, specs)` when they land under a container already in the scene. `worldPosesOf(specs, composition)` reads them back as world, and `heldInFrame(scene, id)` names what a container's frame holds.
|
|
150
|
+
- `sceneToAdapter`'s `setPose` no longer moves a container's descendants by the same delta, and a container's pose target no longer shows them traveling beside it: the frame carries them. `PoseCascade` and the `cascade` argument of `scenePoseTarget` and `sceneClipTargets` are gone.
|
|
151
|
+
- The group action makes a frame group, whose pose is authored, under any composition; it no longer derives an envelope's pose from its members under the identity strategy.
|
|
152
|
+
- `resolvePreviews` answers each entry's pose in world, and a previewed frame brings the children it carries.
|
|
153
|
+
- `SnapTarget.slotPose` is world, as documented; `snapToContainer` stores it in the target's frame.
|
|
154
|
+
- `diagramScene` returns each spec's pose in its container's frame (`composition`, default `RIGID_POSE_COMPOSITION`; pass `IDENTITY_POSE_COMPOSITION` to read the layout in world). Body rows, container titles, lanes, and members are stored relative to what holds them; `DiagramView`'s reports, `diagramLayoutOf`, and `documentFromScene` stay in world.
|
|
155
|
+
- `svgNodesFromKit` folds a tree's `composition` when it has one, and `unpackSvgFiles` stores each imported node in its container's frame.
|
|
156
|
+
- 6940f43: A container's content can be scrolled. `scene.scroll` holds one offset per container (`scene.scroll.set(id, { x, y })`); like the selection it is view state, never recorded, serialized or counted in the scene's version. The document never moves: painting, picking, selection chrome, the preview ghost, an insert's landing and every gesture show a scrolled container's children moved by minus its offset, and write back through the same fold, so a move inside a scrolled container stores the content position. The layout pass and other document writers fold through the unscrolled frame. This adds API.
|
|
157
|
+
|
|
158
|
+
`PoseComposition` gains an optional `scroll(frame, offset)`; `RECT_POSE_COMPOSITION` and `RIGID_POSE_COMPOSITION` supply it (`scrollRectPose`, `scrollRigidPose`, along a turned container's own axes), and a composition without it cannot scroll. `PoseAdapter` gains an optional `childFrame(id, world)`, which `composeWorldPose` and `rebaseLocalPose` honor; `scrolledFrames`, `viewPoseAdapter` and `sceneViewPoseAdapter` build it from a scene, and `composeChildFrame` and `childFrameAt` read the frame a container gives its children. During a move, a layout strategy is handed its container's content frame as `LayoutContainer.bounds`, so its slots agree with the children as shown.
|
|
159
|
+
- ebe1f55: The default `slice` dep now reads only the nodes the scene's pick index finds under the cut's bounds, instead of every node in the scene, whenever the index is built. A cut in a scene that just changed still walks every node.
|
|
160
|
+
- 3401898: The stroke's Start, Mid and End marker selects draw a glyph beside each built-in marker and beside None, as the sibling cap, join, align and dash controls do: `core:markerArrow`, `core:markerCrowZeroMany` and the rest, in a new "Stroke markers" icon group, each a line with that marker on its end. A marker a consumer registers still reads as its id.
|
|
161
|
+
- 058fcdf: `serializeSvg` without a `textLayout` now writes a text node's lines where weasel lays them out, for any face the font registry has measured. `dominant-baseline="text-before-edge"` sets the face's ascent on the box top, where weasel's layout first puts half the leading, so every line sat that far off (Inter at 24px: 2.3 up at line height 1.4, 2.5 down at 1.0); a `dy` on the `<text>` now moves them by it. A face nothing measured is written as before. New in `@weasel-js/text`, re-exported by core: `halfLeadingFor(family, weight, style, fontSize, lineHeight)`.
|
|
162
|
+
- 880f2b0: Text Along Path draws underline, strikethrough and overline. `textClusters` gives each cluster the decoration rules crossing its slot (`TextCluster.rules`), and a path effect item can carry them as `bands` (`EffectBand`); `textAlongParts` joins alike bands on neighboring items into one ribbon that bends with the path, where the glyphs themselves stay rigid.
|
|
163
|
+
- 871a56b: A double-click that opens a text edit finds its node through the scene's pick index once the index is current, rather than testing every node in the scene. It answers the same node and caret as before.
|
|
164
|
+
- f645651: Text rewritten every frame stops allocating its layout and quad arrays. Adds API: `dropRuns(runs)` tells the text caches a runs array will not be laid out again, and `cachedLayoutRuns` and `cachedPaintRuns` take a last argument, `'keep'` (the default) or `'read'`, for a caller that is done with the result before its next call. A layout that no runs array files, that the structural list has let go, and that every caller only read gives its arrays to the next layout and painting of the same size. The renderer reads; a HUD text widget drops the runs it replaces. Results are unchanged.
|
|
165
|
+
- 1e54d33: `ToolPalette` now lays its tools out in a natural order by default: the selection tools first, then the hand, then the tools that make things (shapes, pen and pencil, text), and the slice tool last. Core's new `TOOL_GROUP_ORDER` names the group order, and `BUILTIN_TOOL_IDS` (now listed group by group, so the lasso follows select) orders the tools within each group; a palette given `groupOrder`, or the new `toolOrder` prop, follows that instead. Groups and tools neither order names keep their registration order after the named ones. `BUILTIN_TOOL_GROUPS` lists the built-in tool sets in that order. The slice tool's presentation group is now `page`, matching `PAGE_TOOLS`, so it gets a group of its own rather than sitting among the shapes.
|
|
166
|
+
- ca31c35: `core:flipX` and `core:flipY` are two trapezoids, short edges facing each other across the middle and long edges along the sides: one filled, its mirror outlined. Their straight edges sit on whole pixels at 16 px, so the glyphs no longer blur there. WeaselDraw's `symmetry` uses the same pair with both halves outlined, so the mode stops looking like the flip action.
|
|
167
|
+
|
|
168
|
+
The transport glyphs (`play`, `pause`, `stop`, `stepBack`, `stepForward`, `rewind`, `fastForward`, `live`) are drawn 1.3 times larger, as tall as `close` or `search`, and `Transport` draws them at 16 px rather than 14.
|
|
169
|
+
- 69d6ede: Every `<SceneCanvas>` and `<LeanSceneCanvas>` now has undo and redo (Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z), whatever its `features`. They used to come only with the `edit` preset, so a canvas that moved shapes, edited anchors, or drew with the shape tools recorded history it could not step back through. `edit` no longer lists them; `BASELINE_ACTION_IDS` names the actions every canvas registers, and `<SceneCanvas actions>` still overrides or mutes them.
|
|
170
|
+
- 7a6f462: `<CanvasView follows>` names a view whose layers this one paints and picks, read at every paint and pick, while it keeps its own rect and camera. Where that view owns a selection, this one acts on it and draws its chrome. A loupe that is a view follows whichever view is under its aim, so a lens over a `<CanvasView>` panel no longer shows or picks a layer the panel hides, and a press in it selects into the panel's own selection where the panel has one.
|
|
171
|
+
- de97d4d: A view marker that covers more than three quarters of the frame it is drawn in now grabs only a band about its rim, `VIEW_MARKER_HIT_SLOP_PX` either side, instead of its whole interior. A zoomed-out view's aim drawn across its whole frame used to take every press there; the frame now keeps them. A marker smaller than that, such as a minimap's, still drags from anywhere inside it.
|
|
172
|
+
- 9abd787: While the view moves, a canvas draws it with its translation rounded to whole device pixels, so a retained layer panned by a fraction of a pixel (kit inertia, trackpad momentum) is composited moved instead of drawn again every frame. Once the view holds, the canvas draws it exactly on the next frame. Hit-testing, `getView` and `clientToWorld` keep the exact view, so while moving the drawn and hit positions can differ by up to half a device pixel. `<Canvas retain={{ whole: false }}>` turns this off; `retain={false}` turns it off too.
|
|
173
|
+
- 09442dc: The points the snap, paths, edit-anchors and `group.enter` code takes from the pointer are typed `WorldPoint`: the `geometryAt` that `sceneSession2d` and `sceneProbeReads` take, `ownGeometryAt`, `AngleCollect.pivot` and `press`, `PathEditDrag.handle`, `PathRepresentation.segmentPress` and the point `worldToScreen` takes. `WorldPoint` is an alias of `Point2`, so nothing that type-checked before stops.
|
|
174
|
+
- fceb8c5: `WorldPoint` names the space the dispatcher's `clientToWorld` maps the pointer into: the world on a 2D canvas, the pane's screen point under the identity mapping a camera-bearing host passes. `InvocationCtx.world`, `drag.start`, `drag.current`, `DragSample`, the dispatcher's `onClick`, `onDoubleClick`, `affordanceAt`, `classifyTarget` and `clientToWorld`, and the point payloads of the `nodeAtPoint`, `lassoSelect`, `insert` and `snap` deps are typed with it. It is an alias of `Point2`, so nothing that type-checked before stops.
|
|
175
|
+
- Updated dependencies [cc0e73a]
|
|
176
|
+
- Updated dependencies [79357fc]
|
|
177
|
+
- Updated dependencies [d6221a7]
|
|
178
|
+
- Updated dependencies [38bb299]
|
|
179
|
+
- Updated dependencies [e1bbdcd]
|
|
180
|
+
- Updated dependencies [592b6e5]
|
|
181
|
+
- Updated dependencies [41a013b]
|
|
182
|
+
- Updated dependencies [e974dba]
|
|
183
|
+
- Updated dependencies [30582cb]
|
|
184
|
+
- Updated dependencies [2999820]
|
|
185
|
+
- Updated dependencies [8b14236]
|
|
186
|
+
- Updated dependencies [a40d519]
|
|
187
|
+
- Updated dependencies [2993216]
|
|
188
|
+
- Updated dependencies [61e07e5]
|
|
189
|
+
- Updated dependencies [a64647a]
|
|
190
|
+
- Updated dependencies [7b048f4]
|
|
191
|
+
- Updated dependencies [aba8563]
|
|
192
|
+
- Updated dependencies [b142892]
|
|
193
|
+
- Updated dependencies [ec07e24]
|
|
194
|
+
- Updated dependencies [6d5f7c2]
|
|
195
|
+
- Updated dependencies [14f8583]
|
|
196
|
+
- Updated dependencies [8ef803d]
|
|
197
|
+
- Updated dependencies [2f3d924]
|
|
198
|
+
- Updated dependencies [783756a]
|
|
199
|
+
- Updated dependencies [7cc9da9]
|
|
200
|
+
- Updated dependencies [2692828]
|
|
201
|
+
- Updated dependencies [1c046e3]
|
|
202
|
+
- Updated dependencies [11e2b7d]
|
|
203
|
+
- Updated dependencies [3c3a219]
|
|
204
|
+
- Updated dependencies [767713e]
|
|
205
|
+
- Updated dependencies [b59380e]
|
|
206
|
+
- Updated dependencies [ba998f9]
|
|
207
|
+
- Updated dependencies [9a963cd]
|
|
208
|
+
- Updated dependencies [2232189]
|
|
209
|
+
- Updated dependencies [6a69c27]
|
|
210
|
+
- Updated dependencies [8763fc3]
|
|
211
|
+
- Updated dependencies [b0a55a6]
|
|
212
|
+
- Updated dependencies [6cb0cf6]
|
|
213
|
+
- Updated dependencies [5b7d0ac]
|
|
214
|
+
- Updated dependencies [a370edb]
|
|
215
|
+
- Updated dependencies [e7fca17]
|
|
216
|
+
- Updated dependencies [f7c49b7]
|
|
217
|
+
- Updated dependencies [64d4e97]
|
|
218
|
+
- Updated dependencies [4aa138a]
|
|
219
|
+
- Updated dependencies [81d08f7]
|
|
220
|
+
- Updated dependencies [e7d70f5]
|
|
221
|
+
- Updated dependencies [d7d29f9]
|
|
222
|
+
- Updated dependencies [a58fb4f]
|
|
223
|
+
- Updated dependencies [8e6ed97]
|
|
224
|
+
- Updated dependencies [6b1b532]
|
|
225
|
+
- Updated dependencies [5af16a6]
|
|
226
|
+
- Updated dependencies [966c37f]
|
|
227
|
+
- Updated dependencies [b06e2b9]
|
|
228
|
+
- Updated dependencies [9a3626f]
|
|
229
|
+
- Updated dependencies [28f476b]
|
|
230
|
+
- Updated dependencies [ffdbbcf]
|
|
231
|
+
- Updated dependencies [ee97c81]
|
|
232
|
+
- Updated dependencies [624cf38]
|
|
233
|
+
- Updated dependencies [941f382]
|
|
234
|
+
- Updated dependencies [9ef7751]
|
|
235
|
+
- Updated dependencies [34a364e]
|
|
236
|
+
- Updated dependencies [561472d]
|
|
237
|
+
- Updated dependencies [d68c4f8]
|
|
238
|
+
- Updated dependencies [c66c536]
|
|
239
|
+
- Updated dependencies [cbb344e]
|
|
240
|
+
- Updated dependencies [4c0141f]
|
|
241
|
+
- Updated dependencies [d3fdba8]
|
|
242
|
+
- Updated dependencies [f9282f7]
|
|
243
|
+
- Updated dependencies [0509c29]
|
|
244
|
+
- Updated dependencies [3803ba2]
|
|
245
|
+
- Updated dependencies [9819d7a]
|
|
246
|
+
- Updated dependencies [230a206]
|
|
247
|
+
- Updated dependencies [2e4943e]
|
|
248
|
+
- Updated dependencies [8ddaf05]
|
|
249
|
+
- Updated dependencies [8e2c8c8]
|
|
250
|
+
- Updated dependencies [cc0e73a]
|
|
251
|
+
- Updated dependencies [4f68c3a]
|
|
252
|
+
- Updated dependencies [78213f7]
|
|
253
|
+
- Updated dependencies [0fc0e50]
|
|
254
|
+
- Updated dependencies [058fcdf]
|
|
255
|
+
- Updated dependencies [dc6f83a]
|
|
256
|
+
- Updated dependencies [880f2b0]
|
|
257
|
+
- Updated dependencies [4a8b5be]
|
|
258
|
+
- Updated dependencies [32889fc]
|
|
259
|
+
- Updated dependencies [6c31588]
|
|
260
|
+
- Updated dependencies [1fb8193]
|
|
261
|
+
- Updated dependencies [ab41dea]
|
|
262
|
+
- Updated dependencies [f645651]
|
|
263
|
+
- Updated dependencies [5399b45]
|
|
264
|
+
- Updated dependencies [55375c0]
|
|
265
|
+
- Updated dependencies [486ff61]
|
|
266
|
+
- Updated dependencies [23dcddf]
|
|
267
|
+
- Updated dependencies [f2acefd]
|
|
268
|
+
- Updated dependencies [95eb9d3]
|
|
269
|
+
- Updated dependencies [ff3c2ad]
|
|
270
|
+
- Updated dependencies [b0d5f18]
|
|
271
|
+
- Updated dependencies [7507e13]
|
|
272
|
+
- Updated dependencies [1e54d33]
|
|
273
|
+
- Updated dependencies [97d889f]
|
|
274
|
+
- Updated dependencies [9adc0cd]
|
|
275
|
+
- Updated dependencies [d5c4ee6]
|
|
276
|
+
- Updated dependencies [24e03ff]
|
|
277
|
+
- Updated dependencies [b43c94c]
|
|
278
|
+
- Updated dependencies [dfdc8a6]
|
|
279
|
+
- Updated dependencies [e23101b]
|
|
280
|
+
- Updated dependencies [fceb8c5]
|
|
281
|
+
- @weasel-js/animator@1.14.0
|
|
282
|
+
- @weasel-js/font@1.14.0
|
|
283
|
+
- @weasel-js/renderer@1.14.0
|
|
284
|
+
- @weasel-js/text@1.14.0
|
|
285
|
+
- @weasel-js/stroke@1.14.0
|
|
286
|
+
- @weasel-js/paint@1.14.0
|
|
287
|
+
- @weasel-js/routing@1.14.0
|
|
288
|
+
- @weasel-js/gestures@1.14.0
|
|
289
|
+
- @weasel-js/geom@1.14.0
|
|
290
|
+
- @weasel-js/headless@1.14.0
|
|
291
|
+
- @weasel-js/quantity@1.14.0
|
|
292
|
+
- @weasel-js/text-dom@1.14.0
|
|
293
|
+
- @weasel-js/prefs@1.14.0
|
|
294
|
+
- @weasel-js/react@1.14.0
|
|
295
|
+
- @weasel-js/simulation@1.14.0
|
|
296
|
+
- @weasel-js/cursor@1.14.0
|
|
297
|
+
- @weasel-js/history@1.14.0
|
|
298
|
+
- @weasel-js/icons@1.14.0
|
|
299
|
+
- @weasel-js/modes@1.14.0
|
|
300
|
+
- @weasel-js/registry@1.14.0
|
|
301
|
+
- @weasel-js/select@1.14.0
|
|
302
|
+
|
|
3
303
|
## 1.13.0
|
|
4
304
|
|
|
5
305
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ import '@weasel-js/core/style.css';
|
|
|
37
37
|
|
|
38
38
|
`useScene` holds the scene: a tree of leaf and container nodes, each carrying your data and a pose. Every change to it is an **op**, which is what makes it undoable. `<SceneCanvas>` renders a scene and routes input to it.
|
|
39
39
|
|
|
40
|
-
A bare `<SceneCanvas>` only renders. It keeps a selection that nothing sets from input,
|
|
40
|
+
A bare `<SceneCanvas>` only renders. It keeps a selection that nothing sets from input, routes any bindings you add, and undoes and redoes whatever changed its scene (Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z), but it has no tools, no other standard actions and no selection chrome. Behavior comes from presets named in `features`, each independent of the others:
|
|
41
41
|
|
|
42
42
|
| Preset | Turns on |
|
|
43
43
|
|---|---|
|
|
@@ -49,7 +49,7 @@ A bare `<SceneCanvas>` only renders. It keeps a selection that nothing sets from
|
|
|
49
49
|
| `resize` | resize handles |
|
|
50
50
|
| `rotate` | the rotation handle |
|
|
51
51
|
| `transform` | `resize` and `rotate` |
|
|
52
|
-
| `edit` |
|
|
52
|
+
| `edit` | delete, duplicate, group/ungroup, nudge, select-all, Escape, cut/copy/paste, fill and stroke, and their keys; Cmd/Ctrl+V pastes nodes a kit canvas copied, and nothing else |
|
|
53
53
|
| `arrange` | align, distribute, reorder, flip |
|
|
54
54
|
| `paths` | pathfinder operations, path editing and anchor editing |
|
|
55
55
|
| `ingest` | dropped and pasted content: images, SVG, files, and consumer content handlers, on Cmd/Ctrl+V or an OS drop |
|
|
@@ -61,11 +61,9 @@ Interactions are **actions** — descriptors registered into an actions registry
|
|
|
61
61
|
|
|
62
62
|
```tsx
|
|
63
63
|
import { SceneCanvas, BUILTIN_TOOL_IDS, useScene, useSelection } from '@weasel-js/core';
|
|
64
|
-
import {
|
|
64
|
+
import { createGridPreset } from '@weasel-js/guides';
|
|
65
65
|
|
|
66
|
-
const
|
|
67
|
-
const grid = createGridLayer({ spacing: 20, bounds: () => ({ x: 0, y: 0, width: W, height: H }) });
|
|
68
|
-
const snap = { sources: [gridSnapSource({ spacing: 20 })] };
|
|
66
|
+
const grid = createGridPreset({ spacing: 20 });
|
|
69
67
|
|
|
70
68
|
export function Editor() {
|
|
71
69
|
const scene = useScene({ systemLayers: [{ id: 'default' }], initial: [] });
|
|
@@ -76,14 +74,12 @@ export function Editor() {
|
|
|
76
74
|
// standard keyboard actions (Cmd+A, Cmd+Z, Delete, …), all snapping to the grid:
|
|
77
75
|
return (
|
|
78
76
|
<SceneCanvas
|
|
79
|
-
width={
|
|
80
|
-
height={
|
|
77
|
+
width={800}
|
|
78
|
+
height={600}
|
|
81
79
|
scene={scene}
|
|
82
80
|
selection={selection}
|
|
83
|
-
features={['draw']}
|
|
81
|
+
features={['draw', grid]}
|
|
84
82
|
defaultTools={BUILTIN_TOOL_IDS}
|
|
85
|
-
snap={snap}
|
|
86
|
-
layers={{ grid: { layer: grid } }}
|
|
87
83
|
/>
|
|
88
84
|
);
|
|
89
85
|
}
|
|
@@ -101,7 +97,7 @@ Lower-level surfaces take a narrow **adapter** instead of a scene — a few meth
|
|
|
101
97
|
|
|
102
98
|
An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `viewport.dragPan` — paired with the input that triggers it. `<ActionsProvider>` holds the registered descriptors, and the gesture dispatcher matches live input against each one's `defaultBinding` and the active tool's bindings. Keystrokes and pointer gestures take the same path, so a keyboard shortcut and a drag are two bindings on one action rather than two mechanisms.
|
|
103
99
|
|
|
104
|
-
`<SceneCanvas>` mounts a provider when none is above it and registers the kit-standard actions its `features` presets name — under `draw`, all of them: escape, select-all, delete, duplicate, group and ungroup,
|
|
100
|
+
`<SceneCanvas>` mounts a provider when none is above it and registers undo and redo, and the kit-standard actions its `features` presets name — under `draw`, all of them: escape, select-all, delete, duplicate, group and ungroup, flip, nudge, reorder, align, distribute, the pathfinder booleans, path-anchor editing, fill and stroke, clipboard, and the pointer-driven move, resize, rotate, clone, area-select and lasso. `BASELINE_ACTION_IDS` lists the ones every canvas registers, and `FEATURE_ACTION_IDS` which preset registers which. A preset can include others: `edit` is `clipboard`, `nudge`, `group`, and the rest, each of which can be named alone, and `pick`, `transform`, and `draw` are presets of that kind. `insert` comes with the shape tools that bind it, which the `tools:shape`, `tools:draw`, `tools:type`, and `tools:page` presets mount. Each name stands for an exported `FeaturePreset` (`selectPreset`, `editPreset`, …), and `features` also takes a package's preset object; `<LeanSceneCanvas>` takes objects only, so an app's bundle holds only the presets it imports.
|
|
105
101
|
|
|
106
102
|
### Tuning an action
|
|
107
103
|
|
|
@@ -22,7 +22,7 @@ function hitAffordanceRegions(affordances, wx, wy, state, view, isVisible) {
|
|
|
22
22
|
for (let i = affordances.length - 1; i >= 0; i--) {
|
|
23
23
|
const a = affordances[i];
|
|
24
24
|
if (isVisible && !isVisible(a.id)) continue;
|
|
25
|
-
const regions = a.regions(state);
|
|
25
|
+
const regions = a.regionsAt ? a.regionsAt(state, wx, wy, view) : a.regions(state);
|
|
26
26
|
let best = null;
|
|
27
27
|
for (let j = 0; j < regions.length; j++) {
|
|
28
28
|
const region = regions[j];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hitAffordanceRegions.js","names":[],"sources":["../../src/affordances/hitAffordanceRegions.ts"],"sourcesContent":["/**\n * The one affordance hit-test walk: an affordance's `regions()` is the single\n * source of truth for where its chrome is and what a press on it means.\n */\n\nimport type { View } from 'core/viewport/view';\nimport { multiply, scale, scaleDelta, screenAngleOf, standoff, strokeHitTest } from '@weasel-js/geom';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport {\n angleOf, IDENTITY_TARGET, localToWorld, normalToWorld, pxExtentIn, targetTransformOf, worldToLocal,\n type TargetTransform,\n} from 'core/geometry/targetFrame';\nimport type { Affordance, AffordanceBinding, AffordanceRegion } from './types';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport { edgeHandlesFit } from 'interactions/actions/resize/cornerHandles';\n\n/** What the walk found: the region, plus the ids needed to describe it. */\nexport interface AffordanceRegionHit {\n /** `Affordance.id` — also the chrome-caps visibility id. */\n affordanceId: string;\n /** `AffordanceRegion.id`. */\n regionId: string;\n /** The region itself, so callers can read `hitKind`. */\n region: AffordanceRegion;\n /** The region's cursor at the hit point. */\n cursor?: CursorSpec;\n /** Result of the region's `bind()`, called exactly once. */\n binding: AffordanceBinding;\n}\n\n/**\n * Walk affordances for a world-space hit, topmost first.\n *\n * **Across affordances**: order mirrors paint. Affordances are drawn first →\n * last (bottom → top), so the walk runs last → first and stops at the first\n * one that claims the point. Layering wins outright — a nearer region on a\n * lower affordance does not beat a farther one on top of it.\n *\n * **Within an affordance**: the *nearest* hit region wins, ties going to the\n * later-declared one. Regions overlap: the four corner handles of a selection\n * smaller than the hit radius all contain the same click.\n *\n * `isVisible` (when supplied) gates by the same chrome id used at paint time,\n * so chrome that isn't drawn can't be hit either.\n */\nexport function hitAffordanceRegions(\n affordances: readonly Affordance[],\n wx: number,\n wy: number,\n state: ChromeState,\n view: View,\n isVisible?: (id: string) => boolean,\n): AffordanceRegionHit | null {\n for (let i = affordances.length - 1; i >= 0; i--) {\n const a = affordances[i];\n if (isVisible && !isVisible(a.id)) continue;\n const regions = a.regions(state);\n let best: { region: AffordanceRegion; d2: number } | null = null;\n for (let j = 0; j < regions.length; j++) {\n const region = regions[j];\n if (isVisible && region.chromeId && !isVisible(region.chromeId)) continue;\n const xf = transformOf(state, region.targetId);\n if (!hitRegion(region, wx, wy, xf, view)) continue;\n const d2 = regionDistance2(region, wx, wy, xf, view);\n // `<=` so a later-declared region wins an exact tie, preserving the\n // \"later is on top\" reading of declaration order.\n if (best === null || d2 <= best.d2) best = { region, d2 };\n }\n if (best) {\n const { cursor } = best.region;\n const at = typeof cursor === 'function' ? cursor({ x: wx, y: wy }, view) : cursor;\n return {\n affordanceId: a.id,\n regionId: best.region.id,\n region: best.region,\n ...(at !== undefined ? { cursor: at } : {}),\n binding: best.region.bind(),\n };\n }\n }\n return null;\n}\n\n/** Squared world distance from the point to a region's representative center,\n * used only to rank regions that all already contain the point. */\nfunction regionDistance2(\n region: AffordanceRegion,\n wx: number,\n wy: number,\n xf: TargetTransform,\n view: View,\n): number {\n const s = region.shape;\n if (s.kind === 'outline') return 0;\n const w = s.kind === 'point'\n ? pointRegionAnchor(s, xf, view)\n : s.kind === 'annulus'\n ? localToWorld(xf, s.cx, s.cy)\n : localToWorld(xf, s.x + s.width / 2, s.y + s.height / 2);\n const dx = w.x - wx;\n const dy = w.y - wy;\n return dx * dx + dy * dy;\n}\n\n// ─── Transform helpers ──────────────────────────────────────────────────────\n\nexport { angleOf, localToWorld, type TargetTransform };\n\n/** The frame `targetId`'s bounds are drawn in; identity for no target. */\nexport function transformOf(state: ChromeState, targetId: string | null): TargetTransform {\n if (targetId === null) return IDENTITY_TARGET;\n return targetTransformOf(state.boundsOf(targetId));\n}\n\n// ─── Shape resolution ───────────────────────────────────────────────────────\n\ntype PointShape = Extract<AffordanceRegion['shape'], { kind: 'point' }>;\n\n/** Where a point region's hit square sits: its center in world coords, its\n * turn on screen, its screen half-extent, and the span of the square that\n * takes a press along each of its own axes (`[-half, half]` unless\n * `cornerOf` cuts it). */\nexport function pointRegionFrame(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n): { x: number; y: number; angle: number; half: number; u: [number, number]; v: [number, number] } {\n const a = pointRegionAnchor(shape, xf, view);\n const angle = shape.turned ? screenAngleOf(angleOf(xf), view.scale) : 0;\n const half = shape.hitRadiusPx;\n const u: [number, number] = [-half, half];\n const v: [number, number] = [-half, half];\n const body = shape.cornerOf;\n if (body) {\n // The body in world units that spans four hit radii on screen, per axis.\n const floor = pxExtentIn(SMALL_BODY_RADII * half, view.scale, xf);\n // `v` runs a quarter turn on from `u` on screen, which is the target's +y\n // only while the view and the target's frame keep their handedness.\n const [a, b, c, d] = xf.toWorld;\n const vOut = body.outward.y * Math.sign(view.scale.x * view.scale.y * (a * d - b * c));\n if (body.width < floor.x) u[body.outward.x > 0 ? 0 : 1] = 0;\n if (body.height < floor.y) v[vOut > 0 ? 0 : 1] = 0;\n }\n return { x: a.x, y: a.y, angle, half, u, v };\n}\n\n/** Whether a point region is live in `view`: false for an edge handle whose\n * body is too small on screen to have room for one (`edgeOf`). A region that\n * is not live is neither hit nor painted. */\nexport function pointRegionLive(shape: PointShape, xf: TargetTransform, view: View): boolean {\n return !shape.edgeOf || edgeHandlesFit(shape.edgeOf, xf, view.scale, shape.hitRadiusPx);\n}\n\n/** A body under this many hit radii across gives up the inward half of each\n * corner's hit square, so there is somewhere on it that is not a handle. */\nconst SMALL_BODY_RADII = 4;\n\n/** A point region's hit square as four screen-space corners, relative to its\n * world center carried to the screen by `toScreen`. */\nexport function pointRegionScreenQuad(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n toScreen: (x: number, y: number) => [number, number],\n): { x: number; y: number }[] {\n const f = pointRegionFrame(shape, xf, view);\n const [cx, cy] = toScreen(f.x, f.y);\n const c = Math.cos(f.angle), sn = Math.sin(f.angle);\n return [[f.u[0], f.v[0]], [f.u[1], f.v[0]], [f.u[1], f.v[1]], [f.u[0], f.v[1]]].map(([u, v]) => ({\n x: cx + c * u - sn * v,\n y: cy + sn * u + c * v,\n }));\n}\n\n/** A point region's anchor in world coords, its `standoff` applied. */\nexport function pointRegionAnchor(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n): { x: number; y: number } {\n const a = localToWorld(xf, shape.x, shape.y);\n if (!shape.standoff) return a;\n return standoff(a, normalToWorld(xf, shape.standoff.normal), shape.standoff.px, view.scale);\n}\n\n/**\n * An annulus region's effective outer semi-axes, in world units, with the\n * screen-space `minBandPx` floor on band thickness applied. Paint calls it\n * too, so the visible ring and the hoverable ring are the same ring.\n */\nexport function annulusSemiAxes(\n shape: Extract<AffordanceRegion['shape'], { kind: 'annulus' }>,\n view: View,\n /** The target's frame, which decides what a screen pixel is along each of\n * its axes. */\n xf: TargetTransform = IDENTITY_TARGET,\n): { rx: number; ry: number } {\n const bandPx = shape.minBandPx ?? 0;\n if (bandPx === 0) return { rx: shape.rx, ry: shape.ry };\n const band = pxExtentIn(bandPx, view.scale, xf);\n return {\n rx: Math.max(shape.rx, shape.innerWidth / 2 + band.x),\n ry: Math.max(shape.ry, shape.innerHeight / 2 + band.y),\n };\n}\n\n// ─── Per-shape hit-test ─────────────────────────────────────────────────────\n\nfunction hitRegion(\n region: AffordanceRegion,\n wx: number,\n wy: number,\n xf: TargetTransform,\n view: View,\n): boolean {\n if (region.shape.kind === 'point') {\n // Square hit, not circular: the handle a `point` region paints is a\n // square, and a circle leaves its corners unclickable.\n //\n // Compared in screen space: testing it in the target's local frame would\n // stretch it under non-uniform zoom.\n if (!pointRegionLive(region.shape, xf, view)) return false;\n const f = pointRegionFrame(region.shape, xf, view);\n const d = scaleDelta(wx - f.x, wy - f.y, view.scale);\n const c = Math.cos(f.angle), sn = Math.sin(f.angle);\n const u = c * d.x + sn * d.y, v = -sn * d.x + c * d.y;\n return u >= f.u[0] && u <= f.u[1] && v >= f.v[0] && v <= f.v[1];\n }\n const local = worldToLocal(xf, wx, wy);\n if (!local) return false;\n if (region.shape.kind === 'outline') {\n const s = region.shape;\n return strokeHitTest(s.path, local.x, local.y, 0, {\n slop: { px: s.hitRadiusPx, transform: multiply(scale(view.scale.x, view.scale.y), xf.toWorld) },\n });\n }\n if (region.shape.kind === 'annulus') {\n const s = region.shape;\n // Outside the inner-rect cutout?\n const insideInner =\n local.x >= s.innerX && local.x <= s.innerX + s.innerWidth &&\n local.y >= s.innerY && local.y <= s.innerY + s.innerHeight;\n if (insideInner) return false;\n // Inside the outer ellipse? `((x-cx)/rx)² + ((y-cy)/ry)² ≤ 1`.\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n if (rx <= 0 || ry <= 0) return false;\n const ex = (local.x - s.cx) / rx;\n const ey = (local.y - s.cy) / ry;\n return ex * ex + ey * ey <= 1;\n }\n // rect: axis-aligned in local frame.\n const r = region.shape;\n return local.x >= r.x && local.x <= r.x + r.width\n && local.y >= r.y && local.y <= r.y + r.height;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hitAffordanceRegions.js","names":[],"sources":["../../src/affordances/hitAffordanceRegions.ts"],"sourcesContent":["/**\n * The one affordance hit-test walk: an affordance's `regions()` is the single\n * source of truth for where its chrome is and what a press on it means.\n */\n\nimport type { View } from 'core/viewport/view';\nimport { multiply, scale, scaleDelta, screenAngleOf, standoff, strokeHitTest } from '@weasel-js/geom';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport {\n angleOf, IDENTITY_TARGET, localToWorld, normalToWorld, pxExtentIn, targetTransformOf, worldToLocal,\n type TargetTransform,\n} from 'core/geometry/targetFrame';\nimport type { Affordance, AffordanceBinding, AffordanceRegion } from './types';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport { edgeHandlesFit } from 'interactions/actions/resize/cornerHandles';\n\n/** What the walk found: the region, plus the ids needed to describe it. */\nexport interface AffordanceRegionHit {\n /** `Affordance.id` — also the chrome-caps visibility id. */\n affordanceId: string;\n /** `AffordanceRegion.id`. */\n regionId: string;\n /** The region itself, so callers can read `hitKind`. */\n region: AffordanceRegion;\n /** The region's cursor at the hit point. */\n cursor?: CursorSpec;\n /** Result of the region's `bind()`, called exactly once. */\n binding: AffordanceBinding;\n}\n\n/**\n * Walk affordances for a world-space hit, topmost first.\n *\n * **Across affordances**: order mirrors paint. Affordances are drawn first →\n * last (bottom → top), so the walk runs last → first and stops at the first\n * one that claims the point. Layering wins outright — a nearer region on a\n * lower affordance does not beat a farther one on top of it.\n *\n * **Within an affordance**: the *nearest* hit region wins, ties going to the\n * later-declared one. Regions overlap: the four corner handles of a selection\n * smaller than the hit radius all contain the same click.\n *\n * `isVisible` (when supplied) gates by the same chrome id used at paint time,\n * so chrome that isn't drawn can't be hit either.\n */\nexport function hitAffordanceRegions(\n affordances: readonly Affordance[],\n wx: number,\n wy: number,\n state: ChromeState,\n view: View,\n isVisible?: (id: string) => boolean,\n): AffordanceRegionHit | null {\n for (let i = affordances.length - 1; i >= 0; i--) {\n const a = affordances[i];\n if (isVisible && !isVisible(a.id)) continue;\n const regions = a.regionsAt ? a.regionsAt(state, wx, wy, view) : a.regions(state);\n let best: { region: AffordanceRegion; d2: number } | null = null;\n for (let j = 0; j < regions.length; j++) {\n const region = regions[j];\n if (isVisible && region.chromeId && !isVisible(region.chromeId)) continue;\n const xf = transformOf(state, region.targetId);\n if (!hitRegion(region, wx, wy, xf, view)) continue;\n const d2 = regionDistance2(region, wx, wy, xf, view);\n // `<=` so a later-declared region wins an exact tie, preserving the\n // \"later is on top\" reading of declaration order.\n if (best === null || d2 <= best.d2) best = { region, d2 };\n }\n if (best) {\n const { cursor } = best.region;\n const at = typeof cursor === 'function' ? cursor({ x: wx, y: wy }, view) : cursor;\n return {\n affordanceId: a.id,\n regionId: best.region.id,\n region: best.region,\n ...(at !== undefined ? { cursor: at } : {}),\n binding: best.region.bind(),\n };\n }\n }\n return null;\n}\n\n/** Squared world distance from the point to a region's representative center,\n * used only to rank regions that all already contain the point. */\nfunction regionDistance2(\n region: AffordanceRegion,\n wx: number,\n wy: number,\n xf: TargetTransform,\n view: View,\n): number {\n const s = region.shape;\n if (s.kind === 'outline') return 0;\n const w = s.kind === 'point'\n ? pointRegionAnchor(s, xf, view)\n : s.kind === 'annulus'\n ? localToWorld(xf, s.cx, s.cy)\n : localToWorld(xf, s.x + s.width / 2, s.y + s.height / 2);\n const dx = w.x - wx;\n const dy = w.y - wy;\n return dx * dx + dy * dy;\n}\n\n// ─── Transform helpers ──────────────────────────────────────────────────────\n\nexport { angleOf, localToWorld, type TargetTransform };\n\n/** The frame `targetId`'s bounds are drawn in; identity for no target. */\nexport function transformOf(state: ChromeState, targetId: string | null): TargetTransform {\n if (targetId === null) return IDENTITY_TARGET;\n return targetTransformOf(state.boundsOf(targetId));\n}\n\n// ─── Shape resolution ───────────────────────────────────────────────────────\n\ntype PointShape = Extract<AffordanceRegion['shape'], { kind: 'point' }>;\n\n/** Where a point region's hit square sits: its center in world coords, its\n * turn on screen, its screen half-extent, and the span of the square that\n * takes a press along each of its own axes (`[-half, half]` unless\n * `cornerOf` cuts it). */\nexport function pointRegionFrame(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n): { x: number; y: number; angle: number; half: number; u: [number, number]; v: [number, number] } {\n const a = pointRegionAnchor(shape, xf, view);\n const angle = shape.turned ? screenAngleOf(angleOf(xf), view.scale) : 0;\n const half = shape.hitRadiusPx;\n const u: [number, number] = [-half, half];\n const v: [number, number] = [-half, half];\n const body = shape.cornerOf;\n if (body) {\n // The body in world units that spans four hit radii on screen, per axis.\n const floor = pxExtentIn(SMALL_BODY_RADII * half, view.scale, xf);\n // `v` runs a quarter turn on from `u` on screen, which is the target's +y\n // only while the view and the target's frame keep their handedness.\n const [a, b, c, d] = xf.toWorld;\n const vOut = body.outward.y * Math.sign(view.scale.x * view.scale.y * (a * d - b * c));\n if (body.width < floor.x) u[body.outward.x > 0 ? 0 : 1] = 0;\n if (body.height < floor.y) v[vOut > 0 ? 0 : 1] = 0;\n }\n return { x: a.x, y: a.y, angle, half, u, v };\n}\n\n/** Whether a point region is live in `view`: false for an edge handle whose\n * body is too small on screen to have room for one (`edgeOf`). A region that\n * is not live is neither hit nor painted. */\nexport function pointRegionLive(shape: PointShape, xf: TargetTransform, view: View): boolean {\n return !shape.edgeOf || edgeHandlesFit(shape.edgeOf, xf, view.scale, shape.hitRadiusPx);\n}\n\n/** A body under this many hit radii across gives up the inward half of each\n * corner's hit square, so there is somewhere on it that is not a handle. */\nconst SMALL_BODY_RADII = 4;\n\n/** A point region's hit square as four screen-space corners, relative to its\n * world center carried to the screen by `toScreen`. */\nexport function pointRegionScreenQuad(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n toScreen: (x: number, y: number) => [number, number],\n): { x: number; y: number }[] {\n const f = pointRegionFrame(shape, xf, view);\n const [cx, cy] = toScreen(f.x, f.y);\n const c = Math.cos(f.angle), sn = Math.sin(f.angle);\n return [[f.u[0], f.v[0]], [f.u[1], f.v[0]], [f.u[1], f.v[1]], [f.u[0], f.v[1]]].map(([u, v]) => ({\n x: cx + c * u - sn * v,\n y: cy + sn * u + c * v,\n }));\n}\n\n/** A point region's anchor in world coords, its `standoff` applied. */\nexport function pointRegionAnchor(\n shape: PointShape,\n xf: TargetTransform,\n view: View,\n): { x: number; y: number } {\n const a = localToWorld(xf, shape.x, shape.y);\n if (!shape.standoff) return a;\n return standoff(a, normalToWorld(xf, shape.standoff.normal), shape.standoff.px, view.scale);\n}\n\n/**\n * An annulus region's effective outer semi-axes, in world units, with the\n * screen-space `minBandPx` floor on band thickness applied. Paint calls it\n * too, so the visible ring and the hoverable ring are the same ring.\n */\nexport function annulusSemiAxes(\n shape: Extract<AffordanceRegion['shape'], { kind: 'annulus' }>,\n view: View,\n /** The target's frame, which decides what a screen pixel is along each of\n * its axes. */\n xf: TargetTransform = IDENTITY_TARGET,\n): { rx: number; ry: number } {\n const bandPx = shape.minBandPx ?? 0;\n if (bandPx === 0) return { rx: shape.rx, ry: shape.ry };\n const band = pxExtentIn(bandPx, view.scale, xf);\n return {\n rx: Math.max(shape.rx, shape.innerWidth / 2 + band.x),\n ry: Math.max(shape.ry, shape.innerHeight / 2 + band.y),\n };\n}\n\n// ─── Per-shape hit-test ─────────────────────────────────────────────────────\n\nfunction hitRegion(\n region: AffordanceRegion,\n wx: number,\n wy: number,\n xf: TargetTransform,\n view: View,\n): boolean {\n if (region.shape.kind === 'point') {\n // Square hit, not circular: the handle a `point` region paints is a\n // square, and a circle leaves its corners unclickable.\n //\n // Compared in screen space: testing it in the target's local frame would\n // stretch it under non-uniform zoom.\n if (!pointRegionLive(region.shape, xf, view)) return false;\n const f = pointRegionFrame(region.shape, xf, view);\n const d = scaleDelta(wx - f.x, wy - f.y, view.scale);\n const c = Math.cos(f.angle), sn = Math.sin(f.angle);\n const u = c * d.x + sn * d.y, v = -sn * d.x + c * d.y;\n return u >= f.u[0] && u <= f.u[1] && v >= f.v[0] && v <= f.v[1];\n }\n const local = worldToLocal(xf, wx, wy);\n if (!local) return false;\n if (region.shape.kind === 'outline') {\n const s = region.shape;\n return strokeHitTest(s.path, local.x, local.y, 0, {\n slop: { px: s.hitRadiusPx, transform: multiply(scale(view.scale.x, view.scale.y), xf.toWorld) },\n });\n }\n if (region.shape.kind === 'annulus') {\n const s = region.shape;\n // Outside the inner-rect cutout?\n const insideInner =\n local.x >= s.innerX && local.x <= s.innerX + s.innerWidth &&\n local.y >= s.innerY && local.y <= s.innerY + s.innerHeight;\n if (insideInner) return false;\n // Inside the outer ellipse? `((x-cx)/rx)² + ((y-cy)/ry)² ≤ 1`.\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n if (rx <= 0 || ry <= 0) return false;\n const ex = (local.x - s.cx) / rx;\n const ey = (local.y - s.cy) / ry;\n return ex * ex + ey * ey <= 1;\n }\n // rect: axis-aligned in local frame.\n const r = region.shape;\n return local.x >= r.x && local.x <= r.x + r.width\n && local.y >= r.y && local.y <= r.y + r.height;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathAnchors.js","names":[],"sources":["../../src/affordances/pathAnchors.ts"],"sourcesContent":["/** Path anchor, control-handle, and segment affordances, and the parts path-edit extensions add. */\n\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { Affordance, AffordanceBinding, AffordanceRegion, CommonAffordanceScratch } from './types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport { enumerateAnchors } from 'interactions/actions/edit-anchors/geometry';\nimport type { PathEditExtension } from 'features/paths/pathEditExtension';\n\n/**\n * Live anchor-editing state. The thunk that returns it is called on every\n * pointer event, so it must be cheap (O(1) field reads).\n */\nexport interface AnchorState {\n /** Id of the path currently in anchor-edit mode, or `null`. Control handles\n * are only hittable on the path that is being edited. */\n editingId: string | null;\n /** Current editable path for a node id. Non-polygon values are ignored. */\n getPose(id: string): unknown;\n /** The committed pose and data of a node, which extensions' parts read. */\n getNode?(id: string): { pose: unknown; data: unknown } | null;\n /** The path-edit extensions whose parts are pressed, in order. */\n extensions?: readonly PathEditExtension[];\n /** The active path-edit tool, if any. While one is set, anchors and controls\n * carry no cursor of their own, so the tool's action shows its own. */\n tool?: string | null;\n /** The path whose outline counts as a segment press, when it is not\n * `getPose`'s: a path drawn from a form, edited through the form's edit\n * path. */\n getOutline?(id: string): unknown;\n}\n\n/** Options for the path-anchor affordances. */\nexport interface PathAnchorAffordanceOptions {\n /** Hit radius (screen-px) for anchor and control points. Default 8. */\n hitRadius?: number;\n /** Cursor while hovering an anchor or control, with no path-edit tool\n * active. Defaults to `'pointer'`. */\n cursor?: CursorSpec;\n}\n\n/** What an anchor hit hands to the action that follows. */\nexport interface AnchorScratch extends CommonAffordanceScratch {\n /** Node id of the path the anchor belongs to. */\n targetId: string;\n /** Sequential anchor index in path walk order. */\n anchorIndex: number;\n /** Which of the anchor's three grabbable points this is. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n}\n\n/** Chrome-caps id gating the anchor, control, and segment affordances. */\nexport const PATH_ANCHOR_CHROME_ID = 'path-edit.anchors';\n\n/**\n * Segment, extension-part, anchor, and control affordances for the selected\n * paths, bottom → top.\n *\n * Three affordances rather than one, so that a control handle beats an anchor\n * regardless of which is nearer, the way controls draw on top, and both beat\n * the segment they sit on. Within each, nearest wins. A press on the edited\n * path's outline is a `segment` hit, which the segment bend binds to and a\n * selection move turns down. Anchors, controls, and segments share one id:\n * chrome-caps gates anchor editing as a single piece of chrome. A part a\n * path-edit extension adds (`PathEditExtension.parts`) may name a chrome id of\n * its own as well, so it can be shown apart from the anchors.\n *\n * Order matters — spread this into an affordance list in the returned order.\n */\nexport function createPathAnchorAffordances(\n getAnchorState: () => AnchorState | null,\n opts: PathAnchorAffordanceOptions = {},\n): Affordance[] {\n const { hitRadius = 8, cursor = 'pointer' } = opts;\n\n /** Selected polygon paths, and the one being edited, paired with their node id. */\n const editablePaths = (state: ChromeState): Array<{ id: string; path: PolygonPath }> => {\n const anchorState = getAnchorState();\n if (!anchorState) return [];\n const out: Array<{ id: string; path: PolygonPath }> = [];\n for (const id of withEdited(state.selection, anchorState.editingId)) {\n const pose = anchorState.getPose(id);\n if (!pose || (pose as { kind?: string }).kind !== 'polygon') continue;\n out.push({ id, path: pose as PolygonPath });\n }\n return out;\n };\n\n const point = (\n id: string,\n targetId: string,\n x: number,\n y: number,\n scratch: AnchorScratch,\n hitKind: string,\n ): AffordanceRegion => ({\n id,\n // Anchor coordinates are already world space, pose rotation baked in.\n // Anchoring to `null` keeps the framework from applying it twice.\n targetId: null,\n shape: { kind: 'point', x, y, hitRadiusPx: hitRadius },\n hitKind,\n ...(getAnchorState()?.tool ? {} : { cursor }),\n bind: (): AffordanceBinding => ({ initialScratch: { ...scratch, targetId } }),\n });\n\n const anchors: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // `editAnchorsAction` edits whatever path the dep says is in edit\n // mode, ignoring which one was hit — so once a path IS being edited,\n // only its own anchors may claim the press.\n if (editingId !== null && id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n out.push(point(\n `${id}:anchor:${a.anchorIndex}`,\n id, a.x, a.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'anchor' },\n `anchor:${a.anchorIndex}`,\n ));\n }\n }\n return out;\n },\n };\n\n const controls: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n if (editingId === null) return [];\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // Controls are grabbable only on the path actually in edit mode.\n if (id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n if (a.controlIn) {\n out.push(point(\n `${id}:controlIn:${a.anchorIndex}`,\n id, a.controlIn.x, a.controlIn.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlIn' },\n `controlIn:${a.anchorIndex}`,\n ));\n }\n if (a.controlOut) {\n out.push(point(\n `${id}:controlOut:${a.anchorIndex}`,\n id, a.controlOut.x, a.controlOut.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlOut' },\n `controlOut:${a.anchorIndex}`,\n ));\n }\n }\n }\n return out;\n },\n };\n\n const segments: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n if (editingId === null) return [];\n const edited = editablePaths(state).find(({ id }) => id === editingId);\n if (!edited) return [];\n const outline = getAnchorState()?.getOutline?.(editingId) as PolygonPath | null | undefined;\n return [{\n id: `${editingId}:segment`,\n targetId: null,\n shape: { kind: 'outline', path: outline?.kind === 'polygon' ? outline : edited.path, hitRadiusPx: hitRadius },\n hitKind: 'segment',\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n }];\n },\n };\n\n return [segments, extensionParts(getAnchorState, hitRadius, cursor), anchors, controls];\n}\n\n/** The parts path-edit extensions add to the path in edit mode, each pressed\n * as its own `hitKind`. */\nfunction extensionParts(\n getAnchorState: () => AnchorState | null,\n hitRadius: number,\n cursor: CursorSpec,\n): Affordance {\n return {\n id: PATH_ANCHOR_CHROME_ID,\n regions() {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n const extensions = anchorState?.extensions?.filter((e) => e.parts) ?? [];\n if (editingId === null || extensions.length === 0) return [];\n const pose = anchorState!.getPose(editingId);\n const node = anchorState!.getNode?.(editingId);\n if (!node || !pose || (pose as { kind?: string }).kind !== 'polygon') return [];\n const target = {\n id: editingId, node, path: pose as PolygonPath, anchors: pathToAnchors(pose as PolygonPath) as AnchorSet,\n };\n return extensions.flatMap((ext) => ext.parts!(target).map((part): AffordanceRegion => ({\n id: `${editingId}:${part.hitKind}`,\n targetId: null,\n shape: {\n kind: 'point', x: part.x, y: part.y, hitRadiusPx: part.hitRadiusPx ?? hitRadius,\n ...(part.standoff ? { standoff: part.standoff } : {}),\n },\n hitKind: part.hitKind,\n ...(part.chromeId ? { chromeId: part.chromeId } : {}),\n cursor,\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n })));\n },\n };\n}\n\n/** `selection`, and the path being edited after it when the selection does not hold it: a path edited through the\n * effect that hides it is edited while the effect is what is selected. */\nfunction withEdited(selection: readonly string[], editingId: string | null): readonly string[] {\n return editingId === null || selection.includes(editingId) ? selection : [...selection, editingId];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pathAnchors.js","names":[],"sources":["../../src/affordances/pathAnchors.ts"],"sourcesContent":["/** Path anchor, control-handle, and segment affordances, and the parts path-edit extensions add. */\n\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { Affordance, AffordanceBinding, AffordanceRegion, CommonAffordanceScratch } from './types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport { enumerateAnchors } from 'interactions/actions/edit-anchors/geometry';\nimport type { PathEditExtension } from 'domains/paths/pathEditExtension';\n\n/**\n * Live anchor-editing state. The thunk that returns it is called on every\n * pointer event, so it must be cheap (O(1) field reads).\n */\nexport interface AnchorState {\n /** Id of the path currently in anchor-edit mode, or `null`. Control handles\n * are only hittable on the path that is being edited. */\n editingId: string | null;\n /** Current editable path for a node id. Non-polygon values are ignored. */\n getPose(id: string): unknown;\n /** The committed pose and data of a node, which extensions' parts read. */\n getNode?(id: string): { pose: unknown; data: unknown } | null;\n /** The path-edit extensions whose parts are pressed, in order. */\n extensions?: readonly PathEditExtension[];\n /** The active path-edit tool, if any. While one is set, anchors and controls\n * carry no cursor of their own, so the tool's action shows its own. */\n tool?: string | null;\n /** The path whose outline counts as a segment press, when it is not\n * `getPose`'s: a path drawn from a form, edited through the form's edit\n * path. */\n getOutline?(id: string): unknown;\n}\n\n/** Options for the path-anchor affordances. */\nexport interface PathAnchorAffordanceOptions {\n /** Hit radius (screen-px) for anchor and control points. Default 8. */\n hitRadius?: number;\n /** Cursor while hovering an anchor or control, with no path-edit tool\n * active. Defaults to `'pointer'`. */\n cursor?: CursorSpec;\n}\n\n/** What an anchor hit hands to the action that follows. */\nexport interface AnchorScratch extends CommonAffordanceScratch {\n /** Node id of the path the anchor belongs to. */\n targetId: string;\n /** Sequential anchor index in path walk order. */\n anchorIndex: number;\n /** Which of the anchor's three grabbable points this is. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n}\n\n/** Chrome-caps id gating the anchor, control, and segment affordances. */\nexport const PATH_ANCHOR_CHROME_ID = 'path-edit.anchors';\n\n/**\n * Segment, extension-part, anchor, and control affordances for the selected\n * paths, bottom → top.\n *\n * Three affordances rather than one, so that a control handle beats an anchor\n * regardless of which is nearer, the way controls draw on top, and both beat\n * the segment they sit on. Within each, nearest wins. A press on the edited\n * path's outline is a `segment` hit, which the segment bend binds to and a\n * selection move turns down. Anchors, controls, and segments share one id:\n * chrome-caps gates anchor editing as a single piece of chrome. A part a\n * path-edit extension adds (`PathEditExtension.parts`) may name a chrome id of\n * its own as well, so it can be shown apart from the anchors.\n *\n * Order matters — spread this into an affordance list in the returned order.\n */\nexport function createPathAnchorAffordances(\n getAnchorState: () => AnchorState | null,\n opts: PathAnchorAffordanceOptions = {},\n): Affordance[] {\n const { hitRadius = 8, cursor = 'pointer' } = opts;\n\n /** Selected polygon paths, and the one being edited, paired with their node id. */\n const editablePaths = (state: ChromeState): Array<{ id: string; path: PolygonPath }> => {\n const anchorState = getAnchorState();\n if (!anchorState) return [];\n const out: Array<{ id: string; path: PolygonPath }> = [];\n for (const id of withEdited(state.selection, anchorState.editingId)) {\n const pose = anchorState.getPose(id);\n if (!pose || (pose as { kind?: string }).kind !== 'polygon') continue;\n out.push({ id, path: pose as PolygonPath });\n }\n return out;\n };\n\n const point = (\n id: string,\n targetId: string,\n x: number,\n y: number,\n scratch: AnchorScratch,\n hitKind: string,\n ): AffordanceRegion => ({\n id,\n // Anchor coordinates are already world space, pose rotation baked in.\n // Anchoring to `null` keeps the framework from applying it twice.\n targetId: null,\n shape: { kind: 'point', x, y, hitRadiusPx: hitRadius },\n hitKind,\n ...(getAnchorState()?.tool ? {} : { cursor }),\n bind: (): AffordanceBinding => ({ initialScratch: { ...scratch, targetId } }),\n });\n\n const anchors: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // `editAnchorsAction` edits whatever path the dep says is in edit\n // mode, ignoring which one was hit — so once a path IS being edited,\n // only its own anchors may claim the press.\n if (editingId !== null && id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n out.push(point(\n `${id}:anchor:${a.anchorIndex}`,\n id, a.x, a.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'anchor' },\n `anchor:${a.anchorIndex}`,\n ));\n }\n }\n return out;\n },\n };\n\n const controls: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n if (editingId === null) return [];\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // Controls are grabbable only on the path actually in edit mode.\n if (id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n if (a.controlIn) {\n out.push(point(\n `${id}:controlIn:${a.anchorIndex}`,\n id, a.controlIn.x, a.controlIn.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlIn' },\n `controlIn:${a.anchorIndex}`,\n ));\n }\n if (a.controlOut) {\n out.push(point(\n `${id}:controlOut:${a.anchorIndex}`,\n id, a.controlOut.x, a.controlOut.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlOut' },\n `controlOut:${a.anchorIndex}`,\n ));\n }\n }\n }\n return out;\n },\n };\n\n const segments: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n if (editingId === null) return [];\n const edited = editablePaths(state).find(({ id }) => id === editingId);\n if (!edited) return [];\n const outline = getAnchorState()?.getOutline?.(editingId) as PolygonPath | null | undefined;\n return [{\n id: `${editingId}:segment`,\n targetId: null,\n shape: { kind: 'outline', path: outline?.kind === 'polygon' ? outline : edited.path, hitRadiusPx: hitRadius },\n hitKind: 'segment',\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n }];\n },\n };\n\n return [segments, extensionParts(getAnchorState, hitRadius, cursor), anchors, controls];\n}\n\n/** The parts path-edit extensions add to the path in edit mode, each pressed\n * as its own `hitKind`. */\nfunction extensionParts(\n getAnchorState: () => AnchorState | null,\n hitRadius: number,\n cursor: CursorSpec,\n): Affordance {\n return {\n id: PATH_ANCHOR_CHROME_ID,\n regions() {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n const extensions = anchorState?.extensions?.filter((e) => e.parts) ?? [];\n if (editingId === null || extensions.length === 0) return [];\n const pose = anchorState!.getPose(editingId);\n const node = anchorState!.getNode?.(editingId);\n if (!node || !pose || (pose as { kind?: string }).kind !== 'polygon') return [];\n const target = {\n id: editingId, node, path: pose as PolygonPath, anchors: pathToAnchors(pose as PolygonPath) as AnchorSet,\n };\n return extensions.flatMap((ext) => ext.parts!(target).map((part): AffordanceRegion => ({\n id: `${editingId}:${part.hitKind}`,\n targetId: null,\n shape: {\n kind: 'point', x: part.x, y: part.y, hitRadiusPx: part.hitRadiusPx ?? hitRadius,\n ...(part.standoff ? { standoff: part.standoff } : {}),\n },\n hitKind: part.hitKind,\n ...(part.chromeId ? { chromeId: part.chromeId } : {}),\n cursor,\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n })));\n },\n };\n}\n\n/** `selection`, and the path being edited after it when the selection does not hold it: a path edited through the\n * effect that hides it is edited while the effect is what is selected. */\nfunction withEdited(selection: readonly string[], editingId: string | null): readonly string[] {\n return editingId === null || selection.includes(editingId) ? selection : [...selection, editingId];\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import { scenePoseTarget } from "./poseTarget.js";
|
|
|
9
9
|
*/
|
|
10
10
|
function sceneClipTargets(scene, options = {}) {
|
|
11
11
|
return (name) => {
|
|
12
|
-
if (name.startsWith("pose:")) return scenePoseTarget(scene, name.slice(5), options.descriptor
|
|
12
|
+
if (name.startsWith("pose:")) return scenePoseTarget(scene, name.slice(5), options.descriptor);
|
|
13
13
|
if (name.startsWith("rig:")) return options.rigs?.[name.slice(4)]?.target;
|
|
14
14
|
};
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipTargets.js","names":[],"sources":["../../src/animation/clipTargets.ts"],"sourcesContent":["import type { ClipBinder, Pose, Target } from '@weasel-js/animator';\nimport type { PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport { scenePoseTarget, type
|
|
1
|
+
{"version":3,"file":"clipTargets.js","names":[],"sources":["../../src/animation/clipTargets.ts"],"sourcesContent":["import type { ClipBinder, Pose, Target } from '@weasel-js/animator';\nimport type { PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport { scenePoseTarget, type PoseTargetScene } from './poseTarget';\n\n/** Options for `sceneClipTargets`. */\nexport interface SceneClipTargetsOptions<TPose> {\n /** Rigs by the name a clip calls them: a track naming `rig:<name>` moves that rig's pose. */\n rigs?: Readonly<Record<string, { readonly target: Target<Pose> }>>;\n /** As `scenePoseTarget` takes them. */\n descriptor?: PoseDescriptor<TPose>;\n}\n\n/**\n * A `ClipBinder.target` over a scene. `pose:<node id>` is that node's pose target, the one\n * `scenePoseTarget` answers, and `rig:<name>` is the target of the rig given under that\n * name. Any other name is `undefined`, for a caller to resolve itself first or after. A track\n * over part of a rig names joints as its `axes`, which the rig's target resolves.\n */\nexport function sceneClipTargets<TPose>(\n scene: PoseTargetScene<TPose>,\n options: SceneClipTargetsOptions<TPose> = {},\n): NonNullable<ClipBinder['target']> {\n return (name) => {\n if (name.startsWith('pose:')) return scenePoseTarget(scene, name.slice(5), options.descriptor);\n if (name.startsWith('rig:')) return options.rigs?.[name.slice(4)]?.target;\n return undefined;\n };\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAgB,iBACd,OACA,UAA0C,CAAC,GACR;CACnC,QAAQ,SAAS;EACf,IAAI,KAAK,WAAW,OAAO,GAAG,OAAO,gBAAgB,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ,UAAU;EAC7F,IAAI,KAAK,WAAW,MAAM,GAAG,OAAO,QAAQ,OAAO,KAAK,MAAM,CAAC,EAAE,EAAE;CAErE;AACF"}
|