@weasel-js/core 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -0
- package/dist/{DrawCommand-Dl0bXNfS.d.ts → DrawCommand-CJtqqt8H.d.ts} +26 -2
- package/dist/{chunk-7V6JEOXE.js → chunk-7F3SDUJ4.js} +7884 -9167
- package/dist/chunk-7F3SDUJ4.js.map +1 -0
- package/dist/chunk-FSZEXVCR.js +171 -0
- package/dist/chunk-FSZEXVCR.js.map +1 -0
- package/dist/clone.d.ts +1 -1
- package/dist/geometry-D9BDMiQi.d.ts +114 -0
- package/dist/{grid-Cf87knjU.d.ts → grid-CaSK9bHV.d.ts} +1 -1
- package/dist/index-D31EADQG.d.ts +2847 -0
- package/dist/index.css +0 -35
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +913 -2546
- package/dist/index.js +2 -2
- package/dist/insert.d.ts +2 -2
- package/dist/move.d.ts +3 -3
- package/dist/{options-BPPBWMa7.d.ts → options-DMWeTELe.d.ts} +1 -1
- package/dist/{pointSnapToGrid-D7s7QmOF.d.ts → pointSnapToGrid-C3EruUwt.d.ts} +3 -54
- package/dist/renderer.css +0 -35
- package/dist/renderer.css.map +1 -1
- package/dist/renderer.d.ts +5 -4
- package/dist/renderer.js +2 -2
- package/dist/resize.d.ts +3 -3
- package/dist/routing.d.ts +10 -8
- package/dist/routing.js +1 -1
- package/dist/{types-BjUi2vA-.d.ts → types-Dcaa0tPq.d.ts} +1 -25
- package/dist/{registerFont-CP-wCsrz.d.ts → viewToMat3-D4lrBigW.d.ts} +2 -44
- package/package.json +6 -5
- package/dist/chunk-7V6JEOXE.js.map +0 -1
- package/dist/chunk-AM6ARSPN.js +0 -517
- package/dist/chunk-AM6ARSPN.js.map +0 -1
- package/dist/fitViewToBounds-evGsnR8Q.d.ts +0 -62
- package/dist/index-DZBYMsHI.d.ts +0 -1555
- package/dist/routing.css +0 -35
- package/dist/routing.css.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
import * as React$1 from 'react';
|
|
2
|
-
import React__default, { RefObject,
|
|
2
|
+
import React__default, { RefObject, ReactNode, Ref, MouseEvent as MouseEvent$1, 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-
|
|
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-
|
|
7
|
-
import { R as RenderLayer, D as Dims,
|
|
8
|
-
export {
|
|
9
|
-
import { a as Stroke, F as FillStyle, D as DrawCommand, S as ShaderProgramHandle, P as PathDrawCommand, f as StyledRun, R as ResolvedTextStyle, g as TextStyle, h as TextVerticalAlign } from './DrawCommand-
|
|
10
|
-
export { i as DEFAULT_TEXT_STYLE, G as GradStop, b as GroupDrawCommand, I as ImageDrawCommand, M as Mat3, j as Region, k as ResolvedRun, c as ShaderDrawCommand, d as ShaderUniform, l as StrokeAlign, T as TextDrawCommand, n as alignedStrokeRect, o as fontString, p as markdownToRuns, q as resolveRuns, s as resolveTextStyle, t as runsToMarkdown, u as runsToPlainText, v as toRuns, w as verticalAlignOffset } from './DrawCommand-
|
|
11
|
-
import { B as Bounds, F as FitViewToBoundsOptions, V as ViewportDims } from './
|
|
12
|
-
export { f as fitViewToBounds } from './
|
|
13
|
-
import { N as NodeId, S as Scene,
|
|
14
|
-
export { A as AddLayerSpec, e as AddNodeSpec, C as ContainerNode, I as IDENTITY_POSE_COMPOSITION, L as LayerRecord, f as LeafNode, g as PoseAdapter, h as SerializedNode, i as SystemLayerRecord, j as SystemLayerSpec, k as UserLayerRecord, l as asNodeId, m as composeRectPose, n as composeWorldPose, o as decomposeRectPose, r as rebaseLocalPose, t as translateRectPose, w as worldPoseLookup } from './types-Cpb4hii1.js';
|
|
15
|
-
import { M as ModifierState, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior,
|
|
16
|
-
export { A as ActionBehavior,
|
|
17
|
-
import {
|
|
18
|
-
|
|
5
|
+
import { a as UnitValue, b as UnitSystem, U as UseMoveOptions, O as OriginProjection } from './grid-CaSK9bHV.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-CaSK9bHV.js';
|
|
7
|
+
import { R as RenderLayer, D as Dims, C as Condition, a as Rule, b as RuleCtx, V as VisibilityRules, c as ChromeCtx, d as ChromeId, A as Action, e as DepSchema, S as SliceDep, f as ActionsRegistry, T as Tool, g as AffordanceHit, h as Dispatcher, i as AnyTool, j as ToolCtx, k as ToolKeybinding, H as HotkeyTrigger, l as AffordanceBinding, m as ChromeState, n as SelectionApi, I as InsertExtras, G as GeometryProjection, o as ContentHandlerEntry, p as SvgIngestOptions, U as UseSelectionOptions, q as ActionsProp, r as Affordance, B as BooleansAdapter } from './index-D31EADQG.js';
|
|
8
|
+
export { s as ALWAYS, t as ActionDeps, u as ActionDisabledReason, v as ActionEnabledResult, w as ActionEntry, x as ActionsProvider, y as ActiveToolContextProvider, z as ActiveToolContextProviderIfRoot, E as ActiveToolContextProviderProps, F as ActiveToolContextValue, J as AffordanceRegion, K as AreaSelectDep, L as BindingOpts, M as BindingScope, N as BooleanOp, O as BooleanOpResult, P as BoundGesture, Q as BuildRuleCtxArgs, W as ClipboardIngestCtx, X as CustomPaintContext, Y as DepName, Z as DepRegistry, _ as DepRegistryProvider, $ as DispatcherContext, a0 as DragSample, a1 as EditAnchorsDep, a2 as GestureBinding, a3 as ImmediateInvoker, a4 as IngestCtx, a5 as IngestionDep, a6 as InsertDep, a7 as InvocationCtx, a8 as Invoker, a9 as LassoSelectDep, aa as LayoutDep, ab as MatchResult, ac as NEVER, ad as NodeAtPointDep, ae as OngoingHandle, af as OngoingInvoker, ag as OngoingOverlay, ah as Point2, ai as PointerContextProvider, aj as PointerContextValue, ak as PointerWorldPos, al as ResizePolicy, am as ResolveAllOptions, an as ResolveOnlyResult, ao as ResolvedCandidate, ap as ScopedBinding, aq as SelectionExtendKey, ar as SelectionMode, as as Selector, at as SnapDep, au as SvgUnpacker, av as TextEditDep, aw as ToolModifiers, ax as ToolPresentation, ay as ToolSlot, az as UiOngoingControl, aA as ViewApi, aB as applyBooleanOp, aC as buildRuleCtx, aD as createDispatcher, aE as describeRule, aF as drawLayers, aG as enterTextEditAction, aH as evaluate, aI as evaluateEnabled, aJ as registerContentHandler, aK as routing, aL as sliceAction, aM as specificity, aN as useAction, aO as useActionsRegistry, aP as useActiveToolContext, aQ as useDepRegistry, aR as useDepSource, aS as useOptionalActiveToolContext, aT as useOptionalDepRegistry, aU as usePointerContext, aV as useSelection } from './index-D31EADQG.js';
|
|
9
|
+
import { a as Stroke, F as FillStyle, D as DrawCommand, S as ShaderProgramHandle, P as PathDrawCommand, f as StyledRun, R as ResolvedTextStyle, g as TextStyle, h as TextVerticalAlign } from './DrawCommand-CJtqqt8H.js';
|
|
10
|
+
export { i as DEFAULT_TEXT_STYLE, G as GradStop, b as GroupDrawCommand, I as ImageDrawCommand, M as Mat3, j as Region, k as ResolvedRun, c as ShaderDrawCommand, d as ShaderUniform, l as StrokeAlign, T as TextDrawCommand, n as alignedStrokeRect, o as fontString, p as markdownToRuns, q as resolveRuns, s as resolveTextStyle, t as runsToMarkdown, u as runsToPlainText, v as toRuns, w as verticalAlignOffset } from './DrawCommand-CJtqqt8H.js';
|
|
11
|
+
import { B as Bounds, P as PoseProjection, F as FitViewToBoundsOptions, V as ViewportDims } from './geometry-D9BDMiQi.js';
|
|
12
|
+
export { R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR, f as fitViewToBounds } from './geometry-D9BDMiQi.js';
|
|
13
|
+
import { N as NodeId, S as Scene, a as Node$1, b as SerializedScene, R as RectPose, U as UseSceneOptions, c as SceneRegistry, d as RegisteredOp } from './types-Cpb4hii1.js';
|
|
14
|
+
export { A as AddLayerSpec, e as AddNodeSpec, C as ContainerNode, I as IDENTITY_POSE_COMPOSITION, L as LayerRecord, f as LeafNode, g as PoseAdapter, P as PoseComposition, h as SerializedNode, i as SystemLayerRecord, j as SystemLayerSpec, k as UserLayerRecord, l as asNodeId, m as composeRectPose, n as composeWorldPose, o as decomposeRectPose, r as rebaseLocalPose, t as translateRectPose, w as worldPoseLookup } from './types-Cpb4hii1.js';
|
|
15
|
+
import { M as ModifierState, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior, L as LassoSelectBehavior, a as RotatedPose, e as RotateBehavior, S as SnapStrategy, c as ResizeAnchor, b as MoveBehavior, I as InsertBehavior } from './types-Dcaa0tPq.js';
|
|
16
|
+
export { A as ActionBehavior, f as AreaSelectOverlay, g as BehaviorMoveResult, h as BehaviorResult, C as CloneBehavior, i as CloneLayer, j as ClonePose, G as GestureContext, k as GroupTransform, l as InsertMoveResult, m as InsertOverlay, n as InsertPoint, o as InsertProposed, p as LassoSelectMoveResult, q as LassoSelectOverlay, r as LassoSelectPose, s as LassoSelectProposed, t as PointSnapContext, d as PointSnapFrame, u as PointSnapResult, v as PointerState, w as ResizeMoveResult, x as ResizeOverlay, y as ResizeProposed, z as RotateMoveResult, D as RotateOverlay, E as RotateProposed } from './types-Dcaa0tPq.js';
|
|
17
|
+
import { U as UseResizeOptions } from './pointSnapToGrid-C3EruUwt.js';
|
|
18
|
+
export { C as CORNER_ANCHORS, a as CornerAnchor, e as CornerEdge, b as CornerHandle, c as cornerPoint, d as cornerResizeHandles, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-C3EruUwt.js';
|
|
19
|
+
import { BodyClassification, IngestItem } from '@weasel-js/gestures';
|
|
19
20
|
export { ClickSpec, DragSpec, DropSpec, GestureSpec, IngestItem, InputEvent, KeyHeldSpec, KeySpec, ModSpec, MultiTouchSpec, PasteSpec, TargetSpec, WheelSpec } from '@weasel-js/gestures';
|
|
20
|
-
import
|
|
21
|
-
import { P as Path, a as PolygonPath, b as PathFillRule, R as RectPath } from './types-B6MMiodD.js';
|
|
22
|
-
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 './types-B6MMiodD.js';
|
|
23
|
-
import { Op, History, CreateHistoryOptions } from '@weasel-js/history';
|
|
24
|
-
export * from '@weasel-js/history';
|
|
25
|
-
export { Op } from '@weasel-js/history';
|
|
26
|
-
import { L as LayoutStrategy, I as InsertAdapter, M as MoveAdapter, A as AreaSelectAdapter, R as RotateAdapter, a as LassoHitMode, b as LassoSelectAdapter, c as ResizeAdapter, d as SceneAdapter, e as LayerEnumerableAdapter, C as ClipboardSnapshot, O as OrderedAdapter, f as LayoutSnap } from './types-B_-khFM0.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-B_-khFM0.js';
|
|
27
22
|
export { g as ContainerBounds, D as DropTarget, h as LayoutChild, i as LayoutContainer, j as LayoutDragged, S as SnapTarget } from './types-B_-khFM0.js';
|
|
28
|
-
import { P as PoseProjection, U as UseResizeOptions } from './pointSnapToGrid-D7s7QmOF.js';
|
|
29
|
-
export { C as CORNER_ANCHORS, a as CornerAnchor, g as CornerEdge, b as CornerHandle, R as RECT_POSE_DESCRIPTOR, c as ROTATED_POSE_DESCRIPTOR, d as cornerPoint, e as cornerResizeHandles, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-D7s7QmOF.js';
|
|
30
|
-
import { Mat3 } from '@weasel-js/geom';
|
|
31
23
|
import { D as DebugSink, a as DebugConfig, b as DebugSnapshot, c as DebugTheme } from './types-BJ8_cyT7.js';
|
|
32
24
|
export { d as DebugFeature, H as HandleKind, e as HitShape, R as RecordedBounds, f as RecordedHandle, g as RecordedHitbox, h as RecordedLayer, i as RecordedOrigin, j as RecordedSnap } from './types-BJ8_cyT7.js';
|
|
33
|
-
import {
|
|
25
|
+
import { a as PolygonPath, P as Path, b as PathFillRule, R as RectPath } from './types-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 './types-B6MMiodD.js';
|
|
27
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
28
|
+
import { Op, CreateHistoryOptions, History } from '@weasel-js/history';
|
|
29
|
+
export * from '@weasel-js/history';
|
|
30
|
+
export { Op } from '@weasel-js/history';
|
|
34
31
|
import { T as TextureHandle } from './registerTexture-BzHTLhD9.js';
|
|
32
|
+
import { Mat3 } from '@weasel-js/geom';
|
|
35
33
|
import { G as Guide } from './types-D2tTKEU0.js';
|
|
34
|
+
export { U as UseInsertOptions } from './options-DMWeTELe.js';
|
|
36
35
|
export { UseClipboardOpsOptions, UseClipboardOpsReturn, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, buildWeaselClipboardText, embedWeaselMetadataInSvg, extractWeaselClipboardFromSvg, parseWeaselClipboardText, sniffWeaselClipboardText, useClipboardOps } from './clipboard.js';
|
|
37
36
|
export { cloneByAltDrag } from './clone.js';
|
|
38
|
-
export {
|
|
37
|
+
export { I as ImageMinification, v as viewToMat3 } from './viewToMat3-D4lrBigW.js';
|
|
38
|
+
export { FontVariant, isCanvasFont, registerCanvasFont, registerFont, subscribeGlyphReady, unregisterCanvasFont } from '@weasel-js/font';
|
|
39
|
+
import '@weasel-js/modes';
|
|
39
40
|
|
|
40
41
|
/** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */
|
|
41
42
|
declare function roundToCell(value: number, cellSize: number): number;
|
|
@@ -836,182 +837,6 @@ declare function isEditableTarget(target: EventTarget | null): boolean;
|
|
|
836
837
|
*/
|
|
837
838
|
declare function matchesKeyBinding(e: KeyboardEvent, b: KeyBinding): boolean;
|
|
838
839
|
|
|
839
|
-
/**
|
|
840
|
-
* Live state read by rule evaluation. Built once per frame on the consuming
|
|
841
|
-
* surface — chrome-caps, the affordance pipeline, the dispatcher's
|
|
842
|
-
* eligibility filter — and discarded.
|
|
843
|
-
*
|
|
844
|
-
* Adding a new field is additive: existing rules don't change, new
|
|
845
|
-
* selector atoms can read it.
|
|
846
|
-
*/
|
|
847
|
-
interface RuleCtx {
|
|
848
|
-
readonly focused: boolean;
|
|
849
|
-
readonly selection: readonly NodeId[];
|
|
850
|
-
readonly multiActive: boolean;
|
|
851
|
-
readonly modifiers: ModifierState;
|
|
852
|
-
readonly action: {
|
|
853
|
-
readonly kind: string | null;
|
|
854
|
-
readonly id: string | null;
|
|
855
|
-
};
|
|
856
|
-
readonly hover: NodeId | null;
|
|
857
|
-
readonly view: View;
|
|
858
|
-
/** Active mode id. `'normal'` when no non-default mode is engaged. */
|
|
859
|
-
readonly mode: string;
|
|
860
|
-
/** Capability tags allowed by the active mode (the union of
|
|
861
|
-
* `ModeDefinition.allows` plus implicit tags). The `capability:`
|
|
862
|
-
* selector reads this to determine whether a tag is permitted. */
|
|
863
|
-
readonly allowedCapabilities: ReadonlySet<CapabilityTag>;
|
|
864
|
-
/** Whether the current selection may be resized. `<SceneCanvas>` folds
|
|
865
|
-
* `selectTool.resize.resizable` over the selection (true only when every
|
|
866
|
-
* selected node is resizable). Read by the `resizable:` selector to gate
|
|
867
|
-
* `selection.resize-handles`. Absent (legacy ctx builders) is treated as
|
|
868
|
-
* resizable — back-compat: handles show unless a consumer opts a node out. */
|
|
869
|
-
readonly selectionResizable?: boolean;
|
|
870
|
-
}
|
|
871
|
-
interface BuildRuleCtxArgs {
|
|
872
|
-
focused: boolean;
|
|
873
|
-
selection: readonly NodeId[];
|
|
874
|
-
multiActive: boolean;
|
|
875
|
-
modifiers: ModifierState;
|
|
876
|
-
action: {
|
|
877
|
-
kind: string | null;
|
|
878
|
-
id: string | null;
|
|
879
|
-
};
|
|
880
|
-
hover: NodeId | null;
|
|
881
|
-
view: View;
|
|
882
|
-
mode: string;
|
|
883
|
-
allowedCapabilities: ReadonlySet<CapabilityTag>;
|
|
884
|
-
/** Optional — omitted means "resizable" (handles show). See {@link RuleCtx}. */
|
|
885
|
-
selectionResizable?: boolean;
|
|
886
|
-
}
|
|
887
|
-
declare function buildRuleCtx(args: BuildRuleCtxArgs): RuleCtx;
|
|
888
|
-
|
|
889
|
-
/**
|
|
890
|
-
* A selector is a conjunction of key/value tests. Multiple keys at the same
|
|
891
|
-
* level AND together. Each key maps to a selector primitive in the evaluator.
|
|
892
|
-
*/
|
|
893
|
-
interface Selector {
|
|
894
|
-
selection?: {
|
|
895
|
-
is?: number;
|
|
896
|
-
atLeast?: number;
|
|
897
|
-
empty?: boolean;
|
|
898
|
-
};
|
|
899
|
-
mode?: string | {
|
|
900
|
-
not: string;
|
|
901
|
-
} | {
|
|
902
|
-
in: readonly string[];
|
|
903
|
-
};
|
|
904
|
-
capability?: CapabilityTag | readonly CapabilityTag[] | {
|
|
905
|
-
in: readonly CapabilityTag[];
|
|
906
|
-
} | {
|
|
907
|
-
not: CapabilityTag;
|
|
908
|
-
};
|
|
909
|
-
gesturing?: boolean;
|
|
910
|
-
actionIs?: string;
|
|
911
|
-
modifierHeld?: keyof ModifierState;
|
|
912
|
-
focused?: boolean;
|
|
913
|
-
hovering?: boolean;
|
|
914
|
-
hoveringSelected?: boolean;
|
|
915
|
-
zoomAtLeast?: number;
|
|
916
|
-
/** Matches `ctx.selectionResizable`. Absent flag is treated as `true`
|
|
917
|
-
* (resizable), so `{ resizable: true }` passes for legacy ctx builders
|
|
918
|
-
* that don't compute it. */
|
|
919
|
-
resizable?: boolean;
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* Composable visibility/eligibility rule. Trees of `all`/`any`/`not` nodes
|
|
923
|
-
* over `Selector` leaves. `when` is the escape hatch — its closure is
|
|
924
|
-
* opaque to introspection and should be avoided when a declarative form
|
|
925
|
-
* exists. Empty `all` is true; empty `any` is false.
|
|
926
|
-
*/
|
|
927
|
-
type Rule = Selector | {
|
|
928
|
-
all: readonly Rule[];
|
|
929
|
-
} | {
|
|
930
|
-
any: readonly Rule[];
|
|
931
|
-
} | {
|
|
932
|
-
not: Rule;
|
|
933
|
-
} | {
|
|
934
|
-
when: (ctx: RuleCtx) => boolean;
|
|
935
|
-
};
|
|
936
|
-
/** Constant rules. Kept here so they have a single source. */
|
|
937
|
-
declare const ALWAYS: Rule;
|
|
938
|
-
declare const NEVER: Rule;
|
|
939
|
-
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* Live state read by chrome-visibility {@link Condition}s. Backward-compat
|
|
943
|
-
* alias for the legacy ChromeCtx shape — kept for consumers that still
|
|
944
|
-
* import `ChromeCtx`. Subset of `RuleCtx`: legacy ChromeCtx didn't carry
|
|
945
|
-
* mode/capability info. The resolver builds a `RuleCtx` for evaluation;
|
|
946
|
-
* surfaces that still operate in `ChromeCtx` shape supply defaults
|
|
947
|
-
* (mode='normal', empty allowedCapabilities) at the construction site.
|
|
948
|
-
*/
|
|
949
|
-
interface ChromeCtx {
|
|
950
|
-
readonly focused: boolean;
|
|
951
|
-
readonly selection: readonly NodeId[];
|
|
952
|
-
readonly multiActive: boolean;
|
|
953
|
-
readonly modifiers: ModifierState;
|
|
954
|
-
readonly action: {
|
|
955
|
-
readonly kind: string | null;
|
|
956
|
-
readonly id: string | null;
|
|
957
|
-
};
|
|
958
|
-
readonly hover: NodeId | null;
|
|
959
|
-
readonly view: View;
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Composable visibility predicate with fluent surface. Carries its underlying
|
|
963
|
-
* `Rule` tree at `.rule` so the resolver can introspect / share trees with
|
|
964
|
-
* the affordance pipeline and the dispatcher's eligibility filter.
|
|
965
|
-
*
|
|
966
|
-
* Callable form `cond(ctx)` evaluates the tree against ctx. The fluent
|
|
967
|
-
* methods return new Conditions wrapping new trees.
|
|
968
|
-
*
|
|
969
|
-
* **Chain semantics: strict left-to-right, no precedence.**
|
|
970
|
-
* `a.and(b).or(c)` is `(a && b) || c`; `a.or(b).and(c)` is
|
|
971
|
-
* `(a || b) && c`. Mix `.and` and `.or` only when you mean
|
|
972
|
-
* left-to-right evaluation. For grouped disjunction, name the
|
|
973
|
-
* subexpression or use the top-level `or(...)`.
|
|
974
|
-
*/
|
|
975
|
-
interface Condition {
|
|
976
|
-
(ctx: RuleCtx): boolean;
|
|
977
|
-
readonly rule: Rule;
|
|
978
|
-
/** `this && other` */
|
|
979
|
-
and(other: Condition | Rule): Condition;
|
|
980
|
-
/** `this || other` */
|
|
981
|
-
or(other: Condition | Rule): Condition;
|
|
982
|
-
/** `this && !other` */
|
|
983
|
-
andNot(other: Condition | Rule): Condition;
|
|
984
|
-
/** `this || !other` */
|
|
985
|
-
orNot(other: Condition | Rule): Condition;
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Stable identifier for one user-visible chrome element. The same id
|
|
989
|
-
* gates both paint and hit-test — there is no separate
|
|
990
|
-
* `affordance.X` / `selection.X` split — so toggling a rule cannot
|
|
991
|
-
* leave a visually-present but un-hittable handle (or vice versa).
|
|
992
|
-
*
|
|
993
|
-
* Naming convention by lifecycle:
|
|
994
|
-
*
|
|
995
|
-
* - `selection.*` — chrome reflecting committed selection state
|
|
996
|
-
* (persists between actions).
|
|
997
|
-
* - `action.*` — chrome that only exists during an in-flight
|
|
998
|
-
* action (vanishes on commit / cancel).
|
|
999
|
-
* - `snap.*` — snapping system chrome (guides, target highlights).
|
|
1000
|
-
* - `grid`, `debug.*` — environment chrome.
|
|
1001
|
-
*
|
|
1002
|
-
* The intersection `(string & {})` keeps the union open so consumers
|
|
1003
|
-
* can register their own ids; the kit's built-ins are listed
|
|
1004
|
-
* explicitly for autocomplete.
|
|
1005
|
-
*/
|
|
1006
|
-
type ChromeId = 'selection.outline' | 'selection.resize-handles' | 'selection.rotation-handle' | 'action.marquee' | 'action.lasso' | 'action.move-ghosts' | 'action.insert-preview' | 'action.commands' | 'snap.guides' | 'snap.targets' | 'grid' | (string & {});
|
|
1007
|
-
/**
|
|
1008
|
-
* Consumer override map. Merged on top of the kit's
|
|
1009
|
-
* `defaultVisibilityRules`; absent keys fall through to defaults,
|
|
1010
|
-
* absent ids fall through to `always`. Entries may be either fluent
|
|
1011
|
-
* `Condition` instances OR raw `Rule` trees — the resolver normalizes.
|
|
1012
|
-
*/
|
|
1013
|
-
type VisibilityRules = Partial<Record<ChromeId, Condition | Rule>>;
|
|
1014
|
-
|
|
1015
840
|
/**
|
|
1016
841
|
* Promote a Rule tree to a fluent Condition. The function form evaluates
|
|
1017
842
|
* the tree against a RuleCtx; combinator methods produce new Conditions
|
|
@@ -1097,11 +922,16 @@ declare const capabilityNot: (cap: string) => Condition;
|
|
|
1097
922
|
* each rule depends on are immediately visible. The fluent atoms compile
|
|
1098
923
|
* to the same trees; either form is valid in a VisibilityRules entry.
|
|
1099
924
|
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
* the
|
|
1104
|
-
*
|
|
925
|
+
* Gating is written in terms of **capabilities, not mode ids**. A
|
|
926
|
+
* capability rule keeps holding when a new mode is added that permits the
|
|
927
|
+
* same thing; a mode rule has to be found and edited every time. The one
|
|
928
|
+
* exception is the `path-edit.*` chrome below, which is genuinely
|
|
929
|
+
* mode-specific: it's the visual signature of that mode, not a statement
|
|
930
|
+
* about what the user is allowed to do.
|
|
931
|
+
*
|
|
932
|
+
* Chrome that gates on the *absence* of a capability (the selection
|
|
933
|
+
* outline, suppressed while an anchor-editing overlay owns the visuals)
|
|
934
|
+
* uses `capability: { not: … }` for the same reason.
|
|
1105
935
|
*/
|
|
1106
936
|
declare const defaultVisibilityRules: VisibilityRules;
|
|
1107
937
|
|
|
@@ -1116,10 +946,9 @@ declare const defaultVisibilityRules: VisibilityRules;
|
|
|
1116
946
|
* Predicates are O(1) and run once per chrome id per frame; no
|
|
1117
947
|
* memoization in v1 — profile before adding any.
|
|
1118
948
|
*
|
|
1119
|
-
* `ChromeCtx` is the legacy shape; surfaces still on it
|
|
1120
|
-
* `
|
|
1121
|
-
*
|
|
1122
|
-
* legacy shape keep working transparently.
|
|
949
|
+
* `ChromeCtx` is the legacy shape; surfaces still on it get `mode='normal'`
|
|
950
|
+
* and `DEFAULT_ALLOWED_CAPABILITIES` filled in here, so a caller that never
|
|
951
|
+
* opted into modality behaves exactly like one sitting in normal mode.
|
|
1123
952
|
*/
|
|
1124
953
|
declare function resolveVisibility(consumer: VisibilityRules | undefined, ctx: ChromeCtx | RuleCtx): (id: ChromeId) => boolean;
|
|
1125
954
|
|
|
@@ -1181,1426 +1010,158 @@ interface UseHoverTrackingArgs {
|
|
|
1181
1010
|
}
|
|
1182
1011
|
declare function useHoverTracking(args: UseHoverTrackingArgs): () => NodeId | null;
|
|
1183
1012
|
|
|
1184
|
-
/** Boolean op identifiers — five Pathfinder primaries plus Crop. */
|
|
1185
|
-
type BooleanOp = 'union' | 'intersect' | 'subtract' | 'exclude' | 'divide' | 'crop';
|
|
1186
1013
|
/**
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1014
|
+
* `moveAction` — first `ongoing`-timing Action descriptor.
|
|
1015
|
+
*
|
|
1016
|
+
* Mirrors the per-frame translate semantics of the `useMove` hook:
|
|
1017
|
+
* - `start`: capture origin poses for all selected nodes; record the
|
|
1018
|
+
* current drag delta in scratch each frame.
|
|
1019
|
+
* - `onMove`: update the in-scratch `currentDelta` only — no scene writes.
|
|
1020
|
+
* This avoids polluting the undo stack with O(N-frames) entries.
|
|
1021
|
+
* - `onEnd('commit')`: emit the final delta as transform ops and route
|
|
1022
|
+
* them through the consumer `applyOps` hook when present, else
|
|
1023
|
+
* `scene.applyBatch(ops, 'Move', adapter)` — either way a single batch
|
|
1024
|
+
* → exactly one undo entry for the whole drag.
|
|
1025
|
+
* - `onEnd('cancel')`: no scene writes — the scene was never mutated during
|
|
1026
|
+
* the drag, so no restoration is needed.
|
|
1027
|
+
*
|
|
1028
|
+
* ## Why no per-frame scene writes
|
|
1029
|
+
*
|
|
1030
|
+
* `Scene.setPose` calls `executeAndLog`, which immediately records an undo
|
|
1031
|
+
* entry on the scene's history engine. Per-frame writes during drag would create
|
|
1032
|
+
* O(frames) history entries — matching `useMove`'s approach of tracking
|
|
1033
|
+
* poses only in React state (overlay) during the drag and committing a
|
|
1034
|
+
* single `createTransformOp` batch at the end.
|
|
1035
|
+
*
|
|
1036
|
+
* The behavior pipeline (snap-to-grid, snap-back-or-delete, snap-to-container,
|
|
1037
|
+
* etc.) via `opts.behaviors` from `BindingOpts` IS wired: `start` builds a
|
|
1038
|
+
* `GestureContext` + scene-backed adapter (`moveGestureAdapter`), `onMove`
|
|
1039
|
+
* folds each behavior's proposed-transform result, and `onEnd` runs a
|
|
1040
|
+
* first-non-undefined-wins reducer (`Op[]` commits via `scene.applyBatch`,
|
|
1041
|
+
* `null` aborts, all-`undefined` falls through to the default translate path).
|
|
1042
|
+
*
|
|
1043
|
+
* ## Pose generics
|
|
1044
|
+
*
|
|
1045
|
+
* `scene` dep is typed `Scene<unknown, string, unknown>` (the erased DepSchema
|
|
1046
|
+
* entry). Poses are read and written as `unknown`; `translatePoseGeneric`
|
|
1047
|
+
* delegates to `RECT_POSE_DESCRIPTOR.translate` which treats any pose as
|
|
1048
|
+
* `{x, y, ...}`. Consumers with non-rect poses should register a custom
|
|
1049
|
+
* action with a typed translatePose.
|
|
1191
1050
|
*/
|
|
1192
|
-
/** @internal */
|
|
1193
|
-
interface BooleanZOrder {
|
|
1194
|
-
parentId: string | null;
|
|
1195
|
-
index: number;
|
|
1196
|
-
}
|
|
1197
|
-
/** Adapter the hook and the pure core both consume. */
|
|
1198
|
-
interface BooleansAdapter {
|
|
1199
|
-
getSelection(): NodeId[];
|
|
1200
|
-
getWorldPath(id: NodeId): Path | undefined;
|
|
1201
|
-
compareZ(a: NodeId, b: NodeId): number;
|
|
1202
|
-
/**
|
|
1203
|
-
* Mint a new node from a boolean-op result `Path`. `producedBy` names the
|
|
1204
|
-
* op that synthesized it — adapters that store provenance (e.g. for a
|
|
1205
|
-
* layer-panel icon) record it; others ignore the arg.
|
|
1206
|
-
*/
|
|
1207
|
-
createPathNode(path: Path, producedBy: BooleanOp): {
|
|
1208
|
-
id: string;
|
|
1209
|
-
};
|
|
1210
|
-
/**
|
|
1211
|
-
* Optional: return the full object for an id, used by the delete ops so
|
|
1212
|
-
* their `invert` (an insert) can restore the complete object on undo.
|
|
1213
|
-
* If omitted, a `{ id }` stub is captured — undo will reinstate the id
|
|
1214
|
-
* but consumers reading other fields (path, fill, etc.) will see them as
|
|
1215
|
-
* undefined. Mirrors `DeleteAdapter.getNode`; should be provided whenever
|
|
1216
|
-
* undo over boolean ops is expected to be lossless.
|
|
1217
|
-
*/
|
|
1218
|
-
getNode?(id: NodeId): {
|
|
1219
|
-
id: string;
|
|
1220
|
-
} | undefined | null;
|
|
1221
|
-
/**
|
|
1222
|
-
* Optional: return the parent + child-index of `id` so the result of a
|
|
1223
|
-
* boolean op can be placed in the topmost source's z-slot. Adapters that
|
|
1224
|
-
* also expose `getChildren`/`setChildOrder` (the `ReorderAdapter`
|
|
1225
|
-
* contract) will have the kit emit a `createMoveToIndexOp` after the
|
|
1226
|
-
* inserts. Adapters that omit this method get v1 behavior — the result
|
|
1227
|
-
* lands wherever the adapter's plain `insertNode` defaults to.
|
|
1228
|
-
*/
|
|
1229
|
-
getZOrder?(id: NodeId): BooleanZOrder | undefined;
|
|
1230
|
-
applyOps?(ops: Op[], label?: string): void;
|
|
1231
|
-
setSelection?(ids: NodeId[]): void;
|
|
1232
|
-
insertNode?(node: {
|
|
1233
|
-
id: string;
|
|
1234
|
-
}): void;
|
|
1235
|
-
removeNode?(id: string): void;
|
|
1236
|
-
}
|
|
1237
|
-
/** Outcome reported back to callers (lets the hook surface no-op signals). */
|
|
1238
|
-
type BooleanOpResult = {
|
|
1239
|
-
kind: 'applied';
|
|
1240
|
-
resultIds: string[];
|
|
1241
|
-
} | {
|
|
1242
|
-
kind: 'noop';
|
|
1243
|
-
reason: 'no-paths' | 'too-few-for-subtract' | 'empty-result';
|
|
1244
|
-
};
|
|
1245
|
-
declare function applyBooleanOp(adapter: BooleansAdapter, op: BooleanOp): BooleanOpResult;
|
|
1246
|
-
|
|
1247
|
-
/** Context handed to every content handler for one ingest event. */
|
|
1248
|
-
interface IngestCtx {
|
|
1249
|
-
/** World-space arrival point (drop / pointed imperative ingest); `null`
|
|
1250
|
-
* for paste and point-less calls — handlers pick their own policy
|
|
1251
|
-
* (the kit image handler centers on the viewport). */
|
|
1252
|
-
point: {
|
|
1253
|
-
x: number;
|
|
1254
|
-
y: number;
|
|
1255
|
-
} | null;
|
|
1256
|
-
/** Visible canvas area in world coordinates. */
|
|
1257
|
-
viewportWorldRect(): {
|
|
1258
|
-
x: number;
|
|
1259
|
-
y: number;
|
|
1260
|
-
width: number;
|
|
1261
|
-
height: number;
|
|
1262
|
-
};
|
|
1263
|
-
/** The kit insert dep — id/layer/undoable-op supplied; the canonical way
|
|
1264
|
-
* for a handler to mint a node (`insert.commit(bounds, { kind, ... })`). */
|
|
1265
|
-
insert: InsertDep;
|
|
1266
|
-
/** Raw op commit for handlers that build their own ops. */
|
|
1267
|
-
applyOps(ops: Op[], label?: string): void;
|
|
1268
|
-
scene: Scene<unknown, string, unknown>;
|
|
1269
|
-
selection: SelectionApi;
|
|
1270
|
-
/** Consumer file→src resolver (SceneCanvas `ingestion.resolveSrc`).
|
|
1271
|
-
* When absent, the kit image handler embeds as a `data:` URI. */
|
|
1272
|
-
resolveSrc?: (file: File) => Promise<string>;
|
|
1273
|
-
/** Kit SVG-handler options (SceneCanvas `ingestion.svg`) — e.g.
|
|
1274
|
-
* `{ unpack: unpackSvgFiles }` (from `@weasel-js/svg`) to parse SVG files
|
|
1275
|
-
* into scene nodes. */
|
|
1276
|
-
svg?: SvgIngestOptions;
|
|
1277
|
-
/** Clipboard-paste seam — present when the hosting `SceneCanvas` supplied
|
|
1278
|
-
* an adapter with `commitPaste`. `reviver` comes from
|
|
1279
|
-
* `SceneCanvasProps.ingestion.clipboard`. Absent ⇒ the kit weasel-JSON
|
|
1280
|
-
* handler declines inert (dwarn, nothing ingested) — its matched items
|
|
1281
|
-
* were already consumed at match time, so they do NOT fall through;
|
|
1282
|
-
* only match-level misses flow on to other handlers. */
|
|
1283
|
-
clipboard?: ClipboardIngestCtx;
|
|
1284
|
-
/** Set to `true` by the kit weasel-JSON handler when it successfully
|
|
1285
|
-
* pastes a payload in this event. The `ctx` object is shared across all
|
|
1286
|
-
* handlers in one `runIngest` call, and higher-priority handlers' `handle`
|
|
1287
|
-
* bodies run (synchronously) before lower ones — so `kit:svg`'s
|
|
1288
|
-
* `text/plain` SVG fallback reads this to decline the SVG flavor of a copy
|
|
1289
|
-
* whose canonical weasel-JSON flavor already ingested (avoids a
|
|
1290
|
-
* double-paste when both flavors ride one clipboard event). */
|
|
1291
|
-
consumedWeaselPayload?: boolean;
|
|
1292
|
-
/** Full action-deps bag, for consumer handlers that need more. */
|
|
1293
|
-
deps: ActionDeps;
|
|
1294
|
-
}
|
|
1295
|
-
interface ContentHandlerEntry {
|
|
1296
|
-
/** Stable identifier — used for unregistration and debugging
|
|
1297
|
-
* (`'kit:image'`, `'app:csv'`). */
|
|
1298
|
-
id: string;
|
|
1299
|
-
/** MIME glob(s) (`'image/*'`, `'text/csv'`) or an item predicate. */
|
|
1300
|
-
match: string | string[] | ((item: IngestItem) => boolean);
|
|
1301
|
-
/** Higher runs earlier. Kit defaults register at -100 so any consumer
|
|
1302
|
-
* handler (default 0) beats them. */
|
|
1303
|
-
priority?: number;
|
|
1304
|
-
handle(items: IngestItem[], ctx: IngestCtx): void | Promise<void>;
|
|
1305
|
-
}
|
|
1306
|
-
/** Register a content handler. Returns a disposer that removes it. */
|
|
1307
|
-
declare function registerContentHandler(entry: ContentHandlerEntry): () => void;
|
|
1308
1051
|
|
|
1309
1052
|
/**
|
|
1310
1053
|
* @experimental
|
|
1311
|
-
*
|
|
1312
|
-
* position of the canvas pointer, refreshed on every `pointermove` over
|
|
1313
|
-
* the canvas. Cleared (set to `null`) on `pointerleave`.
|
|
1314
|
-
*
|
|
1315
|
-
* Why ref-based and not state-based: cursor moves fire dozens of times per
|
|
1316
|
-
* second; routing those through React state would re-render every consumer
|
|
1317
|
-
* in the tree. The context exposes a stable `pointerRef` whose `.current`
|
|
1318
|
-
* is mutated directly by the publisher, plus a thunk `getDropPoint()` that
|
|
1319
|
-
* reads it on demand. Consumers (e.g. `useClipboard`) pull via the thunk
|
|
1320
|
-
* inside their callbacks — no subscription, no re-render.
|
|
1321
|
-
*
|
|
1322
|
-
* `<SceneCanvas>` publishes automatically. `useClipboardOps` consumes when
|
|
1323
|
-
* the caller didn't pass an explicit `getDropPoint` option. Other future
|
|
1324
|
-
* hit-on-cursor consumers (drop-zone hover, context-menu anchor) can reuse
|
|
1325
|
-
* the same context.
|
|
1326
|
-
*/
|
|
1327
|
-
|
|
1328
|
-
/** @experimental World-space pointer position, or `null` when the pointer
|
|
1329
|
-
* isn't over the publishing canvas. */
|
|
1330
|
-
type PointerWorldPos = {
|
|
1331
|
-
worldX: number;
|
|
1332
|
-
worldY: number;
|
|
1333
|
-
} | null;
|
|
1334
|
-
/** @experimental */
|
|
1335
|
-
interface PointerContextValue {
|
|
1336
|
-
/** Live ref — mutate to publish, read for the latest snapshot. The
|
|
1337
|
-
* identity is stable for the lifetime of the provider. */
|
|
1338
|
-
readonly pointerRef: MutableRefObject<PointerWorldPos>;
|
|
1339
|
-
/** Convenience thunk equivalent to `() => pointerRef.current`. Stable
|
|
1340
|
-
* identity for the lifetime of the provider; safe to pass to hooks. */
|
|
1341
|
-
readonly getDropPoint: () => PointerWorldPos;
|
|
1342
|
-
}
|
|
1343
|
-
/**
|
|
1344
|
-
* @experimental
|
|
1345
|
-
* Wrap the part of the React tree that should share a pointer-position
|
|
1346
|
-
* context. Usually placed at the demo / app root, alongside
|
|
1347
|
-
* `<ActionsProvider>` and `<SelectionContextProvider>`.
|
|
1054
|
+
* Static descriptor for the `move` Action.
|
|
1348
1055
|
*
|
|
1349
|
-
*
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
*
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
children: ReactNode;
|
|
1356
|
-
}): ReactNode;
|
|
1357
|
-
/** @experimental Read the surrounding pointer-context value, or `null` when
|
|
1358
|
-
* no provider is in scope. */
|
|
1359
|
-
declare function usePointerContext(): PointerContextValue | null;
|
|
1360
|
-
|
|
1361
|
-
interface ActiveToolContextValue {
|
|
1362
|
-
active: string;
|
|
1363
|
-
hotkeyStack: string[];
|
|
1364
|
-
setActive(id: string): void;
|
|
1365
|
-
pushHotkey(id: string): void;
|
|
1366
|
-
popHotkey(): void;
|
|
1367
|
-
}
|
|
1368
|
-
interface ActiveToolContextProviderProps {
|
|
1369
|
-
children: ReactNode;
|
|
1370
|
-
initialActive?: string;
|
|
1371
|
-
}
|
|
1372
|
-
declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react_jsx_runtime.JSX.Element;
|
|
1373
|
-
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
1374
|
-
/**
|
|
1375
|
-
* Like `useActiveToolContext`, but returns `null` when no
|
|
1376
|
-
* `<ActiveToolContextProvider>` is in scope instead of throwing. Used by
|
|
1377
|
-
* `useStandardActions` to preserve its silent-no-op contract when no provider
|
|
1378
|
-
* is present.
|
|
1379
|
-
*/
|
|
1380
|
-
declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
|
|
1381
|
-
/**
|
|
1382
|
-
* Conditional `<ActiveToolContextProvider>` wrapper. Mounts a provider only
|
|
1383
|
-
* when no parent provider is in scope — otherwise renders children unwrapped
|
|
1384
|
-
* so callers (e.g. `<WeaselProvider>`, `<SceneCanvas>`) defer to the host's
|
|
1385
|
-
* existing scope. Mirrors `ActionsProviderIfRoot` / `DepRegistryProviderIfRoot`.
|
|
1056
|
+
* Requires dep-schema entries: `selection`, `scene`.
|
|
1057
|
+
*
|
|
1058
|
+
* The invoker is `ongoing` — it returns an `OngoingHandle` from `start` that
|
|
1059
|
+
* the dispatcher pumps via `onMove`/`onEnd` for the duration of the drag.
|
|
1060
|
+
*
|
|
1061
|
+
* @see useMove — the React hook this descriptor mirrors for the simple case.
|
|
1386
1062
|
*/
|
|
1387
|
-
declare
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1063
|
+
declare const moveAction: Action & {
|
|
1064
|
+
requires: string[];
|
|
1065
|
+
};
|
|
1390
1066
|
|
|
1391
1067
|
/**
|
|
1392
|
-
* `
|
|
1393
|
-
* text editing on a selected text node.
|
|
1068
|
+
* `resizeAction` — ongoing Action descriptor for anchor-relative drag resize.
|
|
1394
1069
|
*
|
|
1395
1070
|
* ## Status: REAL
|
|
1396
1071
|
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1401
|
-
* ## No defaultBinding / defaultBinding
|
|
1402
|
-
*
|
|
1403
|
-
* This action has no ambient key or gesture binding — it fires ONLY via
|
|
1404
|
-
* `useTextTool`'s `Tool.bindings` entry:
|
|
1405
|
-
*
|
|
1406
|
-
* ```ts
|
|
1407
|
-
* bindings: [
|
|
1408
|
-
* { spec: { kind: 'click', target: 'selected-body' }, actionId: 'enterTextEdit' },
|
|
1409
|
-
* ]
|
|
1410
|
-
* ```
|
|
1411
|
-
*
|
|
1412
|
-
* Keeping it binding-free avoids ambient double-fire and scopes the action to
|
|
1413
|
-
* the text tool context where `classifyTarget` is already wired.
|
|
1414
|
-
*
|
|
1415
|
-
* ## Self-guard: only act on text nodes
|
|
1416
|
-
*
|
|
1417
|
-
* The `classifyTarget` thunk yields `'selected-body'` for any selected node
|
|
1418
|
-
* kind — there's no per-kind filter yet (that's a Phase 14e follow-up). To
|
|
1419
|
-
* avoid entering text-edit mode when the text tool happens to have a non-text
|
|
1420
|
-
* node selected, the action self-guards via an optional `isTextNode` predicate
|
|
1421
|
-
* on `TextEditDep`:
|
|
1422
|
-
*
|
|
1423
|
-
* - When `isTextNode` is absent: action fires unconditionally (the binding
|
|
1424
|
-
* spec is the real gate — consumers should only bind this action from the
|
|
1425
|
-
* text tool).
|
|
1426
|
-
* - When `isTextNode(id)` returns `false`: action is a no-op for that node.
|
|
1072
|
+
* Performs real anchor-relative resize math for rect-shaped poses. Reads
|
|
1073
|
+
* `ctx.drag.affordance.anchor` to determine which corner is fixed, captures
|
|
1074
|
+
* start poses, applies per-frame bounds remapping, and commits a single
|
|
1075
|
+
* `scene.batch` entry on end.
|
|
1427
1076
|
*
|
|
1428
|
-
*
|
|
1077
|
+
* ## Behaviors / point-snap / expandIds / geometry — via `resizePolicy` dep
|
|
1429
1078
|
*
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1432
|
-
*
|
|
1433
|
-
*
|
|
1079
|
+
* The resize-behaviors-api wired the four behavior options the
|
|
1080
|
+
* legacy `useResize` hook exposed (`behaviors`, `pointSnapBehaviors`,
|
|
1081
|
+
* `expandIds`, `geometry`) through the `resizePolicy` dep entry. When
|
|
1082
|
+
* the dep is registered, this invoker:
|
|
1434
1083
|
*
|
|
1435
|
-
*
|
|
1084
|
+
* - Calls `expandIds([id])` at start. When the result expands beyond the
|
|
1085
|
+
* starting id, takes the group path (union-AABB origin, per-leaf remap).
|
|
1086
|
+
* - Projects poses through the supplied `geometry: PoseProjection<TPose>`
|
|
1087
|
+
* instead of the rect-shaped `RECT_POSE_DESCRIPTOR` default.
|
|
1088
|
+
* - Runs `behaviors[]` after the raw anchor-math bounds are computed.
|
|
1089
|
+
* Behaviors return `{ pose? }` and rewrite the proposed bounds before
|
|
1090
|
+
* they're projected back into pose space.
|
|
1091
|
+
* - Runs `pointSnap[]` after the bounds→pose projection. The first non-null
|
|
1092
|
+
* `PointSnapResult` wins; this invoker back-solves a pose so the chosen
|
|
1093
|
+
* frame's world point lands on the snap target.
|
|
1436
1094
|
*
|
|
1437
|
-
* When
|
|
1095
|
+
* When the dep is absent the invoker falls back to identity defaults
|
|
1096
|
+
* (no behaviors, no snap, `ids => ids` expansion, `RECT_POSE_DESCRIPTOR`),
|
|
1097
|
+
* which matches the unrotated rect-pose path the invoker already
|
|
1098
|
+
* implemented.
|
|
1438
1099
|
*
|
|
1439
|
-
*
|
|
1440
|
-
*
|
|
1441
|
-
* { spec: { kind: 'click', target: 'selected-body' }, actionId: 'enterTextEdit' }
|
|
1442
|
-
* ```
|
|
1443
|
-
* 2. Register a `textEdit` dep sourced from the `useTextEdit` / `useSceneTextEdit`
|
|
1444
|
-
* return value, plus an `isTextNode` predicate that checks `data.kind === 'text'`
|
|
1445
|
-
* (or however the consumer identifies text nodes).
|
|
1446
|
-
* 3. The existing `hitExisting` gate in `useTextTool`'s click route becomes
|
|
1447
|
-
* redundant — remove it in Phase 14e dead-code cleanup.
|
|
1100
|
+
* @see useResize — the React hook this descriptor mirrors.
|
|
1101
|
+
* @see src/interactions/actions/resize/geometry.ts — `PoseProjection`.
|
|
1448
1102
|
*/
|
|
1449
1103
|
|
|
1450
|
-
/**
|
|
1451
|
-
* Dep for `enterTextEditAction`.
|
|
1452
|
-
*
|
|
1453
|
-
* Wrap the return value of `useTextEdit` / `useSceneTextEdit` to source this
|
|
1454
|
-
* dep. The `isTextNode` predicate is optional — when absent the action fires
|
|
1455
|
-
* unconditionally (the binding spec acts as the gate).
|
|
1456
|
-
*
|
|
1457
|
-
* @example
|
|
1458
|
-
* ```ts
|
|
1459
|
-
* const textEdit = useSceneTextEdit({ scene, container });
|
|
1460
|
-
* useDepSource('textEdit', () => ({
|
|
1461
|
-
* startEdit: textEdit.startEdit,
|
|
1462
|
-
* isTextNode: (id) => scene.get(id as NodeId)?.data?.kind === 'text',
|
|
1463
|
-
* }));
|
|
1464
|
-
* ```
|
|
1465
|
-
*/
|
|
1466
|
-
interface TextEditDep {
|
|
1467
|
-
/**
|
|
1468
|
-
* Begin editing the node with `id`. Activates the contenteditable overlay
|
|
1469
|
-
* managed by `useTextEdit` / `useSceneTextEdit`.
|
|
1470
|
-
*/
|
|
1471
|
-
startEdit(id: string, opts?: {
|
|
1472
|
-
caret?: number | 'all';
|
|
1473
|
-
}): void;
|
|
1474
|
-
/**
|
|
1475
|
-
* Optional predicate: returns `true` when the node with `id` is a text node.
|
|
1476
|
-
* When absent the action fires on any selected node (binding spec is the gate).
|
|
1477
|
-
* When present and returning `false`, the invocation is a no-op.
|
|
1478
|
-
*/
|
|
1479
|
-
isTextNode?(id: string): boolean;
|
|
1480
|
-
}
|
|
1481
1104
|
/**
|
|
1482
1105
|
* @experimental
|
|
1483
|
-
* Static descriptor for the `
|
|
1484
|
-
*
|
|
1485
|
-
* Requires dep-schema entries: `textEdit`, `selection`.
|
|
1106
|
+
* Static descriptor for the `resize` Action.
|
|
1486
1107
|
*
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1108
|
+
* Requires dep-schema entries: `selection`, `scene`.
|
|
1109
|
+
* Reads optional `resizePolicy` dep when present.
|
|
1110
|
+
* Requires `InvocationCtx.drag.affordance` with a `handle:*` kind.
|
|
1489
1111
|
*/
|
|
1490
|
-
declare const
|
|
1112
|
+
declare const resizeAction: Action & {
|
|
1491
1113
|
requires: string[];
|
|
1492
1114
|
};
|
|
1493
1115
|
|
|
1494
1116
|
/**
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1117
|
+
* `rotateAction` — ongoing Action descriptor for pointer-driven rotation.
|
|
1118
|
+
*
|
|
1119
|
+
* ## Status: REAL (unrotated-pivot path)
|
|
1120
|
+
*
|
|
1121
|
+
* Implements the core rotation math from `useRotate` for rect-shaped poses:
|
|
1122
|
+
* - `start`: captures origin poses + AABB centers; computes start pointer
|
|
1123
|
+
* angle around the union center of selected nodes.
|
|
1124
|
+
* - `onMove`: derives pointer angle delta from start, applies to each node's
|
|
1125
|
+
* origin rotation. In union-pivot mode (multi-selection) orbits each item's
|
|
1126
|
+
* center around the union center.
|
|
1127
|
+
* - `onEnd('commit')`: builds one `createTransformOp` per node (from =
|
|
1128
|
+
* pre-mutation origin pose, to = rotated pose) and commits them as a
|
|
1129
|
+
* single batch → one undo entry for the whole drag. Routes through the
|
|
1130
|
+
* optional `applyOps` dep (consumer history) when present, else
|
|
1131
|
+
* `scene.applyBatch` + `defaultCommitAdapter`.
|
|
1132
|
+
* - `onEnd('cancel')`: no scene writes (scene never mutated during drag).
|
|
1133
|
+
*
|
|
1134
|
+
* ## Constraints vs `useRotate`
|
|
1135
|
+
*
|
|
1136
|
+
* - Assumes rect-shaped poses `{ x, y, width, height, rotation? }`. Non-rect
|
|
1137
|
+
* TPose consumers need a custom action with a typed `RotateGeometry`.
|
|
1138
|
+
* - No behavior pipeline (snap, etc.). Behaviors wait for a later phase.
|
|
1139
|
+
* - No overlay rendering — deferred to Phase 7 overlay surface.
|
|
1140
|
+
* - Shift-snap (15° quantum) is NOT wired in this phase — omitted deliberately
|
|
1141
|
+
* to keep the invoker self-contained (would need to read shift from onMove ctx).
|
|
1142
|
+
* TODO: thread shift from InvocationCtx.modifiers.shift into snap logic.
|
|
1498
1143
|
*/
|
|
1499
|
-
|
|
1500
|
-
commit(a: Point2, b: Point2): void;
|
|
1501
|
-
}
|
|
1144
|
+
|
|
1502
1145
|
/**
|
|
1503
1146
|
* @experimental
|
|
1504
|
-
* Static descriptor for the `
|
|
1147
|
+
* Static descriptor for the `rotate` Action.
|
|
1148
|
+
*
|
|
1149
|
+
* Requires dep-schema entries: `selection`, `scene`.
|
|
1150
|
+
*
|
|
1151
|
+
* Implements the unrotated-pivot rotation path from `useRotate` for rect-
|
|
1152
|
+
* shaped poses. Non-rect or behavior-rich consumers should register a custom
|
|
1153
|
+
* `rotateAction` with a typed `RotateGeometry` dep.
|
|
1505
1154
|
*
|
|
1506
|
-
*
|
|
1507
|
-
* pointer, renders a live line overlay while the gesture is in flight,
|
|
1508
|
-
* and on commit calls `SliceDep.commit(a, b)`. No-ops gracefully when
|
|
1509
|
-
* the `slice` dep is absent.
|
|
1155
|
+
* @see useRotate — the React hook this descriptor mirrors for the rect case.
|
|
1510
1156
|
*/
|
|
1511
|
-
declare const
|
|
1157
|
+
declare const rotateAction: Action & {
|
|
1512
1158
|
requires: string[];
|
|
1513
1159
|
};
|
|
1514
1160
|
|
|
1515
|
-
/** Optional consumer seam: given a node and the affine `m` that a pose-transform
|
|
1516
|
-
* action applied to the node's POSE, return updated `data` with the node's
|
|
1517
|
-
* data-held geometry transformed by `m`, or `null` if this node has no
|
|
1518
|
-
* data-held geometry (the kit leaves `data` alone). */
|
|
1519
|
-
interface GeometryProjection {
|
|
1520
|
-
transform(node: {
|
|
1521
|
-
id?: string;
|
|
1522
|
-
data: unknown;
|
|
1523
|
-
pose: unknown;
|
|
1524
|
-
}, m: Mat3): unknown | null;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
/** Minimal view API the action layer consumes. May be refined later. */
|
|
1528
|
-
interface ViewApi {
|
|
1529
|
-
get(): View;
|
|
1530
|
-
set(v: View): void;
|
|
1531
|
-
/** Optional recenter callback. When wired, `viewportZoomAction`'s
|
|
1532
|
-
* `reset` branch (Cmd-0) calls this instead of resetting to identity —
|
|
1533
|
-
* letting consumers re-fit the page (or other reference bounds) into
|
|
1534
|
-
* the workspace. Receives no args; the consumer reads its own bounds
|
|
1535
|
-
* + host dims and dispatches `setView(...)`. */
|
|
1536
|
-
recenter?(): void;
|
|
1537
|
-
/** Optional canvas-local host dimensions (CSS px). When wired,
|
|
1538
|
-
* `viewportZoomAction`'s keyboard branches (Cmd+= / Cmd+-) anchor at the
|
|
1539
|
-
* host center instead of the top-left origin. Null when the host isn't
|
|
1540
|
-
* measurable (unmounted). */
|
|
1541
|
-
hostSize?(): {
|
|
1542
|
-
width: number;
|
|
1543
|
-
height: number;
|
|
1544
|
-
} | null;
|
|
1545
|
-
}
|
|
1546
1161
|
/**
|
|
1547
|
-
*
|
|
1162
|
+
* `areaSelectAction` — ongoing Action descriptor for marquee area selection.
|
|
1548
1163
|
*
|
|
1549
|
-
*
|
|
1550
|
-
* overlap over scene nodes. Consumers with custom hit-testing override this
|
|
1551
|
-
* dep entry in their own registrar.
|
|
1552
|
-
*/
|
|
1553
|
-
/**
|
|
1554
|
-
* Topmost-node-at-world-point dep, consumed by `moveAction` for
|
|
1555
|
-
* reparent-on-drop and available to any action that needs a single-best
|
|
1556
|
-
* pick. Mirrors the same hit-test plumbing `<SceneCanvas>` feeds to the
|
|
1557
|
-
* tool dispatcher; consumers with custom hit-testing override here.
|
|
1558
|
-
*
|
|
1559
|
-
* `exclude` is iterated once per call and treated as a set membership
|
|
1560
|
-
* test — the dep walks hits front-to-back and returns the first id not
|
|
1561
|
-
* in the exclude set. Pass moving-node roots + their descendants when
|
|
1562
|
-
* the caller wants to ignore the nodes it's manipulating.
|
|
1563
|
-
*/
|
|
1564
|
-
type NodeAtPointDep = (point: {
|
|
1565
|
-
x: number;
|
|
1566
|
-
y: number;
|
|
1567
|
-
}, exclude?: Iterable<NodeId>) => NodeId | null;
|
|
1568
|
-
interface AreaSelectDep {
|
|
1569
|
-
/** Return ids of all scene nodes whose AABB overlaps `bounds`. */
|
|
1570
|
-
hitTestArea(bounds: {
|
|
1571
|
-
x: number;
|
|
1572
|
-
y: number;
|
|
1573
|
-
width: number;
|
|
1574
|
-
height: number;
|
|
1575
|
-
}): NodeId[];
|
|
1576
|
-
/** Return the current selection id list. */
|
|
1577
|
-
getSelection(): NodeId[];
|
|
1578
|
-
/** Replace the current selection. */
|
|
1579
|
-
setSelection(ids: NodeId[]): void;
|
|
1580
|
-
}
|
|
1581
|
-
/**
|
|
1582
|
-
* Adapter dep for `editAnchorsAction`.
|
|
1583
|
-
*
|
|
1584
|
-
* Provides narrow read/write access to the editable polygon for a single
|
|
1585
|
-
* node. Consumers register this dep so anchor-edit actions can read/write
|
|
1586
|
-
* the polygon WITHOUT knowing whether it lives directly on the node's
|
|
1587
|
-
* pose (`pose.kind === 'polygon'`) or on `node.data.path` (the kit's
|
|
1588
|
-
* built-in pen-tool default, also WeaselDraw's shape).
|
|
1589
|
-
*
|
|
1590
|
-
* Note on live previews: in-flight edit state is surfaced through the
|
|
1591
|
-
* dispatcher's standard `OngoingHandle.previewIds/previewPose/previewData`
|
|
1592
|
-
* triple (not this dep), so chrome and preview-ghost stay in lock-step
|
|
1593
|
-
* via one source of truth.
|
|
1594
|
-
*/
|
|
1595
|
-
interface EditAnchorsDep {
|
|
1596
|
-
/** Id of the node currently being edited. Empty string means no node is
|
|
1597
|
-
* currently in edit mode — the chrome and gesture both opt out. */
|
|
1598
|
-
editingId: string;
|
|
1599
|
-
/** Enter/exit edit mode for a specific node. Pass `null` (or an empty
|
|
1600
|
-
* string) to exit. `enterPathEditAction` and `exitPathEditAction` call
|
|
1601
|
-
* this; consumers can call it directly to drive edit mode programmatically. */
|
|
1602
|
-
setEditingId(id: string | null): void;
|
|
1603
|
-
/** Returns the COMMITTED editable polygon in world coordinates, or
|
|
1604
|
-
* null if this node has no editable polygon. Does NOT consult in-
|
|
1605
|
-
* flight previews — callers that need live state read the dispatcher's
|
|
1606
|
-
* in-flight handles. */
|
|
1607
|
-
getEditablePath(id: string): unknown;
|
|
1608
|
-
/** Returns where the polygon is stored — `'pose'` when `node.pose`
|
|
1609
|
-
* IS the polygon, `'data'` when it lives on `node.data.path` with a
|
|
1610
|
-
* rect pose, or `null` when the node has no editable polygon. The
|
|
1611
|
-
* action uses this to know which preview-ghost axis to populate
|
|
1612
|
-
* (`previewPose` only / `previewData` + `previewPose` for data.path). */
|
|
1613
|
-
getStorageKind(id: string): 'pose' | 'data' | null;
|
|
1614
|
-
/** Returns the node's raw `pose` and `data` so storage-aware actions
|
|
1615
|
-
* can capture origin state at gesture-start and synthesize a matching
|
|
1616
|
-
* `previewPose` / `previewData` during `onMove`. Used by
|
|
1617
|
-
* `editAnchorsAction` for the data.path branch (rect pose + data
|
|
1618
|
-
* carrying extra fields like fill / stroke that must be preserved
|
|
1619
|
-
* through the preview). Returns null when the node is gone. */
|
|
1620
|
-
getNodeShape(id: string): {
|
|
1621
|
-
pose: unknown;
|
|
1622
|
-
data: unknown;
|
|
1623
|
-
} | null;
|
|
1624
|
-
/** Commit `worldPath` as the new value for `id`. Implementation routes
|
|
1625
|
-
* to setPose (when pose IS the polygon) or batched setPose+update
|
|
1626
|
-
* (when the polygon lives on data.path). Records one history entry
|
|
1627
|
-
* labelled `label`. */
|
|
1628
|
-
applyEdit(id: string, worldPath: unknown, label: string): void;
|
|
1629
|
-
}
|
|
1630
|
-
/**
|
|
1631
|
-
* Adapter dep for `lassoSelectAction`.
|
|
1632
|
-
*
|
|
1633
|
-
* Provides polygon-lasso hit-testing + selection read/write.
|
|
1634
|
-
* Consumers that don't implement `hitTestLasso` can omit it; the action
|
|
1635
|
-
* falls back to a bounding-box AABB test via `hitTestArea`.
|
|
1636
|
-
*/
|
|
1637
|
-
interface LassoSelectDep {
|
|
1638
|
-
/**
|
|
1639
|
-
* Hit-test against a closed polygon (vertex order CW or CCW; last→first
|
|
1640
|
-
* closing edge is implicit). Returns matching node ids.
|
|
1641
|
-
* Optional — when absent, `lassoSelectAction` falls back to AABB via
|
|
1642
|
-
* `hitTestArea`.
|
|
1643
|
-
*/
|
|
1644
|
-
hitTestLasso?(polygon: ReadonlyArray<{
|
|
1645
|
-
x: number;
|
|
1646
|
-
y: number;
|
|
1647
|
-
}>, mode: 'centers' | 'intersect' | 'enclosed'): string[];
|
|
1648
|
-
/** Return ids of nodes whose AABB overlaps the given rect (fallback). */
|
|
1649
|
-
hitTestArea(bounds: {
|
|
1650
|
-
x: number;
|
|
1651
|
-
y: number;
|
|
1652
|
-
width: number;
|
|
1653
|
-
height: number;
|
|
1654
|
-
}): string[];
|
|
1655
|
-
/** Return the current selection id list. */
|
|
1656
|
-
getSelection(): string[];
|
|
1657
|
-
/** Replace the current selection. */
|
|
1658
|
-
setSelection(ids: string[]): void;
|
|
1659
|
-
}
|
|
1660
|
-
/**
|
|
1661
|
-
* Options for the kit `image/svg+xml` content handler, threaded from
|
|
1662
|
-
* SceneCanvas's `ingestion={{ svg }}` prop.
|
|
1663
|
-
*/
|
|
1664
|
-
interface SvgIngestOptions {
|
|
1665
|
-
/** Parse dropped/pasted/picked SVG files into native scene nodes (path /
|
|
1666
|
-
* text leaves under containers mirroring the source `<g>` structure)
|
|
1667
|
-
* instead of the default single embedded-image node.
|
|
1668
|
-
*
|
|
1669
|
-
* Pass `unpackSvgFiles` from `@weasel-js/svg`:
|
|
1670
|
-
*
|
|
1671
|
-
* ```ts
|
|
1672
|
-
* import { unpackSvgFiles } from '@weasel-js/svg';
|
|
1673
|
-
* <SceneCanvas ingestion={{ svg: { unpack: unpackSvgFiles } }} />
|
|
1674
|
-
* ```
|
|
1675
|
-
*
|
|
1676
|
-
* It is injected rather than flagged on with `true` because the SVG parser
|
|
1677
|
-
* lives in `@weasel-js/svg`, which depends on this package — core importing
|
|
1678
|
-
* it back would make the two mutually dependent and unpublishable
|
|
1679
|
-
* separately. Passing the function keeps the parser out of core's bundle
|
|
1680
|
-
* for consumers who never unpack. */
|
|
1681
|
-
unpack?: SvgUnpacker;
|
|
1682
|
-
}
|
|
1683
|
-
/** Parses SVG files and inserts the resulting nodes into `ctx.scene`, as one
|
|
1684
|
-
* `applyOps` batch per file. Implemented by `unpackSvgFiles` in
|
|
1685
|
-
* `@weasel-js/svg`; see {@link SvgIngestOptions.unpack}. */
|
|
1686
|
-
type SvgUnpacker = (files: File[], ctx: IngestCtx) => Promise<void>;
|
|
1687
|
-
/**
|
|
1688
|
-
* Clipboard-paste seam consumed by the kit weasel-JSON content handler
|
|
1689
|
-
* (`IngestCtx.clipboard`). Built by `<SceneCanvas>` from its own synthesized
|
|
1690
|
-
* adapter + the `ingestion.clipboard` prop; absent when the consumer set
|
|
1691
|
-
* `ingestion.clipboard.enabled === false` or the adapter lacks `commitPaste`.
|
|
1692
|
-
* Absence makes the handler decline inert (dwarn, nothing ingested) — its
|
|
1693
|
-
* matched items were already consumed at match time and do not fall through
|
|
1694
|
-
* to other handlers.
|
|
1695
|
-
*/
|
|
1696
|
-
interface ClipboardIngestCtx {
|
|
1697
|
-
/** The hosting canvas's adapter — `commitPaste` materializes the pasted
|
|
1698
|
-
* nodes (fresh ids, offset applied); insertion still goes through ops. */
|
|
1699
|
-
adapter: InsertAdapter<{
|
|
1700
|
-
id: string;
|
|
1701
|
-
}>;
|
|
1702
|
-
/** JSON reviver for the weasel wire payload (typed arrays etc.) — from
|
|
1703
|
-
* `SceneCanvasProps.ingestion.clipboard.reviver`. */
|
|
1704
|
-
reviver?: (key: string, value: unknown) => unknown;
|
|
1705
|
-
}
|
|
1706
|
-
/**
|
|
1707
|
-
* Dep for the `ingest` action (external-content ingestion).
|
|
1708
|
-
* Sourced from `<SceneCanvas>` / `<StandardActionsRegistrar>` via
|
|
1709
|
-
* `useIngestionDepSource` — canvas rect + current view.
|
|
1710
|
-
*/
|
|
1711
|
-
interface IngestionDep {
|
|
1712
|
-
/** Visible canvas area in world coordinates. */
|
|
1713
|
-
viewportWorldRect(): {
|
|
1714
|
-
x: number;
|
|
1715
|
-
y: number;
|
|
1716
|
-
width: number;
|
|
1717
|
-
height: number;
|
|
1718
|
-
};
|
|
1719
|
-
/** Consumer file→src resolver (from SceneCanvas's `ingestion` prop).
|
|
1720
|
-
* Live accessor — read it at use time. Destructuring (or copying the
|
|
1721
|
-
* property early) snapshots the current value and won't track later
|
|
1722
|
-
* prop changes across an `await`. */
|
|
1723
|
-
resolveSrc?: (file: File) => Promise<string>;
|
|
1724
|
-
/** Kit SVG-handler options (from SceneCanvas's `ingestion` prop).
|
|
1725
|
-
* Live accessor, same caveat as `resolveSrc`. */
|
|
1726
|
-
svg?: SvgIngestOptions;
|
|
1727
|
-
/** Clipboard-paste seam for the kit weasel-JSON handler.
|
|
1728
|
-
* Live accessor, same caveat as `resolveSrc`. */
|
|
1729
|
-
clipboard?: ClipboardIngestCtx;
|
|
1730
|
-
}
|
|
1731
|
-
/**
|
|
1732
|
-
* Per-kind extra geometry passed to `InsertDep.commit`.
|
|
1733
|
-
*
|
|
1734
|
-
* Built-in tools populate a typed variant so the kit's default factory can
|
|
1735
|
-
* render the true tool params (line endpoints, polygon side count, star
|
|
1736
|
-
* geometry, pencil sample list). Consumer-defined tools may pass any
|
|
1737
|
-
* `{ kind: string; ... }` payload; the kit's factory falls back to AABB
|
|
1738
|
-
* inscription for unknown kinds.
|
|
1739
|
-
*
|
|
1740
|
-
* `bounds` is still passed alongside as a useful AABB pose hint — factories
|
|
1741
|
-
* may use it as the node's pose even when richer geometry is available.
|
|
1742
|
-
*/
|
|
1743
|
-
type InsertExtras = {
|
|
1744
|
-
kind: 'rect';
|
|
1745
|
-
} | {
|
|
1746
|
-
kind: 'ellipse';
|
|
1747
|
-
} | {
|
|
1748
|
-
kind: 'line';
|
|
1749
|
-
a: {
|
|
1750
|
-
x: number;
|
|
1751
|
-
y: number;
|
|
1752
|
-
};
|
|
1753
|
-
b: {
|
|
1754
|
-
x: number;
|
|
1755
|
-
y: number;
|
|
1756
|
-
};
|
|
1757
|
-
} | {
|
|
1758
|
-
kind: 'polygon';
|
|
1759
|
-
sides: number;
|
|
1760
|
-
rotation: number;
|
|
1761
|
-
center?: {
|
|
1762
|
-
x: number;
|
|
1763
|
-
y: number;
|
|
1764
|
-
};
|
|
1765
|
-
radius?: number;
|
|
1766
|
-
} | {
|
|
1767
|
-
kind: 'star';
|
|
1768
|
-
points: number;
|
|
1769
|
-
innerRadiusRatio: number;
|
|
1770
|
-
rotation: number;
|
|
1771
|
-
center?: {
|
|
1772
|
-
x: number;
|
|
1773
|
-
y: number;
|
|
1774
|
-
};
|
|
1775
|
-
outerRadius?: number;
|
|
1776
|
-
} | {
|
|
1777
|
-
kind: 'pencil';
|
|
1778
|
-
samples: ReadonlyArray<{
|
|
1779
|
-
x: number;
|
|
1780
|
-
y: number;
|
|
1781
|
-
}>;
|
|
1782
|
-
} | {
|
|
1783
|
-
kind: 'text';
|
|
1784
|
-
text?: string;
|
|
1785
|
-
} | {
|
|
1786
|
-
kind: string;
|
|
1787
|
-
[extra: string]: unknown;
|
|
1788
|
-
};
|
|
1789
|
-
/**
|
|
1790
|
-
* Adapter dep for `insertAction`.
|
|
1791
|
-
*
|
|
1792
|
-
* Provided by `<SceneCanvas>` / `<StandardActionsRegistrar>`. The `extras`
|
|
1793
|
-
* carry the active tool's kind + per-kind geometry. Callers
|
|
1794
|
-
* that need typed data must supply a richer `insert` dep.
|
|
1795
|
-
*/
|
|
1796
|
-
interface InsertDep {
|
|
1797
|
-
/**
|
|
1798
|
-
* Materialise a new node from the given drag-rect bounds and typed
|
|
1799
|
-
* per-kind extras. Returns the new node's id, or `null` if the consumer
|
|
1800
|
-
* rejected the insert (e.g. sub-threshold bounds, unknown kind).
|
|
1801
|
-
*/
|
|
1802
|
-
commit(bounds: {
|
|
1803
|
-
x: number;
|
|
1804
|
-
y: number;
|
|
1805
|
-
width: number;
|
|
1806
|
-
height: number;
|
|
1807
|
-
}, extras: InsertExtras): NodeId | null;
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* Adapter dep for `resizeAction`.
|
|
1811
|
-
*
|
|
1812
|
-
* Carries the four behavior-shaping options the legacy `useResize` hook
|
|
1813
|
-
* exposed through `UseResizeOptions`: bounds-frame behaviors (e.g.
|
|
1814
|
-
* `lockAspectWithModifier`), world-space anchor-point snap behaviors (e.g.
|
|
1815
|
-
* `pointSnapToGrid`), group-expansion (`expandIds`), and pose↔bounds
|
|
1816
|
-
* projection (`geometry`).
|
|
1817
|
-
*
|
|
1818
|
-
* Optional in `DepSchema`: when absent, `resizeAction` falls back to
|
|
1819
|
-
* identity defaults (no behaviors, identity expandIds, `RECT_POSE_DESCRIPTOR`
|
|
1820
|
-
* geometry). Consumers wire the dep via `useDepSource('resizePolicy', ...)`
|
|
1821
|
-
* from any descendant of `<DepRegistryProvider>` / `<SceneCanvas>`.
|
|
1822
|
-
*
|
|
1823
|
-
* The generic is erased to `unknown` at the schema entry; consumers cast at
|
|
1824
|
-
* the call site (mirrors the `scene` entry's convention).
|
|
1825
|
-
*/
|
|
1826
|
-
interface ResizePolicy<TPose> {
|
|
1827
|
-
/** Bounds-frame constraints. Constrained to `TPose extends ResizePose` since
|
|
1828
|
-
* constraints read/write `{x,y,width,height}`. For non-rect TPose pass `[]`. */
|
|
1829
|
-
constraints: TPose extends ResizePose ? BoundsConstraint<TPose>[] : never[];
|
|
1830
|
-
/** World-space anchor-point snap behaviors. Same TPose constraint as
|
|
1831
|
-
* `constraints`. */
|
|
1832
|
-
pointSnap: TPose extends ResizePose ? PointSnapBehavior<TPose>[] : never[];
|
|
1833
|
-
/** Group-expansion at gesture start. Identity (`ids => ids`) when group
|
|
1834
|
-
* resize isn't wanted. */
|
|
1835
|
-
expandIds: (ids: string[]) => string[];
|
|
1836
|
-
/** Projection from `TPose` to bounds and back. Use `RECT_POSE_DESCRIPTOR`
|
|
1837
|
-
* for plain rect poses. */
|
|
1838
|
-
projection: PoseProjection<TPose>;
|
|
1839
|
-
}
|
|
1840
|
-
/**
|
|
1841
|
-
* Layout-strategy lookup by container id, consumed by `moveAction` to run
|
|
1842
|
-
* the drag-time reflow pass. Sourced by `<SceneCanvas>` from its `layouts`
|
|
1843
|
-
* prop. Optional: `getLayout` returns null for any container when no layout
|
|
1844
|
-
* is configured, so the reflow pass is a no-op then.
|
|
1845
|
-
*/
|
|
1846
|
-
interface LayoutDep {
|
|
1847
|
-
getLayout(containerId: string): LayoutStrategy<unknown> | null;
|
|
1848
|
-
}
|
|
1849
|
-
interface DepSchema {
|
|
1850
|
-
/** Kit selection state — ids of currently selected nodes. */
|
|
1851
|
-
selection: SelectionApi;
|
|
1852
|
-
/** Current viewport — camera position + scale. */
|
|
1853
|
-
view: ViewApi;
|
|
1854
|
-
/**
|
|
1855
|
-
* Scene tree — structural reads + undoable mutations.
|
|
1856
|
-
*
|
|
1857
|
-
* The entry uses the fully-erased form `Scene<unknown, string, unknown>`
|
|
1858
|
-
* because `DepSchema` must be concrete. Actions that need a typed scene
|
|
1859
|
-
* should cast: `deps.scene as Scene<MyData, MyLayer, MyPose>`.
|
|
1860
|
-
*/
|
|
1861
|
-
scene: Scene<unknown, string, unknown>;
|
|
1862
|
-
/** Undo/redo history bound to the current scene. */
|
|
1863
|
-
history: History;
|
|
1864
|
-
/**
|
|
1865
|
-
* Canvas pointer position in world space.
|
|
1866
|
-
*
|
|
1867
|
-
* Exposes `pointerRef` (mutable live ref) and `getDropPoint()` thunk.
|
|
1868
|
-
* Marked `@experimental` in the source.
|
|
1869
|
-
*/
|
|
1870
|
-
pointer: PointerContextValue;
|
|
1871
|
-
/** Currently active tool id + hotkey-hold stack. */
|
|
1872
|
-
activeTool: ActiveToolContextValue;
|
|
1873
|
-
/**
|
|
1874
|
-
* Area-select dep — AABB hit-test + selection read/write.
|
|
1875
|
-
*
|
|
1876
|
-
* Sourced from `<SceneCanvas>` via AABB overlap over all scene
|
|
1877
|
-
* nodes. Override per-consumer for custom hit-testing (e.g. contain-mode,
|
|
1878
|
-
* lock-aware filtering).
|
|
1879
|
-
*/
|
|
1880
|
-
areaSelect: AreaSelectDep;
|
|
1881
|
-
/**
|
|
1882
|
-
* Topmost node at a world-space point. Sourced by `<SceneCanvas>` from
|
|
1883
|
-
* the same picker that feeds the tool dispatcher's `getNodeAtPoint`.
|
|
1884
|
-
* Optional: actions that read this (e.g. `moveAction` reparent-on-drop)
|
|
1885
|
-
* fall back to a no-op when the dep isn't registered.
|
|
1886
|
-
*/
|
|
1887
|
-
nodeAtPoint?: NodeAtPointDep;
|
|
1888
|
-
/**
|
|
1889
|
-
* Insert dep — node factory for drag-to-insert.
|
|
1890
|
-
*
|
|
1891
|
-
* Sourced from `<SceneCanvas>`. The `kind` param comes from
|
|
1892
|
-
* the active binding's `opts.params.kind`. Override per-consumer to
|
|
1893
|
-
* provide a typed node factory (e.g. with custom data payloads).
|
|
1894
|
-
*/
|
|
1895
|
-
insert: InsertDep;
|
|
1896
|
-
/**
|
|
1897
|
-
* Lasso-select dep — polygon hit-test + selection read/write.
|
|
1898
|
-
*
|
|
1899
|
-
* Sourced from `<SceneCanvas>` / `<StandardActionsRegistrar>`.
|
|
1900
|
-
* Falls back to AABB hit-test when `hitTestLasso` is absent.
|
|
1901
|
-
*/
|
|
1902
|
-
lassoSelect: LassoSelectDep;
|
|
1903
|
-
/**
|
|
1904
|
-
* Edit-anchors dep — narrow read/write of one polygon's path pose.
|
|
1905
|
-
*
|
|
1906
|
-
* Sourced from consumer. Wraps `getPose`/`setPose`/`applyOps`
|
|
1907
|
-
* for the currently-being-edited polygon node.
|
|
1908
|
-
*
|
|
1909
|
-
* The `editAnchorsAction` requires this dep to be registered when anchor
|
|
1910
|
-
* editing is active. If absent, `start` returns an empty handle (no-op).
|
|
1911
|
-
*/
|
|
1912
|
-
editAnchors: EditAnchorsDep;
|
|
1913
|
-
/**
|
|
1914
|
-
* Text-edit dep — activates the in-place text editing overlay.
|
|
1915
|
-
*
|
|
1916
|
-
* Sourced from consumer via `useTextEdit` / `useSceneTextEdit`.
|
|
1917
|
-
* The `enterTextEditAction` requires this dep to be registered by the text
|
|
1918
|
-
* tool when text editing is available.
|
|
1919
|
-
*
|
|
1920
|
-
* The optional `isTextNode` predicate guards against entering edit mode on
|
|
1921
|
-
* non-text nodes when no per-kind binding filter is available (Phase 14e
|
|
1922
|
-
* follow-up will add per-kind classification to `classifyTarget`).
|
|
1923
|
-
*/
|
|
1924
|
-
textEdit: TextEditDep;
|
|
1925
|
-
/**
|
|
1926
|
-
* Resize-policy dep — bounds constraints, point-snap behaviors,
|
|
1927
|
-
* group expansion, and pose↔bounds projection for `resizeAction`.
|
|
1928
|
-
*
|
|
1929
|
-
* Optional: when omitted, `resizeAction` falls back to identity defaults
|
|
1930
|
-
* (no constraints, no snap, identity expandIds, `RECT_POSE_DESCRIPTOR`).
|
|
1931
|
-
* Consumers wire via `useDepSource('resizePolicy', ...)` or the
|
|
1932
|
-
* `useResizePolicy` helper.
|
|
1933
|
-
*/
|
|
1934
|
-
resizePolicy?: ResizePolicy<unknown>;
|
|
1935
|
-
/**
|
|
1936
|
-
* Booleans adapter — read selection ids, fetch world-space `Path`s,
|
|
1937
|
-
* compare z-order, and mint result nodes for Pathfinder ops.
|
|
1938
|
-
*
|
|
1939
|
-
* Consumers wire via `useBooleansAdapter(adapter)` (a thin wrapper
|
|
1940
|
-
* around `useDepSource('booleansAdapter', ...)`). The descriptor's
|
|
1941
|
-
* `enabled` predicate reads `deps.selection` for the count check; the
|
|
1942
|
-
* invoker reads `deps.booleansAdapter` to execute the op.
|
|
1943
|
-
*/
|
|
1944
|
-
booleansAdapter?: BooleansAdapter;
|
|
1945
|
-
/**
|
|
1946
|
-
* Gesture dispatcher control surface — exposes `cancelAll(reason)` so
|
|
1947
|
-
* actions that need to abort an in-flight handle (Escape cancels a
|
|
1948
|
-
* drag, etc.) can do so. Sourced by `<SceneCanvas>` from the
|
|
1949
|
-
* dispatcher instance it already owns.
|
|
1950
|
-
*/
|
|
1951
|
-
dispatcher?: {
|
|
1952
|
-
cancelAll(reason: 'commit' | 'cancel'): void;
|
|
1953
|
-
};
|
|
1954
|
-
/**
|
|
1955
|
-
* Layout-strategy lookup. Sourced by `<SceneCanvas>` from `layouts`.
|
|
1956
|
-
* Optional: absent (or all-null) → `moveAction` skips reflow.
|
|
1957
|
-
*/
|
|
1958
|
-
layout?: LayoutDep;
|
|
1959
|
-
/**
|
|
1960
|
-
* Slice dep — consumer-supplied commit for the Slice action.
|
|
1961
|
-
*
|
|
1962
|
-
* Receives the finite slice segment in world coordinates; the consumer
|
|
1963
|
-
* scans the scene, splits crossed paths via `splitPathByLine`, and
|
|
1964
|
-
* applies the result as one undoable batch.
|
|
1965
|
-
*
|
|
1966
|
-
* Optional: when absent, `sliceAction` is a no-op.
|
|
1967
|
-
*/
|
|
1968
|
-
slice?: SliceDep;
|
|
1969
|
-
/**
|
|
1970
|
-
* Optional consumer commit hook. When present, `moveAction` (and other
|
|
1971
|
-
* default actions) submit their committed ops through it instead of
|
|
1972
|
-
* `scene.applyBatch`, so apps with their own history integration
|
|
1973
|
-
* (checkpoint + push entry) capture the gesture as one undo entry.
|
|
1974
|
-
* When absent, commits fall back to `scene.applyBatch`.
|
|
1975
|
-
*/
|
|
1976
|
-
applyOps?: (ops: Op[], label: string) => void;
|
|
1977
|
-
/** Optional pose-composition strategy for hierarchical (local-pose) scenes.
|
|
1978
|
-
* When absent, defaults to IDENTITY (absolute-pose: nodes store world
|
|
1979
|
-
* coords). Local-pose consumers supply { compose: composeRectPose,
|
|
1980
|
-
* decompose: decomposeRectPose } (or their pose shape's equivalent). */
|
|
1981
|
-
poseComposition?: PoseComposition<unknown>;
|
|
1982
|
-
/**
|
|
1983
|
-
* Ingestion dep — canvas viewport rect + consumer file→src resolver.
|
|
1984
|
-
*
|
|
1985
|
-
* Sourced from `<SceneCanvas>` / `<StandardActionsRegistrar>` via
|
|
1986
|
-
* `useIngestionDepSource`. Feeds `ingestAction` with the world-space
|
|
1987
|
-
* viewport rect for paste-placement and image fit-clamping, and forwards
|
|
1988
|
-
* the consumer's optional `resolveSrc` seam.
|
|
1989
|
-
*
|
|
1990
|
-
* Optional: when absent, the `ingest` action no-ops (there is no
|
|
1991
|
-
* placement geometry to work with).
|
|
1992
|
-
*/
|
|
1993
|
-
ingestion?: IngestionDep;
|
|
1994
|
-
/**
|
|
1995
|
-
* Optional consumer seam for the eager-sync layer: lets pose-transform
|
|
1996
|
-
* actions (resize/move/nudge/flip — NOT rotate) ALSO rewrite a node's
|
|
1997
|
-
* data-held geometry. Given a node and the affine `m` applied to its pose,
|
|
1998
|
-
* `transform(node, m)` returns updated `data` (geometry mapped by `m`) or
|
|
1999
|
-
* `null` for nodes with no data-held geometry.
|
|
2000
|
-
*
|
|
2001
|
-
* Strictly opt-in: when absent (or when `transform` returns null), the kit
|
|
2002
|
-
* emits only the pose op and leaves `data` untouched. apps/draw wires this
|
|
2003
|
-
* to mirror `data.path` through `transformPath`. Rotate intentionally never
|
|
2004
|
-
* consults this seam (rotation lives on the pose, baked at render).
|
|
2005
|
-
*/
|
|
2006
|
-
geometryProjection?: GeometryProjection;
|
|
2007
|
-
}
|
|
2008
|
-
/**
|
|
2009
|
-
* Every dep name the registry knows about — derived from {@link DepSchema} so
|
|
2010
|
-
* the two can't drift.
|
|
2011
|
-
*
|
|
2012
|
-
* Declared here rather than beside the registry so that this `keyof` reference
|
|
2013
|
-
* resolves to the exported `DepSchema` declaration; from another module it
|
|
2014
|
-
* resolves to that module's import alias, which the API docs can't link.
|
|
2015
|
-
*/
|
|
2016
|
-
type DepName = keyof DepSchema;
|
|
2017
|
-
|
|
2018
|
-
interface DepRegistry {
|
|
2019
|
-
register<K extends DepName>(name: K, source: () => DepSchema[K]): () => void;
|
|
2020
|
-
get<K extends DepName>(name: K): DepSchema[K] | undefined;
|
|
2021
|
-
}
|
|
2022
|
-
declare function DepRegistryProvider({ children }: {
|
|
2023
|
-
children: ReactNode;
|
|
2024
|
-
}): react_jsx_runtime.JSX.Element;
|
|
2025
|
-
declare function useDepRegistry(): DepRegistry;
|
|
2026
|
-
/**
|
|
2027
|
-
* Like `useDepRegistry`, but returns `null` when no `<DepRegistryProvider>` is
|
|
2028
|
-
* in scope instead of throwing. Used by `useStandardActions` to preserve its
|
|
2029
|
-
* silent-no-op contract when neither provider is present.
|
|
2030
|
-
*/
|
|
2031
|
-
declare function useOptionalDepRegistry(): DepRegistry | null;
|
|
2032
|
-
/** Register a live source for `name` for the lifetime of the calling
|
|
2033
|
-
* component. The `source` thunk is called at dispatch time and should
|
|
2034
|
-
* return the latest value. */
|
|
2035
|
-
declare function useDepSource<K extends DepName>(name: K, source: () => DepSchema[K]): void;
|
|
2036
|
-
|
|
2037
|
-
/**
|
|
2038
|
-
* Pure matcher primitives live in `@weasel-js/gestures`. This file
|
|
2039
|
-
* re-exports them for kit-internal consumers and layers the actions-layer
|
|
2040
|
-
* binding-scope / matchBest logic on top.
|
|
2041
|
-
*/
|
|
2042
|
-
|
|
2043
|
-
type BindingScope = 'ambient' | 'active' | 'hotkey';
|
|
2044
|
-
interface ScopedBinding {
|
|
2045
|
-
binding: GestureBinding;
|
|
2046
|
-
scope: BindingScope;
|
|
2047
|
-
/** Tool id that owns this binding — `'&'`-channel phase atoms resolve
|
|
2048
|
-
* to this. `null` for ambient bindings that came from a registered
|
|
2049
|
-
* Action with no owning tool. */
|
|
2050
|
-
ownerToolId: string | null;
|
|
2051
|
-
}
|
|
2052
|
-
interface MatchResult {
|
|
2053
|
-
binding: GestureBinding;
|
|
2054
|
-
scope: BindingScope;
|
|
2055
|
-
/** Tool id that owns the binding — propagated from `ScopedBinding`
|
|
2056
|
-
* so the dispatcher can record it as the handle owner. */
|
|
2057
|
-
ownerToolId: string | null;
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
/**
|
|
2061
|
-
* Dispatcher orchestrator — pure module, no React, no DOM.
|
|
2062
|
-
*
|
|
2063
|
-
* Assembles `ScopedBinding[]` from the actions registry, active tool, and
|
|
2064
|
-
* hotkey stack; matches input events via `matchSorted`; gates each candidate
|
|
2065
|
-
* on `enabled()`; then invokes `immediate` or `ongoing` invokers and tracks
|
|
2066
|
-
* in-flight handles.
|
|
2067
|
-
*
|
|
2068
|
-
* ## Specificity-ordered fall-through
|
|
2069
|
-
* `matchSorted` returns every matching binding in precedence order
|
|
2070
|
-
* (hotkey > active > ambient, first-declared within scope). The dispatcher
|
|
2071
|
-
* walks that list and fires the first action whose `enabled()` returns
|
|
2072
|
-
* `true`. If every candidate's `enabled()` returns a disabled reason, the
|
|
2073
|
-
* event is unhandled. This mirrors CSS-style specificity matching with a
|
|
2074
|
-
* `:not(:disabled)` filter, and lets a tool declare a high-specificity
|
|
2075
|
-
* binding (e.g. drag-on-empty → areaSelect) that gracefully falls through
|
|
2076
|
-
* to a lower-specificity ambient binding (e.g. drag → viewport.dragPan)
|
|
2077
|
-
* when its required deps aren't wired.
|
|
2078
|
-
*
|
|
2079
|
-
* ## gestureId scheme
|
|
2080
|
-
* - `key-held` ongoing actions: `key-held-<key>` (e.g. `key-held- ` for Space).
|
|
2081
|
-
* Chosen because key-held gestures are identified by the held key alone.
|
|
2082
|
-
* - `pointerdown` / drag ongoing actions: `pointer-<pointerId>`, where
|
|
2083
|
-
* `pointerId` defaults to `'mouse'` (Phase 3 has no real pointer IDs; the
|
|
2084
|
-
* React seam in Task 4 will supply the actual DOM pointerId).
|
|
2085
|
-
* - `multitouch` ongoing actions: `multitouch-<fingers>`.
|
|
2086
|
-
* - Fallback for any other kind that triggers an ongoing invoker: `ongoing-<kind>`.
|
|
2087
|
-
*
|
|
2088
|
-
* ## Action-lookup miss behavior
|
|
2089
|
-
* When `matchBest` resolves a binding whose `actionId` has no entry in
|
|
2090
|
-
* `ctx.actions.list()`, the dispatcher emits `console.warn` and returns
|
|
2091
|
-
* `'unhandled'`. The user's input gesture falls through as if unmatched.
|
|
2092
|
-
* This preserves input flow (nothing is swallowed silently) while flagging
|
|
2093
|
-
* the misconfiguration at dev time.
|
|
2094
|
-
*/
|
|
2095
|
-
|
|
2096
|
-
interface DispatcherContext {
|
|
2097
|
-
/** All registered actions; the dispatcher walks `.defaultBinding` for ambient bindings. */
|
|
2098
|
-
actions: ActionsRegistry;
|
|
2099
|
-
/** Dep sources keyed by name. */
|
|
2100
|
-
depRegistry: DepRegistry;
|
|
2101
|
-
/** Active tool's id (from ActiveToolContext). */
|
|
2102
|
-
activeToolId: string;
|
|
2103
|
-
/** Held-hotkey stack, top of stack last. */
|
|
2104
|
-
hotkeyStack: readonly string[];
|
|
2105
|
-
/** Lookup for tool definitions. */
|
|
2106
|
-
toolsById: ReadonlyMap<string, Tool>;
|
|
2107
|
-
/** Platform flag for `mod` shorthand resolution. */
|
|
2108
|
-
isMac: boolean;
|
|
2109
|
-
/**
|
|
2110
|
-
* Thunk returning a fresh `RuleCtx` for the current frame. When
|
|
2111
|
-
* supplied, the dispatcher filters matched candidates by their
|
|
2112
|
-
* declared `Action.eligible` rule (omitted => always eligible).
|
|
2113
|
-
* When omitted, no eligibility filtering is applied — preserves
|
|
2114
|
-
* backward compatibility for callers (tests, legacy harnesses) that
|
|
2115
|
-
* don't wire up chrome-caps state.
|
|
2116
|
-
*/
|
|
2117
|
-
getRuleCtx?: () => RuleCtx;
|
|
2118
|
-
}
|
|
2119
|
-
/**
|
|
2120
|
-
* Handle returned by `Dispatcher.beginUiOngoing()` for driving an
|
|
2121
|
-
* ongoing invoker from a UI control (color picker, slider).
|
|
2122
|
-
*
|
|
2123
|
-
* - `update(params)` rebuilds an `InvocationCtx` with the new params and
|
|
2124
|
-
* calls the handle's `onMove`. Safe to call many times.
|
|
2125
|
-
* - `end(reason)` calls `onEnd(ctx, reason)` once and removes the handle
|
|
2126
|
-
* from the in-flight map. Idempotent — further calls are no-ops.
|
|
2127
|
-
*/
|
|
2128
|
-
interface UiOngoingControl {
|
|
2129
|
-
readonly gestureId: string;
|
|
2130
|
-
update(params?: Record<string, unknown>): void;
|
|
2131
|
-
end(reason: 'commit' | 'cancel'): void;
|
|
2132
|
-
}
|
|
2133
|
-
/**
|
|
2134
|
-
* Successful `Dispatcher.resolveOnly` prediction: the binding + action that
|
|
2135
|
-
* would fire if `event` were dispatched for real. `action` is the resolved
|
|
2136
|
-
* descriptor so callers (the hover-cursor pump) can read metadata like
|
|
2137
|
-
* `Action.cursor` without a second registry lookup.
|
|
2138
|
-
*/
|
|
2139
|
-
interface ResolveOnlyResult {
|
|
2140
|
-
actionId: string;
|
|
2141
|
-
action: Action;
|
|
2142
|
-
scope: BindingScope;
|
|
2143
|
-
/** Tool id owning the winning binding; `null` for ambient action bindings. */
|
|
2144
|
-
ownerToolId: string | null;
|
|
2145
|
-
}
|
|
2146
|
-
interface Dispatcher {
|
|
2147
|
-
/**
|
|
2148
|
-
* Route an input event through the binding pipeline. Returns `'handled'`
|
|
2149
|
-
* when a binding matched and the action invoked successfully (whether it
|
|
2150
|
-
* returned ops or not). Returns `'unhandled'` when no binding matched or
|
|
2151
|
-
* the matched action's `enabled()` returned a disabled reason.
|
|
2152
|
-
*/
|
|
2153
|
-
handleInput(event: InputEvent, ctx: DispatcherContext): 'handled' | 'unhandled';
|
|
2154
|
-
/**
|
|
2155
|
-
* Predict which action `event` would route to WITHOUT invoking it. Replays
|
|
2156
|
-
* the same walk as `handleInput` — scope assembly, specificity-sorted
|
|
2157
|
-
* match, eligibility filter, per-candidate `enabled()` gate — and returns
|
|
2158
|
-
* the first candidate that would fire, or `null` when the event would go
|
|
2159
|
-
* unhandled. Pure query: no invoker runs, no in-flight state changes, no
|
|
2160
|
-
* trace-log entry.
|
|
2161
|
-
*
|
|
2162
|
-
* Known divergence from a real dispatch: an ongoing invoker that matches
|
|
2163
|
-
* but returns an empty handle at `start()` (runtime bail) makes the real
|
|
2164
|
-
* dispatch fall through to the next candidate; prediction cannot see that
|
|
2165
|
-
* and reports the bailing action. Keep `enabled()` accurate on actions
|
|
2166
|
-
* that rely on prediction (hover cursors).
|
|
2167
|
-
*/
|
|
2168
|
-
resolveOnly(event: InputEvent, ctx: DispatcherContext): ResolveOnlyResult | null;
|
|
2169
|
-
/**
|
|
2170
|
-
* Synthesize an end-of-gesture for every in-flight ongoing handle.
|
|
2171
|
-
* Used by tool-switch cancellation (Q2 decision).
|
|
2172
|
-
*/
|
|
2173
|
-
cancelAll(reason: 'commit' | 'cancel'): void;
|
|
2174
|
-
/**
|
|
2175
|
-
* Read-only view of currently in-flight ongoing handles, keyed by gestureId.
|
|
2176
|
-
* For debug/testing.
|
|
2177
|
-
*/
|
|
2178
|
-
inFlight(): ReadonlyMap<string, OngoingHandle>;
|
|
2179
|
-
/**
|
|
2180
|
-
* Read-only iterator over currently in-flight `OngoingHandle` instances.
|
|
2181
|
-
*
|
|
2182
|
-
* Surface for the canvas's preview-ghost layer (`usePreviewGhostLayer`)
|
|
2183
|
-
* to walk each handle's `previewIds()` / `previewPose(id)` and render
|
|
2184
|
-
* dispatcher-driven gesture previews. Read-only by design:
|
|
2185
|
-
* external consumers must not mutate the in-flight map.
|
|
2186
|
-
*/
|
|
2187
|
-
getInFlightHandles(): Iterable<OngoingHandle>;
|
|
2188
|
-
/**
|
|
2189
|
-
* Subscribe to in-flight state changes. The callback fires after every
|
|
2190
|
-
* mutation that affects what the preview-ghost / dispatcher-overlay
|
|
2191
|
-
* layers read — handle start, every `onMove` pump, end, cancel,
|
|
2192
|
-
* cancel-all. Consumers re-read `getInFlightHandles()` and re-render.
|
|
2193
|
-
*
|
|
2194
|
-
* Returns an unsubscribe function.
|
|
2195
|
-
*/
|
|
2196
|
-
subscribe(fn: () => void): () => void;
|
|
2197
|
-
/**
|
|
2198
|
-
* Snapshot of the currently active action, for surfaces (chrome-caps
|
|
2199
|
-
* visibility rules, debug HUDs) that need to react to "what action
|
|
2200
|
-
* is in flight right now."
|
|
2201
|
-
*
|
|
2202
|
-
* - `kind` — the `OngoingHandle.kind` reported by the in-flight
|
|
2203
|
-
* handle (e.g. `'marquee'`, `'move'`). `null` when no action is
|
|
2204
|
-
* in flight OR the handle didn't declare a kind.
|
|
2205
|
-
* - `id` — the dispatcher's internal `gestureId` (`pointer-mouse`,
|
|
2206
|
-
* `key-held-Space`, …) — the pointer/key channel the action rode
|
|
2207
|
-
* in on. `null` when no action is in flight.
|
|
2208
|
-
*
|
|
2209
|
-
* When multiple handles are in flight simultaneously (e.g. a key-held
|
|
2210
|
-
* action overlapping a pointer action), the most-recently-started
|
|
2211
|
-
* handle wins. This matches user intent: the latest interaction is
|
|
2212
|
-
* the one consumers care about.
|
|
2213
|
-
*/
|
|
2214
|
-
getActiveAction(): {
|
|
2215
|
-
kind: string | null;
|
|
2216
|
-
id: string | null;
|
|
2217
|
-
};
|
|
2218
|
-
/**
|
|
2219
|
-
* Start an ongoing action driven by UI (not a gesture). Builds an
|
|
2220
|
-
* `InvocationCtx` with the given `deps` and `params`, calls
|
|
2221
|
-
* `action.invoker.start(ctx, { params })`, and registers the returned
|
|
2222
|
-
* handle in the in-flight map so `getInFlightHandles()` reports it —
|
|
2223
|
-
* enabling preview rendering via `SceneCanvas`.
|
|
2224
|
-
*
|
|
2225
|
-
* Returns `null` if `actionId` is unknown, the action's invoker is not
|
|
2226
|
-
* ongoing, or `start` returned an empty handle.
|
|
2227
|
-
*
|
|
2228
|
-
* If a UI-driven handle for the same `actionId` is already in flight,
|
|
2229
|
-
* it is committed (`end('commit')`) before the new one starts.
|
|
2230
|
-
*/
|
|
2231
|
-
beginUiOngoing(actionId: string, deps: ActionDeps, params?: Record<string, unknown>): UiOngoingControl | null;
|
|
2232
|
-
}
|
|
2233
|
-
declare function createDispatcher(opts?: {
|
|
2234
|
-
getAction?: (id: string) => Action | undefined;
|
|
2235
|
-
}): Dispatcher;
|
|
2236
|
-
|
|
2237
|
-
/**
|
|
2238
|
-
* @experimental
|
|
2239
|
-
* A single entry in `Action.defaultBinding[]`. Either a bare `GestureSpec`
|
|
2240
|
-
* (no per-binding opts) or an object form that pairs a spec with
|
|
2241
|
-
* `BindingOpts` for parametric actions (e.g. `{ params: { axis: 'x' } }`).
|
|
2242
|
-
* Use the object form when two bindings for the same action differ only in
|
|
2243
|
-
* a runtime parameter — the dispatcher extracts `opts.params` and passes
|
|
2244
|
-
* them to `ImmediateInvoker.run` as its second argument.
|
|
2245
|
-
*/
|
|
2246
|
-
type BoundGesture = GestureSpec | {
|
|
2247
|
-
spec: GestureSpec;
|
|
2248
|
-
opts: BindingOpts;
|
|
2249
|
-
};
|
|
2250
|
-
/**
|
|
2251
|
-
* @experimental
|
|
2252
|
-
* Single registered action. v1: one binding per action.
|
|
2253
|
-
*/
|
|
2254
|
-
interface Action {
|
|
2255
|
-
id: string;
|
|
2256
|
-
label: string;
|
|
2257
|
-
/** The gesture-spec form of the binding, read by the gesture dispatcher.
|
|
2258
|
-
* May be a single `GestureSpec`, a bare `GestureSpec[]` (any-of semantics),
|
|
2259
|
-
* or a `BoundGesture[]` where each entry is either a bare `GestureSpec` or
|
|
2260
|
-
* `{ spec, opts }` — use the object form for parametric actions where two
|
|
2261
|
-
* bindings for the same action differ only by `opts.params` (e.g. `flip`
|
|
2262
|
-
* with `axis: 'x'` vs `'y'`). The dispatcher extracts `opts.params` and
|
|
2263
|
-
* passes them to `ImmediateInvoker.run` as its second argument. */
|
|
2264
|
-
defaultBinding?: GestureSpec | BoundGesture[];
|
|
2265
|
-
/** Names of the deps this action's invoker reads (keys of `DepSchema`).
|
|
2266
|
-
* The dispatcher (and `trigger`, when `requires` is present) resolves
|
|
2267
|
-
* each name against the `DepRegistry` at invocation time and passes the
|
|
2268
|
-
* resulting bag to the invoker. Dev builds warn when the invoker reads a
|
|
2269
|
-
* dep it didn't declare here — see `buildDepsFromRequires`. */
|
|
2270
|
-
requires?: readonly DepName[];
|
|
2271
|
-
/** Inline-SVG icon for palette / toolbar surfaces. Mirrors
|
|
2272
|
-
* `ToolPresentation.icon` so a generic `<ActionBar>` can render from
|
|
2273
|
-
* action metadata the same way `<ToolPalette>` renders from tool
|
|
2274
|
-
* metadata. May be a static `ReactNode` or a function (rare; useful
|
|
2275
|
-
* for state-aware icons like a "lock" toggle). */
|
|
2276
|
-
icon?: ReactNode | (() => ReactNode);
|
|
2277
|
-
/** Grouping key for palette/menu surfaces. Free-form string; the kit
|
|
2278
|
-
* ships defaults for `'align'` (six edges/centers), `'distribute'`
|
|
2279
|
-
* (two axes), and recommends `'pathfinder'` for boolean ops. */
|
|
2280
|
-
group?: string;
|
|
2281
|
-
/** Display override for the keyboard shortcut. When omitted, palette
|
|
2282
|
-
* surfaces derive a label from `defaultBinding` via their own
|
|
2283
|
-
* formatter. */
|
|
2284
|
-
shortcut?: string;
|
|
2285
|
-
/** Pluggable invocation strategy. The gesture dispatcher routes matched
|
|
2286
|
-
* bindings through `invoker.start` / `invoker.run` depending on timing.
|
|
2287
|
-
* All kit-standard descriptors ship one; consumer-supplied actions
|
|
2288
|
-
* without an invoker can still register but won't be triggered. */
|
|
2289
|
-
invoker?: Invoker;
|
|
2290
|
-
/** When set to `'hotkey'`, this action's `defaultBinding` rides the hotkey
|
|
2291
|
-
* `BindingScope` instead of the ambient scope — meaning it beats any
|
|
2292
|
-
* active-tool binding on the same input shape. Use for tool-switch
|
|
2293
|
-
* shortcuts and global held-key triggers. Default: ambient. */
|
|
2294
|
-
scope?: 'hotkey';
|
|
2295
|
-
/**
|
|
2296
|
-
* @experimental
|
|
2297
|
-
* Optional predicate the command palette consults when rendering. Return
|
|
2298
|
-
* `true` when the action is currently triggerable. Return a reason string
|
|
2299
|
-
* (e.g. `'Selection required'`) when disabled — the palette greys out
|
|
2300
|
-
* the row, skips it in keyboard nav, ignores clicks, and shows the
|
|
2301
|
-
* reason next to the label. Keystroke dispatch (the registered binding)
|
|
2302
|
-
* is unaffected; the action's own `run` should self-guard.
|
|
2303
|
-
*
|
|
2304
|
-
* **Contract:** must be pure (no side effects), fast (< 4ms in dev), and
|
|
2305
|
-
* must not throw. If a call throws or exceeds the budget in dev mode,
|
|
2306
|
-
* `evaluateEnabled` logs a one-time warning per action id; throws are
|
|
2307
|
-
* caught and treated as disabled with reason `'(predicate threw)'`.
|
|
2308
|
-
*
|
|
2309
|
-
* Snapshot-on-open semantics: the palette evaluates `enabled` once when
|
|
2310
|
-
* opened and does NOT re-evaluate on selection changes while open. Live
|
|
2311
|
-
* reactive updates are deferred — palette is short-lived.
|
|
2312
|
-
*
|
|
2313
|
-
* The reason set is a closed enum — to add a new reason, edit
|
|
2314
|
-
* `ActionDisabledReason` and the consumer's display map.
|
|
2315
|
-
*
|
|
2316
|
-
* The optional `deps` argument is the same bag passed to
|
|
2317
|
-
* `ImmediateInvoker.run`; callers (`evaluateEnabled` / the ActionBar) may
|
|
2318
|
-
* synthesize it from the surrounding `DepRegistry` so predicates can
|
|
2319
|
-
* inspect selection / scene / etc. Predicates that don't need deps just
|
|
2320
|
-
* ignore the arg.
|
|
2321
|
-
*/
|
|
2322
|
-
enabled?: (deps?: ActionDeps) => true | ActionDisabledReason;
|
|
2323
|
-
/**
|
|
2324
|
-
* Declarative eligibility rule, evaluated against the current
|
|
2325
|
-
* `RuleCtx` by the dispatcher before invoking `start()`. Omitted =
|
|
2326
|
-
* always eligible.
|
|
2327
|
-
*
|
|
2328
|
-
* Accepts either a fluent `Condition` (callable with `.rule`) or a
|
|
2329
|
-
* raw `Rule` tree; the dispatcher normalizes via `.rule` unwrap.
|
|
2330
|
-
*
|
|
2331
|
-
* Prefer `capability:`-based rules (e.g. `{ capability: 'transforms-selection' }`)
|
|
2332
|
-
* over `mode:` rules — capability rules survive new modes being added
|
|
2333
|
-
* that allow the same capability.
|
|
2334
|
-
*/
|
|
2335
|
-
eligible?: Rule | Condition;
|
|
2336
|
-
/**
|
|
2337
|
-
* CSS cursor shown while the pointer hovers a spot where this action
|
|
2338
|
-
* would win the drag. The hover-cursor pump (in `useGestureDispatcher`)
|
|
2339
|
-
* runs `Dispatcher.resolveOnly` on each idle pointermove — the same
|
|
2340
|
-
* match walk a real pointerdown takes — and applies the winning
|
|
2341
|
-
* action's `cursor`, so the hint and the actual click target stay in
|
|
2342
|
-
* sync by construction. Omitted = no override (the active tool's
|
|
2343
|
-
* `Tool.cursor` shows). Affordance hits are resolved earlier in the
|
|
2344
|
-
* pump via `AffordanceRegion.cursor` and never reach this field.
|
|
2345
|
-
*
|
|
2346
|
-
* Static string only. Prediction runs `enabled()` but cannot run the
|
|
2347
|
-
* invoker, so an action that matches yet bails at `start()` (empty
|
|
2348
|
-
* handle) may still show its cursor — keep `enabled` accurate for
|
|
2349
|
-
* actions that declare one.
|
|
2350
|
-
*/
|
|
2351
|
-
cursor?: string;
|
|
2352
|
-
}
|
|
2353
|
-
/**
|
|
2354
|
-
* @experimental
|
|
2355
|
-
* Closed enum of reasons an action might report itself as disabled. The
|
|
2356
|
-
* consumer (palette, menu, etc.) maps these symbolic values to display
|
|
2357
|
-
* strings via its own label map — see `demo/CommandPalette.tsx` for the
|
|
2358
|
-
* canonical mapping.
|
|
2359
|
-
*/
|
|
2360
|
-
declare const ActionDisabledReason: {
|
|
2361
|
-
readonly SelectionRequired: "selection-required";
|
|
2362
|
-
readonly SceneEmpty: "scene-empty";
|
|
2363
|
-
readonly NotApplicable: "not-applicable";
|
|
2364
|
-
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
2365
|
-
readonly PredicateThrew: "predicate-threw";
|
|
2366
|
-
};
|
|
2367
|
-
type ActionDisabledReason = (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];
|
|
2368
|
-
/**
|
|
2369
|
-
* @experimental
|
|
2370
|
-
* Result of evaluating an Action's `enabled` predicate.
|
|
2371
|
-
*/
|
|
2372
|
-
interface ActionEnabledResult {
|
|
2373
|
-
enabled: boolean;
|
|
2374
|
-
reason?: ActionDisabledReason;
|
|
2375
|
-
}
|
|
2376
|
-
declare function evaluateEnabled(action: Action, deps?: ActionDeps): ActionEnabledResult;
|
|
2377
|
-
/**
|
|
2378
|
-
* @experimental
|
|
2379
|
-
* Partial override or full descriptor passed via `<SceneCanvas actions={...}>`.
|
|
2380
|
-
* `null` disables a default at this id.
|
|
2381
|
-
*/
|
|
2382
|
-
type ActionEntry = null | Partial<Action> | Action;
|
|
2383
|
-
/**
|
|
2384
|
-
* @experimental
|
|
2385
|
-
* Shape of the `actions` prop on `<SceneCanvas>`. `null` disables all defaults.
|
|
2386
|
-
*/
|
|
2387
|
-
type ActionsProp = null | Record<string, ActionEntry>;
|
|
2388
|
-
/**
|
|
2389
|
-
* @experimental
|
|
2390
|
-
* Imperative API exposed by `useActionsRegistry()`.
|
|
2391
|
-
*/
|
|
2392
|
-
interface ActionsRegistry {
|
|
2393
|
-
register(action: Action): () => void;
|
|
2394
|
-
unregister(id: string): void;
|
|
2395
|
-
list(): readonly Action[];
|
|
2396
|
-
/** Fire an immediate-invoker action by id. The optional `params` arg is
|
|
2397
|
-
* forwarded to `ImmediateInvoker.run` as its second argument — use it for
|
|
2398
|
-
* parametric actions (e.g. `trigger('tool.activate', { toolId: 'rect' })`).
|
|
2399
|
-
* Ongoing-invoker actions are not reachable from `trigger`. */
|
|
2400
|
-
trigger(id: string, params?: Record<string, unknown>): boolean;
|
|
2401
|
-
/**
|
|
2402
|
-
* Subscribe to registry mutations. The callback fires after any
|
|
2403
|
-
* `register`/`unregister` that changes the version. Returns an
|
|
2404
|
-
* unsubscribe function. Designed for `useSyncExternalStore`-driven
|
|
2405
|
-
* surfaces (e.g. `<ActionBar>` in `@weasel-js/ui`) that need to
|
|
2406
|
-
* re-render when the action set changes.
|
|
2407
|
-
*/
|
|
2408
|
-
subscribe(listener: () => void): () => void;
|
|
2409
|
-
/**
|
|
2410
|
-
* Start an ongoing action driven by UI (color picker, opacity slider).
|
|
2411
|
-
* Returns a control object with `update(params)` and `end(reason)`.
|
|
2412
|
-
*
|
|
2413
|
-
* Returns `null` if no dispatcher is wired into this registry, the
|
|
2414
|
-
* action is unknown, or its invoker is not ongoing.
|
|
2415
|
-
*
|
|
2416
|
-
* See `Dispatcher.beginUiOngoing` for full semantics including
|
|
2417
|
-
* auto-commit when a prior UI handle for the same action is in flight.
|
|
2418
|
-
*/
|
|
2419
|
-
begin(id: string, params?: Record<string, unknown>): UiOngoingControl | null;
|
|
2420
|
-
/** Wire a dispatcher into the registry so `begin()` can delegate to it.
|
|
2421
|
-
* Call with `null` to detach. Idempotent. */
|
|
2422
|
-
setDispatcher(d: Dispatcher | null): void;
|
|
2423
|
-
/** Wire a `DepRegistry` into the registry so `trigger()` / `begin()` can
|
|
2424
|
-
* resolve action deps even when this provider is mounted ABOVE the dep
|
|
2425
|
-
* registry (e.g. a consumer's root `<ActionsProvider>` reused by
|
|
2426
|
-
* SceneCanvas's `ActionsProviderIfRoot`). Takes precedence over the dep
|
|
2427
|
-
* registry read from context at the provider's own level. Call with
|
|
2428
|
-
* `null` to detach. */
|
|
2429
|
-
setDepRegistry(r: DepRegistry | null): void;
|
|
2430
|
-
}
|
|
2431
|
-
/**
|
|
2432
|
-
* @experimental
|
|
2433
|
-
* Mounts an `ActionsRegistry` and one `document` keydown listener for its
|
|
2434
|
-
* lifetime. Children call `useActionsRegistry()` or `useAction()` to participate.
|
|
2435
|
-
*/
|
|
2436
|
-
declare function ActionsProvider({ children }: {
|
|
2437
|
-
children: ReactNode;
|
|
2438
|
-
}): ReactElement;
|
|
2439
|
-
/**
|
|
2440
|
-
* @experimental
|
|
2441
|
-
* Returns the parent `ActionsRegistry`, or `null` when no provider is in scope.
|
|
2442
|
-
*/
|
|
2443
|
-
declare function useActionsRegistry(): ActionsRegistry | null;
|
|
2444
|
-
/**
|
|
2445
|
-
* @experimental
|
|
2446
|
-
* Register an `Action` for the lifetime of the calling component. No-op when
|
|
2447
|
-
* no `ActionsProvider` is in scope. Re-registers on `action` reference change
|
|
2448
|
-
* (consumers should memoize stable identities to avoid churn).
|
|
2449
|
-
*/
|
|
2450
|
-
declare function useAction(action: Action): void;
|
|
2451
|
-
|
|
2452
|
-
/**
|
|
2453
|
-
* `moveAction` — first `ongoing`-timing Action descriptor.
|
|
2454
|
-
*
|
|
2455
|
-
* Mirrors the per-frame translate semantics of the `useMove` hook:
|
|
2456
|
-
* - `start`: capture origin poses for all selected nodes; record the
|
|
2457
|
-
* current drag delta in scratch each frame.
|
|
2458
|
-
* - `onMove`: update the in-scratch `currentDelta` only — no scene writes.
|
|
2459
|
-
* This avoids polluting the undo stack with O(N-frames) entries.
|
|
2460
|
-
* - `onEnd('commit')`: emit the final delta as transform ops and route
|
|
2461
|
-
* them through the consumer `applyOps` hook when present, else
|
|
2462
|
-
* `scene.applyBatch(ops, 'Move', adapter)` — either way a single batch
|
|
2463
|
-
* → exactly one undo entry for the whole drag.
|
|
2464
|
-
* - `onEnd('cancel')`: no scene writes — the scene was never mutated during
|
|
2465
|
-
* the drag, so no restoration is needed.
|
|
2466
|
-
*
|
|
2467
|
-
* ## Why no per-frame scene writes
|
|
2468
|
-
*
|
|
2469
|
-
* `Scene.setPose` calls `executeAndLog`, which immediately records an undo
|
|
2470
|
-
* entry on the scene's history engine. Per-frame writes during drag would create
|
|
2471
|
-
* O(frames) history entries — matching `useMove`'s approach of tracking
|
|
2472
|
-
* poses only in React state (overlay) during the drag and committing a
|
|
2473
|
-
* single `createTransformOp` batch at the end.
|
|
2474
|
-
*
|
|
2475
|
-
* The behavior pipeline (snap-to-grid, snap-back-or-delete, snap-to-container,
|
|
2476
|
-
* etc.) via `opts.behaviors` from `BindingOpts` IS wired: `start` builds a
|
|
2477
|
-
* `GestureContext` + scene-backed adapter (`moveGestureAdapter`), `onMove`
|
|
2478
|
-
* folds each behavior's proposed-transform result, and `onEnd` runs a
|
|
2479
|
-
* first-non-undefined-wins reducer (`Op[]` commits via `scene.applyBatch`,
|
|
2480
|
-
* `null` aborts, all-`undefined` falls through to the default translate path).
|
|
2481
|
-
*
|
|
2482
|
-
* ## Pose generics
|
|
2483
|
-
*
|
|
2484
|
-
* `scene` dep is typed `Scene<unknown, string, unknown>` (the erased DepSchema
|
|
2485
|
-
* entry). Poses are read and written as `unknown`; `translatePoseGeneric`
|
|
2486
|
-
* delegates to `RECT_POSE_DESCRIPTOR.translate` which treats any pose as
|
|
2487
|
-
* `{x, y, ...}`. Consumers with non-rect poses should register a custom
|
|
2488
|
-
* action with a typed translatePose.
|
|
2489
|
-
*/
|
|
2490
|
-
|
|
2491
|
-
/**
|
|
2492
|
-
* @experimental
|
|
2493
|
-
* Static descriptor for the `move` Action.
|
|
2494
|
-
*
|
|
2495
|
-
* Requires dep-schema entries: `selection`, `scene`.
|
|
2496
|
-
*
|
|
2497
|
-
* The invoker is `ongoing` — it returns an `OngoingHandle` from `start` that
|
|
2498
|
-
* the dispatcher pumps via `onMove`/`onEnd` for the duration of the drag.
|
|
2499
|
-
*
|
|
2500
|
-
* @see useMove — the React hook this descriptor mirrors for the simple case.
|
|
2501
|
-
*/
|
|
2502
|
-
declare const moveAction: Action & {
|
|
2503
|
-
requires: string[];
|
|
2504
|
-
};
|
|
2505
|
-
|
|
2506
|
-
/**
|
|
2507
|
-
* `resizeAction` — ongoing Action descriptor for anchor-relative drag resize.
|
|
2508
|
-
*
|
|
2509
|
-
* ## Status: REAL
|
|
2510
|
-
*
|
|
2511
|
-
* Performs real anchor-relative resize math for rect-shaped poses. Reads
|
|
2512
|
-
* `ctx.drag.affordance.anchor` to determine which corner is fixed, captures
|
|
2513
|
-
* start poses, applies per-frame bounds remapping, and commits a single
|
|
2514
|
-
* `scene.batch` entry on end.
|
|
2515
|
-
*
|
|
2516
|
-
* ## Behaviors / point-snap / expandIds / geometry — via `resizePolicy` dep
|
|
2517
|
-
*
|
|
2518
|
-
* The resize-behaviors-api wired the four behavior options the
|
|
2519
|
-
* legacy `useResize` hook exposed (`behaviors`, `pointSnapBehaviors`,
|
|
2520
|
-
* `expandIds`, `geometry`) through the `resizePolicy` dep entry. When
|
|
2521
|
-
* the dep is registered, this invoker:
|
|
2522
|
-
*
|
|
2523
|
-
* - Calls `expandIds([id])` at start. When the result expands beyond the
|
|
2524
|
-
* starting id, takes the group path (union-AABB origin, per-leaf remap).
|
|
2525
|
-
* - Projects poses through the supplied `geometry: PoseProjection<TPose>`
|
|
2526
|
-
* instead of the rect-shaped `RECT_POSE_DESCRIPTOR` default.
|
|
2527
|
-
* - Runs `behaviors[]` after the raw anchor-math bounds are computed.
|
|
2528
|
-
* Behaviors return `{ pose? }` and rewrite the proposed bounds before
|
|
2529
|
-
* they're projected back into pose space.
|
|
2530
|
-
* - Runs `pointSnap[]` after the bounds→pose projection. The first non-null
|
|
2531
|
-
* `PointSnapResult` wins; this invoker back-solves a pose so the chosen
|
|
2532
|
-
* frame's world point lands on the snap target.
|
|
2533
|
-
*
|
|
2534
|
-
* When the dep is absent the invoker falls back to identity defaults
|
|
2535
|
-
* (no behaviors, no snap, `ids => ids` expansion, `RECT_POSE_DESCRIPTOR`),
|
|
2536
|
-
* which matches the unrotated rect-pose path the invoker already
|
|
2537
|
-
* implemented.
|
|
2538
|
-
*
|
|
2539
|
-
* @see useResize — the React hook this descriptor mirrors.
|
|
2540
|
-
* @see src/interactions/actions/resize/geometry.ts — `PoseProjection`.
|
|
2541
|
-
*/
|
|
2542
|
-
|
|
2543
|
-
/**
|
|
2544
|
-
* @experimental
|
|
2545
|
-
* Static descriptor for the `resize` Action.
|
|
2546
|
-
*
|
|
2547
|
-
* Requires dep-schema entries: `selection`, `scene`.
|
|
2548
|
-
* Reads optional `resizePolicy` dep when present.
|
|
2549
|
-
* Requires `InvocationCtx.drag.affordance` with a `handle:*` kind.
|
|
2550
|
-
*/
|
|
2551
|
-
declare const resizeAction: Action & {
|
|
2552
|
-
requires: string[];
|
|
2553
|
-
};
|
|
2554
|
-
|
|
2555
|
-
/**
|
|
2556
|
-
* `rotateAction` — ongoing Action descriptor for pointer-driven rotation.
|
|
2557
|
-
*
|
|
2558
|
-
* ## Status: REAL (unrotated-pivot path)
|
|
2559
|
-
*
|
|
2560
|
-
* Implements the core rotation math from `useRotate` for rect-shaped poses:
|
|
2561
|
-
* - `start`: captures origin poses + AABB centers; computes start pointer
|
|
2562
|
-
* angle around the union center of selected nodes.
|
|
2563
|
-
* - `onMove`: derives pointer angle delta from start, applies to each node's
|
|
2564
|
-
* origin rotation. In union-pivot mode (multi-selection) orbits each item's
|
|
2565
|
-
* center around the union center.
|
|
2566
|
-
* - `onEnd('commit')`: builds one `createTransformOp` per node (from =
|
|
2567
|
-
* pre-mutation origin pose, to = rotated pose) and commits them as a
|
|
2568
|
-
* single batch → one undo entry for the whole drag. Routes through the
|
|
2569
|
-
* optional `applyOps` dep (consumer history) when present, else
|
|
2570
|
-
* `scene.applyBatch` + `defaultCommitAdapter`.
|
|
2571
|
-
* - `onEnd('cancel')`: no scene writes (scene never mutated during drag).
|
|
2572
|
-
*
|
|
2573
|
-
* ## Constraints vs `useRotate`
|
|
2574
|
-
*
|
|
2575
|
-
* - Assumes rect-shaped poses `{ x, y, width, height, rotation? }`. Non-rect
|
|
2576
|
-
* TPose consumers need a custom action with a typed `RotateGeometry`.
|
|
2577
|
-
* - No behavior pipeline (snap, etc.). Behaviors wait for a later phase.
|
|
2578
|
-
* - No overlay rendering — deferred to Phase 7 overlay surface.
|
|
2579
|
-
* - Shift-snap (15° quantum) is NOT wired in this phase — omitted deliberately
|
|
2580
|
-
* to keep the invoker self-contained (would need to read shift from onMove ctx).
|
|
2581
|
-
* TODO: thread shift from InvocationCtx.modifiers.shift into snap logic.
|
|
2582
|
-
*/
|
|
2583
|
-
|
|
2584
|
-
/**
|
|
2585
|
-
* @experimental
|
|
2586
|
-
* Static descriptor for the `rotate` Action.
|
|
2587
|
-
*
|
|
2588
|
-
* Requires dep-schema entries: `selection`, `scene`.
|
|
2589
|
-
*
|
|
2590
|
-
* Implements the unrotated-pivot rotation path from `useRotate` for rect-
|
|
2591
|
-
* shaped poses. Non-rect or behavior-rich consumers should register a custom
|
|
2592
|
-
* `rotateAction` with a typed `RotateGeometry` dep.
|
|
2593
|
-
*
|
|
2594
|
-
* @see useRotate — the React hook this descriptor mirrors for the rect case.
|
|
2595
|
-
*/
|
|
2596
|
-
declare const rotateAction: Action & {
|
|
2597
|
-
requires: string[];
|
|
2598
|
-
};
|
|
2599
|
-
|
|
2600
|
-
/**
|
|
2601
|
-
* `areaSelectAction` — ongoing Action descriptor for marquee area selection.
|
|
2602
|
-
*
|
|
2603
|
-
* ## Status: REAL
|
|
1164
|
+
* ## Status: REAL
|
|
2604
1165
|
*
|
|
2605
1166
|
* Implements the marquee selection logic from `useAreaSelect`:
|
|
2606
1167
|
* - `start`: records the drag start world point and shift state.
|
|
@@ -2669,12 +1230,19 @@ declare const areaSelectAction: Action & {
|
|
|
2669
1230
|
* delegating to the scene's `add()` with a sensible default data payload for
|
|
2670
1231
|
* the given `kind`. Override per-consumer for custom node factories.
|
|
2671
1232
|
*
|
|
1233
|
+
* ## Modifiers and snapping
|
|
1234
|
+
*
|
|
1235
|
+
* Grid snapping comes from the optional `snap` dep, applied to the drag's
|
|
1236
|
+
* start and current point (never to freehand pencil samples). The `line`
|
|
1237
|
+
* kind additionally honors Shift (constrain to 15°) and reads Alt/center as
|
|
1238
|
+
* "mirror the start around the pointer" rather than "grow a symmetric AABB".
|
|
1239
|
+
* All of it resolves in `resolveEndpoints`, which both `overlay()` and
|
|
1240
|
+
* `onEnd()` call — so the live preview and the committed node can't disagree.
|
|
1241
|
+
*
|
|
2672
1242
|
* ## What this does NOT wire (vs `useInsert`)
|
|
2673
1243
|
*
|
|
2674
|
-
* - `InsertBehavior` pipeline (snap, etc.) — deferred to a later phase.
|
|
2675
1244
|
* - `pointInsert` fallback for click / sub-threshold drags — not wired; a
|
|
2676
1245
|
* sub-threshold drag produces no insert.
|
|
2677
|
-
* - Live insert overlay — deferred to Phase 7 overlay surface.
|
|
2678
1246
|
* - `clickOnly` mode — not applicable to the descriptor model.
|
|
2679
1247
|
*
|
|
2680
1248
|
* ## Live preview
|
|
@@ -2730,17 +1298,32 @@ declare const insertAction: Action & {
|
|
|
2730
1298
|
* This avoids ambient double-fire and keeps the action scoped to select-tool
|
|
2731
1299
|
* context where `classifyTarget` is wired.
|
|
2732
1300
|
*
|
|
2733
|
-
* ## enabled
|
|
1301
|
+
* ## enabled — the anchor-editing fall-through
|
|
1302
|
+
*
|
|
1303
|
+
* There is no "is anything selected?" guard: clearing an already-empty
|
|
1304
|
+
* selection is a safe no-op, and the binding spec is the real gate for
|
|
1305
|
+
* where the click landed.
|
|
2734
1306
|
*
|
|
2735
|
-
*
|
|
2736
|
-
*
|
|
1307
|
+
* What `enabled` *does* do is decline while a path is in anchor-edit mode,
|
|
1308
|
+
* so the dispatcher falls through to `selectAnchorAction`. This is the
|
|
1309
|
+
* click twin of the opt-out `areaSelectAction.start` already performs for
|
|
1310
|
+
* the drag gesture, and it exists for the same reason: `useSelectTool`
|
|
1311
|
+
* binds this action at **active** scope while `selectAnchor` is **ambient**,
|
|
1312
|
+
* so without the gate an anchor that happens to sit over empty canvas
|
|
1313
|
+
* clears the node selection instead of selecting the anchor.
|
|
1314
|
+
*
|
|
1315
|
+
* Capability eligibility (`creates-selection`, which `path-edit` does not
|
|
1316
|
+
* allow) already covers consumers that wired a mode registry. Consumers
|
|
1317
|
+
* without one get no eligibility filtering at all, so this gate is the only
|
|
1318
|
+
* thing separating the two. Both mechanisms are needed — see the header of
|
|
1319
|
+
* `anchorEditing.ts`.
|
|
2737
1320
|
*/
|
|
2738
1321
|
|
|
2739
1322
|
/**
|
|
2740
1323
|
* @experimental
|
|
2741
1324
|
* Static descriptor for the `clearSelection` Action.
|
|
2742
1325
|
*
|
|
2743
|
-
* Requires dep-schema
|
|
1326
|
+
* Requires dep-schema entries: `selection`, `editAnchors`.
|
|
2744
1327
|
*
|
|
2745
1328
|
* Registered via `useStandardActions`; bound via `useSelectTool.bindings`.
|
|
2746
1329
|
*/
|
|
@@ -2920,40 +1503,43 @@ declare const viewportZoomAction: Action & {
|
|
|
2920
1503
|
/**
|
|
2921
1504
|
* `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.
|
|
2922
1505
|
*
|
|
2923
|
-
*
|
|
1506
|
+
* Owns the whole press-on-an-anchor gesture, because pressing and dragging
|
|
1507
|
+
* an anchor are two outcomes of one interaction rather than two
|
|
1508
|
+
* interactions:
|
|
1509
|
+
*
|
|
1510
|
+
* - **Press** selects. Bare press replaces the anchor selection; Shift
|
|
1511
|
+
* toggles the pressed anchor in or out of it. Pressing an anchor that
|
|
1512
|
+
* is already part of a multi-anchor selection leaves the selection
|
|
1513
|
+
* alone, so you can grab a set and move it.
|
|
1514
|
+
* - **Drag** then moves whatever the press left selected — one anchor,
|
|
1515
|
+
* or every selected anchor together.
|
|
1516
|
+
* - **Dragging a control handle** moves just that handle, mirroring the
|
|
1517
|
+
* opposite one while the anchor is smooth. Alt breaks the mirror.
|
|
1518
|
+
*
|
|
1519
|
+
* Selection lives on the `editAnchors` dep (flat anchor indices) rather
|
|
1520
|
+
* than in this handle's scratch, because the keyboard actions
|
|
1521
|
+
* (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it
|
|
1522
|
+
* between gestures.
|
|
1523
|
+
*
|
|
1524
|
+
* ## Wiring
|
|
2924
1525
|
*
|
|
2925
|
-
* The descriptor is
|
|
1526
|
+
* The descriptor is operational when the consumer wires:
|
|
2926
1527
|
* 1. `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher
|
|
2927
1528
|
* classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`,
|
|
2928
1529
|
* or `controlOut:N` affordances on pointerdown.
|
|
2929
1530
|
* 2. The `editAnchors` dep in the DepSchema.
|
|
2930
1531
|
*
|
|
2931
|
-
* The invoker reads `ctx.drag.affordance.kind` to identify the hit anchor,
|
|
2932
|
-
* derives the coord index from `enumerateAnchors`, and on every `onMove`
|
|
2933
|
-
* writes the new absolute world position via `withCoord`. On `onEnd('commit')`
|
|
2934
|
-
* it dispatches a `createTransformOp` through `dispatchApplyBatch` so the
|
|
2935
|
-
* edit is undoable.
|
|
2936
|
-
*
|
|
2937
1532
|
* When `ctx.drag.affordance` is absent or is not an anchor/control kind,
|
|
2938
1533
|
* `start` returns `{}` (no-op) so other bindings can handle the drag.
|
|
2939
1534
|
*
|
|
2940
|
-
* @see useEditAnchors — the React hook this descriptor mirrors.
|
|
2941
1535
|
* @see buildAffordanceAt — the classifier that produces anchor affordances.
|
|
1536
|
+
* @see anchorEdits — the pure geometry these handlers drive.
|
|
2942
1537
|
*/
|
|
2943
1538
|
|
|
2944
1539
|
/**
|
|
2945
1540
|
* Static descriptor for the `editAnchors` Action.
|
|
2946
1541
|
*
|
|
2947
1542
|
* Requires dep-schema entries: `selection`, `editAnchors`.
|
|
2948
|
-
*
|
|
2949
|
-
* The invoker reads `ctx.drag.affordance.kind` to identify the anchor hit.
|
|
2950
|
-
* If the affordance is absent or not an anchor kind, `start` returns `{}`
|
|
2951
|
-
* (no-op), allowing other bindings to handle the drag.
|
|
2952
|
-
*
|
|
2953
|
-
* Consumers must provide `buildAffordanceAt` with a `getAnchorState` thunk
|
|
2954
|
-
* so that anchor handles are classified at pointerdown.
|
|
2955
|
-
*
|
|
2956
|
-
* @see useEditAnchors — the React hook this descriptor mirrors.
|
|
2957
1543
|
*/
|
|
2958
1544
|
declare const editAnchorsAction: Action & {
|
|
2959
1545
|
requires: string[];
|
|
@@ -3130,8 +1716,28 @@ interface UseGestureDispatcherOptions {
|
|
|
3130
1716
|
actions: ActionsRegistry;
|
|
3131
1717
|
/** Tool definitions keyed by id. Typically passes an empty Map. */
|
|
3132
1718
|
toolsById: ReadonlyMap<string, Tool>;
|
|
1719
|
+
/** Ids (within `toolsById`) of always-on tools, whose bindings assemble at
|
|
1720
|
+
* ambient scope. See `DispatcherContext.ambientToolIds`. */
|
|
1721
|
+
ambientToolIds?: readonly string[];
|
|
3133
1722
|
/** Default true. Set false to opt out of dispatcher wiring (e.g. demos that disable it). */
|
|
3134
1723
|
enabled?: boolean;
|
|
1724
|
+
/**
|
|
1725
|
+
* Observer fired whenever the dispatcher synthesizes a double click, in
|
|
1726
|
+
* world coordinates. Runs BEFORE the event is dispatched and independently
|
|
1727
|
+
* of which binding (if any) handles it.
|
|
1728
|
+
*
|
|
1729
|
+
* This is deliberately not an Action. `<SceneCanvas onDoubleClick>` is a
|
|
1730
|
+
* notification — "the user double-clicked, here's what they hit" — and a
|
|
1731
|
+
* notification must not compete with behavior for the gesture. As a binding
|
|
1732
|
+
* it would lose to `enterPathEdit` on any body hit and silently never fire.
|
|
1733
|
+
* Routing it here keeps a single definition of "double click" (the point of
|
|
1734
|
+
* consolidating the kit's three detectors) without giving it
|
|
1735
|
+
* first-match-wins semantics it shouldn't have.
|
|
1736
|
+
*/
|
|
1737
|
+
onDoubleClick?: (world: {
|
|
1738
|
+
x: number;
|
|
1739
|
+
y: number;
|
|
1740
|
+
}) => void;
|
|
3135
1741
|
/**
|
|
3136
1742
|
* Default true. Set false to leave the window `keydown`/`keyup` listeners
|
|
3137
1743
|
* unattached so keyboard-bound actions never dispatch — pointer, wheel, and
|
|
@@ -3160,23 +1766,25 @@ interface UseGestureDispatcherOptions {
|
|
|
3160
1766
|
y: number;
|
|
3161
1767
|
}) => AffordanceHit | null;
|
|
3162
1768
|
/**
|
|
3163
|
-
* Optional body
|
|
3164
|
-
*
|
|
3165
|
-
*
|
|
3166
|
-
* `
|
|
1769
|
+
* Optional body classifier. Called on every pointerdown with the world-space
|
|
1770
|
+
* coordinates of the pointer. Its result is packed onto the event as
|
|
1771
|
+
* `bodyTarget` + `bodyKind`, which `matchTarget` reads to resolve the
|
|
1772
|
+
* string-form `TargetSpec` values in `Tool.bindings`.
|
|
3167
1773
|
*
|
|
3168
|
-
*
|
|
1774
|
+
* `body` is `'empty'` when nothing is under the pointer, `'selected-body'`
|
|
3169
1775
|
* when the topmost hit belongs to the current selection, or
|
|
3170
|
-
* `'unselected-body'` when it belongs to a node that isn't selected.
|
|
1776
|
+
* `'unselected-body'` when it belongs to a node that isn't selected. `kind`
|
|
1777
|
+
* is the hit node's semantic kind, when the scene can name it.
|
|
3171
1778
|
*
|
|
3172
|
-
* When omitted,
|
|
3173
|
-
* `'unselected-body'`)
|
|
3174
|
-
*
|
|
1779
|
+
* When omitted, every body-derived target form (`'empty'`,
|
|
1780
|
+
* `'selected-body'`, `'unselected-body'`, `kind:<k>`, `kind:<k>:selected`)
|
|
1781
|
+
* never matches — bindings using those specs are silently skipped.
|
|
1782
|
+
* `<SceneCanvas>` wires this.
|
|
3175
1783
|
*/
|
|
3176
1784
|
classifyTarget?: (worldPoint: {
|
|
3177
1785
|
x: number;
|
|
3178
1786
|
y: number;
|
|
3179
|
-
}) =>
|
|
1787
|
+
}) => BodyClassification;
|
|
3180
1788
|
/**
|
|
3181
1789
|
* Optional pre-created `Dispatcher`. When provided, this hook pumps events
|
|
3182
1790
|
* into the supplied instance instead of creating its own. Lets a parent
|
|
@@ -3292,290 +1900,78 @@ interface DecayLoopConfig {
|
|
|
3292
1900
|
vx: number;
|
|
3293
1901
|
vy: number;
|
|
3294
1902
|
};
|
|
3295
|
-
friction?: number;
|
|
3296
|
-
minSpeed?: number;
|
|
3297
|
-
/** Bounds for boundary clamping. Requires `boundary` to take effect. */
|
|
3298
|
-
viewBounds?: PanBounds;
|
|
3299
|
-
/**
|
|
3300
|
-
* What to do when the accumulated position hits `viewBounds`. Default: no clamping.
|
|
3301
|
-
* - `'stop'`: clamp at boundary, kill velocity component.
|
|
3302
|
-
* - `'bounce'`: linear reflection — flip velocity sign, magnitude preserved.
|
|
3303
|
-
* - `'spring'`: damped reflection — flip velocity sign and shrink magnitude
|
|
3304
|
-
* by `SPRING_DAMPING` per bounce so the motion settles naturally.
|
|
3305
|
-
*/
|
|
3306
|
-
boundary?: 'stop' | 'bounce' | 'spring';
|
|
3307
|
-
/** Starting position for internal boundary tracking. Required when `viewBounds` is set. */
|
|
3308
|
-
initialPosition?: {
|
|
3309
|
-
x: number;
|
|
3310
|
-
y: number;
|
|
3311
|
-
};
|
|
3312
|
-
onTick: (dx: number, dy: number) => void;
|
|
3313
|
-
onEnd?: () => void;
|
|
3314
|
-
}
|
|
3315
|
-
declare function useDecayLoop(): {
|
|
3316
|
-
start: (config: DecayLoopConfig) => void;
|
|
3317
|
-
cancel: () => void;
|
|
3318
|
-
};
|
|
3319
|
-
|
|
3320
|
-
declare function useViewTween(setView: (v: View) => void): {
|
|
3321
|
-
animateTo: (from: View, to: View, opts?: {
|
|
3322
|
-
duration?: number;
|
|
3323
|
-
easing?: (t: number) => number;
|
|
3324
|
-
}) => void;
|
|
3325
|
-
cancel: () => void;
|
|
3326
|
-
isAnimating: React$1.RefObject<boolean>;
|
|
3327
|
-
};
|
|
3328
|
-
|
|
3329
|
-
/**
|
|
3330
|
-
* Detects two-finger pinch on a canvas element.
|
|
3331
|
-
*
|
|
3332
|
-
* @param onPinch Called with the gesture midpoint in **client coordinates** and
|
|
3333
|
-
* a per-frame scale factor (ratio of current distance to previous frame's
|
|
3334
|
-
* distance, not cumulative from gesture start). With 3+ fingers, the first
|
|
3335
|
-
* two by insertion order are tracked.
|
|
3336
|
-
*/
|
|
3337
|
-
declare function usePinchGesture(canvasRef: React.RefObject<HTMLCanvasElement | null>, onPinch: (anchor: {
|
|
3338
|
-
x: number;
|
|
3339
|
-
y: number;
|
|
3340
|
-
}, scaleFactor: number) => void, enabled?: boolean): void;
|
|
3341
|
-
|
|
3342
|
-
/** Options accepted by {@link useViewAnimation}'s `animateToBounds`. */
|
|
3343
|
-
interface AnimateToBoundsOptions extends FitViewToBoundsOptions {
|
|
3344
|
-
/** Tween duration in ms (forwarded to `animateTo`). */
|
|
3345
|
-
duration?: number;
|
|
3346
|
-
/** Tween easing (forwarded to `animateTo`). */
|
|
3347
|
-
easing?: (t: number) => number;
|
|
3348
|
-
}
|
|
3349
|
-
/**
|
|
3350
|
-
* Tween the viewport `View` between values. Wraps `useViewTween`'s `animateTo`
|
|
3351
|
-
* + `cancel`, and adds an `animateToBounds` convenience that composes
|
|
3352
|
-
* `fitViewToBounds` with the existing tween so consumers can say "zoom to this
|
|
3353
|
-
* bounds with animation" in one call.
|
|
3354
|
-
*
|
|
3355
|
-
* `animateToBounds` needs the current `View` and the current viewport
|
|
3356
|
-
* dimensions to compute the target — both are passed as arguments so this
|
|
3357
|
-
* hook stays a leaf (no canvas-size subscription, no `View` storage).
|
|
3358
|
-
*/
|
|
3359
|
-
declare function useViewAnimation(setView: (v: View) => void): {
|
|
3360
|
-
animateTo: (from: View, to: View, opts?: {
|
|
3361
|
-
duration?: number;
|
|
3362
|
-
easing?: (t: number) => number;
|
|
3363
|
-
}) => void;
|
|
3364
|
-
animateToBounds: (bounds: Bounds, currentView: View, viewportDims: ViewportDims, opts?: AnimateToBoundsOptions) => void;
|
|
3365
|
-
cancel: () => void;
|
|
3366
|
-
};
|
|
3367
|
-
|
|
3368
|
-
interface ResizeController<TNode extends {
|
|
3369
|
-
id: string;
|
|
3370
|
-
} = {
|
|
3371
|
-
id: string;
|
|
3372
|
-
}, TPose = unknown> {
|
|
3373
|
-
start(id: string, anchor: ResizeAnchor, worldX: number, worldY: number): void;
|
|
3374
|
-
move(worldX: number, worldY: number, modifiers: ModifierState): boolean | void;
|
|
3375
|
-
end(): void;
|
|
3376
|
-
cancel(): void;
|
|
3377
|
-
isResizing?: boolean;
|
|
3378
|
-
overlay?: unknown;
|
|
3379
|
-
adapter?: unknown;
|
|
3380
|
-
readonly __posePhantom?: TPose;
|
|
3381
|
-
readonly __nodePhantom?: TNode;
|
|
3382
|
-
}
|
|
3383
|
-
interface MoveController<TNode extends {
|
|
3384
|
-
id: string;
|
|
3385
|
-
} = {
|
|
3386
|
-
id: string;
|
|
3387
|
-
}, TPose = unknown> {
|
|
3388
|
-
start(args: {
|
|
3389
|
-
ids: string[];
|
|
3390
|
-
worldX: number;
|
|
3391
|
-
worldY: number;
|
|
3392
|
-
clientX?: number;
|
|
3393
|
-
clientY?: number;
|
|
3394
|
-
}): void;
|
|
3395
|
-
move(args: {
|
|
3396
|
-
worldX: number;
|
|
3397
|
-
worldY: number;
|
|
3398
|
-
clientX?: number;
|
|
3399
|
-
clientY?: number;
|
|
3400
|
-
modifiers: ModifierState;
|
|
3401
|
-
}): void;
|
|
3402
|
-
end(): void;
|
|
3403
|
-
cancel(): void;
|
|
3404
|
-
adapter?: unknown;
|
|
3405
|
-
readonly __posePhantom?: TPose;
|
|
3406
|
-
readonly __nodePhantom?: TNode;
|
|
3407
|
-
}
|
|
3408
|
-
interface RotateController<TNode extends {
|
|
3409
|
-
id: string;
|
|
3410
|
-
} = {
|
|
3411
|
-
id: string;
|
|
3412
|
-
}, TPose = unknown> {
|
|
3413
|
-
start(args: {
|
|
3414
|
-
id: string;
|
|
3415
|
-
worldX: number;
|
|
3416
|
-
worldY: number;
|
|
3417
|
-
}): void;
|
|
3418
|
-
move(args: {
|
|
3419
|
-
worldX: number;
|
|
3420
|
-
worldY: number;
|
|
3421
|
-
modifiers: ModifierState;
|
|
3422
|
-
}): void;
|
|
3423
|
-
end(): void;
|
|
3424
|
-
cancel(): void;
|
|
3425
|
-
readonly __posePhantom?: TPose;
|
|
3426
|
-
readonly __nodePhantom?: TNode;
|
|
3427
|
-
}
|
|
3428
|
-
interface InsertController<TNode extends {
|
|
3429
|
-
id: string;
|
|
3430
|
-
} = {
|
|
3431
|
-
id: string;
|
|
3432
|
-
}, TPose = unknown> {
|
|
3433
|
-
start(worldX: number, worldY: number, modifiers: ModifierState): void;
|
|
3434
|
-
move(worldX: number, worldY: number, modifiers: ModifierState): void;
|
|
3435
|
-
end(): void;
|
|
3436
|
-
cancel(): void;
|
|
3437
|
-
readonly __posePhantom?: TPose;
|
|
3438
|
-
readonly __nodePhantom?: TNode;
|
|
3439
|
-
}
|
|
3440
|
-
interface AreaSelectController {
|
|
3441
|
-
start(worldX: number, worldY: number, modifiers: ModifierState): void;
|
|
3442
|
-
move(worldX: number, worldY: number, modifiers: ModifierState): void;
|
|
3443
|
-
end(): void;
|
|
3444
|
-
cancel(): void;
|
|
3445
|
-
}
|
|
3446
|
-
|
|
3447
|
-
/** Pointer event handlers ready to spread onto a `<canvas>`. */
|
|
3448
|
-
interface PointerGestureBindings {
|
|
3449
|
-
onPointerDown: (e: React__default.PointerEvent<HTMLCanvasElement>) => void;
|
|
3450
|
-
onPointerMove: (e: React__default.PointerEvent<HTMLCanvasElement>) => void;
|
|
3451
|
-
onPointerUp: (e: React__default.PointerEvent<HTMLCanvasElement>) => void;
|
|
3452
|
-
onPointerCancel: (e: React__default.PointerEvent<HTMLCanvasElement>) => void;
|
|
3453
|
-
onLostPointerCapture: (e: React__default.PointerEvent<HTMLCanvasElement>) => void;
|
|
3454
|
-
}
|
|
3455
|
-
/** Context object passed to body-hit / tap-empty callbacks. */
|
|
3456
|
-
interface PointerGestureCallbackCtx {
|
|
3457
|
-
event: React__default.PointerEvent<HTMLCanvasElement>;
|
|
3458
|
-
worldX: number;
|
|
3459
|
-
worldY: number;
|
|
3460
|
-
modifiers: ModifierState;
|
|
3461
|
-
}
|
|
3462
|
-
/** Options for `usePointerGestures` — wires move/resize/rotate/insert/area-select controllers into a single canvas. */
|
|
3463
|
-
interface UsePointerGesturesOptions<TMovePose, TResizePose> {
|
|
3464
|
-
/** clientX/Y → world coords. Default: `clientToCanvas` (no pan/zoom). Apps
|
|
3465
|
-
* with a viewport transform compose pan/zoom into this callback. */
|
|
3466
|
-
clientToWorld?: (canvas: HTMLCanvasElement, clientX: number, clientY: number) => [number, number];
|
|
3467
|
-
/** Live move interaction. Omit to disable body-drag dispatch. */
|
|
3468
|
-
move?: MoveController<{
|
|
3469
|
-
id: string;
|
|
3470
|
-
}, TMovePose>;
|
|
3471
|
-
/** Live resize interaction. Omit to disable handle-drag dispatch. */
|
|
3472
|
-
resize?: ResizeController<{
|
|
3473
|
-
id: string;
|
|
3474
|
-
}, TResizePose>;
|
|
3475
|
-
/** Live rotation interaction. Omit to disable rotation-handle dispatch. */
|
|
3476
|
-
rotate?: RotateController<{
|
|
3477
|
-
id: string;
|
|
3478
|
-
}, TResizePose>;
|
|
3479
|
-
/** Live insert interaction. When `tool === 'insert'`, an empty-space
|
|
3480
|
-
* pointer-down dispatches to `insert.start` instead of `onTapEmpty`. */
|
|
3481
|
-
insert?: InsertController<{
|
|
3482
|
-
id: string;
|
|
3483
|
-
}, unknown>;
|
|
3484
|
-
/** Live area-select interaction. When `tool === 'select'` (or undefined),
|
|
3485
|
-
* an empty-space pointer-down dispatches to `areaSelect.start` instead of
|
|
3486
|
-
* `onTapEmpty`. */
|
|
3487
|
-
areaSelect?: AreaSelectController;
|
|
3488
|
-
/** Empty-space tool. Default `'select'`. Picks insert vs area-select on
|
|
3489
|
-
* empty-space pointer-down. Ignored if neither controller is wired. */
|
|
3490
|
-
tool?: 'select' | 'insert' | 'none';
|
|
3491
|
-
/** Currently rotatable target. The hook positions the rotation handle
|
|
3492
|
-
* above the (rotated) top-center of `bounds` and dispatches
|
|
3493
|
-
* `rotate.start({ id, ... })` on hit.
|
|
3494
|
-
*
|
|
3495
|
-
* When omitted but `rotate`, `selection`, and `boundsOf` are all
|
|
3496
|
-
* supplied, defaults to single-selection bounds (multi → null). */
|
|
3497
|
-
rotateTarget?: () => {
|
|
3498
|
-
id: string;
|
|
3499
|
-
bounds: Bounds;
|
|
3500
|
-
rotation?: number;
|
|
3501
|
-
} | null;
|
|
3502
|
-
/** World-pixel distance from the top edge to the rotation handle.
|
|
3503
|
-
* Default `DEFAULT_ROTATION_HANDLE_DISTANCE`. */
|
|
3504
|
-
rotationHandleDistance?: number;
|
|
3505
|
-
/** Currently resizable target. Hook computes corner handles, hit-tests, and
|
|
3506
|
-
* dispatches `resize.start(id, anchor, ...)`. Return `null` for none.
|
|
3507
|
-
*
|
|
3508
|
-
* When omitted but `selection` and `boundsOf` are both supplied, defaults
|
|
3509
|
-
* to single-selection bounds (multi-selection returns `null`). */
|
|
3510
|
-
resizeTarget?: () => {
|
|
3511
|
-
id: string;
|
|
3512
|
-
bounds: Bounds;
|
|
3513
|
-
rotation?: number;
|
|
3514
|
-
} | null;
|
|
3515
|
-
/** Hit-test radius for resize/rotation handles, in **screen** pixels.
|
|
3516
|
-
* Divided by `view.scale` (via `getView`) at compare time so the hit
|
|
3517
|
-
* area matches the visually rendered handle size under zoom.
|
|
3518
|
-
* Default 8. */
|
|
3519
|
-
handleHitRadius?: number;
|
|
3520
|
-
/** Returns the current view. Used to convert `handleHitRadius` (screen px)
|
|
3521
|
-
* into world units at hit-test time. Defaults to identity (scale=1) so
|
|
3522
|
-
* legacy callers retain world-px semantics until they wire a view. */
|
|
3523
|
-
getView?: () => View;
|
|
3524
|
-
/** Body hit-test for starting a move. Return id(s) to drag, or `null` to
|
|
3525
|
-
* fall through to `onTapEmpty`. */
|
|
3526
|
-
pickEvery?: (worldX: number, worldY: number) => string | string[] | null;
|
|
3527
|
-
/** Selection api (see {@link SelectionApi}). When supplied, the hook wires
|
|
3528
|
-
* selection-driven defaults: `onBodyHit` defaults to `selection.applyClick`
|
|
3529
|
-
* for the first hit id, `onTapEmpty` defaults to `selection.clear`, and
|
|
3530
|
-
* body-drag promotes-then-drags (clicking an unselected obj selects it
|
|
3531
|
-
* first, then drags the resulting selection; clicking a selected obj
|
|
3532
|
-
* drags the entire selection). Explicit callbacks override these defaults. */
|
|
3533
|
-
selection?: SelectionApi;
|
|
3534
|
-
/** Per-id bounds lookup. Combined with `selection`, defaults `resizeTarget`
|
|
3535
|
-
* to single-selection bounds when `resizeTarget` is not explicitly passed. */
|
|
3536
|
-
boundsOf?: (id: string) => Bounds | null;
|
|
3537
|
-
/** Called whenever a body hit occurs. Fires regardless of whether `move` is
|
|
3538
|
-
* wired — selection-only callers still receive notifications. */
|
|
3539
|
-
onBodyHit?: (ids: string[], ctx: PointerGestureCallbackCtx) => void;
|
|
3540
|
-
/** Called when the pointer hits neither a handle nor a body. Defaults to
|
|
3541
|
-
* `selection.clear()` when `selection` is supplied. */
|
|
3542
|
-
onTapEmpty?: (ctx: PointerGestureCallbackCtx) => void;
|
|
3543
|
-
/** Optional debug sink for the overlay subsystem. When supplied, body
|
|
3544
|
-
* hit-test results record one `recordHitbox(id, 'body', rect)` per id
|
|
3545
|
-
* that `pickEvery` returns — visualises which body shapes the kit
|
|
3546
|
-
* evaluated as hit. Tree-shakes when omitted (optional-chain). */
|
|
3547
|
-
debug?: DebugSink;
|
|
1903
|
+
friction?: number;
|
|
1904
|
+
minSpeed?: number;
|
|
1905
|
+
/** Bounds for boundary clamping. Requires `boundary` to take effect. */
|
|
1906
|
+
viewBounds?: PanBounds;
|
|
1907
|
+
/**
|
|
1908
|
+
* What to do when the accumulated position hits `viewBounds`. Default: no clamping.
|
|
1909
|
+
* - `'stop'`: clamp at boundary, kill velocity component.
|
|
1910
|
+
* - `'bounce'`: linear reflection — flip velocity sign, magnitude preserved.
|
|
1911
|
+
* - `'spring'`: damped reflection — flip velocity sign and shrink magnitude
|
|
1912
|
+
* by `SPRING_DAMPING` per bounce so the motion settles naturally.
|
|
1913
|
+
*/
|
|
1914
|
+
boundary?: 'stop' | 'bounce' | 'spring';
|
|
1915
|
+
/** Starting position for internal boundary tracking. Required when `viewBounds` is set. */
|
|
1916
|
+
initialPosition?: {
|
|
1917
|
+
x: number;
|
|
1918
|
+
y: number;
|
|
1919
|
+
};
|
|
1920
|
+
onTick: (dx: number, dy: number) => void;
|
|
1921
|
+
onEnd?: () => void;
|
|
3548
1922
|
}
|
|
1923
|
+
declare function useDecayLoop(): {
|
|
1924
|
+
start: (config: DecayLoopConfig) => void;
|
|
1925
|
+
cancel: () => void;
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
declare function useViewTween(setView: (v: View) => void): {
|
|
1929
|
+
animateTo: (from: View, to: View, opts?: {
|
|
1930
|
+
duration?: number;
|
|
1931
|
+
easing?: (t: number) => number;
|
|
1932
|
+
}) => void;
|
|
1933
|
+
cancel: () => void;
|
|
1934
|
+
isAnimating: React$1.RefObject<boolean>;
|
|
1935
|
+
};
|
|
1936
|
+
|
|
3549
1937
|
/**
|
|
3550
|
-
*
|
|
3551
|
-
* Owns the four `onPointer*` handlers, modifier extraction, world-coord
|
|
3552
|
-
* conversion, pointer capture, and the handle-vs-body dispatch decision.
|
|
3553
|
-
*
|
|
3554
|
-
* Caller still owns selection state, what counts as a body, and which
|
|
3555
|
-
* object (if any) is currently resizable. Pass a `selection` from
|
|
3556
|
-
* {@link useSelection} to opt into the standard click-promote-drag flow
|
|
3557
|
-
* with no extra wiring.
|
|
1938
|
+
* Detects two-finger pinch on a canvas element.
|
|
3558
1939
|
*
|
|
3559
|
-
*
|
|
3560
|
-
*
|
|
3561
|
-
*
|
|
3562
|
-
*
|
|
3563
|
-
* ```
|
|
1940
|
+
* @param onPinch Called with the gesture midpoint in **client coordinates** and
|
|
1941
|
+
* a per-frame scale factor (ratio of current distance to previous frame's
|
|
1942
|
+
* distance, not cumulative from gesture start). With 3+ fingers, the first
|
|
1943
|
+
* two by insertion order are tracked.
|
|
3564
1944
|
*/
|
|
3565
|
-
declare function
|
|
1945
|
+
declare function usePinchGesture(canvasRef: React.RefObject<HTMLCanvasElement | null>, onPinch: (anchor: {
|
|
1946
|
+
x: number;
|
|
1947
|
+
y: number;
|
|
1948
|
+
}, scaleFactor: number) => void, enabled?: boolean): void;
|
|
3566
1949
|
|
|
1950
|
+
/** Options accepted by {@link useViewAnimation}'s `animateToBounds`. */
|
|
1951
|
+
interface AnimateToBoundsOptions extends FitViewToBoundsOptions {
|
|
1952
|
+
/** Tween duration in ms (forwarded to `animateTo`). */
|
|
1953
|
+
duration?: number;
|
|
1954
|
+
/** Tween easing (forwarded to `animateTo`). */
|
|
1955
|
+
easing?: (t: number) => number;
|
|
1956
|
+
}
|
|
3567
1957
|
/**
|
|
3568
|
-
*
|
|
3569
|
-
*
|
|
3570
|
-
*
|
|
1958
|
+
* Tween the viewport `View` between values. Wraps `useViewTween`'s `animateTo`
|
|
1959
|
+
* + `cancel`, and adds an `animateToBounds` convenience that composes
|
|
1960
|
+
* `fitViewToBounds` with the existing tween so consumers can say "zoom to this
|
|
1961
|
+
* bounds with animation" in one call.
|
|
3571
1962
|
*
|
|
3572
|
-
*
|
|
3573
|
-
*
|
|
3574
|
-
*
|
|
3575
|
-
* drag: { onMove: (e, ctx) => { ctx.scratch.anchors.push(...); return 'claim'; } },
|
|
3576
|
-
* });
|
|
1963
|
+
* `animateToBounds` needs the current `View` and the current viewport
|
|
1964
|
+
* dimensions to compute the target — both are passed as arguments so this
|
|
1965
|
+
* hook stays a leaf (no canvas-size subscription, no `View` storage).
|
|
3577
1966
|
*/
|
|
3578
|
-
declare function
|
|
1967
|
+
declare function useViewAnimation(setView: (v: View) => void): {
|
|
1968
|
+
animateTo: (from: View, to: View, opts?: {
|
|
1969
|
+
duration?: number;
|
|
1970
|
+
easing?: (t: number) => number;
|
|
1971
|
+
}) => void;
|
|
1972
|
+
animateToBounds: (bounds: Bounds, currentView: View, viewportDims: ViewportDims, opts?: AnimateToBoundsOptions) => void;
|
|
1973
|
+
cancel: () => void;
|
|
1974
|
+
};
|
|
3579
1975
|
|
|
3580
1976
|
interface UseToolsOptions {
|
|
3581
1977
|
/** Initial active-slot tool id. Must exist in `registry`. */
|
|
@@ -3586,40 +1982,17 @@ interface UseToolsOptions {
|
|
|
3586
1982
|
registry: Record<string, AnyTool>;
|
|
3587
1983
|
/** Always-on tools — listen continuously regardless of active slot. */
|
|
3588
1984
|
ambient?: AnyTool[];
|
|
3589
|
-
/** Optional fallback tool consulted ONLY for `pointer.onClick` when the
|
|
3590
|
-
* active tool's click handler returns `'pass'` (or has no handler).
|
|
3591
|
-
* Lets a non-select tool stay active while unhandled clicks fall through
|
|
3592
|
-
* to the select tool for click-to-select. Not consulted for pointerdown,
|
|
3593
|
-
* drag, keyboard, wheel, or dblTap. */
|
|
3594
|
-
fallback?: AnyTool;
|
|
3595
|
-
/** Per-event base ctx supplier. `<Canvas>` wires this to inject world
|
|
3596
|
-
* coords, modifiers, selection, adapter, applyOps. Tests can supply
|
|
3597
|
-
* a stub. Optional — the dispatcher works with a default empty ctx
|
|
3598
|
-
* for tests that don't need the wiring. */
|
|
3599
|
-
getCtx?: (overrides?: {
|
|
3600
|
-
clientX?: number;
|
|
3601
|
-
clientY?: number;
|
|
3602
|
-
modifiers?: {
|
|
3603
|
-
alt: boolean;
|
|
3604
|
-
shift: boolean;
|
|
3605
|
-
meta: boolean;
|
|
3606
|
-
ctrl: boolean;
|
|
3607
|
-
};
|
|
3608
|
-
}) => Omit<ToolCtx, 'scratch'>;
|
|
3609
|
-
/** Optional scene hit-test for populating `ctx.target` on pointer events.
|
|
3610
|
-
* `<Canvas>` wires this from the effective `pickEvery` + adapter. Tests
|
|
3611
|
-
* may supply a stub; omit for the always-empty fallback. */
|
|
3612
|
-
getNodeAtPoint?: ToolsDispatcherOptions['getNodeAtPoint'];
|
|
3613
1985
|
}
|
|
3614
1986
|
interface ToolsApi {
|
|
3615
1987
|
/** Current active-slot tool id. */
|
|
3616
1988
|
active: string;
|
|
3617
|
-
/** Set the active-slot tool.
|
|
1989
|
+
/** Set the active-slot tool. The gesture dispatcher watches the active
|
|
1990
|
+
* tool and cancels any in-flight handle itself. */
|
|
3618
1991
|
setActive: (id: string) => void;
|
|
3619
1992
|
/** Currently hotkey-engaged tool id (or `null`). Derived as the top of
|
|
3620
1993
|
* the hotkey stack for backwards compat with the pre-stack API. */
|
|
3621
1994
|
hotkeyEngaged: string | null;
|
|
3622
|
-
/** Engage a hotkey-slot tool by id.
|
|
1995
|
+
/** Engage a hotkey-slot tool by id. */
|
|
3623
1996
|
engageHotkey: (id: string) => void;
|
|
3624
1997
|
/** Disengage the hotkey-slot tool, if any. */
|
|
3625
1998
|
disengageHotkey: () => void;
|
|
@@ -3627,13 +2000,6 @@ interface ToolsApi {
|
|
|
3627
2000
|
ambient: readonly AnyTool[];
|
|
3628
2001
|
/** Full registry — for userland UI (palette buttons, etc.). */
|
|
3629
2002
|
registry: Readonly<Record<string, AnyTool>>;
|
|
3630
|
-
/** The dispatcher `<Canvas>` wires to its DOM events. */
|
|
3631
|
-
dispatcher: ToolsDispatcher;
|
|
3632
|
-
/** Increments whenever an in-flight gesture starts, transitions phase, or
|
|
3633
|
-
* ends. Consumers (e.g. `<Canvas>` cursor resolution) include this in
|
|
3634
|
-
* their render deps to re-evaluate derived state on real DOM events
|
|
3635
|
-
* rather than waiting for an unrelated re-render. */
|
|
3636
|
-
gestureTick: number;
|
|
3637
2003
|
/** Returns true if a tool with the given id is in the registry or ambient list. */
|
|
3638
2004
|
has(id: string): boolean;
|
|
3639
2005
|
/** All overlay layers from currently-engaged tools (active slot, hotkey
|
|
@@ -3643,7 +2009,12 @@ interface ToolsApi {
|
|
|
3643
2009
|
getActiveOverlays(): RenderLayer<unknown>[];
|
|
3644
2010
|
}
|
|
3645
2011
|
/**
|
|
3646
|
-
* Manages the active tool
|
|
2012
|
+
* Manages the active tool and hotkey slot.
|
|
2013
|
+
*
|
|
2014
|
+
* It used to also own a dispatcher: `useTools` constructed the tool-routing
|
|
2015
|
+
* dispatcher and `<Canvas>` pumped DOM events into it. Input now belongs
|
|
2016
|
+
* entirely to `useGestureDispatcher`, so what's left here is slot state plus
|
|
2017
|
+
* the overlay roll-up.
|
|
3647
2018
|
*
|
|
3648
2019
|
* Requires `<ActiveToolContextProvider>` (or `<WeaselProvider>` /
|
|
3649
2020
|
* `<SceneCanvas>`, which mount one internally) in scope: active/hotkey state
|
|
@@ -3664,10 +2035,20 @@ interface UseKeybindingsOptions {
|
|
|
3664
2035
|
* `tools.active` was when the hook first ran (i.e. the initial active
|
|
3665
2036
|
* tool). Pass `null` to disable Escape-returns-to-default behavior. */
|
|
3666
2037
|
defaultTool?: string | null;
|
|
2038
|
+
/**
|
|
2039
|
+
* Gate for keyboard tool activation: return false to refuse a tool the
|
|
2040
|
+
* active mode doesn't allow. `<SceneCanvas>` wires this from
|
|
2041
|
+
* `getActiveMode` + each tool's `capabilities`, using the same predicate
|
|
2042
|
+
* `ToolPalette` uses to grey a button out — so the grey-out becomes a
|
|
2043
|
+
* guarantee rather than a hint.
|
|
2044
|
+
*
|
|
2045
|
+
* Omit for consumers with no mode registry: every tool stays activatable.
|
|
2046
|
+
*/
|
|
2047
|
+
isToolEligible?: (toolId: string) => boolean;
|
|
3667
2048
|
}
|
|
3668
2049
|
declare function useKeybindings(tools: ToolsApi, options?: UseKeybindingsOptions): void;
|
|
3669
2050
|
|
|
3670
|
-
type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color';
|
|
2051
|
+
type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint';
|
|
3671
2052
|
interface ToolPrefBase<K extends string, Value> {
|
|
3672
2053
|
kind: K;
|
|
3673
2054
|
/** Human-readable label. */
|
|
@@ -3734,7 +2115,22 @@ interface ToolPrefColor extends ToolPrefBase<'color', string> {
|
|
|
3734
2115
|
*/
|
|
3735
2116
|
interface ToolPrefCustom extends ToolPrefBase<string, unknown> {
|
|
3736
2117
|
}
|
|
3737
|
-
|
|
2118
|
+
/**
|
|
2119
|
+
* A whole `FillStyle`, not a color inside one. Use it wherever the value is
|
|
2120
|
+
* the tagged paint union — a solid color, a pattern, a gradient — rather
|
|
2121
|
+
* than a hex string.
|
|
2122
|
+
*
|
|
2123
|
+
* Addressing `…fill.color` instead reads `undefined` off a gradient (so the
|
|
2124
|
+
* control shows its default and claims the text is black) and writes a
|
|
2125
|
+
* hybrid `{ fill: 'gradient', stops, color }` that the renderer's structural
|
|
2126
|
+
* `'color' in paint` checks then paint flat solid. The union has to be
|
|
2127
|
+
* edited as a union.
|
|
2128
|
+
*/
|
|
2129
|
+
interface ToolPrefPaint extends ToolPrefBase<'paint', unknown> {
|
|
2130
|
+
/** Offer an opacity control alongside the color. */
|
|
2131
|
+
alpha?: boolean;
|
|
2132
|
+
}
|
|
2133
|
+
type ToolPref = ToolPrefNumber | ToolPrefBoolean | ToolPrefString | ToolPrefEnum | ToolPrefColor | ToolPrefPaint;
|
|
3738
2134
|
/** Built-in or app-defined leaf. */
|
|
3739
2135
|
type ToolPrefLeaf = ToolPref | ToolPrefCustom;
|
|
3740
2136
|
/** Nestable group: branch nodes a tool can use to organize its prefs. */
|
|
@@ -3744,61 +2140,7 @@ interface ToolPrefGroup {
|
|
|
3744
2140
|
children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
|
|
3745
2141
|
}
|
|
3746
2142
|
|
|
3747
|
-
interface
|
|
3748
|
-
fill?: string;
|
|
3749
|
-
stroke?: string;
|
|
3750
|
-
dash?: number[];
|
|
3751
|
-
lineWidth?: number;
|
|
3752
|
-
}
|
|
3753
|
-
|
|
3754
|
-
interface UseInsertToolOptions<TPose, TNode extends {
|
|
3755
|
-
id: string;
|
|
3756
|
-
} = {
|
|
3757
|
-
id: string;
|
|
3758
|
-
}> extends UseInsertOptions<TPose, TNode> {
|
|
3759
|
-
overlayStyle?: InsertOverlayStyle;
|
|
3760
|
-
/** Hit-test gate consulted before insertion. On hit, selects via
|
|
3761
|
-
* ctx.selection.set and skips both the click and drag paths. */
|
|
3762
|
-
hitExisting?: (point: {
|
|
3763
|
-
x: number;
|
|
3764
|
-
y: number;
|
|
3765
|
-
}) => string | string[] | null;
|
|
3766
|
-
}
|
|
3767
|
-
/** Drag-to-insert Tool. The legacy `useInsert` hook is
|
|
3768
|
-
* no longer consumed here — the gesture is owned end-to-end by the
|
|
3769
|
-
* dispatcher's `insertAction` (see `src/interactions/actions/defaults/insert.ts`),
|
|
3770
|
-
* which requires the `insert` dep. `SceneCanvas` registers that dep via
|
|
3771
|
-
* `useInsertDepSource`; consumers using bare `<Canvas>` must wire it themselves.
|
|
3772
|
-
*
|
|
3773
|
-
* Note: this tool is now a thin declarative wrapper — no local route-table
|
|
3774
|
-
* drag handler, no local insert overlay layer, no click-to-insert path.
|
|
3775
|
-
* Live-preview marquee and click-to-insert are dispatcher-side features
|
|
3776
|
-
* deferred to a later phase (see insertAction's "What this does NOT wire"
|
|
3777
|
-
* comment). The `overlayStyle` / `hitExisting` / `pointInsert` options
|
|
3778
|
-
* remain on the option surface for forward-compat but are currently
|
|
3779
|
-
* ignored. */
|
|
3780
|
-
declare function useInsertTool<TNode extends {
|
|
3781
|
-
id: string;
|
|
3782
|
-
}, TPose>(_adapter: InsertAdapter<TNode>, _options?: UseInsertToolOptions<TPose, TNode>): Tool<undefined>;
|
|
3783
|
-
|
|
3784
|
-
/** Legacy style hooks for the area-select marquee. The dispatcher
|
|
3785
|
-
* overlay layer (`useDispatcherOverlayLayer`) now owns marquee paint; this
|
|
3786
|
-
* type is retained on the option surface for source-compat with callers
|
|
3787
|
-
* threading it through. */
|
|
3788
|
-
interface AreaSelectOverlayStyle {
|
|
3789
|
-
fill?: string;
|
|
3790
|
-
stroke?: string;
|
|
3791
|
-
dash?: number[];
|
|
3792
|
-
lineWidth?: number;
|
|
3793
|
-
}
|
|
3794
|
-
/** Legacy style hook for the move ghost. The preview-ghost layer
|
|
3795
|
-
* renders moved silhouettes via `drawOne` now. Retained for source-compat. */
|
|
3796
|
-
interface MoveOverlayStyle {
|
|
3797
|
-
ghostAlpha?: number;
|
|
3798
|
-
}
|
|
3799
|
-
interface UseSelectToolOptions<TNode extends {
|
|
3800
|
-
id: string;
|
|
3801
|
-
}, TPose> {
|
|
2143
|
+
interface UseSelectToolOptions<TPose> {
|
|
3802
2144
|
/** Return ids of all objects whose painted body covers (worldX, worldY).
|
|
3803
2145
|
* Order doesn't matter — the tool collapses parent/child overlap via
|
|
3804
2146
|
* `pickTopMostHit`. When omitted, defaults to a rect AABB-vs-point scan
|
|
@@ -3809,8 +2151,6 @@ interface UseSelectToolOptions<TNode extends {
|
|
|
3809
2151
|
/** Optional alt-aware selection-update hit returning the single id the
|
|
3810
2152
|
* click should act on. */
|
|
3811
2153
|
pickBest?: (worldX: number, worldY: number, alt: boolean, selection: readonly string[]) => string | null;
|
|
3812
|
-
/** Return the world-space bounds of `id`, or null if not found. */
|
|
3813
|
-
boundsOf?: (id: string) => Bounds | null;
|
|
3814
2154
|
/** Project a pose to its AABB. Default: identity. */
|
|
3815
2155
|
poseBounds?: (pose: TPose) => Bounds;
|
|
3816
2156
|
/** Move-action options. The move gesture is dispatcher-routed,
|
|
@@ -3818,36 +2158,23 @@ interface UseSelectToolOptions<TNode extends {
|
|
|
3818
2158
|
* `opts.behaviors`. Other `UseMoveOptions` fields are accepted for API shape
|
|
3819
2159
|
* but not read by this tool. */
|
|
3820
2160
|
move?: UseMoveOptions<TPose>;
|
|
3821
|
-
/**
|
|
3822
|
-
*
|
|
3823
|
-
|
|
2161
|
+
/**
|
|
2162
|
+
* When this returns true, a Shift/Meta extend-click must NOT change the
|
|
2163
|
+
* node selection.
|
|
2164
|
+
*
|
|
2165
|
+
* `<SceneCanvas>` wires it to "a path is in anchor-edit mode", where
|
|
2166
|
+
* Shift-click means "add this anchor to the anchor selection". Without
|
|
2167
|
+
* the lock the same click also toggles the edited node out of the node
|
|
2168
|
+
* selection, and the `editAnchors` dep treats that as the edit target
|
|
2169
|
+
* disappearing — so multi-selecting anchors silently exited edit mode.
|
|
2170
|
+
*
|
|
2171
|
+
* Only extend-clicks are locked. A plain click still re-selects, so
|
|
2172
|
+
* clicking a different node exits edit mode as usual.
|
|
2173
|
+
*/
|
|
2174
|
+
extendClickLocked?: () => boolean;
|
|
3824
2175
|
/** Optional debug sink. Reserved for future overlay/affordance hitbox
|
|
3825
2176
|
* recording. */
|
|
3826
2177
|
debug?: DebugSink;
|
|
3827
|
-
/** Style for the area-select marquee. Pre-14e wired the legacy overlay;
|
|
3828
|
-
* after T3 it's consumed by the dispatcher overlay layer via
|
|
3829
|
-
* `useDispatcherOverlayLayer`'s `style` arg (not by this tool). Retained
|
|
3830
|
-
* for option-surface compat. */
|
|
3831
|
-
areaSelectOverlayStyle?: AreaSelectOverlayStyle;
|
|
3832
|
-
/** Style for the move ghost. Pre-14e wired the legacy overlay's alpha;
|
|
3833
|
-
* preview-ghost layer alpha now lives in `usePreviewGhostLayer`. */
|
|
3834
|
-
moveOverlayStyle?: MoveOverlayStyle;
|
|
3835
|
-
/** Consumer's draw function for ghost objects. Pre-14e the select tool
|
|
3836
|
-
* invoked this from its own overlay; the preview-ghost layer now calls
|
|
3837
|
-
* the scene slot's `drawOne` directly, so this option is no longer in
|
|
3838
|
-
* the runtime path. */
|
|
3839
|
-
drawGhost?: (obj: TNode | null, pose: TPose, view: {
|
|
3840
|
-
x: number;
|
|
3841
|
-
y: number;
|
|
3842
|
-
scale: {
|
|
3843
|
-
x: number;
|
|
3844
|
-
y: number;
|
|
3845
|
-
};
|
|
3846
|
-
}) => DrawCommand[];
|
|
3847
|
-
/** Object lookup paired with `drawGhost`. Pre-14e runtime hook; vestigial. */
|
|
3848
|
-
getNode?: (id: string) => TNode | null;
|
|
3849
|
-
/** Returns the live selection ids. */
|
|
3850
|
-
getSelection?: () => readonly string[];
|
|
3851
2178
|
/** Reparent-on-drop behavior for drag-to-move. `'off'` (default)
|
|
3852
2179
|
* preserves translate-only commits. `'top'` lands the moved nodes at
|
|
3853
2180
|
* the top of the container under the drop point. `'above'` lands them
|
|
@@ -3855,33 +2182,16 @@ interface UseSelectToolOptions<TNode extends {
|
|
|
3855
2182
|
* semantics when the hit is itself a container). Requires the
|
|
3856
2183
|
* `nodeAtPoint` dep to be registered (sourced by `<SceneCanvas>`). */
|
|
3857
2184
|
reparentOnDrop?: 'off' | 'top' | 'above';
|
|
3858
|
-
/** Optional double-tap hook. */
|
|
3859
|
-
onDoubleTap?: (args: {
|
|
3860
|
-
worldX: number;
|
|
3861
|
-
worldY: number;
|
|
3862
|
-
ids: string[];
|
|
3863
|
-
event: PointerEvent;
|
|
3864
|
-
}) => void;
|
|
3865
2185
|
}
|
|
3866
2186
|
/** Intersection of the move + area-select adapter interfaces.
|
|
3867
2187
|
* Resize / rotate adapters moved to `useResizeTool` / `useRotateTool`. */
|
|
3868
2188
|
type SelectAdapter<TNode extends {
|
|
3869
2189
|
id: string;
|
|
3870
2190
|
}, TPose> = MoveAdapter<TNode, TPose> & AreaSelectAdapter;
|
|
3871
|
-
/** @internal */
|
|
3872
|
-
type SelectScratch = {
|
|
3873
|
-
kind: 'idle';
|
|
3874
|
-
} | {
|
|
3875
|
-
kind: 'move';
|
|
3876
|
-
ids: string[];
|
|
3877
|
-
deferredClickId: string | null;
|
|
3878
|
-
} | {
|
|
3879
|
-
kind: 'area';
|
|
3880
|
-
};
|
|
3881
2191
|
/** Active-slot Tool:
|
|
3882
|
-
* - pointerDown
|
|
3883
|
-
* so click semantics (selection replace, extend,
|
|
3884
|
-
*
|
|
2192
|
+
* - `select.pick` classifies the press (pointerDown) — it runs the tool's own
|
|
2193
|
+
* pickBest/pickEvery so click semantics (selection replace, extend,
|
|
2194
|
+
* deferred collapse) live in one place.
|
|
3885
2195
|
* - drag is owned exclusively by the dispatcher via `Tool.bindings`
|
|
3886
2196
|
* (selected-body → moveAction, rotate-handle → rotateAction, handle:* →
|
|
3887
2197
|
* resizeAction, empty → areaSelectAction, click on empty → clearSelection).
|
|
@@ -3891,81 +2201,7 @@ type SelectScratch = {
|
|
|
3891
2201
|
*/
|
|
3892
2202
|
declare function useSelectTool<TNode extends {
|
|
3893
2203
|
id: string;
|
|
3894
|
-
}, TPose>(adapter: SelectAdapter<TNode, TPose>, options: UseSelectToolOptions<
|
|
3895
|
-
|
|
3896
|
-
/** @internal */
|
|
3897
|
-
interface HitAdapter<TNode extends {
|
|
3898
|
-
id: string;
|
|
3899
|
-
}, TPose> {
|
|
3900
|
-
getNode: (id: string) => TNode | undefined;
|
|
3901
|
-
getNodes: () => TNode[];
|
|
3902
|
-
getPose: (id: string) => TPose;
|
|
3903
|
-
getParent: (id: string) => string | null;
|
|
3904
|
-
}
|
|
3905
|
-
interface NestedHitOpts<TNode extends {
|
|
3906
|
-
id: string;
|
|
3907
|
-
}, TPose> {
|
|
3908
|
-
/** Compose a child's local pose into world coords given its parent's world
|
|
3909
|
-
* pose. Same shape as `composeRectPose` (the default expectation). */
|
|
3910
|
-
composePose: (parent: TPose, child: TPose) => TPose;
|
|
3911
|
-
/** Derive an axis-aligned bounding rect from a (world-space) pose. Default
|
|
3912
|
-
* reads `x` / `y` / `width` / `height` straight off the pose (matches
|
|
3913
|
-
* `RectPose`). Override for non-rect poses (e.g. paths). */
|
|
3914
|
-
poseBounds?: (pose: TPose) => Bounds;
|
|
3915
|
-
/** Predicate for "this object is a nesting parent body". The leaf scan
|
|
3916
|
-
* skips objects for which this returns true so a click on a parent's
|
|
3917
|
-
* painted body resolves to a child leaf, not the parent itself. Default:
|
|
3918
|
-
* never (treat every object as hittable). */
|
|
3919
|
-
isGroup?: (id: string, obj: TNode | undefined) => boolean;
|
|
3920
|
-
}
|
|
3921
|
-
interface NestedHitTester {
|
|
3922
|
-
/** Outermost-ancestor pick. Suitable as the chrome-level `pickEvery`: a
|
|
3923
|
-
* casual click selects the whole top-level ancestor. Returns `null` on
|
|
3924
|
-
* empty space. */
|
|
3925
|
-
pickOutermost: (worldX: number, worldY: number) => string | null;
|
|
3926
|
-
/** Alt-aware selection-update pick. Without `alt`, returns the outermost
|
|
3927
|
-
* ancestor (same as `pickOutermost`). With `alt`, returns one level
|
|
3928
|
-
* deeper than the deepest currently-selected ancestor in the leaf's
|
|
3929
|
-
* chain — repeated alt-clicks step ancestor → descendant → leaf. With
|
|
3930
|
-
* `alt` and nothing in the chain selected, jumps straight to the leaf.
|
|
3931
|
-
* Plug into `useSelectTool({ pickBest })`. */
|
|
3932
|
-
pickBest: (worldX: number, worldY: number, alt: boolean, selection: readonly string[]) => string | null;
|
|
3933
|
-
}
|
|
3934
|
-
declare function nestedHitTester<TNode extends {
|
|
3935
|
-
id: string;
|
|
3936
|
-
}, TPose>(adapter: HitAdapter<TNode, TPose>, opts: NestedHitOpts<TNode, TPose>): NestedHitTester;
|
|
3937
|
-
|
|
3938
|
-
/** Adapter contract: SelectAdapter (Move + AreaSelect) plus the lookups
|
|
3939
|
-
* `nestedHitTester` needs (`getParent` + the existing `getNode`/`getNodes`/`getPose`). */
|
|
3940
|
-
type NestedSelectAdapter<TNode extends {
|
|
3941
|
-
id: string;
|
|
3942
|
-
}, TPose> = SelectAdapter<TNode, TPose> & {
|
|
3943
|
-
getNode: (id: string) => TNode | undefined;
|
|
3944
|
-
getNodes: () => TNode[];
|
|
3945
|
-
getParent: (id: string) => string | null;
|
|
3946
|
-
};
|
|
3947
|
-
interface UseNestedSelectToolOptions<TNode extends {
|
|
3948
|
-
id: string;
|
|
3949
|
-
}, TPose> extends NestedHitOpts<TNode, TPose>, Omit<UseSelectToolOptions<TNode, TPose>, 'pickBest'> {
|
|
3950
|
-
}
|
|
3951
|
-
/**
|
|
3952
|
-
* Convenience over `useSelectTool` for scenes with nesting. Builds a
|
|
3953
|
-
* `nestedHitTester` from `composePose` + `isGroup` and wires its `pickBest`
|
|
3954
|
-
* into the select tool — alt-clicks drill outermost ancestor → child → leaf
|
|
3955
|
-
* automatically. Casual clicks select the outermost ancestor.
|
|
3956
|
-
*
|
|
3957
|
-
* Equivalent to writing:
|
|
3958
|
-
*
|
|
3959
|
-
* ```tsx
|
|
3960
|
-
* const hitter = useMemo(() => nestedHitTester(adapter, { composePose, isGroup }), [adapter]);
|
|
3961
|
-
* const select = useSelectTool(adapter, { pickBest: (...args) => hitter.pickBest(...args) });
|
|
3962
|
-
* ```
|
|
3963
|
-
*
|
|
3964
|
-
* but with one fewer concept on the consumer surface.
|
|
3965
|
-
*/
|
|
3966
|
-
declare function useNestedSelectTool<TNode extends {
|
|
3967
|
-
id: string;
|
|
3968
|
-
}, TPose>(adapter: NestedSelectAdapter<TNode, TPose>, options: UseNestedSelectToolOptions<TNode, TPose>): ReturnType<typeof useSelectTool<TNode, TPose>>;
|
|
2204
|
+
}, TPose>(adapter: SelectAdapter<TNode, TPose>, options: UseSelectToolOptions<TPose>): Tool<null>;
|
|
3969
2205
|
|
|
3970
2206
|
interface UseRotateToolOptions<TNode extends {
|
|
3971
2207
|
id: string;
|
|
@@ -4098,40 +2334,16 @@ interface HandScratch {
|
|
|
4098
2334
|
*/
|
|
4099
2335
|
declare function useHandTool(opts?: UseHandToolOptions): Tool<HandScratch | null>;
|
|
4100
2336
|
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
y: number;
|
|
4112
|
-
}) => string | string[] | null;
|
|
4113
|
-
minBounds?: {
|
|
4114
|
-
width: number;
|
|
4115
|
-
height: number;
|
|
4116
|
-
};
|
|
4117
|
-
marqueeStyle?: InsertOverlayStyle;
|
|
4118
|
-
}
|
|
4119
|
-
/** Text Tool. The legacy `useInsert` hook is dropped — drag-rect
|
|
4120
|
-
* insertion is owned end-to-end by the dispatcher's `insertAction` (kind:
|
|
4121
|
-
* 'text'); click-on-selected-text-node enters edit mode via the
|
|
4122
|
-
* `enterTextEdit` action.
|
|
4123
|
-
*
|
|
4124
|
-
* Consumers must register both the `insert` dep (e.g. through `SceneCanvas`'s
|
|
4125
|
-
* `useInsertDepSource`) and the `textEdit` dep (`enterTextEditAction`'s
|
|
4126
|
-
* contract — see `src/interactions/actions/defaults/enterTextEdit.ts`).
|
|
4127
|
-
*
|
|
4128
|
-
* Note: `pointInsert`, `hitExisting`, `marqueeStyle`, `minBounds` remain on
|
|
4129
|
-
* the option surface for forward-compat but are currently ignored — they map
|
|
4130
|
-
* onto dispatcher-side features that are wired through dep sources, not
|
|
4131
|
-
* through the tool record. */
|
|
4132
|
-
declare function useTextTool<TNode extends {
|
|
4133
|
-
id: string;
|
|
4134
|
-
}>(_options: UseTextToolOptions<TNode>): Tool<undefined>;
|
|
2337
|
+
/** Text tool. Drag-rect insertion is owned end-to-end by the dispatcher's
|
|
2338
|
+
* `insertAction` (kind: `'text'`); click-on-selected-text-node enters edit
|
|
2339
|
+
* mode via the `enterTextEdit` action.
|
|
2340
|
+
*
|
|
2341
|
+
* Consumers must register both the `insert` dep (e.g. through
|
|
2342
|
+
* `SceneCanvas`'s `useInsertDepSource`) and the `textEdit` dep
|
|
2343
|
+
* (`enterTextEditAction`'s contract — see
|
|
2344
|
+
* `src/interactions/actions/defaults/enterTextEdit.ts`). Custom node
|
|
2345
|
+
* factories and hit gating belong on those deps, not on the tool. */
|
|
2346
|
+
declare function useTextTool(): Tool<undefined>;
|
|
4135
2347
|
|
|
4136
2348
|
interface PinchZoomToolOpts {
|
|
4137
2349
|
min?: number;
|
|
@@ -4147,51 +2359,13 @@ interface PinchZoomToolOpts {
|
|
|
4147
2359
|
*/
|
|
4148
2360
|
declare function usePinchZoomTool(canvasRef: React.RefObject<HTMLCanvasElement | null>, view: View, setView: (v: View) => void, opts?: PinchZoomToolOpts): void;
|
|
4149
2361
|
|
|
4150
|
-
/**
|
|
4151
|
-
* Counts the path anchors used by the per-anchor coloring surface. An
|
|
4152
|
-
* "anchor" is the destination point of a path command: M, L, C, Q each
|
|
4153
|
-
* contribute one (the (x, y) where the pen ends up); Z contributes none
|
|
4154
|
-
* (it closes back to the subpath's first M). RectPath has four implicit
|
|
4155
|
-
* anchors — the corners — matching its M/L/L/L/Z stroke tessellation.
|
|
4156
|
-
*
|
|
4157
|
-
* Consumers use this to size their per-anchor color array; the renderer
|
|
4158
|
-
* uses it to validate the array length in dev builds.
|
|
4159
|
-
*/
|
|
4160
|
-
|
|
4161
|
-
interface PenAnchor$1 {
|
|
4162
|
-
x: number;
|
|
4163
|
-
y: number;
|
|
4164
|
-
inHandle?: {
|
|
4165
|
-
x: number;
|
|
4166
|
-
y: number;
|
|
4167
|
-
};
|
|
4168
|
-
outHandle?: {
|
|
4169
|
-
x: number;
|
|
4170
|
-
y: number;
|
|
4171
|
-
};
|
|
4172
|
-
}
|
|
4173
|
-
/**
|
|
4174
|
-
* Derive a per-subpath anchor model from a PolygonPath. Subpaths split on
|
|
4175
|
-
* every `M` command; a subpath is closed iff it ends with `Z`.
|
|
4176
|
-
*
|
|
4177
|
-
* Cubic-segment control points become the outHandle of the previous anchor
|
|
4178
|
-
* and the inHandle of the next anchor. Quadratic segments are upgraded to
|
|
4179
|
-
* cubics (each control reused for both adjacent handles) — this loses no
|
|
4180
|
-
* geometry. Linear segments produce anchors with no handles.
|
|
4181
|
-
*/
|
|
4182
|
-
declare function pathToAnchors(path: PolygonPath): {
|
|
4183
|
-
anchors: PenAnchor$1[][];
|
|
4184
|
-
closed: boolean[];
|
|
4185
|
-
};
|
|
4186
|
-
declare function countPathAnchors(path: Path): number;
|
|
4187
|
-
|
|
4188
2362
|
/**
|
|
4189
2363
|
* In-progress pen anchor. `outHandle` is set when the anchor was placed via
|
|
4190
2364
|
* click-drag (smooth anchor); undefined for click-placed corners.
|
|
4191
2365
|
* `inHandle` is mirrored from the previous anchor's outHandle on segment
|
|
4192
2366
|
* emission unless `altBroken` is set on that previous anchor.
|
|
4193
2367
|
*/
|
|
4194
|
-
interface PenAnchor {
|
|
2368
|
+
interface PenAnchor$1 {
|
|
4195
2369
|
x: number;
|
|
4196
2370
|
y: number;
|
|
4197
2371
|
outHandle?: {
|
|
@@ -4207,57 +2381,32 @@ interface PenAnchor {
|
|
|
4207
2381
|
altBroken?: boolean;
|
|
4208
2382
|
}
|
|
4209
2383
|
interface PenSubpath {
|
|
4210
|
-
anchors: PenAnchor[];
|
|
2384
|
+
anchors: PenAnchor$1[];
|
|
4211
2385
|
closed: boolean;
|
|
4212
2386
|
}
|
|
4213
|
-
/**
|
|
4214
|
-
*
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
* only its own gesture, not the whole edit session. Null between
|
|
4236
|
-
* gestures. */
|
|
4237
|
-
gestureBaseline: {
|
|
4238
|
-
path: unknown;
|
|
4239
|
-
closed: boolean;
|
|
4240
|
-
params: unknown;
|
|
4241
|
-
} | null;
|
|
4242
|
-
/** In-flight marquee rect (world-space). Null when not dragging. */
|
|
4243
|
-
marquee: {
|
|
4244
|
-
x0: number;
|
|
4245
|
-
y0: number;
|
|
4246
|
-
x1: number;
|
|
4247
|
-
y1: number;
|
|
4248
|
-
additive: boolean;
|
|
4249
|
-
} | null;
|
|
4250
|
-
}
|
|
4251
|
-
/** Mutable scratch shared across pen-tool gestures. The hook keeps a stable
|
|
4252
|
-
* reference to a single instance and `initScratch` returns it on every call,
|
|
4253
|
-
* so click-by-click state survives gesture boundaries and the preview layer
|
|
4254
|
-
* can read the same object. */
|
|
2387
|
+
/**
|
|
2388
|
+
* Mutable scratch shared across pen-tool gestures. The hook keeps a stable
|
|
2389
|
+
* reference to a single instance and `initScratch` returns it on every call,
|
|
2390
|
+
* so click-by-click state survives gesture boundaries and the preview layer
|
|
2391
|
+
* can read the same object.
|
|
2392
|
+
*
|
|
2393
|
+
* ## The pen creates paths and nothing else
|
|
2394
|
+
*
|
|
2395
|
+
* Reshaping an existing path is anchor editing: double-click a path to
|
|
2396
|
+
* enter edit mode, then use the `editAnchors` / `insertPathAnchor` /
|
|
2397
|
+
* `nudgeAnchors` / `deleteAnchors` / `marqueeAnchors` / `cutPathAtAnchor`
|
|
2398
|
+
* Actions.
|
|
2399
|
+
*
|
|
2400
|
+
* The pen used to carry a second, private implementation of all of that,
|
|
2401
|
+
* with its own scratch mode, hit-test override, overlay, and undo
|
|
2402
|
+
* plumbing. It was reachable only through a `getPathObj` option whose
|
|
2403
|
+
* contract required `pose.kind` to be `'polygon'` or `'rect'` — which no
|
|
2404
|
+
* kit-created node has, including the ones the pen itself creates. So it
|
|
2405
|
+
* was dead in every consumer while shadowing the live implementation, and
|
|
2406
|
+
* the two had drifted. Its geometry now lives in
|
|
2407
|
+
* `features/paths/anchorEdits.ts`, driven by those Actions.
|
|
2408
|
+
*/
|
|
4255
2409
|
interface PenScratch {
|
|
4256
|
-
/** Whether the pen is in create (draw new path) or edit (reshape existing
|
|
4257
|
-
* path) mode. Defaults to 'create'. */
|
|
4258
|
-
mode: 'create' | 'edit';
|
|
4259
|
-
/** Edit-mode state, populated when `mode === 'edit'`. Null in create mode. */
|
|
4260
|
-
edit: PenEditState | null;
|
|
4261
2410
|
finishedSubpaths: PenSubpath[];
|
|
4262
2411
|
current: PenSubpath | null;
|
|
4263
2412
|
cursor: {
|
|
@@ -4266,23 +2415,6 @@ interface PenScratch {
|
|
|
4266
2415
|
} | null;
|
|
4267
2416
|
draggingHandleAt: number | null;
|
|
4268
2417
|
closeHintActive: boolean;
|
|
4269
|
-
/** Pointer-down world coords + modifiers, captured on every pointer.onDown.
|
|
4270
|
-
* Used by drag.onStart (anchor lands at the down coords, not the
|
|
4271
|
-
* threshold-crossing coords) and by pointer.onClick. Internal. */
|
|
4272
|
-
_pendingDown: {
|
|
4273
|
-
worldX: number;
|
|
4274
|
-
worldY: number;
|
|
4275
|
-
alt: boolean;
|
|
4276
|
-
shift: boolean;
|
|
4277
|
-
} | null;
|
|
4278
|
-
/** Timestamp + world coords of the most recent click, used to detect a
|
|
4279
|
-
* double-click on the last placed anchor (Illustrator convention for
|
|
4280
|
-
* open-finish). Internal. */
|
|
4281
|
-
_lastClick: {
|
|
4282
|
-
t: number;
|
|
4283
|
-
x: number;
|
|
4284
|
-
y: number;
|
|
4285
|
-
} | null;
|
|
4286
2418
|
}
|
|
4287
2419
|
interface UsePenToolOptions<TPose> {
|
|
4288
2420
|
/** Wrap a finished PolygonPath in the consumer's pose type. */
|
|
@@ -4293,9 +2425,6 @@ interface UsePenToolOptions<TPose> {
|
|
|
4293
2425
|
adapter: {
|
|
4294
2426
|
addNode: (pose: TPose) => string;
|
|
4295
2427
|
setSelection: (ids: string[]) => void;
|
|
4296
|
-
/** Apply an op batch (with optional history label). Required for pen-edit;
|
|
4297
|
-
* safe to omit if the consumer only uses create mode. */
|
|
4298
|
-
applyOps?: (ops: Op[], label: string) => void;
|
|
4299
2428
|
};
|
|
4300
2429
|
/** Auto-select the new object after commit. Default `true`. */
|
|
4301
2430
|
autoSelect?: boolean;
|
|
@@ -4326,36 +2455,9 @@ interface UsePenToolOptions<TPose> {
|
|
|
4326
2455
|
x: number;
|
|
4327
2456
|
y: number;
|
|
4328
2457
|
}) => {
|
|
4329
|
-
x: number;
|
|
4330
|
-
y: number;
|
|
4331
|
-
};
|
|
4332
|
-
/** Resolve a path obj from id. Returns the obj's path, closed flag, params,
|
|
4333
|
-
* and the tool that created it (used to decide whether the trapdoor applies).
|
|
4334
|
-
* Required when pen-edit is wanted; safe to omit if the consumer only uses
|
|
4335
|
-
* create mode (dblclick-to-edit will be a no-op). */
|
|
4336
|
-
getPathObj?: (id: string) => {
|
|
4337
|
-
path: PolygonPath | {
|
|
4338
|
-
kind: 'rect';
|
|
4339
|
-
x: number;
|
|
4340
|
-
y: number;
|
|
4341
|
-
width: number;
|
|
4342
|
-
height: number;
|
|
4343
|
-
};
|
|
4344
|
-
closed: boolean;
|
|
4345
|
-
params: unknown;
|
|
4346
|
-
tool: string;
|
|
4347
|
-
} | null;
|
|
4348
|
-
}
|
|
4349
|
-
interface UsePenToolReturn {
|
|
4350
|
-
tool: Tool<PenScratch>;
|
|
4351
|
-
/**
|
|
4352
|
-
* True when the pen is in edit mode (reshaping an existing path). Consumers
|
|
4353
|
-
* should apply a CSS class like `pen-edit-active` to their canvas container
|
|
4354
|
-
* when `isEditing` is true, to give users a visual cue that they're in edit
|
|
4355
|
-
* mode (e.g. a subtle background tint). The kit does not own the DOM and
|
|
4356
|
-
* ships no default stylesheet for this.
|
|
4357
|
-
*/
|
|
4358
|
-
isEditing: boolean;
|
|
2458
|
+
x: number;
|
|
2459
|
+
y: number;
|
|
2460
|
+
};
|
|
4359
2461
|
}
|
|
4360
2462
|
/**
|
|
4361
2463
|
* Active-slot Tool: click + drag to build a `PolygonPath` Illustrator-style.
|
|
@@ -4367,12 +2469,9 @@ interface UsePenToolReturn {
|
|
|
4367
2469
|
* discards. Shift constrains the placement-drag handle to 0/45/90/135°;
|
|
4368
2470
|
* Alt during drag breaks the handle mirror for the next segment.
|
|
4369
2471
|
*
|
|
4370
|
-
*
|
|
4371
|
-
* `isEditing` boolean. Apply a CSS class like `pen-edit-active` to your canvas
|
|
4372
|
-
* container when `isEditing` is true for a visual cue. The kit owns no DOM and
|
|
4373
|
-
* ships no default stylesheet for this.
|
|
2472
|
+
* Reshaping an existing path is not this tool's job — see {@link PenScratch}.
|
|
4374
2473
|
*/
|
|
4375
|
-
declare function usePenTool<TPose>(options: UsePenToolOptions<TPose>):
|
|
2474
|
+
declare function usePenTool<TPose>(options: UsePenToolOptions<TPose>): Tool<PenScratch>;
|
|
4376
2475
|
declare namespace usePenTool {
|
|
4377
2476
|
var prefs: {
|
|
4378
2477
|
name: string;
|
|
@@ -4388,80 +2487,28 @@ declare namespace usePenTool {
|
|
|
4388
2487
|
};
|
|
4389
2488
|
}
|
|
4390
2489
|
|
|
4391
|
-
interface RectBounds {
|
|
4392
|
-
x: number;
|
|
4393
|
-
y: number;
|
|
4394
|
-
width: number;
|
|
4395
|
-
height: number;
|
|
4396
|
-
}
|
|
4397
|
-
interface UseRectToolOptions<TNode extends {
|
|
4398
|
-
id: string;
|
|
4399
|
-
}> {
|
|
4400
|
-
create: (bounds: RectBounds) => TNode | null;
|
|
4401
|
-
label?: string;
|
|
4402
|
-
minBounds?: {
|
|
4403
|
-
width: number;
|
|
4404
|
-
height: number;
|
|
4405
|
-
};
|
|
4406
|
-
overlayStyle?: InsertOverlayStyle;
|
|
4407
|
-
/** Optional: snap world-space points to the active grid (or any other
|
|
4408
|
-
* snap target). Applied to every coord the gesture ingests, so both the
|
|
4409
|
-
* live overlay and the committed geometry use the snapped values. */
|
|
4410
|
-
snapPoint?: (p: {
|
|
4411
|
-
x: number;
|
|
4412
|
-
y: number;
|
|
4413
|
-
}) => {
|
|
4414
|
-
x: number;
|
|
4415
|
-
y: number;
|
|
4416
|
-
};
|
|
4417
|
-
}
|
|
4418
2490
|
/**
|
|
4419
|
-
* Drag-to-draw rectangle tool.
|
|
4420
|
-
* factory is called with the final bounds and the returned object is inserted
|
|
4421
|
-
* into the scene via an undoable op.
|
|
2491
|
+
* Drag-to-draw rectangle tool.
|
|
4422
2492
|
*
|
|
4423
|
-
*
|
|
4424
|
-
* `
|
|
2493
|
+
* The gesture is owned end-to-end by the dispatcher: the `drag` binding
|
|
2494
|
+
* routes to `insertAction`, which tracks the live bounds, paints the
|
|
2495
|
+
* preview through its `overlay()` surface, and commits via the `insert`
|
|
2496
|
+
* dep on release. `<SceneCanvas>` sources that dep from
|
|
2497
|
+
* `useInsertDepSource`; consumers wanting a custom node factory override
|
|
2498
|
+
* the dep (`useDepSource('insert', …)`) rather than the tool.
|
|
2499
|
+
*
|
|
2500
|
+
* Grid snapping comes from the `snap` dep, also read by `insertAction` —
|
|
2501
|
+
* see `SnapDep` in `interactions/actions/depSchema.ts`.
|
|
4425
2502
|
*/
|
|
4426
|
-
declare function useRectTool<
|
|
4427
|
-
id: string;
|
|
4428
|
-
}>(options: UseRectToolOptions<TNode>): Tool<null>;
|
|
2503
|
+
declare function useRectTool(): Tool<null>;
|
|
4429
2504
|
|
|
4430
|
-
interface EllipseBounds {
|
|
4431
|
-
x: number;
|
|
4432
|
-
y: number;
|
|
4433
|
-
width: number;
|
|
4434
|
-
height: number;
|
|
4435
|
-
}
|
|
4436
|
-
interface UseEllipseToolOptions<TNode extends {
|
|
4437
|
-
id: string;
|
|
4438
|
-
}> {
|
|
4439
|
-
create: (bounds: EllipseBounds) => TNode | null;
|
|
4440
|
-
label?: string;
|
|
4441
|
-
minBounds?: {
|
|
4442
|
-
width: number;
|
|
4443
|
-
height: number;
|
|
4444
|
-
};
|
|
4445
|
-
overlayStyle?: InsertOverlayStyle;
|
|
4446
|
-
/** Optional: snap world-space points to the active grid (or any other
|
|
4447
|
-
* snap target). Applied to every coord the gesture ingests, so both the
|
|
4448
|
-
* live overlay and the committed geometry use the snapped values. */
|
|
4449
|
-
snapPoint?: (p: {
|
|
4450
|
-
x: number;
|
|
4451
|
-
y: number;
|
|
4452
|
-
}) => {
|
|
4453
|
-
x: number;
|
|
4454
|
-
y: number;
|
|
4455
|
-
};
|
|
4456
|
-
}
|
|
4457
2505
|
/**
|
|
4458
|
-
* Drag-to-draw ellipse tool.
|
|
4459
|
-
*
|
|
4460
|
-
*
|
|
2506
|
+
* Drag-to-draw ellipse tool. Mirrors `useRectTool`: the `drag` binding
|
|
2507
|
+
* routes to `insertAction`, which owns the live preview and commits
|
|
2508
|
+
* through the `insert` dep. Alt toggles from-corner ⇄ from-center
|
|
2509
|
+
* mid-drag via the action's live modifier read.
|
|
4461
2510
|
*/
|
|
4462
|
-
declare function useEllipseTool<
|
|
4463
|
-
id: string;
|
|
4464
|
-
}>(options: UseEllipseToolOptions<TNode>): Tool<null>;
|
|
2511
|
+
declare function useEllipseTool(): Tool<null>;
|
|
4465
2512
|
|
|
4466
2513
|
interface UseImageToolOptions {
|
|
4467
2514
|
/** Image source for inserted nodes — a URL, a `blob:` URL, or a
|
|
@@ -4488,7 +2535,8 @@ interface UseEyedropperToolOptions {
|
|
|
4488
2535
|
* on a real hit; empty-click is a no-op in v1. */
|
|
4489
2536
|
onPick: (color: string | null) => void;
|
|
4490
2537
|
/** Map a node id to a color string, or `null` if the node has no color
|
|
4491
|
-
* to sample. Called on click with
|
|
2538
|
+
* to sample. Called on click with the id of the topmost node under the
|
|
2539
|
+
* click point. */
|
|
4492
2540
|
colorOf: (id: string) => string | null;
|
|
4493
2541
|
/** Override the default `{ key: 'I' }` keybinding. Pass `null` to omit
|
|
4494
2542
|
* the keybinding entirely (palette-only or hotkey-only wiring). */
|
|
@@ -4504,6 +2552,10 @@ interface UseEyedropperToolOptions {
|
|
|
4504
2552
|
*
|
|
4505
2553
|
* Pure-read tool — does NOT mutate the scene. v1 is click-only; drag is
|
|
4506
2554
|
* unbound (a future drag-to-sample option is additive).
|
|
2555
|
+
*
|
|
2556
|
+
* The node under the click comes from the `nodeAtPoint` dep, so an
|
|
2557
|
+
* eyedropper in a consumer that never registered that dep samples nothing.
|
|
2558
|
+
* `<SceneCanvas>` sources it from the same picker the rest of the kit hits.
|
|
4507
2559
|
*/
|
|
4508
2560
|
declare function useEyedropperTool(opts: UseEyedropperToolOptions): Tool<null>;
|
|
4509
2561
|
|
|
@@ -4511,39 +2563,19 @@ interface LinePoint {
|
|
|
4511
2563
|
x: number;
|
|
4512
2564
|
y: number;
|
|
4513
2565
|
}
|
|
4514
|
-
interface UseLineToolOptions<TNode extends {
|
|
4515
|
-
id: string;
|
|
4516
|
-
}> {
|
|
4517
|
-
create: (a: LinePoint, b: LinePoint) => TNode | null;
|
|
4518
|
-
label?: string;
|
|
4519
|
-
minLength?: number;
|
|
4520
|
-
/** Optional: snap world-space points to the active grid (or any other
|
|
4521
|
-
* snap target). Applied to every coord the gesture ingests, so both the
|
|
4522
|
-
* live overlay and the committed endpoints use the snapped values.
|
|
4523
|
-
*
|
|
4524
|
-
* Order with modifiers: the Shift-constrain-to-15° branch runs on raw
|
|
4525
|
-
* coords first, then the result is snapped — this preserves the user
|
|
4526
|
-
* intent (constrain the angle), then aligns the endpoint to grid. */
|
|
4527
|
-
snapPoint?: (p: LinePoint) => LinePoint;
|
|
4528
|
-
}
|
|
4529
|
-
/** @internal */
|
|
4530
|
-
interface LineScratch {
|
|
4531
|
-
start: LinePoint;
|
|
4532
|
-
current: LinePoint;
|
|
4533
|
-
/** Modifier snapshot captured on the latest move — used by the live
|
|
4534
|
-
* overlay so the ghost reflects shift-snap / alt-mirror in real time. */
|
|
4535
|
-
shift: boolean;
|
|
4536
|
-
alt: boolean;
|
|
4537
|
-
}
|
|
4538
2566
|
/**
|
|
4539
|
-
* Click-down → drag → release
|
|
4540
|
-
* `
|
|
2567
|
+
* Click-down → drag → release line tool. The `drag` binding routes to
|
|
2568
|
+
* `insertAction`, which owns the live preview and commits through the
|
|
2569
|
+
* `insert` dep with `{ kind: 'line', a, b }` endpoints (not the AABB
|
|
2570
|
+
* diagonal), so the drag direction is preserved.
|
|
2571
|
+
*
|
|
2572
|
+
* Modifiers are applied by `insertAction`:
|
|
4541
2573
|
* - shift: constrain to 15° increments
|
|
4542
|
-
* - alt: mirror end around start (drag is treated as
|
|
2574
|
+
* - alt: mirror the end around the start (the drag is treated as a
|
|
2575
|
+
* half-line) — the line-specific reading of the action's
|
|
2576
|
+
* corner ⇄ center origin toggle
|
|
4543
2577
|
*/
|
|
4544
|
-
declare function useLineTool<
|
|
4545
|
-
id: string;
|
|
4546
|
-
}>(options: UseLineToolOptions<TNode>): Tool<LineScratch | null>;
|
|
2578
|
+
declare function useLineTool(): Tool<null>;
|
|
4547
2579
|
|
|
4548
2580
|
/** Option surface for the `lasso-select` action.
|
|
4549
2581
|
*
|
|
@@ -4652,54 +2684,31 @@ declare function useStarTool<TNode extends {
|
|
|
4652
2684
|
* `pressure: 0.5` while a button is held, `0` otherwise (per the Pointer
|
|
4653
2685
|
* Events spec), so a consumer that wants stylus-only modulation should
|
|
4654
2686
|
* gate on `pointerType` from the originating event — the kit exposes
|
|
4655
|
-
* `usePointerStylus()` for that.
|
|
2687
|
+
* `usePointerStylus()` for that.
|
|
2688
|
+
*
|
|
2689
|
+
* The dispatcher accumulates these on the drag trail and hands the whole
|
|
2690
|
+
* array to `insertAction`, which forwards it as the `pencil` insert
|
|
2691
|
+
* extras' `samples`. A consumer that wants pressure-driven
|
|
2692
|
+
* `Stroke.vertexWidths` reads it off the samples in its own `insert` dep
|
|
2693
|
+
* (`useDepSource('insert', …)`) — see `apps/site/demos/VertexWidthsDemo.tsx`.
|
|
2694
|
+
*/
|
|
4656
2695
|
interface PencilPoint {
|
|
4657
2696
|
x: number;
|
|
4658
2697
|
y: number;
|
|
4659
|
-
/** 0..1.
|
|
4660
|
-
* treat samples as `{x,y}` keep working. */
|
|
2698
|
+
/** 0..1. Absent when the originating event carried no pressure. */
|
|
4661
2699
|
pressure?: number;
|
|
4662
2700
|
/** Degrees, ±90. Zero for mouse/touch. */
|
|
4663
2701
|
tiltX?: number;
|
|
4664
2702
|
/** Degrees, ±90. Zero for mouse/touch. */
|
|
4665
2703
|
tiltY?: number;
|
|
4666
|
-
/** Per-sample stroke width, populated when the tool's
|
|
4667
|
-
* `pressureToWidth` option is set. Consumers can read these widths to
|
|
4668
|
-
* build a parallel `vertexWidths` array for a tapered stroke. */
|
|
4669
|
-
width?: number;
|
|
4670
|
-
}
|
|
4671
|
-
interface UsePencilToolOptions<TNode extends {
|
|
4672
|
-
id: string;
|
|
4673
|
-
}> {
|
|
4674
|
-
create: (path: PolygonPath, opts: {
|
|
4675
|
-
closed: boolean;
|
|
4676
|
-
widths?: number[];
|
|
4677
|
-
}) => TNode | null;
|
|
4678
|
-
label?: string;
|
|
4679
|
-
tolerance?: number;
|
|
4680
|
-
closeThreshold?: number;
|
|
4681
|
-
/** Optional callback that maps a captured sample to a stroke width.
|
|
4682
|
-
* When provided, every PencilPoint gets a `width` field and the
|
|
4683
|
-
* consumer's `create` factory also receives `widths`: one width per
|
|
4684
|
-
* output bezier anchor, ready to drop into `Stroke.vertexWidths`. The
|
|
4685
|
-
* default `pressureToWidth(p, { minWidth, maxWidth, gamma })` helper
|
|
4686
|
-
* is a common drop-in. */
|
|
4687
|
-
pressureToWidth?: (sample: PencilPoint) => number;
|
|
4688
|
-
}
|
|
4689
|
-
/** @internal */
|
|
4690
|
-
interface PencilScratch {
|
|
4691
|
-
samples: PencilPoint[];
|
|
4692
2704
|
}
|
|
4693
2705
|
/**
|
|
4694
|
-
* Freehand pencil tool.
|
|
4695
|
-
*
|
|
4696
|
-
*
|
|
4697
|
-
* `
|
|
4698
|
-
* its path.
|
|
2706
|
+
* Freehand pencil tool. The `drag` binding routes to `insertAction`,
|
|
2707
|
+
* which accumulates the pointer trail and commits it as
|
|
2708
|
+
* `{ kind: 'pencil', samples }`. The kit's default `insert` dep runs
|
|
2709
|
+
* `schneiderFit` over the samples to produce a cubic-Bezier path.
|
|
4699
2710
|
*/
|
|
4700
|
-
declare function usePencilTool<
|
|
4701
|
-
id: string;
|
|
4702
|
-
}>(options: UsePencilToolOptions<TNode>): Tool<PencilScratch | null>;
|
|
2711
|
+
declare function usePencilTool(): Tool<null>;
|
|
4703
2712
|
|
|
4704
2713
|
/** Standard slot names — render in this canonical order.
|
|
4705
2714
|
* `cellHighlight` is internal: emitted from the `grid` slot's nested
|
|
@@ -4743,9 +2752,24 @@ interface CanvasExtensionApi {
|
|
|
4743
2752
|
readonly element: HTMLCanvasElement | null;
|
|
4744
2753
|
requestRedraw(): void;
|
|
4745
2754
|
/** Register an externally-owned RenderLayer. The layer participates in the
|
|
4746
|
-
* draw stack and, if it implements `hitTest`, in
|
|
4747
|
-
* pipeline (see `src/tools/dispatcher.ts`'s `getHitTestContext`). */
|
|
2755
|
+
* draw stack and, if it implements `hitTest`, in {@link hitTestExtras}. */
|
|
4748
2756
|
registerLayer(layer: RenderLayer<unknown>): () => void;
|
|
2757
|
+
/**
|
|
2758
|
+
* Hit-test the registered layers (topmost-first, last-registered wins) at a
|
|
2759
|
+
* world-space point. Returns the id of the layer that claimed the point and
|
|
2760
|
+
* whatever its `hitTest` resolved, or `null` when none did.
|
|
2761
|
+
*
|
|
2762
|
+
* `<SceneCanvas>` folds this into the `affordanceAt` thunk it hands the
|
|
2763
|
+
* gesture dispatcher — ahead of the kit's own selection chrome, since
|
|
2764
|
+
* registered layers draw on top — so a hit surfaces to actions as an
|
|
2765
|
+
* `AffordanceHit` with kind `layer:<id>` and the binding's `initialScratch`
|
|
2766
|
+
* as its `payload`. A layer's owner binds a `kindOf` predicate on that kind
|
|
2767
|
+
* to claim the gesture; see `@weasel-js/hud` for the worked example.
|
|
2768
|
+
*/
|
|
2769
|
+
hitTestExtras(worldX: number, worldY: number): {
|
|
2770
|
+
layerId: string;
|
|
2771
|
+
binding: AffordanceBinding;
|
|
2772
|
+
} | null;
|
|
4749
2773
|
/** Feed external content into the ingestion pipeline imperatively — the
|
|
4750
2774
|
* same content-handler registry that OS drop and clipboard paste hit.
|
|
4751
2775
|
* `input` may be raw `File[]` (e.g. from `openFilePicker`) or
|
|
@@ -4775,6 +2799,52 @@ interface SceneCanvasApi extends CanvasExtensionApi {
|
|
|
4775
2799
|
}): void;
|
|
4776
2800
|
}
|
|
4777
2801
|
|
|
2802
|
+
/**
|
|
2803
|
+
* Union math behind `CanvasHelpers.getGestureBounds()`.
|
|
2804
|
+
*
|
|
2805
|
+
* `getGestureBounds` answers "where is the thing the user is dragging right
|
|
2806
|
+
* now, in total?" — a question every other lookup on `CanvasHelpers` can't
|
|
2807
|
+
* answer because they're keyed by node id, and a drag-to-insert has no node
|
|
2808
|
+
* yet. Canvas collects the parts (preview bounds per in-flight id, plus any
|
|
2809
|
+
* id-less nascent-insert AABBs) and folds them here.
|
|
2810
|
+
*
|
|
2811
|
+
* Kept free of tool / dispatcher types so it stays a pure geometry fold that
|
|
2812
|
+
* both `<Canvas>` and its tests can call without a render.
|
|
2813
|
+
*/
|
|
2814
|
+
|
|
2815
|
+
/**
|
|
2816
|
+
* Everything `<Canvas>` needs to know about in-flight gestures that it can't
|
|
2817
|
+
* see for itself.
|
|
2818
|
+
*
|
|
2819
|
+
* `<Canvas>` is deliberately dispatcher-agnostic — it owns the union math and
|
|
2820
|
+
* the `CanvasHelpers` contract, but the live gesture state lives in the
|
|
2821
|
+
* gesture dispatcher that `<SceneCanvas>` creates. This is the one seam
|
|
2822
|
+
* between them (`createGestureSource` in `SceneCanvas/dispatcherGestureBounds.ts`
|
|
2823
|
+
* builds it); bare `<Canvas>` consumers leave it unwired and get the
|
|
2824
|
+
* "no gesture in flight" answers.
|
|
2825
|
+
*
|
|
2826
|
+
* @public
|
|
2827
|
+
*/
|
|
2828
|
+
interface GestureSource {
|
|
2829
|
+
/**
|
|
2830
|
+
* Ids the in-flight gesture is previewing, so the canvas can resolve each
|
|
2831
|
+
* one's preview bounds.
|
|
2832
|
+
*
|
|
2833
|
+
* Wider than `previewIdsExtra`, which honors `previewHidesSource: false`
|
|
2834
|
+
* because its job is deciding whose committed paint to suppress: a clone
|
|
2835
|
+
* ghost hides nothing yet still proposes content at the drag target.
|
|
2836
|
+
*/
|
|
2837
|
+
ids(): Iterable<string> | null;
|
|
2838
|
+
/** World-space AABBs of in-flight gestures that have no scene id at all —
|
|
2839
|
+
* a drag-to-insert, pre-commit. */
|
|
2840
|
+
bounds(): Iterable<Bounds> | null;
|
|
2841
|
+
/** Fires whenever the above can have changed. See
|
|
2842
|
+
* `CanvasHelpers.subscribeGestures` for the published guarantee. */
|
|
2843
|
+
subscribe(fn: () => void): () => void;
|
|
2844
|
+
/** Monotonic counter bumped on the same events `subscribe` fires on. */
|
|
2845
|
+
getVersion(): number;
|
|
2846
|
+
}
|
|
2847
|
+
|
|
4778
2848
|
/**
|
|
4779
2849
|
* Selection overlay primitives — render layers for drawing selection
|
|
4780
2850
|
* outlines and corner resize handles in world space.
|
|
@@ -5146,16 +3216,6 @@ interface CanvasProps<TNode extends {
|
|
|
5146
3216
|
* force-graph renderers or read-only viewers).
|
|
5147
3217
|
*/
|
|
5148
3218
|
selection?: SelectionApi;
|
|
5149
|
-
/**
|
|
5150
|
-
* Fires when the canvas receives a pointer event that the tool dispatcher
|
|
5151
|
-
* did not handle (no active gesture was started on pointerdown, or the
|
|
5152
|
-
* pointer was released without a gesture). Useful for "click on background
|
|
5153
|
-
* clears selection" — `<SceneCanvas>` wires this to `selection.clear()`.
|
|
5154
|
-
*
|
|
5155
|
-
* The listener is only installed when this callback is supplied. Omitting
|
|
5156
|
-
* the prop means no background-click listener is registered.
|
|
5157
|
-
*/
|
|
5158
|
-
onBackgroundClick?: () => void;
|
|
5159
3219
|
/**
|
|
5160
3220
|
* Pose↔bounds projection for non-rect `TPose` types. When supplied, drives
|
|
5161
3221
|
* the default `boundsOf` fallback and the selection-overlay bounds source so
|
|
@@ -5291,6 +3351,14 @@ interface CanvasProps<TNode extends {
|
|
|
5291
3351
|
* through geometry.getBounds(pose).
|
|
5292
3352
|
*/
|
|
5293
3353
|
previewBoundsExtra?: (id: string) => Bounds | null;
|
|
3354
|
+
/**
|
|
3355
|
+
* In-flight gesture state `<Canvas>` can't see for itself. Backs the
|
|
3356
|
+
* `getGestureBounds` / `subscribeGestures` / `getGestureVersion` trio on
|
|
3357
|
+
* `helpersRef`; wired by `<SceneCanvas>` from the gesture dispatcher
|
|
3358
|
+
* (`createGestureSource`). Leaving it unwired is fine — those three then
|
|
3359
|
+
* report "no gesture in flight" and never fire.
|
|
3360
|
+
*/
|
|
3361
|
+
gestureSource?: GestureSource;
|
|
5294
3362
|
/**
|
|
5295
3363
|
* Pinch-zoom DOM listener attachment for the canvas surface. When supplied,
|
|
5296
3364
|
* `<Canvas>` calls `usePinchZoomTool` with `canvasRef` so two-finger pinch
|
|
@@ -5346,19 +3414,6 @@ interface CanvasProps<TNode extends {
|
|
|
5346
3414
|
* HUD shows the same best-candidate SceneCanvas would pick.
|
|
5347
3415
|
*/
|
|
5348
3416
|
pickBest?: (worldX: number, worldY: number) => string | null;
|
|
5349
|
-
/** Resolves a single hit (id + kind + pose + data) at world coords for the
|
|
5350
|
-
* tool dispatcher. `<SceneCanvas>` synthesizes this from its node-kind
|
|
5351
|
-
* registry and adapter via `makeGetNodeAtPoint`. When omitted, the
|
|
5352
|
-
* dispatcher receives no hit info and tool routing based on `target.kind`
|
|
5353
|
-
* will not fire. Bare-`<Canvas>` consumers that need kind-based routing
|
|
5354
|
-
* should build this with `makeGetNodeAtPoint`. */
|
|
5355
|
-
getNodeAtPoint?: (worldX: number, worldY: number) => {
|
|
5356
|
-
id: string;
|
|
5357
|
-
kind: string;
|
|
5358
|
-
pose: unknown;
|
|
5359
|
-
data: unknown;
|
|
5360
|
-
meta?: Record<string, unknown>;
|
|
5361
|
-
} | null;
|
|
5362
3417
|
/**
|
|
5363
3418
|
* Optional mode-owned decoration layer. When supplied, Canvas inserts it
|
|
5364
3419
|
* between the scene-render slot and the tool-overlay slot so decoration
|
|
@@ -5394,6 +3449,63 @@ interface CanvasHelpers<TPose> {
|
|
|
5394
3449
|
getEffectivePose(id: string): TPose | null;
|
|
5395
3450
|
/** Overlay-aware bounds for `id`. */
|
|
5396
3451
|
getEffectiveBounds(id: string): Bounds | null;
|
|
3452
|
+
/**
|
|
3453
|
+
* World-space AABB of everything the in-flight gesture proposes — the
|
|
3454
|
+
* displaced poses of nodes being moved / resized / rotated / cloned, plus
|
|
3455
|
+
* any nascent insert that has no scene node yet. `null` when no gesture is
|
|
3456
|
+
* in flight.
|
|
3457
|
+
*
|
|
3458
|
+
* This reports the *gesture*, not the document: committed content the
|
|
3459
|
+
* gesture isn't touching is excluded, so a consumer that wants the union
|
|
3460
|
+
* with the rest of the scene still walks its own ids through
|
|
3461
|
+
* `getEffectiveBounds`. It exists because every other lookup here is keyed
|
|
3462
|
+
* by node id, which can't answer "where is the shape the user is drawing
|
|
3463
|
+
* right now" — a drag-to-insert has no id until pointer-up.
|
|
3464
|
+
*
|
|
3465
|
+
* Select-only gestures are deliberately excluded: a marquee or lasso has
|
|
3466
|
+
* geometry but proposes no content, and a consumer sizing itself to the
|
|
3467
|
+
* gesture must not grow because the user swept a selection rectangle.
|
|
3468
|
+
*
|
|
3469
|
+
* The result is a plain AABB — never rotated. Rotated parts are folded in
|
|
3470
|
+
* by their rotated extent (a union of several oriented boxes has no single
|
|
3471
|
+
* orientation to report).
|
|
3472
|
+
*/
|
|
3473
|
+
getGestureBounds(): Bounds | null;
|
|
3474
|
+
/**
|
|
3475
|
+
* Subscribe to the gesture layer's change signal — the other half of the
|
|
3476
|
+
* `useSyncExternalStore` contract for everything on this object that moves
|
|
3477
|
+
* during a drag (`getEffectivePose`, `getEffectiveBounds`,
|
|
3478
|
+
* `getGestureBounds`). Returns an unsubscribe.
|
|
3479
|
+
*
|
|
3480
|
+
* Fires once per dispatcher pump: gesture start, every pointermove that
|
|
3481
|
+
* reaches an in-flight handle, end, and cancel — plus UI-driven ongoing
|
|
3482
|
+
* actions (a slider bound to an ongoing action pumps the same way). It
|
|
3483
|
+
* fires on the pump, not on a diff: a pump that changed nothing observable
|
|
3484
|
+
* still notifies, so don't hang expensive work directly off the callback.
|
|
3485
|
+
*
|
|
3486
|
+
* It does **not** cover committed scene edits (subscribe to the scene for
|
|
3487
|
+
* those) or previews a consumer's own tool publishes from React state
|
|
3488
|
+
* (that tool re-renders on its own).
|
|
3489
|
+
*
|
|
3490
|
+
* Without a gesture source wired — a bare `<Canvas>` — this is a no-op
|
|
3491
|
+
* subscription that never fires.
|
|
3492
|
+
*/
|
|
3493
|
+
subscribeGestures(fn: () => void): () => void;
|
|
3494
|
+
/**
|
|
3495
|
+
* Monotonic counter bumped on exactly the events `subscribeGestures` fires
|
|
3496
|
+
* on. Pair the two for `useSyncExternalStore`:
|
|
3497
|
+
*
|
|
3498
|
+
* ```ts
|
|
3499
|
+
* const gestureVersion = useSyncExternalStore(
|
|
3500
|
+
* useCallback((cb) => helpersRef.current?.subscribeGestures(cb) ?? (() => {}), []),
|
|
3501
|
+
* () => helpersRef.current?.getGestureVersion() ?? 0,
|
|
3502
|
+
* );
|
|
3503
|
+
* ```
|
|
3504
|
+
*
|
|
3505
|
+
* Starts at 0 and only increases. `0` is also what a bare `<Canvas>` with
|
|
3506
|
+
* no gesture source reports, forever.
|
|
3507
|
+
*/
|
|
3508
|
+
getGestureVersion(): number;
|
|
5397
3509
|
/** Returns the live ChromeState built once per render. Affordances and
|
|
5398
3510
|
* custom layers that need overlay-aware selection state (selection ids,
|
|
5399
3511
|
* bounds, multi-union AABB, modifier flags) read from this. */
|
|
@@ -5863,27 +3975,6 @@ interface UseRotateOptions<TPose> {
|
|
|
5863
3975
|
pivot?: 'each' | 'union';
|
|
5864
3976
|
}
|
|
5865
3977
|
|
|
5866
|
-
/** Option surface for the `area-select` action.
|
|
5867
|
-
*
|
|
5868
|
-
* Lives in a sibling file (not `areaSelect.ts`) so the type contract stays
|
|
5869
|
-
* stable even after the legacy `useAreaSelect` hook is gone.
|
|
5870
|
-
* Consumers should import from here directly; `areaSelect.ts`
|
|
5871
|
-
* re-exports the same symbol for back-compat. */
|
|
5872
|
-
|
|
5873
|
-
interface UseAreaSelectOptions {
|
|
5874
|
-
behaviors?: AreaSelectBehavior[];
|
|
5875
|
-
/** When set, overrides any behavior's `defaultTransient`. Default: behaviors decide. */
|
|
5876
|
-
transient?: boolean;
|
|
5877
|
-
/** Label used when transient is false and the hook falls back to applyOps. Default 'Area select'. */
|
|
5878
|
-
label?: string;
|
|
5879
|
-
onGestureStart?: () => void;
|
|
5880
|
-
onGestureEnd?: (committed: boolean) => void;
|
|
5881
|
-
/** Optional debug sink. When supplied, records the in-progress marquee
|
|
5882
|
-
* rectangle as a `bounds` entry under the synthetic id `'area-select'`
|
|
5883
|
-
* on every move. Tree-shakes via optional-chain when omitted. */
|
|
5884
|
-
debug?: DebugSink;
|
|
5885
|
-
}
|
|
5886
|
-
|
|
5887
3978
|
/**
|
|
5888
3979
|
* Built-in shape tool ids handled by `useBuiltinShapeTools`. Each maps to a
|
|
5889
3980
|
* kit tool hook + a default `create` that produces a leaf node compatible
|
|
@@ -5907,10 +3998,13 @@ interface BuiltinToolOptions {
|
|
|
5907
3998
|
mode?: LassoHitMode;
|
|
5908
3999
|
};
|
|
5909
4000
|
/** Snap world-space points to the active grid (or any other snap target).
|
|
5910
|
-
*
|
|
5911
|
-
*
|
|
5912
|
-
*
|
|
5913
|
-
*
|
|
4001
|
+
*
|
|
4002
|
+
* Registered as the `snap` dep, which `insertAction` applies to the
|
|
4003
|
+
* drag start and current point — so the live preview and the committed
|
|
4004
|
+
* geometry agree — and which the pen tool reads for its own anchor
|
|
4005
|
+
* placement. Covers every drag-to-insert tool (rect / ellipse / line /
|
|
4006
|
+
* polygon / star / text); freehand pencil samples are deliberately left
|
|
4007
|
+
* unsnapped. */
|
|
5914
4008
|
snapPoint?: (p: {
|
|
5915
4009
|
x: number;
|
|
5916
4010
|
y: number;
|
|
@@ -6252,10 +4346,6 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
|
|
|
6252
4346
|
rotate?: UseRotateOptions<TPose> | false;
|
|
6253
4347
|
snap?: SnapStrategy<TPose>;
|
|
6254
4348
|
handleHitRadius?: number;
|
|
6255
|
-
/** Marquee area-select. Default: no behaviors (a drag from empty space
|
|
6256
|
-
* doesn't mutate the selection). Pass
|
|
6257
|
-
* `{ behaviors: [selectFromMarquee()] }` to enable rubber-band select. */
|
|
6258
|
-
areaSelect?: UseAreaSelectOptions;
|
|
6259
4349
|
/** Override the body-pick used on click/pointerdown. Alt-aware: receives
|
|
6260
4350
|
* the live alt state + current selection so consumers can implement
|
|
6261
4351
|
* alt-cycling through an overlapping stack. Default: top-most hit
|
|
@@ -6397,15 +4487,6 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
|
|
|
6397
4487
|
* the default select. If you supply your own `tools` prop, this is
|
|
6398
4488
|
* ignored — wire `ambient` through your own `useTools` call instead. */
|
|
6399
4489
|
ambient?: AnyTool[];
|
|
6400
|
-
/** Click-only fallback tool. Its `pointer.onClick` fires only when the
|
|
6401
|
-
* in-flight tool didn't claim the click (e.g. the active select tool
|
|
6402
|
-
* returned `pass` on an empty-space click). Use it for a
|
|
6403
|
-
* click-to-deselect or click-to-spawn behavior that shouldn't interfere
|
|
6404
|
-
* with the active tool's own clicks. Forwarded to the internal
|
|
6405
|
-
* `useTools` as its `fallback` slot. If you supply your own `tools`
|
|
6406
|
-
* prop (takeover form), this is ignored — pass `fallback` to your own
|
|
6407
|
-
* `useTools` call instead. */
|
|
6408
|
-
clickFallback?: AnyTool;
|
|
6409
4490
|
/** Viewport feature wiring.
|
|
6410
4491
|
*
|
|
6411
4492
|
* - `inertia`, `pinchZoom`, `animatedZoom` are opt-in: pass `true`
|
|
@@ -6737,10 +4818,11 @@ declare const inferredNodeRouting: readonly NodeRoutingEntry[];
|
|
|
6737
4818
|
* registry: a declarative schema of the kind's editable properties.
|
|
6738
4819
|
*
|
|
6739
4820
|
* Leaf keys inside `schema` are dotted node paths (`pose.x`,
|
|
6740
|
-
* `data.fill`) —
|
|
6741
|
-
* consumers (weasel-ui `SelectionPanel`)
|
|
6742
|
-
* generically with no per-kind code. Group
|
|
6743
|
-
* only; they do not contribute to the node
|
|
4821
|
+
* `data.fill`, `data.style.fontSize`) — a path of any depth rooted at
|
|
4822
|
+
* `pose` or `data` — so schema consumers (weasel-ui `SelectionPanel`)
|
|
4823
|
+
* can read/aggregate/write generically with no per-kind code. Group
|
|
4824
|
+
* keys are organizational only; they do not contribute to the node
|
|
4825
|
+
* path.
|
|
6744
4826
|
*
|
|
6745
4827
|
* Kind names share the routing trait's vocabulary — an entry registered
|
|
6746
4828
|
* as `'rect'` describes nodes `NodeRouting.classify` maps to `'rect'`.
|
|
@@ -7505,6 +5587,17 @@ declare function createMarkdownRenderer(ctx: CanvasRenderingContext2D, text: str
|
|
|
7505
5587
|
* already matches `style` (use `fontString(style)`).
|
|
7506
5588
|
*/
|
|
7507
5589
|
|
|
5590
|
+
/**
|
|
5591
|
+
* Advance width of `text` in world units, tracking included.
|
|
5592
|
+
*
|
|
5593
|
+
* `letter-spacing` is not part of the CSS `font` shorthand, so a context
|
|
5594
|
+
* whose `font` was set from `fontString(style)` measures glyphs only. The
|
|
5595
|
+
* GL path (`layoutRuns`) adds `letterSpacing` after **every** code point
|
|
5596
|
+
* including the last, matching CSS, so this does too — and every 2D-side
|
|
5597
|
+
* width has to go through here or the two paths disagree about where a line
|
|
5598
|
+
* breaks.
|
|
5599
|
+
*/
|
|
5600
|
+
declare function measuredWidth(ctx: CanvasRenderingContext2D, text: string, style: ResolvedTextStyle): number;
|
|
7508
5601
|
/** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */
|
|
7509
5602
|
interface MeasuredText {
|
|
7510
5603
|
lines: string[];
|
|
@@ -7651,6 +5744,82 @@ interface FitTextPoseOptions {
|
|
|
7651
5744
|
/** Recompute a `TextPose`'s `width`/`height` to fit its content; pure helper, doesn't mutate scene state. */
|
|
7652
5745
|
declare function fitTextPose(ctx: CanvasRenderingContext2D, pose: TextPose, opts?: FitTextPoseOptions): TextPose;
|
|
7653
5746
|
|
|
5747
|
+
/**
|
|
5748
|
+
* The kit-wide "these values disagree" sentinel. Used wherever a value is
|
|
5749
|
+
* aggregated across more than one source and the sources don't agree — e.g.
|
|
5750
|
+
* a UI panel aggregating a property across a multi-node selection, or text
|
|
5751
|
+
* styling aggregated across the runs in a character range. One symbol, one
|
|
5752
|
+
* `isMixed` check, regardless of what's being aggregated.
|
|
5753
|
+
*/
|
|
5754
|
+
declare const MIXED: unique symbol;
|
|
5755
|
+
type Mixed = typeof MIXED;
|
|
5756
|
+
|
|
5757
|
+
/**
|
|
5758
|
+
* Range-addressed styling over `StyledRun[]`. The panel addresses node paths;
|
|
5759
|
+
* a caret addresses a character range — these are the functions for the
|
|
5760
|
+
* second case. Pure and React-free so the semantics are unit-testable, the
|
|
5761
|
+
* same split `SelectionPanel` makes between `model.ts` and its component.
|
|
5762
|
+
*
|
|
5763
|
+
* Character offsets index the concatenated run text (`runsToPlainText`), and
|
|
5764
|
+
* a range is half-open: `[start, end)`. Ranges are clamped, never wrapped —
|
|
5765
|
+
* a negative `start` clamps to 0, an `end` past the text clamps to its
|
|
5766
|
+
* length, and a collapsed or inverted range (`start >= end`) is empty.
|
|
5767
|
+
* Offsets are UTF-16 code units, as everywhere else in this feature, so a
|
|
5768
|
+
* boundary can bisect a surrogate pair (`[1, 2)` over `'a👍b'` splits the
|
|
5769
|
+
* thumbs-up into lone surrogates). A caret-derived range never does this.
|
|
5770
|
+
*
|
|
5771
|
+
* **Run-level flags are additive over the node's `TextStyle`: a run can
|
|
5772
|
+
* turn `bold` / `italic` / `underline` / `strikethrough` on, never off.**
|
|
5773
|
+
* So a flag is stored only when true, and absent reads as `false`. For
|
|
5774
|
+
* `bold` this falls out of the model — node weight is numeric, and
|
|
5775
|
+
* `run.bold ? 700 : baseWeight` has nowhere to put "not bold." For the
|
|
5776
|
+
* boolean decorations it does not: both levels are booleans, so a tri-state
|
|
5777
|
+
* (`true` / `false` / inherit) is expressible and we are collapsing it by
|
|
5778
|
+
* choice, to keep one canonical form per styling. Resolution must therefore
|
|
5779
|
+
* read decorations as `run.underline || style.underline` — a `??` would
|
|
5780
|
+
* make "select a word in an underlined node, hit U to turn it off" look
|
|
5781
|
+
* supported by the type while this file discards the `false` that expresses it.
|
|
5782
|
+
*
|
|
5783
|
+
* Copies here are shallow: a `fill` object is shared by reference between
|
|
5784
|
+
* the input runs, the patch, every run the patch touched, and the value
|
|
5785
|
+
* `styleAtRange` reports. Treat `fill` values as immutable.
|
|
5786
|
+
*/
|
|
5787
|
+
|
|
5788
|
+
/** Every styleable key of a run, each either a concrete value or MIXED (the
|
|
5789
|
+
* runs in the range disagree at that key). */
|
|
5790
|
+
type RangeStyle = {
|
|
5791
|
+
[K in Exclude<keyof StyledRun, 'text'>]?: StyledRun[K] | Mixed;
|
|
5792
|
+
};
|
|
5793
|
+
/**
|
|
5794
|
+
* What `applyStyleToRange` writes. `text` is deliberately not part of it —
|
|
5795
|
+
* this is a styling operation, not an edit.
|
|
5796
|
+
*/
|
|
5797
|
+
type RunStylePatch = Partial<Omit<StyledRun, 'text'>>;
|
|
5798
|
+
/** Every key of a run that carries styling — i.e. everything but `text`. */
|
|
5799
|
+
type StyleKey = Exclude<keyof StyledRun, 'text'>;
|
|
5800
|
+
/**
|
|
5801
|
+
* The styling shared by every run overlapping `[start, end)`: a concrete
|
|
5802
|
+
* value where the range agrees, `MIXED` where it doesn't. Keys no run
|
|
5803
|
+
* in range sets are absent (they inherit the node's `TextStyle`) — except
|
|
5804
|
+
* the additive flags, which read as `false` rather than `undefined` since a
|
|
5805
|
+
* run cannot un-set them. An empty range reads as `{}`. A reported `fill`
|
|
5806
|
+
* is the run's own object, not a copy — treat it as immutable.
|
|
5807
|
+
*/
|
|
5808
|
+
declare function styleAtRange(runs: readonly StyledRun[], start: number, end: number): RangeStyle;
|
|
5809
|
+
/**
|
|
5810
|
+
* Write `patch` over `[start, end)`, splitting runs at the boundaries and
|
|
5811
|
+
* coalescing neighbors that end up identical. A patch value of `undefined`
|
|
5812
|
+
* or `false` deletes the key instead of storing it — see the additive-flags
|
|
5813
|
+
* contract at the top of the file.
|
|
5814
|
+
*
|
|
5815
|
+
* Returns a new array of new runs; neither the input array nor its runs are
|
|
5816
|
+
* mutated (though a nested `fill` is shared by reference). Normalization —
|
|
5817
|
+
* merging identical neighbors, dropping empty runs — applies to the whole
|
|
5818
|
+
* array, not just the patched span, and to every call, including one whose
|
|
5819
|
+
* range is empty and so patches nothing.
|
|
5820
|
+
*/
|
|
5821
|
+
declare function applyStyleToRange(runs: readonly StyledRun[], start: number, end: number, patch: RunStylePatch): StyledRun[];
|
|
5822
|
+
|
|
7654
5823
|
/**
|
|
7655
5824
|
* In-place text editing via a contenteditable overlay positioned over the
|
|
7656
5825
|
* text node's screen-space pose. Enter / blur commit; Shift+Enter inserts a
|
|
@@ -7662,17 +5831,51 @@ declare function fitTextPose(ctx: CanvasRenderingContext2D, pose: TextPose, opts
|
|
|
7662
5831
|
* (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
|
|
7663
5832
|
*/
|
|
7664
5833
|
|
|
5834
|
+
/**
|
|
5835
|
+
* The caret's character range within the text being edited. Half-open
|
|
5836
|
+
* `[start, end)` over the concatenated run text, normalized so `start <= end`
|
|
5837
|
+
* regardless of which way the user dragged. `start === end` is a collapsed
|
|
5838
|
+
* caret — a real position, not the absence of one, which is why the hook
|
|
5839
|
+
* reports `null` rather than a zero-width range when there is no caret.
|
|
5840
|
+
*/
|
|
5841
|
+
interface TextEditSelection {
|
|
5842
|
+
start: number;
|
|
5843
|
+
end: number;
|
|
5844
|
+
}
|
|
7665
5845
|
/** Screen-space pose passed to `useTextEdit` so the overlay can be placed and sized in CSS pixels. */
|
|
7666
5846
|
interface TextEditScreenPose {
|
|
7667
|
-
/** Top-left in CSS pixels relative to `container`.
|
|
5847
|
+
/** Top-left in CSS pixels relative to `container`. Always screen pixels,
|
|
5848
|
+
* including when `zoom` is set — the scale is anchored at this point, not
|
|
5849
|
+
* translated by it. */
|
|
7668
5850
|
x: number;
|
|
7669
5851
|
y: number;
|
|
5852
|
+
/** Pre-scale — CSS pixels, or world units when `zoom` is set. */
|
|
7670
5853
|
width: number;
|
|
7671
5854
|
height: number;
|
|
7672
|
-
/**
|
|
5855
|
+
/** Pre-scale font size: `style.fontSize * zoom` when `zoom` is omitted, the
|
|
5856
|
+
* world-unit `style.fontSize` when it is set. */
|
|
7673
5857
|
fontSize: number;
|
|
7674
5858
|
/** Effective on-screen line height multiplier (defaults to style.lineHeight). */
|
|
7675
5859
|
lineHeight?: number;
|
|
5860
|
+
/**
|
|
5861
|
+
* CSS scale applied to the overlay (`transform: scale(zoom)`, anchored at
|
|
5862
|
+
* its top-left). Every other size on this pose, and every typographic
|
|
5863
|
+
* metric the hook writes, is then **pre-scale** — pass world units and the
|
|
5864
|
+
* transform does the world→screen conversion.
|
|
5865
|
+
*
|
|
5866
|
+
* This is the only way run-level typography can be correct at a zoom other
|
|
5867
|
+
* than 1. `runsToDom` emits run `fontSize` / `letterSpacing` in world units
|
|
5868
|
+
* and `domToRuns` reads them straight back; threading a scale through the
|
|
5869
|
+
* writer and its inverse through the reader would go lossy on fractional
|
|
5870
|
+
* zooms. Scaling the whole overlay instead leaves that serializer pure and
|
|
5871
|
+
* scales node-level and run-level values by the same factor for free.
|
|
5872
|
+
*
|
|
5873
|
+
* Omit it (the default, `1`) and the pose is plain screen pixels — the hook
|
|
5874
|
+
* then infers the world→screen factor from `fontSize / style.fontSize` to
|
|
5875
|
+
* scale node-level `letterSpacing`, and run-level overrides are left
|
|
5876
|
+
* unscaled. Correct only at zoom 1.
|
|
5877
|
+
*/
|
|
5878
|
+
zoom?: number;
|
|
7676
5879
|
}
|
|
7677
5880
|
/** Options for `useTextEdit`. */
|
|
7678
5881
|
interface UseTextEditOptions {
|
|
@@ -7697,9 +5900,27 @@ interface UseTextEditOptions {
|
|
|
7697
5900
|
* Optional: commit rich-text runs back to the node. When omitted, only
|
|
7698
5901
|
* `setText` is called with the plain-text form on commit. When provided,
|
|
7699
5902
|
* commit calls both `setText` (with `runsToPlainText(runs)`) and
|
|
7700
|
-
* `setRuns(id, runs)
|
|
5903
|
+
* `setRuns(id, runs)` — but only when runs are actually in play: the node
|
|
5904
|
+
* already had some, or the edit produced styling. A plain-text edit of a
|
|
5905
|
+
* plain-text node still calls `setText` alone, so a node that has never
|
|
5906
|
+
* been styled doesn't grow a single-run `runs` array just for being edited.
|
|
7701
5907
|
*/
|
|
7702
5908
|
setRuns?: (id: string, runs: StyledRun[]) => void;
|
|
5909
|
+
/**
|
|
5910
|
+
* Is `el` part of the editor's own chrome — a character-options bar, a
|
|
5911
|
+
* color popover, anything whose whole purpose is to style the text being
|
|
5912
|
+
* edited? Focus moving into one does not end the edit.
|
|
5913
|
+
*
|
|
5914
|
+
* Without this, the controls the feature exists for are exactly what
|
|
5915
|
+
* destroys it: clicking a size field blurs the overlay, blur commits, and
|
|
5916
|
+
* the caret the control was about to act on is gone. Toggle buttons can
|
|
5917
|
+
* dodge it by `preventDefault()`-ing their own mousedown, but a field the
|
|
5918
|
+
* user has to type into cannot.
|
|
5919
|
+
*
|
|
5920
|
+
* Chrome focus does not disturb the reported `selection` either — see
|
|
5921
|
+
* `UseTextEditReturn.selection`.
|
|
5922
|
+
*/
|
|
5923
|
+
isEditorChrome?: (el: Element) => boolean;
|
|
7703
5924
|
}
|
|
7704
5925
|
/** Options for `useTextEdit().startEdit`. */
|
|
7705
5926
|
interface StartEditOptions {
|
|
@@ -7716,6 +5937,37 @@ interface UseTextEditReturn {
|
|
|
7716
5937
|
cancelEdit: () => void;
|
|
7717
5938
|
commit: () => void;
|
|
7718
5939
|
isEditing: (id: string) => boolean;
|
|
5940
|
+
/**
|
|
5941
|
+
* The caret's character range, or `null` when nothing is being edited. A
|
|
5942
|
+
* collapsed caret reports `{ start: n, end: n }`, so `null` and "caret at
|
|
5943
|
+
* n" stay distinguishable — a character-styling control routes the
|
|
5944
|
+
* collapsed case to the node's `TextStyle` instead of to a range.
|
|
5945
|
+
*
|
|
5946
|
+
* Follows the DOM selection, which browsers (and jsdom) report from a task
|
|
5947
|
+
* rather than synchronously; anything this hook writes itself updates it
|
|
5948
|
+
* synchronously. A DOM selection that leaves the overlay does **not** clear
|
|
5949
|
+
* it: that is what happens when the user clicks a styling control, and
|
|
5950
|
+
* reporting `null` there would read as "collapsed caret" and send the
|
|
5951
|
+
* control's patch to the node instead of to the range. It is cleared on
|
|
5952
|
+
* `startEdit` and when the edit ends.
|
|
5953
|
+
*/
|
|
5954
|
+
selection: TextEditSelection | null;
|
|
5955
|
+
/**
|
|
5956
|
+
* The styling shared by every run in `selection` — a concrete value where
|
|
5957
|
+
* the range agrees, `MIXED` where it doesn't. `null` exactly when
|
|
5958
|
+
* `selection` is `null`. A collapsed caret reports `{}`: no run is in
|
|
5959
|
+
* range, so the range reader has nothing to say and the node's style is
|
|
5960
|
+
* what applies.
|
|
5961
|
+
*/
|
|
5962
|
+
rangeStyle: RangeStyle | null;
|
|
5963
|
+
/**
|
|
5964
|
+
* Write `patch` over `selection`. A no-op with no active edit, with a
|
|
5965
|
+
* collapsed caret (there is no range to style — patch the node's
|
|
5966
|
+
* `TextStyle` instead), or with an empty patch. The caret survives, so a
|
|
5967
|
+
* second style can be applied without re-selecting, and `rangeStyle`
|
|
5968
|
+
* reflects the write before this returns.
|
|
5969
|
+
*/
|
|
5970
|
+
applyStyleToSelection: (patch: RunStylePatch) => void;
|
|
7719
5971
|
}
|
|
7720
5972
|
/** In-place text editing via a contenteditable overlay positioned over the text node's screen-space pose. */
|
|
7721
5973
|
declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
|
|
@@ -7748,8 +6000,10 @@ declare function unionBounds<TPose extends RectPose>(poses: Iterable<TPose>): Re
|
|
|
7748
6000
|
* (`getText` / `getStyle` / `getRuns` / `setText` / `setRuns`).
|
|
7749
6001
|
*
|
|
7750
6002
|
* Pose component: the helper reads `(x, y, width, height)` straight off
|
|
7751
|
-
* the node's pose (typed `RectPose`).
|
|
7752
|
-
*
|
|
6003
|
+
* the node's pose (typed `RectPose`). Pass `view` and it projects that box
|
|
6004
|
+
* through the viewport; omit it and world units are handed through as
|
|
6005
|
+
* screen pixels, which is correct only for an unpanned, unzoomed canvas.
|
|
6006
|
+
* Consumers with non-rect poses should drop down to raw `useTextEdit` and
|
|
7753
6007
|
* supply their own `getScreenPose`.
|
|
7754
6008
|
*/
|
|
7755
6009
|
|
|
@@ -7775,6 +6029,25 @@ interface UseSceneTextEditOptions<TData> {
|
|
|
7775
6029
|
setRuns?: (data: TData, runs: StyledRun[]) => TData;
|
|
7776
6030
|
/** Fallback fontSize when `style.fontSize` is unset. Default `16`. */
|
|
7777
6031
|
defaultFontSize?: number;
|
|
6032
|
+
/**
|
|
6033
|
+
* Current viewport. Supply it on a canvas that pans or zooms: the overlay
|
|
6034
|
+
* is then positioned at the node's projected screen origin and CSS-scaled
|
|
6035
|
+
* by the view, so every typographic metric on it — including the
|
|
6036
|
+
* `fontSize` / `letterSpacing` a *run* carries — stays in world units and
|
|
6037
|
+
* scales together. Omit it and the node's world box is passed through as
|
|
6038
|
+
* screen pixels (correct at `{x: 0, y: 0, scale: 1}`).
|
|
6039
|
+
*
|
|
6040
|
+
* The overlay takes a single scale factor, so a non-uniform view scale is
|
|
6041
|
+
* represented by its `scale.x`; text under `scale.x !== scale.y` will not
|
|
6042
|
+
* match the canvas.
|
|
6043
|
+
*/
|
|
6044
|
+
view?: View;
|
|
6045
|
+
/**
|
|
6046
|
+
* Forwarded to `useTextEdit`: is `el` part of the editor's own chrome?
|
|
6047
|
+
* Focus moving into it does not end the edit. Wire it to whatever renders
|
|
6048
|
+
* the character controls.
|
|
6049
|
+
*/
|
|
6050
|
+
isEditorChrome?: (el: Element) => boolean;
|
|
7778
6051
|
}
|
|
7779
6052
|
/** Return shape extends `UseTextEditReturn` with an `onDoubleClick`
|
|
7780
6053
|
* binding for the canvas container. Double-clicking inside a text
|
|
@@ -7790,10 +6063,12 @@ interface UseSceneTextEditReturn extends UseTextEditReturn {
|
|
|
7790
6063
|
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;
|
|
7791
6064
|
|
|
7792
6065
|
/**
|
|
7793
|
-
* Build a `TextDrawCommand`
|
|
7794
|
-
*
|
|
7795
|
-
* `resolveTextStyle` + `resolveRuns
|
|
7796
|
-
*
|
|
6066
|
+
* Build a `TextDrawCommand`. `textCommand` takes a plain string and wraps it
|
|
6067
|
+
* in one run; `textCommandFromRuns` takes the styled runs directly. Both
|
|
6068
|
+
* resolve through `resolveTextStyle` + `resolveRuns`, so every emitter of a
|
|
6069
|
+
* text command derives `align` and per-run resolution the same way — a
|
|
6070
|
+
* caller that assembles the command by hand is one field away from a
|
|
6071
|
+
* silently different result.
|
|
7797
6072
|
*/
|
|
7798
6073
|
|
|
7799
6074
|
declare function textCommand(x: number, y: number, text: string, style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign): DrawCommand;
|
|
@@ -7823,8 +6098,15 @@ declare function charOffsetToDomPosition(parent: HTMLElement, offset: number): {
|
|
|
7823
6098
|
/**
|
|
7824
6099
|
* Inverse of `charOffsetToDomPosition`. Walks text nodes in document order;
|
|
7825
6100
|
* sums the lengths of every text node preceding `node` and adds `offset`.
|
|
7826
|
-
*
|
|
7827
|
-
*
|
|
6101
|
+
*
|
|
6102
|
+
* When `node` is an element the DOM offset indexes *child nodes*, not
|
|
6103
|
+
* characters, so it can't simply be added — `(overlay, 0)` is the start of
|
|
6104
|
+
* the text and `(overlay, childNodes.length)` its end. That shape is not
|
|
6105
|
+
* exotic: `Range.selectNodeContents`, which `useTextEdit` uses for its
|
|
6106
|
+
* select-all caret, produces exactly it. So resolve an element position by
|
|
6107
|
+
* counting the text that precedes the boundary point. A text node can never
|
|
6108
|
+
* *contain* that point (the container isn't one), so each is wholly before
|
|
6109
|
+
* or wholly after it.
|
|
7828
6110
|
*/
|
|
7829
6111
|
declare function domPositionToCharOffset(parent: HTMLElement, node: Node, offset: number): number;
|
|
7830
6112
|
|
|
@@ -8072,6 +6354,48 @@ declare function registerOpFactory<A>(name: string, build: (args: A) => Op): voi
|
|
|
8072
6354
|
* a no-op placeholder so stack ordering survives across kit version skew). */
|
|
8073
6355
|
declare function rebuildOp(name: string, args: unknown): Op | null;
|
|
8074
6356
|
|
|
6357
|
+
/** @internal */
|
|
6358
|
+
interface HitAdapter<TNode extends {
|
|
6359
|
+
id: string;
|
|
6360
|
+
}, TPose> {
|
|
6361
|
+
getNode: (id: string) => TNode | undefined;
|
|
6362
|
+
getNodes: () => TNode[];
|
|
6363
|
+
getPose: (id: string) => TPose;
|
|
6364
|
+
getParent: (id: string) => string | null;
|
|
6365
|
+
}
|
|
6366
|
+
interface NestedHitOpts<TNode extends {
|
|
6367
|
+
id: string;
|
|
6368
|
+
}, TPose> {
|
|
6369
|
+
/** Compose a child's local pose into world coords given its parent's world
|
|
6370
|
+
* pose. Same shape as `composeRectPose` (the default expectation). */
|
|
6371
|
+
composePose: (parent: TPose, child: TPose) => TPose;
|
|
6372
|
+
/** Derive an axis-aligned bounding rect from a (world-space) pose. Default
|
|
6373
|
+
* reads `x` / `y` / `width` / `height` straight off the pose (matches
|
|
6374
|
+
* `RectPose`). Override for non-rect poses (e.g. paths). */
|
|
6375
|
+
poseBounds?: (pose: TPose) => Bounds;
|
|
6376
|
+
/** Predicate for "this object is a nesting parent body". The leaf scan
|
|
6377
|
+
* skips objects for which this returns true so a click on a parent's
|
|
6378
|
+
* painted body resolves to a child leaf, not the parent itself. Default:
|
|
6379
|
+
* never (treat every object as hittable). */
|
|
6380
|
+
isGroup?: (id: string, obj: TNode | undefined) => boolean;
|
|
6381
|
+
}
|
|
6382
|
+
interface NestedHitTester {
|
|
6383
|
+
/** Outermost-ancestor pick. Suitable as the chrome-level `pickEvery`: a
|
|
6384
|
+
* casual click selects the whole top-level ancestor. Returns `null` on
|
|
6385
|
+
* empty space. */
|
|
6386
|
+
pickOutermost: (worldX: number, worldY: number) => string | null;
|
|
6387
|
+
/** Alt-aware selection-update pick. Without `alt`, returns the outermost
|
|
6388
|
+
* ancestor (same as `pickOutermost`). With `alt`, returns one level
|
|
6389
|
+
* deeper than the deepest currently-selected ancestor in the leaf's
|
|
6390
|
+
* chain — repeated alt-clicks step ancestor → descendant → leaf. With
|
|
6391
|
+
* `alt` and nothing in the chain selected, jumps straight to the leaf.
|
|
6392
|
+
* Plug into `useSelectTool({ pickBest })`. */
|
|
6393
|
+
pickBest: (worldX: number, worldY: number, alt: boolean, selection: readonly string[]) => string | null;
|
|
6394
|
+
}
|
|
6395
|
+
declare function nestedHitTester<TNode extends {
|
|
6396
|
+
id: string;
|
|
6397
|
+
}, TPose>(adapter: HitAdapter<TNode, TPose>, opts: NestedHitOpts<TNode, TPose>): NestedHitTester;
|
|
6398
|
+
|
|
8075
6399
|
/**
|
|
8076
6400
|
* Fluent builder for `PolygonPath`. Hides the `Uint8Array` / `Float32Array`
|
|
8077
6401
|
* encoding behind move/line/curve/close calls and a final `build()`. Use
|
|
@@ -8173,6 +6497,44 @@ declare function linePath(a: {
|
|
|
8173
6497
|
/** AABB of a `Path`, returned as a `RectPath` for direct reuse with rect-fast-path machinery. */
|
|
8174
6498
|
declare function boundsOfPath(path: Path): RectPath;
|
|
8175
6499
|
|
|
6500
|
+
/**
|
|
6501
|
+
* Counts the path anchors used by the per-anchor coloring surface. An
|
|
6502
|
+
* "anchor" is the destination point of a path command: M, L, C, Q each
|
|
6503
|
+
* contribute one (the (x, y) where the pen ends up); Z contributes none
|
|
6504
|
+
* (it closes back to the subpath's first M). RectPath has four implicit
|
|
6505
|
+
* anchors — the corners — matching its M/L/L/L/Z stroke tessellation.
|
|
6506
|
+
*
|
|
6507
|
+
* Consumers use this to size their per-anchor color array; the renderer
|
|
6508
|
+
* uses it to validate the array length in dev builds.
|
|
6509
|
+
*/
|
|
6510
|
+
|
|
6511
|
+
interface PenAnchor {
|
|
6512
|
+
x: number;
|
|
6513
|
+
y: number;
|
|
6514
|
+
inHandle?: {
|
|
6515
|
+
x: number;
|
|
6516
|
+
y: number;
|
|
6517
|
+
};
|
|
6518
|
+
outHandle?: {
|
|
6519
|
+
x: number;
|
|
6520
|
+
y: number;
|
|
6521
|
+
};
|
|
6522
|
+
}
|
|
6523
|
+
/**
|
|
6524
|
+
* Derive a per-subpath anchor model from a PolygonPath. Subpaths split on
|
|
6525
|
+
* every `M` command; a subpath is closed iff it ends with `Z`.
|
|
6526
|
+
*
|
|
6527
|
+
* Cubic-segment control points become the outHandle of the previous anchor
|
|
6528
|
+
* and the inHandle of the next anchor. Quadratic segments are upgraded to
|
|
6529
|
+
* cubics (each control reused for both adjacent handles) — this loses no
|
|
6530
|
+
* geometry. Linear segments produce anchors with no handles.
|
|
6531
|
+
*/
|
|
6532
|
+
declare function pathToAnchors(path: PolygonPath): {
|
|
6533
|
+
anchors: PenAnchor[][];
|
|
6534
|
+
closed: boolean[];
|
|
6535
|
+
};
|
|
6536
|
+
declare function countPathAnchors(path: Path): number;
|
|
6537
|
+
|
|
8176
6538
|
interface Point {
|
|
8177
6539
|
x: number;
|
|
8178
6540
|
y: number;
|
|
@@ -8654,10 +7016,12 @@ declare function createPenPreviewLayer(opts: CreatePenPreviewLayerOptions): Rend
|
|
|
8654
7016
|
* `getEditingId()` + `getPose(id)` thunks. Returns `[]` when no editing
|
|
8655
7017
|
* target is set or the target isn't a polygon — the layer never throws.
|
|
8656
7018
|
*
|
|
8657
|
-
*
|
|
8658
|
-
*
|
|
8659
|
-
*
|
|
8660
|
-
*
|
|
7019
|
+
* Anchors render as small white-filled stroked squares — filled solid
|
|
7020
|
+
* when selected; control points as small filled circles connected to
|
|
7021
|
+
* their anchor by a thin stem line. This is the kit's only anchor
|
|
7022
|
+
* chrome; the pen tool's parallel overlay and `apps/draw`'s
|
|
7023
|
+
* mode-decoration painter both drew the same thing from different state
|
|
7024
|
+
* and have been removed.
|
|
8661
7025
|
*
|
|
8662
7026
|
* All coordinates are projected via `worldToScreen` so the marker sizes
|
|
8663
7027
|
* stay constant regardless of zoom.
|
|
@@ -8671,7 +7035,26 @@ interface CreatePathEditingOverlayLayerOptions {
|
|
|
8671
7035
|
/** Returns the pose for an id, or null if the node has been deleted.
|
|
8672
7036
|
* Non-polygon poses are tolerated (the layer no-ops on them). */
|
|
8673
7037
|
getPose(id: string): Path | null;
|
|
8674
|
-
/**
|
|
7038
|
+
/** Flat indices of the selected anchors. Selected anchors render
|
|
7039
|
+
* filled; unselected ones hollow — the standard vector-editor cue for
|
|
7040
|
+
* "these are what the arrow keys and Delete will act on". Omit when
|
|
7041
|
+
* the consumer has no anchor selection to show. */
|
|
7042
|
+
getSelectedAnchors?(): ReadonlySet<number>;
|
|
7043
|
+
/** In-flight anchor-marquee rect in world coords, or null. Drawn as a
|
|
7044
|
+
* rubber band while `marqueeAnchorsAction` is dragging. */
|
|
7045
|
+
getMarquee?(): {
|
|
7046
|
+
x: number;
|
|
7047
|
+
y: number;
|
|
7048
|
+
width: number;
|
|
7049
|
+
height: number;
|
|
7050
|
+
} | null;
|
|
7051
|
+
/** Chrome-caps predicate. The layer asks `'path-edit.anchors'` before
|
|
7052
|
+
* drawing anything, so paint and the anchor hit-test in
|
|
7053
|
+
* `affordanceAt` consult the same rule — otherwise a consumer that
|
|
7054
|
+
* hides the chrome still gets grabbable invisible anchors, or vice
|
|
7055
|
+
* versa. Omit to always draw when an edit target is set. */
|
|
7056
|
+
isVisible?(chromeId: string): boolean;
|
|
7057
|
+
/** Optional styling overrides. */
|
|
8675
7058
|
style?: PathEditingOverlayStyle;
|
|
8676
7059
|
}
|
|
8677
7060
|
interface PathEditingOverlayStyle {
|
|
@@ -8685,6 +7068,13 @@ interface PathEditingOverlayStyle {
|
|
|
8685
7068
|
* curve / background show through, which matches Figma's "translucent
|
|
8686
7069
|
* handle dot" idiom. */
|
|
8687
7070
|
handleDotFill?: string;
|
|
7071
|
+
/** Fill of a selected anchor square. Defaults to the anchor stroke
|
|
7072
|
+
* color, so selection reads as "the marker filled in". */
|
|
7073
|
+
anchorFillSelected?: string;
|
|
7074
|
+
/** Stroke of the marquee rubber band. */
|
|
7075
|
+
marqueeStroke?: string;
|
|
7076
|
+
/** Fill of the marquee rubber band. */
|
|
7077
|
+
marqueeFill?: string;
|
|
8688
7078
|
}
|
|
8689
7079
|
declare function createPathEditingOverlayLayer(opts: CreatePathEditingOverlayLayerOptions): RenderLayer<unknown>;
|
|
8690
7080
|
|
|
@@ -9421,36 +7811,6 @@ interface AnchorHit {
|
|
|
9421
7811
|
* are within `threshold` (controls are smaller and rendered on top). */
|
|
9422
7812
|
declare function hitAnchor(path: PolygonPath, worldX: number, worldY: number, threshold: number): AnchorHit | null;
|
|
9423
7813
|
|
|
9424
|
-
/** Visual options for the anchor-edit overlay. */
|
|
9425
|
-
interface AnchorEditOverlayOpts {
|
|
9426
|
-
/** Returns the live editing state (or `null` when not editing). The overlay
|
|
9427
|
-
* draws nothing when `null`. */
|
|
9428
|
-
getOverlay: () => {
|
|
9429
|
-
pose: PolygonPath;
|
|
9430
|
-
selectedAnchors: number[];
|
|
9431
|
-
} | null;
|
|
9432
|
-
/** Tangent-line stroke color. Default `#888`. */
|
|
9433
|
-
tangentStroke?: string;
|
|
9434
|
-
/** Anchor circle radius (world units). Default 4. */
|
|
9435
|
-
anchorRadius?: number;
|
|
9436
|
-
/** Control circle radius (world units). Default 3. */
|
|
9437
|
-
controlRadius?: number;
|
|
9438
|
-
/** Anchor fill. Default `#fff`. */
|
|
9439
|
-
anchorFill?: string;
|
|
9440
|
-
/** Anchor stroke. Default `#1a130d`. */
|
|
9441
|
-
anchorStroke?: string;
|
|
9442
|
-
/** Selected-anchor fill (highlight). Default `#7fb069`. */
|
|
9443
|
-
selectedAnchorFill?: string;
|
|
9444
|
-
/** Control fill. Default `#1a130d`. */
|
|
9445
|
-
controlFill?: string;
|
|
9446
|
-
/** Control stroke. Default `#fff`. */
|
|
9447
|
-
controlStroke?: string;
|
|
9448
|
-
}
|
|
9449
|
-
declare function createAnchorEditOverlayLayer(opts: AnchorEditOverlayOpts): RenderLayer<unknown>;
|
|
9450
|
-
|
|
9451
|
-
/** Default area-select behavior: replace selection with hits inside the marquee, or extend with shift held. */
|
|
9452
|
-
declare function selectFromMarquee(): AreaSelectBehavior;
|
|
9453
|
-
|
|
9454
7814
|
/**
|
|
9455
7815
|
* Typed keys for the gesture/behavior scratch store.
|
|
9456
7816
|
*
|
|
@@ -10197,4 +8557,11 @@ declare function ExcludeIcon(): react_jsx_runtime.JSX.Element;
|
|
|
10197
8557
|
declare function DivideIcon(): react_jsx_runtime.JSX.Element;
|
|
10198
8558
|
declare function CropIcon(): react_jsx_runtime.JSX.Element;
|
|
10199
8559
|
|
|
10200
|
-
export { ALWAYS, type Action, ActionDeps, ActionDisabledReason, type ActionEnabledResult, type ActionEntry, type ActionsProp, ActionsProvider, ActionsProviderIfRoot, type ActionsRegistry, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, type ActiveToolContextProviderProps, type ActiveToolContextValue, Affordance, AffordanceBinding, AffordanceHit, type AlignAdapter, type AlignAnchor, type AlignBounds, type AlignBoundsProjection, type AlignEdge, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type AnchorEditOverlayOpts, type AnchorHit, type AnimateOnSetPoseOptions, type AnimateToBoundsOptions, type AnimationHandle, type Animator, AnyTool, AreaSelectAdapter, AreaSelectBehavior, type AreaSelectDep, type AreaSelectOverlayStyle, type ArrayAdapter, type ArrayAdapterConfig, BUNDLE_TOOLS, BindingOpts, type BindingScope, type BooleanOp, type BooleanOpResult, type BooleansAdapter, type BoundGesture, Bounds, BoundsConstraint, type BuildChromeCtxArgs, type BuildRuleCtxArgs, type BuiltinShapeToolId, type BuiltinToolId, type BuiltinToolOptions, CURVE_REPS, type CanvasExtensionApi, type CanvasFocusReturn, type CanvasHelpers, type CanvasProps, type CanvasSelectionMode, type CanvasSize, type CanvasSizeSnapshot, type CellHighlightLayerOpts, type ChromeCtx, type ChromeId, ChromeState, type ClampBounds, type ClipboardIngestCtx, ClipboardSnapshot, type CoalescedCtx, type ColorInterpolate, type ColorOverride, type ColorOverrideFn, ColorOverrideRegistry, type ColorSpace, type ComposeSelectionPoseOpts, type ComputeFitViewOptions, type Condition, type ContentHandlerEntry, type CornerResizeAffordanceOptions, type CornerResizeScratch, type CreateChildrenLayerOpts, type CreateParallaxLayerOpts, type CreatePathEditingOverlayLayerOptions, type CreatePathLayerOpts, type CreatePenPreviewLayerOptions, type CreateTextLayerOpts, type CreateViewportLayerOpts, CropIcon, CursorCoordsHud, type CursorCoordsHudProps, type CurveRepKind, type CurveRepresentation, type CustomLayerEntry, type CycleHandle, type CycleVertexColorsOptions, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_THEME, 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, DebugTheme, type DecayLoopConfig, type DecayOptions, type DefaultTextData, type DepName, type DepRegistry, DepRegistryProvider, DepRegistryProviderIfRoot, type DepSchema, type DeriveAlignmentGuidesOptions, Dims, type Discriminator, type Dispatcher, type DispatcherContext, 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, type EditAnchorsDep, type EllipseBounds, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, FillStyle, type FitTextPoseOptions, FitViewToBoundsOptions, type FreeformOptions, GHOST_STROKE, type GateLayerOptions, type GeometryProjection, GestureBinding, type GridLayerOpts, type GridSlotConfig, Guide, type GuideSnapOptions, type GuidesLayerOpts, HandIcon, type InertiaConfig as HandToolInertiaConfig, type HandleDragPoint, type HeadlessCanvasLike, HotkeyTrigger, type IconProps, type ImageNodeData, type ImageStatus, type IndicatorStyle, type IngestCtx, type IngestionDep, InsertAdapter, InsertBehavior, type InsertDep, type InsertExtras, type InsertNodeFactory, type InsertOp, type InsertOverlayStyle, type Interpolate, type InterpolatorFactory, IntersectIcon, Invoker, KIT_SHAPE_KINDS, type LabelOptions, LassoHitMode, LassoIcon, LassoSelectAdapter, LassoSelectBehavior, type LassoSelectDep, LayerEnumerableAdapter, type LayerSlotValue, type LayersMap, type LayoutDep, type LayoutLine, type LayoutResult, LayoutSnap, LayoutStrategy, type LifecycleAnimation, LineIcon, type LinePoint, type LoopFactory, type LoopOptions, MOVE_ANCHORS, type MarkdownFontOptions, type MatchResult, type MeasureFn, type MeasureTextBoundsOpts, type MeasuredText, MinimapCanvas, type MinimapCanvasProps, type MinimapFit, ModifierState, type MomentumOptions, MoveAdapter, MoveBehavior, type MoveOverlayStyle, NEVER, type NestedHitOpts, type NestedHitTester, type NestedSelectAdapter, type NodeAtPointDep, type NodeClientRect, NodeId, type NodePaintCtx, type NodeProperties, type NodePropertiesEntry, type NodeRouting, type NodeRoutingEntry, type NodeShapeEntry, OngoingHandle, type OpenFilePickerOptions, OrderedAdapter, OriginProjection, type PanBounds, type ParallaxOpts, Path, type PathAnchor, PathBuilder, PathDrawCommand, type PathEditingOverlayStyle, PathFillRule, type PathInWorldPose, type PenAnchor$1 as PenAnchor, PenIcon, type PenPreviewStyle, type PenScratch, type PenSubpath, PencilIcon, type PhysicsHandle, type PhysicsOptions, PickHud, type PickHudProps, type PickTopMostHitAdapter, type PinchZoomToolOpts, type PixelRenderPlan, Point2, type PointInPathOptions, type PointInTextPoseOpts, PointSnapBehavior, PointerContextProvider, type PointerContextValue, type PointerGestureBindings, type PointerGestureCallbackCtx, PointerProviderIfRoot, type PointerSample, type PointerStylusState, type PointerWorldPos, PolygonIcon, PolygonPath, type PolygonPoint, PoseComposition, PoseProjection, type PoseRotation, type PositionedRun, type PressureToWidthOptions, type Pt, RECT_ALIGN_PROJECTION, type RasterImage, type Rect, type RectBounds, RectIcon, RectPath, RectPose, type RegisterNodeShapeOptions, RegisteredOp, RenderLayer, type RenderSceneToCanvasArgs, type RenderSceneToPixelsArgs, type ReorderDirection, ResizeAdapter, ResizeAnchor, type ResizePolicy, ResizePose, type ResolveOnlyResult, ResolvedTextStyle, RotateAdapter, RotateBehavior, type RotateGeometry, RotatedPose, type RotationAffordanceOptions, type RotationHandle, type RotationScratch, type Rule, type RuleCtx, SPRING_PRESETS, 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, type ScopedBinding, type ScratchKey, type ScratchStore, type SelectAdapter, type SelectFromLassoOptions, SelectIcon, SelectionApi, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type SelectionHandlesLayerOpts, type SelectionOutlineLayerOpts, type SelectionOverlayLayerOpts, type SelectionOverlaySlotConfig, type Selector, SerializedScene, type SetPathFields, ShaderProgramHandle, type SharedAnchor, type Simulation, type SimulationForce, type SimulationNode, type 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, StyledRun, type StylusData, SubtractIcon, type SvgIngestOptions, type SvgUnpacker, type TextEditDep, type TextEditScreenPose, TextIcon, type TextPose, type TextRenderer, TextStyle, TextVerticalAlign, TextureHandle, type ThresholdDragHandle, type ThresholdDragOptions, type TileGridOptions, type TilePatternOpts, Tool, type ToolBundle, ToolCtx, 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 ToolPrefString, type ToolPrefStringControl, type ToolsApi, ToolsDispatcher, type TweenLoopOptions, type TweenOptions, type TweenPoseOptions, type TweenVertexColorsOptions, type UiOngoingControl, UnionIcon, UnitSystem, UnitValue, UnknownIcon, type UseAlignOptions, type UseAlignReturn, type UseAnimatorOptions, type UseAreaSelectOptions, type UseArrayAdapterOptions, type UseBooleansOptions, type UseBooleansReturn, type UseCanvasFocusOptions, type UseDistributeOptions, type UseDistributeReturn, type UseDragGestureOptions, type UseDragRadialOptions, type UseDragRectOptions, type UseEllipseToolOptions, type UseEyedropperToolOptions, type UseGestureDispatcherOptions, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, type UseHandToolOptions, type UseHandleDragOptions, type UseHandleDragReturn, type UseHoverTrackingArgs, type UseImageToolOptions, UseInsertOptions, type UseInsertToolOptions, type UseKeybindingsOptions, type UseLassoSelectOptions, type UseLassoToolOptions, type UseLineToolOptions, UseMoveOptions, type UseNestedSelectToolOptions, type UsePenToolOptions, type UsePenToolReturn, type UsePencilToolOptions, type UsePointerGesturesOptions, type UsePointerStylusOptions, type UsePolygonToolOptions, type UseRectToolOptions, 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 UseTextToolOptions, type UseToolsOptions, type UseZoomOptions, type UseZoomReturn, type Vec2, type VertexColorChannel, View, type ViewApi, ViewTransform, type ViewportConfig, ViewportDims, type ViewportZoomOptions, type VisibilityRules, WeaselProvider, type WheelInput, type WheelState, ZoomBound, type ZoomBounds, type ZoomClampOpts, ZoomFactor, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, applyBooleanOp, applyHitExistingGate, applyOpsTo, areaSelectAction, arrayAdapter, bezierCubic, bezierQuadratic, boundsOfPath, buildChromeCtx, buildRuleCtx, buildSceneViewCommands, canBringForward, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, cloneAction, composeAffordanceLayer, composePath, composeSelectionPose, computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createAnchorEditOverlayLayer, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDeleteOp, createDispatcher, createGridLayer, createGuidesLayer, createHistory, createInsertOp, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createReparentOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetSelectionOp, createSetTextOp, createTextLayer, createTilePattern, createTransformOp, createViewportLayer, cycleVertexColors, decomposePath, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, 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, enterTextEditAction, enumerateAnchors, evaluate, evaluateEnabled, findNodeShape, findShapeSilhouette, fitTextPose, fitToBounds, fitZoom, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, focused, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getScratch, getStylusData, guideSnapStrategy, hexToRgba, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isDebugEnabled, isEditableTarget, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linePath, linear, makeViewportZoomAction, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, mergeAlphaFromPrev, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, 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, rainbowVertexColors, rebuildOp, rectCorners, rectPath, registerContentHandler, registerNodeShape, registerOpFactory, regularPolygonPath, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, scalePathToBounds, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scratchKey, selectFromLasso, selectFromMarquee, selectionAtLeast, selectionEmpty, selectionIs, setScratch, sliceAction, snap, snapPoint, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, starPath, startThresholdDrag, subscribeImageReady, textCommand, tileGrid, toHex8, transformPath, translatePath, translatePolygonInPlace, translatePoseViaDescriptor, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useAction, useActionsRegistry, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDepRegistry, useDepSource, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useInsertTool, useKeybindings, useLassoTool, useLineTool, useNestedSelectTool, useOptionalActiveToolContext, useOptionalDepRegistry, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerContext, usePointerGestures, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, viewportDragPanAction, viewportZoomAction, when, withAlpha01, withCoord, worldEditToStorage, zoomAt, zoomAtLeast };
|
|
8560
|
+
interface InsertOverlayStyle {
|
|
8561
|
+
fill?: string;
|
|
8562
|
+
stroke?: string;
|
|
8563
|
+
dash?: number[];
|
|
8564
|
+
lineWidth?: number;
|
|
8565
|
+
}
|
|
8566
|
+
|
|
8567
|
+
export { Action, ActionsProp, ActionsProviderIfRoot, ActionsRegistry, Affordance, AffordanceBinding, AffordanceHit, type AlignAdapter, type AlignAnchor, type AlignBounds, type AlignBoundsProjection, type AlignEdge, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type AnchorHit, type AnimateOnSetPoseOptions, type AnimateToBoundsOptions, type AnimationHandle, type Animator, AnyTool, AreaSelectAdapter, type ArrayAdapter, type ArrayAdapterConfig, BUNDLE_TOOLS, BooleansAdapter, Bounds, BoundsConstraint, type BuildChromeCtxArgs, type BuiltinShapeToolId, type BuiltinToolId, type BuiltinToolOptions, CURVE_REPS, type CanvasExtensionApi, type CanvasFocusReturn, type CanvasHelpers, type CanvasProps, type CanvasSelectionMode, type CanvasSize, type CanvasSizeSnapshot, type CellHighlightLayerOpts, ChromeCtx, ChromeId, ChromeState, type ClampBounds, ClipboardSnapshot, type CoalescedCtx, type ColorInterpolate, type ColorOverride, type ColorOverrideFn, ColorOverrideRegistry, type ColorSpace, type ComposeSelectionPoseOpts, type ComputeFitViewOptions, Condition, ContentHandlerEntry, type CornerResizeAffordanceOptions, type CornerResizeScratch, type CreateChildrenLayerOpts, type CreateParallaxLayerOpts, type CreatePathEditingOverlayLayerOptions, type CreatePathLayerOpts, type CreatePenPreviewLayerOptions, type CreateTextLayerOpts, type CreateViewportLayerOpts, CropIcon, CursorCoordsHud, type CursorCoordsHudProps, type CurveRepKind, type CurveRepresentation, type CustomLayerEntry, type CycleHandle, type CycleVertexColorsOptions, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_THEME, 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, DebugTheme, type DecayLoopConfig, type DecayOptions, type DefaultTextData, DepRegistryProviderIfRoot, DepSchema, type DeriveAlignmentGuidesOptions, 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, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, FillStyle, type FitTextPoseOptions, FitViewToBoundsOptions, type FreeformOptions, GHOST_STROKE, type GateLayerOptions, GeometryProjection, type GestureSource, type GridLayerOpts, type GridSlotConfig, Guide, type GuideSnapOptions, type GuidesLayerOpts, HandIcon, type InertiaConfig as HandToolInertiaConfig, type HandleDragPoint, type HeadlessCanvasLike, HotkeyTrigger, type IconProps, type ImageNodeData, type ImageStatus, type IndicatorStyle, InsertAdapter, InsertBehavior, InsertExtras, type InsertNodeFactory, type InsertOp, type InsertOverlayStyle, type Interpolate, type InterpolatorFactory, IntersectIcon, KIT_SHAPE_KINDS, type LabelOptions, LassoHitMode, LassoIcon, LassoSelectAdapter, LassoSelectBehavior, LayerEnumerableAdapter, type LayerSlotValue, type LayersMap, type LayoutLine, type LayoutResult, LayoutSnap, LayoutStrategy, type LifecycleAnimation, LineIcon, type LinePoint, type LoopFactory, type LoopOptions, MIXED, MOVE_ANCHORS, type MarkdownFontOptions, type MeasureFn, type MeasureTextBoundsOpts, type MeasuredText, MinimapCanvas, type MinimapCanvasProps, type MinimapFit, type Mixed, ModifierState, type MomentumOptions, MoveAdapter, MoveBehavior, type NestedHitOpts, type NestedHitTester, type NodeClientRect, NodeId, type NodePaintCtx, type NodeProperties, type NodePropertiesEntry, type NodeRouting, type NodeRoutingEntry, type NodeShapeEntry, type OpenFilePickerOptions, OrderedAdapter, OriginProjection, type PanBounds, type ParallaxOpts, Path, type PathAnchor, 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, PoseProjection, type PoseRotation, type PositionedRun, type PressureToWidthOptions, type Pt, RECT_ALIGN_PROJECTION, type RangeStyle, type RasterImage, type Rect, RectIcon, RectPath, RectPose, type RegisterNodeShapeOptions, RegisteredOp, RenderLayer, type RenderSceneToCanvasArgs, type RenderSceneToPixelsArgs, type ReorderDirection, ResizeAdapter, ResizeAnchor, ResizePose, ResolvedTextStyle, RotateAdapter, RotateBehavior, type RotateGeometry, RotatedPose, type RotationAffordanceOptions, type RotationHandle, type RotationScratch, Rule, RuleCtx, type RunStylePatch, SPRING_PRESETS, type 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, type ScratchKey, type ScratchStore, type SelectAdapter, type SelectFromLassoOptions, SelectIcon, SelectionApi, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type SelectionHandlesLayerOpts, type SelectionOutlineLayerOpts, type SelectionOverlayLayerOpts, type SelectionOverlaySlotConfig, SerializedScene, type SetPathFields, ShaderProgramHandle, type SharedAnchor, type Simulation, type SimulationForce, type SimulationNode, 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 TextPose, type TextRenderer, TextStyle, TextVerticalAlign, TextureHandle, type ThresholdDragHandle, type ThresholdDragOptions, type TileGridOptions, type TilePatternOpts, Tool, type ToolBundle, ToolCtx, 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 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, type Vec2, type VertexColorChannel, View, ViewTransform, type ViewportConfig, ViewportDims, type ViewportZoomOptions, VisibilityRules, WeaselProvider, type WheelInput, type WheelState, ZoomBound, type ZoomBounds, type ZoomClampOpts, ZoomFactor, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, applyHitExistingGate, applyOpsTo, applyStyleToRange, areaSelectAction, arrayAdapter, bezierCubic, bezierQuadratic, boundsOfPath, buildChromeCtx, buildSceneViewCommands, canBringForward, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, cloneAction, composeAffordanceLayer, composePath, composeSelectionPose, computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDeleteOp, createGridLayer, createGuidesLayer, createHistory, createInsertOp, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createReparentOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetSelectionOp, createSetTextOp, createTextLayer, createTilePattern, createTransformOp, createViewportLayer, cycleVertexColors, decomposePath, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, 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, findNodeShape, findShapeSilhouette, fitTextPose, fitToBounds, fitZoom, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, focused, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getScratch, getStylusData, guideSnapStrategy, hexToRgba, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isDebugEnabled, isEditableTarget, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linePath, linear, makeViewportZoomAction, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, 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, rainbowVertexColors, rebuildOp, rectCorners, rectPath, registerNodeShape, registerOpFactory, regularPolygonPath, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, scalePathToBounds, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scratchKey, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setScratch, snap, snapPoint, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, starPath, startThresholdDrag, styleAtRange, subscribeImageReady, textCommand, tileGrid, toHex8, transformPath, translatePath, translatePolygonInPlace, translatePoseViaDescriptor, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, 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, when, withAlpha01, withCoord, worldEditToStorage, zoomAt, zoomAtLeast };
|