@weasel-js/core 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +273 -0
- package/dist/{autoPoseDescriptor-Dr6CwNZK.d.ts → autoPoseDescriptor-_XOyF7t4.d.ts} +2 -1
- package/dist/{chunk-P6MGECVO.js → chunk-44EWMJV2.js} +3 -3
- package/dist/{chunk-P6MGECVO.js.map → chunk-44EWMJV2.js.map} +1 -1
- package/dist/{chunk-XXQ6FLCJ.js → chunk-H6N2JSZ3.js} +3 -3
- package/dist/{chunk-XXQ6FLCJ.js.map → chunk-H6N2JSZ3.js.map} +1 -1
- package/dist/{chunk-HAGOFNP5.js → chunk-HPFJUUTF.js} +25 -20
- package/dist/chunk-HPFJUUTF.js.map +1 -0
- package/dist/{chunk-3LPQ2XZG.js → chunk-L5YNZYEG.js} +8 -13
- package/dist/chunk-L5YNZYEG.js.map +1 -0
- package/dist/{chunk-LXJDWBEL.js → chunk-PNNZSYRD.js} +2 -2
- package/dist/{chunk-LXJDWBEL.js.map → chunk-PNNZSYRD.js.map} +1 -1
- package/dist/{chunk-GREL4MVO.js → chunk-THCSDHII.js} +2084 -1344
- package/dist/chunk-THCSDHII.js.map +1 -0
- package/dist/clipboard.d.ts +2 -1
- package/dist/clone.d.ts +3 -2
- package/dist/{depSchema-BubKMv-2.d.ts → depSchema-BhgZc0-Q.d.ts} +79 -55
- package/dist/{grid-Z_Af3vTl.d.ts → grid-Dx2gQbNv.d.ts} +22 -6
- package/dist/index.d.ts +374 -92
- package/dist/index.js +6 -6
- package/dist/insert.d.ts +4 -3
- package/dist/insert.js +2 -2
- package/dist/{math-E3rZn4bR.d.ts → math-DRa6HBBs.d.ts} +7 -21
- package/dist/math.d.ts +5 -4
- package/dist/math.js +2 -2
- package/dist/move.d.ts +5 -4
- package/dist/move.js +3 -3
- package/dist/{options-BPcmwYk7.d.ts → options-CzjxFoYZ.d.ts} +1 -1
- package/dist/path-JEV2c5If.d.ts +48 -0
- package/dist/{pointSnapToGrid-CgcK2R_I.d.ts → pointSnapToGrid-7mCcm6qG.d.ts} +1 -1
- package/dist/{poseDescriptor-PgfVKfa0.d.ts → poseDescriptor-rZa9W5hJ.d.ts} +1 -48
- package/dist/renderer.d.ts +8 -7
- package/dist/renderer.js +6 -6
- package/dist/resize.d.ts +6 -5
- package/dist/resize.js +3 -3
- package/dist/{types-BdaK9PcP.d.ts → types-CxC8P6W0.d.ts} +27 -1
- package/dist/{types-BHGdrOcu.d.ts → types-D_74YlWs.d.ts} +1 -1
- package/package.json +10 -10
- package/dist/chunk-3LPQ2XZG.js.map +0 -1
- package/dist/chunk-GREL4MVO.js.map +0 -1
- package/dist/chunk-HAGOFNP5.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { rotatePoint, aabbCenter, extractPolylines, createSimulation } from './chunk-
|
|
2
|
-
import { createReparentOp, createTransformOp, snap } from './chunk-
|
|
3
|
-
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-
|
|
4
|
-
import { resolveUnit, ANGLE_RADIANS, withinPxRadius, scaleDelta, pxExtent, withinPxBox, pointToGridCell } from './chunk-
|
|
5
|
-
import { unionBounds, visualBoundsViaDescriptor, translatePoseViaDescriptor, poseDescriptorForNode, unionAABB, AUTO_POSE_DESCRIPTOR, remapRotatedLeaf, PATH_Z, PATH_Q, PATH_C, PATH_L, PATH_M, polylineFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, polygonFromPoints, PathBuilder, boundsOfPath, pointInPath, isPathLike, strokeHitTest, isRectPose, transformPath, translatePath, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, flattenCubicWithArcLen, flattenQuadraticWithArcLen, RECT_POSE_DESCRIPTOR, aabbIntersectsRect } from './chunk-
|
|
1
|
+
import { rotatePoint, aabbCenter, extractPolylines, createSimulation } from './chunk-PNNZSYRD.js';
|
|
2
|
+
import { createReparentOp, createTransformOp, snap } from './chunk-44EWMJV2.js';
|
|
3
|
+
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-H6N2JSZ3.js';
|
|
4
|
+
import { unitScale, formatUnit, resolveUnit, ANGLE_RADIANS, withinPxRadius, scaleDelta, pxExtent, withinPxBox, pointToGridCell } from './chunk-HPFJUUTF.js';
|
|
5
|
+
import { unionBounds, visualBoundsViaDescriptor, translatePoseViaDescriptor, poseDescriptorForNode, unionAABB, AUTO_POSE_DESCRIPTOR, remapRotatedLeaf, PATH_Z, PATH_Q, PATH_C, PATH_L, PATH_M, polylineFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, polygonFromPoints, PathBuilder, boundsOfPath, pointInPath, isPathLike, strokeHitTest, isRectPose, transformPath, translatePath, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, flattenCubicWithArcLen, flattenQuadraticWithArcLen, RECT_POSE_DESCRIPTOR, aabbIntersectsRect } from './chunk-L5YNZYEG.js';
|
|
6
6
|
import { dispatchApplyBatch, dlog, dwarn, usePointerContext, PointerContextProvider, extractWeaselClipboardFromSvg, parseWeaselClipboardText, materializePaste, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, sniffWeaselClipboardText, applyOpsTo } from './chunk-T3UQ3F6R.js';
|
|
7
7
|
import { createSetSelectionOp } from './chunk-DOBSZOPR.js';
|
|
8
8
|
import { registerOpFactory, createInsertOp, createDeleteOp, rebuildOp } from './chunk-4RJP2N2L.js';
|
|
@@ -10,17 +10,17 @@ import { getTexture, chunks, dots, crosshatch, hatch } from './chunk-BJN5ZRIJ.js
|
|
|
10
10
|
import { createContext, createElement, forwardRef, useMemo, useRef, useCallback, useState, useLayoutEffect, useEffect, useImperativeHandle, useSyncExternalStore, useContext, useId, useReducer } from 'react';
|
|
11
11
|
import { GLYPH_COVERAGE_GLSL, subscribeGlyphReady, DEFAULT_BAKE_BUDGET, markAllFontsNotUploaded, resetBakeBudget, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, GLYPH_MODE_R8, GLYPH_MODE_MSDF } from '@weasel-js/font';
|
|
12
12
|
export { canQueryLocalFonts, canQueryLocalFonts as canQueryLocalFonts2, enableLocalFontOutlines, enableLocalFontOutlines as enableLocalFontOutlines2, hasFontOutlines, hasFontOutlines as hasFontOutlines2, isCanvasFont, isCanvasFont as isCanvasFont2, listFontOutlines, listFontOutlines as listFontOutlines2, outlineStatus, outlineStatus as outlineStatus2, registerCanvasFont, registerCanvasFont as registerCanvasFont2, registerFont, registerFont as registerFont2, registerFontOutlines, registerFontOutlines as registerFontOutlines2, subscribeGlyphReady, subscribeGlyphReady as subscribeGlyphReady2, unregisterCanvasFont, unregisterCanvasFont as unregisterCanvasFont2, unregisterFontOutlines, unregisterFontOutlines as unregisterFontOutlines2 } from '@weasel-js/font';
|
|
13
|
-
import { dashForStrokeStyle, strokeDashStyleOf, resolveScreenLength, alignedStrokeRect, lerpColorArray,
|
|
14
|
-
export { STROKE_DASH_RATIOS, alignedStrokeRect, dashForStrokeStyle, lerpColorArray, lerpOklab, lerpOklch, oklabToOklch, oklabToSrgbU8, oklchToOklab, resolveScreenLength, srgbU8ToOklab, strokeDashStyleOf } from '@weasel-js/paint';
|
|
13
|
+
import { hexToColorLiteral, resolvePaletteColor, colorLiteralToHex, dashForStrokeStyle, strokeDashStyleOf, resolveScreenLength, alignedStrokeRect, lerpColorArray, hslToSrgb, srgbFloatToOklab, oklabToOklch, oklchToOklab, oklabToSrgbU8 } from '@weasel-js/paint';
|
|
14
|
+
export { STROKE_DASH_RATIOS, alignedStrokeRect, contrastLineColor, dashForStrokeStyle, hexToOklchDeg, hslToSrgb, interpolateSrgb, lerpColorArray, lerpOklab, lerpOklch, linearSrgbToOklab, linearToSrgb, oklabToLinearSrgb, oklabToOklch, oklabToSrgbU8, oklchDegToHex, oklchToOklab, resolveScreenLength, srgbFloatToOklab, srgbToHsl, srgbToLinear, srgbU8ToOklab, strokeDashStyleOf } from '@weasel-js/paint';
|
|
15
15
|
import { boxToBox, forEachSegment, multiply as multiply$1, rotate, translate as translate$1, rectToContour, invert as invert$1, clampRectWithin, applyToPoint, rotateAboutPoint, cubicEvalAt, pointInPolygon, segmentsCross, pathCommandCoordCount, pointSegmentDist2, elevateQuadraticToCubic } from '@weasel-js/geom';
|
|
16
16
|
import { resolveTextStyle, resolveAlign, resolveRuns, cachedLayoutRuns, verticalAlignOffset, textPoseLayoutInput, runsToPlainText, layoutTextPose, fontString, textLineBoxes } from '@weasel-js/text';
|
|
17
|
-
export { DEFAULT_TEXT_STYLE, MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, cachedLayoutRuns, createMarkdownRenderer, fontString, layoutMarkdown, layoutRuns, layoutTextPose, markdownToRuns, measureText, measureTextBounds, measuredWidth, resolveAlign, resolveRuns, resolveTextStyle, runsToMarkdown, runsToPlainText, textLineBoxes, textPoseLayoutInput, toRuns, verticalAlignOffset } from '@weasel-js/text';
|
|
17
|
+
export { DEFAULT_TEXT_STYLE, MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, cachedLayoutRuns, createMarkdownRenderer, fontString, layoutMarkdown, layoutRuns, layoutTextPose, markdownToRuns, measureText, measureTextBounds, measuredWidth, resolveAlign, resolveRuns, resolveTextStyle, runsToMarkdown, runsToPlainText, textLineBoxes, textPoseLayoutInput, toRuns, transformRunTexts, verticalAlignOffset } from '@weasel-js/text';
|
|
18
18
|
import earcut from 'earcut';
|
|
19
19
|
import { isAnchorOrControl, isBody, ActionDisabledReason, scratchKey, resolveParams, createDispatcher, DEFAULT_ALLOWED_CAPABILITIES, itemsFromFiles, evaluate, resolveDeviceProfile, isResizeHandle, isRotateHandle, defineViewportTool as defineViewportTool$1, NEVER, recordModeSwitch, DEFAULT_DEVICE_PROFILE, defineTool as defineTool$1, getScratch, openPointerSession, actionBindings, ALWAYS, clientToCanvas, setScratch, clientToCanvasRect, isEditableTarget } from '@weasel-js/routing';
|
|
20
|
-
export { ALWAYS, ActionDisabledReason, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, NEVER, actionBindings, buildRuleCtx, clientToCanvas, createDispatcher, deleteScratch, describeRule, evaluate, evaluateEnabled, getScratch, isEditableTarget, liveScope, matchesKeyBinding, mergeContributions, openPointerSession, resolveDeviceProfile, resolveParams, scopeBindings, scratchKey, setScratch, specificity } from '@weasel-js/routing';
|
|
20
|
+
export { ALWAYS, ActionDisabledReason, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, NEVER, actionBindings, actionItems, buildDepsFromRequires, buildRuleCtx, clientToCanvas, createDispatcher, deleteScratch, describeRule, evaluate, evaluateEnabled, getScratch, isEditableTarget, liveScope, matchesKeyBinding, mergeContributions, openPointerSession, resolveDeviceProfile, resolveParams, scopeBindings, scratchKey, setScratch, specificity } from '@weasel-js/routing';
|
|
21
21
|
import { ActiveToolContextProviderIfRoot, useOptionalActiveToolContext, useTools as useTools$1, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsScope, ActionsProvider, useDepRegistry, useGestureDispatcher, useDepSource, ActiveToolContextProvider } from '@weasel-js/routing/react';
|
|
22
|
-
export { ActionsProvider, ActionsScope, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, DepRegistryProvider, useAction, useActionsRegistry, useActiveToolContext, useDepRegistry, useDepSource, useGestureDispatcher, useOptionalActiveToolContext, useOptionalDepRegistry } from '@weasel-js/routing/react';
|
|
23
|
-
import {
|
|
22
|
+
export { ActionsProvider, ActionsScope, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, DepRegistryProvider, useAction, useActionsRegistry, useActiveToolContext, useDepRegistry, useDepSource, useGestureDispatcher, useOngoingAction, useOptionalActiveToolContext, useOptionalDepRegistry } from '@weasel-js/routing/react';
|
|
23
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
24
24
|
import { pathCrop as pathCrop$1, pathDivide as pathDivide$1, pathUnion as pathUnion$1, pathSubtract as pathSubtract$1, pathExclude as pathExclude$1, pathIntersect as pathIntersect$1 } from '@weasel-js/geom/booleans';
|
|
25
25
|
import { mimeMatchesGlob } from '@weasel-js/gestures';
|
|
26
26
|
import { createPaintedCursorState, GLYPHS, cursorWorldSize, cursorPaintOps, chromeLineWidthScale, cursorPaintMatrix, resolveCursorTier, cursorFor } from '@weasel-js/cursor';
|
|
@@ -29,7 +29,7 @@ import { createHistory } from '@weasel-js/history';
|
|
|
29
29
|
export * from '@weasel-js/history';
|
|
30
30
|
|
|
31
31
|
// src/version.ts
|
|
32
|
-
var VERSION = "1.
|
|
32
|
+
var VERSION = "1.6.0" ;
|
|
33
33
|
|
|
34
34
|
// src/features/grid/roundToCell.ts
|
|
35
35
|
function roundToCell(value, cellSize) {
|
|
@@ -757,6 +757,16 @@ var GLTextureCache = class {
|
|
|
757
757
|
};
|
|
758
758
|
|
|
759
759
|
// src/renderer/cache/GLImageCache.ts
|
|
760
|
+
var DEFAULT_MAX_TEXTURE_SIDE = 4096;
|
|
761
|
+
var live = /* @__PURE__ */ new Set();
|
|
762
|
+
function releaseImageSource(key) {
|
|
763
|
+
for (const cache7 of live) cache7.release(key);
|
|
764
|
+
}
|
|
765
|
+
function maxImageTextureSide() {
|
|
766
|
+
let side = Infinity;
|
|
767
|
+
for (const cache7 of live) side = Math.min(side, cache7.maxTextureSide);
|
|
768
|
+
return Number.isFinite(side) ? side : DEFAULT_MAX_TEXTURE_SIDE;
|
|
769
|
+
}
|
|
760
770
|
var GLImageCache = class {
|
|
761
771
|
/** `minification` selects the MIN_FILTER strategy for uploaded textures.
|
|
762
772
|
* `'linear'` (default) is the screen path's existing behavior. `'mipmap'`
|
|
@@ -767,9 +777,14 @@ var GLImageCache = class {
|
|
|
767
777
|
constructor(gl, minification = "linear") {
|
|
768
778
|
this.gl = gl;
|
|
769
779
|
this.minification = minification;
|
|
780
|
+
const reported = gl.getParameter(gl.MAX_TEXTURE_SIZE);
|
|
781
|
+
this.maxTextureSide = typeof reported === "number" && reported > 0 ? reported : DEFAULT_MAX_TEXTURE_SIDE;
|
|
782
|
+
live.add(this);
|
|
770
783
|
}
|
|
771
784
|
gl;
|
|
772
785
|
minification;
|
|
786
|
+
/** This context's `MAX_TEXTURE_SIZE`. */
|
|
787
|
+
maxTextureSide;
|
|
773
788
|
map = /* @__PURE__ */ new WeakMap();
|
|
774
789
|
/**
|
|
775
790
|
* MAG_FILTER each texture currently carries, so a redundant write can be
|
|
@@ -788,6 +803,19 @@ var GLImageCache = class {
|
|
|
788
803
|
* was redundant either way.
|
|
789
804
|
*/
|
|
790
805
|
magFilters = /* @__PURE__ */ new WeakMap();
|
|
806
|
+
/** Delete `key`'s texture, if it has one. */
|
|
807
|
+
release(key) {
|
|
808
|
+
const tex = this.map.get(key);
|
|
809
|
+
if (!tex) return;
|
|
810
|
+
this.gl.deleteTexture(tex);
|
|
811
|
+
this.map.delete(key);
|
|
812
|
+
this.magFilters.delete(key);
|
|
813
|
+
}
|
|
814
|
+
/** Stop answering {@link releaseImageSource} and {@link maxImageTextureSide}.
|
|
815
|
+
* Call when the context is disposed or lost. */
|
|
816
|
+
dispose() {
|
|
817
|
+
live.delete(this);
|
|
818
|
+
}
|
|
791
819
|
upload(key, source, repetition) {
|
|
792
820
|
const existing = this.map.get(key);
|
|
793
821
|
if (existing) return existing;
|
|
@@ -839,8 +867,6 @@ function wrapModes(gl, rep) {
|
|
|
839
867
|
return [gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE];
|
|
840
868
|
}
|
|
841
869
|
}
|
|
842
|
-
|
|
843
|
-
// src/renderer/math/color.ts
|
|
844
870
|
function parseColor(input) {
|
|
845
871
|
const cached = CACHE.get(input);
|
|
846
872
|
if (cached !== void 0) return cached;
|
|
@@ -1033,21 +1059,7 @@ function parsePercentOrNumber(tok) {
|
|
|
1033
1059
|
const n = Number(tok);
|
|
1034
1060
|
return Number.isFinite(n) ? clamp01(n) : null;
|
|
1035
1061
|
}
|
|
1036
|
-
|
|
1037
|
-
if (s === 0) return [l, l, l];
|
|
1038
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
1039
|
-
const p = 2 * l - q;
|
|
1040
|
-
const hk = h / 360;
|
|
1041
|
-
return [hueToChannel(p, q, hk + 1 / 3), hueToChannel(p, q, hk), hueToChannel(p, q, hk - 1 / 3)];
|
|
1042
|
-
}
|
|
1043
|
-
function hueToChannel(p, q, t) {
|
|
1044
|
-
if (t < 0) t += 1;
|
|
1045
|
-
if (t > 1) t -= 1;
|
|
1046
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
1047
|
-
if (t < 1 / 2) return q;
|
|
1048
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
1049
|
-
return p;
|
|
1050
|
-
}
|
|
1062
|
+
var hslToRgb = hslToSrgb;
|
|
1051
1063
|
function clamp01(n) {
|
|
1052
1064
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
1053
1065
|
}
|
|
@@ -1517,9 +1529,15 @@ function scaled(m, sx, sy) {
|
|
|
1517
1529
|
const s = create(sx, 0, 0, sy, 0, 0);
|
|
1518
1530
|
return multiply(m, s);
|
|
1519
1531
|
}
|
|
1532
|
+
function toAffine(m) {
|
|
1533
|
+
return [m[0], m[1], m[3], m[4], m[6], m[7]];
|
|
1534
|
+
}
|
|
1535
|
+
function fromAffine(a) {
|
|
1536
|
+
return create(a[0], a[1], a[2], a[3], a[4], a[5]);
|
|
1537
|
+
}
|
|
1520
1538
|
function invert(m) {
|
|
1521
|
-
const inv = invert$1(
|
|
1522
|
-
return inv &&
|
|
1539
|
+
const inv = invert$1(toAffine(m));
|
|
1540
|
+
return inv && fromAffine(inv);
|
|
1523
1541
|
}
|
|
1524
1542
|
function apply(m, x, y) {
|
|
1525
1543
|
const a = m[0], b = m[1];
|
|
@@ -1548,6 +1566,8 @@ function meanScaleOf(m) {
|
|
|
1548
1566
|
}
|
|
1549
1567
|
var mat3 = {
|
|
1550
1568
|
identity,
|
|
1569
|
+
toAffine,
|
|
1570
|
+
fromAffine,
|
|
1551
1571
|
multiply,
|
|
1552
1572
|
translated,
|
|
1553
1573
|
scaled,
|
|
@@ -5009,31 +5029,217 @@ var EffectTargets = class {
|
|
|
5009
5029
|
}
|
|
5010
5030
|
};
|
|
5011
5031
|
|
|
5012
|
-
// src/renderer/
|
|
5013
|
-
var
|
|
5032
|
+
// src/renderer/shaders/extractUniformNames.ts
|
|
5033
|
+
var QUALIFIERS = /* @__PURE__ */ new Set([
|
|
5034
|
+
"lowp",
|
|
5035
|
+
"mediump",
|
|
5036
|
+
"highp",
|
|
5037
|
+
"precise",
|
|
5038
|
+
"invariant",
|
|
5039
|
+
"centroid",
|
|
5040
|
+
"flat",
|
|
5041
|
+
"smooth",
|
|
5042
|
+
"noperspective"
|
|
5043
|
+
]);
|
|
5044
|
+
var IDENT = /^[A-Za-z_]\w*$/;
|
|
5045
|
+
var MAX_DEPTH = 16;
|
|
5014
5046
|
function extractUniformNames(glsl) {
|
|
5015
|
-
const
|
|
5016
|
-
const
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
const
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5047
|
+
const { code, defines } = preprocess(glsl);
|
|
5048
|
+
const toks = tokenize2(code);
|
|
5049
|
+
const consts = /* @__PURE__ */ new Map();
|
|
5050
|
+
const structs = /* @__PURE__ */ new Map();
|
|
5051
|
+
const out = /* @__PURE__ */ new Set();
|
|
5052
|
+
const resolveName = (name, depth2) => {
|
|
5053
|
+
if (depth2 > MAX_DEPTH) return null;
|
|
5054
|
+
let best = null;
|
|
5055
|
+
for (const body of defines.get(name) ?? []) {
|
|
5056
|
+
const v = evalInt(tokenize2(body), (n) => resolveName(n, depth2 + 1));
|
|
5057
|
+
if (v !== null && (best === null || v > best)) best = v;
|
|
5058
|
+
}
|
|
5059
|
+
return best ?? consts.get(name) ?? null;
|
|
5060
|
+
};
|
|
5061
|
+
const evalSize = (from, to) => evalInt(toks.slice(from, to), (n) => resolveName(n, 0));
|
|
5062
|
+
const closer = (open) => {
|
|
5063
|
+
let depth2 = 0;
|
|
5064
|
+
for (let k = open; k < toks.length; k++) {
|
|
5065
|
+
if (toks[k] === "(" || toks[k] === "[" || toks[k] === "{") depth2++;
|
|
5066
|
+
else if (toks[k] === ")" || toks[k] === "]" || toks[k] === "}") {
|
|
5067
|
+
if (--depth2 === 0) return k;
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return toks.length;
|
|
5071
|
+
};
|
|
5072
|
+
const arraySuffix = (j) => {
|
|
5073
|
+
if (toks[j] !== "[") return [void 0, j];
|
|
5074
|
+
const end = closer(j);
|
|
5075
|
+
return [evalSize(j + 1, end), end + 1];
|
|
5076
|
+
};
|
|
5077
|
+
const declarators = (j, typeSize) => {
|
|
5078
|
+
const found = [];
|
|
5079
|
+
while (j < toks.length && toks[j] !== ";" && toks[j] !== "}") {
|
|
5080
|
+
const name = toks[j];
|
|
5081
|
+
const [size, next] = arraySuffix(j + 1);
|
|
5082
|
+
j = next;
|
|
5083
|
+
if (IDENT.test(name)) found.push({ name, size: size === void 0 ? typeSize : size });
|
|
5084
|
+
while (j < toks.length && toks[j] !== "," && toks[j] !== ";" && toks[j] !== "}") {
|
|
5085
|
+
j = toks[j] === "(" || toks[j] === "[" || toks[j] === "{" ? closer(j) + 1 : j + 1;
|
|
5086
|
+
}
|
|
5087
|
+
if (toks[j] === ",") j++;
|
|
5088
|
+
}
|
|
5089
|
+
return [found, j];
|
|
5090
|
+
};
|
|
5091
|
+
const structSpecifier = (i) => {
|
|
5092
|
+
let j = i + 1;
|
|
5093
|
+
const name = toks[j] !== "{" ? toks[j++] : void 0;
|
|
5094
|
+
const members = [];
|
|
5095
|
+
if (toks[j] !== "{") return [members, j];
|
|
5096
|
+
const end = closer(j);
|
|
5097
|
+
j++;
|
|
5098
|
+
while (j < end) {
|
|
5099
|
+
while (QUALIFIERS.has(toks[j])) j++;
|
|
5100
|
+
let type;
|
|
5101
|
+
if (toks[j] === "struct") [type, j] = structSpecifier(j);
|
|
5102
|
+
else type = toks[j++];
|
|
5103
|
+
const [typeSize, afterType] = arraySuffix(j);
|
|
5104
|
+
const [decls, term] = declarators(afterType, typeSize);
|
|
5105
|
+
for (const d of decls) if (d.size !== null) members.push({ type, name: d.name, size: d.size ?? null });
|
|
5106
|
+
j = term + 1;
|
|
5107
|
+
}
|
|
5108
|
+
if (name) {
|
|
5109
|
+
const prior = structs.get(name) ?? [];
|
|
5110
|
+
for (const m of members) if (!prior.some((p) => p.name === m.name)) prior.push(m);
|
|
5111
|
+
structs.set(name, prior);
|
|
5112
|
+
}
|
|
5113
|
+
return [members, end + 1];
|
|
5114
|
+
};
|
|
5115
|
+
const expand = (name, type, size, depth2) => {
|
|
5116
|
+
if (depth2 > MAX_DEPTH) return;
|
|
5117
|
+
if (size !== null) {
|
|
5118
|
+
for (let k = 0; k < size; k++) expand(`${name}[${k}]`, type, null, depth2 + 1);
|
|
5119
|
+
return;
|
|
5120
|
+
}
|
|
5121
|
+
const members = typeof type === "string" ? structs.get(type) : type;
|
|
5122
|
+
if (!members) {
|
|
5123
|
+
out.add(name);
|
|
5124
|
+
return;
|
|
5125
|
+
}
|
|
5126
|
+
for (const m of members) expand(`${name}.${m.name}`, m.type, m.size, depth2 + 1);
|
|
5127
|
+
};
|
|
5128
|
+
let depth = 0;
|
|
5129
|
+
for (let i = 0; i < toks.length; i++) {
|
|
5130
|
+
const t = toks[i];
|
|
5131
|
+
if (t === "{") depth++;
|
|
5132
|
+
else if (t === "}") depth--;
|
|
5133
|
+
else if (t === "struct") i = structSpecifier(i)[1] - 1;
|
|
5134
|
+
else if (depth === 0 && t === "const") {
|
|
5135
|
+
let j = i + 1;
|
|
5136
|
+
while (QUALIFIERS.has(toks[j])) j++;
|
|
5137
|
+
if ((toks[j] === "int" || toks[j] === "uint") && IDENT.test(toks[j + 1] ?? "") && toks[j + 2] === "=") {
|
|
5138
|
+
let end = j + 3;
|
|
5139
|
+
while (end < toks.length && toks[end] !== ";") end++;
|
|
5140
|
+
const v = evalSize(j + 3, end);
|
|
5141
|
+
const prior = consts.get(toks[j + 1]);
|
|
5142
|
+
if (v !== null && (prior === void 0 || v > prior)) consts.set(toks[j + 1], v);
|
|
5143
|
+
}
|
|
5144
|
+
} else if (depth === 0 && t === "uniform") {
|
|
5145
|
+
let j = i + 1;
|
|
5146
|
+
while (QUALIFIERS.has(toks[j])) j++;
|
|
5147
|
+
let type;
|
|
5148
|
+
if (toks[j] === "struct") {
|
|
5149
|
+
[type, j] = structSpecifier(j);
|
|
5150
|
+
} else if (toks[j + 1] === "{") {
|
|
5151
|
+
j = closer(j + 1) + 1;
|
|
5152
|
+
while (j < toks.length && toks[j] !== ";") j++;
|
|
5153
|
+
i = j;
|
|
5154
|
+
continue;
|
|
5155
|
+
} else {
|
|
5156
|
+
type = toks[j++];
|
|
5157
|
+
}
|
|
5158
|
+
const [typeSize, afterType] = arraySuffix(j);
|
|
5159
|
+
const [decls, term] = declarators(afterType, typeSize);
|
|
5160
|
+
for (const d of decls) if (d.size !== null) expand(d.name, type, d.size ?? null, 0);
|
|
5161
|
+
i = term;
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
return [...out];
|
|
5165
|
+
}
|
|
5166
|
+
function preprocess(glsl) {
|
|
5167
|
+
const spliced = glsl.replace(/\\\r?\n/g, "");
|
|
5168
|
+
let stripped = "";
|
|
5169
|
+
for (let k = 0; k < spliced.length; k++) {
|
|
5170
|
+
if (spliced[k] === "/" && spliced[k + 1] === "/") {
|
|
5171
|
+
while (k < spliced.length && spliced[k] !== "\n") k++;
|
|
5172
|
+
stripped += "\n";
|
|
5173
|
+
} else if (spliced[k] === "/" && spliced[k + 1] === "*") {
|
|
5174
|
+
const end = spliced.indexOf("*/", k + 2);
|
|
5175
|
+
k = end < 0 ? spliced.length : end + 1;
|
|
5176
|
+
stripped += " ";
|
|
5177
|
+
} else {
|
|
5178
|
+
stripped += spliced[k];
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
const defines = /* @__PURE__ */ new Map();
|
|
5182
|
+
const lines = stripped.split("\n").map((line) => {
|
|
5183
|
+
if (!/^\s*#/.test(line)) return line;
|
|
5184
|
+
const def = /^\s*#\s*define\s+([A-Za-z_]\w*)(\(?)(.*)$/.exec(line);
|
|
5185
|
+
if (def && !def[2]) defines.set(def[1], [...defines.get(def[1]) ?? [], def[3].trim()]);
|
|
5186
|
+
return "";
|
|
5187
|
+
});
|
|
5188
|
+
return { code: lines.join("\n"), defines };
|
|
5189
|
+
}
|
|
5190
|
+
function tokenize2(src) {
|
|
5191
|
+
return src.match(/[A-Za-z_]\w*|\d[\w.]*|\S/g) ?? [];
|
|
5192
|
+
}
|
|
5193
|
+
function intLiteral(tok) {
|
|
5194
|
+
const m = /^(?:0[xX]([0-9a-fA-F]+)|(0[0-7]*)|([1-9]\d*))[uU]?$/.exec(tok);
|
|
5195
|
+
if (!m) return null;
|
|
5196
|
+
if (m[1] !== void 0) return parseInt(m[1], 16);
|
|
5197
|
+
if (m[2] !== void 0) return parseInt(m[2], 8);
|
|
5198
|
+
return parseInt(m[3], 10);
|
|
5199
|
+
}
|
|
5200
|
+
function evalInt(toks, lookup) {
|
|
5201
|
+
let p = 0;
|
|
5202
|
+
const primary = () => {
|
|
5203
|
+
const t = toks[p++];
|
|
5204
|
+
if (t === "(") {
|
|
5205
|
+
const v2 = sum();
|
|
5206
|
+
return toks[p++] === ")" ? v2 : null;
|
|
5207
|
+
}
|
|
5208
|
+
if (t === "-") {
|
|
5209
|
+
const v2 = primary();
|
|
5210
|
+
return v2 === null ? null : -v2;
|
|
5211
|
+
}
|
|
5212
|
+
if (t === "+") return primary();
|
|
5213
|
+
if (t === void 0) return null;
|
|
5214
|
+
return intLiteral(t) ?? (IDENT.test(t) ? lookup(t) : null);
|
|
5215
|
+
};
|
|
5216
|
+
const product = () => {
|
|
5217
|
+
let v2 = primary();
|
|
5218
|
+
while (v2 !== null && (toks[p] === "*" || toks[p] === "/" || toks[p] === "%")) {
|
|
5219
|
+
const op = toks[p++];
|
|
5220
|
+
const r = primary();
|
|
5221
|
+
if (r === null || op !== "*" && r === 0) return null;
|
|
5222
|
+
v2 = op === "*" ? v2 * r : op === "/" ? Math.trunc(v2 / r) : v2 % r;
|
|
5223
|
+
}
|
|
5224
|
+
return v2;
|
|
5225
|
+
};
|
|
5226
|
+
const sum = () => {
|
|
5227
|
+
let v2 = product();
|
|
5228
|
+
while (v2 !== null && (toks[p] === "+" || toks[p] === "-")) {
|
|
5229
|
+
const op = toks[p++];
|
|
5230
|
+
const r = product();
|
|
5231
|
+
if (r === null) return null;
|
|
5232
|
+
v2 = op === "+" ? v2 + r : v2 - r;
|
|
5233
|
+
}
|
|
5234
|
+
return v2;
|
|
5235
|
+
};
|
|
5236
|
+
const v = sum();
|
|
5237
|
+
return p === toks.length && v !== null && v >= 0 ? v : null;
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5240
|
+
// src/renderer/WeaselRenderer.ts
|
|
5241
|
+
function customUniformNames(vertSrc, fragSrc) {
|
|
5242
|
+
return [...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(vertSrc), ...extractUniformNames(fragSrc)];
|
|
5037
5243
|
}
|
|
5038
5244
|
var WeaselRenderer = class {
|
|
5039
5245
|
gl;
|
|
@@ -5170,11 +5376,7 @@ var WeaselRenderer = class {
|
|
|
5170
5376
|
const vertSrc = src.vert === "" ? CUSTOM_VERT_SRC : src.vert;
|
|
5171
5377
|
const fragSrc = src.frag;
|
|
5172
5378
|
const program = new ShaderProgram(this.gl, vertSrc, fragSrc);
|
|
5173
|
-
program.lookupUniforms(
|
|
5174
|
-
...CUSTOM_KIT_UNIFORMS,
|
|
5175
|
-
...extractUniformNames(vertSrc),
|
|
5176
|
-
...extractUniformNames(fragSrc)
|
|
5177
|
-
]);
|
|
5379
|
+
program.lookupUniforms(customUniformNames(vertSrc, fragSrc));
|
|
5178
5380
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
5179
5381
|
const previous = this.programRegistry.get(handle.id);
|
|
5180
5382
|
if (previous) this.gl.deleteProgram(previous.handle);
|
|
@@ -5269,6 +5471,7 @@ var WeaselRenderer = class {
|
|
|
5269
5471
|
if (aPos === void 0) throw new Error("a_position missing after restore");
|
|
5270
5472
|
this.meshCache = new GLMeshCache(this.gl, aPos);
|
|
5271
5473
|
this.textureCache = new GLTextureCache(this.gl);
|
|
5474
|
+
this.imageCache.dispose();
|
|
5272
5475
|
this.imageCache = new GLImageCache(this.gl, this.imageMinification);
|
|
5273
5476
|
this.gradRamps = new GradientRampAtlas(this.gl);
|
|
5274
5477
|
markAllFontsNotUploaded();
|
|
@@ -5281,7 +5484,7 @@ var WeaselRenderer = class {
|
|
|
5281
5484
|
const vertSrc = src.vert === "" ? CUSTOM_VERT_SRC : src.vert;
|
|
5282
5485
|
try {
|
|
5283
5486
|
const program = new ShaderProgram(this.gl, vertSrc, src.frag);
|
|
5284
|
-
program.lookupUniforms(
|
|
5487
|
+
program.lookupUniforms(customUniformNames(vertSrc, src.frag));
|
|
5285
5488
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
5286
5489
|
this.programRegistry.set(id, program);
|
|
5287
5490
|
} catch (e) {
|
|
@@ -5331,6 +5534,7 @@ var WeaselRenderer = class {
|
|
|
5331
5534
|
}
|
|
5332
5535
|
this.programRegistry.clear();
|
|
5333
5536
|
this.textureCache.free();
|
|
5537
|
+
this.imageCache.dispose();
|
|
5334
5538
|
this.gradRamps.free();
|
|
5335
5539
|
if (this.quadVbo) gl.deleteBuffer(this.quadVbo);
|
|
5336
5540
|
if (this.quadIbo) gl.deleteBuffer(this.quadIbo);
|
|
@@ -5835,32 +6039,32 @@ function drawLayers(layers, data, visibility, order, view, dims, cache7, onLayer
|
|
|
5835
6039
|
}
|
|
5836
6040
|
const groupOf = groupMembership(groups, layerById.keys());
|
|
5837
6041
|
const bracketed = /* @__PURE__ */ new Set();
|
|
5838
|
-
let
|
|
6042
|
+
let run2 = null;
|
|
5839
6043
|
const closeRun = () => {
|
|
5840
|
-
if (!
|
|
5841
|
-
for (const c of wrapGroup(
|
|
5842
|
-
|
|
6044
|
+
if (!run2) return;
|
|
6045
|
+
for (const c of wrapGroup(run2.group, run2.children, v, dims)) out.push(c);
|
|
6046
|
+
run2 = null;
|
|
5843
6047
|
};
|
|
5844
6048
|
for (const layer of sequence) {
|
|
5845
6049
|
if (!isLayerVisible(layer, visibility)) continue;
|
|
5846
6050
|
const cmds = drawOneLayer(layer, data, v, dims, cache7, onLayerError);
|
|
5847
6051
|
if (cmds.length === 0) continue;
|
|
5848
6052
|
const group = groupOf?.get(layer.id);
|
|
5849
|
-
if (
|
|
6053
|
+
if (run2 && run2.group !== group) closeRun();
|
|
5850
6054
|
if (!group) {
|
|
5851
6055
|
for (const c of cmds) out.push(c);
|
|
5852
6056
|
continue;
|
|
5853
6057
|
}
|
|
5854
|
-
if (!
|
|
6058
|
+
if (!run2) {
|
|
5855
6059
|
if (bracketed.has(group)) {
|
|
5856
6060
|
console.warn(
|
|
5857
6061
|
`[weasel] layer group "${group.id}" is not one run \u2014 another layer is drawn between its members, so it composites as more than one pass over more than one buffer. Move its members together in the layer order.`
|
|
5858
6062
|
);
|
|
5859
6063
|
}
|
|
5860
6064
|
bracketed.add(group);
|
|
5861
|
-
|
|
6065
|
+
run2 = { group, children: [] };
|
|
5862
6066
|
}
|
|
5863
|
-
for (const c of cmds)
|
|
6067
|
+
for (const c of cmds) run2.children.push(c);
|
|
5864
6068
|
}
|
|
5865
6069
|
closeRun();
|
|
5866
6070
|
return out;
|
|
@@ -6656,7 +6860,7 @@ function composeWorldPose(adapter, id, compose2) {
|
|
|
6656
6860
|
while (cursor !== null) {
|
|
6657
6861
|
if (seen.has(cursor)) break;
|
|
6658
6862
|
seen.add(cursor);
|
|
6659
|
-
chain.push(cursor);
|
|
6863
|
+
if (adapter.definesFrame?.(cursor) ?? true) chain.push(cursor);
|
|
6660
6864
|
cursor = adapter.getParent(cursor);
|
|
6661
6865
|
}
|
|
6662
6866
|
let world = adapter.getPose(chain[chain.length - 1]);
|
|
@@ -6676,9 +6880,19 @@ function translateRectPose(pose, dx, dy) {
|
|
|
6676
6880
|
return { ...pose, x: pose.x + dx, y: pose.y + dy };
|
|
6677
6881
|
}
|
|
6678
6882
|
function rebaseLocalPose(adapter, worldPose, newParentId, compose2, decompose) {
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
return decompose(
|
|
6883
|
+
const frame = frameAtOrAbove(adapter, newParentId);
|
|
6884
|
+
if (frame === null) return worldPose;
|
|
6885
|
+
return decompose(composeWorldPose(adapter, frame, compose2), worldPose);
|
|
6886
|
+
}
|
|
6887
|
+
function frameAtOrAbove(adapter, id) {
|
|
6888
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6889
|
+
let cursor = id;
|
|
6890
|
+
while (cursor !== null && !seen.has(cursor)) {
|
|
6891
|
+
if (adapter.definesFrame?.(cursor) ?? true) return cursor;
|
|
6892
|
+
seen.add(cursor);
|
|
6893
|
+
cursor = adapter.getParent(cursor);
|
|
6894
|
+
}
|
|
6895
|
+
return null;
|
|
6682
6896
|
}
|
|
6683
6897
|
function decomposeRectPose(parent, world) {
|
|
6684
6898
|
return {
|
|
@@ -6744,6 +6958,88 @@ function worldPoseLookup(adapter, compose2) {
|
|
|
6744
6958
|
};
|
|
6745
6959
|
}
|
|
6746
6960
|
|
|
6961
|
+
// src/core/scene/poseSnapshot.ts
|
|
6962
|
+
var DEPTH = 4;
|
|
6963
|
+
function isPlainObject(v) {
|
|
6964
|
+
if (typeof v !== "object" || v === null) return false;
|
|
6965
|
+
const proto = Object.getPrototypeOf(v);
|
|
6966
|
+
return proto === Object.prototype || proto === null;
|
|
6967
|
+
}
|
|
6968
|
+
function isTypedArray(v) {
|
|
6969
|
+
return ArrayBuffer.isView(v) && !(v instanceof DataView);
|
|
6970
|
+
}
|
|
6971
|
+
function snapshotPose(value, depth = DEPTH) {
|
|
6972
|
+
if (depth <= 0) return value;
|
|
6973
|
+
if (Array.isArray(value)) {
|
|
6974
|
+
return value.map((v) => snapshotPose(v, depth - 1));
|
|
6975
|
+
}
|
|
6976
|
+
if (isTypedArray(value)) return value.slice();
|
|
6977
|
+
if (isPlainObject(value)) {
|
|
6978
|
+
const out = { ...value };
|
|
6979
|
+
for (const key in out) {
|
|
6980
|
+
const field = out[key];
|
|
6981
|
+
if (typeof field === "object" && field !== null) out[key] = snapshotPose(field, depth - 1);
|
|
6982
|
+
}
|
|
6983
|
+
return out;
|
|
6984
|
+
}
|
|
6985
|
+
return value;
|
|
6986
|
+
}
|
|
6987
|
+
function samePoseValue(snapshot, value, depth = DEPTH) {
|
|
6988
|
+
if (snapshot === value) return true;
|
|
6989
|
+
if (typeof snapshot !== "object" || snapshot === null || typeof value !== "object" || value === null) return Object.is(snapshot, value);
|
|
6990
|
+
if (depth <= 0) return false;
|
|
6991
|
+
const proto = Object.getPrototypeOf(snapshot);
|
|
6992
|
+
if (proto === Object.prototype || proto === null) {
|
|
6993
|
+
const other = Object.getPrototypeOf(value);
|
|
6994
|
+
if (other !== Object.prototype && other !== null) return false;
|
|
6995
|
+
const a = snapshot;
|
|
6996
|
+
const b = value;
|
|
6997
|
+
let count = 0;
|
|
6998
|
+
for (const key in a) {
|
|
6999
|
+
count++;
|
|
7000
|
+
const left = a[key];
|
|
7001
|
+
const right = b[key];
|
|
7002
|
+
if (left === right) {
|
|
7003
|
+
if (right === void 0 && !(key in b)) return false;
|
|
7004
|
+
continue;
|
|
7005
|
+
}
|
|
7006
|
+
if (!samePoseValue(left, right, depth - 1)) return false;
|
|
7007
|
+
}
|
|
7008
|
+
for (const _key in b) if (--count < 0) return false;
|
|
7009
|
+
return true;
|
|
7010
|
+
}
|
|
7011
|
+
if (Array.isArray(snapshot) || isTypedArray(snapshot)) {
|
|
7012
|
+
const a = snapshot;
|
|
7013
|
+
if (!(Array.isArray(value) || isTypedArray(value))) return false;
|
|
7014
|
+
const b = value;
|
|
7015
|
+
if (a.length !== b.length) return false;
|
|
7016
|
+
for (let i = 0; i < a.length; i++) {
|
|
7017
|
+
if (!samePoseValue(a[i], b[i], depth - 1)) return false;
|
|
7018
|
+
}
|
|
7019
|
+
return true;
|
|
7020
|
+
}
|
|
7021
|
+
return false;
|
|
7022
|
+
}
|
|
7023
|
+
|
|
7024
|
+
// src/core/scene/depMemo.ts
|
|
7025
|
+
function recordDeps(into, deps) {
|
|
7026
|
+
into.nodes.length = 0;
|
|
7027
|
+
into.poses.length = 0;
|
|
7028
|
+
for (const dep of deps) {
|
|
7029
|
+
into.nodes.push(dep?.node);
|
|
7030
|
+
into.poses.push(dep === void 0 ? void 0 : snapshotPose(dep.pose));
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
function sameDeps2(record, ids, depOf2) {
|
|
7034
|
+
if (record.nodes.length !== ids.length) return false;
|
|
7035
|
+
for (let i = 0; i < ids.length; i++) {
|
|
7036
|
+
const dep = depOf2(ids[i]);
|
|
7037
|
+
if (record.nodes[i] !== (dep === void 0 ? void 0 : dep.node)) return false;
|
|
7038
|
+
if (dep !== void 0 && !samePoseValue(record.poses[i], dep.pose)) return false;
|
|
7039
|
+
}
|
|
7040
|
+
return true;
|
|
7041
|
+
}
|
|
7042
|
+
|
|
6747
7043
|
// src/core/scene/dependents.ts
|
|
6748
7044
|
var NONE = Object.freeze([]);
|
|
6749
7045
|
function dependencyIdsOf(node, childrenOf) {
|
|
@@ -6817,69 +7113,6 @@ function sameDependsOn(a, b) {
|
|
|
6817
7113
|
return true;
|
|
6818
7114
|
}
|
|
6819
7115
|
|
|
6820
|
-
// src/core/scene/poseSnapshot.ts
|
|
6821
|
-
var DEPTH = 4;
|
|
6822
|
-
function isPlainObject(v) {
|
|
6823
|
-
if (typeof v !== "object" || v === null) return false;
|
|
6824
|
-
const proto = Object.getPrototypeOf(v);
|
|
6825
|
-
return proto === Object.prototype || proto === null;
|
|
6826
|
-
}
|
|
6827
|
-
function isTypedArray(v) {
|
|
6828
|
-
return ArrayBuffer.isView(v) && !(v instanceof DataView);
|
|
6829
|
-
}
|
|
6830
|
-
function snapshotPose(value, depth = DEPTH) {
|
|
6831
|
-
if (depth <= 0) return value;
|
|
6832
|
-
if (Array.isArray(value)) {
|
|
6833
|
-
return value.map((v) => snapshotPose(v, depth - 1));
|
|
6834
|
-
}
|
|
6835
|
-
if (isTypedArray(value)) return value.slice();
|
|
6836
|
-
if (isPlainObject(value)) {
|
|
6837
|
-
const out = { ...value };
|
|
6838
|
-
for (const key in out) {
|
|
6839
|
-
const field = out[key];
|
|
6840
|
-
if (typeof field === "object" && field !== null) out[key] = snapshotPose(field, depth - 1);
|
|
6841
|
-
}
|
|
6842
|
-
return out;
|
|
6843
|
-
}
|
|
6844
|
-
return value;
|
|
6845
|
-
}
|
|
6846
|
-
function samePoseValue(snapshot, value, depth = DEPTH) {
|
|
6847
|
-
if (snapshot === value) return true;
|
|
6848
|
-
if (typeof snapshot !== "object" || snapshot === null || typeof value !== "object" || value === null) return Object.is(snapshot, value);
|
|
6849
|
-
if (depth <= 0) return false;
|
|
6850
|
-
const proto = Object.getPrototypeOf(snapshot);
|
|
6851
|
-
if (proto === Object.prototype || proto === null) {
|
|
6852
|
-
const other = Object.getPrototypeOf(value);
|
|
6853
|
-
if (other !== Object.prototype && other !== null) return false;
|
|
6854
|
-
const a = snapshot;
|
|
6855
|
-
const b = value;
|
|
6856
|
-
let count = 0;
|
|
6857
|
-
for (const key in a) {
|
|
6858
|
-
count++;
|
|
6859
|
-
const left = a[key];
|
|
6860
|
-
const right = b[key];
|
|
6861
|
-
if (left === right) {
|
|
6862
|
-
if (right === void 0 && !(key in b)) return false;
|
|
6863
|
-
continue;
|
|
6864
|
-
}
|
|
6865
|
-
if (!samePoseValue(left, right, depth - 1)) return false;
|
|
6866
|
-
}
|
|
6867
|
-
for (const _key in b) if (--count < 0) return false;
|
|
6868
|
-
return true;
|
|
6869
|
-
}
|
|
6870
|
-
if (Array.isArray(snapshot) || isTypedArray(snapshot)) {
|
|
6871
|
-
const a = snapshot;
|
|
6872
|
-
if (!(Array.isArray(value) || isTypedArray(value))) return false;
|
|
6873
|
-
const b = value;
|
|
6874
|
-
if (a.length !== b.length) return false;
|
|
6875
|
-
for (let i = 0; i < a.length; i++) {
|
|
6876
|
-
if (!samePoseValue(a[i], b[i], depth - 1)) return false;
|
|
6877
|
-
}
|
|
6878
|
-
return true;
|
|
6879
|
-
}
|
|
6880
|
-
return false;
|
|
6881
|
-
}
|
|
6882
|
-
|
|
6883
7116
|
// src/core/scene/derivedPath.ts
|
|
6884
7117
|
var SLOT = "kit:derivedPath";
|
|
6885
7118
|
var resolving = /* @__PURE__ */ new Set();
|
|
@@ -6901,7 +7134,7 @@ function resolveDerivedPath(node, depOf2, childrenOf, memoSlot = SLOT) {
|
|
|
6901
7134
|
computed = true;
|
|
6902
7135
|
return draw(node, derivePath, ids.map(depOf2), { nodes: [], poses: [], path: null });
|
|
6903
7136
|
});
|
|
6904
|
-
if (!computed && !
|
|
7137
|
+
if (!computed && !sameDeps2(record, ids, depOf2)) {
|
|
6905
7138
|
draw(node, derivePath, ids.map(depOf2), record);
|
|
6906
7139
|
}
|
|
6907
7140
|
if (cycleHits !== hitsBefore) dropPoseKeyedMemoSlots(node);
|
|
@@ -6912,23 +7145,9 @@ function resolveDerivedPath(node, depOf2, childrenOf, memoSlot = SLOT) {
|
|
|
6912
7145
|
}
|
|
6913
7146
|
function draw(node, derivePath, deps, into) {
|
|
6914
7147
|
into.path = derivePath(node, deps);
|
|
6915
|
-
into
|
|
6916
|
-
into.poses.length = 0;
|
|
6917
|
-
for (const dep of deps) {
|
|
6918
|
-
into.nodes.push(dep?.node);
|
|
6919
|
-
into.poses.push(dep === void 0 ? void 0 : snapshotPose(dep.pose));
|
|
6920
|
-
}
|
|
7148
|
+
recordDeps(into, deps);
|
|
6921
7149
|
return into;
|
|
6922
7150
|
}
|
|
6923
|
-
function matches(record, ids, depOf2) {
|
|
6924
|
-
if (record.nodes.length !== ids.length) return false;
|
|
6925
|
-
for (let i = 0; i < ids.length; i++) {
|
|
6926
|
-
const dep = depOf2(ids[i]);
|
|
6927
|
-
if (record.nodes[i] !== (dep === void 0 ? void 0 : dep.node)) return false;
|
|
6928
|
-
if (dep !== void 0 && !samePoseValue(record.poses[i], dep.pose)) return false;
|
|
6929
|
-
}
|
|
6930
|
-
return true;
|
|
6931
|
-
}
|
|
6932
7151
|
|
|
6933
7152
|
// src/core/scene/effectivePose.ts
|
|
6934
7153
|
var POSE_SLOT = {
|
|
@@ -6956,18 +7175,26 @@ function derivedPoseIn(source, node, reading) {
|
|
|
6956
7175
|
resolving2.add(node.id);
|
|
6957
7176
|
const hitsBefore = cycleHits2;
|
|
6958
7177
|
try {
|
|
6959
|
-
const
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
);
|
|
7178
|
+
const depOf2 = (id) => depIn(source, id, reading);
|
|
7179
|
+
let computed = false;
|
|
7180
|
+
const record = nodeMemo(node, POSE_SLOT[reading], node.pose, () => {
|
|
7181
|
+
computed = true;
|
|
7182
|
+
return run(node, derive, ids.map(depOf2), { nodes: [], poses: [], value: null });
|
|
7183
|
+
});
|
|
7184
|
+
if (!computed && !sameDeps2(record, ids, depOf2)) {
|
|
7185
|
+
run(node, derive, ids.map(depOf2), record);
|
|
7186
|
+
}
|
|
6965
7187
|
if (cycleHits2 !== hitsBefore) dropPoseKeyedMemoSlots(node);
|
|
6966
|
-
return value;
|
|
7188
|
+
return record.value;
|
|
6967
7189
|
} finally {
|
|
6968
7190
|
resolving2.delete(node.id);
|
|
6969
7191
|
}
|
|
6970
7192
|
}
|
|
7193
|
+
function run(node, derive, deps, into) {
|
|
7194
|
+
into.value = derive(node, deps);
|
|
7195
|
+
recordDeps(into, deps);
|
|
7196
|
+
return into;
|
|
7197
|
+
}
|
|
6971
7198
|
function derivedDepOf(source, id) {
|
|
6972
7199
|
return depIn(source, id, "effective");
|
|
6973
7200
|
}
|
|
@@ -7012,6 +7239,9 @@ function documentPose(source, node) {
|
|
|
7012
7239
|
function effectivePose(source, node) {
|
|
7013
7240
|
return poseIn(source, node, "effective");
|
|
7014
7241
|
}
|
|
7242
|
+
function definesFrame(node) {
|
|
7243
|
+
return !(node.dependsOn === "children" && node.derivePose !== void 0);
|
|
7244
|
+
}
|
|
7015
7245
|
function poseRotationOf(pose) {
|
|
7016
7246
|
const p = pose;
|
|
7017
7247
|
if (p.rotation && p.x != null && p.y != null && p.width != null && p.height != null) {
|
|
@@ -7222,14 +7452,14 @@ function defaultRectEl(target) {
|
|
|
7222
7452
|
}
|
|
7223
7453
|
function useHandleDrag(opts) {
|
|
7224
7454
|
const { onMove, onStart, onEnd, onCancel, getRect } = opts;
|
|
7225
|
-
const
|
|
7455
|
+
const live2 = useRef(null);
|
|
7226
7456
|
useEffect(() => () => {
|
|
7227
|
-
|
|
7457
|
+
live2.current?.cancel();
|
|
7228
7458
|
}, []);
|
|
7229
7459
|
const onPointerDown = useCallback(
|
|
7230
7460
|
(e) => {
|
|
7231
7461
|
e.preventDefault();
|
|
7232
|
-
|
|
7462
|
+
live2.current?.cancel();
|
|
7233
7463
|
const target = e.currentTarget;
|
|
7234
7464
|
const rectEl = getRect ? getRect(target) : defaultRectEl(target);
|
|
7235
7465
|
const rect = rectEl.getBoundingClientRect();
|
|
@@ -7238,17 +7468,17 @@ function useHandleDrag(opts) {
|
|
|
7238
7468
|
return { x, y };
|
|
7239
7469
|
};
|
|
7240
7470
|
let moved = false;
|
|
7241
|
-
|
|
7471
|
+
live2.current = openPointerSession(target, e, {
|
|
7242
7472
|
onMove: (ev) => {
|
|
7243
7473
|
moved = true;
|
|
7244
7474
|
onMove(localOf(ev.clientX, ev.clientY), ev);
|
|
7245
7475
|
},
|
|
7246
7476
|
onEnd: (ev) => {
|
|
7247
|
-
|
|
7477
|
+
live2.current = null;
|
|
7248
7478
|
onEnd?.({ point: localOf(ev.clientX, ev.clientY), moved, event: ev });
|
|
7249
7479
|
},
|
|
7250
7480
|
onCancel: (reason) => {
|
|
7251
|
-
|
|
7481
|
+
live2.current = null;
|
|
7252
7482
|
onCancel?.(reason);
|
|
7253
7483
|
}
|
|
7254
7484
|
});
|
|
@@ -7354,9 +7584,9 @@ var DRAG_THRESHOLD_PX_SQ = 25;
|
|
|
7354
7584
|
function useDragHandle(getPayload, options) {
|
|
7355
7585
|
const optsRef = useRef(options);
|
|
7356
7586
|
optsRef.current = options;
|
|
7357
|
-
const
|
|
7587
|
+
const live2 = useRef(null);
|
|
7358
7588
|
useEffect(() => () => {
|
|
7359
|
-
|
|
7589
|
+
live2.current?.cancel();
|
|
7360
7590
|
}, []);
|
|
7361
7591
|
const onPointerDown = useCallback((e) => {
|
|
7362
7592
|
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
@@ -7366,7 +7596,7 @@ function useDragHandle(getPayload, options) {
|
|
|
7366
7596
|
const startY = e.clientY;
|
|
7367
7597
|
let ghost = null;
|
|
7368
7598
|
let past = false;
|
|
7369
|
-
|
|
7599
|
+
live2.current = openPointerSession(target, e, {
|
|
7370
7600
|
onMove: (ev) => {
|
|
7371
7601
|
if (ghost) {
|
|
7372
7602
|
ghost.move(ev);
|
|
@@ -7396,11 +7626,11 @@ function useDragHandle(getPayload, options) {
|
|
|
7396
7626
|
);
|
|
7397
7627
|
},
|
|
7398
7628
|
onEnd: (ev) => {
|
|
7399
|
-
|
|
7629
|
+
live2.current = null;
|
|
7400
7630
|
ghost?.drop(ev);
|
|
7401
7631
|
},
|
|
7402
7632
|
onCancel: () => {
|
|
7403
|
-
|
|
7633
|
+
live2.current = null;
|
|
7404
7634
|
ghost?.cancel();
|
|
7405
7635
|
}
|
|
7406
7636
|
}, { capture: false });
|
|
@@ -7763,10 +7993,11 @@ function keySpecShortcut(spec) {
|
|
|
7763
7993
|
shift: mods?.shift === true
|
|
7764
7994
|
};
|
|
7765
7995
|
}
|
|
7766
|
-
function actionShortcuts(action) {
|
|
7996
|
+
function actionShortcuts(action, params) {
|
|
7767
7997
|
const out = [];
|
|
7768
7998
|
const seen = /* @__PURE__ */ new Set();
|
|
7769
|
-
for (const { spec } of actionBindings(action)) {
|
|
7999
|
+
for (const { spec, opts } of actionBindings(action)) {
|
|
8000
|
+
if (params && !paramsMatch(opts?.params, params)) continue;
|
|
7770
8001
|
const shortcut = keySpecShortcut(spec);
|
|
7771
8002
|
if (!shortcut) continue;
|
|
7772
8003
|
const token = `${shortcut.key}|${shortcut.mod}|${shortcut.alt}|${shortcut.shift}`;
|
|
@@ -7776,6 +8007,10 @@ function actionShortcuts(action) {
|
|
|
7776
8007
|
}
|
|
7777
8008
|
return out;
|
|
7778
8009
|
}
|
|
8010
|
+
function paramsMatch(bound, wanted) {
|
|
8011
|
+
if (typeof bound !== "object" || bound === null) return false;
|
|
8012
|
+
return Object.entries(wanted).every(([k, v]) => bound[k] === v);
|
|
8013
|
+
}
|
|
7779
8014
|
|
|
7780
8015
|
// src/core/scene/types.ts
|
|
7781
8016
|
var asNodeId = (s) => s;
|
|
@@ -7975,7 +8210,11 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
7975
8210
|
y: moveCtx.drag.current.y - selectionCenter.y
|
|
7976
8211
|
};
|
|
7977
8212
|
const candidates = [];
|
|
7978
|
-
const
|
|
8213
|
+
const mode = layoutDep.dropTarget ?? "innermost";
|
|
8214
|
+
const testInside = (id, cPose, aabb, layout2) => {
|
|
8215
|
+
const region = layout2.dropRegion?.({ id, bounds: aabb }) ?? null;
|
|
8216
|
+
if (region) return regionContains(region, aabb, selectionCenter);
|
|
8217
|
+
if (mode === "region") return false;
|
|
7979
8218
|
if (layout2.contains) return layout2.contains(cPose, selectionCenter);
|
|
7980
8219
|
return selectionCenter.x >= aabb.x && selectionCenter.x < aabb.x + aabb.width && selectionCenter.y >= aabb.y && selectionCenter.y < aabb.y + aabb.height;
|
|
7981
8220
|
};
|
|
@@ -7987,7 +8226,7 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
7987
8226
|
if (!node) return;
|
|
7988
8227
|
const worldPose = composeWorldPose(poseAdapter, id, pc.compose);
|
|
7989
8228
|
const worldAABB = poseDescriptorForNode(d, node).getBounds(worldPose);
|
|
7990
|
-
if (!testInside(worldPose, worldAABB, layout2)) return;
|
|
8229
|
+
if (!testInside(id, worldPose, worldAABB, layout2)) return;
|
|
7991
8230
|
if (layout2.acceptsDrop) {
|
|
7992
8231
|
const arg = { id, bounds: worldAABB };
|
|
7993
8232
|
for (const c of dragged) if (!layout2.acceptsDrop(arg, c.arg)) return;
|
|
@@ -8010,28 +8249,33 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8010
8249
|
};
|
|
8011
8250
|
walk(null, []);
|
|
8012
8251
|
let dest = null;
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
}
|
|
8022
|
-
if (c.depth < dest.depth) continue;
|
|
8023
|
-
let cAfter = false;
|
|
8024
|
-
for (let i = 0; i < c.zPath.length; i++) {
|
|
8025
|
-
if (c.zPath[i] > dest.zPath[i]) {
|
|
8026
|
-
cAfter = true;
|
|
8027
|
-
break;
|
|
8252
|
+
if (mode === "topmost") {
|
|
8253
|
+
const byId = new Map(candidates.map((c) => [c.id, c]));
|
|
8254
|
+
for (const id of scene.renderOrder()) dest = byId.get(id) ?? dest;
|
|
8255
|
+
} else {
|
|
8256
|
+
for (const c of candidates) {
|
|
8257
|
+
if (dest === null) {
|
|
8258
|
+
dest = c;
|
|
8259
|
+
continue;
|
|
8028
8260
|
}
|
|
8029
|
-
if (c.
|
|
8030
|
-
|
|
8031
|
-
|
|
8261
|
+
if (c.depth > dest.depth) {
|
|
8262
|
+
dest = c;
|
|
8263
|
+
continue;
|
|
8264
|
+
}
|
|
8265
|
+
if (c.depth < dest.depth) continue;
|
|
8266
|
+
let cAfter = false;
|
|
8267
|
+
for (let i = 0; i < c.zPath.length; i++) {
|
|
8268
|
+
if (c.zPath[i] > dest.zPath[i]) {
|
|
8269
|
+
cAfter = true;
|
|
8270
|
+
break;
|
|
8271
|
+
}
|
|
8272
|
+
if (c.zPath[i] < dest.zPath[i]) {
|
|
8273
|
+
cAfter = false;
|
|
8274
|
+
break;
|
|
8275
|
+
}
|
|
8032
8276
|
}
|
|
8277
|
+
if (cAfter) dest = c;
|
|
8033
8278
|
}
|
|
8034
|
-
if (cAfter) dest = c;
|
|
8035
8279
|
}
|
|
8036
8280
|
if (!dest) return;
|
|
8037
8281
|
const layout = dest.layout;
|
|
@@ -8098,6 +8342,10 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8098
8342
|
}
|
|
8099
8343
|
scratch.layoutPass = { layout, container, placements, sourceReflow, reflowIds };
|
|
8100
8344
|
}
|
|
8345
|
+
function regionContains(region, bounds, point) {
|
|
8346
|
+
const local = { x: point.x - bounds.x, y: point.y - bounds.y };
|
|
8347
|
+
return typeof region === "function" ? region(local) : pointInPath(region, local.x, local.y);
|
|
8348
|
+
}
|
|
8101
8349
|
function groupBySourceContainer(dragged, exclude) {
|
|
8102
8350
|
const out = /* @__PURE__ */ new Map();
|
|
8103
8351
|
for (const d of dragged) {
|
|
@@ -8113,17 +8361,18 @@ function groupBySourceContainer(dragged, exclude) {
|
|
|
8113
8361
|
return out;
|
|
8114
8362
|
}
|
|
8115
8363
|
function translateCommitOps(scratch, ids, dx, dy) {
|
|
8116
|
-
const m = [1, 0, 0, 1, dx, dy];
|
|
8117
8364
|
const ops = [];
|
|
8118
8365
|
for (const id of ids) {
|
|
8119
|
-
if (!scratch.startPoses.has(id)) continue;
|
|
8366
|
+
if (!scratch.startPoses.has(id) || scratch.carried.has(id)) continue;
|
|
8120
8367
|
const node = scratch.scene.get(id);
|
|
8121
8368
|
if (!node) continue;
|
|
8369
|
+
const l = localDelta(scratch, id, dx, dy);
|
|
8370
|
+
const m = [1, 0, 0, 1, l.dx, l.dy];
|
|
8122
8371
|
const authored = node.pose;
|
|
8123
8372
|
ops.push(createTransformOp({
|
|
8124
8373
|
id,
|
|
8125
8374
|
from: authored,
|
|
8126
|
-
to: translatePoseViaDescriptor(authored, dx, dy, scratch.projection),
|
|
8375
|
+
to: translatePoseViaDescriptor(authored, l.dx, l.dy, scratch.projection),
|
|
8127
8376
|
label: "Move"
|
|
8128
8377
|
}));
|
|
8129
8378
|
if (scratch.geometryProjection) {
|
|
@@ -8138,6 +8387,16 @@ function translateCommitOps(scratch, ids, dx, dy) {
|
|
|
8138
8387
|
}
|
|
8139
8388
|
return ops;
|
|
8140
8389
|
}
|
|
8390
|
+
function localDelta(scratch, id, dx, dy) {
|
|
8391
|
+
const f = scratch.frames.get(id);
|
|
8392
|
+
if (f === void 0) return { dx, dy };
|
|
8393
|
+
const { pc, projection: d } = scratch;
|
|
8394
|
+
const from = d.getBounds(pc.decompose(f.frameWorld, f.world));
|
|
8395
|
+
const to = d.getBounds(
|
|
8396
|
+
pc.decompose(f.frameWorld, translatePoseViaDescriptor(f.world, dx, dy, d))
|
|
8397
|
+
);
|
|
8398
|
+
return { dx: to.x - from.x, dy: to.y - from.y };
|
|
8399
|
+
}
|
|
8141
8400
|
function unreleasedIds(scratch, released) {
|
|
8142
8401
|
const selected = new Set(scratch.ids);
|
|
8143
8402
|
const out = scratch.ids.filter((id) => !released.has(id));
|
|
@@ -8159,7 +8418,11 @@ function unreleasedIds(scratch, released) {
|
|
|
8159
8418
|
function scenePoseAdapter(scene) {
|
|
8160
8419
|
return {
|
|
8161
8420
|
getPose: (id) => documentPose(scene, scene.get(id)),
|
|
8162
|
-
getParent: (id) => scene.get(id)?.parent ?? null
|
|
8421
|
+
getParent: (id) => scene.get(id)?.parent ?? null,
|
|
8422
|
+
definesFrame: (id) => {
|
|
8423
|
+
const node = scene.get(id);
|
|
8424
|
+
return node === void 0 || definesFrame(node);
|
|
8425
|
+
}
|
|
8163
8426
|
};
|
|
8164
8427
|
}
|
|
8165
8428
|
function resolveDropTarget(endCtx, scratch) {
|
|
@@ -8264,6 +8527,23 @@ var moveAction = {
|
|
|
8264
8527
|
queue.push(childId);
|
|
8265
8528
|
}
|
|
8266
8529
|
}
|
|
8530
|
+
const carried = /* @__PURE__ */ new Set();
|
|
8531
|
+
const frames = /* @__PURE__ */ new Map();
|
|
8532
|
+
if (pc.closure !== "identity") {
|
|
8533
|
+
const poseAdapter = scenePoseAdapter(scene);
|
|
8534
|
+
for (const id of startPoses.keys()) {
|
|
8535
|
+
const frame = frameAtOrAbove(poseAdapter, scene.get(id)?.parent ?? null);
|
|
8536
|
+
if (frame === null) continue;
|
|
8537
|
+
if (startPoses.has(asNodeId(frame))) {
|
|
8538
|
+
carried.add(id);
|
|
8539
|
+
continue;
|
|
8540
|
+
}
|
|
8541
|
+
frames.set(id, {
|
|
8542
|
+
world: composeWorldPose(poseAdapter, id, pc.compose),
|
|
8543
|
+
frameWorld: composeWorldPose(poseAdapter, frame, pc.compose)
|
|
8544
|
+
});
|
|
8545
|
+
}
|
|
8546
|
+
}
|
|
8267
8547
|
const behaviors = opts?.behaviors ?? [];
|
|
8268
8548
|
const adapter = moveGestureAdapter(scene);
|
|
8269
8549
|
const origin = /* @__PURE__ */ new Map();
|
|
@@ -8283,6 +8563,8 @@ var moveAction = {
|
|
|
8283
8563
|
startPoses,
|
|
8284
8564
|
ids,
|
|
8285
8565
|
cascadeIds,
|
|
8566
|
+
carried,
|
|
8567
|
+
frames,
|
|
8286
8568
|
scene,
|
|
8287
8569
|
currentDelta: { dx: 0, dy: 0 },
|
|
8288
8570
|
previews: /* @__PURE__ */ new Map(),
|
|
@@ -8335,7 +8617,12 @@ var moveAction = {
|
|
|
8335
8617
|
scratch.currentDelta = { dx, dy };
|
|
8336
8618
|
scratch.previews.clear();
|
|
8337
8619
|
for (const [id, ori] of scratch.startPoses) {
|
|
8338
|
-
scratch.
|
|
8620
|
+
if (scratch.carried.has(id)) {
|
|
8621
|
+
scratch.previews.set(id, ori);
|
|
8622
|
+
continue;
|
|
8623
|
+
}
|
|
8624
|
+
const l = localDelta(scratch, id, dx, dy);
|
|
8625
|
+
scratch.previews.set(id, translatePoseViaDescriptor(ori, l.dx, l.dy, scratch.projection));
|
|
8339
8626
|
}
|
|
8340
8627
|
scratch.layoutPass = null;
|
|
8341
8628
|
runLayoutPass(scratch, moveCtx);
|
|
@@ -8534,15 +8821,18 @@ var moveAction = {
|
|
|
8534
8821
|
};
|
|
8535
8822
|
|
|
8536
8823
|
// src/interactions/actions/defaultCommitAdapter.ts
|
|
8537
|
-
function defaultCommitAdapter(scene) {
|
|
8824
|
+
function defaultCommitAdapter(scene, selection) {
|
|
8538
8825
|
return {
|
|
8539
8826
|
getNode: (id) => scene.get(asNodeId(id)),
|
|
8540
8827
|
getNodes: () => {
|
|
8541
8828
|
return [...scene.renderOrderNodes()];
|
|
8542
8829
|
},
|
|
8543
8830
|
getPose: (id) => documentPose(scene, scene.get(asNodeId(id))),
|
|
8544
|
-
getSelection: () => [...scene.getSelection()],
|
|
8545
|
-
setSelection: (ids) =>
|
|
8831
|
+
getSelection: () => [...selection ? selection.getSelection() : scene.getSelection()],
|
|
8832
|
+
setSelection: (ids) => {
|
|
8833
|
+
if (selection) selection.setSelection(ids.map(asNodeId));
|
|
8834
|
+
else scene.setSelection(ids.map(asNodeId));
|
|
8835
|
+
},
|
|
8546
8836
|
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
|
|
8547
8837
|
getRemovalClosure: (ids) => [...scene.removalClosure(ids.map(asNodeId))],
|
|
8548
8838
|
getChildren: (parentId) => parentId === null ? [...scene.roots] : [...scene.childrenOf(asNodeId(parentId))],
|
|
@@ -8586,8 +8876,8 @@ function poseFrame(adapter, pc) {
|
|
|
8586
8876
|
pc,
|
|
8587
8877
|
world: (id) => composeWorldPose(adapter, id, pc.compose),
|
|
8588
8878
|
parentWorld: (id) => {
|
|
8589
|
-
const
|
|
8590
|
-
return
|
|
8879
|
+
const frame = frameAtOrAbove(adapter, adapter.getParent(id));
|
|
8880
|
+
return frame === null ? null : composeWorldPose(adapter, frame, pc.compose);
|
|
8591
8881
|
},
|
|
8592
8882
|
local: (id, worldPose) => rebaseLocalPose(adapter, worldPose, adapter.getParent(id), pc.compose, pc.decompose),
|
|
8593
8883
|
localUnder: (parentId, worldPose) => rebaseLocalPose(adapter, worldPose, parentId, pc.compose, pc.decompose)
|
|
@@ -8601,7 +8891,11 @@ function scenePoseFrame(scene, poseComposition) {
|
|
|
8601
8891
|
const node = scene.get(id);
|
|
8602
8892
|
return node === void 0 ? { x: 0, y: 0, width: 0, height: 0 } : effectivePose(source, node);
|
|
8603
8893
|
},
|
|
8604
|
-
getParent: (id) => scene.get(id)?.parent ?? null
|
|
8894
|
+
getParent: (id) => scene.get(id)?.parent ?? null,
|
|
8895
|
+
definesFrame: (id) => {
|
|
8896
|
+
const node = scene.get(id);
|
|
8897
|
+
return node === void 0 || definesFrame(node);
|
|
8898
|
+
}
|
|
8605
8899
|
};
|
|
8606
8900
|
return poseFrame(adapter, pc);
|
|
8607
8901
|
}
|
|
@@ -8958,7 +9252,7 @@ var resizeAction = {
|
|
|
8958
9252
|
}
|
|
8959
9253
|
if (ops.length > 0) {
|
|
8960
9254
|
if (scratch.applyOps) scratch.applyOps(ops, "Resize");
|
|
8961
|
-
else scratch.scene.applyBatch(ops, "Resize", defaultCommitAdapter(scratch.scene));
|
|
9255
|
+
else scratch.scene.applyBatch(ops, "Resize", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
8962
9256
|
}
|
|
8963
9257
|
dropPreviewOverrides(scratch);
|
|
8964
9258
|
scratch.previews.clear();
|
|
@@ -9114,7 +9408,7 @@ var rotateAction = {
|
|
|
9114
9408
|
}
|
|
9115
9409
|
if (ops.length > 0) {
|
|
9116
9410
|
if (scratch.applyOps) scratch.applyOps(ops, "Rotate");
|
|
9117
|
-
else scratch.scene.applyBatch(ops, "Rotate", defaultCommitAdapter(scratch.scene));
|
|
9411
|
+
else scratch.scene.applyBatch(ops, "Rotate", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
9118
9412
|
}
|
|
9119
9413
|
dropPreviewOverrides(scratch);
|
|
9120
9414
|
scratch.previews.clear();
|
|
@@ -9581,7 +9875,7 @@ var cloneAction = {
|
|
|
9581
9875
|
}
|
|
9582
9876
|
if (ops.length > 0) {
|
|
9583
9877
|
if (scratch.applyOps) scratch.applyOps(ops, "Clone");
|
|
9584
|
-
else scratch.scene.applyBatch(ops, "Clone", defaultCommitAdapter(scratch.scene));
|
|
9878
|
+
else scratch.scene.applyBatch(ops, "Clone", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
9585
9879
|
}
|
|
9586
9880
|
scratch.previews.clear();
|
|
9587
9881
|
},
|
|
@@ -9618,6 +9912,14 @@ function createVelocityTracker() {
|
|
|
9618
9912
|
}
|
|
9619
9913
|
|
|
9620
9914
|
// src/interactions/actions/defaults/viewportDragPan.ts
|
|
9915
|
+
function screenDeltaFromWorld(drag, scratch) {
|
|
9916
|
+
const v0 = scratch.startView;
|
|
9917
|
+
const now = scratch.view.get();
|
|
9918
|
+
return {
|
|
9919
|
+
x: (drag.current.x - now.x - (drag.start.x - v0.x)) * v0.scale.x,
|
|
9920
|
+
y: (drag.current.y - now.y - (drag.start.y - v0.y)) * v0.scale.y
|
|
9921
|
+
};
|
|
9922
|
+
}
|
|
9621
9923
|
var viewportDragPanAction = {
|
|
9622
9924
|
id: "viewport.dragPan",
|
|
9623
9925
|
label: "Drag to pan viewport",
|
|
@@ -9655,7 +9957,7 @@ var viewportDragPanAction = {
|
|
|
9655
9957
|
kind: "pan",
|
|
9656
9958
|
onMove(moveCtx) {
|
|
9657
9959
|
if (!moveCtx.drag) return;
|
|
9658
|
-
const screen = moveCtx.drag.screenDelta ?? moveCtx.drag
|
|
9960
|
+
const screen = moveCtx.drag.screenDelta ?? screenDeltaFromWorld(moveCtx.drag, scratch);
|
|
9659
9961
|
const screenDx = scratch.axis === "y" ? 0 : screen.x;
|
|
9660
9962
|
const screenDy = scratch.axis === "x" ? 0 : screen.y;
|
|
9661
9963
|
if (scratch.tracker) {
|
|
@@ -9753,6 +10055,11 @@ function makeViewportZoomAction(opts = {}) {
|
|
|
9753
10055
|
spec: { kind: "wheel", mods: { ctrl: true } },
|
|
9754
10056
|
opts: { params: { kind: "wheel" } }
|
|
9755
10057
|
},
|
|
10058
|
+
// Safari's trackpad pinch, reported as a scale rather than a wheel.
|
|
10059
|
+
{
|
|
10060
|
+
spec: { kind: "pinch" },
|
|
10061
|
+
opts: { params: { kind: "pinch" } }
|
|
10062
|
+
},
|
|
9756
10063
|
// Cmd+= → zoom in (also accepts Cmd+Shift+= which is Cmd++ on many keyboards)
|
|
9757
10064
|
{
|
|
9758
10065
|
spec: { kind: "key", key: "=", mods: { mod: true, shift: "optional" } },
|
|
@@ -9786,14 +10093,21 @@ function makeViewportZoomAction(opts = {}) {
|
|
|
9786
10093
|
view.animate(target, { ...tweenOpts, ms: ms ?? tweenOpts.ms });
|
|
9787
10094
|
};
|
|
9788
10095
|
const stepFrom = () => canAnimate && view.animationTarget?.() || current;
|
|
10096
|
+
const focal = () => ({
|
|
10097
|
+
x: params?.clientX ?? 0,
|
|
10098
|
+
y: params?.clientY ?? 0
|
|
10099
|
+
});
|
|
10100
|
+
const scaleBy = (factor) => {
|
|
10101
|
+
if (!(factor > 0) || !Number.isFinite(factor)) return;
|
|
10102
|
+
view.set(zoomAt(current, focal(), factor, clamp));
|
|
10103
|
+
};
|
|
9789
10104
|
switch (kind) {
|
|
9790
|
-
case "wheel":
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
10105
|
+
case "wheel":
|
|
10106
|
+
scaleBy(wheelZoomFactor(params?.deltaY ?? 0));
|
|
10107
|
+
break;
|
|
10108
|
+
case "pinch":
|
|
10109
|
+
scaleBy(params?.scale ?? Number.NaN);
|
|
9795
10110
|
break;
|
|
9796
|
-
}
|
|
9797
10111
|
case "in":
|
|
9798
10112
|
stepTo(zoomAt(stepFrom(), keyAnchor(view), KEY_STEP, clamp));
|
|
9799
10113
|
break;
|
|
@@ -10594,6 +10908,150 @@ function makePinchZoomAction(opts = {}) {
|
|
|
10594
10908
|
};
|
|
10595
10909
|
}
|
|
10596
10910
|
var pinchZoomAction = makePinchZoomAction();
|
|
10911
|
+
var SVG_BASE = {
|
|
10912
|
+
viewBox: "0 0 20 20",
|
|
10913
|
+
width: 20,
|
|
10914
|
+
height: 20,
|
|
10915
|
+
fill: "none",
|
|
10916
|
+
stroke: "currentColor",
|
|
10917
|
+
strokeWidth: 1.5,
|
|
10918
|
+
"aria-hidden": true
|
|
10919
|
+
};
|
|
10920
|
+
function CutIcon() {
|
|
10921
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10922
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "14", r: "2.5" }),
|
|
10923
|
+
/* @__PURE__ */ jsx("circle", { cx: "14", cy: "14", r: "2.5" }),
|
|
10924
|
+
/* @__PURE__ */ jsx("path", { d: "M8 12 L16 4", strokeLinecap: "round" }),
|
|
10925
|
+
/* @__PURE__ */ jsx("path", { d: "M12 12 L4 4", strokeLinecap: "round" })
|
|
10926
|
+
] });
|
|
10927
|
+
}
|
|
10928
|
+
function CopyIcon() {
|
|
10929
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10930
|
+
/* @__PURE__ */ jsx("path", { d: "M6 14 H3 V3 H14 V6", strokeLinejoin: "round" }),
|
|
10931
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "11", height: "11" })
|
|
10932
|
+
] });
|
|
10933
|
+
}
|
|
10934
|
+
function PasteIcon() {
|
|
10935
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10936
|
+
/* @__PURE__ */ jsx("rect", { x: "4", y: "5", width: "12", height: "13", rx: "1" }),
|
|
10937
|
+
/* @__PURE__ */ jsx("rect", { x: "7.5", y: "2.5", width: "5", height: "4", rx: "0.5", fill: "currentColor", stroke: "none" })
|
|
10938
|
+
] });
|
|
10939
|
+
}
|
|
10940
|
+
function DuplicateIcon() {
|
|
10941
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10942
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "10", height: "10" }),
|
|
10943
|
+
/* @__PURE__ */ jsx("rect", { x: "7", y: "7", width: "10", height: "10", fill: "currentColor", stroke: "none" })
|
|
10944
|
+
] });
|
|
10945
|
+
}
|
|
10946
|
+
function GroupIcon() {
|
|
10947
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10948
|
+
/* @__PURE__ */ jsx("path", { d: "M 2 5 L 2 2 L 5 2" }),
|
|
10949
|
+
/* @__PURE__ */ jsx("path", { d: "M 18 5 L 18 2 L 15 2" }),
|
|
10950
|
+
/* @__PURE__ */ jsx("path", { d: "M 2 15 L 2 18 L 5 18" }),
|
|
10951
|
+
/* @__PURE__ */ jsx("path", { d: "M 18 15 L 18 18 L 15 18" }),
|
|
10952
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "6", width: "4.5", height: "8", fill: "currentColor", stroke: "none" }),
|
|
10953
|
+
/* @__PURE__ */ jsx("rect", { x: "11", y: "6", width: "4", height: "8", fill: "currentColor", stroke: "none" })
|
|
10954
|
+
] });
|
|
10955
|
+
}
|
|
10956
|
+
function UngroupIcon() {
|
|
10957
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10958
|
+
/* @__PURE__ */ jsx("path", { d: "M 2 5 L 2 2 L 5 2", strokeDasharray: "1.5 1.5" }),
|
|
10959
|
+
/* @__PURE__ */ jsx("path", { d: "M 18 15 L 18 18 L 15 18", strokeDasharray: "1.5 1.5" }),
|
|
10960
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "4.5", height: "8", fill: "currentColor", stroke: "none" }),
|
|
10961
|
+
/* @__PURE__ */ jsx("rect", { x: "13", y: "7", width: "4", height: "8", fill: "currentColor", stroke: "none" })
|
|
10962
|
+
] });
|
|
10963
|
+
}
|
|
10964
|
+
function BringForwardIcon() {
|
|
10965
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10966
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "11", height: "11" }),
|
|
10967
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "11", height: "11", fill: "currentColor", stroke: "none" })
|
|
10968
|
+
] });
|
|
10969
|
+
}
|
|
10970
|
+
function BringToFrontIcon() {
|
|
10971
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10972
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "9", height: "9" }),
|
|
10973
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "9", height: "9" }),
|
|
10974
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "9", height: "9", fill: "currentColor", stroke: "none" })
|
|
10975
|
+
] });
|
|
10976
|
+
}
|
|
10977
|
+
function SendBackwardIcon() {
|
|
10978
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10979
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "11", height: "11", fill: "currentColor", stroke: "none" }),
|
|
10980
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "11", height: "11" })
|
|
10981
|
+
] });
|
|
10982
|
+
}
|
|
10983
|
+
function SendToBackIcon() {
|
|
10984
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10985
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "9", height: "9", fill: "currentColor", stroke: "none" }),
|
|
10986
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "9", height: "9" }),
|
|
10987
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "9", height: "9" })
|
|
10988
|
+
] });
|
|
10989
|
+
}
|
|
10990
|
+
function FlipXIcon() {
|
|
10991
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10992
|
+
/* @__PURE__ */ jsx("line", { x1: "10", y1: "2", x2: "10", y2: "18", strokeDasharray: "2 1.5" }),
|
|
10993
|
+
/* @__PURE__ */ jsx("path", { d: "M 8 4 L 3 10 L 8 16 Z" }),
|
|
10994
|
+
/* @__PURE__ */ jsx("path", { d: "M 12 4 L 17 10 L 12 16 Z", fill: "currentColor", stroke: "none" })
|
|
10995
|
+
] });
|
|
10996
|
+
}
|
|
10997
|
+
function FlipYIcon() {
|
|
10998
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE, children: [
|
|
10999
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "10", x2: "18", y2: "10", strokeDasharray: "2 1.5" }),
|
|
11000
|
+
/* @__PURE__ */ jsx("path", { d: "M 4 8 L 10 3 L 16 8 Z" }),
|
|
11001
|
+
/* @__PURE__ */ jsx("path", { d: "M 4 12 L 10 17 L 16 12 Z", fill: "currentColor", stroke: "none" })
|
|
11002
|
+
] });
|
|
11003
|
+
}
|
|
11004
|
+
|
|
11005
|
+
// src/interactions/actions/defaults/icons/actionGlyphs.ts
|
|
11006
|
+
var ACTION_GLYPHS = {
|
|
11007
|
+
delete: `<path d="M3.6 5.8h12.8M8 5.8V4.5A1.1 1.1 0 0 1 9.1 3.4h1.8A1.1 1.1 0 0 1 12 4.5v1.3"/><path d="M5.2 5.8v9.6A1.6 1.6 0 0 0 6.8 17h6.4a1.6 1.6 0 0 0 1.6-1.6V5.8"/><path d="M8.4 8.8v5.2M11.6 8.8v5.2" stroke-width="1"/>`,
|
|
11008
|
+
undo: `<path d="M4.4 8.4h6.8a3.6 3.6 0 0 1 0 7.2H8.6"/><path d="M6.47 10.14 4.4 8.4 6.47 6.66"/>`,
|
|
11009
|
+
redo: `<path d="M15.6 8.4H8.8a3.6 3.6 0 0 0 0 7.2H11.4"/><path d="M13.53 6.66 15.6 8.4 13.53 10.14"/>`
|
|
11010
|
+
};
|
|
11011
|
+
var SVG_BASE2 = {
|
|
11012
|
+
viewBox: "0 0 20 20",
|
|
11013
|
+
fill: "none",
|
|
11014
|
+
stroke: "currentColor",
|
|
11015
|
+
strokeWidth: 1.5,
|
|
11016
|
+
strokeLinecap: "round",
|
|
11017
|
+
strokeLinejoin: "round"
|
|
11018
|
+
};
|
|
11019
|
+
var MARKUP = {
|
|
11020
|
+
delete: { __html: ACTION_GLYPHS.delete },
|
|
11021
|
+
undo: { __html: ACTION_GLYPHS.undo },
|
|
11022
|
+
redo: { __html: ACTION_GLYPHS.redo }
|
|
11023
|
+
};
|
|
11024
|
+
function renderGlyph(name, { className, size = 20, label }) {
|
|
11025
|
+
return /* @__PURE__ */ jsx(
|
|
11026
|
+
"svg",
|
|
11027
|
+
{
|
|
11028
|
+
...SVG_BASE2,
|
|
11029
|
+
className,
|
|
11030
|
+
width: size,
|
|
11031
|
+
height: size,
|
|
11032
|
+
role: label ? "img" : void 0,
|
|
11033
|
+
"aria-label": label,
|
|
11034
|
+
"aria-hidden": label ? void 0 : true,
|
|
11035
|
+
dangerouslySetInnerHTML: MARKUP[name]
|
|
11036
|
+
}
|
|
11037
|
+
);
|
|
11038
|
+
}
|
|
11039
|
+
function UndoIcon(p) {
|
|
11040
|
+
return renderGlyph("undo", p);
|
|
11041
|
+
}
|
|
11042
|
+
function RedoIcon(p) {
|
|
11043
|
+
return renderGlyph("redo", p);
|
|
11044
|
+
}
|
|
11045
|
+
function DeleteIcon(p) {
|
|
11046
|
+
return renderGlyph("delete", p);
|
|
11047
|
+
}
|
|
11048
|
+
function requiresSelection(deps) {
|
|
11049
|
+
const selection = deps?.selection;
|
|
11050
|
+
if (!selection || selection.get().length === 0) {
|
|
11051
|
+
return ActionDisabledReason.SelectionRequired;
|
|
11052
|
+
}
|
|
11053
|
+
return true;
|
|
11054
|
+
}
|
|
10597
11055
|
|
|
10598
11056
|
// src/interactions/actions/defaults/delete.ts
|
|
10599
11057
|
function hostIndex(scene, id) {
|
|
@@ -10620,6 +11078,8 @@ function buildDeleteOps(scene, ids, label) {
|
|
|
10620
11078
|
var deleteAction = {
|
|
10621
11079
|
id: "delete",
|
|
10622
11080
|
label: "Delete",
|
|
11081
|
+
icon: createElement(DeleteIcon),
|
|
11082
|
+
group: "edit",
|
|
10623
11083
|
// Suppressed while any tool is mid-gesture — accidentally hitting
|
|
10624
11084
|
// Delete during a drag shouldn't wipe the selection out from under
|
|
10625
11085
|
// the in-flight handle.
|
|
@@ -10642,25 +11102,20 @@ var deleteAction = {
|
|
|
10642
11102
|
const ops = buildDeleteOps(scene, ids, "Delete");
|
|
10643
11103
|
if (ops.length > 0) {
|
|
10644
11104
|
if (applyOps) applyOps(ops, "Delete");
|
|
10645
|
-
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene));
|
|
11105
|
+
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10646
11106
|
}
|
|
10647
11107
|
selection.set([]);
|
|
10648
11108
|
}
|
|
10649
11109
|
},
|
|
10650
|
-
enabled:
|
|
11110
|
+
enabled: requiresSelection
|
|
10651
11111
|
};
|
|
10652
|
-
function requiresSelection(deps) {
|
|
10653
|
-
const selection = deps?.selection;
|
|
10654
|
-
if (!selection || selection.get().length === 0) {
|
|
10655
|
-
return ActionDisabledReason.SelectionRequired;
|
|
10656
|
-
}
|
|
10657
|
-
return true;
|
|
10658
|
-
}
|
|
10659
11112
|
|
|
10660
11113
|
// src/interactions/actions/defaults/clipboard.ts
|
|
10661
11114
|
var clipboardCopyAction = {
|
|
10662
11115
|
id: "clipboard.copy",
|
|
10663
11116
|
label: "Copy",
|
|
11117
|
+
icon: createElement(CopyIcon),
|
|
11118
|
+
group: "clipboard",
|
|
10664
11119
|
defaultBinding: { kind: "key", key: "c", mods: { mod: true } },
|
|
10665
11120
|
// Same gate as `duplicate`: a mode that can neither edit the page nor own a
|
|
10666
11121
|
// selection has nothing to copy. Notably excludes `text-edit`, where Cmd+C
|
|
@@ -10680,6 +11135,8 @@ var clipboardCopyAction = {
|
|
|
10680
11135
|
var clipboardCutAction = {
|
|
10681
11136
|
id: "clipboard.cut",
|
|
10682
11137
|
label: "Cut",
|
|
11138
|
+
icon: createElement(CutIcon),
|
|
11139
|
+
group: "clipboard",
|
|
10683
11140
|
defaultBinding: { kind: "key", key: "x", mods: { mod: true } },
|
|
10684
11141
|
eligible: { capability: "edits-page" },
|
|
10685
11142
|
requires: ["clipboard", "scene", "selection", "applyOps"],
|
|
@@ -10697,13 +11154,31 @@ var clipboardCutAction = {
|
|
|
10697
11154
|
const ops = buildDeleteOps(scene, ids, "Cut");
|
|
10698
11155
|
if (ops.length > 0) {
|
|
10699
11156
|
if (applyOps) applyOps(ops, "Cut");
|
|
10700
|
-
else scene.applyBatch(ops, "Cut", defaultCommitAdapter(scene));
|
|
11157
|
+
else scene.applyBatch(ops, "Cut", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10701
11158
|
}
|
|
10702
11159
|
selection.set([]);
|
|
10703
11160
|
}
|
|
10704
11161
|
},
|
|
10705
11162
|
enabled: requiresSelection
|
|
10706
11163
|
};
|
|
11164
|
+
var clipboardPasteAction = {
|
|
11165
|
+
id: "clipboard.paste",
|
|
11166
|
+
label: "Paste",
|
|
11167
|
+
icon: createElement(PasteIcon),
|
|
11168
|
+
group: "clipboard",
|
|
11169
|
+
eligible: { capability: "edits-page" },
|
|
11170
|
+
requires: ["clipboard"],
|
|
11171
|
+
invoker: {
|
|
11172
|
+
timing: "immediate",
|
|
11173
|
+
run: (deps) => {
|
|
11174
|
+
deps.clipboard?.paste();
|
|
11175
|
+
}
|
|
11176
|
+
},
|
|
11177
|
+
enabled: (deps) => {
|
|
11178
|
+
const clipboard = deps?.clipboard;
|
|
11179
|
+
return clipboard && !clipboard.isEmpty() ? true : ActionDisabledReason.NotApplicable;
|
|
11180
|
+
}
|
|
11181
|
+
};
|
|
10707
11182
|
|
|
10708
11183
|
// src/interactions/actions/defaults/enterTextEdit.ts
|
|
10709
11184
|
var enterTextEditAction = {
|
|
@@ -10798,8 +11273,6 @@ var selectAllAction = {
|
|
|
10798
11273
|
}
|
|
10799
11274
|
}
|
|
10800
11275
|
};
|
|
10801
|
-
|
|
10802
|
-
// src/interactions/actions/defaults/duplicate.ts
|
|
10803
11276
|
var DUPLICATE_OFFSET = 12;
|
|
10804
11277
|
function hasSelectedAncestor(scene, id, set) {
|
|
10805
11278
|
let parent = scene.get(id)?.parent ?? null;
|
|
@@ -10829,6 +11302,8 @@ function copySubtree(scene, id, parent, offset, offsetDescendants, ops, d) {
|
|
|
10829
11302
|
var duplicateAction = {
|
|
10830
11303
|
id: "duplicate",
|
|
10831
11304
|
label: "Duplicate",
|
|
11305
|
+
icon: createElement(DuplicateIcon),
|
|
11306
|
+
group: "edit",
|
|
10832
11307
|
defaultBinding: { kind: "key", key: "d", mods: { mod: true } },
|
|
10833
11308
|
eligible: { capability: ["edits-page", "creates-selection"] },
|
|
10834
11309
|
// `selection` is read by the `enabled` gate as well as the invoker. An
|
|
@@ -10859,7 +11334,7 @@ var duplicateAction = {
|
|
|
10859
11334
|
}
|
|
10860
11335
|
if (ops.length === 0) return;
|
|
10861
11336
|
if (applyOps) applyOps(ops, "Duplicate");
|
|
10862
|
-
else scene.applyBatch(ops, "Duplicate", defaultCommitAdapter(scene));
|
|
11337
|
+
else scene.applyBatch(ops, "Duplicate", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10863
11338
|
selection.set(newIds);
|
|
10864
11339
|
}
|
|
10865
11340
|
},
|
|
@@ -10895,8 +11370,6 @@ function withKitRegistry(registry2) {
|
|
|
10895
11370
|
}
|
|
10896
11371
|
};
|
|
10897
11372
|
}
|
|
10898
|
-
|
|
10899
|
-
// src/interactions/actions/defaults/group.ts
|
|
10900
11373
|
var containerIdCounter = 0;
|
|
10901
11374
|
function freshContainerId() {
|
|
10902
11375
|
return asNodeId(`n${(containerIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`);
|
|
@@ -10904,6 +11377,8 @@ function freshContainerId() {
|
|
|
10904
11377
|
var groupAction = {
|
|
10905
11378
|
id: "group",
|
|
10906
11379
|
label: "Group",
|
|
11380
|
+
icon: createElement(GroupIcon),
|
|
11381
|
+
group: "structure",
|
|
10907
11382
|
defaultBinding: { kind: "key", key: "g", mods: { mod: true } },
|
|
10908
11383
|
eligible: { capability: "edits-page" },
|
|
10909
11384
|
requires: ["scene", "selection", "applyOps", "poseDescriptor", "poseComposition"],
|
|
@@ -10963,15 +11438,17 @@ var groupAction = {
|
|
|
10963
11438
|
}));
|
|
10964
11439
|
}
|
|
10965
11440
|
if (applyOps) applyOps(ops, "Group");
|
|
10966
|
-
else scene.applyBatch(ops, "Group", defaultCommitAdapter(scene));
|
|
11441
|
+
else scene.applyBatch(ops, "Group", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10967
11442
|
selection.set([containerId]);
|
|
10968
11443
|
}
|
|
10969
11444
|
},
|
|
10970
|
-
enabled:
|
|
11445
|
+
enabled: requiresSelection
|
|
10971
11446
|
};
|
|
10972
11447
|
var ungroupAction = {
|
|
10973
11448
|
id: "ungroup",
|
|
10974
11449
|
label: "Ungroup",
|
|
11450
|
+
icon: createElement(UngroupIcon),
|
|
11451
|
+
group: "structure",
|
|
10975
11452
|
defaultBinding: { kind: "key", key: "g", mods: { mod: true, shift: true } },
|
|
10976
11453
|
eligible: { capability: "edits-page" },
|
|
10977
11454
|
requires: ["scene", "selection", "poseComposition"],
|
|
@@ -11012,13 +11489,22 @@ var ungroupAction = {
|
|
|
11012
11489
|
});
|
|
11013
11490
|
}
|
|
11014
11491
|
},
|
|
11015
|
-
enabled: () =>
|
|
11492
|
+
enabled: (deps) => {
|
|
11493
|
+
const selection = deps?.selection;
|
|
11494
|
+
const scene = deps?.scene;
|
|
11495
|
+
const any = selection?.get().some((id) => scene?.get(id)?.kind === "container");
|
|
11496
|
+
return any ? true : ActionDisabledReason.NotApplicable;
|
|
11497
|
+
}
|
|
11016
11498
|
};
|
|
11017
|
-
|
|
11018
|
-
|
|
11499
|
+
function historyCan(deps, which) {
|
|
11500
|
+
const history = deps?.history;
|
|
11501
|
+
return history?.[which]?.() ? true : ActionDisabledReason.NotApplicable;
|
|
11502
|
+
}
|
|
11019
11503
|
var undoAction = {
|
|
11020
11504
|
id: "undo",
|
|
11021
11505
|
label: "Undo",
|
|
11506
|
+
icon: createElement(UndoIcon),
|
|
11507
|
+
group: "history",
|
|
11022
11508
|
defaultBinding: { kind: "key", key: "z", mods: { mod: true } },
|
|
11023
11509
|
requires: ["history"],
|
|
11024
11510
|
invoker: {
|
|
@@ -11026,11 +11512,14 @@ var undoAction = {
|
|
|
11026
11512
|
run: (deps) => {
|
|
11027
11513
|
deps.history?.undo?.();
|
|
11028
11514
|
}
|
|
11029
|
-
}
|
|
11515
|
+
},
|
|
11516
|
+
enabled: (deps) => historyCan(deps, "canUndo")
|
|
11030
11517
|
};
|
|
11031
11518
|
var redoAction = {
|
|
11032
11519
|
id: "redo",
|
|
11033
11520
|
label: "Redo",
|
|
11521
|
+
icon: createElement(RedoIcon),
|
|
11522
|
+
group: "history",
|
|
11034
11523
|
defaultBinding: { kind: "key", key: "z", mods: { mod: true, shift: true } },
|
|
11035
11524
|
requires: ["history"],
|
|
11036
11525
|
invoker: {
|
|
@@ -11038,7 +11527,8 @@ var redoAction = {
|
|
|
11038
11527
|
run: (deps) => {
|
|
11039
11528
|
deps.history?.redo?.();
|
|
11040
11529
|
}
|
|
11041
|
-
}
|
|
11530
|
+
},
|
|
11531
|
+
enabled: (deps) => historyCan(deps, "canRedo")
|
|
11042
11532
|
};
|
|
11043
11533
|
|
|
11044
11534
|
// src/interactions/actions/flip/helpers.ts
|
|
@@ -11142,8 +11632,8 @@ function localMirror(axis, frame, id, pivotWorld) {
|
|
|
11142
11632
|
rotation: 0
|
|
11143
11633
|
});
|
|
11144
11634
|
const theta = at.rotation ?? 0;
|
|
11145
|
-
const
|
|
11146
|
-
const turned = multiply$1(multiply$1(rotate(theta),
|
|
11635
|
+
const mirror = axis === "x" ? [-1, 0, 0, 1, 0, 0] : [1, 0, 0, -1, 0, 0];
|
|
11636
|
+
const turned = multiply$1(multiply$1(rotate(theta), mirror), rotate(-theta));
|
|
11147
11637
|
return multiply$1(translate$1(at.x, at.y), multiply$1(turned, translate$1(-at.x, -at.y)));
|
|
11148
11638
|
}
|
|
11149
11639
|
function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection, geom, poseComposition) {
|
|
@@ -11178,11 +11668,16 @@ function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjecti
|
|
|
11178
11668
|
}
|
|
11179
11669
|
if (ops.length === 0) return;
|
|
11180
11670
|
if (applyOps) applyOps(ops, "Flip");
|
|
11181
|
-
else scene.applyBatch(ops, "Flip", defaultCommitAdapter(scene));
|
|
11671
|
+
else scene.applyBatch(ops, "Flip", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11182
11672
|
}
|
|
11183
11673
|
var flipAction = {
|
|
11184
11674
|
id: "flip",
|
|
11185
11675
|
label: "Flip",
|
|
11676
|
+
group: "flip",
|
|
11677
|
+
variants: [
|
|
11678
|
+
{ key: "x", label: "Flip Horizontal", icon: createElement(FlipXIcon), params: { axis: "x" } },
|
|
11679
|
+
{ key: "y", label: "Flip Vertical", icon: createElement(FlipYIcon), params: { axis: "y" } }
|
|
11680
|
+
],
|
|
11186
11681
|
defaultBinding: [
|
|
11187
11682
|
{ spec: { kind: "key", key: ["h", "H"], mods: { shift: true } }, opts: { params: { axis: "x" } } },
|
|
11188
11683
|
{ spec: { kind: "key", key: ["v", "V"], mods: { shift: true } }, opts: { params: { axis: "y" } } }
|
|
@@ -11265,7 +11760,7 @@ function nudgeSelection(selection, scene, dx, dy, applyOps, geometryProjection,
|
|
|
11265
11760
|
}
|
|
11266
11761
|
if (ops.length === 0) return;
|
|
11267
11762
|
if (applyOps) applyOps(ops, "Nudge");
|
|
11268
|
-
else scene.applyBatch(ops, "Nudge", defaultCommitAdapter(scene));
|
|
11763
|
+
else scene.applyBatch(ops, "Nudge", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11269
11764
|
}
|
|
11270
11765
|
function makeNudgeAction(dir2) {
|
|
11271
11766
|
return {
|
|
@@ -11495,20 +11990,22 @@ function createMoveToIndexOp(args) {
|
|
|
11495
11990
|
};
|
|
11496
11991
|
}
|
|
11497
11992
|
registerOpFactory("moveToIndex", (args) => createMoveToIndexOp(args));
|
|
11498
|
-
|
|
11499
|
-
// src/interactions/actions/defaults/reorder.ts
|
|
11500
11993
|
function reorderSelection(selection, scene, direction, distance, applyOps) {
|
|
11501
11994
|
const ids = selection.get();
|
|
11502
11995
|
if (ids.length === 0) return;
|
|
11503
11996
|
const reorderDir = direction === "forward" ? distance === "extreme" ? "front" : "forward" : distance === "extreme" ? "back" : "backward";
|
|
11504
11997
|
const op = createReorderOp({ ids, direction: reorderDir, label: "Reorder" });
|
|
11505
11998
|
if (applyOps) applyOps([op], "Reorder");
|
|
11506
|
-
else scene.applyBatch([op], "Reorder", defaultCommitAdapter(scene));
|
|
11999
|
+
else scene.applyBatch([op], "Reorder", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11507
12000
|
}
|
|
11508
12001
|
var reorderForwardAction = {
|
|
11509
12002
|
id: "reorder.forward",
|
|
11510
12003
|
label: "Bring Forward",
|
|
11511
12004
|
group: "reorder",
|
|
12005
|
+
variants: [
|
|
12006
|
+
{ key: "adjacent", label: "Bring Forward", icon: createElement(BringForwardIcon), params: { distance: "adjacent" } },
|
|
12007
|
+
{ key: "extreme", label: "Bring to Front", icon: createElement(BringToFrontIcon), params: { distance: "extreme" } }
|
|
12008
|
+
],
|
|
11512
12009
|
defaultBinding: [
|
|
11513
12010
|
{
|
|
11514
12011
|
// Modifier matching is strict, so Shift must be absent here — listing
|
|
@@ -11554,6 +12051,10 @@ var reorderBackwardAction = {
|
|
|
11554
12051
|
id: "reorder.backward",
|
|
11555
12052
|
label: "Send Backward",
|
|
11556
12053
|
group: "reorder",
|
|
12054
|
+
variants: [
|
|
12055
|
+
{ key: "adjacent", label: "Send Backward", icon: createElement(SendBackwardIcon), params: { distance: "adjacent" } },
|
|
12056
|
+
{ key: "extreme", label: "Send to Back", icon: createElement(SendToBackIcon), params: { distance: "extreme" } }
|
|
12057
|
+
],
|
|
11557
12058
|
defaultBinding: [
|
|
11558
12059
|
{
|
|
11559
12060
|
spec: { kind: "key", key: ["["], mods: { mod: true } },
|
|
@@ -11586,7 +12087,7 @@ var reorderBackwardAction = {
|
|
|
11586
12087
|
},
|
|
11587
12088
|
enabled: requiresSelection
|
|
11588
12089
|
};
|
|
11589
|
-
var
|
|
12090
|
+
var SVG_BASE3 = {
|
|
11590
12091
|
viewBox: "0 0 16 16",
|
|
11591
12092
|
width: 16,
|
|
11592
12093
|
height: 16,
|
|
@@ -11599,42 +12100,42 @@ var AXIS = {
|
|
|
11599
12100
|
fill: "none"
|
|
11600
12101
|
};
|
|
11601
12102
|
function AlignLeftIcon() {
|
|
11602
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12103
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11603
12104
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "2", y2: "14", ...AXIS }),
|
|
11604
12105
|
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "3", width: "9", height: "3" }),
|
|
11605
12106
|
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "9", width: "6", height: "3" })
|
|
11606
12107
|
] });
|
|
11607
12108
|
}
|
|
11608
12109
|
function AlignRightIcon() {
|
|
11609
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12110
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11610
12111
|
/* @__PURE__ */ jsx("line", { x1: "14", y1: "2", x2: "14", y2: "14", ...AXIS }),
|
|
11611
12112
|
/* @__PURE__ */ jsx("rect", { x: "4.5", y: "3", width: "9", height: "3" }),
|
|
11612
12113
|
/* @__PURE__ */ jsx("rect", { x: "7.5", y: "9", width: "6", height: "3" })
|
|
11613
12114
|
] });
|
|
11614
12115
|
}
|
|
11615
12116
|
function AlignTopIcon() {
|
|
11616
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12117
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11617
12118
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "14", y2: "2", ...AXIS }),
|
|
11618
12119
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "2.5", width: "3", height: "9" }),
|
|
11619
12120
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "2.5", width: "3", height: "6" })
|
|
11620
12121
|
] });
|
|
11621
12122
|
}
|
|
11622
12123
|
function AlignBottomIcon() {
|
|
11623
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12124
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11624
12125
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "14", x2: "14", y2: "14", ...AXIS }),
|
|
11625
12126
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "4.5", width: "3", height: "9" }),
|
|
11626
12127
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "7.5", width: "3", height: "6" })
|
|
11627
12128
|
] });
|
|
11628
12129
|
}
|
|
11629
12130
|
function AlignCenterXIcon() {
|
|
11630
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12131
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11631
12132
|
/* @__PURE__ */ jsx("line", { x1: "8", y1: "2", x2: "8", y2: "14", ...AXIS }),
|
|
11632
12133
|
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "3", width: "9", height: "3" }),
|
|
11633
12134
|
/* @__PURE__ */ jsx("rect", { x: "5", y: "9", width: "6", height: "3" })
|
|
11634
12135
|
] });
|
|
11635
12136
|
}
|
|
11636
12137
|
function AlignCenterYIcon() {
|
|
11637
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12138
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11638
12139
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "8", x2: "14", y2: "8", ...AXIS }),
|
|
11639
12140
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "3", height: "9" }),
|
|
11640
12141
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "5", width: "3", height: "6" })
|
|
@@ -11748,7 +12249,7 @@ function planDistribute(bounds, axis, mode) {
|
|
|
11748
12249
|
}
|
|
11749
12250
|
return targets;
|
|
11750
12251
|
}
|
|
11751
|
-
var
|
|
12252
|
+
var SVG_BASE4 = {
|
|
11752
12253
|
viewBox: "0 0 16 16",
|
|
11753
12254
|
width: 16,
|
|
11754
12255
|
height: 16,
|
|
@@ -11756,14 +12257,14 @@ var SVG_BASE2 = {
|
|
|
11756
12257
|
"aria-hidden": true
|
|
11757
12258
|
};
|
|
11758
12259
|
function DistributeHorizontalIcon() {
|
|
11759
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12260
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE4, children: [
|
|
11760
12261
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "4", width: "3", height: "8" }),
|
|
11761
12262
|
/* @__PURE__ */ jsx("rect", { x: "6.5", y: "4", width: "3", height: "8" }),
|
|
11762
12263
|
/* @__PURE__ */ jsx("rect", { x: "12", y: "4", width: "3", height: "8" })
|
|
11763
12264
|
] });
|
|
11764
12265
|
}
|
|
11765
12266
|
function DistributeVerticalIcon() {
|
|
11766
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12267
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE4, children: [
|
|
11767
12268
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "1", width: "8", height: "3" }),
|
|
11768
12269
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "6.5", width: "8", height: "3" }),
|
|
11769
12270
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "12", width: "8", height: "3" })
|
|
@@ -11933,7 +12434,7 @@ function applyBooleanOp(adapter, op) {
|
|
|
11933
12434
|
dispatchApplyBatch(adapter, ops, LABEL[op]);
|
|
11934
12435
|
return { kind: "applied", resultIds: newNodes.map((n) => n.id) };
|
|
11935
12436
|
}
|
|
11936
|
-
var
|
|
12437
|
+
var SVG_BASE5 = {
|
|
11937
12438
|
viewBox: "0 0 20 20",
|
|
11938
12439
|
width: 20,
|
|
11939
12440
|
height: 20,
|
|
@@ -11943,7 +12444,7 @@ var SVG_BASE3 = {
|
|
|
11943
12444
|
"aria-hidden": true
|
|
11944
12445
|
};
|
|
11945
12446
|
function UnionIcon() {
|
|
11946
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12447
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11947
12448
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5", fill: "currentColor", stroke: "none" }),
|
|
11948
12449
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5", fill: "currentColor", stroke: "none" }),
|
|
11949
12450
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
@@ -11951,7 +12452,7 @@ function UnionIcon() {
|
|
|
11951
12452
|
] });
|
|
11952
12453
|
}
|
|
11953
12454
|
function IntersectIcon() {
|
|
11954
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12455
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11955
12456
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11956
12457
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
11957
12458
|
/* @__PURE__ */ jsx(
|
|
@@ -11965,7 +12466,7 @@ function IntersectIcon() {
|
|
|
11965
12466
|
] });
|
|
11966
12467
|
}
|
|
11967
12468
|
function SubtractIcon() {
|
|
11968
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12469
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11969
12470
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11970
12471
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
11971
12472
|
/* @__PURE__ */ jsx(
|
|
@@ -11979,7 +12480,7 @@ function SubtractIcon() {
|
|
|
11979
12480
|
] });
|
|
11980
12481
|
}
|
|
11981
12482
|
function ExcludeIcon() {
|
|
11982
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12483
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11983
12484
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11984
12485
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
11985
12486
|
/* @__PURE__ */ jsx(
|
|
@@ -11994,14 +12495,14 @@ function ExcludeIcon() {
|
|
|
11994
12495
|
] });
|
|
11995
12496
|
}
|
|
11996
12497
|
function DivideIcon() {
|
|
11997
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12498
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11998
12499
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11999
12500
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
12000
12501
|
/* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "10", y2: "14" })
|
|
12001
12502
|
] });
|
|
12002
12503
|
}
|
|
12003
12504
|
function CropIcon() {
|
|
12004
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12505
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
12005
12506
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: "weasel-default-crop-mask", children: /* @__PURE__ */ jsx("rect", { x: "9", y: "5", width: "8", height: "10" }) }) }),
|
|
12006
12507
|
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "10", r: "5" }),
|
|
12007
12508
|
/* @__PURE__ */ jsx(
|
|
@@ -12457,7 +12958,7 @@ function createPaintAction(spec) {
|
|
|
12457
12958
|
}
|
|
12458
12959
|
if (ops.length > 0) {
|
|
12459
12960
|
if (scratch.applyOps) scratch.applyOps(ops, spec.label);
|
|
12460
|
-
else scratch.scene.applyBatch(ops, spec.label, defaultCommitAdapter(scratch.scene));
|
|
12961
|
+
else scratch.scene.applyBatch(ops, spec.label, defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
12461
12962
|
}
|
|
12462
12963
|
scratch.previews.clear();
|
|
12463
12964
|
},
|
|
@@ -12508,14 +13009,23 @@ function mergeAlphaFromPrev(picked, prev) {
|
|
|
12508
13009
|
// src/util/paint.ts
|
|
12509
13010
|
var DEFAULT_FILL_COLOR = "#ffffffff";
|
|
12510
13011
|
var DEFAULT_STROKE_COLOR = "#000000ff";
|
|
12511
|
-
var
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
13012
|
+
var srgb = (hex) => hexToColorLiteral(hex);
|
|
13013
|
+
var DEFAULT_PALETTE = {
|
|
13014
|
+
entries: [
|
|
13015
|
+
{ name: "sage", color: srgb("#7fb069") },
|
|
13016
|
+
{ name: "tan", color: srgb("#d4a574") },
|
|
13017
|
+
{ name: "lavender", color: srgb("#a48bd4") },
|
|
13018
|
+
{ name: "sky", color: srgb("#7ab8d4") },
|
|
13019
|
+
{ name: "rose", color: srgb("#d47a7a") }
|
|
13020
|
+
]
|
|
13021
|
+
};
|
|
13022
|
+
function cycleFill(seq, palette = DEFAULT_PALETTE, fallback = DEFAULT_FILL_COLOR) {
|
|
13023
|
+
const { entries } = palette;
|
|
13024
|
+
const entry = entries[(seq % entries.length + entries.length) % entries.length];
|
|
13025
|
+
const color = entry && resolvePaletteColor(palette, { ref: entry.name });
|
|
13026
|
+
return (color && colorLiteralToHex(color)) ?? fallback;
|
|
13027
|
+
}
|
|
13028
|
+
var GHOST_STROKE = cycleFill(0);
|
|
12519
13029
|
function solid(color) {
|
|
12520
13030
|
const a = getAlpha01(color);
|
|
12521
13031
|
const rgb = color.length === 9 && color.startsWith("#") ? color.slice(0, 7) : color;
|
|
@@ -13018,7 +13528,7 @@ var ingestAction = {
|
|
|
13018
13528
|
insert,
|
|
13019
13529
|
applyOps: (ops, label) => {
|
|
13020
13530
|
if (applyOps) applyOps(ops, label ?? "Ingest");
|
|
13021
|
-
else scene.applyBatch(ops, label ?? "Ingest",
|
|
13531
|
+
else scene.applyBatch(ops, label ?? "Ingest", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
13022
13532
|
},
|
|
13023
13533
|
scene,
|
|
13024
13534
|
selection,
|
|
@@ -13045,8 +13555,8 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13045
13555
|
escapeAction,
|
|
13046
13556
|
cancelGestureAction,
|
|
13047
13557
|
selectAllAction,
|
|
13048
|
-
deleteAction,
|
|
13049
13558
|
duplicateAction,
|
|
13559
|
+
deleteAction,
|
|
13050
13560
|
groupAction,
|
|
13051
13561
|
ungroupAction,
|
|
13052
13562
|
undoAction,
|
|
@@ -13059,11 +13569,11 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13059
13569
|
reorderForwardAction,
|
|
13060
13570
|
reorderBackwardAction,
|
|
13061
13571
|
alignLeftAction,
|
|
13572
|
+
alignCenterXAction,
|
|
13062
13573
|
alignRightAction,
|
|
13063
13574
|
alignTopAction,
|
|
13064
|
-
alignBottomAction,
|
|
13065
|
-
alignCenterXAction,
|
|
13066
13575
|
alignCenterYAction,
|
|
13576
|
+
alignBottomAction,
|
|
13067
13577
|
distributeHorizontalAction,
|
|
13068
13578
|
distributeVerticalAction,
|
|
13069
13579
|
pathfinderUnionAction,
|
|
@@ -13101,8 +13611,9 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13101
13611
|
setFillOpacityAction,
|
|
13102
13612
|
setStrokeOpacityAction,
|
|
13103
13613
|
ingestAction,
|
|
13614
|
+
clipboardCutAction,
|
|
13104
13615
|
clipboardCopyAction,
|
|
13105
|
-
|
|
13616
|
+
clipboardPasteAction
|
|
13106
13617
|
];
|
|
13107
13618
|
var KIT_STANDARD_ACTION_IDS = KIT_STANDARD_DESCRIPTORS.map((a) => a.id);
|
|
13108
13619
|
function useStandardActions(opts) {
|
|
@@ -14075,7 +14586,7 @@ function createTimeline(register, id, opts) {
|
|
|
14075
14586
|
let playhead = 0;
|
|
14076
14587
|
let prevPlayhead = -Infinity;
|
|
14077
14588
|
let done = false;
|
|
14078
|
-
let
|
|
14589
|
+
let live2 = true;
|
|
14079
14590
|
let cancelled = false;
|
|
14080
14591
|
let wantPaused = opts.autoplay === false;
|
|
14081
14592
|
let wantScale = 1;
|
|
@@ -14163,12 +14674,12 @@ function createTimeline(register, id, opts) {
|
|
|
14163
14674
|
opts.onDone?.();
|
|
14164
14675
|
}
|
|
14165
14676
|
if (playhead < duration) return false;
|
|
14166
|
-
|
|
14677
|
+
live2 = false;
|
|
14167
14678
|
return true;
|
|
14168
14679
|
};
|
|
14169
14680
|
const onCancel = () => {
|
|
14170
14681
|
cancelled = true;
|
|
14171
|
-
|
|
14682
|
+
live2 = false;
|
|
14172
14683
|
booker?.retract();
|
|
14173
14684
|
};
|
|
14174
14685
|
const base = register({ id, cancelKey: opts.cancelKey, tick, onCancel });
|
|
@@ -14176,11 +14687,11 @@ function createTimeline(register, id, opts) {
|
|
|
14176
14687
|
const rearm = () => {
|
|
14177
14688
|
if (cancelled || playhead >= duration) return;
|
|
14178
14689
|
done = false;
|
|
14179
|
-
if (
|
|
14690
|
+
if (live2) return;
|
|
14180
14691
|
booker?.rearm();
|
|
14181
14692
|
lastVirtual = 0;
|
|
14182
14693
|
offset = playhead;
|
|
14183
|
-
|
|
14694
|
+
live2 = true;
|
|
14184
14695
|
register({ id, cancelKey: opts.cancelKey, keepExisting: true, tick, onCancel });
|
|
14185
14696
|
if (wantPaused) base.pause();
|
|
14186
14697
|
base.setTimeScale(wantScale);
|
|
@@ -14208,8 +14719,8 @@ function createTimeline(register, id, opts) {
|
|
|
14208
14719
|
wantScale = s;
|
|
14209
14720
|
base.setTimeScale(s);
|
|
14210
14721
|
},
|
|
14211
|
-
timeScale: () =>
|
|
14212
|
-
isPaused: () =>
|
|
14722
|
+
timeScale: () => live2 ? base.timeScale() : wantScale,
|
|
14723
|
+
isPaused: () => live2 ? base.isPaused() : wantPaused,
|
|
14213
14724
|
seek(t) {
|
|
14214
14725
|
booker?.retract();
|
|
14215
14726
|
offset = t - lastVirtual;
|
|
@@ -14890,22 +15401,34 @@ function useKeybindings(tools, options = {}) {
|
|
|
14890
15401
|
|
|
14891
15402
|
// src/tools/prefs.ts
|
|
14892
15403
|
function prefUnit(system, display, opts) {
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
);
|
|
15404
|
+
let self;
|
|
15405
|
+
try {
|
|
15406
|
+
self = unitScale(system, display);
|
|
15407
|
+
} catch (e) {
|
|
15408
|
+
throw new Error(`prefUnit: ${e instanceof Error ? e.message : String(e)}`);
|
|
14899
15409
|
}
|
|
14900
15410
|
const scale = opts?.precision === void 0 ? void 0 : 10 ** opts.precision;
|
|
14901
15411
|
const suffix = opts?.suffix ?? display;
|
|
14902
15412
|
const accepts = { [suffix]: 1 };
|
|
14903
|
-
for (const
|
|
15413
|
+
for (const name of Object.keys(system.units)) {
|
|
15414
|
+
const other = unitScale(system, name);
|
|
15415
|
+
const factor = other.factor / self.factor;
|
|
15416
|
+
const offset = (other.offset - self.offset) / self.factor;
|
|
15417
|
+
accepts[name] = offset === 0 ? factor : { factor, offset };
|
|
15418
|
+
}
|
|
15419
|
+
const toDisplay = (stored) => {
|
|
15420
|
+
const shown = (stored - self.offset) / self.factor;
|
|
15421
|
+
return scale === void 0 ? shown : Math.round(shown * scale) / scale;
|
|
15422
|
+
};
|
|
14904
15423
|
return {
|
|
14905
|
-
toDisplay
|
|
14906
|
-
fromDisplay: (shown) => shown * factor,
|
|
15424
|
+
toDisplay,
|
|
15425
|
+
fromDisplay: (shown) => shown * self.factor + self.offset,
|
|
14907
15426
|
suffix,
|
|
14908
|
-
accepts
|
|
15427
|
+
accepts,
|
|
15428
|
+
format: (stored) => `${formatUnit(stored, display, system, {
|
|
15429
|
+
precision: opts?.precision ?? 2,
|
|
15430
|
+
suffix: false
|
|
15431
|
+
})}${suffix}`
|
|
14909
15432
|
};
|
|
14910
15433
|
}
|
|
14911
15434
|
var TOOL_PREF_KINDS = {
|
|
@@ -15609,17 +16132,53 @@ function resolveFillPattern(fill) {
|
|
|
15609
16132
|
}
|
|
15610
16133
|
|
|
15611
16134
|
// src/features/images/imageCache.ts
|
|
16135
|
+
var MAX_VECTOR_RASTER_PIXELS = 4096 * 4096;
|
|
16136
|
+
var SHRINK_SLACK = 4;
|
|
16137
|
+
var FALLBACK_WIDTH = 300;
|
|
16138
|
+
var FALLBACK_HEIGHT = 150;
|
|
15612
16139
|
var cache6 = /* @__PURE__ */ new Map();
|
|
15613
16140
|
var subscribers = /* @__PURE__ */ new Set();
|
|
15614
|
-
var
|
|
16141
|
+
var decodedVectors = /* @__PURE__ */ new Map();
|
|
16142
|
+
function isVectorImageSrc(src) {
|
|
16143
|
+
if (/^data:image\/svg\+xml[;,]/i.test(src)) return true;
|
|
16144
|
+
if (/^(data|blob):/i.test(src)) return false;
|
|
16145
|
+
return /\.svgz?$/i.test(src.replace(/[?#].*$/, ""));
|
|
16146
|
+
}
|
|
16147
|
+
function requireDom() {
|
|
15615
16148
|
if (typeof Image === "undefined" || typeof createImageBitmap === "undefined") {
|
|
15616
16149
|
throw new Error("weasel imageCache: no DOM image loader in this environment");
|
|
15617
16150
|
}
|
|
16151
|
+
}
|
|
16152
|
+
async function decodeImage(src) {
|
|
15618
16153
|
const img = new Image();
|
|
15619
16154
|
img.crossOrigin = "anonymous";
|
|
15620
16155
|
img.src = src;
|
|
15621
16156
|
await img.decode();
|
|
15622
|
-
return
|
|
16157
|
+
return img;
|
|
16158
|
+
}
|
|
16159
|
+
function decodeVector(src) {
|
|
16160
|
+
let decoded = decodedVectors.get(src);
|
|
16161
|
+
if (!decoded) {
|
|
16162
|
+
decoded = decodeImage(src);
|
|
16163
|
+
decodedVectors.set(src, decoded);
|
|
16164
|
+
decoded.catch(() => decodedVectors.delete(src));
|
|
16165
|
+
}
|
|
16166
|
+
return decoded;
|
|
16167
|
+
}
|
|
16168
|
+
async function rasterizeVector(src, size) {
|
|
16169
|
+
const img = await decodeVector(src);
|
|
16170
|
+
const width = size?.width ?? (img.naturalWidth || FALLBACK_WIDTH);
|
|
16171
|
+
const height = size?.height ?? (img.naturalHeight || FALLBACK_HEIGHT);
|
|
16172
|
+
const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(width, height) : Object.assign(document.createElement("canvas"), { width, height });
|
|
16173
|
+
const c2d = canvas.getContext("2d");
|
|
16174
|
+
if (!c2d) throw new Error("weasel imageCache: no 2D context to rasterize an SVG");
|
|
16175
|
+
c2d.drawImage(img, 0, 0, width, height);
|
|
16176
|
+
return createImageBitmap(canvas);
|
|
16177
|
+
}
|
|
16178
|
+
var defaultLoader = async (src, size) => {
|
|
16179
|
+
requireDom();
|
|
16180
|
+
if (isVectorImageSrc(src)) return rasterizeVector(src, size);
|
|
16181
|
+
return createImageBitmap(await decodeImage(src));
|
|
15623
16182
|
};
|
|
15624
16183
|
var loader = defaultLoader;
|
|
15625
16184
|
function notify() {
|
|
@@ -15631,7 +16190,11 @@ function startLoad(src) {
|
|
|
15631
16190
|
loader(src).then(
|
|
15632
16191
|
(bitmap) => {
|
|
15633
16192
|
if (cache6.get(src) !== entry) return;
|
|
15634
|
-
cache6.set(src, {
|
|
16193
|
+
cache6.set(src, {
|
|
16194
|
+
status: "ready",
|
|
16195
|
+
bitmap,
|
|
16196
|
+
...isVectorImageSrc(src) ? { vector: { natural: { width: bitmap.width, height: bitmap.height }, scale: 1, failed: /* @__PURE__ */ new Set() } } : {}
|
|
16197
|
+
});
|
|
15635
16198
|
notify();
|
|
15636
16199
|
},
|
|
15637
16200
|
() => {
|
|
@@ -15641,11 +16204,54 @@ function startLoad(src) {
|
|
|
15641
16204
|
}
|
|
15642
16205
|
);
|
|
15643
16206
|
}
|
|
15644
|
-
function
|
|
16207
|
+
function rasterScaleFor(natural, drawnAt) {
|
|
16208
|
+
const need = Math.max(drawnAt.width / natural.width, drawnAt.height / natural.height);
|
|
16209
|
+
if (!(need > 0) || !Number.isFinite(need)) return void 0;
|
|
16210
|
+
const side = maxImageTextureSide();
|
|
16211
|
+
const cap = Math.min(
|
|
16212
|
+
side / natural.width,
|
|
16213
|
+
side / natural.height,
|
|
16214
|
+
Math.sqrt(MAX_VECTOR_RASTER_PIXELS / (natural.width * natural.height))
|
|
16215
|
+
);
|
|
16216
|
+
return Math.min(2 ** Math.ceil(Math.log2(need)), cap);
|
|
16217
|
+
}
|
|
16218
|
+
function maybeReraster(src, entry, v, drawnAt) {
|
|
16219
|
+
if (v.pending !== void 0) return;
|
|
16220
|
+
const target = rasterScaleFor(v.natural, drawnAt);
|
|
16221
|
+
if (target === void 0) return;
|
|
16222
|
+
const grow = target > v.scale;
|
|
16223
|
+
if (!grow && target * SHRINK_SLACK > v.scale) return;
|
|
16224
|
+
for (const f of v.failed) if (target === f || grow && f > v.scale && target >= f) return;
|
|
16225
|
+
v.pending = target;
|
|
16226
|
+
const size = {
|
|
16227
|
+
width: Math.max(1, Math.round(v.natural.width * target)),
|
|
16228
|
+
height: Math.max(1, Math.round(v.natural.height * target))
|
|
16229
|
+
};
|
|
16230
|
+
loader(src, size).then(
|
|
16231
|
+
(bitmap) => {
|
|
16232
|
+
if (cache6.get(src) !== entry) return;
|
|
16233
|
+
v.pending = void 0;
|
|
16234
|
+
const old = entry.bitmap;
|
|
16235
|
+
entry.bitmap = bitmap;
|
|
16236
|
+
v.scale = target;
|
|
16237
|
+
if (old && old !== bitmap) releaseImageSource(old);
|
|
16238
|
+
notify();
|
|
16239
|
+
},
|
|
16240
|
+
() => {
|
|
16241
|
+
if (cache6.get(src) !== entry) return;
|
|
16242
|
+
v.pending = void 0;
|
|
16243
|
+
v.failed.add(target);
|
|
16244
|
+
}
|
|
16245
|
+
);
|
|
16246
|
+
}
|
|
16247
|
+
function getImageBitmap(src, drawnAt) {
|
|
15645
16248
|
const entry = cache6.get(src);
|
|
15646
|
-
if (entry)
|
|
15647
|
-
|
|
15648
|
-
|
|
16249
|
+
if (!entry) {
|
|
16250
|
+
startLoad(src);
|
|
16251
|
+
return void 0;
|
|
16252
|
+
}
|
|
16253
|
+
if (drawnAt && entry.vector) maybeReraster(src, entry, entry.vector, drawnAt);
|
|
16254
|
+
return entry.bitmap;
|
|
15649
16255
|
}
|
|
15650
16256
|
function imageStatus(src) {
|
|
15651
16257
|
return cache6.get(src)?.status ?? "idle";
|
|
@@ -15967,6 +16573,21 @@ function pathForShape(d, p) {
|
|
|
15967
16573
|
return { kind: "rect", x: p.x, y: p.y, width: p.width, height: p.height };
|
|
15968
16574
|
}
|
|
15969
16575
|
}
|
|
16576
|
+
function sourceInPixels(s, bmp) {
|
|
16577
|
+
return {
|
|
16578
|
+
x: s.x * bmp.width,
|
|
16579
|
+
y: s.y * bmp.height,
|
|
16580
|
+
w: s.width * bmp.width,
|
|
16581
|
+
h: s.height * bmp.height
|
|
16582
|
+
};
|
|
16583
|
+
}
|
|
16584
|
+
function drawnSize(pose, source, pixelScale) {
|
|
16585
|
+
if (!(pixelScale && pixelScale > 0)) return void 0;
|
|
16586
|
+
return {
|
|
16587
|
+
width: Math.abs(pose.width) * pixelScale / (source?.width || 1),
|
|
16588
|
+
height: Math.abs(pose.height) * pixelScale / (source?.height || 1)
|
|
16589
|
+
};
|
|
16590
|
+
}
|
|
15970
16591
|
var IMAGE_PAINTER = {
|
|
15971
16592
|
id: "kit:image",
|
|
15972
16593
|
matches: (node) => {
|
|
@@ -15977,7 +16598,7 @@ var IMAGE_PAINTER = {
|
|
|
15977
16598
|
paint: (node, pose, ctx) => {
|
|
15978
16599
|
const d = node.data;
|
|
15979
16600
|
const p = pose;
|
|
15980
|
-
const bmp = ctx?.resolveImage ? ctx.resolveImage(node) : getImageBitmap(d.image.src);
|
|
16601
|
+
const bmp = ctx?.resolveImage ? ctx.resolveImage(node) : getImageBitmap(d.image.src, drawnSize(p, d.image.source, ctx?.pixelScale));
|
|
15981
16602
|
if (bmp) {
|
|
15982
16603
|
return [{
|
|
15983
16604
|
kind: "image",
|
|
@@ -15986,7 +16607,10 @@ var IMAGE_PAINTER = {
|
|
|
15986
16607
|
y: p.y,
|
|
15987
16608
|
w: p.width,
|
|
15988
16609
|
h: p.height,
|
|
15989
|
-
...d.image.opacity !== void 0 ? { opacity: d.image.opacity } : {}
|
|
16610
|
+
...d.image.opacity !== void 0 ? { opacity: d.image.opacity } : {},
|
|
16611
|
+
...d.image.source ? { source: sourceInPixels(d.image.source, bmp) } : {},
|
|
16612
|
+
...d.image.flipX ? { flipX: true } : {},
|
|
16613
|
+
...d.image.flipY ? { flipY: true } : {}
|
|
15990
16614
|
}];
|
|
15991
16615
|
}
|
|
15992
16616
|
const error = ctx?.resolveImage ? false : imageStatus(d.image.src) === "error";
|
|
@@ -16232,7 +16856,11 @@ function scenePickSource(scene, opts = {}) {
|
|
|
16232
16856
|
const n = scene.get(asNodeId(id));
|
|
16233
16857
|
return n ? effectivePose(scene, n) : void 0;
|
|
16234
16858
|
},
|
|
16235
|
-
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null
|
|
16859
|
+
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
|
|
16860
|
+
definesFrame: (id) => {
|
|
16861
|
+
const n = scene.get(asNodeId(id));
|
|
16862
|
+
return n === void 0 || definesFrame(n);
|
|
16863
|
+
}
|
|
16236
16864
|
} : null;
|
|
16237
16865
|
const poseOf = getPose ? (node) => getPose(node.id) : chain ? (node) => composeWorldPose(chain, node.id, composition.compose) : (node) => effectivePose(scene, node);
|
|
16238
16866
|
const childrenOf = (id) => scene.childrenOf(id);
|
|
@@ -16683,6 +17311,92 @@ function useTextTool() {
|
|
|
16683
17311
|
[]
|
|
16684
17312
|
);
|
|
16685
17313
|
}
|
|
17314
|
+
var PERCENT = {
|
|
17315
|
+
toDisplay: (v) => Math.round(v * 1e3) / 10,
|
|
17316
|
+
// Rounded rather than divided: 33.3 / 100 is 0.33299999999999996, and that
|
|
17317
|
+
// dust would be written into the document and shown back on the next read.
|
|
17318
|
+
fromDisplay: (v) => Math.round(v * 1e3) / 1e5,
|
|
17319
|
+
suffix: "%"
|
|
17320
|
+
};
|
|
17321
|
+
var flag = (name, icon) => ({
|
|
17322
|
+
kind: "boolean",
|
|
17323
|
+
name,
|
|
17324
|
+
description: `${name} for the characters in the range.`,
|
|
17325
|
+
icon,
|
|
17326
|
+
control: "toggle",
|
|
17327
|
+
pair: "Character style",
|
|
17328
|
+
default: false
|
|
17329
|
+
});
|
|
17330
|
+
useTextTool.options = {
|
|
17331
|
+
name: "Text",
|
|
17332
|
+
description: "Character styling for the selected range.",
|
|
17333
|
+
children: {
|
|
17334
|
+
bold: flag("Bold", "bold"),
|
|
17335
|
+
italic: flag("Italic", "italic"),
|
|
17336
|
+
underline: flag("Underline", "underline"),
|
|
17337
|
+
strikethrough: flag("Strikethrough", "strikethrough"),
|
|
17338
|
+
overline: flag("Overline", "overline"),
|
|
17339
|
+
// Its own kind rather than an enum: the two values are exclusive *and*
|
|
17340
|
+
// absent is a third state an enum cannot name, so clicking the lit
|
|
17341
|
+
// segment has to clear it. The host draws it and owns that rule.
|
|
17342
|
+
script: {
|
|
17343
|
+
kind: "script",
|
|
17344
|
+
name: "Script",
|
|
17345
|
+
description: "Raise or lower the range off the baseline, at a smaller size.",
|
|
17346
|
+
default: void 0
|
|
17347
|
+
},
|
|
17348
|
+
textTransform: {
|
|
17349
|
+
kind: "enum",
|
|
17350
|
+
name: "Case",
|
|
17351
|
+
description: "Draw the range in capitals, lowercase or title case. The text itself is unchanged.",
|
|
17352
|
+
control: "toggle",
|
|
17353
|
+
default: "none",
|
|
17354
|
+
options: [{ value: "none", label: "None", short: "\u2013" }, { value: "uppercase", label: "Uppercase", short: "AA" }, { value: "lowercase", label: "Lowercase", short: "aa" }, { value: "capitalize", label: "Capitalize", short: "Aa" }]
|
|
17355
|
+
},
|
|
17356
|
+
fontFamily: {
|
|
17357
|
+
kind: "font-family",
|
|
17358
|
+
name: "Font",
|
|
17359
|
+
description: "Typeface for the range.",
|
|
17360
|
+
default: void 0
|
|
17361
|
+
},
|
|
17362
|
+
fontSize: { kind: "number", name: "Size", description: "Type size.", min: 1, step: 1, default: 16 },
|
|
17363
|
+
letterSpacing: {
|
|
17364
|
+
kind: "number",
|
|
17365
|
+
name: "Tracking",
|
|
17366
|
+
// The typographic term, short enough for a strip; `name` stays the
|
|
17367
|
+
// accessible one.
|
|
17368
|
+
short: "VA",
|
|
17369
|
+
description: "Space added between characters.",
|
|
17370
|
+
step: 0.1,
|
|
17371
|
+
default: 0
|
|
17372
|
+
},
|
|
17373
|
+
baselineShift: {
|
|
17374
|
+
kind: "number",
|
|
17375
|
+
name: "Baseline shift",
|
|
17376
|
+
short: "Shift",
|
|
17377
|
+
description: "Raise or lower the range, in ems of the inherited size.",
|
|
17378
|
+
step: 1e-3,
|
|
17379
|
+
unit: PERCENT,
|
|
17380
|
+
default: 0
|
|
17381
|
+
},
|
|
17382
|
+
fontScale: {
|
|
17383
|
+
kind: "number",
|
|
17384
|
+
name: "Scale",
|
|
17385
|
+
description: "Size of the range against the inherited size.",
|
|
17386
|
+
min: 0.01,
|
|
17387
|
+
step: 1e-3,
|
|
17388
|
+
unit: PERCENT,
|
|
17389
|
+
default: 1
|
|
17390
|
+
},
|
|
17391
|
+
fill: {
|
|
17392
|
+
kind: "color",
|
|
17393
|
+
name: "Color",
|
|
17394
|
+
description: "Ink for the range.",
|
|
17395
|
+
alpha: true,
|
|
17396
|
+
default: "#000000"
|
|
17397
|
+
}
|
|
17398
|
+
}
|
|
17399
|
+
};
|
|
16686
17400
|
|
|
16687
17401
|
// src/features/paths/nearestAnchor.ts
|
|
16688
17402
|
function nearestPathAnchor(paths, point, px, scale, accept) {
|
|
@@ -16748,44 +17462,9 @@ function anchorCount(s) {
|
|
|
16748
17462
|
return (s.current ? s.current.anchors.length : 0) + s.finishedSubpaths.reduce((n, sp) => n + sp.anchors.length, 0);
|
|
16749
17463
|
}
|
|
16750
17464
|
function buildPath(subpaths, trailing) {
|
|
16751
|
-
const b = new PathBuilder();
|
|
16752
17465
|
const all = [...subpaths];
|
|
16753
17466
|
if (trailing && trailing.anchors.length > 0) all.push(trailing);
|
|
16754
|
-
|
|
16755
|
-
if (sp.anchors.length === 0) continue;
|
|
16756
|
-
const first = sp.anchors[0];
|
|
16757
|
-
b.moveTo(first.x, first.y);
|
|
16758
|
-
for (let i = 1; i < sp.anchors.length; i++) {
|
|
16759
|
-
const prev = sp.anchors[i - 1];
|
|
16760
|
-
const curr = sp.anchors[i];
|
|
16761
|
-
const out = prev.outHandle;
|
|
16762
|
-
const inH = curr.inHandle ?? mirrorHandle(prev, out);
|
|
16763
|
-
if (out || curr.inHandle) {
|
|
16764
|
-
const c1 = out ?? { x: prev.x, y: prev.y };
|
|
16765
|
-
const c2 = inH ?? { x: curr.x, y: curr.y };
|
|
16766
|
-
b.curveTo(c1.x, c1.y, c2.x, c2.y, curr.x, curr.y);
|
|
16767
|
-
} else {
|
|
16768
|
-
b.lineTo(curr.x, curr.y);
|
|
16769
|
-
}
|
|
16770
|
-
}
|
|
16771
|
-
if (sp.closed) {
|
|
16772
|
-
const last = sp.anchors[sp.anchors.length - 1];
|
|
16773
|
-
const first0 = sp.anchors[0];
|
|
16774
|
-
const out = last.outHandle;
|
|
16775
|
-
const inH = first0.inHandle ?? mirrorHandle(last, out);
|
|
16776
|
-
if (out || first0.inHandle) {
|
|
16777
|
-
const c1 = out ?? { x: last.x, y: last.y };
|
|
16778
|
-
const c2 = inH ?? { x: first0.x, y: first0.y };
|
|
16779
|
-
b.curveTo(c1.x, c1.y, c2.x, c2.y, first0.x, first0.y);
|
|
16780
|
-
}
|
|
16781
|
-
b.close();
|
|
16782
|
-
}
|
|
16783
|
-
}
|
|
16784
|
-
return b.build();
|
|
16785
|
-
}
|
|
16786
|
-
function mirrorHandle(anchor, out) {
|
|
16787
|
-
if (!out || anchor.altBroken) return void 0;
|
|
16788
|
-
return { x: 2 * anchor.x - out.x, y: 2 * anchor.y - out.y };
|
|
17467
|
+
return anchorsToPath(all.map((sp) => sp.anchors), all.map((sp) => sp.closed));
|
|
16789
17468
|
}
|
|
16790
17469
|
function usePenTool(options) {
|
|
16791
17470
|
const { wrapPath, adapter, autoSelect = true, autoCommitOnClose = true, closeHitRadius = 8, anchorSnapRadius = 8, snapPoint: snapPoint2 } = options;
|
|
@@ -16830,6 +17509,64 @@ function usePenTool(options) {
|
|
|
16830
17509
|
if (optsRef.current.autoSelect) optsRef.current.adapter.setSelection([id]);
|
|
16831
17510
|
resetScratch(s);
|
|
16832
17511
|
}, [commitContinuation]);
|
|
17512
|
+
const joinable = useCallback((s, deps, hit) => {
|
|
17513
|
+
if (!hit || hit.end === null || !s.current || s.current.anchors.length === 0) return false;
|
|
17514
|
+
const cont = s.continuing;
|
|
17515
|
+
if (cont && hit.id === cont.id && hit.sub === cont.sub) return false;
|
|
17516
|
+
const scene = deps.scene;
|
|
17517
|
+
if (!scene && !deps.applyOps) return false;
|
|
17518
|
+
if (!(cont ? deps.editAnchors.editOps : optsRef.current.adapter.makeNode)) return false;
|
|
17519
|
+
return cont?.id === hit.id || scene?.get(hit.id) !== void 0;
|
|
17520
|
+
}, []);
|
|
17521
|
+
const commitJoin = useCallback((s, deps, hit) => {
|
|
17522
|
+
const edit = deps.editAnchors;
|
|
17523
|
+
const scene = deps.scene;
|
|
17524
|
+
const current = s.current;
|
|
17525
|
+
const { anchors: added, closed: addedClosed } = pathToAnchors(buildPath([...s.finishedSubpaths, current], null));
|
|
17526
|
+
const other = edit.getEditablePath(hit.id);
|
|
17527
|
+
const otherSet = pathToAnchors(other);
|
|
17528
|
+
const tail = hit.end === "last" ? reverseAnchors(otherSet.anchors[hit.sub]) : otherSet.anchors[hit.sub];
|
|
17529
|
+
const joined = added[added.length - 1];
|
|
17530
|
+
const joint = joined[joined.length - 1];
|
|
17531
|
+
const out = current.anchors[current.anchors.length - 1].outHandle ?? tail[0].outHandle;
|
|
17532
|
+
if (out) joint.outHandle = { ...out };
|
|
17533
|
+
joined.push(...tail.slice(1));
|
|
17534
|
+
const cont = s.continuing;
|
|
17535
|
+
const sameNode = cont?.id === hit.id;
|
|
17536
|
+
const rest = sameNode ? { anchors: [], closed: [] } : {
|
|
17537
|
+
anchors: otherSet.anchors.filter((_, i) => i !== hit.sub),
|
|
17538
|
+
closed: otherSet.closed.filter((_, i) => i !== hit.sub)
|
|
17539
|
+
};
|
|
17540
|
+
const label = "Join paths";
|
|
17541
|
+
const ops = [];
|
|
17542
|
+
let keptId;
|
|
17543
|
+
if (cont) {
|
|
17544
|
+
if (cont.reversed) added[0] = reverseAnchors(added[0]);
|
|
17545
|
+
const set = pathToAnchors(cont.original);
|
|
17546
|
+
set.anchors.splice(cont.sub, 1, added[0]);
|
|
17547
|
+
set.closed.splice(cont.sub, 1, addedClosed[0]);
|
|
17548
|
+
set.anchors.push(...added.slice(1), ...rest.anchors);
|
|
17549
|
+
set.closed.push(...addedClosed.slice(1), ...rest.closed);
|
|
17550
|
+
if (sameNode) {
|
|
17551
|
+
set.anchors.splice(hit.sub, 1);
|
|
17552
|
+
set.closed.splice(hit.sub, 1);
|
|
17553
|
+
}
|
|
17554
|
+
const next = { ...anchorsToPath(set.anchors, set.closed), fillRule: cont.original.fillRule };
|
|
17555
|
+
ops.push(...edit.editOps(cont.id, next, label));
|
|
17556
|
+
keptId = cont.id;
|
|
17557
|
+
} else {
|
|
17558
|
+
const path = anchorsToPath([...added, ...rest.anchors], [...addedClosed, ...rest.closed]);
|
|
17559
|
+
const node = optsRef.current.adapter.makeNode(optsRef.current.wrapPath(path, { closed: false }));
|
|
17560
|
+
ops.push(createInsertOp({ node }));
|
|
17561
|
+
keptId = node.id;
|
|
17562
|
+
}
|
|
17563
|
+
if (!sameNode) ops.push(createDeleteOp({ node: scene.get(hit.id) }));
|
|
17564
|
+
const applyOps = deps.applyOps;
|
|
17565
|
+
if (applyOps) applyOps(ops, label);
|
|
17566
|
+
else scene.applyBatch(ops, label, defaultCommitAdapter(scene));
|
|
17567
|
+
if (optsRef.current.autoSelect) optsRef.current.adapter.setSelection([keptId]);
|
|
17568
|
+
resetScratch(s);
|
|
17569
|
+
}, []);
|
|
16833
17570
|
const pickUpEndpoint = useCallback((deps, wx, wy) => {
|
|
16834
17571
|
const scratch = scratchRef.current;
|
|
16835
17572
|
if (scratch.current || scratch.finishedSubpaths.length > 0) return false;
|
|
@@ -16852,9 +17589,9 @@ function usePenTool(options) {
|
|
|
16852
17589
|
const radius = optsRef.current.anchorSnapRadius;
|
|
16853
17590
|
if (radius > 0) {
|
|
16854
17591
|
const hit = nearestPathAnchor(pathsNear(deps, x, y, radius), { x, y }, radius, viewScale(deps));
|
|
16855
|
-
if (hit) return { x: hit.x, y: hit.y };
|
|
17592
|
+
if (hit) return { x: hit.x, y: hit.y, hit };
|
|
16856
17593
|
}
|
|
16857
|
-
return snap2(x, y);
|
|
17594
|
+
return { ...snap2(x, y), hit: null };
|
|
16858
17595
|
}, [snap2]);
|
|
16859
17596
|
const withinCloseRadius = useCallback(
|
|
16860
17597
|
(deps, ax, ay, wx, wy) => {
|
|
@@ -16869,7 +17606,7 @@ function usePenTool(options) {
|
|
|
16869
17606
|
id: "pen.placeAnchor",
|
|
16870
17607
|
label: "Pen \u2014 place anchor",
|
|
16871
17608
|
eligible: { capability: "creates-paths" },
|
|
16872
|
-
requires: ["view", "areaSelect", "editAnchors"],
|
|
17609
|
+
requires: ["view", "areaSelect", "editAnchors", "scene", "applyOps"],
|
|
16873
17610
|
invoker: {
|
|
16874
17611
|
timing: "immediate",
|
|
16875
17612
|
run: (deps, params) => {
|
|
@@ -16879,7 +17616,7 @@ function usePenTool(options) {
|
|
|
16879
17616
|
forceRenderRef.current();
|
|
16880
17617
|
return;
|
|
16881
17618
|
}
|
|
16882
|
-
const { x: wx, y: wy } = placeAt(deps, p.pressX, p.pressY);
|
|
17619
|
+
const { x: wx, y: wy, hit } = placeAt(deps, p.pressX, p.pressY);
|
|
16883
17620
|
const scratch = s();
|
|
16884
17621
|
if (scratch.current && scratch.current.anchors.length >= 3) {
|
|
16885
17622
|
const first = scratch.current.anchors[0];
|
|
@@ -16893,8 +17630,10 @@ function usePenTool(options) {
|
|
|
16893
17630
|
return;
|
|
16894
17631
|
}
|
|
16895
17632
|
}
|
|
17633
|
+
const join = joinable(scratch, deps, hit);
|
|
16896
17634
|
if (!scratch.current) scratch.current = { anchors: [], closed: false };
|
|
16897
17635
|
scratch.current.anchors.push({ x: wx, y: wy });
|
|
17636
|
+
if (join) commitJoin(scratch, deps, hit);
|
|
16898
17637
|
forceRenderRef.current();
|
|
16899
17638
|
}
|
|
16900
17639
|
}
|
|
@@ -16926,32 +17665,35 @@ function usePenTool(options) {
|
|
|
16926
17665
|
id: "pen.dragHandle",
|
|
16927
17666
|
label: "Pen \u2014 drag out a bezier handle",
|
|
16928
17667
|
eligible: { capability: "creates-paths" },
|
|
16929
|
-
requires: ["view", "areaSelect", "editAnchors"],
|
|
17668
|
+
requires: ["view", "areaSelect", "editAnchors", "scene", "applyOps"],
|
|
16930
17669
|
invoker: {
|
|
16931
17670
|
timing: "ongoing",
|
|
16932
17671
|
start: (ctx) => {
|
|
16933
17672
|
const scratch = s();
|
|
16934
17673
|
const origin = ctx.drag?.start ?? ctx.world;
|
|
16935
17674
|
const pickedUp = pickUpEndpoint(ctx.deps, origin.x, origin.y);
|
|
17675
|
+
let joinHit = null;
|
|
16936
17676
|
if (!pickedUp) {
|
|
16937
|
-
const { x: ax, y: ay } = placeAt(ctx.deps, origin.x, origin.y);
|
|
17677
|
+
const { x: ax, y: ay, hit } = placeAt(ctx.deps, origin.x, origin.y);
|
|
17678
|
+
if (joinable(scratch, ctx.deps, hit)) joinHit = hit;
|
|
16938
17679
|
if (!scratch.current) scratch.current = { anchors: [], closed: false };
|
|
16939
17680
|
scratch.current.anchors.push({ x: ax, y: ay });
|
|
16940
17681
|
}
|
|
16941
17682
|
scratch.draggingHandleAt = scratch.current.anchors.length - 1;
|
|
16942
|
-
applyOutHandle(scratch, ctx.world, ctx.modifiers.shift, optsRef.current.snapPoint);
|
|
16943
17683
|
if (ctx.modifiers.alt) {
|
|
16944
17684
|
scratch.current.anchors[scratch.draggingHandleAt].altBroken = true;
|
|
16945
17685
|
}
|
|
17686
|
+
const linked = !pickedUp;
|
|
17687
|
+
applyOutHandle(scratch, ctx.world, ctx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
16946
17688
|
forceRenderRef.current();
|
|
16947
17689
|
return {
|
|
16948
17690
|
onMove: (moveCtx) => {
|
|
16949
17691
|
const sm = s();
|
|
16950
17692
|
if (sm.draggingHandleAt === null) return;
|
|
16951
|
-
applyOutHandle(sm, moveCtx.world, moveCtx.modifiers.shift, optsRef.current.snapPoint);
|
|
16952
17693
|
if (moveCtx.modifiers.alt && sm.current) {
|
|
16953
17694
|
sm.current.anchors[sm.draggingHandleAt].altBroken = true;
|
|
16954
17695
|
}
|
|
17696
|
+
applyOutHandle(sm, moveCtx.world, moveCtx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
16955
17697
|
forceRenderRef.current();
|
|
16956
17698
|
},
|
|
16957
17699
|
onEnd: (endCtx, reason) => {
|
|
@@ -16968,12 +17710,13 @@ function usePenTool(options) {
|
|
|
16968
17710
|
return;
|
|
16969
17711
|
}
|
|
16970
17712
|
if (se.draggingHandleAt !== null) {
|
|
16971
|
-
applyOutHandle(se, endCtx.world, endCtx.modifiers.shift, optsRef.current.snapPoint);
|
|
16972
17713
|
if (endCtx.modifiers.alt && se.current) {
|
|
16973
17714
|
se.current.anchors[se.draggingHandleAt].altBroken = true;
|
|
16974
17715
|
}
|
|
17716
|
+
applyOutHandle(se, endCtx.world, endCtx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
16975
17717
|
se.draggingHandleAt = null;
|
|
16976
17718
|
}
|
|
17719
|
+
if (joinHit && se.current) commitJoin(se, ctx.deps, joinHit);
|
|
16977
17720
|
forceRenderRef.current();
|
|
16978
17721
|
}
|
|
16979
17722
|
};
|
|
@@ -17014,7 +17757,7 @@ function usePenTool(options) {
|
|
|
17014
17757
|
}
|
|
17015
17758
|
}
|
|
17016
17759
|
];
|
|
17017
|
-
}, [commit, placeAt, withinCloseRadius, pickUpEndpoint]);
|
|
17760
|
+
}, [commit, placeAt, withinCloseRadius, pickUpEndpoint, joinable, commitJoin]);
|
|
17018
17761
|
return useMemo(() => {
|
|
17019
17762
|
return defineTool({
|
|
17020
17763
|
id: "pen",
|
|
@@ -17072,7 +17815,7 @@ usePenTool.prefs = {
|
|
|
17072
17815
|
}
|
|
17073
17816
|
}
|
|
17074
17817
|
};
|
|
17075
|
-
function applyOutHandle(s, target, shift, snap2) {
|
|
17818
|
+
function applyOutHandle(s, target, shift, linked, snap2) {
|
|
17076
17819
|
if (s.current === null || s.draggingHandleAt === null) return;
|
|
17077
17820
|
const anchor = s.current.anchors[s.draggingHandleAt];
|
|
17078
17821
|
const to = snap2 ? snap2(target) : target;
|
|
@@ -17084,6 +17827,7 @@ function applyOutHandle(s, target, shift, snap2) {
|
|
|
17084
17827
|
dy = c.dy;
|
|
17085
17828
|
}
|
|
17086
17829
|
anchor.outHandle = { x: anchor.x + dx, y: anchor.y + dy };
|
|
17830
|
+
if (linked && !anchor.altBroken) anchor.inHandle = { x: anchor.x - dx, y: anchor.y - dy };
|
|
17087
17831
|
}
|
|
17088
17832
|
function viewScale(deps) {
|
|
17089
17833
|
return deps.view?.get().scale ?? { x: 1, y: 1 };
|
|
@@ -17536,9 +18280,24 @@ function usePencilTool() {
|
|
|
17536
18280
|
}
|
|
17537
18281
|
|
|
17538
18282
|
// src/canvas/defaultDrawOne.ts
|
|
17539
|
-
function
|
|
18283
|
+
function pixelScaleOf(view) {
|
|
18284
|
+
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
18285
|
+
return Math.max(Math.abs(view.scale.x), Math.abs(view.scale.y)) * dpr;
|
|
18286
|
+
}
|
|
18287
|
+
var lastView;
|
|
18288
|
+
var lastViewCtx = {};
|
|
18289
|
+
function viewOnlyCtx(view) {
|
|
18290
|
+
const pixelScale = pixelScaleOf(view);
|
|
18291
|
+
if (view !== lastView || lastViewCtx.pixelScale !== pixelScale) {
|
|
18292
|
+
lastView = view;
|
|
18293
|
+
lastViewCtx = { pixelScale };
|
|
18294
|
+
}
|
|
18295
|
+
return lastViewCtx;
|
|
18296
|
+
}
|
|
18297
|
+
function defaultDrawOne(node, pose, view, ctx) {
|
|
17540
18298
|
const painter = findNodeShape(node);
|
|
17541
|
-
const
|
|
18299
|
+
const paintCtx = !view || ctx?.pixelScale !== void 0 ? ctx : ctx ? { ...ctx, pixelScale: pixelScaleOf(view) } : viewOnlyCtx(view);
|
|
18300
|
+
const primary = painter ? painter.paint(node, pose, paintCtx) : [];
|
|
17542
18301
|
const data = node.data;
|
|
17543
18302
|
if (data?.label && data.text == null) {
|
|
17544
18303
|
const p = pose;
|
|
@@ -18604,11 +19363,11 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf) {
|
|
|
18604
19363
|
const buckets = /* @__PURE__ */ new Map();
|
|
18605
19364
|
for (const l of layers) buckets.set(l.id, []);
|
|
18606
19365
|
const compose2 = adapter.composePose?.bind(adapter);
|
|
18607
|
-
function visit(id, ancestorClips,
|
|
19366
|
+
function visit(id, ancestorClips, parentFrame) {
|
|
18608
19367
|
const node = adapter.getNode(id);
|
|
18609
19368
|
if (!node) return;
|
|
18610
19369
|
const local = adapter.getPose(id);
|
|
18611
|
-
const pose = compose2 &&
|
|
19370
|
+
const pose = compose2 && parentFrame !== null ? compose2(parentFrame, local) : local;
|
|
18612
19371
|
const paints = forLayer === void 0 || node.layer === forLayer;
|
|
18613
19372
|
const self = paints ? drawOne(node, pose, view) : [];
|
|
18614
19373
|
let ownClips = ancestorClips;
|
|
@@ -18632,7 +19391,8 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf) {
|
|
|
18632
19391
|
bucket.push(ownClips.length > 0 ? wrapInClips(self, ownClips) : { kind: "group", children: self });
|
|
18633
19392
|
}
|
|
18634
19393
|
}
|
|
18635
|
-
|
|
19394
|
+
const childFrame = definesFrame(node) ? pose : parentFrame;
|
|
19395
|
+
for (const cid of adapter.getChildren(id)) visit(cid, ownClips, childFrame);
|
|
18636
19396
|
}
|
|
18637
19397
|
for (const rootId of adapter.getChildren(null)) visit(rootId, [], null);
|
|
18638
19398
|
const out = [];
|
|
@@ -19842,219 +20602,641 @@ function wireSceneSlotToScene(slot, scene, alphaFor, colorOverrides) {
|
|
|
19842
20602
|
alphaFor: composeAlphaFor(scene, alphaFor)
|
|
19843
20603
|
};
|
|
19844
20604
|
}
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
|
|
19849
|
-
|
|
19850
|
-
let generation2 = 0;
|
|
19851
|
-
function invalidate(id) {
|
|
19852
|
-
const node = getNode(id);
|
|
19853
|
-
if (node) dropPoseKeyedMemoSlots(node);
|
|
19854
|
-
onInvalidate?.(id);
|
|
20605
|
+
function flattenVerts2(poly2) {
|
|
20606
|
+
const flat = new Array(poly2.length * 2);
|
|
20607
|
+
for (let i = 0; i < poly2.length; i++) {
|
|
20608
|
+
flat[i * 2] = poly2[i].x;
|
|
20609
|
+
flat[i * 2 + 1] = poly2[i].y;
|
|
19855
20610
|
}
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
|
|
20611
|
+
return flat;
|
|
20612
|
+
}
|
|
20613
|
+
function polygonContainsRectCenter(poly2, rect) {
|
|
20614
|
+
return pointInPolygon(flattenVerts2(poly2), rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
20615
|
+
}
|
|
20616
|
+
function polygonContainsRect(poly2, rect) {
|
|
20617
|
+
if (poly2.length < 3) return false;
|
|
20618
|
+
const flat = flattenVerts2(poly2);
|
|
20619
|
+
if (!pointInPolygon(flat, rect.x, rect.y)) return false;
|
|
20620
|
+
if (!pointInPolygon(flat, rect.x + rect.width, rect.y)) return false;
|
|
20621
|
+
if (!pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return false;
|
|
20622
|
+
if (!pointInPolygon(flat, rect.x, rect.y + rect.height)) return false;
|
|
20623
|
+
return !anyPolyEdgeCrossesRect(poly2, rect);
|
|
20624
|
+
}
|
|
20625
|
+
function polygonIntersectsRect(poly2, rect) {
|
|
20626
|
+
if (poly2.length < 3) return false;
|
|
20627
|
+
const ab = polygonAabb(poly2);
|
|
20628
|
+
if (ab.x + ab.width < rect.x || ab.x > rect.x + rect.width) return false;
|
|
20629
|
+
if (ab.y + ab.height < rect.y || ab.y > rect.y + rect.height) return false;
|
|
20630
|
+
for (const v of poly2) {
|
|
20631
|
+
if (v.x >= rect.x && v.x <= rect.x + rect.width && v.y >= rect.y && v.y <= rect.y + rect.height) return true;
|
|
19859
20632
|
}
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
20633
|
+
const flat = flattenVerts2(poly2);
|
|
20634
|
+
if (pointInPolygon(flat, rect.x, rect.y)) return true;
|
|
20635
|
+
if (pointInPolygon(flat, rect.x + rect.width, rect.y)) return true;
|
|
20636
|
+
if (pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return true;
|
|
20637
|
+
if (pointInPolygon(flat, rect.x, rect.y + rect.height)) return true;
|
|
20638
|
+
return anyPolyEdgeCrossesRect(poly2, rect);
|
|
20639
|
+
}
|
|
20640
|
+
function polygonAabb(poly2) {
|
|
20641
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
20642
|
+
for (const v of poly2) {
|
|
20643
|
+
if (v.x < minX) minX = v.x;
|
|
20644
|
+
if (v.y < minY) minY = v.y;
|
|
20645
|
+
if (v.x > maxX) maxX = v.x;
|
|
20646
|
+
if (v.y > maxY) maxY = v.y;
|
|
20647
|
+
}
|
|
20648
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
20649
|
+
}
|
|
20650
|
+
function anyPolyEdgeCrossesRect(poly2, rect) {
|
|
20651
|
+
const r0x = rect.x, r0y = rect.y;
|
|
20652
|
+
const r1x = rect.x + rect.width, r1y = rect.y;
|
|
20653
|
+
const r2x = rect.x + rect.width, r2y = rect.y + rect.height;
|
|
20654
|
+
const r3x = rect.x, r3y = rect.y + rect.height;
|
|
20655
|
+
for (let i = 0, j = poly2.length - 1; i < poly2.length; j = i++) {
|
|
20656
|
+
const a = poly2[j], b = poly2[i];
|
|
20657
|
+
if (segmentsCross(a.x, a.y, b.x, b.y, r0x, r0y, r1x, r1y)) return true;
|
|
20658
|
+
if (segmentsCross(a.x, a.y, b.x, b.y, r1x, r1y, r2x, r2y)) return true;
|
|
20659
|
+
if (segmentsCross(a.x, a.y, b.x, b.y, r2x, r2y, r3x, r3y)) return true;
|
|
20660
|
+
if (segmentsCross(a.x, a.y, b.x, b.y, r3x, r3y, r0x, r0y)) return true;
|
|
20661
|
+
}
|
|
20662
|
+
return false;
|
|
20663
|
+
}
|
|
20664
|
+
|
|
20665
|
+
// src/canvas/sceneAdapter.ts
|
|
20666
|
+
function walkClipAware(scene, poseBounds, nodeTest, poseComposition) {
|
|
20667
|
+
return pickWalk(scenePickSource(scene, poseComposition ? { poseComposition } : {}), {
|
|
20668
|
+
hits: (node, pose) => nodeTest(node, pose),
|
|
20669
|
+
clipAdmits: (clip, _node, pose) => pathIntersectsRect(clip, poseBounds(pose))
|
|
20670
|
+
});
|
|
20671
|
+
}
|
|
20672
|
+
var adapterFallbackIdCounter = 0;
|
|
20673
|
+
function adapterFallbackId() {
|
|
20674
|
+
return `n${(adapterFallbackIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
20675
|
+
}
|
|
20676
|
+
var pasteIdCounter = 0;
|
|
20677
|
+
function pasteNodeId() {
|
|
20678
|
+
return `paste-${(pasteIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
20679
|
+
}
|
|
20680
|
+
function copyField(value) {
|
|
20681
|
+
if (Array.isArray(value)) return [...value];
|
|
20682
|
+
if (value !== null && typeof value === "object") return { ...value };
|
|
20683
|
+
return value;
|
|
20684
|
+
}
|
|
20685
|
+
function sceneToAdapter(scene, options = {}) {
|
|
20686
|
+
const visibleLayers = () => {
|
|
20687
|
+
const out = /* @__PURE__ */ new Set();
|
|
20688
|
+
for (const layer of scene.layers) {
|
|
20689
|
+
if (layer.visible) out.add(layer.id);
|
|
20690
|
+
}
|
|
20691
|
+
return out;
|
|
20692
|
+
};
|
|
20693
|
+
const sel = options.selection;
|
|
20694
|
+
const getSelection = sel?.getSelection ?? sel?.get ?? (() => []);
|
|
20695
|
+
const setSelection = sel?.setSelection ?? sel?.set ?? (() => {
|
|
20696
|
+
});
|
|
20697
|
+
const d = options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
20698
|
+
const poseBounds = (p) => visualBoundsViaDescriptor(p, d);
|
|
20699
|
+
const translate3 = (p, dx, dy, node) => translatePoseViaDescriptor(
|
|
20700
|
+
p,
|
|
20701
|
+
dx,
|
|
20702
|
+
dy,
|
|
20703
|
+
node === void 0 ? d : poseDescriptorForNode(d, node)
|
|
20704
|
+
);
|
|
20705
|
+
const composition = options.poseComposition;
|
|
20706
|
+
const composes = composition !== void 0 && composition.closure !== "identity";
|
|
20707
|
+
if (composes && options.cascadeContainerPose) {
|
|
20708
|
+
throw new Error(
|
|
20709
|
+
"sceneToAdapter: poseComposition and cascadeContainerPose contradict each other. Under a composing strategy a child's pose is already relative to its parent, so the cascade would move every descendant twice. Drop cascadeContainerPose."
|
|
20710
|
+
);
|
|
20711
|
+
}
|
|
20712
|
+
const adapter = {
|
|
20713
|
+
getNode(id) {
|
|
20714
|
+
return scene.get(asNodeId(id));
|
|
19865
20715
|
},
|
|
19866
|
-
|
|
19867
|
-
|
|
20716
|
+
getNodes() {
|
|
20717
|
+
const visible = visibleLayers();
|
|
20718
|
+
const out = [];
|
|
20719
|
+
for (const n of scene.renderOrderNodes()) {
|
|
20720
|
+
if (visible.has(n.layer)) out.push(n);
|
|
20721
|
+
}
|
|
20722
|
+
return out;
|
|
19868
20723
|
},
|
|
19869
|
-
|
|
19870
|
-
|
|
20724
|
+
getPose(id) {
|
|
20725
|
+
const nid = asNodeId(id);
|
|
20726
|
+
const n = scene.get(nid);
|
|
20727
|
+
if (!n) throw new Error(`sceneToAdapter: unknown node "${id}"`);
|
|
20728
|
+
return effectivePose(scene, n);
|
|
19871
20729
|
},
|
|
19872
|
-
|
|
19873
|
-
|
|
20730
|
+
getParent(id) {
|
|
20731
|
+
const n = scene.get(asNodeId(id));
|
|
20732
|
+
return n?.parent ?? null;
|
|
19874
20733
|
},
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
published();
|
|
20734
|
+
definesFrame(id) {
|
|
20735
|
+
const n = scene.get(asNodeId(id));
|
|
20736
|
+
return n === void 0 || definesFrame(n);
|
|
19879
20737
|
},
|
|
19880
|
-
|
|
19881
|
-
if (
|
|
19882
|
-
|
|
19883
|
-
entries.clear();
|
|
19884
|
-
published();
|
|
20738
|
+
getWorldPose(id) {
|
|
20739
|
+
if (!composes) return adapter.getPose(id);
|
|
20740
|
+
return composeWorldPose(adapter, id, composition.compose);
|
|
19885
20741
|
},
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
20742
|
+
// Only present when it would do something: the render walk skips the fold
|
|
20743
|
+
// entirely when this is absent, which is the absolute-pose path.
|
|
20744
|
+
...composes ? { composePose: composition.compose } : {},
|
|
20745
|
+
setPose(id, pose) {
|
|
20746
|
+
if (options.cascadeContainerPose) {
|
|
20747
|
+
const node = scene.get(asNodeId(id));
|
|
20748
|
+
if (node && node.kind === "container") {
|
|
20749
|
+
const g = poseDescriptorForNode(d, node);
|
|
20750
|
+
const before = g.getBounds(node.pose);
|
|
20751
|
+
const after = g.getBounds(pose);
|
|
20752
|
+
const dx = after.x - before.x;
|
|
20753
|
+
const dy = after.y - before.y;
|
|
20754
|
+
if (dx !== 0 || dy !== 0) {
|
|
20755
|
+
const descIds = [];
|
|
20756
|
+
const collect = (rootId) => {
|
|
20757
|
+
for (const cid of scene.childrenOf(asNodeId(rootId))) {
|
|
20758
|
+
descIds.push(cid);
|
|
20759
|
+
collect(cid);
|
|
20760
|
+
}
|
|
20761
|
+
};
|
|
20762
|
+
collect(id);
|
|
20763
|
+
scene.batch("move container", () => {
|
|
20764
|
+
scene.setPose(asNodeId(id), pose);
|
|
20765
|
+
for (const cid of descIds) {
|
|
20766
|
+
const cn = scene.get(asNodeId(cid));
|
|
20767
|
+
if (!cn) continue;
|
|
20768
|
+
scene.setPose(asNodeId(cid), translate3(cn.pose, dx, dy, cn));
|
|
20769
|
+
}
|
|
20770
|
+
});
|
|
20771
|
+
return;
|
|
20772
|
+
}
|
|
20773
|
+
}
|
|
20774
|
+
}
|
|
20775
|
+
scene.setPose(asNodeId(id), pose);
|
|
19889
20776
|
},
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
return () => {
|
|
19893
|
-
listeners.delete(fn);
|
|
19894
|
-
};
|
|
20777
|
+
setParent(id, parentId) {
|
|
20778
|
+
scene.move(asNodeId(id), parentId === null ? null : asNodeId(parentId));
|
|
19895
20779
|
},
|
|
19896
|
-
|
|
19897
|
-
return
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
const base = {
|
|
19907
|
-
id: spec.id,
|
|
19908
|
-
visible: spec.visible ?? true,
|
|
19909
|
-
locked: spec.locked ?? false
|
|
19910
|
-
};
|
|
19911
|
-
return spec.kind === "user" ? { ...base, kind: "user", name: spec.name ?? spec.id } : { ...base, kind: "system" };
|
|
19912
|
-
}
|
|
19913
|
-
function createScene(options) {
|
|
19914
|
-
if (!options.systemLayers || options.systemLayers.length === 0) {
|
|
19915
|
-
throw new Error("useScene: systemLayers must declare at least one layer");
|
|
19916
|
-
}
|
|
19917
|
-
const state = {
|
|
19918
|
-
nodes: /* @__PURE__ */ new Map(),
|
|
19919
|
-
roots: [],
|
|
19920
|
-
layers: [],
|
|
19921
|
-
layerIndex: /* @__PURE__ */ new Map()
|
|
19922
|
-
};
|
|
19923
|
-
const registry2 = withKitRegistry(options.registry ?? {});
|
|
19924
|
-
const fnFields = createNodeFnFields(registry2);
|
|
19925
|
-
const fnField = (name) => fnFields.find((f) => f.spec.field === name);
|
|
19926
|
-
const dependents = createDependentsIndex();
|
|
19927
|
-
const childDerived = /* @__PURE__ */ new Set();
|
|
19928
|
-
function invalidateDependents(id) {
|
|
19929
|
-
invalidateChildDerivedFrom(state.nodes.get(id)?.parent ?? null);
|
|
19930
|
-
if (dependents.isEmpty()) return;
|
|
19931
|
-
const subtree = [id];
|
|
19932
|
-
descendants(id, subtree);
|
|
19933
|
-
for (const src of subtree) {
|
|
19934
|
-
for (const dep of dependents.transitiveDependentsOf(src)) {
|
|
19935
|
-
const node = state.nodes.get(dep);
|
|
19936
|
-
if (node !== void 0) dropPoseKeyedMemoSlots(node);
|
|
19937
|
-
}
|
|
19938
|
-
}
|
|
19939
|
-
}
|
|
19940
|
-
function invalidateChildDerivedFrom(start) {
|
|
19941
|
-
if (childDerived.size === 0) return;
|
|
19942
|
-
let cursor = start;
|
|
19943
|
-
while (cursor !== null) {
|
|
19944
|
-
const node = state.nodes.get(cursor);
|
|
19945
|
-
if (node === void 0) return;
|
|
19946
|
-
if (childDerived.has(cursor)) {
|
|
19947
|
-
dropPoseKeyedMemoSlots(node);
|
|
19948
|
-
for (const dep of dependents.transitiveDependentsOf(cursor)) {
|
|
19949
|
-
const d = state.nodes.get(dep);
|
|
19950
|
-
if (d !== void 0) dropPoseKeyedMemoSlots(d);
|
|
20780
|
+
getChildren(id) {
|
|
20781
|
+
if (id === null) return [...scene.roots];
|
|
20782
|
+
return [...scene.childrenOf(asNodeId(id))];
|
|
20783
|
+
},
|
|
20784
|
+
setChildOrder(parentId, ids) {
|
|
20785
|
+
scene.batch("Reorder", () => {
|
|
20786
|
+
for (let i = 0; i < ids.length; i++) {
|
|
20787
|
+
const current = parentId === null ? scene.roots : scene.childrenOf(asNodeId(parentId));
|
|
20788
|
+
if (current[i] === ids[i]) continue;
|
|
20789
|
+
scene.reorder(asNodeId(ids[i]), i);
|
|
19951
20790
|
}
|
|
20791
|
+
});
|
|
20792
|
+
},
|
|
20793
|
+
getLayers() {
|
|
20794
|
+
return scene.layers.map((l) => ({ id: l.id, visible: l.visible }));
|
|
20795
|
+
},
|
|
20796
|
+
...options.layouts ? {
|
|
20797
|
+
getLayout: typeof options.layouts === "function" ? options.layouts : (id) => options.layouts[id] ?? null
|
|
20798
|
+
} : {},
|
|
20799
|
+
// Unified applyOps: when a label is supplied the ops are wrapped in a
|
|
20800
|
+
// batch checkpoint (history-aware path used by Move/Resize/Rotate/Insert/
|
|
20801
|
+
// Delete tools). When a journal is active (via scene's getActiveJournal
|
|
20802
|
+
// option), scene.applyBatch routes to the journal instead of recording a
|
|
20803
|
+
// new parent-history entry. Without a label the ops are applied transiently
|
|
20804
|
+
// via applyOpsTo (AreaSelectAdapter contract — no checkpoint).
|
|
20805
|
+
applyOps(ops, label) {
|
|
20806
|
+
if (label !== void 0) {
|
|
20807
|
+
scene.applyBatch(ops, label, this);
|
|
20808
|
+
} else {
|
|
20809
|
+
applyOpsTo(this, ops);
|
|
19952
20810
|
}
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
|
|
19967
|
-
|
|
19968
|
-
|
|
19969
|
-
|
|
19970
|
-
|
|
19971
|
-
|
|
19972
|
-
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
|
|
20811
|
+
},
|
|
20812
|
+
// Node-mutation surface used by kit InsertOps / DeleteOps (e.g.
|
|
20813
|
+
// useNest wrapping the selection in a new container node, or its
|
|
20814
|
+
// inverse). Re-adds via the full structural spec so the round-trip
|
|
20815
|
+
// survives undo/redo; removes by id.
|
|
20816
|
+
insertNode(node, index) {
|
|
20817
|
+
scene.add({
|
|
20818
|
+
kind: node.kind,
|
|
20819
|
+
layer: node.layer,
|
|
20820
|
+
pose: node.pose,
|
|
20821
|
+
data: node.data,
|
|
20822
|
+
id: node.id,
|
|
20823
|
+
...index !== void 0 ? { index } : {},
|
|
20824
|
+
...node.parent !== null ? { parent: node.parent } : {},
|
|
20825
|
+
// Derivation travels with the node, or undo of a delete brings an
|
|
20826
|
+
// edge back as a static path that never follows its endpoints again.
|
|
20827
|
+
...node.dependsOn !== void 0 ? { dependsOn: node.dependsOn } : {},
|
|
20828
|
+
...fnFieldsOfNode(node)
|
|
20829
|
+
});
|
|
20830
|
+
},
|
|
20831
|
+
removeNode(id) {
|
|
20832
|
+
scene.remove(asNodeId(id));
|
|
20833
|
+
},
|
|
20834
|
+
setData(id, data) {
|
|
20835
|
+
scene.update(asNodeId(id), { data });
|
|
20836
|
+
},
|
|
20837
|
+
getRemovalClosure(ids) {
|
|
20838
|
+
return [...scene.removalClosure(ids.map(asNodeId))];
|
|
20839
|
+
},
|
|
20840
|
+
// AreaSelectAdapter surface — included unconditionally so plain
|
|
20841
|
+
// `useSelectTool(sceneToAdapter(scene, { selection }))` Just Works for the
|
|
20842
|
+
// marquee gesture. `applyOps` (above) dispatches transiently when called
|
|
20843
|
+
// without a label, matching the AreaSelectAdapter contract; `hitTestArea`
|
|
20844
|
+
// does an AABB-vs-AABB scan over `scene.renderOrder()`, reading bounds
|
|
20845
|
+
// through the pose descriptor.
|
|
20846
|
+
getSelection,
|
|
20847
|
+
setSelection,
|
|
20848
|
+
hitTestArea(rect) {
|
|
20849
|
+
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
20850
|
+
const b = poseBounds(pose);
|
|
20851
|
+
return b.x < rect.x + rect.width && b.x + b.width > rect.x && b.y < rect.y + rect.height && b.y + b.height > rect.y;
|
|
20852
|
+
}, composition);
|
|
20853
|
+
},
|
|
20854
|
+
hitTestLasso(polygon, mode) {
|
|
20855
|
+
if (polygon.length < 3) return [];
|
|
20856
|
+
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
20857
|
+
const b = poseBounds(pose);
|
|
20858
|
+
return mode === "centers" ? polygonContainsRectCenter(polygon, b) : mode === "enclosed" ? polygonContainsRect(polygon, b) : polygonIntersectsRect(polygon, b);
|
|
20859
|
+
}, composition);
|
|
20860
|
+
},
|
|
20861
|
+
// commitInsert (gesture-time leaf insert) is opt-in: present only when
|
|
20862
|
+
// `options.commitInsert` is. The full insertNode/removeNode mutators
|
|
20863
|
+
// above are always present so kit-side InsertOp / DeleteOp round-trip
|
|
20864
|
+
// through scene.add / scene.remove regardless.
|
|
20865
|
+
//
|
|
20866
|
+
// Contract: this is a pure factory — it constructs a Node spec but does
|
|
20867
|
+
// NOT touch the scene. `useInsert`'s onEnd dispatches an `InsertOp`
|
|
20868
|
+
// whose apply() calls `adapter.insertNode` → `scene.add`, which is the
|
|
20869
|
+
// sole insertion path. Calling `scene.add` here too would double-insert,
|
|
20870
|
+
// and the second add throws on id collision (silently dropping the
|
|
20871
|
+
// gesture's pointer-up cleanup and breaking the next drag).
|
|
20872
|
+
...options.commitInsert ? {
|
|
20873
|
+
commitInsert: (bounds) => {
|
|
20874
|
+
const created = options.commitInsert(bounds);
|
|
20875
|
+
if (!created) return null;
|
|
20876
|
+
const layer = options.insertLayer ?? "default";
|
|
20877
|
+
const id = asNodeId(created.id ?? adapterFallbackId());
|
|
20878
|
+
return {
|
|
20879
|
+
kind: "leaf",
|
|
20880
|
+
id,
|
|
20881
|
+
layer,
|
|
20882
|
+
pose: created.pose,
|
|
20883
|
+
data: created.data,
|
|
20884
|
+
parent: null
|
|
20885
|
+
};
|
|
20886
|
+
}
|
|
20887
|
+
} : {},
|
|
20888
|
+
// Clipboard seam (`useClipboardOps` / the paste ingestion path).
|
|
20889
|
+
//
|
|
20890
|
+
// `snapshotSelection` captures adapter-shaped node copies; `commitPaste`
|
|
20891
|
+
// is a pure factory like `commitInsert` above — it mints fresh nodes but
|
|
20892
|
+
// NEVER touches the scene. Insertion happens via the hook's InsertOps
|
|
20893
|
+
// (one per created node, parents before children), which is what makes a
|
|
20894
|
+
// whole paste a single undo entry.
|
|
20895
|
+
snapshotSelection(ids) {
|
|
20896
|
+
const idSet = new Set(ids);
|
|
20897
|
+
const taken = /* @__PURE__ */ new Set();
|
|
20898
|
+
const items = [];
|
|
20899
|
+
const poseFor = (n, isRoot) => {
|
|
20900
|
+
if (!composes) return n.pose;
|
|
20901
|
+
if (isRoot) return adapter.getWorldPose(n.id);
|
|
20902
|
+
const frame = frameAtOrAbove(adapter, n.parent);
|
|
20903
|
+
return frame !== null && taken.has(frame) ? n.pose : adapter.getWorldPose(n.id);
|
|
20904
|
+
};
|
|
20905
|
+
const derivation = (n) => n.dependsOn === "children" && n.derivePose ? { dependsOn: n.dependsOn, derivePose: n.derivePose } : {};
|
|
20906
|
+
const capture = (id, isRoot = false) => {
|
|
20907
|
+
if (taken.has(id)) return;
|
|
20908
|
+
const n = scene.get(asNodeId(id));
|
|
20909
|
+
if (!n) return;
|
|
20910
|
+
taken.add(id);
|
|
20911
|
+
if (n.kind === "container") {
|
|
20912
|
+
items.push({
|
|
20913
|
+
kind: "container",
|
|
20914
|
+
id: n.id,
|
|
20915
|
+
layer: n.layer,
|
|
20916
|
+
parent: n.parent,
|
|
20917
|
+
pose: copyField(poseFor(n, isRoot)),
|
|
20918
|
+
data: copyField(n.data),
|
|
20919
|
+
children: [...n.children],
|
|
20920
|
+
...n.clipFromPose ? { clipFromPose: n.clipFromPose } : {},
|
|
20921
|
+
...derivation(n)
|
|
20922
|
+
});
|
|
20923
|
+
for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
|
|
20924
|
+
} else {
|
|
20925
|
+
items.push({
|
|
20926
|
+
kind: "leaf",
|
|
20927
|
+
id: n.id,
|
|
20928
|
+
layer: n.layer,
|
|
20929
|
+
parent: n.parent,
|
|
20930
|
+
pose: copyField(poseFor(n, isRoot)),
|
|
20931
|
+
data: copyField(n.data)
|
|
20932
|
+
});
|
|
20003
20933
|
}
|
|
20004
|
-
}
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
20008
|
-
let selection = [];
|
|
20009
|
-
const overrides = createPoseOverrides(
|
|
20010
|
-
(id) => state.nodes.get(id),
|
|
20011
|
-
invalidateDependents
|
|
20012
|
-
);
|
|
20013
|
-
const history = createHistory(void 0, {
|
|
20014
|
-
selection: {
|
|
20015
|
-
get: () => selection,
|
|
20016
|
-
set: (ids) => {
|
|
20017
|
-
selection = reachable(ids);
|
|
20018
|
-
notify2();
|
|
20934
|
+
};
|
|
20935
|
+
for (const id of ids) {
|
|
20936
|
+
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
20937
|
+
capture(id, true);
|
|
20019
20938
|
}
|
|
20939
|
+
return { items };
|
|
20020
20940
|
},
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20941
|
+
commitPaste(clipboard, offset, ctx) {
|
|
20942
|
+
const src = clipboard.items;
|
|
20943
|
+
if (src.length === 0) return [];
|
|
20944
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
20945
|
+
for (const item of src) idMap.set(item.id, asNodeId(pasteNodeId()));
|
|
20946
|
+
const isRoot = (item) => item.parent === null || !idMap.has(item.parent);
|
|
20947
|
+
const byId = new Map(src.map((item) => [item.id, item]));
|
|
20948
|
+
const ridesPastedFrame = (item) => {
|
|
20949
|
+
if (!composes) return false;
|
|
20950
|
+
let p = item.parent === null ? void 0 : byId.get(item.parent);
|
|
20951
|
+
while (p !== void 0) {
|
|
20952
|
+
if (definesFrame(p)) return true;
|
|
20953
|
+
p = p.parent === null ? void 0 : byId.get(p.parent);
|
|
20954
|
+
}
|
|
20955
|
+
return false;
|
|
20956
|
+
};
|
|
20957
|
+
let dx = offset.dx;
|
|
20958
|
+
let dy = offset.dy;
|
|
20959
|
+
if (ctx?.dropPoint) {
|
|
20960
|
+
let minX = Infinity;
|
|
20961
|
+
let minY = Infinity;
|
|
20962
|
+
for (const item of src) {
|
|
20963
|
+
if (!isRoot(item)) continue;
|
|
20964
|
+
const b = poseBounds(item.pose);
|
|
20965
|
+
if (b.x < minX) minX = b.x;
|
|
20966
|
+
if (b.y < minY) minY = b.y;
|
|
20967
|
+
}
|
|
20968
|
+
if (Number.isFinite(minX) && Number.isFinite(minY)) {
|
|
20969
|
+
dx = ctx.dropPoint.worldX - minX;
|
|
20970
|
+
dy = ctx.dropPoint.worldY - minY;
|
|
20971
|
+
}
|
|
20972
|
+
}
|
|
20973
|
+
const out = [];
|
|
20974
|
+
for (const item of src) {
|
|
20975
|
+
const base = {
|
|
20976
|
+
id: idMap.get(item.id),
|
|
20977
|
+
layer: item.layer,
|
|
20978
|
+
parent: isRoot(item) ? null : idMap.get(item.parent),
|
|
20979
|
+
pose: ridesPastedFrame(item) ? copyField(item.pose) : translate3(copyField(item.pose), dx, dy),
|
|
20980
|
+
data: copyField(item.data)
|
|
20981
|
+
};
|
|
20982
|
+
if (item.kind === "container") {
|
|
20983
|
+
out.push({
|
|
20984
|
+
...base,
|
|
20985
|
+
kind: "container",
|
|
20986
|
+
children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)),
|
|
20987
|
+
...item.clipFromPose ? { clipFromPose: item.clipFromPose } : {},
|
|
20988
|
+
...item.dependsOn === "children" && item.derivePose ? { dependsOn: item.dependsOn, derivePose: item.derivePose } : {}
|
|
20989
|
+
});
|
|
20990
|
+
} else {
|
|
20991
|
+
out.push({ ...base, kind: "leaf" });
|
|
20992
|
+
}
|
|
20993
|
+
}
|
|
20994
|
+
return out;
|
|
20026
20995
|
},
|
|
20027
|
-
//
|
|
20028
|
-
//
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
// then places its no-op placeholder. (The engine's own global-registry
|
|
20032
|
-
// fallback never fires for scene histories: this hook already
|
|
20033
|
-
// consulted it.)
|
|
20034
|
-
rebuildOp: (name, args) => {
|
|
20035
|
-
if (registered.has(name)) return makeOp(name, args);
|
|
20036
|
-
const rebuilt = rebuildOp(name, args);
|
|
20037
|
-
return rebuilt === null ? null : bindOpToHistoryAdapter(rebuilt);
|
|
20038
|
-
}
|
|
20039
|
-
});
|
|
20040
|
-
let version = 0;
|
|
20041
|
-
let batchDepth = 0;
|
|
20042
|
-
let currentBatch = null;
|
|
20043
|
-
let suppressRecording = false;
|
|
20044
|
-
let untrackedDepth = 0;
|
|
20045
|
-
let lockBypass = 0;
|
|
20046
|
-
let appliedLog = null;
|
|
20047
|
-
let batchDirty = false;
|
|
20048
|
-
let activeJournalAccessor = options.getActiveJournal ?? null;
|
|
20049
|
-
function isLockedInternal(id) {
|
|
20050
|
-
if (!state.layers.some((l) => l.locked)) return false;
|
|
20051
|
-
let cur = state.nodes.get(id);
|
|
20052
|
-
while (cur !== void 0) {
|
|
20053
|
-
if (state.layers[state.layerIndex.get(cur.layer) ?? -1]?.locked) return true;
|
|
20054
|
-
cur = cur.parent === null ? void 0 : state.nodes.get(cur.parent);
|
|
20996
|
+
// Constant cascade: the hook re-snapshots the just-created items, so
|
|
20997
|
+
// repeated pastes stack +12/+12 each time.
|
|
20998
|
+
getPasteOffset() {
|
|
20999
|
+
return { dx: 12, dy: 12 };
|
|
20055
21000
|
}
|
|
20056
|
-
|
|
20057
|
-
|
|
21001
|
+
};
|
|
21002
|
+
return adapter;
|
|
21003
|
+
}
|
|
21004
|
+
function useSceneAdapter(scene, options = {}) {
|
|
21005
|
+
const {
|
|
21006
|
+
selection,
|
|
21007
|
+
commitInsert,
|
|
21008
|
+
insertLayer,
|
|
21009
|
+
layouts,
|
|
21010
|
+
poseDescriptor,
|
|
21011
|
+
cascadeContainerPose,
|
|
21012
|
+
poseComposition
|
|
21013
|
+
} = options;
|
|
21014
|
+
return useMemo(
|
|
21015
|
+
() => sceneToAdapter(scene, {
|
|
21016
|
+
selection,
|
|
21017
|
+
commitInsert,
|
|
21018
|
+
insertLayer,
|
|
21019
|
+
layouts,
|
|
21020
|
+
poseDescriptor,
|
|
21021
|
+
cascadeContainerPose,
|
|
21022
|
+
poseComposition
|
|
21023
|
+
}),
|
|
21024
|
+
[scene, selection, commitInsert, insertLayer, layouts, poseDescriptor, cascadeContainerPose, poseComposition]
|
|
21025
|
+
);
|
|
21026
|
+
}
|
|
21027
|
+
|
|
21028
|
+
// src/core/scene/poseOverrides.ts
|
|
21029
|
+
function createPoseOverrides(getNode, onInvalidate) {
|
|
21030
|
+
const entries = /* @__PURE__ */ new Map();
|
|
21031
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
21032
|
+
let generation2 = 0;
|
|
21033
|
+
function invalidate(id) {
|
|
21034
|
+
const node = getNode(id);
|
|
21035
|
+
if (node) dropPoseKeyedMemoSlots(node);
|
|
21036
|
+
onInvalidate?.(id);
|
|
21037
|
+
}
|
|
21038
|
+
function published() {
|
|
21039
|
+
generation2++;
|
|
21040
|
+
for (const listener of listeners) listener();
|
|
21041
|
+
}
|
|
21042
|
+
return {
|
|
21043
|
+
set(id, entry) {
|
|
21044
|
+
entries.set(id, entry);
|
|
21045
|
+
invalidate(id);
|
|
21046
|
+
published();
|
|
21047
|
+
},
|
|
21048
|
+
get(id) {
|
|
21049
|
+
return entries.get(id);
|
|
21050
|
+
},
|
|
21051
|
+
has(id) {
|
|
21052
|
+
return entries.has(id);
|
|
21053
|
+
},
|
|
21054
|
+
ids() {
|
|
21055
|
+
return [...entries.keys()];
|
|
21056
|
+
},
|
|
21057
|
+
clear(id) {
|
|
21058
|
+
if (!entries.delete(id)) return;
|
|
21059
|
+
invalidate(id);
|
|
21060
|
+
published();
|
|
21061
|
+
},
|
|
21062
|
+
clearAll() {
|
|
21063
|
+
if (entries.size === 0) return;
|
|
21064
|
+
for (const id of entries.keys()) invalidate(id);
|
|
21065
|
+
entries.clear();
|
|
21066
|
+
published();
|
|
21067
|
+
},
|
|
21068
|
+
commit() {
|
|
21069
|
+
for (const id of entries.keys()) invalidate(id);
|
|
21070
|
+
published();
|
|
21071
|
+
},
|
|
21072
|
+
subscribe(fn) {
|
|
21073
|
+
listeners.add(fn);
|
|
21074
|
+
return () => {
|
|
21075
|
+
listeners.delete(fn);
|
|
21076
|
+
};
|
|
21077
|
+
},
|
|
21078
|
+
getGeneration() {
|
|
21079
|
+
return generation2;
|
|
21080
|
+
}
|
|
21081
|
+
};
|
|
21082
|
+
}
|
|
21083
|
+
|
|
21084
|
+
// src/core/scene/scene.ts
|
|
21085
|
+
var defaultIdCounter = 0;
|
|
21086
|
+
var defaultGenerateId = () => asNodeId(`n${(defaultIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`);
|
|
21087
|
+
function layerFromSerialized(spec) {
|
|
21088
|
+
const base = {
|
|
21089
|
+
id: spec.id,
|
|
21090
|
+
visible: spec.visible ?? true,
|
|
21091
|
+
locked: spec.locked ?? false
|
|
21092
|
+
};
|
|
21093
|
+
return spec.kind === "user" ? { ...base, kind: "user", name: spec.name ?? spec.id } : { ...base, kind: "system" };
|
|
21094
|
+
}
|
|
21095
|
+
function createScene(options) {
|
|
21096
|
+
if (!options.systemLayers || options.systemLayers.length === 0) {
|
|
21097
|
+
throw new Error("useScene: systemLayers must declare at least one layer");
|
|
21098
|
+
}
|
|
21099
|
+
const state = {
|
|
21100
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
21101
|
+
roots: [],
|
|
21102
|
+
layers: [],
|
|
21103
|
+
layerIndex: /* @__PURE__ */ new Map()
|
|
21104
|
+
};
|
|
21105
|
+
const registry2 = withKitRegistry(options.registry ?? {});
|
|
21106
|
+
const fnFields = createNodeFnFields(registry2);
|
|
21107
|
+
const fnField = (name) => fnFields.find((f) => f.spec.field === name);
|
|
21108
|
+
const dependents = createDependentsIndex();
|
|
21109
|
+
const childDerived = /* @__PURE__ */ new Set();
|
|
21110
|
+
function invalidateDependents(id) {
|
|
21111
|
+
invalidateChildDerivedFrom(state.nodes.get(id)?.parent ?? null);
|
|
21112
|
+
if (dependents.isEmpty()) return;
|
|
21113
|
+
const subtree = [id];
|
|
21114
|
+
descendants(id, subtree);
|
|
21115
|
+
for (const src of subtree) {
|
|
21116
|
+
for (const dep of dependents.transitiveDependentsOf(src)) {
|
|
21117
|
+
const node = state.nodes.get(dep);
|
|
21118
|
+
if (node !== void 0) dropPoseKeyedMemoSlots(node);
|
|
21119
|
+
}
|
|
21120
|
+
}
|
|
21121
|
+
}
|
|
21122
|
+
function invalidateChildDerivedFrom(start) {
|
|
21123
|
+
if (childDerived.size === 0) return;
|
|
21124
|
+
let cursor = start;
|
|
21125
|
+
while (cursor !== null) {
|
|
21126
|
+
const node = state.nodes.get(cursor);
|
|
21127
|
+
if (node === void 0) return;
|
|
21128
|
+
if (childDerived.has(cursor)) {
|
|
21129
|
+
dropPoseKeyedMemoSlots(node);
|
|
21130
|
+
for (const dep of dependents.transitiveDependentsOf(cursor)) {
|
|
21131
|
+
const d = state.nodes.get(dep);
|
|
21132
|
+
if (d !== void 0) dropPoseKeyedMemoSlots(d);
|
|
21133
|
+
}
|
|
21134
|
+
}
|
|
21135
|
+
cursor = node.parent;
|
|
21136
|
+
}
|
|
21137
|
+
}
|
|
21138
|
+
function trackChildDerived(id, dependsOn) {
|
|
21139
|
+
if (dependsOn === "children") childDerived.add(id);
|
|
21140
|
+
else childDerived.delete(id);
|
|
21141
|
+
}
|
|
21142
|
+
function retarget(id, dependsOn) {
|
|
21143
|
+
const node = requireNode(id);
|
|
21144
|
+
if (dependsOn === void 0) delete node.dependsOn;
|
|
21145
|
+
else node.dependsOn = dependsOn;
|
|
21146
|
+
dependents.add(id, dependsOn === void 0 || dependsOn === "children" ? [] : dependsOn);
|
|
21147
|
+
trackChildDerived(id, dependsOn);
|
|
21148
|
+
dropPoseKeyedMemoSlots(requireNode(id));
|
|
21149
|
+
invalidateDependents(id);
|
|
21150
|
+
}
|
|
21151
|
+
for (let i = 0; i < options.systemLayers.length; i++) {
|
|
21152
|
+
const spec = options.systemLayers[i];
|
|
21153
|
+
if (state.layerIndex.has(spec.id)) {
|
|
21154
|
+
throw new Error(`useScene: duplicate system layer id "${spec.id}"`);
|
|
21155
|
+
}
|
|
21156
|
+
state.layers.push({
|
|
21157
|
+
kind: "system",
|
|
21158
|
+
id: spec.id,
|
|
21159
|
+
visible: spec.visible ?? true,
|
|
21160
|
+
locked: spec.locked ?? false
|
|
21161
|
+
});
|
|
21162
|
+
state.layerIndex.set(spec.id, i);
|
|
21163
|
+
}
|
|
21164
|
+
const generateId = options.generateId ?? defaultGenerateId;
|
|
21165
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
21166
|
+
const registered = /* @__PURE__ */ new Map();
|
|
21167
|
+
let historyAdapterAccessor = null;
|
|
21168
|
+
function bindOpToHistoryAdapter(op) {
|
|
21169
|
+
return {
|
|
21170
|
+
name: op.name,
|
|
21171
|
+
args: op.args,
|
|
21172
|
+
label: op.label,
|
|
21173
|
+
coalesceKey: op.coalesceKey,
|
|
21174
|
+
apply: () => {
|
|
21175
|
+
const get = historyAdapterAccessor;
|
|
21176
|
+
if (!get) {
|
|
21177
|
+
dwarn("scene", `restored op "${op.name ?? "?"}" has no history adapter \u2014 skipping (setHistoryAdapter was not wired)`);
|
|
21178
|
+
return "noop";
|
|
21179
|
+
}
|
|
21180
|
+
try {
|
|
21181
|
+
return op.apply(get());
|
|
21182
|
+
} catch (err) {
|
|
21183
|
+
dwarn("scene", `restored op "${op.name ?? "?"}" failed against the history adapter \u2014 skipping: ${String(err)}`);
|
|
21184
|
+
return "noop";
|
|
21185
|
+
}
|
|
21186
|
+
},
|
|
21187
|
+
invert: () => bindOpToHistoryAdapter(op.invert())
|
|
21188
|
+
};
|
|
21189
|
+
}
|
|
21190
|
+
let selection = [];
|
|
21191
|
+
const overrides = createPoseOverrides(
|
|
21192
|
+
(id) => state.nodes.get(id),
|
|
21193
|
+
invalidateDependents
|
|
21194
|
+
);
|
|
21195
|
+
const history = createHistory(void 0, {
|
|
21196
|
+
selection: {
|
|
21197
|
+
get: () => selection,
|
|
21198
|
+
set: (ids) => {
|
|
21199
|
+
selection = reachable(ids);
|
|
21200
|
+
notify2();
|
|
21201
|
+
}
|
|
21202
|
+
},
|
|
21203
|
+
...options.coalesceWindowMs !== void 0 ? { coalesceWindowMs: options.coalesceWindowMs } : {},
|
|
21204
|
+
...options.historyLimit !== void 0 ? { historyLimit: options.historyLimit } : {},
|
|
21205
|
+
onEvict: (entry) => {
|
|
21206
|
+
pruneCacheForDroppedOps(entry.forwardOps);
|
|
21207
|
+
pruneCacheForDroppedOps(entry.baseOps);
|
|
21208
|
+
},
|
|
21209
|
+
// Restore-time rebuild: scene-registered kinds (kit:* + consumer
|
|
21210
|
+
// registerOp) rebuild via makeOp; external ops rebuild via the global
|
|
21211
|
+
// op-factory registry, wrapped in the lazy history-adapter binding.
|
|
21212
|
+
// Return null only when the global registry also misses — the engine
|
|
21213
|
+
// then places its no-op placeholder. (The engine's own global-registry
|
|
21214
|
+
// fallback never fires for scene histories: this hook already
|
|
21215
|
+
// consulted it.)
|
|
21216
|
+
rebuildOp: (name, args) => {
|
|
21217
|
+
if (registered.has(name)) return makeOp(name, args);
|
|
21218
|
+
const rebuilt = rebuildOp(name, args);
|
|
21219
|
+
return rebuilt === null ? null : bindOpToHistoryAdapter(rebuilt);
|
|
21220
|
+
}
|
|
21221
|
+
});
|
|
21222
|
+
let version = 0;
|
|
21223
|
+
let batchDepth = 0;
|
|
21224
|
+
let currentBatch = null;
|
|
21225
|
+
let suppressRecording = false;
|
|
21226
|
+
let untrackedDepth = 0;
|
|
21227
|
+
let lockBypass = 0;
|
|
21228
|
+
let appliedLog = null;
|
|
21229
|
+
let batchDirty = false;
|
|
21230
|
+
let activeJournalAccessor = options.getActiveJournal ?? null;
|
|
21231
|
+
function isLockedInternal(id) {
|
|
21232
|
+
if (!state.layers.some((l) => l.locked)) return false;
|
|
21233
|
+
let cur = state.nodes.get(id);
|
|
21234
|
+
while (cur !== void 0) {
|
|
21235
|
+
if (state.layers[state.layerIndex.get(cur.layer) ?? -1]?.locked) return true;
|
|
21236
|
+
cur = cur.parent === null ? void 0 : state.nodes.get(cur.parent);
|
|
21237
|
+
}
|
|
21238
|
+
return false;
|
|
21239
|
+
}
|
|
20058
21240
|
function refuseUntracked(verb) {
|
|
20059
21241
|
if (untrackedDepth > 0) throw new Error(`Scene: cannot ${verb} inside untracked() \u2014 it records`);
|
|
20060
21242
|
}
|
|
@@ -21899,7 +23081,7 @@ function CanvasView(props) {
|
|
|
21899
23081
|
const viewSelection = selectionProp ?? (selectionOptions ? ownSelection : void 0);
|
|
21900
23082
|
const [internalView, setInternalView] = useState(() => normalizeView(defaultView ?? IDENTITY_VIEW2));
|
|
21901
23083
|
const effectiveView = viewProp ?? internalView;
|
|
21902
|
-
const
|
|
23084
|
+
const live2 = useRef({
|
|
21903
23085
|
view: effectiveView,
|
|
21904
23086
|
bounds,
|
|
21905
23087
|
layers,
|
|
@@ -21913,8 +23095,8 @@ function CanvasView(props) {
|
|
|
21913
23095
|
// surface's otherwise. Filled in below, once the surface's is in hand.
|
|
21914
23096
|
selection: viewSelection ?? ownSelection
|
|
21915
23097
|
});
|
|
21916
|
-
|
|
21917
|
-
...
|
|
23098
|
+
live2.current = {
|
|
23099
|
+
...live2.current,
|
|
21918
23100
|
view: effectiveView,
|
|
21919
23101
|
bounds,
|
|
21920
23102
|
layers,
|
|
@@ -21926,7 +23108,7 @@ function CanvasView(props) {
|
|
|
21926
23108
|
viewSelection
|
|
21927
23109
|
};
|
|
21928
23110
|
const rectAt = useCallback((outer, dims) => {
|
|
21929
|
-
const b =
|
|
23111
|
+
const b = live2.current.bounds;
|
|
21930
23112
|
return typeof b === "function" ? b(outer, dims) : b;
|
|
21931
23113
|
}, []);
|
|
21932
23114
|
const rectNow = useCallback(() => {
|
|
@@ -21934,18 +23116,18 @@ function CanvasView(props) {
|
|
|
21934
23116
|
return rectAt(surface?.view() ?? IDENTITY_VIEW2, surface?.dims() ?? UNMEASURED_DIMS);
|
|
21935
23117
|
}, [registry2, rectAt]);
|
|
21936
23118
|
const cameraAt = useCallback((outer, dims) => {
|
|
21937
|
-
const v =
|
|
23119
|
+
const v = live2.current.view;
|
|
21938
23120
|
return normalizeView(typeof v === "function" ? v(outer, dims) : v);
|
|
21939
23121
|
}, []);
|
|
21940
23122
|
const camera = useCallback(() => {
|
|
21941
23123
|
const surface = registry2?.surface();
|
|
21942
23124
|
return cameraAt(surface?.view() ?? IDENTITY_VIEW2, surface?.dims() ?? UNMEASURED_DIMS);
|
|
21943
23125
|
}, [registry2, cameraAt]);
|
|
21944
|
-
const drawnLayers = useCallback(() => paintedLayers(registry2?.surface()?.layers() ?? [],
|
|
23126
|
+
const drawnLayers = useCallback(() => paintedLayers(registry2?.surface()?.layers() ?? [], live2.current), [registry2]);
|
|
21945
23127
|
const [sceneLayerGate] = useState(createSceneLayerGate);
|
|
21946
|
-
const layerIsPainted = useCallback((layerId) => sceneLayerGate(registry2?.surface()?.layers() ?? [],
|
|
23128
|
+
const layerIsPainted = useCallback((layerId) => sceneLayerGate(registry2?.surface()?.layers() ?? [], live2.current)(layerId), [registry2, sceneLayerGate]);
|
|
21947
23129
|
const setView = useCallback((next) => {
|
|
21948
|
-
const { viewBounds: vb, onViewChange: cb, viewProp: controlled } =
|
|
23130
|
+
const { viewBounds: vb, onViewChange: cb, viewProp: controlled } = live2.current;
|
|
21949
23131
|
const rect = rectNow();
|
|
21950
23132
|
const valid = normalizeView(next);
|
|
21951
23133
|
const clamped = vb ? clampView(valid, vb, { width: rect.w, height: rect.h }) : valid;
|
|
@@ -21988,11 +23170,11 @@ function CanvasView(props) {
|
|
|
21988
23170
|
...createDispatcherPreviewSources(() => dispatcherRef.current)
|
|
21989
23171
|
}), []);
|
|
21990
23172
|
const selection = viewSelection ?? inputs?.selectionApi ?? ownSelection;
|
|
21991
|
-
|
|
23173
|
+
live2.current.selection = selection;
|
|
21992
23174
|
const inputsRef = useRef(inputs);
|
|
21993
23175
|
inputsRef.current = inputs;
|
|
21994
23176
|
const ruleInputs = useCallback(() => ({
|
|
21995
|
-
selection:
|
|
23177
|
+
selection: live2.current.selection.get(),
|
|
21996
23178
|
view: camera(),
|
|
21997
23179
|
action: dispatcherRef.current.getActiveAction()
|
|
21998
23180
|
}), [camera]);
|
|
@@ -22036,550 +23218,166 @@ function CanvasView(props) {
|
|
|
22036
23218
|
helpersRef.current,
|
|
22037
23219
|
drawnLayers()
|
|
22038
23220
|
);
|
|
22039
|
-
if (extra) {
|
|
22040
|
-
const claim = extra.hit;
|
|
22041
|
-
return {
|
|
22042
|
-
kind: `layer:${extra.layerId}`,
|
|
22043
|
-
owner: extra.layerId,
|
|
22044
|
-
strength: claim.strength ?? "shared",
|
|
22045
|
-
...claim.claimedKinds !== void 0 ? { claimedKinds: claim.claimedKinds } : {},
|
|
22046
|
-
...claim.cursor !== void 0 ? { cursor: claim.cursor } : {},
|
|
22047
|
-
...claim.initialScratch !== void 0 ? { payload: claim.initialScratch } : {}
|
|
22048
|
-
};
|
|
22049
|
-
}
|
|
22050
|
-
return inner(world);
|
|
22051
|
-
};
|
|
22052
|
-
}, [getAnchorState, rectNow, registry2, targetScale, camera, drawnLayers]);
|
|
22053
|
-
const classifyTarget = useMemo(() => {
|
|
22054
|
-
const inner = buildClassifyTarget(
|
|
22055
|
-
() => live.current.selection.get(),
|
|
22056
|
-
(wx, wy) => {
|
|
22057
|
-
const i = inputsRef.current;
|
|
22058
|
-
const frame = { ...camera(), layerIsPainted };
|
|
22059
|
-
if (i?.pickBest) return i.pickBest(wx, wy, frame);
|
|
22060
|
-
const ids = i?.pickEvery?.(wx, wy, frame) ?? [];
|
|
22061
|
-
return ids.length > 0 ? ids[ids.length - 1] : null;
|
|
22062
|
-
},
|
|
22063
|
-
(id2) => inputsRef.current?.kindOfNode?.(id2)
|
|
22064
|
-
);
|
|
22065
|
-
return (world) => inner(world);
|
|
22066
|
-
}, [camera, layerIsPainted]);
|
|
22067
|
-
const registration = useMemo(() => ({
|
|
22068
|
-
id,
|
|
22069
|
-
order,
|
|
22070
|
-
interactive,
|
|
22071
|
-
paint,
|
|
22072
|
-
layer: createViewportLayer({
|
|
22073
|
-
id,
|
|
22074
|
-
label: label ?? id,
|
|
22075
|
-
source: drawnLayers,
|
|
22076
|
-
view: cameraAt,
|
|
22077
|
-
bounds: (outer, dims) => rectAt(outer, dims),
|
|
22078
|
-
// The surface half of the envelope passes through; the view half is
|
|
22079
|
-
// this view's, so its layers paint its chrome rather than the
|
|
22080
|
-
// surface's.
|
|
22081
|
-
data: (outer) => ({ ...outer, ...helpersRef.current }),
|
|
22082
|
-
...background !== void 0 ? { background } : {}
|
|
22083
|
-
}),
|
|
22084
|
-
layerIsPainted,
|
|
22085
|
-
target: {
|
|
22086
|
-
dispatcher: dispatcherRef.current,
|
|
22087
|
-
affordanceAt,
|
|
22088
|
-
classifyTarget,
|
|
22089
|
-
clientToWorld: clientToWorldHere,
|
|
22090
|
-
deps: () => ({
|
|
22091
|
-
view: viewApi,
|
|
22092
|
-
ingestion: ingestionApi,
|
|
22093
|
-
// An Escape here cancels what is in flight here.
|
|
22094
|
-
dispatcher: { cancelAll: (reason) => dispatcherRef.current.cancelAll(reason) },
|
|
22095
|
-
// Only a view with its own selection overlays that one; otherwise the
|
|
22096
|
-
// surface's answer stands, wrappers (`selectionMode`) included.
|
|
22097
|
-
...live.current.viewSelection ? { selection: live.current.viewSelection } : {}
|
|
22098
|
-
}),
|
|
22099
|
-
// Action eligibility is chrome-caps evaluated against this view, so a
|
|
22100
|
-
// rule that hides an action in one panel cannot decline a gesture in
|
|
22101
|
-
// another.
|
|
22102
|
-
getRuleCtx: () => inputsRef.current?.chromeCaps?.ruleCtx(ruleInputs())
|
|
22103
|
-
}
|
|
22104
|
-
}), [
|
|
22105
|
-
id,
|
|
22106
|
-
order,
|
|
22107
|
-
interactive,
|
|
22108
|
-
paint,
|
|
22109
|
-
label,
|
|
22110
|
-
background,
|
|
22111
|
-
rectAt,
|
|
22112
|
-
cameraAt,
|
|
22113
|
-
viewApi,
|
|
22114
|
-
affordanceAt,
|
|
22115
|
-
classifyTarget,
|
|
22116
|
-
clientToWorldHere,
|
|
22117
|
-
ruleInputs,
|
|
22118
|
-
ingestionApi,
|
|
22119
|
-
drawnLayers,
|
|
22120
|
-
layerIsPainted
|
|
22121
|
-
]);
|
|
22122
|
-
useEffect(() => {
|
|
22123
|
-
if (!registry2) return;
|
|
22124
|
-
return registry2.register(registration);
|
|
22125
|
-
}, [registry2, registration]);
|
|
22126
|
-
useEffect(() => {
|
|
22127
|
-
registry2?.surface()?.requestRedraw();
|
|
22128
|
-
}, [registry2, effectiveView]);
|
|
22129
|
-
return null;
|
|
22130
|
-
}
|
|
22131
|
-
function ActionsProviderIfRoot({ children }) {
|
|
22132
|
-
const parent = useActionsRegistry();
|
|
22133
|
-
if (parent) return /* @__PURE__ */ jsx(ActionsScope, { children });
|
|
22134
|
-
return /* @__PURE__ */ jsx(ActionsProvider, { children });
|
|
22135
|
-
}
|
|
22136
|
-
function useToolActions(tools) {
|
|
22137
|
-
const registry2 = useActionsRegistry();
|
|
22138
|
-
useEffect(() => {
|
|
22139
|
-
if (!registry2) return;
|
|
22140
|
-
const unregisters = [];
|
|
22141
|
-
for (const tool of [...Object.values(tools.registry), ...tools.ambient]) {
|
|
22142
|
-
for (const action of tool.actions ?? []) {
|
|
22143
|
-
unregisters.push(registry2.register(action));
|
|
22144
|
-
}
|
|
22145
|
-
}
|
|
22146
|
-
return () => {
|
|
22147
|
-
for (const u of unregisters) u();
|
|
22148
|
-
};
|
|
22149
|
-
}, [registry2, tools.registry, tools.ambient]);
|
|
22150
|
-
}
|
|
22151
|
-
function PointerProviderIfRoot({ children }) {
|
|
22152
|
-
const parent = usePointerContext();
|
|
22153
|
-
if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
22154
|
-
return /* @__PURE__ */ jsx(PointerContextProvider, { children });
|
|
22155
|
-
}
|
|
22156
|
-
function PointerPublisher({
|
|
22157
|
-
canvasRef
|
|
22158
|
-
}) {
|
|
22159
|
-
const ctx = usePointerContext();
|
|
22160
|
-
const registry2 = useOptionalViewRegistry();
|
|
22161
|
-
useEffect(() => {
|
|
22162
|
-
if (!ctx) return;
|
|
22163
|
-
const el = canvasRef.current;
|
|
22164
|
-
if (!el) return;
|
|
22165
|
-
const publish = (pointerId, clientX, clientY) => {
|
|
22166
|
-
const target = registry2?.resolver.at(pointerId, clientX, clientY);
|
|
22167
|
-
const rect = el.getBoundingClientRect();
|
|
22168
|
-
const [worldX, worldY] = clientToWorld(
|
|
22169
|
-
clientX,
|
|
22170
|
-
clientY,
|
|
22171
|
-
target?.origin ?? rect,
|
|
22172
|
-
target?.view ?? { x: 0, y: 0, scale: { x: 1, y: 1 } }
|
|
22173
|
-
);
|
|
22174
|
-
ctx.pointerRef.current = { worldX, worldY };
|
|
22175
|
-
};
|
|
22176
|
-
const onMove = (e) => publish(e.pointerId, e.clientX, e.clientY);
|
|
22177
|
-
const onLeave = (e) => {
|
|
22178
|
-
if (e.buttons !== 0) {
|
|
22179
|
-
publish(e.pointerId, e.clientX, e.clientY);
|
|
22180
|
-
return;
|
|
22181
|
-
}
|
|
22182
|
-
ctx.pointerRef.current = null;
|
|
22183
|
-
};
|
|
22184
|
-
const onDocMove = (e) => {
|
|
22185
|
-
if (e.target === el) return;
|
|
22186
|
-
if (e.buttons === 0) return;
|
|
22187
|
-
publish(e.pointerId, e.clientX, e.clientY);
|
|
22188
|
-
};
|
|
22189
|
-
el.addEventListener("pointermove", onMove);
|
|
22190
|
-
el.addEventListener("pointerleave", onLeave);
|
|
22191
|
-
document.addEventListener("pointermove", onDocMove);
|
|
22192
|
-
return () => {
|
|
22193
|
-
el.removeEventListener("pointermove", onMove);
|
|
22194
|
-
el.removeEventListener("pointerleave", onLeave);
|
|
22195
|
-
document.removeEventListener("pointermove", onDocMove);
|
|
22196
|
-
};
|
|
22197
|
-
}, [ctx, canvasRef, registry2]);
|
|
22198
|
-
return null;
|
|
22199
|
-
}
|
|
22200
|
-
function flattenVerts2(poly2) {
|
|
22201
|
-
const flat = new Array(poly2.length * 2);
|
|
22202
|
-
for (let i = 0; i < poly2.length; i++) {
|
|
22203
|
-
flat[i * 2] = poly2[i].x;
|
|
22204
|
-
flat[i * 2 + 1] = poly2[i].y;
|
|
22205
|
-
}
|
|
22206
|
-
return flat;
|
|
22207
|
-
}
|
|
22208
|
-
function polygonContainsRectCenter(poly2, rect) {
|
|
22209
|
-
return pointInPolygon(flattenVerts2(poly2), rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
22210
|
-
}
|
|
22211
|
-
function polygonContainsRect(poly2, rect) {
|
|
22212
|
-
if (poly2.length < 3) return false;
|
|
22213
|
-
const flat = flattenVerts2(poly2);
|
|
22214
|
-
if (!pointInPolygon(flat, rect.x, rect.y)) return false;
|
|
22215
|
-
if (!pointInPolygon(flat, rect.x + rect.width, rect.y)) return false;
|
|
22216
|
-
if (!pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return false;
|
|
22217
|
-
if (!pointInPolygon(flat, rect.x, rect.y + rect.height)) return false;
|
|
22218
|
-
return !anyPolyEdgeCrossesRect(poly2, rect);
|
|
22219
|
-
}
|
|
22220
|
-
function polygonIntersectsRect(poly2, rect) {
|
|
22221
|
-
if (poly2.length < 3) return false;
|
|
22222
|
-
const ab = polygonAabb(poly2);
|
|
22223
|
-
if (ab.x + ab.width < rect.x || ab.x > rect.x + rect.width) return false;
|
|
22224
|
-
if (ab.y + ab.height < rect.y || ab.y > rect.y + rect.height) return false;
|
|
22225
|
-
for (const v of poly2) {
|
|
22226
|
-
if (v.x >= rect.x && v.x <= rect.x + rect.width && v.y >= rect.y && v.y <= rect.y + rect.height) return true;
|
|
22227
|
-
}
|
|
22228
|
-
const flat = flattenVerts2(poly2);
|
|
22229
|
-
if (pointInPolygon(flat, rect.x, rect.y)) return true;
|
|
22230
|
-
if (pointInPolygon(flat, rect.x + rect.width, rect.y)) return true;
|
|
22231
|
-
if (pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return true;
|
|
22232
|
-
if (pointInPolygon(flat, rect.x, rect.y + rect.height)) return true;
|
|
22233
|
-
return anyPolyEdgeCrossesRect(poly2, rect);
|
|
22234
|
-
}
|
|
22235
|
-
function polygonAabb(poly2) {
|
|
22236
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
22237
|
-
for (const v of poly2) {
|
|
22238
|
-
if (v.x < minX) minX = v.x;
|
|
22239
|
-
if (v.y < minY) minY = v.y;
|
|
22240
|
-
if (v.x > maxX) maxX = v.x;
|
|
22241
|
-
if (v.y > maxY) maxY = v.y;
|
|
22242
|
-
}
|
|
22243
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
22244
|
-
}
|
|
22245
|
-
function anyPolyEdgeCrossesRect(poly2, rect) {
|
|
22246
|
-
const r0x = rect.x, r0y = rect.y;
|
|
22247
|
-
const r1x = rect.x + rect.width, r1y = rect.y;
|
|
22248
|
-
const r2x = rect.x + rect.width, r2y = rect.y + rect.height;
|
|
22249
|
-
const r3x = rect.x, r3y = rect.y + rect.height;
|
|
22250
|
-
for (let i = 0, j = poly2.length - 1; i < poly2.length; j = i++) {
|
|
22251
|
-
const a = poly2[j], b = poly2[i];
|
|
22252
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r0x, r0y, r1x, r1y)) return true;
|
|
22253
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r1x, r1y, r2x, r2y)) return true;
|
|
22254
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r2x, r2y, r3x, r3y)) return true;
|
|
22255
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r3x, r3y, r0x, r0y)) return true;
|
|
22256
|
-
}
|
|
22257
|
-
return false;
|
|
22258
|
-
}
|
|
22259
|
-
|
|
22260
|
-
// src/canvas/sceneAdapter.ts
|
|
22261
|
-
function walkClipAware(scene, poseBounds, nodeTest, poseComposition) {
|
|
22262
|
-
return pickWalk(scenePickSource(scene, poseComposition ? { poseComposition } : {}), {
|
|
22263
|
-
hits: (node, pose) => nodeTest(node, pose),
|
|
22264
|
-
clipAdmits: (clip, _node, pose) => pathIntersectsRect(clip, poseBounds(pose))
|
|
22265
|
-
});
|
|
22266
|
-
}
|
|
22267
|
-
var adapterFallbackIdCounter = 0;
|
|
22268
|
-
function adapterFallbackId() {
|
|
22269
|
-
return `n${(adapterFallbackIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22270
|
-
}
|
|
22271
|
-
var pasteIdCounter = 0;
|
|
22272
|
-
function pasteNodeId() {
|
|
22273
|
-
return `paste-${(pasteIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22274
|
-
}
|
|
22275
|
-
function copyField(value) {
|
|
22276
|
-
if (Array.isArray(value)) return [...value];
|
|
22277
|
-
if (value !== null && typeof value === "object") return { ...value };
|
|
22278
|
-
return value;
|
|
22279
|
-
}
|
|
22280
|
-
function sceneToAdapter(scene, options = {}) {
|
|
22281
|
-
const visibleLayers = () => {
|
|
22282
|
-
const out = /* @__PURE__ */ new Set();
|
|
22283
|
-
for (const layer of scene.layers) {
|
|
22284
|
-
if (layer.visible) out.add(layer.id);
|
|
22285
|
-
}
|
|
22286
|
-
return out;
|
|
22287
|
-
};
|
|
22288
|
-
const sel = options.selection;
|
|
22289
|
-
const getSelection = sel?.getSelection ?? sel?.get ?? (() => []);
|
|
22290
|
-
const setSelection = sel?.setSelection ?? sel?.set ?? (() => {
|
|
22291
|
-
});
|
|
22292
|
-
const d = options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
22293
|
-
const poseBounds = (p) => visualBoundsViaDescriptor(p, d);
|
|
22294
|
-
const translate3 = (p, dx, dy, node) => translatePoseViaDescriptor(
|
|
22295
|
-
p,
|
|
22296
|
-
dx,
|
|
22297
|
-
dy,
|
|
22298
|
-
node === void 0 ? d : poseDescriptorForNode(d, node)
|
|
22299
|
-
);
|
|
22300
|
-
const composition = options.poseComposition;
|
|
22301
|
-
const composes = composition !== void 0 && composition.closure !== "identity";
|
|
22302
|
-
if (composes && options.cascadeContainerPose) {
|
|
22303
|
-
throw new Error(
|
|
22304
|
-
"sceneToAdapter: poseComposition and cascadeContainerPose contradict each other. Under a composing strategy a child's pose is already relative to its parent, so the cascade would move every descendant twice. Drop cascadeContainerPose."
|
|
22305
|
-
);
|
|
22306
|
-
}
|
|
22307
|
-
const adapter = {
|
|
22308
|
-
getNode(id) {
|
|
22309
|
-
return scene.get(asNodeId(id));
|
|
22310
|
-
},
|
|
22311
|
-
getNodes() {
|
|
22312
|
-
const visible = visibleLayers();
|
|
22313
|
-
const out = [];
|
|
22314
|
-
for (const n of scene.renderOrderNodes()) {
|
|
22315
|
-
if (visible.has(n.layer)) out.push(n);
|
|
22316
|
-
}
|
|
22317
|
-
return out;
|
|
22318
|
-
},
|
|
22319
|
-
getPose(id) {
|
|
22320
|
-
const nid = asNodeId(id);
|
|
22321
|
-
const n = scene.get(nid);
|
|
22322
|
-
if (!n) throw new Error(`sceneToAdapter: unknown node "${id}"`);
|
|
22323
|
-
return effectivePose(scene, n);
|
|
22324
|
-
},
|
|
22325
|
-
getParent(id) {
|
|
22326
|
-
const n = scene.get(asNodeId(id));
|
|
22327
|
-
return n?.parent ?? null;
|
|
22328
|
-
},
|
|
22329
|
-
getWorldPose(id) {
|
|
22330
|
-
if (!composes) return adapter.getPose(id);
|
|
22331
|
-
return composeWorldPose(adapter, id, composition.compose);
|
|
22332
|
-
},
|
|
22333
|
-
// Only present when it would do something: the render walk skips the fold
|
|
22334
|
-
// entirely when this is absent, which is the absolute-pose path.
|
|
22335
|
-
...composes ? { composePose: composition.compose } : {},
|
|
22336
|
-
setPose(id, pose) {
|
|
22337
|
-
if (options.cascadeContainerPose) {
|
|
22338
|
-
const node = scene.get(asNodeId(id));
|
|
22339
|
-
if (node && node.kind === "container") {
|
|
22340
|
-
const g = poseDescriptorForNode(d, node);
|
|
22341
|
-
const before = g.getBounds(node.pose);
|
|
22342
|
-
const after = g.getBounds(pose);
|
|
22343
|
-
const dx = after.x - before.x;
|
|
22344
|
-
const dy = after.y - before.y;
|
|
22345
|
-
if (dx !== 0 || dy !== 0) {
|
|
22346
|
-
const descIds = [];
|
|
22347
|
-
const collect = (rootId) => {
|
|
22348
|
-
for (const cid of scene.childrenOf(asNodeId(rootId))) {
|
|
22349
|
-
descIds.push(cid);
|
|
22350
|
-
collect(cid);
|
|
22351
|
-
}
|
|
22352
|
-
};
|
|
22353
|
-
collect(id);
|
|
22354
|
-
scene.batch("setPose", () => {
|
|
22355
|
-
scene.setPose(asNodeId(id), pose);
|
|
22356
|
-
for (const cid of descIds) {
|
|
22357
|
-
const cn = scene.get(asNodeId(cid));
|
|
22358
|
-
if (!cn) continue;
|
|
22359
|
-
scene.setPose(asNodeId(cid), translate3(cn.pose, dx, dy, cn));
|
|
22360
|
-
}
|
|
22361
|
-
});
|
|
22362
|
-
return;
|
|
22363
|
-
}
|
|
22364
|
-
}
|
|
22365
|
-
}
|
|
22366
|
-
scene.setPose(asNodeId(id), pose);
|
|
22367
|
-
},
|
|
22368
|
-
setParent(id, parentId) {
|
|
22369
|
-
scene.move(asNodeId(id), parentId === null ? null : asNodeId(parentId));
|
|
22370
|
-
},
|
|
22371
|
-
getChildren(id) {
|
|
22372
|
-
if (id === null) return [...scene.roots];
|
|
22373
|
-
return [...scene.childrenOf(asNodeId(id))];
|
|
22374
|
-
},
|
|
22375
|
-
setChildOrder(parentId, ids) {
|
|
22376
|
-
scene.batch("Reorder", () => {
|
|
22377
|
-
for (let i = 0; i < ids.length; i++) {
|
|
22378
|
-
const current = parentId === null ? scene.roots : scene.childrenOf(asNodeId(parentId));
|
|
22379
|
-
if (current[i] === ids[i]) continue;
|
|
22380
|
-
scene.reorder(asNodeId(ids[i]), i);
|
|
22381
|
-
}
|
|
22382
|
-
});
|
|
22383
|
-
},
|
|
22384
|
-
getLayers() {
|
|
22385
|
-
return scene.layers.map((l) => ({ id: l.id, visible: l.visible }));
|
|
22386
|
-
},
|
|
22387
|
-
...options.layouts ? {
|
|
22388
|
-
getLayout: typeof options.layouts === "function" ? options.layouts : (id) => options.layouts[id] ?? null
|
|
22389
|
-
} : {},
|
|
22390
|
-
// Unified applyOps: when a label is supplied the ops are wrapped in a
|
|
22391
|
-
// batch checkpoint (history-aware path used by Move/Resize/Rotate/Insert/
|
|
22392
|
-
// Delete tools). When a journal is active (via scene's getActiveJournal
|
|
22393
|
-
// option), scene.applyBatch routes to the journal instead of recording a
|
|
22394
|
-
// new parent-history entry. Without a label the ops are applied transiently
|
|
22395
|
-
// via applyOpsTo (AreaSelectAdapter contract — no checkpoint).
|
|
22396
|
-
applyOps(ops, label) {
|
|
22397
|
-
if (label !== void 0) {
|
|
22398
|
-
scene.applyBatch(ops, label, this);
|
|
22399
|
-
} else {
|
|
22400
|
-
applyOpsTo(this, ops);
|
|
22401
|
-
}
|
|
22402
|
-
},
|
|
22403
|
-
// Node-mutation surface used by kit InsertOps / DeleteOps (e.g.
|
|
22404
|
-
// useNest wrapping the selection in a new container node, or its
|
|
22405
|
-
// inverse). Re-adds via the full structural spec so the round-trip
|
|
22406
|
-
// survives undo/redo; removes by id.
|
|
22407
|
-
insertNode(node, index) {
|
|
22408
|
-
scene.add({
|
|
22409
|
-
kind: node.kind,
|
|
22410
|
-
layer: node.layer,
|
|
22411
|
-
pose: node.pose,
|
|
22412
|
-
data: node.data,
|
|
22413
|
-
id: node.id,
|
|
22414
|
-
...index !== void 0 ? { index } : {},
|
|
22415
|
-
...node.parent !== null ? { parent: node.parent } : {},
|
|
22416
|
-
// Derivation travels with the node, or undo of a delete brings an
|
|
22417
|
-
// edge back as a static path that never follows its endpoints again.
|
|
22418
|
-
...node.dependsOn !== void 0 ? { dependsOn: node.dependsOn } : {},
|
|
22419
|
-
...fnFieldsOfNode(node)
|
|
22420
|
-
});
|
|
22421
|
-
},
|
|
22422
|
-
removeNode(id) {
|
|
22423
|
-
scene.remove(asNodeId(id));
|
|
22424
|
-
},
|
|
22425
|
-
setData(id, data) {
|
|
22426
|
-
scene.update(asNodeId(id), { data });
|
|
22427
|
-
},
|
|
22428
|
-
getRemovalClosure(ids) {
|
|
22429
|
-
return [...scene.removalClosure(ids.map(asNodeId))];
|
|
22430
|
-
},
|
|
22431
|
-
// AreaSelectAdapter surface — included unconditionally so plain
|
|
22432
|
-
// `useSelectTool(sceneToAdapter(scene, { selection }))` Just Works for the
|
|
22433
|
-
// marquee gesture. `applyOps` (above) dispatches transiently when called
|
|
22434
|
-
// without a label, matching the AreaSelectAdapter contract; `hitTestArea`
|
|
22435
|
-
// does an AABB-vs-AABB scan over `scene.renderOrder()`, reading bounds
|
|
22436
|
-
// through the pose descriptor.
|
|
22437
|
-
getSelection,
|
|
22438
|
-
setSelection,
|
|
22439
|
-
hitTestArea(rect) {
|
|
22440
|
-
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
22441
|
-
const b = poseBounds(pose);
|
|
22442
|
-
return b.x < rect.x + rect.width && b.x + b.width > rect.x && b.y < rect.y + rect.height && b.y + b.height > rect.y;
|
|
22443
|
-
}, composition);
|
|
22444
|
-
},
|
|
22445
|
-
hitTestLasso(polygon, mode) {
|
|
22446
|
-
if (polygon.length < 3) return [];
|
|
22447
|
-
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
22448
|
-
const b = poseBounds(pose);
|
|
22449
|
-
return mode === "centers" ? polygonContainsRectCenter(polygon, b) : mode === "enclosed" ? polygonContainsRect(polygon, b) : polygonIntersectsRect(polygon, b);
|
|
22450
|
-
}, composition);
|
|
22451
|
-
},
|
|
22452
|
-
// commitInsert (gesture-time leaf insert) is opt-in: present only when
|
|
22453
|
-
// `options.commitInsert` is. The full insertNode/removeNode mutators
|
|
22454
|
-
// above are always present so kit-side InsertOp / DeleteOp round-trip
|
|
22455
|
-
// through scene.add / scene.remove regardless.
|
|
22456
|
-
//
|
|
22457
|
-
// Contract: this is a pure factory — it constructs a Node spec but does
|
|
22458
|
-
// NOT touch the scene. `useInsert`'s onEnd dispatches an `InsertOp`
|
|
22459
|
-
// whose apply() calls `adapter.insertNode` → `scene.add`, which is the
|
|
22460
|
-
// sole insertion path. Calling `scene.add` here too would double-insert,
|
|
22461
|
-
// and the second add throws on id collision (silently dropping the
|
|
22462
|
-
// gesture's pointer-up cleanup and breaking the next drag).
|
|
22463
|
-
...options.commitInsert ? {
|
|
22464
|
-
commitInsert: (bounds) => {
|
|
22465
|
-
const created = options.commitInsert(bounds);
|
|
22466
|
-
if (!created) return null;
|
|
22467
|
-
const layer = options.insertLayer ?? "default";
|
|
22468
|
-
const id = asNodeId(created.id ?? adapterFallbackId());
|
|
22469
|
-
return {
|
|
22470
|
-
kind: "leaf",
|
|
22471
|
-
id,
|
|
22472
|
-
layer,
|
|
22473
|
-
pose: created.pose,
|
|
22474
|
-
data: created.data,
|
|
22475
|
-
parent: null
|
|
22476
|
-
};
|
|
22477
|
-
}
|
|
22478
|
-
} : {},
|
|
22479
|
-
// Clipboard seam (`useClipboardOps` / the paste ingestion path).
|
|
22480
|
-
//
|
|
22481
|
-
// `snapshotSelection` captures adapter-shaped node copies; `commitPaste`
|
|
22482
|
-
// is a pure factory like `commitInsert` above — it mints fresh nodes but
|
|
22483
|
-
// NEVER touches the scene. Insertion happens via the hook's InsertOps
|
|
22484
|
-
// (one per created node, parents before children), which is what makes a
|
|
22485
|
-
// whole paste a single undo entry.
|
|
22486
|
-
snapshotSelection(ids) {
|
|
22487
|
-
const idSet = new Set(ids);
|
|
22488
|
-
const taken = /* @__PURE__ */ new Set();
|
|
22489
|
-
const items = [];
|
|
22490
|
-
const poseFor = (n, isRoot) => isRoot && composes ? adapter.getWorldPose(n.id) : n.pose;
|
|
22491
|
-
const capture = (id, isRoot = false) => {
|
|
22492
|
-
if (taken.has(id)) return;
|
|
22493
|
-
const n = scene.get(asNodeId(id));
|
|
22494
|
-
if (!n) return;
|
|
22495
|
-
taken.add(id);
|
|
22496
|
-
if (n.kind === "container") {
|
|
22497
|
-
items.push({
|
|
22498
|
-
kind: "container",
|
|
22499
|
-
id: n.id,
|
|
22500
|
-
layer: n.layer,
|
|
22501
|
-
parent: n.parent,
|
|
22502
|
-
pose: copyField(poseFor(n, isRoot)),
|
|
22503
|
-
data: copyField(n.data),
|
|
22504
|
-
children: [...n.children],
|
|
22505
|
-
...n.clipFromPose ? { clipFromPose: n.clipFromPose } : {}
|
|
22506
|
-
});
|
|
22507
|
-
for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
|
|
22508
|
-
} else {
|
|
22509
|
-
items.push({
|
|
22510
|
-
kind: "leaf",
|
|
22511
|
-
id: n.id,
|
|
22512
|
-
layer: n.layer,
|
|
22513
|
-
parent: n.parent,
|
|
22514
|
-
pose: copyField(poseFor(n, isRoot)),
|
|
22515
|
-
data: copyField(n.data)
|
|
22516
|
-
});
|
|
22517
|
-
}
|
|
22518
|
-
};
|
|
22519
|
-
for (const id of ids) {
|
|
22520
|
-
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
22521
|
-
capture(id, true);
|
|
22522
|
-
}
|
|
22523
|
-
return { items };
|
|
22524
|
-
},
|
|
22525
|
-
commitPaste(clipboard, offset, ctx) {
|
|
22526
|
-
const src = clipboard.items;
|
|
22527
|
-
if (src.length === 0) return [];
|
|
22528
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
22529
|
-
for (const item of src) idMap.set(item.id, asNodeId(pasteNodeId()));
|
|
22530
|
-
const isRoot = (item) => item.parent === null || !idMap.has(item.parent);
|
|
22531
|
-
let dx = offset.dx;
|
|
22532
|
-
let dy = offset.dy;
|
|
22533
|
-
if (ctx?.dropPoint) {
|
|
22534
|
-
let minX = Infinity;
|
|
22535
|
-
let minY = Infinity;
|
|
22536
|
-
for (const item of src) {
|
|
22537
|
-
if (!isRoot(item)) continue;
|
|
22538
|
-
const b = poseBounds(item.pose);
|
|
22539
|
-
if (b.x < minX) minX = b.x;
|
|
22540
|
-
if (b.y < minY) minY = b.y;
|
|
22541
|
-
}
|
|
22542
|
-
if (Number.isFinite(minX) && Number.isFinite(minY)) {
|
|
22543
|
-
dx = ctx.dropPoint.worldX - minX;
|
|
22544
|
-
dy = ctx.dropPoint.worldY - minY;
|
|
22545
|
-
}
|
|
22546
|
-
}
|
|
22547
|
-
const out = [];
|
|
22548
|
-
for (const item of src) {
|
|
22549
|
-
const base = {
|
|
22550
|
-
id: idMap.get(item.id),
|
|
22551
|
-
layer: item.layer,
|
|
22552
|
-
parent: isRoot(item) ? null : idMap.get(item.parent),
|
|
22553
|
-
pose: translate3(copyField(item.pose), dx, dy),
|
|
22554
|
-
data: copyField(item.data)
|
|
23221
|
+
if (extra) {
|
|
23222
|
+
const claim = extra.hit;
|
|
23223
|
+
return {
|
|
23224
|
+
kind: `layer:${extra.layerId}`,
|
|
23225
|
+
owner: extra.layerId,
|
|
23226
|
+
strength: claim.strength ?? "shared",
|
|
23227
|
+
...claim.claimedKinds !== void 0 ? { claimedKinds: claim.claimedKinds } : {},
|
|
23228
|
+
...claim.cursor !== void 0 ? { cursor: claim.cursor } : {},
|
|
23229
|
+
...claim.initialScratch !== void 0 ? { payload: claim.initialScratch } : {}
|
|
22555
23230
|
};
|
|
22556
|
-
if (item.kind === "container") {
|
|
22557
|
-
out.push({
|
|
22558
|
-
...base,
|
|
22559
|
-
kind: "container",
|
|
22560
|
-
children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)),
|
|
22561
|
-
...item.clipFromPose ? { clipFromPose: item.clipFromPose } : {}
|
|
22562
|
-
});
|
|
22563
|
-
} else {
|
|
22564
|
-
out.push({ ...base, kind: "leaf" });
|
|
22565
|
-
}
|
|
22566
23231
|
}
|
|
22567
|
-
return
|
|
22568
|
-
}
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
23232
|
+
return inner(world);
|
|
23233
|
+
};
|
|
23234
|
+
}, [getAnchorState, rectNow, registry2, targetScale, camera, drawnLayers]);
|
|
23235
|
+
const classifyTarget = useMemo(() => {
|
|
23236
|
+
const inner = buildClassifyTarget(
|
|
23237
|
+
() => live2.current.selection.get(),
|
|
23238
|
+
(wx, wy) => {
|
|
23239
|
+
const i = inputsRef.current;
|
|
23240
|
+
const frame = { ...camera(), layerIsPainted };
|
|
23241
|
+
if (i?.pickBest) return i.pickBest(wx, wy, frame);
|
|
23242
|
+
const ids = i?.pickEvery?.(wx, wy, frame) ?? [];
|
|
23243
|
+
return ids.length > 0 ? ids[ids.length - 1] : null;
|
|
23244
|
+
},
|
|
23245
|
+
(id2) => inputsRef.current?.kindOfNode?.(id2)
|
|
23246
|
+
);
|
|
23247
|
+
return (world) => inner(world);
|
|
23248
|
+
}, [camera, layerIsPainted]);
|
|
23249
|
+
const registration = useMemo(() => ({
|
|
23250
|
+
id,
|
|
23251
|
+
order,
|
|
23252
|
+
interactive,
|
|
23253
|
+
paint,
|
|
23254
|
+
layer: createViewportLayer({
|
|
23255
|
+
id,
|
|
23256
|
+
label: label ?? id,
|
|
23257
|
+
source: drawnLayers,
|
|
23258
|
+
view: cameraAt,
|
|
23259
|
+
bounds: (outer, dims) => rectAt(outer, dims),
|
|
23260
|
+
// The surface half of the envelope passes through; the view half is
|
|
23261
|
+
// this view's, so its layers paint its chrome rather than the
|
|
23262
|
+
// surface's.
|
|
23263
|
+
data: (outer) => ({ ...outer, ...helpersRef.current }),
|
|
23264
|
+
...background !== void 0 ? { background } : {}
|
|
23265
|
+
}),
|
|
23266
|
+
layerIsPainted,
|
|
23267
|
+
target: {
|
|
23268
|
+
dispatcher: dispatcherRef.current,
|
|
23269
|
+
affordanceAt,
|
|
23270
|
+
classifyTarget,
|
|
23271
|
+
clientToWorld: clientToWorldHere,
|
|
23272
|
+
deps: () => ({
|
|
23273
|
+
view: viewApi,
|
|
23274
|
+
ingestion: ingestionApi,
|
|
23275
|
+
// An Escape here cancels what is in flight here.
|
|
23276
|
+
dispatcher: { cancelAll: (reason) => dispatcherRef.current.cancelAll(reason) },
|
|
23277
|
+
// Only a view with its own selection overlays that one; otherwise the
|
|
23278
|
+
// surface's answer stands, wrappers (`selectionMode`) included.
|
|
23279
|
+
...live2.current.viewSelection ? { selection: live2.current.viewSelection } : {}
|
|
23280
|
+
}),
|
|
23281
|
+
// Action eligibility is chrome-caps evaluated against this view, so a
|
|
23282
|
+
// rule that hides an action in one panel cannot decline a gesture in
|
|
23283
|
+
// another.
|
|
23284
|
+
getRuleCtx: () => inputsRef.current?.chromeCaps?.ruleCtx(ruleInputs())
|
|
22573
23285
|
}
|
|
22574
|
-
}
|
|
22575
|
-
|
|
23286
|
+
}), [
|
|
23287
|
+
id,
|
|
23288
|
+
order,
|
|
23289
|
+
interactive,
|
|
23290
|
+
paint,
|
|
23291
|
+
label,
|
|
23292
|
+
background,
|
|
23293
|
+
rectAt,
|
|
23294
|
+
cameraAt,
|
|
23295
|
+
viewApi,
|
|
23296
|
+
affordanceAt,
|
|
23297
|
+
classifyTarget,
|
|
23298
|
+
clientToWorldHere,
|
|
23299
|
+
ruleInputs,
|
|
23300
|
+
ingestionApi,
|
|
23301
|
+
drawnLayers,
|
|
23302
|
+
layerIsPainted
|
|
23303
|
+
]);
|
|
23304
|
+
useEffect(() => {
|
|
23305
|
+
if (!registry2) return;
|
|
23306
|
+
return registry2.register(registration);
|
|
23307
|
+
}, [registry2, registration]);
|
|
23308
|
+
useEffect(() => {
|
|
23309
|
+
registry2?.surface()?.requestRedraw();
|
|
23310
|
+
}, [registry2, effectiveView]);
|
|
23311
|
+
return null;
|
|
22576
23312
|
}
|
|
22577
|
-
function
|
|
22578
|
-
const
|
|
22579
|
-
return
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
23313
|
+
function ActionsProviderIfRoot({ children }) {
|
|
23314
|
+
const parent = useActionsRegistry();
|
|
23315
|
+
if (parent) return /* @__PURE__ */ jsx(ActionsScope, { children });
|
|
23316
|
+
return /* @__PURE__ */ jsx(ActionsProvider, { children });
|
|
23317
|
+
}
|
|
23318
|
+
function useToolActions(tools) {
|
|
23319
|
+
const registry2 = useActionsRegistry();
|
|
23320
|
+
useEffect(() => {
|
|
23321
|
+
if (!registry2) return;
|
|
23322
|
+
const unregisters = [];
|
|
23323
|
+
for (const tool of [...Object.values(tools.registry), ...tools.ambient]) {
|
|
23324
|
+
for (const action of tool.actions ?? []) {
|
|
23325
|
+
unregisters.push(registry2.register(action));
|
|
23326
|
+
}
|
|
23327
|
+
}
|
|
23328
|
+
return () => {
|
|
23329
|
+
for (const u of unregisters) u();
|
|
23330
|
+
};
|
|
23331
|
+
}, [registry2, tools.registry, tools.ambient]);
|
|
23332
|
+
}
|
|
23333
|
+
function PointerProviderIfRoot({ children }) {
|
|
23334
|
+
const parent = usePointerContext();
|
|
23335
|
+
if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
23336
|
+
return /* @__PURE__ */ jsx(PointerContextProvider, { children });
|
|
23337
|
+
}
|
|
23338
|
+
function PointerPublisher({
|
|
23339
|
+
canvasRef
|
|
23340
|
+
}) {
|
|
23341
|
+
const ctx = usePointerContext();
|
|
23342
|
+
const registry2 = useOptionalViewRegistry();
|
|
23343
|
+
useEffect(() => {
|
|
23344
|
+
if (!ctx) return;
|
|
23345
|
+
const el = canvasRef.current;
|
|
23346
|
+
if (!el) return;
|
|
23347
|
+
const publish = (pointerId, clientX, clientY) => {
|
|
23348
|
+
const target = registry2?.resolver.at(pointerId, clientX, clientY);
|
|
23349
|
+
const rect = el.getBoundingClientRect();
|
|
23350
|
+
const [worldX, worldY] = clientToWorld(
|
|
23351
|
+
clientX,
|
|
23352
|
+
clientY,
|
|
23353
|
+
target?.origin ?? rect,
|
|
23354
|
+
target?.view ?? { x: 0, y: 0, scale: { x: 1, y: 1 } }
|
|
23355
|
+
);
|
|
23356
|
+
ctx.pointerRef.current = { worldX, worldY };
|
|
23357
|
+
};
|
|
23358
|
+
const onMove = (e) => publish(e.pointerId, e.clientX, e.clientY);
|
|
23359
|
+
const onLeave = (e) => {
|
|
23360
|
+
if (e.buttons !== 0) {
|
|
23361
|
+
publish(e.pointerId, e.clientX, e.clientY);
|
|
23362
|
+
return;
|
|
23363
|
+
}
|
|
23364
|
+
ctx.pointerRef.current = null;
|
|
23365
|
+
};
|
|
23366
|
+
const onDocMove = (e) => {
|
|
23367
|
+
if (e.target === el) return;
|
|
23368
|
+
if (e.buttons === 0) return;
|
|
23369
|
+
publish(e.pointerId, e.clientX, e.clientY);
|
|
23370
|
+
};
|
|
23371
|
+
el.addEventListener("pointermove", onMove);
|
|
23372
|
+
el.addEventListener("pointerleave", onLeave);
|
|
23373
|
+
document.addEventListener("pointermove", onDocMove);
|
|
23374
|
+
return () => {
|
|
23375
|
+
el.removeEventListener("pointermove", onMove);
|
|
23376
|
+
el.removeEventListener("pointerleave", onLeave);
|
|
23377
|
+
document.removeEventListener("pointermove", onDocMove);
|
|
23378
|
+
};
|
|
23379
|
+
}, [ctx, canvasRef, registry2]);
|
|
23380
|
+
return null;
|
|
22583
23381
|
}
|
|
22584
23382
|
function aabbOfPose(pose, descriptor = AUTO_POSE_DESCRIPTOR) {
|
|
22585
23383
|
return descriptor.getBounds(pose);
|
|
@@ -22612,18 +23410,7 @@ function rotationAboutCenter(pose, descriptor) {
|
|
|
22612
23410
|
// src/canvas/SceneCanvas/useSceneSelectTool.ts
|
|
22613
23411
|
var DEFAULT_PICK_TOLERANCE_PX = 4;
|
|
22614
23412
|
function useSceneSelectTool(args) {
|
|
22615
|
-
const {
|
|
22616
|
-
scene,
|
|
22617
|
-
selection,
|
|
22618
|
-
geometry,
|
|
22619
|
-
selectTool: opts,
|
|
22620
|
-
insertTool,
|
|
22621
|
-
layouts,
|
|
22622
|
-
poseComposition,
|
|
22623
|
-
getView,
|
|
22624
|
-
alphaOf,
|
|
22625
|
-
layerIsPainted
|
|
22626
|
-
} = args;
|
|
23413
|
+
const { scene, adapter, geometry, selectTool: opts, getView, alphaOf, layerIsPainted } = args;
|
|
22627
23414
|
const d = args.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
22628
23415
|
const pickEveryProp = geometry?.pickEvery;
|
|
22629
23416
|
const boundsOfProp = geometry?.boundsOf;
|
|
@@ -22631,62 +23418,6 @@ function useSceneSelectTool(args) {
|
|
|
22631
23418
|
const pickTolerancePx = geometry?.pickTolerancePx ?? DEFAULT_PICK_TOLERANCE_PX;
|
|
22632
23419
|
const moveOptions = opts?.move;
|
|
22633
23420
|
const snap2 = opts?.snap;
|
|
22634
|
-
const commitInsert = insertTool?.create;
|
|
22635
|
-
const insertLayer = insertTool?.layer;
|
|
22636
|
-
const adapter = useMemo(() => {
|
|
22637
|
-
const base = sceneToAdapter(scene, {
|
|
22638
|
-
commitInsert,
|
|
22639
|
-
insertLayer,
|
|
22640
|
-
layouts,
|
|
22641
|
-
poseDescriptor: d,
|
|
22642
|
-
...poseComposition ? { poseComposition } : {}
|
|
22643
|
-
});
|
|
22644
|
-
const framed = poseComposition !== void 0 && poseComposition.closure !== "identity";
|
|
22645
|
-
const collectDescendants = (id, out) => {
|
|
22646
|
-
for (const cid of scene.childrenOf(asNodeId(id))) {
|
|
22647
|
-
out.push(cid);
|
|
22648
|
-
collectDescendants(cid, out);
|
|
22649
|
-
}
|
|
22650
|
-
};
|
|
22651
|
-
return {
|
|
22652
|
-
...base,
|
|
22653
|
-
setPose(id, pose) {
|
|
22654
|
-
const n = scene.get(asNodeId(id));
|
|
22655
|
-
if (framed || !n || n.kind !== "container") {
|
|
22656
|
-
base.setPose(id, pose);
|
|
22657
|
-
return;
|
|
22658
|
-
}
|
|
22659
|
-
const g = poseDescriptorForNode(d, n);
|
|
22660
|
-
const prev = g.getBounds(n.pose);
|
|
22661
|
-
const next = g.getBounds(pose);
|
|
22662
|
-
const dx = next.x - prev.x;
|
|
22663
|
-
const dy = next.y - prev.y;
|
|
22664
|
-
if (dx === 0 && dy === 0) {
|
|
22665
|
-
base.setPose(id, pose);
|
|
22666
|
-
return;
|
|
22667
|
-
}
|
|
22668
|
-
const desc = [];
|
|
22669
|
-
collectDescendants(id, desc);
|
|
22670
|
-
scene.batch("move container", () => {
|
|
22671
|
-
base.setPose(id, pose);
|
|
22672
|
-
for (const cid of desc) {
|
|
22673
|
-
const cn = scene.get(asNodeId(cid));
|
|
22674
|
-
if (!cn) continue;
|
|
22675
|
-
base.setPose(
|
|
22676
|
-
cid,
|
|
22677
|
-
translatePoseViaDescriptor(cn.pose, dx, dy, poseDescriptorForNode(d, cn))
|
|
22678
|
-
);
|
|
22679
|
-
}
|
|
22680
|
-
});
|
|
22681
|
-
},
|
|
22682
|
-
// Selection methods, widened to `string[]` so the synthesized adapter
|
|
22683
|
-
// satisfies `AreaSelectAdapter` (which keeps the kit-internal adapter
|
|
22684
|
-
// contract on plain `string[]`; the brand lives on the public selection
|
|
22685
|
-
// surface, not on adapter shapes).
|
|
22686
|
-
getSelection: () => [...selection.adapterMethods.getSelection()],
|
|
22687
|
-
setSelection: (ids) => selection.adapterMethods.setSelection(ids)
|
|
22688
|
-
};
|
|
22689
|
-
}, [scene, commitInsert, insertLayer, layouts, selection, d, poseComposition]);
|
|
22690
23421
|
const wiredMoveOptions = useMemo(() => {
|
|
22691
23422
|
const merged = { ...moveOptions ?? {} };
|
|
22692
23423
|
if (snap2) {
|
|
@@ -22761,7 +23492,6 @@ function useSceneSelectTool(args) {
|
|
|
22761
23492
|
};
|
|
22762
23493
|
}, [wiredHitBody, adapter]);
|
|
22763
23494
|
return {
|
|
22764
|
-
adapter,
|
|
22765
23495
|
selectTool,
|
|
22766
23496
|
pickEvery: wiredHitBody,
|
|
22767
23497
|
pickBest: wiredPickBest,
|
|
@@ -23218,60 +23948,21 @@ var CLOSE_HINT_RADIUS_PX = 8;
|
|
|
23218
23948
|
function w2s(wx, wy, view) {
|
|
23219
23949
|
return [(wx - view.x) * view.scale.x, (wy - view.y) * view.scale.y];
|
|
23220
23950
|
}
|
|
23221
|
-
function mirror(anchor, out) {
|
|
23222
|
-
if (!out || anchor.altBroken) return void 0;
|
|
23223
|
-
return { x: 2 * anchor.x - out.x, y: 2 * anchor.y - out.y };
|
|
23224
|
-
}
|
|
23225
23951
|
function approximateCircle(cx, cy, r) {
|
|
23226
23952
|
return circlePath(cx, cy, r);
|
|
23227
23953
|
}
|
|
23228
23954
|
function subpathToPath(sp, view) {
|
|
23229
23955
|
if (sp.anchors.length === 0) return null;
|
|
23230
|
-
const
|
|
23231
|
-
|
|
23232
|
-
|
|
23233
|
-
cmds.push(PATH_M);
|
|
23234
|
-
xs.push(sx, sy);
|
|
23235
|
-
for (let i = 1; i < sp.anchors.length; i++) {
|
|
23236
|
-
const prev = sp.anchors[i - 1];
|
|
23237
|
-
const curr = sp.anchors[i];
|
|
23238
|
-
const out = prev.outHandle;
|
|
23239
|
-
const inH = curr.inHandle ?? mirror(prev, out);
|
|
23240
|
-
const [tx, ty] = w2s(curr.x, curr.y, view);
|
|
23241
|
-
if (out || curr.inHandle) {
|
|
23242
|
-
const c1 = out ?? prev;
|
|
23243
|
-
const c2 = inH ?? curr;
|
|
23244
|
-
const [c1x, c1y] = w2s(c1.x, c1.y, view);
|
|
23245
|
-
const [c2x, c2y] = w2s(c2.x, c2.y, view);
|
|
23246
|
-
cmds.push(PATH_C);
|
|
23247
|
-
xs.push(c1x, c1y, c2x, c2y, tx, ty);
|
|
23248
|
-
} else {
|
|
23249
|
-
cmds.push(PATH_L);
|
|
23250
|
-
xs.push(tx, ty);
|
|
23251
|
-
}
|
|
23252
|
-
}
|
|
23253
|
-
if (sp.closed) {
|
|
23254
|
-
const last = sp.anchors[sp.anchors.length - 1];
|
|
23255
|
-
const first = sp.anchors[0];
|
|
23256
|
-
const out = last.outHandle;
|
|
23257
|
-
const inH = first.inHandle ?? mirror(last, out);
|
|
23258
|
-
if (out || first.inHandle) {
|
|
23259
|
-
const c1 = out ?? last;
|
|
23260
|
-
const c2 = inH ?? first;
|
|
23261
|
-
const [c1x, c1y] = w2s(c1.x, c1.y, view);
|
|
23262
|
-
const [c2x, c2y] = w2s(c2.x, c2.y, view);
|
|
23263
|
-
const [tx, ty] = w2s(first.x, first.y, view);
|
|
23264
|
-
cmds.push(PATH_C);
|
|
23265
|
-
xs.push(c1x, c1y, c2x, c2y, tx, ty);
|
|
23266
|
-
}
|
|
23267
|
-
cmds.push(PATH_Z);
|
|
23268
|
-
}
|
|
23269
|
-
return {
|
|
23270
|
-
kind: "polygon",
|
|
23271
|
-
commands: new Uint8Array(cmds),
|
|
23272
|
-
coords: new Float32Array(xs),
|
|
23273
|
-
fillRule: "nonzero"
|
|
23956
|
+
const pt = (p) => {
|
|
23957
|
+
const [x, y] = w2s(p.x, p.y, view);
|
|
23958
|
+
return { x, y };
|
|
23274
23959
|
};
|
|
23960
|
+
const anchors = sp.anchors.map((a) => ({
|
|
23961
|
+
...pt(a),
|
|
23962
|
+
...a.inHandle ? { inHandle: pt(a.inHandle) } : {},
|
|
23963
|
+
...a.outHandle ? { outHandle: pt(a.outHandle) } : {}
|
|
23964
|
+
}));
|
|
23965
|
+
return anchorsToPath([anchors], [sp.closed]);
|
|
23275
23966
|
}
|
|
23276
23967
|
function createPenPreviewLayer(opts) {
|
|
23277
23968
|
const style = { ...DEFAULT_STYLE2, ...opts.style ?? {} };
|
|
@@ -23360,10 +24051,10 @@ function createPenPreviewLayer(opts) {
|
|
|
23360
24051
|
path: approximateCircle(hx, hy, HANDLE_DOT_RADIUS_PX),
|
|
23361
24052
|
fill: { fill: "solid", color: style.handleStroke }
|
|
23362
24053
|
});
|
|
23363
|
-
const inSide =
|
|
24054
|
+
const inSide = a.inHandle;
|
|
23364
24055
|
if (inSide) {
|
|
23365
24056
|
const [mx, my] = w2s(inSide.x, inSide.y, view);
|
|
23366
|
-
const
|
|
24057
|
+
const inLine = {
|
|
23367
24058
|
kind: "polygon",
|
|
23368
24059
|
commands: new Uint8Array([PATH_M, PATH_L]),
|
|
23369
24060
|
coords: new Float32Array([ax, ay, mx, my]),
|
|
@@ -23371,7 +24062,7 @@ function createPenPreviewLayer(opts) {
|
|
|
23371
24062
|
};
|
|
23372
24063
|
out.push({
|
|
23373
24064
|
kind: "path",
|
|
23374
|
-
path:
|
|
24065
|
+
path: inLine,
|
|
23375
24066
|
stroke: { paint: { fill: "solid", color: style.handleStroke }, width: 1 }
|
|
23376
24067
|
});
|
|
23377
24068
|
out.push({
|
|
@@ -23587,7 +24278,7 @@ function useBuiltinShapeTools(args) {
|
|
|
23587
24278
|
const { scene, adapter, options } = args;
|
|
23588
24279
|
const seqRef = useRef(0);
|
|
23589
24280
|
const freshId = (prefix) => asNodeId(`${prefix}-${++seqRef.current}`);
|
|
23590
|
-
const nextFill = () =>
|
|
24281
|
+
const nextFill = () => cycleFill(seqRef.current);
|
|
23591
24282
|
const layerOf = () => scene.layers[0]?.id ?? "default";
|
|
23592
24283
|
const makeLeaf = (id, pose, data) => ({
|
|
23593
24284
|
id,
|
|
@@ -23604,6 +24295,15 @@ function useBuiltinShapeTools(args) {
|
|
|
23604
24295
|
const polygon = usePolygonTool();
|
|
23605
24296
|
const star = useStarTool();
|
|
23606
24297
|
const pencil = usePencilTool();
|
|
24298
|
+
const makePenLeaf = (carrier) => {
|
|
24299
|
+
const id = freshId("pn");
|
|
24300
|
+
const color = nextFill();
|
|
24301
|
+
return makeLeaf(id, carrier.bounds, {
|
|
24302
|
+
path: carrier.path,
|
|
24303
|
+
fill: carrier.closed ? solid(nextFill()) : null,
|
|
24304
|
+
stroke: strokeOf(color, 2)
|
|
24305
|
+
});
|
|
24306
|
+
};
|
|
23607
24307
|
const pen = usePenTool({
|
|
23608
24308
|
snapPoint: snapPoint2,
|
|
23609
24309
|
wrapPath: (path, { closed }) => {
|
|
@@ -23612,16 +24312,11 @@ function useBuiltinShapeTools(args) {
|
|
|
23612
24312
|
},
|
|
23613
24313
|
adapter: {
|
|
23614
24314
|
addNode: (carrier) => {
|
|
23615
|
-
const
|
|
23616
|
-
const color = nextFill();
|
|
23617
|
-
const node = makeLeaf(id, carrier.bounds, {
|
|
23618
|
-
path: carrier.path,
|
|
23619
|
-
fill: carrier.closed ? solid(nextFill()) : null,
|
|
23620
|
-
stroke: strokeOf(color, 2)
|
|
23621
|
-
});
|
|
24315
|
+
const node = makePenLeaf(carrier);
|
|
23622
24316
|
adapter.insertNode(node);
|
|
23623
|
-
return String(id);
|
|
24317
|
+
return String(node.id);
|
|
23624
24318
|
},
|
|
24319
|
+
makeNode: (carrier) => makePenLeaf(carrier),
|
|
23625
24320
|
setSelection: (ids) => adapter.setSelection(ids)
|
|
23626
24321
|
}
|
|
23627
24322
|
});
|
|
@@ -23891,6 +24586,19 @@ function useEditAnchorsDepSource(scene, selection, adapter, externalState, optio
|
|
|
23891
24586
|
}
|
|
23892
24587
|
const selectedAnchors = effectiveId ? ext?.getSelectedAnchors() ?? EMPTY_ANCHOR_SELECTION : EMPTY_ANCHOR_SELECTION;
|
|
23893
24588
|
const marquee = effectiveId ? ext?.getMarquee() ?? null : null;
|
|
24589
|
+
const editOps = (id, worldPath, label) => {
|
|
24590
|
+
const wp = worldPath;
|
|
24591
|
+
const node = sc.get(id);
|
|
24592
|
+
if (!node) return [];
|
|
24593
|
+
const storage = classifyStorage(node);
|
|
24594
|
+
if (!storage) return [];
|
|
24595
|
+
if (storage.kind === "pose") return [createTransformOp({ id, from: node.pose, to: wp, label })];
|
|
24596
|
+
const { pose: newPose, path: aligned } = worldEditToStorage(storage.pose, wp);
|
|
24597
|
+
return [
|
|
24598
|
+
createTransformOp({ id, from: node.pose, to: newPose, label }),
|
|
24599
|
+
createSetDataOp({ id, from: node.data, to: { ...node.data, path: aligned }, label })
|
|
24600
|
+
];
|
|
24601
|
+
};
|
|
23894
24602
|
return {
|
|
23895
24603
|
editingId: effectiveId,
|
|
23896
24604
|
setEditingId(id) {
|
|
@@ -23933,24 +24641,10 @@ function useEditAnchorsDepSource(scene, selection, adapter, externalState, optio
|
|
|
23933
24641
|
return { pose: node.pose, data: node.data };
|
|
23934
24642
|
},
|
|
23935
24643
|
applyEdit(id, worldPath, label) {
|
|
23936
|
-
const
|
|
23937
|
-
|
|
23938
|
-
|
|
23939
|
-
|
|
23940
|
-
if (!storage) return;
|
|
23941
|
-
if (storage.kind === "pose") {
|
|
23942
|
-
ad.applyOps([createTransformOp({ id, from: node.pose, to: wp, label })], label);
|
|
23943
|
-
return;
|
|
23944
|
-
}
|
|
23945
|
-
const { pose: newPose, path: aligned } = worldEditToStorage(storage.pose, wp);
|
|
23946
|
-
ad.applyOps(
|
|
23947
|
-
[
|
|
23948
|
-
createTransformOp({ id, from: node.pose, to: newPose, label }),
|
|
23949
|
-
createSetDataOp({ id, from: node.data, to: { ...node.data, path: aligned }, label })
|
|
23950
|
-
],
|
|
23951
|
-
label
|
|
23952
|
-
);
|
|
23953
|
-
}
|
|
24644
|
+
const ops = editOps(id, worldPath, label);
|
|
24645
|
+
if (ops.length > 0) ad.applyOps(ops, label);
|
|
24646
|
+
},
|
|
24647
|
+
editOps
|
|
23954
24648
|
};
|
|
23955
24649
|
});
|
|
23956
24650
|
}
|
|
@@ -24143,7 +24837,7 @@ function useInsertDepSource(scene, adapter, factories) {
|
|
|
24143
24837
|
seq = ++insertSeqRef.current;
|
|
24144
24838
|
id = asNodeId(`kit-${kind}-${seq}`);
|
|
24145
24839
|
}
|
|
24146
|
-
const color =
|
|
24840
|
+
const color = cycleFill(seq);
|
|
24147
24841
|
const fill = solid(color);
|
|
24148
24842
|
const layer = sc.layers[0]?.id ?? "default";
|
|
24149
24843
|
const extent = insertPreviewExtent({ shape: kind, bounds, extras });
|
|
@@ -24239,15 +24933,16 @@ function useDispatcherDepSource(dispatcher) {
|
|
|
24239
24933
|
cancelAll: (reason) => ref.current.cancelAll(reason)
|
|
24240
24934
|
}));
|
|
24241
24935
|
}
|
|
24242
|
-
function useLayoutDepSource(layouts) {
|
|
24243
|
-
const ref = useRef(layouts);
|
|
24244
|
-
ref.current = layouts;
|
|
24936
|
+
function useLayoutDepSource(layouts, dropTarget) {
|
|
24937
|
+
const ref = useRef({ layouts, dropTarget });
|
|
24938
|
+
ref.current = { layouts, dropTarget };
|
|
24245
24939
|
useDepSource("layout", () => ({
|
|
24246
24940
|
getLayout: (containerId) => {
|
|
24247
|
-
const l = ref.current;
|
|
24941
|
+
const l = ref.current.layouts;
|
|
24248
24942
|
if (!l) return null;
|
|
24249
24943
|
return typeof l === "function" ? l(containerId) : l[containerId] ?? null;
|
|
24250
|
-
}
|
|
24944
|
+
},
|
|
24945
|
+
dropTarget: ref.current.dropTarget
|
|
24251
24946
|
}));
|
|
24252
24947
|
}
|
|
24253
24948
|
function useGeometryProjection(projection) {
|
|
@@ -24348,8 +25043,8 @@ var kitImageHandler = {
|
|
|
24348
25043
|
|
|
24349
25044
|
// src/features/ingestion/svgHandler.ts
|
|
24350
25045
|
var SVG_MIME = "image/svg+xml";
|
|
24351
|
-
var
|
|
24352
|
-
var
|
|
25046
|
+
var FALLBACK_WIDTH2 = 300;
|
|
25047
|
+
var FALLBACK_HEIGHT2 = 150;
|
|
24353
25048
|
function isSvgFileItem(item) {
|
|
24354
25049
|
if (item.kind !== "file") return false;
|
|
24355
25050
|
if (item.mime === SVG_MIME) return true;
|
|
@@ -24387,8 +25082,8 @@ var defaultMeasure2 = async (file) => {
|
|
|
24387
25082
|
img.src = url;
|
|
24388
25083
|
await img.decode();
|
|
24389
25084
|
return {
|
|
24390
|
-
width: img.naturalWidth ||
|
|
24391
|
-
height: img.naturalHeight ||
|
|
25085
|
+
width: img.naturalWidth || FALLBACK_WIDTH2,
|
|
25086
|
+
height: img.naturalHeight || FALLBACK_HEIGHT2
|
|
24392
25087
|
};
|
|
24393
25088
|
} finally {
|
|
24394
25089
|
URL.revokeObjectURL(url);
|
|
@@ -24932,6 +25627,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
24932
25627
|
insertNodeFactories,
|
|
24933
25628
|
ingestion,
|
|
24934
25629
|
layouts,
|
|
25630
|
+
layoutDropTarget,
|
|
24935
25631
|
poseComposition,
|
|
24936
25632
|
geometryProjection,
|
|
24937
25633
|
routing,
|
|
@@ -25063,6 +25759,12 @@ function SceneCanvasInner(props, ref) {
|
|
|
25063
25759
|
}, [scene, selectionOptions, selectionMode]);
|
|
25064
25760
|
const internalSelection = useSelection(derivedSelectionOptions);
|
|
25065
25761
|
const baseSelection = selectionProp ?? internalSelection;
|
|
25762
|
+
const baseAdapterMethods = baseSelection.adapterMethods;
|
|
25763
|
+
const readOnlyAdapterMethods = useMemo(() => ({
|
|
25764
|
+
getSelection: () => baseAdapterMethods.getSelection(),
|
|
25765
|
+
setSelection: () => {
|
|
25766
|
+
}
|
|
25767
|
+
}), [baseAdapterMethods]);
|
|
25066
25768
|
const selection = useMemo(() => {
|
|
25067
25769
|
if (selectionMode !== "none") return baseSelection;
|
|
25068
25770
|
const noopSet = () => {
|
|
@@ -25074,9 +25776,10 @@ function SceneCanvasInner(props, ref) {
|
|
|
25074
25776
|
remove: noopSet,
|
|
25075
25777
|
toggle: noopSet,
|
|
25076
25778
|
clear: noopSet,
|
|
25077
|
-
applyClick: noopSet
|
|
25779
|
+
applyClick: noopSet,
|
|
25780
|
+
adapterMethods: readOnlyAdapterMethods
|
|
25078
25781
|
};
|
|
25079
|
-
}, [baseSelection, selectionMode]);
|
|
25782
|
+
}, [baseSelection, selectionMode, readOnlyAdapterMethods]);
|
|
25080
25783
|
const currentSelection = selection.current;
|
|
25081
25784
|
const selectionKinds = useMemo(() => {
|
|
25082
25785
|
if (currentSelection.length === 0) return void 0;
|
|
@@ -25168,9 +25871,20 @@ function SceneCanvasInner(props, ref) {
|
|
|
25168
25871
|
const [sceneLayerGate] = useState(createSceneLayerGate);
|
|
25169
25872
|
const layerIsPainted = useCallback((layerId) => sceneLayerGate(surfaceViewRegistry?.surface()?.layers() ?? [], layerPaintRef.current)(layerId), [surfaceViewRegistry, sceneLayerGate]);
|
|
25170
25873
|
const viewLayerGate = layerVisibility !== void 0 || layerOrder !== void 0 ? layerIsPainted : void 0;
|
|
25171
|
-
const
|
|
25874
|
+
const framed = poseComposition !== void 0 && poseComposition.closure !== "identity";
|
|
25875
|
+
const adapter = useSceneAdapter(scene, {
|
|
25876
|
+
// `adapterMethods` is memoized; `selection` itself is a fresh object each render.
|
|
25877
|
+
selection: selection.adapterMethods,
|
|
25878
|
+
poseDescriptor: descriptor,
|
|
25879
|
+
commitInsert: insertTool?.create,
|
|
25880
|
+
insertLayer: insertTool?.layer,
|
|
25881
|
+
layouts,
|
|
25882
|
+
poseComposition,
|
|
25883
|
+
cascadeContainerPose: !framed
|
|
25884
|
+
});
|
|
25885
|
+
const { selectTool: internalSelect, pickEvery: internalPickEvery, pickBest: internalPickBest, boundsOf: internalBoundsOf } = useSceneSelectTool({
|
|
25172
25886
|
scene,
|
|
25173
|
-
|
|
25887
|
+
adapter,
|
|
25174
25888
|
poseDescriptor: descriptor,
|
|
25175
25889
|
geometry,
|
|
25176
25890
|
// The pick tolerance is declared in screen pixels; this is what converts it.
|
|
@@ -25180,10 +25894,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
25180
25894
|
// that once per walk instead of once per candidate.
|
|
25181
25895
|
...alphaFor ? { alphaOf: composedAlphaFor } : {},
|
|
25182
25896
|
...viewLayerGate ? { layerIsPainted: viewLayerGate } : {},
|
|
25183
|
-
selectTool: selectToolWithDefaults
|
|
25184
|
-
...insertTool ? { insertTool } : {},
|
|
25185
|
-
...layouts ? { layouts } : {},
|
|
25186
|
-
...poseComposition ? { poseComposition } : {}
|
|
25897
|
+
selectTool: selectToolWithDefaults
|
|
25187
25898
|
});
|
|
25188
25899
|
const getNodeAtPoint = useMemo(() => {
|
|
25189
25900
|
if (!internalPickEvery) return void 0;
|
|
@@ -25695,6 +26406,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
25695
26406
|
editAnchorsExternalState,
|
|
25696
26407
|
anchorEditingAllowed,
|
|
25697
26408
|
layouts,
|
|
26409
|
+
layoutDropTarget,
|
|
25698
26410
|
poseComposition,
|
|
25699
26411
|
insertNodeFactories,
|
|
25700
26412
|
snapPoint: toolOptions?.snapPoint,
|
|
@@ -25945,6 +26657,7 @@ function StandardActionsRegistrar({
|
|
|
25945
26657
|
anchorEditingAllowed,
|
|
25946
26658
|
poseComposition,
|
|
25947
26659
|
layouts,
|
|
26660
|
+
layoutDropTarget,
|
|
25948
26661
|
insertNodeFactories,
|
|
25949
26662
|
snapPoint: snapPoint2,
|
|
25950
26663
|
canvasRef,
|
|
@@ -25993,7 +26706,7 @@ function StandardActionsRegistrar({
|
|
|
25993
26706
|
useViewportActions({ pan: viewportPanEnabled, zoom: viewportZoom, pinchZoom: viewportPinchZoom });
|
|
25994
26707
|
useAreaSelectDepSource(scene, selection, poseDescriptor, poseComposition, alphaOf);
|
|
25995
26708
|
useNodeAtPointDepSource(pickEvery);
|
|
25996
|
-
useLayoutDepSource(layouts);
|
|
26709
|
+
useLayoutDepSource(layouts, layoutDropTarget);
|
|
25997
26710
|
useInsertDepSource(scene, adapter, insertNodeFactories);
|
|
25998
26711
|
useSnapDepSource(snapPoint2);
|
|
25999
26712
|
useIngestionDepSource(canvasRef, () => currentViewRef.current, ingestionResolveSrc, ingestionSvg, ingestionClipboard);
|
|
@@ -26186,7 +26899,8 @@ function shapeSchema(opts = {}) {
|
|
|
26186
26899
|
letterSpacing: { kind: "number", name: "Tracking", description: "Extra advance per glyph, world units.", default: 0, step: 0.1 },
|
|
26187
26900
|
underline: { kind: "boolean", name: "Underline", description: "Underline the text.", default: false, control: "toggle", short: "U", pair: "Decoration" },
|
|
26188
26901
|
strikethrough: { kind: "boolean", name: "Strikethrough", description: "Strike through the text.", default: false, control: "toggle", short: "S", pair: "Decoration" },
|
|
26189
|
-
overline: { kind: "boolean", name: "Overline", description: "Rule the text above its ascent.", default: false, control: "toggle", short: "O", pair: "Decoration" }
|
|
26902
|
+
overline: { kind: "boolean", name: "Overline", description: "Rule the text above its ascent.", default: false, control: "toggle", short: "O", pair: "Decoration" },
|
|
26903
|
+
textTransform: { kind: "enum", name: "Case", description: "Draw the text in capitals, lowercase or title case. The text itself is unchanged.", default: "none", control: "toggle", options: [{ value: "none", label: "None", short: "\u2013" }, { value: "uppercase", label: "Uppercase", short: "AA" }, { value: "lowercase", label: "Lowercase", short: "aa" }, { value: "capitalize", label: "Capitalize", short: "Aa" }] }
|
|
26190
26904
|
}
|
|
26191
26905
|
},
|
|
26192
26906
|
paragraph: {
|
|
@@ -27213,6 +27927,20 @@ function createTextLayer(opts) {
|
|
|
27213
27927
|
}
|
|
27214
27928
|
};
|
|
27215
27929
|
}
|
|
27930
|
+
function sourceClusters(cells) {
|
|
27931
|
+
const out = [];
|
|
27932
|
+
for (const c of cells) {
|
|
27933
|
+
const prev = out[out.length - 1];
|
|
27934
|
+
if (prev && prev.srcIndex === c.srcIndex && prev.srcEnd === c.srcEnd) {
|
|
27935
|
+
const x0 = Math.min(prev.x, c.x);
|
|
27936
|
+
const x1 = Math.max(prev.x + prev.advance, c.x + c.advance);
|
|
27937
|
+
out[out.length - 1] = { ...prev, x: x0, advance: x1 - x0 };
|
|
27938
|
+
} else {
|
|
27939
|
+
out.push(c);
|
|
27940
|
+
}
|
|
27941
|
+
}
|
|
27942
|
+
return out;
|
|
27943
|
+
}
|
|
27216
27944
|
function pointInTextPose(x, y, pose, opts = {}) {
|
|
27217
27945
|
const p = opts.padding ?? 0;
|
|
27218
27946
|
return x >= pose.x - p && x <= pose.x + pose.width + p && y >= pose.y - p && y <= pose.y + pose.height + p;
|
|
@@ -27231,8 +27959,8 @@ function caretIndexAt(x, y, pose) {
|
|
|
27231
27959
|
break;
|
|
27232
27960
|
}
|
|
27233
27961
|
}
|
|
27234
|
-
|
|
27235
|
-
|
|
27962
|
+
if (line.cells.length === 0) return line.srcEnd;
|
|
27963
|
+
const cells = sourceClusters(line.cells);
|
|
27236
27964
|
const visual = cells.map((_, i) => i).sort((a, b) => cells[a].x - cells[b].x);
|
|
27237
27965
|
for (const i of visual) {
|
|
27238
27966
|
const c = cells[i];
|
|
@@ -27265,6 +27993,7 @@ function solidColor(p) {
|
|
|
27265
27993
|
if ("color" in p) return p.color;
|
|
27266
27994
|
return null;
|
|
27267
27995
|
}
|
|
27996
|
+
var TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
27268
27997
|
var EMPTY_STYLE = {
|
|
27269
27998
|
bold: false,
|
|
27270
27999
|
italic: false,
|
|
@@ -27320,6 +28049,8 @@ function styleStateFromElement(el, parent) {
|
|
|
27320
28049
|
const shift = parseFloat(rawShift);
|
|
27321
28050
|
if (Number.isFinite(shift)) next.baselineShift = shift;
|
|
27322
28051
|
}
|
|
28052
|
+
const tt = el.style.textTransform;
|
|
28053
|
+
if (TRANSFORMS.has(tt)) next.textTransform = tt;
|
|
27323
28054
|
if (el.style.fontFamily) next.fontFamily = el.style.fontFamily;
|
|
27324
28055
|
if (el.style.color) next.color = el.style.color;
|
|
27325
28056
|
const ls = el.style.letterSpacing;
|
|
@@ -27333,23 +28064,24 @@ function styleStateFromElement(el, parent) {
|
|
|
27333
28064
|
return next;
|
|
27334
28065
|
}
|
|
27335
28066
|
function styleEquals(a, b) {
|
|
27336
|
-
return a.bold === b.bold && a.italic === b.italic && a.underline === b.underline && a.strikethrough === b.strikethrough && a.overline === b.overline && a.fontSize === b.fontSize && a.fontFamily === b.fontFamily && a.color === b.color && a.letterSpacing === b.letterSpacing && a.script === b.script && a.baselineShift === b.baselineShift && a.fontScale === b.fontScale;
|
|
28067
|
+
return a.bold === b.bold && a.italic === b.italic && a.underline === b.underline && a.strikethrough === b.strikethrough && a.overline === b.overline && a.fontSize === b.fontSize && a.fontFamily === b.fontFamily && a.color === b.color && a.letterSpacing === b.letterSpacing && a.script === b.script && a.baselineShift === b.baselineShift && a.fontScale === b.fontScale && a.textTransform === b.textTransform;
|
|
27337
28068
|
}
|
|
27338
28069
|
function toRun(text, style) {
|
|
27339
|
-
const
|
|
27340
|
-
if (style.bold)
|
|
27341
|
-
if (style.italic)
|
|
27342
|
-
if (style.fontSize != null)
|
|
27343
|
-
if (style.fontFamily != null)
|
|
27344
|
-
if (style.color != null)
|
|
27345
|
-
if (style.letterSpacing != null)
|
|
27346
|
-
if (style.underline)
|
|
27347
|
-
if (style.strikethrough)
|
|
27348
|
-
if (style.overline)
|
|
27349
|
-
if (style.script != null)
|
|
27350
|
-
if (style.baselineShift != null)
|
|
27351
|
-
if (style.fontScale != null)
|
|
27352
|
-
|
|
28070
|
+
const run2 = { text };
|
|
28071
|
+
if (style.bold) run2.bold = true;
|
|
28072
|
+
if (style.italic) run2.italic = true;
|
|
28073
|
+
if (style.fontSize != null) run2.fontSize = style.fontSize;
|
|
28074
|
+
if (style.fontFamily != null) run2.fontFamily = style.fontFamily;
|
|
28075
|
+
if (style.color != null) run2.fill = { fill: "solid", color: style.color };
|
|
28076
|
+
if (style.letterSpacing != null) run2.letterSpacing = style.letterSpacing;
|
|
28077
|
+
if (style.underline) run2.underline = true;
|
|
28078
|
+
if (style.strikethrough) run2.strikethrough = true;
|
|
28079
|
+
if (style.overline) run2.overline = true;
|
|
28080
|
+
if (style.script != null) run2.script = style.script;
|
|
28081
|
+
if (style.baselineShift != null) run2.baselineShift = style.baselineShift;
|
|
28082
|
+
if (style.fontScale != null) run2.fontScale = style.fontScale;
|
|
28083
|
+
if (style.textTransform != null) run2.textTransform = style.textTransform;
|
|
28084
|
+
return run2;
|
|
27353
28085
|
}
|
|
27354
28086
|
function domToRuns(parent) {
|
|
27355
28087
|
const fragments = [];
|
|
@@ -27391,27 +28123,28 @@ function domToRuns(parent) {
|
|
|
27391
28123
|
}
|
|
27392
28124
|
function runsToDom(runs, parent) {
|
|
27393
28125
|
parent.replaceChildren();
|
|
27394
|
-
for (const
|
|
28126
|
+
for (const run2 of runs) {
|
|
27395
28127
|
const span = document.createElement("span");
|
|
27396
28128
|
span.setAttribute("data-run", "");
|
|
27397
|
-
span.textContent =
|
|
27398
|
-
if (
|
|
27399
|
-
if (
|
|
27400
|
-
if (
|
|
27401
|
-
else if (
|
|
27402
|
-
if (
|
|
27403
|
-
const color = solidColor(
|
|
28129
|
+
span.textContent = run2.text;
|
|
28130
|
+
if (run2.bold) span.style.fontWeight = "700";
|
|
28131
|
+
if (run2.italic) span.style.fontStyle = "italic";
|
|
28132
|
+
if (run2.fontSize != null) span.style.fontSize = `${run2.fontSize}px`;
|
|
28133
|
+
else if (run2.fontScale != null) span.style.fontSize = `${run2.fontScale * 100}%`;
|
|
28134
|
+
if (run2.fontFamily != null) span.style.fontFamily = run2.fontFamily;
|
|
28135
|
+
const color = solidColor(run2.fill);
|
|
27404
28136
|
if (color != null) span.style.color = color;
|
|
27405
28137
|
const decorations = [];
|
|
27406
|
-
if (
|
|
27407
|
-
if (
|
|
27408
|
-
if (
|
|
28138
|
+
if (run2.underline) decorations.push("underline");
|
|
28139
|
+
if (run2.strikethrough) decorations.push("line-through");
|
|
28140
|
+
if (run2.overline) decorations.push("overline");
|
|
27409
28141
|
if (decorations.length > 0) span.style.textDecoration = decorations.join(" ");
|
|
27410
|
-
if (
|
|
27411
|
-
if (
|
|
27412
|
-
span.setAttribute("data-baseline-shift", String(
|
|
28142
|
+
if (run2.script != null) span.style.verticalAlign = run2.script;
|
|
28143
|
+
if (run2.baselineShift != null) {
|
|
28144
|
+
span.setAttribute("data-baseline-shift", String(run2.baselineShift));
|
|
27413
28145
|
}
|
|
27414
|
-
if (
|
|
28146
|
+
if (run2.letterSpacing != null) span.style.letterSpacing = `${run2.letterSpacing}px`;
|
|
28147
|
+
if (run2.textTransform != null) span.style.textTransform = run2.textTransform;
|
|
27415
28148
|
parent.appendChild(span);
|
|
27416
28149
|
}
|
|
27417
28150
|
}
|
|
@@ -27471,7 +28204,8 @@ var STYLE_KEYS = [
|
|
|
27471
28204
|
"stroke",
|
|
27472
28205
|
"script",
|
|
27473
28206
|
"baselineShift",
|
|
27474
|
-
"fontScale"
|
|
28207
|
+
"fontScale",
|
|
28208
|
+
"textTransform"
|
|
27475
28209
|
];
|
|
27476
28210
|
var FLAG_KEYS = /* @__PURE__ */ new Set([
|
|
27477
28211
|
"bold",
|
|
@@ -27495,8 +28229,8 @@ function deepEqual(a, b) {
|
|
|
27495
28229
|
}
|
|
27496
28230
|
return false;
|
|
27497
28231
|
}
|
|
27498
|
-
function valueAt(
|
|
27499
|
-
const value =
|
|
28232
|
+
function valueAt(run2, key) {
|
|
28233
|
+
const value = run2[key];
|
|
27500
28234
|
if (FLAG_KEYS.has(key)) return value ?? false;
|
|
27501
28235
|
return value;
|
|
27502
28236
|
}
|
|
@@ -27509,7 +28243,7 @@ function sameStyle(a, b) {
|
|
|
27509
28243
|
}
|
|
27510
28244
|
function totalLength(runs) {
|
|
27511
28245
|
let n = 0;
|
|
27512
|
-
for (const
|
|
28246
|
+
for (const run2 of runs) n += run2.text.length;
|
|
27513
28247
|
return n;
|
|
27514
28248
|
}
|
|
27515
28249
|
function styleAtRange(runs, start, end) {
|
|
@@ -27520,14 +28254,14 @@ function styleAtRange(runs, start, end) {
|
|
|
27520
28254
|
const mixed = /* @__PURE__ */ new Set();
|
|
27521
28255
|
let seen = false;
|
|
27522
28256
|
let pos = 0;
|
|
27523
|
-
for (const
|
|
27524
|
-
const runEnd = pos +
|
|
28257
|
+
for (const run2 of runs) {
|
|
28258
|
+
const runEnd = pos + run2.text.length;
|
|
27525
28259
|
const a = Math.max(pos, lo);
|
|
27526
28260
|
const b = Math.min(runEnd, hi);
|
|
27527
28261
|
pos = runEnd;
|
|
27528
28262
|
if (!(a < b)) continue;
|
|
27529
28263
|
for (const key of STYLE_KEYS) {
|
|
27530
|
-
const value = valueAt(
|
|
28264
|
+
const value = valueAt(run2, key);
|
|
27531
28265
|
if (!seen) first.set(key, value);
|
|
27532
28266
|
else if (!mixed.has(key) && !sameValue(key, first.get(key), value)) {
|
|
27533
28267
|
mixed.add(key);
|
|
@@ -27548,14 +28282,14 @@ function styleAtRange(runs, start, end) {
|
|
|
27548
28282
|
}
|
|
27549
28283
|
function runsCarryStyling(runs) {
|
|
27550
28284
|
return runs.some(
|
|
27551
|
-
(
|
|
27552
|
-
const value =
|
|
28285
|
+
(run2) => STYLE_KEYS.some((key) => {
|
|
28286
|
+
const value = run2[key];
|
|
27553
28287
|
return value !== void 0 && value !== false;
|
|
27554
28288
|
})
|
|
27555
28289
|
);
|
|
27556
28290
|
}
|
|
27557
|
-
function patchRun(
|
|
27558
|
-
const next = { ...
|
|
28291
|
+
function patchRun(run2, patch) {
|
|
28292
|
+
const next = { ...run2 };
|
|
27559
28293
|
for (const key of STYLE_KEYS) {
|
|
27560
28294
|
if (!(key in patch)) continue;
|
|
27561
28295
|
const value = patch[key];
|
|
@@ -27566,11 +28300,11 @@ function patchRun(run, patch) {
|
|
|
27566
28300
|
}
|
|
27567
28301
|
function coalesceRuns(runs) {
|
|
27568
28302
|
const out = [];
|
|
27569
|
-
for (const
|
|
27570
|
-
if (
|
|
28303
|
+
for (const run2 of runs) {
|
|
28304
|
+
if (run2.text.length === 0) continue;
|
|
27571
28305
|
const prev = out[out.length - 1];
|
|
27572
|
-
if (prev && sameStyle(prev,
|
|
27573
|
-
else out.push({ ...
|
|
28306
|
+
if (prev && sameStyle(prev, run2)) prev.text += run2.text;
|
|
28307
|
+
else out.push({ ...run2 });
|
|
27574
28308
|
}
|
|
27575
28309
|
return out;
|
|
27576
28310
|
}
|
|
@@ -27580,16 +28314,16 @@ function applyStyleToRange(runs, start, end, patch) {
|
|
|
27580
28314
|
if (!(lo < hi)) return coalesceRuns(runs);
|
|
27581
28315
|
const out = [];
|
|
27582
28316
|
let pos = 0;
|
|
27583
|
-
for (const
|
|
27584
|
-
const runEnd = pos +
|
|
28317
|
+
for (const run2 of runs) {
|
|
28318
|
+
const runEnd = pos + run2.text.length;
|
|
27585
28319
|
const a = Math.max(pos, lo);
|
|
27586
28320
|
const b = Math.min(runEnd, hi);
|
|
27587
28321
|
if (a < b) {
|
|
27588
|
-
if (pos < a) out.push({ ...
|
|
27589
|
-
out.push(patchRun({ ...
|
|
27590
|
-
if (b < runEnd) out.push({ ...
|
|
28322
|
+
if (pos < a) out.push({ ...run2, text: run2.text.slice(0, a - pos) });
|
|
28323
|
+
out.push(patchRun({ ...run2, text: run2.text.slice(a - pos, b - pos) }, patch));
|
|
28324
|
+
if (b < runEnd) out.push({ ...run2, text: run2.text.slice(b - pos) });
|
|
27591
28325
|
} else {
|
|
27592
|
-
out.push({ ...
|
|
28326
|
+
out.push({ ...run2 });
|
|
27593
28327
|
}
|
|
27594
28328
|
pos = runEnd;
|
|
27595
28329
|
}
|
|
@@ -27685,7 +28419,7 @@ function patchForToggle(current, toggle, nodeStyle) {
|
|
|
27685
28419
|
function styleAtCaret(runs, at) {
|
|
27686
28420
|
return at > 0 ? styleAtRange(runs, at - 1, at) : styleAtRange(runs, 0, 1);
|
|
27687
28421
|
}
|
|
27688
|
-
function spliceRunAt(runs, at,
|
|
28422
|
+
function spliceRunAt(runs, at, run2) {
|
|
27689
28423
|
const out = [];
|
|
27690
28424
|
let pos = 0;
|
|
27691
28425
|
let placed2 = false;
|
|
@@ -27694,7 +28428,7 @@ function spliceRunAt(runs, at, run) {
|
|
|
27694
28428
|
if (!placed2 && at <= end) {
|
|
27695
28429
|
const k = at - pos;
|
|
27696
28430
|
if (k > 0) out.push({ ...r, text: r.text.slice(0, k) });
|
|
27697
|
-
out.push(
|
|
28431
|
+
out.push(run2);
|
|
27698
28432
|
if (k < r.text.length) out.push({ ...r, text: r.text.slice(k) });
|
|
27699
28433
|
placed2 = true;
|
|
27700
28434
|
} else {
|
|
@@ -27702,7 +28436,7 @@ function spliceRunAt(runs, at, run) {
|
|
|
27702
28436
|
}
|
|
27703
28437
|
pos = end;
|
|
27704
28438
|
}
|
|
27705
|
-
if (!placed2) out.push(
|
|
28439
|
+
if (!placed2) out.push(run2);
|
|
27706
28440
|
return applyStyleToRange(out, 0, 0, {});
|
|
27707
28441
|
}
|
|
27708
28442
|
function restoreOverlayFocus(overlay, range) {
|
|
@@ -28112,6 +28846,7 @@ function applyOverlayStyle(el, style) {
|
|
|
28112
28846
|
if (style.underline) decorations.push("underline");
|
|
28113
28847
|
if (style.strikethrough) decorations.push("line-through");
|
|
28114
28848
|
el.style.textDecoration = decorations.length > 0 ? decorations.join(" ") : "none";
|
|
28849
|
+
el.style.textTransform = style.textTransform;
|
|
28115
28850
|
el.style.whiteSpace = style.wrap ? "pre-wrap" : "pre";
|
|
28116
28851
|
el.style.overflowWrap = "normal";
|
|
28117
28852
|
el.style.wordBreak = "normal";
|
|
@@ -28313,7 +29048,8 @@ var OVERRIDES = [
|
|
|
28313
29048
|
"fill",
|
|
28314
29049
|
"script",
|
|
28315
29050
|
"baselineShift",
|
|
28316
|
-
"fontScale"
|
|
29051
|
+
"fontScale",
|
|
29052
|
+
"textTransform"
|
|
28317
29053
|
];
|
|
28318
29054
|
function effectiveRangeStyle(range, style, paint) {
|
|
28319
29055
|
const resolved = resolveTextStyle(style, paint);
|
|
@@ -28321,13 +29057,14 @@ function effectiveRangeStyle(range, style, paint) {
|
|
|
28321
29057
|
const out = {
|
|
28322
29058
|
fontFamily: resolved.fontFamily,
|
|
28323
29059
|
fontSize: resolved.fontSize,
|
|
28324
|
-
letterSpacing: resolved.letterSpacing
|
|
29060
|
+
letterSpacing: resolved.letterSpacing,
|
|
29061
|
+
textTransform: resolved.textTransform
|
|
28325
29062
|
};
|
|
28326
29063
|
if (resolved.fill !== null) out.fill = resolved.fill;
|
|
28327
29064
|
for (const key of FLAGS) {
|
|
28328
29065
|
const on = nodeHasFlag(node, key);
|
|
28329
|
-
const
|
|
28330
|
-
out[key] = on ||
|
|
29066
|
+
const run2 = range?.[key];
|
|
29067
|
+
out[key] = on || run2 === void 0 ? on : run2;
|
|
28331
29068
|
}
|
|
28332
29069
|
if (range === null) return out;
|
|
28333
29070
|
for (const key of OVERRIDES) {
|
|
@@ -28567,7 +29304,7 @@ function asMesh(fill) {
|
|
|
28567
29304
|
}
|
|
28568
29305
|
function shifted(color, dL, cScale, dHueDeg) {
|
|
28569
29306
|
const [r, g, b, a] = resolveColor(color);
|
|
28570
|
-
const [L, A, B] =
|
|
29307
|
+
const [L, A, B] = srgbFloatToOklab(r, g, b);
|
|
28571
29308
|
const [, C, h] = oklabToOklch(L, A, B);
|
|
28572
29309
|
const [nL, nA, nB] = oklchToOklab(
|
|
28573
29310
|
Math.min(1, Math.max(0, L + dL)),
|
|
@@ -30541,19 +31278,19 @@ var isDev2 = typeof import.meta !== "undefined" && import.meta.env?.DEV;
|
|
|
30541
31278
|
function useBooleans(adapter, _options = {}) {
|
|
30542
31279
|
const adapterRef = useRef(adapter);
|
|
30543
31280
|
adapterRef.current = adapter;
|
|
30544
|
-
const
|
|
31281
|
+
const run2 = useCallback((op) => {
|
|
30545
31282
|
const result = applyBooleanOp(adapterRef.current, op);
|
|
30546
31283
|
if (isDev2 && result.kind === "noop" && result.reason === "too-few-for-subtract") {
|
|
30547
31284
|
console.warn("[useBooleans] subtract requires at least 2 selected paths");
|
|
30548
31285
|
}
|
|
30549
31286
|
}, []);
|
|
30550
31287
|
return {
|
|
30551
|
-
union: useCallback(() =>
|
|
30552
|
-
intersect: useCallback(() =>
|
|
30553
|
-
subtract: useCallback(() =>
|
|
30554
|
-
exclude: useCallback(() =>
|
|
30555
|
-
divide: useCallback(() =>
|
|
30556
|
-
crop: useCallback(() =>
|
|
31288
|
+
union: useCallback(() => run2("union"), [run2]),
|
|
31289
|
+
intersect: useCallback(() => run2("intersect"), [run2]),
|
|
31290
|
+
subtract: useCallback(() => run2("subtract"), [run2]),
|
|
31291
|
+
exclude: useCallback(() => run2("exclude"), [run2]),
|
|
31292
|
+
divide: useCallback(() => run2("divide"), [run2]),
|
|
31293
|
+
crop: useCallback(() => run2("crop"), [run2])
|
|
30557
31294
|
};
|
|
30558
31295
|
}
|
|
30559
31296
|
function useBooleansAdapter(adapter) {
|
|
@@ -31244,9 +31981,6 @@ function cellRectAt(bounds, cols, rows, gap, col, row) {
|
|
|
31244
31981
|
height: ch
|
|
31245
31982
|
};
|
|
31246
31983
|
}
|
|
31247
|
-
function sortedChildIds(children) {
|
|
31248
|
-
return children.map((c) => c.id).sort();
|
|
31249
|
-
}
|
|
31250
31984
|
function tileGrid(opts) {
|
|
31251
31985
|
const { cols, rows } = opts;
|
|
31252
31986
|
const gap = opts.gap ?? 0;
|
|
@@ -31297,12 +32031,18 @@ function tileGrid(opts) {
|
|
|
31297
32031
|
snap: snap2,
|
|
31298
32032
|
childPoses(container, children) {
|
|
31299
32033
|
const out = /* @__PURE__ */ new Map();
|
|
31300
|
-
const
|
|
31301
|
-
|
|
31302
|
-
|
|
32034
|
+
const ordered = children.map((c) => ({ c, cell: cellIndexOf(container.bounds, c.pose) })).sort((a, b) => {
|
|
32035
|
+
if (a.cell !== b.cell) {
|
|
32036
|
+
if (a.cell === null) return 1;
|
|
32037
|
+
if (b.cell === null) return -1;
|
|
32038
|
+
return a.cell - b.cell;
|
|
32039
|
+
}
|
|
32040
|
+
return a.c.id < b.c.id ? -1 : a.c.id > b.c.id ? 1 : 0;
|
|
32041
|
+
});
|
|
32042
|
+
for (let i = 0; i < ordered.length && i < capacity; i++) {
|
|
31303
32043
|
const col = i % cols;
|
|
31304
32044
|
const row = Math.floor(i / cols);
|
|
31305
|
-
out.set(
|
|
32045
|
+
out.set(ordered[i].c.id, cellPose(container.bounds, col, row, ordered[i].c.pose));
|
|
31306
32046
|
}
|
|
31307
32047
|
return out;
|
|
31308
32048
|
},
|
|
@@ -31438,6 +32178,6 @@ function snapPoint(opts) {
|
|
|
31438
32178
|
};
|
|
31439
32179
|
}
|
|
31440
32180
|
|
|
31441
|
-
export { ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ArrowIcon, BUILTIN_MARKERS, BUNDLE_TOOLS, CURVE_REPS, CanvasView, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_SHAPE_FILL, DEFAULT_STROKE_COLOR, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EFFECT_KIT_UNIFORMS, EFFECT_VERT_SRC, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, ImageIcon, IntersectIcon, KIT_SHAPE_KINDS, KIT_STANDARD_ACTION_IDS, LassoIcon, LineIcon, MESH_BAKE_SIZE, MESH_GRADIENT_KIND, MIXED, MOVE_ANCHORS, MinimapCanvas, OUTLINE_MIN_SCREEN_PX, PAINTED_CURSOR_LAYER_ID, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_POSE_COMPOSITION, RIGID_POSE_COMPOSITION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SPRITE_STRIDE, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TOOL_PREF_KINDS, TextIcon, UNION_OF_CHILDREN, UnionIcon, UnknownIcon, VERSION, VIEW_ANIMATION_KEY, WHEEL_ZOOM_STEP, WeaselProvider, WeaselRenderer, ZOOM_FLOOR, _resetMarkersForTests, _resetPaintKindsForTests, actionIs, actionShortcuts, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, asPaint, bakeMesh, bezierCubic, bezierQuadratic, bindRig, blendPoses, blur, buildChromeCtx, buildGradientRamp, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, circlePath, clampView, clearSelectionAction, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeRigidPose, composeSelectionPose, composeWorldPose, computeFitView, computeFitViewport, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, cornerWeights, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createKeyState, createMoveToIndexOp, createNodeProperties, createNodeRouting, createPaintedCursorLayer, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createPoseFeed, createPoseOverrides, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createSlew, createTextLayer, createTiledLayer, createVelocityTracker, createViewResolver, createViewportLayer, cubicBezierEasing, cullDrawCommands, cycleVertexColors, decomposePath, decomposeRectPose, decomposeRigidPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, derivedDepOf, derivedPose, documentPose, domPositionToCharOffset, domToRuns, drawLayers, drawOneLayer, 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, effectivePose, effectiveRangeStyle, enterTextEditAction, enumerateAnchors, evalPatch, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, flattenPreviews, focused, forEachCoalesced, frameRect, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getMarker, getNodeShapes, getPaintKind, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hostAnchorCss, hostAnchorRect, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, insertPreviewExtent, interpolateView, isBuiltinToolPref, isGradientFill, isLayerPainted, isLayerVisible, isMeshGradientFill, isPatternSpec, isTensorPatch, isValidPatch, keySpecShortcut, lassoSelectAction, linear, listMarkers, listPaintKinds, makePinchZoomAction, makeViewportWheelPanAction, makeViewportZoomAction, markerDrawCommands, markerInset, markerKeyOf, markerSites, mat3, matchAlignment, meanScale, mergeAlphaFromPrev, meshBounds, meshGradientXml, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, normalizeView, normalizeZoom, not, nurbs, openFilePicker, or, paintAlpha, paintKindOf, paintWithAlpha, paintWithColor, parseColor, parseColorToRgba255, parseDebugFlags, patchBounds, patchCorner, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInTextPose, poseRotationOf, prefUnit, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectMarkerPath, registerContentHandler, registerEffect, registerMarker, registerNodeShape, registerPaintKind, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveEasing, resolveFillPattern, resolveMarkerSize, resolveOverlays, resolvePatternSpec, resolvePreviews, resolveSkeleton, resolveStrokeWidth, resolveVisibility, rgbaToHex, rigidRigApply, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotationDegreesUnit, rotationHandle, roundRectPath, roundToCell, runsToDom, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneSelectionStore, sceneToAdapter, screenToWorld, seedMeshPatch, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, slewToward, sliceAction, snapPoint, solid, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, squarePath, staggerVertexColors, startThresholdDrag, strokeInsets, strokeOf, strokeWith, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textCommandFromPose, textCommandFromRuns, tileGrid, tiledProject, toHex8, translateRectPose, trimPolyline, tweenPose, tweenVertexColors, unionBoundsPath, unionOfChildren, unionOfChildrenVia, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHostAnchor, useHoverTracking, useImageTool, useKeyState, useKeybindings, useLassoTool, useLineTool, useNodeOverlayFrame, usePenTool, usePencilTool, usePinchGesture, usePointerStylus, usePolygonTool, usePoseCompositionDepSource, usePoseDescriptorDepSource, usePoseRun, usePublishSelection, useRectTool, useResizePolicy, useRig, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewAnimationOn, useVisibleRaf, viewToMat3, viewToTransform, viewZoom, viewportDragPanAction, viewportWheelPanAction, viewportZoomAction, viewportsAt, vignette, wheelPan, wheelZoom, wheelZoomFactor, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
|
|
31442
|
-
//# sourceMappingURL=chunk-
|
|
31443
|
-
//# sourceMappingURL=chunk-
|
|
32181
|
+
export { ACTION_GLYPHS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, AlignBottomIcon, AlignCenterXIcon, AlignCenterYIcon, AlignLeftIcon, AlignRightIcon, AlignTopIcon, ArrowIcon, BUILTIN_MARKERS, BUNDLE_TOOLS, BringForwardIcon, BringToFrontIcon, CURVE_REPS, CanvasView, ColorOverrideRegistry, CopyIcon, CropIcon, CursorCoordsHud, CutIcon, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_SHAPE_FILL, DEFAULT_STROKE_COLOR, DeleteIcon, DepRegistryProviderIfRoot, DeviceProfileProvider, DistributeHorizontalIcon, DistributeVerticalIcon, DivideIcon, DuplicateIcon, EASINGS, EFFECT_KIT_UNIFORMS, EFFECT_VERT_SRC, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, FlipXIcon, FlipYIcon, GHOST_STROKE, GroupIcon, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, ImageIcon, IntersectIcon, KIT_SHAPE_KINDS, KIT_STANDARD_ACTION_IDS, LassoIcon, LineIcon, MESH_BAKE_SIZE, MESH_GRADIENT_KIND, MIXED, MOVE_ANCHORS, MinimapCanvas, OUTLINE_MIN_SCREEN_PX, PAINTED_CURSOR_LAYER_ID, PATH_ANCHOR_CHROME_ID, PasteIcon, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_POSE_COMPOSITION, RIGID_POSE_COMPOSITION, ROTATION_HANDLE_BASE_PX, RectIcon, RedoIcon, SPRING_PRESETS, SPRITE_STRIDE, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, SendBackwardIcon, SendToBackIcon, ShaderCompileError, StarIcon, SubtractIcon, TOOL_PREF_KINDS, TextIcon, UNION_OF_CHILDREN, UndoIcon, UngroupIcon, UnionIcon, UnknownIcon, VERSION, VIEW_ANIMATION_KEY, WHEEL_ZOOM_STEP, WeaselProvider, WeaselRenderer, ZOOM_FLOOR, _resetMarkersForTests, _resetPaintKindsForTests, actionIs, actionShortcuts, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, asPaint, bakeMesh, bezierCubic, bezierQuadratic, bindRig, blendPoses, blur, buildChromeCtx, buildGradientRamp, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, circlePath, clampView, clearSelectionAction, clipboardCopyAction, clipboardCutAction, clipboardPasteAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeRigidPose, composeSelectionPose, composeWorldPose, computeFitView, computeFitViewport, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, cornerWeights, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createKeyState, createMoveToIndexOp, createNodeProperties, createNodeRouting, createPaintedCursorLayer, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createPoseFeed, createPoseOverrides, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createSlew, createTextLayer, createTiledLayer, createVelocityTracker, createViewResolver, createViewportLayer, cubicBezierEasing, cullDrawCommands, cycleFill, cycleVertexColors, decomposePath, decomposeRectPose, decomposeRigidPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, definesFrame, deriveAlignmentGuides, deriveParallaxView, derivedDepOf, derivedPose, documentPose, domPositionToCharOffset, domToRuns, drawLayers, drawOneLayer, 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, effectivePose, effectiveRangeStyle, enterTextEditAction, enumerateAnchors, evalPatch, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, flattenPreviews, focused, forEachCoalesced, frameRect, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getMarker, getNodeShapes, getPaintKind, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hostAnchorCss, hostAnchorRect, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, insertPreviewExtent, interpolateView, isBuiltinToolPref, isGradientFill, isLayerPainted, isLayerVisible, isMeshGradientFill, isPatternSpec, isTensorPatch, isValidPatch, isVectorImageSrc, keySpecShortcut, lassoSelectAction, linear, listMarkers, listPaintKinds, makePinchZoomAction, makeViewportWheelPanAction, makeViewportZoomAction, markerDrawCommands, markerInset, markerKeyOf, markerSites, mat3, matchAlignment, meanScale, mergeAlphaFromPrev, meshBounds, meshGradientXml, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, normalizeView, normalizeZoom, not, nurbs, openFilePicker, or, paintAlpha, paintKindOf, paintWithAlpha, paintWithColor, parseColor, parseColorToRgba255, parseDebugFlags, patchBounds, patchCorner, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInTextPose, poseRotationOf, prefUnit, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectMarkerPath, registerContentHandler, registerEffect, registerMarker, registerNodeShape, registerPaintKind, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveEasing, resolveFillPattern, resolveMarkerSize, resolveOverlays, resolvePatternSpec, resolvePreviews, resolveSkeleton, resolveStrokeWidth, resolveVisibility, rgbaToHex, rigidRigApply, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotationDegreesUnit, rotationHandle, roundRectPath, roundToCell, runsToDom, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneSelectionStore, sceneToAdapter, screenToWorld, seedMeshPatch, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, slewToward, sliceAction, snapPoint, solid, solidVertexColors, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, squarePath, staggerVertexColors, startThresholdDrag, strokeInsets, strokeOf, strokeWith, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textCommandFromPose, textCommandFromRuns, tileGrid, tiledProject, toHex8, translateRectPose, trimPolyline, tweenPose, tweenVertexColors, unionBoundsPath, unionOfChildren, unionOfChildrenVia, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHostAnchor, useHoverTracking, useImageTool, useKeyState, useKeybindings, useLassoTool, useLineTool, useNodeOverlayFrame, usePenTool, usePencilTool, usePinchGesture, usePointerStylus, usePolygonTool, usePoseCompositionDepSource, usePoseDescriptorDepSource, usePoseRun, usePublishSelection, useRectTool, useResizePolicy, useRig, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewAnimationOn, useVisibleRaf, viewToMat3, viewToTransform, viewZoom, viewportDragPanAction, viewportWheelPanAction, viewportZoomAction, viewportsAt, vignette, wheelPan, wheelZoom, wheelZoomFactor, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
|
|
32182
|
+
//# sourceMappingURL=chunk-THCSDHII.js.map
|
|
32183
|
+
//# sourceMappingURL=chunk-THCSDHII.js.map
|