@weasel-js/core 1.2.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 (59) hide show
  1. package/CHANGELOG.md +1508 -0
  2. package/dist/{DrawCommand-uNkv5TjO.d.ts → DrawCommand-BnZyQaDc.d.ts} +6 -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-CKADTCHK.js → chunk-3ZV53ZW2.js} +51 -14
  8. package/dist/chunk-3ZV53ZW2.js.map +1 -0
  9. package/dist/{chunk-ADWOB5K2.js → chunk-67KE7SDP.js} +4 -4
  10. package/dist/{chunk-ADWOB5K2.js.map → chunk-67KE7SDP.js.map} +1 -1
  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-7KYLIC3E.js → chunk-IFOVTYAK.js} +16611 -15749
  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 +1510 -1292
  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-l3JX0Z6b.d.ts → registry-BU1_3L_C.d.ts} +629 -34
  41. package/dist/renderer.d.ts +38 -37
  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-DaxnQ-u7.d.ts → stroke-5pBwOm9s.d.ts} +41 -4
  48. package/dist/{types-C2R2bjxf.d.ts → types-DIQAisSG.d.ts} +99 -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-4Z6J4IVH.js.map +0 -1
  53. package/dist/chunk-7KYLIC3E.js.map +0 -1
  54. package/dist/chunk-CKADTCHK.js.map +0 -1
  55. package/dist/chunk-CSMF654J.js.map +0 -1
  56. package/dist/chunk-GVCNT7UH.js +0 -47
  57. package/dist/chunk-GVCNT7UH.js.map +0 -1
  58. package/dist/chunk-HGF5X6MZ.js.map +0 -1
  59. package/dist/paint-types-1fUdZUaH.d.ts +0 -266
package/dist/index.d.ts CHANGED
@@ -1,39 +1,42 @@
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, U as UseSelectionOptions, I as InsertExtras, G as GeometryProjection, q as ContentHandlerEntry, r as SvgIngestOptions, 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-l3JX0Z6b.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 LayerCommandCache, an as LayoutDep, ao as MatchResult, ap as NEVER, aq as NodeAtPointDep, ar as OngoingHandle, as as OngoingInvoker, at as OngoingOverlay, au as Point2, av as PointerContextProvider, aw as PointerContextValue, ax as PointerWorldPos, ay as PoseAdapter, az as PoseComposition, aA as ResizePolicy, aB as ResolveAllOptions, aC as ResolveOnlyResult, aD as ResolvedCandidate, aE as SelectionExtendKey, aF as SelectionMode, aG as Selector, aH as SnapDep, aI as SvgUnpacker, aJ as TextEditDep, aK as ToolCtx, aL as ToolModifiers, aM as ToolPresentation, aN as ToolSlot, aO as UiOngoingControl, aP as ViewApi, aQ as actionBindings, aR as applyBooleanOp, aS as buildRuleCtx, aT as clipboardCopyAction, aU as clipboardCutAction, aV as composeRectPose, aW as composeWorldPose, aX as createDispatcher, aY as decomposeRectPose, aZ as describeRule, a_ as drawLayers, a$ as drawOneLayer, b0 as enterTextEditAction, b1 as evaluate, b2 as evaluateEnabled, b3 as isLayerVisible, b4 as rebaseLocalPose, b5 as registerContentHandler, b6 as resolveParams, b7 as sliceAction, b8 as specificity, b9 as translateRectPose, ba as useAction, bb as useActionsRegistry, bc as useActiveToolContext, bd as useDepRegistry, be as useDepSource, bf as useOptionalActiveToolContext, bg as useOptionalDepRegistry, bh as usePointerContext, bi as useSelection, bj as worldPoseLookup } from './registry-l3JX0Z6b.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-1fUdZUaH.js';
10
- export { d as GradientUnits, R as Region, e as StrokeAlign, f as alignedStrokeRect } from './paint-types-1fUdZUaH.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';
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';
13
15
  import { GestureSpec, BodyClassification, IngestItem } from '@weasel-js/gestures';
14
16
  export { ClickSpec, DragSpec, DropSpec, GestureSpec, IngestItem, InputEvent, KeyHeldSpec, KeySpec, ModSpec, MultiTouchSpec, PasteSpec, TargetSpec, WheelSpec } from '@weasel-js/gestures';
15
- 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-C2R2bjxf.js';
16
- 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-C2R2bjxf.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-BhrifbZ1.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-BhrifbZ1.js';
19
- import { U as UseResizeOptions } from './pointSnapToGrid-BBtn5bus.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-BBtn5bus.js';
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-uNkv5TjO.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-uNkv5TjO.js';
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';
30
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
- export { I as ImageMinification, S as SpriteSheet, f as frameRect, r as resolveStrokeWidth, v as viewToMat3 } from './stroke-DaxnQ-u7.js';
34
- 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';
35
38
  import { G as Guide } from './types-D2tTKEU0.js';
36
- export { U as UseInsertOptions } from './options-C6HYCKP7.js';
39
+ export { U as UseInsertOptions } from './options-CdFl510T.js';
37
40
  export { UseClipboardOpsOptions, UseClipboardOpsReturn, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, buildWeaselClipboardText, embedWeaselMetadataInSvg, extractWeaselClipboardFromSvg, parseWeaselClipboardText, sniffWeaselClipboardText, useClipboardOps } from './clipboard.js';
38
41
  export { cloneByAltDrag } from './clone.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';
@@ -552,6 +555,71 @@ declare const DEFAULT_VELOCITY_DECAY = 0.4;
552
555
  */
553
556
  declare function useSimulation<TNode extends SimulationNode>(opts: UseSimulationOptions<TNode>): Simulation<TNode>;
554
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
+
555
623
  /**
556
624
  * Stylus / Pencil pointer-event helpers.
557
625
  *
@@ -888,7 +956,7 @@ interface CanvasSizeSnapshot extends CanvasSize {
888
956
  * so dragging a window to a different-density display without resizing it
889
957
  * left the snapshot stale. The profile watches a re-armed resolution media
890
958
  * query instead. */
891
- declare function useCanvasSize(containerRef: RefObject<HTMLDivElement | null>): CanvasSizeSnapshot;
959
+ declare function useCanvasSize(containerRef: RefObject<HTMLElement | null>): CanvasSizeSnapshot;
892
960
 
893
961
  /**
894
962
  * Handle/hit multiplier applied on a coarse pointer.
@@ -918,25 +986,23 @@ declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?:
918
986
  declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
919
987
 
920
988
  /**
921
- * Base sizes for grabbable chrome, in CSS pixels at `targetScale = 1`.
922
- *
923
- * These were six separate literal `8`s and one `24` scattered across
924
- * `SceneCanvas`, `features/selection/overlay`, `affordances/cornerResize`,
925
- * `canvas/affordanceAt`, and `interactions/actions/rotate/handle`. They are
926
- * consolidated here because paint and hit-test MUST scale together: chrome
927
- * you can see but cannot grab is the exact failure `chrome-caps` exists to
928
- * make impossible, and duplicated literals in five files is how that failure
929
- * 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.
930
991
  *
931
- * Multiply by `DeviceProfile.targetScale` at the point of use the profile
932
- * 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.
933
999
  */
934
1000
  /** Selection corner-handle visual size and hit radius. */
935
- declare const HANDLE_BASE_PX = 8;
1001
+ declare const HANDLE_BASE_PX: 8;
936
1002
  /** Path anchor / control-point hit radius. */
937
- declare const ANCHOR_HIT_BASE_PX = 8;
1003
+ declare const ANCHOR_HIT_BASE_PX: 8;
938
1004
  /** Distance from a selection's top edge to the rotation handle's center. */
939
- declare const ROTATION_HANDLE_BASE_PX = 24;
1005
+ declare const ROTATION_HANDLE_BASE_PX: 24;
940
1006
 
941
1007
  /**
942
1008
  * Read the ambient device profile.
@@ -1327,10 +1393,9 @@ declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
1327
1393
  /**
1328
1394
  * useHoverTracking — last-hovered NodeId for chrome-caps rules.
1329
1395
  *
1330
- * Attaches a `pointermove` listener to the supplied canvas; on each
1331
- * move, converts client coords world coords and runs the supplied
1332
- * `getNodeAtPoint`, caching the resulting id on a ref. Cleared on
1333
- * `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).
1334
1399
  *
1335
1400
  * Returns a stable getter — call once per frame from `buildChromeCtx`.
1336
1401
  *
@@ -1342,13 +1407,11 @@ declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
1342
1407
  /** Options for `useHoverTracking`. */
1343
1408
  interface UseHoverTrackingArgs {
1344
1409
  canvasRef: RefObject<HTMLCanvasElement | null>;
1345
- /** Client world conversion. Same shape as the dispatcher's. */
1346
- clientToWorld: (clientX: number, clientY: number) => {
1347
- x: number;
1348
- y: number;
1349
- };
1350
- /** Topmost-id resolver. Returns null when the world point hits nothing. */
1351
- 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) => {
1352
1415
  id: NodeId;
1353
1416
  } | null;
1354
1417
  enabled?: boolean;
@@ -1404,22 +1467,29 @@ declare function actionShortcuts(action: Action): readonly ActionShortcut[];
1404
1467
  * Mirrors the per-frame translate semantics of the `useMove` hook:
1405
1468
  * - `start`: capture origin poses for all selected nodes; record the
1406
1469
  * current drag delta in scratch each frame.
1407
- * - `onMove`: update the in-scratch `currentDelta` only no scene writes.
1408
- * 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.
1409
1473
  * - `onEnd('commit')`: emit the final delta as transform ops and route
1410
1474
  * them through the consumer `applyOps` hook when present, else
1411
1475
  * `scene.applyBatch(ops, 'Move', adapter)` — either way a single batch
1412
1476
  * → exactly one undo entry for the whole drag.
1413
- * - `onEnd('cancel')`: no scene writes — the scene was never mutated during
1414
- * 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.
1415
1479
  *
1416
- * ## Why no per-frame scene writes
1480
+ * ## Why no per-frame document writes
1417
1481
  *
1418
1482
  * `Scene.setPose` calls `executeAndLog`, which immediately records an undo
1419
- * entry on the scene's history engine. Per-frame writes during drag would create
1420
- * O(frames) history entries matching `useMove`'s approach of tracking
1421
- * poses only in React state (overlay) during the drag and committing a
1422
- * 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.
1423
1493
  *
1424
1494
  * The behavior pipeline (snap-to-grid, snap-back-or-delete, snap-to-container,
1425
1495
  * etc.) via `opts.behaviors` from `BindingOpts` IS wired: `start` builds a
@@ -1828,7 +1898,10 @@ declare const viewportDragPanAction: Action & {
1828
1898
  *
1829
1899
  * ## Bindings (parametric — each passes `params.kind`)
1830
1900
  * - `{ kind: 'wheel', mods: { mod: true } }` → Cmd+wheel, params: `{ kind: 'wheel' }`
1831
- * (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.
1832
1905
  * - `{ kind: 'key', key: '=' }` → zoom in, params: `{ kind: 'in' }`
1833
1906
  * - `{ kind: 'key', key: '-' }` → zoom out, params: `{ kind: 'out' }`
1834
1907
  * - `{ kind: 'key', key: '0' }` → reset zoom, params: `{ kind: 'reset' }`
@@ -1842,13 +1915,27 @@ declare const viewportDragPanAction: Action & {
1842
1915
  * - `'in'`/`'out'`: step zoom by ×1.25 / ×0.8, anchored at the host center
1843
1916
  * when the `view` dep wires `hostSize()` (SceneCanvas does), falling back
1844
1917
  * to the canvas top-left origin for consumers that don't.
1845
- * - `'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.
1846
1925
  *
1847
1926
  * ## Key binding modifier notes
1848
1927
  * The key bindings (`=`, `-`, `0`) require `mod: true` (Cmd on Mac, Ctrl elsewhere).
1849
1928
  * This matches the behaviour of the dissolved `useKeyboardZoomTool`.
1850
1929
  */
1851
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
+ }
1852
1939
  /**
1853
1940
  * @experimental
1854
1941
  * Tuning for {@link makeViewportZoomAction}.
@@ -1857,7 +1944,8 @@ interface ViewportZoomOptions {
1857
1944
  /**
1858
1945
  * Which wheel gesture triggers zoom.
1859
1946
  * - `'mod'` (default): Cmd/Ctrl+wheel — coexists with plain-wheel pan
1860
- * (`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.
1861
1949
  * - `'plain'`: bare wheel, no modifier. Pair with `viewport.pan: false`,
1862
1950
  * since plain wheel otherwise drives pan and the two would compete.
1863
1951
  */
@@ -1866,6 +1954,13 @@ interface ViewportZoomOptions {
1866
1954
  min?: number;
1867
1955
  /** Upper clamp on the resulting view scale, forwarded to `zoomAt`. Default 8. */
1868
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;
1869
1964
  }
1870
1965
  /**
1871
1966
  * @experimental
@@ -2013,12 +2108,31 @@ declare const lassoSelectAction: Action & {
2013
2108
 
2014
2109
  /**
2015
2110
  * @experimental
2016
- * 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.
2017
2123
  *
2018
2124
  * Requires dep-schema entries: `view`.
2019
2125
  *
2020
2126
  * The invoker is `ongoing`. Zoom is applied per-frame via `view.set(zoomAt(...))`.
2021
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
+ */
2022
2136
  declare const pinchZoomAction: Action & {
2023
2137
  requires: string[];
2024
2138
  };
@@ -2143,6 +2257,14 @@ interface DispatcherViewTarget {
2143
2257
  * routing correct.
2144
2258
  */
2145
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;
2146
2268
  }
2147
2269
  /**
2148
2270
  * The part of `features/viewports`' `ViewResolver` the dispatcher needs: which
@@ -2286,7 +2408,7 @@ interface UseGestureDispatcherOptions {
2286
2408
  * `Action.eligible` rule (omitted => always eligible). `<SceneCanvas>`
2287
2409
  * wires this; tests / harnesses without chrome-caps state can omit it.
2288
2410
  */
2289
- getRuleCtx?: () => RuleCtx;
2411
+ getRuleCtx?: () => RuleCtx | undefined;
2290
2412
  /**
2291
2413
  * Routing for a canvas hosting more than one view: the non-root dispatch
2292
2414
  * records to choose between, read fresh per event, and the resolver that
@@ -2317,6 +2439,56 @@ interface UseGestureDispatcherOptions {
2317
2439
  */
2318
2440
  declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
2319
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
+
2320
2492
  /**
2321
2493
  * Pure wheel-event reducer. Stateless sibling of `useZoom`'s
2322
2494
  * `onWheel` handler — useful in tests, in non-React reducers, or anywhere
@@ -2410,17 +2582,6 @@ declare function useDecayLoop(): {
2410
2582
  cancel: () => void;
2411
2583
  };
2412
2584
 
2413
- /** Animate the view from where it is to a target view — zoom-to-fit,
2414
- * zoom-to-selection, and anything else that should glide rather than jump. */
2415
- declare function useViewTween(setView: (v: View) => void): {
2416
- animateTo: (from: View, to: View, opts?: {
2417
- duration?: number;
2418
- easing?: (t: number) => number;
2419
- }) => void;
2420
- cancel: () => void;
2421
- isAnimating: React$1.RefObject<boolean>;
2422
- };
2423
-
2424
2585
  /**
2425
2586
  * Detects two-finger pinch on a canvas element.
2426
2587
  *
@@ -2434,31 +2595,11 @@ declare function usePinchGesture(canvasRef: React.RefObject<HTMLCanvasElement |
2434
2595
  y: number;
2435
2596
  }, scaleFactor: number) => void, enabled?: boolean): void;
2436
2597
 
2437
- /** Options accepted by {@link useViewAnimation}'s `animateToBounds`. */
2438
- interface AnimateToBoundsOptions extends FitViewToBoundsOptions {
2439
- /** Tween duration in ms (forwarded to `animateTo`). */
2440
- duration?: number;
2441
- /** Tween easing (forwarded to `animateTo`). */
2442
- easing?: (t: number) => number;
2443
- }
2444
2598
  /**
2445
- * Tween the viewport `View` between values. Wraps `useViewTween`'s `animateTo`
2446
- * + `cancel`, and adds an `animateToBounds` convenience that composes
2447
- * `fitViewToBounds` with the existing tween so consumers can say "zoom to this
2448
- * bounds with animation" in one call.
2449
- *
2450
- * `animateToBounds` needs the current `View` and the current viewport
2451
- * dimensions to compute the target — both are passed as arguments so this
2452
- * 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`.
2453
2601
  */
2454
- declare function useViewAnimation(setView: (v: View) => void): {
2455
- animateTo: (from: View, to: View, opts?: {
2456
- duration?: number;
2457
- easing?: (t: number) => number;
2458
- }) => void;
2459
- animateToBounds: (bounds: Bounds, currentView: View, viewportDims: ViewportDims, opts?: AnimateToBoundsOptions) => void;
2460
- cancel: () => void;
2461
- };
2602
+ declare function interpolateView(from: View, to: View): (t: number) => View;
2462
2603
 
2463
2604
  /**
2464
2605
  * Build a `Tool<TScratch>` from a declarative `ToolDef<TScratch>`.
@@ -2574,7 +2715,7 @@ interface UseKeybindingsOptions {
2574
2715
  declare function useKeybindings(tools: ToolsApi, options?: UseKeybindingsOptions): void;
2575
2716
 
2576
2717
  /** The value types a built-in pref leaf can hold. */
2577
- type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint';
2718
+ type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint' | 'object';
2578
2719
  interface ToolPrefBase<K extends string, Value> {
2579
2720
  kind: K;
2580
2721
  /** Human-readable label. */
@@ -2589,6 +2730,12 @@ interface ToolPrefBase<K extends string, Value> {
2589
2730
  * (weasel-ui `PrefsForm` honors this for leaves whose control brings
2590
2731
  * its own chrome). */
2591
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;
2592
2739
  /** Row-pairing hint for compact property UIs (weasel-ui
2593
2740
  * `SelectionPanel`): leaves sharing a `pair` id render side-by-side
2594
2741
  * on one row labeled with the `pair` string (e.g. `'Position'` for
@@ -2602,7 +2749,7 @@ type ToolPrefBooleanControl = 'checkbox' | 'switch';
2602
2749
  /** How a schema-driven UI should present a string pref. */
2603
2750
  type ToolPrefStringControl = 'input' | 'textarea';
2604
2751
  /** How a schema-driven UI should present an enum pref. */
2605
- type ToolPrefEnumControl = 'select' | 'radio';
2752
+ type ToolPrefEnumControl = 'select' | 'radio' | 'toggle';
2606
2753
  /** Display-unit conversion for number leaves whose stored value uses a
2607
2754
  * canonical unit the user shouldn't see (e.g. radians stored, degrees
2608
2755
  * shown). The stored value stays canonical; UIs convert at the edge. */
@@ -2629,13 +2776,51 @@ interface ToolPrefBoolean extends ToolPrefBase<'boolean', boolean> {
2629
2776
  interface ToolPrefString extends ToolPrefBase<'string', string> {
2630
2777
  control?: ToolPrefStringControl;
2631
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
+ }
2632
2801
  /** A pref with a fixed set of labeled choices. */
2633
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. */
2634
2815
  options: readonly {
2635
2816
  value: T;
2636
2817
  label: string;
2818
+ short?: string;
2819
+ icon?: string;
2820
+ disabled?: boolean;
2637
2821
  }[];
2638
2822
  control?: ToolPrefEnumControl;
2823
+ encoding?: ToolPrefEnumEncoding<T>;
2639
2824
  }
2640
2825
  /** A single color, stored as a hex string. For a value that may also be a
2641
2826
  * gradient or a pattern, use {@link ToolPrefPaint} instead. */
@@ -2666,13 +2851,60 @@ interface ToolPrefPaint extends ToolPrefBase<'paint', unknown> {
2666
2851
  /** Offer an opacity control alongside the color. */
2667
2852
  alpha?: boolean;
2668
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
+ }
2669
2882
  /** One built-in pref leaf. `ToolPrefLeaf` widens this to include
2670
2883
  * app-defined kinds. */
2671
- 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;
2672
2899
  /** Built-in or app-defined leaf. */
2673
2900
  type ToolPrefLeaf = ToolPref | ToolPrefCustom;
2674
2901
  /** Nestable group: branch nodes a tool can use to organize its prefs. */
2675
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). */
2676
2908
  name: string;
2677
2909
  description?: string;
2678
2910
  children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
@@ -2773,8 +3005,8 @@ declare function useSelectTool<TNode extends {
2773
3005
  id: string;
2774
3006
  }, TPose>(adapter: SelectAdapter<TNode, TPose>, options: UseSelectToolOptions<TPose>): Tool<null>;
2775
3007
 
2776
- /** Options for `useRotateTool` — mostly the placement and hit size of the
2777
- * rotation handle. */
3008
+ /** Options for `useRotateTool` — mostly the placement of the rotation
3009
+ * handle's hover band. */
2778
3010
  interface UseRotateToolOptions<TNode extends {
2779
3011
  id: string;
2780
3012
  }, _TPose> {
@@ -2784,10 +3016,10 @@ interface UseRotateToolOptions<TNode extends {
2784
3016
  * backwards-compat with consumers (notably `SceneCanvas`'s
2785
3017
  * `rotateOptions`) that still pass a value. */
2786
3018
  rotate?: unknown;
2787
- /** 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. */
2788
3022
  rotationHandleDistance?: number;
2789
- /** Square hit-radius for the rotation handle. Default: 8. */
2790
- handleHitRadius?: number;
2791
3023
  /** World-space bounds lookup. Required for the rotation affordance hit-test
2792
3024
  * in consumers that wire a `boundsOf` source separately from the
2793
3025
  * ChromeState-driven affordance pipeline. Retained for parity with
@@ -2951,8 +3183,12 @@ interface PinchZoomToolOpts {
2951
3183
  *
2952
3184
  * The anchor point under the gesture midpoint stays fixed on screen as the
2953
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.
2954
3190
  */
2955
- 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;
2956
3192
 
2957
3193
  /**
2958
3194
  * In-progress pen anchor. `outHandle` is set when the anchor was placed via
@@ -3364,6 +3600,35 @@ interface CanvasExtensionApi {
3364
3600
  * (Replaces the pre-A2 pattern where `ref.current` directly *was* the element.) */
3365
3601
  readonly element: HTMLCanvasElement | null;
3366
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;
3367
3632
  /** Register an externally-owned RenderLayer. The layer participates in the
3368
3633
  * draw stack and, if it implements `hitTest`, in {@link hitTestExtras}. */
3369
3634
  registerLayer(layer: RenderLayer<unknown>): () => void;
@@ -3421,21 +3686,50 @@ interface SceneCanvasApi extends CanvasExtensionApi {
3421
3686
  x: number;
3422
3687
  y: number;
3423
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;
3424
3699
  }
3425
3700
 
3426
3701
  /**
3427
- * Union math behind `CanvasHelpers.getGestureBounds()`.
3702
+ * The seam `<Canvas>` reads in-flight gesture state through.
3428
3703
  *
3429
3704
  * `getGestureBounds` answers "where is the thing the user is dragging right
3430
3705
  * now, in total?" — a question every other lookup on `CanvasHelpers` can't
3431
3706
  * answer because they're keyed by node id, and a drag-to-insert has no node
3432
- * yet. Canvas collects the parts (preview bounds per in-flight id, plus any
3433
- * 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`).
3434
3709
  *
3435
- * Kept free of tool / dispatcher types so it stays a pure geometry fold that
3436
- * 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.
3437
3714
  */
3438
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
+ }
3439
3733
  /**
3440
3734
  * Everything `<Canvas>` needs to know about in-flight gestures that it can't
3441
3735
  * see for itself.
@@ -3467,6 +3761,13 @@ interface GestureSource {
3467
3761
  subscribe(fn: () => void): () => void;
3468
3762
  /** Monotonic counter bumped on the same events `subscribe` fires on. */
3469
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[];
3470
3771
  }
3471
3772
 
3472
3773
  /**
@@ -3542,11 +3843,35 @@ interface CanvasViewHelpers<TPose> {
3542
3843
  * custom layers that need overlay-aware selection state (selection ids,
3543
3844
  * bounds, multi-union AABB, modifier flags) read from this. */
3544
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;
3545
3871
  }
3546
3872
  /**
3547
3873
  * The half of {@link CanvasHelpers} that belongs to the surface — one GL
3548
- * context, one debug sink, one chrome-caps resolver, however many views are
3549
- * drawn on it.
3874
+ * context, one debug sink, however many views are drawn on it.
3550
3875
  */
3551
3876
  interface CanvasSurfaceHelpers {
3552
3877
  /** Active debug sink, when `<Canvas debug=...>` is enabled. Layers that
@@ -3554,11 +3879,6 @@ interface CanvasSurfaceHelpers {
3554
3879
  * call into this from their `draw` callback. Returns `null` when
3555
3880
  * debug is off — no-op for production renders. */
3556
3881
  getDebug(): DebugSink | null;
3557
- /** Chrome-caps visibility predicate, keyed by chrome id. Returns a
3558
- * function that affordance/overlay layers can call per-element to
3559
- * decide whether to draw / hit-test. When the parent didn't supply
3560
- * a resolver, this returns the universal `() => true`. */
3561
- getIsVisible(): (id: string) => boolean;
3562
3882
  }
3563
3883
  /** Live overlay-aware lookups exposed to custom layers via `helpersRef`.
3564
3884
  * What a layer receives as its `data` argument, unchanged: the two halves
@@ -3568,155 +3888,335 @@ interface CanvasHelpers<TPose> extends CanvasViewHelpers<TPose>, CanvasSurfaceHe
3568
3888
  }
3569
3889
 
3570
3890
  /**
3571
- * Selection overlay primitives render layers for drawing selection
3572
- * outlines and corner resize handles in world space.
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.
3573
3896
  *
3574
- * Three pieces:
3575
- * - `composeSelectionPose` resolves the live pose for a selected id by
3576
- * consulting the move overlay first, then the resize overlay, then the
3577
- * stored pose. When `getChildren`/`isContainer` are supplied and the id
3578
- * resolves to a container, the returned pose is the union AABB of all
3579
- * transitive leaf poses (with the same precedence rules applied per leaf).
3580
- * - `createSelectionOutlineLayer` draws the outline rect for each selected
3581
- * id (container ids resolve to a union AABB via `getChildren`/`isContainer`).
3582
- * - `createSelectionHandlesLayer` draws resize-handle rects (default 4
3583
- * corners) for each selected id, with the same container-resolution rules.
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.).
3584
3902
  *
3585
- * `createSelectionOverlayLayer` is a thin convenience that returns a single
3586
- * `RenderLayer` whose draw runs the outline pass then the handles pass.
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.
3587
3907
  *
3588
- * **Pose shape:** TPose is generic; callers must supply `getBounds(pose)`
3589
- * to project any pose into the AABB the renderer needs. For rect-shaped
3590
- * poses (`{x, y, width, height}`) pass the identity. For `Path` poses pass
3591
- * `boundsOfPath`. Container ids reduce via `unionBounds` over the projected
3592
- * AABBs.
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.
3593
3921
  */
3594
3922
 
3595
- /** Options for `composeSelectionPose`. */
3596
- interface ComposeSelectionPoseOpts<TPose> {
3597
- /** Move overlay; when present its `poses` map wins over everything else. */
3598
- moveOverlay?: {
3599
- poses: Map<string, TPose>;
3600
- } | null;
3601
- /**
3602
- * Resize overlay; consulted only when move overlay does not own the id.
3603
- * For container resize, `leafPoses` (when present) maps each leaf id under
3604
- * the container to its overlay pose. If absent the container falls back to
3605
- * stored leaf poses (defensive container-resize integration is in flight).
3606
- */
3607
- resizeOverlay?: {
3608
- id: string;
3609
- currentPose: TPose;
3610
- leafPoses?: Map<string, TPose>;
3611
- } | null;
3612
- /** Fallback pose lookup (typically the stored/committed pose). */
3613
- getStoredPose: (id: string) => TPose;
3614
- /**
3615
- * Project a pose into its AABB. Used when reducing a group of leaf poses
3616
- * into a single union AABB. Defaults to the identity — rect-shaped poses
3617
- * (`{x, y, width, height}`) need no override. For `Path` poses pass
3618
- * `boundsOfPath`.
3619
- */
3620
- getBounds?: (pose: TPose) => Bounds;
3621
- /**
3622
- * Wrap an AABB back into a TPose. Called only when the resolver collapses
3623
- * a container's leaves into a single union AABB. Defaults to the identity —
3624
- * for `Path` poses pass `(b) => ({ kind: 'rect', ...b })`.
3625
- */
3626
- fromBounds?: (bounds: Bounds) => TPose;
3627
- /** Walk a container's direct children (e.g. `scene.childrenOf`). With
3628
- * `isContainer`, a selected container resolves to the union AABB of its
3629
- * transitive leaf poses instead of its own stored pose. */
3630
- getChildren?: (id: string) => readonly string[];
3631
- /** True when `id` is a structural container. */
3632
- isContainer?: (id: string) => boolean;
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;
3633
3940
  }
3634
3941
  /**
3635
- * Build a pose resolver for a selection. Precedence per id:
3636
- * move overlay > resize overlay > stored. When `getChildren`/`isContainer`
3637
- * are supplied and the id resolves to a container, the resolver returns the
3638
- * union AABB of all transitive leaf poses (each leaf still subject to the
3639
- * precedence rules). Containers with no leaves resolve to `null`.
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}.
3640
3949
  */
3641
- declare function composeSelectionPose<TPose>(opts: ComposeSelectionPoseOpts<TPose>): (id: string) => TPose | null;
3642
- /** Shared options between outline and handles layers. */
3643
- interface SelectionLayerCommon<TPose> {
3644
- getSelection: () => readonly NodeId[];
3645
- /** Return null to skip rendering for an id (e.g. resolved pose unavailable).
3646
- * Takes `string` rather than `NodeId` because the container-aware bounds
3647
- * resolver internally walks expanded leaf ids via `getChildren`, which is
3648
- * generic over arbitrary string ids. NodeIds flow in fine — a NodeId is a
3649
- * string. */
3650
- getPose: (id: string) => TPose | null;
3651
- /**
3652
- * Project a pose into its AABB. Defaults to the identity rect-shaped
3653
- * poses (`{x, y, width, height}`) need no override. For `Path` poses pass
3654
- * `boundsOfPath`.
3655
- */
3656
- getBounds?: (pose: TPose) => Bounds;
3657
- /** Walk a container's direct children (e.g. `scene.childrenOf`). When
3658
- * supplied with `isContainer`, any id that resolves to a container is
3659
- * rendered using the union bounds of all its transitive leaves. */
3660
- getChildren?: (id: string) => readonly string[];
3661
- /** True when `id` is a structural container. */
3662
- isContainer?: (id: string) => boolean;
3663
- }
3664
- /** Options for `createSelectionOutlineLayer`. */
3665
- interface SelectionOutlineLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
3666
- /** Outline stroke style + outset distance from the pose rect. */
3667
- outline?: Stroke & {
3668
- pad?: number;
3669
- };
3670
- }
3671
- /** Options for `createSelectionHandlesLayer`. */
3672
- interface SelectionHandlesLayerOpts<TPose> extends SelectionLayerCommon<TPose> {
3673
- /** Handle visuals. Omit for defaults. */
3674
- handles?: {
3675
- size?: number;
3676
- fill?: FillStyle;
3677
- outline?: Stroke;
3678
- };
3679
- /** Override handle placement. Default: 4 corners of the AABB. */
3680
- handlesOf?: (bounds: Bounds) => {
3681
- x: number;
3682
- y: number;
3683
- }[];
3684
- /** Render a rotation handle above the (rotated) top-center of the AABB.
3685
- * When `true`, uses default visuals + distance. When an object, override
3686
- * the world-space distance from the top edge. Defaults to `false` —
3687
- * consumers opt in only when wiring `useRotate`. */
3688
- rotationHandle?: boolean | {
3689
- /** World-pixel distance from the top edge to the handle center. */
3690
- distance?: number;
3691
- };
3692
- }
3693
- /** Options for `createSelectionOverlayLayer`. */
3694
- interface SelectionOverlayLayerOpts<TPose> extends Omit<SelectionLayerCommon<TPose>, 'getSelection' | 'getPose'> {
3695
- /**
3696
- * Which ids to draw chrome for. Omit to take them from the `ChromeState` on
3697
- * the draw envelope, which is what makes one canvas's several views each
3698
- * outline their own selection the layer is shared, the envelope is not.
3699
- *
3700
- * A multi-selection resolves to the synthetic union id, with the real
3701
- * members going to the outline pass, exactly as the explicit form does.
3702
- */
3703
- getSelection?: () => readonly NodeId[];
3704
- /**
3705
- * Resolve an id to the pose to draw chrome around. Omit to take bounds from
3706
- * the same envelope `getSelection` omitted takes ids from — one cascade,
3707
- * the one the chrome state was built with, rather than a second one here
3708
- * that has to agree with it.
3709
- */
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
+ */
4149
+ getBounds?: (pose: TPose) => Bounds;
4150
+ /**
4151
+ * Wrap an AABB back into a TPose. Called only when the resolver collapses
4152
+ * a container's leaves into a single union AABB. Defaults to the identity —
4153
+ * for `Path` poses pass `(b) => ({ kind: 'rect', ...b })`.
4154
+ */
4155
+ fromBounds?: (bounds: Bounds) => TPose;
4156
+ /** Walk a container's direct children (e.g. `scene.childrenOf`). With
4157
+ * `isContainer`, a selected container resolves to the union AABB of its
4158
+ * transitive leaf poses instead of its own stored pose. */
4159
+ getChildren?: (id: string) => readonly string[];
4160
+ /** True when `id` is a structural container. */
4161
+ isContainer?: (id: string) => boolean;
4162
+ }
4163
+ /**
4164
+ * Build a pose resolver for a selection. Precedence per id:
4165
+ * move overlay > resize overlay > stored. When `getChildren`/`isContainer`
4166
+ * are supplied and the id resolves to a container, the resolver returns the
4167
+ * union AABB of all transitive leaf poses (each leaf still subject to the
4168
+ * precedence rules). Containers with no leaves resolve to `null`.
4169
+ */
4170
+ declare function composeSelectionPose<TPose>(opts: ComposeSelectionPoseOpts<TPose>): (id: string) => TPose | null;
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. */
4174
+ interface SelectionLayerCommon<TPose> {
4175
+ /**
4176
+ * Project a pose into its AABB. Defaults to the identity — rect-shaped
4177
+ * poses (`{x, y, width, height}`) need no override. For `Path` poses pass
4178
+ * `boundsOfPath`.
4179
+ */
4180
+ getBounds?: (pose: TPose) => Bounds;
4181
+ /** Walk a container's direct children (e.g. `scene.childrenOf`). When
4182
+ * supplied with `isContainer`, any id that resolves to a container is
4183
+ * rendered using the union bounds of all its transitive leaves. */
4184
+ getChildren?: (id: string) => readonly string[];
4185
+ /** True when `id` is a structural container. */
4186
+ isContainer?: (id: string) => boolean;
4187
+ }
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'>;
4194
+ /** Options for `createSelectionOverlayLayer`. */
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
+ */
3710
4214
  getPose?: (id: string) => TPose | null;
3711
4215
  outline?: Stroke & {
3712
4216
  pad?: number;
3713
4217
  };
3714
4218
  /** Pass `false` to render outlines only. */
3715
- handles?: {
3716
- size?: number;
3717
- fill?: FillStyle;
3718
- outline?: Stroke;
3719
- } | false;
4219
+ handles?: SelectionHandleStyle | false;
3720
4220
  handlesOf?: (bounds: Bounds) => {
3721
4221
  x: number;
3722
4222
  y: number;
@@ -3737,10 +4237,16 @@ interface SelectionOverlayLayerOpts<TPose> extends Omit<SelectionLayerCommon<TPo
3737
4237
  * per-anchor chrome takes over. */
3738
4238
  getSuppressedIds?: () => ReadonlySet<string>;
3739
4239
  }
4240
+ /** Handle visuals, as a consumer supplies them. */
4241
+ interface SelectionHandleStyle {
4242
+ size?: number;
4243
+ fill?: FillStyle;
4244
+ outline?: Stroke;
4245
+ }
3740
4246
  /**
3741
4247
  * `RenderLayer` that draws selection outlines only. Stack alongside
3742
- * `createSelectionHandlesLayer` (or just use `createSelectionOverlayLayer`
3743
- * 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.
3744
4250
  */
3745
4251
  declare function createSelectionOutlineLayer<TPose>(opts: SelectionOutlineLayerOpts<TPose>): RenderLayer<unknown>;
3746
4252
  /**
@@ -3749,10 +4255,9 @@ declare function createSelectionOutlineLayer<TPose>(opts: SelectionOutlineLayerO
3749
4255
  */
3750
4256
  declare function createSelectionHandlesLayer<TPose>(opts: SelectionHandlesLayerOpts<TPose>): RenderLayer<unknown>;
3751
4257
  /**
3752
- * Convenience wrapper that draws outlines then handles in a single layer.
3753
- * Equivalent to stacking `createSelectionOutlineLayer` and
3754
- * `createSelectionHandlesLayer` in a layer sequence. Pass `handles: false` to
3755
- * 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.
3756
4261
  */
3757
4262
  declare function createSelectionOverlayLayer<TPose>(opts: SelectionOverlayLayerOpts<TPose>): RenderLayer<unknown>;
3758
4263
 
@@ -3830,8 +4335,15 @@ interface SceneSlotConfig<TNode extends {
3830
4335
  objects?: TNode[];
3831
4336
  /** Project an object to its committed pose. Defaults to `adapter.getPose(obj.id)`. */
3832
4337
  toPose?: (obj: TNode) => TPose;
3833
- /** Draw a single object as a `DrawCommand` tree. */
3834
- 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;
3835
4347
  /** Default ghost alpha for the move-overlay slot. Default 0.85. */
3836
4348
  ghostAlpha?: number;
3837
4349
  /**
@@ -3930,6 +4442,20 @@ interface CanvasProps<TNode extends {
3930
4442
  * same contract the headless `renderSceneToPixels` path follows (that
3931
4443
  * path never reads ambient density at all). */
3932
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;
3933
4459
  /**
3934
4460
  * Combined adapter for scene-slot rendering, bounds computation, and
3935
4461
  * move/resize/rotate gesture math. Optional — bare-Canvas consumers that
@@ -3993,6 +4519,12 @@ interface CanvasProps<TNode extends {
3993
4519
  className?: string;
3994
4520
  style?: React__default.CSSProperties;
3995
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>;
3996
4528
  /**
3997
4529
  * When `true` (default), the canvas element receives focus on `pointerdown`
3998
4530
  * so keyboard events (tool hotkeys, undo/redo) are captured without a
@@ -4101,7 +4633,9 @@ interface CanvasProps<TNode extends {
4101
4633
  * Hand tool registration, wheel pan/zoom action descriptors, and keyboard
4102
4634
  * zoom shortcuts are SceneCanvas-level concerns and are NOT owned by Canvas.
4103
4635
  * Those belong with the tool registry and gesture dispatcher that live in
4104
- * 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.
4105
4639
  *
4106
4640
  * When omitted, no pinch-zoom listener is attached.
4107
4641
  */
@@ -4196,448 +4730,6 @@ interface CanvasProps<TNode extends {
4196
4730
  layerOrder?: string[];
4197
4731
  }
4198
4732
 
4199
- /** Which of a node's two per-anchor color arrays an override applies to. */
4200
- type VertexColorChannel = 'fill' | 'stroke';
4201
- /** Function-form override: receives the consumer-supplied base color
4202
- * array and the current animation timestamp (ms, from the animator's
4203
- * clock). Returns a flat RGBA float array (values in 0..1, matching
4204
- * the renderer's `stroke.vertexColors` / `PathDrawCommand.vertexColors`
4205
- * color space) of the same length as `base`. */
4206
- type ColorOverrideFn = (base: readonly number[], tMs: number) => number[];
4207
- /** Either a static per-anchor RGBA float array (0..1) or a function-form
4208
- * override (see {@link ColorOverrideFn}). */
4209
- type ColorOverride = readonly number[] | ColorOverrideFn;
4210
- /** Per-node, per-channel store of color overrides consulted by `createPathLayer`
4211
- * before falling back to the consumer's `getVertexColors` / `getStrokeVertexColors`
4212
- * accessor. Attached to `useAnimator` as `animator.colorOverrides`. */
4213
- declare class ColorOverrideRegistry {
4214
- private readonly map;
4215
- private _version;
4216
- set(id: string, channel: VertexColorChannel, override: ColorOverride): void;
4217
- clear(id: string, channel: VertexColorChannel): void;
4218
- clearAll(): void;
4219
- get(id: string, channel: VertexColorChannel): ColorOverride | undefined;
4220
- version(): number;
4221
- }
4222
-
4223
- /** One keyframe. `easing` shapes the approach INTO this key from the previous
4224
- * one, so the first key's easing is never consulted. */
4225
- interface Keyframe<T> {
4226
- /** Time within the track's timeline, in ms. */
4227
- t: number;
4228
- value: T;
4229
- easing?: EasingFn;
4230
- }
4231
- /** A track sampled as a pure function of the playhead. Scrubbing one is free
4232
- * and order-independent. */
4233
- interface SampledTrack<T> {
4234
- kind: 'sampled';
4235
- label?: string;
4236
- /** Sorted ascending by `t`. `sampleTrack` assumes this and does not sort. */
4237
- keys: Keyframe<T>[];
4238
- /** Required when T is not `number`; defaults to numeric lerp otherwise. */
4239
- interpolate?: Interpolate<T>;
4240
- /** Built once per segment and cached. Takes precedence over `interpolate`. */
4241
- interpolator?: InterpolatorFactory<T>;
4242
- onTick: (value: T) => void;
4243
- }
4244
- /** A track of edge crossings. Fires only when the playhead advances forward
4245
- * under playback — never on `seek`. */
4246
- interface EventTrack {
4247
- kind: 'event';
4248
- label?: string;
4249
- /** Sorted ascending by `t`. */
4250
- events: {
4251
- t: number;
4252
- fire: () => void;
4253
- }[];
4254
- }
4255
- /** A nested timeline, evaluated at `playhead - at`. Children are NOT registered
4256
- * with the animator separately; the parent evaluates them. */
4257
- interface TimelineTrack {
4258
- kind: 'timeline';
4259
- label?: string;
4260
- at: number;
4261
- timeline: NestedTimeline;
4262
- }
4263
- type Track = SampledTrack<any> | EventTrack | TimelineTrack;
4264
- /** What a child timeline may declare. The parent owns playback, so `loop`,
4265
- * `autoplay`, `onDone` and `cancelKey` have no meaning below the root. */
4266
- interface NestedTimeline {
4267
- tracks: Track[];
4268
- /** Defaults to the largest end time across `tracks`. */
4269
- duration?: number;
4270
- }
4271
- interface TimelineOptions extends NestedTimeline {
4272
- /** `true` loops forever, `n` loops n additional times. Default false. */
4273
- loop?: boolean | number;
4274
- /** Default true. When false the timeline registers but holds at t=0 until resumed. */
4275
- autoplay?: boolean;
4276
- onDone?: () => void;
4277
- cancelKey?: string;
4278
- }
4279
- interface TimelineHandle extends AnimationHandle {
4280
- /** Move the playhead. Never fires event tracks, at any depth. */
4281
- seek(t: number): void;
4282
- /** Current playhead in ms. */
4283
- time(): number;
4284
- duration(): number;
4285
- tracks(): readonly Track[];
4286
- /** Run `fn`, then recompute duration, drop cached interpolators, and notify.
4287
- * Every mutation must go through this — an edited keyframe otherwise keeps
4288
- * interpolating toward its old value with no visible error. */
4289
- edit(fn: () => void): void;
4290
- /** Notified after each `edit`. Returns an unsubscribe. */
4291
- subscribe(cb: () => void): () => void;
4292
- }
4293
-
4294
- /** An easing curve: maps normalized progress `t ∈ [0, 1]` to eased progress.
4295
- * Curves may leave the 0–1 range in the middle (back, elastic) but should
4296
- * pass through 0 at 0 and 1 at 1. */
4297
- type EasingFn = (t: number) => number;
4298
- /** Blends two `T` values at eased progress `t`. Called once per frame; see
4299
- * {@link InterpolatorFactory} when the blend has setup worth hoisting. */
4300
- type Interpolate<T> = (from: T, to: T, t: number) => T;
4301
- /** Factory interpolator: built ONCE at tween start with (from, to), the returned
4302
- * function is called with `t ∈ [0, 1]` each frame. Use for interpolators with
4303
- * expensive setup (color-space conversion, path-string parsing) — d3-interpolate's
4304
- * shape exactly. For cheap interpolations the per-tick `Interpolate<T>` form is
4305
- * fine; this is the escape hatch when setup-per-tick is wasteful. */
4306
- type InterpolatorFactory<T> = (from: T, to: T) => (t: number) => T;
4307
- /** A spring's physical parameters. Higher stiffness settles faster, higher
4308
- * damping overshoots less, higher mass makes both sluggish. */
4309
- interface SpringPreset {
4310
- stiffness: number;
4311
- damping: number;
4312
- mass: number;
4313
- }
4314
- /** One of the tunings in `SPRING_PRESETS`. */
4315
- type SpringPresetName = 'gentle' | 'wobbly' | 'stiff' | 'slow';
4316
- /** A running animation. Cancel it, or bend its time — pausing and time-scaling
4317
- * act on this animation's own virtual clock, independent of the animator's. */
4318
- interface AnimationHandle {
4319
- /** Monotonic id assigned by the animator. */
4320
- id: number;
4321
- /** Cancel this animation. Idempotent — no-op once already finished/canceled. */
4322
- cancel(): void;
4323
- /** Freeze this animation's virtual clock. Idempotent. */
4324
- pause(): void;
4325
- /** Resume this animation's virtual clock. Idempotent. */
4326
- resume(): void;
4327
- /** Multiply this animation's virtual-clock rate by `scale`. 1 = normal. */
4328
- setTimeScale(scale: number): void;
4329
- /** True iff this handle is currently paused. */
4330
- isPaused(): boolean;
4331
- }
4332
- /** A duration-based animation from `from` to `to` over `ms`, shaped by an
4333
- * easing curve. Reach for a spring instead when the motion should respond to
4334
- * where the value already is rather than restart from a fixed duration. */
4335
- interface TweenOptions<T> {
4336
- from: T;
4337
- to: T;
4338
- ms: number;
4339
- easing?: EasingFn;
4340
- /** Required when T is not `number`. For T = number, defaults to linear numeric lerp.
4341
- * Called per-tick with `(from, to, t)`. For interpolators with expensive setup,
4342
- * prefer `interpolator` which is built once at tween start. */
4343
- interpolate?: Interpolate<T>;
4344
- /** Factory interpolator built once at tween start. Takes precedence over
4345
- * `interpolate` when both are provided. Use this for d3-interpolate or any
4346
- * `(from, to) => (t) => v` shape. */
4347
- interpolator?: InterpolatorFactory<T>;
4348
- onTick: (value: T) => void;
4349
- onDone?: () => void;
4350
- /** Any new animation passed the same cancelKey cancels the prior one in flight. */
4351
- cancelKey?: string;
4352
- }
4353
- /** A spring animation: runs until the value settles on `to` rather than for a
4354
- * set duration, so it absorbs an initial velocity naturally. Non-numeric `T`
4355
- * needs the four vector helpers. */
4356
- interface SpringOptions<T> {
4357
- from: T;
4358
- to: T;
4359
- /** Initial velocity in T-units per second. Default: zero (T-shape-aware). */
4360
- velocity?: T;
4361
- preset?: SpringPresetName;
4362
- stiffness?: number;
4363
- damping?: number;
4364
- mass?: number;
4365
- interpolate?: Interpolate<T>;
4366
- /** Vector helpers — required for non-numeric T. */
4367
- add?: (a: T, b: T) => T;
4368
- subtract?: (a: T, b: T) => T;
4369
- scale?: (v: T, k: number) => T;
4370
- magnitude?: (v: T) => number;
4371
- /** Velocity magnitude below which the spring is considered settled. Default 0.01. */
4372
- restThreshold?: number;
4373
- onTick: (value: T) => void;
4374
- onDone?: () => void;
4375
- cancelKey?: string;
4376
- }
4377
- /** Spring and decay as one animation. With a `to`, a spring pulls toward it;
4378
- * with `to: null`, the value coasts on its velocity. Either can become the
4379
- * other mid-flight through the handle. */
4380
- interface PhysicsOptions<T> {
4381
- from: T;
4382
- /** Target. `null` ⇒ no spring force (decay-mode). */
4383
- to?: T | null;
4384
- /** Initial velocity in T-units per second. */
4385
- velocity?: T;
4386
- preset?: SpringPresetName;
4387
- stiffness?: number;
4388
- damping?: number;
4389
- mass?: number;
4390
- restThreshold?: number;
4391
- /** Vector helpers — required for non-numeric T. */
4392
- add?: (a: T, b: T) => T;
4393
- subtract?: (a: T, b: T) => T;
4394
- scale?: (v: T, k: number) => T;
4395
- magnitude?: (v: T) => number;
4396
- onTick: (value: T) => void;
4397
- onDone?: () => void;
4398
- cancelKey?: string;
4399
- }
4400
- /** An `AnimationHandle` that can also be steered while it runs — the point of
4401
- * the physics primitive. */
4402
- interface PhysicsHandle<T = unknown> extends AnimationHandle {
4403
- /** Retarget mid-flight. `null` ⇒ switch to decay-mode (no spring force). */
4404
- setTarget(to: T | null): void;
4405
- /** Replace the current velocity in T-units per second. */
4406
- setVelocity(v: T): void;
4407
- }
4408
- /** Momentum: coast from `from` at `velocity`, slowing by `friction` each
4409
- * second until below `threshold`. What a flick-to-pan leaves behind. */
4410
- interface DecayOptions<T> {
4411
- from: T;
4412
- velocity: T;
4413
- /** Per-second velocity multiplier in (0, 1). Default 0.95. */
4414
- friction?: number;
4415
- /** Velocity magnitude below which decay stops. Default 0.5. */
4416
- threshold?: number;
4417
- add: (a: T, b: T) => T;
4418
- scale: (v: T, k: number) => T;
4419
- magnitude: (v: T) => number;
4420
- onTick: (value: T) => void;
4421
- onDone?: () => void;
4422
- cancelKey?: string;
4423
- }
4424
- /** Options for `useAnimator`. Everything here is an injection seam for tests;
4425
- * the defaults are the real clock, rAF, and `setTimeout`. */
4426
- interface UseAnimatorOptions {
4427
- /** Optional clock injection for tests. Returns ms since some epoch. */
4428
- now?: () => number;
4429
- /** Optional rAF / cAF injection for tests. Defaults to window.requestAnimationFrame. */
4430
- requestFrame?: (cb: (t: number) => void) => number;
4431
- cancelFrame?: (handle: number) => void;
4432
- /** Optional `setTimeout` injection used by `stagger` for per-item delays.
4433
- * Defaults to the global `setTimeout`. Tests inject a virtual scheduler. */
4434
- setTimer?: (cb: () => void, ms: number) => unknown;
4435
- /** Companion to `setTimer`. Defaults to global `clearTimeout`. */
4436
- clearTimer?: (handle: unknown) => void;
4437
- }
4438
- /**
4439
- * Owns every running animation on a canvas and drives them from one rAF loop.
4440
- * Beyond the primitives (`tween`, `spring`, `decay`, `physics`) it offers
4441
- * composition — `loop`, `stagger` — and bulk control by handle, by cancel-key,
4442
- * or over everything at once.
4443
- *
4444
- * An animator does not know about the scene: animations report values through
4445
- * `onTick` and the caller decides what to do with them.
4446
- */
4447
- interface Animator {
4448
- tween<T>(opts: TweenOptions<T>): AnimationHandle;
4449
- spring<T>(opts: SpringOptions<T>): AnimationHandle;
4450
- decay<T>(opts: DecayOptions<T>): AnimationHandle;
4451
- /** Unified spring/decay primitive. With `to` set, behaves as a spring;
4452
- * with `to: null`, behaves as a velocity-driven decay. Supports
4453
- * mid-flight retargeting via the returned handle's `setTarget`. */
4454
- physics<T>(opts: PhysicsOptions<T>): PhysicsHandle<T>;
4455
- /** Cancel a specific animation by handle. Pose stays at current value (no jump). */
4456
- cancel(handle: AnimationHandle): void;
4457
- /** Cancel every animation currently active under `key`. */
4458
- cancelKey(key: string): void;
4459
- /** Cancel everything. Useful from a destructor or "reset scene" path. */
4460
- cancelAll(): void;
4461
- /** True iff at least one animation is active. With `key`, scoped to that cancelKey. */
4462
- isActive(key?: string): boolean;
4463
- /**
4464
- * True while the animator is currently executing an animation tick. Useful
4465
- * for adapter wrappers (e.g. `animateOnSetPose`) that need to detect
4466
- * "this `setPose` was called from inside another animation's onTick"
4467
- * (momentum decay, in-flight tween, spring) and avoid recursively
4468
- * scheduling a new wrap-animation that would fight the caller.
4469
- */
4470
- isTicking(): boolean;
4471
- /** Freeze every animation managed by this animator. */
4472
- pause(): void;
4473
- /** Resume every animation managed by this animator. */
4474
- resume(): void;
4475
- /** True iff the animator is currently globally paused. */
4476
- isPaused(): boolean;
4477
- /** Multiply every animation's virtual-clock rate by `scale`. 1 = normal. */
4478
- setTimeScale(scale: number): void;
4479
- /** Freeze every animation whose `cancelKey` matches. */
4480
- pauseKey(key: string): void;
4481
- /** Resume every animation whose `cancelKey` matches. */
4482
- resumeKey(key: string): void;
4483
- /** Set per-animation timeScale for every animation whose `cancelKey` matches. */
4484
- setTimeScaleByKey(key: string, scale: number): void;
4485
- /**
4486
- * Loop primitive: repeatedly invoke `factory` to produce a child animation.
4487
- * The factory must wire its returned handle's `onDone` to call `next` so
4488
- * the loop advances. Returns a handle whose pause/resume/setTimeScale/cancel
4489
- * delegate to the current in-flight child (and prevent future iterations
4490
- * on cancel).
4491
- *
4492
- * The loop is registered with the animator under a supervisor entry so
4493
- * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
4494
- * `animator.isActive(opts.cancelKey)` all work for it.
4495
- */
4496
- loop(factory: LoopFactory, opts?: LoopOptions): AnimationHandle;
4497
- /** Sugar over `loop` for the common case of looping a tween between two
4498
- * values with optional direction handling (`restart` | `reverse` |
4499
- * `alternate`). Registered with the animator like `loop`. */
4500
- tweenLoop<T>(opts: TweenLoopOptions<T>): AnimationHandle;
4501
- /**
4502
- * Stagger primitive: schedule a per-item animation, offset by `delay` ms
4503
- * per index (or a custom function of the index). Two forms:
4504
- * - Factory form: pass `factory` directly, returns a composite
4505
- * `AnimationHandle`.
4506
- * - Builder form: omit `factory`, get a `StaggerBuilder` for fluent
4507
- * `.each` / `.tween` / `.springPose` calls.
4508
- *
4509
- * The composite handle's `cancel` cancels pending timers AND in-flight
4510
- * children. `pause` / `resume` / `setTimeScale` propagate to in-flight
4511
- * children; `pause`/`resume` also freeze and thaw pending per-item timers
4512
- * (the remaining time before each pending fire is preserved across the
4513
- * pause).
4514
- *
4515
- * The stagger is registered with the animator under a supervisor entry so
4516
- * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
4517
- * `animator.isActive(opts.cancelKey)` all work for it.
4518
- */
4519
- stagger<TItem>(items: readonly TItem[], delay: StaggerDelay): StaggerBuilder<TItem>;
4520
- stagger<TItem>(items: readonly TItem[], delay: StaggerDelay, factory: StaggerFactory<TItem>, opts?: StaggerOptions): AnimationHandle;
4521
- /**
4522
- * Keyframe timeline. Registered like any other animation, so its playhead
4523
- * responds to `pause`, `setTimeScale` and `cancelKey`. Sampled tracks are a
4524
- * pure function of the playhead; event tracks fire only on forward playback.
4525
- */
4526
- timeline(opts: TimelineOptions): TimelineHandle;
4527
- /** Per-node, per-channel color override registry consulted by the renderer's
4528
- * path layer before reading consumer accessors. Used by `tweenVertexColors`,
4529
- * `springVertexColors`, `cycleVertexColors`, `staggerVertexColors`. Cleared
4530
- * automatically on animator unmount. */
4531
- colorOverrides: ColorOverrideRegistry;
4532
- /**
4533
- * Subscribe to a callback fired once per RAF frame while any animation is
4534
- * active. Returns an unsubscribe function. Used by consumers (typically
4535
- * `<SceneCanvas>`) that need to repaint when an animation's side-effect
4536
- * is read from a non-scene channel (e.g. `colorOverrides` consulted from
4537
- * a custom `drawOne`) — scene mutations naturally trigger a repaint, but
4538
- * `colorOverrides` writes do not.
4539
- *
4540
- * The callback fires AFTER the per-frame tick of each registered
4541
- * animation, so by the time it runs `colorOverrides.get(...)` returns
4542
- * the latest values. If no animations are active, no tick fires.
4543
- */
4544
- onTick(cb: () => void): () => void;
4545
- /**
4546
- * Keep the animator's RAF loop running until the returned cancel
4547
- * function is called. Use for animations whose effect is read on every
4548
- * frame but which don't have a natural progress state (e.g.
4549
- * `cycleVertexColors`, which expresses its current value as a function
4550
- * of `performance.now()` rather than as a tween from `from` to `to`).
4551
- * Without a keep-alive entry the loop would idle and `onTick` would
4552
- * stop firing even though the override is still installed.
4553
- */
4554
- keepAlive(): () => void;
4555
- }
4556
- /** Options for `Animator.loop`. */
4557
- interface LoopOptions {
4558
- /** Maximum number of iterations. Default Infinity. */
4559
- count?: number;
4560
- /** Invoked when the loop reaches `count` iterations naturally (not on cancel). */
4561
- onDone?: () => void;
4562
- /** Any new animation passed the same cancelKey cancels the prior one in flight.
4563
- * Also enables `animator.cancelKey` / `animator.isActive(key)` for this loop. */
4564
- cancelKey?: string;
4565
- }
4566
- /** Options for the top-level `Animator.stagger` factory form (third overload). */
4567
- interface StaggerOptions {
4568
- /** Cancel-key for the supervising registration. `animator.cancelKey(key)`
4569
- * cancels the whole stagger; `animator.isActive(key)` returns true while
4570
- * any timer or child is alive. */
4571
- cancelKey?: string;
4572
- }
4573
- /** Produces one iteration of a loop. Must arrange for `next` to be called when
4574
- * the animation it returns finishes, or the loop stalls after one pass. */
4575
- type LoopFactory = (iteration: number, next: () => void) => AnimationHandle;
4576
- /** Per-index delay schedule. Number ⇒ `index * delay` ms. Function ⇒ caller
4577
- * decides the absolute delay for each index (e.g. `i => i * i * 30`). */
4578
- type StaggerDelay = number | ((index: number) => number);
4579
- /** Produces the animation for one staggered item. */
4580
- type StaggerFactory<TItem> = (item: TItem, index: number) => AnimationHandle;
4581
- /** A `T` value or a function that derives one from the per-item context. Used
4582
- * by the fluent builder methods (`.tween`, `.springPose`) so each item can
4583
- * vary an option (e.g. `to: (_item, i) => (i + 1) * 10`). */
4584
- type StaggerPerItem<T, TItem> = T | ((item: TItem, index: number) => T);
4585
- /** Options for the stagger builder's `.tween`: a tween per item, where
4586
- * `from`, `to` and `ms` may each vary by item. */
4587
- interface StaggerTweenOptions<T, TItem> {
4588
- from: StaggerPerItem<T, TItem>;
4589
- to: StaggerPerItem<T, TItem>;
4590
- ms: StaggerPerItem<number, TItem>;
4591
- easing?: EasingFn;
4592
- interpolate?: Interpolate<T>;
4593
- onTick: (value: T, item: TItem, index: number) => void;
4594
- onDone?: (item: TItem, index: number) => void;
4595
- }
4596
- /** Options for the stagger builder's `.springPose`: the spring tuning, and
4597
- * whether each item's settle is recorded as an undoable op. */
4598
- interface StaggerSpringPoseOptions<TPose> {
4599
- preset?: SpringPresetName;
4600
- stiffness?: number;
4601
- damping?: number;
4602
- mass?: number;
4603
- geometry?: PoseProjection<TPose>;
4604
- recordOp?: boolean;
4605
- opLabel?: string;
4606
- }
4607
- /** Fluent form of `Animator.stagger`: pick what to run per item after the
4608
- * items and the delay schedule are already fixed. */
4609
- interface StaggerBuilder<TItem> {
4610
- /** Run an arbitrary per-item factory. */
4611
- each(factory: StaggerFactory<TItem>): AnimationHandle;
4612
- /** Sugar: per-item `animator.tween` with per-item-varying options. */
4613
- tween<T>(opts: StaggerTweenOptions<T, TItem>): AnimationHandle;
4614
- /** Sugar: per-item `springPose` against an adapter. `poseFn` returns the
4615
- * target pose for each item. Each item must either be a primitive
4616
- * (string/number) or expose a string `id` field — otherwise pose ids
4617
- * would collide on `"[object Object]"` and successive tweens would
4618
- * cancel each other. Throws on items that satisfy neither. */
4619
- springPose<TPose>(adapter: SceneAdapter<{
4620
- id: string;
4621
- }, TPose>, poseFn: (item: TItem, index: number) => TPose, opts?: StaggerSpringPoseOptions<TPose>): AnimationHandle;
4622
- }
4623
- /** Options for `Animator.tweenLoop` — a tween's options plus how each
4624
- * iteration relates to the last. */
4625
- interface TweenLoopOptions<T> {
4626
- from: T;
4627
- to: T;
4628
- ms: number;
4629
- easing?: EasingFn;
4630
- /** `restart` (default): from→to every iteration.
4631
- * `reverse`: to→from every iteration.
4632
- * `alternate`: even iterations from→to, odd iterations to→from. */
4633
- direction?: 'restart' | 'reverse' | 'alternate';
4634
- count?: number;
4635
- interpolate?: Interpolate<T>;
4636
- onTick: (value: T) => void;
4637
- onDone?: () => void;
4638
- cancelKey?: string;
4639
- }
4640
-
4641
4733
  /** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /
4642
4734
  * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative
4643
4735
  * read; pass `useSelection()` itself or `selection.adapterMethods`. */
@@ -4838,21 +4930,6 @@ interface CanvasViewProps {
4838
4930
  */
4839
4931
  declare function CanvasView(props: CanvasViewProps): null;
4840
4932
 
4841
- /**
4842
- * Built-in shape tool ids handled by `useBuiltinShapeTools`. Each maps to a
4843
- * kit tool hook + a default `create` that produces a leaf node compatible
4844
- * with `PATH_PAINTER`.
4845
- *
4846
- * Runtime mirror in `KIT_SHAPE_KINDS` below — keep the two in sync. The
4847
- * `src/index.barrel.test.ts` parity gate enforces that every member of this
4848
- * union is present in the exported tuple.
4849
- */
4850
- type BuiltinShapeToolId = 'rect' | 'ellipse' | 'line' | 'polygon' | 'star' | 'pen' | 'pencil' | 'lasso' | 'text';
4851
- /** Runtime, iterable list of the shape-tool ids in `BuiltinShapeToolId`.
4852
- * Surfaced so consumers (e.g. the Bundle Inspector) can enumerate the
4853
- * builtin shape kinds without re-encoding the union. */
4854
- declare const KIT_SHAPE_KINDS: readonly ["rect", "ellipse", "line", "polygon", "star", "pen", "pencil", "lasso", "text"];
4855
-
4856
4933
  /** Per-tool option overrides for the built-in shape/lasso tools.
4857
4934
  * Each entry is a narrow subset of the underlying hook's options surface
4858
4935
  * — just the knobs that need consumer control under the bundle pattern. */
@@ -4990,182 +5067,6 @@ declare function createNodeRouting(): NodeRouting;
4990
5067
  * matches `poseRotationOf` (the shared rotation convention). */
4991
5068
  declare function rotateAroundAABBCenter(x: number, y: number, width: number, height: number, rotation: number): Float32Array;
4992
5069
 
4993
- /**
4994
- * NodeShape — the **shape trait's** registry. Each trait of a node
4995
- * (shape, routing, label, icon, affordances, …) is its own registry;
4996
- * this one holds the per-kind `paint` + `silhouette` (and future
4997
- * fields) used by `defaultDrawOne`, clipping, non-rect hit-testing,
4998
- * lasso/area-select, and SVG export.
4999
- *
5000
- * Teaching the kit about a new kind of shape goes through this registry
5001
- * rather than by overriding `drawOne`. Overrides are still possible but
5002
- * shouldn't be the default seam: most consumers want the same dispatch
5003
- * logic, just extended with their own shape kinds (images, custom paths,
5004
- * SVG fragments, etc.).
5005
- *
5006
- * Built-in entries (`kit:text`, `kit:path`, `kit:rect-fallback`) are
5007
- * registered at module load. Consumer entries added via
5008
- * `registerNodeShape` join the chain; the first entry whose
5009
- * `matches` predicate returns true paints the node.
5010
- *
5011
- * Two priority tiers:
5012
- * - `'high'` — checked before all `'normal'` entries. Use this to
5013
- * override a kit built-in for a specific data shape (e.g. a custom
5014
- * text renderer that wins over `kit:text`).
5015
- * - `'normal'` (default) — appended after the built-ins.
5016
- *
5017
- * Within a tier, entries run in registration order. Each
5018
- * `registerNodeShape` call returns a disposer that removes the
5019
- * entry — useful for tests, for plugin lifecycles, and for swapping
5020
- * implementations at runtime.
5021
- *
5022
- * See `docs/superpowers/specs/2026-05-24-node-traits-reframe-design.md`
5023
- * for the trait taxonomy.
5024
- */
5025
-
5026
- /** Optional per-call paint context, threaded through `defaultDrawOne`'s third
5027
- * argument. Lets a rendering entry point override ambient environment reads
5028
- * — the headless `renderSceneToPixels` path supplies its own bitmap resolver
5029
- * here so consumers reuse their own decode caches. Custom painters may
5030
- * ignore it entirely. */
5031
- interface NodePaintCtx {
5032
- /** Override bitmap resolution for image nodes. When set it is authoritative:
5033
- * the global `imageCache` is not consulted, and an `undefined` result
5034
- * paints the deterministic grey placeholder outline (never the ambient
5035
- * load-status error variant). */
5036
- resolveImage?: (node: Node$1<unknown, string, unknown>) => ImageBitmap | undefined;
5037
- }
5038
- /** A painter for one kind of node: which nodes it claims, and the draw
5039
- * commands it emits for them. Registering one is how a consumer teaches the
5040
- * default renderer to draw its own node kinds. */
5041
- interface NodeShapeEntry<TData = unknown, TPose = unknown> {
5042
- /** Stable identifier — used for unregistration and debugging. Pick
5043
- * something descriptive: `'kit:text'`, `'app:image'`, etc. */
5044
- id: string;
5045
- /** Returns true when this painter renders the node. The first matching
5046
- * painter (`'high'` tier first, then `'normal'`) wins. */
5047
- matches(node: Node$1<TData, string, TPose>): boolean;
5048
- /** Emits the draw commands for the node's primary visual. `ctx` is an
5049
- * optional per-call paint context (see `NodePaintCtx`); painters that
5050
- * don't need it can keep a two-argument signature.
5051
- *
5052
- * **The returned array belongs to the painter.** Callers must treat it as
5053
- * immutable and copy before appending — a painter is free to memoize its
5054
- * command list (`kit:shape` and `kit:path` do), and an in-place `push`
5055
- * would grow that list on every frame. `defaultDrawOne` copies before
5056
- * adding its label overlay for exactly this reason. */
5057
- paint(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodePaintCtx): DrawCommand[];
5058
- /** Optional: derive the node's silhouette path from its pose.
5059
- * Used by clipping (when the container has no explicit
5060
- * `clipFromPose`), by non-rect hit-testing, by lasso/area-select,
5061
- * and by SVG export. Painters whose visual has no meaningful closed
5062
- * silhouette (e.g. text) leave this undefined. */
5063
- silhouette?(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
5064
- /** Optional: how the silhouette is inked — whether the interior is filled,
5065
- * and how wide the outline is. Read by picking, so that an unfilled shape
5066
- * is grabbable by its outline rather than by its empty middle.
5067
- *
5068
- * This is declared separately from `paint` rather than read back off the
5069
- * emitted draw commands because picking runs on every pointer move, and
5070
- * `paint` is allowed to be expensive (`kit:text` lays out glyphs). Keep it
5071
- * to cheap field reads.
5072
- *
5073
- * Painters that leave it undefined are treated as `{ filled: true,
5074
- * strokeWidth: 0 }` — the pre-`ink` behavior, where the whole silhouette
5075
- * interior is grabbable and the outline adds nothing. */
5076
- ink?(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
5077
- }
5078
- /** How a painter inks its silhouette. See {@link NodeShapeEntry.ink}. */
5079
- interface NodeInk {
5080
- /** True when the silhouette's interior is painted, and so grabbable. */
5081
- filled: boolean;
5082
- /** Stroke width in world units. `0` for no stroke. */
5083
- strokeWidth: number;
5084
- }
5085
- /** Options for `registerNodeShape`. */
5086
- interface RegisterNodeShapeOptions {
5087
- /** `'high'` puts the painter ahead of all normally-registered ones (so
5088
- * it can win over a kit built-in). `'normal'` appends at the end. */
5089
- priority?: 'high' | 'normal';
5090
- }
5091
- /** Register a shape painter. Returns a disposer that removes it. */
5092
- declare function registerNodeShape<TData, TPose>(painter: NodeShapeEntry<TData, TPose>, opts?: RegisterNodeShapeOptions): () => void;
5093
- /** Find the painter that will render `node` — first match in priority
5094
- * order. Returns undefined if no painter (including the built-in
5095
- * fallback) accepts the node. */
5096
- declare function findNodeShape<TData, TPose>(node: Node$1<TData, string, TPose>): NodeShapeEntry<TData, TPose> | undefined;
5097
- /** Find the painter for `node` and ask it for the node's silhouette path,
5098
- * in **world** coords. Returns null if no painter matches, or the matching
5099
- * painter has no `silhouette` method, or the method returns null. Used by
5100
- * clipping, generic non-rect hit-testing, lasso, and SVG export — anywhere
5101
- * the kit needs the "closed boundary" of whatever this kind of node draws as.
5102
- *
5103
- * Painters return their silhouette in the pose's local (unrotated) frame;
5104
- * this bakes `pose.rotation` on top via the shared rotation convention, so
5105
- * clips/area-select of a rotated node use the rotated boundary the renderer
5106
- * draws. (`paint()` is unaffected — it applies rotation via the render wrap,
5107
- * not the silhouette, so there is no double-rotation.) */
5108
- declare function findShapeSilhouette<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
5109
- /** Find the painter for `node` and ask how it inks its silhouette. Returns
5110
- * the painter's declared {@link NodeInk}, or `null` when no painter matches
5111
- * or it declares none — callers substitute {@link DEFAULT_INK}. */
5112
- declare function findShapeInk<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
5113
- /** Options for {@link shapeCoversPoint}. */
5114
- interface ShapeCoversPointOptions {
5115
- /** Extra grab distance around the outline, in **world** units. Callers
5116
- * derive it from a screen-pixel slop and the view scale, the same way
5117
- * affordance hit radii work.
5118
- *
5119
- * Without slop a hairline is a mathematically zero-width target: the
5120
- * stroke of a 1px outline is half a world unit wide at scale 1, which no
5121
- * one can hit. Defaults to `0` so a caller that hasn't thought about the
5122
- * view still gets exact geometry rather than a wrong guess. */
5123
- tolerance?: number;
5124
- }
5125
- /**
5126
- * Does the shape `node` actually paints cover the world point?
5127
- *
5128
- * The pose rect says a node covers its whole bounding box. That is wrong for
5129
- * everything that is not a rectangle: the concave notch of a star, the corner
5130
- * outside an ellipse, the blank right half of a text box. This asks the
5131
- * painter's silhouette instead, which is the same boundary used for clipping
5132
- * and SVG export, so "what you can click" and "what is drawn" answer together.
5133
- *
5134
- * "What is drawn" includes the *ink*, not just the boundary. A shape whose
5135
- * interior isn't filled — an outlined rect, a pencil stroke, a bare line — is
5136
- * grabbable along its outline and not through its empty middle, which is the
5137
- * opposite of what a fill test alone answers. The outline's grab width is the
5138
- * stroke's half-width plus `tolerance`.
5139
- *
5140
- * A painter with no `silhouette`, or one that returns `null` for this node
5141
- * (`kit:text` does, for a node with no non-blank lines), reports `true` —
5142
- * "no opinion", leaving the caller's own AABB test as the answer. Callers
5143
- * should keep that AABB test as a cheap pre-filter; this is the refinement,
5144
- * not a replacement.
5145
- *
5146
- * Rotation is already baked by `findShapeSilhouette`, so the point is in
5147
- * plain world coordinates.
5148
- */
5149
- declare function shapeCoversPoint<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, x: number, y: number, opts?: ShapeCoversPointOptions): boolean;
5150
- /** Snapshot of the current painters in evaluation order — `'high'` tier
5151
- * first, then `'normal'`. Useful for debugging which painter handles a
5152
- * given node. */
5153
- declare function getNodeShapes(): readonly NodeShapeEntry[];
5154
- /**
5155
- * What a node's `data.fill` means to the built-in painters.
5156
- *
5157
- * A string is a color, `'none'` skips the fill, and an object is a
5158
- * {@link FillStyle} used as-is — which is how a gradient or a pattern reaches
5159
- * the renderer without a consumer registering a painter of its own. The
5160
- * renderer has taken every `FillStyle` variant since the paint model landed;
5161
- * only these two painters were narrower than it.
5162
- *
5163
- * `undefined` falls back to `data.color`, then to a default fill — but only
5164
- * when there is no stroke, since a stroke-only path (pencil) should not
5165
- * acquire one.
5166
- */
5167
- type NodeFill = string | FillStyle;
5168
-
5169
5070
  /**
5170
5071
  * `defaultDrawOne` — the kit's default per-node draw function.
5171
5072
  *
@@ -5195,7 +5096,7 @@ type NodeFill = string | FillStyle;
5195
5096
  /** Paint one node the way the kit does by default: dispatch to the first
5196
5097
  * registered node-shape painter that matches, then draw its `data.label` on
5197
5098
  * top when it has one. Consumers replace or wrap this via `drawOne`. */
5198
- 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[];
5199
5100
 
5200
5101
  /** Default size in CSS pixels for selection corner-handles AND their
5201
5102
  * hit-test radius, at `targetScale = 1`. Used by the SceneCanvas defaults;
@@ -5205,10 +5106,13 @@ declare function defaultDrawOne<TData, TLayer extends string, TPose>(node: Node$
5205
5106
  * Deliberately unscaled: consumers reading this constant keep getting the
5206
5107
  * number they always got. Kit-internal use sites multiply by
5207
5108
  * `DeviceProfile.targetScale`. */
5208
- 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"];
5209
5113
  /** Built-in tool ids SceneCanvas knows how to mount when no `tools` prop
5210
5114
  * is supplied. Pass a subset via `defaultTools` to slim the registered set. */
5211
- type BuiltinToolId = 'select' | 'rotate' | 'hand' | BuiltinShapeToolId;
5115
+ type BuiltinToolId = (typeof NON_SHAPE_BUILTIN_TOOLS)[number] | BuiltinShapeToolId;
5212
5116
  /** Named preset tool collections for the `toolBundle` prop. Maps to a
5213
5117
  * `BuiltinToolId[]` consumed by SceneCanvas's internal `useTools`. */
5214
5118
  type ToolBundle = 'minimal' | 'standard' | 'exhaustive';
@@ -5227,7 +5131,7 @@ interface SceneCanvasHit {
5227
5131
  /** Props for `<SceneCanvas>`. Most are optional: a scene is the only thing it
5228
5132
  * truly needs, and everything else — tools, layers, selection handling,
5229
5133
  * layouts, animation — layers onto sensible defaults. */
5230
- 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'> & {
5231
5135
  /** A `Scene` (typically from `useScene`) — or a `SerializedScene`
5232
5136
  * JSON object, which SceneCanvas bakes into a Scene internally on
5233
5137
  * first render. The serialized form is read once; subsequent
@@ -5483,17 +5387,19 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5483
5387
  ambient?: AnyTool[];
5484
5388
  /** Viewport feature wiring.
5485
5389
  *
5486
- * - `inertia`, `pinchZoom`, `animatedZoom` are opt-in: pass `true`
5487
- * for defaults or an object to tune. Omitted means off.
5488
- * - `pan` (wheel pan) and `zoom` (Cmd+wheel + Cmd+=/-/0) are opt-OUT:
5489
- * on by default; pass `false` to disable. They are wired by registering
5490
- * the kit's `viewport.pan` / `viewport.zoom` action descriptors with
5491
- * the actions registry — disabling via the `actions` prop
5492
- * (`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.
5493
5398
  *
5494
- * When omitted entirely, no hand/pinch tools are registered but the
5495
- * default wheel pan + Cmd+wheel/key zoom remain wired (canvas-first
5496
- * 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. */
5497
5403
  viewport?: {
5498
5404
  inertia?: boolean | {
5499
5405
  friction?: number;
@@ -5501,15 +5407,14 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5501
5407
  boundary?: 'stop' | 'bounce' | 'spring';
5502
5408
  bounds?: PanBounds;
5503
5409
  };
5504
- pinchZoom?: boolean | {
5505
- min?: number;
5506
- max?: number;
5507
- };
5508
- animatedZoom?: boolean | {
5509
- duration?: number;
5510
- resetDuration?: number;
5511
- easing?: (t: number) => number;
5512
- };
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;
5513
5418
  pan?: boolean;
5514
5419
  /** Wheel/keyboard zoom. `true`/omitted = default Cmd+wheel zoom with the
5515
5420
  * kit's 0.1–8 clamp; `false` disables. Pass a {@link ViewportZoomOptions}
@@ -5518,10 +5423,11 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
5518
5423
  zoom?: boolean | ViewportZoomOptions;
5519
5424
  /** Callback invoked by Cmd-0 (`viewport.zoom` action's `reset` branch).
5520
5425
  * When supplied, replaces the default reset-to-identity behavior —
5521
- * consumers typically refit the document page into the workspace
5522
- * via `fitViewToBounds`. The callback owns its own bounds + host
5523
- * dims and dispatches the resulting view via `onViewChange`. */
5524
- 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;
5525
5431
  };
5526
5432
  /**
5527
5433
  * @experimental
@@ -5933,17 +5839,6 @@ declare const inferredNodeProperties: readonly NodePropertiesEntry[];
5933
5839
  * in sync by hand where it matters (DPR handling, viewToMat3 wrap).
5934
5840
  */
5935
5841
 
5936
- /**
5937
- * Per-node draw function. Mirrors the scene-slot `drawOne` signature on
5938
- * `<SceneCanvas>` (`SceneSlotConfig.drawOne`) so consumers can reuse the
5939
- * same callback (or a simplified variant) between a main canvas and a
5940
- * detached scene-view canvas.
5941
- *
5942
- * The function is called once per node in `scene.renderOrder()`. Returned
5943
- * commands are in world coords; the caller's `view` is applied at the
5944
- * group level (see `renderSceneToCanvas`'s implementation).
5945
- */
5946
- type SceneViewDrawOne<TData, TLayer extends string, TPose> = (node: Node$1<TData, TLayer, TPose>, pose: TPose, view: View) => DrawCommand[];
5947
5842
  /** What to draw into an existing canvas: the scene, the view, and the same
5948
5843
  * painting hooks `<SceneCanvas>` takes. */
5949
5844
  interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {
@@ -6101,12 +5996,16 @@ interface UseSceneTrivialOptions<TItem extends {
6101
5996
  items: readonly TItem[];
6102
5997
  historyLimit?: number;
6103
5998
  generateId?: () => NodeId;
5999
+ /** Re-render the host on every scene mutation. Default `true`. See
6000
+ * {@link UseSceneOptions.subscribe}. */
6001
+ subscribe?: boolean;
6104
6002
  }
6105
6003
  declare const DEFAULT_LAYER: "default";
6106
6004
  type DefaultLayer = typeof DEFAULT_LAYER;
6107
6005
  /** React hook returning a kit-owned `Scene`. The Scene is constructed once
6108
6006
  * per host component and tracked via `useSyncExternalStore`, so React re-
6109
- * renders on every Scene mutation (including undo/redo).
6007
+ * renders on every Scene mutation (including undo/redo) unless
6008
+ * `subscribe: false` opts out.
6110
6009
  *
6111
6010
  * Two call shapes:
6112
6011
  * - **Trivial**: `useScene({ items })` — one auto-registered system layer
@@ -6224,7 +6123,12 @@ interface MinimapCanvasProps<TData, TLayer extends string, TPose> {
6224
6123
  * can reuse it (typically simplified — just colored AABBs). */
6225
6124
  drawOne: SceneViewDrawOne<TData, TLayer, TPose>;
6226
6125
  /** Fit policy. Defaults to `"scene"` (AABB union of leaf poses). See
6227
- * `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. */
6228
6132
  fit?: MinimapFit<TData, TLayer, TPose>;
6229
6133
  /** Pose → AABB. Defaults to identity (`pose as Bounds`), matching
6230
6134
  * `sceneAdapter` / `useSelectTool`. */
@@ -6283,9 +6187,10 @@ interface RenderSceneToPixelsArgs<TData, TLayer extends string, TPose> {
6283
6187
  y: number;
6284
6188
  };
6285
6189
  /** Per-node draw callback. Default: `defaultDrawOne` with `resolveImage`
6286
- * threaded as its `NodePaintCtx`. Custom `drawOne` callers that still
6287
- * want resolver injection should call `defaultDrawOne(node, pose, ctx)`
6288
- * 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. */
6289
6194
  drawOne?: SceneViewDrawOne<TData, TLayer, TPose>;
6290
6195
  /** Bitmap resolver for image nodes — lets consumers reuse their own decode
6291
6196
  * caches. `undefined` results paint the deterministic grey placeholder
@@ -6766,8 +6671,6 @@ declare const PATH_ANCHOR_CHROME_ID = "path-edit.anchors";
6766
6671
  */
6767
6672
  declare function createPathAnchorAffordances(getAnchorState: () => AnchorState | null, opts?: PathAnchorAffordanceOptions): Affordance[];
6768
6673
 
6769
- /** Pluggable text-painting strategy. The default fills white at `(x, y)`; markdown renderers replace this. */
6770
- type TextRenderer = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number) => void;
6771
6674
  /** Visual options for `renderLabel`. */
6772
6675
  interface LabelOptions {
6773
6676
  align?: 'center' | 'left';
@@ -6789,112 +6692,6 @@ declare function renderLabel(ctx: CanvasRenderingContext2D, text: string, x: num
6789
6692
  /** Default `TextRenderer` used by `renderLabel`: white fill, no styling. */
6790
6693
  declare const defaultLabelTextRenderer: TextRenderer;
6791
6694
 
6792
- /** Width-measurement strategy for `layoutMarkdown`; canvas-backed default supplied by `createMarkdownRenderer`. */
6793
- type MeasureFn = (text: string, fontSize: number, bold: boolean, italic: boolean) => number;
6794
- /** A `StyledRun` with its computed x-offset relative to the start of its line. */
6795
- interface PositionedRun extends StyledRun {
6796
- x: number;
6797
- }
6798
- /** A single laid-out line of text: its positioned runs, total width, and computed line height. */
6799
- interface LayoutLine {
6800
- runs: PositionedRun[];
6801
- width: number;
6802
- height: number;
6803
- }
6804
- /** Output of `layoutMarkdown`: per-line breakdown plus overall block dimensions. */
6805
- interface LayoutResult {
6806
- lines: LayoutLine[];
6807
- width: number;
6808
- height: number;
6809
- }
6810
- /** Word-wrap parsed runs into lines bounded by `maxWidth`; pass `Infinity` for single-line layout. */
6811
- declare function layoutMarkdown(runs: StyledRun[], maxWidth: number, fontSize: number, measure: MeasureFn, lineHeightFactor?: number): LayoutResult;
6812
-
6813
- /** Font styling options threaded through `createMarkdownRenderer`. */
6814
- interface MarkdownFontOptions {
6815
- /** Font-family spec (e.g. `'"Iowan Old Style", Georgia, serif'`). Defaults to `sans-serif`. */
6816
- family?: string;
6817
- /** Numeric weight applied to non-bold runs. Bold runs always use `bold`. Default `normal`. */
6818
- weight?: string | number;
6819
- /** Override fill color. When set, used for all runs (italic and bold). */
6820
- color?: string;
6821
- /** Multiplier applied to font size for line height. Default 1.3. */
6822
- lineHeight?: number;
6823
- }
6824
- /** Build a fill+stroke `TextRenderer` pair for a markdown string at the given size; pre-computes layout once. */
6825
- declare function createMarkdownRenderer(ctx: CanvasRenderingContext2D, text: string, fontSize: number, maxWidth?: number, fontOpts?: MarkdownFontOptions): {
6826
- renderer: TextRenderer;
6827
- strokeRenderer: TextRenderer;
6828
- width: number;
6829
- height: number;
6830
- };
6831
-
6832
- /**
6833
- * Wrap-aware text measurement. Greedy word-wrap against `maxWidth` using
6834
- * `ctx.measureText`. Lines explicitly broken by `\n` are preserved; long
6835
- * single words that exceed `maxWidth` are emitted on their own line without
6836
- * mid-word breaking (caller can decide to clip).
6837
- *
6838
- * Returns the laid-out lines, the total block height in world units
6839
- * (`lines.length * fontSize * lineHeight`), and per-line `lineStarts` —
6840
- * the offset of each line's first character in the original `text`. The
6841
- * starts are used by `caretIndexAt` to map a clicked (x, y) back to a
6842
- * character offset in the source string. Trailing whitespace consumed by
6843
- * the wrap is not included in `lines[i]` but is implicit in the gap
6844
- * between `lineStarts[i] + lines[i].length` and `lineStarts[i + 1]`.
6845
- *
6846
- * The caller owns the `ctx.font` setup — pass a context whose `font`
6847
- * already matches `style` (use `fontString(style)`).
6848
- */
6849
-
6850
- /**
6851
- * Advance width of `text` in world units, tracking included.
6852
- *
6853
- * `letter-spacing` is not part of the CSS `font` shorthand, so a context
6854
- * whose `font` was set from `fontString(style)` measures glyphs only. The
6855
- * GL path (`layoutRuns`) adds `letterSpacing` after **every** code point
6856
- * including the last, matching CSS, so this does too — and every 2D-side
6857
- * width has to go through here or the two paths disagree about where a line
6858
- * breaks.
6859
- */
6860
- declare function measuredWidth(ctx: CanvasRenderingContext2D, text: string, style: ResolvedTextStyle): number;
6861
- /** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */
6862
- interface MeasuredText {
6863
- lines: string[];
6864
- lineStarts: number[];
6865
- height: number;
6866
- }
6867
- /** Greedy word-wrap text measurement against `maxWidth`; preserves explicit `\n` breaks. */
6868
- declare function measureText(ctx: CanvasRenderingContext2D, text: string, maxWidth: number, style: ResolvedTextStyle): MeasuredText;
6869
-
6870
- /** Options for `measureTextBounds`. */
6871
- interface MeasureTextBoundsOpts {
6872
- /** Wrap width; words exceeding it start a new line. Default `Infinity` (no wrap). */
6873
- maxWidth?: number;
6874
- /** Overrides `style`'s `lineHeight` multiplier for this measurement. */
6875
- lineHeight?: number;
6876
- }
6877
- /**
6878
- * Measure how the GL/MSDF renderer will lay out a single plain-text string,
6879
- * using the registered font atlas metrics. Mirrors {@link textCommand} exactly
6880
- * (`resolveTextStyle` → `resolveRuns` → `layoutRuns`), so the returned bounds
6881
- * match what actually gets drawn — use it to size backgrounds/pills, place
6882
- * labels, or test overlap without guessing widths.
6883
- *
6884
- * Pass `opts.maxWidth` to measure word-wrapped height (e.g. for a fixed-width
6885
- * text box); omitting it measures the unwrapped single-line width. `opts.lineHeight`
6886
- * overrides the style's multiplier without changing `style` itself.
6887
- *
6888
- * Returns `{ width, height }` in the same units as `style.fontSize`. The font
6889
- * must already be registered via `registerFont`; an unregistered family falls
6890
- * back to the atlas fallback glyph (and warns), so register fonts at app boot
6891
- * before measuring.
6892
- */
6893
- declare function measureTextBounds(text: string, style?: TextStyle, opts?: MeasureTextBoundsOpts): {
6894
- width: number;
6895
- height: number;
6896
- };
6897
-
6898
6695
  /**
6899
6696
  * Text RenderLayer. Emits one TextDrawCommand per text node carrying the
6900
6697
  * node's resolved runs and bounding rect. Word wrap and multi-line layout
@@ -6907,135 +6704,70 @@ declare function measureTextBounds(text: string, style?: TextStyle, opts?: Measu
6907
6704
  * `TextDrawCommand`'s contract).
6908
6705
  */
6909
6706
 
6910
- /** Pose for a text node: bounding rect plus the text and optional style. */
6911
- interface TextPose {
6912
- x: number;
6913
- y: number;
6914
- width: number;
6915
- height: number;
6916
- text: string;
6917
- /** Rich-text runs. When present, `runsToPlainText(runs)` must equal `text`. */
6918
- runs?: StyledRun[];
6919
- style?: TextStyle;
6920
- /** Box vertical alignment within `[y, y+height]`. Default 'top'. */
6921
- verticalAlign?: TextVerticalAlign;
6922
- }
6923
6707
  /** Options for `createTextLayer`. */
6924
6708
  interface CreateTextLayerOpts<T> {
6925
6709
  id?: string;
6926
6710
  label?: string;
6927
- getTexts: () => readonly T[];
6928
- getPose: (node: T) => TextPose;
6929
- /** Optional per-node hide hook (e.g., suppress while editing). */
6930
- isHidden?: (node: T) => boolean;
6931
- /** When `true`, each text command is wrapped in a clipped group so any
6932
- * overflow beyond the pose's `(width × height)` is hidden. Default
6933
- * `false` (legacy: text can spill outside the declared bounds). Opt-in
6934
- * so existing consumers who rely on overflow keep working. */
6935
- clipToBounds?: boolean;
6936
- }
6937
- /** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */
6938
- declare function createTextLayer<T>(opts: CreateTextLayerOpts<T>): RenderLayer<unknown>;
6939
-
6940
- /**
6941
- * Hit-testing for text nodes.
6942
- *
6943
- * `pointInTextPose` does a coarse pose-rect test — suitable for click-to-edit
6944
- * where the pose rect is the authoritative bounding box (selection outline,
6945
- * drag target).
6946
- *
6947
- * `caretIndexAt` does the finer test: given a world-space (x, y) inside the
6948
- * pose, returns the corresponding character offset in `pose.text` so the
6949
- * consumer can place the caret on click. Re-runs the wrap to map the click
6950
- * back through `lineStarts`; respects `style.align` for line anchoring.
6951
- */
6952
-
6953
- /** Options for `pointInTextPose`. */
6954
- interface PointInTextPoseOpts {
6955
- /** Extra padding (world units) added to the rect on all sides. Default 0. */
6956
- padding?: number;
6957
- }
6958
- /** Coarse pose-rect hit-test for a text node — suitable for click-to-edit dispatch. */
6959
- declare function pointInTextPose(x: number, y: number, pose: TextPose, opts?: PointInTextPoseOpts): boolean;
6960
- /**
6961
- * Map a world-space point inside `pose` to a character offset into
6962
- * `pose.text` (0..text.length). Clicks above the first line clamp to 0;
6963
- * clicks below the last line clamp to `text.length`. Within a line, the
6964
- * caret lands between two glyphs at whichever side of the glyph midpoint
6965
- * `x` falls on — the standard "snap caret to nearest character boundary"
6966
- * rule.
6967
- *
6968
- * The `ctx` is used only for `measureText`; its `font` is set internally
6969
- * to match the resolved text style. Pass any 2D context (the same one used
6970
- * to render is fine).
6971
- */
6972
- declare function caretIndexAt(ctx: CanvasRenderingContext2D, x: number, y: number, pose: TextPose): number;
6973
-
6974
- /**
6975
- * Polygon-vs-axis-aligned-rect tests. Pure functions; no React/kit deps.
6976
- *
6977
- * `polygon` is a closed polyline given as an ordered vertex array; the
6978
- * closing edge from `polygon[N-1]` to `polygon[0]` is implicit. Even-odd
6979
- * fill rule (matches `pointInPath`).
6980
- */
6981
- /** A 2D point or vector. */
6982
- interface Vec2 {
6983
- x: number;
6984
- y: number;
6985
- }
6986
- /** An axis-aligned rectangle. */
6987
- interface Rect {
6988
- x: number;
6989
- y: number;
6990
- width: number;
6991
- 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;
6992
6720
  }
6721
+ /** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */
6722
+ declare function createTextLayer<T>(opts: CreateTextLayerOpts<T>): RenderLayer<unknown>;
6993
6723
 
6994
6724
  /**
6995
- * Where a text node's lines actually sit inside its pose box.
6996
- *
6997
- * A text pose is a *wrap box*, not a bounding box: `"Away"` in a 309-unit-wide
6998
- * box leaves most of the box empty, and anything that treats the pose as the
6999
- * node's extent — picking, lasso, clipping, SVG export — claims that empty
7000
- * space. `textLineBoxes` returns the per-line rectangles instead.
6725
+ * Hit-testing for text nodes.
7001
6726
  *
7002
- * The numbers come from `layoutRuns`, the same walk that positions the glyphs,
7003
- * through the same `resolveTextStyle` `resolveRuns` `layoutRuns` chain as
7004
- * `textCommand` — so the boxes cannot drift from what is painted. In
7005
- * particular they honor `align` (a centered line reports its own span, not the
7006
- * wrap width) and `verticalAlign` (the block shifts inside `[y, y + height]`
7007
- * 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).
7008
6730
  *
7009
- * These are line boxes, not ink boxes: each is `fontSize * lineHeight` tall
7010
- * from the pen's line top. Ink can escape vertically at a `lineHeight` low
7011
- * enough see the `bounds` note at the end of `layoutRuns` which is the
7012
- * right trade for hit-testing, where a box that hugged the ink would make
7013
- * 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.
7014
6737
  */
7015
6738
 
7016
- /** Options for {@link textLineBoxes}. */
7017
- interface TextLineBoxesOpts {
7018
- /** Grow every box by this much on all four sides (world units). Default 0.
7019
- * Picking wants a little slack so a single hairline row of text is still
7020
- * 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. */
7021
6742
  padding?: number;
7022
- /** Keep boxes for blank lines (zero width). Default `false` — a blank line
7023
- * covers no area, so for hit-testing and silhouettes it is noise. Pass
7024
- * `true` when the indices have to line up with the wrapped lines. */
7025
- 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 {
7026
6748
  /**
7027
6749
  * Wrap width. Default `pose.width`, which is what `createTextLayer` passes
7028
6750
  * and what `TextPose` means by its box.
7029
6751
  *
7030
6752
  * Pass `Infinity` for a node painted by the built-in `kit:text` painter:
7031
- * that painter deliberately does **not** forward `maxWidth` (see
7032
- * `NodeShape.ts`), so its text does not wrap, and boxes computed with a
7033
- * 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`.
7034
6756
  */
7035
6757
  maxWidth?: number;
7036
6758
  }
7037
- /** Per-line rectangles for a text pose, in world units, in layout order. */
7038
- 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;
7039
6771
 
7040
6772
  /**
7041
6773
  * Resize a text node's pose to fit its content. Pure helper — does not own
@@ -7069,7 +6801,60 @@ interface FitTextPoseOptions {
7069
6801
  };
7070
6802
  }
7071
6803
  /** Recompute a `TextPose`'s `width`/`height` to fit its content; pure helper, doesn't mutate scene state. */
7072
- 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;
7073
6858
 
7074
6859
  /**
7075
6860
  * The kit-wide "these values disagree" sentinel. Used wherever a value is
@@ -7160,6 +6945,13 @@ declare function applyStyleToRange(runs: readonly StyledRun[], start: number, en
7160
6945
  * (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
7161
6946
  */
7162
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';
7163
6955
  /**
7164
6956
  * The caret's character range within the text being edited. Half-open
7165
6957
  * `[start, end)` over the concatenated run text, normalized so `start <= end`
@@ -7214,6 +7006,13 @@ interface UseTextEditOptions {
7214
7006
  getText: (id: string) => string;
7215
7007
  /** Read style for `id` (used for font setup on the overlay). */
7216
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;
7217
7016
  /**
7218
7017
  * Write style back for `id`. Optional; needed only to turn a flag **off**
7219
7018
  * inside a node whose own `TextStyle` sets it. Run flags are additive, so
@@ -7278,8 +7077,8 @@ interface UseTextEditReturn {
7278
7077
  /**
7279
7078
  * The caret's character range, or `null` when nothing is being edited. A
7280
7079
  * collapsed caret reports `{ start: n, end: n }`, so `null` and "caret at
7281
- * n" stay distinguishable — a character-styling control routes the
7282
- * 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.
7283
7082
  *
7284
7083
  * Follows the DOM selection, which browsers (and jsdom) report from a task
7285
7084
  * rather than synchronously; anything this hook writes itself updates it
@@ -7293,33 +7092,82 @@ interface UseTextEditReturn {
7293
7092
  /**
7294
7093
  * The styling shared by every run in `selection` — a concrete value where
7295
7094
  * the range agrees, `MIXED` where it doesn't. `null` exactly when
7296
- * `selection` is `null`. A collapsed caret reports `{}`: no run is in
7297
- * range, so the range reader has nothing to say and the node's style is
7298
- * 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.
7299
7102
  */
7300
7103
  rangeStyle: RangeStyle | null;
7301
7104
  /**
7302
- * Write `patch` over `selection`. A no-op with no active edit, with a
7303
- * collapsed caret (there is no range to stylepatch the node's
7304
- * `TextStyle` instead), or with an empty patch. The caret survives, so a
7305
- * second style can be applied without re-selecting, and `rangeStyle`
7306
- * 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.
7307
7126
  */
7308
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;
7309
7134
  }
7310
7135
  /** In-place text editing via a contenteditable overlay positioned over the text node's screen-space pose. */
7311
7136
  declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
7312
7137
 
7313
7138
  /**
7314
- * Compute the union AABB of a set of rectangle-shaped poses.
7139
+ * Union math over rect-shaped poses.
7315
7140
  *
7316
- * Returns null when the input is empty. Useful for selection overlay
7317
- * rendering of groups (where the displayed bounds are the envelope of
7318
- * 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.
7319
7148
  */
7320
7149
 
7321
- /** 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}. */
7322
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;
7323
7171
 
7324
7172
  /**
7325
7173
  * `useSceneTextEdit` — scene-aware wrapper around `useTextEdit`.
@@ -7352,6 +7200,8 @@ interface DefaultTextData {
7352
7200
  text?: string;
7353
7201
  style?: TextStyle;
7354
7202
  runs?: readonly StyledRun[];
7203
+ fill?: FillStyle | null;
7204
+ stroke?: Stroke | null;
7355
7205
  }
7356
7206
  /** All-optional projections + fontSize fallback. */
7357
7207
  interface UseSceneTextEditOptions<TData> {
@@ -7359,6 +7209,8 @@ interface UseSceneTextEditOptions<TData> {
7359
7209
  getText?: (data: TData) => string;
7360
7210
  /** Read style from `data`. Default: `data.style`. */
7361
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;
7362
7214
  /** Read rich-text runs from `data`. Default: `data.runs`. */
7363
7215
  getRuns?: (data: TData) => readonly StyledRun[] | undefined;
7364
7216
  /** Produce updated data with new text. Default: `{ ...data, text }`. */
@@ -7386,8 +7238,13 @@ interface UseSceneTextEditOptions<TData> {
7386
7238
  * The overlay takes a single scale factor, so a non-uniform view scale is
7387
7239
  * represented by its `scale.x`; text under `scale.x !== scale.y` will not
7388
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.
7389
7246
  */
7390
- view?: View;
7247
+ view?: View | (() => View);
7391
7248
  /**
7392
7249
  * Forwarded to `useTextEdit`: is `el` part of the editor's own chrome?
7393
7250
  * Focus moving into it does not end the edit. Wire it to whatever renders
@@ -7410,59 +7267,6 @@ interface UseSceneTextEditReturn extends UseTextEditReturn {
7410
7267
  * over the node being edited and writes the result back as an undoable op. */
7411
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;
7412
7269
 
7413
- /**
7414
- * Turning a run-level flag **off** inside a node that sets it.
7415
- *
7416
- * Run flags are additive over the node's `TextStyle` — a run turns `bold` /
7417
- * `italic` / `underline` / `strikethrough` on, never off (see the contract at
7418
- * the top of `rangeStyle.ts`). So "select a word inside an underlined node and
7419
- * hit U" is unrepresentable by the stored shape, and the character bar can
7420
- * only refuse.
7421
- *
7422
- * This resolves it by rewriting rather than by widening the model: clear the
7423
- * flag on the node, and set it on every run *outside* the range. The rendered
7424
- * result is identical and the edit becomes expressible, with `StyledRun`
7425
- * unchanged — so nothing a document can already contain changes meaning, which
7426
- * matters while the flags are heading for a persisted format.
7427
- *
7428
- * The alternative was a tri-state run flag (`true` / `false` / inherit). It
7429
- * cannot cover `bold` or `italic`: those are booleans on a run but
7430
- * `fontWeight` and `fontStyle` on the node, so a run's `false` has no node-level
7431
- * boolean to override. Tri-state fixes two of the four flags; this fixes all
7432
- * four.
7433
- */
7434
-
7435
- /** The additive run flags. */
7436
- type FlagKey = 'bold' | 'italic' | 'underline' | 'strikethrough';
7437
- /** The result of toggling a style flag over a text range: the rewritten runs
7438
- * and node style, and whether the change could be made at all. */
7439
- interface SetFlagResult {
7440
- runs: StyledRun[];
7441
- style: TextStyle;
7442
- /**
7443
- * False when the node flag could not be lowered without changing what is
7444
- * drawn, and nothing was written. The only case is a `fontWeight` the run
7445
- * boolean cannot express: `run.bold` resolves to exactly 700 everywhere, so
7446
- * a node at 900 cannot have its weight pushed onto its runs. Callers should
7447
- * disable the control rather than apply a silent downgrade.
7448
- */
7449
- applied: boolean;
7450
- }
7451
- /** Does the node style carry this flag? */
7452
- declare function nodeHasFlag(style: TextStyle, key: FlagKey): boolean;
7453
- /**
7454
- * Set `key` to `value` over `[start, end)`.
7455
- *
7456
- * Turning a flag **on**, or off in a node that doesn't set it, is the ordinary
7457
- * additive write and leaves `style` alone. Turning it off in a node that *does*
7458
- * set it takes the rewrite: the node flag is cleared and the flag is written
7459
- * onto the complement of the range.
7460
- *
7461
- * `runs` is normalized on every path, so the complement collapses back to one
7462
- * run when the range is empty and the whole array coalesces as usual.
7463
- */
7464
- declare function setFlagOverRange(runs: readonly StyledRun[], style: TextStyle, start: number, end: number, key: FlagKey, value: boolean): SetFlagResult;
7465
-
7466
7270
  /**
7467
7271
  * Build a `TextDrawCommand`. `textCommand` takes a plain string and wraps it
7468
7272
  * in one run; `textCommandFromRuns` takes the styled runs directly. Both
@@ -7470,12 +7274,16 @@ declare function setFlagOverRange(runs: readonly StyledRun[], style: TextStyle,
7470
7274
  * text command derives `align` and per-run resolution the same way — a
7471
7275
  * caller that assembles the command by hand is one field away from a
7472
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.
7473
7280
  */
7474
7281
 
7282
+ declare function textCommandFromRuns(x: number, y: number, runs: readonly StyledRun[], style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign, paint?: TextPaint): DrawCommand;
7475
7283
  /** Build a draw command for a single unstyled string. Text is laid out with
7476
7284
  * the registered font's metrics, wrapped at `maxWidth` and aligned within
7477
7285
  * `height` when both are given. */
7478
- 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;
7479
7287
 
7480
7288
  /**
7481
7289
  * Pure DOM ↔ `StyledRun[]` serializers for the contenteditable overlay
@@ -7517,7 +7325,7 @@ declare function domPositionToCharOffset(parent: HTMLElement, node: Node, offset
7517
7325
  /**
7518
7326
  * Tile-pattern primitive — a small helper for building repeating texture
7519
7327
  * tiles from a draw callback. The result plugs into a `FillStyle` of
7520
- * `{ fill: 'pattern', pattern }` (see `../../core/paint-types.ts`).
7328
+ * `{ fill: 'pattern', pattern }` (see `@weasel-js/paint`).
7521
7329
  *
7522
7330
  * GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback
7523
7331
  * to a regular `<canvas>` for environments without `OffscreenCanvas`),
@@ -7579,13 +7387,16 @@ declare function resolveFillPattern(fill: FillStyle): FillStyle | null;
7579
7387
  * `withGradientKind` exists to translate.
7580
7388
  */
7581
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;
7582
7394
  /**
7583
- * Color at position `t` (0..1) along a stop list, matching how the GL ramp
7584
- * texture is built: stops sorted by offset, flat extension past either end,
7585
- * 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.
7586
7398
  *
7587
- * Returns transparent black for an empty list. Alpha is interpolated too, so
7588
- * a stop list fading to `transparent` samples correctly.
7399
+ * Returns transparent black for an empty list.
7589
7400
  */
7590
7401
  declare function sampleGradientStops(stops: readonly GradStop[], t: number): string;
7591
7402
  /**
@@ -7622,9 +7433,10 @@ declare function gradientGeometry(fill: GradientFill): {
7622
7433
  angle: number;
7623
7434
  };
7624
7435
  /**
7625
- * A gradient spanning a box corner-to-corner (linear) or filling it
7626
- * (radial / conic) — the sensible starting geometry when a consumer turns a
7627
- * 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.
7628
7440
  */
7629
7441
  declare function gradientForBounds(kind: GradientKind, bounds: {
7630
7442
  x: number;
@@ -7710,16 +7522,40 @@ interface TransformArgs<TPose> {
7710
7522
  * coalesce window. Pass an explicit key (or a unique one) to opt out. */
7711
7523
  declare function createTransformOp<TPose>(args: TransformArgs<TPose>): Op;
7712
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
+
7713
7535
  /** @internal */
7714
7536
  interface ReparentArgs {
7715
7537
  id: string;
7716
7538
  fromParentId: string | null;
7717
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;
7718
7553
  label?: string;
7719
7554
  coalesceKey?: string;
7720
7555
  }
7721
7556
  /**
7722
- * 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.
7723
7559
  *
7724
7560
  * `coalesceKey` defaults to `reparent:${id}` so successive reparents of the
7725
7561
  * same id batch-merge cleanly.
@@ -7735,32 +7571,46 @@ interface InsertArgs<TNode extends {
7735
7571
  }> {
7736
7572
  node: TNode;
7737
7573
  label?: string;
7738
- /** Original z-index in the host array. Forwarded to
7739
- * `adapter.insertNode(node, index)` when present so undo of a multi-delete
7740
- * batch restores paint order instead of reversing it. Optional — adapters
7741
- * 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. */
7742
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;
7743
7580
  }
7744
7581
  /** Op: insert `node` into the scene; inverts to a delete of the same id. */
7745
7582
  declare function createInsertOp<TNode extends {
7746
7583
  id: string;
7747
7584
  }>(args: InsertArgs<TNode>): InsertOp;
7748
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
+ }
7749
7591
  /** @internal */
7750
7592
  interface DeleteArgs<TNode extends {
7751
7593
  id: string;
7752
7594
  }> {
7753
7595
  node: TNode;
7754
7596
  label?: string;
7755
- /** Z-index the node occupies in its host array at the moment of delete.
7756
- * Forwarded through `invert()` to the re-insert so undo restores paint
7757
- * order without this, undo of a multi-delete batch fully reverses
7758
- * the stack. Required: every delete callsite knows where the node sits
7759
- * (it has to, to remove it), so there's no good reason to drop the
7760
- * data on the floor. */
7761
- index: number;
7762
- }
7763
- /** 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. */
7764
7614
  declare function createDeleteOp<TNode extends {
7765
7615
  id: string;
7766
7616
  }>(args: DeleteArgs<TNode>): Op;
@@ -7903,6 +7753,10 @@ declare function registerOpFactory<A>(name: string, build: (args: A) => Op): voi
7903
7753
  * `name` isn't registered — callers handle this (typically by substituting
7904
7754
  * a no-op placeholder so stack ordering survives across kit version skew). */
7905
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[];
7906
7760
 
7907
7761
  /** @internal */
7908
7762
  interface HitAdapter<TNode extends {
@@ -8121,6 +7975,20 @@ interface PointInPathOptions {
8121
7975
  /** Filled-region hit-test for a `Path`. Rect short-circuits to AABB; polygons run ray-cast / winding per `fillRule`. */
8122
7976
  declare function pointInPath(path: Path, x: number, y: number, opts?: PointInPathOptions): boolean;
8123
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
+
8124
7992
  /**
8125
7993
  * Path-vs-geometry hit-test helpers. Five pure functions covering all
8126
7994
  * combinations of path-vs-point, path-vs-rect, and path-vs-polygon.
@@ -8272,22 +8140,6 @@ declare function worldEditToStorage<P extends PathInWorldPose>(pose: P, worldPat
8272
8140
  path: PolygonPath;
8273
8141
  };
8274
8142
 
8275
- /**
8276
- * The kit's single encoding of the pose-rotation convention: "rotate about the
8277
- * pose's unrotated AABB center by `pose.rotation` (radians)."
8278
- *
8279
- * Every consumer that needs to apply rotation — the render-tree wrap
8280
- * (`canvas/poseRotation.ts`), the world coordinate bake (`pathInWorld`), the
8281
- * silhouette/clip bake (`findShapeSilhouette`), the hit-test inverse
8282
- * (`poseContainsRotated`), and selection chrome — derives its pivot + angle
8283
- * from `poseRotationOf` rather than re-inlining the `if (rotation && x != null
8284
- * …)` gate. If the convention ever changes (different pivot, a transform
8285
- * field, etc.), it changes here.
8286
- *
8287
- * Lives in `features/paths` (the low layer) so both `features` and `canvas`
8288
- * can share it without `features` importing `canvas`.
8289
- */
8290
-
8291
8143
  /** Pivot (`cx`, `cy`) and angle resolved from a pose's rotation convention. */
8292
8144
  interface PoseRotation {
8293
8145
  cx: number;
@@ -8568,8 +8420,9 @@ declare function createPenPreviewLayer(opts: CreatePenPreviewLayerOptions): Rend
8568
8420
  * but a human user needs to see what's hittable.
8569
8421
  *
8570
8422
  * The layer reads the live edit target each frame via the supplied
8571
- * `getEditingId()` + `getPose(id)` thunks. Returns `[]` when no editing
8572
- * 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.
8573
8426
  *
8574
8427
  * Anchors render as small white-filled stroked squares — filled solid
8575
8428
  * when selected; control points as small filled circles connected to
@@ -8589,9 +8442,15 @@ interface CreatePathEditingOverlayLayerOptions {
8589
8442
  * when no node is being edited. Read each frame so live selection /
8590
8443
  * edit-target changes show up without re-creating the layer. */
8591
8444
  getEditingId(): string | null;
8592
- /** Returns the pose for an id, or null if the node has been deleted.
8593
- * Non-polygon poses are tolerated (the layer no-ops on them). */
8594
- 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;
8595
8454
  /** Flat indices of the selected anchors. Selected anchors render
8596
8455
  * filled; unselected ones hollow — the standard vector-editor cue for
8597
8456
  * "these are what the arrow keys and Delete will act on". Omit when
@@ -8605,12 +8464,6 @@ interface CreatePathEditingOverlayLayerOptions {
8605
8464
  width: number;
8606
8465
  height: number;
8607
8466
  } | null;
8608
- /** Chrome-caps predicate. The layer asks `'path-edit.anchors'` before
8609
- * drawing anything, so paint and the anchor hit-test in
8610
- * `affordanceAt` consult the same rule — otherwise a consumer that
8611
- * hides the chrome still gets grabbable invisible anchors, or vice
8612
- * versa. Omit to always draw when an edit target is set. */
8613
- isVisible?(chromeId: string): boolean;
8614
8467
  /** Optional styling overrides. */
8615
8468
  style?: PathEditingOverlayStyle;
8616
8469
  }
@@ -8791,6 +8644,301 @@ declare function withAlpha01(color: string, alpha01: number): string;
8791
8644
  * strips the alpha channel and returns a bare 6-char hex. */
8792
8645
  declare function mergeAlphaFromPrev(picked: string, prev: string): string;
8793
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
+
8794
8942
  /** Kit-shipped default paint constants.
8795
8943
  *
8796
8944
  * Single source of truth for the colors the built-in tools and the default
@@ -8801,6 +8949,7 @@ declare function mergeAlphaFromPrev(picked: string, prev: string): string;
8801
8949
  * All values are `#rrggbbaa` so they round-trip through the kit's hex8 alpha
8802
8950
  * helpers (`util/color`) without ambiguity.
8803
8951
  */
8952
+
8804
8953
  /** Default fill paint for shapes and the fill action — opaque white. */
8805
8954
  declare const DEFAULT_FILL_COLOR = "#ffffffff";
8806
8955
  /** Default stroke paint for shapes and the stroke action — opaque black. */
@@ -8812,6 +8961,41 @@ declare const DEFAULT_PALETTE: readonly string[];
8812
8961
  * selected-anchor highlight. Derived from the first palette entry so the
8813
8962
  * preview chrome matches the default insert color. */
8814
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;
8815
8999
 
8816
9000
  /**
8817
9001
  * Build an op-batched undo/redo `History`, defaulting restore-time op
@@ -8845,7 +9029,9 @@ interface ArrayAdapterConfig<TNode extends {
8845
9029
  getParent?: (id: string) => string | null;
8846
9030
  /** Optional reparent mutator. Default is a noop. */
8847
9031
  setParent?: (id: string, parentId: string | null) => void;
8848
- /** 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. */
8849
9035
  getChildren?: (id: string) => string[] | undefined;
8850
9036
  /** Live ref to the current selection. Default: empty array. */
8851
9037
  selectionRef?: MutableRefObject<string[]>;
@@ -9041,7 +9227,8 @@ interface GuidesLayerOpts {
9041
9227
  * screen space, projected from world offsets via the active view. */
9042
9228
  declare function createGuidesLayer(opts: GuidesLayerOpts): RenderLayer<unknown>;
9043
9229
 
9044
- /** 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`). */
9045
9232
  interface AlignBounds {
9046
9233
  x: number;
9047
9234
  y: number;
@@ -9064,18 +9251,27 @@ interface AlignMatchResult {
9064
9251
  * and translates a pose. The rect default handles `{x,y,width,height}` poses;
9065
9252
  * non-rect poses (Path, polygon) supply their own. */
9066
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. */
9067
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. */
9068
9261
  translate(pose: TPose, dx: number, dy: number): TPose;
9069
9262
  }
9070
- /** 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
9071
9264
  * both, and whether the page box contributes its own. */
9072
- interface DeriveAlignmentGuidesOptions {
9265
+ interface DeriveAlignmentGuidesOptions<TPose = AlignBounds> {
9073
9266
  /** Include the document/page box's edges + center as candidates. */
9074
9267
  page?: AlignBounds;
9075
9268
  /** Emit left/right (x) and top/bottom (y) edge guides. Default true. */
9076
9269
  edges?: boolean;
9077
9270
  /** Emit centerX (x) and centerY (y) guides. Default true. */
9078
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>;
9079
9275
  }
9080
9276
  /** Common options shared by the three alignment behavior factories. */
9081
9277
  interface AlignmentBehaviorBase {
@@ -9092,18 +9288,23 @@ interface AlignmentBehaviorBase {
9092
9288
  bypassKey?: keyof ModifierState;
9093
9289
  }
9094
9290
 
9095
- /** 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
9096
9292
  * optional page box. Each box contributes up to 3 guides per axis: the two
9097
- * edges and the center. Overlapping offsets collapse to one candidate. */
9098
- 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[];
9099
9297
 
9100
9298
  /** Move/insert test all three features per axis. */
9101
9299
  declare const MOVE_ANCHORS: {
9102
9300
  x: readonly AlignAnchor[];
9103
9301
  y: readonly AlignAnchor[];
9104
9302
  };
9105
- /** Default projection for rect-shaped poses (`{x,y,width,height}`). */
9106
- 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>;
9107
9308
  /**
9108
9309
  * Match a moving box's selected edge/center features against candidate guide
9109
9310
  * lines. Returns the per-axis snap delta and the matched candidate line(s).
@@ -9170,7 +9371,7 @@ declare function pointInRotatedRect(pose: RotatedPose, worldX: number, worldY: n
9170
9371
  * handle's center. Matches the demo's visual default; consumers can
9171
9372
  * override per-call. Unscaled — kit-internal use sites multiply by
9172
9373
  * `DeviceProfile.targetScale`. */
9173
- declare const DEFAULT_ROTATION_HANDLE_DISTANCE = 24;
9374
+ declare const DEFAULT_ROTATION_HANDLE_DISTANCE: 24;
9174
9375
  /** Rotation handle position in world coords. */
9175
9376
  interface RotationHandle {
9176
9377
  /** Handle center in world coords. */
@@ -9506,7 +9707,8 @@ interface SelectFromLassoOptions {
9506
9707
  * preserve, with shift). */
9507
9708
  declare function selectFromLasso(opts?: SelectFromLassoOptions): LassoSelectBehavior;
9508
9709
 
9509
- /** 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). */
9510
9712
  type AlignEdge = 'left' | 'right' | 'top' | 'bottom' | 'center-x' | 'center-y';
9511
9713
  /** Adapter for `useAlign`. */
9512
9714
  interface AlignAdapter<TPose> {
@@ -9528,6 +9730,17 @@ interface UseAlignReturn {
9528
9730
  /** Imperative trigger. No-op when fewer than 2 items selected. */
9529
9731
  align(edge: AlignEdge): void;
9530
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;
9531
9744
  /** Compute the (dx, dy) translation that moves AABB `b` so that the requested
9532
9745
  * `edge`/center matches the corresponding feature of the union AABB `u`. */
9533
9746
  declare function alignDeltaFor(b: ResizePose, u: ResizePose, edge: AlignEdge): {
@@ -9544,8 +9757,9 @@ declare function useAlign<TPose>(adapter: AlignAdapter<TPose>, options?: UseAlig
9544
9757
 
9545
9758
  /** Axis along which selection is distributed. `'x'` spreads horizontally. */
9546
9759
  type DistributeAxis = 'x' | 'y';
9547
- /** `'centers'` spaces AABB centers equally; `'gaps'` makes the gap between
9548
- * 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. */
9549
9763
  type DistributeMode = 'centers' | 'gaps';
9550
9764
  /** Adapter for `useDistribute`. */
9551
9765
  interface DistributeAdapter<TPose> {
@@ -10345,6 +10559,10 @@ declare function RectIcon({ className, size }: IconProps): react_jsx_runtime.JSX
10345
10559
  /** Outlined ellipse. Used for the ellipse insertion tool. */
10346
10560
  declare function EllipseIcon({ className, size }: IconProps): react_jsx_runtime.JSX.Element;
10347
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
+
10348
10566
  /** Eyedropper — diagonal stem running from upper-right (bulb) to
10349
10567
  * lower-left (tip), with a small squared bulb cap at the top. Used
10350
10568
  * for the eyedropper / color-picker tool. */
@@ -10438,4 +10656,4 @@ interface InsertOverlayStyle {
10438
10656
  lineWidth?: number;
10439
10657
  }
10440
10658
 
10441
- 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, 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 CanvasSurfaceHelpers, CanvasView, type CanvasViewHelpers, type CanvasViewProps, 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 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_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, type ResolvableView, 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, type ViewRect, type ViewResolver, type ViewTarget, ViewTransform, type ViewportConfig, ViewportDims, type ViewportLayer, ViewportToolDef, type ViewportZoomOptions, VisibilityRules, WeaselProvider, type WheelInput, type WheelState, ZoomBound, type ZoomBounds, type ZoomClampOpts, ZoomFactor, aabbCenter, actionIs, actionShortcuts, 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, computeFitViewport, 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, 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, getNodeShapes, getScratch, getStylusData, gradientForBounds, gradientGeometry, guideSnapStrategy, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isDebugEnabled, isEditableTarget, isPatternSpec, keySpecShortcut, 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, sceneSelectionStore, 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 };