@weasel-js/core 1.1.0 → 1.3.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +1988 -0
  2. package/dist/{DrawCommand-BbPrSTra.d.ts → DrawCommand-BnZyQaDc.d.ts} +29 -189
  3. package/dist/chunk-2KKYDDDD.js +121 -0
  4. package/dist/chunk-2KKYDDDD.js.map +1 -0
  5. package/dist/{chunk-J7LUY47N.js → chunk-3FB44DBC.js} +3 -3
  6. package/dist/{chunk-J7LUY47N.js.map → chunk-3FB44DBC.js.map} +1 -1
  7. package/dist/{chunk-J3YC3GVF.js → chunk-3ZV53ZW2.js} +51 -14
  8. package/dist/chunk-3ZV53ZW2.js.map +1 -0
  9. package/dist/{chunk-JOSUVS6C.js → chunk-67KE7SDP.js} +4 -4
  10. package/dist/chunk-67KE7SDP.js.map +1 -0
  11. package/dist/{chunk-CSMF654J.js → chunk-AQS4JGRA.js} +50 -7
  12. package/dist/chunk-AQS4JGRA.js.map +1 -0
  13. package/dist/{chunk-HGF5X6MZ.js → chunk-BJN5ZRIJ.js} +2 -2
  14. package/dist/chunk-BJN5ZRIJ.js.map +1 -0
  15. package/dist/{chunk-4Z6J4IVH.js → chunk-F74NVEHT.js} +14 -46
  16. package/dist/chunk-F74NVEHT.js.map +1 -0
  17. package/dist/{chunk-3B4QEB2G.js → chunk-IFOVTYAK.js} +26291 -24809
  18. package/dist/chunk-IFOVTYAK.js.map +1 -0
  19. package/dist/{chunk-UGFFCMQP.js → chunk-MXFSHJOM.js} +4 -4
  20. package/dist/{chunk-UGFFCMQP.js.map → chunk-MXFSHJOM.js.map} +1 -1
  21. package/dist/{chunk-BHVYVFGV.js → chunk-ZB7UYJVG.js} +3 -3
  22. package/dist/{chunk-BHVYVFGV.js.map → chunk-ZB7UYJVG.js.map} +1 -1
  23. package/dist/clipboard.d.ts +3 -3
  24. package/dist/clipboard.js +3 -3
  25. package/dist/clone.d.ts +2 -2
  26. package/dist/clone.js +3 -3
  27. package/dist/{geometry-C56YbnfD.d.ts → geometry-Dtt_k6Dq.d.ts} +1 -1
  28. package/dist/{grid-DqOi0Vgi.d.ts → grid-nnXU4VjN.d.ts} +1 -1
  29. package/dist/index.d.ts +1929 -1342
  30. package/dist/index.js +10 -10
  31. package/dist/insert.d.ts +3 -3
  32. package/dist/insert.js +1 -1
  33. package/dist/move.d.ts +4 -4
  34. package/dist/move.js +3 -3
  35. package/dist/{options-C6HYCKP7.d.ts → options-CdFl510T.d.ts} +1 -1
  36. package/dist/{path-B6MMiodD.d.ts → path-JEV2c5If.d.ts} +2 -13
  37. package/dist/patterns-builtin.d.ts +1 -1
  38. package/dist/patterns-builtin.js +1 -1
  39. package/dist/{pointSnapToGrid-BBtn5bus.d.ts → pointSnapToGrid-Dmthv94u.d.ts} +2 -2
  40. package/dist/{registry-DGBrjGbB.d.ts → registry-BU1_3L_C.d.ts} +691 -38
  41. package/dist/renderer.d.ts +38 -83
  42. package/dist/renderer.js +10 -10
  43. package/dist/resize.d.ts +4 -4
  44. package/dist/resize.js +2 -2
  45. package/dist/routing.d.ts +35 -9
  46. package/dist/routing.js +1 -1
  47. package/dist/stroke-5pBwOm9s.d.ts +190 -0
  48. package/dist/{types-vqxpCFVp.d.ts → types-DIQAisSG.d.ts} +107 -5
  49. package/dist/{types-DbysOO-2.d.ts → types-XBcDEp3Y.d.ts} +14 -9
  50. package/dist/{types-BhrifbZ1.d.ts → types-miXHGrZM.d.ts} +1 -1
  51. package/package.json +8 -6
  52. package/dist/chunk-3B4QEB2G.js.map +0 -1
  53. package/dist/chunk-4Z6J4IVH.js.map +0 -1
  54. package/dist/chunk-CSMF654J.js.map +0 -1
  55. package/dist/chunk-GVCNT7UH.js +0 -47
  56. package/dist/chunk-GVCNT7UH.js.map +0 -1
  57. package/dist/chunk-HGF5X6MZ.js.map +0 -1
  58. package/dist/chunk-J3YC3GVF.js.map +0 -1
  59. package/dist/chunk-JOSUVS6C.js.map +0 -1
  60. package/dist/paint-types-CnLIzqq1.d.ts +0 -261
  61. package/dist/viewToMat3-CQW_YqDw.d.ts +0 -67
package/dist/index.d.ts CHANGED
@@ -1,41 +1,44 @@
1
1
  import * as React$1 from 'react';
2
- import React__default, { RefObject, ReactNode, Ref, MouseEvent as MouseEvent$1, MutableRefObject } from 'react';
2
+ import React__default, { RefObject, ReactNode, Ref, MouseEvent as MouseEvent$1, ComponentType, MutableRefObject } from 'react';
3
3
  import { a as ViewTransform, V as View, Z as ZoomBound, b as ZoomFactor } from './view-DSQgxBJB.js';
4
4
  export { s as screenToWorld, v as viewToTransform, w as worldToScreen } from './view-DSQgxBJB.js';
5
- import { a as UnitValue, b as UnitSystem, U as UseMoveOptions, O as OriginProjection } from './grid-DqOi0Vgi.js';
6
- export { I as IMPERIAL_INCHES, M as METRIC_MM, P as PIXELS, R as RECT_ORIGIN_PROJECTION, c as Unit, f as formatUnit, g as gridSnapStrategy, p as pointToGridCell, r as resolveUnit } from './grid-DqOi0Vgi.js';
7
- import { R as RenderLayer, D as Dims, a as DeviceProfile, b as DetectedDeviceFacts, C as Condition, c as Rule, d as RuleCtx, V as VisibilityRules, e as ChromeCtx, f as ChromeId, A as Action, g as DepSchema, S as SliceDep, h as ActionsRegistry, T as Tool, i as AffordanceHit, j as Dispatcher, k as ToolDef, l as ViewportToolDef, m as AnyTool, O as OverlayPosition, n as ToolKeybinding, H as HotkeyTrigger, L as LayerHit, o as ChromeState, p as SelectionApi, I as InsertExtras, G as GeometryProjection, q as ContentHandlerEntry, r as SvgIngestOptions, U as UseSelectionOptions, s as ActionsProp, t as Affordance, u as AffordanceRegion, v as AffordanceBinding, w as CommonAffordanceScratch, B as BooleansAdapter, E as Eligibility, x as BindingScope, y as Contribution, z as ScopedBinding } from './registry-DGBrjGbB.js';
8
- export { F as ALWAYS, J as ActionDeps, K as ActionDisabledReason, M as ActionEnabledResult, N as ActionEntry, P as ActionsProvider, Q as ActiveToolContextProvider, W as ActiveToolContextProviderIfRoot, X as ActiveToolContextProviderProps, Y as ActiveToolContextValue, Z as AreaSelectDep, _ as BindingOpts, $ as BooleanOp, a0 as BooleanOpResult, a1 as BoundGesture, a2 as BuildRuleCtxArgs, a3 as ClaimableGesture, a4 as ClipboardDep, a5 as ClipboardIngestCtx, a6 as CustomPaintContext, a7 as DepName, a8 as DepRegistry, a9 as DepRegistryProvider, aa as DispatcherContext, ab as DragSample, ac as EditAnchorsDep, ad as GestureBinding, ae as IDENTITY_POSE_COMPOSITION, af as ImmediateInvoker, ag as IngestCtx, ah as IngestionDep, ai as InsertDep, aj as InvocationCtx, ak as Invoker, al as LassoSelectDep, am as LayoutDep, an as MatchResult, ao as NEVER, ap as NodeAtPointDep, aq as OngoingHandle, ar as OngoingInvoker, as as OngoingOverlay, at as Point2, au as PointerContextProvider, av as PointerContextValue, aw as PointerWorldPos, ax as PoseAdapter, ay as PoseComposition, az as ResizePolicy, aA as ResolveAllOptions, aB as ResolveOnlyResult, aC as ResolvedCandidate, aD as SelectionExtendKey, aE as SelectionMode, aF as Selector, aG as SnapDep, aH as SvgUnpacker, aI as TextEditDep, aJ as ToolCtx, aK as ToolModifiers, aL as ToolPresentation, aM as ToolSlot, aN as UiOngoingControl, aO as ViewApi, aP as applyBooleanOp, aQ as buildRuleCtx, aR as clipboardCopyAction, aS as clipboardCutAction, aT as composeRectPose, aU as composeWorldPose, aV as createDispatcher, aW as decomposeRectPose, aX as describeRule, aY as drawLayers, aZ as enterTextEditAction, a_ as evaluate, a$ as evaluateEnabled, b0 as rebaseLocalPose, b1 as registerContentHandler, b2 as sliceAction, b3 as specificity, b4 as translateRectPose, b5 as useAction, b6 as useActionsRegistry, b7 as useActiveToolContext, b8 as useDepRegistry, b9 as useDepSource, ba as useOptionalActiveToolContext, bb as useOptionalDepRegistry, bc as usePointerContext, bd as useSelection, be as worldPoseLookup } from './registry-DGBrjGbB.js';
9
- import { S as Stroke, F as FillStyle, a as TextureHandle, T as TilePatternSpec, b as GradientKind, G as GradStop, c as GradientFill } from './paint-types-CnLIzqq1.js';
10
- export { d as GradientUnits, R as Region, e as StrokeAlign, f as alignedStrokeRect } from './paint-types-CnLIzqq1.js';
11
- import { B as Bounds, P as PoseProjection, F as FitViewToBoundsOptions, V as ViewportDims } from './geometry-C56YbnfD.js';
12
- export { R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR, f as fitViewToBounds } from './geometry-C56YbnfD.js';
13
- import { N as NodeId, S as Scene, a as Node$1, b as SerializedScene, R as RectPose, U as UseSceneOptions, c as SceneRegistry, d as RegisteredOp } from './types-vqxpCFVp.js';
14
- export { A as AddLayerSpec, e as AddNodeSpec, C as ContainerNode, L as LayerRecord, f as LeafNode, g as SerializedNode, h as SystemLayerRecord, i as SystemLayerSpec, j as UserLayerRecord, k as asNodeId } from './types-vqxpCFVp.js';
15
- import { M as ModifierState, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior, L as LassoSelectBehavior, a as RotatedPose, e as RotateBehavior, S as SnapStrategy, c as ResizeAnchor, b as MoveBehavior, I as InsertBehavior } from './types-BhrifbZ1.js';
16
- export { A as ActionBehavior, f as AreaSelectOverlay, g as BehaviorMoveResult, h as BehaviorResult, C as CloneBehavior, i as CloneLayer, j as ClonePose, G as GestureContext, k as GroupTransform, l as InsertMoveResult, m as InsertOverlay, n as InsertPoint, o as InsertProposed, p as LassoSelectMoveResult, q as LassoSelectOverlay, r as LassoSelectPose, s as LassoSelectProposed, t as PointSnapContext, d as PointSnapFrame, u as PointSnapResult, v as PointerState, w as ResizeMoveResult, x as ResizeOverlay, y as ResizeProposed, z as RotateMoveResult, D as RotateOverlay, E as RotateProposed } from './types-BhrifbZ1.js';
17
- import { U as UseResizeOptions } from './pointSnapToGrid-BBtn5bus.js';
18
- export { C as CORNER_ANCHORS, a as CornerAnchor, e as CornerEdge, b as CornerHandle, c as cornerPoint, d as cornerResizeHandles, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-BBtn5bus.js';
19
- import { BodyClassification, IngestItem } from '@weasel-js/gestures';
5
+ import { a as UnitValue, b as UnitSystem, U as UseMoveOptions, O as OriginProjection } from './grid-nnXU4VjN.js';
6
+ export { I as IMPERIAL_INCHES, M as METRIC_MM, P as PIXELS, R as RECT_ORIGIN_PROJECTION, c as Unit, f as formatUnit, g as gridSnapStrategy, p as pointToGridCell, r as resolveUnit } from './grid-nnXU4VjN.js';
7
+ import { R as RenderLayer, D as Dims, a as DeviceProfile, b as DetectedDeviceFacts, C as Condition, c as Rule, d as RuleCtx, V as VisibilityRules, e as ChromeCtx, f as ChromeId, A as Action, g as ViewAnimationOptions, h as DepSchema, S as SliceDep, i as ActionsRegistry, T as Tool, j as AffordanceHit, k as Dispatcher, l as ToolDef, m as ViewportToolDef, n as AnyTool, O as OverlayPosition, o as ToolKeybinding, H as HotkeyTrigger, L as LayerHit, p as OngoingOverlay, q as ChromeState, r as SelectionApi, U as UseSelectionOptions, I as InsertExtras, B as BuiltinShapeToolId, G as GeometryProjection, s as ContentHandlerEntry, t as SvgIngestOptions, u as ActionsProp, v as Animator, w as Affordance, x as AffordanceRegion, y as AffordanceBinding, z as CommonAffordanceScratch, E as ColorOverrideRegistry, F as BooleansAdapter, J as EasingFn, K as SpringPresetName, M as SpringPreset, N as UseAnimatorOptions, P as AnimationHandle, Q as VertexColorChannel, W as SampledTrack, X as Eligibility, Y as BindingScope, Z as Contribution, _ as ScopedBinding } from './registry-BU1_3L_C.js';
8
+ export { $ as ALWAYS, a0 as ActionDeps, a1 as ActionDisabledReason, a2 as ActionEnabledResult, a3 as ActionEntry, a4 as ActionsProvider, a5 as ActiveToolContextProvider, a6 as ActiveToolContextProviderIfRoot, a7 as ActiveToolContextProviderProps, a8 as ActiveToolContextValue, a9 as AnimateToBoundsOptions, aa as AreaSelectDep, ab as BindingOpts, ac as BooleanOp, ad as BooleanOpResult, ae as BoundGesture, af as BuildRuleCtxArgs, ag as ClaimableGesture, ah as ClipboardDep, ai as ClipboardIngestCtx, aj as ColorOverride, ak as ColorOverrideFn, al as CustomPaintContext, am as DecayOptions, an as DepName, ao as DepRegistry, ap as DepRegistryProvider, aq as DispatcherContext, ar as DragSample, as as EditAnchorsDep, at as EventTrack, au as GestureBinding, av as IDENTITY_POSE_COMPOSITION, aw as ImmediateInvoker, ax as IngestCtx, ay as IngestionDep, az as InsertDep, aA as Interpolate, aB as InterpolatorFactory, aC as InvocationCtx, aD as Invoker, aE as KIT_SHAPE_KINDS, aF as Keyframe, aG as LassoSelectDep, aH as LayerCommandCache, aI as LayoutDep, aJ as LoopFactory, aK as LoopOptions, aL as MatchResult, aM as NEVER, aN as NestedTimeline, aO as NodeAtPointDep, aP as OngoingHandle, aQ as OngoingInvoker, aR as PhysicsHandle, aS as PhysicsOptions, aT as Point2, aU as PointerContextProvider, aV as PointerContextValue, aW as PointerWorldPos, aX as PoseAdapter, aY as PoseComposition, aZ as ResizePolicy, a_ as ResolveAllOptions, a$ as ResolveOnlyResult, b0 as ResolvedCandidate, b1 as SelectionExtendKey, b2 as SelectionMode, b3 as Selector, b4 as SnapDep, b5 as SpringOptions, b6 as StaggerBuilder, b7 as StaggerDelay, b8 as StaggerFactory, b9 as StaggerOptions, ba as StaggerPerItem, bb as StaggerSpringPoseOptions, bc as StaggerTweenOptions, bd as SvgUnpacker, be as TextEditDep, bf as TimelineHandle, bg as TimelineOptions, bh as TimelineTrack, bi as ToolCtx, bj as ToolModifiers, bk as ToolPresentation, bl as ToolSlot, bm as Track, bn as TweenLoopOptions, bo as TweenOptions, bp as UiOngoingControl, bq as VIEW_ANIMATION_KEY, br as ViewAnimationApi, bs as ViewApi, bt as ViewChannel, bu as actionBindings, bv as applyBooleanOp, bw as buildRuleCtx, bx as clipboardCopyAction, by as clipboardCutAction, bz as composeRectPose, bA as composeWorldPose, bB as createDispatcher, bC as decomposeRectPose, bD as describeRule, bE as drawLayers, bF as drawOneLayer, bG as enterTextEditAction, bH as evaluate, bI as evaluateEnabled, bJ as isLayerPainted, bK as isLayerVisible, bL as rebaseLocalPose, bM as registerContentHandler, bN as resolveParams, bO as sliceAction, bP as specificity, bQ as translateRectPose, bR as useAction, bS as useActionsRegistry, bT as useActiveToolContext, bU as useDepRegistry, bV as useDepSource, bW as useOptionalActiveToolContext, bX as useOptionalDepRegistry, bY as usePointerContext, bZ as useSelection, b_ as useViewAnimation, b$ as worldPoseLookup } from './registry-BU1_3L_C.js';
9
+ import { Stroke, FillStyle, TextureHandle, TilePatternSpec, GradientKind, GradStop, GradientFill, GradientUnits, MarkerKey, MarkerRef } from '@weasel-js/paint';
10
+ export { FillStyle, GradStop, GradientFill, GradientKind, GradientUnits, KitMarkerKey, MarkerKey, MarkerRef, Region, STROKE_DASH_RATIOS, Stroke, StrokeAlign, StrokeDashStyle, TextureHandle, TilePatternSpec, alignedStrokeRect, dashForStrokeStyle, strokeDashStyleOf } from '@weasel-js/paint';
11
+ import { R as RectPose, S as Scene, N as NodeId, a as Node$1, b as SerializedScene, U as UseSceneOptions, c as SceneRegistry, d as RegisteredOp } from './types-DIQAisSG.js';
12
+ export { A as AddLayerSpec, e as AddNodeSpec, C as ContainerNode, L as LayerRecord, f as LeafNode, P as PoseOverride, g as PoseOverrides, h as SerializedNode, i as SystemLayerRecord, j as SystemLayerSpec, k as UserLayerRecord, l as asNodeId } from './types-DIQAisSG.js';
13
+ import { B as Bounds, P as PoseProjection, V as ViewportDims } from './geometry-Dtt_k6Dq.js';
14
+ export { F as FitViewToBoundsOptions, R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR, f as fitViewToBounds } from './geometry-Dtt_k6Dq.js';
15
+ import { GestureSpec, BodyClassification, IngestItem } from '@weasel-js/gestures';
20
16
  export { ClickSpec, DragSpec, DropSpec, GestureSpec, IngestItem, InputEvent, KeyHeldSpec, KeySpec, ModSpec, MultiTouchSpec, PasteSpec, TargetSpec, WheelSpec } from '@weasel-js/gestures';
21
- import { M as MoveAdapter, A as AreaSelectAdapter, R as RotateAdapter, a as LassoHitMode, b as LassoSelectAdapter, c as ResizeAdapter, d as SceneAdapter, L as LayoutStrategy, e as LayerEnumerableAdapter, I as InsertAdapter, C as ClipboardSnapshot, O as OrderedAdapter, f as LayoutSnap } from './types-DbysOO-2.js';
22
- export { g as ContainerBounds, D as DropTarget, h as LayoutChild, i as LayoutContainer, j as LayoutDragged, S as SnapTarget } from './types-DbysOO-2.js';
17
+ import { M as ModifierState, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior, L as LassoSelectBehavior, a as RotatedPose, e as RotateBehavior, S as SnapStrategy, c as ResizeAnchor, b as MoveBehavior, I as InsertBehavior } from './types-miXHGrZM.js';
18
+ export { A as ActionBehavior, f as AreaSelectOverlay, g as BehaviorMoveResult, h as BehaviorResult, C as CloneBehavior, i as CloneLayer, j as ClonePose, G as GestureContext, k as GroupTransform, l as InsertMoveResult, m as InsertOverlay, n as InsertPoint, o as InsertProposed, p as LassoSelectMoveResult, q as LassoSelectOverlay, r as LassoSelectPose, s as LassoSelectProposed, t as PointSnapContext, d as PointSnapFrame, u as PointSnapResult, v as PointerState, w as ResizeMoveResult, x as ResizeOverlay, y as ResizeProposed, z as RotateMoveResult, D as RotateOverlay, E as RotateProposed } from './types-miXHGrZM.js';
19
+ import { U as UseResizeOptions } from './pointSnapToGrid-Dmthv94u.js';
20
+ export { C as CORNER_ANCHORS, a as CornerAnchor, e as CornerEdge, b as CornerHandle, c as cornerPoint, d as cornerResizeHandles, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-Dmthv94u.js';
21
+ import { M as MoveAdapter, A as AreaSelectAdapter, R as RotateAdapter, b as LassoHitMode, c as LassoSelectAdapter, d as ResizeAdapter, L as LayoutStrategy, e as LayerEnumerableAdapter, I as InsertAdapter, C as ClipboardSnapshot, O as OrderedAdapter, a as SceneAdapter, f as LayoutSnap } from './types-XBcDEp3Y.js';
22
+ export { g as ContainerBounds, D as DropTarget, h as LayoutChild, i as LayoutContainer, j as LayoutDragged, S as SnapTarget } from './types-XBcDEp3Y.js';
23
23
  import { D as DebugSink, a as DebugConfig, b as DebugSnapshot, c as DebugStrokes, d as DebugTheme } from './types-BHK2dkMu.js';
24
24
  export { e as DebugFeature, f as DebugStroke, H as HandleKind, g as HitShape, R as RecordedBounds, h as RecordedHandle, i as RecordedHitbox, j as RecordedLayer, k as RecordedOrigin, l as RecordedSnap } from './types-BHK2dkMu.js';
25
- import { a as PolygonPath, P as Path, b as PathFillRule, R as RectPath } from './path-B6MMiodD.js';
26
- export { c as PATH_C, d as PATH_CMD_LENGTHS, e as PATH_L, f as PATH_M, g as PATH_Q, h as PATH_Z } from './path-B6MMiodD.js';
25
+ import { a as PolygonPath, P as Path, b as PathFillRule, R as RectPath } from './path-JEV2c5If.js';
27
26
  import * as react_jsx_runtime from 'react/jsx-runtime';
28
- import { D as DrawCommand, S as ShaderProgramHandle, P as PathDrawCommand, d as StyledRun, R as ResolvedTextStyle, e as TextStyle, f as TextVerticalAlign, M as Mat3$1 } from './DrawCommand-BbPrSTra.js';
29
- export { g as DEFAULT_TEXT_STYLE, G as GroupDrawCommand, I as ImageDrawCommand, h as ResolvedRun, a as ShaderDrawCommand, b as ShaderUniform, T as TextDrawCommand, i as fontString, j as markdownToRuns, m as mat3, k as resolveRuns, l as resolveTextStyle, n as runsToMarkdown, o as runsToPlainText, t as toRuns, v as verticalAlignOffset } from './DrawCommand-BbPrSTra.js';
30
- import { Op, CreateHistoryOptions, History } from '@weasel-js/history';
27
+ import { D as DrawCommand, S as ShaderProgramHandle, P as PathDrawCommand, M as Mat3$1 } from './DrawCommand-BnZyQaDc.js';
28
+ export { G as GroupDrawCommand, I as ImageDrawCommand, a as ShaderDrawCommand, b as ShaderUniform, T as TextDrawCommand, m as mat3 } from './DrawCommand-BnZyQaDc.js';
29
+ import { Op, HistorySelection, CreateHistoryOptions, History } from '@weasel-js/history';
31
30
  export * from '@weasel-js/history';
32
31
  export { Op } from '@weasel-js/history';
33
- import { Mat3 } from '@weasel-js/geom';
32
+ import { TextRenderer, TextPose, StyledRun, TextStyle, TextPaint, TextVerticalAlign } from '@weasel-js/text';
33
+ export { DEFAULT_TEXT_STYLE, LaidOutDecoration, LaidOutGroup, LaidOutLineBox, LaidOutOutlineGlyph, LaidOutQuad, LaidOutRuns, LayoutLine, LayoutResult, LayoutRunsOpts, MARKDOWN_RUN_GRAMMAR, MarkdownFontOptions, MeasureFn, MeasureTextBoundsOpts, MeasuredText, PositionedRun, ResolvedRun, ResolvedTextStyle, RunFlag, RunGrammar, RunMarker, SCRIPT_METRICS, StyledRun, TextAlign, TextDirection, TextLineBoxesOpts, TextPaint, TextPose, TextRenderer, TextStyle, TextVerticalAlign, cachedLayoutRuns, createMarkdownRenderer, fontString, layoutMarkdown, layoutRuns, markdownToRuns, measureText, measureTextBounds, measuredWidth, resolveAlign, resolveRuns, resolveTextStyle, runsToMarkdown, runsToPlainText, textLineBoxes, toRuns, verticalAlignOffset } from '@weasel-js/text';
34
+ import { S as ShaderProgram } from './stroke-5pBwOm9s.js';
35
+ export { I as ImageMinification, b as SpriteSheet, f as frameRect, r as resolveStrokeWidth, v as viewToMat3 } from './stroke-5pBwOm9s.js';
36
+ import { Rect, Mat3 } from '@weasel-js/geom';
37
+ export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, Rect, pathCommandCoordCount } from '@weasel-js/geom';
34
38
  import { G as Guide } from './types-D2tTKEU0.js';
35
- export { U as UseInsertOptions } from './options-C6HYCKP7.js';
39
+ export { U as UseInsertOptions } from './options-CdFl510T.js';
36
40
  export { UseClipboardOpsOptions, UseClipboardOpsReturn, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, buildWeaselClipboardText, embedWeaselMetadataInSvg, extractWeaselClipboardFromSvg, parseWeaselClipboardText, sniffWeaselClipboardText, useClipboardOps } from './clipboard.js';
37
41
  export { cloneByAltDrag } from './clone.js';
38
- export { I as ImageMinification, v as viewToMat3 } from './viewToMat3-CQW_YqDw.js';
39
42
  export { FontVariant, LocalFontOutlinesResult, OutlineFontStyle, OutlineSource, OutlineStatus, OutlineVariant, canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasFont, listFontOutlines, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, subscribeGlyphReady, unregisterCanvasFont, unregisterFontOutlines } from '@weasel-js/font';
40
43
  import { CapabilityTag } from '@weasel-js/modes';
41
44
 
@@ -189,6 +192,79 @@ interface CellHighlightLayerOpts {
189
192
  /** Build a `RenderLayer` that fills a single grid cell — typically a snap-target preview. */
190
193
  declare function createCellHighlightLayer(opts: CellHighlightLayerOpts): RenderLayer<unknown>;
191
194
 
195
+ /**
196
+ * @experimental
197
+ *
198
+ * A view the resolver can route input to: a camera and the rect it paints
199
+ * into, in canvas CSS-pixel space. A viewport node supplies one of these per
200
+ * frame; so could any other per-view surface.
201
+ */
202
+ interface ResolvableView {
203
+ id: string;
204
+ view: View;
205
+ rect: {
206
+ x: number;
207
+ y: number;
208
+ w: number;
209
+ h: number;
210
+ };
211
+ }
212
+ /**
213
+ * @experimental
214
+ *
215
+ * The view an input event belongs to. `id` is `null` for the root view —
216
+ * the whole canvas, when no viewport contains the point.
217
+ *
218
+ * `origin` is the client-space origin of that view's surface, ready to hand
219
+ * to `clientToWorld` — which is the whole reason this type carries it rather
220
+ * than leaving callers to add the canvas rect and the view rect themselves.
221
+ */
222
+ interface ViewTarget {
223
+ id: string | null;
224
+ view: View;
225
+ origin: {
226
+ left: number;
227
+ top: number;
228
+ };
229
+ }
230
+ /**
231
+ * @experimental
232
+ *
233
+ * Routes client points to views, and holds a captured pointer on the view its
234
+ * gesture started in.
235
+ *
236
+ * Stickiness is the point. A drag that leaves its view's rect — over a
237
+ * neighbouring view, or off the canvas — must keep reporting coordinates in
238
+ * the space it began in, or a marquee crossing a panel edge silently starts
239
+ * measuring against the wrong camera.
240
+ */
241
+ interface ViewResolver {
242
+ /** Resolve and pin `pointerId`. Call on pointerdown. */
243
+ begin(pointerId: number, clientX: number, clientY: number): ViewTarget;
244
+ /**
245
+ * The view for a point. A pinned `pointerId` gets its pinned view, looked
246
+ * up fresh so a rect that moved mid-gesture is honored. Pass `null` for
247
+ * input with no pointer to capture — wheel, hover, keys.
248
+ */
249
+ at(pointerId: number | null, clientX: number, clientY: number): ViewTarget;
250
+ /** Release `pointerId`. Call on pointerup and pointercancel. */
251
+ end(pointerId: number): void;
252
+ /** Release every pin. */
253
+ clear(): void;
254
+ }
255
+ interface CreateViewResolverOpts {
256
+ /** Candidate views in paint order — the last one containing a point wins. */
257
+ views: () => readonly ResolvableView[];
258
+ /** The camera for points no view claims. */
259
+ root: () => View;
260
+ /** Client-space origin of the canvas element. */
261
+ canvasOrigin: () => {
262
+ left: number;
263
+ top: number;
264
+ };
265
+ }
266
+ declare function createViewResolver(opts: CreateViewResolverOpts): ViewResolver;
267
+
192
268
  /**
193
269
  * @experimental
194
270
  *
@@ -202,33 +278,50 @@ declare function createCellHighlightLayer(opts: CellHighlightLayerOpts): RenderL
202
278
  * lenses on the source.
203
279
  *
204
280
  * **Inner view semantics.** The source layers draw as if the inner view
205
- * filled the screen at world-origin (their own `viewToMat3(innerView)` runs
206
- * normally). The viewport then translates the result so that the inner
207
- * view's origin lands at `bounds.x, bounds.y` and clips to `(bounds.w,
208
- * bounds.h)`. Caller chooses `innerView.{x,y,scale}` to control which slice
209
- * of source-world is shown.
281
+ * filled the screen at world-origin each is wrapped in `viewToMat3(view)`
282
+ * by the same `drawOneLayer` the outer canvas uses. The viewport then
283
+ * translates the result so that the inner view's origin lands at `bounds.x,
284
+ * bounds.y` and clips to `(bounds.w, bounds.h)`. Caller chooses
285
+ * `innerView.{x,y,scale}` to control which slice of source-world is shown.
210
286
  *
211
287
  * **Input is re-projected on request, not automatically.** `reproject` maps a
212
288
  * screen point into the inner view's world; a consumer that wants a click
213
- * inside a viewport to mean something calls it from its own handler. The
214
- * dispatcher is untouched, so tools still target the outer view — making
215
- * tools work *inside* a viewport is a larger question (which view a pinch
216
- * zooms, what a drag leaving the rect does) and wants its own design.
289
+ * inside a viewport to mean something calls it from its own handler, or feeds
290
+ * `resolvable` to `createViewResolver` to route a whole pointer stream.
291
+ * `<CanvasView>` is that wiring done for you; build on this directly for a
292
+ * viewport that takes no input, or to route it yourself.
217
293
  *
218
294
  * **Screen-space source layers** (e.g., debug overlays, selection chrome)
219
- * still render to the outer canvas, not into the viewport. To include
220
- * them, wrap them as world-space layers first.
295
+ * draw in the viewport's own CSS-pixel space: their coords are relative to
296
+ * the rect's top-left, and they clip to it, rather than to the outer canvas.
221
297
  */
222
- interface CreateViewportLayerOpts<TData> {
298
+ interface CreateViewportLayerOpts<TData, TSource = TData> {
223
299
  id: string;
224
300
  label: string;
225
301
  /** Layers re-rendered through `view`. Each receives `(data, view, dims)`
226
- * exactly as the outer Canvas would call it. */
227
- source: RenderLayer<TData>[];
228
- /** The inner view. Static for nowa future revision will accept
229
- * `View | (outer: View, dims: Dims) => View` so derivations like
230
- * parallax and node-anchored scroll can compose. */
231
- view: View;
302
+ * exactly as the outer Canvas would call it.
303
+ *
304
+ * Pass a thunk for a stack that is assembled elsewhere it is read fresh
305
+ * on every `draw`, the same way a thunked `view` is. */
306
+ source: readonly RenderLayer<TSource>[] | (() => readonly RenderLayer<TSource>[]);
307
+ /**
308
+ * The `data` the source layers receive, derived from what the outer canvas
309
+ * passed down. Omit and they get the outer canvas's own.
310
+ *
311
+ * A viewport showing the same scene through a second camera wants its own
312
+ * per-view helpers here — selection, chrome state and gesture previews that
313
+ * belong to *this* view rather than the one hosting it.
314
+ */
315
+ data?: (outer: TData) => TSource;
316
+ /**
317
+ * The inner view. Pass a thunk for a camera that moves — it is read fresh
318
+ * on every `draw`, `reproject` and `resolvable`, so those three cannot
319
+ * disagree about where the viewport is looking mid-gesture.
320
+ *
321
+ * The thunk receives the outer view and dims, so a derived camera
322
+ * (parallax, node-anchored scroll) is a function of the one hosting it.
323
+ */
324
+ view: View | ((outer: View, dims: Dims) => View);
232
325
  /** Where on the outer canvas this viewport is painted, in screen-space
233
326
  * CSS pixels. Recomputed every frame so the rect can track an outer
234
327
  * pose, follow a corner, etc. */
@@ -260,9 +353,9 @@ interface ViewportLayer<TData> extends RenderLayer<TData> {
260
353
  * `bounds` is a pure function of those, so this reproduces the exact rect
261
354
  * that was painted rather than a remembered one.
262
355
  *
263
- * This does not touch the dispatcher: tools still receive outer-view
264
- * coords, and a consumer that wants a click inside a viewport to mean
265
- * something calls this from its own handler.
356
+ * This does not touch the dispatcher: a consumer that wants a click inside
357
+ * a viewport to mean something calls this from its own handler, or declares
358
+ * the viewport as a `<CanvasView>` and lets the canvas route to it.
266
359
  */
267
360
  reproject(outer: View, dims: Dims, screen: {
268
361
  x: number;
@@ -271,12 +364,20 @@ interface ViewportLayer<TData> extends RenderLayer<TData> {
271
364
  x: number;
272
365
  y: number;
273
366
  } | null;
367
+ /**
368
+ * This viewport as a routing candidate for {@link createViewResolver} —
369
+ * its inner view and the rect it paints into for the given outer frame.
370
+ *
371
+ * Pass the `outer` view and `dims` the frame was drawn with, for the same
372
+ * reason `reproject` wants them: `bounds` is recomputed, not remembered.
373
+ */
374
+ resolvable(outer: View, dims: Dims): ResolvableView;
274
375
  }
275
376
  /** Build a layer that renders other layers through a second view, inside a
276
377
  * sub-region of the canvas — a minimap, an inset, a magnifier. Its
277
378
  * `reproject` maps screen points back through the inner view so the region
278
379
  * can be interacted with. */
279
- declare function createViewportLayer<TData>(opts: CreateViewportLayerOpts<TData>): ViewportLayer<TData>;
380
+ declare function createViewportLayer<TData, TSource = TData>(opts: CreateViewportLayerOpts<TData, TSource>): ViewportLayer<TData>;
280
381
  /**
281
382
  * @experimental
282
383
  *
@@ -454,6 +555,71 @@ declare const DEFAULT_VELOCITY_DECAY = 0.4;
454
555
  */
455
556
  declare function useSimulation<TNode extends SimulationNode>(opts: UseSimulationOptions<TNode>): Simulation<TNode>;
456
557
 
558
+ /**
559
+ * `useNodeOverlayFrame` — the coordinate frame a DOM overlay pinned to one
560
+ * scene node needs: the node's world box, the projection from that box's
561
+ * frame into overlay pixels, and the overlay's measured size.
562
+ *
563
+ * This is the frame half of `subscribeView`'s "DOM pinned to world
564
+ * coordinates" case. Consumers positioning chrome over a node — gradient
565
+ * handles, a badge, a caret — otherwise re-derive it, and the
566
+ * translate-and-scale inverse that derivation usually reaches for drops
567
+ * `pose.rotation`, so the chrome sits where the node would be unrotated.
568
+ *
569
+ * `toScreen` maps the node's **unrotated world box** frame, which is the
570
+ * frame `fillInPoseFrame` resolves a `units: 'bounds'` paint into and the
571
+ * one `pathInPoseFrame` projects geometry into. Rotation lives in this hook,
572
+ * not in those: a node's stored geometry is pre-rotation by definition.
573
+ */
574
+
575
+ /** Structural point, matching every other `{ x, y }` the kit passes across a
576
+ * boundary. */
577
+ interface OverlayPoint {
578
+ x: number;
579
+ y: number;
580
+ }
581
+ /** The overlay frame for one node. `null` from the hook means there is no
582
+ * frame yet — no node, or nothing measured to draw it on. */
583
+ interface NodeOverlayFrame {
584
+ /** The node's composed world box, **unrotated**: the frame `toScreen` maps
585
+ * from, and the box to hand `fillInPoseFrame` / `fillToBoundsFrame`. */
586
+ box: {
587
+ x: number;
588
+ y: number;
589
+ width: number;
590
+ height: number;
591
+ };
592
+ /** Node box frame → overlay pixels, rotation and view included. */
593
+ toScreen: (p: OverlayPoint) => OverlayPoint;
594
+ /** Overlay pixels → node box frame. Inverts `toScreen`. */
595
+ toLocal: (p: OverlayPoint) => OverlayPoint;
596
+ /** The container's size in CSS pixels — the overlay's own box. */
597
+ width: number;
598
+ height: number;
599
+ }
600
+ interface UseNodeOverlayFrameOptions<TPose> {
601
+ /**
602
+ * Current viewport. A thunk is re-read on every projection, which is what
603
+ * an uncontrolled `SceneCanvas` needs — its camera lives in a ref and moves
604
+ * without a render, so pass the handle's `getView`. A plain `View` is the
605
+ * value from the render that supplied it, correct for a controlled
606
+ * consumer. Omit it and world units are handed through as screen pixels.
607
+ */
608
+ view?: View | (() => View);
609
+ /** Fold a child pose into its parent's frame. Default `composeRectPose`. */
610
+ compose?: (parent: TPose, child: TPose) => TPose;
611
+ }
612
+ /**
613
+ * Resolve the overlay frame for `nodeId` against `containerRef` — the element
614
+ * the overlay is positioned in, which must be the canvas's own box for the
615
+ * projection to land.
616
+ *
617
+ * Returns `null` when there is no node under `nodeId`, when the container has
618
+ * not been measured, or when the view collapses an axis to zero — the three
619
+ * states in which an overlay has nothing to draw.
620
+ */
621
+ declare function useNodeOverlayFrame<TData, TLayer extends string, TPose extends RectPose>(scene: Scene<TData, TLayer, TPose>, containerRef: RefObject<HTMLElement | null>, nodeId: string | null | undefined, options?: UseNodeOverlayFrameOptions<TPose>): NodeOverlayFrame | null;
622
+
457
623
  /**
458
624
  * Stylus / Pencil pointer-event helpers.
459
625
  *
@@ -790,7 +956,7 @@ interface CanvasSizeSnapshot extends CanvasSize {
790
956
  * so dragging a window to a different-density display without resizing it
791
957
  * left the snapshot stale. The profile watches a re-armed resolution media
792
958
  * query instead. */
793
- declare function useCanvasSize(containerRef: RefObject<HTMLDivElement | null>): CanvasSizeSnapshot;
959
+ declare function useCanvasSize(containerRef: RefObject<HTMLElement | null>): CanvasSizeSnapshot;
794
960
 
795
961
  /**
796
962
  * Handle/hit multiplier applied on a coarse pointer.
@@ -820,25 +986,23 @@ declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?:
820
986
  declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
821
987
 
822
988
  /**
823
- * Base sizes for grabbable chrome, in CSS pixels at `targetScale = 1`.
824
- *
825
- * These were six separate literal `8`s and one `24` scattered across
826
- * `SceneCanvas`, `features/selection/overlay`, `affordances/cornerResize`,
827
- * `canvas/affordanceAt`, and `interactions/actions/rotate/handle`. They are
828
- * consolidated here because paint and hit-test MUST scale together: chrome
829
- * you can see but cannot grab is the exact failure `chrome-caps` exists to
830
- * make impossible, and duplicated literals in five files is how that failure
831
- * gets reintroduced.
989
+ * Base sizes for grabbable chrome, in CSS pixels at `targetScale = 1`, and the
990
+ * one accessor that resolves them for a live pointer type.
832
991
  *
833
- * Multiply by `DeviceProfile.targetScale` at the point of use the profile
834
- * is a runtime value, these are not.
992
+ * Paint and hit-test MUST scale together: chrome you can see but cannot grab
993
+ * is the exact failure `chrome-caps` exists to make impossible, and a literal
994
+ * `8` copied into a fifth file is how that failure gets reintroduced. So every
995
+ * kit-internal use site goes through {@link targetSizesPx} — the selection
996
+ * overlay's handle size, `buildAffordanceAt`'s hit radii, and the slops debug
997
+ * overlay all read the same object. The bare constants below stay exported for
998
+ * consumers, who get the unscaled numbers they always got.
835
999
  */
836
1000
  /** Selection corner-handle visual size and hit radius. */
837
- declare const HANDLE_BASE_PX = 8;
1001
+ declare const HANDLE_BASE_PX: 8;
838
1002
  /** Path anchor / control-point hit radius. */
839
- declare const ANCHOR_HIT_BASE_PX = 8;
1003
+ declare const ANCHOR_HIT_BASE_PX: 8;
840
1004
  /** Distance from a selection's top edge to the rotation handle's center. */
841
- declare const ROTATION_HANDLE_BASE_PX = 24;
1005
+ declare const ROTATION_HANDLE_BASE_PX: 24;
842
1006
 
843
1007
  /**
844
1008
  * Read the ambient device profile.
@@ -1007,7 +1171,17 @@ interface UseZoomReturn {
1007
1171
  declare function useZoom(opts: UseZoomOptions): UseZoomReturn;
1008
1172
 
1009
1173
  /**
1010
- * Run `computeFitView` once when the viewport first has non-zero size, and
1174
+ * Compute zoom and pan that fit `contentW x contentH` (in content units)
1175
+ * inside `viewportW x viewportH` (in pixels). `padRatio` is the fraction of
1176
+ * each axis to fill (default 0.85 — leaves a uniform 15% margin).
1177
+ */
1178
+ declare function computeFitViewport(viewportW: number, viewportH: number, contentW: number, contentH: number, padRatio?: number): {
1179
+ zoom: number;
1180
+ panX: number;
1181
+ panY: number;
1182
+ };
1183
+ /**
1184
+ * Run `computeFitViewport` once when the viewport first has non-zero size, and
1011
1185
  * apply the result via the supplied setters. Subsequent size changes are
1012
1186
  * ignored — this hook centers exactly once.
1013
1187
  */
@@ -1219,10 +1393,9 @@ declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
1219
1393
  /**
1220
1394
  * useHoverTracking — last-hovered NodeId for chrome-caps rules.
1221
1395
  *
1222
- * Attaches a `pointermove` listener to the supplied canvas; on each
1223
- * move, converts client coords world coords and runs the supplied
1224
- * `getNodeAtPoint`, caching the resulting id on a ref. Cleared on
1225
- * `pointerleave` (pointer left the canvas → nothing is hovered).
1396
+ * Attaches a `pointermove` listener to the supplied canvas; on each move it
1397
+ * runs the supplied `nodeAtClientPoint` and caches the resulting id on a ref.
1398
+ * Cleared on `pointerleave` (pointer left the canvas nothing is hovered).
1226
1399
  *
1227
1400
  * Returns a stable getter — call once per frame from `buildChromeCtx`.
1228
1401
  *
@@ -1234,13 +1407,11 @@ declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
1234
1407
  /** Options for `useHoverTracking`. */
1235
1408
  interface UseHoverTrackingArgs {
1236
1409
  canvasRef: RefObject<HTMLCanvasElement | null>;
1237
- /** Client world conversion. Same shape as the dispatcher's. */
1238
- clientToWorld: (clientX: number, clientY: number) => {
1239
- x: number;
1240
- y: number;
1241
- };
1242
- /** Topmost-id resolver. Returns null when the world point hits nothing. */
1243
- getNodeAtPoint: (worldX: number, worldY: number) => {
1410
+ /** Topmost id under a client point, or null. One lookup rather than a
1411
+ * client→world thunk beside a world-space picker: a screen-pixel pick
1412
+ * tolerance converts against the camera the point resolved to, and two
1413
+ * thunks are two chances to disagree about which view that is. */
1414
+ nodeAtClientPoint: (clientX: number, clientY: number) => {
1244
1415
  id: NodeId;
1245
1416
  } | null;
1246
1417
  enabled?: boolean;
@@ -1250,28 +1421,75 @@ interface UseHoverTrackingArgs {
1250
1421
  * the component on every pointer move. */
1251
1422
  declare function useHoverTracking(args: UseHoverTrackingArgs): () => NodeId | null;
1252
1423
 
1424
+ /**
1425
+ * Project an action's keyboard bindings into the shape a shortcut chip
1426
+ * renders from (`formatShortcut` / `formatShortcutParts` in
1427
+ * `@weasel-js/ui`). Palette and menu surfaces show what an action answers
1428
+ * to; the dispatcher reads the specs themselves.
1429
+ */
1430
+
1431
+ /** One keyboard shortcut, flattened for display. Structurally the
1432
+ * `ShortcutInput` weasel-ui formats — kept local so core doesn't depend on
1433
+ * the UI package. */
1434
+ interface ActionShortcut {
1435
+ key: string;
1436
+ mod: boolean;
1437
+ alt: boolean;
1438
+ shift: boolean;
1439
+ }
1440
+ /**
1441
+ * The chip form of a single gesture spec, or `undefined` when the spec has
1442
+ * none.
1443
+ *
1444
+ * Two kinds of collapsing, both because a binding list is written for a
1445
+ * matcher rather than for a reader:
1446
+ *
1447
+ * - A spec's `key` may list spellings of one keycap (`['[', '{']` — the
1448
+ * shifted bracket reports as `'{'`). The first is the shortcut's name.
1449
+ * - A modifier declared `'optional'` matches held or unheld, so it isn't
1450
+ * part of what the user has to press.
1451
+ *
1452
+ * Non-keyboard specs (drag, wheel, click) have no chip form. Neither does a
1453
+ * *required* `ctrl` or `meta` — nothing in the kit declares one, and the
1454
+ * display shape carries only `mod`.
1455
+ */
1456
+ declare function keySpecShortcut(spec: GestureSpec): ActionShortcut | undefined;
1457
+ /**
1458
+ * Every keyboard shortcut an action answers to, in declaration order.
1459
+ * Specs `keySpecShortcut` has no chip for are skipped, and bindings it leaves
1460
+ * identical are emitted once; an action bound only to those returns empty.
1461
+ */
1462
+ declare function actionShortcuts(action: Action): readonly ActionShortcut[];
1463
+
1253
1464
  /**
1254
1465
  * `moveAction` — first `ongoing`-timing Action descriptor.
1255
1466
  *
1256
1467
  * Mirrors the per-frame translate semantics of the `useMove` hook:
1257
1468
  * - `start`: capture origin poses for all selected nodes; record the
1258
1469
  * current drag delta in scratch each frame.
1259
- * - `onMove`: update the in-scratch `currentDelta` only no scene writes.
1260
- * This avoids polluting the undo stack with O(N-frames) entries.
1470
+ * - `onMove`: update the in-scratch `currentDelta`, and publish the frame's
1471
+ * poses as ephemeral overrides. No *document* writes, so the undo stack
1472
+ * still sees one entry for the whole drag.
1261
1473
  * - `onEnd('commit')`: emit the final delta as transform ops and route
1262
1474
  * them through the consumer `applyOps` hook when present, else
1263
1475
  * `scene.applyBatch(ops, 'Move', adapter)` — either way a single batch
1264
1476
  * → exactly one undo entry for the whole drag.
1265
- * - `onEnd('cancel')`: no scene writes — the scene was never mutated during
1266
- * the drag, so no restoration is needed.
1477
+ * - `onEnd('cancel')`: drop the overrides — the document was never mutated,
1478
+ * so the committed poses are the restoration.
1267
1479
  *
1268
- * ## Why no per-frame scene writes
1480
+ * ## Why no per-frame document writes
1269
1481
  *
1270
1482
  * `Scene.setPose` calls `executeAndLog`, which immediately records an undo
1271
- * entry on the scene's history engine. Per-frame writes during drag would create
1272
- * O(frames) history entries matching `useMove`'s approach of tracking
1273
- * poses only in React state (overlay) during the drag and committing a
1274
- * single `createTransformOp` batch at the end.
1483
+ * entry on the scene's history engine. Per-frame writes during drag would
1484
+ * create O(frames) history entries, so the drag commits one
1485
+ * `createTransformOp` batch at the end.
1486
+ *
1487
+ * The per-frame poses still go *somewhere* the scene can see: the ephemeral
1488
+ * override table, which bypasses history entirely. That is what lets a node
1489
+ * deriving its geometry from a dragged one follow the drag — `scenePoseLookup`
1490
+ * and the pick source both read overrides, and neither can see this action's
1491
+ * scratch. Keeping the frame in scratch alone is why the edge used to stay
1492
+ * anchored to the old endpoint and jump on drop.
1275
1493
  *
1276
1494
  * The behavior pipeline (snap-to-grid, snap-back-or-delete, snap-to-container,
1277
1495
  * etc.) via `opts.behaviors` from `BindingOpts` IS wired: `start` builds a
@@ -1680,7 +1898,10 @@ declare const viewportDragPanAction: Action & {
1680
1898
  *
1681
1899
  * ## Bindings (parametric — each passes `params.kind`)
1682
1900
  * - `{ kind: 'wheel', mods: { mod: true } }` → Cmd+wheel, params: `{ kind: 'wheel' }`
1683
- * (also covers Mac trackpad pinch, which the browser synthesizes as ctrl+wheel)
1901
+ * - `{ kind: 'wheel', mods: { ctrl: true } }` → trackpad pinch, params: `{ kind: 'wheel' }`.
1902
+ * Browsers synthesize a trackpad pinch as ctrl+wheel. On a Mac that is a
1903
+ * *different* event from the `mod` binding above (`mod` is metaKey there), so
1904
+ * pinch needs its own binding or nothing claims it and the page zooms instead.
1684
1905
  * - `{ kind: 'key', key: '=' }` → zoom in, params: `{ kind: 'in' }`
1685
1906
  * - `{ kind: 'key', key: '-' }` → zoom out, params: `{ kind: 'out' }`
1686
1907
  * - `{ kind: 'key', key: '0' }` → reset zoom, params: `{ kind: 'reset' }`
@@ -1694,13 +1915,27 @@ declare const viewportDragPanAction: Action & {
1694
1915
  * - `'in'`/`'out'`: step zoom by ×1.25 / ×0.8, anchored at the host center
1695
1916
  * when the `view` dep wires `hostSize()` (SceneCanvas does), falling back
1696
1917
  * to the canvas top-left origin for consumers that don't.
1697
- * - `'reset'`: resets scale to 1, translation to 0.
1918
+ * - `'reset'`: resets scale to 1, translation to 0, or calls the `view` dep's
1919
+ * `recenter()`.
1920
+ *
1921
+ * The three discrete branches glide instead of jumping when the `animate`
1922
+ * option is configured and the `view` dep implements `animate` — they hand the
1923
+ * action a target and nothing in between, which is the whole condition for
1924
+ * tweening. `'wheel'` always jumps per sample.
1698
1925
  *
1699
1926
  * ## Key binding modifier notes
1700
1927
  * The key bindings (`=`, `-`, `0`) require `mod: true` (Cmd on Mac, Ctrl elsewhere).
1701
1928
  * This matches the behaviour of the dissolved `useKeyboardZoomTool`.
1702
1929
  */
1703
1930
 
1931
+ /**
1932
+ * @experimental
1933
+ * Tuning for the animated form of the discrete zoom steps.
1934
+ */
1935
+ interface ViewportZoomAnimateOptions extends ViewAnimationOptions {
1936
+ /** Duration for the Cmd+0 reset branch. Defaults to `ms`. */
1937
+ resetMs?: number;
1938
+ }
1704
1939
  /**
1705
1940
  * @experimental
1706
1941
  * Tuning for {@link makeViewportZoomAction}.
@@ -1709,7 +1944,8 @@ interface ViewportZoomOptions {
1709
1944
  /**
1710
1945
  * Which wheel gesture triggers zoom.
1711
1946
  * - `'mod'` (default): Cmd/Ctrl+wheel — coexists with plain-wheel pan
1712
- * (`viewport.wheelPan`) and Mac trackpad pinch (ctrl+wheel).
1947
+ * (`viewport.wheelPan`). A Mac trackpad pinch arrives as ctrl+wheel, which
1948
+ * `mod: true` forbids on Mac; nothing here handles it.
1713
1949
  * - `'plain'`: bare wheel, no modifier. Pair with `viewport.pan: false`,
1714
1950
  * since plain wheel otherwise drives pan and the two would compete.
1715
1951
  */
@@ -1718,6 +1954,13 @@ interface ViewportZoomOptions {
1718
1954
  min?: number;
1719
1955
  /** Upper clamp on the resulting view scale, forwarded to `zoomAt`. Default 8. */
1720
1956
  max?: number;
1957
+ /**
1958
+ * Glide the discrete steps (Cmd+=, Cmd+-, Cmd+0) instead of jumping.
1959
+ * `true` uses the kit defaults; an object tunes them. Wheel and pinch never
1960
+ * animate — their input already samples every frame. Requires a `view` dep
1961
+ * that implements `animate`; without one this is ignored.
1962
+ */
1963
+ animate?: boolean | ViewportZoomAnimateOptions;
1721
1964
  }
1722
1965
  /**
1723
1966
  * @experimental
@@ -1865,12 +2108,31 @@ declare const lassoSelectAction: Action & {
1865
2108
 
1866
2109
  /**
1867
2110
  * @experimental
1868
- * Static descriptor for the `viewport.pinchZoom` Action.
2111
+ * Tuning for {@link makePinchZoomAction}.
2112
+ */
2113
+ interface PinchZoomOptions {
2114
+ /** Lower clamp on the resulting view scale, forwarded to `zoomAt`. Default 0.1. */
2115
+ min?: number;
2116
+ /** Upper clamp on the resulting view scale, forwarded to `zoomAt`. Default 8. */
2117
+ max?: number;
2118
+ }
2119
+ /**
2120
+ * @experimental
2121
+ * Build a `viewport.pinchZoom` Action descriptor with a configurable scale
2122
+ * clamp. The binding (two-finger multitouch) is fixed; only the clamp varies.
1869
2123
  *
1870
2124
  * Requires dep-schema entries: `view`.
1871
2125
  *
1872
2126
  * The invoker is `ongoing`. Zoom is applied per-frame via `view.set(zoomAt(...))`.
1873
2127
  */
2128
+ declare function makePinchZoomAction(opts?: PinchZoomOptions): Action & {
2129
+ requires: string[];
2130
+ };
2131
+ /**
2132
+ * @experimental
2133
+ * Default `viewport.pinchZoom` descriptor: two-finger pinch with the kit's
2134
+ * default 0.1–8 scale clamp. Equivalent to `makePinchZoomAction()`.
2135
+ */
1874
2136
  declare const pinchZoomAction: Action & {
1875
2137
  requires: string[];
1876
2138
  };
@@ -1916,10 +2178,9 @@ declare function useStandardActions(opts: UseStandardActionsOptions): void;
1916
2178
  * this adapter. It carries the scene-backed op-apply methods (`setPose` /
1917
2179
  * `setParent` / `setData` / `setLayer` / `removeNode` / `insertNode` /
1918
2180
  * `setChildOrder`) plus the read-side queries (`getNode` / `getNodes` /
1919
- * `getPose` / `getParent` / `getChildren`). Known exclusions from the op
1920
- * factory roster surfaces the scene doesn't own: `setSelection` (spread
1921
- * `SelectionApi.adapterMethods` over this adapter when selection-carrying
1922
- * ops must replay), `setPath`, and `setText`.
2181
+ * `getPose` / `getParent` / `getChildren` / `getSelection`) and
2182
+ * `setSelection`. Known exclusions from the op factory roster surfaces the
2183
+ * scene doesn't own: `setPath` and `setText`.
1923
2184
  *
1924
2185
  * Lives in `interactions/actions/` (not `canvas/sceneAdapter.ts`) so default
1925
2186
  * actions never import from `canvas/` — that back-edge would create an
@@ -1929,12 +2190,14 @@ declare function useStandardActions(opts: UseStandardActionsOptions): void;
1929
2190
 
1930
2191
  /** Build the adapter the default actions commit through when the consumer
1931
2192
  * supplies no `applyOps` hook of its own — it applies ops straight to the
1932
- * scene. Selection, path and text ops are not covered; those surfaces belong
1933
- * to their own owners. */
2193
+ * scene. Path and text ops are not covered; those surfaces belong to their
2194
+ * own owners. */
1934
2195
  declare function defaultCommitAdapter<TPose>(scene: Scene<unknown, string, TPose>): {
1935
2196
  getNode: (id: string) => Node$1<unknown, string, TPose> | undefined;
1936
2197
  getNodes: () => Node$1<unknown, string, TPose>[];
1937
2198
  getPose: (id: string) => TPose;
2199
+ getSelection: () => string[];
2200
+ setSelection: (ids: string[]) => void;
1938
2201
  getParent: (id: string) => string | null;
1939
2202
  getChildren: (parentId: string | null) => string[];
1940
2203
  setChildOrder: (parentId: string | null, orderedIds: string[]) => void;
@@ -1962,6 +2225,63 @@ declare function useResizePolicy<TPose>(options: UseResizePolicyOptions<TPose>):
1962
2225
  * run against the consumer's geometry. */
1963
2226
  declare function useSliceDep(dep: SliceDep): void;
1964
2227
 
2228
+ /**
2229
+ * Everything about handling an input event that depends on *which view* it
2230
+ * landed in: the dispatcher that runs it, and the three lookups that resolve
2231
+ * a client point against that view's camera.
2232
+ *
2233
+ * @internal One per view. The hook builds the `id: null` one from its flat
2234
+ * options; `views` supplies the rest, and the hook chooses between them per
2235
+ * event, so that it — and its listener set — still mounts exactly once.
2236
+ */
2237
+ interface DispatcherViewTarget {
2238
+ /** `null` is the root view — the whole canvas. Matches `ViewTarget.id`. */
2239
+ id: string | null;
2240
+ dispatcher: Dispatcher;
2241
+ affordanceAt: UseGestureDispatcherOptions['affordanceAt'];
2242
+ classifyTarget: UseGestureDispatcherOptions['classifyTarget'];
2243
+ clientToWorld: UseGestureDispatcherOptions['clientToWorld'];
2244
+ /**
2245
+ * The deps this view answers for itself — its camera, its selection, and
2246
+ * whatever else is per-view rather than per-canvas. Read per event; every
2247
+ * name it does not carry still resolves through the canvas registry.
2248
+ *
2249
+ * This is why a view does not get a `DepRegistryProvider` of its own. The
2250
+ * registry is where a consumer registers *sources*, and one per view would
2251
+ * fragment that: a consumer overriding `insert` would have to know how many
2252
+ * views exist and override each. An overlay keeps one place to register and
2253
+ * one authority per dep, with the view claiming only what is genuinely its.
2254
+ *
2255
+ * Without this a gesture inside a panel pans the whole canvas and selects
2256
+ * into the canvas's selection — correct coordinates alone do not make
2257
+ * routing correct.
2258
+ */
2259
+ deps?: () => Partial<DepSchema>;
2260
+ /**
2261
+ * This view's chrome-caps rule context, for the eligibility filter. The rule
2262
+ * table is the surface's, but a rule keyed on selection or the in-flight
2263
+ * action answers per view — so an action hidden in one panel must not
2264
+ * decline a gesture in another. `undefined` means "nothing gates here", the
2265
+ * same answer a surface with no mode registry gives.
2266
+ */
2267
+ getRuleCtx?: () => RuleCtx | undefined;
2268
+ }
2269
+ /**
2270
+ * The part of `features/viewports`' `ViewResolver` the dispatcher needs: which
2271
+ * view a client point belongs to, pinned to the view a gesture began in.
2272
+ *
2273
+ * @internal Structural on purpose — a `ViewResolver` satisfies it, and the
2274
+ * dispatcher stays free of the viewport module.
2275
+ */
2276
+ interface ViewIdResolver {
2277
+ begin(pointerId: number, clientX: number, clientY: number): {
2278
+ id: string | null;
2279
+ };
2280
+ at(pointerId: number | null, clientX: number, clientY: number): {
2281
+ id: string | null;
2282
+ };
2283
+ end(pointerId: number): void;
2284
+ }
1965
2285
  /** Options for `useGestureDispatcher`: the element to listen on, the actions
1966
2286
  * and tools in play, and the hooks that turn raw DOM events into the world
1967
2287
  * coordinates and hit targets bindings match against. */
@@ -2088,7 +2408,25 @@ interface UseGestureDispatcherOptions {
2088
2408
  * `Action.eligible` rule (omitted => always eligible). `<SceneCanvas>`
2089
2409
  * wires this; tests / harnesses without chrome-caps state can omit it.
2090
2410
  */
2091
- getRuleCtx?: () => RuleCtx;
2411
+ getRuleCtx?: () => RuleCtx | undefined;
2412
+ /**
2413
+ * Routing for a canvas hosting more than one view: the non-root dispatch
2414
+ * records to choose between, read fresh per event, and the resolver that
2415
+ * chooses. Omit for a single view — then every event runs on the record the
2416
+ * flat options above describe, which is what they are: view zero.
2417
+ *
2418
+ * A resolved id with no live record falls back to the root, so a view that
2419
+ * unmounts mid-gesture degrades instead of dropping the event.
2420
+ *
2421
+ * Keyboard and paste have no coordinates to resolve; they run on the view
2422
+ * the last coordinate-bearing event resolved to.
2423
+ *
2424
+ * @internal
2425
+ */
2426
+ views?: {
2427
+ targets: () => readonly DispatcherViewTarget[];
2428
+ resolver: ViewIdResolver;
2429
+ };
2092
2430
  }
2093
2431
  /**
2094
2432
  * Attach the input pipeline to a canvas: DOM pointer, wheel, keyboard and
@@ -2101,6 +2439,56 @@ interface UseGestureDispatcherOptions {
2101
2439
  */
2102
2440
  declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
2103
2441
 
2442
+ /**
2443
+ * The one place weasel decides whether a frame may run, so that "a loop does no
2444
+ * work nobody can see" holds by construction rather than by every loop author
2445
+ * remembering. Nothing runs while the document is hidden; a loop that names an
2446
+ * element also stops while that element is outside the viewport.
2447
+ *
2448
+ * A request made while suspended is held, not dropped, and re-armed when the
2449
+ * surface comes back — so a loop never needs to poll visibility itself.
2450
+ */
2451
+
2452
+ /** What a loop names as its element, resolved every time the gate is consulted
2453
+ * so a ref filled in after mount still starts the observer. */
2454
+ type VisibleRafTarget = RefObject<Element | null> | (() => Element | null);
2455
+ interface VisibleRafOptions {
2456
+ /** Also gate on this element's intersection with the viewport. Omit it and
2457
+ * the loop is gated on document visibility alone. */
2458
+ target?: VisibleRafTarget;
2459
+ /**
2460
+ * Run frames regardless of visibility. The escape hatch for a loop that is
2461
+ * not painting for a viewer — an offscreen recording, an export driving its
2462
+ * own frames. It is the wrong fix for a loop that stalls after a tab switch:
2463
+ * that is a missing `request()`, and setting this hides it.
2464
+ */
2465
+ dangerouslyRunWhenHidden?: boolean;
2466
+ /** Frame clock. Read live, so a test may inject one after mount. */
2467
+ requestFrame?: (cb: FrameRequestCallback) => number;
2468
+ cancelFrame?: (handle: number) => void;
2469
+ /**
2470
+ * Runs once when the loop leaves suspension, before the frame that follows
2471
+ * it. Where a loop that measures elapsed time drops the interval it spent
2472
+ * suspended — without it, an hour hidden arrives as one hour-long frame.
2473
+ */
2474
+ onResume?: () => void;
2475
+ }
2476
+ interface VisibleRaf {
2477
+ /** Ask for a frame. Idempotent while one is outstanding, and held rather than
2478
+ * dropped while suspended. Identity is stable for the component's lifetime. */
2479
+ request(): void;
2480
+ /** Drop the outstanding request, held or scheduled. */
2481
+ cancel(): void;
2482
+ /** Whether a frame would run right now. */
2483
+ isVisible(): boolean;
2484
+ }
2485
+ /**
2486
+ * @param frame Runs on the frame, with the timestamp the clock supplied.
2487
+ * A continuous loop calls `request()` again from inside it; a one-shot loop
2488
+ * does not.
2489
+ */
2490
+ declare function useVisibleRaf(frame: (time: number) => void, options?: VisibleRafOptions): VisibleRaf;
2491
+
2104
2492
  /**
2105
2493
  * Pure wheel-event reducer. Stateless sibling of `useZoom`'s
2106
2494
  * `onWheel` handler — useful in tests, in non-React reducers, or anywhere
@@ -2194,17 +2582,6 @@ declare function useDecayLoop(): {
2194
2582
  cancel: () => void;
2195
2583
  };
2196
2584
 
2197
- /** Animate the view from where it is to a target view — zoom-to-fit,
2198
- * zoom-to-selection, and anything else that should glide rather than jump. */
2199
- declare function useViewTween(setView: (v: View) => void): {
2200
- animateTo: (from: View, to: View, opts?: {
2201
- duration?: number;
2202
- easing?: (t: number) => number;
2203
- }) => void;
2204
- cancel: () => void;
2205
- isAnimating: React$1.RefObject<boolean>;
2206
- };
2207
-
2208
2585
  /**
2209
2586
  * Detects two-finger pinch on a canvas element.
2210
2587
  *
@@ -2218,31 +2595,11 @@ declare function usePinchGesture(canvasRef: React.RefObject<HTMLCanvasElement |
2218
2595
  y: number;
2219
2596
  }, scaleFactor: number) => void, enabled?: boolean): void;
2220
2597
 
2221
- /** Options accepted by {@link useViewAnimation}'s `animateToBounds`. */
2222
- interface AnimateToBoundsOptions extends FitViewToBoundsOptions {
2223
- /** Tween duration in ms (forwarded to `animateTo`). */
2224
- duration?: number;
2225
- /** Tween easing (forwarded to `animateTo`). */
2226
- easing?: (t: number) => number;
2227
- }
2228
2598
  /**
2229
- * Tween the viewport `View` between values. Wraps `useViewTween`'s `animateTo`
2230
- * + `cancel`, and adds an `animateToBounds` convenience that composes
2231
- * `fitViewToBounds` with the existing tween so consumers can say "zoom to this
2232
- * bounds with animation" in one call.
2233
- *
2234
- * `animateToBounds` needs the current `View` and the current viewport
2235
- * dimensions to compute the target — both are passed as arguments so this
2236
- * hook stays a leaf (no canvas-size subscription, no `View` storage).
2599
+ * `InterpolatorFactory<View>` for camera animation built once per animation,
2600
+ * called with eased `t` each frame. Pass to `Animator.tween`'s `interpolator`.
2237
2601
  */
2238
- declare function useViewAnimation(setView: (v: View) => void): {
2239
- animateTo: (from: View, to: View, opts?: {
2240
- duration?: number;
2241
- easing?: (t: number) => number;
2242
- }) => void;
2243
- animateToBounds: (bounds: Bounds, currentView: View, viewportDims: ViewportDims, opts?: AnimateToBoundsOptions) => void;
2244
- cancel: () => void;
2245
- };
2602
+ declare function interpolateView(from: View, to: View): (t: number) => View;
2246
2603
 
2247
2604
  /**
2248
2605
  * Build a `Tool<TScratch>` from a declarative `ToolDef<TScratch>`.
@@ -2358,7 +2715,7 @@ interface UseKeybindingsOptions {
2358
2715
  declare function useKeybindings(tools: ToolsApi, options?: UseKeybindingsOptions): void;
2359
2716
 
2360
2717
  /** The value types a built-in pref leaf can hold. */
2361
- type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint';
2718
+ type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint' | 'object';
2362
2719
  interface ToolPrefBase<K extends string, Value> {
2363
2720
  kind: K;
2364
2721
  /** Human-readable label. */
@@ -2373,6 +2730,12 @@ interface ToolPrefBase<K extends string, Value> {
2373
2730
  * (weasel-ui `PrefsForm` honors this for leaves whose control brings
2374
2731
  * its own chrome). */
2375
2732
  block?: boolean;
2733
+ /** Glyph naming this leaf in a host UI's icon set (weasel-ui resolves it
2734
+ * against `ICON_PATHS`). A plain string because core ships no icon set and
2735
+ * cannot depend on one. Read where a leaf's `name` has nowhere to go — a
2736
+ * `pair`ed row is labeled by the pair, so its fields have only the glyph
2737
+ * to tell them apart. */
2738
+ icon?: string;
2376
2739
  /** Row-pairing hint for compact property UIs (weasel-ui
2377
2740
  * `SelectionPanel`): leaves sharing a `pair` id render side-by-side
2378
2741
  * on one row labeled with the `pair` string (e.g. `'Position'` for
@@ -2386,7 +2749,7 @@ type ToolPrefBooleanControl = 'checkbox' | 'switch';
2386
2749
  /** How a schema-driven UI should present a string pref. */
2387
2750
  type ToolPrefStringControl = 'input' | 'textarea';
2388
2751
  /** How a schema-driven UI should present an enum pref. */
2389
- type ToolPrefEnumControl = 'select' | 'radio';
2752
+ type ToolPrefEnumControl = 'select' | 'radio' | 'toggle';
2390
2753
  /** Display-unit conversion for number leaves whose stored value uses a
2391
2754
  * canonical unit the user shouldn't see (e.g. radians stored, degrees
2392
2755
  * shown). The stored value stays canonical; UIs convert at the edge. */
@@ -2413,13 +2776,51 @@ interface ToolPrefBoolean extends ToolPrefBase<'boolean', boolean> {
2413
2776
  interface ToolPrefString extends ToolPrefBase<'string', string> {
2414
2777
  control?: ToolPrefStringControl;
2415
2778
  }
2779
+ /**
2780
+ * Stored-value bridge for an enum leaf whose value is not the option string —
2781
+ * the counterpart of {@link ToolPrefNumberUnit}, which does the same for a
2782
+ * number stored in a canonical unit.
2783
+ *
2784
+ * A dash array is the case that needs it: `Stroke.dash` stores lengths, and
2785
+ * the thing a person chooses is a style. The presets scale by the stroke's
2786
+ * width, so both directions are given the object's other fields — a style is
2787
+ * meaningless without the width it is a multiple of.
2788
+ */
2789
+ interface ToolPrefEnumEncoding<T extends string = string> {
2790
+ /**
2791
+ * The option `stored` reads as, or `undefined` for none — which a UI shows
2792
+ * the way it shows a mixed selection, by selecting nothing.
2793
+ *
2794
+ * `siblings` is the object the leaf is a field of, or `undefined` when the
2795
+ * node does not hold that object (and for a top-level leaf, which has none).
2796
+ */
2797
+ read: (stored: unknown, siblings: Record<string, unknown> | undefined) => T | undefined;
2798
+ /** What to store for `option`. `undefined` removes the field. */
2799
+ write: (option: T, siblings: Record<string, unknown> | undefined) => unknown;
2800
+ }
2416
2801
  /** A pref with a fixed set of labeled choices. */
2417
2802
  interface ToolPrefEnum<T extends string = string> extends ToolPrefBase<'enum', T> {
2803
+ /** `short` is the label a segmented control uses when a full one would not
2804
+ * fit — a capital or two. `icon` names a glyph in the host UI's set
2805
+ * (weasel-ui resolves it against `ICON_PATHS`) and outranks `short` where
2806
+ * it resolves. It is a plain string because core ships no icon set and
2807
+ * cannot depend on one. The full `label` stays the accessible name, so
2808
+ * neither the abbreviation nor the glyph becomes the only thing naming
2809
+ * the option.
2810
+ *
2811
+ * `disabled` marks an option a control reports but cannot author — the
2812
+ * value a stored form reads as when it matches nothing offered. Dropping it
2813
+ * from the list instead would leave the control selecting nothing and
2814
+ * claiming the field is unset. */
2418
2815
  options: readonly {
2419
2816
  value: T;
2420
2817
  label: string;
2818
+ short?: string;
2819
+ icon?: string;
2820
+ disabled?: boolean;
2421
2821
  }[];
2422
2822
  control?: ToolPrefEnumControl;
2823
+ encoding?: ToolPrefEnumEncoding<T>;
2423
2824
  }
2424
2825
  /** A single color, stored as a hex string. For a value that may also be a
2425
2826
  * gradient or a pattern, use {@link ToolPrefPaint} instead. */
@@ -2434,8 +2835,7 @@ interface ToolPrefColor extends ToolPrefBase<'color', string> {
2434
2835
  * app-supplied renderer. Deliberately NOT index-signatured so concrete
2435
2836
  * app interfaces stay assignable. Mirrors weasel-ui's `PrefCustom`.
2436
2837
  */
2437
- interface ToolPrefCustom extends ToolPrefBase<string, unknown> {
2438
- }
2838
+ type ToolPrefCustom = ToolPrefBase<string, unknown>;
2439
2839
  /**
2440
2840
  * A whole `FillStyle`, not a color inside one. Use it wherever the value is
2441
2841
  * the tagged paint union — a solid color, a pattern, a gradient — rather
@@ -2451,13 +2851,60 @@ interface ToolPrefPaint extends ToolPrefBase<'paint', unknown> {
2451
2851
  /** Offer an opacity control alongside the color. */
2452
2852
  alpha?: boolean;
2453
2853
  }
2854
+ /**
2855
+ * A leaf whose value is one object, with its own fields hanging off it.
2856
+ *
2857
+ * A compound value — a stroke, a shadow, a pattern spec — could be described
2858
+ * as several sibling leaves addressing into it (`data.stroke.width`,
2859
+ * `data.stroke.cap`). It shouldn't be: each control would then write one field
2860
+ * of a value it can only half see, and writing into something that is not an
2861
+ * object yet corrupts it. Here the fields are `children` of one leaf, and
2862
+ * every edit commits the parent object whole.
2863
+ *
2864
+ * `children` paths are relative to the object. They are ordinary leaves, so a
2865
+ * field that is itself a union (a stroke's `paint`) declares the kind that
2866
+ * edits that union. A child may also be a {@link ToolPrefGroup}, which
2867
+ * organises the fields under a heading without contributing to the path —
2868
+ * the same rule group keys follow at the top level. A `TextStyle` needs it:
2869
+ * its character and paragraph fields belong to one value but read as two
2870
+ * lists.
2871
+ */
2872
+ interface ToolPrefObject extends ToolPrefBase<'object', unknown> {
2873
+ children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
2874
+ /**
2875
+ * Lift a non-object value into the object form, for a consumer field that
2876
+ * may also be held as a scalar. Called before a child edit is applied;
2877
+ * without it a scalar-valued leaf shows its children empty and refuses the
2878
+ * edit.
2879
+ */
2880
+ fromScalar?: (value: unknown) => Record<string, unknown>;
2881
+ }
2454
2882
  /** One built-in pref leaf. `ToolPrefLeaf` widens this to include
2455
2883
  * app-defined kinds. */
2456
- type ToolPref = ToolPrefNumber | ToolPrefBoolean | ToolPrefString | ToolPrefEnum | ToolPrefColor | ToolPrefPaint;
2884
+ type ToolPref = ToolPrefNumber | ToolPrefBoolean | ToolPrefString | ToolPrefEnum | ToolPrefColor | ToolPrefPaint | ToolPrefObject;
2885
+ /**
2886
+ * The built-in kinds, as a table. A kind added to {@link ToolPrefKind} is a
2887
+ * compile error here, and from here it is one in every renderer's `never`
2888
+ * guard — the only thing standing between a new kind and rendering as
2889
+ * nothing in four places at once.
2890
+ */
2891
+ declare const TOOL_PREF_KINDS: Record<ToolPrefKind, true>;
2892
+ /**
2893
+ * Narrows a leaf to the built-in union, so a renderer's switch discriminates
2894
+ * on {@link ToolPrefKind} instead of the open `string` that
2895
+ * {@link ToolPrefCustom} widens `kind` to. An app-defined kind answers false
2896
+ * and belongs to the renderer's custom-renderer path.
2897
+ */
2898
+ declare function isBuiltinToolPref(leaf: ToolPrefLeaf): leaf is ToolPref;
2457
2899
  /** Built-in or app-defined leaf. */
2458
2900
  type ToolPrefLeaf = ToolPref | ToolPrefCustom;
2459
2901
  /** Nestable group: branch nodes a tool can use to organize its prefs. */
2460
2902
  interface ToolPrefGroup {
2903
+ /** Heading for the group's rows. **Empty means no heading** — for a group
2904
+ * that exists to organise, not to name: one whose children are themselves
2905
+ * groups carrying the labels a reader needs. Give it a name whenever the
2906
+ * name is the referent (a `Border` group over `Top` / `Right` / `Bottom`
2907
+ * reads as nothing without it). */
2461
2908
  name: string;
2462
2909
  description?: string;
2463
2910
  children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
@@ -2558,8 +3005,8 @@ declare function useSelectTool<TNode extends {
2558
3005
  id: string;
2559
3006
  }, TPose>(adapter: SelectAdapter<TNode, TPose>, options: UseSelectToolOptions<TPose>): Tool<null>;
2560
3007
 
2561
- /** Options for `useRotateTool` — mostly the placement and hit size of the
2562
- * rotation handle. */
3008
+ /** Options for `useRotateTool` — mostly the placement of the rotation
3009
+ * handle's hover band. */
2563
3010
  interface UseRotateToolOptions<TNode extends {
2564
3011
  id: string;
2565
3012
  }, _TPose> {
@@ -2569,10 +3016,10 @@ interface UseRotateToolOptions<TNode extends {
2569
3016
  * backwards-compat with consumers (notably `SceneCanvas`'s
2570
3017
  * `rotateOptions`) that still pass a value. */
2571
3018
  rotate?: unknown;
2572
- /** Distance from top edge of bounds to rotation handle center. Default: 24. */
3019
+ /** Minimum thickness of the rotate band outside the selection AABB, in
3020
+ * screen px. Defaults to {@link ROTATION_HANDLE_BASE_PX}; `<SceneCanvas>`
3021
+ * passes the device-scaled value. */
2573
3022
  rotationHandleDistance?: number;
2574
- /** Square hit-radius for the rotation handle. Default: 8. */
2575
- handleHitRadius?: number;
2576
3023
  /** World-space bounds lookup. Required for the rotation affordance hit-test
2577
3024
  * in consumers that wire a `boundsOf` source separately from the
2578
3025
  * ChromeState-driven affordance pipeline. Retained for parity with
@@ -2706,12 +3153,29 @@ declare function useHandTool(opts?: UseHandToolOptions): Tool<HandScratch | null
2706
3153
  * factories and hit gating belong on those deps, not on the tool. */
2707
3154
  declare function useTextTool(): Tool<undefined>;
2708
3155
 
3156
+ /** The camera a pinch acts on, and the origin its anchor is measured from. */
3157
+ interface PinchZoomTarget {
3158
+ view: View;
3159
+ setView: (v: View) => void;
3160
+ /** Client-space origin of the surface this camera paints into. */
3161
+ origin: {
3162
+ left: number;
3163
+ top: number;
3164
+ };
3165
+ }
2709
3166
  /** Options for `usePinchZoomTool`: the zoom limits, and whether it is wired
2710
3167
  * at all. */
2711
3168
  interface PinchZoomToolOpts {
2712
3169
  min?: number;
2713
3170
  max?: number;
2714
3171
  enabled?: boolean;
3172
+ /**
3173
+ * Which camera the anchor point belongs to. Return `null` — or omit this —
3174
+ * for the canvas's own. A canvas hosting several views resolves the point
3175
+ * here, so a pinch inside a panel zooms the panel rather than the canvas
3176
+ * underneath it.
3177
+ */
3178
+ resolveTarget?: (clientX: number, clientY: number) => PinchZoomTarget | null;
2715
3179
  }
2716
3180
  /**
2717
3181
  * Two-finger pinch zoom on the canvas. Standalone hook (not a Tool record)
@@ -2719,8 +3183,12 @@ interface PinchZoomToolOpts {
2719
3183
  *
2720
3184
  * The anchor point under the gesture midpoint stays fixed on screen as the
2721
3185
  * view scales.
3186
+ *
3187
+ * @param getView Read at gesture time, not at render time: each pinch move
3188
+ * applies a per-frame delta to the view the previous move produced, and the
3189
+ * canvas does not re-render between them.
2722
3190
  */
2723
- declare function usePinchZoomTool(canvasRef: React.RefObject<HTMLCanvasElement | null>, view: View, setView: (v: View) => void, opts?: PinchZoomToolOpts): void;
3191
+ declare function usePinchZoomTool(canvasRef: React.RefObject<HTMLCanvasElement | null>, getView: () => View, setView: (v: View) => void, opts?: PinchZoomToolOpts): void;
2724
3192
 
2725
3193
  /**
2726
3194
  * In-progress pen anchor. `outHandle` is set when the anchor was placed via
@@ -3132,6 +3600,35 @@ interface CanvasExtensionApi {
3132
3600
  * (Replaces the pre-A2 pattern where `ref.current` directly *was* the element.) */
3133
3601
  readonly element: HTMLCanvasElement | null;
3134
3602
  requestRedraw(): void;
3603
+ /**
3604
+ * Run `fn` after every paint, on the frame that painted — for chrome that
3605
+ * must observe landed pixels (a loupe readback, a frame counter). Returns
3606
+ * an unsubscribe.
3607
+ */
3608
+ subscribeFrame(fn: () => void): () => void;
3609
+ /** The current view. Readable mid-frame — this is the value the next paint
3610
+ * will use, not a value from the last React commit. */
3611
+ getView(): View;
3612
+ /**
3613
+ * Set the view without a React render: the ref updates now and the next
3614
+ * frame paints with it.
3615
+ *
3616
+ * Not applied locally when the canvas is controlled by a `view` prop — there
3617
+ * the prop is the authority and a local write would only desynchronize
3618
+ * pixels from props. The value still goes out through `onViewChange`, so a
3619
+ * controlled owner can honor it; without an `onViewChange` it is dropped.
3620
+ * Either way the canvas warns once per mount.
3621
+ */
3622
+ setView(next: View | ((current: View) => View)): void;
3623
+ /** Called after each view change, for chrome that mirrors the camera — a
3624
+ * zoom readout, a minimap, DOM pinned to world coordinates. Returns an
3625
+ * unsubscribe. */
3626
+ subscribeView(fn: (view: View) => void): () => void;
3627
+ /** The `contentVersion` the current pixels were painted from — on
3628
+ * `<SceneCanvas>`, the scene version. Chrome in lockstep with canvas
3629
+ * content compares this against the version it is about to render and
3630
+ * defers a frame when they differ. `0` until the first paint lands. */
3631
+ getPaintedVersion(): number;
3135
3632
  /** Register an externally-owned RenderLayer. The layer participates in the
3136
3633
  * draw stack and, if it implements `hitTest`, in {@link hitTestExtras}. */
3137
3634
  registerLayer(layer: RenderLayer<unknown>): () => void;
@@ -3148,8 +3645,17 @@ interface CanvasExtensionApi {
3148
3645
  * `strength` carry through from the hit when the layer sets them.
3149
3646
  * A layer's owner binds a `kindOf` predicate on that kind to claim the
3150
3647
  * gesture; see `@weasel-js/hud` for the worked example.
3648
+ *
3649
+ * `frame` names the camera and surface size the point is expressed against.
3650
+ * Omit it for the canvas's own view — the only case until a caller routes
3651
+ * input to a viewport node, where the point is in that node's inner world
3652
+ * and a layer resolving screen-pixel tolerances needs its view and rect
3653
+ * size rather than the canvas's.
3151
3654
  */
3152
- hitTestExtras(worldX: number, worldY: number): {
3655
+ hitTestExtras(worldX: number, worldY: number, frame?: {
3656
+ view: View;
3657
+ dims: Dims;
3658
+ }): {
3153
3659
  layerId: string;
3154
3660
  hit: LayerHit;
3155
3661
  } | null;
@@ -3180,21 +3686,50 @@ interface SceneCanvasApi extends CanvasExtensionApi {
3180
3686
  x: number;
3181
3687
  y: number;
3182
3688
  }): void;
3689
+ /**
3690
+ * Glide the camera to `to` rather than jumping there — a fit-to-selection, a
3691
+ * recenter, a scripted tour. A thunk receives the pending target when an
3692
+ * animation is already in flight, so steps compound. Any other view write
3693
+ * cancels it.
3694
+ */
3695
+ animateView(to: View | ((base: View) => View), opts?: ViewAnimationOptions): void;
3696
+ /** Cancel a camera animation. The view stays where it is. */
3697
+ stopViewAnimation(): void;
3698
+ isViewAnimating(): boolean;
3183
3699
  }
3184
3700
 
3185
3701
  /**
3186
- * Union math behind `CanvasHelpers.getGestureBounds()`.
3702
+ * The seam `<Canvas>` reads in-flight gesture state through.
3187
3703
  *
3188
3704
  * `getGestureBounds` answers "where is the thing the user is dragging right
3189
3705
  * now, in total?" — a question every other lookup on `CanvasHelpers` can't
3190
3706
  * answer because they're keyed by node id, and a drag-to-insert has no node
3191
- * yet. Canvas collects the parts (preview bounds per in-flight id, plus any
3192
- * id-less nascent-insert AABBs) and folds them here.
3707
+ * yet. `<Canvas>` collects the parts named here and folds them with
3708
+ * `unionAABB` (`core/geometry/unionBounds`).
3193
3709
  *
3194
- * Kept free of tool / dispatcher types so it stays a pure geometry fold that
3195
- * both `<Canvas>` and its tests can call without a render.
3710
+ * Kept free of the dispatcher itself so `<Canvas>` stays dispatcher-agnostic:
3711
+ * it owns the `CanvasHelpers` contract, and the live gesture state lives in
3712
+ * the dispatcher. The overlay and preview shapes an action publishes are data,
3713
+ * and travel through here as such.
3196
3714
  */
3197
3715
 
3716
+ /**
3717
+ * A source of in-flight preview state — a tool from the tools registry, or an
3718
+ * `OngoingHandle` from the dispatcher's in-flight map. Readers merge several
3719
+ * with first-non-null semantics.
3720
+ *
3721
+ * @public
3722
+ */
3723
+ interface GesturePreviewSource {
3724
+ previewIds?(): Iterable<string> | null;
3725
+ previewPose?(id: string): unknown;
3726
+ /** Companion to `previewPose` for actions that mutate node data (anchor
3727
+ * edits on `data.path` nodes). Absent/null falls back to committed data. */
3728
+ previewData?(id: string): unknown;
3729
+ /** Subset of `previewIds` painted at full opacity rather than as a ghost —
3730
+ * a layout sibling reflowing to its destination is not in flight. */
3731
+ previewOpaqueIds?(): Iterable<string> | null;
3732
+ }
3198
3733
  /**
3199
3734
  * Everything `<Canvas>` needs to know about in-flight gestures that it can't
3200
3735
  * see for itself.
@@ -3226,58 +3761,391 @@ interface GestureSource {
3226
3761
  subscribe(fn: () => void): () => void;
3227
3762
  /** Monotonic counter bumped on the same events `subscribe` fires on. */
3228
3763
  getVersion(): number;
3764
+ /** The in-flight handles' preview surfaces, in dispatch order. What a ghost
3765
+ * layer paints from — read off the draw envelope so a layer drawn for one
3766
+ * view cannot report another's gesture. */
3767
+ previewSources(): readonly GesturePreviewSource[];
3768
+ /** The overlay shapes the in-flight handles publish — marquee, lasso, insert
3769
+ * preview, raw commands. */
3770
+ overlays(): readonly OngoingOverlay[];
3229
3771
  }
3230
3772
 
3231
3773
  /**
3232
- * Selection overlay primitives render layers for drawing selection
3233
- * outlines and corner resize handles in world space.
3234
- *
3235
- * Three pieces:
3236
- * - `composeSelectionPose` resolves the live pose for a selected id by
3237
- * consulting the move overlay first, then the resize overlay, then the
3238
- * stored pose. When `getChildren`/`isContainer` are supplied and the id
3239
- * resolves to a container, the returned pose is the union AABB of all
3240
- * transitive leaf poses (with the same precedence rules applied per leaf).
3241
- * - `createSelectionOutlineLayer` draws the outline rect for each selected
3242
- * id (container ids resolve to a union AABB via `getChildren`/`isContainer`).
3243
- * - `createSelectionHandlesLayer` draws resize-handle rects (default 4
3244
- * corners) for each selected id, with the same container-resolution rules.
3245
- *
3246
- * `createSelectionOverlayLayer` is a thin convenience that returns a single
3247
- * `RenderLayer` whose draw runs the outline pass then the handles pass.
3248
- *
3249
- * **Pose shape:** TPose is generic; callers must supply `getBounds(pose)`
3250
- * to project any pose into the AABB the renderer needs. For rect-shaped
3251
- * poses (`{x, y, width, height}`) pass the identity. For `Path` poses pass
3252
- * `boundsOfPath`. Container ids reduce via `unionBounds` over the projected
3253
- * AABBs.
3774
+ * The half of {@link CanvasHelpers} that belongs to one view — everything
3775
+ * answered by a camera's own tools, gestures and selection. A canvas hosting
3776
+ * several viewports needs one of these per view; the surface half is shared.
3254
3777
  */
3255
-
3256
- /** Options for `composeSelectionPose`. */
3257
- interface ComposeSelectionPoseOpts<TPose> {
3258
- /** Move overlay; when present its `poses` map wins over everything else. */
3259
- moveOverlay?: {
3260
- poses: Map<string, TPose>;
3261
- } | null;
3262
- /**
3263
- * Resize overlay; consulted only when move overlay does not own the id.
3264
- * For container resize, `leafPoses` (when present) maps each leaf id under
3265
- * the container to its overlay pose. If absent the container falls back to
3266
- * stored leaf poses (defensive — container-resize integration is in flight).
3267
- */
3268
- resizeOverlay?: {
3269
- id: string;
3270
- currentPose: TPose;
3271
- leafPoses?: Map<string, TPose>;
3272
- } | null;
3273
- /** Fallback pose lookup (typically the stored/committed pose). */
3274
- getStoredPose: (id: string) => TPose;
3778
+ interface CanvasViewHelpers<TPose> {
3779
+ /** Pose currently displayed for `id` — drag/resize/rotate overlay if active,
3780
+ * otherwise the committed pose from the adapter. Returns `null` if the id
3781
+ * isn't known. */
3782
+ getEffectivePose(id: string): TPose | null;
3783
+ /** Overlay-aware bounds for `id`. */
3784
+ getEffectiveBounds(id: string): Bounds | null;
3275
3785
  /**
3276
- * Project a pose into its AABB. Used when reducing a group of leaf poses
3277
- * into a single union AABB. Defaults to the identity rect-shaped poses
3278
- * (`{x, y, width, height}`) need no override. For `Path` poses pass
3279
- * `boundsOfPath`.
3280
- */
3786
+ * World-space AABB of everything the in-flight gesture proposes the
3787
+ * displaced poses of nodes being moved / resized / rotated / cloned, plus
3788
+ * any nascent insert that has no scene node yet. `null` when no gesture is
3789
+ * in flight.
3790
+ *
3791
+ * This reports the *gesture*, not the document: committed content the
3792
+ * gesture isn't touching is excluded, so a consumer that wants the union
3793
+ * with the rest of the scene still walks its own ids through
3794
+ * `getEffectiveBounds`. It exists because every other lookup here is keyed
3795
+ * by node id, which can't answer "where is the shape the user is drawing
3796
+ * right now" — a drag-to-insert has no id until pointer-up.
3797
+ *
3798
+ * Select-only gestures are deliberately excluded: a marquee or lasso has
3799
+ * geometry but proposes no content, and a consumer sizing itself to the
3800
+ * gesture must not grow because the user swept a selection rectangle.
3801
+ *
3802
+ * The result is a plain AABB — never rotated. Rotated parts are folded in
3803
+ * by their rotated extent (a union of several oriented boxes has no single
3804
+ * orientation to report).
3805
+ */
3806
+ getGestureBounds(): Bounds | null;
3807
+ /**
3808
+ * Subscribe to the gesture layer's change signal — the other half of the
3809
+ * `useSyncExternalStore` contract for everything on this object that moves
3810
+ * during a drag (`getEffectivePose`, `getEffectiveBounds`,
3811
+ * `getGestureBounds`). Returns an unsubscribe.
3812
+ *
3813
+ * Fires once per dispatcher pump: gesture start, every pointermove that
3814
+ * reaches an in-flight handle, end, and cancel — plus UI-driven ongoing
3815
+ * actions (a slider bound to an ongoing action pumps the same way). It
3816
+ * fires on the pump, not on a diff: a pump that changed nothing observable
3817
+ * still notifies, so don't hang expensive work directly off the callback.
3818
+ *
3819
+ * It does **not** cover committed scene edits (subscribe to the scene for
3820
+ * those) or previews a consumer's own tool publishes from React state
3821
+ * (that tool re-renders on its own).
3822
+ *
3823
+ * Without a gesture source wired — a bare `<Canvas>` — this is a no-op
3824
+ * subscription that never fires.
3825
+ */
3826
+ subscribeGestures(fn: () => void): () => void;
3827
+ /**
3828
+ * Monotonic counter bumped on exactly the events `subscribeGestures` fires
3829
+ * on. Pair the two for `useSyncExternalStore`:
3830
+ *
3831
+ * ```ts
3832
+ * const gestureVersion = useSyncExternalStore(
3833
+ * useCallback((cb) => helpersRef.current?.subscribeGestures(cb) ?? (() => {}), []),
3834
+ * () => helpersRef.current?.getGestureVersion() ?? 0,
3835
+ * );
3836
+ * ```
3837
+ *
3838
+ * Starts at 0 and only increases. `0` is also what a bare `<Canvas>` with
3839
+ * no gesture source reports, forever.
3840
+ */
3841
+ getGestureVersion(): number;
3842
+ /** Returns the live ChromeState built once per render. Affordances and
3843
+ * custom layers that need overlay-aware selection state (selection ids,
3844
+ * bounds, multi-union AABB, modifier flags) read from this. */
3845
+ getChromeState(): ChromeState;
3846
+ /**
3847
+ * Everything publishing an in-flight preview for this view, in resolution
3848
+ * order: tool-side first (hotkey → active → registry → ambient), then the
3849
+ * handles in flight on this view's dispatcher. Readers take the first
3850
+ * non-null answer per id.
3851
+ *
3852
+ * A ghost layer must read this off its draw envelope rather than close over
3853
+ * a dispatcher: one layer array paints every view, so a closure ghosts view
3854
+ * zero's drag into every panel.
3855
+ */
3856
+ getPreviewSources(): readonly GesturePreviewSource[];
3857
+ /** Overlay shapes this view's in-flight handles publish — marquee, lasso,
3858
+ * insert preview, raw commands. Same envelope rule as
3859
+ * {@link CanvasViewHelpers.getPreviewSources}. */
3860
+ getGestureOverlays(): readonly OngoingOverlay[];
3861
+ /**
3862
+ * Chrome-caps visibility predicate, keyed by chrome id. Overlay layers and
3863
+ * affordances call it per element to decide whether to draw / hit-test.
3864
+ *
3865
+ * The rule table is the surface's; the context it resolves against is this
3866
+ * view's selection, camera and in-flight action — so the predicate belongs
3867
+ * here rather than on {@link CanvasSurfaceHelpers}. Unwired, it is the
3868
+ * universal `() => true`.
3869
+ */
3870
+ getIsVisible(): (id: string) => boolean;
3871
+ }
3872
+ /**
3873
+ * The half of {@link CanvasHelpers} that belongs to the surface — one GL
3874
+ * context, one debug sink, however many views are drawn on it.
3875
+ */
3876
+ interface CanvasSurfaceHelpers {
3877
+ /** Active debug sink, when `<Canvas debug=...>` is enabled. Layers that
3878
+ * want to participate in `hitboxes`/`bounds`/etc. visualization can
3879
+ * call into this from their `draw` callback. Returns `null` when
3880
+ * debug is off — no-op for production renders. */
3881
+ getDebug(): DebugSink | null;
3882
+ }
3883
+ /** Live overlay-aware lookups exposed to custom layers via `helpersRef`.
3884
+ * What a layer receives as its `data` argument, unchanged: the two halves
3885
+ * are split so a per-view set can be built independently of the surface's,
3886
+ * not to make layers ask for one. */
3887
+ interface CanvasHelpers<TPose> extends CanvasViewHelpers<TPose>, CanvasSurfaceHelpers {
3888
+ }
3889
+
3890
+ /**
3891
+ * NodeShape — the **shape trait's** registry. Each trait of a node
3892
+ * (shape, routing, label, icon, affordances, …) is its own registry;
3893
+ * this one holds the per-kind `paint` + `silhouette` (and future
3894
+ * fields) used by `defaultDrawOne`, clipping, non-rect hit-testing,
3895
+ * and lasso/area-select.
3896
+ *
3897
+ * Teaching the kit about a new kind of shape goes through this registry
3898
+ * rather than by overriding `drawOne`. Overrides are still possible but
3899
+ * shouldn't be the default seam: most consumers want the same dispatch
3900
+ * logic, just extended with their own shape kinds (images, custom paths,
3901
+ * SVG fragments, etc.).
3902
+ *
3903
+ * Built-in entries (`kit:text`, `kit:path`, `kit:rect-fallback`) are
3904
+ * registered at module load. Consumer entries added via
3905
+ * `registerNodeShape` join the chain; the first entry whose
3906
+ * `matches` predicate returns true paints the node.
3907
+ *
3908
+ * Two priority tiers:
3909
+ * - `'high'` — checked before all `'normal'` entries. Use this to
3910
+ * override a kit built-in for a specific data shape (e.g. a custom
3911
+ * text renderer that wins over `kit:text`).
3912
+ * - `'normal'` (default) — appended after the built-ins.
3913
+ *
3914
+ * Within a tier, entries run in registration order. Each
3915
+ * `registerNodeShape` call returns a disposer that removes the
3916
+ * entry — useful for tests, for plugin lifecycles, and for swapping
3917
+ * implementations at runtime.
3918
+ *
3919
+ * See `docs/superpowers/specs/2026-05-24-node-traits-reframe-design.md`
3920
+ * for the trait taxonomy.
3921
+ */
3922
+
3923
+ /** Optional per-call paint context, threaded through `defaultDrawOne`'s third
3924
+ * argument. Lets a rendering entry point override ambient environment reads
3925
+ * — the headless `renderSceneToPixels` path supplies its own bitmap resolver
3926
+ * here so consumers reuse their own decode caches. Custom painters may
3927
+ * ignore it entirely. */
3928
+ interface NodePaintCtx {
3929
+ /** Override bitmap resolution for image nodes. When set it is authoritative:
3930
+ * the global `imageCache` is not consulted, and an `undefined` result
3931
+ * paints the deterministic grey placeholder outline (never the ambient
3932
+ * load-status error variant). */
3933
+ resolveImage?: (node: Node$1<unknown, string, unknown>) => ImageBitmap | undefined;
3934
+ /** The node's derived path, resolved by the scene-aware `drawOne` wrapper
3935
+ * before painting: `paint` has no scene handle, and deriving needs the
3936
+ * dependencies' poses. **Absent** for a node that derives from nothing —
3937
+ * the wrapper hands such a node the caller's own ctx untouched. `null`
3938
+ * means the node derives but has nothing to draw. */
3939
+ derivedPath?: Path | null;
3940
+ }
3941
+ /**
3942
+ * Per-node draw function — the scene-slot `drawOne` signature shared by
3943
+ * `<SceneCanvas>`, `renderSceneToCanvas` and `renderSceneToPixels`, so a
3944
+ * consumer can reuse one callback across a main canvas and a detached view.
3945
+ *
3946
+ * Called once per node in the walk. Returned commands are world-space; the
3947
+ * caller applies the view transform at the group level. `ctx` is supplied by
3948
+ * the scene-aware wrappers — see {@link NodePaintCtx}.
3949
+ */
3950
+ type SceneViewDrawOne<TData, TLayer extends string, TPose> = (node: Node$1<TData, TLayer, TPose>, pose: TPose, view: View, ctx?: NodePaintCtx) => DrawCommand[];
3951
+ /** A painter for one kind of node: which nodes it claims, and the draw
3952
+ * commands it emits for them. Registering one is how a consumer teaches the
3953
+ * default renderer to draw its own node kinds. */
3954
+ interface NodeShapeEntry<TData = unknown, TPose = unknown> {
3955
+ /** Stable identifier — used for unregistration and debugging. Pick
3956
+ * something descriptive: `'kit:text'`, `'app:image'`, etc. */
3957
+ id: string;
3958
+ /** Returns true when this painter renders the node. The first matching
3959
+ * painter (`'high'` tier first, then `'normal'`) wins. */
3960
+ matches(node: Node$1<TData, string, TPose>): boolean;
3961
+ /** Emits the draw commands for the node's primary visual. `ctx` is an
3962
+ * optional per-call paint context (see `NodePaintCtx`); painters that
3963
+ * don't need it can keep a two-argument signature.
3964
+ *
3965
+ * **The returned array belongs to the painter.** Callers must treat it as
3966
+ * immutable and copy before appending — a painter is free to memoize its
3967
+ * command list (`kit:shape` and `kit:path` do), and an in-place `push`
3968
+ * would grow that list on every frame. `defaultDrawOne` copies before
3969
+ * adding its label overlay for exactly this reason. */
3970
+ paint(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodePaintCtx): DrawCommand[];
3971
+ /** Optional: derive the node's silhouette path from its pose.
3972
+ * Used by clipping (when the container has no explicit
3973
+ * `clipFromPose`), by non-rect hit-testing, and by lasso/area-select.
3974
+ * Painters whose visual has no meaningful closed silhouette (e.g. text)
3975
+ * leave this undefined. */
3976
+ silhouette?(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodeSilhouetteCtx): Path | null;
3977
+ /** Optional: how the silhouette is inked — whether the interior is filled,
3978
+ * and how wide the outline is. Read by picking, so that an unfilled shape
3979
+ * is grabbable by its outline rather than by its empty middle.
3980
+ *
3981
+ * This is declared separately from `paint` rather than read back off the
3982
+ * emitted draw commands because picking runs on every pointer move, and
3983
+ * `paint` is allowed to be expensive (`kit:text` lays out glyphs). Keep it
3984
+ * to cheap field reads.
3985
+ *
3986
+ * Painters that leave it undefined are treated as {@link DEFAULT_INK} —
3987
+ * the pre-`ink` behavior, where the whole silhouette interior is grabbable
3988
+ * and the outline adds nothing.
3989
+ *
3990
+ * `ctx.scale` carries the view scale so a `{ px }` stroke width resolves to
3991
+ * world units; without it a screen-pixel width is read as world units. */
3992
+ ink?(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodeInkCtx): NodeInk | null;
3993
+ }
3994
+ /** How a painter inks its silhouette. See {@link NodeShapeEntry.ink}.
3995
+ *
3996
+ * Reach is per-side because `align` decides which side the ribbon lands on:
3997
+ * a centered stroke straddles the outline, `'inner'` puts nothing outside it,
3998
+ * `'outer'` nothing inside. */
3999
+ interface NodeInk {
4000
+ /** True when the silhouette's interior is painted, and so grabbable. */
4001
+ filled: boolean;
4002
+ /** How far the ink reaches outside the outline, world units. */
4003
+ outset: number;
4004
+ /** How far it reaches inside. */
4005
+ inset: number;
4006
+ }
4007
+ /** Per-call context for {@link NodeShapeEntry.silhouette}. */
4008
+ interface NodeSilhouetteCtx {
4009
+ /** The node's derived path, resolved by the caller — a painter has no scene
4010
+ * handle and deriving needs the dependencies' poses. Same convention as
4011
+ * {@link NodePaintCtx.derivedPath}: absent for a node that derives from
4012
+ * nothing, `null` when it derives but has nothing to draw. */
4013
+ derivedPath?: Path | null;
4014
+ }
4015
+ /** Per-call context for {@link NodeShapeEntry.ink}. */
4016
+ interface NodeInkCtx {
4017
+ /** View scale, for resolving `{ px }` stroke widths to world units. */
4018
+ scale?: number;
4019
+ }
4020
+ /** Options for `registerNodeShape`. */
4021
+ interface RegisterNodeShapeOptions {
4022
+ /** `'high'` puts the painter ahead of all normally-registered ones (so
4023
+ * it can win over a kit built-in). `'normal'` appends at the end. */
4024
+ priority?: 'high' | 'normal';
4025
+ }
4026
+ /** Register a shape painter. Returns a disposer that removes it. */
4027
+ declare function registerNodeShape<TData, TPose>(painter: NodeShapeEntry<TData, TPose>, opts?: RegisterNodeShapeOptions): () => void;
4028
+ /** Find the painter that will render `node` — first match in priority
4029
+ * order. Returns undefined if no painter (including the built-in
4030
+ * fallback) accepts the node. */
4031
+ declare function findNodeShape<TData, TPose>(node: Node$1<TData, string, TPose>): NodeShapeEntry<TData, TPose> | undefined;
4032
+ /** Find the painter for `node` and ask it for the node's silhouette path,
4033
+ * in **world** coords. Returns null if no painter matches, or the matching
4034
+ * painter has no `silhouette` method, or the method returns null. Used by
4035
+ * clipping, generic non-rect hit-testing and lasso/area-select — anywhere
4036
+ * the kit needs the "closed boundary" of whatever this kind of node draws as.
4037
+ *
4038
+ * Painters return their silhouette in the pose's local (unrotated) frame;
4039
+ * this bakes `pose.rotation` on top via the shared rotation convention, so
4040
+ * clips/area-select of a rotated node use the rotated boundary the renderer
4041
+ * draws. (`paint()` is unaffected — it applies rotation via the render wrap,
4042
+ * not the silhouette, so there is no double-rotation.) */
4043
+ declare function findShapeSilhouette<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodeSilhouetteCtx): Path | null;
4044
+ /** Find the painter for `node` and ask how it inks its silhouette. Returns
4045
+ * the painter's declared {@link NodeInk}, or `null` when no painter matches
4046
+ * or it declares none — callers substitute {@link DEFAULT_INK}. */
4047
+ declare function findShapeInk<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodeInkCtx): NodeInk | null;
4048
+ /** Options for {@link shapeCoversPoint}. */
4049
+ interface ShapeCoversPointOptions {
4050
+ /** Extra grab distance around the outline, in **world** units. Callers
4051
+ * derive it from a screen-pixel slop and the view scale, the same way
4052
+ * affordance hit radii work.
4053
+ *
4054
+ * Without slop a hairline is a mathematically zero-width target: the
4055
+ * stroke of a 1px outline is half a world unit wide at scale 1, which no
4056
+ * one can hit. Defaults to `0` so a caller that hasn't thought about the
4057
+ * view still gets exact geometry rather than a wrong guess. */
4058
+ tolerance?: number;
4059
+ /** View scale, passed to the painter's `ink` so a `{ px }` stroke width
4060
+ * resolves to world units. Defaults to 1. */
4061
+ scale?: number;
4062
+ /** The node's derived path — see {@link NodeSilhouetteCtx.derivedPath}.
4063
+ * Without it a derived node reports no silhouette and this answers `true`
4064
+ * everywhere, which degrades picking to the caller's AABB: for an edge,
4065
+ * a zero-sized pose. */
4066
+ derivedPath?: Path | null;
4067
+ }
4068
+ /**
4069
+ * Does the shape `node` actually paints cover the world point?
4070
+ *
4071
+ * The pose rect says a node covers its whole bounding box. That is wrong for
4072
+ * everything that is not a rectangle: the concave notch of a star, the corner
4073
+ * outside an ellipse, the blank right half of a text box. This asks the
4074
+ * painter's silhouette instead, which is the same boundary used for clipping
4075
+ * and area-select, so "what you can click" and "what is drawn" answer
4076
+ * together.
4077
+ *
4078
+ * "What is drawn" includes the *ink*, not just the boundary. A shape whose
4079
+ * interior isn't filled — an outlined rect, a pencil stroke, a bare line — is
4080
+ * grabbable along its outline and not through its empty middle, which is the
4081
+ * opposite of what a fill test alone answers. The outline's grab width is the
4082
+ * stroke's half-width plus `tolerance`.
4083
+ *
4084
+ * A painter with no `silhouette`, or one that returns `null` for this node
4085
+ * (`kit:text` does, for a node with no non-blank lines), reports `true` —
4086
+ * "no opinion", leaving the caller's own AABB test as the answer. Callers
4087
+ * should keep that AABB test as a cheap pre-filter; this is the refinement,
4088
+ * not a replacement.
4089
+ *
4090
+ * Rotation is already baked by `findShapeSilhouette`, so the point is in
4091
+ * plain world coordinates.
4092
+ */
4093
+ declare function shapeCoversPoint<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, x: number, y: number, opts?: ShapeCoversPointOptions): boolean;
4094
+ /** Snapshot of the current painters in evaluation order — `'high'` tier
4095
+ * first, then `'normal'`. Useful for debugging which painter handles a
4096
+ * given node. */
4097
+ declare function getNodeShapes(): readonly NodeShapeEntry[];
4098
+
4099
+ /**
4100
+ * Selection overlay primitives — render layers for drawing selection
4101
+ * outlines and corner resize handles in world space.
4102
+ *
4103
+ * Three pieces:
4104
+ * - `composeSelectionPose` resolves the live pose for a selected id by
4105
+ * consulting the move overlay first, then the resize overlay, then the
4106
+ * stored pose. When `getChildren`/`isContainer` are supplied and the id
4107
+ * resolves to a container, the returned pose is the union AABB of all
4108
+ * transitive leaf poses (with the same precedence rules applied per leaf).
4109
+ * - `createSelectionOutlineLayer` draws the outline rect for each selected
4110
+ * id (container ids resolve to a union AABB via `getChildren`/`isContainer`).
4111
+ * - `createSelectionHandlesLayer` draws resize-handle rects (default 4
4112
+ * corners) for each selected id, with the same container-resolution rules.
4113
+ *
4114
+ * `createSelectionOverlayLayer` is a thin convenience that returns a single
4115
+ * `RenderLayer` whose draw runs the outline pass then the handles pass.
4116
+ *
4117
+ * **Pose shape:** TPose is generic; callers must supply `getBounds(pose)`
4118
+ * to project any pose into the AABB the renderer needs. For rect-shaped
4119
+ * poses (`{x, y, width, height}`) pass the identity. For `Path` poses pass
4120
+ * `boundsOfPath`. Container ids reduce via `unionAABB` over the projected
4121
+ * AABBs.
4122
+ */
4123
+
4124
+ /** Options for `composeSelectionPose`. */
4125
+ interface ComposeSelectionPoseOpts<TPose> {
4126
+ /** Move overlay; when present its `poses` map wins over everything else. */
4127
+ moveOverlay?: {
4128
+ poses: Map<string, TPose>;
4129
+ } | null;
4130
+ /**
4131
+ * Resize overlay; consulted only when move overlay does not own the id.
4132
+ * For container resize, `leafPoses` (when present) maps each leaf id under
4133
+ * the container to its overlay pose. If absent the container falls back to
4134
+ * stored leaf poses (defensive — container-resize integration is in flight).
4135
+ */
4136
+ resizeOverlay?: {
4137
+ id: string;
4138
+ currentPose: TPose;
4139
+ leafPoses?: Map<string, TPose>;
4140
+ } | null;
4141
+ /** Fallback pose lookup (typically the stored/committed pose). */
4142
+ getStoredPose: (id: string) => TPose;
4143
+ /**
4144
+ * Project a pose into its AABB. Used when reducing a group of leaf poses
4145
+ * into a single union AABB. Defaults to the identity — rect-shaped poses
4146
+ * (`{x, y, width, height}`) need no override. For `Path` poses pass
4147
+ * `boundsOfPath`.
4148
+ */
3281
4149
  getBounds?: (pose: TPose) => Bounds;
3282
4150
  /**
3283
4151
  * Wrap an AABB back into a TPose. Called only when the resolver collapses
@@ -3300,15 +4168,10 @@ interface ComposeSelectionPoseOpts<TPose> {
3300
4168
  * precedence rules). Containers with no leaves resolve to `null`.
3301
4169
  */
3302
4170
  declare function composeSelectionPose<TPose>(opts: ComposeSelectionPoseOpts<TPose>): (id: string) => TPose | null;
3303
- /** Shared options between outline and handles layers. */
4171
+ /** Options every selection layer shares. `getSelection` and `getPose` are
4172
+ * declared on {@link SelectionOverlayLayerOpts}, which makes both optional —
4173
+ * omitted, they come off the draw envelope. */
3304
4174
  interface SelectionLayerCommon<TPose> {
3305
- getSelection: () => readonly NodeId[];
3306
- /** Return null to skip rendering for an id (e.g. resolved pose unavailable).
3307
- * Takes `string` rather than `NodeId` because the container-aware bounds
3308
- * resolver internally walks expanded leaf ids via `getChildren`, which is
3309
- * generic over arbitrary string ids. NodeIds flow in fine — a NodeId is a
3310
- * string. */
3311
- getPose: (id: string) => TPose | null;
3312
4175
  /**
3313
4176
  * Project a pose into its AABB. Defaults to the identity — rect-shaped
3314
4177
  * poses (`{x, y, width, height}`) need no override. For `Path` poses pass
@@ -3322,46 +4185,38 @@ interface SelectionLayerCommon<TPose> {
3322
4185
  /** True when `id` is a structural container. */
3323
4186
  isContainer?: (id: string) => boolean;
3324
4187
  }
3325
- /** Options for `createSelectionOutlineLayer`. */
3326
- interface SelectionOutlineLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
3327
- /** Outline stroke style + outset distance from the pose rect. */
3328
- outline?: Stroke & {
3329
- pad?: number;
3330
- };
3331
- }
3332
- /** Options for `createSelectionHandlesLayer`. */
3333
- interface SelectionHandlesLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
3334
- /** Handle visuals. Omit for defaults. */
3335
- handles?: {
3336
- size?: number;
3337
- fill?: FillStyle;
3338
- outline?: Stroke;
3339
- };
3340
- /** Override handle placement. Default: 4 corners of the AABB. */
3341
- handlesOf?: (bounds: Bounds) => {
3342
- x: number;
3343
- y: number;
3344
- }[];
3345
- /** Render a rotation handle above the (rotated) top-center of the AABB.
3346
- * When `true`, uses default visuals + distance. When an object, override
3347
- * the world-space distance from the top edge. Defaults to `false` —
3348
- * consumers opt in only when wiring `useRotate`. */
3349
- rotationHandle?: boolean | {
3350
- /** World-pixel distance from the top edge to the handle center. */
3351
- distance?: number;
3352
- };
3353
- }
4188
+ /** Options for `createSelectionOutlineLayer`. The overlay layer's options
4189
+ * minus the handle visuals — the two run the same body. */
4190
+ type SelectionOutlineLayerOpts<TPose> = Omit<SelectionOverlayLayerOpts<TPose>, 'handles' | 'handlesOf' | 'rotationHandle'>;
4191
+ /** Options for `createSelectionHandlesLayer`. The overlay layer's options
4192
+ * minus the outline stroke — the two run the same body. */
4193
+ type SelectionHandlesLayerOpts<TPose> = Omit<SelectionOverlayLayerOpts<TPose>, 'outline'>;
3354
4194
  /** Options for `createSelectionOverlayLayer`. */
3355
4195
  interface SelectionOverlayLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
4196
+ /**
4197
+ * Which ids to draw chrome for. Omit to take them from the `ChromeState` on
4198
+ * the draw envelope, which is what makes one canvas's several views each
4199
+ * outline their own selection — the layer is shared, the envelope is not.
4200
+ *
4201
+ * A multi-selection resolves to the synthetic union id, with the real
4202
+ * members going to the outline pass, exactly as the explicit form does.
4203
+ */
4204
+ getSelection?: () => readonly NodeId[];
4205
+ /**
4206
+ * Resolve an id to the pose to draw chrome around. Return null to skip an
4207
+ * id. Omit to take bounds from the same envelope `getSelection` omitted
4208
+ * takes ids from — one cascade, the one the chrome state was built with,
4209
+ * rather than a second one here that has to agree with it.
4210
+ *
4211
+ * Takes `string` rather than `NodeId` because the container-aware resolver
4212
+ * walks expanded leaf ids via `getChildren`, which is generic over strings.
4213
+ */
4214
+ getPose?: (id: string) => TPose | null;
3356
4215
  outline?: Stroke & {
3357
4216
  pad?: number;
3358
4217
  };
3359
4218
  /** Pass `false` to render outlines only. */
3360
- handles?: {
3361
- size?: number;
3362
- fill?: FillStyle;
3363
- outline?: Stroke;
3364
- } | false;
4219
+ handles?: SelectionHandleStyle | false;
3365
4220
  handlesOf?: (bounds: Bounds) => {
3366
4221
  x: number;
3367
4222
  y: number;
@@ -3382,10 +4237,16 @@ interface SelectionOverlayLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
3382
4237
  * per-anchor chrome takes over. */
3383
4238
  getSuppressedIds?: () => ReadonlySet<string>;
3384
4239
  }
4240
+ /** Handle visuals, as a consumer supplies them. */
4241
+ interface SelectionHandleStyle {
4242
+ size?: number;
4243
+ fill?: FillStyle;
4244
+ outline?: Stroke;
4245
+ }
3385
4246
  /**
3386
4247
  * `RenderLayer` that draws selection outlines only. Stack alongside
3387
- * `createSelectionHandlesLayer` (or just use `createSelectionOverlayLayer`
3388
- * for the common case) when both passes are wanted.
4248
+ * `createSelectionHandlesLayer` (or use `createSelectionOverlayLayer`, which
4249
+ * runs both passes in one layer) when both are wanted.
3389
4250
  */
3390
4251
  declare function createSelectionOutlineLayer<TPose>(opts: SelectionOutlineLayerOpts<TPose>): RenderLayer<unknown>;
3391
4252
  /**
@@ -3394,10 +4255,9 @@ declare function createSelectionOutlineLayer<TPose>(opts: SelectionOutlineLayerO
3394
4255
  */
3395
4256
  declare function createSelectionHandlesLayer<TPose>(opts: SelectionHandlesLayerOpts<TPose>): RenderLayer<unknown>;
3396
4257
  /**
3397
- * Convenience wrapper that draws outlines then handles in a single layer.
3398
- * Equivalent to stacking `createSelectionOutlineLayer` and
3399
- * `createSelectionHandlesLayer` in a layer sequence. Pass `handles: false` to
3400
- * render outlines only.
4258
+ * Draws outlines then handles in a single layer. Exactly equivalent to
4259
+ * stacking `createSelectionOutlineLayer` and `createSelectionHandlesLayer` —
4260
+ * all three run the same body. Pass `handles: false` to render outlines only.
3401
4261
  */
3402
4262
  declare function createSelectionOverlayLayer<TPose>(opts: SelectionOverlayLayerOpts<TPose>): RenderLayer<unknown>;
3403
4263
 
@@ -3475,8 +4335,15 @@ interface SceneSlotConfig<TNode extends {
3475
4335
  objects?: TNode[];
3476
4336
  /** Project an object to its committed pose. Defaults to `adapter.getPose(obj.id)`. */
3477
4337
  toPose?: (obj: TNode) => TPose;
3478
- /** Draw a single object as a `DrawCommand` tree. */
3479
- drawOne: (obj: TNode, pose: TPose, view: View) => DrawCommand[];
4338
+ /** Draw a single object as a `DrawCommand` tree. `ctx` carries what only a
4339
+ * scene-aware caller can supply (a node's derived path); `<SceneCanvas>`
4340
+ * fills it in, bare `<Canvas>` never does. */
4341
+ drawOne: (obj: TNode, pose: TPose, view: View, ctx?: NodePaintCtx) => DrawCommand[];
4342
+ /** The path a container derives from its dependencies' poses, for the clip
4343
+ * it imposes on its subtree. The other half of what only a scene-aware
4344
+ * caller can supply — `drawOne`'s `ctx` carries the derived path a node
4345
+ * *paints*, this one the clip it *imposes*. */
4346
+ derivedPathOf?: (obj: TNode, pose: TPose) => Path | null;
3480
4347
  /** Default ghost alpha for the move-overlay slot. Default 0.85. */
3481
4348
  ghostAlpha?: number;
3482
4349
  /**
@@ -3575,6 +4442,20 @@ interface CanvasProps<TNode extends {
3575
4442
  * same contract the headless `renderSceneToPixels` path follows (that
3576
4443
  * path never reads ambient density at all). */
3577
4444
  dpr?: number;
4445
+ /** The version of whatever content this canvas draws, sampled at paint time
4446
+ * and reported by {@link CanvasExtensionApi.getPaintedVersion}.
4447
+ * `<SceneCanvas>` wires this to `scene.getVersion`. Chrome that must not
4448
+ * show DOM ahead of pixels compares the two and defers a frame. */
4449
+ contentVersion?: () => number;
4450
+ /** Paint inside the React commit rather than on the next animation frame.
4451
+ * Costs a synchronous paint per commit and per redraw request; buys
4452
+ * single-commit consistency between React-rendered DOM and canvas pixels.
4453
+ * For consumers with DOM chrome pinned to canvas content that cannot
4454
+ * tolerate a frame of skew. Live: toggling it switches modes from the next
4455
+ * redraw on. A redraw requested from inside a layer's `draw`, or from a
4456
+ * `subscribeFrame` callback, still waits for a frame — painting it in place
4457
+ * would recurse. */
4458
+ syncPaint?: boolean;
3578
4459
  /**
3579
4460
  * Combined adapter for scene-slot rendering, bounds computation, and
3580
4461
  * move/resize/rotate gesture math. Optional — bare-Canvas consumers that
@@ -3638,6 +4519,12 @@ interface CanvasProps<TNode extends {
3638
4519
  className?: string;
3639
4520
  style?: React__default.CSSProperties;
3640
4521
  tabIndex?: number;
4522
+ /** Fired when the canvas element gains focus. The canvas is focusable by
4523
+ * default (`tabIndex` 0), so consumers driving focus-dependent chrome —
4524
+ * a focus ring, a "keyboard shortcuts active" hint — need to observe it. */
4525
+ onFocus?: React__default.FocusEventHandler<HTMLCanvasElement>;
4526
+ /** Fired when the canvas element loses focus. */
4527
+ onBlur?: React__default.FocusEventHandler<HTMLCanvasElement>;
3641
4528
  /**
3642
4529
  * When `true` (default), the canvas element receives focus on `pointerdown`
3643
4530
  * so keyboard events (tool hotkeys, undo/redo) are captured without a
@@ -3746,7 +4633,9 @@ interface CanvasProps<TNode extends {
3746
4633
  * Hand tool registration, wheel pan/zoom action descriptors, and keyboard
3747
4634
  * zoom shortcuts are SceneCanvas-level concerns and are NOT owned by Canvas.
3748
4635
  * Those belong with the tool registry and gesture dispatcher that live in
3749
- * SceneCanvas.
4636
+ * SceneCanvas — which is also why SceneCanvas does not pass this prop: it
4637
+ * drives pinch through the `viewport.pinchZoom` action instead, and both
4638
+ * paths at once would apply one gesture's zoom factor twice.
3750
4639
  *
3751
4640
  * When omitted, no pinch-zoom listener is attached.
3752
4641
  */
@@ -3819,528 +4708,26 @@ interface CanvasProps<TNode extends {
3819
4708
  * every chrome element visible (pre-chrome-caps behavior).
3820
4709
  */
3821
4710
  getIsVisible?: () => (id: string) => boolean;
3822
- }
3823
- /** Live overlay-aware lookups exposed to custom layers via `helpersRef`. */
3824
- interface CanvasHelpers<TPose> {
3825
- /** Pose currently displayed for `id` — drag/resize/rotate overlay if active,
3826
- * otherwise the committed pose from the adapter. Returns `null` if the id
3827
- * isn't known. */
3828
- getEffectivePose(id: string): TPose | null;
3829
- /** Overlay-aware bounds for `id`. */
3830
- getEffectiveBounds(id: string): Bounds | null;
3831
- /**
3832
- * World-space AABB of everything the in-flight gesture proposes — the
3833
- * displaced poses of nodes being moved / resized / rotated / cloned, plus
3834
- * any nascent insert that has no scene node yet. `null` when no gesture is
3835
- * in flight.
3836
- *
3837
- * This reports the *gesture*, not the document: committed content the
3838
- * gesture isn't touching is excluded, so a consumer that wants the union
3839
- * with the rest of the scene still walks its own ids through
3840
- * `getEffectiveBounds`. It exists because every other lookup here is keyed
3841
- * by node id, which can't answer "where is the shape the user is drawing
3842
- * right now" — a drag-to-insert has no id until pointer-up.
3843
- *
3844
- * Select-only gestures are deliberately excluded: a marquee or lasso has
3845
- * geometry but proposes no content, and a consumer sizing itself to the
3846
- * gesture must not grow because the user swept a selection rectangle.
3847
- *
3848
- * The result is a plain AABB — never rotated. Rotated parts are folded in
3849
- * by their rotated extent (a union of several oriented boxes has no single
3850
- * orientation to report).
3851
- */
3852
- getGestureBounds(): Bounds | null;
3853
4711
  /**
3854
- * Subscribe to the gesture layer's change signal the other half of the
3855
- * `useSyncExternalStore` contract for everything on this object that moves
3856
- * during a drag (`getEffectivePose`, `getEffectiveBounds`,
3857
- * `getGestureBounds`). Returns an unsubscribe.
4712
+ * Show or hide whole render layers by id. An id absent from the map falls
4713
+ * back to the layer's own `defaultVisible`, and a layer marked `alwaysOn`
4714
+ * ignores the map entirely.
3858
4715
  *
3859
- * Fires once per dispatcher pump: gesture start, every pointermove that
3860
- * reaches an in-flight handle, end, and cancel plus UI-driven ongoing
3861
- * actions (a slider bound to an ongoing action pumps the same way). It
3862
- * fires on the pump, not on a diff: a pump that changed nothing observable
3863
- * still notifies, so don't hang expensive work directly off the callback.
4716
+ * Hiding a layer also stops it claiming pointer events through
4717
+ * `hitTestExtras` a layer nobody can see must not swallow a click.
3864
4718
  *
3865
- * It does **not** cover committed scene edits (subscribe to the scene for
3866
- * those) or previews a consumer's own tool publishes from React state
3867
- * (that tool re-renders on its own).
3868
- *
3869
- * Without a gesture source wired — a bare `<Canvas>` — this is a no-op
3870
- * subscription that never fires.
4719
+ * This is `getIsVisible`'s coarser sibling: that one gates individual
4720
+ * chrome elements *within* a layer, this one gates the layer.
3871
4721
  */
3872
- subscribeGestures(fn: () => void): () => void;
4722
+ layerVisibility?: Record<string, boolean>;
3873
4723
  /**
3874
- * Monotonic counter bumped on exactly the events `subscribeGestures` fires
3875
- * on. Pair the two for `useSyncExternalStore`:
3876
- *
3877
- * ```ts
3878
- * const gestureVersion = useSyncExternalStore(
3879
- * useCallback((cb) => helpersRef.current?.subscribeGestures(cb) ?? (() => {}), []),
3880
- * () => helpersRef.current?.getGestureVersion() ?? 0,
3881
- * );
3882
- * ```
4724
+ * Draw order, by layer id, bottom first. Omit for the order the layers
4725
+ * arrive in.
3883
4726
  *
3884
- * Starts at 0 and only increases. `0` is also what a bare `<Canvas>` with
3885
- * no gesture source reports, forever.
4727
+ * **A listed order is the whole list:** any layer whose id is missing from
4728
+ * it is not drawn. Reordering two layers means naming all of them.
3886
4729
  */
3887
- getGestureVersion(): number;
3888
- /** Returns the live ChromeState built once per render. Affordances and
3889
- * custom layers that need overlay-aware selection state (selection ids,
3890
- * bounds, multi-union AABB, modifier flags) read from this. */
3891
- getChromeState(): ChromeState;
3892
- /** Active debug sink, when `<Canvas debug=...>` is enabled. Layers that
3893
- * want to participate in `hitboxes`/`bounds`/etc. visualization can
3894
- * call into this from their `draw` callback. Returns `null` when
3895
- * debug is off — no-op for production renders. */
3896
- getDebug(): DebugSink | null;
3897
- /** Chrome-caps visibility predicate, keyed by chrome id. Returns a
3898
- * function that affordance/overlay layers can call per-element to
3899
- * decide whether to draw / hit-test. When the parent didn't supply
3900
- * a resolver, this returns the universal `() => true`. */
3901
- getIsVisible(): (id: string) => boolean;
3902
- }
3903
-
3904
- /** Which of a node's two per-anchor color arrays an override applies to. */
3905
- type VertexColorChannel = 'fill' | 'stroke';
3906
- /** Function-form override: receives the consumer-supplied base color
3907
- * array and the current animation timestamp (ms, from the animator's
3908
- * clock). Returns a flat RGBA float array (values in 0..1, matching
3909
- * the renderer's `stroke.vertexColors` / `PathDrawCommand.vertexColors`
3910
- * color space) of the same length as `base`. */
3911
- type ColorOverrideFn = (base: readonly number[], tMs: number) => number[];
3912
- /** Either a static per-anchor RGBA float array (0..1) or a function-form
3913
- * override (see {@link ColorOverrideFn}). */
3914
- type ColorOverride = readonly number[] | ColorOverrideFn;
3915
- /** Per-node, per-channel store of color overrides consulted by `createPathLayer`
3916
- * before falling back to the consumer's `getVertexColors` / `getStrokeVertexColors`
3917
- * accessor. Attached to `useAnimator` as `animator.colorOverrides`. */
3918
- declare class ColorOverrideRegistry {
3919
- private readonly map;
3920
- private _version;
3921
- set(id: string, channel: VertexColorChannel, override: ColorOverride): void;
3922
- clear(id: string, channel: VertexColorChannel): void;
3923
- clearAll(): void;
3924
- get(id: string, channel: VertexColorChannel): ColorOverride | undefined;
3925
- version(): number;
3926
- }
3927
-
3928
- /** One keyframe. `easing` shapes the approach INTO this key from the previous
3929
- * one, so the first key's easing is never consulted. */
3930
- interface Keyframe<T> {
3931
- /** Time within the track's timeline, in ms. */
3932
- t: number;
3933
- value: T;
3934
- easing?: EasingFn;
3935
- }
3936
- /** A track sampled as a pure function of the playhead. Scrubbing one is free
3937
- * and order-independent. */
3938
- interface SampledTrack<T> {
3939
- kind: 'sampled';
3940
- label?: string;
3941
- /** Sorted ascending by `t`. `sampleTrack` assumes this and does not sort. */
3942
- keys: Keyframe<T>[];
3943
- /** Required when T is not `number`; defaults to numeric lerp otherwise. */
3944
- interpolate?: Interpolate<T>;
3945
- /** Built once per segment and cached. Takes precedence over `interpolate`. */
3946
- interpolator?: InterpolatorFactory<T>;
3947
- onTick: (value: T) => void;
3948
- }
3949
- /** A track of edge crossings. Fires only when the playhead advances forward
3950
- * under playback — never on `seek`. */
3951
- interface EventTrack {
3952
- kind: 'event';
3953
- label?: string;
3954
- /** Sorted ascending by `t`. */
3955
- events: {
3956
- t: number;
3957
- fire: () => void;
3958
- }[];
3959
- }
3960
- /** A nested timeline, evaluated at `playhead - at`. Children are NOT registered
3961
- * with the animator separately; the parent evaluates them. */
3962
- interface TimelineTrack {
3963
- kind: 'timeline';
3964
- label?: string;
3965
- at: number;
3966
- timeline: NestedTimeline;
3967
- }
3968
- type Track = SampledTrack<any> | EventTrack | TimelineTrack;
3969
- /** What a child timeline may declare. The parent owns playback, so `loop`,
3970
- * `autoplay`, `onDone` and `cancelKey` have no meaning below the root. */
3971
- interface NestedTimeline {
3972
- tracks: Track[];
3973
- /** Defaults to the largest end time across `tracks`. */
3974
- duration?: number;
3975
- }
3976
- interface TimelineOptions extends NestedTimeline {
3977
- /** `true` loops forever, `n` loops n additional times. Default false. */
3978
- loop?: boolean | number;
3979
- /** Default true. When false the timeline registers but holds at t=0 until resumed. */
3980
- autoplay?: boolean;
3981
- onDone?: () => void;
3982
- cancelKey?: string;
3983
- }
3984
- interface TimelineHandle extends AnimationHandle {
3985
- /** Move the playhead. Never fires event tracks, at any depth. */
3986
- seek(t: number): void;
3987
- /** Current playhead in ms. */
3988
- time(): number;
3989
- duration(): number;
3990
- tracks(): readonly Track[];
3991
- /** Run `fn`, then recompute duration, drop cached interpolators, and notify.
3992
- * Every mutation must go through this — an edited keyframe otherwise keeps
3993
- * interpolating toward its old value with no visible error. */
3994
- edit(fn: () => void): void;
3995
- /** Notified after each `edit`. Returns an unsubscribe. */
3996
- subscribe(cb: () => void): () => void;
3997
- }
3998
-
3999
- /** An easing curve: maps normalized progress `t ∈ [0, 1]` to eased progress.
4000
- * Curves may leave the 0–1 range in the middle (back, elastic) but should
4001
- * pass through 0 at 0 and 1 at 1. */
4002
- type EasingFn = (t: number) => number;
4003
- /** Blends two `T` values at eased progress `t`. Called once per frame; see
4004
- * {@link InterpolatorFactory} when the blend has setup worth hoisting. */
4005
- type Interpolate<T> = (from: T, to: T, t: number) => T;
4006
- /** Factory interpolator: built ONCE at tween start with (from, to), the returned
4007
- * function is called with `t ∈ [0, 1]` each frame. Use for interpolators with
4008
- * expensive setup (color-space conversion, path-string parsing) — d3-interpolate's
4009
- * shape exactly. For cheap interpolations the per-tick `Interpolate<T>` form is
4010
- * fine; this is the escape hatch when setup-per-tick is wasteful. */
4011
- type InterpolatorFactory<T> = (from: T, to: T) => (t: number) => T;
4012
- /** A spring's physical parameters. Higher stiffness settles faster, higher
4013
- * damping overshoots less, higher mass makes both sluggish. */
4014
- interface SpringPreset {
4015
- stiffness: number;
4016
- damping: number;
4017
- mass: number;
4018
- }
4019
- /** One of the tunings in `SPRING_PRESETS`. */
4020
- type SpringPresetName = 'gentle' | 'wobbly' | 'stiff' | 'slow';
4021
- /** A running animation. Cancel it, or bend its time — pausing and time-scaling
4022
- * act on this animation's own virtual clock, independent of the animator's. */
4023
- interface AnimationHandle {
4024
- /** Monotonic id assigned by the animator. */
4025
- id: number;
4026
- /** Cancel this animation. Idempotent — no-op once already finished/canceled. */
4027
- cancel(): void;
4028
- /** Freeze this animation's virtual clock. Idempotent. */
4029
- pause(): void;
4030
- /** Resume this animation's virtual clock. Idempotent. */
4031
- resume(): void;
4032
- /** Multiply this animation's virtual-clock rate by `scale`. 1 = normal. */
4033
- setTimeScale(scale: number): void;
4034
- /** True iff this handle is currently paused. */
4035
- isPaused(): boolean;
4036
- }
4037
- /** A duration-based animation from `from` to `to` over `ms`, shaped by an
4038
- * easing curve. Reach for a spring instead when the motion should respond to
4039
- * where the value already is rather than restart from a fixed duration. */
4040
- interface TweenOptions<T> {
4041
- from: T;
4042
- to: T;
4043
- ms: number;
4044
- easing?: EasingFn;
4045
- /** Required when T is not `number`. For T = number, defaults to linear numeric lerp.
4046
- * Called per-tick with `(from, to, t)`. For interpolators with expensive setup,
4047
- * prefer `interpolator` which is built once at tween start. */
4048
- interpolate?: Interpolate<T>;
4049
- /** Factory interpolator built once at tween start. Takes precedence over
4050
- * `interpolate` when both are provided. Use this for d3-interpolate or any
4051
- * `(from, to) => (t) => v` shape. */
4052
- interpolator?: InterpolatorFactory<T>;
4053
- onTick: (value: T) => void;
4054
- onDone?: () => void;
4055
- /** Any new animation passed the same cancelKey cancels the prior one in flight. */
4056
- cancelKey?: string;
4057
- }
4058
- /** A spring animation: runs until the value settles on `to` rather than for a
4059
- * set duration, so it absorbs an initial velocity naturally. Non-numeric `T`
4060
- * needs the four vector helpers. */
4061
- interface SpringOptions<T> {
4062
- from: T;
4063
- to: T;
4064
- /** Initial velocity in T-units per second. Default: zero (T-shape-aware). */
4065
- velocity?: T;
4066
- preset?: SpringPresetName;
4067
- stiffness?: number;
4068
- damping?: number;
4069
- mass?: number;
4070
- interpolate?: Interpolate<T>;
4071
- /** Vector helpers — required for non-numeric T. */
4072
- add?: (a: T, b: T) => T;
4073
- subtract?: (a: T, b: T) => T;
4074
- scale?: (v: T, k: number) => T;
4075
- magnitude?: (v: T) => number;
4076
- /** Velocity magnitude below which the spring is considered settled. Default 0.01. */
4077
- restThreshold?: number;
4078
- onTick: (value: T) => void;
4079
- onDone?: () => void;
4080
- cancelKey?: string;
4081
- }
4082
- /** Spring and decay as one animation. With a `to`, a spring pulls toward it;
4083
- * with `to: null`, the value coasts on its velocity. Either can become the
4084
- * other mid-flight through the handle. */
4085
- interface PhysicsOptions<T> {
4086
- from: T;
4087
- /** Target. `null` ⇒ no spring force (decay-mode). */
4088
- to?: T | null;
4089
- /** Initial velocity in T-units per second. */
4090
- velocity?: T;
4091
- preset?: SpringPresetName;
4092
- stiffness?: number;
4093
- damping?: number;
4094
- mass?: number;
4095
- restThreshold?: number;
4096
- /** Vector helpers — required for non-numeric T. */
4097
- add?: (a: T, b: T) => T;
4098
- subtract?: (a: T, b: T) => T;
4099
- scale?: (v: T, k: number) => T;
4100
- magnitude?: (v: T) => number;
4101
- onTick: (value: T) => void;
4102
- onDone?: () => void;
4103
- cancelKey?: string;
4104
- }
4105
- /** An `AnimationHandle` that can also be steered while it runs — the point of
4106
- * the physics primitive. */
4107
- interface PhysicsHandle<T = unknown> extends AnimationHandle {
4108
- /** Retarget mid-flight. `null` ⇒ switch to decay-mode (no spring force). */
4109
- setTarget(to: T | null): void;
4110
- /** Replace the current velocity in T-units per second. */
4111
- setVelocity(v: T): void;
4112
- }
4113
- /** Momentum: coast from `from` at `velocity`, slowing by `friction` each
4114
- * second until below `threshold`. What a flick-to-pan leaves behind. */
4115
- interface DecayOptions<T> {
4116
- from: T;
4117
- velocity: T;
4118
- /** Per-second velocity multiplier in (0, 1). Default 0.95. */
4119
- friction?: number;
4120
- /** Velocity magnitude below which decay stops. Default 0.5. */
4121
- threshold?: number;
4122
- add: (a: T, b: T) => T;
4123
- scale: (v: T, k: number) => T;
4124
- magnitude: (v: T) => number;
4125
- onTick: (value: T) => void;
4126
- onDone?: () => void;
4127
- cancelKey?: string;
4128
- }
4129
- /** Options for `useAnimator`. Everything here is an injection seam for tests;
4130
- * the defaults are the real clock, rAF, and `setTimeout`. */
4131
- interface UseAnimatorOptions {
4132
- /** Optional clock injection for tests. Returns ms since some epoch. */
4133
- now?: () => number;
4134
- /** Optional rAF / cAF injection for tests. Defaults to window.requestAnimationFrame. */
4135
- requestFrame?: (cb: (t: number) => void) => number;
4136
- cancelFrame?: (handle: number) => void;
4137
- /** Optional `setTimeout` injection used by `stagger` for per-item delays.
4138
- * Defaults to the global `setTimeout`. Tests inject a virtual scheduler. */
4139
- setTimer?: (cb: () => void, ms: number) => unknown;
4140
- /** Companion to `setTimer`. Defaults to global `clearTimeout`. */
4141
- clearTimer?: (handle: unknown) => void;
4142
- }
4143
- /**
4144
- * Owns every running animation on a canvas and drives them from one rAF loop.
4145
- * Beyond the primitives (`tween`, `spring`, `decay`, `physics`) it offers
4146
- * composition — `loop`, `stagger` — and bulk control by handle, by cancel-key,
4147
- * or over everything at once.
4148
- *
4149
- * An animator does not know about the scene: animations report values through
4150
- * `onTick` and the caller decides what to do with them.
4151
- */
4152
- interface Animator {
4153
- tween<T>(opts: TweenOptions<T>): AnimationHandle;
4154
- spring<T>(opts: SpringOptions<T>): AnimationHandle;
4155
- decay<T>(opts: DecayOptions<T>): AnimationHandle;
4156
- /** Unified spring/decay primitive. With `to` set, behaves as a spring;
4157
- * with `to: null`, behaves as a velocity-driven decay. Supports
4158
- * mid-flight retargeting via the returned handle's `setTarget`. */
4159
- physics<T>(opts: PhysicsOptions<T>): PhysicsHandle<T>;
4160
- /** Cancel a specific animation by handle. Pose stays at current value (no jump). */
4161
- cancel(handle: AnimationHandle): void;
4162
- /** Cancel every animation currently active under `key`. */
4163
- cancelKey(key: string): void;
4164
- /** Cancel everything. Useful from a destructor or "reset scene" path. */
4165
- cancelAll(): void;
4166
- /** True iff at least one animation is active. With `key`, scoped to that cancelKey. */
4167
- isActive(key?: string): boolean;
4168
- /**
4169
- * True while the animator is currently executing an animation tick. Useful
4170
- * for adapter wrappers (e.g. `animateOnSetPose`) that need to detect
4171
- * "this `setPose` was called from inside another animation's onTick"
4172
- * (momentum decay, in-flight tween, spring) and avoid recursively
4173
- * scheduling a new wrap-animation that would fight the caller.
4174
- */
4175
- isTicking(): boolean;
4176
- /** Freeze every animation managed by this animator. */
4177
- pause(): void;
4178
- /** Resume every animation managed by this animator. */
4179
- resume(): void;
4180
- /** True iff the animator is currently globally paused. */
4181
- isPaused(): boolean;
4182
- /** Multiply every animation's virtual-clock rate by `scale`. 1 = normal. */
4183
- setTimeScale(scale: number): void;
4184
- /** Freeze every animation whose `cancelKey` matches. */
4185
- pauseKey(key: string): void;
4186
- /** Resume every animation whose `cancelKey` matches. */
4187
- resumeKey(key: string): void;
4188
- /** Set per-animation timeScale for every animation whose `cancelKey` matches. */
4189
- setTimeScaleByKey(key: string, scale: number): void;
4190
- /**
4191
- * Loop primitive: repeatedly invoke `factory` to produce a child animation.
4192
- * The factory must wire its returned handle's `onDone` to call `next` so
4193
- * the loop advances. Returns a handle whose pause/resume/setTimeScale/cancel
4194
- * delegate to the current in-flight child (and prevent future iterations
4195
- * on cancel).
4196
- *
4197
- * The loop is registered with the animator under a supervisor entry so
4198
- * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
4199
- * `animator.isActive(opts.cancelKey)` all work for it.
4200
- */
4201
- loop(factory: LoopFactory, opts?: LoopOptions): AnimationHandle;
4202
- /** Sugar over `loop` for the common case of looping a tween between two
4203
- * values with optional direction handling (`restart` | `reverse` |
4204
- * `alternate`). Registered with the animator like `loop`. */
4205
- tweenLoop<T>(opts: TweenLoopOptions<T>): AnimationHandle;
4206
- /**
4207
- * Stagger primitive: schedule a per-item animation, offset by `delay` ms
4208
- * per index (or a custom function of the index). Two forms:
4209
- * - Factory form: pass `factory` directly, returns a composite
4210
- * `AnimationHandle`.
4211
- * - Builder form: omit `factory`, get a `StaggerBuilder` for fluent
4212
- * `.each` / `.tween` / `.springPose` calls.
4213
- *
4214
- * The composite handle's `cancel` cancels pending timers AND in-flight
4215
- * children. `pause` / `resume` / `setTimeScale` propagate to in-flight
4216
- * children; `pause`/`resume` also freeze and thaw pending per-item timers
4217
- * (the remaining time before each pending fire is preserved across the
4218
- * pause).
4219
- *
4220
- * The stagger is registered with the animator under a supervisor entry so
4221
- * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
4222
- * `animator.isActive(opts.cancelKey)` all work for it.
4223
- */
4224
- stagger<TItem>(items: readonly TItem[], delay: StaggerDelay): StaggerBuilder<TItem>;
4225
- stagger<TItem>(items: readonly TItem[], delay: StaggerDelay, factory: StaggerFactory<TItem>, opts?: StaggerOptions): AnimationHandle;
4226
- /**
4227
- * Keyframe timeline. Registered like any other animation, so its playhead
4228
- * responds to `pause`, `setTimeScale` and `cancelKey`. Sampled tracks are a
4229
- * pure function of the playhead; event tracks fire only on forward playback.
4230
- */
4231
- timeline(opts: TimelineOptions): TimelineHandle;
4232
- /** Per-node, per-channel color override registry consulted by the renderer's
4233
- * path layer before reading consumer accessors. Used by `tweenVertexColors`,
4234
- * `springVertexColors`, `cycleVertexColors`, `staggerVertexColors`. Cleared
4235
- * automatically on animator unmount. */
4236
- colorOverrides: ColorOverrideRegistry;
4237
- /**
4238
- * Subscribe to a callback fired once per RAF frame while any animation is
4239
- * active. Returns an unsubscribe function. Used by consumers (typically
4240
- * `<SceneCanvas>`) that need to repaint when an animation's side-effect
4241
- * is read from a non-scene channel (e.g. `colorOverrides` consulted from
4242
- * a custom `drawOne`) — scene mutations naturally trigger a repaint, but
4243
- * `colorOverrides` writes do not.
4244
- *
4245
- * The callback fires AFTER the per-frame tick of each registered
4246
- * animation, so by the time it runs `colorOverrides.get(...)` returns
4247
- * the latest values. If no animations are active, no tick fires.
4248
- */
4249
- onTick(cb: () => void): () => void;
4250
- /**
4251
- * Keep the animator's RAF loop running until the returned cancel
4252
- * function is called. Use for animations whose effect is read on every
4253
- * frame but which don't have a natural progress state (e.g.
4254
- * `cycleVertexColors`, which expresses its current value as a function
4255
- * of `performance.now()` rather than as a tween from `from` to `to`).
4256
- * Without a keep-alive entry the loop would idle and `onTick` would
4257
- * stop firing even though the override is still installed.
4258
- */
4259
- keepAlive(): () => void;
4260
- }
4261
- /** Options for `Animator.loop`. */
4262
- interface LoopOptions {
4263
- /** Maximum number of iterations. Default Infinity. */
4264
- count?: number;
4265
- /** Invoked when the loop reaches `count` iterations naturally (not on cancel). */
4266
- onDone?: () => void;
4267
- /** Any new animation passed the same cancelKey cancels the prior one in flight.
4268
- * Also enables `animator.cancelKey` / `animator.isActive(key)` for this loop. */
4269
- cancelKey?: string;
4270
- }
4271
- /** Options for the top-level `Animator.stagger` factory form (third overload). */
4272
- interface StaggerOptions {
4273
- /** Cancel-key for the supervising registration. `animator.cancelKey(key)`
4274
- * cancels the whole stagger; `animator.isActive(key)` returns true while
4275
- * any timer or child is alive. */
4276
- cancelKey?: string;
4277
- }
4278
- /** Produces one iteration of a loop. Must arrange for `next` to be called when
4279
- * the animation it returns finishes, or the loop stalls after one pass. */
4280
- type LoopFactory = (iteration: number, next: () => void) => AnimationHandle;
4281
- /** Per-index delay schedule. Number ⇒ `index * delay` ms. Function ⇒ caller
4282
- * decides the absolute delay for each index (e.g. `i => i * i * 30`). */
4283
- type StaggerDelay = number | ((index: number) => number);
4284
- /** Produces the animation for one staggered item. */
4285
- type StaggerFactory<TItem> = (item: TItem, index: number) => AnimationHandle;
4286
- /** A `T` value or a function that derives one from the per-item context. Used
4287
- * by the fluent builder methods (`.tween`, `.springPose`) so each item can
4288
- * vary an option (e.g. `to: (_item, i) => (i + 1) * 10`). */
4289
- type StaggerPerItem<T, TItem> = T | ((item: TItem, index: number) => T);
4290
- /** Options for the stagger builder's `.tween`: a tween per item, where
4291
- * `from`, `to` and `ms` may each vary by item. */
4292
- interface StaggerTweenOptions<T, TItem> {
4293
- from: StaggerPerItem<T, TItem>;
4294
- to: StaggerPerItem<T, TItem>;
4295
- ms: StaggerPerItem<number, TItem>;
4296
- easing?: EasingFn;
4297
- interpolate?: Interpolate<T>;
4298
- onTick: (value: T, item: TItem, index: number) => void;
4299
- onDone?: (item: TItem, index: number) => void;
4300
- }
4301
- /** Options for the stagger builder's `.springPose`: the spring tuning, and
4302
- * whether each item's settle is recorded as an undoable op. */
4303
- interface StaggerSpringPoseOptions<TPose> {
4304
- preset?: SpringPresetName;
4305
- stiffness?: number;
4306
- damping?: number;
4307
- mass?: number;
4308
- geometry?: PoseProjection<TPose>;
4309
- recordOp?: boolean;
4310
- opLabel?: string;
4311
- }
4312
- /** Fluent form of `Animator.stagger`: pick what to run per item after the
4313
- * items and the delay schedule are already fixed. */
4314
- interface StaggerBuilder<TItem> {
4315
- /** Run an arbitrary per-item factory. */
4316
- each(factory: StaggerFactory<TItem>): AnimationHandle;
4317
- /** Sugar: per-item `animator.tween` with per-item-varying options. */
4318
- tween<T>(opts: StaggerTweenOptions<T, TItem>): AnimationHandle;
4319
- /** Sugar: per-item `springPose` against an adapter. `poseFn` returns the
4320
- * target pose for each item. Each item must either be a primitive
4321
- * (string/number) or expose a string `id` field — otherwise pose ids
4322
- * would collide on `"[object Object]"` and successive tweens would
4323
- * cancel each other. Throws on items that satisfy neither. */
4324
- springPose<TPose>(adapter: SceneAdapter<{
4325
- id: string;
4326
- }, TPose>, poseFn: (item: TItem, index: number) => TPose, opts?: StaggerSpringPoseOptions<TPose>): AnimationHandle;
4327
- }
4328
- /** Options for `Animator.tweenLoop` — a tween's options plus how each
4329
- * iteration relates to the last. */
4330
- interface TweenLoopOptions<T> {
4331
- from: T;
4332
- to: T;
4333
- ms: number;
4334
- easing?: EasingFn;
4335
- /** `restart` (default): from→to every iteration.
4336
- * `reverse`: to→from every iteration.
4337
- * `alternate`: even iterations from→to, odd iterations to→from. */
4338
- direction?: 'restart' | 'reverse' | 'alternate';
4339
- count?: number;
4340
- interpolate?: Interpolate<T>;
4341
- onTick: (value: T) => void;
4342
- onDone?: () => void;
4343
- cancelKey?: string;
4730
+ layerOrder?: string[];
4344
4731
  }
4345
4732
 
4346
4733
  /** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /
@@ -4487,21 +4874,61 @@ interface UseRotateOptions<TPose> {
4487
4874
  * Has no effect on single-id gestures. */
4488
4875
  pivot?: 'each' | 'union';
4489
4876
  }
4490
-
4877
+
4878
+ /** A rect on the surface, in CSS pixels from the canvas top-left. */
4879
+ interface ViewRect {
4880
+ x: number;
4881
+ y: number;
4882
+ w: number;
4883
+ h: number;
4884
+ }
4885
+ /** Props for `<CanvasView>`. @experimental */
4886
+ interface CanvasViewProps {
4887
+ /** Identifies the view to the surface and to input routing. Must be unique
4888
+ * among the views on one canvas. */
4889
+ id: string;
4890
+ /** Where this view paints, recomputed every frame so the rect can track the
4891
+ * outer camera. A plain rect is accepted for a fixed panel. */
4892
+ bounds: ViewRect | ((outer: View, dims: Dims) => ViewRect);
4893
+ /** Camera. Supply this to control it; otherwise the view keeps its own,
4894
+ * seeded from `defaultView`. `onViewChange` fires either way. */
4895
+ view?: View;
4896
+ defaultView?: View;
4897
+ onViewChange?: (v: View) => void;
4898
+ /** Pan limits, applied to every camera change the same way `<Canvas>`
4899
+ * applies its own. */
4900
+ viewBounds?: Bounds;
4901
+ /** Which of the surface's layers this view paints. Defaults to all of them —
4902
+ * the same content through a second camera. */
4903
+ layers?: (surface: readonly RenderLayer<unknown>[]) => readonly RenderLayer<unknown>[];
4904
+ /** Opaque ground painted before the source layers, so the surface does not
4905
+ * show through where the inner camera sees nothing. */
4906
+ background?: string;
4907
+ /** Paint and hit order. Defaults to `Infinity` — after every view the
4908
+ * surface declared as a prop. */
4909
+ order?: number;
4910
+ /** Label for debug overlays. Defaults to the id. */
4911
+ label?: string;
4912
+ /** A selection of this view's own, which actions dispatched inside it read
4913
+ * and write instead of the surface's. Omit both this and
4914
+ * `selectionOptions` and the view shares the surface's selection — the
4915
+ * default, so undo restores one selection rather than N. */
4916
+ selection?: SelectionApi;
4917
+ /** Opts this view into owning a selection, configured thus. Ignored when
4918
+ * `selection` is supplied. */
4919
+ selectionOptions?: UseSelectionOptions;
4920
+ }
4491
4921
  /**
4492
- * Built-in shape tool ids handled by `useBuiltinShapeTools`. Each maps to a
4493
- * kit tool hook + a default `create` that produces a leaf node compatible
4494
- * with `PATH_PAINTER`.
4922
+ * @experimental
4495
4923
  *
4496
- * Runtime mirror in `KIT_SHAPE_KINDS` below keep the two in sync. The
4497
- * `src/index.barrel.test.ts` parity gate enforces that every member of this
4498
- * union is present in the exported tuple.
4924
+ * One view on a shared canvas. Owns a camera, contributes the viewport node
4925
+ * that paints it, and registers the dispatch record that sends gestures inside
4926
+ * its rect to that camera rather than to the canvas's.
4927
+ *
4928
+ * Renders nothing itself — it is a declaration, and the surface does the
4929
+ * drawing. Outside a surface that mounts a view registry it is inert.
4499
4930
  */
4500
- type BuiltinShapeToolId = 'rect' | 'ellipse' | 'line' | 'polygon' | 'star' | 'pen' | 'pencil' | 'lasso' | 'text';
4501
- /** Runtime, iterable list of the shape-tool ids in `BuiltinShapeToolId`.
4502
- * Surfaced so consumers (e.g. the Bundle Inspector) can enumerate the
4503
- * builtin shape kinds without re-encoding the union. */
4504
- declare const KIT_SHAPE_KINDS: readonly ["rect", "ellipse", "line", "polygon", "star", "pen", "pencil", "lasso", "text"];
4931
+ declare function CanvasView(props: CanvasViewProps): null;
4505
4932
 
4506
4933
  /** Per-tool option overrides for the built-in shape/lasso tools.
4507
4934
  * Each entry is a narrow subset of the underlying hook's options surface
@@ -4640,182 +5067,6 @@ declare function createNodeRouting(): NodeRouting;
4640
5067
  * matches `poseRotationOf` (the shared rotation convention). */
4641
5068
  declare function rotateAroundAABBCenter(x: number, y: number, width: number, height: number, rotation: number): Float32Array;
4642
5069
 
4643
- /**
4644
- * NodeShape — the **shape trait's** registry. Each trait of a node
4645
- * (shape, routing, label, icon, affordances, …) is its own registry;
4646
- * this one holds the per-kind `paint` + `silhouette` (and future
4647
- * fields) used by `defaultDrawOne`, clipping, non-rect hit-testing,
4648
- * lasso/area-select, and SVG export.
4649
- *
4650
- * Teaching the kit about a new kind of shape goes through this registry
4651
- * rather than by overriding `drawOne`. Overrides are still possible but
4652
- * shouldn't be the default seam: most consumers want the same dispatch
4653
- * logic, just extended with their own shape kinds (images, custom paths,
4654
- * SVG fragments, etc.).
4655
- *
4656
- * Built-in entries (`kit:text`, `kit:path`, `kit:rect-fallback`) are
4657
- * registered at module load. Consumer entries added via
4658
- * `registerNodeShape` join the chain; the first entry whose
4659
- * `matches` predicate returns true paints the node.
4660
- *
4661
- * Two priority tiers:
4662
- * - `'high'` — checked before all `'normal'` entries. Use this to
4663
- * override a kit built-in for a specific data shape (e.g. a custom
4664
- * text renderer that wins over `kit:text`).
4665
- * - `'normal'` (default) — appended after the built-ins.
4666
- *
4667
- * Within a tier, entries run in registration order. Each
4668
- * `registerNodeShape` call returns a disposer that removes the
4669
- * entry — useful for tests, for plugin lifecycles, and for swapping
4670
- * implementations at runtime.
4671
- *
4672
- * See `docs/superpowers/specs/2026-05-24-node-traits-reframe-design.md`
4673
- * for the trait taxonomy.
4674
- */
4675
-
4676
- /** Optional per-call paint context, threaded through `defaultDrawOne`'s third
4677
- * argument. Lets a rendering entry point override ambient environment reads
4678
- * — the headless `renderSceneToPixels` path supplies its own bitmap resolver
4679
- * here so consumers reuse their own decode caches. Custom painters may
4680
- * ignore it entirely. */
4681
- interface NodePaintCtx {
4682
- /** Override bitmap resolution for image nodes. When set it is authoritative:
4683
- * the global `imageCache` is not consulted, and an `undefined` result
4684
- * paints the deterministic grey placeholder outline (never the ambient
4685
- * load-status error variant). */
4686
- resolveImage?: (node: Node$1<unknown, string, unknown>) => ImageBitmap | undefined;
4687
- }
4688
- /** A painter for one kind of node: which nodes it claims, and the draw
4689
- * commands it emits for them. Registering one is how a consumer teaches the
4690
- * default renderer to draw its own node kinds. */
4691
- interface NodeShapeEntry<TData = unknown, TPose = unknown> {
4692
- /** Stable identifier — used for unregistration and debugging. Pick
4693
- * something descriptive: `'kit:text'`, `'app:image'`, etc. */
4694
- id: string;
4695
- /** Returns true when this painter renders the node. The first matching
4696
- * painter (`'high'` tier first, then `'normal'`) wins. */
4697
- matches(node: Node$1<TData, string, TPose>): boolean;
4698
- /** Emits the draw commands for the node's primary visual. `ctx` is an
4699
- * optional per-call paint context (see `NodePaintCtx`); painters that
4700
- * don't need it can keep a two-argument signature.
4701
- *
4702
- * **The returned array belongs to the painter.** Callers must treat it as
4703
- * immutable and copy before appending — a painter is free to memoize its
4704
- * command list (`kit:shape` and `kit:path` do), and an in-place `push`
4705
- * would grow that list on every frame. `defaultDrawOne` copies before
4706
- * adding its label overlay for exactly this reason. */
4707
- paint(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodePaintCtx): DrawCommand[];
4708
- /** Optional: derive the node's silhouette path from its pose.
4709
- * Used by clipping (when the container has no explicit
4710
- * `clipFromPose`), by non-rect hit-testing, by lasso/area-select,
4711
- * and by SVG export. Painters whose visual has no meaningful closed
4712
- * silhouette (e.g. text) leave this undefined. */
4713
- silhouette?(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
4714
- /** Optional: how the silhouette is inked — whether the interior is filled,
4715
- * and how wide the outline is. Read by picking, so that an unfilled shape
4716
- * is grabbable by its outline rather than by its empty middle.
4717
- *
4718
- * This is declared separately from `paint` rather than read back off the
4719
- * emitted draw commands because picking runs on every pointer move, and
4720
- * `paint` is allowed to be expensive (`kit:text` lays out glyphs). Keep it
4721
- * to cheap field reads.
4722
- *
4723
- * Painters that leave it undefined are treated as `{ filled: true,
4724
- * strokeWidth: 0 }` — the pre-`ink` behavior, where the whole silhouette
4725
- * interior is grabbable and the outline adds nothing. */
4726
- ink?(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
4727
- }
4728
- /** How a painter inks its silhouette. See {@link NodeShapeEntry.ink}. */
4729
- interface NodeInk {
4730
- /** True when the silhouette's interior is painted, and so grabbable. */
4731
- filled: boolean;
4732
- /** Stroke width in world units. `0` for no stroke. */
4733
- strokeWidth: number;
4734
- }
4735
- /** Options for `registerNodeShape`. */
4736
- interface RegisterNodeShapeOptions {
4737
- /** `'high'` puts the painter ahead of all normally-registered ones (so
4738
- * it can win over a kit built-in). `'normal'` appends at the end. */
4739
- priority?: 'high' | 'normal';
4740
- }
4741
- /** Register a shape painter. Returns a disposer that removes it. */
4742
- declare function registerNodeShape<TData, TPose>(painter: NodeShapeEntry<TData, TPose>, opts?: RegisterNodeShapeOptions): () => void;
4743
- /** Find the painter that will render `node` — first match in priority
4744
- * order. Returns undefined if no painter (including the built-in
4745
- * fallback) accepts the node. */
4746
- declare function findNodeShape<TData, TPose>(node: Node$1<TData, string, TPose>): NodeShapeEntry<TData, TPose> | undefined;
4747
- /** Find the painter for `node` and ask it for the node's silhouette path,
4748
- * in **world** coords. Returns null if no painter matches, or the matching
4749
- * painter has no `silhouette` method, or the method returns null. Used by
4750
- * clipping, generic non-rect hit-testing, lasso, and SVG export — anywhere
4751
- * the kit needs the "closed boundary" of whatever this kind of node draws as.
4752
- *
4753
- * Painters return their silhouette in the pose's local (unrotated) frame;
4754
- * this bakes `pose.rotation` on top via the shared rotation convention, so
4755
- * clips/area-select of a rotated node use the rotated boundary the renderer
4756
- * draws. (`paint()` is unaffected — it applies rotation via the render wrap,
4757
- * not the silhouette, so there is no double-rotation.) */
4758
- declare function findShapeSilhouette<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
4759
- /** Find the painter for `node` and ask how it inks its silhouette. Returns
4760
- * the painter's declared {@link NodeInk}, or `null` when no painter matches
4761
- * or it declares none — callers substitute {@link DEFAULT_INK}. */
4762
- declare function findShapeInk<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
4763
- /** Options for {@link shapeCoversPoint}. */
4764
- interface ShapeCoversPointOptions {
4765
- /** Extra grab distance around the outline, in **world** units. Callers
4766
- * derive it from a screen-pixel slop and the view scale, the same way
4767
- * affordance hit radii work.
4768
- *
4769
- * Without slop a hairline is a mathematically zero-width target: the
4770
- * stroke of a 1px outline is half a world unit wide at scale 1, which no
4771
- * one can hit. Defaults to `0` so a caller that hasn't thought about the
4772
- * view still gets exact geometry rather than a wrong guess. */
4773
- tolerance?: number;
4774
- }
4775
- /**
4776
- * Does the shape `node` actually paints cover the world point?
4777
- *
4778
- * The pose rect says a node covers its whole bounding box. That is wrong for
4779
- * everything that is not a rectangle: the concave notch of a star, the corner
4780
- * outside an ellipse, the blank right half of a text box. This asks the
4781
- * painter's silhouette instead, which is the same boundary used for clipping
4782
- * and SVG export, so "what you can click" and "what is drawn" answer together.
4783
- *
4784
- * "What is drawn" includes the *ink*, not just the boundary. A shape whose
4785
- * interior isn't filled — an outlined rect, a pencil stroke, a bare line — is
4786
- * grabbable along its outline and not through its empty middle, which is the
4787
- * opposite of what a fill test alone answers. The outline's grab width is the
4788
- * stroke's half-width plus `tolerance`.
4789
- *
4790
- * A painter with no `silhouette`, or one that returns `null` for this node
4791
- * (`kit:text` does, for a node with no non-blank lines), reports `true` —
4792
- * "no opinion", leaving the caller's own AABB test as the answer. Callers
4793
- * should keep that AABB test as a cheap pre-filter; this is the refinement,
4794
- * not a replacement.
4795
- *
4796
- * Rotation is already baked by `findShapeSilhouette`, so the point is in
4797
- * plain world coordinates.
4798
- */
4799
- declare function shapeCoversPoint<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, x: number, y: number, opts?: ShapeCoversPointOptions): boolean;
4800
- /** Snapshot of the current painters in evaluation order — `'high'` tier
4801
- * first, then `'normal'`. Useful for debugging which painter handles a
4802
- * given node. */
4803
- declare function getNodeShapes(): readonly NodeShapeEntry[];
4804
- /**
4805
- * What a node's `data.fill` means to the built-in painters.
4806
- *
4807
- * A string is a color, `'none'` skips the fill, and an object is a
4808
- * {@link FillStyle} used as-is — which is how a gradient or a pattern reaches
4809
- * the renderer without a consumer registering a painter of its own. The
4810
- * renderer has taken every `FillStyle` variant since the paint model landed;
4811
- * only these two painters were narrower than it.
4812
- *
4813
- * `undefined` falls back to `data.color`, then to a default fill — but only
4814
- * when there is no stroke, since a stroke-only path (pencil) should not
4815
- * acquire one.
4816
- */
4817
- type NodeFill = string | FillStyle;
4818
-
4819
5070
  /**
4820
5071
  * `defaultDrawOne` — the kit's default per-node draw function.
4821
5072
  *
@@ -4845,7 +5096,7 @@ type NodeFill = string | FillStyle;
4845
5096
  /** Paint one node the way the kit does by default: dispatch to the first
4846
5097
  * registered node-shape painter that matches, then draw its `data.label` on
4847
5098
  * top when it has one. Consumers replace or wrap this via `drawOne`. */
4848
- declare function defaultDrawOne<TData, TLayer extends string, TPose>(node: Node$1<TData, TLayer, TPose>, pose: TPose, ctx?: NodePaintCtx): DrawCommand[];
5099
+ declare function defaultDrawOne<TData, TLayer extends string, TPose>(node: Node$1<TData, TLayer, TPose>, pose: TPose, _view?: View, ctx?: NodePaintCtx): DrawCommand[];
4849
5100
 
4850
5101
  /** Default size in CSS pixels for selection corner-handles AND their
4851
5102
  * hit-test radius, at `targetScale = 1`. Used by the SceneCanvas defaults;
@@ -4855,10 +5106,13 @@ declare function defaultDrawOne<TData, TLayer extends string, TPose>(node: Node$
4855
5106
  * Deliberately unscaled: consumers reading this constant keep getting the
4856
5107
  * number they always got. Kit-internal use sites multiply by
4857
5108
  * `DeviceProfile.targetScale`. */
4858
- declare const DEFAULT_HANDLE_SIZE = 8;
5109
+ declare const DEFAULT_HANDLE_SIZE: 8;
5110
+ /** Built-in tool ids that aren't shape tools — the ones with no entry in
5111
+ * the `core/shapeKinds` table. */
5112
+ declare const NON_SHAPE_BUILTIN_TOOLS: readonly ["select", "rotate", "hand"];
4859
5113
  /** Built-in tool ids SceneCanvas knows how to mount when no `tools` prop
4860
5114
  * is supplied. Pass a subset via `defaultTools` to slim the registered set. */
4861
- type BuiltinToolId = 'select' | 'rotate' | 'hand' | BuiltinShapeToolId;
5115
+ type BuiltinToolId = (typeof NON_SHAPE_BUILTIN_TOOLS)[number] | BuiltinShapeToolId;
4862
5116
  /** Named preset tool collections for the `toolBundle` prop. Maps to a
4863
5117
  * `BuiltinToolId[]` consumed by SceneCanvas's internal `useTools`. */
4864
5118
  type ToolBundle = 'minimal' | 'standard' | 'exhaustive';
@@ -4877,7 +5131,7 @@ interface SceneCanvasHit {
4877
5131
  /** Props for `<SceneCanvas>`. Most are optional: a scene is the only thing it
4878
5132
  * truly needs, and everything else — tools, layers, selection handling,
4879
5133
  * layouts, animation — layers onto sensible defaults. */
4880
- type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<Node$1<TData, TLayer, TPose>, TPose>, 'adapter' | 'moveOptions' | 'resizeOptions' | 'rotateOptions' | 'snap' | 'pickEvery' | 'boundsOf' | 'handleHitRadius' | 'selection' | 'selectionOptions' | 'tools' | 'geometry' | 'layers' | 'onBackgroundClick' | 'getIsVisible'> & {
5134
+ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<Node$1<TData, TLayer, TPose>, TPose>, 'adapter' | 'moveOptions' | 'resizeOptions' | 'rotateOptions' | 'snap' | 'pickEvery' | 'boundsOf' | 'handleHitRadius' | 'selection' | 'selectionOptions' | 'tools' | 'geometry' | 'layers' | 'onBackgroundClick' | 'getIsVisible' | 'contentVersion'> & {
4881
5135
  /** A `Scene` (typically from `useScene`) — or a `SerializedScene`
4882
5136
  * JSON object, which SceneCanvas bakes into a Scene internally on
4883
5137
  * first render. The serialized form is read once; subsequent
@@ -5133,17 +5387,19 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5133
5387
  ambient?: AnyTool[];
5134
5388
  /** Viewport feature wiring.
5135
5389
  *
5136
- * - `inertia`, `pinchZoom`, `animatedZoom` are opt-in: pass `true`
5137
- * for defaults or an object to tune. Omitted means off.
5138
- * - `pan` (wheel pan) and `zoom` (Cmd+wheel + Cmd+=/-/0) are opt-OUT:
5139
- * on by default; pass `false` to disable. They are wired by registering
5140
- * the kit's `viewport.pan` / `viewport.zoom` action descriptors with
5141
- * the actions registry — disabling via the `actions` prop
5142
- * (`actions: { 'viewport.wheelPan': null }`) also works and runs after this.
5390
+ * - `inertia` and `animatedZoom` are opt-in: pass `true` for defaults or
5391
+ * an object to tune. Omitted means off.
5392
+ * - `pan` (wheel pan), `zoom` (Cmd+wheel + Cmd+=/-/0) and `pinchZoom`
5393
+ * (two-finger pinch) are opt-OUT: on by default; pass `false` to
5394
+ * disable. All three are wired by registering the kit's `viewport.*`
5395
+ * action descriptors with the actions registry — disabling via the
5396
+ * `actions` prop (`actions: { 'viewport.wheelPan': null }`) also works
5397
+ * and runs after this.
5143
5398
  *
5144
- * When omitted entirely, no hand/pinch tools are registered but the
5145
- * default wheel pan + Cmd+wheel/key zoom remain wired (canvas-first
5146
- * default). Pass `{ pan: false, zoom: false }` to opt out entirely. */
5399
+ * When omitted entirely, no hand tool is registered but the default wheel
5400
+ * pan, Cmd+wheel/key zoom and pinch zoom remain wired (canvas-first
5401
+ * default). Pass `{ pan: false, zoom: false, pinchZoom: false }` to opt
5402
+ * out entirely. */
5147
5403
  viewport?: {
5148
5404
  inertia?: boolean | {
5149
5405
  friction?: number;
@@ -5151,15 +5407,14 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5151
5407
  boundary?: 'stop' | 'bounce' | 'spring';
5152
5408
  bounds?: PanBounds;
5153
5409
  };
5154
- pinchZoom?: boolean | {
5155
- min?: number;
5156
- max?: number;
5157
- };
5158
- animatedZoom?: boolean | {
5159
- duration?: number;
5160
- resetDuration?: number;
5161
- easing?: (t: number) => number;
5162
- };
5410
+ /** Two-finger pinch zoom. `true`/omitted = on with the kit's 0.1–8
5411
+ * clamp; `false` disables. An object sets the scale clamp. */
5412
+ pinchZoom?: boolean | PinchZoomOptions;
5413
+ /** Glide Cmd+=/-/0 instead of jumping. `true` uses the kit defaults
5414
+ * (250 ms, ease-out-cubic); a {@link ViewportZoomAnimateOptions} tunes
5415
+ * duration, easing, interpolator and the reset-branch duration. Wheel
5416
+ * and pinch are unaffected — their input already samples every frame. */
5417
+ animatedZoom?: boolean | ViewportZoomAnimateOptions;
5163
5418
  pan?: boolean;
5164
5419
  /** Wheel/keyboard zoom. `true`/omitted = default Cmd+wheel zoom with the
5165
5420
  * kit's 0.1–8 clamp; `false` disables. Pass a {@link ViewportZoomOptions}
@@ -5168,10 +5423,11 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5168
5423
  zoom?: boolean | ViewportZoomOptions;
5169
5424
  /** Callback invoked by Cmd-0 (`viewport.zoom` action's `reset` branch).
5170
5425
  * When supplied, replaces the default reset-to-identity behavior —
5171
- * consumers typically refit the document page into the workspace
5172
- * via `fitViewToBounds`. The callback owns its own bounds + host
5173
- * dims and dispatches the resulting view via `onViewChange`. */
5174
- recenter?: () => void;
5426
+ * consumers typically refit the document page into the workspace via
5427
+ * `fitViewToBounds`. Return the target `View` to let the kit animate
5428
+ * there when `animatedZoom` is on; return nothing to dispatch it
5429
+ * yourself, which is what a controlled canvas does. */
5430
+ recenter?: () => View | void;
5175
5431
  };
5176
5432
  /**
5177
5433
  * @experimental
@@ -5231,6 +5487,17 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5231
5487
  * subscriber list stays quiet).
5232
5488
  */
5233
5489
  animator?: Animator;
5490
+ /**
5491
+ * Extra views on this canvas: each is a camera over a rect of the same
5492
+ * surface, drawn through one GL context, with input routed to it. The flat
5493
+ * `view` / `onViewChange` props above stay the canvas's own camera — view
5494
+ * zero — and are unaffected by anything declared here.
5495
+ *
5496
+ * Order is paint and hit order, low to high. Each entry is a
5497
+ * `<CanvasView>`; mounting one as a child is the same declaration, and
5498
+ * children land after every entry here.
5499
+ */
5500
+ views?: readonly CanvasViewProps[];
5234
5501
  /**
5235
5502
  * Children rendered alongside the canvas. Useful for siblings that need
5236
5503
  * the same `<ActionsProvider>` scope (e.g. shortcuts overlays, probes).
@@ -5572,17 +5839,6 @@ declare const inferredNodeProperties: readonly NodePropertiesEntry[];
5572
5839
  * in sync by hand where it matters (DPR handling, viewToMat3 wrap).
5573
5840
  */
5574
5841
 
5575
- /**
5576
- * Per-node draw function. Mirrors the scene-slot `drawOne` signature on
5577
- * `<SceneCanvas>` (`SceneSlotConfig.drawOne`) so consumers can reuse the
5578
- * same callback (or a simplified variant) between a main canvas and a
5579
- * detached scene-view canvas.
5580
- *
5581
- * The function is called once per node in `scene.renderOrder()`. Returned
5582
- * commands are in world coords; the caller's `view` is applied at the
5583
- * group level (see `renderSceneToCanvas`'s implementation).
5584
- */
5585
- type SceneViewDrawOne<TData, TLayer extends string, TPose> = (node: Node$1<TData, TLayer, TPose>, pose: TPose, view: View) => DrawCommand[];
5586
5842
  /** What to draw into an existing canvas: the scene, the view, and the same
5587
5843
  * painting hooks `<SceneCanvas>` takes. */
5588
5844
  interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {
@@ -5706,6 +5962,14 @@ declare const SceneViewCanvas: typeof SceneViewCanvasInner;
5706
5962
  * this in tests, in headless rendering, or wherever a scene must outlive a
5707
5963
  * component. Throws if no system layers are declared. */
5708
5964
  declare function createScene<TData, TLayer extends string, TPose = RectPose>(options: UseSceneOptions<TData, TLayer, TPose>): Scene<TData, TLayer, TPose>;
5965
+ /** The scene's selection in the shape `createHistory` wants, for a consumer
5966
+ * building a second History over the same scene (a modality machine, a
5967
+ * journal owner) that should restore selection the way the scene's own
5968
+ * history does. */
5969
+ declare function sceneSelectionStore(scene: {
5970
+ getSelection(): readonly NodeId[];
5971
+ setSelection(ids: readonly NodeId[]): void;
5972
+ }): HistorySelection;
5709
5973
  /** Reconstruct a Scene from a JSON snapshot produced by `scene.toJSON()`.
5710
5974
  * Function fields (e.g., `clipFromPose`) are resolved by string key via the
5711
5975
  * registry passed in `options`. Throws on unknown version, unknown registry
@@ -5732,12 +5996,16 @@ interface UseSceneTrivialOptions<TItem extends {
5732
5996
  items: readonly TItem[];
5733
5997
  historyLimit?: number;
5734
5998
  generateId?: () => NodeId;
5999
+ /** Re-render the host on every scene mutation. Default `true`. See
6000
+ * {@link UseSceneOptions.subscribe}. */
6001
+ subscribe?: boolean;
5735
6002
  }
5736
6003
  declare const DEFAULT_LAYER: "default";
5737
6004
  type DefaultLayer = typeof DEFAULT_LAYER;
5738
6005
  /** React hook returning a kit-owned `Scene`. The Scene is constructed once
5739
6006
  * per host component and tracked via `useSyncExternalStore`, so React re-
5740
- * renders on every Scene mutation (including undo/redo).
6007
+ * renders on every Scene mutation (including undo/redo) unless
6008
+ * `subscribe: false` opts out.
5741
6009
  *
5742
6010
  * Two call shapes:
5743
6011
  * - **Trivial**: `useScene({ items })` — one auto-registered system layer
@@ -5855,7 +6123,12 @@ interface MinimapCanvasProps<TData, TLayer extends string, TPose> {
5855
6123
  * can reuse it (typically simplified — just colored AABBs). */
5856
6124
  drawOne: SceneViewDrawOne<TData, TLayer, TPose>;
5857
6125
  /** Fit policy. Defaults to `"scene"` (AABB union of leaf poses). See
5858
- * `MinimapFit` for the full shape. */
6126
+ * `MinimapFit` for the full shape.
6127
+ *
6128
+ * Framing is derived from **document** poses, not `scene.overrides` — a
6129
+ * node moved by an override paints where the override puts it, outside the
6130
+ * frame if it goes there, rather than making the whole minimap rescale on
6131
+ * every frame of a drag or a settle. */
5859
6132
  fit?: MinimapFit<TData, TLayer, TPose>;
5860
6133
  /** Pose → AABB. Defaults to identity (`pose as Bounds`), matching
5861
6134
  * `sceneAdapter` / `useSelectTool`. */
@@ -5914,9 +6187,10 @@ interface RenderSceneToPixelsArgs<TData, TLayer extends string, TPose> {
5914
6187
  y: number;
5915
6188
  };
5916
6189
  /** Per-node draw callback. Default: `defaultDrawOne` with `resolveImage`
5917
- * threaded as its `NodePaintCtx`. Custom `drawOne` callers that still
5918
- * want resolver injection should call `defaultDrawOne(node, pose, ctx)`
5919
- * themselves. */
6190
+ * merged into the `NodePaintCtx` the scene walk supplies. Custom `drawOne`
6191
+ * callers that still want resolver injection should call
6192
+ * `defaultDrawOne(node, pose, view, { ...ctx, resolveImage })` themselves
6193
+ * dropping the walk's `ctx` drops derived geometry with it. */
5920
6194
  drawOne?: SceneViewDrawOne<TData, TLayer, TPose>;
5921
6195
  /** Bitmap resolver for image nodes — lets consumers reuse their own decode
5922
6196
  * caches. `undefined` results paint the deterministic grey placeholder
@@ -6397,8 +6671,6 @@ declare const PATH_ANCHOR_CHROME_ID = "path-edit.anchors";
6397
6671
  */
6398
6672
  declare function createPathAnchorAffordances(getAnchorState: () => AnchorState | null, opts?: PathAnchorAffordanceOptions): Affordance[];
6399
6673
 
6400
- /** Pluggable text-painting strategy. The default fills white at `(x, y)`; markdown renderers replace this. */
6401
- type TextRenderer = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number) => void;
6402
6674
  /** Visual options for `renderLabel`. */
6403
6675
  interface LabelOptions {
6404
6676
  align?: 'center' | 'left';
@@ -6420,112 +6692,6 @@ declare function renderLabel(ctx: CanvasRenderingContext2D, text: string, x: num
6420
6692
  /** Default `TextRenderer` used by `renderLabel`: white fill, no styling. */
6421
6693
  declare const defaultLabelTextRenderer: TextRenderer;
6422
6694
 
6423
- /** Width-measurement strategy for `layoutMarkdown`; canvas-backed default supplied by `createMarkdownRenderer`. */
6424
- type MeasureFn = (text: string, fontSize: number, bold: boolean, italic: boolean) => number;
6425
- /** A `StyledRun` with its computed x-offset relative to the start of its line. */
6426
- interface PositionedRun extends StyledRun {
6427
- x: number;
6428
- }
6429
- /** A single laid-out line of text: its positioned runs, total width, and computed line height. */
6430
- interface LayoutLine {
6431
- runs: PositionedRun[];
6432
- width: number;
6433
- height: number;
6434
- }
6435
- /** Output of `layoutMarkdown`: per-line breakdown plus overall block dimensions. */
6436
- interface LayoutResult {
6437
- lines: LayoutLine[];
6438
- width: number;
6439
- height: number;
6440
- }
6441
- /** Word-wrap parsed runs into lines bounded by `maxWidth`; pass `Infinity` for single-line layout. */
6442
- declare function layoutMarkdown(runs: StyledRun[], maxWidth: number, fontSize: number, measure: MeasureFn, lineHeightFactor?: number): LayoutResult;
6443
-
6444
- /** Font styling options threaded through `createMarkdownRenderer`. */
6445
- interface MarkdownFontOptions {
6446
- /** Font-family spec (e.g. `'"Iowan Old Style", Georgia, serif'`). Defaults to `sans-serif`. */
6447
- family?: string;
6448
- /** Numeric weight applied to non-bold runs. Bold runs always use `bold`. Default `normal`. */
6449
- weight?: string | number;
6450
- /** Override fill color. When set, used for all runs (italic and bold). */
6451
- color?: string;
6452
- /** Multiplier applied to font size for line height. Default 1.3. */
6453
- lineHeight?: number;
6454
- }
6455
- /** Build a fill+stroke `TextRenderer` pair for a markdown string at the given size; pre-computes layout once. */
6456
- declare function createMarkdownRenderer(ctx: CanvasRenderingContext2D, text: string, fontSize: number, maxWidth?: number, fontOpts?: MarkdownFontOptions): {
6457
- renderer: TextRenderer;
6458
- strokeRenderer: TextRenderer;
6459
- width: number;
6460
- height: number;
6461
- };
6462
-
6463
- /**
6464
- * Wrap-aware text measurement. Greedy word-wrap against `maxWidth` using
6465
- * `ctx.measureText`. Lines explicitly broken by `\n` are preserved; long
6466
- * single words that exceed `maxWidth` are emitted on their own line without
6467
- * mid-word breaking (caller can decide to clip).
6468
- *
6469
- * Returns the laid-out lines, the total block height in world units
6470
- * (`lines.length * fontSize * lineHeight`), and per-line `lineStarts` —
6471
- * the offset of each line's first character in the original `text`. The
6472
- * starts are used by `caretIndexAt` to map a clicked (x, y) back to a
6473
- * character offset in the source string. Trailing whitespace consumed by
6474
- * the wrap is not included in `lines[i]` but is implicit in the gap
6475
- * between `lineStarts[i] + lines[i].length` and `lineStarts[i + 1]`.
6476
- *
6477
- * The caller owns the `ctx.font` setup — pass a context whose `font`
6478
- * already matches `style` (use `fontString(style)`).
6479
- */
6480
-
6481
- /**
6482
- * Advance width of `text` in world units, tracking included.
6483
- *
6484
- * `letter-spacing` is not part of the CSS `font` shorthand, so a context
6485
- * whose `font` was set from `fontString(style)` measures glyphs only. The
6486
- * GL path (`layoutRuns`) adds `letterSpacing` after **every** code point
6487
- * including the last, matching CSS, so this does too — and every 2D-side
6488
- * width has to go through here or the two paths disagree about where a line
6489
- * breaks.
6490
- */
6491
- declare function measuredWidth(ctx: CanvasRenderingContext2D, text: string, style: ResolvedTextStyle): number;
6492
- /** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */
6493
- interface MeasuredText {
6494
- lines: string[];
6495
- lineStarts: number[];
6496
- height: number;
6497
- }
6498
- /** Greedy word-wrap text measurement against `maxWidth`; preserves explicit `\n` breaks. */
6499
- declare function measureText(ctx: CanvasRenderingContext2D, text: string, maxWidth: number, style: ResolvedTextStyle): MeasuredText;
6500
-
6501
- /** Options for `measureTextBounds`. */
6502
- interface MeasureTextBoundsOpts {
6503
- /** Wrap width; words exceeding it start a new line. Default `Infinity` (no wrap). */
6504
- maxWidth?: number;
6505
- /** Overrides `style`'s `lineHeight` multiplier for this measurement. */
6506
- lineHeight?: number;
6507
- }
6508
- /**
6509
- * Measure how the GL/MSDF renderer will lay out a single plain-text string,
6510
- * using the registered font atlas metrics. Mirrors {@link textCommand} exactly
6511
- * (`resolveTextStyle` → `resolveRuns` → `layoutRuns`), so the returned bounds
6512
- * match what actually gets drawn — use it to size backgrounds/pills, place
6513
- * labels, or test overlap without guessing widths.
6514
- *
6515
- * Pass `opts.maxWidth` to measure word-wrapped height (e.g. for a fixed-width
6516
- * text box); omitting it measures the unwrapped single-line width. `opts.lineHeight`
6517
- * overrides the style's multiplier without changing `style` itself.
6518
- *
6519
- * Returns `{ width, height }` in the same units as `style.fontSize`. The font
6520
- * must already be registered via `registerFont`; an unregistered family falls
6521
- * back to the atlas fallback glyph (and warns), so register fonts at app boot
6522
- * before measuring.
6523
- */
6524
- declare function measureTextBounds(text: string, style?: TextStyle, opts?: MeasureTextBoundsOpts): {
6525
- width: number;
6526
- height: number;
6527
- };
6528
-
6529
6695
  /**
6530
6696
  * Text RenderLayer. Emits one TextDrawCommand per text node carrying the
6531
6697
  * node's resolved runs and bounding rect. Word wrap and multi-line layout
@@ -6538,135 +6704,70 @@ declare function measureTextBounds(text: string, style?: TextStyle, opts?: Measu
6538
6704
  * `TextDrawCommand`'s contract).
6539
6705
  */
6540
6706
 
6541
- /** Pose for a text node: bounding rect plus the text and optional style. */
6542
- interface TextPose {
6543
- x: number;
6544
- y: number;
6545
- width: number;
6546
- height: number;
6547
- text: string;
6548
- /** Rich-text runs. When present, `runsToPlainText(runs)` must equal `text`. */
6549
- runs?: StyledRun[];
6550
- style?: TextStyle;
6551
- /** Box vertical alignment within `[y, y+height]`. Default 'top'. */
6552
- verticalAlign?: TextVerticalAlign;
6553
- }
6554
6707
  /** Options for `createTextLayer`. */
6555
6708
  interface CreateTextLayerOpts<T> {
6556
6709
  id?: string;
6557
6710
  label?: string;
6558
- getTexts: () => readonly T[];
6559
- getPose: (node: T) => TextPose;
6560
- /** Optional per-node hide hook (e.g., suppress while editing). */
6561
- isHidden?: (node: T) => boolean;
6562
- /** When `true`, each text command is wrapped in a clipped group so any
6563
- * overflow beyond the pose's `(width × height)` is hidden. Default
6564
- * `false` (legacy: text can spill outside the declared bounds). Opt-in
6565
- * so existing consumers who rely on overflow keep working. */
6566
- clipToBounds?: boolean;
6567
- }
6568
- /** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */
6569
- declare function createTextLayer<T>(opts: CreateTextLayerOpts<T>): RenderLayer<unknown>;
6570
-
6571
- /**
6572
- * Hit-testing for text nodes.
6573
- *
6574
- * `pointInTextPose` does a coarse pose-rect test — suitable for click-to-edit
6575
- * where the pose rect is the authoritative bounding box (selection outline,
6576
- * drag target).
6577
- *
6578
- * `caretIndexAt` does the finer test: given a world-space (x, y) inside the
6579
- * pose, returns the corresponding character offset in `pose.text` so the
6580
- * consumer can place the caret on click. Re-runs the wrap to map the click
6581
- * back through `lineStarts`; respects `style.align` for line anchoring.
6582
- */
6583
-
6584
- /** Options for `pointInTextPose`. */
6585
- interface PointInTextPoseOpts {
6586
- /** Extra padding (world units) added to the rect on all sides. Default 0. */
6587
- padding?: number;
6588
- }
6589
- /** Coarse pose-rect hit-test for a text node — suitable for click-to-edit dispatch. */
6590
- declare function pointInTextPose(x: number, y: number, pose: TextPose, opts?: PointInTextPoseOpts): boolean;
6591
- /**
6592
- * Map a world-space point inside `pose` to a character offset into
6593
- * `pose.text` (0..text.length). Clicks above the first line clamp to 0;
6594
- * clicks below the last line clamp to `text.length`. Within a line, the
6595
- * caret lands between two glyphs at whichever side of the glyph midpoint
6596
- * `x` falls on — the standard "snap caret to nearest character boundary"
6597
- * rule.
6598
- *
6599
- * The `ctx` is used only for `measureText`; its `font` is set internally
6600
- * to match the resolved text style. Pass any 2D context (the same one used
6601
- * to render is fine).
6602
- */
6603
- declare function caretIndexAt(ctx: CanvasRenderingContext2D, x: number, y: number, pose: TextPose): number;
6604
-
6605
- /**
6606
- * Polygon-vs-axis-aligned-rect tests. Pure functions; no React/kit deps.
6607
- *
6608
- * `polygon` is a closed polyline given as an ordered vertex array; the
6609
- * closing edge from `polygon[N-1]` to `polygon[0]` is implicit. Even-odd
6610
- * fill rule (matches `pointInPath`).
6611
- */
6612
- /** A 2D point or vector. */
6613
- interface Vec2 {
6614
- x: number;
6615
- y: number;
6616
- }
6617
- /** An axis-aligned rectangle. */
6618
- interface Rect {
6619
- x: number;
6620
- y: number;
6621
- width: number;
6622
- height: number;
6711
+ getTexts: () => readonly T[];
6712
+ getPose: (node: T) => TextPose;
6713
+ /** Optional per-node hide hook (e.g., suppress while editing). */
6714
+ isHidden?: (node: T) => boolean;
6715
+ /** When `true`, each text command is wrapped in a clipped group so any
6716
+ * overflow beyond the pose's `(width × height)` is hidden. Default
6717
+ * `false` (legacy: text can spill outside the declared bounds). Opt-in
6718
+ * so existing consumers who rely on overflow keep working. */
6719
+ clipToBounds?: boolean;
6623
6720
  }
6721
+ /** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */
6722
+ declare function createTextLayer<T>(opts: CreateTextLayerOpts<T>): RenderLayer<unknown>;
6624
6723
 
6625
6724
  /**
6626
- * Where a text node's lines actually sit inside its pose box.
6627
- *
6628
- * A text pose is a *wrap box*, not a bounding box: `"Away"` in a 309-unit-wide
6629
- * box leaves most of the box empty, and anything that treats the pose as the
6630
- * node's extent — picking, lasso, clipping, SVG export — claims that empty
6631
- * space. `textLineBoxes` returns the per-line rectangles instead.
6725
+ * Hit-testing for text nodes.
6632
6726
  *
6633
- * The numbers come from `layoutRuns`, the same walk that positions the glyphs,
6634
- * through the same `resolveTextStyle` `resolveRuns` `layoutRuns` chain as
6635
- * `textCommand` — so the boxes cannot drift from what is painted. In
6636
- * particular they honor `align` (a centered line reports its own span, not the
6637
- * wrap width) and `verticalAlign` (the block shifts inside `[y, y + height]`
6638
- * exactly as `drawText` shifts the quads).
6727
+ * `pointInTextPose` does a coarse pose-rect test suitable for click-to-edit
6728
+ * where the pose rect is the authoritative bounding box (selection outline,
6729
+ * drag target).
6639
6730
  *
6640
- * These are line boxes, not ink boxes: each is `fontSize * lineHeight` tall
6641
- * from the pen's line top. Ink can escape vertically at a `lineHeight` low
6642
- * enough see the `bounds` note at the end of `layoutRuns` which is the
6643
- * right trade for hit-testing, where a box that hugged the ink would make
6644
- * an `x` harder to click than an `X`.
6731
+ * `caretIndexAt` does the finer test: given a world-space (x, y) inside the
6732
+ * pose, returns the corresponding character offset so the consumer can place
6733
+ * the caret on click. It reads the caret stops off the same
6734
+ * `cachedLayoutRuns` result the renderer paints and `textLineBoxes` picks
6735
+ * against, so the caret cannot land on a different line — or between
6736
+ * different glyphs — than the one under the pointer.
6645
6737
  */
6646
6738
 
6647
- /** Options for {@link textLineBoxes}. */
6648
- interface TextLineBoxesOpts {
6649
- /** Grow every box by this much on all four sides (world units). Default 0.
6650
- * Picking wants a little slack so a single hairline row of text is still
6651
- * grabbable; clipping and export want none. */
6739
+ /** Options for `pointInTextPose`. */
6740
+ interface PointInTextPoseOpts {
6741
+ /** Extra padding (world units) added to the rect on all sides. Default 0. */
6652
6742
  padding?: number;
6653
- /** Keep boxes for blank lines (zero width). Default `false` — a blank line
6654
- * covers no area, so for hit-testing and silhouettes it is noise. Pass
6655
- * `true` when the indices have to line up with the wrapped lines. */
6656
- includeEmpty?: boolean;
6743
+ }
6744
+ /** Coarse pose-rect hit-test for a text node suitable for click-to-edit dispatch. */
6745
+ declare function pointInTextPose(x: number, y: number, pose: TextPose, opts?: PointInTextPoseOpts): boolean;
6746
+ /** Options for `caretIndexAt`. */
6747
+ interface CaretIndexAtOpts {
6657
6748
  /**
6658
6749
  * Wrap width. Default `pose.width`, which is what `createTextLayer` passes
6659
6750
  * and what `TextPose` means by its box.
6660
6751
  *
6661
6752
  * Pass `Infinity` for a node painted by the built-in `kit:text` painter:
6662
- * that painter deliberately does **not** forward `maxWidth` (see
6663
- * `NodeShape.ts`), so its text does not wrap, and boxes computed with a
6664
- * finite width would wrap where the paint did not.
6753
+ * that painter deliberately does not forward `maxWidth`, so its text does
6754
+ * not wrap, and a caret mapped through a finite width would answer for a
6755
+ * line break the paint never made. Mirrors `textLineBoxes`.
6665
6756
  */
6666
6757
  maxWidth?: number;
6667
6758
  }
6668
- /** Per-line rectangles for a text pose, in world units, in layout order. */
6669
- declare function textLineBoxes(pose: TextPose, opts?: TextLineBoxesOpts): Rect[];
6759
+ /**
6760
+ * Map a world-space point inside `pose` to a character offset into the pose's
6761
+ * text (0..length). Clicks above the first line clamp to 0; clicks below the
6762
+ * last line clamp to the end. Within a line, the caret lands between two
6763
+ * glyphs at whichever side of the advance cell's midpoint `x` falls on — the
6764
+ * standard "snap caret to nearest character boundary" rule.
6765
+ *
6766
+ * Honors `pose.runs` (a mixed-size line snaps on the cells each run actually
6767
+ * produced) and `pose.verticalAlign`. The offset is into the runs'
6768
+ * concatenated text, which `TextPose` requires to equal `pose.text`.
6769
+ */
6770
+ declare function caretIndexAt(x: number, y: number, pose: TextPose, opts?: CaretIndexAtOpts): number;
6670
6771
 
6671
6772
  /**
6672
6773
  * Resize a text node's pose to fit its content. Pure helper — does not own
@@ -6700,7 +6801,60 @@ interface FitTextPoseOptions {
6700
6801
  };
6701
6802
  }
6702
6803
  /** Recompute a `TextPose`'s `width`/`height` to fit its content; pure helper, doesn't mutate scene state. */
6703
- declare function fitTextPose(ctx: CanvasRenderingContext2D, pose: TextPose, opts?: FitTextPoseOptions): TextPose;
6804
+ declare function fitTextPose(pose: TextPose, opts?: FitTextPoseOptions): TextPose;
6805
+
6806
+ /**
6807
+ * Turning a run-level flag **off** inside a node that sets it.
6808
+ *
6809
+ * Run flags are additive over the node's `TextStyle` — a run turns `bold` /
6810
+ * `italic` / `underline` / `strikethrough` on, never off (see the contract at
6811
+ * the top of `rangeStyle.ts`). So "select a word inside an underlined node and
6812
+ * hit U" is unrepresentable by the stored shape, and the character bar can
6813
+ * only refuse.
6814
+ *
6815
+ * This resolves it by rewriting rather than by widening the model: clear the
6816
+ * flag on the node, and set it on every run *outside* the range. The rendered
6817
+ * result is identical and the edit becomes expressible, with `StyledRun`
6818
+ * unchanged — so nothing a document can already contain changes meaning, which
6819
+ * matters while the flags are heading for a persisted format.
6820
+ *
6821
+ * The alternative was a tri-state run flag (`true` / `false` / inherit). It
6822
+ * cannot cover `bold` or `italic`: those are booleans on a run but
6823
+ * `fontWeight` and `fontStyle` on the node, so a run's `false` has no node-level
6824
+ * boolean to override. Tri-state fixes two of the five flags; this fixes all
6825
+ * five.
6826
+ */
6827
+
6828
+ /** The additive run flags. */
6829
+ type FlagKey = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'overline';
6830
+ /** The result of toggling a style flag over a text range: the rewritten runs
6831
+ * and node style, and whether the change could be made at all. */
6832
+ interface SetFlagResult {
6833
+ runs: StyledRun[];
6834
+ style: TextStyle;
6835
+ /**
6836
+ * False when the node flag could not be lowered without changing what is
6837
+ * drawn, and nothing was written. The only case is a `fontWeight` the run
6838
+ * boolean cannot express: `run.bold` resolves to exactly 700 everywhere, so
6839
+ * a node at 900 cannot have its weight pushed onto its runs. Callers should
6840
+ * disable the control rather than apply a silent downgrade.
6841
+ */
6842
+ applied: boolean;
6843
+ }
6844
+ /** Does the node style carry this flag? */
6845
+ declare function nodeHasFlag(style: TextStyle, key: FlagKey): boolean;
6846
+ /**
6847
+ * Set `key` to `value` over `[start, end)`.
6848
+ *
6849
+ * Turning a flag **on**, or off in a node that doesn't set it, is the ordinary
6850
+ * additive write and leaves `style` alone. Turning it off in a node that *does*
6851
+ * set it takes the rewrite: the node flag is cleared and the flag is written
6852
+ * onto the complement of the range.
6853
+ *
6854
+ * `runs` is normalized on every path, so the complement collapses back to one
6855
+ * run when the range is empty and the whole array coalesces as usual.
6856
+ */
6857
+ declare function setFlagOverRange(runs: readonly StyledRun[], style: TextStyle, start: number, end: number, key: FlagKey, value: boolean): SetFlagResult;
6704
6858
 
6705
6859
  /**
6706
6860
  * The kit-wide "these values disagree" sentinel. Used wherever a value is
@@ -6791,6 +6945,13 @@ declare function applyStyleToRange(runs: readonly StyledRun[], start: number, en
6791
6945
  * (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
6792
6946
  */
6793
6947
 
6948
+ type StyleFlag = FlagKey;
6949
+ /**
6950
+ * A styling a shortcut or a control toggles. The additive booleans, plus the
6951
+ * two `script` values — which are exclusive rather than additive, so they
6952
+ * toggle against one enum rather than each owning a boolean.
6953
+ */
6954
+ type StyleToggle = StyleFlag | 'super' | 'sub';
6794
6955
  /**
6795
6956
  * The caret's character range within the text being edited. Half-open
6796
6957
  * `[start, end)` over the concatenated run text, normalized so `start <= end`
@@ -6845,6 +7006,13 @@ interface UseTextEditOptions {
6845
7006
  getText: (id: string) => string;
6846
7007
  /** Read style for `id` (used for font setup on the overlay). */
6847
7008
  getStyle: (id: string) => TextStyle | undefined;
7009
+ /**
7010
+ * Read the node's paint for `id` — its `data.fill` / `data.stroke`. The
7011
+ * overlay paints its text and caret from the fill, so omitting this
7012
+ * renders every node's editor in the default black however the node
7013
+ * itself is painted.
7014
+ */
7015
+ getPaint?: (id: string) => TextPaint | undefined;
6848
7016
  /**
6849
7017
  * Write style back for `id`. Optional; needed only to turn a flag **off**
6850
7018
  * inside a node whose own `TextStyle` sets it. Run flags are additive, so
@@ -6909,8 +7077,8 @@ interface UseTextEditReturn {
6909
7077
  /**
6910
7078
  * The caret's character range, or `null` when nothing is being edited. A
6911
7079
  * collapsed caret reports `{ start: n, end: n }`, so `null` and "caret at
6912
- * n" stay distinguishable — a character-styling control routes the
6913
- * collapsed case to the node's `TextStyle` instead of to a range.
7080
+ * n" stay distinguishable — a styling written at a collapsed caret arms
7081
+ * {@link UseTextEditReturn.pendingStyle} instead of restyling a range.
6914
7082
  *
6915
7083
  * Follows the DOM selection, which browsers (and jsdom) report from a task
6916
7084
  * rather than synchronously; anything this hook writes itself updates it
@@ -6924,33 +7092,82 @@ interface UseTextEditReturn {
6924
7092
  /**
6925
7093
  * The styling shared by every run in `selection` — a concrete value where
6926
7094
  * the range agrees, `MIXED` where it doesn't. `null` exactly when
6927
- * `selection` is `null`. A collapsed caret reports `{}`: no run is in
6928
- * range, so the range reader has nothing to say and the node's style is
6929
- * what applies.
7095
+ * `selection` is `null`. A collapsed caret reports the styling *at* the
7096
+ * caret (the run to its left, or the run to its right at offset 0), which
7097
+ * is what the next typed character inherits.
7098
+ *
7099
+ * This does not include {@link UseTextEditReturn.pendingStyle}. A control
7100
+ * showing what the next character will look like wants both, merged in
7101
+ * that order; one showing what is already written wants only this.
6930
7102
  */
6931
7103
  rangeStyle: RangeStyle | null;
6932
7104
  /**
6933
- * Write `patch` over `selection`. A no-op with no active edit, with a
6934
- * collapsed caret (there is no range to stylepatch the node's
6935
- * `TextStyle` instead), or with an empty patch. The caret survives, so a
6936
- * second style can be applied without re-selecting, and `rangeStyle`
6937
- * reflects the write before this returns.
7105
+ * Styling armed at a collapsed caret, applied to the next character typed
7106
+ * and then dropped. `{}` when nothing is armedwhich is always the case
7107
+ * while `selection` covers a real range, since a range is styled directly.
7108
+ *
7109
+ * Moving the caret abandons it, as in any other editor.
7110
+ */
7111
+ pendingStyle: RunStylePatch;
7112
+ /**
7113
+ * Write `patch` over `selection`. A no-op with no active edit or an empty
7114
+ * patch. Over a real range this restyles the runs under it; at a collapsed
7115
+ * caret it merges into `pendingStyle` instead, so the styling lands on
7116
+ * what gets typed next rather than on text the user didn't select.
7117
+ *
7118
+ * Lowering a flag the *node* sets is neither of those — a run cannot say
7119
+ * "not bold" — so it rewrites instead: the node flag is cleared and raised
7120
+ * on every run outside the range. That path can decline (a node at
7121
+ * `fontWeight: 900` has no run boolean to move it to), in which case
7122
+ * nothing is written.
7123
+ *
7124
+ * The caret survives, so a second style can be applied without
7125
+ * re-selecting, and `rangeStyle` reflects the write before this returns.
6938
7126
  */
6939
7127
  applyStyleToSelection: (patch: RunStylePatch) => void;
7128
+ /**
7129
+ * Toggle one styling over `selection` — set it if the range doesn't
7130
+ * uniformly carry it, clear it if it does. The shape a B / I / x² control
7131
+ * or a keyboard shortcut wants, and what this hook's own shortcuts call.
7132
+ */
7133
+ toggleStyle: (toggle: StyleToggle) => void;
6940
7134
  }
6941
7135
  /** In-place text editing via a contenteditable overlay positioned over the text node's screen-space pose. */
6942
7136
  declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
6943
7137
 
6944
7138
  /**
6945
- * Compute the union AABB of a set of rectangle-shaped poses.
7139
+ * Union math over rect-shaped poses.
6946
7140
  *
6947
- * Returns null when the input is empty. Useful for selection overlay
6948
- * rendering of groups (where the displayed bounds are the envelope of
6949
- * all transitive leaf members).
7141
+ * Two answers, deliberately distinct. `unionBounds` folds the *unrotated*
7142
+ * boxes and is what commit-time actions want, because they go on to write
7143
+ * poses back in that same frame. `unionAABB` expands each rotated member to
7144
+ * the extent of its ink first, and is what anything a user looks at or clicks
7145
+ * wants — selection chrome, gesture bounds, an export viewBox. Folding the
7146
+ * unrotated box there under-reports the extent of a rotated member exactly
7147
+ * when the consumer is watching it move.
6950
7148
  */
6951
7149
 
6952
- /** Compute the AABB envelope of a set of rect-shaped poses; returns null when empty. */
7150
+ /** Compute the AABB envelope of a set of rect-shaped poses, ignoring any
7151
+ * rotation they carry. See the module docstring before reaching for this
7152
+ * over {@link unionAABB}. */
6953
7153
  declare function unionBounds<TPose extends RectPose>(poses: Iterable<TPose>): RectPose | null;
7154
+ /**
7155
+ * Axis-align a pose that carries a `rotation`: returns the AABB of the
7156
+ * rotated rectangle, with `rotation` dropped. Follows the kit's rotation
7157
+ * convention (`poseRotationOf`): rotate about the unrotated AABB center.
7158
+ *
7159
+ * Unrotated input is returned as-is.
7160
+ */
7161
+ declare function axisAlignedBounds<TPose extends RectPose>(b: TPose): RectPose;
7162
+ /**
7163
+ * Fold every part into one world-space AABB, skipping `null` / `undefined`
7164
+ * entries. Rotated parts are expanded via {@link axisAlignedBounds} first;
7165
+ * the result never carries a `rotation` (a union of several oriented boxes
7166
+ * has no single orientation to report).
7167
+ *
7168
+ * Returns `null` when nothing was contributed.
7169
+ */
7170
+ declare function unionAABB<TPose extends RectPose>(parts: Iterable<TPose | null | undefined>): RectPose | null;
6954
7171
 
6955
7172
  /**
6956
7173
  * `useSceneTextEdit` — scene-aware wrapper around `useTextEdit`.
@@ -6983,6 +7200,8 @@ interface DefaultTextData {
6983
7200
  text?: string;
6984
7201
  style?: TextStyle;
6985
7202
  runs?: readonly StyledRun[];
7203
+ fill?: FillStyle | null;
7204
+ stroke?: Stroke | null;
6986
7205
  }
6987
7206
  /** All-optional projections + fontSize fallback. */
6988
7207
  interface UseSceneTextEditOptions<TData> {
@@ -6990,6 +7209,8 @@ interface UseSceneTextEditOptions<TData> {
6990
7209
  getText?: (data: TData) => string;
6991
7210
  /** Read style from `data`. Default: `data.style`. */
6992
7211
  getStyle?: (data: TData) => TextStyle | undefined;
7212
+ /** Read the node's paint from `data`. Default: `data.fill` / `data.stroke`. */
7213
+ getPaint?: (data: TData) => TextPaint | undefined;
6993
7214
  /** Read rich-text runs from `data`. Default: `data.runs`. */
6994
7215
  getRuns?: (data: TData) => readonly StyledRun[] | undefined;
6995
7216
  /** Produce updated data with new text. Default: `{ ...data, text }`. */
@@ -7017,8 +7238,13 @@ interface UseSceneTextEditOptions<TData> {
7017
7238
  * The overlay takes a single scale factor, so a non-uniform view scale is
7018
7239
  * represented by its `scale.x`; text under `scale.x !== scale.y` will not
7019
7240
  * match the canvas.
7241
+ *
7242
+ * A thunk is re-read on every projection, which is what an uncontrolled
7243
+ * `SceneCanvas` needs — its camera lives in a ref and moves without a
7244
+ * render, so pass the handle's `getView`. A plain `View` is the value from
7245
+ * the render that supplied it, which is correct for a controlled consumer.
7020
7246
  */
7021
- view?: View;
7247
+ view?: View | (() => View);
7022
7248
  /**
7023
7249
  * Forwarded to `useTextEdit`: is `el` part of the editor's own chrome?
7024
7250
  * Focus moving into it does not end the edit. Wire it to whatever renders
@@ -7041,59 +7267,6 @@ interface UseSceneTextEditReturn extends UseTextEditReturn {
7041
7267
  * over the node being edited and writes the result back as an undoable op. */
7042
7268
  declare function useSceneTextEdit<TData extends DefaultTextData, TLayer extends string, TPose extends RectPose = RectPose>(scene: Scene<TData, TLayer, TPose>, container: HTMLElement | null, options?: UseSceneTextEditOptions<TData>): UseSceneTextEditReturn;
7043
7269
 
7044
- /**
7045
- * Turning a run-level flag **off** inside a node that sets it.
7046
- *
7047
- * Run flags are additive over the node's `TextStyle` — a run turns `bold` /
7048
- * `italic` / `underline` / `strikethrough` on, never off (see the contract at
7049
- * the top of `rangeStyle.ts`). So "select a word inside an underlined node and
7050
- * hit U" is unrepresentable by the stored shape, and the character bar can
7051
- * only refuse.
7052
- *
7053
- * This resolves it by rewriting rather than by widening the model: clear the
7054
- * flag on the node, and set it on every run *outside* the range. The rendered
7055
- * result is identical and the edit becomes expressible, with `StyledRun`
7056
- * unchanged — so nothing a document can already contain changes meaning, which
7057
- * matters while the flags are heading for a persisted format.
7058
- *
7059
- * The alternative was a tri-state run flag (`true` / `false` / inherit). It
7060
- * cannot cover `bold` or `italic`: those are booleans on a run but
7061
- * `fontWeight` and `fontStyle` on the node, so a run's `false` has no node-level
7062
- * boolean to override. Tri-state fixes two of the four flags; this fixes all
7063
- * four.
7064
- */
7065
-
7066
- /** The additive run flags. */
7067
- type FlagKey = 'bold' | 'italic' | 'underline' | 'strikethrough';
7068
- /** The result of toggling a style flag over a text range: the rewritten runs
7069
- * and node style, and whether the change could be made at all. */
7070
- interface SetFlagResult {
7071
- runs: StyledRun[];
7072
- style: TextStyle;
7073
- /**
7074
- * False when the node flag could not be lowered without changing what is
7075
- * drawn, and nothing was written. The only case is a `fontWeight` the run
7076
- * boolean cannot express: `run.bold` resolves to exactly 700 everywhere, so
7077
- * a node at 900 cannot have its weight pushed onto its runs. Callers should
7078
- * disable the control rather than apply a silent downgrade.
7079
- */
7080
- applied: boolean;
7081
- }
7082
- /** Does the node style carry this flag? */
7083
- declare function nodeHasFlag(style: TextStyle, key: FlagKey): boolean;
7084
- /**
7085
- * Set `key` to `value` over `[start, end)`.
7086
- *
7087
- * Turning a flag **on**, or off in a node that doesn't set it, is the ordinary
7088
- * additive write and leaves `style` alone. Turning it off in a node that *does*
7089
- * set it takes the rewrite: the node flag is cleared and the flag is written
7090
- * onto the complement of the range.
7091
- *
7092
- * `runs` is normalized on every path, so the complement collapses back to one
7093
- * run when the range is empty and the whole array coalesces as usual.
7094
- */
7095
- declare function setFlagOverRange(runs: readonly StyledRun[], style: TextStyle, start: number, end: number, key: FlagKey, value: boolean): SetFlagResult;
7096
-
7097
7270
  /**
7098
7271
  * Build a `TextDrawCommand`. `textCommand` takes a plain string and wraps it
7099
7272
  * in one run; `textCommandFromRuns` takes the styled runs directly. Both
@@ -7101,12 +7274,16 @@ declare function setFlagOverRange(runs: readonly StyledRun[], style: TextStyle,
7101
7274
  * text command derives `align` and per-run resolution the same way — a
7102
7275
  * caller that assembles the command by hand is one field away from a
7103
7276
  * silently different result.
7277
+ *
7278
+ * `paint` is the node's `data.fill` / `data.stroke`. A caller with no node —
7279
+ * a HUD widget, a debug overlay — states its color on the run instead.
7104
7280
  */
7105
7281
 
7282
+ declare function textCommandFromRuns(x: number, y: number, runs: readonly StyledRun[], style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign, paint?: TextPaint): DrawCommand;
7106
7283
  /** Build a draw command for a single unstyled string. Text is laid out with
7107
7284
  * the registered font's metrics, wrapped at `maxWidth` and aligned within
7108
7285
  * `height` when both are given. */
7109
- declare function textCommand(x: number, y: number, text: string, style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign): DrawCommand;
7286
+ declare function textCommand(x: number, y: number, text: string, style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign, paint?: TextPaint): DrawCommand;
7110
7287
 
7111
7288
  /**
7112
7289
  * Pure DOM ↔ `StyledRun[]` serializers for the contenteditable overlay
@@ -7148,7 +7325,7 @@ declare function domPositionToCharOffset(parent: HTMLElement, node: Node, offset
7148
7325
  /**
7149
7326
  * Tile-pattern primitive — a small helper for building repeating texture
7150
7327
  * tiles from a draw callback. The result plugs into a `FillStyle` of
7151
- * `{ fill: 'pattern', pattern }` (see `../../core/paint-types.ts`).
7328
+ * `{ fill: 'pattern', pattern }` (see `@weasel-js/paint`).
7152
7329
  *
7153
7330
  * GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback
7154
7331
  * to a regular `<canvas>` for environments without `OffscreenCanvas`),
@@ -7210,13 +7387,16 @@ declare function resolveFillPattern(fill: FillStyle): FillStyle | null;
7210
7387
  * `withGradientKind` exists to translate.
7211
7388
  */
7212
7389
 
7390
+ /** Narrow a paint to the gradient members of `FillStyle`. The three
7391
+ * discriminants are the union's own definition of `GradientFill`, so every
7392
+ * consumer that reaches for gradient geometry starts here. */
7393
+ declare function isGradientFill(fill: FillStyle | null | undefined): fill is GradientFill;
7213
7394
  /**
7214
- * Color at position `t` (0..1) along a stop list, matching how the GL ramp
7215
- * texture is built: stops sorted by offset, flat extension past either end,
7216
- * linear RGB interpolation between neighbors.
7395
+ * Color at position `t` (0..1) along a stop list, as a hex string — the same
7396
+ * sampling the GL ramp texture bakes, so an editor track and the painted
7397
+ * gradient cannot disagree.
7217
7398
  *
7218
- * Returns transparent black for an empty list. Alpha is interpolated too, so
7219
- * a stop list fading to `transparent` samples correctly.
7399
+ * Returns transparent black for an empty list.
7220
7400
  */
7221
7401
  declare function sampleGradientStops(stops: readonly GradStop[], t: number): string;
7222
7402
  /**
@@ -7253,9 +7433,10 @@ declare function gradientGeometry(fill: GradientFill): {
7253
7433
  angle: number;
7254
7434
  };
7255
7435
  /**
7256
- * A gradient spanning a box corner-to-corner (linear) or filling it
7257
- * (radial / conic) — the sensible starting geometry when a consumer turns a
7258
- * solid fill into a gradient and has only the shape's bounds to go on.
7436
+ * A gradient spanning a box left edge to right edge through its vertical
7437
+ * center (linear) or filling it (radial / conic) — the sensible starting
7438
+ * geometry when a consumer turns a solid fill into a gradient and has only
7439
+ * the shape's bounds to go on.
7259
7440
  */
7260
7441
  declare function gradientForBounds(kind: GradientKind, bounds: {
7261
7442
  x: number;
@@ -7341,16 +7522,40 @@ interface TransformArgs<TPose> {
7341
7522
  * coalesce window. Pass an explicit key (or a unique one) to opt out. */
7342
7523
  declare function createTransformOp<TPose>(args: TransformArgs<TPose>): Op;
7343
7524
 
7525
+ interface Slot {
7526
+ /** Ordinal among the siblings. `-1` means "nowhere in particular". */
7527
+ index: number;
7528
+ /** Id of the sibling that followed at capture time; `null` when the node
7529
+ * was last. Absent when nothing observed it — `null` survives JSON and
7530
+ * `undefined` does not, so the two stay distinguishable across a
7531
+ * serialize/restore round trip. */
7532
+ before?: string | null;
7533
+ }
7534
+
7344
7535
  /** @internal */
7345
7536
  interface ReparentArgs {
7346
7537
  id: string;
7347
7538
  fromParentId: string | null;
7348
7539
  toParentId: string | null;
7540
+ /** Sibling ordinal under the old parent. Sugar for `fromSlot: { index }`,
7541
+ * and only a seed: `apply` re-observes the node's full slot through the
7542
+ * adapter and that observation supersedes this. */
7543
+ fromIndex?: number;
7544
+ /** Sibling ordinal under the new parent. Sugar for `toSlot: { index }`.
7545
+ * Omit to append. */
7546
+ toIndex?: number;
7547
+ /** Slot under the old parent, anchor included. `invert()` forwards it as
7548
+ * the destination slot; without it undo re-parents with no position and
7549
+ * the node lands last, quietly changing paint order. */
7550
+ fromSlot?: Slot;
7551
+ /** Slot under the new parent, anchor included. Omit to append. */
7552
+ toSlot?: Slot;
7349
7553
  label?: string;
7350
7554
  coalesceKey?: string;
7351
7555
  }
7352
7556
  /**
7353
- * Op: change `id`'s parent, inverting back to the prior parent.
7557
+ * Op: change `id`'s parent and sibling slot, inverting back to the prior
7558
+ * parent and slot.
7354
7559
  *
7355
7560
  * `coalesceKey` defaults to `reparent:${id}` so successive reparents of the
7356
7561
  * same id batch-merge cleanly.
@@ -7366,32 +7571,46 @@ interface InsertArgs<TNode extends {
7366
7571
  }> {
7367
7572
  node: TNode;
7368
7573
  label?: string;
7369
- /** Original z-index in the host array. Forwarded to
7370
- * `adapter.insertNode(node, index)` when present so undo of a multi-delete
7371
- * batch restores paint order instead of reversing it. Optional — adapters
7372
- * that don't honor it still work. */
7574
+ /** Sibling ordinal to insert at. Sugar for `slot: { index }` — the weaker
7575
+ * of the two forms, and all a fresh-insert callsite can know. */
7373
7576
  index?: number;
7577
+ /** Full slot, anchor included. Produced only by `captureSlot` (via a
7578
+ * delete op's `invert`), and supersedes `index` when present. */
7579
+ slot?: Slot;
7374
7580
  }
7375
7581
  /** Op: insert `node` into the scene; inverts to a delete of the same id. */
7376
7582
  declare function createInsertOp<TNode extends {
7377
7583
  id: string;
7378
7584
  }>(args: InsertArgs<TNode>): InsertOp;
7379
7585
 
7586
+ /** A captured node plus its slot in its own parent's child list. */
7587
+ interface Placed<TNode> {
7588
+ node: TNode;
7589
+ index: number;
7590
+ }
7380
7591
  /** @internal */
7381
7592
  interface DeleteArgs<TNode extends {
7382
7593
  id: string;
7383
7594
  }> {
7384
7595
  node: TNode;
7385
7596
  label?: string;
7386
- /** Z-index the node occupies in its host array at the moment of delete.
7387
- * Forwarded through `invert()` to the re-insert so undo restores paint
7388
- * order without this, undo of a multi-delete batch fully reverses
7389
- * the stack. Required: every delete callsite knows where the node sits
7390
- * (it has to, to remove it), so there's no good reason to drop the
7391
- * data on the floor. */
7392
- index: number;
7393
- }
7394
- /** Op: remove `node` from the scene; inverts to a re-insert of the captured node. */
7597
+ /** Sibling ordinal the node occupies at the moment of delete. Sugar for
7598
+ * `slot: { index }`, and only a seed: `apply` re-observes the node's full
7599
+ * slot through the adapter and that observation supersedes this. Kept
7600
+ * because it is the whole story for a flat adapter with no `getChildren`,
7601
+ * where nothing can be observed. */
7602
+ index?: number;
7603
+ /** Full slot, anchor included. Written by `apply`; supersedes `index`. */
7604
+ slot?: Slot;
7605
+ /** Descendants of `node`, preorder (parents before children). `removeNode`
7606
+ * cascades the whole subtree, so an inverse that re-inserts `node` alone
7607
+ * brings the container back empty and drops every child on the floor.
7608
+ * Captured on apply; mirrored into the serialized args so a rebuilt op
7609
+ * can invert without having run. */
7610
+ descendants?: Placed<TNode>[];
7611
+ }
7612
+ /** Op: remove `node` and its subtree from the scene; inverts to a re-insert
7613
+ * of the whole subtree at its captured slots. */
7395
7614
  declare function createDeleteOp<TNode extends {
7396
7615
  id: string;
7397
7616
  }>(args: DeleteArgs<TNode>): Op;
@@ -7534,6 +7753,10 @@ declare function registerOpFactory<A>(name: string, build: (args: A) => Op): voi
7534
7753
  * `name` isn't registered — callers handle this (typically by substituting
7535
7754
  * a no-op placeholder so stack ordering survives across kit version skew). */
7536
7755
  declare function rebuildOp(name: string, args: unknown): Op | null;
7756
+ /** Every op name currently registered, in registration order. The kit's
7757
+ * built-ins self-register at module init, so importing the barrel is enough
7758
+ * to see all of them; consumer factories appear once their module has run. */
7759
+ declare function registeredOpNames(): readonly string[];
7537
7760
 
7538
7761
  /** @internal */
7539
7762
  interface HitAdapter<TNode extends {
@@ -7752,6 +7975,20 @@ interface PointInPathOptions {
7752
7975
  /** Filled-region hit-test for a `Path`. Rect short-circuits to AABB; polygons run ray-cast / winding per `fillRule`. */
7753
7976
  declare function pointInPath(path: Path, x: number, y: number, opts?: PointInPathOptions): boolean;
7754
7977
 
7978
+ /**
7979
+ * Polygon-vs-axis-aligned-rect tests. Pure functions; no React/kit deps.
7980
+ *
7981
+ * `polygon` is a closed polyline given as an ordered vertex array; the
7982
+ * closing edge from `polygon[N-1]` to `polygon[0]` is implicit. Even-odd
7983
+ * fill rule (matches `pointInPath`).
7984
+ */
7985
+
7986
+ /** A 2D point or vector. */
7987
+ interface Vec2 {
7988
+ x: number;
7989
+ y: number;
7990
+ }
7991
+
7755
7992
  /**
7756
7993
  * Path-vs-geometry hit-test helpers. Five pure functions covering all
7757
7994
  * combinations of path-vs-point, path-vs-rect, and path-vs-polygon.
@@ -7903,22 +8140,6 @@ declare function worldEditToStorage<P extends PathInWorldPose>(pose: P, worldPat
7903
8140
  path: PolygonPath;
7904
8141
  };
7905
8142
 
7906
- /**
7907
- * The kit's single encoding of the pose-rotation convention: "rotate about the
7908
- * pose's unrotated AABB center by `pose.rotation` (radians)."
7909
- *
7910
- * Every consumer that needs to apply rotation — the render-tree wrap
7911
- * (`canvas/poseRotation.ts`), the world coordinate bake (`pathInWorld`), the
7912
- * silhouette/clip bake (`findShapeSilhouette`), the hit-test inverse
7913
- * (`poseContainsRotated`), and selection chrome — derives its pivot + angle
7914
- * from `poseRotationOf` rather than re-inlining the `if (rotation && x != null
7915
- * …)` gate. If the convention ever changes (different pivot, a transform
7916
- * field, etc.), it changes here.
7917
- *
7918
- * Lives in `features/paths` (the low layer) so both `features` and `canvas`
7919
- * can share it without `features` importing `canvas`.
7920
- */
7921
-
7922
8143
  /** Pivot (`cx`, `cy`) and angle resolved from a pose's rotation convention. */
7923
8144
  interface PoseRotation {
7924
8145
  cx: number;
@@ -8199,8 +8420,9 @@ declare function createPenPreviewLayer(opts: CreatePenPreviewLayerOptions): Rend
8199
8420
  * but a human user needs to see what's hittable.
8200
8421
  *
8201
8422
  * The layer reads the live edit target each frame via the supplied
8202
- * `getEditingId()` + `getPose(id)` thunks. Returns `[]` when no editing
8203
- * target is set or the target isn't a polygon the layer never throws.
8423
+ * `getEditingId()` + `getPose(...)` thunks, resolving the path against the
8424
+ * drawing view's own previews. Returns `[]` when no editing target is set or
8425
+ * the target isn't a polygon — the layer never throws.
8204
8426
  *
8205
8427
  * Anchors render as small white-filled stroked squares — filled solid
8206
8428
  * when selected; control points as small filled circles connected to
@@ -8220,9 +8442,15 @@ interface CreatePathEditingOverlayLayerOptions {
8220
8442
  * when no node is being edited. Read each frame so live selection /
8221
8443
  * edit-target changes show up without re-creating the layer. */
8222
8444
  getEditingId(): string | null;
8223
- /** Returns the pose for an id, or null if the node has been deleted.
8224
- * Non-polygon poses are tolerated (the layer no-ops on them). */
8225
- getPose(id: string): Path | null;
8445
+ /**
8446
+ * Returns the pose for an id, or null if the node has been deleted.
8447
+ * Non-polygon poses are tolerated (the layer no-ops on them).
8448
+ *
8449
+ * `previews` are the drawing view's in-flight preview surfaces, so an
8450
+ * anchor dragged in one panel does not move in the others. Resolve against
8451
+ * them before falling back to the committed pose.
8452
+ */
8453
+ getPose(id: string, previews: readonly GesturePreviewSource[]): Path | null;
8226
8454
  /** Flat indices of the selected anchors. Selected anchors render
8227
8455
  * filled; unselected ones hollow — the standard vector-editor cue for
8228
8456
  * "these are what the arrow keys and Delete will act on". Omit when
@@ -8236,12 +8464,6 @@ interface CreatePathEditingOverlayLayerOptions {
8236
8464
  width: number;
8237
8465
  height: number;
8238
8466
  } | null;
8239
- /** Chrome-caps predicate. The layer asks `'path-edit.anchors'` before
8240
- * drawing anything, so paint and the anchor hit-test in
8241
- * `affordanceAt` consult the same rule — otherwise a consumer that
8242
- * hides the chrome still gets grabbable invisible anchors, or vice
8243
- * versa. Omit to always draw when an edit target is set. */
8244
- isVisible?(chromeId: string): boolean;
8245
8467
  /** Optional styling overrides. */
8246
8468
  style?: PathEditingOverlayStyle;
8247
8469
  }
@@ -8422,6 +8644,301 @@ declare function withAlpha01(color: string, alpha01: number): string;
8422
8644
  * strips the alpha channel and returns a bare 6-char hex. */
8423
8645
  declare function mergeAlphaFromPrev(picked: string, prev: string): string;
8424
8646
 
8647
+ /**
8648
+ * The paint-kind registry — what makes `FillStyle` open.
8649
+ *
8650
+ * A consumer registers a sixth kind and it renders, converts between the
8651
+ * bounds and pose frames, and serializes, with no kit edits. The five built-in
8652
+ * kinds are registered here at module load so an editor's kind bar can
8653
+ * enumerate every kind through one list.
8654
+ *
8655
+ * Each kit layer consults this registry for a kind it does not recognize and
8656
+ * otherwise runs its own built-in branch. That split is not laziness: a built-in
8657
+ * kind's render slot lives in the GL renderer, its serialize slot lives in
8658
+ * `@weasel-js/svg`, and neither can be imported from here without inverting a
8659
+ * package dependency.
8660
+ */
8661
+
8662
+ /** A compiled GL program. A paint kind gets one from
8663
+ * {@link PaintBindContext.program} and hands it back from `bind`. */
8664
+ type PaintProgram = ShaderProgram;
8665
+ /**
8666
+ * `FillStyle`'s discriminant, open on the string the way `ChromeId` is: the
8667
+ * five kinds the kit ships, plus whatever a consumer registers.
8668
+ */
8669
+ type PaintKind = 'solid' | 'linear-gradient' | 'radial-gradient' | 'conic-gradient' | 'pattern' | (string & {});
8670
+ /** What a registered kind's editor renders. `PaintInput` in `@weasel-js/ui`
8671
+ * is the control that mounts it. */
8672
+ interface PaintKindEditorProps {
8673
+ value: FillStyle;
8674
+ onInput?(next: FillStyle): void;
8675
+ onChange(next: FillStyle): void;
8676
+ }
8677
+ /**
8678
+ * The renderer surface a paint kind binds against, narrowed to what a paint
8679
+ * needs. The full `DrawContext` is not consumer surface.
8680
+ */
8681
+ interface PaintBindContext {
8682
+ readonly gl: WebGL2RenderingContext;
8683
+ /** The group alpha this draw inherits — multiply it into the paint's own. */
8684
+ readonly alpha: number;
8685
+ /**
8686
+ * The compiled program for a `registerProgram` id, compiled against this
8687
+ * renderer on first use. `null` when no source is registered under `id` or
8688
+ * compilation failed.
8689
+ */
8690
+ program(id: string): PaintProgram | null;
8691
+ /** Send `u_proj` and `u_model`, which every kit vertex shader takes. */
8692
+ setProjAndModel(program: PaintProgram): void;
8693
+ /**
8694
+ * `u_worldInv` for a paint declaring `units` — the inverse of the transform
8695
+ * from the paint's space to the frame the geometry arrives in. Paired with
8696
+ * the vertex shader's `v_world` varying this is *the* paint-space
8697
+ * convention; it is not gradient-specific.
8698
+ */
8699
+ spaceInverse(units: GradientUnits | undefined): Mat3$1;
8700
+ /** Upload a stop ramp and bind it to a texture unit. */
8701
+ bindRamp(stops: GradStop[], unit: number): void;
8702
+ }
8703
+ /**
8704
+ * One paint kind.
8705
+ *
8706
+ * `seed`, `label` and `colorOf` are the editor's slots and every kind has
8707
+ * them. The rest are optional because a kind may not need them — but
8708
+ * `inPoseFrame` and `toBoundsFrame` come as a pair or not at all: a kind that
8709
+ * converts one direction and not the other paints correctly once and then
8710
+ * drifts on the next resize.
8711
+ */
8712
+ interface PaintKindEntry {
8713
+ id: string;
8714
+ label: string;
8715
+ /** Glyph naming this kind in an editor's kind bar, as an icon name the UI
8716
+ * layer resolves — the same indirection `ToolPrefBase.icon` uses. A kind
8717
+ * without one is named by its `label`. */
8718
+ icon?: string;
8719
+ /** A paint of this kind seeded from a color — what an editor writes when a
8720
+ * consumer switches a solid to this kind. */
8721
+ seed(fromColor: string): FillStyle;
8722
+ /** The single color this paint shows, or `undefined` when it has none. */
8723
+ colorOf(paint: FillStyle): string | undefined;
8724
+ /** Editor slot. */
8725
+ Editor?: ComponentType<PaintKindEditorProps>;
8726
+ /**
8727
+ * Render slot: bind program, uniforms and textures for `fill` and return the
8728
+ * bound program; `null` declines the paint.
8729
+ *
8730
+ * Binding is split from drawing on purpose — a caller owning its own stencil
8731
+ * state (an inner/outer-aligned stroke, an even-odd fill) must issue its own
8732
+ * draw call, and the kit's draw wrapper would clobber that state. Shader
8733
+ * output must be premultiplied: `outColor = vec4(rgb * a, a)`.
8734
+ */
8735
+ bind?(ctx: PaintBindContext, fill: FillStyle): PaintProgram | null;
8736
+ /** Bounds frame → the frame the node is painted in. */
8737
+ inPoseFrame?(fill: FillStyle, box: FillPoseBox): FillStyle;
8738
+ /** The inverse. Required whenever `inPoseFrame` is supplied. */
8739
+ toBoundsFrame?(fill: FillStyle, box: FillPoseBox): FillStyle;
8740
+ /** The `<defs>` entry backing a `url(#id)` reference. */
8741
+ toSvg?(id: string, fill: FillStyle): string;
8742
+ }
8743
+ /**
8744
+ * A consumer's own paint, typed as a `FillStyle`.
8745
+ *
8746
+ * `FillStyle` stays a closed union: opening its discriminant would widen every
8747
+ * built-in member and break the narrowing the kit's own branches depend on.
8748
+ * A registered kind declares its own interface instead and passes it through
8749
+ * here — the kit reads only `fill` and hands the whole object back to that
8750
+ * kind's slots.
8751
+ */
8752
+ declare function asPaint<T extends {
8753
+ fill: string;
8754
+ }>(paint: T): FillStyle;
8755
+ /** Register a paint kind. Returns a disposer that removes it. */
8756
+ declare function registerPaintKind(entry: PaintKindEntry): () => void;
8757
+ /** The entry for `kind`, or `undefined`. */
8758
+ declare function getPaintKind(kind: string | undefined): PaintKindEntry | undefined;
8759
+ /** Every registered kind, built-ins first, in registration order. */
8760
+ declare function listPaintKinds(): readonly PaintKindEntry[];
8761
+ /** The registry entry for a paint, or `undefined` when its kind is unknown. */
8762
+ declare function paintKindOf(fill: FillStyle): PaintKindEntry | undefined;
8763
+ /** @internal Test helper — do not call from product code. */
8764
+ declare function _resetPaintKindsForTests(): void;
8765
+
8766
+ /**
8767
+ * The stroke-marker registry — arrowheads and other line terminators.
8768
+ *
8769
+ * Keyed by string, because a key is what sits in `stroke.markerEnd`. That is
8770
+ * the difference from `registerNodeShape`, which resolves by first-matching
8771
+ * predicate and whose `id` is only a label.
8772
+ *
8773
+ * A built-in's SVG slot lives in `@weasel-js/svg` and cannot be imported here
8774
+ * without inverting a package dependency, so an entry carries `toSvg` for that
8775
+ * package to consume — the same split `PaintKindEntry` uses.
8776
+ */
8777
+
8778
+ /** What an entry's `path` is given. Entries that ignore it may return a
8779
+ * constant path. */
8780
+ interface MarkerCtx {
8781
+ /** One geometry unit, in the same world units the ribbon is tessellated in.
8782
+ * Defaults to the resolved stroke width. */
8783
+ readonly size: number;
8784
+ /** The stroke this marker belongs to, already width-resolved. */
8785
+ readonly stroke: Stroke;
8786
+ }
8787
+ /** `'line'` means the stroke's own paint — SVG 2's `context-stroke`, as the
8788
+ * default rather than an opt-in. */
8789
+ type MarkerPaint = FillStyle | 'line' | 'none';
8790
+ interface MarkerEntry {
8791
+ /** `'kit:'`-free for built-ins so the key matches the SVG attribute value;
8792
+ * consumers should prefix (`'app-my-head'`). A key becomes the `id` of the
8793
+ * emitted `<marker>` def, so it must be a valid XML name — no colons. */
8794
+ id: MarkerKey;
8795
+ /** Geometry with its anchor at the origin, pointing +X, in units of
8796
+ * `ctx.size`. An arbitrary anchor is expressed by where the geometry is
8797
+ * drawn, which is why there is no `refX`/`refY`. */
8798
+ path(ctx: MarkerCtx): Path;
8799
+ /** Default `'line'`. */
8800
+ fill?: MarkerPaint;
8801
+ /** Outline width is in the same units as `path`. `false` (the default)
8802
+ * means no outline. */
8803
+ outline?: {
8804
+ width: number;
8805
+ paint?: MarkerPaint;
8806
+ } | false;
8807
+ /** How far back along the line the stroke stops, in units of `ctx.size`.
8808
+ * Default 0. A property of the shape, not a setting on the stroke: an open
8809
+ * V needs 0 or its arms stop meeting the line. */
8810
+ inset?: number;
8811
+ /** `'auto'` (default) follows the line; a number is a fixed angle in
8812
+ * radians, ignoring the line — SVG's `orient="<angle>"`. */
8813
+ orient?: 'auto' | number;
8814
+ /** Emits the `<marker>` def. Consumed by `@weasel-js/svg`. */
8815
+ toSvg?(id: string, entry: MarkerEntry): string;
8816
+ }
8817
+ /** Register a marker. Returns a disposer. Re-registering a built-in id is an
8818
+ * override; disposing it restores the built-in rather than deleting the key. */
8819
+ declare function registerMarker(entry: MarkerEntry): () => void;
8820
+ /** The entry for `key`, or `undefined`. */
8821
+ declare function getMarker(key: string | undefined): MarkerEntry | undefined;
8822
+ /** Every registered marker, built-ins first, in registration order. */
8823
+ declare function listMarkers(): readonly MarkerEntry[];
8824
+ /** Test helper. Do not call from product code. */
8825
+ declare function _resetMarkersForTests(): void;
8826
+
8827
+ /**
8828
+ * Resolving a `MarkerRef` to the distance the ribbon must stop short.
8829
+ *
8830
+ * The tessellation layer takes these as plain numbers and knows nothing about
8831
+ * the registry — keeping that layer free of upward dependencies is deliberate.
8832
+ * The ribbon cache, the SVG serializer and `inkReach` all resolve through here,
8833
+ * so the ribbon, the export and the hit region cannot disagree about where the
8834
+ * line ends.
8835
+ */
8836
+
8837
+ /** The size of one marker unit, in the same world units as `strokeWidth`. */
8838
+ declare function resolveMarkerSize(ref: MarkerRef, strokeWidth: number): number;
8839
+ declare function markerKeyOf(ref: MarkerRef): string;
8840
+ /**
8841
+ * How far back the ribbon stops for `ref`, in world units. Zero for an absent
8842
+ * marker, an unregistered key, or an open head — never throws, because an
8843
+ * unknown key is a data problem and dropping the head is the graceful answer.
8844
+ */
8845
+ declare function markerInset(ref: MarkerRef | undefined, strokeWidth: number): number;
8846
+ /** The start and end insets a stroke asks for. `markerMid` never insets —
8847
+ * trimming at an interior vertex would cut the line in two. */
8848
+ declare function strokeInsets(stroke: Stroke, strokeWidth: number): {
8849
+ start: number;
8850
+ end: number;
8851
+ };
8852
+
8853
+ interface Polyline {
8854
+ /** Interleaved x,y vertices (length = 2 × point count). */
8855
+ points: number[];
8856
+ /** Whether the contour was closed (ends with Z, or is a RectPath). */
8857
+ closed: boolean;
8858
+ /** For each point, the previous anchor index. Anchor-aligned points set A === B. */
8859
+ anchorA?: Uint32Array;
8860
+ /** For each point, the next anchor index. */
8861
+ anchorB?: Uint32Array;
8862
+ /** For each point, the arc-length fraction along (A, B). 0 at anchor A; for anchor-aligned, set to 0. */
8863
+ anchorT?: Float32Array;
8864
+ /** Per-point stroke width, populated by the tessellator when the Stroke
8865
+ * carries `vertexWidths`. Undefined means uniform `Stroke.width`. */
8866
+ widths?: Float32Array;
8867
+ }
8868
+
8869
+ /**
8870
+ * Where a stroke's markers sit and which way they point.
8871
+ *
8872
+ * Computed from the *untrimmed* polyline: trimming moves the endpoint, and a
8873
+ * marker anchors to where the line was authored to end, not to where the
8874
+ * ribbon was cut.
8875
+ */
8876
+
8877
+ interface MarkerSite {
8878
+ x: number;
8879
+ y: number;
8880
+ /** Radians. The direction the marker's +X axis should point — outward at a
8881
+ * start or end, along the bisector at an interior vertex. */
8882
+ angle: number;
8883
+ role: 'start' | 'mid' | 'end';
8884
+ }
8885
+ interface MarkerSiteRequest {
8886
+ start: boolean;
8887
+ mid: boolean;
8888
+ end: boolean;
8889
+ }
8890
+ declare function markerSites(pl: Polyline, want: MarkerSiteRequest): MarkerSite[];
8891
+
8892
+ /**
8893
+ * Turning a stroke's markers into draw commands.
8894
+ *
8895
+ * Separate `PathDrawCommand`s rather than triangles appended to the stroke
8896
+ * ribbon: an entry may carry a fill and an outline at once, or a paint that
8897
+ * differs from the line's, neither of which one mesh can express — and folding
8898
+ * them in would drag the whole marker vocabulary into the ribbon cache key.
8899
+ */
8900
+
8901
+ /**
8902
+ * Every marker command for `path` under `stroke`. `strokeWidth` is the already
8903
+ * width-resolved stroke width; `flattenTolerance` matches what the ribbon used,
8904
+ * so markers land on the same flattened vertices the stroke did.
8905
+ */
8906
+ declare function markerDrawCommands(path: Path, stroke: Stroke, strokeWidth: number, flattenTolerance: number | undefined): PathDrawCommand[];
8907
+
8908
+ /**
8909
+ * The kit's built-in stroke-marker vocabulary.
8910
+ *
8911
+ * Every entry is authored with its anchor at the origin and pointing +X, so
8912
+ * the line arrives from -X and no geometry sits at positive X. Coordinates are
8913
+ * in units of `MarkerCtx.size`, which defaults to the resolved stroke width —
8914
+ * one definition is therefore correct at any line weight.
8915
+ *
8916
+ * Not to be confused with `features/paths/markers.ts`, which builds
8917
+ * decorative chrome shapes and is unrelated.
8918
+ */
8919
+
8920
+ declare const BUILTIN_MARKERS: readonly MarkerEntry[];
8921
+
8922
+ /**
8923
+ * Shortening a flattened subpath so a filled marker is not speared by its own
8924
+ * line. SVG has no equivalent — it paints the marker over a full-length
8925
+ * stroke — so this is the one place our rendering deliberately differs.
8926
+ *
8927
+ * Runs before dash splitting, so a dash pattern fits the visible line rather
8928
+ * than running off under the head. The anchor-param interpolation matches
8929
+ * `splitForDash`'s rule for a boundary landing mid-segment.
8930
+ */
8931
+
8932
+ /**
8933
+ * Shorten `pl` by `startInset` from its first point and `endInset` from its
8934
+ * last, both in the same world units as the points.
8935
+ *
8936
+ * Returns `pl` itself when there is nothing to do (both insets zero, or the
8937
+ * subpath is closed and so has no free ends), and `null` when the insets
8938
+ * consume the whole run — a caller should then draw no ribbon at all.
8939
+ */
8940
+ declare function trimPolyline(pl: Polyline, startInset: number, endInset: number): Polyline | null;
8941
+
8425
8942
  /** Kit-shipped default paint constants.
8426
8943
  *
8427
8944
  * Single source of truth for the colors the built-in tools and the default
@@ -8432,6 +8949,7 @@ declare function mergeAlphaFromPrev(picked: string, prev: string): string;
8432
8949
  * All values are `#rrggbbaa` so they round-trip through the kit's hex8 alpha
8433
8950
  * helpers (`util/color`) without ambiguity.
8434
8951
  */
8952
+
8435
8953
  /** Default fill paint for shapes and the fill action — opaque white. */
8436
8954
  declare const DEFAULT_FILL_COLOR = "#ffffffff";
8437
8955
  /** Default stroke paint for shapes and the stroke action — opaque black. */
@@ -8443,6 +8961,41 @@ declare const DEFAULT_PALETTE: readonly string[];
8443
8961
  * selected-anchor highlight. Derived from the first palette entry so the
8444
8962
  * preview chrome matches the default insert color. */
8445
8963
  declare const GHOST_STROKE: string;
8964
+ /**
8965
+ * A solid paint from a color string — the authoring shorthand for the one
8966
+ * shape a node's `data.fill` (and a `Stroke.paint`) may take.
8967
+ *
8968
+ * An alpha channel in `color` moves to `opacity`, because that is where every
8969
+ * paint kind carries its alpha; leaving it in the hex too would multiply the
8970
+ * two in the renderer.
8971
+ */
8972
+ declare function solid(color: string): FillStyle;
8973
+ /** A solid stroke of `color` at `width` world units. Authoring shorthand for
8974
+ * `{ paint: solid(color), width }`. */
8975
+ declare function strokeOf(color: string, width?: number): Stroke;
8976
+ /** A stroke painted with `paint` at `width` world units — `strokeOf`'s sibling
8977
+ * for a gradient or pattern, which has no color to pass. */
8978
+ declare function strokeWith(paint: FillStyle, width?: number): Stroke;
8979
+ /** The kit's default node paint — what a shape with no declared fill paints. */
8980
+ declare const DEFAULT_SHAPE_FILL: FillStyle;
8981
+ /**
8982
+ * The 0..1 alpha a paint paints at.
8983
+ *
8984
+ * Every paint kind carries its alpha in `opacity` — that is the one slot a
8985
+ * gradient or a pattern has, so it is the slot all of them use.
8986
+ */
8987
+ declare function paintAlpha(paint: FillStyle | undefined): number;
8988
+ /** `paint` painting at `alpha01` (clamped to 0..1). */
8989
+ declare function paintWithAlpha(paint: FillStyle, alpha01: number): FillStyle;
8990
+ /**
8991
+ * `paint` recolored to `color`, keeping everything else about it.
8992
+ *
8993
+ * A solid paint takes the new color; a gradient or a pattern has no single
8994
+ * color to replace, so it is superseded by a solid one — picking a color off
8995
+ * a swatch means "paint this color". An alpha channel in `color` wins;
8996
+ * without one, the paint's existing opacity carries over.
8997
+ */
8998
+ declare function paintWithColor(paint: FillStyle | undefined, color: string): FillStyle;
8446
8999
 
8447
9000
  /**
8448
9001
  * Build an op-batched undo/redo `History`, defaulting restore-time op
@@ -8476,7 +9029,9 @@ interface ArrayAdapterConfig<TNode extends {
8476
9029
  getParent?: (id: string) => string | null;
8477
9030
  /** Optional reparent mutator. Default is a noop. */
8478
9031
  setParent?: (id: string, parentId: string | null) => void;
8479
- /** Optional children lookup. Default omits the method. */
9032
+ /** Optional children lookup for a node id. The adapter answers the root
9033
+ * itself, from `ref` and `getParent` in array order, so this is never
9034
+ * called with `null`. Default omits the method entirely. */
8480
9035
  getChildren?: (id: string) => string[] | undefined;
8481
9036
  /** Live ref to the current selection. Default: empty array. */
8482
9037
  selectionRef?: MutableRefObject<string[]>;
@@ -8672,7 +9227,8 @@ interface GuidesLayerOpts {
8672
9227
  * screen space, projected from world offsets via the active view. */
8673
9228
  declare function createGuidesLayer(opts: GuidesLayerOpts): RenderLayer<unknown>;
8674
9229
 
8675
- /** Axis-aligned bounding box. Rotation is ignored in v1 (alignment uses AABBs). */
9230
+ /** Axis-aligned bounding box. Alignment matches AABBs throughout; a rotated
9231
+ * pose enters as the AABB of its ink (see `AlignBoundsProjection.boundsOf`). */
8676
9232
  interface AlignBounds {
8677
9233
  x: number;
8678
9234
  y: number;
@@ -8695,18 +9251,27 @@ interface AlignMatchResult {
8695
9251
  * and translates a pose. The rect default handles `{x,y,width,height}` poses;
8696
9252
  * non-rect poses (Path, polygon) supply their own. */
8697
9253
  interface AlignBoundsProjection<TPose> {
9254
+ /** The pose's *visual* AABB — a rotated pose reports the extent of its ink,
9255
+ * not the box it was posed in. Guides and matching both read this, so an
9256
+ * implementation that returns the stored box makes a rotated shape snap to
9257
+ * lines nothing is drawn at. */
8698
9258
  boundsOf(pose: TPose): AlignBounds;
9259
+ /** Move the pose, preserving every other field it carries — nothing
9260
+ * downstream re-derives rotation or style from anywhere else. */
8699
9261
  translate(pose: TPose, dx: number, dy: number): TPose;
8700
9262
  }
8701
- /** Which candidate lines to derive from a set of bounds — edges, centers, or
9263
+ /** Which candidate lines to derive from a set of poses — edges, centers, or
8702
9264
  * both, and whether the page box contributes its own. */
8703
- interface DeriveAlignmentGuidesOptions {
9265
+ interface DeriveAlignmentGuidesOptions<TPose = AlignBounds> {
8704
9266
  /** Include the document/page box's edges + center as candidates. */
8705
9267
  page?: AlignBounds;
8706
9268
  /** Emit left/right (x) and top/bottom (y) edge guides. Default true. */
8707
9269
  edges?: boolean;
8708
9270
  /** Emit centerX (x) and centerY (y) guides. Default true. */
8709
9271
  centers?: boolean;
9272
+ /** Reads each target's AABB. Defaults to `RECT_ALIGN_PROJECTION`. Pass the
9273
+ * same projection `alignMoveBehavior` gets, or the two sides disagree. */
9274
+ projection?: AlignBoundsProjection<TPose>;
8710
9275
  }
8711
9276
  /** Common options shared by the three alignment behavior factories. */
8712
9277
  interface AlignmentBehaviorBase {
@@ -8723,18 +9288,23 @@ interface AlignmentBehaviorBase {
8723
9288
  bypassKey?: keyof ModifierState;
8724
9289
  }
8725
9290
 
8726
- /** Derive candidate alignment lines from a set of AABBs (siblings) plus an
9291
+ /** Derive candidate alignment lines from a set of sibling poses plus an
8727
9292
  * optional page box. Each box contributes up to 3 guides per axis: the two
8728
- * edges and the center. Overlapping offsets collapse to one candidate. */
8729
- declare function deriveAlignmentGuides(targets: readonly AlignBounds[], opts?: DeriveAlignmentGuidesOptions): Guide[];
9293
+ * edges and the center. Overlapping offsets collapse to one candidate.
9294
+ * Poses go through the same projection `alignMoveBehavior` matches with, so
9295
+ * a rotated sibling advertises its ink edges rather than its stored box. */
9296
+ declare function deriveAlignmentGuides<TPose = AlignBounds>(targets: readonly TPose[], opts?: DeriveAlignmentGuidesOptions<TPose>): Guide[];
8730
9297
 
8731
9298
  /** Move/insert test all three features per axis. */
8732
9299
  declare const MOVE_ANCHORS: {
8733
9300
  x: readonly AlignAnchor[];
8734
9301
  y: readonly AlignAnchor[];
8735
9302
  };
8736
- /** Default projection for rect-shaped poses (`{x,y,width,height}`). */
8737
- declare const RECT_ALIGN_PROJECTION: AlignBoundsProjection<AlignBounds>;
9303
+ /** Default projection for rect-shaped poses (`{x,y,width,height,rotation?}`).
9304
+ * A rotated pose reports the AABB of its ink, so guides derived from a
9305
+ * stationary sibling and the box a dragged selection matches with are the
9306
+ * same measurement. */
9307
+ declare const RECT_ALIGN_PROJECTION: AlignBoundsProjection<RectPose>;
8738
9308
  /**
8739
9309
  * Match a moving box's selected edge/center features against candidate guide
8740
9310
  * lines. Returns the per-axis snap delta and the matched candidate line(s).
@@ -8801,7 +9371,7 @@ declare function pointInRotatedRect(pose: RotatedPose, worldX: number, worldY: n
8801
9371
  * handle's center. Matches the demo's visual default; consumers can
8802
9372
  * override per-call. Unscaled — kit-internal use sites multiply by
8803
9373
  * `DeviceProfile.targetScale`. */
8804
- declare const DEFAULT_ROTATION_HANDLE_DISTANCE = 24;
9374
+ declare const DEFAULT_ROTATION_HANDLE_DISTANCE: 24;
8805
9375
  /** Rotation handle position in world coords. */
8806
9376
  interface RotationHandle {
8807
9377
  /** Handle center in world coords. */
@@ -9137,7 +9707,8 @@ interface SelectFromLassoOptions {
9137
9707
  * preserve, with shift). */
9138
9708
  declare function selectFromLasso(opts?: SelectFromLassoOptions): LassoSelectBehavior;
9139
9709
 
9140
- /** Edge or center the selection should align to within the selection's union AABB. */
9710
+ /** Edge or center the selection should align to within the selection's visual
9711
+ * union AABB (rotated members contribute their ink extent). */
9141
9712
  type AlignEdge = 'left' | 'right' | 'top' | 'bottom' | 'center-x' | 'center-y';
9142
9713
  /** Adapter for `useAlign`. */
9143
9714
  interface AlignAdapter<TPose> {
@@ -9159,6 +9730,17 @@ interface UseAlignReturn {
9159
9730
  /** Imperative trigger. No-op when fewer than 2 items selected. */
9160
9731
  align(edge: AlignEdge): void;
9161
9732
  }
9733
+ /**
9734
+ * The pose's *visual* bounds: its descriptor bounds expanded to cover the
9735
+ * rotated rectangle, so a turned shape reports the extent of its ink rather
9736
+ * than the box it was posed in. Align, distribute and flip all fold these.
9737
+ *
9738
+ * Both ends of an align must use it — a visual union measured against
9739
+ * unrotated member boxes misplaces every rotated member. The expanded box
9740
+ * shares its centre with the stored one, so the delta stays a translation of
9741
+ * the stored pose and no re-posing is needed.
9742
+ */
9743
+ declare function visualBoundsViaDescriptor<TPose>(pose: TPose, geometry: PoseProjection<TPose>): ResizePose;
9162
9744
  /** Compute the (dx, dy) translation that moves AABB `b` so that the requested
9163
9745
  * `edge`/center matches the corresponding feature of the union AABB `u`. */
9164
9746
  declare function alignDeltaFor(b: ResizePose, u: ResizePose, edge: AlignEdge): {
@@ -9175,8 +9757,9 @@ declare function useAlign<TPose>(adapter: AlignAdapter<TPose>, options?: UseAlig
9175
9757
 
9176
9758
  /** Axis along which selection is distributed. `'x'` spreads horizontally. */
9177
9759
  type DistributeAxis = 'x' | 'y';
9178
- /** `'centers'` spaces AABB centers equally; `'gaps'` makes the gap between
9179
- * consecutive AABBs equal. Endpoints stay put in both modes. */
9760
+ /** `'centers'` spaces centers equally; `'gaps'` makes the gap between
9761
+ * consecutive items equal. Both measure a rotated item by its ink extent.
9762
+ * Endpoints stay put in both modes. */
9180
9763
  type DistributeMode = 'centers' | 'gaps';
9181
9764
  /** Adapter for `useDistribute`. */
9182
9765
  interface DistributeAdapter<TPose> {
@@ -9976,6 +10559,10 @@ declare function RectIcon({ className, size }: IconProps): react_jsx_runtime.JSX
9976
10559
  /** Outlined ellipse. Used for the ellipse insertion tool. */
9977
10560
  declare function EllipseIcon({ className, size }: IconProps): react_jsx_runtime.JSX.Element;
9978
10561
 
10562
+ /** Framed picture with a sun and mountain — the conventional "image" glyph.
10563
+ * Used for the image insertion tool (`useImageTool`). */
10564
+ declare function ImageIcon({ className, size }: IconProps): react_jsx_runtime.JSX.Element;
10565
+
9979
10566
  /** Eyedropper — diagonal stem running from upper-right (bulb) to
9980
10567
  * lower-left (tip), with a small squared bulb cap at the top. Used
9981
10568
  * for the eyedropper / color-picker tool. */
@@ -10069,4 +10656,4 @@ interface InsertOverlayStyle {
10069
10656
  lineWidth?: number;
10070
10657
  }
10071
10658
 
10072
- export { ANCHOR_HIT_BASE_PX, Action, ActionsProp, ActionsProviderIfRoot, ActionsRegistry, Affordance, AffordanceBinding, AffordanceHit, AffordanceRegion, type AffordanceRegionHit, type AlignAdapter, type AlignAnchor, type AlignBounds, type AlignBoundsProjection, type AlignEdge, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type AnchorHit, type AnchorScratch, type AnchorState, type AnimateOnSetPoseOptions, type AnimateToBoundsOptions, type AnimationHandle, type Animator, AnyTool, AreaSelectAdapter, type ArrayAdapter, type ArrayAdapterConfig, BUNDLE_TOOLS, BindingScope, BooleansAdapter, Bounds, BoundsConstraint, type BuildChromeCtxArgs, type BuiltinShapeToolId, type BuiltinToolId, type BuiltinToolOptions, COARSE_TARGET_SCALE, CURVE_REPS, type CanvasExtensionApi, type CanvasFocusReturn, type CanvasHelpers, type CanvasProps, type CanvasSelectionMode, type CanvasSize, type CanvasSizeSnapshot, type CellHighlightLayerOpts, ChromeCtx, ChromeId, ChromeState, type ClampBounds, ClipboardSnapshot, type CoalescedCtx, type ColorInterpolate, type ColorOverride, type ColorOverrideFn, ColorOverrideRegistry, type ColorSpace, CommonAffordanceScratch, type ComposeSelectionPoseOpts, type ComputeFitViewOptions, Condition, ContentHandlerEntry, Contribution, type CornerResizeAffordanceOptions, type CornerResizeScratch, type CreateChildrenLayerOpts, type CreateParallaxLayerOpts, type CreatePathEditingOverlayLayerOptions, type CreatePathLayerOpts, type CreatePenPreviewLayerOptions, type CreateTextLayerOpts, type CreateViewportLayerOpts, CropIcon, CursorCoordsHud, type CursorCoordsHudProps, type CurveRepKind, type CurveRepresentation, type CustomLayerEntry, type CycleHandle, type CycleVertexColorsOptions, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_FLATTEN_TOLERANCE, DEFAULT_GUIDE_TOLERANCE_PX, DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_VELOCITY_DECAY, DebugConfig, DebugSink, DebugSnapshot, DebugStrokes, DebugTheme, type DecayLoopConfig, type DecayOptions, type DefaultTextData, DepRegistryProviderIfRoot, DepSchema, type DeriveAlignmentGuidesOptions, DetectedDeviceFacts, DeviceProfile, DeviceProfileProvider, type DeviceProfileProviderProps, Dims, type Discriminator, Dispatcher, type DistributeAdapter, type DistributeAxis, type DistributeMode, DivideIcon, type DragGestureController, type DragGestureCtx, type DragGestureEndCtx, type DragGesturePhase, type DragGesturePoint, type DragHandleOptions, type DragPayload, type DragRadialController, type DragRadialCtx, type DragRadialEndCtx, type DragRadialPoint, type DragRadialState, type DragRectBounds, type DragRectController, type DragRectCtx, type DragRectEndCtx, type DragRectPoint, DrawCommand, type DropZoneOptions, EASINGS, type EasingFn, type EasingName, Eligibility, type EligibilityState, EllipseIcon, type EventTrack, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, type FillPoseBox, FillStyle, type FitTextPoseOptions, FitViewToBoundsOptions, type FlagKey, type FreeformOptions, GHOST_STROKE, type GateLayerOptions, GeometryProjection, type GestureSource, GradStop, GradientFill, GradientKind, type GridLayerOpts, type GridSlotConfig, Guide, type GuideSnapOptions, type GuidesLayerOpts, HANDLE_BASE_PX, HandIcon, type InertiaConfig as HandToolInertiaConfig, type HandleDragPoint, type HeadlessCanvasLike, HotkeyTrigger, IDENTITY_JOINT, type IconProps, type ImageNodeData, type ImageStatus, type IndicatorStyle, InsertAdapter, InsertBehavior, InsertExtras, type InsertNodeFactory, type InsertOp, type InsertOverlayStyle, type Interpolate, type InterpolatorFactory, IntersectIcon, type Joint, type JointTransform, KIT_SHAPE_KINDS, type Keyframe, type LabelOptions, LassoHitMode, LassoIcon, LassoSelectAdapter, LassoSelectBehavior, LayerEnumerableAdapter, LayerHit, type LayerSlotValue, type LayersMap, type LayoutLine, type LayoutResult, LayoutSnap, LayoutStrategy, type LifecycleAnimation, LineIcon, type LinePoint, type LoopFactory, type LoopOptions, MIXED, MOVE_ANCHORS, type MarkdownFontOptions, Mat3$1 as Mat3, type MeasureFn, type MeasureTextBoundsOpts, type MeasuredText, MinimapCanvas, type MinimapCanvasProps, type MinimapFit, type Mixed, ModifierState, type MomentumOptions, MoveAdapter, MoveBehavior, type NestedHitOpts, type NestedHitTester, type NestedTimeline, type NodeClientRect, type NodeFill, NodeId, type NodeInk, type NodePaintCtx, type NodeProperties, type NodePropertiesEntry, type NodeRouting, type NodeRoutingEntry, type NodeShapeEntry, type OpenFilePickerOptions, OrderedAdapter, OriginProjection, OverlayPosition, PATH_ANCHOR_CHROME_ID, type PanBounds, type ParallaxOpts, Path, type PathAnchor, type PathAnchorAffordanceOptions, PathBuilder, PathDrawCommand, type PathEditingOverlayStyle, PathFillRule, type PathInWorldPose, type PenAnchor, PenIcon, type PenPreviewStyle, type PenScratch, type PenSubpath, PencilIcon, type PencilPoint, type PhysicsHandle, type PhysicsOptions, PickHud, type PickHudProps, type PickTopMostHitAdapter, type PinchZoomToolOpts, type PixelRenderPlan, type PointInPathOptions, type PointInTextPoseOpts, PointSnapBehavior, PointerProviderIfRoot, type PointerSample, type PointerStylusState, PolygonIcon, PolygonPath, type PolygonPoint, type Pose, PoseProjection, type PoseRotation, type PositionedRun, type PressureToWidthOptions, type Pt, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, type RangeStyle, type RasterImage, type Rect, RectIcon, RectPath, RectPose, type RegisterNodeShapeOptions, RegisteredOp, RenderLayer, type RenderSceneToCanvasArgs, type RenderSceneToPixelsArgs, type ReorderDirection, ResizeAdapter, ResizeAnchor, ResizePose, ResolvedTextStyle, RotateAdapter, RotateBehavior, type RotateGeometry, RotatedPose, type RotationAffordanceOptions, type RotationHandle, type RotationScratch, Rule, RuleCtx, type RunStylePatch, SPRING_PRESETS, type SampledTrack, type ScalarOrXY, Scene, SceneAdapter, type SceneAdapterSelection, SceneCanvas, type SceneCanvasAdapter, type SceneCanvasApi, type SceneCanvasHit, type SceneCanvasProps, Node$1 as SceneNode, type SceneNodeClientRectOpts, SceneRegistry, type SceneSlotConfig, type SceneToAdapterOptions, SceneViewCanvas, type SceneViewCanvasProps, type SceneViewDrawOne, ScopedBinding, type ScratchKey, type ScratchStore, type SelectAdapter, type SelectFromLassoOptions, SelectIcon, SelectionApi, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type SelectionHandlesLayerOpts, type SelectionOutlineLayerOpts, type SelectionOverlayLayerOpts, type SelectionOverlaySlotConfig, SerializedScene, type SetFlagResult, type SetPathFields, ShaderProgramHandle, type ShapeCoversPointOptions, type SharedAnchor, type Simulation, type SimulationForce, type SimulationNode, type Skeleton, SliceDep, type SnapPattern, type SnapPointOptions, SnapStrategy, type SplitByLineOptions, type SpringOptions, type SpringPoseOptions, type SpringPreset, type SpringPresetName, type SpringVertexColorsOptions, type StaggerBuilder, type StaggerDelay, type StaggerFactory, type StaggerOptions, type StaggerPerItem, type StaggerSpringPoseOptions, type StaggerTweenOptions, type StaggerVertexColorsOptions, type StandardSlotConfig, type StandardSlotName, StarIcon, type StarPoint, type StartEditOptions, Stroke, type StyleKey, StyledRun, type StylusData, SubtractIcon, SvgIngestOptions, type TextEditScreenPose, type TextEditSelection, TextIcon, type TextLineBoxesOpts, type TextPose, type TextRenderer, TextStyle, TextVerticalAlign, TextureHandle, type ThresholdDragHandle, type ThresholdDragOptions, type TileGridOptions, type TilePatternOpts, TilePatternSpec, type TimelineHandle, type TimelineOptions, type TimelineTrack, Tool, type ToolBundle, ToolDef, ToolKeybinding, type ToolPref, type ToolPrefBoolean, type ToolPrefBooleanControl, type ToolPrefColor, type ToolPrefCustom, type ToolPrefEnum, type ToolPrefEnumControl, type ToolPrefGroup, type ToolPrefKind, type ToolPrefLeaf, type ToolPrefNumber, type ToolPrefNumberControl, type ToolPrefNumberUnit, type ToolPrefPaint, type ToolPrefString, type ToolPrefStringControl, type ToolsApi, type Track, type TweenLoopOptions, type TweenOptions, type TweenPoseOptions, type TweenVertexColorsOptions, UnionIcon, UnitSystem, UnitValue, UnknownIcon, type UseAlignOptions, type UseAlignReturn, type UseAnimatorOptions, type UseArrayAdapterOptions, type UseBooleansOptions, type UseBooleansReturn, type UseCanvasFocusOptions, type UseDistributeOptions, type UseDistributeReturn, type UseDragGestureOptions, type UseDragRadialOptions, type UseDragRectOptions, type UseEyedropperToolOptions, type UseGestureDispatcherOptions, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, type UseHandToolOptions, type UseHandleDragOptions, type UseHandleDragReturn, type UseHoverTrackingArgs, type UseImageToolOptions, type UseKeybindingsOptions, type UseLassoSelectOptions, type UseLassoToolOptions, UseMoveOptions, type UsePenToolOptions, type UsePointerStylusOptions, type UsePolygonToolOptions, UseResizeOptions, type UseResizePolicyOptions, type UseRotateOptions, type UseRotateToolOptions, UseSceneOptions, type UseSceneTextEditOptions, type UseSceneTextEditReturn, type UseSceneTrivialOptions, type UseSelectToolOptions, UseSelectionOptions, type UseSimulationOptions, type UseStandardActionsOptions, type UseStarToolOptions, type UseTextEditOptions, type UseTextEditReturn, type UseToolsOptions, type UseZoomOptions, type UseZoomReturn, VERSION, type Vec2, type VertexColorChannel, View, ViewTransform, type ViewportConfig, ViewportDims, type ViewportLayer, ViewportToolDef, type ViewportZoomOptions, VisibilityRules, WeaselProvider, type WheelInput, type WheelState, ZoomBound, type ZoomBounds, type ZoomClampOpts, ZoomFactor, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyOpsTo, applyStyleToRange, areaSelectAction, arrayAdapter, bezierCubic, bezierQuadratic, blendPoses, boundsOfPath, buildChromeCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeSelectionPose, computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDeleteOp, createGridLayer, createGuidesLayer, createHistory, createInsertOp, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createReparentOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetSelectionOp, createSetTextOp, createTextLayer, createTilePattern, createTransformOp, createViewportLayer, cycleVertexColors, decomposePath, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deleteScratch, deriveAlignmentGuides, deriveParallaxView, dispatchApplyBatch, dlog, domPositionToCharOffset, domToRuns, dwarn, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, ellipsePath, enumerateAnchors, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitZoom, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, focused, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getScratch, getStylusData, gradientForBounds, gradientGeometry, guideSnapStrategy, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isDebugEnabled, isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linePath, linear, liveScope, makeViewportZoomAction, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathPoseDescriptor, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInPath, pointInRotatedRect, pointInTextPose, polygonFromPoints, poseRotationOf, pressureToWidth, pxExtent, rainbowVertexColors, rebuildOp, rectCorners, rectPath, registerNodeShape, registerOpFactory, regularPolygonPath, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveSkeleton, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, sampleGradientStops, sampleTrack, scaleDelta, scalePathToBounds, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, scratchKey, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, setScratch, shapeCoversPoint, snap, snapPoint, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, starPath, startThresholdDrag, styleAtRange, subscribeImageReady, textCommand, textLineBoxes, tileGrid, toHex8, transformPath, translatePath, translatePolygonInPlace, translatePoseViaDescriptor, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, viewportDragPanAction, viewportZoomAction, viewportsAt, when, withAlpha01, withCoord, withGradientKind, withinPxBox, withinPxRadius, worldEditToStorage, zoomAt, zoomAtLeast };
10659
+ export { ANCHOR_HIT_BASE_PX, Action, type ActionShortcut, ActionsProp, ActionsProviderIfRoot, ActionsRegistry, Affordance, AffordanceBinding, AffordanceHit, AffordanceRegion, type AffordanceRegionHit, type AlignAdapter, type AlignAnchor, type AlignBounds, type AlignBoundsProjection, type AlignEdge, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type AnchorHit, type AnchorScratch, type AnchorState, type AnimateOnSetPoseOptions, AnimationHandle, Animator, AnyTool, AreaSelectAdapter, type ArrayAdapter, type ArrayAdapterConfig, BUILTIN_MARKERS, BUNDLE_TOOLS, BindingScope, BooleansAdapter, Bounds, BoundsConstraint, type BuildChromeCtxArgs, BuiltinShapeToolId, type BuiltinToolId, type BuiltinToolOptions, COARSE_TARGET_SCALE, CURVE_REPS, type CanvasExtensionApi, type CanvasFocusReturn, type CanvasHelpers, type CanvasProps, type CanvasSelectionMode, type CanvasSize, type CanvasSizeSnapshot, type CanvasSurfaceHelpers, CanvasView, type CanvasViewHelpers, type CanvasViewProps, type CaretIndexAtOpts, type CellHighlightLayerOpts, ChromeCtx, ChromeId, ChromeState, type ClampBounds, ClipboardSnapshot, type CoalescedCtx, type ColorInterpolate, ColorOverrideRegistry, type ColorSpace, CommonAffordanceScratch, type ComposeSelectionPoseOpts, type ComputeFitViewOptions, Condition, ContentHandlerEntry, Contribution, type CornerResizeAffordanceOptions, type CornerResizeScratch, type CreateChildrenLayerOpts, type CreateParallaxLayerOpts, type CreatePathEditingOverlayLayerOptions, type CreatePathLayerOpts, type CreatePenPreviewLayerOptions, type CreateTextLayerOpts, type CreateViewResolverOpts, type CreateViewportLayerOpts, CropIcon, CursorCoordsHud, type CursorCoordsHudProps, type CurveRepKind, type CurveRepresentation, type CustomLayerEntry, type CycleHandle, type CycleVertexColorsOptions, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_FLATTEN_TOLERANCE, DEFAULT_GUIDE_TOLERANCE_PX, DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_SHAPE_FILL, DEFAULT_STROKE_COLOR, DEFAULT_VELOCITY_DECAY, DebugConfig, DebugSink, DebugSnapshot, DebugStrokes, DebugTheme, type DecayLoopConfig, type DefaultTextData, DepRegistryProviderIfRoot, DepSchema, type DeriveAlignmentGuidesOptions, DetectedDeviceFacts, DeviceProfile, DeviceProfileProvider, type DeviceProfileProviderProps, Dims, type Discriminator, Dispatcher, type DistributeAdapter, type DistributeAxis, type DistributeMode, DivideIcon, type DragGestureController, type DragGestureCtx, type DragGestureEndCtx, type DragGesturePhase, type DragGesturePoint, type DragHandleOptions, type DragPayload, type DragRadialController, type DragRadialCtx, type DragRadialEndCtx, type DragRadialPoint, type DragRadialState, type DragRectBounds, type DragRectController, type DragRectCtx, type DragRectEndCtx, type DragRectPoint, DrawCommand, type DropZoneOptions, EASINGS, EasingFn, type EasingName, Eligibility, type EligibilityState, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, type FillPoseBox, type FitTextPoseOptions, type FlagKey, type FreeformOptions, GHOST_STROKE, type GateLayerOptions, GeometryProjection, type GesturePreviewSource, type GestureSource, type GridLayerOpts, type GridSlotConfig, Guide, type GuideSnapOptions, type GuidesLayerOpts, HANDLE_BASE_PX, HandIcon, type InertiaConfig as HandToolInertiaConfig, type HandleDragPoint, type HeadlessCanvasLike, HotkeyTrigger, IDENTITY_JOINT, type IconProps, ImageIcon, type ImageNodeData, type ImageStatus, type IndicatorStyle, InsertAdapter, InsertBehavior, InsertExtras, type InsertNodeFactory, type InsertOp, type InsertOverlayStyle, IntersectIcon, type Joint, type JointTransform, type LabelOptions, LassoHitMode, LassoIcon, LassoSelectAdapter, LassoSelectBehavior, LayerEnumerableAdapter, LayerHit, type LayerSlotValue, type LayersMap, LayoutSnap, LayoutStrategy, type LifecycleAnimation, LineIcon, type LinePoint, MIXED, MOVE_ANCHORS, type MarkerCtx, type MarkerEntry, type MarkerPaint, type MarkerSite, type MarkerSiteRequest, Mat3$1 as Mat3, MinimapCanvas, type MinimapCanvasProps, type MinimapFit, type Mixed, ModifierState, type MomentumOptions, MoveAdapter, MoveBehavior, type NestedHitOpts, type NestedHitTester, type NodeClientRect, NodeId, type NodeInk, type NodeInkCtx, type NodeOverlayFrame, type NodePaintCtx, type NodeProperties, type NodePropertiesEntry, type NodeRouting, type NodeRoutingEntry, type NodeShapeEntry, type NodeSilhouetteCtx, OngoingOverlay, type OpenFilePickerOptions, OrderedAdapter, OriginProjection, type OverlayPoint, OverlayPosition, PATH_ANCHOR_CHROME_ID, type PaintBindContext, type PaintKind, type PaintKindEditorProps, type PaintKindEntry, type PaintProgram, type PanBounds, type ParallaxOpts, Path, type PathAnchor, type PathAnchorAffordanceOptions, PathBuilder, PathDrawCommand, type PathEditingOverlayStyle, PathFillRule, type PathInWorldPose, type PenAnchor, PenIcon, type PenPreviewStyle, type PenScratch, type PenSubpath, PencilIcon, type PencilPoint, PickHud, type PickHudProps, type PickTopMostHitAdapter, type PinchZoomOptions, type PinchZoomToolOpts, type PixelRenderPlan, type PointInPathOptions, type PointInTextPoseOpts, PointSnapBehavior, PointerProviderIfRoot, type PointerSample, type PointerStylusState, PolygonIcon, PolygonPath, type PolygonPoint, type Pose, PoseProjection, type PoseRotation, type PressureToWidthOptions, type Pt, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, type RangeStyle, type RasterImage, RectIcon, RectPath, RectPose, type RegisterNodeShapeOptions, RegisteredOp, RenderLayer, type RenderSceneToCanvasArgs, type RenderSceneToPixelsArgs, type ReorderDirection, ResizeAdapter, ResizeAnchor, ResizePose, type ResolvableView, RotateAdapter, RotateBehavior, type RotateGeometry, RotatedPose, type RotationAffordanceOptions, type RotationHandle, type RotationScratch, Rule, RuleCtx, type RunStylePatch, SPRING_PRESETS, SampledTrack, type ScalarOrXY, Scene, SceneAdapter, type SceneAdapterSelection, SceneCanvas, type SceneCanvasAdapter, type SceneCanvasApi, type SceneCanvasHit, type SceneCanvasProps, Node$1 as SceneNode, type SceneNodeClientRectOpts, SceneRegistry, type SceneSlotConfig, type SceneToAdapterOptions, SceneViewCanvas, type SceneViewCanvasProps, type SceneViewDrawOne, ScopedBinding, type ScratchKey, type ScratchStore, type SelectAdapter, type SelectFromLassoOptions, SelectIcon, SelectionApi, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type SelectionHandleStyle, type SelectionHandlesLayerOpts, type SelectionOutlineLayerOpts, type SelectionOverlayLayerOpts, type SelectionOverlaySlotConfig, SerializedScene, type SetFlagResult, type SetPathFields, ShaderProgramHandle, type ShapeCoversPointOptions, type SharedAnchor, type Simulation, type SimulationForce, type SimulationNode, type Skeleton, SliceDep, type SnapPattern, type SnapPointOptions, SnapStrategy, type SplitByLineOptions, type SpringPoseOptions, SpringPreset, SpringPresetName, type SpringVertexColorsOptions, type StaggerVertexColorsOptions, type StandardSlotConfig, type StandardSlotName, StarIcon, type StarPoint, type StartEditOptions, type StyleKey, type StylusData, SubtractIcon, SvgIngestOptions, TOOL_PREF_KINDS, type TextEditScreenPose, type TextEditSelection, TextIcon, type ThresholdDragHandle, type ThresholdDragOptions, type TileGridOptions, type TilePatternOpts, Tool, type ToolBundle, ToolDef, ToolKeybinding, type ToolPref, type ToolPrefBoolean, type ToolPrefBooleanControl, type ToolPrefColor, type ToolPrefCustom, type ToolPrefEnum, type ToolPrefEnumControl, type ToolPrefEnumEncoding, type ToolPrefGroup, type ToolPrefKind, type ToolPrefLeaf, type ToolPrefNumber, type ToolPrefNumberControl, type ToolPrefNumberUnit, type ToolPrefObject, type ToolPrefPaint, type ToolPrefString, type ToolPrefStringControl, type ToolsApi, type TweenPoseOptions, type TweenVertexColorsOptions, UnionIcon, UnitSystem, UnitValue, UnknownIcon, type UseAlignOptions, type UseAlignReturn, UseAnimatorOptions, type UseArrayAdapterOptions, type UseBooleansOptions, type UseBooleansReturn, type UseCanvasFocusOptions, type UseDistributeOptions, type UseDistributeReturn, type UseDragGestureOptions, type UseDragRadialOptions, type UseDragRectOptions, type UseEyedropperToolOptions, type UseGestureDispatcherOptions, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, type UseHandToolOptions, type UseHandleDragOptions, type UseHandleDragReturn, type UseHoverTrackingArgs, type UseImageToolOptions, type UseKeybindingsOptions, type UseLassoSelectOptions, type UseLassoToolOptions, UseMoveOptions, type UseNodeOverlayFrameOptions, type UsePenToolOptions, type UsePointerStylusOptions, type UsePolygonToolOptions, UseResizeOptions, type UseResizePolicyOptions, type UseRotateOptions, type UseRotateToolOptions, UseSceneOptions, type UseSceneTextEditOptions, type UseSceneTextEditReturn, type UseSceneTrivialOptions, type UseSelectToolOptions, UseSelectionOptions, type UseSimulationOptions, type UseStandardActionsOptions, type UseStarToolOptions, type UseTextEditOptions, type UseTextEditReturn, type UseToolsOptions, type UseZoomOptions, type UseZoomReturn, VERSION, type Vec2, VertexColorChannel, View, ViewAnimationOptions, type ViewRect, type ViewResolver, type ViewTarget, ViewTransform, type ViewportConfig, ViewportDims, type ViewportLayer, ViewportToolDef, type ViewportZoomAnimateOptions, type ViewportZoomOptions, VisibilityRules, type VisibleRaf, type VisibleRafOptions, type VisibleRafTarget, WeaselProvider, type WheelInput, type WheelState, ZoomBound, type ZoomBounds, type ZoomClampOpts, ZoomFactor, _resetMarkersForTests, _resetPaintKindsForTests, aabbCenter, actionIs, actionShortcuts, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyOpsTo, applyStyleToRange, areaSelectAction, arrayAdapter, asPaint, axisAlignedBounds, bezierCubic, bezierQuadratic, blendPoses, boundsOfPath, buildChromeCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeSelectionPose, computeFitView, computeFitViewport, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDeleteOp, createGridLayer, createGuidesLayer, createHistory, createInsertOp, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createReparentOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetSelectionOp, createSetTextOp, createTextLayer, createTilePattern, createTransformOp, createViewResolver, createViewportLayer, cycleVertexColors, decomposePath, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deleteScratch, deriveAlignmentGuides, deriveParallaxView, dispatchApplyBatch, dlog, domPositionToCharOffset, domToRuns, dwarn, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, ellipsePath, enumerateAnchors, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitZoom, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, focused, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getMarker, getNodeShapes, getPaintKind, getScratch, getStylusData, gradientForBounds, gradientGeometry, guideSnapStrategy, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, interpolateView, isBuiltinToolPref, isDebugEnabled, isEditableTarget, isGradientFill, isPatternSpec, keySpecShortcut, lassoSelectAction, lerpColorArray, lerpOklab, lerpOklch, linePath, linear, listMarkers, listPaintKinds, liveScope, makePinchZoomAction, makeViewportZoomAction, markerDrawCommands, markerInset, markerKeyOf, markerSites, matchAlignment, matchesKeyBinding, meanScale, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, paintAlpha, paintKindOf, paintWithAlpha, paintWithColor, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathPoseDescriptor, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInPath, pointInRotatedRect, pointInTextPose, polygonFromPoints, poseRotationOf, pressureToWidth, pxExtent, rainbowVertexColors, rebuildOp, rectCorners, rectPath, registerMarker, registerNodeShape, registerOpFactory, registerPaintKind, registeredOpNames, regularPolygonPath, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolveMarkerSize, resolvePatternSpec, resolveSkeleton, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, sampleGradientStops, sampleTrack, scaleDelta, scalePathToBounds, sceneFromJSON, sceneNodeClientRect, sceneSelectionStore, sceneToAdapter, scopeBindings, scratchKey, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, setScratch, shapeCoversPoint, snap, snapPoint, solid, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, starPath, startThresholdDrag, strokeInsets, strokeOf, strokeWith, styleAtRange, subscribeImageReady, textCommand, textCommandFromRuns, tileGrid, toHex8, transformPath, translatePath, translatePolygonInPlace, translatePoseViaDescriptor, trimPolyline, tweenPose, tweenVertexColors, unionAABB, unionBounds, unionBoundsPath, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useNodeOverlayFrame, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useVisibleRaf, useZoom, viewportDragPanAction, viewportZoomAction, viewportsAt, visualBoundsViaDescriptor, when, withAlpha01, withCoord, withGradientKind, withinPxBox, withinPxRadius, worldEditToStorage, zoomAt, zoomAtLeast };