@weasel-js/core 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +233 -0
- package/dist/{DrawCommand-Dl0bXNfS.d.ts → DrawCommand-CJtqqt8H.d.ts} +26 -2
- package/dist/{chunk-7V6JEOXE.js → chunk-2J6V527H.js} +10033 -10912
- package/dist/chunk-2J6V527H.js.map +1 -0
- package/dist/chunk-SYM6RAM4.js +232 -0
- package/dist/chunk-SYM6RAM4.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-DOYRTfP0.d.ts +2986 -0
- package/dist/index.css +0 -35
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1174 -2481
- 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 +30 -5
- 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,62 @@
|
|
|
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, s as AffordanceRegion, t as CommonAffordanceScratch, B as BooleansAdapter } from './index-DOYRTfP0.js';
|
|
8
|
+
export { u as ALWAYS, v as ActionDeps, w as ActionDisabledReason, x as ActionEnabledResult, y as ActionEntry, z as ActionsProvider, E as ActiveToolContextProvider, F as ActiveToolContextProviderIfRoot, J as ActiveToolContextProviderProps, K as ActiveToolContextValue, L as AreaSelectDep, M as BindingOpts, N as BindingScope, O as BooleanOp, P as BooleanOpResult, Q as BoundGesture, W as BuildRuleCtxArgs, X as ClipboardIngestCtx, Y as CustomPaintContext, Z as DepName, _ as DepRegistry, $ as DepRegistryProvider, a0 as DispatcherContext, a1 as DragSample, a2 as EditAnchorsDep, a3 as GestureBinding, a4 as ImmediateInvoker, a5 as IngestCtx, a6 as IngestionDep, a7 as InsertDep, a8 as InvocationCtx, a9 as Invoker, aa as LassoSelectDep, ab as LayoutDep, ac as MatchResult, ad as NEVER, ae as NodeAtPointDep, af as OngoingHandle, ag as OngoingInvoker, ah as OngoingOverlay, ai as Point2, aj as PointerContextProvider, ak as PointerContextValue, al as PointerWorldPos, am as ResizePolicy, an as ResolveAllOptions, ao as ResolveOnlyResult, ap as ResolvedCandidate, aq as ScopedBinding, ar as SelectionExtendKey, as as SelectionMode, at as Selector, au as SnapDep, av as SvgUnpacker, aw as TextEditDep, ax as ToolModifiers, ay as ToolPresentation, az as ToolSlot, aA as UiOngoingControl, aB as ViewApi, aC as applyBooleanOp, aD as buildRuleCtx, aE as createDispatcher, aF as describeRule, aG as drawLayers, aH as enterTextEditAction, aI as evaluate, aJ as evaluateEnabled, aK as registerContentHandler, aL as routing, aM as sliceAction, aN as specificity, aO as useAction, aP as useActionsRegistry, aQ as useActiveToolContext, aR as useDepRegistry, aS as useDepSource, aT as useOptionalActiveToolContext, aU as useOptionalDepRegistry, aV as usePointerContext, aW as useSelection } from './index-DOYRTfP0.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, LocalFontOutlinesResult, OutlineFontStyle, OutlineSource, OutlineStatus, OutlineVariant, canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasFont, listFontOutlines, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, subscribeGlyphReady, unregisterCanvasFont, unregisterFontOutlines } from '@weasel-js/font';
|
|
39
|
+
import '@weasel-js/modes';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The kit version a build was compiled from, so an app can report what it is
|
|
43
|
+
* running. Substituted at build time — by `tsup.config.ts` for the published
|
|
44
|
+
* package, and by `scripts/vite-build-info.ts` for anything in this repo that
|
|
45
|
+
* bundles core's source through the workspace aliases.
|
|
46
|
+
*
|
|
47
|
+
* Apps pair this with their own compile timestamp; the kit deliberately does
|
|
48
|
+
* not carry one, since a published package's build date says nothing about
|
|
49
|
+
* when the app embedding it was built.
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* Semver of `@weasel-js/core` as of the build.
|
|
53
|
+
*
|
|
54
|
+
* Falls back to `'0.0.0-unknown'` when core's source is imported by a bundler
|
|
55
|
+
* that hasn't been given the define — honest about not knowing rather than
|
|
56
|
+
* throwing, since nothing about version reporting should be able to break a
|
|
57
|
+
* consumer's build.
|
|
58
|
+
*/
|
|
59
|
+
declare const VERSION: string;
|
|
39
60
|
|
|
40
61
|
/** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */
|
|
41
62
|
declare function roundToCell(value: number, cellSize: number): number;
|
|
@@ -836,182 +857,6 @@ declare function isEditableTarget(target: EventTarget | null): boolean;
|
|
|
836
857
|
*/
|
|
837
858
|
declare function matchesKeyBinding(e: KeyboardEvent, b: KeyBinding): boolean;
|
|
838
859
|
|
|
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
860
|
/**
|
|
1016
861
|
* Promote a Rule tree to a fluent Condition. The function form evaluates
|
|
1017
862
|
* the tree against a RuleCtx; combinator methods produce new Conditions
|
|
@@ -1079,1375 +924,111 @@ declare const modeIs: (m: string) => Condition;
|
|
|
1079
924
|
/** Active mode is one of `modes`. */
|
|
1080
925
|
declare const modeIn: (modes: readonly string[]) => Condition;
|
|
1081
926
|
/** Active mode is NOT `m`. */
|
|
1082
|
-
declare const modeNot: (m: string) => Condition;
|
|
1083
|
-
/** Active mode's `allows` includes capability `cap`. */
|
|
1084
|
-
declare const capabilityIs: (cap: string) => Condition;
|
|
1085
|
-
/** Active mode's `allows` includes ANY of `caps` (OR). */
|
|
1086
|
-
declare const capabilityIn: (caps: readonly string[]) => Condition;
|
|
1087
|
-
/** Active mode's `allows` includes ALL of `caps` (AND). */
|
|
1088
|
-
declare const capabilityAll: (caps: readonly string[]) => Condition;
|
|
1089
|
-
/** Active mode's `allows` does NOT include capability `cap`. */
|
|
1090
|
-
declare const capabilityNot: (cap: string) => Condition;
|
|
1091
|
-
|
|
1092
|
-
/**
|
|
1093
|
-
* Kit-shipped defaults. Merged with the consumer's `chromeVisibility` map
|
|
1094
|
-
* at resolve time; consumer entries take precedence per id.
|
|
1095
|
-
*
|
|
1096
|
-
* Written as literal Rule trees rather than fluent chains so the inputs
|
|
1097
|
-
* each rule depends on are immediately visible. The fluent atoms compile
|
|
1098
|
-
* to the same trees; either form is valid in a VisibilityRules entry.
|
|
1099
|
-
*
|
|
1100
|
-
* Mode gating: selection chrome is suppressed in `path-edit` because the
|
|
1101
|
-
* path-editing overlay takes over visually. Path-edit-specific chrome
|
|
1102
|
-
* ids (`path-edit.*`) are positively gated to that mode. This replaces
|
|
1103
|
-
* the old `suppressedIds` set, which only the selection-outline path
|
|
1104
|
-
* honored.
|
|
1105
|
-
*/
|
|
1106
|
-
declare const defaultVisibilityRules: VisibilityRules;
|
|
1107
|
-
|
|
1108
|
-
/**
|
|
1109
|
-
* Build the per-frame visibility check. Merges consumer rules on top
|
|
1110
|
-
* of the kit defaults, then closes over `ctx` so each chrome-id
|
|
1111
|
-
* lookup runs its rule against the current state.
|
|
1112
|
-
*
|
|
1113
|
-
* `VisibilityRules` entries may be either fluent `Condition` values
|
|
1114
|
-
* (callable) or raw `Rule` trees — we normalize at lookup time.
|
|
1115
|
-
*
|
|
1116
|
-
* Predicates are O(1) and run once per chrome id per frame; no
|
|
1117
|
-
* memoization in v1 — profile before adding any.
|
|
1118
|
-
*
|
|
1119
|
-
* `ChromeCtx` is the legacy shape; surfaces still on it supply
|
|
1120
|
-
* `mode='normal'` and an empty `allowedCapabilities` to satisfy
|
|
1121
|
-
* `RuleCtx` when calling. We do the same here so callers in
|
|
1122
|
-
* legacy shape keep working transparently.
|
|
1123
|
-
*/
|
|
1124
|
-
declare function resolveVisibility(consumer: VisibilityRules | undefined, ctx: ChromeCtx | RuleCtx): (id: ChromeId) => boolean;
|
|
1125
|
-
|
|
1126
|
-
/**
|
|
1127
|
-
* Pure assembler for {@link ChromeCtx}.
|
|
1128
|
-
*
|
|
1129
|
-
* Called once per frame on the rendering side from whichever surface
|
|
1130
|
-
* needs to evaluate visibility rules — `composeAffordanceLayer`,
|
|
1131
|
-
* `createSelectionOverlayLayer`, the dispatcher overlay layer, etc.
|
|
1132
|
-
*
|
|
1133
|
-
* Sources are passed as plain getters / values rather than the live
|
|
1134
|
-
* objects they come from. This keeps `chrome-caps` from depending on
|
|
1135
|
-
* React, the dispatcher's concrete type, or `ChromeState`'s bounds
|
|
1136
|
-
* machinery — the same builder works from a test harness, a Storybook
|
|
1137
|
-
* fixture, or `<SceneCanvas>`.
|
|
1138
|
-
*/
|
|
1139
|
-
|
|
1140
|
-
interface BuildChromeCtxArgs {
|
|
1141
|
-
focused: boolean;
|
|
1142
|
-
selection: readonly NodeId[];
|
|
1143
|
-
multiActive: boolean;
|
|
1144
|
-
modifiers: ModifierState;
|
|
1145
|
-
action: {
|
|
1146
|
-
kind: string | null;
|
|
1147
|
-
id: string | null;
|
|
1148
|
-
};
|
|
1149
|
-
hover: NodeId | null;
|
|
1150
|
-
view: View;
|
|
1151
|
-
}
|
|
1152
|
-
declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
|
|
1153
|
-
|
|
1154
|
-
/**
|
|
1155
|
-
* useHoverTracking — last-hovered NodeId for chrome-caps rules.
|
|
1156
|
-
*
|
|
1157
|
-
* Attaches a `pointermove` listener to the supplied canvas; on each
|
|
1158
|
-
* move, converts client coords → world coords and runs the supplied
|
|
1159
|
-
* `getNodeAtPoint`, caching the resulting id on a ref. Cleared on
|
|
1160
|
-
* `pointerleave` (pointer left the canvas → nothing is hovered).
|
|
1161
|
-
*
|
|
1162
|
-
* Returns a stable getter — call once per frame from `buildChromeCtx`.
|
|
1163
|
-
*
|
|
1164
|
-
* No re-render: the ref updates silently, and the next render that
|
|
1165
|
-
* reads it picks up the fresh value. Chrome-caps rules are evaluated
|
|
1166
|
-
* during paint, so the hover state is always read fresh.
|
|
1167
|
-
*/
|
|
1168
|
-
|
|
1169
|
-
interface UseHoverTrackingArgs {
|
|
1170
|
-
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
1171
|
-
/** Client → world conversion. Same shape as the dispatcher's. */
|
|
1172
|
-
clientToWorld: (clientX: number, clientY: number) => {
|
|
1173
|
-
x: number;
|
|
1174
|
-
y: number;
|
|
1175
|
-
};
|
|
1176
|
-
/** Topmost-id resolver. Returns null when the world point hits nothing. */
|
|
1177
|
-
getNodeAtPoint: (worldX: number, worldY: number) => {
|
|
1178
|
-
id: NodeId;
|
|
1179
|
-
} | null;
|
|
1180
|
-
enabled?: boolean;
|
|
1181
|
-
}
|
|
1182
|
-
declare function useHoverTracking(args: UseHoverTrackingArgs): () => NodeId | null;
|
|
1183
|
-
|
|
1184
|
-
/** Boolean op identifiers — five Pathfinder primaries plus Crop. */
|
|
1185
|
-
type BooleanOp = 'union' | 'intersect' | 'subtract' | 'exclude' | 'divide' | 'crop';
|
|
1186
|
-
/**
|
|
1187
|
-
* z-position descriptor for a path node. `parentId` is the direct parent
|
|
1188
|
-
* (or `null` for a top-level node); `index` is the position within that
|
|
1189
|
-
* parent's child order. Used by the optional `getZOrder` hook below to
|
|
1190
|
-
* reposition the result of a boolean op at the topmost source's slot.
|
|
1191
|
-
*/
|
|
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
|
-
|
|
1309
|
-
/**
|
|
1310
|
-
* @experimental
|
|
1311
|
-
* PointerContext — a tiny ambient context that publishes the world-space
|
|
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>`.
|
|
1348
|
-
*
|
|
1349
|
-
* Most consumers don't need to mount this directly — `<SceneCanvas>` mounts
|
|
1350
|
-
* an internal provider when no parent provider is in scope, so child hooks
|
|
1351
|
-
* (`useClipboard` without an explicit `getDropPoint`) read the canvas's
|
|
1352
|
-
* tracked pointer for free.
|
|
1353
|
-
*/
|
|
1354
|
-
declare function PointerContextProvider({ children }: {
|
|
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`.
|
|
1386
|
-
*/
|
|
1387
|
-
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
1388
|
-
children: ReactNode;
|
|
1389
|
-
}): react_jsx_runtime.JSX.Element;
|
|
1390
|
-
|
|
1391
|
-
/**
|
|
1392
|
-
* `enterTextEditAction` — immediate Action descriptor for entering in-place
|
|
1393
|
-
* text editing on a selected text node.
|
|
1394
|
-
*
|
|
1395
|
-
* ## Status: REAL
|
|
1396
|
-
*
|
|
1397
|
-
* Fires via `useTextTool.bindings` when the user clicks on a
|
|
1398
|
-
* selected text node. Calls `deps.textEdit.startEdit(id)` to activate the
|
|
1399
|
-
* contenteditable overlay managed by `useTextEdit` / `useSceneTextEdit`.
|
|
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.
|
|
1427
|
-
*
|
|
1428
|
-
* ### Future: per-kind target classification (Phase 14e)
|
|
1429
|
-
*
|
|
1430
|
-
* Once `classifyTarget` surfaces node-kind info on body hits, the binding spec
|
|
1431
|
-
* can use a `{ kindOf: hit => hit?.kind === 'text' }` predicate to pre-filter
|
|
1432
|
-
* at dispatch time, making the `isTextNode` guard redundant. The action can
|
|
1433
|
-
* then drop `isTextNode` from `TextEditDep`.
|
|
1434
|
-
*
|
|
1435
|
-
* ## Migration plan for useTextTool
|
|
1436
|
-
*
|
|
1437
|
-
* When wiring `useTextTool` to `Tool.bindings`:
|
|
1438
|
-
*
|
|
1439
|
-
* 1. Add to `useTextTool`'s `bindings`:
|
|
1440
|
-
* ```ts
|
|
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.
|
|
1448
|
-
*/
|
|
1449
|
-
|
|
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
|
-
/**
|
|
1482
|
-
* @experimental
|
|
1483
|
-
* Static descriptor for the `enterTextEdit` Action.
|
|
1484
|
-
*
|
|
1485
|
-
* Requires dep-schema entries: `textEdit`, `selection`.
|
|
1486
|
-
*
|
|
1487
|
-
* No `defaultBinding` / `defaultBinding` — fires only via `Tool.bindings`.
|
|
1488
|
-
* Self-guards via `TextEditDep.isTextNode` when provided.
|
|
1489
|
-
*/
|
|
1490
|
-
declare const enterTextEditAction: Action & {
|
|
1491
|
-
requires: string[];
|
|
1492
|
-
};
|
|
1493
|
-
|
|
1494
|
-
/**
|
|
1495
|
-
* Consumer-supplied commit for the Slice action. `commit` receives the finite
|
|
1496
|
-
* slice segment (world coords); the consumer scans the scene, splits crossed
|
|
1497
|
-
* paths via `splitPathByLine`, and applies the result as one undoable batch.
|
|
1498
|
-
*/
|
|
1499
|
-
interface SliceDep {
|
|
1500
|
-
commit(a: Point2, b: Point2): void;
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* @experimental
|
|
1504
|
-
* Static descriptor for the `slice` Action.
|
|
1505
|
-
*
|
|
1506
|
-
* Ongoing drag invoker: tracks a slice line from drag start to current
|
|
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.
|
|
1510
|
-
*/
|
|
1511
|
-
declare const sliceAction: Action & {
|
|
1512
|
-
requires: string[];
|
|
1513
|
-
};
|
|
1514
|
-
|
|
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
|
-
/**
|
|
1547
|
-
* Adapter dep for `areaSelectAction`.
|
|
1548
|
-
*
|
|
1549
|
-
* Provided by `<SceneCanvas>` / `<StandardActionsRegistrar>` via AABB
|
|
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];
|
|
927
|
+
declare const modeNot: (m: string) => Condition;
|
|
928
|
+
/** Active mode's `allows` includes capability `cap`. */
|
|
929
|
+
declare const capabilityIs: (cap: string) => Condition;
|
|
930
|
+
/** Active mode's `allows` includes ANY of `caps` (OR). */
|
|
931
|
+
declare const capabilityIn: (caps: readonly string[]) => Condition;
|
|
932
|
+
/** Active mode's `allows` includes ALL of `caps` (AND). */
|
|
933
|
+
declare const capabilityAll: (caps: readonly string[]) => Condition;
|
|
934
|
+
/** Active mode's `allows` does NOT include capability `cap`. */
|
|
935
|
+
declare const capabilityNot: (cap: string) => Condition;
|
|
936
|
+
|
|
2368
937
|
/**
|
|
2369
|
-
*
|
|
2370
|
-
*
|
|
938
|
+
* Kit-shipped defaults. Merged with the consumer's `chromeVisibility` map
|
|
939
|
+
* at resolve time; consumer entries take precedence per id.
|
|
940
|
+
*
|
|
941
|
+
* Written as literal Rule trees rather than fluent chains so the inputs
|
|
942
|
+
* each rule depends on are immediately visible. The fluent atoms compile
|
|
943
|
+
* to the same trees; either form is valid in a VisibilityRules entry.
|
|
944
|
+
*
|
|
945
|
+
* Gating is written in terms of **capabilities, not mode ids**. A
|
|
946
|
+
* capability rule keeps holding when a new mode is added that permits the
|
|
947
|
+
* same thing; a mode rule has to be found and edited every time. The one
|
|
948
|
+
* exception is the `path-edit.*` chrome below, which is genuinely
|
|
949
|
+
* mode-specific: it's the visual signature of that mode, not a statement
|
|
950
|
+
* about what the user is allowed to do.
|
|
951
|
+
*
|
|
952
|
+
* Chrome that gates on the *absence* of a capability (the selection
|
|
953
|
+
* outline, suppressed while an anchor-editing overlay owns the visuals)
|
|
954
|
+
* uses `capability: { not: … }` for the same reason.
|
|
2371
955
|
*/
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
reason?: ActionDisabledReason;
|
|
2375
|
-
}
|
|
2376
|
-
declare function evaluateEnabled(action: Action, deps?: ActionDeps): ActionEnabledResult;
|
|
956
|
+
declare const defaultVisibilityRules: VisibilityRules;
|
|
957
|
+
|
|
2377
958
|
/**
|
|
2378
|
-
*
|
|
2379
|
-
*
|
|
2380
|
-
*
|
|
959
|
+
* Build the per-frame visibility check. Merges consumer rules on top
|
|
960
|
+
* of the kit defaults, then closes over `ctx` so each chrome-id
|
|
961
|
+
* lookup runs its rule against the current state.
|
|
962
|
+
*
|
|
963
|
+
* `VisibilityRules` entries may be either fluent `Condition` values
|
|
964
|
+
* (callable) or raw `Rule` trees — we normalize at lookup time.
|
|
965
|
+
*
|
|
966
|
+
* Predicates are O(1) and run once per chrome id per frame; no
|
|
967
|
+
* memoization in v1 — profile before adding any.
|
|
968
|
+
*
|
|
969
|
+
* `ChromeCtx` is the legacy shape; surfaces still on it get `mode='normal'`
|
|
970
|
+
* and `DEFAULT_ALLOWED_CAPABILITIES` filled in here, so a caller that never
|
|
971
|
+
* opted into modality behaves exactly like one sitting in normal mode.
|
|
2381
972
|
*/
|
|
2382
|
-
|
|
973
|
+
declare function resolveVisibility(consumer: VisibilityRules | undefined, ctx: ChromeCtx | RuleCtx): (id: ChromeId) => boolean;
|
|
974
|
+
|
|
2383
975
|
/**
|
|
2384
|
-
* @
|
|
2385
|
-
*
|
|
976
|
+
* Pure assembler for {@link ChromeCtx}.
|
|
977
|
+
*
|
|
978
|
+
* Called once per frame on the rendering side from whichever surface
|
|
979
|
+
* needs to evaluate visibility rules — `composeAffordanceLayer`,
|
|
980
|
+
* `createSelectionOverlayLayer`, the dispatcher overlay layer, etc.
|
|
981
|
+
*
|
|
982
|
+
* Sources are passed as plain getters / values rather than the live
|
|
983
|
+
* objects they come from. This keeps `chrome-caps` from depending on
|
|
984
|
+
* React, the dispatcher's concrete type, or `ChromeState`'s bounds
|
|
985
|
+
* machinery — the same builder works from a test harness, a Storybook
|
|
986
|
+
* fixture, or `<SceneCanvas>`.
|
|
2386
987
|
*/
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
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;
|
|
988
|
+
|
|
989
|
+
interface BuildChromeCtxArgs {
|
|
990
|
+
focused: boolean;
|
|
991
|
+
selection: readonly NodeId[];
|
|
992
|
+
multiActive: boolean;
|
|
993
|
+
modifiers: ModifierState;
|
|
994
|
+
action: {
|
|
995
|
+
kind: string | null;
|
|
996
|
+
id: string | null;
|
|
997
|
+
};
|
|
998
|
+
hover: NodeId | null;
|
|
999
|
+
view: View;
|
|
2430
1000
|
}
|
|
1001
|
+
declare function buildChromeCtx(args: BuildChromeCtxArgs): ChromeCtx;
|
|
1002
|
+
|
|
2431
1003
|
/**
|
|
2432
|
-
*
|
|
2433
|
-
*
|
|
2434
|
-
*
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
*
|
|
2441
|
-
*
|
|
2442
|
-
|
|
2443
|
-
|
|
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).
|
|
1004
|
+
* useHoverTracking — last-hovered NodeId for chrome-caps rules.
|
|
1005
|
+
*
|
|
1006
|
+
* Attaches a `pointermove` listener to the supplied canvas; on each
|
|
1007
|
+
* move, converts client coords → world coords and runs the supplied
|
|
1008
|
+
* `getNodeAtPoint`, caching the resulting id on a ref. Cleared on
|
|
1009
|
+
* `pointerleave` (pointer left the canvas → nothing is hovered).
|
|
1010
|
+
*
|
|
1011
|
+
* Returns a stable getter — call once per frame from `buildChromeCtx`.
|
|
1012
|
+
*
|
|
1013
|
+
* No re-render: the ref updates silently, and the next render that
|
|
1014
|
+
* reads it picks up the fresh value. Chrome-caps rules are evaluated
|
|
1015
|
+
* during paint, so the hover state is always read fresh.
|
|
2449
1016
|
*/
|
|
2450
|
-
|
|
1017
|
+
|
|
1018
|
+
interface UseHoverTrackingArgs {
|
|
1019
|
+
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
1020
|
+
/** Client → world conversion. Same shape as the dispatcher's. */
|
|
1021
|
+
clientToWorld: (clientX: number, clientY: number) => {
|
|
1022
|
+
x: number;
|
|
1023
|
+
y: number;
|
|
1024
|
+
};
|
|
1025
|
+
/** Topmost-id resolver. Returns null when the world point hits nothing. */
|
|
1026
|
+
getNodeAtPoint: (worldX: number, worldY: number) => {
|
|
1027
|
+
id: NodeId;
|
|
1028
|
+
} | null;
|
|
1029
|
+
enabled?: boolean;
|
|
1030
|
+
}
|
|
1031
|
+
declare function useHoverTracking(args: UseHoverTrackingArgs): () => NodeId | null;
|
|
2451
1032
|
|
|
2452
1033
|
/**
|
|
2453
1034
|
* `moveAction` — first `ongoing`-timing Action descriptor.
|
|
@@ -2669,12 +1250,19 @@ declare const areaSelectAction: Action & {
|
|
|
2669
1250
|
* delegating to the scene's `add()` with a sensible default data payload for
|
|
2670
1251
|
* the given `kind`. Override per-consumer for custom node factories.
|
|
2671
1252
|
*
|
|
1253
|
+
* ## Modifiers and snapping
|
|
1254
|
+
*
|
|
1255
|
+
* Grid snapping comes from the optional `snap` dep, applied to the drag's
|
|
1256
|
+
* start and current point (never to freehand pencil samples). The `line`
|
|
1257
|
+
* kind additionally honors Shift (constrain to 15°) and reads Alt/center as
|
|
1258
|
+
* "mirror the start around the pointer" rather than "grow a symmetric AABB".
|
|
1259
|
+
* All of it resolves in `resolveEndpoints`, which both `overlay()` and
|
|
1260
|
+
* `onEnd()` call — so the live preview and the committed node can't disagree.
|
|
1261
|
+
*
|
|
2672
1262
|
* ## What this does NOT wire (vs `useInsert`)
|
|
2673
1263
|
*
|
|
2674
|
-
* - `InsertBehavior` pipeline (snap, etc.) — deferred to a later phase.
|
|
2675
1264
|
* - `pointInsert` fallback for click / sub-threshold drags — not wired; a
|
|
2676
1265
|
* sub-threshold drag produces no insert.
|
|
2677
|
-
* - Live insert overlay — deferred to Phase 7 overlay surface.
|
|
2678
1266
|
* - `clickOnly` mode — not applicable to the descriptor model.
|
|
2679
1267
|
*
|
|
2680
1268
|
* ## Live preview
|
|
@@ -2730,17 +1318,32 @@ declare const insertAction: Action & {
|
|
|
2730
1318
|
* This avoids ambient double-fire and keeps the action scoped to select-tool
|
|
2731
1319
|
* context where `classifyTarget` is wired.
|
|
2732
1320
|
*
|
|
2733
|
-
* ## enabled
|
|
1321
|
+
* ## enabled — the anchor-editing fall-through
|
|
1322
|
+
*
|
|
1323
|
+
* There is no "is anything selected?" guard: clearing an already-empty
|
|
1324
|
+
* selection is a safe no-op, and the binding spec is the real gate for
|
|
1325
|
+
* where the click landed.
|
|
1326
|
+
*
|
|
1327
|
+
* What `enabled` *does* do is decline while a path is in anchor-edit mode,
|
|
1328
|
+
* so the dispatcher falls through to `selectAnchorAction`. This is the
|
|
1329
|
+
* click twin of the opt-out `areaSelectAction.start` already performs for
|
|
1330
|
+
* the drag gesture, and it exists for the same reason: `useSelectTool`
|
|
1331
|
+
* binds this action at **active** scope while `selectAnchor` is **ambient**,
|
|
1332
|
+
* so without the gate an anchor that happens to sit over empty canvas
|
|
1333
|
+
* clears the node selection instead of selecting the anchor.
|
|
2734
1334
|
*
|
|
2735
|
-
*
|
|
2736
|
-
*
|
|
1335
|
+
* Capability eligibility (`creates-selection`, which `path-edit` does not
|
|
1336
|
+
* allow) already covers consumers that wired a mode registry. Consumers
|
|
1337
|
+
* without one get no eligibility filtering at all, so this gate is the only
|
|
1338
|
+
* thing separating the two. Both mechanisms are needed — see the header of
|
|
1339
|
+
* `anchorEditing.ts`.
|
|
2737
1340
|
*/
|
|
2738
1341
|
|
|
2739
1342
|
/**
|
|
2740
1343
|
* @experimental
|
|
2741
1344
|
* Static descriptor for the `clearSelection` Action.
|
|
2742
1345
|
*
|
|
2743
|
-
* Requires dep-schema
|
|
1346
|
+
* Requires dep-schema entries: `selection`, `editAnchors`.
|
|
2744
1347
|
*
|
|
2745
1348
|
* Registered via `useStandardActions`; bound via `useSelectTool.bindings`.
|
|
2746
1349
|
*/
|
|
@@ -2920,40 +1523,43 @@ declare const viewportZoomAction: Action & {
|
|
|
2920
1523
|
/**
|
|
2921
1524
|
* `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.
|
|
2922
1525
|
*
|
|
2923
|
-
*
|
|
1526
|
+
* Owns the whole press-on-an-anchor gesture, because pressing and dragging
|
|
1527
|
+
* an anchor are two outcomes of one interaction rather than two
|
|
1528
|
+
* interactions:
|
|
1529
|
+
*
|
|
1530
|
+
* - **Press** selects. Bare press replaces the anchor selection; Shift
|
|
1531
|
+
* toggles the pressed anchor in or out of it. Pressing an anchor that
|
|
1532
|
+
* is already part of a multi-anchor selection leaves the selection
|
|
1533
|
+
* alone, so you can grab a set and move it.
|
|
1534
|
+
* - **Drag** then moves whatever the press left selected — one anchor,
|
|
1535
|
+
* or every selected anchor together.
|
|
1536
|
+
* - **Dragging a control handle** moves just that handle, mirroring the
|
|
1537
|
+
* opposite one while the anchor is smooth. Alt breaks the mirror.
|
|
1538
|
+
*
|
|
1539
|
+
* Selection lives on the `editAnchors` dep (flat anchor indices) rather
|
|
1540
|
+
* than in this handle's scratch, because the keyboard actions
|
|
1541
|
+
* (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it
|
|
1542
|
+
* between gestures.
|
|
2924
1543
|
*
|
|
2925
|
-
*
|
|
1544
|
+
* ## Wiring
|
|
1545
|
+
*
|
|
1546
|
+
* The descriptor is operational when the consumer wires:
|
|
2926
1547
|
* 1. `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher
|
|
2927
1548
|
* classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`,
|
|
2928
1549
|
* or `controlOut:N` affordances on pointerdown.
|
|
2929
1550
|
* 2. The `editAnchors` dep in the DepSchema.
|
|
2930
1551
|
*
|
|
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
1552
|
* When `ctx.drag.affordance` is absent or is not an anchor/control kind,
|
|
2938
1553
|
* `start` returns `{}` (no-op) so other bindings can handle the drag.
|
|
2939
1554
|
*
|
|
2940
|
-
* @see useEditAnchors — the React hook this descriptor mirrors.
|
|
2941
1555
|
* @see buildAffordanceAt — the classifier that produces anchor affordances.
|
|
1556
|
+
* @see anchorEdits — the pure geometry these handlers drive.
|
|
2942
1557
|
*/
|
|
2943
1558
|
|
|
2944
1559
|
/**
|
|
2945
1560
|
* Static descriptor for the `editAnchors` Action.
|
|
2946
1561
|
*
|
|
2947
1562
|
* 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
1563
|
*/
|
|
2958
1564
|
declare const editAnchorsAction: Action & {
|
|
2959
1565
|
requires: string[];
|
|
@@ -3130,8 +1736,28 @@ interface UseGestureDispatcherOptions {
|
|
|
3130
1736
|
actions: ActionsRegistry;
|
|
3131
1737
|
/** Tool definitions keyed by id. Typically passes an empty Map. */
|
|
3132
1738
|
toolsById: ReadonlyMap<string, Tool>;
|
|
1739
|
+
/** Ids (within `toolsById`) of always-on tools, whose bindings assemble at
|
|
1740
|
+
* ambient scope. See `DispatcherContext.ambientToolIds`. */
|
|
1741
|
+
ambientToolIds?: readonly string[];
|
|
3133
1742
|
/** Default true. Set false to opt out of dispatcher wiring (e.g. demos that disable it). */
|
|
3134
1743
|
enabled?: boolean;
|
|
1744
|
+
/**
|
|
1745
|
+
* Observer fired whenever the dispatcher synthesizes a double click, in
|
|
1746
|
+
* world coordinates. Runs BEFORE the event is dispatched and independently
|
|
1747
|
+
* of which binding (if any) handles it.
|
|
1748
|
+
*
|
|
1749
|
+
* This is deliberately not an Action. `<SceneCanvas onDoubleClick>` is a
|
|
1750
|
+
* notification — "the user double-clicked, here's what they hit" — and a
|
|
1751
|
+
* notification must not compete with behavior for the gesture. As a binding
|
|
1752
|
+
* it would lose to `enterPathEdit` on any body hit and silently never fire.
|
|
1753
|
+
* Routing it here keeps a single definition of "double click" (the point of
|
|
1754
|
+
* consolidating the kit's three detectors) without giving it
|
|
1755
|
+
* first-match-wins semantics it shouldn't have.
|
|
1756
|
+
*/
|
|
1757
|
+
onDoubleClick?: (world: {
|
|
1758
|
+
x: number;
|
|
1759
|
+
y: number;
|
|
1760
|
+
}) => void;
|
|
3135
1761
|
/**
|
|
3136
1762
|
* Default true. Set false to leave the window `keydown`/`keyup` listeners
|
|
3137
1763
|
* unattached so keyboard-bound actions never dispatch — pointer, wheel, and
|
|
@@ -3160,23 +1786,25 @@ interface UseGestureDispatcherOptions {
|
|
|
3160
1786
|
y: number;
|
|
3161
1787
|
}) => AffordanceHit | null;
|
|
3162
1788
|
/**
|
|
3163
|
-
* Optional body
|
|
3164
|
-
*
|
|
3165
|
-
*
|
|
3166
|
-
* `
|
|
1789
|
+
* Optional body classifier. Called on every pointerdown with the world-space
|
|
1790
|
+
* coordinates of the pointer. Its result is packed onto the event as
|
|
1791
|
+
* `bodyTarget` + `bodyKind`, which `matchTarget` reads to resolve the
|
|
1792
|
+
* string-form `TargetSpec` values in `Tool.bindings`.
|
|
3167
1793
|
*
|
|
3168
|
-
*
|
|
1794
|
+
* `body` is `'empty'` when nothing is under the pointer, `'selected-body'`
|
|
3169
1795
|
* when the topmost hit belongs to the current selection, or
|
|
3170
|
-
* `'unselected-body'` when it belongs to a node that isn't selected.
|
|
1796
|
+
* `'unselected-body'` when it belongs to a node that isn't selected. `kind`
|
|
1797
|
+
* is the hit node's semantic kind, when the scene can name it.
|
|
3171
1798
|
*
|
|
3172
|
-
* When omitted,
|
|
3173
|
-
* `'unselected-body'`)
|
|
3174
|
-
*
|
|
1799
|
+
* When omitted, every body-derived target form (`'empty'`,
|
|
1800
|
+
* `'selected-body'`, `'unselected-body'`, `kind:<k>`, `kind:<k>:selected`)
|
|
1801
|
+
* never matches — bindings using those specs are silently skipped.
|
|
1802
|
+
* `<SceneCanvas>` wires this.
|
|
3175
1803
|
*/
|
|
3176
1804
|
classifyTarget?: (worldPoint: {
|
|
3177
1805
|
x: number;
|
|
3178
1806
|
y: number;
|
|
3179
|
-
}) =>
|
|
1807
|
+
}) => BodyClassification;
|
|
3180
1808
|
/**
|
|
3181
1809
|
* Optional pre-created `Dispatcher`. When provided, this hook pumps events
|
|
3182
1810
|
* into the supplied instance instead of creating its own. Lets a parent
|
|
@@ -3365,218 +1993,6 @@ declare function useViewAnimation(setView: (v: View) => void): {
|
|
|
3365
1993
|
cancel: () => void;
|
|
3366
1994
|
};
|
|
3367
1995
|
|
|
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;
|
|
3548
|
-
}
|
|
3549
|
-
/**
|
|
3550
|
-
* Pointer-event dispatcher that wires `useMove` + `useResize` to a canvas.
|
|
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.
|
|
3558
|
-
*
|
|
3559
|
-
* Spread the returned bindings onto a `<canvas>`:
|
|
3560
|
-
* ```tsx
|
|
3561
|
-
* const bindings = usePointerGestures({ move, resize, pickEvery, selection });
|
|
3562
|
-
* return <canvas ref={canvasRef} {...bindings} />;
|
|
3563
|
-
* ```
|
|
3564
|
-
*/
|
|
3565
|
-
declare function usePointerGestures<TMovePose, TResizePose>(options: UsePointerGesturesOptions<TMovePose, TResizePose>): PointerGestureBindings;
|
|
3566
|
-
|
|
3567
|
-
/**
|
|
3568
|
-
* Identity helper for declaring a `Tool`. Exists for TypeScript inference:
|
|
3569
|
-
* passing the spec directly to a generic site loses `TScratch` inference, so
|
|
3570
|
-
* authors would have to spell out the type argument.
|
|
3571
|
-
*
|
|
3572
|
-
* const pen = defineTool({
|
|
3573
|
-
* id: 'pen',
|
|
3574
|
-
* initScratch: () => ({ anchors: [] as Point[] }),
|
|
3575
|
-
* drag: { onMove: (e, ctx) => { ctx.scratch.anchors.push(...); return 'claim'; } },
|
|
3576
|
-
* });
|
|
3577
|
-
*/
|
|
3578
|
-
declare function defineTool<TScratch = undefined>(spec: Tool<TScratch>): Tool<TScratch>;
|
|
3579
|
-
|
|
3580
1996
|
interface UseToolsOptions {
|
|
3581
1997
|
/** Initial active-slot tool id. Must exist in `registry`. */
|
|
3582
1998
|
active: string;
|
|
@@ -3586,40 +2002,17 @@ interface UseToolsOptions {
|
|
|
3586
2002
|
registry: Record<string, AnyTool>;
|
|
3587
2003
|
/** Always-on tools — listen continuously regardless of active slot. */
|
|
3588
2004
|
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
2005
|
}
|
|
3614
2006
|
interface ToolsApi {
|
|
3615
2007
|
/** Current active-slot tool id. */
|
|
3616
2008
|
active: string;
|
|
3617
|
-
/** Set the active-slot tool.
|
|
2009
|
+
/** Set the active-slot tool. The gesture dispatcher watches the active
|
|
2010
|
+
* tool and cancels any in-flight handle itself. */
|
|
3618
2011
|
setActive: (id: string) => void;
|
|
3619
2012
|
/** Currently hotkey-engaged tool id (or `null`). Derived as the top of
|
|
3620
2013
|
* the hotkey stack for backwards compat with the pre-stack API. */
|
|
3621
2014
|
hotkeyEngaged: string | null;
|
|
3622
|
-
/** Engage a hotkey-slot tool by id.
|
|
2015
|
+
/** Engage a hotkey-slot tool by id. */
|
|
3623
2016
|
engageHotkey: (id: string) => void;
|
|
3624
2017
|
/** Disengage the hotkey-slot tool, if any. */
|
|
3625
2018
|
disengageHotkey: () => void;
|
|
@@ -3627,13 +2020,6 @@ interface ToolsApi {
|
|
|
3627
2020
|
ambient: readonly AnyTool[];
|
|
3628
2021
|
/** Full registry — for userland UI (palette buttons, etc.). */
|
|
3629
2022
|
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
2023
|
/** Returns true if a tool with the given id is in the registry or ambient list. */
|
|
3638
2024
|
has(id: string): boolean;
|
|
3639
2025
|
/** All overlay layers from currently-engaged tools (active slot, hotkey
|
|
@@ -3643,7 +2029,12 @@ interface ToolsApi {
|
|
|
3643
2029
|
getActiveOverlays(): RenderLayer<unknown>[];
|
|
3644
2030
|
}
|
|
3645
2031
|
/**
|
|
3646
|
-
* Manages the active tool
|
|
2032
|
+
* Manages the active tool and hotkey slot.
|
|
2033
|
+
*
|
|
2034
|
+
* It used to also own a dispatcher: `useTools` constructed the tool-routing
|
|
2035
|
+
* dispatcher and `<Canvas>` pumped DOM events into it. Input now belongs
|
|
2036
|
+
* entirely to `useGestureDispatcher`, so what's left here is slot state plus
|
|
2037
|
+
* the overlay roll-up.
|
|
3647
2038
|
*
|
|
3648
2039
|
* Requires `<ActiveToolContextProvider>` (or `<WeaselProvider>` /
|
|
3649
2040
|
* `<SceneCanvas>`, which mount one internally) in scope: active/hotkey state
|
|
@@ -3664,10 +2055,20 @@ interface UseKeybindingsOptions {
|
|
|
3664
2055
|
* `tools.active` was when the hook first ran (i.e. the initial active
|
|
3665
2056
|
* tool). Pass `null` to disable Escape-returns-to-default behavior. */
|
|
3666
2057
|
defaultTool?: string | null;
|
|
2058
|
+
/**
|
|
2059
|
+
* Gate for keyboard tool activation: return false to refuse a tool the
|
|
2060
|
+
* active mode doesn't allow. `<SceneCanvas>` wires this from
|
|
2061
|
+
* `getActiveMode` + each tool's `capabilities`, using the same predicate
|
|
2062
|
+
* `ToolPalette` uses to grey a button out — so the grey-out becomes a
|
|
2063
|
+
* guarantee rather than a hint.
|
|
2064
|
+
*
|
|
2065
|
+
* Omit for consumers with no mode registry: every tool stays activatable.
|
|
2066
|
+
*/
|
|
2067
|
+
isToolEligible?: (toolId: string) => boolean;
|
|
3667
2068
|
}
|
|
3668
2069
|
declare function useKeybindings(tools: ToolsApi, options?: UseKeybindingsOptions): void;
|
|
3669
2070
|
|
|
3670
|
-
type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color';
|
|
2071
|
+
type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint';
|
|
3671
2072
|
interface ToolPrefBase<K extends string, Value> {
|
|
3672
2073
|
kind: K;
|
|
3673
2074
|
/** Human-readable label. */
|
|
@@ -3734,7 +2135,22 @@ interface ToolPrefColor extends ToolPrefBase<'color', string> {
|
|
|
3734
2135
|
*/
|
|
3735
2136
|
interface ToolPrefCustom extends ToolPrefBase<string, unknown> {
|
|
3736
2137
|
}
|
|
3737
|
-
|
|
2138
|
+
/**
|
|
2139
|
+
* A whole `FillStyle`, not a color inside one. Use it wherever the value is
|
|
2140
|
+
* the tagged paint union — a solid color, a pattern, a gradient — rather
|
|
2141
|
+
* than a hex string.
|
|
2142
|
+
*
|
|
2143
|
+
* Addressing `…fill.color` instead reads `undefined` off a gradient (so the
|
|
2144
|
+
* control shows its default and claims the text is black) and writes a
|
|
2145
|
+
* hybrid `{ fill: 'gradient', stops, color }` that the renderer's structural
|
|
2146
|
+
* `'color' in paint` checks then paint flat solid. The union has to be
|
|
2147
|
+
* edited as a union.
|
|
2148
|
+
*/
|
|
2149
|
+
interface ToolPrefPaint extends ToolPrefBase<'paint', unknown> {
|
|
2150
|
+
/** Offer an opacity control alongside the color. */
|
|
2151
|
+
alpha?: boolean;
|
|
2152
|
+
}
|
|
2153
|
+
type ToolPref = ToolPrefNumber | ToolPrefBoolean | ToolPrefString | ToolPrefEnum | ToolPrefColor | ToolPrefPaint;
|
|
3738
2154
|
/** Built-in or app-defined leaf. */
|
|
3739
2155
|
type ToolPrefLeaf = ToolPref | ToolPrefCustom;
|
|
3740
2156
|
/** Nestable group: branch nodes a tool can use to organize its prefs. */
|
|
@@ -3744,61 +2160,7 @@ interface ToolPrefGroup {
|
|
|
3744
2160
|
children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
|
|
3745
2161
|
}
|
|
3746
2162
|
|
|
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> {
|
|
2163
|
+
interface UseSelectToolOptions<TPose> {
|
|
3802
2164
|
/** Return ids of all objects whose painted body covers (worldX, worldY).
|
|
3803
2165
|
* Order doesn't matter — the tool collapses parent/child overlap via
|
|
3804
2166
|
* `pickTopMostHit`. When omitted, defaults to a rect AABB-vs-point scan
|
|
@@ -3809,45 +2171,60 @@ interface UseSelectToolOptions<TNode extends {
|
|
|
3809
2171
|
/** Optional alt-aware selection-update hit returning the single id the
|
|
3810
2172
|
* click should act on. */
|
|
3811
2173
|
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
2174
|
/** Project a pose to its AABB. Default: identity. */
|
|
3815
2175
|
poseBounds?: (pose: TPose) => Bounds;
|
|
2176
|
+
/**
|
|
2177
|
+
* How the default `pickEvery` decides a **leaf** node covers the pointer.
|
|
2178
|
+
* Ignored when `pickEvery` is supplied.
|
|
2179
|
+
*
|
|
2180
|
+
* - `'aabb'` (default) — the pose rect. Cheap, and the historical behavior:
|
|
2181
|
+
* a click anywhere in the bounding box selects the node.
|
|
2182
|
+
* - `'silhouette'` — the pose rect as a pre-test, then the ink the painter
|
|
2183
|
+
* lays down: `findShapeSilhouette` filled or not per the painter's `ink`,
|
|
2184
|
+
* plus its outline widened by the stroke half-width and `pickTolerance`.
|
|
2185
|
+
* A click inside the bounding box but outside the drawn shape misses —
|
|
2186
|
+
* the concave notch of a star, the corner outside an ellipse, the blank
|
|
2187
|
+
* half of a text box — while a click on the thin outline of an unfilled
|
|
2188
|
+
* shape hits.
|
|
2189
|
+
*
|
|
2190
|
+
* Container nodes already consult their silhouette unconditionally (it is
|
|
2191
|
+
* how their clip is derived); this extends the same test to leaves. Not the
|
|
2192
|
+
* default because it changes what a click selects for every existing
|
|
2193
|
+
* consumer, and a painter with no `silhouette` is unaffected either way —
|
|
2194
|
+
* it falls back to the AABB.
|
|
2195
|
+
*/
|
|
2196
|
+
leafPicking?: 'aabb' | 'silhouette';
|
|
2197
|
+
/**
|
|
2198
|
+
* Grab slop around a shape's outline, in **world** units. Default 0.
|
|
2199
|
+
*
|
|
2200
|
+
* Only consulted under `leafPicking: 'silhouette'`. World units because
|
|
2201
|
+
* this hook has no view; `<SceneCanvas>` takes a screen-pixel figure
|
|
2202
|
+
* (`geometry.pickTolerancePx`) and divides by scale on its own pick path.
|
|
2203
|
+
* Without some slop a hairline outline is an unhittable target.
|
|
2204
|
+
*/
|
|
2205
|
+
pickTolerance?: number;
|
|
3816
2206
|
/** Move-action options. The move gesture is dispatcher-routed,
|
|
3817
2207
|
* so only `behaviors` is consumed here — threaded into the move binding's
|
|
3818
2208
|
* `opts.behaviors`. Other `UseMoveOptions` fields are accepted for API shape
|
|
3819
2209
|
* but not read by this tool. */
|
|
3820
2210
|
move?: UseMoveOptions<TPose>;
|
|
3821
|
-
/**
|
|
3822
|
-
*
|
|
3823
|
-
|
|
2211
|
+
/**
|
|
2212
|
+
* When this returns true, a Shift/Meta extend-click must NOT change the
|
|
2213
|
+
* node selection.
|
|
2214
|
+
*
|
|
2215
|
+
* `<SceneCanvas>` wires it to "a path is in anchor-edit mode", where
|
|
2216
|
+
* Shift-click means "add this anchor to the anchor selection". Without
|
|
2217
|
+
* the lock the same click also toggles the edited node out of the node
|
|
2218
|
+
* selection, and the `editAnchors` dep treats that as the edit target
|
|
2219
|
+
* disappearing — so multi-selecting anchors silently exited edit mode.
|
|
2220
|
+
*
|
|
2221
|
+
* Only extend-clicks are locked. A plain click still re-selects, so
|
|
2222
|
+
* clicking a different node exits edit mode as usual.
|
|
2223
|
+
*/
|
|
2224
|
+
extendClickLocked?: () => boolean;
|
|
3824
2225
|
/** Optional debug sink. Reserved for future overlay/affordance hitbox
|
|
3825
2226
|
* recording. */
|
|
3826
2227
|
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
2228
|
/** Reparent-on-drop behavior for drag-to-move. `'off'` (default)
|
|
3852
2229
|
* preserves translate-only commits. `'top'` lands the moved nodes at
|
|
3853
2230
|
* the top of the container under the drop point. `'above'` lands them
|
|
@@ -3855,33 +2232,16 @@ interface UseSelectToolOptions<TNode extends {
|
|
|
3855
2232
|
* semantics when the hit is itself a container). Requires the
|
|
3856
2233
|
* `nodeAtPoint` dep to be registered (sourced by `<SceneCanvas>`). */
|
|
3857
2234
|
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
2235
|
}
|
|
3866
2236
|
/** Intersection of the move + area-select adapter interfaces.
|
|
3867
2237
|
* Resize / rotate adapters moved to `useResizeTool` / `useRotateTool`. */
|
|
3868
2238
|
type SelectAdapter<TNode extends {
|
|
3869
|
-
id: string;
|
|
3870
|
-
}, 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
|
-
};
|
|
2239
|
+
id: string;
|
|
2240
|
+
}, TPose> = MoveAdapter<TNode, TPose> & AreaSelectAdapter;
|
|
3881
2241
|
/** Active-slot Tool:
|
|
3882
|
-
* - pointerDown
|
|
3883
|
-
* so click semantics (selection replace, extend,
|
|
3884
|
-
*
|
|
2242
|
+
* - `select.pick` classifies the press (pointerDown) — it runs the tool's own
|
|
2243
|
+
* pickBest/pickEvery so click semantics (selection replace, extend,
|
|
2244
|
+
* deferred collapse) live in one place.
|
|
3885
2245
|
* - drag is owned exclusively by the dispatcher via `Tool.bindings`
|
|
3886
2246
|
* (selected-body → moveAction, rotate-handle → rotateAction, handle:* →
|
|
3887
2247
|
* resizeAction, empty → areaSelectAction, click on empty → clearSelection).
|
|
@@ -3891,81 +2251,7 @@ type SelectScratch = {
|
|
|
3891
2251
|
*/
|
|
3892
2252
|
declare function useSelectTool<TNode extends {
|
|
3893
2253
|
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>>;
|
|
2254
|
+
}, TPose>(adapter: SelectAdapter<TNode, TPose>, options: UseSelectToolOptions<TPose>): Tool<null>;
|
|
3969
2255
|
|
|
3970
2256
|
interface UseRotateToolOptions<TNode extends {
|
|
3971
2257
|
id: string;
|
|
@@ -4036,7 +2322,10 @@ declare function useRotateTool<TNode extends {
|
|
|
4036
2322
|
* (resolve before calling) — this helper doesn't try to detect that.
|
|
4037
2323
|
*
|
|
4038
2324
|
* Pure sibling z-order without a parent/child relation is not resolved
|
|
4039
|
-
* here yet — see
|
|
2325
|
+
* here yet — see "Sibling z-order is unresolved in hit-picking" under
|
|
2326
|
+
* Tools & gestures in `docs/TODO.md`. The shape it wants is an optional
|
|
2327
|
+
* `getZIndex` / `compareZ` on the adapter below, composing with the
|
|
2328
|
+
* parent/child collapse rather than replacing it.
|
|
4040
2329
|
*/
|
|
4041
2330
|
interface PickTopMostHitAdapter {
|
|
4042
2331
|
getParent?: (id: string) => string | null;
|
|
@@ -4098,40 +2387,16 @@ interface HandScratch {
|
|
|
4098
2387
|
*/
|
|
4099
2388
|
declare function useHandTool(opts?: UseHandToolOptions): Tool<HandScratch | null>;
|
|
4100
2389
|
|
|
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>;
|
|
2390
|
+
/** Text tool. Drag-rect insertion is owned end-to-end by the dispatcher's
|
|
2391
|
+
* `insertAction` (kind: `'text'`); click-on-selected-text-node enters edit
|
|
2392
|
+
* mode via the `enterTextEdit` action.
|
|
2393
|
+
*
|
|
2394
|
+
* Consumers must register both the `insert` dep (e.g. through
|
|
2395
|
+
* `SceneCanvas`'s `useInsertDepSource`) and the `textEdit` dep
|
|
2396
|
+
* (`enterTextEditAction`'s contract — see
|
|
2397
|
+
* `src/interactions/actions/defaults/enterTextEdit.ts`). Custom node
|
|
2398
|
+
* factories and hit gating belong on those deps, not on the tool. */
|
|
2399
|
+
declare function useTextTool(): Tool<undefined>;
|
|
4135
2400
|
|
|
4136
2401
|
interface PinchZoomToolOpts {
|
|
4137
2402
|
min?: number;
|
|
@@ -4147,51 +2412,13 @@ interface PinchZoomToolOpts {
|
|
|
4147
2412
|
*/
|
|
4148
2413
|
declare function usePinchZoomTool(canvasRef: React.RefObject<HTMLCanvasElement | null>, view: View, setView: (v: View) => void, opts?: PinchZoomToolOpts): void;
|
|
4149
2414
|
|
|
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
2415
|
/**
|
|
4189
2416
|
* In-progress pen anchor. `outHandle` is set when the anchor was placed via
|
|
4190
2417
|
* click-drag (smooth anchor); undefined for click-placed corners.
|
|
4191
2418
|
* `inHandle` is mirrored from the previous anchor's outHandle on segment
|
|
4192
2419
|
* emission unless `altBroken` is set on that previous anchor.
|
|
4193
2420
|
*/
|
|
4194
|
-
interface PenAnchor {
|
|
2421
|
+
interface PenAnchor$1 {
|
|
4195
2422
|
x: number;
|
|
4196
2423
|
y: number;
|
|
4197
2424
|
outHandle?: {
|
|
@@ -4207,57 +2434,32 @@ interface PenAnchor {
|
|
|
4207
2434
|
altBroken?: boolean;
|
|
4208
2435
|
}
|
|
4209
2436
|
interface PenSubpath {
|
|
4210
|
-
anchors: PenAnchor[];
|
|
2437
|
+
anchors: PenAnchor$1[];
|
|
4211
2438
|
closed: boolean;
|
|
4212
2439
|
}
|
|
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. */
|
|
2440
|
+
/**
|
|
2441
|
+
* Mutable scratch shared across pen-tool gestures. The hook keeps a stable
|
|
2442
|
+
* reference to a single instance and `initScratch` returns it on every call,
|
|
2443
|
+
* so click-by-click state survives gesture boundaries and the preview layer
|
|
2444
|
+
* can read the same object.
|
|
2445
|
+
*
|
|
2446
|
+
* ## The pen creates paths and nothing else
|
|
2447
|
+
*
|
|
2448
|
+
* Reshaping an existing path is anchor editing: double-click a path to
|
|
2449
|
+
* enter edit mode, then use the `editAnchors` / `insertPathAnchor` /
|
|
2450
|
+
* `nudgeAnchors` / `deleteAnchors` / `marqueeAnchors` / `cutPathAtAnchor`
|
|
2451
|
+
* Actions.
|
|
2452
|
+
*
|
|
2453
|
+
* The pen used to carry a second, private implementation of all of that,
|
|
2454
|
+
* with its own scratch mode, hit-test override, overlay, and undo
|
|
2455
|
+
* plumbing. It was reachable only through a `getPathObj` option whose
|
|
2456
|
+
* contract required `pose.kind` to be `'polygon'` or `'rect'` — which no
|
|
2457
|
+
* kit-created node has, including the ones the pen itself creates. So it
|
|
2458
|
+
* was dead in every consumer while shadowing the live implementation, and
|
|
2459
|
+
* the two had drifted. Its geometry now lives in
|
|
2460
|
+
* `features/paths/anchorEdits.ts`, driven by those Actions.
|
|
2461
|
+
*/
|
|
4255
2462
|
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
2463
|
finishedSubpaths: PenSubpath[];
|
|
4262
2464
|
current: PenSubpath | null;
|
|
4263
2465
|
cursor: {
|
|
@@ -4266,23 +2468,6 @@ interface PenScratch {
|
|
|
4266
2468
|
} | null;
|
|
4267
2469
|
draggingHandleAt: number | null;
|
|
4268
2470
|
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
2471
|
}
|
|
4287
2472
|
interface UsePenToolOptions<TPose> {
|
|
4288
2473
|
/** Wrap a finished PolygonPath in the consumer's pose type. */
|
|
@@ -4293,9 +2478,6 @@ interface UsePenToolOptions<TPose> {
|
|
|
4293
2478
|
adapter: {
|
|
4294
2479
|
addNode: (pose: TPose) => string;
|
|
4295
2480
|
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
2481
|
};
|
|
4300
2482
|
/** Auto-select the new object after commit. Default `true`. */
|
|
4301
2483
|
autoSelect?: boolean;
|
|
@@ -4329,33 +2511,6 @@ interface UsePenToolOptions<TPose> {
|
|
|
4329
2511
|
x: number;
|
|
4330
2512
|
y: number;
|
|
4331
2513
|
};
|
|
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;
|
|
4359
2514
|
}
|
|
4360
2515
|
/**
|
|
4361
2516
|
* Active-slot Tool: click + drag to build a `PolygonPath` Illustrator-style.
|
|
@@ -4367,12 +2522,9 @@ interface UsePenToolReturn {
|
|
|
4367
2522
|
* discards. Shift constrains the placement-drag handle to 0/45/90/135°;
|
|
4368
2523
|
* Alt during drag breaks the handle mirror for the next segment.
|
|
4369
2524
|
*
|
|
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.
|
|
2525
|
+
* Reshaping an existing path is not this tool's job — see {@link PenScratch}.
|
|
4374
2526
|
*/
|
|
4375
|
-
declare function usePenTool<TPose>(options: UsePenToolOptions<TPose>):
|
|
2527
|
+
declare function usePenTool<TPose>(options: UsePenToolOptions<TPose>): Tool<PenScratch>;
|
|
4376
2528
|
declare namespace usePenTool {
|
|
4377
2529
|
var prefs: {
|
|
4378
2530
|
name: string;
|
|
@@ -4388,80 +2540,28 @@ declare namespace usePenTool {
|
|
|
4388
2540
|
};
|
|
4389
2541
|
}
|
|
4390
2542
|
|
|
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
2543
|
/**
|
|
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.
|
|
2544
|
+
* Drag-to-draw rectangle tool.
|
|
4422
2545
|
*
|
|
4423
|
-
*
|
|
4424
|
-
* `
|
|
2546
|
+
* The gesture is owned end-to-end by the dispatcher: the `drag` binding
|
|
2547
|
+
* routes to `insertAction`, which tracks the live bounds, paints the
|
|
2548
|
+
* preview through its `overlay()` surface, and commits via the `insert`
|
|
2549
|
+
* dep on release. `<SceneCanvas>` sources that dep from
|
|
2550
|
+
* `useInsertDepSource`; consumers wanting a custom node factory override
|
|
2551
|
+
* the dep (`useDepSource('insert', …)`) rather than the tool.
|
|
2552
|
+
*
|
|
2553
|
+
* Grid snapping comes from the `snap` dep, also read by `insertAction` —
|
|
2554
|
+
* see `SnapDep` in `interactions/actions/depSchema.ts`.
|
|
4425
2555
|
*/
|
|
4426
|
-
declare function useRectTool<
|
|
4427
|
-
id: string;
|
|
4428
|
-
}>(options: UseRectToolOptions<TNode>): Tool<null>;
|
|
2556
|
+
declare function useRectTool(): Tool<null>;
|
|
4429
2557
|
|
|
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
2558
|
/**
|
|
4458
|
-
* Drag-to-draw ellipse tool.
|
|
4459
|
-
*
|
|
4460
|
-
*
|
|
2559
|
+
* Drag-to-draw ellipse tool. Mirrors `useRectTool`: the `drag` binding
|
|
2560
|
+
* routes to `insertAction`, which owns the live preview and commits
|
|
2561
|
+
* through the `insert` dep. Alt toggles from-corner ⇄ from-center
|
|
2562
|
+
* mid-drag via the action's live modifier read.
|
|
4461
2563
|
*/
|
|
4462
|
-
declare function useEllipseTool<
|
|
4463
|
-
id: string;
|
|
4464
|
-
}>(options: UseEllipseToolOptions<TNode>): Tool<null>;
|
|
2564
|
+
declare function useEllipseTool(): Tool<null>;
|
|
4465
2565
|
|
|
4466
2566
|
interface UseImageToolOptions {
|
|
4467
2567
|
/** Image source for inserted nodes — a URL, a `blob:` URL, or a
|
|
@@ -4488,7 +2588,8 @@ interface UseEyedropperToolOptions {
|
|
|
4488
2588
|
* on a real hit; empty-click is a no-op in v1. */
|
|
4489
2589
|
onPick: (color: string | null) => void;
|
|
4490
2590
|
/** Map a node id to a color string, or `null` if the node has no color
|
|
4491
|
-
* to sample. Called on click with
|
|
2591
|
+
* to sample. Called on click with the id of the topmost node under the
|
|
2592
|
+
* click point. */
|
|
4492
2593
|
colorOf: (id: string) => string | null;
|
|
4493
2594
|
/** Override the default `{ key: 'I' }` keybinding. Pass `null` to omit
|
|
4494
2595
|
* the keybinding entirely (palette-only or hotkey-only wiring). */
|
|
@@ -4504,6 +2605,10 @@ interface UseEyedropperToolOptions {
|
|
|
4504
2605
|
*
|
|
4505
2606
|
* Pure-read tool — does NOT mutate the scene. v1 is click-only; drag is
|
|
4506
2607
|
* unbound (a future drag-to-sample option is additive).
|
|
2608
|
+
*
|
|
2609
|
+
* The node under the click comes from the `nodeAtPoint` dep, so an
|
|
2610
|
+
* eyedropper in a consumer that never registered that dep samples nothing.
|
|
2611
|
+
* `<SceneCanvas>` sources it from the same picker the rest of the kit hits.
|
|
4507
2612
|
*/
|
|
4508
2613
|
declare function useEyedropperTool(opts: UseEyedropperToolOptions): Tool<null>;
|
|
4509
2614
|
|
|
@@ -4511,39 +2616,19 @@ interface LinePoint {
|
|
|
4511
2616
|
x: number;
|
|
4512
2617
|
y: number;
|
|
4513
2618
|
}
|
|
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
2619
|
/**
|
|
4539
|
-
* Click-down → drag → release
|
|
4540
|
-
* `
|
|
2620
|
+
* Click-down → drag → release line tool. The `drag` binding routes to
|
|
2621
|
+
* `insertAction`, which owns the live preview and commits through the
|
|
2622
|
+
* `insert` dep with `{ kind: 'line', a, b }` endpoints (not the AABB
|
|
2623
|
+
* diagonal), so the drag direction is preserved.
|
|
2624
|
+
*
|
|
2625
|
+
* Modifiers are applied by `insertAction`:
|
|
4541
2626
|
* - shift: constrain to 15° increments
|
|
4542
|
-
* - alt: mirror end around start (drag is treated as
|
|
2627
|
+
* - alt: mirror the end around the start (the drag is treated as a
|
|
2628
|
+
* half-line) — the line-specific reading of the action's
|
|
2629
|
+
* corner ⇄ center origin toggle
|
|
4543
2630
|
*/
|
|
4544
|
-
declare function useLineTool<
|
|
4545
|
-
id: string;
|
|
4546
|
-
}>(options: UseLineToolOptions<TNode>): Tool<LineScratch | null>;
|
|
2631
|
+
declare function useLineTool(): Tool<null>;
|
|
4547
2632
|
|
|
4548
2633
|
/** Option surface for the `lasso-select` action.
|
|
4549
2634
|
*
|
|
@@ -4652,54 +2737,31 @@ declare function useStarTool<TNode extends {
|
|
|
4652
2737
|
* `pressure: 0.5` while a button is held, `0` otherwise (per the Pointer
|
|
4653
2738
|
* Events spec), so a consumer that wants stylus-only modulation should
|
|
4654
2739
|
* gate on `pointerType` from the originating event — the kit exposes
|
|
4655
|
-
* `usePointerStylus()` for that.
|
|
2740
|
+
* `usePointerStylus()` for that.
|
|
2741
|
+
*
|
|
2742
|
+
* The dispatcher accumulates these on the drag trail and hands the whole
|
|
2743
|
+
* array to `insertAction`, which forwards it as the `pencil` insert
|
|
2744
|
+
* extras' `samples`. A consumer that wants pressure-driven
|
|
2745
|
+
* `Stroke.vertexWidths` reads it off the samples in its own `insert` dep
|
|
2746
|
+
* (`useDepSource('insert', …)`) — see `apps/site/demos/VertexWidthsDemo.tsx`.
|
|
2747
|
+
*/
|
|
4656
2748
|
interface PencilPoint {
|
|
4657
2749
|
x: number;
|
|
4658
2750
|
y: number;
|
|
4659
|
-
/** 0..1.
|
|
4660
|
-
* treat samples as `{x,y}` keep working. */
|
|
2751
|
+
/** 0..1. Absent when the originating event carried no pressure. */
|
|
4661
2752
|
pressure?: number;
|
|
4662
2753
|
/** Degrees, ±90. Zero for mouse/touch. */
|
|
4663
2754
|
tiltX?: number;
|
|
4664
2755
|
/** Degrees, ±90. Zero for mouse/touch. */
|
|
4665
2756
|
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
2757
|
}
|
|
4693
2758
|
/**
|
|
4694
|
-
* Freehand pencil tool.
|
|
4695
|
-
*
|
|
4696
|
-
*
|
|
4697
|
-
* `
|
|
4698
|
-
* its path.
|
|
2759
|
+
* Freehand pencil tool. The `drag` binding routes to `insertAction`,
|
|
2760
|
+
* which accumulates the pointer trail and commits it as
|
|
2761
|
+
* `{ kind: 'pencil', samples }`. The kit's default `insert` dep runs
|
|
2762
|
+
* `schneiderFit` over the samples to produce a cubic-Bezier path.
|
|
4699
2763
|
*/
|
|
4700
|
-
declare function usePencilTool<
|
|
4701
|
-
id: string;
|
|
4702
|
-
}>(options: UsePencilToolOptions<TNode>): Tool<PencilScratch | null>;
|
|
2764
|
+
declare function usePencilTool(): Tool<null>;
|
|
4703
2765
|
|
|
4704
2766
|
/** Standard slot names — render in this canonical order.
|
|
4705
2767
|
* `cellHighlight` is internal: emitted from the `grid` slot's nested
|
|
@@ -4743,9 +2805,24 @@ interface CanvasExtensionApi {
|
|
|
4743
2805
|
readonly element: HTMLCanvasElement | null;
|
|
4744
2806
|
requestRedraw(): void;
|
|
4745
2807
|
/** 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`). */
|
|
2808
|
+
* draw stack and, if it implements `hitTest`, in {@link hitTestExtras}. */
|
|
4748
2809
|
registerLayer(layer: RenderLayer<unknown>): () => void;
|
|
2810
|
+
/**
|
|
2811
|
+
* Hit-test the registered layers (topmost-first, last-registered wins) at a
|
|
2812
|
+
* world-space point. Returns the id of the layer that claimed the point and
|
|
2813
|
+
* whatever its `hitTest` resolved, or `null` when none did.
|
|
2814
|
+
*
|
|
2815
|
+
* `<SceneCanvas>` folds this into the `affordanceAt` thunk it hands the
|
|
2816
|
+
* gesture dispatcher — ahead of the kit's own selection chrome, since
|
|
2817
|
+
* registered layers draw on top — so a hit surfaces to actions as an
|
|
2818
|
+
* `AffordanceHit` with kind `layer:<id>` and the binding's `initialScratch`
|
|
2819
|
+
* as its `payload`. A layer's owner binds a `kindOf` predicate on that kind
|
|
2820
|
+
* to claim the gesture; see `@weasel-js/hud` for the worked example.
|
|
2821
|
+
*/
|
|
2822
|
+
hitTestExtras(worldX: number, worldY: number): {
|
|
2823
|
+
layerId: string;
|
|
2824
|
+
binding: AffordanceBinding;
|
|
2825
|
+
} | null;
|
|
4749
2826
|
/** Feed external content into the ingestion pipeline imperatively — the
|
|
4750
2827
|
* same content-handler registry that OS drop and clipboard paste hit.
|
|
4751
2828
|
* `input` may be raw `File[]` (e.g. from `openFilePicker`) or
|
|
@@ -4775,6 +2852,52 @@ interface SceneCanvasApi extends CanvasExtensionApi {
|
|
|
4775
2852
|
}): void;
|
|
4776
2853
|
}
|
|
4777
2854
|
|
|
2855
|
+
/**
|
|
2856
|
+
* Union math behind `CanvasHelpers.getGestureBounds()`.
|
|
2857
|
+
*
|
|
2858
|
+
* `getGestureBounds` answers "where is the thing the user is dragging right
|
|
2859
|
+
* now, in total?" — a question every other lookup on `CanvasHelpers` can't
|
|
2860
|
+
* answer because they're keyed by node id, and a drag-to-insert has no node
|
|
2861
|
+
* yet. Canvas collects the parts (preview bounds per in-flight id, plus any
|
|
2862
|
+
* id-less nascent-insert AABBs) and folds them here.
|
|
2863
|
+
*
|
|
2864
|
+
* Kept free of tool / dispatcher types so it stays a pure geometry fold that
|
|
2865
|
+
* both `<Canvas>` and its tests can call without a render.
|
|
2866
|
+
*/
|
|
2867
|
+
|
|
2868
|
+
/**
|
|
2869
|
+
* Everything `<Canvas>` needs to know about in-flight gestures that it can't
|
|
2870
|
+
* see for itself.
|
|
2871
|
+
*
|
|
2872
|
+
* `<Canvas>` is deliberately dispatcher-agnostic — it owns the union math and
|
|
2873
|
+
* the `CanvasHelpers` contract, but the live gesture state lives in the
|
|
2874
|
+
* gesture dispatcher that `<SceneCanvas>` creates. This is the one seam
|
|
2875
|
+
* between them (`createGestureSource` in `SceneCanvas/dispatcherGestureBounds.ts`
|
|
2876
|
+
* builds it); bare `<Canvas>` consumers leave it unwired and get the
|
|
2877
|
+
* "no gesture in flight" answers.
|
|
2878
|
+
*
|
|
2879
|
+
* @public
|
|
2880
|
+
*/
|
|
2881
|
+
interface GestureSource {
|
|
2882
|
+
/**
|
|
2883
|
+
* Ids the in-flight gesture is previewing, so the canvas can resolve each
|
|
2884
|
+
* one's preview bounds.
|
|
2885
|
+
*
|
|
2886
|
+
* Wider than `previewIdsExtra`, which honors `previewHidesSource: false`
|
|
2887
|
+
* because its job is deciding whose committed paint to suppress: a clone
|
|
2888
|
+
* ghost hides nothing yet still proposes content at the drag target.
|
|
2889
|
+
*/
|
|
2890
|
+
ids(): Iterable<string> | null;
|
|
2891
|
+
/** World-space AABBs of in-flight gestures that have no scene id at all —
|
|
2892
|
+
* a drag-to-insert, pre-commit. */
|
|
2893
|
+
bounds(): Iterable<Bounds> | null;
|
|
2894
|
+
/** Fires whenever the above can have changed. See
|
|
2895
|
+
* `CanvasHelpers.subscribeGestures` for the published guarantee. */
|
|
2896
|
+
subscribe(fn: () => void): () => void;
|
|
2897
|
+
/** Monotonic counter bumped on the same events `subscribe` fires on. */
|
|
2898
|
+
getVersion(): number;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
4778
2901
|
/**
|
|
4779
2902
|
* Selection overlay primitives — render layers for drawing selection
|
|
4780
2903
|
* outlines and corner resize handles in world space.
|
|
@@ -5146,16 +3269,6 @@ interface CanvasProps<TNode extends {
|
|
|
5146
3269
|
* force-graph renderers or read-only viewers).
|
|
5147
3270
|
*/
|
|
5148
3271
|
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
3272
|
/**
|
|
5160
3273
|
* Pose↔bounds projection for non-rect `TPose` types. When supplied, drives
|
|
5161
3274
|
* the default `boundsOf` fallback and the selection-overlay bounds source so
|
|
@@ -5291,6 +3404,14 @@ interface CanvasProps<TNode extends {
|
|
|
5291
3404
|
* through geometry.getBounds(pose).
|
|
5292
3405
|
*/
|
|
5293
3406
|
previewBoundsExtra?: (id: string) => Bounds | null;
|
|
3407
|
+
/**
|
|
3408
|
+
* In-flight gesture state `<Canvas>` can't see for itself. Backs the
|
|
3409
|
+
* `getGestureBounds` / `subscribeGestures` / `getGestureVersion` trio on
|
|
3410
|
+
* `helpersRef`; wired by `<SceneCanvas>` from the gesture dispatcher
|
|
3411
|
+
* (`createGestureSource`). Leaving it unwired is fine — those three then
|
|
3412
|
+
* report "no gesture in flight" and never fire.
|
|
3413
|
+
*/
|
|
3414
|
+
gestureSource?: GestureSource;
|
|
5294
3415
|
/**
|
|
5295
3416
|
* Pinch-zoom DOM listener attachment for the canvas surface. When supplied,
|
|
5296
3417
|
* `<Canvas>` calls `usePinchZoomTool` with `canvasRef` so two-finger pinch
|
|
@@ -5346,19 +3467,6 @@ interface CanvasProps<TNode extends {
|
|
|
5346
3467
|
* HUD shows the same best-candidate SceneCanvas would pick.
|
|
5347
3468
|
*/
|
|
5348
3469
|
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
3470
|
/**
|
|
5363
3471
|
* Optional mode-owned decoration layer. When supplied, Canvas inserts it
|
|
5364
3472
|
* between the scene-render slot and the tool-overlay slot so decoration
|
|
@@ -5384,16 +3492,73 @@ interface CanvasProps<TNode extends {
|
|
|
5384
3492
|
* predicate that closes over per-frame `ChromeCtx`. Omit to leave
|
|
5385
3493
|
* every chrome element visible (pre-chrome-caps behavior).
|
|
5386
3494
|
*/
|
|
5387
|
-
getIsVisible?: () => (id: string) => boolean;
|
|
5388
|
-
}
|
|
5389
|
-
/** Live overlay-aware lookups exposed to custom layers via `helpersRef`. */
|
|
5390
|
-
interface CanvasHelpers<TPose> {
|
|
5391
|
-
/** Pose currently displayed for `id` — drag/resize/rotate overlay if active,
|
|
5392
|
-
* otherwise the committed pose from the adapter. Returns `null` if the id
|
|
5393
|
-
* isn't known. */
|
|
5394
|
-
getEffectivePose(id: string): TPose | null;
|
|
5395
|
-
/** Overlay-aware bounds for `id`. */
|
|
5396
|
-
getEffectiveBounds(id: string): Bounds | null;
|
|
3495
|
+
getIsVisible?: () => (id: string) => boolean;
|
|
3496
|
+
}
|
|
3497
|
+
/** Live overlay-aware lookups exposed to custom layers via `helpersRef`. */
|
|
3498
|
+
interface CanvasHelpers<TPose> {
|
|
3499
|
+
/** Pose currently displayed for `id` — drag/resize/rotate overlay if active,
|
|
3500
|
+
* otherwise the committed pose from the adapter. Returns `null` if the id
|
|
3501
|
+
* isn't known. */
|
|
3502
|
+
getEffectivePose(id: string): TPose | null;
|
|
3503
|
+
/** Overlay-aware bounds for `id`. */
|
|
3504
|
+
getEffectiveBounds(id: string): Bounds | null;
|
|
3505
|
+
/**
|
|
3506
|
+
* World-space AABB of everything the in-flight gesture proposes — the
|
|
3507
|
+
* displaced poses of nodes being moved / resized / rotated / cloned, plus
|
|
3508
|
+
* any nascent insert that has no scene node yet. `null` when no gesture is
|
|
3509
|
+
* in flight.
|
|
3510
|
+
*
|
|
3511
|
+
* This reports the *gesture*, not the document: committed content the
|
|
3512
|
+
* gesture isn't touching is excluded, so a consumer that wants the union
|
|
3513
|
+
* with the rest of the scene still walks its own ids through
|
|
3514
|
+
* `getEffectiveBounds`. It exists because every other lookup here is keyed
|
|
3515
|
+
* by node id, which can't answer "where is the shape the user is drawing
|
|
3516
|
+
* right now" — a drag-to-insert has no id until pointer-up.
|
|
3517
|
+
*
|
|
3518
|
+
* Select-only gestures are deliberately excluded: a marquee or lasso has
|
|
3519
|
+
* geometry but proposes no content, and a consumer sizing itself to the
|
|
3520
|
+
* gesture must not grow because the user swept a selection rectangle.
|
|
3521
|
+
*
|
|
3522
|
+
* The result is a plain AABB — never rotated. Rotated parts are folded in
|
|
3523
|
+
* by their rotated extent (a union of several oriented boxes has no single
|
|
3524
|
+
* orientation to report).
|
|
3525
|
+
*/
|
|
3526
|
+
getGestureBounds(): Bounds | null;
|
|
3527
|
+
/**
|
|
3528
|
+
* Subscribe to the gesture layer's change signal — the other half of the
|
|
3529
|
+
* `useSyncExternalStore` contract for everything on this object that moves
|
|
3530
|
+
* during a drag (`getEffectivePose`, `getEffectiveBounds`,
|
|
3531
|
+
* `getGestureBounds`). Returns an unsubscribe.
|
|
3532
|
+
*
|
|
3533
|
+
* Fires once per dispatcher pump: gesture start, every pointermove that
|
|
3534
|
+
* reaches an in-flight handle, end, and cancel — plus UI-driven ongoing
|
|
3535
|
+
* actions (a slider bound to an ongoing action pumps the same way). It
|
|
3536
|
+
* fires on the pump, not on a diff: a pump that changed nothing observable
|
|
3537
|
+
* still notifies, so don't hang expensive work directly off the callback.
|
|
3538
|
+
*
|
|
3539
|
+
* It does **not** cover committed scene edits (subscribe to the scene for
|
|
3540
|
+
* those) or previews a consumer's own tool publishes from React state
|
|
3541
|
+
* (that tool re-renders on its own).
|
|
3542
|
+
*
|
|
3543
|
+
* Without a gesture source wired — a bare `<Canvas>` — this is a no-op
|
|
3544
|
+
* subscription that never fires.
|
|
3545
|
+
*/
|
|
3546
|
+
subscribeGestures(fn: () => void): () => void;
|
|
3547
|
+
/**
|
|
3548
|
+
* Monotonic counter bumped on exactly the events `subscribeGestures` fires
|
|
3549
|
+
* on. Pair the two for `useSyncExternalStore`:
|
|
3550
|
+
*
|
|
3551
|
+
* ```ts
|
|
3552
|
+
* const gestureVersion = useSyncExternalStore(
|
|
3553
|
+
* useCallback((cb) => helpersRef.current?.subscribeGestures(cb) ?? (() => {}), []),
|
|
3554
|
+
* () => helpersRef.current?.getGestureVersion() ?? 0,
|
|
3555
|
+
* );
|
|
3556
|
+
* ```
|
|
3557
|
+
*
|
|
3558
|
+
* Starts at 0 and only increases. `0` is also what a bare `<Canvas>` with
|
|
3559
|
+
* no gesture source reports, forever.
|
|
3560
|
+
*/
|
|
3561
|
+
getGestureVersion(): number;
|
|
5397
3562
|
/** Returns the live ChromeState built once per render. Affordances and
|
|
5398
3563
|
* custom layers that need overlay-aware selection state (selection ids,
|
|
5399
3564
|
* bounds, multi-union AABB, modifier flags) read from this. */
|
|
@@ -5863,27 +4028,6 @@ interface UseRotateOptions<TPose> {
|
|
|
5863
4028
|
pivot?: 'each' | 'union';
|
|
5864
4029
|
}
|
|
5865
4030
|
|
|
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
4031
|
/**
|
|
5888
4032
|
* Built-in shape tool ids handled by `useBuiltinShapeTools`. Each maps to a
|
|
5889
4033
|
* kit tool hook + a default `create` that produces a leaf node compatible
|
|
@@ -5907,10 +4051,13 @@ interface BuiltinToolOptions {
|
|
|
5907
4051
|
mode?: LassoHitMode;
|
|
5908
4052
|
};
|
|
5909
4053
|
/** Snap world-space points to the active grid (or any other snap target).
|
|
5910
|
-
*
|
|
5911
|
-
*
|
|
5912
|
-
*
|
|
5913
|
-
*
|
|
4054
|
+
*
|
|
4055
|
+
* Registered as the `snap` dep, which `insertAction` applies to the
|
|
4056
|
+
* drag start and current point — so the live preview and the committed
|
|
4057
|
+
* geometry agree — and which the pen tool reads for its own anchor
|
|
4058
|
+
* placement. Covers every drag-to-insert tool (rect / ellipse / line /
|
|
4059
|
+
* polygon / star / text); freehand pencil samples are deliberately left
|
|
4060
|
+
* unsnapped. */
|
|
5914
4061
|
snapPoint?: (p: {
|
|
5915
4062
|
x: number;
|
|
5916
4063
|
y: number;
|
|
@@ -6081,7 +4228,13 @@ interface NodeShapeEntry<TData = unknown, TPose = unknown> {
|
|
|
6081
4228
|
matches(node: Node$1<TData, string, TPose>): boolean;
|
|
6082
4229
|
/** Emits the draw commands for the node's primary visual. `ctx` is an
|
|
6083
4230
|
* optional per-call paint context (see `NodePaintCtx`); painters that
|
|
6084
|
-
* don't need it can keep a two-argument signature.
|
|
4231
|
+
* don't need it can keep a two-argument signature.
|
|
4232
|
+
*
|
|
4233
|
+
* **The returned array belongs to the painter.** Callers must treat it as
|
|
4234
|
+
* immutable and copy before appending — a painter is free to memoize its
|
|
4235
|
+
* command list (`kit:shape` and `kit:path` do), and an in-place `push`
|
|
4236
|
+
* would grow that list on every frame. `defaultDrawOne` copies before
|
|
4237
|
+
* adding its label overlay for exactly this reason. */
|
|
6085
4238
|
paint(node: Node$1<TData, string, TPose>, pose: TPose, ctx?: NodePaintCtx): DrawCommand[];
|
|
6086
4239
|
/** Optional: derive the node's silhouette path from its pose.
|
|
6087
4240
|
* Used by clipping (when the container has no explicit
|
|
@@ -6089,6 +4242,26 @@ interface NodeShapeEntry<TData = unknown, TPose = unknown> {
|
|
|
6089
4242
|
* and by SVG export. Painters whose visual has no meaningful closed
|
|
6090
4243
|
* silhouette (e.g. text) leave this undefined. */
|
|
6091
4244
|
silhouette?(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
|
|
4245
|
+
/** Optional: how the silhouette is inked — whether the interior is filled,
|
|
4246
|
+
* and how wide the outline is. Read by picking, so that an unfilled shape
|
|
4247
|
+
* is grabbable by its outline rather than by its empty middle.
|
|
4248
|
+
*
|
|
4249
|
+
* This is declared separately from `paint` rather than read back off the
|
|
4250
|
+
* emitted draw commands because picking runs on every pointer move, and
|
|
4251
|
+
* `paint` is allowed to be expensive (`kit:text` lays out glyphs). Keep it
|
|
4252
|
+
* to cheap field reads.
|
|
4253
|
+
*
|
|
4254
|
+
* Painters that leave it undefined are treated as `{ filled: true,
|
|
4255
|
+
* strokeWidth: 0 }` — the pre-`ink` behavior, where the whole silhouette
|
|
4256
|
+
* interior is grabbable and the outline adds nothing. */
|
|
4257
|
+
ink?(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
|
|
4258
|
+
}
|
|
4259
|
+
/** How a painter inks its silhouette. See {@link NodeShapeEntry.ink}. */
|
|
4260
|
+
interface NodeInk {
|
|
4261
|
+
/** True when the silhouette's interior is painted, and so grabbable. */
|
|
4262
|
+
filled: boolean;
|
|
4263
|
+
/** Stroke width in world units. `0` for no stroke. */
|
|
4264
|
+
strokeWidth: number;
|
|
6092
4265
|
}
|
|
6093
4266
|
interface RegisterNodeShapeOptions {
|
|
6094
4267
|
/** `'high'` puts the painter ahead of all normally-registered ones (so
|
|
@@ -6113,6 +4286,47 @@ declare function findNodeShape<TData, TPose>(node: Node$1<TData, string, TPose>)
|
|
|
6113
4286
|
* draws. (`paint()` is unaffected — it applies rotation via the render wrap,
|
|
6114
4287
|
* not the silhouette, so there is no double-rotation.) */
|
|
6115
4288
|
declare function findShapeSilhouette<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): Path | null;
|
|
4289
|
+
/** Find the painter for `node` and ask how it inks its silhouette. Returns
|
|
4290
|
+
* the painter's declared {@link NodeInk}, or `null` when no painter matches
|
|
4291
|
+
* or it declares none — callers substitute {@link DEFAULT_INK}. */
|
|
4292
|
+
declare function findShapeInk<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose): NodeInk | null;
|
|
4293
|
+
/** Options for {@link shapeCoversPoint}. */
|
|
4294
|
+
interface ShapeCoversPointOptions {
|
|
4295
|
+
/** Extra grab distance around the outline, in **world** units. Callers
|
|
4296
|
+
* derive it from a screen-pixel slop and the view scale, the same way
|
|
4297
|
+
* affordance hit radii work.
|
|
4298
|
+
*
|
|
4299
|
+
* Without slop a hairline is a mathematically zero-width target: the
|
|
4300
|
+
* stroke of a 1px outline is half a world unit wide at scale 1, which no
|
|
4301
|
+
* one can hit. Defaults to `0` so a caller that hasn't thought about the
|
|
4302
|
+
* view still gets exact geometry rather than a wrong guess. */
|
|
4303
|
+
tolerance?: number;
|
|
4304
|
+
}
|
|
4305
|
+
/**
|
|
4306
|
+
* Does the shape `node` actually paints cover the world point?
|
|
4307
|
+
*
|
|
4308
|
+
* The pose rect says a node covers its whole bounding box. That is wrong for
|
|
4309
|
+
* everything that is not a rectangle: the concave notch of a star, the corner
|
|
4310
|
+
* outside an ellipse, the blank right half of a text box. This asks the
|
|
4311
|
+
* painter's silhouette instead, which is the same boundary used for clipping
|
|
4312
|
+
* and SVG export, so "what you can click" and "what is drawn" answer together.
|
|
4313
|
+
*
|
|
4314
|
+
* "What is drawn" includes the *ink*, not just the boundary. A shape whose
|
|
4315
|
+
* interior isn't filled — an outlined rect, a pencil stroke, a bare line — is
|
|
4316
|
+
* grabbable along its outline and not through its empty middle, which is the
|
|
4317
|
+
* opposite of what a fill test alone answers. The outline's grab width is the
|
|
4318
|
+
* stroke's half-width plus `tolerance`.
|
|
4319
|
+
*
|
|
4320
|
+
* A painter with no `silhouette`, or one that returns `null` for this node
|
|
4321
|
+
* (`kit:text` does, for a node with no non-blank lines), reports `true` —
|
|
4322
|
+
* "no opinion", leaving the caller's own AABB test as the answer. Callers
|
|
4323
|
+
* should keep that AABB test as a cheap pre-filter; this is the refinement,
|
|
4324
|
+
* not a replacement.
|
|
4325
|
+
*
|
|
4326
|
+
* Rotation is already baked by `findShapeSilhouette`, so the point is in
|
|
4327
|
+
* plain world coordinates.
|
|
4328
|
+
*/
|
|
4329
|
+
declare function shapeCoversPoint<TData, TPose>(node: Node$1<TData, string, TPose>, pose: TPose, x: number, y: number, opts?: ShapeCoversPointOptions): boolean;
|
|
6116
4330
|
/** Snapshot of the current painters in evaluation order — `'high'` tier
|
|
6117
4331
|
* first, then `'normal'`. Useful for debugging which painter handles a
|
|
6118
4332
|
* given node. */
|
|
@@ -6240,6 +4454,35 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
|
|
|
6240
4454
|
* pre-collapsing to one id. Matches `Canvas`'s `pickEvery` shape. */
|
|
6241
4455
|
pickEvery?: (worldX: number, worldY: number) => string | string[] | null;
|
|
6242
4456
|
boundsOf?: (id: string) => Bounds | null;
|
|
4457
|
+
/**
|
|
4458
|
+
* What "the pointer is on this node" means for the default body-pick.
|
|
4459
|
+
*
|
|
4460
|
+
* - `'pose'` — the node's pose rect, rotation honored. What every
|
|
4461
|
+
* consumer got before `'shape'` existed, and now the opt-out.
|
|
4462
|
+
* - `'shape'` (default) — the pose rect as a pre-filter, then the ink the painter
|
|
4463
|
+
* actually lays down: its silhouette (`findShapeSilhouette`) filled
|
|
4464
|
+
* or not per the painter's `ink`, plus its outline widened by the
|
|
4465
|
+
* stroke half-width and `pickTolerancePx`. A click in the concave
|
|
4466
|
+
* notch of a star, in the corner outside an ellipse, or in the blank
|
|
4467
|
+
* half of a text box falls through to whatever is beneath; a click on
|
|
4468
|
+
* the thin outline of an unfilled shape hits it.
|
|
4469
|
+
*
|
|
4470
|
+
* Painters with no silhouette are unaffected — they keep the pose-rect
|
|
4471
|
+
* answer either way, so this can never make a node unreachable.
|
|
4472
|
+
*
|
|
4473
|
+
* Ignored when `pickEvery` is supplied: that override owns the test.
|
|
4474
|
+
*/
|
|
4475
|
+
picking?: 'pose' | 'shape';
|
|
4476
|
+
/**
|
|
4477
|
+
* Grab slop around a shape's outline, in **screen** pixels. Default 4.
|
|
4478
|
+
*
|
|
4479
|
+
* Screen pixels rather than world units so the target keeps its
|
|
4480
|
+
* apparent size at any zoom. It widens the outline test under
|
|
4481
|
+
* `picking: 'shape'` (a 1px hairline is otherwise a half-world-unit
|
|
4482
|
+
* target, which is unhittable), and it grows the pose-rect pre-filter
|
|
4483
|
+
* so those outline hits survive it. Set `0` for exact geometry.
|
|
4484
|
+
*/
|
|
4485
|
+
pickTolerancePx?: number;
|
|
6243
4486
|
};
|
|
6244
4487
|
selectTool?: {
|
|
6245
4488
|
move?: UseMoveOptions<TPose>;
|
|
@@ -6252,10 +4495,6 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
|
|
|
6252
4495
|
rotate?: UseRotateOptions<TPose> | false;
|
|
6253
4496
|
snap?: SnapStrategy<TPose>;
|
|
6254
4497
|
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
4498
|
/** Override the body-pick used on click/pointerdown. Alt-aware: receives
|
|
6260
4499
|
* the live alt state + current selection so consumers can implement
|
|
6261
4500
|
* alt-cycling through an overlapping stack. Default: top-most hit
|
|
@@ -6397,15 +4636,6 @@ type SceneCanvasProps<TData, TLayer extends string, TPose> = Omit<CanvasProps<No
|
|
|
6397
4636
|
* the default select. If you supply your own `tools` prop, this is
|
|
6398
4637
|
* ignored — wire `ambient` through your own `useTools` call instead. */
|
|
6399
4638
|
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
4639
|
/** Viewport feature wiring.
|
|
6410
4640
|
*
|
|
6411
4641
|
* - `inertia`, `pinchZoom`, `animatedZoom` are opt-in: pass `true`
|
|
@@ -6737,10 +4967,11 @@ declare const inferredNodeRouting: readonly NodeRoutingEntry[];
|
|
|
6737
4967
|
* registry: a declarative schema of the kind's editable properties.
|
|
6738
4968
|
*
|
|
6739
4969
|
* 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
|
|
4970
|
+
* `data.fill`, `data.style.fontSize`) — a path of any depth rooted at
|
|
4971
|
+
* `pose` or `data` — so schema consumers (weasel-ui `SelectionPanel`)
|
|
4972
|
+
* can read/aggregate/write generically with no per-kind code. Group
|
|
4973
|
+
* keys are organizational only; they do not contribute to the node
|
|
4974
|
+
* path.
|
|
6744
4975
|
*
|
|
6745
4976
|
* Kind names share the routing trait's vocabulary — an entry registered
|
|
6746
4977
|
* as `'rect'` describes nodes `NodeRouting.classify` maps to `'rect'`.
|
|
@@ -7353,6 +5584,70 @@ declare function composeAffordanceLayer(id: string, label: string, affordances:
|
|
|
7353
5584
|
}, isVisible?: (id: string) => boolean): AffordanceBinding | null;
|
|
7354
5585
|
};
|
|
7355
5586
|
|
|
5587
|
+
/**
|
|
5588
|
+
* The one affordance hit-test walk.
|
|
5589
|
+
*
|
|
5590
|
+
* The kit used to hit-test selection chrome two ways: this region walk
|
|
5591
|
+
* (reached through `composeAffordanceLayer`'s `RenderLayer.hitTest`) and a
|
|
5592
|
+
* hand-written classifier in `canvas/affordanceAt.ts` that re-derived corner
|
|
5593
|
+
* positions, the rotate ring, and anchor points from `ChromeState` itself.
|
|
5594
|
+
* Both read the same state and answered the same question, so the geometry
|
|
5595
|
+
* lived twice and could disagree — and only the hand-written side ran for the
|
|
5596
|
+
* kit's own chrome, which is why `AffordanceRegion.cursor` was declared, set,
|
|
5597
|
+
* and never consumed.
|
|
5598
|
+
*
|
|
5599
|
+
* Now there is one walk. `composeAffordanceLayer.hitTest` delegates here, and
|
|
5600
|
+
* so does `buildAffordanceAt`, so an affordance's `regions()` is the single
|
|
5601
|
+
* source of truth for where its chrome is and what a press on it means.
|
|
5602
|
+
*/
|
|
5603
|
+
|
|
5604
|
+
/** What the walk found: the region, plus the ids needed to describe it. */
|
|
5605
|
+
interface AffordanceRegionHit {
|
|
5606
|
+
/** `Affordance.id` — also the chrome-caps visibility id. */
|
|
5607
|
+
affordanceId: string;
|
|
5608
|
+
/** `AffordanceRegion.id`. */
|
|
5609
|
+
regionId: string;
|
|
5610
|
+
/** The region itself, so callers can read `hitKind` / `cursor`. */
|
|
5611
|
+
region: AffordanceRegion;
|
|
5612
|
+
/** Result of the region's `bind()`, called exactly once. */
|
|
5613
|
+
binding: AffordanceBinding;
|
|
5614
|
+
}
|
|
5615
|
+
/**
|
|
5616
|
+
* Walk affordances for a world-space hit, topmost first.
|
|
5617
|
+
*
|
|
5618
|
+
* **Across affordances**: order mirrors paint. Affordances are drawn first →
|
|
5619
|
+
* last (bottom → top), so the walk runs last → first and stops at the first
|
|
5620
|
+
* one that claims the point. Layering wins outright — a nearer region on a
|
|
5621
|
+
* lower affordance does not beat a farther one on top of it.
|
|
5622
|
+
*
|
|
5623
|
+
* **Within an affordance**: the *nearest* hit region wins, ties going to the
|
|
5624
|
+
* later-declared one. Both hit-testers this replaced took the first region
|
|
5625
|
+
* they found instead, in opposite orders, which is only invisible while
|
|
5626
|
+
* regions don't overlap. They do: the four corner handles of a selection
|
|
5627
|
+
* smaller than the hit radius all contain the same click, and "first in the
|
|
5628
|
+
* table" silently answered top-left (or bottom-right) no matter which handle
|
|
5629
|
+
* the pointer was actually on.
|
|
5630
|
+
*
|
|
5631
|
+
* `isVisible` (when supplied) gates by the same chrome id used at paint time,
|
|
5632
|
+
* so chrome that isn't drawn can't be hit either.
|
|
5633
|
+
*/
|
|
5634
|
+
declare function hitAffordanceRegions(affordances: readonly Affordance[], wx: number, wy: number, state: ChromeState, view: View, isVisible?: (id: string) => boolean): AffordanceRegionHit | null;
|
|
5635
|
+
/**
|
|
5636
|
+
* An annulus region's effective outer semi-axes, with `minBandPx` applied.
|
|
5637
|
+
*
|
|
5638
|
+
* The affordance declares the natural ellipse (typically the smallest one
|
|
5639
|
+
* containing the inner rect) plus a screen-space floor on band thickness;
|
|
5640
|
+
* this is where the floor becomes world units, because this is where the view
|
|
5641
|
+
* is known. Paint calls it too, so the visible ring and the hoverable ring
|
|
5642
|
+
* are the same ring.
|
|
5643
|
+
*/
|
|
5644
|
+
declare function annulusSemiAxes(shape: Extract<AffordanceRegion['shape'], {
|
|
5645
|
+
kind: 'annulus';
|
|
5646
|
+
}>, view: View): {
|
|
5647
|
+
rx: number;
|
|
5648
|
+
ry: number;
|
|
5649
|
+
};
|
|
5650
|
+
|
|
7356
5651
|
interface CornerResizeAffordanceOptions {
|
|
7357
5652
|
/** Hit radius (screen-px) for the corner handles. Default 8. */
|
|
7358
5653
|
handleHitRadius?: number;
|
|
@@ -7361,12 +5656,19 @@ interface CornerResizeAffordanceOptions {
|
|
|
7361
5656
|
fill?: string;
|
|
7362
5657
|
stroke?: string;
|
|
7363
5658
|
}
|
|
7364
|
-
interface CornerResizeScratch {
|
|
5659
|
+
interface CornerResizeScratch extends CommonAffordanceScratch {
|
|
7365
5660
|
/** Resize anchor identifying the OPPOSITE corner (the one that stays
|
|
7366
5661
|
* fixed). Matches the kit's existing ResizeAnchor convention. */
|
|
7367
5662
|
anchor: ResizeAnchor;
|
|
7368
5663
|
/** Id of the resize target. In multi-mode this is `MULTI_RESIZE_TARGET_ID`. */
|
|
7369
5664
|
targetId: string;
|
|
5665
|
+
/** The fixed corner in **world** coords, with the target's rotation already
|
|
5666
|
+
* applied. `resizeAction` scales from this point, and it has no access to
|
|
5667
|
+
* the target transform, so the affordance resolves it here. */
|
|
5668
|
+
fixedPoint: {
|
|
5669
|
+
x: number;
|
|
5670
|
+
y: number;
|
|
5671
|
+
};
|
|
7370
5672
|
}
|
|
7371
5673
|
/**
|
|
7372
5674
|
* @experimental
|
|
@@ -7400,10 +5702,15 @@ interface RotationAffordanceOptions {
|
|
|
7400
5702
|
/** Cursor while hovering the ring. Defaults to `'grab'`. */
|
|
7401
5703
|
cursor?: string;
|
|
7402
5704
|
}
|
|
7403
|
-
interface RotationScratch {
|
|
5705
|
+
interface RotationScratch extends CommonAffordanceScratch {
|
|
7404
5706
|
/** Id of the rotation target — single selection id, or
|
|
7405
5707
|
* `MULTI_RESIZE_TARGET_ID` for multi-selection. */
|
|
7406
5708
|
targetId: string;
|
|
5709
|
+
/** Rotation pivot in world coords — the AABB (or union AABB) center. */
|
|
5710
|
+
fixedPoint: {
|
|
5711
|
+
x: number;
|
|
5712
|
+
y: number;
|
|
5713
|
+
};
|
|
7407
5714
|
}
|
|
7408
5715
|
/**
|
|
7409
5716
|
* @experimental
|
|
@@ -7424,6 +5731,61 @@ interface RotationScratch {
|
|
|
7424
5731
|
*/
|
|
7425
5732
|
declare function createRotationAffordance(opts?: RotationAffordanceOptions): Affordance;
|
|
7426
5733
|
|
|
5734
|
+
/**
|
|
5735
|
+
* Path anchor + control-handle affordances.
|
|
5736
|
+
*
|
|
5737
|
+
* These were the last piece of selection chrome whose geometry lived only
|
|
5738
|
+
* inside `canvas/affordanceAt.ts`'s hand-written classifier. Expressing them
|
|
5739
|
+
* as regions puts every kit affordance behind one hit-test walk, which is
|
|
5740
|
+
* what lets `AffordanceRegion.cursor` and nearest-region picking apply here
|
|
5741
|
+
* too rather than only to the chrome that happened to be declarative already.
|
|
5742
|
+
*/
|
|
5743
|
+
|
|
5744
|
+
/**
|
|
5745
|
+
* Live anchor-editing state, read fresh on every hit-test.
|
|
5746
|
+
*
|
|
5747
|
+
* Anchors are not derivable from `ChromeState` — they come off the editable
|
|
5748
|
+
* path behind whatever the consumer's `editAnchors` dep exposes — so this
|
|
5749
|
+
* thunk is the seam. It's called on every pointer event, so it must be cheap
|
|
5750
|
+
* (O(1) field reads); enumeration happens inside `regions()`.
|
|
5751
|
+
*/
|
|
5752
|
+
interface AnchorState {
|
|
5753
|
+
/** Id of the path currently in anchor-edit mode, or `null`. Control handles
|
|
5754
|
+
* are only hittable on the path that is being edited. */
|
|
5755
|
+
editingId: string | null;
|
|
5756
|
+
/** Current editable path for a node id. Non-polygon values are ignored. */
|
|
5757
|
+
getPose(id: string): unknown;
|
|
5758
|
+
}
|
|
5759
|
+
interface PathAnchorAffordanceOptions {
|
|
5760
|
+
/** Hit radius (screen-px) for anchor and control points. Default 8. */
|
|
5761
|
+
hitRadius?: number;
|
|
5762
|
+
/** Cursor while hovering an anchor or control. Defaults to `'pointer'`. */
|
|
5763
|
+
cursor?: string;
|
|
5764
|
+
}
|
|
5765
|
+
interface AnchorScratch extends CommonAffordanceScratch {
|
|
5766
|
+
/** Node id of the path the anchor belongs to. */
|
|
5767
|
+
targetId: string;
|
|
5768
|
+
/** Sequential anchor index in path walk order. */
|
|
5769
|
+
anchorIndex: number;
|
|
5770
|
+
/** Which of the anchor's three grabbable points this is. */
|
|
5771
|
+
part: 'anchor' | 'controlIn' | 'controlOut';
|
|
5772
|
+
}
|
|
5773
|
+
/** Chrome-caps id gating both returned affordances. */
|
|
5774
|
+
declare const PATH_ANCHOR_CHROME_ID = "path-edit.anchors";
|
|
5775
|
+
/**
|
|
5776
|
+
* Anchor + control affordances for the selected paths, bottom → top.
|
|
5777
|
+
*
|
|
5778
|
+
* Returns **two** affordances rather than one so that a control handle beats
|
|
5779
|
+
* an anchor regardless of which is nearer — the same preference `hitAnchor`
|
|
5780
|
+
* encodes, and the same one the rendering has (controls draw on top). Within
|
|
5781
|
+
* each of the two, nearest wins. They deliberately share one id: chrome-caps
|
|
5782
|
+
* gates anchor editing as a single piece of chrome, and splitting the id
|
|
5783
|
+
* would invent a visibility rule nothing asked for.
|
|
5784
|
+
*
|
|
5785
|
+
* Order matters — spread this into an affordance list in the returned order.
|
|
5786
|
+
*/
|
|
5787
|
+
declare function createPathAnchorAffordances(getAnchorState: () => AnchorState | null, opts?: PathAnchorAffordanceOptions): Affordance[];
|
|
5788
|
+
|
|
7427
5789
|
/** Pluggable text-painting strategy. The default fills white at `(x, y)`; markdown renderers replace this. */
|
|
7428
5790
|
type TextRenderer = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number) => void;
|
|
7429
5791
|
/** Visual options for `renderLabel`. */
|
|
@@ -7505,6 +5867,17 @@ declare function createMarkdownRenderer(ctx: CanvasRenderingContext2D, text: str
|
|
|
7505
5867
|
* already matches `style` (use `fontString(style)`).
|
|
7506
5868
|
*/
|
|
7507
5869
|
|
|
5870
|
+
/**
|
|
5871
|
+
* Advance width of `text` in world units, tracking included.
|
|
5872
|
+
*
|
|
5873
|
+
* `letter-spacing` is not part of the CSS `font` shorthand, so a context
|
|
5874
|
+
* whose `font` was set from `fontString(style)` measures glyphs only. The
|
|
5875
|
+
* GL path (`layoutRuns`) adds `letterSpacing` after **every** code point
|
|
5876
|
+
* including the last, matching CSS, so this does too — and every 2D-side
|
|
5877
|
+
* width has to go through here or the two paths disagree about where a line
|
|
5878
|
+
* breaks.
|
|
5879
|
+
*/
|
|
5880
|
+
declare function measuredWidth(ctx: CanvasRenderingContext2D, text: string, style: ResolvedTextStyle): number;
|
|
7508
5881
|
/** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */
|
|
7509
5882
|
interface MeasuredText {
|
|
7510
5883
|
lines: string[];
|
|
@@ -7617,6 +5990,70 @@ declare function pointInTextPose(x: number, y: number, pose: TextPose, opts?: Po
|
|
|
7617
5990
|
*/
|
|
7618
5991
|
declare function caretIndexAt(ctx: CanvasRenderingContext2D, x: number, y: number, pose: TextPose): number;
|
|
7619
5992
|
|
|
5993
|
+
/**
|
|
5994
|
+
* Polygon-vs-axis-aligned-rect tests. Pure functions; no React/kit deps.
|
|
5995
|
+
*
|
|
5996
|
+
* `polygon` is a closed polyline given as an ordered vertex array; the
|
|
5997
|
+
* closing edge from `polygon[N-1]` to `polygon[0]` is implicit. Even-odd
|
|
5998
|
+
* fill rule (matches `pointInPath`).
|
|
5999
|
+
*/
|
|
6000
|
+
interface Vec2 {
|
|
6001
|
+
x: number;
|
|
6002
|
+
y: number;
|
|
6003
|
+
}
|
|
6004
|
+
interface Rect {
|
|
6005
|
+
x: number;
|
|
6006
|
+
y: number;
|
|
6007
|
+
width: number;
|
|
6008
|
+
height: number;
|
|
6009
|
+
}
|
|
6010
|
+
|
|
6011
|
+
/**
|
|
6012
|
+
* Where a text node's lines actually sit inside its pose box.
|
|
6013
|
+
*
|
|
6014
|
+
* A text pose is a *wrap box*, not a bounding box: `"Away"` in a 309-unit-wide
|
|
6015
|
+
* box leaves most of the box empty, and anything that treats the pose as the
|
|
6016
|
+
* node's extent — picking, lasso, clipping, SVG export — claims that empty
|
|
6017
|
+
* space. `textLineBoxes` returns the per-line rectangles instead.
|
|
6018
|
+
*
|
|
6019
|
+
* The numbers come from `layoutRuns`, the same walk that positions the glyphs,
|
|
6020
|
+
* through the same `resolveTextStyle` → `resolveRuns` → `layoutRuns` chain as
|
|
6021
|
+
* `textCommand` — so the boxes cannot drift from what is painted. In
|
|
6022
|
+
* particular they honor `align` (a centered line reports its own span, not the
|
|
6023
|
+
* wrap width) and `verticalAlign` (the block shifts inside `[y, y + height]`
|
|
6024
|
+
* exactly as `drawText` shifts the quads).
|
|
6025
|
+
*
|
|
6026
|
+
* These are line boxes, not ink boxes: each is `fontSize * lineHeight` tall
|
|
6027
|
+
* from the pen's line top. Ink can escape vertically at a `lineHeight` low
|
|
6028
|
+
* enough — see the `bounds` note at the end of `layoutRuns` — which is the
|
|
6029
|
+
* right trade for hit-testing, where a box that hugged the ink would make
|
|
6030
|
+
* an `x` harder to click than an `X`.
|
|
6031
|
+
*/
|
|
6032
|
+
|
|
6033
|
+
/** Options for {@link textLineBoxes}. */
|
|
6034
|
+
interface TextLineBoxesOpts {
|
|
6035
|
+
/** Grow every box by this much on all four sides (world units). Default 0.
|
|
6036
|
+
* Picking wants a little slack so a single hairline row of text is still
|
|
6037
|
+
* grabbable; clipping and export want none. */
|
|
6038
|
+
padding?: number;
|
|
6039
|
+
/** Keep boxes for blank lines (zero width). Default `false` — a blank line
|
|
6040
|
+
* covers no area, so for hit-testing and silhouettes it is noise. Pass
|
|
6041
|
+
* `true` when the indices have to line up with the wrapped lines. */
|
|
6042
|
+
includeEmpty?: boolean;
|
|
6043
|
+
/**
|
|
6044
|
+
* Wrap width. Default `pose.width`, which is what `createTextLayer` passes
|
|
6045
|
+
* and what `TextPose` means by its box.
|
|
6046
|
+
*
|
|
6047
|
+
* Pass `Infinity` for a node painted by the built-in `kit:text` painter:
|
|
6048
|
+
* that painter deliberately does **not** forward `maxWidth` (see
|
|
6049
|
+
* `NodeShape.ts`), so its text does not wrap, and boxes computed with a
|
|
6050
|
+
* finite width would wrap where the paint did not.
|
|
6051
|
+
*/
|
|
6052
|
+
maxWidth?: number;
|
|
6053
|
+
}
|
|
6054
|
+
/** Per-line rectangles for a text pose, in world units, in layout order. */
|
|
6055
|
+
declare function textLineBoxes(pose: TextPose, opts?: TextLineBoxesOpts): Rect[];
|
|
6056
|
+
|
|
7620
6057
|
/**
|
|
7621
6058
|
* Resize a text node's pose to fit its content. Pure helper — does not own
|
|
7622
6059
|
* scene state. Call it whenever the text, style, or relevant axis changes,
|
|
@@ -7651,6 +6088,82 @@ interface FitTextPoseOptions {
|
|
|
7651
6088
|
/** Recompute a `TextPose`'s `width`/`height` to fit its content; pure helper, doesn't mutate scene state. */
|
|
7652
6089
|
declare function fitTextPose(ctx: CanvasRenderingContext2D, pose: TextPose, opts?: FitTextPoseOptions): TextPose;
|
|
7653
6090
|
|
|
6091
|
+
/**
|
|
6092
|
+
* The kit-wide "these values disagree" sentinel. Used wherever a value is
|
|
6093
|
+
* aggregated across more than one source and the sources don't agree — e.g.
|
|
6094
|
+
* a UI panel aggregating a property across a multi-node selection, or text
|
|
6095
|
+
* styling aggregated across the runs in a character range. One symbol, one
|
|
6096
|
+
* `isMixed` check, regardless of what's being aggregated.
|
|
6097
|
+
*/
|
|
6098
|
+
declare const MIXED: unique symbol;
|
|
6099
|
+
type Mixed = typeof MIXED;
|
|
6100
|
+
|
|
6101
|
+
/**
|
|
6102
|
+
* Range-addressed styling over `StyledRun[]`. The panel addresses node paths;
|
|
6103
|
+
* a caret addresses a character range — these are the functions for the
|
|
6104
|
+
* second case. Pure and React-free so the semantics are unit-testable, the
|
|
6105
|
+
* same split `SelectionPanel` makes between `model.ts` and its component.
|
|
6106
|
+
*
|
|
6107
|
+
* Character offsets index the concatenated run text (`runsToPlainText`), and
|
|
6108
|
+
* a range is half-open: `[start, end)`. Ranges are clamped, never wrapped —
|
|
6109
|
+
* a negative `start` clamps to 0, an `end` past the text clamps to its
|
|
6110
|
+
* length, and a collapsed or inverted range (`start >= end`) is empty.
|
|
6111
|
+
* Offsets are UTF-16 code units, as everywhere else in this feature, so a
|
|
6112
|
+
* boundary can bisect a surrogate pair (`[1, 2)` over `'a👍b'` splits the
|
|
6113
|
+
* thumbs-up into lone surrogates). A caret-derived range never does this.
|
|
6114
|
+
*
|
|
6115
|
+
* **Run-level flags are additive over the node's `TextStyle`: a run can
|
|
6116
|
+
* turn `bold` / `italic` / `underline` / `strikethrough` on, never off.**
|
|
6117
|
+
* So a flag is stored only when true, and absent reads as `false`. For
|
|
6118
|
+
* `bold` this falls out of the model — node weight is numeric, and
|
|
6119
|
+
* `run.bold ? 700 : baseWeight` has nowhere to put "not bold." For the
|
|
6120
|
+
* boolean decorations it does not: both levels are booleans, so a tri-state
|
|
6121
|
+
* (`true` / `false` / inherit) is expressible and we are collapsing it by
|
|
6122
|
+
* choice, to keep one canonical form per styling. Resolution must therefore
|
|
6123
|
+
* read decorations as `run.underline || style.underline` — a `??` would
|
|
6124
|
+
* make "select a word in an underlined node, hit U to turn it off" look
|
|
6125
|
+
* supported by the type while this file discards the `false` that expresses it.
|
|
6126
|
+
*
|
|
6127
|
+
* Copies here are shallow: a `fill` object is shared by reference between
|
|
6128
|
+
* the input runs, the patch, every run the patch touched, and the value
|
|
6129
|
+
* `styleAtRange` reports. Treat `fill` values as immutable.
|
|
6130
|
+
*/
|
|
6131
|
+
|
|
6132
|
+
/** Every styleable key of a run, each either a concrete value or MIXED (the
|
|
6133
|
+
* runs in the range disagree at that key). */
|
|
6134
|
+
type RangeStyle = {
|
|
6135
|
+
[K in Exclude<keyof StyledRun, 'text'>]?: StyledRun[K] | Mixed;
|
|
6136
|
+
};
|
|
6137
|
+
/**
|
|
6138
|
+
* What `applyStyleToRange` writes. `text` is deliberately not part of it —
|
|
6139
|
+
* this is a styling operation, not an edit.
|
|
6140
|
+
*/
|
|
6141
|
+
type RunStylePatch = Partial<Omit<StyledRun, 'text'>>;
|
|
6142
|
+
/** Every key of a run that carries styling — i.e. everything but `text`. */
|
|
6143
|
+
type StyleKey = Exclude<keyof StyledRun, 'text'>;
|
|
6144
|
+
/**
|
|
6145
|
+
* The styling shared by every run overlapping `[start, end)`: a concrete
|
|
6146
|
+
* value where the range agrees, `MIXED` where it doesn't. Keys no run
|
|
6147
|
+
* in range sets are absent (they inherit the node's `TextStyle`) — except
|
|
6148
|
+
* the additive flags, which read as `false` rather than `undefined` since a
|
|
6149
|
+
* run cannot un-set them. An empty range reads as `{}`. A reported `fill`
|
|
6150
|
+
* is the run's own object, not a copy — treat it as immutable.
|
|
6151
|
+
*/
|
|
6152
|
+
declare function styleAtRange(runs: readonly StyledRun[], start: number, end: number): RangeStyle;
|
|
6153
|
+
/**
|
|
6154
|
+
* Write `patch` over `[start, end)`, splitting runs at the boundaries and
|
|
6155
|
+
* coalescing neighbors that end up identical. A patch value of `undefined`
|
|
6156
|
+
* or `false` deletes the key instead of storing it — see the additive-flags
|
|
6157
|
+
* contract at the top of the file.
|
|
6158
|
+
*
|
|
6159
|
+
* Returns a new array of new runs; neither the input array nor its runs are
|
|
6160
|
+
* mutated (though a nested `fill` is shared by reference). Normalization —
|
|
6161
|
+
* merging identical neighbors, dropping empty runs — applies to the whole
|
|
6162
|
+
* array, not just the patched span, and to every call, including one whose
|
|
6163
|
+
* range is empty and so patches nothing.
|
|
6164
|
+
*/
|
|
6165
|
+
declare function applyStyleToRange(runs: readonly StyledRun[], start: number, end: number, patch: RunStylePatch): StyledRun[];
|
|
6166
|
+
|
|
7654
6167
|
/**
|
|
7655
6168
|
* In-place text editing via a contenteditable overlay positioned over the
|
|
7656
6169
|
* text node's screen-space pose. Enter / blur commit; Shift+Enter inserts a
|
|
@@ -7662,17 +6175,51 @@ declare function fitTextPose(ctx: CanvasRenderingContext2D, pose: TextPose, opts
|
|
|
7662
6175
|
* (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
|
|
7663
6176
|
*/
|
|
7664
6177
|
|
|
6178
|
+
/**
|
|
6179
|
+
* The caret's character range within the text being edited. Half-open
|
|
6180
|
+
* `[start, end)` over the concatenated run text, normalized so `start <= end`
|
|
6181
|
+
* regardless of which way the user dragged. `start === end` is a collapsed
|
|
6182
|
+
* caret — a real position, not the absence of one, which is why the hook
|
|
6183
|
+
* reports `null` rather than a zero-width range when there is no caret.
|
|
6184
|
+
*/
|
|
6185
|
+
interface TextEditSelection {
|
|
6186
|
+
start: number;
|
|
6187
|
+
end: number;
|
|
6188
|
+
}
|
|
7665
6189
|
/** Screen-space pose passed to `useTextEdit` so the overlay can be placed and sized in CSS pixels. */
|
|
7666
6190
|
interface TextEditScreenPose {
|
|
7667
|
-
/** Top-left in CSS pixels relative to `container`.
|
|
6191
|
+
/** Top-left in CSS pixels relative to `container`. Always screen pixels,
|
|
6192
|
+
* including when `zoom` is set — the scale is anchored at this point, not
|
|
6193
|
+
* translated by it. */
|
|
7668
6194
|
x: number;
|
|
7669
6195
|
y: number;
|
|
6196
|
+
/** Pre-scale — CSS pixels, or world units when `zoom` is set. */
|
|
7670
6197
|
width: number;
|
|
7671
6198
|
height: number;
|
|
7672
|
-
/**
|
|
6199
|
+
/** Pre-scale font size: `style.fontSize * zoom` when `zoom` is omitted, the
|
|
6200
|
+
* world-unit `style.fontSize` when it is set. */
|
|
7673
6201
|
fontSize: number;
|
|
7674
6202
|
/** Effective on-screen line height multiplier (defaults to style.lineHeight). */
|
|
7675
6203
|
lineHeight?: number;
|
|
6204
|
+
/**
|
|
6205
|
+
* CSS scale applied to the overlay (`transform: scale(zoom)`, anchored at
|
|
6206
|
+
* its top-left). Every other size on this pose, and every typographic
|
|
6207
|
+
* metric the hook writes, is then **pre-scale** — pass world units and the
|
|
6208
|
+
* transform does the world→screen conversion.
|
|
6209
|
+
*
|
|
6210
|
+
* This is the only way run-level typography can be correct at a zoom other
|
|
6211
|
+
* than 1. `runsToDom` emits run `fontSize` / `letterSpacing` in world units
|
|
6212
|
+
* and `domToRuns` reads them straight back; threading a scale through the
|
|
6213
|
+
* writer and its inverse through the reader would go lossy on fractional
|
|
6214
|
+
* zooms. Scaling the whole overlay instead leaves that serializer pure and
|
|
6215
|
+
* scales node-level and run-level values by the same factor for free.
|
|
6216
|
+
*
|
|
6217
|
+
* Omit it (the default, `1`) and the pose is plain screen pixels — the hook
|
|
6218
|
+
* then infers the world→screen factor from `fontSize / style.fontSize` to
|
|
6219
|
+
* scale node-level `letterSpacing`, and run-level overrides are left
|
|
6220
|
+
* unscaled. Correct only at zoom 1.
|
|
6221
|
+
*/
|
|
6222
|
+
zoom?: number;
|
|
7676
6223
|
}
|
|
7677
6224
|
/** Options for `useTextEdit`. */
|
|
7678
6225
|
interface UseTextEditOptions {
|
|
@@ -7697,9 +6244,27 @@ interface UseTextEditOptions {
|
|
|
7697
6244
|
* Optional: commit rich-text runs back to the node. When omitted, only
|
|
7698
6245
|
* `setText` is called with the plain-text form on commit. When provided,
|
|
7699
6246
|
* commit calls both `setText` (with `runsToPlainText(runs)`) and
|
|
7700
|
-
* `setRuns(id, runs)
|
|
6247
|
+
* `setRuns(id, runs)` — but only when runs are actually in play: the node
|
|
6248
|
+
* already had some, or the edit produced styling. A plain-text edit of a
|
|
6249
|
+
* plain-text node still calls `setText` alone, so a node that has never
|
|
6250
|
+
* been styled doesn't grow a single-run `runs` array just for being edited.
|
|
7701
6251
|
*/
|
|
7702
6252
|
setRuns?: (id: string, runs: StyledRun[]) => void;
|
|
6253
|
+
/**
|
|
6254
|
+
* Is `el` part of the editor's own chrome — a character-options bar, a
|
|
6255
|
+
* color popover, anything whose whole purpose is to style the text being
|
|
6256
|
+
* edited? Focus moving into one does not end the edit.
|
|
6257
|
+
*
|
|
6258
|
+
* Without this, the controls the feature exists for are exactly what
|
|
6259
|
+
* destroys it: clicking a size field blurs the overlay, blur commits, and
|
|
6260
|
+
* the caret the control was about to act on is gone. Toggle buttons can
|
|
6261
|
+
* dodge it by `preventDefault()`-ing their own mousedown, but a field the
|
|
6262
|
+
* user has to type into cannot.
|
|
6263
|
+
*
|
|
6264
|
+
* Chrome focus does not disturb the reported `selection` either — see
|
|
6265
|
+
* `UseTextEditReturn.selection`.
|
|
6266
|
+
*/
|
|
6267
|
+
isEditorChrome?: (el: Element) => boolean;
|
|
7703
6268
|
}
|
|
7704
6269
|
/** Options for `useTextEdit().startEdit`. */
|
|
7705
6270
|
interface StartEditOptions {
|
|
@@ -7716,6 +6281,37 @@ interface UseTextEditReturn {
|
|
|
7716
6281
|
cancelEdit: () => void;
|
|
7717
6282
|
commit: () => void;
|
|
7718
6283
|
isEditing: (id: string) => boolean;
|
|
6284
|
+
/**
|
|
6285
|
+
* The caret's character range, or `null` when nothing is being edited. A
|
|
6286
|
+
* collapsed caret reports `{ start: n, end: n }`, so `null` and "caret at
|
|
6287
|
+
* n" stay distinguishable — a character-styling control routes the
|
|
6288
|
+
* collapsed case to the node's `TextStyle` instead of to a range.
|
|
6289
|
+
*
|
|
6290
|
+
* Follows the DOM selection, which browsers (and jsdom) report from a task
|
|
6291
|
+
* rather than synchronously; anything this hook writes itself updates it
|
|
6292
|
+
* synchronously. A DOM selection that leaves the overlay does **not** clear
|
|
6293
|
+
* it: that is what happens when the user clicks a styling control, and
|
|
6294
|
+
* reporting `null` there would read as "collapsed caret" and send the
|
|
6295
|
+
* control's patch to the node instead of to the range. It is cleared on
|
|
6296
|
+
* `startEdit` and when the edit ends.
|
|
6297
|
+
*/
|
|
6298
|
+
selection: TextEditSelection | null;
|
|
6299
|
+
/**
|
|
6300
|
+
* The styling shared by every run in `selection` — a concrete value where
|
|
6301
|
+
* the range agrees, `MIXED` where it doesn't. `null` exactly when
|
|
6302
|
+
* `selection` is `null`. A collapsed caret reports `{}`: no run is in
|
|
6303
|
+
* range, so the range reader has nothing to say and the node's style is
|
|
6304
|
+
* what applies.
|
|
6305
|
+
*/
|
|
6306
|
+
rangeStyle: RangeStyle | null;
|
|
6307
|
+
/**
|
|
6308
|
+
* Write `patch` over `selection`. A no-op with no active edit, with a
|
|
6309
|
+
* collapsed caret (there is no range to style — patch the node's
|
|
6310
|
+
* `TextStyle` instead), or with an empty patch. The caret survives, so a
|
|
6311
|
+
* second style can be applied without re-selecting, and `rangeStyle`
|
|
6312
|
+
* reflects the write before this returns.
|
|
6313
|
+
*/
|
|
6314
|
+
applyStyleToSelection: (patch: RunStylePatch) => void;
|
|
7719
6315
|
}
|
|
7720
6316
|
/** In-place text editing via a contenteditable overlay positioned over the text node's screen-space pose. */
|
|
7721
6317
|
declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
|
|
@@ -7748,8 +6344,10 @@ declare function unionBounds<TPose extends RectPose>(poses: Iterable<TPose>): Re
|
|
|
7748
6344
|
* (`getText` / `getStyle` / `getRuns` / `setText` / `setRuns`).
|
|
7749
6345
|
*
|
|
7750
6346
|
* Pose component: the helper reads `(x, y, width, height)` straight off
|
|
7751
|
-
* the node's pose (typed `RectPose`).
|
|
7752
|
-
*
|
|
6347
|
+
* the node's pose (typed `RectPose`). Pass `view` and it projects that box
|
|
6348
|
+
* through the viewport; omit it and world units are handed through as
|
|
6349
|
+
* screen pixels, which is correct only for an unpanned, unzoomed canvas.
|
|
6350
|
+
* Consumers with non-rect poses should drop down to raw `useTextEdit` and
|
|
7753
6351
|
* supply their own `getScreenPose`.
|
|
7754
6352
|
*/
|
|
7755
6353
|
|
|
@@ -7775,6 +6373,25 @@ interface UseSceneTextEditOptions<TData> {
|
|
|
7775
6373
|
setRuns?: (data: TData, runs: StyledRun[]) => TData;
|
|
7776
6374
|
/** Fallback fontSize when `style.fontSize` is unset. Default `16`. */
|
|
7777
6375
|
defaultFontSize?: number;
|
|
6376
|
+
/**
|
|
6377
|
+
* Current viewport. Supply it on a canvas that pans or zooms: the overlay
|
|
6378
|
+
* is then positioned at the node's projected screen origin and CSS-scaled
|
|
6379
|
+
* by the view, so every typographic metric on it — including the
|
|
6380
|
+
* `fontSize` / `letterSpacing` a *run* carries — stays in world units and
|
|
6381
|
+
* scales together. Omit it and the node's world box is passed through as
|
|
6382
|
+
* screen pixels (correct at `{x: 0, y: 0, scale: 1}`).
|
|
6383
|
+
*
|
|
6384
|
+
* The overlay takes a single scale factor, so a non-uniform view scale is
|
|
6385
|
+
* represented by its `scale.x`; text under `scale.x !== scale.y` will not
|
|
6386
|
+
* match the canvas.
|
|
6387
|
+
*/
|
|
6388
|
+
view?: View;
|
|
6389
|
+
/**
|
|
6390
|
+
* Forwarded to `useTextEdit`: is `el` part of the editor's own chrome?
|
|
6391
|
+
* Focus moving into it does not end the edit. Wire it to whatever renders
|
|
6392
|
+
* the character controls.
|
|
6393
|
+
*/
|
|
6394
|
+
isEditorChrome?: (el: Element) => boolean;
|
|
7778
6395
|
}
|
|
7779
6396
|
/** Return shape extends `UseTextEditReturn` with an `onDoubleClick`
|
|
7780
6397
|
* binding for the canvas container. Double-clicking inside a text
|
|
@@ -7790,10 +6407,12 @@ interface UseSceneTextEditReturn extends UseTextEditReturn {
|
|
|
7790
6407
|
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
6408
|
|
|
7792
6409
|
/**
|
|
7793
|
-
* Build a `TextDrawCommand`
|
|
7794
|
-
*
|
|
7795
|
-
* `resolveTextStyle` + `resolveRuns
|
|
7796
|
-
*
|
|
6410
|
+
* Build a `TextDrawCommand`. `textCommand` takes a plain string and wraps it
|
|
6411
|
+
* in one run; `textCommandFromRuns` takes the styled runs directly. Both
|
|
6412
|
+
* resolve through `resolveTextStyle` + `resolveRuns`, so every emitter of a
|
|
6413
|
+
* text command derives `align` and per-run resolution the same way — a
|
|
6414
|
+
* caller that assembles the command by hand is one field away from a
|
|
6415
|
+
* silently different result.
|
|
7797
6416
|
*/
|
|
7798
6417
|
|
|
7799
6418
|
declare function textCommand(x: number, y: number, text: string, style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign): DrawCommand;
|
|
@@ -7823,8 +6442,15 @@ declare function charOffsetToDomPosition(parent: HTMLElement, offset: number): {
|
|
|
7823
6442
|
/**
|
|
7824
6443
|
* Inverse of `charOffsetToDomPosition`. Walks text nodes in document order;
|
|
7825
6444
|
* sums the lengths of every text node preceding `node` and adds `offset`.
|
|
7826
|
-
*
|
|
7827
|
-
*
|
|
6445
|
+
*
|
|
6446
|
+
* When `node` is an element the DOM offset indexes *child nodes*, not
|
|
6447
|
+
* characters, so it can't simply be added — `(overlay, 0)` is the start of
|
|
6448
|
+
* the text and `(overlay, childNodes.length)` its end. That shape is not
|
|
6449
|
+
* exotic: `Range.selectNodeContents`, which `useTextEdit` uses for its
|
|
6450
|
+
* select-all caret, produces exactly it. So resolve an element position by
|
|
6451
|
+
* counting the text that precedes the boundary point. A text node can never
|
|
6452
|
+
* *contain* that point (the container isn't one), so each is wholly before
|
|
6453
|
+
* or wholly after it.
|
|
7828
6454
|
*/
|
|
7829
6455
|
declare function domPositionToCharOffset(parent: HTMLElement, node: Node, offset: number): number;
|
|
7830
6456
|
|
|
@@ -8072,6 +6698,48 @@ declare function registerOpFactory<A>(name: string, build: (args: A) => Op): voi
|
|
|
8072
6698
|
* a no-op placeholder so stack ordering survives across kit version skew). */
|
|
8073
6699
|
declare function rebuildOp(name: string, args: unknown): Op | null;
|
|
8074
6700
|
|
|
6701
|
+
/** @internal */
|
|
6702
|
+
interface HitAdapter<TNode extends {
|
|
6703
|
+
id: string;
|
|
6704
|
+
}, TPose> {
|
|
6705
|
+
getNode: (id: string) => TNode | undefined;
|
|
6706
|
+
getNodes: () => TNode[];
|
|
6707
|
+
getPose: (id: string) => TPose;
|
|
6708
|
+
getParent: (id: string) => string | null;
|
|
6709
|
+
}
|
|
6710
|
+
interface NestedHitOpts<TNode extends {
|
|
6711
|
+
id: string;
|
|
6712
|
+
}, TPose> {
|
|
6713
|
+
/** Compose a child's local pose into world coords given its parent's world
|
|
6714
|
+
* pose. Same shape as `composeRectPose` (the default expectation). */
|
|
6715
|
+
composePose: (parent: TPose, child: TPose) => TPose;
|
|
6716
|
+
/** Derive an axis-aligned bounding rect from a (world-space) pose. Default
|
|
6717
|
+
* reads `x` / `y` / `width` / `height` straight off the pose (matches
|
|
6718
|
+
* `RectPose`). Override for non-rect poses (e.g. paths). */
|
|
6719
|
+
poseBounds?: (pose: TPose) => Bounds;
|
|
6720
|
+
/** Predicate for "this object is a nesting parent body". The leaf scan
|
|
6721
|
+
* skips objects for which this returns true so a click on a parent's
|
|
6722
|
+
* painted body resolves to a child leaf, not the parent itself. Default:
|
|
6723
|
+
* never (treat every object as hittable). */
|
|
6724
|
+
isGroup?: (id: string, obj: TNode | undefined) => boolean;
|
|
6725
|
+
}
|
|
6726
|
+
interface NestedHitTester {
|
|
6727
|
+
/** Outermost-ancestor pick. Suitable as the chrome-level `pickEvery`: a
|
|
6728
|
+
* casual click selects the whole top-level ancestor. Returns `null` on
|
|
6729
|
+
* empty space. */
|
|
6730
|
+
pickOutermost: (worldX: number, worldY: number) => string | null;
|
|
6731
|
+
/** Alt-aware selection-update pick. Without `alt`, returns the outermost
|
|
6732
|
+
* ancestor (same as `pickOutermost`). With `alt`, returns one level
|
|
6733
|
+
* deeper than the deepest currently-selected ancestor in the leaf's
|
|
6734
|
+
* chain — repeated alt-clicks step ancestor → descendant → leaf. With
|
|
6735
|
+
* `alt` and nothing in the chain selected, jumps straight to the leaf.
|
|
6736
|
+
* Plug into `useSelectTool({ pickBest })`. */
|
|
6737
|
+
pickBest: (worldX: number, worldY: number, alt: boolean, selection: readonly string[]) => string | null;
|
|
6738
|
+
}
|
|
6739
|
+
declare function nestedHitTester<TNode extends {
|
|
6740
|
+
id: string;
|
|
6741
|
+
}, TPose>(adapter: HitAdapter<TNode, TPose>, opts: NestedHitOpts<TNode, TPose>): NestedHitTester;
|
|
6742
|
+
|
|
8075
6743
|
/**
|
|
8076
6744
|
* Fluent builder for `PolygonPath`. Hides the `Uint8Array` / `Float32Array`
|
|
8077
6745
|
* encoding behind move/line/curve/close calls and a final `build()`. Use
|
|
@@ -8173,6 +6841,44 @@ declare function linePath(a: {
|
|
|
8173
6841
|
/** AABB of a `Path`, returned as a `RectPath` for direct reuse with rect-fast-path machinery. */
|
|
8174
6842
|
declare function boundsOfPath(path: Path): RectPath;
|
|
8175
6843
|
|
|
6844
|
+
/**
|
|
6845
|
+
* Counts the path anchors used by the per-anchor coloring surface. An
|
|
6846
|
+
* "anchor" is the destination point of a path command: M, L, C, Q each
|
|
6847
|
+
* contribute one (the (x, y) where the pen ends up); Z contributes none
|
|
6848
|
+
* (it closes back to the subpath's first M). RectPath has four implicit
|
|
6849
|
+
* anchors — the corners — matching its M/L/L/L/Z stroke tessellation.
|
|
6850
|
+
*
|
|
6851
|
+
* Consumers use this to size their per-anchor color array; the renderer
|
|
6852
|
+
* uses it to validate the array length in dev builds.
|
|
6853
|
+
*/
|
|
6854
|
+
|
|
6855
|
+
interface PenAnchor {
|
|
6856
|
+
x: number;
|
|
6857
|
+
y: number;
|
|
6858
|
+
inHandle?: {
|
|
6859
|
+
x: number;
|
|
6860
|
+
y: number;
|
|
6861
|
+
};
|
|
6862
|
+
outHandle?: {
|
|
6863
|
+
x: number;
|
|
6864
|
+
y: number;
|
|
6865
|
+
};
|
|
6866
|
+
}
|
|
6867
|
+
/**
|
|
6868
|
+
* Derive a per-subpath anchor model from a PolygonPath. Subpaths split on
|
|
6869
|
+
* every `M` command; a subpath is closed iff it ends with `Z`.
|
|
6870
|
+
*
|
|
6871
|
+
* Cubic-segment control points become the outHandle of the previous anchor
|
|
6872
|
+
* and the inHandle of the next anchor. Quadratic segments are upgraded to
|
|
6873
|
+
* cubics (each control reused for both adjacent handles) — this loses no
|
|
6874
|
+
* geometry. Linear segments produce anchors with no handles.
|
|
6875
|
+
*/
|
|
6876
|
+
declare function pathToAnchors(path: PolygonPath): {
|
|
6877
|
+
anchors: PenAnchor[][];
|
|
6878
|
+
closed: boolean[];
|
|
6879
|
+
};
|
|
6880
|
+
declare function countPathAnchors(path: Path): number;
|
|
6881
|
+
|
|
8176
6882
|
interface Point {
|
|
8177
6883
|
x: number;
|
|
8178
6884
|
y: number;
|
|
@@ -8198,24 +6904,6 @@ interface PointInPathOptions {
|
|
|
8198
6904
|
/** Filled-region hit-test for a `Path`. Rect short-circuits to AABB; polygons run ray-cast / winding per `fillRule`. */
|
|
8199
6905
|
declare function pointInPath(path: Path, x: number, y: number, opts?: PointInPathOptions): boolean;
|
|
8200
6906
|
|
|
8201
|
-
/**
|
|
8202
|
-
* Polygon-vs-axis-aligned-rect tests. Pure functions; no React/kit deps.
|
|
8203
|
-
*
|
|
8204
|
-
* `polygon` is a closed polyline given as an ordered vertex array; the
|
|
8205
|
-
* closing edge from `polygon[N-1]` to `polygon[0]` is implicit. Even-odd
|
|
8206
|
-
* fill rule (matches `pointInPath`).
|
|
8207
|
-
*/
|
|
8208
|
-
interface Vec2 {
|
|
8209
|
-
x: number;
|
|
8210
|
-
y: number;
|
|
8211
|
-
}
|
|
8212
|
-
interface Rect {
|
|
8213
|
-
x: number;
|
|
8214
|
-
y: number;
|
|
8215
|
-
width: number;
|
|
8216
|
-
height: number;
|
|
8217
|
-
}
|
|
8218
|
-
|
|
8219
6907
|
/**
|
|
8220
6908
|
* Path-vs-geometry hit-test helpers. Five pure functions covering all
|
|
8221
6909
|
* combinations of path-vs-point, path-vs-rect, and path-vs-polygon.
|
|
@@ -8654,10 +7342,12 @@ declare function createPenPreviewLayer(opts: CreatePenPreviewLayerOptions): Rend
|
|
|
8654
7342
|
* `getEditingId()` + `getPose(id)` thunks. Returns `[]` when no editing
|
|
8655
7343
|
* target is set or the target isn't a polygon — the layer never throws.
|
|
8656
7344
|
*
|
|
8657
|
-
*
|
|
8658
|
-
*
|
|
8659
|
-
*
|
|
8660
|
-
*
|
|
7345
|
+
* Anchors render as small white-filled stroked squares — filled solid
|
|
7346
|
+
* when selected; control points as small filled circles connected to
|
|
7347
|
+
* their anchor by a thin stem line. This is the kit's only anchor
|
|
7348
|
+
* chrome; the pen tool's parallel overlay and `apps/draw`'s
|
|
7349
|
+
* mode-decoration painter both drew the same thing from different state
|
|
7350
|
+
* and have been removed.
|
|
8661
7351
|
*
|
|
8662
7352
|
* All coordinates are projected via `worldToScreen` so the marker sizes
|
|
8663
7353
|
* stay constant regardless of zoom.
|
|
@@ -8671,7 +7361,26 @@ interface CreatePathEditingOverlayLayerOptions {
|
|
|
8671
7361
|
/** Returns the pose for an id, or null if the node has been deleted.
|
|
8672
7362
|
* Non-polygon poses are tolerated (the layer no-ops on them). */
|
|
8673
7363
|
getPose(id: string): Path | null;
|
|
8674
|
-
/**
|
|
7364
|
+
/** Flat indices of the selected anchors. Selected anchors render
|
|
7365
|
+
* filled; unselected ones hollow — the standard vector-editor cue for
|
|
7366
|
+
* "these are what the arrow keys and Delete will act on". Omit when
|
|
7367
|
+
* the consumer has no anchor selection to show. */
|
|
7368
|
+
getSelectedAnchors?(): ReadonlySet<number>;
|
|
7369
|
+
/** In-flight anchor-marquee rect in world coords, or null. Drawn as a
|
|
7370
|
+
* rubber band while `marqueeAnchorsAction` is dragging. */
|
|
7371
|
+
getMarquee?(): {
|
|
7372
|
+
x: number;
|
|
7373
|
+
y: number;
|
|
7374
|
+
width: number;
|
|
7375
|
+
height: number;
|
|
7376
|
+
} | null;
|
|
7377
|
+
/** Chrome-caps predicate. The layer asks `'path-edit.anchors'` before
|
|
7378
|
+
* drawing anything, so paint and the anchor hit-test in
|
|
7379
|
+
* `affordanceAt` consult the same rule — otherwise a consumer that
|
|
7380
|
+
* hides the chrome still gets grabbable invisible anchors, or vice
|
|
7381
|
+
* versa. Omit to always draw when an edit target is set. */
|
|
7382
|
+
isVisible?(chromeId: string): boolean;
|
|
7383
|
+
/** Optional styling overrides. */
|
|
8675
7384
|
style?: PathEditingOverlayStyle;
|
|
8676
7385
|
}
|
|
8677
7386
|
interface PathEditingOverlayStyle {
|
|
@@ -8685,6 +7394,13 @@ interface PathEditingOverlayStyle {
|
|
|
8685
7394
|
* curve / background show through, which matches Figma's "translucent
|
|
8686
7395
|
* handle dot" idiom. */
|
|
8687
7396
|
handleDotFill?: string;
|
|
7397
|
+
/** Fill of a selected anchor square. Defaults to the anchor stroke
|
|
7398
|
+
* color, so selection reads as "the marker filled in". */
|
|
7399
|
+
anchorFillSelected?: string;
|
|
7400
|
+
/** Stroke of the marquee rubber band. */
|
|
7401
|
+
marqueeStroke?: string;
|
|
7402
|
+
/** Fill of the marquee rubber band. */
|
|
7403
|
+
marqueeFill?: string;
|
|
8688
7404
|
}
|
|
8689
7405
|
declare function createPathEditingOverlayLayer(opts: CreatePathEditingOverlayLayerOptions): RenderLayer<unknown>;
|
|
8690
7406
|
|
|
@@ -9421,36 +8137,6 @@ interface AnchorHit {
|
|
|
9421
8137
|
* are within `threshold` (controls are smaller and rendered on top). */
|
|
9422
8138
|
declare function hitAnchor(path: PolygonPath, worldX: number, worldY: number, threshold: number): AnchorHit | null;
|
|
9423
8139
|
|
|
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
8140
|
/**
|
|
9455
8141
|
* Typed keys for the gesture/behavior scratch store.
|
|
9456
8142
|
*
|
|
@@ -10197,4 +8883,11 @@ declare function ExcludeIcon(): react_jsx_runtime.JSX.Element;
|
|
|
10197
8883
|
declare function DivideIcon(): react_jsx_runtime.JSX.Element;
|
|
10198
8884
|
declare function CropIcon(): react_jsx_runtime.JSX.Element;
|
|
10199
8885
|
|
|
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 };
|
|
8886
|
+
interface InsertOverlayStyle {
|
|
8887
|
+
fill?: string;
|
|
8888
|
+
stroke?: string;
|
|
8889
|
+
dash?: number[];
|
|
8890
|
+
lineWidth?: number;
|
|
8891
|
+
}
|
|
8892
|
+
|
|
8893
|
+
export { Action, ActionsProp, ActionsProviderIfRoot, ActionsRegistry, Affordance, AffordanceBinding, AffordanceHit, AffordanceRegion, type AffordanceRegionHit, type AlignAdapter, type AlignAnchor, type AlignBounds, type AlignBoundsProjection, type AlignEdge, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type AnchorHit, type AnchorScratch, type AnchorState, type AnimateOnSetPoseOptions, type AnimateToBoundsOptions, type AnimationHandle, type Animator, AnyTool, AreaSelectAdapter, type ArrayAdapter, type ArrayAdapterConfig, BUNDLE_TOOLS, 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, CommonAffordanceScratch, 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 NodeInk, type NodePaintCtx, type NodeProperties, type NodePropertiesEntry, type NodeRouting, type NodeRoutingEntry, type NodeShapeEntry, type OpenFilePickerOptions, OrderedAdapter, OriginProjection, PATH_ANCHOR_CHROME_ID, type PanBounds, type ParallaxOpts, Path, type PathAnchor, type PathAnchorAffordanceOptions, PathBuilder, PathDrawCommand, type PathEditingOverlayStyle, PathFillRule, type PathInWorldPose, type PenAnchor, PenIcon, type PenPreviewStyle, type PenScratch, type PenSubpath, PencilIcon, type PencilPoint, type PhysicsHandle, type PhysicsOptions, PickHud, type PickHudProps, type PickTopMostHitAdapter, type PinchZoomToolOpts, type PixelRenderPlan, type PointInPathOptions, type PointInTextPoseOpts, PointSnapBehavior, PointerProviderIfRoot, type PointerSample, type PointerStylusState, PolygonIcon, PolygonPath, type PolygonPoint, 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 ShapeCoversPointOptions, 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 TextLineBoxesOpts, 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, VERSION, 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, annulusSemiAxes, 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, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createReparentOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetSelectionOp, createSetTextOp, createTextLayer, createTilePattern, createTransformOp, createViewportLayer, cycleVertexColors, decomposePath, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, 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, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitZoom, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, focused, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getScratch, getStylusData, guideSnapStrategy, hexToRgba, hitAffordanceRegions, 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, shapeCoversPoint, snap, snapPoint, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, starPath, startThresholdDrag, styleAtRange, subscribeImageReady, textCommand, textLineBoxes, tileGrid, toHex8, transformPath, translatePath, translatePolygonInPlace, translatePoseViaDescriptor, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, 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 };
|