@weasel-js/core 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +168 -0
- package/README.md +33 -0
- package/dist/affordances/composeAffordanceLayer.js +21 -21
- package/dist/affordances/composeAffordanceLayer.js.map +1 -1
- package/dist/affordances/hitAffordanceRegions.js +48 -32
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/pathAnchors.js +50 -28
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/affordances/rotateCursor.js +12 -0
- package/dist/affordances/rotateCursor.js.map +1 -0
- package/dist/affordances/rotationHandle.js +40 -43
- package/dist/affordances/rotationHandle.js.map +1 -1
- package/dist/animation/rig/rigNode.js +9 -3
- package/dist/animation/rig/rigNode.js.map +1 -1
- package/dist/animation/rig/rigSeat.js +46 -13
- package/dist/animation/rig/rigSeat.js.map +1 -1
- package/dist/animation/rig/skinBinding.js +22 -14
- package/dist/animation/rig/skinBinding.js.map +1 -1
- package/dist/barrel/canvas.js +29 -28
- package/dist/barrel/interactions.js +33 -32
- package/dist/barrel/paths.js +11 -10
- package/dist/barrel/routing.js +49 -48
- package/dist/barrel/scene.js +10 -10
- package/dist/barrel/viewport.js +24 -21
- package/dist/canvas/Canvas.js +6 -17
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +34 -37
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/CursorCoordsHud.js +3 -3
- package/dist/canvas/CursorCoordsHud.js.map +1 -1
- package/dist/canvas/DrawCanvas.js +3 -3
- package/dist/canvas/DrawCanvas.js.map +1 -1
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +24 -23
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +40 -49
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +297 -259
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +3 -24
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/insertPreviewCommands.js +97 -0
- package/dist/canvas/SceneCanvas/insertPreviewCommands.js.map +1 -0
- package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +50 -0
- package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -0
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +52 -0
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -0
- package/dist/canvas/SceneCanvas/useActionTunings.js +17 -0
- package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -0
- package/dist/canvas/SceneCanvas/useCanvasCamera.js +37 -29
- package/dist/canvas/SceneCanvas/useCanvasCamera.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasDispatcher.js +11 -11
- package/dist/canvas/SceneCanvas/useCanvasDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +45 -109
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +49 -41
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +64 -96
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/worldPointAt.js +3 -4
- package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +77 -80
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +3 -3
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/SurfaceCanvas.js +40 -0
- package/dist/canvas/SurfaceCanvas.js.map +1 -0
- package/dist/canvas/adapterDerivedPath.js +13 -13
- package/dist/canvas/adapterDerivedPath.js.map +1 -1
- package/dist/canvas/adapterPickSource.js +34 -0
- package/dist/canvas/adapterPickSource.js.map +1 -0
- package/dist/canvas/affordanceAt.js +1 -1
- package/dist/canvas/affordanceAt.js.map +1 -1
- package/dist/canvas/buildSceneLayer.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +56 -34
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/copiesAbove.js +27 -0
- package/dist/canvas/copiesAbove.js.map +1 -0
- package/dist/canvas/defaultDrawOne.js +24 -23
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/editAnchors.js +45 -50
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/hitTestArea.js +30 -15
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +134 -100
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/layout.js +9 -15
- package/dist/canvas/deps/layout.js.map +1 -1
- package/dist/canvas/derivedPath.js +53 -29
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/insertPreviewExtent.js +33 -31
- package/dist/canvas/insertPreviewExtent.js.map +1 -1
- package/dist/canvas/overChildren.js +29 -0
- package/dist/canvas/overChildren.js.map +1 -0
- package/dist/canvas/paintedCursorLayer.js.map +1 -1
- package/dist/canvas/pickReach.js +23 -0
- package/dist/canvas/pickReach.js.map +1 -0
- package/dist/canvas/pickWalk.js +51 -80
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +14 -35
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +11 -10
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +38 -44
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/useCanvasView.js +42 -44
- package/dist/canvas/useCanvasView.js.map +1 -1
- package/dist/canvas/useSceneAdapter.js +22 -0
- package/dist/canvas/useSceneAdapter.js.map +1 -0
- package/dist/canvas/useSurfaceInput.js +13 -16
- package/dist/canvas/useSurfaceInput.js.map +1 -1
- package/dist/canvas/viewRegistry.js +31 -23
- package/dist/canvas/viewRegistry.js.map +1 -1
- package/dist/canvas/wrapNodeOutput.js +7 -2
- package/dist/canvas/wrapNodeOutput.js.map +1 -1
- package/dist/clipboard.d.ts +1 -1
- package/dist/{clone-2a5hVbBd.d.ts → clone-C0eADtS0.d.ts} +13 -13
- package/dist/clone.d.ts +2 -2
- package/dist/core/geometry/poseDescriptor.js +49 -23
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/regionHit.js +2 -2
- package/dist/core/geometry/regionHit.js.map +1 -1
- package/dist/core/geometry/targetFrame.js.map +1 -1
- package/dist/core/layers/children.js +1 -3
- package/dist/core/layers/children.js.map +1 -1
- package/dist/core/ops/create.js +2 -2
- package/dist/core/ops/create.js.map +1 -1
- package/dist/core/ops/delete.js +19 -23
- package/dist/core/ops/delete.js.map +1 -1
- package/dist/core/ops/reorder/index.js +3 -7
- package/dist/core/ops/reorder/index.js.map +1 -1
- package/dist/core/ops/reorder/setChildOrder.js +2 -4
- package/dist/core/ops/reorder/setChildOrder.js.map +1 -1
- package/dist/core/ops/reparent.js +0 -1
- package/dist/core/ops/reparent.js.map +1 -1
- package/dist/core/ops/slot.js +3 -6
- package/dist/core/ops/slot.js.map +1 -1
- package/dist/core/pages/fitViewToPage.js +15 -0
- package/dist/core/pages/fitViewToPage.js.map +1 -0
- package/dist/core/pages/pages.js +65 -0
- package/dist/core/pages/pages.js.map +1 -0
- package/dist/core/pages/pagesLayer.js +27 -0
- package/dist/core/pages/pagesLayer.js.map +1 -0
- package/dist/core/paint/meshKindLazy.js +2 -2
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/composeFrame.js.map +1 -1
- package/dist/core/scene/composePose.js +58 -20
- package/dist/core/scene/composePose.js.map +1 -1
- package/dist/core/scene/effectivePose.js +5 -1
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/historyFacade.js +19 -13
- package/dist/core/scene/historyFacade.js.map +1 -1
- package/dist/core/scene/nodeFnFields.js +7 -0
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/outerOps.js +2 -1
- package/dist/core/scene/outerOps.js.map +1 -1
- package/dist/core/scene/reconcileSpecs.js +1 -1
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/sceneRecording.js +2 -1
- package/dist/core/scene/sceneRecording.js.map +1 -1
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/viewport/screenSlop.js.map +1 -1
- package/dist/core/viewport/useDecayLoop.js +28 -30
- package/dist/core/viewport/useDecayLoop.js.map +1 -1
- package/dist/core/viewport/useNodeOverlayFrame.js.map +1 -1
- package/dist/core/viewport/useViewAnimation.js +70 -69
- package/dist/core/viewport/useViewAnimation.js.map +1 -1
- package/dist/core/viewport/view.js +8 -5
- package/dist/core/viewport/view.js.map +1 -1
- package/dist/core/viewport/zoomAt.js +11 -2
- package/dist/core/viewport/zoomAt.js.map +1 -1
- package/dist/{cornerHandles-BEshc4z3.d.ts → cornerHandles-BSKbyuvQ.d.ts} +1 -1
- package/dist/features/paths/editablePath.js +24 -8
- package/dist/features/paths/editablePath.js.map +1 -1
- package/dist/features/paths/index.js +4 -4
- package/dist/features/paths/shapeGenerator.js +89 -0
- package/dist/features/paths/shapeGenerator.js.map +1 -0
- package/dist/features/paths/usePathEdit.js +13 -13
- package/dist/features/poseRun/usePoseRun.js +25 -25
- package/dist/features/poseRun/usePoseRun.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +82 -64
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/viewports/viewResolver.js +28 -24
- package/dist/features/viewports/viewResolver.js.map +1 -1
- package/dist/icons/SliceIcon.js +24 -0
- package/dist/icons/SliceIcon.js.map +1 -0
- package/dist/index.d.ts +956 -581
- package/dist/index.js +260 -251
- package/dist/interactions/actions/align/align.js +9 -9
- package/dist/interactions/actions/align/align.js.map +1 -1
- package/dist/interactions/actions/booleans/booleans.js +18 -18
- package/dist/interactions/actions/booleans/booleans.js.map +1 -1
- package/dist/interactions/actions/booleans/operands.js.map +1 -1
- package/dist/interactions/actions/clone/action.js +35 -30
- package/dist/interactions/actions/clone/action.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +56 -59
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorHandles.js +42 -0
- package/dist/interactions/actions/defaults/anchorHandles.js.map +1 -0
- package/dist/interactions/actions/defaults/anchorJoin.js +92 -0
- package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -0
- package/dist/interactions/actions/defaults/bendSegment.js +46 -0
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -0
- package/dist/interactions/actions/defaults/copySubtree.js +1 -1
- package/dist/interactions/actions/defaults/copySubtree.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +32 -17
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js +19 -7
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/icons/anchorIcons.js +75 -0
- package/dist/interactions/actions/defaults/icons/anchorIcons.js.map +1 -0
- package/dist/interactions/actions/defaults/icons/editIcons.js +35 -8
- package/dist/interactions/actions/defaults/icons/editIcons.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +16 -31
- package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +28 -23
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/pinchZoom.js +29 -29
- package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
- package/dist/interactions/actions/defaults/segmentUnder.js +22 -0
- package/dist/interactions/actions/defaults/segmentUnder.js.map +1 -0
- package/dist/interactions/actions/defaults/viewportZoom.js +82 -79
- package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
- package/dist/interactions/actions/distribute/distribute.js +8 -8
- package/dist/interactions/actions/distribute/distribute.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/action.js +58 -55
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js +26 -0
- package/dist/interactions/actions/edit-anchors/preview.js.map +1 -0
- package/dist/interactions/actions/edit-anchors/symmetry.js +11 -0
- package/dist/interactions/actions/edit-anchors/symmetry.js.map +1 -0
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/geometryProjection.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +37 -30
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/frame.js +9 -2
- package/dist/interactions/actions/insert/frame.js.map +1 -1
- package/dist/interactions/actions/insert/repositionAction.js +31 -0
- package/dist/interactions/actions/insert/repositionAction.js.map +1 -0
- package/dist/interactions/actions/lasso-select/action.js +32 -32
- package/dist/interactions/actions/move/action.js +29 -25
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +12 -6
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/capture.js +32 -23
- package/dist/interactions/actions/move/capture.js.map +1 -1
- package/dist/interactions/actions/move/frame.js +2 -14
- package/dist/interactions/actions/move/frame.js.map +1 -1
- package/dist/interactions/actions/move/session.js +1 -1
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/move/translate.js.map +1 -1
- package/dist/interactions/actions/outlines/createOutlines.js.map +1 -1
- package/dist/interactions/actions/outlines/replaceWithPaths.js +13 -13
- package/dist/interactions/actions/outlines/replaceWithPaths.js.map +1 -1
- package/dist/interactions/actions/outlines/strokeRegion.js.map +1 -1
- package/dist/interactions/actions/pathCommands/cutJoin.js +102 -0
- package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -0
- package/dist/interactions/actions/pathCommands/livePathEdit.js +64 -0
- package/dist/interactions/actions/pathCommands/livePathEdit.js.map +1 -0
- package/dist/interactions/actions/pathCommands/pathNodes.js +99 -0
- package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -0
- package/dist/interactions/actions/pathCommands/simplifyOffset.js +42 -0
- package/dist/interactions/actions/pathCommands/simplifyOffset.js.map +1 -0
- package/dist/interactions/actions/resize/action.js +31 -20
- package/dist/interactions/actions/resize/action.js.map +1 -1
- package/dist/interactions/actions/resize/commit.js.map +1 -1
- package/dist/interactions/actions/resize/frame.js +29 -15
- package/dist/interactions/actions/resize/frame.js.map +1 -1
- package/dist/interactions/actions/resize/replay.js +47 -0
- package/dist/interactions/actions/resize/replay.js.map +1 -0
- package/dist/interactions/actions/resize/session.js +49 -35
- package/dist/interactions/actions/resize/session.js.map +1 -1
- package/dist/interactions/actions/resolveOverlays.js +2 -1
- package/dist/interactions/actions/resolveOverlays.js.map +1 -1
- package/dist/interactions/actions/rotate/action.js +40 -25
- 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/replay.js +22 -0
- package/dist/interactions/actions/rotate/replay.js.map +1 -0
- package/dist/interactions/actions/rotate/session.js +42 -32
- package/dist/interactions/actions/rotate/session.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +63 -0
- package/dist/interactions/actions/transform-again/action.js.map +1 -0
- package/dist/interactions/actions/transform-again/memory.js +35 -0
- package/dist/interactions/actions/transform-again/memory.js.map +1 -0
- package/dist/interactions/actions/useStandardActions.js +101 -83
- package/dist/interactions/actions/useStandardActions.js.map +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/{originProjection-PPT88jZv.d.ts → originProjection-CoNVpITV.d.ts} +1 -1
- package/dist/resize.d.ts +3 -3
- package/dist/style.css +1 -1
- package/dist/tools/builtin/ellipse/useEllipseTool.js +16 -15
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js +16 -15
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +111 -111
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/pickTopMostHit.js +11 -14
- package/dist/tools/builtin/pickTopMostHit.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +17 -15
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/rect/useRectTool.js +15 -14
- package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
- package/dist/tools/builtin/select/adapterPointQuery.js +22 -18
- package/dist/tools/builtin/select/adapterPointQuery.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 +2 -1
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/slice/useSliceTool.js +42 -40
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +18 -16
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +2 -2
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/{types-CIKDwlsP.d.ts → types-DBbbCy0i.d.ts} +22 -15
- package/dist/version.js +1 -1
- package/package.json +22 -22
- package/dist/canvas/surface.module.js +0 -6
- package/dist/canvas/surface.module.js.map +0 -1
- package/dist/canvas/surfaceBox.js +0 -16
- package/dist/canvas/surfaceBox.js.map +0 -1
- package/dist/layout/arrivals.js +0 -33
- package/dist/layout/arrivals.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultNodeProperties.js","names":["rotationDegreesUnit","prefUnit","ANGLE_RADIANS","strokeWidthOf","w","stroke","strokeDashEncoding","strokeDashStyleOf","dash","style","dashForStrokeStyle","markerEncoding","ref","value","italicEncoding","stored","on","alignEncoding","direction","resolveAlign","edge","markerOptionsCache","markerOptions","generation","markerGeneration","listMarkers","m","markerLeaf","leaf","FILL_LEAF","shapeSchema","opts","defaultNodeProperties","KIT_SHAPE_KINDS","name","inferredNodeProperties","inferredNodeRouting"],"sources":["../../../src/canvas/SceneCanvas/defaultNodeProperties.ts"],"sourcesContent":["import { prefUnit, type PrefBooleanEncoding, type PrefEnum, type PrefEnumEncoding, type PrefLeaf, type PrefNumberUnit, type PrefSection } from '@weasel-js/prefs';\nimport { inferredNodeRouting } from './defaultNodeRouting';\nimport { KIT_SHAPE_KINDS } from 'core/shapeKinds';\nimport { listMarkers, markerGeneration } from '@weasel-js/stroke';\nimport { dashForStrokeStyle, strokeDashStyleOf } from '@weasel-js/paint';\nimport { resolveAlign, type TextAlign } from '@weasel-js/text';\nimport type { NodePropertiesEntry } from 'core/scene/NodeProperties';\nimport { ANGLE_RADIANS } from '@weasel-js/quantity';\n\n/** Radians-stored / degrees-shown conversion for `pose.rotation` leaves.\n * Display rounds to 0.1° so a canonical radian value doesn't render as\n * a 15-digit float. */\nexport const rotationDegreesUnit: PrefNumberUnit = prefUnit(ANGLE_RADIANS, 'deg', {\n precision: 1,\n suffix: '°',\n});\n\n/** `Stroke.width` off a partly-typed stroke object, or `undefined`. */\nfunction strokeWidthOf(stroke: Record<string, unknown> | undefined): number | { px: number } | undefined {\n const w = stroke?.width;\n if (typeof w === 'number') return w;\n if (typeof w === 'object' && w !== null && typeof (w as { px?: unknown }).px === 'number') {\n return w as { px: number };\n }\n return undefined;\n}\n\n/** `Stroke.dash` — a stored array of lengths — read and written as a named\n * style. The presets are multiples of the sibling `width`, which is why the\n * encoding is handed the whole stroke rather than the field. */\nconst strokeDashEncoding: PrefEnumEncoding = {\n read: (dash, stroke) =>\n stroke === undefined\n ? undefined\n : strokeDashStyleOf(Array.isArray(dash) ? (dash as number[]) : undefined, strokeWidthOf(stroke)),\n write: (style, stroke) =>\n style === 'dashed' || style === 'dotted'\n ? dashForStrokeStyle(style, strokeWidthOf(stroke))\n // `solid` is stored as no dash at all. `custom` is reportable, not\n // authorable: it names an imported array, and the array it names is the\n // one already stored.\n : style === 'solid'\n ? undefined\n : stroke?.dash,\n};\n\n/** `Stroke.markerStart` / `markerMid` / `markerEnd` — a `MarkerRef` — read and\n * written as a bare key, with the empty string standing for no marker. */\nconst markerEncoding: PrefEnumEncoding = {\n read: (ref) => {\n if (typeof ref === 'string') return ref;\n if (ref && typeof ref === 'object' && 'key' in ref) return String((ref as { key: string }).key);\n return '';\n },\n write: (value) => (value === '' ? undefined : value),\n};\n\n/** `TextStyle.fontStyle` as an Italic flag. Off removes the field rather than\n * storing `'normal'`, which is what an absent one already means. */\nconst italicEncoding: PrefBooleanEncoding = {\n read: (stored) => stored === 'italic',\n write: (on) => (on ? 'italic' : undefined),\n};\n\n/** `TextStyle.align` read as the edge that paints, or as `justify`. The model\n * also holds the reading-order pair `start` / `end`, which the segments would\n * otherwise leave unlit; they read through `direction`, and a click writes the\n * edge. */\nconst alignEncoding: PrefEnumEncoding = {\n read: (stored, style) => {\n if (stored === undefined || stored === 'justify') return stored;\n const direction = style?.direction === 'rtl' ? 'rtl' : 'ltr';\n return resolveAlign(stored as TextAlign, direction);\n },\n write: (edge) => edge,\n};\n\ntype MarkerOptions = PrefEnum['options'];\nlet markerOptionsCache: { generation: number; options: MarkerOptions } | undefined;\n\n/** A `select` rather than a `toggle`: nine options will not fit a segmented\n * strip. One array per state of the registry, so a reader comparing\n * references sees a change only when the set changed. */\nfunction markerOptions(): MarkerOptions {\n const generation = markerGeneration();\n if (markerOptionsCache?.generation !== generation) {\n markerOptionsCache = {\n generation,\n options: [\n { value: '', label: 'None' },\n ...listMarkers().map((m) => ({ value: m.id, label: m.id })),\n ],\n };\n }\n return markerOptionsCache.options;\n}\n\n// An accessor: these schemas are built at module load, before a consumer can\n// have registered a marker, so the list is read from the registry when asked.\nfunction markerLeaf(leaf: Omit<PrefEnum, 'kind' | 'options' | 'default' | 'control' | 'encoding' | 'clearable'>): PrefEnum {\n return {\n kind: 'enum', default: '', control: 'select', encoding: markerEncoding, ...leaf,\n get options() { return markerOptions(); },\n };\n}\n\n/** `data.fill` is a whole `Fill`, so it is an object leaf, as the stroke is.\n * Its field is a `paint` leaf, not a `color` one, because a color control\n * would read `undefined` off a gradient and write a bare string over it.\n * `block` for the same reason every stroke field is: the kind bar and the\n * per-kind editor want the width a 64px label column takes. */\nconst FILL_LEAF: PrefLeaf = {\n kind: 'object',\n name: 'Fill',\n description: 'Fill paint.',\n default: { paint: { kind: 'solid', color: '#000000ff' } },\n block: true,\n children: {\n paint: { kind: 'paint', name: 'Fill', description: 'Fill paint.', default: { kind: 'solid', color: '#000000ff' }, alpha: true, block: true },\n },\n};\n\n/** Build the standard shape schema — Layout (pose box + rotation) +\n * Appearance (fill / stroke), optionally a Text group. Matches the kit's\n * builtin-shape data template (`{ path, fill, stroke?, text? }`,\n * `useBuiltinShapeTools`). */\nfunction shapeSchema(opts: { text?: boolean } = {}): PrefSection {\n return {\n name: 'Properties',\n members: {\n layout: {\n name: 'Layout',\n members: {\n 'pose.x': { kind: 'number', name: 'X', description: 'Left edge, world units.', default: 0, pair: { with: 'pose.y', label: 'Position' } },\n 'pose.y': { kind: 'number', name: 'Y', description: 'Top edge, world units.', default: 0 },\n 'pose.width': { kind: 'number', name: 'W', description: 'Width, world units.', default: 0, min: 0, pair: { with: 'pose.height', label: 'Size' } },\n 'pose.height': { kind: 'number', name: 'H', description: 'Height, world units.', default: 0, min: 0 },\n 'pose.rotation': { kind: 'number', name: 'Rotation', description: 'Rotation about the box center.', default: 0, step: 1, unit: rotationDegreesUnit, absentIsDefault: true },\n },\n },\n // What the node *is* outranks how it is painted: a text node's copy is\n // the first thing an editor reaches for.\n ...(opts.text\n ? {\n content: {\n name: 'Content',\n members: {\n // `block`: the content is the section, so a 64px label column\n // reading \"Text\" inside a section reading \"Text\" is one word\n // twice and a narrower field for the sake of it.\n 'data.text': { kind: 'string', name: 'Text', description: 'Text content.', absentIsDefault: true, default: '', block: true },\n },\n },\n }\n : {}),\n appearance: {\n // Headless: `Fill` and `Stroke` head their own blocks, and an\n // `Appearance` rule over them names neither of the two paints a\n // reader is looking at.\n name: '',\n members: {\n 'data.fill': FILL_LEAF,\n // An object leaf: `data.stroke` is a whole `Stroke`, and its fields\n // belong to one value. Sibling leaves addressing into it would each\n // write one field of a value they can only half see.\n 'data.stroke': {\n kind: 'object',\n name: 'Stroke',\n description: 'Stroke paint and line geometry.',\n default: { paint: { kind: 'solid', color: '#000000ff' }, width: 1 },\n block: true,\n children: {\n // Every row is `block`: under a heading already reading STROKE,\n // a 64px label column spells the section name again and takes\n // the width the controls need. Each field leads with its own\n // glyph instead.\n // Unpaired: the paint field is a whole paint editor — a kind bar\n // over a per-kind body — and cannot share a row with a slider.\n width: { kind: 'number', name: 'Width', description: 'Stroke width, world units.', absentIsDefault: true, default: 1, min: 0, max: 20, step: 0.5, control: 'slider', icon: 'strokeWidth', block: true },\n paint: { kind: 'paint', name: 'Paint', description: 'Stroke paint.', default: { kind: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // Three options each: a segmented control shows every one at\n // once where a select shows the current one and hides the rest\n // behind a click.\n cap: { kind: 'enum', name: 'Cap', description: 'How an open end is drawn.', absentIsDefault: true, default: 'butt', control: 'toggle', block: true, pair: { with: ['data.stroke.join', 'data.stroke.align'], label: 'Line' }, options: [{ value: 'butt', label: 'Butt', icon: 'capButt' }, { value: 'round', label: 'Round', icon: 'capRound' }, { value: 'square', label: 'Square', icon: 'capSquare' }] },\n join: { kind: 'enum', name: 'Join', description: 'How a corner is drawn.', absentIsDefault: true, default: 'miter', control: 'toggle', block: true, options: [{ value: 'miter', label: 'Miter', icon: 'joinMiter' }, { value: 'round', label: 'Round', icon: 'joinRound' }, { value: 'bevel', label: 'Bevel', icon: 'joinBevel' }] },\n align: { kind: 'enum', name: 'Align', description: 'Where the ribbon sits relative to the edge.', absentIsDefault: true, default: 'center', control: 'toggle', block: true, options: [{ value: 'inner', label: 'Inner', icon: 'alignInner' }, { value: 'center', label: 'Center', icon: 'alignCenter' }, { value: 'outer', label: 'Outer', icon: 'alignOuter' }] },\n // Its own row: three bars already fill the one above, and the\n // dash pattern is a property of the line rather than of how the\n // ribbon meets the geometry.\n dash: { kind: 'enum', name: 'Style', description: 'Solid, dashed or dotted. Dash lengths are multiples of the stroke width, so a style holds as the width changes.', default: 'solid', control: 'toggle', block: true, encoding: strokeDashEncoding, options: [{ value: 'solid', label: 'Solid', icon: 'dashSolid' }, { value: 'dashed', label: 'Dashed', icon: 'dashDashed' }, { value: 'dotted', label: 'Dotted', icon: 'dashDotted' }, { value: 'custom', label: 'Custom', icon: 'dashCustom', disabled: true }] },\n markerStart: markerLeaf({ name: 'Start', description: 'Marker at the first vertex of each open subpath.', block: true, pair: { with: ['data.stroke.markerMid', 'data.stroke.markerEnd'], label: 'Markers' } }),\n markerMid: markerLeaf({ name: 'Mid', description: 'Marker at every interior authored vertex.', block: true }),\n markerEnd: markerLeaf({ name: 'End', description: 'Marker at the last vertex of each open subpath.', block: true }),\n },\n },\n },\n },\n ...(opts.text\n ? {\n text: {\n // Headless: `Character` and `Paragraph` carry the labels, and a\n // `Typography` heading over them names nothing a reader can't\n // already see.\n name: '',\n members: {\n // One object leaf, not a row of siblings addressing into it:\n // `data.style` is a `TextStyle`, and its character and\n // paragraph fields are one value that reads as two lists.\n // The groups head those lists and contribute nothing to the\n // path — a field inside one is still a field of the style.\n 'data.style': {\n kind: 'object',\n name: 'Style',\n description: 'Typography for the whole node. Its color is the node\\'s own `data.fill`, in Appearance.',\n default: {},\n absentIsDefault: true,\n block: true,\n children: {\n character: {\n name: 'Character',\n members: {\n fontFamily: { kind: 'font-family', name: 'Font', description: 'Registered font family.', absentIsDefault: true, default: 'sans-serif' },\n fontSize: { kind: 'number', name: 'Size', description: 'Font size, world units.', absentIsDefault: true, default: 16, min: 1, step: 1, pair: { with: 'data.style.fontWeight', label: 'Size / weight' } },\n fontWeight: { kind: 'font-weight', name: 'Weight', description: 'Font weight, from the faces the family has.', absentIsDefault: true, default: 400 },\n // One row of segments, the strip every text editor\n // draws: the slant, the three rules, then the two\n // scripts. Italic is a flag here although the field\n // is `fontStyle`, whose only other value is upright.\n fontStyle: { kind: 'boolean', name: 'Italic', description: 'Set the text in its italic face.', default: false, control: 'toggle', icon: 'italic', short: ['I'], pair: { with: ['data.style.underline', 'data.style.strikethrough', 'data.style.overline', 'data.style.script'], label: 'Style' }, encoding: italicEncoding },\n underline: { kind: 'boolean', name: 'Underline', description: 'Underline the text.', absentIsDefault: true, default: false, control: 'toggle', icon: 'underline', short: ['U'] },\n strikethrough: { kind: 'boolean', name: 'Strikethrough', description: 'Strike through the text.', absentIsDefault: true, default: false, control: 'toggle', icon: 'strikethrough', short: ['S'] },\n overline: { kind: 'boolean', name: 'Overline', description: 'Rule the text above its ascent.', absentIsDefault: true, default: false, control: 'toggle', icon: 'overline', short: ['O'] },\n script: { kind: 'enum', name: 'Script', description: 'Set the text smaller and raised or lowered, on the line it would otherwise hold.', default: undefined, control: 'toggle', clearable: true, options: [{ value: 'super', label: 'Superscript', icon: 'superscript' }, { value: 'sub', label: 'Subscript', icon: 'subscript' }] },\n letterSpacing: { kind: 'number', name: 'Tracking', description: 'Extra advance per glyph, world units.', absentIsDefault: true, default: 0, step: 0.1 },\n textTransform: { kind: 'enum', name: 'Case', description: 'Draw the text in capitals, lowercase or title case. The text itself is unchanged.', absentIsDefault: true, default: 'none', control: 'toggle', options: [{ value: 'none', label: 'None', short: ['–'] }, { value: 'uppercase', label: 'Uppercase', short: ['AA'] }, { value: 'lowercase', label: 'Lowercase', short: ['aa'] }, { value: 'capitalize', label: 'Capitalize', short: ['Aa'] }] },\n fontVariantCaps: { kind: 'enum', name: 'Small caps', description: 'Draw lowercase letters as smaller capitals. The text itself is unchanged.', absentIsDefault: true, default: 'normal', control: 'toggle', options: [{ value: 'normal', label: 'Normal', short: ['–'] }, { value: 'small-caps', label: 'Small caps', short: ['Sc'] }] },\n },\n },\n paragraph: {\n name: 'Paragraph',\n members: {\n lineHeight: { kind: 'number', name: 'Leading', description: 'Line height as a multiple of font size.', absentIsDefault: true, default: 1.2, min: 0.5, step: 0.1 },\n wrap: { kind: 'boolean', name: 'Wrap', description: 'Break lines between words at the box width.', absentIsDefault: true, default: false, control: 'switch' },\n tabSize: { kind: 'number', name: 'Tab size', description: 'Distance between tab stops, in spaces.', absentIsDefault: true, default: 8, min: 1, step: 1 },\n direction: { kind: 'enum', name: 'Direction', description: 'Reading direction. Auto takes each paragraph\\'s from its first strong character.', absentIsDefault: true, default: 'auto', control: 'toggle', options: [{ value: 'auto', label: 'Auto', short: ['Auto'] }, { value: 'ltr', label: 'Left to right', short: ['LTR'] }, { value: 'rtl', label: 'Right to left', short: ['RTL'] }] },\n // Last, so the box's vertical alignment — a field of\n // the node rather than of its style — follows it.\n align: { kind: 'enum', name: 'Align', description: 'Horizontal alignment.', absentIsDefault: true, default: 'left', control: 'toggle', encoding: alignEncoding, options: [{ value: 'left', label: 'Left', icon: 'textAlignLeft' }, { value: 'center', label: 'Center', icon: 'textAlignCenter' }, { value: 'right', label: 'Right', icon: 'textAlignRight' }, { value: 'justify', label: 'Justify', icon: 'textAlignJustify' }] },\n },\n },\n },\n },\n 'data.verticalAlign': { kind: 'enum', name: 'Vertical', description: 'Where the lines sit in the box\\'s height.', absentIsDefault: true, default: 'top', control: 'toggle', options: [{ value: 'top', label: 'Top', icon: 'textAlignTop' }, { value: 'center', label: 'Middle', icon: 'textAlignMiddle' }, { value: 'bottom', label: 'Bottom', icon: 'textAlignBottom' }] },\n },\n },\n }\n : {}),\n },\n };\n}\n\n/**\n * Default properties-trait entries covering the kit's built-in shape\n * kinds (`KIT_SHAPE_KINDS`) — the sibling of `defaultNodeRouting`. Every\n * kind gets the standard box schema; `text` adds a Text group. In\n * lockstep with `KIT_SHAPE_KINDS` by construction (derived via `.map`).\n */\nexport const defaultNodeProperties: readonly NodePropertiesEntry[] =\n KIT_SHAPE_KINDS.map((name) => ({\n name,\n schema: shapeSchema({ text: name === 'text' }),\n }));\n\n/**\n * Properties-trait entries for the *inferred* routing kinds\n * (`inferredNodeRouting`: `text` / `path` / `image`) — the vocabulary\n * consumers produce when they don't tag `data.kind` (e.g. WeaselDraw).\n * In lockstep with `inferredNodeRouting` by construction.\n */\nexport const inferredNodeProperties: readonly NodePropertiesEntry[] =\n inferredNodeRouting.map((e) => ({\n name: e.name,\n schema: shapeSchema({ text: e.name === 'text' }),\n }));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"defaultNodeProperties.js","names":["rotationDegreesUnit","prefUnit","ANGLE_RADIANS","TWO_PLACES","decimal","strokeWidthOf","w","stroke","strokeDashEncoding","strokeDashStyleOf","dash","style","dashForStrokeStyle","markerEncoding","ref","value","italicEncoding","stored","on","alignEncoding","direction","resolveAlign","edge","transformEncoding","poseLinearOf","pose","markerOptionsCache","markerOptions","generation","markerGeneration","listMarkers","m","markerLeaf","leaf","FILL_LEAF","STROKE_LEAF","LAYOUT","shapeSchema","opts","g","SHAPE_GENERATOR_SECTIONS","GENERATED_KINDS","shapeEntry","generators","name","settleShapeGenerator","defaultNodeProperties","KIT_SHAPE_KINDS","inferredNodeProperties","inferredNodeRouting"],"sources":["../../../src/canvas/SceneCanvas/defaultNodeProperties.ts"],"sourcesContent":["import { prefUnit, type PrefBooleanEncoding, type PrefEnum, type PrefEnumEncoding, type PrefLeaf, type PrefNumberUnit, type PrefSection } from '@weasel-js/prefs';\nimport { inferredNodeRouting } from './defaultNodeRouting';\nimport { KIT_SHAPE_KINDS } from 'core/shapeKinds';\nimport { listMarkers, markerGeneration } from '@weasel-js/stroke';\nimport { dashForStrokeStyle, strokeDashStyleOf } from '@weasel-js/paint';\nimport { resolveAlign } from '@weasel-js/text';\nimport type { NodePropertiesEntry } from 'core/scene/NodeProperties';\nimport { ANGLE_RADIANS, decimal } from '@weasel-js/quantity';\nimport { poseLinearOf } from '@weasel-js/geom';\nimport { settleShapeGenerator } from 'features/paths/shapeGenerator';\nimport { SHAPE_GENERATOR_SECTIONS } from './shapeGeneratorProperties';\n\n/** Radians-stored / degrees-shown conversion for `pose.rotation` leaves.\n * Display rounds to 0.1° so a canonical radian value doesn't render as\n * a 15-digit float. */\nexport const rotationDegreesUnit: PrefNumberUnit = prefUnit(ANGLE_RADIANS, 'deg', {\n precision: 1,\n suffix: '°',\n});\n\n/** How a typed number reads. The stored value keeps every digit a drag or a scale gave it. */\nconst TWO_PLACES = decimal({ maxPlaces: 2, grouping: false });\n\n/** `Stroke.width` off a partly-typed stroke object, or `undefined`. */\nfunction strokeWidthOf(stroke: Record<string, unknown> | undefined): number | { px: number } | undefined {\n const w = stroke?.width;\n if (typeof w === 'number') return w;\n if (typeof w === 'object' && w !== null && typeof (w as { px?: unknown }).px === 'number') {\n return w as { px: number };\n }\n return undefined;\n}\n\n/** `Stroke.dash` — a stored array of lengths — read and written as a named\n * style. The presets are multiples of the sibling `width`, which is why the\n * encoding is handed the whole stroke rather than the field. */\nconst strokeDashEncoding: PrefEnumEncoding = {\n read: (dash, stroke) =>\n stroke === undefined\n ? undefined\n : strokeDashStyleOf(Array.isArray(dash) ? (dash as number[]) : undefined, strokeWidthOf(stroke)),\n write: (style, stroke) =>\n style === 'dashed' || style === 'dotted'\n ? dashForStrokeStyle(style, strokeWidthOf(stroke))\n // `solid` is stored as no dash at all. `custom` is reportable, not\n // authorable: it names an imported array, and the array it names is the\n // one already stored.\n : style === 'solid'\n ? undefined\n : stroke?.dash,\n};\n\n/** `Stroke.markerStart` / `markerMid` / `markerEnd` — a `MarkerRef` — read and\n * written as a bare key, with the empty string standing for no marker. */\nconst markerEncoding: PrefEnumEncoding = {\n read: (ref) => {\n if (typeof ref === 'string') return ref;\n if (ref && typeof ref === 'object' && 'key' in ref) return String((ref as { key: string }).key);\n return '';\n },\n write: (value) => (value === '' ? undefined : value),\n};\n\n/** `TextStyle.fontStyle` as an Italic flag. Off removes the field rather than\n * storing `'normal'`, which is what an absent one already means. */\nconst italicEncoding: PrefBooleanEncoding = {\n read: (stored) => stored === 'italic',\n write: (on) => (on ? 'italic' : undefined),\n};\n\n/** `TextStyle.align` read as the edge that paints, or as `justify`. The model\n * also holds the reading-order pair `start` / `end`, which name an edge only\n * under a stated `direction`. Under `auto` each paragraph takes its own from\n * its text, so no segment is lit. A click writes the edge. */\nconst alignEncoding: PrefEnumEncoding = {\n read: (stored, style) => {\n if (stored === 'start' || stored === 'end') {\n const direction = style?.direction;\n return direction === 'ltr' || direction === 'rtl' ? resolveAlign(stored, direction) : undefined;\n }\n return stored as string | undefined;\n },\n write: (edge) => edge,\n};\n\n/** `pose.transform` as a flag: on while the pose carries a skew or a per-axis\n * scale, and off removes it. On keeps the one held, since a flag cannot\n * author a matrix. */\nconst transformEncoding: PrefBooleanEncoding = {\n read: (stored) => poseLinearOf({ transform: stored }) !== null,\n write: (on, pose) => (on ? pose?.transform : undefined),\n};\n\ntype MarkerOptions = PrefEnum['options'];\nlet markerOptionsCache: { generation: number; options: MarkerOptions } | undefined;\n\n/** A `select` rather than a `toggle`: nine options will not fit a segmented\n * strip. One array per state of the registry, so a reader comparing\n * references sees a change only when the set changed. */\nfunction markerOptions(): MarkerOptions {\n const generation = markerGeneration();\n if (markerOptionsCache?.generation !== generation) {\n markerOptionsCache = {\n generation,\n options: [\n { value: '', label: 'None' },\n ...listMarkers().map((m) => ({ value: m.id, label: m.id })),\n ],\n };\n }\n return markerOptionsCache.options;\n}\n\n// An accessor: these schemas are built at module load, before a consumer can\n// have registered a marker, so the list is read from the registry when asked.\nfunction markerLeaf(leaf: Omit<PrefEnum, 'kind' | 'options' | 'default' | 'control' | 'encoding' | 'clearable'>): PrefEnum {\n return {\n kind: 'enum', default: '', control: 'select', encoding: markerEncoding, ...leaf,\n get options() { return markerOptions(); },\n };\n}\n\n/** `data.fill` is a whole `Fill`, so it is an object leaf, as the stroke is.\n * Its field is a `paint` leaf, not a `color` one, because a color control\n * would read `undefined` off a gradient and write a bare string over it.\n * `block` for the same reason every stroke field is: the kind bar and the\n * per-kind editor want the width a 64px label column takes. */\nconst FILL_LEAF: PrefLeaf = {\n kind: 'object',\n name: 'Fill',\n description: 'Fill paint.',\n default: { paint: { kind: 'solid', color: '#000000ff' } },\n block: true,\n children: {\n paint: { kind: 'paint', name: 'Fill', description: 'Fill paint.', default: { kind: 'solid', color: '#000000ff' }, alpha: true, block: true },\n },\n};\n\n/** A whole `Stroke`: paint and line geometry, one value. */\nconst STROKE_LEAF: PrefLeaf = {\n kind: 'object',\n name: 'Stroke',\n description: 'Stroke paint and line geometry.',\n default: { paint: { kind: 'solid', color: '#000000ff' }, width: 1 },\n block: true,\n children: {\n // Every row is `block`: under a heading already reading STROKE,\n // a 64px label column spells the section name again and takes\n // the width the controls need. Each field leads with its own\n // glyph instead.\n // Unpaired: the paint field is a whole paint editor — a kind bar\n // over a per-kind body — and cannot share a row with a slider.\n width: { kind: 'number', name: 'Width', description: 'Stroke width, world units.', absentIsDefault: true, default: 1, min: 0, max: 20, step: 0.5, control: 'slider', icon: 'strokeWidth', block: true },\n paint: { kind: 'paint', name: 'Paint', description: 'Stroke paint.', default: { kind: 'solid', color: '#000000ff' }, alpha: true, block: true },\n // Three options each: a segmented control shows every one at\n // once where a select shows the current one and hides the rest\n // behind a click.\n cap: { kind: 'enum', name: 'Cap', description: 'How an open end is drawn.', absentIsDefault: true, default: 'butt', control: 'toggle', block: true, pair: { with: ['data.stroke.join', 'data.stroke.align'], label: 'Line' }, options: [{ value: 'butt', label: 'Butt', icon: 'capButt' }, { value: 'round', label: 'Round', icon: 'capRound' }, { value: 'square', label: 'Square', icon: 'capSquare' }] },\n join: { kind: 'enum', name: 'Join', description: 'How a corner is drawn.', absentIsDefault: true, default: 'miter', control: 'toggle', block: true, options: [{ value: 'miter', label: 'Miter', icon: 'joinMiter' }, { value: 'round', label: 'Round', icon: 'joinRound' }, { value: 'bevel', label: 'Bevel', icon: 'joinBevel' }] },\n align: { kind: 'enum', name: 'Align', description: 'Where the ribbon sits relative to the edge.', absentIsDefault: true, default: 'center', control: 'toggle', block: true, options: [{ value: 'inner', label: 'Inner', icon: 'alignInner' }, { value: 'center', label: 'Center', icon: 'alignCenter' }, { value: 'outer', label: 'Outer', icon: 'alignOuter' }] },\n // Its own row: three bars already fill the one above, and the\n // dash pattern is a property of the line rather than of how the\n // ribbon meets the geometry.\n dash: { kind: 'enum', name: 'Style', description: 'Solid, dashed or dotted. Dash lengths are multiples of the stroke width, so a style holds as the width changes.', default: 'solid', control: 'toggle', block: true, encoding: strokeDashEncoding, options: [{ value: 'solid', label: 'Solid', icon: 'dashSolid' }, { value: 'dashed', label: 'Dashed', icon: 'dashDashed' }, { value: 'dotted', label: 'Dotted', icon: 'dashDotted' }, { value: 'custom', label: 'Custom', icon: 'dashCustom', disabled: true }] },\n markerStart: markerLeaf({ name: 'Start', description: 'Marker at the first vertex of each open subpath.', block: true, pair: { with: ['data.stroke.markerMid', 'data.stroke.markerEnd'], label: 'Markers' } }),\n markerMid: markerLeaf({ name: 'Mid', description: 'Marker at every interior authored vertex.', block: true }),\n markerEnd: markerLeaf({ name: 'End', description: 'Marker at the last vertex of each open subpath.', block: true }),\n },\n};\n\n/** The pose box, its rotation, and the `transform` inside that. */\nconst LAYOUT: PrefSection = {\n name: 'Layout',\n members: {\n 'pose.x': { kind: 'number', display: TWO_PLACES, name: 'X', description: 'Left edge, world units.', default: 0, pair: { with: 'pose.y', label: 'Position' } },\n 'pose.y': { kind: 'number', display: TWO_PLACES, name: 'Y', description: 'Top edge, world units.', default: 0 },\n 'pose.width': { kind: 'number', display: TWO_PLACES, name: 'W', description: 'Width, world units.', default: 0, min: 0, pair: { with: 'pose.height', label: 'Size' } },\n 'pose.height': { kind: 'number', display: TWO_PLACES, name: 'H', description: 'Height, world units.', default: 0, min: 0 },\n 'pose.rotation': { kind: 'number', name: 'Rotation', description: 'Rotation about the box center.', default: 0, step: 1, unit: rotationDegreesUnit, absentIsDefault: true },\n 'pose.transform': { kind: 'boolean', name: 'Skew', description: 'The skew or per-axis scale the box and rotation cannot hold. Off removes it.', default: false, control: 'switch', encoding: transformEncoding },\n },\n};\n\n/** Build the standard shape schema — Layout (pose box + rotation) +\n * Appearance (fill / stroke), optionally a Text group. Matches the kit's\n * builtin-shape data template (`{ path, fill, stroke?, text? }`,\n * `useBuiltinShapeTools`). */\nfunction shapeSchema(opts: { text?: boolean; generators?: readonly string[] } = {}): PrefSection {\n return {\n name: 'Properties',\n members: {\n layout: LAYOUT,\n ...Object.fromEntries((opts.generators ?? []).map((g) => [g, SHAPE_GENERATOR_SECTIONS[g]])),\n // What the node *is* outranks how it is painted: a text node's copy is\n // the first thing an editor reaches for.\n ...(opts.text\n ? {\n content: {\n name: 'Content',\n members: {\n // `block`: the content is the section, so a 64px label column\n // reading \"Text\" inside a section reading \"Text\" is one word\n // twice and a narrower field for the sake of it.\n 'data.text': { kind: 'string', name: 'Text', description: 'Text content.', absentIsDefault: true, default: '', block: true },\n },\n },\n }\n : {}),\n appearance: {\n // Headless: `Fill` and `Stroke` head their own blocks, and an\n // `Appearance` rule over them names neither of the two paints a\n // reader is looking at.\n name: '',\n members: {\n 'data.fill': FILL_LEAF,\n // An object leaf: `data.stroke` is a whole `Stroke`, and its fields\n // belong to one value. Sibling leaves addressing into it would each\n // write one field of a value they can only half see.\n 'data.stroke': STROKE_LEAF,\n },\n },\n ...(opts.text\n ? {\n text: {\n // Headless: `Character` and `Paragraph` carry the labels, and a\n // `Typography` heading over them names nothing a reader can't\n // already see.\n name: '',\n members: {\n // One object leaf, not a row of siblings addressing into it:\n // `data.style` is a `TextStyle`, and its character and\n // paragraph fields are one value that reads as two lists.\n // The groups head those lists and contribute nothing to the\n // path — a field inside one is still a field of the style.\n 'data.style': {\n kind: 'object',\n name: 'Style',\n description: 'Typography for the whole node. Its color is the node\\'s own `data.fill`, in Appearance.',\n default: {},\n absentIsDefault: true,\n block: true,\n children: {\n character: {\n name: 'Character',\n members: {\n fontFamily: { kind: 'font-family', name: 'Font', description: 'Registered font family.', absentIsDefault: true, default: 'sans-serif' },\n fontSize: { kind: 'number', display: TWO_PLACES, name: 'Size', description: 'Font size, world units.', absentIsDefault: true, default: 16, min: 1, step: 1, pair: { with: 'data.style.fontWeight', label: 'Size / weight' } },\n fontWeight: { kind: 'font-weight', name: 'Weight', description: 'Font weight, from the faces the family has.', absentIsDefault: true, default: 400 },\n // One row of segments, the strip every text editor\n // draws: the slant, the three rules, then the two\n // scripts. Italic is a flag here although the field\n // is `fontStyle`, whose only other value is upright.\n fontStyle: { kind: 'boolean', name: 'Italic', description: 'Set the text in its italic face.', default: false, control: 'toggle', icon: 'italic', short: ['I'], pair: { with: ['data.style.underline', 'data.style.strikethrough', 'data.style.overline', 'data.style.script'], label: 'Style' }, encoding: italicEncoding },\n underline: { kind: 'boolean', name: 'Underline', description: 'Underline the text.', absentIsDefault: true, default: false, control: 'toggle', icon: 'underline', short: ['U'] },\n strikethrough: { kind: 'boolean', name: 'Strikethrough', description: 'Strike through the text.', absentIsDefault: true, default: false, control: 'toggle', icon: 'strikethrough', short: ['S'] },\n overline: { kind: 'boolean', name: 'Overline', description: 'Rule the text above its ascent.', absentIsDefault: true, default: false, control: 'toggle', icon: 'overline', short: ['O'] },\n script: { kind: 'enum', name: 'Script', description: 'Set the text smaller and raised or lowered, on the line it would otherwise hold.', default: undefined, control: 'toggle', clearable: true, options: [{ value: 'super', label: 'Superscript', icon: 'superscript' }, { value: 'sub', label: 'Subscript', icon: 'subscript' }] },\n letterSpacing: { kind: 'number', display: TWO_PLACES, name: 'Tracking', description: 'Extra advance per glyph, world units.', absentIsDefault: true, default: 0, step: 0.1 },\n textTransform: { kind: 'enum', name: 'Case', description: 'Draw the text in capitals, lowercase or title case. The text itself is unchanged.', absentIsDefault: true, default: 'none', control: 'toggle', options: [{ value: 'none', label: 'None', short: ['–'] }, { value: 'uppercase', label: 'Uppercase', short: ['AA'] }, { value: 'lowercase', label: 'Lowercase', short: ['aa'] }, { value: 'capitalize', label: 'Capitalize', short: ['Aa'] }] },\n fontVariantCaps: { kind: 'enum', name: 'Small caps', description: 'Draw lowercase letters as smaller capitals. The text itself is unchanged.', absentIsDefault: true, default: 'normal', control: 'toggle', options: [{ value: 'normal', label: 'Normal', short: ['–'] }, { value: 'small-caps', label: 'Small caps', short: ['Sc'] }] },\n },\n },\n paragraph: {\n name: 'Paragraph',\n members: {\n lineHeight: { kind: 'number', display: TWO_PLACES, name: 'Leading', description: 'Line height as a multiple of font size.', absentIsDefault: true, default: 1.2, min: 0.5, step: 0.1 },\n wrap: { kind: 'boolean', name: 'Wrap', description: 'Break lines between words at the box width.', absentIsDefault: true, default: false, control: 'switch' },\n tabSize: { kind: 'number', display: TWO_PLACES, name: 'Tab size', description: 'Distance between tab stops, in spaces.', absentIsDefault: true, default: 8, min: 1, step: 1 },\n direction: { kind: 'enum', name: 'Direction', description: 'Reading direction. Auto takes each paragraph\\'s from its first strong character.', absentIsDefault: true, default: 'auto', control: 'toggle', options: [{ value: 'auto', label: 'Auto', short: ['Auto'] }, { value: 'ltr', label: 'Left to right', short: ['LTR'] }, { value: 'rtl', label: 'Right to left', short: ['RTL'] }] },\n // Last, so the box's vertical alignment — a field of\n // the node rather than of its style — follows it.\n align: { kind: 'enum', name: 'Align', description: 'Horizontal alignment.', absentIsDefault: true, default: 'left', control: 'toggle', encoding: alignEncoding, options: [{ value: 'left', label: 'Left', icon: 'textAlignLeft' }, { value: 'center', label: 'Center', icon: 'textAlignCenter' }, { value: 'right', label: 'Right', icon: 'textAlignRight' }, { value: 'justify', label: 'Justify', icon: 'textAlignJustify' }] },\n },\n },\n },\n },\n 'data.verticalAlign': { kind: 'enum', name: 'Vertical', description: 'Where the lines sit in the box\\'s height.', absentIsDefault: true, default: 'top', control: 'toggle', options: [{ value: 'top', label: 'Top', icon: 'textAlignTop' }, { value: 'center', label: 'Middle', icon: 'textAlignMiddle' }, { value: 'bottom', label: 'Bottom', icon: 'textAlignBottom' }] },\n },\n },\n }\n : {}),\n },\n };\n}\n\n/** The kinds whose nodes can hold a `data.generator`, and which kinds of it. */\nconst GENERATED_KINDS: Record<string, readonly string[] | undefined> = {\n polygon: ['polygon'],\n star: ['star'],\n path: ['polygon', 'star'],\n};\n\nfunction shapeEntry(name: string): NodePropertiesEntry {\n const generators = GENERATED_KINDS[name];\n return {\n name,\n schema: shapeSchema({ text: name === 'text', ...(generators ? { generators } : {}) }),\n ...(generators ? { settle: settleShapeGenerator } : {}),\n };\n}\n\n/**\n * Default properties-trait entries covering the kit's built-in shape\n * kinds (`KIT_SHAPE_KINDS`) — the sibling of `defaultNodeRouting`. Every\n * kind gets the standard box schema; `text` adds a Text group. In\n * lockstep with `KIT_SHAPE_KINDS` by construction (derived via `.map`).\n */\nexport const defaultNodeProperties: readonly NodePropertiesEntry[] = KIT_SHAPE_KINDS.map(shapeEntry);\n\n/**\n * Properties-trait entries for the *inferred* routing kinds\n * (`inferredNodeRouting`: `text` / `path` / `image`) — the vocabulary\n * consumers produce when they don't tag `data.kind` (e.g. WeaselDraw).\n * In lockstep with `inferredNodeRouting` by construction.\n */\nexport const inferredNodeProperties: readonly NodePropertiesEntry[] =\n inferredNodeRouting.map((e) => shapeEntry(e.name));\n"],"mappings":"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"}
|
|
@@ -51,6 +51,7 @@ var i = [
|
|
|
51
51
|
"cancelGesture",
|
|
52
52
|
"selectAll",
|
|
53
53
|
"duplicate",
|
|
54
|
+
"transformAgain",
|
|
54
55
|
"delete",
|
|
55
56
|
"group",
|
|
56
57
|
"ungroup",
|
|
@@ -82,30 +83,7 @@ var i = [
|
|
|
82
83
|
"distribute.horizontal",
|
|
83
84
|
"distribute.vertical"
|
|
84
85
|
] },
|
|
85
|
-
paths: { actions:
|
|
86
|
-
"pathfinder.union",
|
|
87
|
-
"pathfinder.subtract",
|
|
88
|
-
"pathfinder.intersect",
|
|
89
|
-
"pathfinder.exclude",
|
|
90
|
-
"pathfinder.divide",
|
|
91
|
-
"pathfinder.crop",
|
|
92
|
-
"pathfinder.trim",
|
|
93
|
-
"pathfinder.merge",
|
|
94
|
-
"createOutlines",
|
|
95
|
-
"outlineStroke",
|
|
96
|
-
"editAnchors",
|
|
97
|
-
"enterPathEdit",
|
|
98
|
-
"exitPathEdit",
|
|
99
|
-
"insertPathAnchor",
|
|
100
|
-
"nudgeAnchors.up",
|
|
101
|
-
"nudgeAnchors.down",
|
|
102
|
-
"nudgeAnchors.left",
|
|
103
|
-
"nudgeAnchors.right",
|
|
104
|
-
"deleteAnchors",
|
|
105
|
-
"cutPathAtAnchor",
|
|
106
|
-
"marqueeAnchors",
|
|
107
|
-
"selectAnchor"
|
|
108
|
-
] },
|
|
86
|
+
paths: { actions: /* @__PURE__ */ "pathfinder.union,pathfinder.subtract,pathfinder.intersect,pathfinder.exclude,pathfinder.divide,pathfinder.crop,pathfinder.trim,pathfinder.merge,createOutlines,outlineStroke,editAnchors,enterPathEdit,exitPathEdit,insertPathAnchor,bendSegment,nudgeAnchors.up,nudgeAnchors.down,nudgeAnchors.left,nudgeAnchors.right,deleteAnchors,cutPathAtAnchor,marqueeAnchors,selectAnchor,retractHandles,makeHandlesSymmetric,averageAnchors,joinAnchors,simplifyPath,offsetPath,snipAtCrossings,joinPaths".split(",") },
|
|
109
87
|
ingest: { actions: ["ingest"] }
|
|
110
88
|
}, o = Object.keys(a), s = {
|
|
111
89
|
pick: ["select", "outline"],
|
|
@@ -114,6 +92,7 @@ var i = [
|
|
|
114
92
|
}, c = Object.fromEntries(o.map((e) => [e, a[e].actions])), l = [
|
|
115
93
|
"insert",
|
|
116
94
|
"insert.adjustRotation",
|
|
95
|
+
"insert.reposition",
|
|
117
96
|
"enterTextEdit"
|
|
118
97
|
];
|
|
119
98
|
function u(e, t = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features.js","names":["SCENE_CANVAS_FEATURES","FEATURE_PRESETS","selectionResizeContribution","selectionRotateContribution","selectionMoveContribution","BASE_FEATURES","COMPOSITE_FEATURES","FEATURE_ACTION_IDS","f","TOOL_DRIVEN_ACTION_IDS","resolveFeatures","out","features","b","opts","featureActionIds","ids","id","featureToolIds","featureAmbient","ambient","options","featureChromeRules","rules","never"],"sources":["../../../src/canvas/SceneCanvas/features.ts"],"sourcesContent":["/**\n * The behavior presets `<SceneCanvas features>` composes. A bare canvas renders\n * its scene and keeps a selection nothing sets; every behavior beyond that is\n * turned on by naming a preset here.\n *\n * A preset is not one of the `packages/core/src/features/<name>/` source\n * directories that `docs/taxonomy.md` calls a feature; the two share a word\n * and nothing else.\n */\nimport { never } from 'features/chrome-caps/conditions';\nimport type { ChromeId, VisibilityRules } from 'features/chrome-caps/types';\nimport type { UseMoveOptions } from 'interactions/actions/move/options';\nimport type { UseRotateOptions } from 'interactions/actions/rotate/options';\nimport {\n selectionMoveContribution,\n selectionResizeContribution,\n selectionRotateContribution,\n} from 'tools/builtin/select/selectionContributions';\nimport type { Contribution } from 'tools/overlayBinding';\nimport type { BuiltinToolId } from './props';\n\n/** A preset that names one behavior and abbreviates nothing. */\nexport type BaseFeature =\n | 'view'\n | 'select'\n | 'outline'\n | 'move'\n | 'resize'\n | 'rotate'\n | 'edit'\n | 'arrange'\n | 'paths'\n | 'ingest';\n\n/** A preset that abbreviates others: `pick` is `select` + `outline`,\n * `transform` is `resize` + `rotate`, and `draw` is every base preset. */\nexport type CompositeFeature = 'pick' | 'transform' | 'draw';\n\n/** A behavior preset for `<SceneCanvas features>`. */\nexport type Feature = BaseFeature | CompositeFeature;\n\n/** Every preset `<SceneCanvas features>` accepts, each composite after the\n * presets it names. */\nexport const SCENE_CANVAS_FEATURES: readonly Feature[] = [\n 'view', 'select', 'outline', 'pick', 'move', 'resize', 'rotate', 'transform',\n 'edit', 'arrange', 'paths', 'ingest', 'draw',\n];\n\n/** What a preset's ambient bindings are built from: the `selectTool` options\n * that configure them. */\nexport interface FeatureBindingOptions {\n move?: UseMoveOptions<unknown>;\n rotate?: UseRotateOptions<unknown>;\n}\n\n/** Everything turning one preset on means. */\nexport interface FeaturePreset {\n /** The kit-standard actions it registers. */\n actions: readonly string[];\n /** The built-in tools it mounts. */\n tools?: readonly BuiltinToolId[];\n /** Its ambient bindings: an always-live entry routing a gesture to its\n * actions under any tool that does not claim the gesture itself. */\n ambient?: (options: FeatureBindingOptions) => Contribution;\n /** The selection chrome that is drawn, and can be grabbed, only while it is on. */\n chrome?: readonly ChromeId[];\n}\n\n/**\n * What each preset turns on.\n *\n * Row order is the order ambient bindings are matched in, and a tie goes to\n * the first: a handle sits over the selected body, so `resize` and `rotate`\n * come before `move`, whose binding matches a handle drag too.\n *\n * Every id `useStandardActions` knows appears under exactly one preset, except\n * the ones in {@link TOOL_DRIVEN_ACTION_IDS}.\n *\n * Cmd/Ctrl+V arrives as a DOM `paste` event, and two presets answer it.\n * `edit` brings `clipboard.pasteEvent`, which pastes nodes a kit canvas\n * copied and nothing else; `ingest` brings `ingest`, which takes those plus\n * images, SVG, files, consumer content handlers, and every OS drop. With both\n * on, `ingest` answers alone: `useStandardActions` holds\n * `clipboard.pasteEvent` back wherever `ingest` is registered.\n */\nexport const FEATURE_PRESETS: Readonly<Record<BaseFeature, FeaturePreset>> = {\n view: {\n actions: ['viewport.dragPan', 'viewport.pinchZoom'],\n tools: ['hand'],\n },\n select: {\n actions: ['areaSelect', 'clearSelection', 'lassoSelect'],\n tools: ['select'],\n },\n outline: {\n actions: [],\n chrome: ['selection.outline'],\n },\n resize: {\n actions: ['resize'],\n ambient: () => selectionResizeContribution(),\n chrome: ['selection.resize-handles'],\n },\n rotate: {\n actions: ['rotate'],\n ambient: ({ rotate }) => selectionRotateContribution({ rotate }),\n chrome: ['selection.rotation-handle'],\n },\n move: {\n actions: ['move', 'clone'],\n ambient: ({ move }) => selectionMoveContribution({ move }),\n },\n edit: {\n actions: [\n 'escape', 'cancelGesture', 'selectAll', 'duplicate', 'delete',\n 'group', 'ungroup', 'undo', 'redo',\n 'nudge.up', 'nudge.down', 'nudge.left', 'nudge.right',\n 'clipboard.cut', 'clipboard.copy', 'clipboard.paste', 'clipboard.pasteEvent',\n 'setFill', 'setStroke', 'setFillOpacity', 'setStrokeOpacity',\n ],\n },\n arrange: {\n actions: [\n 'flip',\n 'reorder.forward', 'reorder.backward',\n 'align.left', 'align.centerX', 'align.right',\n 'align.top', 'align.centerY', 'align.bottom',\n 'distribute.horizontal', 'distribute.vertical',\n ],\n },\n paths: {\n actions: [\n 'pathfinder.union', 'pathfinder.subtract', 'pathfinder.intersect',\n 'pathfinder.exclude', 'pathfinder.divide', 'pathfinder.crop', 'pathfinder.trim', 'pathfinder.merge',\n 'createOutlines', 'outlineStroke',\n 'editAnchors', 'enterPathEdit', 'exitPathEdit', 'insertPathAnchor',\n 'nudgeAnchors.up', 'nudgeAnchors.down', 'nudgeAnchors.left', 'nudgeAnchors.right',\n 'deleteAnchors', 'cutPathAtAnchor', 'marqueeAnchors', 'selectAnchor',\n ],\n },\n ingest: {\n actions: ['ingest'],\n },\n};\n\nconst BASE_FEATURES = Object.keys(FEATURE_PRESETS) as BaseFeature[];\n\n/** The base presets each composite preset turns on. */\nexport const COMPOSITE_FEATURES: Readonly<Record<CompositeFeature, readonly BaseFeature[]>> = {\n pick: ['select', 'outline'],\n transform: ['resize', 'rotate'],\n draw: BASE_FEATURES,\n};\n\n/** The kit-standard actions each preset registers. */\nexport const FEATURE_ACTION_IDS = Object.fromEntries(\n BASE_FEATURES.map((f) => [f, FEATURE_PRESETS[f].actions]),\n) as Readonly<Record<BaseFeature, readonly string[]>>;\n\n/**\n * Kit-standard actions no preset registers: they belong to the tools that bind\n * them. A canvas registers each one whenever a tool it carries names it in a\n * binding — `insert` with any shape tool, `enterTextEdit` with the text tool.\n */\nexport const TOOL_DRIVEN_ACTION_IDS: readonly string[] = ['insert', 'insert.adjustRotation', 'enterTextEdit'];\n\n/**\n * The base presets a canvas runs with: `features` with every composite\n * expanded, plus `view` whenever the canvas was handed a `viewport` config,\n * since that prop only configures what `view` turns on.\n */\nexport function resolveFeatures(\n features: readonly Feature[] | undefined,\n opts: { viewport?: boolean } = {},\n): ReadonlySet<BaseFeature> {\n const out = new Set<BaseFeature>();\n for (const f of features ?? []) {\n if (f in COMPOSITE_FEATURES) {\n for (const b of COMPOSITE_FEATURES[f as CompositeFeature]) out.add(b);\n } else {\n out.add(f as BaseFeature);\n }\n }\n if (opts.viewport) out.add('view');\n return out;\n}\n\n/** The kit-standard action ids the given presets register. */\nexport function featureActionIds(features: ReadonlySet<BaseFeature>): Set<string> {\n const ids = new Set<string>();\n for (const f of features) for (const id of FEATURE_PRESETS[f].actions) ids.add(id);\n return ids;\n}\n\n/** The built-in tools the given presets mount. */\nexport function featureToolIds(features: ReadonlySet<BaseFeature>): Set<BuiltinToolId> {\n const ids = new Set<BuiltinToolId>();\n for (const f of features) for (const id of FEATURE_PRESETS[f].tools ?? []) ids.add(id);\n return ids;\n}\n\n/** The given presets' ambient entries, in the order they are matched. */\nexport function featureAmbient(\n features: ReadonlySet<BaseFeature>,\n options: FeatureBindingOptions,\n): Contribution[] {\n return BASE_FEATURES.flatMap((f) => {\n const ambient = features.has(f) ? FEATURE_PRESETS[f].ambient : undefined;\n return ambient ? [ambient(options)] : [];\n });\n}\n\n/** Rules hiding the chrome of every preset that is not among `features`.\n * `never` also takes a handle out of hit-testing, so a hidden one cannot be\n * grabbed. */\nexport function featureChromeRules(features: ReadonlySet<BaseFeature>): VisibilityRules {\n const rules: VisibilityRules = {};\n for (const f of BASE_FEATURES) {\n if (features.has(f)) continue;\n for (const id of FEATURE_PRESETS[f].chrome ?? []) rules[id] = never;\n }\n return rules;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features.js","names":["SCENE_CANVAS_FEATURES","FEATURE_PRESETS","selectionResizeContribution","selectionRotateContribution","selectionMoveContribution","BASE_FEATURES","COMPOSITE_FEATURES","FEATURE_ACTION_IDS","f","TOOL_DRIVEN_ACTION_IDS","resolveFeatures","out","features","b","opts","featureActionIds","ids","id","featureToolIds","featureAmbient","ambient","options","featureChromeRules","rules","never"],"sources":["../../../src/canvas/SceneCanvas/features.ts"],"sourcesContent":["/**\n * The behavior presets `<SceneCanvas features>` composes. A bare canvas renders\n * its scene and keeps a selection nothing sets; every behavior beyond that is\n * turned on by naming a preset here.\n *\n * A preset is not one of the `packages/core/src/features/<name>/` source\n * directories that `docs/taxonomy.md` calls a feature; the two share a word\n * and nothing else.\n */\nimport { never } from 'features/chrome-caps/conditions';\nimport type { ChromeId, VisibilityRules } from 'features/chrome-caps/types';\nimport type { UseMoveOptions } from 'interactions/actions/move/options';\nimport type { UseRotateOptions } from 'interactions/actions/rotate/options';\nimport {\n selectionMoveContribution,\n selectionResizeContribution,\n selectionRotateContribution,\n} from 'tools/builtin/select/selectionContributions';\nimport type { Contribution } from 'tools/overlayBinding';\nimport type { BuiltinToolId } from './props';\n\n/** A preset that names one behavior and abbreviates nothing. */\nexport type BaseFeature =\n | 'view'\n | 'select'\n | 'outline'\n | 'move'\n | 'resize'\n | 'rotate'\n | 'edit'\n | 'arrange'\n | 'paths'\n | 'ingest';\n\n/** A preset that abbreviates others: `pick` is `select` + `outline`,\n * `transform` is `resize` + `rotate`, and `draw` is every base preset. */\nexport type CompositeFeature = 'pick' | 'transform' | 'draw';\n\n/** A behavior preset for `<SceneCanvas features>`. */\nexport type Feature = BaseFeature | CompositeFeature;\n\n/** Every preset `<SceneCanvas features>` accepts, each composite after the\n * presets it names. */\nexport const SCENE_CANVAS_FEATURES: readonly Feature[] = [\n 'view', 'select', 'outline', 'pick', 'move', 'resize', 'rotate', 'transform',\n 'edit', 'arrange', 'paths', 'ingest', 'draw',\n];\n\n/** What a preset's ambient bindings are built from: the `selectTool` options\n * that configure them. */\nexport interface FeatureBindingOptions {\n move?: UseMoveOptions<unknown>;\n rotate?: UseRotateOptions<unknown>;\n}\n\n/** Everything turning one preset on means. */\nexport interface FeaturePreset {\n /** The kit-standard actions it registers. */\n actions: readonly string[];\n /** The built-in tools it mounts. */\n tools?: readonly BuiltinToolId[];\n /** Its ambient bindings: an always-live entry routing a gesture to its\n * actions under any tool that does not claim the gesture itself. */\n ambient?: (options: FeatureBindingOptions) => Contribution;\n /** The selection chrome that is drawn, and can be grabbed, only while it is on. */\n chrome?: readonly ChromeId[];\n}\n\n/**\n * What each preset turns on.\n *\n * Row order is the order ambient bindings are matched in, and a tie goes to\n * the first: a handle sits over the selected body, so `resize` and `rotate`\n * come before `move`, whose binding matches a handle drag too.\n *\n * Every id `useStandardActions` knows appears under exactly one preset, except\n * the ones in {@link TOOL_DRIVEN_ACTION_IDS}.\n *\n * Cmd/Ctrl+V arrives as a DOM `paste` event, and two presets answer it.\n * `edit` brings `clipboard.pasteEvent`, which pastes nodes a kit canvas\n * copied and nothing else; `ingest` brings `ingest`, which takes those plus\n * images, SVG, files, consumer content handlers, and every OS drop. With both\n * on, `ingest` answers alone: `useStandardActions` holds\n * `clipboard.pasteEvent` back wherever `ingest` is registered.\n */\nexport const FEATURE_PRESETS: Readonly<Record<BaseFeature, FeaturePreset>> = {\n view: {\n actions: ['viewport.dragPan', 'viewport.pinchZoom'],\n tools: ['hand'],\n },\n select: {\n actions: ['areaSelect', 'clearSelection', 'lassoSelect'],\n tools: ['select'],\n },\n outline: {\n actions: [],\n chrome: ['selection.outline'],\n },\n resize: {\n actions: ['resize'],\n ambient: () => selectionResizeContribution(),\n chrome: ['selection.resize-handles'],\n },\n rotate: {\n actions: ['rotate'],\n ambient: ({ rotate }) => selectionRotateContribution({ rotate }),\n chrome: ['selection.rotation-handle'],\n },\n move: {\n actions: ['move', 'clone'],\n ambient: ({ move }) => selectionMoveContribution({ move }),\n },\n edit: {\n actions: [\n 'escape', 'cancelGesture', 'selectAll', 'duplicate', 'transformAgain', 'delete',\n 'group', 'ungroup', 'undo', 'redo',\n 'nudge.up', 'nudge.down', 'nudge.left', 'nudge.right',\n 'clipboard.cut', 'clipboard.copy', 'clipboard.paste', 'clipboard.pasteEvent',\n 'setFill', 'setStroke', 'setFillOpacity', 'setStrokeOpacity',\n ],\n },\n arrange: {\n actions: [\n 'flip',\n 'reorder.forward', 'reorder.backward',\n 'align.left', 'align.centerX', 'align.right',\n 'align.top', 'align.centerY', 'align.bottom',\n 'distribute.horizontal', 'distribute.vertical',\n ],\n },\n paths: {\n actions: [\n 'pathfinder.union', 'pathfinder.subtract', 'pathfinder.intersect',\n 'pathfinder.exclude', 'pathfinder.divide', 'pathfinder.crop', 'pathfinder.trim', 'pathfinder.merge',\n 'createOutlines', 'outlineStroke',\n 'editAnchors', 'enterPathEdit', 'exitPathEdit', 'insertPathAnchor', 'bendSegment',\n 'nudgeAnchors.up', 'nudgeAnchors.down', 'nudgeAnchors.left', 'nudgeAnchors.right',\n 'deleteAnchors', 'cutPathAtAnchor', 'marqueeAnchors', 'selectAnchor',\n 'retractHandles', 'makeHandlesSymmetric', 'averageAnchors', 'joinAnchors',\n 'simplifyPath', 'offsetPath', 'snipAtCrossings', 'joinPaths',\n ],\n },\n ingest: {\n actions: ['ingest'],\n },\n};\n\nconst BASE_FEATURES = Object.keys(FEATURE_PRESETS) as BaseFeature[];\n\n/** The base presets each composite preset turns on. */\nexport const COMPOSITE_FEATURES: Readonly<Record<CompositeFeature, readonly BaseFeature[]>> = {\n pick: ['select', 'outline'],\n transform: ['resize', 'rotate'],\n draw: BASE_FEATURES,\n};\n\n/** The kit-standard actions each preset registers. */\nexport const FEATURE_ACTION_IDS = Object.fromEntries(\n BASE_FEATURES.map((f) => [f, FEATURE_PRESETS[f].actions]),\n) as Readonly<Record<BaseFeature, readonly string[]>>;\n\n/**\n * Kit-standard actions no preset registers: they belong to the tools that bind\n * them. A canvas registers each one whenever a tool it carries names it in a\n * binding — `insert` with any shape tool, `enterTextEdit` with the text tool.\n */\nexport const TOOL_DRIVEN_ACTION_IDS: readonly string[] = ['insert', 'insert.adjustRotation', 'insert.reposition', 'enterTextEdit'];\n\n/**\n * The base presets a canvas runs with: `features` with every composite\n * expanded, plus `view` whenever the canvas was handed a `viewport` config,\n * since that prop only configures what `view` turns on.\n */\nexport function resolveFeatures(\n features: readonly Feature[] | undefined,\n opts: { viewport?: boolean } = {},\n): ReadonlySet<BaseFeature> {\n const out = new Set<BaseFeature>();\n for (const f of features ?? []) {\n if (f in COMPOSITE_FEATURES) {\n for (const b of COMPOSITE_FEATURES[f as CompositeFeature]) out.add(b);\n } else {\n out.add(f as BaseFeature);\n }\n }\n if (opts.viewport) out.add('view');\n return out;\n}\n\n/** The kit-standard action ids the given presets register. */\nexport function featureActionIds(features: ReadonlySet<BaseFeature>): Set<string> {\n const ids = new Set<string>();\n for (const f of features) for (const id of FEATURE_PRESETS[f].actions) ids.add(id);\n return ids;\n}\n\n/** The built-in tools the given presets mount. */\nexport function featureToolIds(features: ReadonlySet<BaseFeature>): Set<BuiltinToolId> {\n const ids = new Set<BuiltinToolId>();\n for (const f of features) for (const id of FEATURE_PRESETS[f].tools ?? []) ids.add(id);\n return ids;\n}\n\n/** The given presets' ambient entries, in the order they are matched. */\nexport function featureAmbient(\n features: ReadonlySet<BaseFeature>,\n options: FeatureBindingOptions,\n): Contribution[] {\n return BASE_FEATURES.flatMap((f) => {\n const ambient = features.has(f) ? FEATURE_PRESETS[f].ambient : undefined;\n return ambient ? [ambient(options)] : [];\n });\n}\n\n/** Rules hiding the chrome of every preset that is not among `features`.\n * `never` also takes a handle out of hit-testing, so a hidden one cannot be\n * grabbed. */\nexport function featureChromeRules(features: ReadonlySet<BaseFeature>): VisibilityRules {\n const rules: VisibilityRules = {};\n for (const f of BASE_FEATURES) {\n if (features.has(f)) continue;\n for (const id of FEATURE_PRESETS[f].chrome ?? []) rules[id] = never;\n }\n return rules;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { viewToTransform as e } from "../../core/viewport/view.js";
|
|
2
|
+
import { worldToScreen as t } from "../../core/viewport/viewTransform.js";
|
|
3
|
+
import { getImageBitmap as n } from "@weasel-js/renderer";
|
|
4
|
+
import { ellipsePath as r, invert as i, linePath as a, mat3 as o, multiply as s, polylineFromPoints as c, rectPath as l, regularPolygonPath as u, starPath as d } from "@weasel-js/geom";
|
|
5
|
+
//#region src/canvas/SceneCanvas/insertPreviewCommands.ts
|
|
6
|
+
var f = .85;
|
|
7
|
+
function p(p, m, h) {
|
|
8
|
+
let g = e(m), _ = [], v = p.bounds, y = (e, n) => {
|
|
9
|
+
let [r, i] = t(e, n, g);
|
|
10
|
+
return {
|
|
11
|
+
x: r,
|
|
12
|
+
y: i
|
|
13
|
+
};
|
|
14
|
+
}, b = (e) => e.map((e) => y(e.x, e.y)), x = (m.scale.x + m.scale.y) / 2, S = null, C = p.geometry;
|
|
15
|
+
switch (C.kind) {
|
|
16
|
+
case "box": {
|
|
17
|
+
let [e, i] = t(v.x, v.y, g), a = v.width * m.scale.x, o = v.height * m.scale.y;
|
|
18
|
+
if (p.shape === "image") {
|
|
19
|
+
let t = p.extras, r = t.preview === "outline" || !t.src ? void 0 : n(t.src);
|
|
20
|
+
r && _.push({
|
|
21
|
+
kind: "image",
|
|
22
|
+
image: r,
|
|
23
|
+
x: e,
|
|
24
|
+
y: i,
|
|
25
|
+
w: a,
|
|
26
|
+
h: o,
|
|
27
|
+
opacity: f
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
S = p.shape === "ellipse" ? r({
|
|
31
|
+
x: e,
|
|
32
|
+
y: i,
|
|
33
|
+
width: a,
|
|
34
|
+
height: o
|
|
35
|
+
}) : l(e, i, a, o);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case "line":
|
|
39
|
+
S = a(y(C.a.x, C.a.y), y(C.b.x, C.b.y));
|
|
40
|
+
break;
|
|
41
|
+
case "polygon":
|
|
42
|
+
S = u(y(C.center.x, C.center.y), C.radius * x, C.sides, C.rotation);
|
|
43
|
+
break;
|
|
44
|
+
case "star":
|
|
45
|
+
S = d(y(C.center.x, C.center.y), C.outerRadius * x, C.points, C.innerRadius * x, C.rotation);
|
|
46
|
+
break;
|
|
47
|
+
case "pencil": S = c(b(C.samples));
|
|
48
|
+
}
|
|
49
|
+
if (!S) return _;
|
|
50
|
+
let w = {
|
|
51
|
+
kind: "path",
|
|
52
|
+
path: S,
|
|
53
|
+
fill: p.shape === "line" || p.shape === "pencil" || p.shape === "image" ? void 0 : { paint: { color: h.fill } },
|
|
54
|
+
stroke: {
|
|
55
|
+
paint: { color: h.stroke },
|
|
56
|
+
width: h.lineWidth,
|
|
57
|
+
dash: h.dash
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
if (_.push(w), p.copies && p.copies.length > 0) {
|
|
61
|
+
let e = [
|
|
62
|
+
m.scale.x,
|
|
63
|
+
0,
|
|
64
|
+
0,
|
|
65
|
+
m.scale.y,
|
|
66
|
+
g.panX,
|
|
67
|
+
g.panY
|
|
68
|
+
], t = i(e);
|
|
69
|
+
for (let n of p.copies) {
|
|
70
|
+
if (!t) break;
|
|
71
|
+
let r = s(e, s(n, t));
|
|
72
|
+
_.push({
|
|
73
|
+
kind: "group",
|
|
74
|
+
transform: o.fromAffine(r),
|
|
75
|
+
children: [w]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (p.anchorPoint) {
|
|
80
|
+
let e = y(p.anchorPoint.x, p.anchorPoint.y);
|
|
81
|
+
_.push({
|
|
82
|
+
kind: "path",
|
|
83
|
+
path: r({
|
|
84
|
+
x: e.x - 4,
|
|
85
|
+
y: e.y - 4,
|
|
86
|
+
width: 8,
|
|
87
|
+
height: 8
|
|
88
|
+
}),
|
|
89
|
+
fill: { paint: { color: h.stroke } }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return _;
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { p as insertPreviewCommands };
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=insertPreviewCommands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insertPreviewCommands.js","names":["mat3","mat3$1","PREVIEW_CONTENT_OPACITY","insertPreviewCommands","t","viewToTransform","view","out","b","ov","projectPoint","sx","sy","worldToScreen","wx","wy","projectPoints","pts","p","radialScale","pathCmd","geom","sw","sh","e","bmp","getImageBitmap","ellipsePath","rectPath","linePath","regularPolygonPath","starPath","polylineFromPoints","cmd","cfg","toScreen","toWorld","invert","m","s","multiply","anchorScreen"],"sources":["../../../src/canvas/SceneCanvas/insertPreviewCommands.ts"],"sourcesContent":["import {\n ellipsePath, invert, linePath, mat3, multiply, rectPath, regularPolygonPath, starPath, polylineFromPoints,\n type Affine,\n} from '@weasel-js/geom';\nimport { getImageBitmap, type DrawCommand, type PathDrawCommand } from '@weasel-js/renderer';\nimport { viewToTransform, type View } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\nimport type { ResolvedOverlay } from 'interactions/actions/resolveOverlays';\n\n/** Alpha for content previewed inside an in-flight gesture. Matches the\n * preview-ghost layer, so a dragged node and an uncommitted insert read as\n * equally provisional. */\nconst PREVIEW_CONTENT_OPACITY = 0.85;\n\n/** The overlay colors an insert preview is drawn in. */\nexport interface InsertPreviewStyle {\n fill: string;\n stroke: string;\n lineWidth: number;\n dash: number[];\n}\n\n/** An insert preview in screen space: its outline, under each copy it will be\n * drawn in, and the dot at the press. */\nexport function insertPreviewCommands(\n ov: Extract<ResolvedOverlay, { kind: 'insertPreview' }>,\n view: View,\n cfg: InsertPreviewStyle,\n): DrawCommand[] {\n const t = viewToTransform(view);\n const out: DrawCommand[] = [];\n const b = ov.bounds;\n const projectPoint = (wx: number, wy: number): { x: number; y: number } => {\n const [sx, sy] = worldToScreen(wx, wy, t);\n return { x: sx, y: sy };\n };\n const projectPoints = (\n pts: ReadonlyArray<{ x: number; y: number }>,\n ): { x: number; y: number }[] => pts.map((p) => projectPoint(p.x, p.y));\n // Radial shapes inscribe in a square, so a single screen-space\n // radius needs one scale factor; average the axes.\n const radialScale = (view.scale.x + view.scale.y) / 2;\n\n let pathCmd: PathDrawCommand['path'] | null = null;\n const geom = ov.geometry;\n switch (geom.kind) {\n case 'box': {\n const [sx, sy] = worldToScreen(b.x, b.y, t);\n const sw = b.width * view.scale.x;\n const sh = b.height * view.scale.y;\n if (ov.shape === 'image') {\n const e = ov.extras as Partial<{ src: string; preview: string }>;\n // The bitmap when it has decoded, the bare outline until then\n // (or when the tool opted out) — the outline is drawn either\n // way, so the drag always has an edge to read.\n const bmp = e.preview === 'outline' || !e.src\n ? undefined\n : getImageBitmap(e.src);\n if (bmp) {\n out.push({\n kind: 'image',\n image: bmp,\n x: sx, y: sy, w: sw, h: sh,\n opacity: PREVIEW_CONTENT_OPACITY,\n });\n }\n }\n pathCmd = ov.shape === 'ellipse'\n ? ellipsePath({ x: sx, y: sy, width: sw, height: sh })\n : rectPath(sx, sy, sw, sh);\n break;\n }\n case 'line':\n pathCmd = linePath(projectPoint(geom.a.x, geom.a.y), projectPoint(geom.b.x, geom.b.y));\n break;\n case 'polygon':\n pathCmd = regularPolygonPath(\n projectPoint(geom.center.x, geom.center.y),\n geom.radius * radialScale,\n geom.sides,\n geom.rotation,\n );\n break;\n case 'star':\n pathCmd = starPath(\n projectPoint(geom.center.x, geom.center.y),\n geom.outerRadius * radialScale,\n geom.points,\n geom.innerRadius * radialScale,\n geom.rotation,\n );\n break;\n case 'pencil':\n // Genuinely open: `polygonFromPoints` closes the path, and the\n // closing edge from the last sample back to the first reads as\n // a marquee rather than as the stroke being drawn.\n pathCmd = polylineFromPoints(projectPoints(geom.samples));\n break;\n }\n\n if (!pathCmd) return out;\n const cmd: PathDrawCommand = {\n kind: 'path',\n path: pathCmd,\n fill: ov.shape === 'line' || ov.shape === 'pencil' || ov.shape === 'image'\n ? undefined\n : { paint: { color: cfg.fill } },\n stroke: { paint: { color: cfg.stroke }, width: cfg.lineWidth, dash: cfg.dash },\n };\n out.push(cmd);\n // Each copy the shape will be drawn in, carried into screen space.\n if (ov.copies && ov.copies.length > 0) {\n const toScreen: Affine = [view.scale.x, 0, 0, view.scale.y, t.panX, t.panY];\n const toWorld = invert(toScreen);\n for (const m of ov.copies) {\n if (!toWorld) break;\n const s = multiply(toScreen, multiply(m, toWorld));\n out.push({ kind: 'group', transform: mat3.fromAffine(s), children: [cmd] });\n }\n }\n // Anchor dot at the drag's click point: for radial shapes no\n // vertex sits at the AABB corner, and in center mode the dot marks\n // the growth axis.\n if (ov.anchorPoint) {\n const anchorScreen = projectPoint(ov.anchorPoint.x, ov.anchorPoint.y);\n out.push({\n kind: 'path',\n path: ellipsePath({\n x: anchorScreen.x - 4,\n y: anchorScreen.y - 4,\n width: 8,\n height: 8,\n }),\n fill: { paint: { color: cfg.stroke } },\n });\n }\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poseGeometry.js","names":["aabbOfPose","AUTO_POSE_DESCRIPTOR","descriptor","pose","DEFAULT_PATH_STROKE_SLOP","poseContains","containsInFrame","wx","wy","tolerance","poseContainsRotated","framed","framedBounds","frame","poseFrameOf","back","poseFrameInverseOf","lx","ly","applyToPoint","poseLinearOf","WORLD_SCALE","pathLike","isPathLike","world","throughFrame","stretched","reach","screen","slopForStrokeHit","pointInPath","x","y","strokeHitTest","opts","b","grown","hasSlop"],"sources":["../../../src/canvas/SceneCanvas/poseGeometry.ts"],"sourcesContent":["/**\n * Pose-shape helpers shared by `<SceneCanvas>` internals. Every bounds read goes through a\n * `PoseDescriptor`, defaulting to `AUTO_POSE_DESCRIPTOR`, so a consumer-supplied descriptor\n * reaches hit-test, marquee, and bounds extraction alike.\n */\nimport type { Bounds } from 'tools/builtin/select';\nimport { applyToPoint, pointInPath, strokeHitTest, type
|
|
1
|
+
{"version":3,"file":"poseGeometry.js","names":["aabbOfPose","AUTO_POSE_DESCRIPTOR","descriptor","pose","DEFAULT_PATH_STROKE_SLOP","poseContains","containsInFrame","wx","wy","tolerance","poseContainsRotated","framed","framedBounds","frame","poseFrameOf","back","poseFrameInverseOf","lx","ly","applyToPoint","poseLinearOf","WORLD_SCALE","pathLike","isPathLike","world","throughFrame","stretched","reach","screen","slopForStrokeHit","pointInPath","x","y","strokeHitTest","opts","b","grown","hasSlop"],"sources":["../../../src/canvas/SceneCanvas/poseGeometry.ts"],"sourcesContent":["/**\n * Pose-shape helpers shared by `<SceneCanvas>` internals. Every bounds read goes through a\n * `PoseDescriptor`, defaulting to `AUTO_POSE_DESCRIPTOR`, so a consumer-supplied descriptor\n * reaches hit-test, marquee, and bounds extraction alike.\n */\nimport type { Bounds } from 'tools/builtin/select';\nimport { applyToPoint, pointInPath, strokeHitTest, type Affine } from '@weasel-js/geom';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { framedBounds } from 'core/geometry/poseDescriptor';\nimport { AUTO_POSE_DESCRIPTOR, isPathLike } from 'core/geometry/autoPoseDescriptor';\nimport { poseFrameInverseOf, poseFrameOf, poseLinearOf } from '@weasel-js/geom';\nimport { hasSlop, slopForStrokeHit, type ScreenSlop } from 'core/viewport/screenSlop';\n\nexport { isPathLike };\n\nexport function aabbOfPose<TPose>(\n pose: TPose,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n): Bounds {\n return descriptor.getBounds(pose);\n}\n\n/** Default slop for the stroke-distance hit-test on paths, in world units. Independent of\n * the stroke's rendered width, which the hit-test layer doesn't know. */\nconst DEFAULT_PATH_STROKE_SLOP = 4;\n\n/** A containment slop: `reach` world units the caller knows the ink extends\n * past the pose (a stroke's outward half-width), then `px`/`scale` of screen\n * slop measured from there. */\nexport interface PoseSlop extends ScreenSlop {\n reach?: number;\n}\n\n/**\n * @param tolerance - Grab slop. A number is **world** units and grows the\n * AABB test and the path stroke-distance test alike; a {@link PoseSlop}\n * is measured on screen, from `reach` world units out. Defaults to\n * {@link DEFAULT_PATH_STROKE_SLOP} for path-like poses and to `0` for rect poses.\n */\nexport function poseContains<TPose>(\n pose: TPose,\n wx: number,\n wy: number,\n tolerance?: number | PoseSlop,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n): boolean {\n return containsInFrame(pose, wx, wy, tolerance, descriptor, null, false);\n}\n\n/**\n * Containment for `<SceneCanvas>`'s default `pickEvery`. When the pose has a\n * frame (`poseFrameOf`: a rotation, a `transform`, or both), takes the query\n * point back through it and tests in the pose's own box frame, mirroring the\n * renderer's wrap so the click target matches the drawn shape. A pose with no\n * frame tests directly, and one whose `transform` is singular draws nothing and\n * contains nothing.\n *\n * Rotation comes from the descriptor when one is supplied, and otherwise from\n * `pose.rotation`, the convention the renderer reads. A `tolerance` is a\n * distance in world or on screen whatever the frame; only a `PoseSlop`'s\n * `reach` is in the pose's own frame, where the stroke it stands for is drawn.\n */\nexport function poseContainsRotated<TPose>(\n pose: TPose,\n wx: number,\n wy: number,\n tolerance?: number | PoseSlop,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n): boolean {\n const framed = descriptor === AUTO_POSE_DESCRIPTOR ? pose : framedBounds(pose, descriptor);\n const frame = poseFrameOf(framed);\n if (!frame) return containsInFrame(pose, wx, wy, tolerance, descriptor, null, false);\n const back = poseFrameInverseOf(framed);\n if (!back) return false;\n const [lx, ly] = applyToPoint(back, wx, wy);\n return containsInFrame(pose, lx, ly, tolerance, descriptor, frame, poseLinearOf(framed) !== null);\n}\n\nconst WORLD_SCALE = { x: 1, y: 1 };\n\n/** `(x, y)` is in the pose's own frame, which reaches world by `frame`.\n * `stretched` is whether that frame changes lengths: it carries a `transform`. */\nfunction containsInFrame<TPose>(\n pose: TPose,\n x: number,\n y: number,\n tolerance: number | PoseSlop | undefined,\n descriptor: PoseDescriptor<unknown>,\n frame: Affine | null,\n stretched: boolean,\n): boolean {\n const pathLike = isPathLike(pose);\n const world = typeof tolerance === 'object' ? undefined\n : tolerance ?? (pathLike ? DEFAULT_PATH_STROKE_SLOP : 0);\n // A world distance is not one in a stretched frame, so it is tested through\n // the frame, as a screen slop is: at a scale of one.\n const throughFrame = stretched && world !== undefined && world > 0;\n const reach = throughFrame ? 0 : world ?? (tolerance as PoseSlop).reach ?? 0;\n const screen = throughFrame ? { px: world, scale: WORLD_SCALE }\n : typeof tolerance === 'object' ? tolerance : undefined;\n const { opts } = slopForStrokeHit(screen, frame);\n if (pathLike) {\n // Inside the filled region, or within stroke slop.\n if (pointInPath(pose, x, y)) return true;\n return strokeHitTest(pose, x, y, reach, opts);\n }\n const b = aabbOfPose(pose, descriptor);\n // `reach` is the caller's budget for being at least as generous as whatever\n // refinement follows: the stroke's outward reach, which this function\n // cannot see. Grown square, so it contains the round band the refinement\n // tests.\n const grown = {\n kind: 'rect' as const,\n x: b.x - reach, y: b.y - reach, width: b.width + 2 * reach, height: b.height + 2 * reach,\n };\n if (pointInPath(grown, x, y)) return true;\n return hasSlop(screen) && strokeHitTest(grown, x, y, 0, opts);\n}\n"],"mappings":";;;;;AAeA,SAAgBA,EACd,GACA,IAAsCC,GAC9B;CACR,OAAOC,EAAW,UAAUC,CAAI;AAClC;AAIA,IAAMC,IAA2B;AAejC,SAAgBC,EACd,GACA,GACA,GACA,GACA,IAAsCJ,GAC7B;CACT,OAAOK,EAAgBH,GAAMI,GAAIC,GAAIC,GAAWP,GAAY,MAAM,EAAK;AACzE;AAeA,SAAgBQ,EACd,GACA,GACA,GACA,GACA,IAAsCT,GAC7B;CACT,IAAMU,IAAST,MAAeD,IAAuBE,IAAOS,EAAaT,GAAMD,CAAU,GACnFW,IAAQC,EAAYH,CAAM;CAChC,IAAI,CAACE,GAAO,OAAOP,EAAgBH,GAAMI,GAAIC,GAAIC,GAAWP,GAAY,MAAM,EAAK;CACnF,IAAMa,IAAOC,EAAmBL,CAAM;CACtC,IAAI,CAACI,GAAM,OAAO;CAClB,IAAM,CAACE,GAAIC,KAAMC,EAAaJ,GAAMR,GAAIC,CAAE;CAC1C,OAAOF,EAAgBH,GAAMc,GAAIC,GAAIT,GAAWP,GAAYW,GAAOO,EAAaT,CAAM,MAAM,IAAI;AAClG;AAEA,IAAMU,IAAc;CAAE,GAAG;CAAG,GAAG;AAAE;AAIjC,SAASf,EACP,GACA,GACA,GACA,GACA,GACA,GACA,GACS;CACT,IAAMgB,IAAWC,EAAWpB,CAAI,GAC1BqB,IAAQ,OAAOf,KAAc,WAAW,KAAA,IAC1CA,MAAca,IAAWlB,IAA2B,IAGlDqB,IAAeC,KAAaF,MAAU,KAAA,KAAaA,IAAQ,GAC3DG,IAAQF,IAAe,IAAID,KAAUf,EAAuB,SAAS,GACrEmB,IAASH,IAAe;EAAE,IAAID;EAAO,OAAOH;CAAY,IAC1D,OAAOZ,KAAc,WAAWA,IAAY,KAAA,GAC1C,EAAE,YAASoB,EAAiBD,GAAQf,CAAK;CAC/C,IAAIS,GAGF,OADIQ,EAAY3B,GAAM4B,GAAGC,CAAC,IAAU,KAC7BC,EAAc9B,GAAM4B,GAAGC,GAAGL,GAAOO,CAAI;CAE9C,IAAMC,IAAInC,EAAWG,GAAMD,CAAU,GAK/BkC,IAAQ;EACZ,MAAM;EACN,GAAGD,EAAE,IAAIR;EAAO,GAAGQ,EAAE,IAAIR;EAAO,OAAOQ,EAAE,QAAQ,IAAIR;EAAO,QAAQQ,EAAE,SAAS,IAAIR;CACrF;CAEA,OADIG,EAAYM,GAAOL,GAAGC,CAAC,IAAU,KAC9BK,EAAQT,CAAM,KAAKK,EAAcG,GAAOL,GAAGC,GAAG,GAAGE,CAAI;AAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"props.js","names":["DEFAULT_HANDLE_SIZE","HANDLE_BASE_PX","mergeLayersWithDefaults","defaults","defaultDrawOne","defaultPaintBounds","targetSizesPx","targetScale","user","result","isCustomEntry","scene","paintBoundsFor","NON_SHAPE_BUILTIN_TOOLS","BUILTIN_TOOL_IDS","KIT_SHAPE_KINDS"],"sources":["../../../src/canvas/SceneCanvas/props.ts"],"sourcesContent":["import type { ModeRegistry } from '@weasel-js/modes';\nimport type { PathEditHost } from './pathEditHost';\nimport type { Paint } from '@weasel-js/paint';\nimport type { ActionsProp } from '@weasel-js/routing';\nimport { Yoke } from '@weasel-js/routing/react';\nimport type { PasteOmission } from 'core/adapters/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene, SerializedScene } from 'core/scene/types';\nimport type { SelectionApi, UseSelectionOptions } from 'core/selection/useSelection';\nimport { KIT_SHAPE_KINDS, type BuiltinShapeToolId } from 'core/shapeKinds';\nimport type { InertiaConfig } from 'core/viewport/useDecayLoop';\nimport type { View } from 'core/viewport/view';\nimport type {\n ContentHandlerEntry\n} from 'features/ingestion';\nimport type { PinchZoomOptions } from 'interactions/actions/defaults/pinchZoom';\nimport type { WheelPanOptions } from 'interactions/actions/defaults/viewportWheelPan';\nimport type { ViewportZoomAnimateOptions, ViewportZoomOptions } from 'interactions/actions/defaults/viewportZoom';\nimport type { SvgIngestOptions } from 'interactions/actions/depSchema';\nimport type { GeometryProjection } from 'interactions/actions/geometryProjection';\nimport type { UseMoveOptions } from 'interactions/actions/move/options';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { UseResizeOptions } from 'interactions/actions/resize/options';\nimport type { UseRotateOptions } from 'interactions/actions/rotate/options';\nimport type { SnapStrategy } from 'interactions/gestures/types';\nimport type { ReactNode } from 'react';\nimport type { Bounds } from 'tools/builtin/select';\nimport type { Animator } from '@weasel-js/animator';\nimport { HANDLE_BASE_PX, targetSizesPx } from '../../core/device/targets';\nimport type { DeviceProfile } from '../../core/device/types';\nimport type { NodeRoutingEntry } from '../../core/scene/NodeRouting';\nimport type { DebugConfig } from '../../debug/types';\nimport type { LayoutDropTargetMode, ReflowTransition } from '../../layout/types';\nimport type { DrawCommand, ShaderProgramHandle } from '@weasel-js/renderer';\nimport type { Tool, ToolsApi } from '../../tools/overlayBinding';\nimport type { SceneSlotConfig } from '../buildSceneLayer';\nimport type { CanvasProps } from '../Canvas';\nimport { isCustomEntry, type LayersMap } from '../layerSlots';\nimport type { CanvasViewProps } from '../CanvasView';\nimport { defaultDrawOne, defaultPaintBounds, paintBoundsFor } from '../defaultDrawOne';\nimport type {\n InsertNodeFactory,\n InsertPaint\n} from '../deps';\nimport type { SceneToAdapterOptions } from '../sceneAdapter';\nimport type { SurfaceContribution } from '../surfaceContribution';\nimport type { CameraProps } from '../useCanvasView';\nimport type {\n Feature\n} from './features';\nimport type { BuiltinToolOptions } from './useBuiltinShapeTools';\nimport type { VersionCheck } from './useContributionRoles';\n\n/** The adapter surface delete / duplicate / group / ungroup need, narrowed from\n * `SceneCanvasAdapter` because `StandardActionsRegistrar` is non-generic. */\nexport interface BridgeAdapter {\n applyOps(ops: Op[], label?: string): void;\n insertNode(node: { id: string; [k: string]: unknown }): void;\n removeNode(id: string): void;\n setSelection(ids: string[]): void;\n}\n\n/** Default size in CSS pixels for selection corner-handles AND their\n * hit-test radius, at `targetScale = 1`. Deliberately unscaled; kit-internal\n * use sites multiply by `DeviceProfile.targetScale`. */\nexport const DEFAULT_HANDLE_SIZE = HANDLE_BASE_PX;\n\n/** `<SceneCanvas longPress>`. */\nexport interface SceneCanvasLongPress {\n /**\n * Shown while a long-press is held and some binding would fire on it.\n * Default: a ring that fills around the press point\n * (`createLongPressFeedbackContribution()`), static under\n * `prefers-reduced-motion`. `false` shows nothing; a contribution replaces\n * it, reading the `longPress` dep. Installed beside `ambient`, so under a\n * `tools` takeover add it to your own `useTools` call instead.\n */\n feedback?: boolean | SurfaceContribution;\n /** Default true. A long-press some binding handled pulses\n * `navigator.vibrate` briefly, for touch and pen, where the API exists. */\n haptics?: boolean;\n /** Hold time in ms. Default `LONG_PRESS_MS` (500). */\n duration?: number;\n}\n\n/** `<SceneCanvas layers>`: a {@link LayersMap} whose scene slot may be partial,\n * since the defaults fill in whatever it leaves out. */\nexport type SceneCanvasLayers<TNode extends { id: string }, TPose> = LayersMap<\n TNode,\n TPose,\n Partial<SceneSlotConfig<TNode, TPose>>\n>;\n\n/** Deep-merge user-supplied `layers` with kit defaults. Slots the user\n * doesn't mention get filled with defaults; slots explicitly set to\n * `null` are dropped. Partial slot configs are shallow-spread on top of the\n * default slot config. `targetScale` comes from the resolved {@link DeviceProfile}. */\nexport function mergeLayersWithDefaults<TData, TLayer extends string, TPose>(\n user: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose> | undefined,\n targetScale = 1,\n): LayersMap<Node<TData, TLayer, TPose>, TPose> {\n const defaults = {\n scene: {\n drawOne: defaultDrawOne as (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n ) => DrawCommand[],\n paintBounds: defaultPaintBounds<TData, TLayer, TPose>,\n },\n selectionOverlay: { handles: { size: targetSizesPx(targetScale).handle } },\n };\n\n if (!user) return defaults as LayersMap<Node<TData, TLayer, TPose>, TPose>;\n\n // Start from a shallow copy of the user map so unknown slots pass through.\n const result = { ...user } as LayersMap<Node<TData, TLayer, TPose>, TPose>;\n\n if (!('scene' in user)) {\n result.scene = defaults.scene;\n } else if (user.scene === null || isCustomEntry(user.scene)) {\n result.scene = user.scene;\n } else {\n const scene = { ...defaults.scene, ...user.scene };\n result.scene = user.scene && 'paintBounds' in user.scene\n ? scene\n : { ...scene, paintBounds: paintBoundsFor<TData, TLayer, TPose>(scene.drawOne) };\n }\n\n if (!('selectionOverlay' in user)) {\n result.selectionOverlay = defaults.selectionOverlay;\n } else if (user.selectionOverlay === null || isCustomEntry(user.selectionOverlay)) {\n result.selectionOverlay = user.selectionOverlay;\n } else {\n result.selectionOverlay = { ...defaults.selectionOverlay, ...user.selectionOverlay };\n }\n\n return result;\n}\n\n/** Built-in tool ids SceneCanvas knows how to mount when no `tools` prop\n * is supplied, via `defaultTools`. */\nexport type BuiltinToolId = 'select' | 'hand' | BuiltinShapeToolId;\n\n/** Built-in tool ids that aren't shape tools — the ones with no entry in\n * the `core/shapeKinds` table. */\nexport const NON_SHAPE_BUILTIN_TOOLS: readonly Exclude<BuiltinToolId, BuiltinShapeToolId>[] = ['select', 'hand'];\n\n/** Every built-in tool id, shape tools included. */\nexport const BUILTIN_TOOL_IDS: readonly BuiltinToolId[] = [...NON_SHAPE_BUILTIN_TOOLS, ...KIT_SHAPE_KINDS];\n\n/** Minimal hit descriptor passed to `onDoubleClick`. Contains the fields\n * modality dispatch needs; extends as the kit's `Hit` type evolves. */\nexport interface SceneCanvasHit {\n id: string;\n kind: string;\n}\n\n/** Props for `<SceneCanvas>`. A scene is the only thing it needs. With nothing\n * else it renders that scene and keeps a selection no input sets; `features`\n * turns behavior on by preset, and the rest configures what those turn on. */\nexport type SceneCanvasProps<TData, TLayer extends string, TPose> =\n Omit<\n CanvasProps<TPose>,\n | 'adapter' | 'boundsOf' | 'selection' | 'tools'\n | 'viewState' // SceneCanvas makes the camera from the view props below\n | 'layers' // stripped so we can re-add as optional below\n | 'getIsVisible' // SceneCanvas synthesizes this from chromeVisibility\n | 'contentVersion' // SceneCanvas wires this to the scene's own version\n | 'layerVisibility' | 'layerOrder' // re-declared below: they gate picking here too\n | 'debug' // re-declared below with SceneCanvas's own `slops`\n > & CameraProps\n & {\n /** A `Scene` (typically from `useScene`) — or a `SerializedScene` JSON\n * object, which SceneCanvas bakes into a Scene on first render. The\n * serialized form is read once; pass a `key` on `<SceneCanvas>` to force\n * a fresh canvas from updated JSON.\n *\n * The JSON shape is relaxed (`version: number` rather than the `1`\n * literal) so an `import json from './x.json'` result satisfies the type\n * without an `as` cast. */\n scene:\n | Scene<TData, TLayer, TPose>\n | SerializedScene<TData, TLayer, TPose>\n | { version: number; systemLayers?: ReadonlyArray<{ id: string }>; nodes: ReadonlyArray<unknown> };\n\n /** Layer configuration. When omitted, SceneCanvas applies kit defaults\n * (a scene slot that paints `node.data.fill` rects + a default\n * selection overlay). A partial scene or selection-overlay config is\n * spread over that slot's defaults; pass `slot: null` to suppress a\n * default explicitly. */\n layers?: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose>;\n\n /** Layout strategies keyed by container node id (or a resolver), for\n * containers whose scene node declares none (`ContainerNode.layout`\n * wins). Applied to a drag and to every other change to their children,\n * through the scene's layout pass. */\n layouts?: SceneToAdapterOptions<TData, TLayer, TPose>['layouts'];\n\n /** Which layout container a drag lands in when several contain the drop\n * point — the innermost (default), the topmost in paint order, or only\n * containers whose strategy declares a `dropRegion`. Decides both the\n * drag-time reflow preview and the commit. */\n layoutDropTarget?: LayoutDropTargetMode;\n\n /** Carries the siblings a layout reflow displaces to their slots over time\n * instead of snapping them — `useAnimatedReflow` builds one. Covers a\n * drag's reflow and every one the scene records (an insert, delete,\n * reorder or resize inside a laid-out container). Absent or `null`,\n * they snap. */\n reflowTransition?: ReflowTransition<TPose> | null;\n\n /**\n * Lets pose-transform actions (move, resize, nudge, flip — NOT rotate) also rewrite a\n * node's data-held geometry. Given a node and the affine `m` applied to its pose,\n * `transform(node, m)` returns updated `data`, or `null` for nodes with no data-held\n * geometry. When absent, the kit emits only the pose op and leaves `data` untouched.\n *\n * @see GeometryProjection\n */\n geometryProjection?: GeometryProjection;\n\n /**\n * Routing-trait classifiers. The kit builds a `NodeRouting` registry per `<SceneCanvas>`\n * from this prop and uses it to derive each hit's `kind`, which tool routing tables\n * (e.g. `{ target: 'rect', actionId: 'move' }`) match against. Pass `defaultNodeRouting`\n * for the kit's built-in shape kinds; spread additional entries for consumer-defined\n * kinds.\n *\n * **Memoize the `routing` value.** The registry is memoized on the prop's reference\n * identity: a fresh array each render rebuilds it and cascades into a new adapter,\n * churning gesture state.\n */\n routing?: readonly NodeRoutingEntry[];\n\n /** How to read and rewrite this scene's poses. Every built-in action,\n * the selection chrome and picking read it. Default `AUTO_POSE_DESCRIPTOR`\n * (rect poses and `Path` poses). */\n poseDescriptor?: PoseDescriptor<TPose>;\n\n // --- Geometry: hit-test + bounds overrides consumed by the internal\n // `useSelectTool`. Ignored if the consumer passes their own `tools`. ---\n geometry?: {\n /** Hit-test override. Return the topmost id, the full back-to-front hit\n * stack (`string[]`), or `null` for empty space. The stack form lets a\n * consumer with domain overlap ordering (e.g. children over their\n * container) feed the kit's `pickTopMostHit` the true order instead of\n * pre-collapsing to one id. Matches `Canvas`'s `pickEvery` shape. */\n pickEvery?: (worldX: number, worldY: number) => string | string[] | null;\n boundsOf?: (id: string) => Bounds | null;\n /**\n * What \"the pointer is on this node\" means for the default body-pick.\n *\n * - `'pose'` — the node's pose rect, rotation honored.\n * - `'shape'` (default) — the pose rect as a pre-filter, then the ink the painter\n * actually lays down: its silhouette filled or not per the painter's `ink`, plus\n * its outline widened by the stroke half-width and `pickTolerancePx`.\n *\n * Painters with no silhouette keep the pose-rect answer either way. Ignored when\n * `pickEvery` is supplied: that override owns the test.\n */\n picking?: 'pose' | 'shape';\n /**\n * Grab slop around a shape's outline, in **screen** pixels, so the target keeps\n * its apparent size at any zoom. Default 4. It widens the outline test under\n * `picking: 'shape'` and grows the pose-rect pre-filter so those outline hits\n * survive it. Set `0` for exact geometry.\n */\n pickTolerancePx?: number;\n };\n\n // --- Selection options. `pickBest` and `handleHitRadius` configure the\n // internal select tool, and are ignored under a `tools` takeover;\n // `move`, `snap`, `resize` and `rotate` configure the `move`,\n // `resize` and `rotate` presets, and apply either way. ---\n selectTool?: {\n move?: UseMoveOptions<TPose>;\n resize?: UseResizeOptions<TPose>;\n /** Rotation options, or `false` to disable rotation entirely — drops the\n * `rotate` action AND hides the selection rotation-handle chrome. */\n rotate?: UseRotateOptions<TPose> | false;\n snap?: SnapStrategy<TPose>;\n handleHitRadius?: number;\n /** Override the body-pick used on click/pointerdown. Alt-aware: receives\n * the live alt state + current selection so consumers can implement\n * alt-cycling through an overlapping stack. Default: top-most hit\n * (alt ignored). */\n pickBest?: (worldX: number, worldY: number, alt: boolean, sel: readonly string[]) => string | null;\n };\n\n /** Consumer node factories for the `insert` action, keyed by tool `kind`.\n * Each factory receives the drag AABB + tool `extras` and returns the\n * node's `data` (in this canvas's own data shape) plus an optional `pose`.\n * A factory for a kit kind (`rect`, `line`, …) replaces the kit's default\n * `{ path, fill }` node for that kind; a factory for a novel kind (e.g.\n * `text`) adds insert support the kit doesn't ship. The dep supplies id,\n * layer, and the undoable op. Return `null` to reject an insert. */\n insertNodeFactories?: Record<string, InsertNodeFactory>;\n\n /** The fill, stroke, and fill rule a kit-built shape is inserted with, by\n * tool `kind` and insert count; the kit keeps the geometry. Default\n * `paletteInsertPaint`, the kit palette in turn. A kind with a factory in\n * `insertNodeFactories` is not asked. */\n insertPaint?: InsertPaint;\n\n /** External-content ingestion (OS drop / clipboard paste / picker).\n *\n * - `handlers` — consumer content handlers registered for this canvas's\n * lifetime (priority 0 by default — they beat the kit's `image/*` /\n * `image/svg+xml` handlers at -100/-90).\n * - `resolveSrc` — overrides the image handler's `data:`-URI embed (e.g.\n * upload to an asset store, return the URL).\n * - `svg.unpack` — makes the kit SVG handler parse dropped SVG files into\n * native scene nodes instead of one embedded-image node each.\n * - `clipboard` — the kit weasel-JSON paste handler, enabled by default.\n * `reviver` restores JSON-unfriendly values the copying side encoded via\n * `jsonReplacer`. `enabled: false` opts the canvas out, but the kit\n * handler still consumes weasel-matching items and declines them inert\n * rather than letting them fall through. Also governs Cmd/Ctrl+V under\n * the `edit` preset; the rest takes the `ingest` preset.\n *\n * Memoize `handlers` and `clipboard.reviver`: an inline array literal\n * re-registers the handlers on every render. */\n ingestion?: {\n handlers?: ContentHandlerEntry[];\n resolveSrc?: (file: File) => Promise<string>;\n svg?: SvgIngestOptions;\n clipboard?: {\n reviver?: (key: string, value: unknown) => unknown;\n enabled?: boolean;\n /** Called when a paste left copied nodes out, with each one and the\n * reason. `useClipboardOps` takes the same callback as\n * `onPasteOmitted` for a paste from its own in-memory copy. */\n onOmitted?: (omitted: PasteOmission[]) => void;\n };\n };\n\n selection?: SelectionApi;\n selectionOptions?: UseSelectionOptions;\n\n /**\n * `false` stops the canvas writing the selection: no click, marquee,\n * lasso, action or tool, and no op committed through the adapter it hands\n * its tools. The consumer's own `SelectionApi` — the `selection` prop, or\n * `useSelection({ scene })` — still writes, and the canvas draws what it\n * holds. Undo and redo still restore the selection each history entry\n * recorded. Default `true`.\n *\n * Click policy (whether shift-click extends) is the selection's `mode`:\n * `useSelection({ mode: 'multi' })`, or `selectionOptions={{ mode: 'multi' }}`\n * for the selection the canvas builds itself.\n */\n selectable?: boolean;\n\n /**\n * Behavior presets to turn on, composable in any combination. A canvas\n * with none renders its scene and keeps a selection (the `selection` API\n * works) that no input sets: no tools, no standard actions or their keys,\n * no clipboard, no drop or paste, and no selection chrome. The dispatcher\n * is still mounted, so bindings a consumer adds work.\n *\n * - `view` — wheel pan and zoom, pinch, the zoom keys, and the hand tool\n * (H, or hold Space). Passing `viewport` implies it.\n * - `select` — the select tool, as the initial active tool and Escape's\n * return target: click to pick, drag on empty to marquee, click on empty\n * to clear.\n * - `outline` — the selection outline, whichever tool sets the selection.\n * - `pick` — `select` and `outline`.\n * - `move` — drag a body to move it, Alt-drag to clone it. Ambient, so it\n * runs under any tool that does not claim the drag.\n * - `resize` — resize handles, drawn and bound.\n * - `rotate` — the rotation handle, drawn and bound.\n * - `transform` — `resize` and `rotate`.\n * - `edit` — undo/redo, delete, duplicate, group/ungroup, nudge,\n * select-all, Escape, cancel-gesture, cut/copy/paste, fill and stroke,\n * and their keys. Cmd/Ctrl+V pastes nodes a kit canvas copied, from\n * this tab or another, and nothing else.\n * - `arrange` — align, distribute, reorder, flip.\n * - `paths` — pathfinder operations, path-edit entry and anchor editing.\n * - `ingest` — dropped and pasted content: images, SVG, files, and\n * whatever `ingestion.handlers` accept, on Cmd/Ctrl+V or an OS drop.\n * - `draw` — all of the above.\n *\n * Tools add the actions they bind — a shape tool brings `insert` — so\n * `defaultTools` composes with any preset. `actions` then adds, overrides\n * or removes on top of what the presets registered.\n */\n features?: readonly Feature[];\n\n /** Extra tools or overrides keyed by id, or a full `ToolsApi` takeover.\n *\n * **Patch form** (`Record<string, Tool | true | false>`): merged\n * into the built-in registry on top of whatever `defaultTools` and\n * `features` already selected.\n * - `true` pulls in the built-in for this id (`'pen'`, `'lasso'`,\n * `'hand'`, …), the same as listing it in `defaultTools`. Unknown\n * built-in ids warn in dev and are ignored.\n * - `Tool` adds a new id or replaces an existing one\n * (dev-only warning on replace).\n * - `false` omits a bundled tool entirely.\n * Auto-wiring (keybindings, dispatcher, action registry) still runs.\n *\n * **Takeover form** (`ToolsApi`): the internal registry is bypassed and\n * this `tools` value is forwarded to Canvas as-is — the consumer owns\n * active-slot management, and `pick`'s select tool is theirs to supply.\n * Every other preset still applies. Keybindings are still auto-wired\n * against the supplied registry; pass `enableKeybindings={false}` to opt\n * out. */\n tools?: ToolsApi | Record<string, Tool | true | false>;\n\n /**\n * Auto-wire keyboard shortcuts. Default `true`. When `false`, SceneCanvas still mounts\n * its tools but routes no keyboard input to them — neither tool hotkeys nor\n * keyboard-bound actions like delete / escape / nudge. Pointer, wheel, and contextmenu\n * interactions are unaffected.\n */\n enableKeybindings?: boolean;\n\n /**\n * Take the keyboard on mount, as a press in the canvas would. A key that\n * reaches the page goes to the canvas the user last pressed or scrolled\n * in, and to no canvas before the first such interaction, so a canvas in a\n * side panel does not take Space from a page that wants it. An app whose\n * canvas is the page sets this so its shortcuts work before the first\n * click. Default `false`.\n */\n claimKeys?: boolean;\n\n /**\n * Auto-mount the gesture dispatcher (`useGestureDispatcher`). Default `true`. `false` is\n * for tests or demos that drive actions another way, or that call\n * `useGestureDispatcher` themselves.\n */\n enableGestureDispatcher?: boolean;\n\n /** Called with the `ToolsApi` SceneCanvas constructs or receives, whenever its identity\n * changes. For introspection. */\n onToolsCreated?: (tools: ToolsApi) => void;\n\n /**\n * Built-in tools SceneCanvas registers in its internal `useTools`, on top\n * of the ones presets bring (`select` from `pick`, `hand` from `view`).\n * Default: none. Each tool also registers the kit actions it binds, so\n * `['rect']` brings `insert`. Ignored when the consumer supplies their own\n * `tools` prop.\n */\n defaultTools?: readonly BuiltinToolId[];\n\n /** Per-tool option overrides for the built-in shape/lasso/clone tools.\n * Each entry is a narrow subset of the underlying hook's options\n * surface — `lasso.mode`, `clone.cloneSelection`, etc. */\n toolOptions?: BuiltinToolOptions;\n\n /** Initial active-slot tool id. Default: `'select'` when it is\n * registered, otherwise none — a canvas can run on ambient bindings\n * alone. Must be one of the registered tools. Ignored when the consumer\n * supplies their own `tools` prop. */\n initialActiveTool?: string;\n\n /** Always-on entries to register alongside the internal tools — every one\n * a `SurfaceContribution`, whose views, deps, overlay, bindings, actions\n * and `attach` all install from this one list, and uninstall when the\n * entry leaves it. A tool is a contribution too, so one can go here. If\n * you supply your own `tools` prop, this is ignored — wire `ambient`\n * through your own `useTools` call instead. */\n ambient?: readonly SurfaceContribution[];\n\n /** What an entry's `requires` does when the running kit does not meet\n * it: `'warn'` (the default) logs in development, `'strict'` throws,\n * `'off'` skips the check. */\n versionCheck?: VersionCheck;\n\n /** Touch and pen long-press: its hold time, its haptic, and the\n * feedback shown while it is held. */\n longPress?: SceneCanvasLongPress;\n\n /** Configures the `view` preset, and implies it: passing this turns\n * `view` on.\n *\n * - `inertia` and `animatedZoom` are opt-in: pass `true` for defaults or\n * an object to tune. Omitted means off.\n * - `pan` (wheel pan), `zoom` (Cmd+wheel + Cmd+=/-/0), and `pinchZoom`\n * (two-finger pinch) are on under `view`; pass `false` to disable one.\n * Disabling via the `actions` prop\n * (`actions: { 'viewport.wheelPan': null }`) also works and runs after\n * this. */\n viewport?: {\n inertia?: boolean | InertiaConfig;\n /** Two-finger pinch zoom. `true`/omitted = on with the kit's 0.1–8\n * clamp; `false` disables. An object sets the scale clamp. */\n pinchZoom?: boolean | PinchZoomOptions;\n /** Glide Cmd+=/-/0 instead of jumping. `true` uses the kit defaults\n * (250 ms, ease-out-cubic); a {@link ViewportZoomAnimateOptions} tunes\n * duration, easing, interpolator and the reset-branch duration. Wheel\n * and pinch are unaffected — their input already samples every frame. */\n animatedZoom?: boolean | ViewportZoomAnimateOptions;\n /** Wheel pan. `true`/omitted = on; `false` disables; an object locks the\n * axis (`{ axis: 'x' }`) so a single-axis viewport needs no commit clamp. */\n pan?: boolean | WheelPanOptions;\n /** Wheel/keyboard zoom. `true`/omitted = default Cmd+wheel zoom with the\n * kit's 0.1–8 clamp; `false` disables. Pass a {@link ViewportZoomOptions}\n * object to bind zoom to plain wheel (`wheel: 'plain'`, pair with\n * `pan: false`) and/or set `min`/`max` scale clamps. */\n zoom?: boolean | ViewportZoomOptions;\n /** Callback invoked by Cmd-0 (`viewport.zoom` action's `reset` branch).\n * When supplied, replaces the default reset-to-identity behavior —\n * consumers typically refit the document page into the workspace via\n * `fitViewToBounds`. Return the target `View` to let the kit animate\n * there when `animatedZoom` is on; return nothing to dispatch it\n * yourself, which is what a controlled canvas does. */\n recenter?: () => View | void;\n };\n\n /**\n * @experimental\n * Override / disable / extend the default action set. Pass `null` to disable all\n * defaults.\n */\n actions?: ActionsProp;\n\n /**\n * @experimental\n * Given a scene node, return a short human-readable \"kind\" label (e.g. `'rectangle'`,\n * `'sticky note'`), which the kit publishes per id into any surrounding\n * `<SelectionContextProvider>`. Return `undefined` to skip an entry. When omitted,\n * containers report `'group'`, poses with a `kind` property report `'path'`, and\n * everything else is left unlabelled.\n */\n describeKind?: (node: Node<TData, TLayer, TPose>) => string | undefined;\n\n /**\n * Animator to bind for per-frame redraws. SceneCanvas requests a redraw on every active\n * animation frame and paints `animator.colorOverrides` onto the scene's nodes: the\n * kit's path and shape painters apply them, and a custom `drawOne` receives them as\n * `ctx.vertexColors`.\n */\n animator?: Animator;\n\n /**\n * Extra views on this canvas: each is a camera over a rect of the same\n * surface, drawn through one GL context, with input routed to it. The flat\n * `view` / `onViewChange` props above stay the canvas's own camera — view\n * zero — and are unaffected by anything declared here.\n *\n * Order is paint and hit order, low to high. Each entry is a\n * `<CanvasView>`; mounting one as a child is the same declaration, and\n * children land after every entry here.\n */\n views?: readonly CanvasViewProps[];\n\n /**\n * Children rendered alongside the canvas, inside its input scope (e.g.\n * shortcuts overlays, probes).\n */\n children?: ReactNode;\n\n /**\n * A yoke to join, from `useYoke()`: canvases on one yoke share the\n * active tool, the gesture in flight and history. Omitted, this canvas\n * keeps its own.\n */\n yoke?: Yoke;\n\n /**\n * Chrome-caps visibility overrides, keyed by chrome id (`selection.outline`,\n * `selection.rotation-handle`, `gesture.marquee`, …). Each entry is a composable\n * {@link Condition} built from the `cond()` builder, merged on top of the kit's\n * `defaultVisibilityRules`. Set an id to `never` to suppress a chrome element\n * entirely (also unhittable), or to `always` to force-show.\n */\n chromeVisibility?: import('features/chrome-caps').VisibilityRules;\n\n /**\n * The app's mode registry (`createModeRegistry` in `@weasel-js/modes`).\n * The canvas reads the active mode from it to gate chrome, tool\n * activation and the dispatcher's eligibility filter, repaints when it\n * switches, and hands every registered mode to the dev-time route-conflict\n * check. Omitted, the canvas behaves as the kit's normal mode and nothing\n * revokes anchor editing.\n */\n modes?: ModeRegistry;\n\n /** Hands which path is being anchor-edited to the host. See {@link PathEditHost}. */\n pathEdit?: PathEditHost;\n\n /**\n * Override detected device facts. Merged over what `matchMedia` reports;\n * `targetScale` is re-derived from the merged `coarsePointer` unless you\n * override it explicitly.\n */\n device?: Partial<DeviceProfile>;\n\n /**\n * Live focus getter for chrome-caps' `focused` ctx field, for visibility rules that\n * read the `focused` atom (the kit's default `selection.rotation-handle` rule does).\n * Omit to default `focused` to `true`.\n */\n getFocused?: () => boolean;\n\n /** Custom shader programs to compile on the renderer. See `CanvasProps.shaders`. */\n shaders?: ShaderProgramHandle[];\n\n /** Flatness tolerance for curve tessellation, in world units. See\n * `CanvasProps.flattenTolerance`. */\n flattenTolerance?: number;\n\n /** Fill for the full canvas behind the scene, drawn as a screen-space layer slotted\n * before `'scene'` — independent of pan / zoom. */\n backgroundFill?: Paint;\n /** Dev HUD: a fixed-position widget showing live cursor coords in both viewport\n * (client) and canvas (world) frames. */\n cursorCoordsHud?: boolean;\n /** Dev HUD: a fixed-position widget listing the ids `pickEvery(world)` returns under\n * the cursor. */\n pickHud?: boolean;\n /**\n * The debug overlay: `<Canvas debug>`'s config, plus `slops: true` for\n * translucent halos at every affordance hit zone. `false` turns it off;\n * absent reads `?debug=…` from the URL, as `<Canvas>` does.\n */\n debug?: (DebugConfig & { slops?: boolean }) | false;\n /**\n * Dev HUD: when true (or object), mounts a fixed-position widget below\n * the pick HUD showing the active modality mode, active-slot tool, and\n * hotkey stack. Pass `{ modeId }` to populate the mode line.\n */\n modalityHud?: boolean | { modeId?: string };\n\n /**\n * Per-id alpha multiplier for the scene-render slot: each node's draw output is wrapped\n * in a group with the returned alpha, and 1 emits no wrapper. Typical use: dimming\n * non-active nodes during a mode transition.\n */\n alphaFor?: (id: string) => number;\n\n /**\n * Show or hide render layers in this canvas's own view, by id — the map\n * `<Canvas>` takes. Each scene layer paints as its own render layer, keyed\n * `scene:<layerId>`, so `{ 'scene:guides': false }` hides one scene layer\n * here and nowhere else.\n *\n * A layer hidden this way is gone from this view: it does not paint, and a\n * click, a marquee or Cmd+A here passes over it. Another view of the same\n * scene — a `<CanvasView>`, a second canvas, a minimap — still shows it and\n * can still take it. It applies on top of the scene's own\n * `LayerRecord.visible` and cannot show a layer the scene hides.\n */\n layerVisibility?: Record<string, boolean>;\n\n /**\n * Draw order for this view, by render-layer id, bottom first. A listed\n * order is the whole list: a scene layer left out of it is neither painted\n * nor picked here. See `CanvasProps.layerOrder`.\n */\n layerOrder?: string[];\n\n /**\n * Per-id pointer-interactivity predicate: an id it returns `false` for is excluded from\n * hit-test results. Typical use: suppressing pointer events for dimmed nodes.\n */\n isPointerInteractive?: (id: string) => boolean;\n\n /**\n * Called when the user double-clicks the canvas, with the hit node (id + kind) at that\n * position, or `null` on empty canvas. Typical use: modality dispatch — enter path-edit\n * on a path node, isolation on a group, text-edit on a text node.\n */\n onDoubleClick?: (hit: SceneCanvasHit | null) => void;\n /** Called on every click — a press and release without a drag — with the\n * node under it, or `null` on empty canvas. Fires after the click's own\n * behavior, so a pick it made is already in the selection, and fires\n * again on a node that was already picked. */\n onClick?: (hit: SceneCanvasHit | null) => void;\n };\n"],"mappings":";;;;;;AAiEA,IAAaA,IAAsBC;AAgCnC,SAAgBC,EACd,GACA,IAAc,GACgC;CAC9C,IAAMC,IAAW;EACf,OAAO;GACL,SAASC;GAIT,aAAaC;EACf;EACA,kBAAkB,EAAE,SAAS,EAAE,MAAMC,EAAcC,CAAW,CAAC,CAAC,OAAO,EAAE;CAC3E;CAEA,IAAI,CAACC,GAAM,OAAOL;CAGlB,IAAMM,IAAS,EAAE,GAAGD,EAAK;CAEzB,IAAI,EAAE,WAAWA,IACf,EAAO,QAAQL,EAAS;MACnB,IAAIK,EAAK,UAAU,QAAQE,EAAcF,EAAK,KAAK,GACxD,EAAO,QAAQA,EAAK;MACf;EACL,IAAMG,IAAQ;GAAE,GAAGR,EAAS;GAAO,GAAGK,EAAK;EAAM;EACjD,EAAO,QAAQA,EAAK,SAAS,iBAAiBA,EAAK,QAC/CG,IACA;GAAE,GAAGA;GAAO,aAAaC,EAAqCD,EAAM,OAAO;EAAE;CACnF;CAUA,OARA,AACEF,EAAO,mBADH,sBAAsBD,IAEjBA,EAAK,qBAAqB,QAAQE,EAAcF,EAAK,gBAAgB,IACpDA,EAAK,mBAEL;EAAE,GAAGL,EAAS;EAAkB,GAAGK,EAAK;CAAiB,IAJzDL,EAAS,kBAO9BM;AACT;AAQA,IAAaI,IAAiF,CAAC,UAAU,MAAM,GAGlGC,IAA6C,CAAC,GAAGD,GAAyB,GAAGE,CAAe"}
|
|
1
|
+
{"version":3,"file":"props.js","names":["DEFAULT_HANDLE_SIZE","HANDLE_BASE_PX","mergeLayersWithDefaults","defaults","defaultDrawOne","defaultPaintBounds","targetSizesPx","targetScale","user","result","isCustomEntry","scene","paintBoundsFor","NON_SHAPE_BUILTIN_TOOLS","BUILTIN_TOOL_IDS","KIT_SHAPE_KINDS"],"sources":["../../../src/canvas/SceneCanvas/props.ts"],"sourcesContent":["import type { ModeRegistry } from '@weasel-js/modes';\nimport type { PathEditHost } from './pathEditHost';\nimport type { Paint } from '@weasel-js/paint';\nimport type { ActionsProp, ActionTunings } from '@weasel-js/routing';\nimport { Yoke } from '@weasel-js/routing/react';\nimport type { PasteOmission } from 'core/adapters/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene, SerializedScene } from 'core/scene/types';\nimport type { SelectionApi, UseSelectionOptions } from 'core/selection/useSelection';\nimport { KIT_SHAPE_KINDS, type BuiltinShapeToolId } from 'core/shapeKinds';\nimport type {\n ContentHandlerEntry\n} from 'features/ingestion';\nimport type { SvgIngestOptions } from 'interactions/actions/depSchema';\nimport type { GeometryProjection } from 'interactions/actions/geometryProjection';\nimport type { UseMoveOptions } from 'interactions/actions/move/options';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { UseResizeOptions } from 'interactions/actions/resize/options';\nimport type { UseRotateOptions } from 'interactions/actions/rotate/options';\nimport type { SnapStrategy } from 'interactions/gestures/types';\nimport type { ReactNode } from 'react';\nimport type { Bounds } from 'tools/builtin/select';\nimport type { Animator } from '@weasel-js/animator';\nimport { HANDLE_BASE_PX, targetSizesPx } from '../../core/device/targets';\nimport type { DeviceProfile } from '../../core/device/types';\nimport type { NodeRoutingEntry } from '../../core/scene/NodeRouting';\nimport type { DebugConfig } from '../../debug/types';\nimport type { LayoutDropTargetMode, ReflowTransition } from '../../layout/types';\nimport type { DrawCommand, ShaderProgramHandle } from '@weasel-js/renderer';\nimport type { Tool, ToolsApi } from '../../tools/overlayBinding';\nimport type { SceneSlotConfig } from '../buildSceneLayer';\nimport type { CanvasProps } from '../Canvas';\nimport { isCustomEntry, type LayersMap } from '../layerSlots';\nimport type { CanvasViewProps } from '../CanvasView';\nimport { defaultDrawOne, defaultPaintBounds, paintBoundsFor } from '../defaultDrawOne';\nimport type {\n InsertNodeFactory,\n InsertPaint\n} from '../deps';\nimport type { SurfaceContribution } from '../surfaceContribution';\nimport type { CameraProps } from '../useCanvasView';\nimport type {\n Feature\n} from './features';\nimport type { BuiltinToolOptions } from './useBuiltinShapeTools';\nimport type { VersionCheck } from './useContributionRoles';\nimport type { SceneCanvasViewport } from './viewportProp';\n\n/** The adapter surface delete / duplicate / group / ungroup need, narrowed from\n * `SceneCanvasAdapter` because `StandardActionsRegistrar` is non-generic. */\nexport interface BridgeAdapter {\n applyOps(ops: Op[], label?: string): void;\n insertNode(node: { id: string; [k: string]: unknown }): void;\n removeNode(id: string): void;\n setSelection(ids: string[]): void;\n}\n\n/** Default size in CSS pixels for selection corner-handles AND their\n * hit-test radius, at `targetScale = 1`. Deliberately unscaled; kit-internal\n * use sites multiply by `DeviceProfile.targetScale`. */\nexport const DEFAULT_HANDLE_SIZE = HANDLE_BASE_PX;\n\n/** `<SceneCanvas longPress>`. */\nexport interface SceneCanvasLongPress {\n /**\n * Shown while a long-press is held and some binding would fire on it.\n * Default: a ring that fills around the press point\n * (`createLongPressFeedbackContribution()`), static under\n * `prefers-reduced-motion`. `false` shows nothing; a contribution replaces\n * it, reading the `longPress` dep. Installed beside `ambient`, so under a\n * `tools` takeover add it to your own `useTools` call instead.\n */\n feedback?: boolean | SurfaceContribution;\n /** Default true. A long-press some binding handled pulses\n * `navigator.vibrate` briefly, for touch and pen, where the API exists. */\n haptics?: boolean;\n /** Hold time in ms. Default `LONG_PRESS_MS` (500). */\n duration?: number;\n}\n\n/** `<SceneCanvas layers>`: a {@link LayersMap} whose scene slot may be partial,\n * since the defaults fill in whatever it leaves out. */\nexport type SceneCanvasLayers<TNode extends { id: string }, TPose> = LayersMap<\n TNode,\n TPose,\n Partial<SceneSlotConfig<TNode, TPose>>\n>;\n\n/** Deep-merge user-supplied `layers` with kit defaults. Slots the user\n * doesn't mention get filled with defaults; slots explicitly set to\n * `null` are dropped. Partial slot configs are shallow-spread on top of the\n * default slot config. `targetScale` comes from the resolved {@link DeviceProfile}. */\nexport function mergeLayersWithDefaults<TData, TLayer extends string, TPose>(\n user: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose> | undefined,\n targetScale = 1,\n): LayersMap<Node<TData, TLayer, TPose>, TPose> {\n const defaults = {\n scene: {\n drawOne: defaultDrawOne as (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n ) => DrawCommand[],\n paintBounds: defaultPaintBounds<TData, TLayer, TPose>,\n },\n selectionOverlay: { handles: { size: targetSizesPx(targetScale).handle } },\n };\n\n if (!user) return defaults as LayersMap<Node<TData, TLayer, TPose>, TPose>;\n\n // Start from a shallow copy of the user map so unknown slots pass through.\n const result = { ...user } as LayersMap<Node<TData, TLayer, TPose>, TPose>;\n\n if (!('scene' in user)) {\n result.scene = defaults.scene;\n } else if (user.scene === null || isCustomEntry(user.scene)) {\n result.scene = user.scene;\n } else {\n const scene = { ...defaults.scene, ...user.scene };\n result.scene = user.scene && 'paintBounds' in user.scene\n ? scene\n : { ...scene, paintBounds: paintBoundsFor<TData, TLayer, TPose>(scene.drawOne) };\n }\n\n if (!('selectionOverlay' in user)) {\n result.selectionOverlay = defaults.selectionOverlay;\n } else if (user.selectionOverlay === null || isCustomEntry(user.selectionOverlay)) {\n result.selectionOverlay = user.selectionOverlay;\n } else {\n result.selectionOverlay = { ...defaults.selectionOverlay, ...user.selectionOverlay };\n }\n\n return result;\n}\n\n/** Built-in tool ids SceneCanvas knows how to mount when no `tools` prop\n * is supplied, via `defaultTools`. */\nexport type BuiltinToolId = 'select' | 'hand' | BuiltinShapeToolId;\n\n/** Built-in tool ids that aren't shape tools — the ones with no entry in\n * the `core/shapeKinds` table. */\nexport const NON_SHAPE_BUILTIN_TOOLS: readonly Exclude<BuiltinToolId, BuiltinShapeToolId>[] = ['select', 'hand'];\n\n/** Every built-in tool id, shape tools included. */\nexport const BUILTIN_TOOL_IDS: readonly BuiltinToolId[] = [...NON_SHAPE_BUILTIN_TOOLS, ...KIT_SHAPE_KINDS];\n\n/** Minimal hit descriptor passed to `onDoubleClick`. Contains the fields\n * modality dispatch needs; extends as the kit's `Hit` type evolves. */\nexport interface SceneCanvasHit {\n id: string;\n kind: string;\n}\n\n/** Props for `<SceneCanvas>`. A scene is the only thing it needs. With nothing\n * else it renders that scene and keeps a selection no input sets; `features`\n * turns behavior on by preset, and the rest configures what those turn on. */\nexport type SceneCanvasProps<TData, TLayer extends string, TPose> =\n Omit<\n CanvasProps<TPose>,\n | 'adapter' | 'boundsOf' | 'selection' | 'tools'\n | 'viewState' // SceneCanvas makes the camera from the view props below\n | 'layers' // stripped so we can re-add as optional below\n | 'getIsVisible' // SceneCanvas synthesizes this from chromeVisibility\n | 'contentVersion' // SceneCanvas wires this to the scene's own version\n | 'layerVisibility' | 'layerOrder' // re-declared below: they gate picking here too\n | 'debug' // re-declared below with SceneCanvas's own `slops`\n > & CameraProps\n & {\n /** A `Scene` (typically from `useScene`) — or a `SerializedScene` JSON\n * object, which SceneCanvas bakes into a Scene on first render. The\n * serialized form is read once; pass a `key` on `<SceneCanvas>` to force\n * a fresh canvas from updated JSON.\n *\n * The JSON shape is relaxed (`version: number` rather than the `1`\n * literal) so an `import json from './x.json'` result satisfies the type\n * without an `as` cast. */\n scene:\n | Scene<TData, TLayer, TPose>\n | SerializedScene<TData, TLayer, TPose>\n | { version: number; systemLayers?: ReadonlyArray<{ id: string }>; nodes: ReadonlyArray<unknown> };\n\n /** Layer configuration. When omitted, SceneCanvas applies kit defaults\n * (a scene slot that paints `node.data.fill` rects + a default\n * selection overlay). A partial scene or selection-overlay config is\n * spread over that slot's defaults; pass `slot: null` to suppress a\n * default explicitly. */\n layers?: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose>;\n\n /** Which layout container a drag lands in when several contain the drop\n * point — the innermost (default), the topmost in paint order, or only\n * containers whose strategy declares a `dropRegion`. Decides both the\n * drag-time reflow preview and the commit. */\n layoutDropTarget?: LayoutDropTargetMode;\n\n /** Carries the siblings a layout reflow displaces to their slots over time\n * instead of snapping them — `useAnimatedReflow` builds one. Covers a\n * drag's reflow and every one the scene records (an insert, delete,\n * reorder or resize inside a laid-out container). Absent or `null`,\n * they snap. */\n reflowTransition?: ReflowTransition<TPose> | null;\n\n /**\n * Lets pose-transform actions (move, resize, nudge, flip — NOT rotate) also rewrite a\n * node's data-held geometry. Given a node and the affine `m` applied to its pose,\n * `transform(node, m)` returns updated `data`, or `null` for nodes with no data-held\n * geometry. When absent, the kit emits only the pose op and leaves `data` untouched.\n *\n * @see GeometryProjection\n */\n geometryProjection?: GeometryProjection;\n\n /**\n * Routing-trait classifiers. The kit builds a `NodeRouting` registry per `<SceneCanvas>`\n * from this prop and uses it to derive each hit's `kind`, which tool routing tables\n * (e.g. `{ target: 'rect', actionId: 'move' }`) match against. Pass `defaultNodeRouting`\n * for the kit's built-in shape kinds; spread additional entries for consumer-defined\n * kinds.\n *\n * **Memoize the `routing` value.** The registry is memoized on the prop's reference\n * identity: a fresh array each render rebuilds it and cascades into a new adapter,\n * churning gesture state.\n */\n routing?: readonly NodeRoutingEntry[];\n\n /** How to read and rewrite this scene's poses. Every built-in action,\n * the selection chrome and picking read it. Default `AUTO_POSE_DESCRIPTOR`\n * (rect poses and `Path` poses). */\n poseDescriptor?: PoseDescriptor<TPose>;\n\n // --- Geometry: hit-test + bounds overrides consumed by the internal\n // `useSelectTool`. Ignored if the consumer passes their own `tools`. ---\n geometry?: {\n /** Hit-test override. Return the topmost id, the full back-to-front hit\n * stack (`string[]`), or `null` for empty space. The stack form lets a\n * consumer with domain overlap ordering (e.g. children over their\n * container) feed the kit's `pickTopMostHit` the true order instead of\n * pre-collapsing to one id. Matches `Canvas`'s `pickEvery` shape. */\n pickEvery?: (worldX: number, worldY: number) => string | string[] | null;\n boundsOf?: (id: string) => Bounds | null;\n /**\n * What \"the pointer is on this node\" means for the default body-pick.\n *\n * - `'pose'` — the node's pose rect, rotation honored.\n * - `'shape'` (default) — the pose rect as a pre-filter, then the ink the painter\n * actually lays down: its silhouette filled or not per the painter's `ink`, plus\n * its outline widened by the stroke half-width and `pickTolerancePx`.\n *\n * Painters with no silhouette keep the pose-rect answer either way. Ignored when\n * `pickEvery` is supplied: that override owns the test.\n */\n picking?: 'pose' | 'shape';\n /**\n * Grab slop around a shape's outline, in **screen** pixels, so the target keeps\n * its apparent size at any zoom. Default 4. It widens the outline test under\n * `picking: 'shape'` and grows the pose-rect pre-filter so those outline hits\n * survive it. Set `0` for exact geometry.\n */\n pickTolerancePx?: number;\n };\n\n // --- Selection options. `pickBest` and `handleHitRadius` configure the\n // internal select tool, and are ignored under a `tools` takeover;\n // `move`, `snap`, `resize` and `rotate` configure the `move`,\n // `resize` and `rotate` presets, and apply either way. ---\n selectTool?: {\n move?: UseMoveOptions<TPose>;\n resize?: UseResizeOptions<TPose>;\n /** Rotation options, or `false` to disable rotation entirely — drops the\n * `rotate` action AND hides the selection rotation-handle chrome. */\n rotate?: UseRotateOptions<TPose> | false;\n snap?: SnapStrategy<TPose>;\n handleHitRadius?: number;\n /** Override the body-pick used on click/pointerdown. Alt-aware: receives\n * the live alt state + current selection so consumers can implement\n * alt-cycling through an overlapping stack. Default: top-most hit\n * (alt ignored). */\n pickBest?: (worldX: number, worldY: number, alt: boolean, sel: readonly string[]) => string | null;\n };\n\n /** Consumer node factories for the `insert` action, keyed by tool `kind`.\n * Each factory receives the drag AABB + tool `extras` and returns the\n * node's `data` (in this canvas's own data shape) plus an optional `pose`.\n * A factory for a kit kind (`rect`, `line`, …) replaces the kit's default\n * `{ path, fill }` node for that kind; a factory for a novel kind (e.g.\n * `text`) adds insert support the kit doesn't ship. The dep supplies id,\n * layer, and the undoable op. Return `null` to reject an insert. */\n insertNodeFactories?: Record<string, InsertNodeFactory>;\n\n /** The fill, stroke, and fill rule a kit-built shape is inserted with, by\n * tool `kind` and insert count; the kit keeps the geometry. Default\n * `paletteInsertPaint`, the kit palette in turn. A kind with a factory in\n * `insertNodeFactories` is not asked. */\n insertPaint?: InsertPaint;\n\n /** External-content ingestion (OS drop / clipboard paste / picker).\n *\n * - `handlers` — consumer content handlers registered for this canvas's\n * lifetime (priority 0 by default — they beat the kit's `image/*` /\n * `image/svg+xml` handlers at -100/-90).\n * - `resolveSrc` — overrides the image handler's `data:`-URI embed (e.g.\n * upload to an asset store, return the URL).\n * - `svg.unpack` — makes the kit SVG handler parse dropped SVG files into\n * native scene nodes instead of one embedded-image node each.\n * - `clipboard` — the kit weasel-JSON paste handler, enabled by default.\n * `reviver` restores JSON-unfriendly values the copying side encoded via\n * `jsonReplacer`. `enabled: false` opts the canvas out, but the kit\n * handler still consumes weasel-matching items and declines them inert\n * rather than letting them fall through. Also governs Cmd/Ctrl+V under\n * the `edit` preset; the rest takes the `ingest` preset.\n *\n * Memoize `handlers` and `clipboard.reviver`: an inline array literal\n * re-registers the handlers on every render. */\n ingestion?: {\n handlers?: ContentHandlerEntry[];\n resolveSrc?: (file: File) => Promise<string>;\n svg?: SvgIngestOptions;\n clipboard?: {\n reviver?: (key: string, value: unknown) => unknown;\n enabled?: boolean;\n /** Called when a paste left copied nodes out, with each one and the\n * reason. `useClipboardOps` takes the same callback as\n * `onPasteOmitted` for a paste from its own in-memory copy. */\n onOmitted?: (omitted: PasteOmission[]) => void;\n };\n };\n\n selection?: SelectionApi;\n selectionOptions?: UseSelectionOptions;\n\n /**\n * `false` stops the canvas writing the selection: no click, marquee,\n * lasso, action or tool, and no op committed through the adapter it hands\n * its tools. The consumer's own `SelectionApi` — the `selection` prop, or\n * `useSelection({ scene })` — still writes, and the canvas draws what it\n * holds. Undo and redo still restore the selection each history entry\n * recorded. Default `true`.\n *\n * Click policy (whether shift-click extends) is the selection's `mode`:\n * `useSelection({ mode: 'multi' })`, or `selectionOptions={{ mode: 'multi' }}`\n * for the selection the canvas builds itself.\n */\n selectable?: boolean;\n\n /**\n * Behavior presets to turn on, composable in any combination. A canvas\n * with none renders its scene and keeps a selection (the `selection` API\n * works) that no input sets: no tools, no standard actions or their keys,\n * no clipboard, no drop or paste, and no selection chrome. The dispatcher\n * is still mounted, so bindings a consumer adds work.\n *\n * - `view` — wheel pan and zoom, pinch, the zoom keys, and the hand tool\n * (H, or hold Space). Passing `viewport` implies it.\n * - `select` — the select tool, as the initial active tool and Escape's\n * return target: click to pick, drag on empty to marquee, click on empty\n * to clear.\n * - `outline` — the selection outline, whichever tool sets the selection.\n * - `pick` — `select` and `outline`.\n * - `move` — drag a body to move it, Alt-drag to clone it. Ambient, so it\n * runs under any tool that does not claim the drag.\n * - `resize` — resize handles, drawn and bound.\n * - `rotate` — the rotation handle, drawn and bound.\n * - `transform` — `resize` and `rotate`.\n * - `edit` — undo/redo, delete, duplicate, group/ungroup, nudge,\n * select-all, Escape, cancel-gesture, cut/copy/paste, fill and stroke,\n * and their keys. Cmd/Ctrl+V pastes nodes a kit canvas copied, from\n * this tab or another, and nothing else.\n * - `arrange` — align, distribute, reorder, flip.\n * - `paths` — pathfinder operations, path-edit entry and anchor editing.\n * - `ingest` — dropped and pasted content: images, SVG, files, and\n * whatever `ingestion.handlers` accept, on Cmd/Ctrl+V or an OS drop.\n * - `draw` — all of the above.\n *\n * Tools add the actions they bind — a shape tool brings `insert` — so\n * `defaultTools` composes with any preset. `actions` then adds, overrides\n * or removes on top of what the presets registered.\n */\n features?: readonly Feature[];\n\n /** Extra tools or overrides keyed by id, or a full `ToolsApi` takeover.\n *\n * **Patch form** (`Record<string, Tool | true | false>`): merged\n * into the built-in registry on top of whatever `defaultTools` and\n * `features` already selected.\n * - `true` pulls in the built-in for this id (`'pen'`, `'lasso'`,\n * `'hand'`, …), the same as listing it in `defaultTools`. Unknown\n * built-in ids warn in dev and are ignored.\n * - `Tool` adds a new id or replaces an existing one\n * (dev-only warning on replace).\n * - `false` omits a bundled tool entirely.\n * Auto-wiring (keybindings, dispatcher, action registry) still runs.\n *\n * **Takeover form** (`ToolsApi`): the internal registry is bypassed and\n * this `tools` value is forwarded to Canvas as-is — the consumer owns\n * active-slot management, and `pick`'s select tool is theirs to supply.\n * Every other preset still applies. Keybindings are still auto-wired\n * against the supplied registry; pass `enableKeybindings={false}` to opt\n * out. */\n tools?: ToolsApi | Record<string, Tool | true | false>;\n\n /**\n * Auto-wire keyboard shortcuts. Default `true`. When `false`, SceneCanvas still mounts\n * its tools but routes no keyboard input to them — neither tool hotkeys nor\n * keyboard-bound actions like delete / escape / nudge. Pointer, wheel, and contextmenu\n * interactions are unaffected.\n */\n enableKeybindings?: boolean;\n\n /**\n * Take the keyboard on mount, as a press in the canvas would. A key that\n * reaches the page goes to the canvas the user last pressed or scrolled\n * in, and to no canvas before the first such interaction, so a canvas in a\n * side panel does not take Space from a page that wants it. An app whose\n * canvas is the page sets this so its shortcuts work before the first\n * click. Default `false`.\n */\n claimKeys?: boolean;\n\n /**\n * Auto-mount the gesture dispatcher (`useGestureDispatcher`). Default `true`. `false` is\n * for tests or demos that drive actions another way, or that call\n * `useGestureDispatcher` themselves.\n */\n enableGestureDispatcher?: boolean;\n\n /** Called with the `ToolsApi` SceneCanvas constructs or receives, whenever its identity\n * changes. For introspection. */\n onToolsCreated?: (tools: ToolsApi) => void;\n\n /**\n * Built-in tools SceneCanvas registers in its internal `useTools`, on top\n * of the ones presets bring (`select` from `pick`, `hand` from `view`).\n * Default: none. Each tool also registers the kit actions it binds, so\n * `['rect']` brings `insert`. Ignored when the consumer supplies their own\n * `tools` prop.\n */\n defaultTools?: readonly BuiltinToolId[];\n\n /** Per-tool option overrides for the built-in shape/lasso/clone tools.\n * Each entry is a narrow subset of the underlying hook's options\n * surface — `lasso.mode`, `clone.cloneSelection`, etc. */\n toolOptions?: BuiltinToolOptions;\n\n /** Initial active-slot tool id. Default: `'select'` when it is\n * registered, otherwise none — a canvas can run on ambient bindings\n * alone. Must be one of the registered tools. Ignored when the consumer\n * supplies their own `tools` prop. */\n initialActiveTool?: string;\n\n /** Always-on entries to register alongside the internal tools — every one\n * a `SurfaceContribution`, whose views, deps, overlay, bindings, actions\n * and `attach` all install from this one list, and uninstall when the\n * entry leaves it. A tool is a contribution too, so one can go here. If\n * you supply your own `tools` prop, this is ignored — wire `ambient`\n * through your own `useTools` call instead. */\n ambient?: readonly SurfaceContribution[];\n\n /** What an entry's `requires` does when the running kit does not meet\n * it: `'warn'` (the default) logs in development, `'strict'` throws,\n * `'off'` skips the check. */\n versionCheck?: VersionCheck;\n\n /** Touch and pen long-press: its hold time, its haptic, and the\n * feedback shown while it is held. */\n longPress?: SceneCanvasLongPress;\n\n /** Configures the `view` preset, and implies it. See {@link SceneCanvasViewport}. */\n viewport?: SceneCanvasViewport;\n\n /**\n * Tune actions on this canvas without restating them, keyed by action id:\n * `params` every invocation starts from, and `bindings` added beside the\n * action's own. A tuning applies to whichever descriptor holds the id, a\n * preset's or a tool's. Pass a stable object; a params thunk is read on\n * every call. See the core README (\"Tuning an action\").\n */\n tune?: ActionTunings;\n\n /**\n * @experimental\n * Replace, add or mute whole action descriptors. Pass `null` to disable\n * all defaults. To change a built-in's params or add a key, use `tune`.\n */\n actions?: ActionsProp;\n\n /**\n * @experimental\n * Given a scene node, return a short human-readable \"kind\" label (e.g. `'rectangle'`,\n * `'sticky note'`), which the kit publishes per id into any surrounding\n * `<SelectionContextProvider>`. Return `undefined` to skip an entry. When omitted,\n * containers report `'group'`, poses with a `kind` property report `'path'`, and\n * everything else is left unlabelled.\n */\n describeKind?: (node: Node<TData, TLayer, TPose>) => string | undefined;\n\n /**\n * Animator to bind for per-frame redraws. SceneCanvas requests a redraw on every active\n * animation frame and paints `animator.colorOverrides` onto the scene's nodes: the\n * kit's path and shape painters apply them, and a custom `drawOne` receives them as\n * `ctx.vertexColors`.\n */\n animator?: Animator;\n\n /**\n * Extra views on this canvas: each is a camera over a rect of the same\n * surface, drawn through one GL context, with input routed to it. The flat\n * `view` / `onViewChange` props above stay the canvas's own camera — view\n * zero — and are unaffected by anything declared here.\n *\n * Order is paint and hit order, low to high. Each entry is a\n * `<CanvasView>`; mounting one as a child is the same declaration, and\n * children land after every entry here.\n */\n views?: readonly CanvasViewProps[];\n\n /**\n * Children rendered alongside the canvas, inside its input scope (e.g.\n * shortcuts overlays, probes).\n */\n children?: ReactNode;\n\n /**\n * A yoke to join, from `useYoke()`: canvases on one yoke share the\n * active tool, the gesture in flight and history. Omitted, this canvas\n * keeps its own.\n */\n yoke?: Yoke;\n\n /**\n * Chrome-caps visibility overrides, keyed by chrome id (`selection.outline`,\n * `selection.rotation-handle`, `gesture.marquee`, …). Each entry is a composable\n * {@link Condition} built from the `cond()` builder, merged on top of the kit's\n * `defaultVisibilityRules`. Set an id to `never` to suppress a chrome element\n * entirely (also unhittable), or to `always` to force-show.\n */\n chromeVisibility?: import('features/chrome-caps').VisibilityRules;\n\n /**\n * The app's mode registry (`createModeRegistry` in `@weasel-js/modes`).\n * The canvas reads the active mode from it to gate chrome, tool\n * activation and the dispatcher's eligibility filter, repaints when it\n * switches, and hands every registered mode to the dev-time route-conflict\n * check. Omitted, the canvas behaves as the kit's normal mode and nothing\n * revokes anchor editing.\n */\n modes?: ModeRegistry;\n\n /** Hands which path is being anchor-edited to the host. See {@link PathEditHost}. */\n pathEdit?: PathEditHost;\n\n /**\n * Override detected device facts. Merged over what `matchMedia` reports;\n * `targetScale` is re-derived from the merged `coarsePointer` unless you\n * override it explicitly.\n */\n device?: Partial<DeviceProfile>;\n\n /**\n * Live focus getter for chrome-caps' `focused` ctx field, for visibility rules that\n * read the `focused` atom (the kit's default `selection.rotation-handle` rule does).\n * Omit to default `focused` to `true`.\n */\n getFocused?: () => boolean;\n\n /** Custom shader programs to compile on the renderer. See `CanvasProps.shaders`. */\n shaders?: ShaderProgramHandle[];\n\n /** Flatness tolerance for curve tessellation, in world units. See\n * `CanvasProps.flattenTolerance`. */\n flattenTolerance?: number;\n\n /** Fill for the full canvas behind the scene, drawn as a screen-space layer slotted\n * before `'scene'` — independent of pan / zoom. */\n backgroundFill?: Paint;\n /** Dev HUD: a fixed-position widget showing live cursor coords in both viewport\n * (client) and canvas (world) frames. */\n cursorCoordsHud?: boolean;\n /** Dev HUD: a fixed-position widget listing the ids `pickEvery(world)` returns under\n * the cursor. */\n pickHud?: boolean;\n /**\n * The debug overlay: `<Canvas debug>`'s config, plus `slops: true` for\n * translucent halos at every affordance hit zone. `false` turns it off;\n * absent reads `?debug=…` from the URL, as `<Canvas>` does.\n */\n debug?: (DebugConfig & { slops?: boolean }) | false;\n /**\n * Dev HUD: when true (or object), mounts a fixed-position widget below\n * the pick HUD showing the active modality mode, active-slot tool, and\n * hotkey stack. Pass `{ modeId }` to populate the mode line.\n */\n modalityHud?: boolean | { modeId?: string };\n\n /**\n * Per-id alpha multiplier for the scene-render slot: each node's draw output is wrapped\n * in a group with the returned alpha, and 1 emits no wrapper. Typical use: dimming\n * non-active nodes during a mode transition.\n */\n alphaFor?: (id: string) => number;\n\n /**\n * Show or hide render layers in this canvas's own view, by id — the map\n * `<Canvas>` takes. Each scene layer paints as its own render layer, keyed\n * `scene:<layerId>`, so `{ 'scene:guides': false }` hides one scene layer\n * here and nowhere else.\n *\n * A layer hidden this way is gone from this view: it does not paint, and a\n * click, a marquee or Cmd+A here passes over it. Another view of the same\n * scene — a `<CanvasView>`, a second canvas, a minimap — still shows it and\n * can still take it. It applies on top of the scene's own\n * `LayerRecord.visible` and cannot show a layer the scene hides.\n */\n layerVisibility?: Record<string, boolean>;\n\n /**\n * Draw order for this view, by render-layer id, bottom first. A listed\n * order is the whole list: a scene layer left out of it is neither painted\n * nor picked here. See `CanvasProps.layerOrder`.\n */\n layerOrder?: string[];\n\n /**\n * Per-id pointer-interactivity predicate: an id it returns `false` for is excluded from\n * hit-test results. Typical use: suppressing pointer events for dimmed nodes.\n */\n isPointerInteractive?: (id: string) => boolean;\n\n /**\n * Called when the user double-clicks the canvas, with the hit node (id + kind) at that\n * position, or `null` on empty canvas. Typical use: modality dispatch — enter path-edit\n * on a path node, isolation on a group, text-edit on a text node.\n */\n onDoubleClick?: (hit: SceneCanvasHit | null) => void;\n /** Called on every click — a press and release without a drag — with the\n * node under it, or `null` on empty canvas. Fires after the click's own\n * behavior, so a pick it made is already in the selection, and fires\n * again on a node that was already picked. */\n onClick?: (hit: SceneCanvasHit | null) => void;\n };\n"],"mappings":";;;;;;AA4DA,IAAaA,IAAsBC;AAgCnC,SAAgBC,EACd,GACA,IAAc,GACgC;CAC9C,IAAMC,IAAW;EACf,OAAO;GACL,SAASC;GAIT,aAAaC;EACf;EACA,kBAAkB,EAAE,SAAS,EAAE,MAAMC,EAAcC,CAAW,CAAC,CAAC,OAAO,EAAE;CAC3E;CAEA,IAAI,CAACC,GAAM,OAAOL;CAGlB,IAAMM,IAAS,EAAE,GAAGD,EAAK;CAEzB,IAAI,EAAE,WAAWA,IACf,EAAO,QAAQL,EAAS;MACnB,IAAIK,EAAK,UAAU,QAAQE,EAAcF,EAAK,KAAK,GACxD,EAAO,QAAQA,EAAK;MACf;EACL,IAAMG,IAAQ;GAAE,GAAGR,EAAS;GAAO,GAAGK,EAAK;EAAM;EACjD,EAAO,QAAQA,EAAK,SAAS,iBAAiBA,EAAK,QAC/CG,IACA;GAAE,GAAGA;GAAO,aAAaC,EAAqCD,EAAM,OAAO;EAAE;CACnF;CAUA,OARA,AACEF,EAAO,mBADH,sBAAsBD,IAEjBA,EAAK,qBAAqB,QAAQE,EAAcF,EAAK,gBAAgB,IACpDA,EAAK,mBAEL;EAAE,GAAGL,EAAS;EAAkB,GAAGK,EAAK;CAAiB,IAJzDL,EAAS,kBAO9BM;AACT;AAQA,IAAaI,IAAiF,CAAC,UAAU,MAAM,GAGlGC,IAA6C,CAAC,GAAGD,GAAyB,GAAGE,CAAe"}
|