@weasel-js/core 1.5.2 → 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 +135 -0
- package/dist/{autoPoseDescriptor-Dr6CwNZK.d.ts → autoPoseDescriptor-_XOyF7t4.d.ts} +2 -1
- package/dist/{chunk-QEZL53V6.js → chunk-THCSDHII.js} +1760 -1142
- 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-FKQBEfg7.d.ts → depSchema-BhgZc0-Q.d.ts} +26 -4
- package/dist/{grid-CuxXjaQV.d.ts → grid-Dx2gQbNv.d.ts} +1 -1
- package/dist/index.d.ts +237 -62
- package/dist/index.js +1 -1
- package/dist/insert.d.ts +4 -3
- package/dist/{math-CeGmGPy8.d.ts → math-DRa6HBBs.d.ts} +3 -2
- package/dist/math.d.ts +4 -3
- package/dist/move.d.ts +5 -4
- 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 +6 -5
- package/dist/renderer.js +1 -1
- package/dist/resize.d.ts +6 -5
- 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-QEZL53V6.js.map +0 -1
|
@@ -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, srgbFloatToOklab, oklabToOklch, oklchToOklab, oklabToSrgbU8 } from '@weasel-js/paint';
|
|
14
|
-
export { STROKE_DASH_RATIOS, alignedStrokeRect, dashForStrokeStyle, hexToOklchDeg, lerpColorArray, lerpOklab, lerpOklch, oklabToOklch, oklabToSrgbU8, oklchDegToHex, oklchToOklab, resolveScreenLength, srgbFloatToOklab, 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
|
}
|
|
@@ -5017,31 +5029,217 @@ var EffectTargets = class {
|
|
|
5017
5029
|
}
|
|
5018
5030
|
};
|
|
5019
5031
|
|
|
5020
|
-
// src/renderer/
|
|
5021
|
-
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;
|
|
5022
5046
|
function extractUniformNames(glsl) {
|
|
5023
|
-
const
|
|
5024
|
-
const
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
const
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
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)];
|
|
5045
5243
|
}
|
|
5046
5244
|
var WeaselRenderer = class {
|
|
5047
5245
|
gl;
|
|
@@ -5178,11 +5376,7 @@ var WeaselRenderer = class {
|
|
|
5178
5376
|
const vertSrc = src.vert === "" ? CUSTOM_VERT_SRC : src.vert;
|
|
5179
5377
|
const fragSrc = src.frag;
|
|
5180
5378
|
const program = new ShaderProgram(this.gl, vertSrc, fragSrc);
|
|
5181
|
-
program.lookupUniforms(
|
|
5182
|
-
...CUSTOM_KIT_UNIFORMS,
|
|
5183
|
-
...extractUniformNames(vertSrc),
|
|
5184
|
-
...extractUniformNames(fragSrc)
|
|
5185
|
-
]);
|
|
5379
|
+
program.lookupUniforms(customUniformNames(vertSrc, fragSrc));
|
|
5186
5380
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
5187
5381
|
const previous = this.programRegistry.get(handle.id);
|
|
5188
5382
|
if (previous) this.gl.deleteProgram(previous.handle);
|
|
@@ -5277,6 +5471,7 @@ var WeaselRenderer = class {
|
|
|
5277
5471
|
if (aPos === void 0) throw new Error("a_position missing after restore");
|
|
5278
5472
|
this.meshCache = new GLMeshCache(this.gl, aPos);
|
|
5279
5473
|
this.textureCache = new GLTextureCache(this.gl);
|
|
5474
|
+
this.imageCache.dispose();
|
|
5280
5475
|
this.imageCache = new GLImageCache(this.gl, this.imageMinification);
|
|
5281
5476
|
this.gradRamps = new GradientRampAtlas(this.gl);
|
|
5282
5477
|
markAllFontsNotUploaded();
|
|
@@ -5289,7 +5484,7 @@ var WeaselRenderer = class {
|
|
|
5289
5484
|
const vertSrc = src.vert === "" ? CUSTOM_VERT_SRC : src.vert;
|
|
5290
5485
|
try {
|
|
5291
5486
|
const program = new ShaderProgram(this.gl, vertSrc, src.frag);
|
|
5292
|
-
program.lookupUniforms(
|
|
5487
|
+
program.lookupUniforms(customUniformNames(vertSrc, src.frag));
|
|
5293
5488
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
5294
5489
|
this.programRegistry.set(id, program);
|
|
5295
5490
|
} catch (e) {
|
|
@@ -5339,6 +5534,7 @@ var WeaselRenderer = class {
|
|
|
5339
5534
|
}
|
|
5340
5535
|
this.programRegistry.clear();
|
|
5341
5536
|
this.textureCache.free();
|
|
5537
|
+
this.imageCache.dispose();
|
|
5342
5538
|
this.gradRamps.free();
|
|
5343
5539
|
if (this.quadVbo) gl.deleteBuffer(this.quadVbo);
|
|
5344
5540
|
if (this.quadIbo) gl.deleteBuffer(this.quadIbo);
|
|
@@ -6664,7 +6860,7 @@ function composeWorldPose(adapter, id, compose2) {
|
|
|
6664
6860
|
while (cursor !== null) {
|
|
6665
6861
|
if (seen.has(cursor)) break;
|
|
6666
6862
|
seen.add(cursor);
|
|
6667
|
-
chain.push(cursor);
|
|
6863
|
+
if (adapter.definesFrame?.(cursor) ?? true) chain.push(cursor);
|
|
6668
6864
|
cursor = adapter.getParent(cursor);
|
|
6669
6865
|
}
|
|
6670
6866
|
let world = adapter.getPose(chain[chain.length - 1]);
|
|
@@ -6684,9 +6880,19 @@ function translateRectPose(pose, dx, dy) {
|
|
|
6684
6880
|
return { ...pose, x: pose.x + dx, y: pose.y + dy };
|
|
6685
6881
|
}
|
|
6686
6882
|
function rebaseLocalPose(adapter, worldPose, newParentId, compose2, decompose) {
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
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;
|
|
6690
6896
|
}
|
|
6691
6897
|
function decomposeRectPose(parent, world) {
|
|
6692
6898
|
return {
|
|
@@ -7033,6 +7239,9 @@ function documentPose(source, node) {
|
|
|
7033
7239
|
function effectivePose(source, node) {
|
|
7034
7240
|
return poseIn(source, node, "effective");
|
|
7035
7241
|
}
|
|
7242
|
+
function definesFrame(node) {
|
|
7243
|
+
return !(node.dependsOn === "children" && node.derivePose !== void 0);
|
|
7244
|
+
}
|
|
7036
7245
|
function poseRotationOf(pose) {
|
|
7037
7246
|
const p = pose;
|
|
7038
7247
|
if (p.rotation && p.x != null && p.y != null && p.width != null && p.height != null) {
|
|
@@ -7243,14 +7452,14 @@ function defaultRectEl(target) {
|
|
|
7243
7452
|
}
|
|
7244
7453
|
function useHandleDrag(opts) {
|
|
7245
7454
|
const { onMove, onStart, onEnd, onCancel, getRect } = opts;
|
|
7246
|
-
const
|
|
7455
|
+
const live2 = useRef(null);
|
|
7247
7456
|
useEffect(() => () => {
|
|
7248
|
-
|
|
7457
|
+
live2.current?.cancel();
|
|
7249
7458
|
}, []);
|
|
7250
7459
|
const onPointerDown = useCallback(
|
|
7251
7460
|
(e) => {
|
|
7252
7461
|
e.preventDefault();
|
|
7253
|
-
|
|
7462
|
+
live2.current?.cancel();
|
|
7254
7463
|
const target = e.currentTarget;
|
|
7255
7464
|
const rectEl = getRect ? getRect(target) : defaultRectEl(target);
|
|
7256
7465
|
const rect = rectEl.getBoundingClientRect();
|
|
@@ -7259,17 +7468,17 @@ function useHandleDrag(opts) {
|
|
|
7259
7468
|
return { x, y };
|
|
7260
7469
|
};
|
|
7261
7470
|
let moved = false;
|
|
7262
|
-
|
|
7471
|
+
live2.current = openPointerSession(target, e, {
|
|
7263
7472
|
onMove: (ev) => {
|
|
7264
7473
|
moved = true;
|
|
7265
7474
|
onMove(localOf(ev.clientX, ev.clientY), ev);
|
|
7266
7475
|
},
|
|
7267
7476
|
onEnd: (ev) => {
|
|
7268
|
-
|
|
7477
|
+
live2.current = null;
|
|
7269
7478
|
onEnd?.({ point: localOf(ev.clientX, ev.clientY), moved, event: ev });
|
|
7270
7479
|
},
|
|
7271
7480
|
onCancel: (reason) => {
|
|
7272
|
-
|
|
7481
|
+
live2.current = null;
|
|
7273
7482
|
onCancel?.(reason);
|
|
7274
7483
|
}
|
|
7275
7484
|
});
|
|
@@ -7375,9 +7584,9 @@ var DRAG_THRESHOLD_PX_SQ = 25;
|
|
|
7375
7584
|
function useDragHandle(getPayload, options) {
|
|
7376
7585
|
const optsRef = useRef(options);
|
|
7377
7586
|
optsRef.current = options;
|
|
7378
|
-
const
|
|
7587
|
+
const live2 = useRef(null);
|
|
7379
7588
|
useEffect(() => () => {
|
|
7380
|
-
|
|
7589
|
+
live2.current?.cancel();
|
|
7381
7590
|
}, []);
|
|
7382
7591
|
const onPointerDown = useCallback((e) => {
|
|
7383
7592
|
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
@@ -7387,7 +7596,7 @@ function useDragHandle(getPayload, options) {
|
|
|
7387
7596
|
const startY = e.clientY;
|
|
7388
7597
|
let ghost = null;
|
|
7389
7598
|
let past = false;
|
|
7390
|
-
|
|
7599
|
+
live2.current = openPointerSession(target, e, {
|
|
7391
7600
|
onMove: (ev) => {
|
|
7392
7601
|
if (ghost) {
|
|
7393
7602
|
ghost.move(ev);
|
|
@@ -7417,11 +7626,11 @@ function useDragHandle(getPayload, options) {
|
|
|
7417
7626
|
);
|
|
7418
7627
|
},
|
|
7419
7628
|
onEnd: (ev) => {
|
|
7420
|
-
|
|
7629
|
+
live2.current = null;
|
|
7421
7630
|
ghost?.drop(ev);
|
|
7422
7631
|
},
|
|
7423
7632
|
onCancel: () => {
|
|
7424
|
-
|
|
7633
|
+
live2.current = null;
|
|
7425
7634
|
ghost?.cancel();
|
|
7426
7635
|
}
|
|
7427
7636
|
}, { capture: false });
|
|
@@ -7784,10 +7993,11 @@ function keySpecShortcut(spec) {
|
|
|
7784
7993
|
shift: mods?.shift === true
|
|
7785
7994
|
};
|
|
7786
7995
|
}
|
|
7787
|
-
function actionShortcuts(action) {
|
|
7996
|
+
function actionShortcuts(action, params) {
|
|
7788
7997
|
const out = [];
|
|
7789
7998
|
const seen = /* @__PURE__ */ new Set();
|
|
7790
|
-
for (const { spec } of actionBindings(action)) {
|
|
7999
|
+
for (const { spec, opts } of actionBindings(action)) {
|
|
8000
|
+
if (params && !paramsMatch(opts?.params, params)) continue;
|
|
7791
8001
|
const shortcut = keySpecShortcut(spec);
|
|
7792
8002
|
if (!shortcut) continue;
|
|
7793
8003
|
const token = `${shortcut.key}|${shortcut.mod}|${shortcut.alt}|${shortcut.shift}`;
|
|
@@ -7797,6 +8007,10 @@ function actionShortcuts(action) {
|
|
|
7797
8007
|
}
|
|
7798
8008
|
return out;
|
|
7799
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
|
+
}
|
|
7800
8014
|
|
|
7801
8015
|
// src/core/scene/types.ts
|
|
7802
8016
|
var asNodeId = (s) => s;
|
|
@@ -7996,7 +8210,11 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
7996
8210
|
y: moveCtx.drag.current.y - selectionCenter.y
|
|
7997
8211
|
};
|
|
7998
8212
|
const candidates = [];
|
|
7999
|
-
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;
|
|
8000
8218
|
if (layout2.contains) return layout2.contains(cPose, selectionCenter);
|
|
8001
8219
|
return selectionCenter.x >= aabb.x && selectionCenter.x < aabb.x + aabb.width && selectionCenter.y >= aabb.y && selectionCenter.y < aabb.y + aabb.height;
|
|
8002
8220
|
};
|
|
@@ -8008,7 +8226,7 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8008
8226
|
if (!node) return;
|
|
8009
8227
|
const worldPose = composeWorldPose(poseAdapter, id, pc.compose);
|
|
8010
8228
|
const worldAABB = poseDescriptorForNode(d, node).getBounds(worldPose);
|
|
8011
|
-
if (!testInside(worldPose, worldAABB, layout2)) return;
|
|
8229
|
+
if (!testInside(id, worldPose, worldAABB, layout2)) return;
|
|
8012
8230
|
if (layout2.acceptsDrop) {
|
|
8013
8231
|
const arg = { id, bounds: worldAABB };
|
|
8014
8232
|
for (const c of dragged) if (!layout2.acceptsDrop(arg, c.arg)) return;
|
|
@@ -8031,28 +8249,33 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8031
8249
|
};
|
|
8032
8250
|
walk(null, []);
|
|
8033
8251
|
let dest = null;
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
}
|
|
8043
|
-
if (c.depth < dest.depth) continue;
|
|
8044
|
-
let cAfter = false;
|
|
8045
|
-
for (let i = 0; i < c.zPath.length; i++) {
|
|
8046
|
-
if (c.zPath[i] > dest.zPath[i]) {
|
|
8047
|
-
cAfter = true;
|
|
8048
|
-
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;
|
|
8049
8260
|
}
|
|
8050
|
-
if (c.
|
|
8051
|
-
|
|
8052
|
-
|
|
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
|
+
}
|
|
8053
8276
|
}
|
|
8277
|
+
if (cAfter) dest = c;
|
|
8054
8278
|
}
|
|
8055
|
-
if (cAfter) dest = c;
|
|
8056
8279
|
}
|
|
8057
8280
|
if (!dest) return;
|
|
8058
8281
|
const layout = dest.layout;
|
|
@@ -8119,6 +8342,10 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8119
8342
|
}
|
|
8120
8343
|
scratch.layoutPass = { layout, container, placements, sourceReflow, reflowIds };
|
|
8121
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
|
+
}
|
|
8122
8349
|
function groupBySourceContainer(dragged, exclude) {
|
|
8123
8350
|
const out = /* @__PURE__ */ new Map();
|
|
8124
8351
|
for (const d of dragged) {
|
|
@@ -8134,17 +8361,18 @@ function groupBySourceContainer(dragged, exclude) {
|
|
|
8134
8361
|
return out;
|
|
8135
8362
|
}
|
|
8136
8363
|
function translateCommitOps(scratch, ids, dx, dy) {
|
|
8137
|
-
const m = [1, 0, 0, 1, dx, dy];
|
|
8138
8364
|
const ops = [];
|
|
8139
8365
|
for (const id of ids) {
|
|
8140
|
-
if (!scratch.startPoses.has(id)) continue;
|
|
8366
|
+
if (!scratch.startPoses.has(id) || scratch.carried.has(id)) continue;
|
|
8141
8367
|
const node = scratch.scene.get(id);
|
|
8142
8368
|
if (!node) continue;
|
|
8369
|
+
const l = localDelta(scratch, id, dx, dy);
|
|
8370
|
+
const m = [1, 0, 0, 1, l.dx, l.dy];
|
|
8143
8371
|
const authored = node.pose;
|
|
8144
8372
|
ops.push(createTransformOp({
|
|
8145
8373
|
id,
|
|
8146
8374
|
from: authored,
|
|
8147
|
-
to: translatePoseViaDescriptor(authored, dx, dy, scratch.projection),
|
|
8375
|
+
to: translatePoseViaDescriptor(authored, l.dx, l.dy, scratch.projection),
|
|
8148
8376
|
label: "Move"
|
|
8149
8377
|
}));
|
|
8150
8378
|
if (scratch.geometryProjection) {
|
|
@@ -8159,6 +8387,16 @@ function translateCommitOps(scratch, ids, dx, dy) {
|
|
|
8159
8387
|
}
|
|
8160
8388
|
return ops;
|
|
8161
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
|
+
}
|
|
8162
8400
|
function unreleasedIds(scratch, released) {
|
|
8163
8401
|
const selected = new Set(scratch.ids);
|
|
8164
8402
|
const out = scratch.ids.filter((id) => !released.has(id));
|
|
@@ -8180,7 +8418,11 @@ function unreleasedIds(scratch, released) {
|
|
|
8180
8418
|
function scenePoseAdapter(scene) {
|
|
8181
8419
|
return {
|
|
8182
8420
|
getPose: (id) => documentPose(scene, scene.get(id)),
|
|
8183
|
-
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
|
+
}
|
|
8184
8426
|
};
|
|
8185
8427
|
}
|
|
8186
8428
|
function resolveDropTarget(endCtx, scratch) {
|
|
@@ -8285,6 +8527,23 @@ var moveAction = {
|
|
|
8285
8527
|
queue.push(childId);
|
|
8286
8528
|
}
|
|
8287
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
|
+
}
|
|
8288
8547
|
const behaviors = opts?.behaviors ?? [];
|
|
8289
8548
|
const adapter = moveGestureAdapter(scene);
|
|
8290
8549
|
const origin = /* @__PURE__ */ new Map();
|
|
@@ -8304,6 +8563,8 @@ var moveAction = {
|
|
|
8304
8563
|
startPoses,
|
|
8305
8564
|
ids,
|
|
8306
8565
|
cascadeIds,
|
|
8566
|
+
carried,
|
|
8567
|
+
frames,
|
|
8307
8568
|
scene,
|
|
8308
8569
|
currentDelta: { dx: 0, dy: 0 },
|
|
8309
8570
|
previews: /* @__PURE__ */ new Map(),
|
|
@@ -8356,7 +8617,12 @@ var moveAction = {
|
|
|
8356
8617
|
scratch.currentDelta = { dx, dy };
|
|
8357
8618
|
scratch.previews.clear();
|
|
8358
8619
|
for (const [id, ori] of scratch.startPoses) {
|
|
8359
|
-
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));
|
|
8360
8626
|
}
|
|
8361
8627
|
scratch.layoutPass = null;
|
|
8362
8628
|
runLayoutPass(scratch, moveCtx);
|
|
@@ -8555,15 +8821,18 @@ var moveAction = {
|
|
|
8555
8821
|
};
|
|
8556
8822
|
|
|
8557
8823
|
// src/interactions/actions/defaultCommitAdapter.ts
|
|
8558
|
-
function defaultCommitAdapter(scene) {
|
|
8824
|
+
function defaultCommitAdapter(scene, selection) {
|
|
8559
8825
|
return {
|
|
8560
8826
|
getNode: (id) => scene.get(asNodeId(id)),
|
|
8561
8827
|
getNodes: () => {
|
|
8562
8828
|
return [...scene.renderOrderNodes()];
|
|
8563
8829
|
},
|
|
8564
8830
|
getPose: (id) => documentPose(scene, scene.get(asNodeId(id))),
|
|
8565
|
-
getSelection: () => [...scene.getSelection()],
|
|
8566
|
-
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
|
+
},
|
|
8567
8836
|
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
|
|
8568
8837
|
getRemovalClosure: (ids) => [...scene.removalClosure(ids.map(asNodeId))],
|
|
8569
8838
|
getChildren: (parentId) => parentId === null ? [...scene.roots] : [...scene.childrenOf(asNodeId(parentId))],
|
|
@@ -8607,8 +8876,8 @@ function poseFrame(adapter, pc) {
|
|
|
8607
8876
|
pc,
|
|
8608
8877
|
world: (id) => composeWorldPose(adapter, id, pc.compose),
|
|
8609
8878
|
parentWorld: (id) => {
|
|
8610
|
-
const
|
|
8611
|
-
return
|
|
8879
|
+
const frame = frameAtOrAbove(adapter, adapter.getParent(id));
|
|
8880
|
+
return frame === null ? null : composeWorldPose(adapter, frame, pc.compose);
|
|
8612
8881
|
},
|
|
8613
8882
|
local: (id, worldPose) => rebaseLocalPose(adapter, worldPose, adapter.getParent(id), pc.compose, pc.decompose),
|
|
8614
8883
|
localUnder: (parentId, worldPose) => rebaseLocalPose(adapter, worldPose, parentId, pc.compose, pc.decompose)
|
|
@@ -8622,7 +8891,11 @@ function scenePoseFrame(scene, poseComposition) {
|
|
|
8622
8891
|
const node = scene.get(id);
|
|
8623
8892
|
return node === void 0 ? { x: 0, y: 0, width: 0, height: 0 } : effectivePose(source, node);
|
|
8624
8893
|
},
|
|
8625
|
-
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
|
+
}
|
|
8626
8899
|
};
|
|
8627
8900
|
return poseFrame(adapter, pc);
|
|
8628
8901
|
}
|
|
@@ -8979,7 +9252,7 @@ var resizeAction = {
|
|
|
8979
9252
|
}
|
|
8980
9253
|
if (ops.length > 0) {
|
|
8981
9254
|
if (scratch.applyOps) scratch.applyOps(ops, "Resize");
|
|
8982
|
-
else scratch.scene.applyBatch(ops, "Resize", defaultCommitAdapter(scratch.scene));
|
|
9255
|
+
else scratch.scene.applyBatch(ops, "Resize", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
8983
9256
|
}
|
|
8984
9257
|
dropPreviewOverrides(scratch);
|
|
8985
9258
|
scratch.previews.clear();
|
|
@@ -9135,7 +9408,7 @@ var rotateAction = {
|
|
|
9135
9408
|
}
|
|
9136
9409
|
if (ops.length > 0) {
|
|
9137
9410
|
if (scratch.applyOps) scratch.applyOps(ops, "Rotate");
|
|
9138
|
-
else scratch.scene.applyBatch(ops, "Rotate", defaultCommitAdapter(scratch.scene));
|
|
9411
|
+
else scratch.scene.applyBatch(ops, "Rotate", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
9139
9412
|
}
|
|
9140
9413
|
dropPreviewOverrides(scratch);
|
|
9141
9414
|
scratch.previews.clear();
|
|
@@ -9602,7 +9875,7 @@ var cloneAction = {
|
|
|
9602
9875
|
}
|
|
9603
9876
|
if (ops.length > 0) {
|
|
9604
9877
|
if (scratch.applyOps) scratch.applyOps(ops, "Clone");
|
|
9605
|
-
else scratch.scene.applyBatch(ops, "Clone", defaultCommitAdapter(scratch.scene));
|
|
9878
|
+
else scratch.scene.applyBatch(ops, "Clone", defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
9606
9879
|
}
|
|
9607
9880
|
scratch.previews.clear();
|
|
9608
9881
|
},
|
|
@@ -9782,6 +10055,11 @@ function makeViewportZoomAction(opts = {}) {
|
|
|
9782
10055
|
spec: { kind: "wheel", mods: { ctrl: true } },
|
|
9783
10056
|
opts: { params: { kind: "wheel" } }
|
|
9784
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
|
+
},
|
|
9785
10063
|
// Cmd+= → zoom in (also accepts Cmd+Shift+= which is Cmd++ on many keyboards)
|
|
9786
10064
|
{
|
|
9787
10065
|
spec: { kind: "key", key: "=", mods: { mod: true, shift: "optional" } },
|
|
@@ -9815,14 +10093,21 @@ function makeViewportZoomAction(opts = {}) {
|
|
|
9815
10093
|
view.animate(target, { ...tweenOpts, ms: ms ?? tweenOpts.ms });
|
|
9816
10094
|
};
|
|
9817
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
|
+
};
|
|
9818
10104
|
switch (kind) {
|
|
9819
|
-
case "wheel":
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
10105
|
+
case "wheel":
|
|
10106
|
+
scaleBy(wheelZoomFactor(params?.deltaY ?? 0));
|
|
10107
|
+
break;
|
|
10108
|
+
case "pinch":
|
|
10109
|
+
scaleBy(params?.scale ?? Number.NaN);
|
|
9824
10110
|
break;
|
|
9825
|
-
}
|
|
9826
10111
|
case "in":
|
|
9827
10112
|
stepTo(zoomAt(stepFrom(), keyAnchor(view), KEY_STEP, clamp));
|
|
9828
10113
|
break;
|
|
@@ -10623,6 +10908,150 @@ function makePinchZoomAction(opts = {}) {
|
|
|
10623
10908
|
};
|
|
10624
10909
|
}
|
|
10625
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
|
+
}
|
|
10626
11055
|
|
|
10627
11056
|
// src/interactions/actions/defaults/delete.ts
|
|
10628
11057
|
function hostIndex(scene, id) {
|
|
@@ -10649,6 +11078,8 @@ function buildDeleteOps(scene, ids, label) {
|
|
|
10649
11078
|
var deleteAction = {
|
|
10650
11079
|
id: "delete",
|
|
10651
11080
|
label: "Delete",
|
|
11081
|
+
icon: createElement(DeleteIcon),
|
|
11082
|
+
group: "edit",
|
|
10652
11083
|
// Suppressed while any tool is mid-gesture — accidentally hitting
|
|
10653
11084
|
// Delete during a drag shouldn't wipe the selection out from under
|
|
10654
11085
|
// the in-flight handle.
|
|
@@ -10671,25 +11102,20 @@ var deleteAction = {
|
|
|
10671
11102
|
const ops = buildDeleteOps(scene, ids, "Delete");
|
|
10672
11103
|
if (ops.length > 0) {
|
|
10673
11104
|
if (applyOps) applyOps(ops, "Delete");
|
|
10674
|
-
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene));
|
|
11105
|
+
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10675
11106
|
}
|
|
10676
11107
|
selection.set([]);
|
|
10677
11108
|
}
|
|
10678
11109
|
},
|
|
10679
|
-
enabled:
|
|
11110
|
+
enabled: requiresSelection
|
|
10680
11111
|
};
|
|
10681
|
-
function requiresSelection(deps) {
|
|
10682
|
-
const selection = deps?.selection;
|
|
10683
|
-
if (!selection || selection.get().length === 0) {
|
|
10684
|
-
return ActionDisabledReason.SelectionRequired;
|
|
10685
|
-
}
|
|
10686
|
-
return true;
|
|
10687
|
-
}
|
|
10688
11112
|
|
|
10689
11113
|
// src/interactions/actions/defaults/clipboard.ts
|
|
10690
11114
|
var clipboardCopyAction = {
|
|
10691
11115
|
id: "clipboard.copy",
|
|
10692
11116
|
label: "Copy",
|
|
11117
|
+
icon: createElement(CopyIcon),
|
|
11118
|
+
group: "clipboard",
|
|
10693
11119
|
defaultBinding: { kind: "key", key: "c", mods: { mod: true } },
|
|
10694
11120
|
// Same gate as `duplicate`: a mode that can neither edit the page nor own a
|
|
10695
11121
|
// selection has nothing to copy. Notably excludes `text-edit`, where Cmd+C
|
|
@@ -10709,6 +11135,8 @@ var clipboardCopyAction = {
|
|
|
10709
11135
|
var clipboardCutAction = {
|
|
10710
11136
|
id: "clipboard.cut",
|
|
10711
11137
|
label: "Cut",
|
|
11138
|
+
icon: createElement(CutIcon),
|
|
11139
|
+
group: "clipboard",
|
|
10712
11140
|
defaultBinding: { kind: "key", key: "x", mods: { mod: true } },
|
|
10713
11141
|
eligible: { capability: "edits-page" },
|
|
10714
11142
|
requires: ["clipboard", "scene", "selection", "applyOps"],
|
|
@@ -10726,13 +11154,31 @@ var clipboardCutAction = {
|
|
|
10726
11154
|
const ops = buildDeleteOps(scene, ids, "Cut");
|
|
10727
11155
|
if (ops.length > 0) {
|
|
10728
11156
|
if (applyOps) applyOps(ops, "Cut");
|
|
10729
|
-
else scene.applyBatch(ops, "Cut", defaultCommitAdapter(scene));
|
|
11157
|
+
else scene.applyBatch(ops, "Cut", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10730
11158
|
}
|
|
10731
11159
|
selection.set([]);
|
|
10732
11160
|
}
|
|
10733
11161
|
},
|
|
10734
11162
|
enabled: requiresSelection
|
|
10735
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
|
+
};
|
|
10736
11182
|
|
|
10737
11183
|
// src/interactions/actions/defaults/enterTextEdit.ts
|
|
10738
11184
|
var enterTextEditAction = {
|
|
@@ -10827,8 +11273,6 @@ var selectAllAction = {
|
|
|
10827
11273
|
}
|
|
10828
11274
|
}
|
|
10829
11275
|
};
|
|
10830
|
-
|
|
10831
|
-
// src/interactions/actions/defaults/duplicate.ts
|
|
10832
11276
|
var DUPLICATE_OFFSET = 12;
|
|
10833
11277
|
function hasSelectedAncestor(scene, id, set) {
|
|
10834
11278
|
let parent = scene.get(id)?.parent ?? null;
|
|
@@ -10858,6 +11302,8 @@ function copySubtree(scene, id, parent, offset, offsetDescendants, ops, d) {
|
|
|
10858
11302
|
var duplicateAction = {
|
|
10859
11303
|
id: "duplicate",
|
|
10860
11304
|
label: "Duplicate",
|
|
11305
|
+
icon: createElement(DuplicateIcon),
|
|
11306
|
+
group: "edit",
|
|
10861
11307
|
defaultBinding: { kind: "key", key: "d", mods: { mod: true } },
|
|
10862
11308
|
eligible: { capability: ["edits-page", "creates-selection"] },
|
|
10863
11309
|
// `selection` is read by the `enabled` gate as well as the invoker. An
|
|
@@ -10888,7 +11334,7 @@ var duplicateAction = {
|
|
|
10888
11334
|
}
|
|
10889
11335
|
if (ops.length === 0) return;
|
|
10890
11336
|
if (applyOps) applyOps(ops, "Duplicate");
|
|
10891
|
-
else scene.applyBatch(ops, "Duplicate", defaultCommitAdapter(scene));
|
|
11337
|
+
else scene.applyBatch(ops, "Duplicate", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10892
11338
|
selection.set(newIds);
|
|
10893
11339
|
}
|
|
10894
11340
|
},
|
|
@@ -10924,8 +11370,6 @@ function withKitRegistry(registry2) {
|
|
|
10924
11370
|
}
|
|
10925
11371
|
};
|
|
10926
11372
|
}
|
|
10927
|
-
|
|
10928
|
-
// src/interactions/actions/defaults/group.ts
|
|
10929
11373
|
var containerIdCounter = 0;
|
|
10930
11374
|
function freshContainerId() {
|
|
10931
11375
|
return asNodeId(`n${(containerIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`);
|
|
@@ -10933,6 +11377,8 @@ function freshContainerId() {
|
|
|
10933
11377
|
var groupAction = {
|
|
10934
11378
|
id: "group",
|
|
10935
11379
|
label: "Group",
|
|
11380
|
+
icon: createElement(GroupIcon),
|
|
11381
|
+
group: "structure",
|
|
10936
11382
|
defaultBinding: { kind: "key", key: "g", mods: { mod: true } },
|
|
10937
11383
|
eligible: { capability: "edits-page" },
|
|
10938
11384
|
requires: ["scene", "selection", "applyOps", "poseDescriptor", "poseComposition"],
|
|
@@ -10992,15 +11438,17 @@ var groupAction = {
|
|
|
10992
11438
|
}));
|
|
10993
11439
|
}
|
|
10994
11440
|
if (applyOps) applyOps(ops, "Group");
|
|
10995
|
-
else scene.applyBatch(ops, "Group", defaultCommitAdapter(scene));
|
|
11441
|
+
else scene.applyBatch(ops, "Group", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
10996
11442
|
selection.set([containerId]);
|
|
10997
11443
|
}
|
|
10998
11444
|
},
|
|
10999
|
-
enabled:
|
|
11445
|
+
enabled: requiresSelection
|
|
11000
11446
|
};
|
|
11001
11447
|
var ungroupAction = {
|
|
11002
11448
|
id: "ungroup",
|
|
11003
11449
|
label: "Ungroup",
|
|
11450
|
+
icon: createElement(UngroupIcon),
|
|
11451
|
+
group: "structure",
|
|
11004
11452
|
defaultBinding: { kind: "key", key: "g", mods: { mod: true, shift: true } },
|
|
11005
11453
|
eligible: { capability: "edits-page" },
|
|
11006
11454
|
requires: ["scene", "selection", "poseComposition"],
|
|
@@ -11041,13 +11489,22 @@ var ungroupAction = {
|
|
|
11041
11489
|
});
|
|
11042
11490
|
}
|
|
11043
11491
|
},
|
|
11044
|
-
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
|
+
}
|
|
11045
11498
|
};
|
|
11046
|
-
|
|
11047
|
-
|
|
11499
|
+
function historyCan(deps, which) {
|
|
11500
|
+
const history = deps?.history;
|
|
11501
|
+
return history?.[which]?.() ? true : ActionDisabledReason.NotApplicable;
|
|
11502
|
+
}
|
|
11048
11503
|
var undoAction = {
|
|
11049
11504
|
id: "undo",
|
|
11050
11505
|
label: "Undo",
|
|
11506
|
+
icon: createElement(UndoIcon),
|
|
11507
|
+
group: "history",
|
|
11051
11508
|
defaultBinding: { kind: "key", key: "z", mods: { mod: true } },
|
|
11052
11509
|
requires: ["history"],
|
|
11053
11510
|
invoker: {
|
|
@@ -11055,11 +11512,14 @@ var undoAction = {
|
|
|
11055
11512
|
run: (deps) => {
|
|
11056
11513
|
deps.history?.undo?.();
|
|
11057
11514
|
}
|
|
11058
|
-
}
|
|
11515
|
+
},
|
|
11516
|
+
enabled: (deps) => historyCan(deps, "canUndo")
|
|
11059
11517
|
};
|
|
11060
11518
|
var redoAction = {
|
|
11061
11519
|
id: "redo",
|
|
11062
11520
|
label: "Redo",
|
|
11521
|
+
icon: createElement(RedoIcon),
|
|
11522
|
+
group: "history",
|
|
11063
11523
|
defaultBinding: { kind: "key", key: "z", mods: { mod: true, shift: true } },
|
|
11064
11524
|
requires: ["history"],
|
|
11065
11525
|
invoker: {
|
|
@@ -11067,7 +11527,8 @@ var redoAction = {
|
|
|
11067
11527
|
run: (deps) => {
|
|
11068
11528
|
deps.history?.redo?.();
|
|
11069
11529
|
}
|
|
11070
|
-
}
|
|
11530
|
+
},
|
|
11531
|
+
enabled: (deps) => historyCan(deps, "canRedo")
|
|
11071
11532
|
};
|
|
11072
11533
|
|
|
11073
11534
|
// src/interactions/actions/flip/helpers.ts
|
|
@@ -11171,8 +11632,8 @@ function localMirror(axis, frame, id, pivotWorld) {
|
|
|
11171
11632
|
rotation: 0
|
|
11172
11633
|
});
|
|
11173
11634
|
const theta = at.rotation ?? 0;
|
|
11174
|
-
const
|
|
11175
|
-
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));
|
|
11176
11637
|
return multiply$1(translate$1(at.x, at.y), multiply$1(turned, translate$1(-at.x, -at.y)));
|
|
11177
11638
|
}
|
|
11178
11639
|
function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection, geom, poseComposition) {
|
|
@@ -11207,11 +11668,16 @@ function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjecti
|
|
|
11207
11668
|
}
|
|
11208
11669
|
if (ops.length === 0) return;
|
|
11209
11670
|
if (applyOps) applyOps(ops, "Flip");
|
|
11210
|
-
else scene.applyBatch(ops, "Flip", defaultCommitAdapter(scene));
|
|
11671
|
+
else scene.applyBatch(ops, "Flip", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11211
11672
|
}
|
|
11212
11673
|
var flipAction = {
|
|
11213
11674
|
id: "flip",
|
|
11214
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
|
+
],
|
|
11215
11681
|
defaultBinding: [
|
|
11216
11682
|
{ spec: { kind: "key", key: ["h", "H"], mods: { shift: true } }, opts: { params: { axis: "x" } } },
|
|
11217
11683
|
{ spec: { kind: "key", key: ["v", "V"], mods: { shift: true } }, opts: { params: { axis: "y" } } }
|
|
@@ -11294,7 +11760,7 @@ function nudgeSelection(selection, scene, dx, dy, applyOps, geometryProjection,
|
|
|
11294
11760
|
}
|
|
11295
11761
|
if (ops.length === 0) return;
|
|
11296
11762
|
if (applyOps) applyOps(ops, "Nudge");
|
|
11297
|
-
else scene.applyBatch(ops, "Nudge", defaultCommitAdapter(scene));
|
|
11763
|
+
else scene.applyBatch(ops, "Nudge", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11298
11764
|
}
|
|
11299
11765
|
function makeNudgeAction(dir2) {
|
|
11300
11766
|
return {
|
|
@@ -11524,20 +11990,22 @@ function createMoveToIndexOp(args) {
|
|
|
11524
11990
|
};
|
|
11525
11991
|
}
|
|
11526
11992
|
registerOpFactory("moveToIndex", (args) => createMoveToIndexOp(args));
|
|
11527
|
-
|
|
11528
|
-
// src/interactions/actions/defaults/reorder.ts
|
|
11529
11993
|
function reorderSelection(selection, scene, direction, distance, applyOps) {
|
|
11530
11994
|
const ids = selection.get();
|
|
11531
11995
|
if (ids.length === 0) return;
|
|
11532
11996
|
const reorderDir = direction === "forward" ? distance === "extreme" ? "front" : "forward" : distance === "extreme" ? "back" : "backward";
|
|
11533
11997
|
const op = createReorderOp({ ids, direction: reorderDir, label: "Reorder" });
|
|
11534
11998
|
if (applyOps) applyOps([op], "Reorder");
|
|
11535
|
-
else scene.applyBatch([op], "Reorder", defaultCommitAdapter(scene));
|
|
11999
|
+
else scene.applyBatch([op], "Reorder", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
11536
12000
|
}
|
|
11537
12001
|
var reorderForwardAction = {
|
|
11538
12002
|
id: "reorder.forward",
|
|
11539
12003
|
label: "Bring Forward",
|
|
11540
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
|
+
],
|
|
11541
12009
|
defaultBinding: [
|
|
11542
12010
|
{
|
|
11543
12011
|
// Modifier matching is strict, so Shift must be absent here — listing
|
|
@@ -11583,6 +12051,10 @@ var reorderBackwardAction = {
|
|
|
11583
12051
|
id: "reorder.backward",
|
|
11584
12052
|
label: "Send Backward",
|
|
11585
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
|
+
],
|
|
11586
12058
|
defaultBinding: [
|
|
11587
12059
|
{
|
|
11588
12060
|
spec: { kind: "key", key: ["["], mods: { mod: true } },
|
|
@@ -11615,7 +12087,7 @@ var reorderBackwardAction = {
|
|
|
11615
12087
|
},
|
|
11616
12088
|
enabled: requiresSelection
|
|
11617
12089
|
};
|
|
11618
|
-
var
|
|
12090
|
+
var SVG_BASE3 = {
|
|
11619
12091
|
viewBox: "0 0 16 16",
|
|
11620
12092
|
width: 16,
|
|
11621
12093
|
height: 16,
|
|
@@ -11628,42 +12100,42 @@ var AXIS = {
|
|
|
11628
12100
|
fill: "none"
|
|
11629
12101
|
};
|
|
11630
12102
|
function AlignLeftIcon() {
|
|
11631
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12103
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11632
12104
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "2", y2: "14", ...AXIS }),
|
|
11633
12105
|
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "3", width: "9", height: "3" }),
|
|
11634
12106
|
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "9", width: "6", height: "3" })
|
|
11635
12107
|
] });
|
|
11636
12108
|
}
|
|
11637
12109
|
function AlignRightIcon() {
|
|
11638
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12110
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11639
12111
|
/* @__PURE__ */ jsx("line", { x1: "14", y1: "2", x2: "14", y2: "14", ...AXIS }),
|
|
11640
12112
|
/* @__PURE__ */ jsx("rect", { x: "4.5", y: "3", width: "9", height: "3" }),
|
|
11641
12113
|
/* @__PURE__ */ jsx("rect", { x: "7.5", y: "9", width: "6", height: "3" })
|
|
11642
12114
|
] });
|
|
11643
12115
|
}
|
|
11644
12116
|
function AlignTopIcon() {
|
|
11645
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12117
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11646
12118
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "14", y2: "2", ...AXIS }),
|
|
11647
12119
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "2.5", width: "3", height: "9" }),
|
|
11648
12120
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "2.5", width: "3", height: "6" })
|
|
11649
12121
|
] });
|
|
11650
12122
|
}
|
|
11651
12123
|
function AlignBottomIcon() {
|
|
11652
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12124
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11653
12125
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "14", x2: "14", y2: "14", ...AXIS }),
|
|
11654
12126
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "4.5", width: "3", height: "9" }),
|
|
11655
12127
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "7.5", width: "3", height: "6" })
|
|
11656
12128
|
] });
|
|
11657
12129
|
}
|
|
11658
12130
|
function AlignCenterXIcon() {
|
|
11659
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12131
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11660
12132
|
/* @__PURE__ */ jsx("line", { x1: "8", y1: "2", x2: "8", y2: "14", ...AXIS }),
|
|
11661
12133
|
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "3", width: "9", height: "3" }),
|
|
11662
12134
|
/* @__PURE__ */ jsx("rect", { x: "5", y: "9", width: "6", height: "3" })
|
|
11663
12135
|
] });
|
|
11664
12136
|
}
|
|
11665
12137
|
function AlignCenterYIcon() {
|
|
11666
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12138
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE3, children: [
|
|
11667
12139
|
/* @__PURE__ */ jsx("line", { x1: "2", y1: "8", x2: "14", y2: "8", ...AXIS }),
|
|
11668
12140
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "3", height: "9" }),
|
|
11669
12141
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "5", width: "3", height: "6" })
|
|
@@ -11777,7 +12249,7 @@ function planDistribute(bounds, axis, mode) {
|
|
|
11777
12249
|
}
|
|
11778
12250
|
return targets;
|
|
11779
12251
|
}
|
|
11780
|
-
var
|
|
12252
|
+
var SVG_BASE4 = {
|
|
11781
12253
|
viewBox: "0 0 16 16",
|
|
11782
12254
|
width: 16,
|
|
11783
12255
|
height: 16,
|
|
@@ -11785,14 +12257,14 @@ var SVG_BASE2 = {
|
|
|
11785
12257
|
"aria-hidden": true
|
|
11786
12258
|
};
|
|
11787
12259
|
function DistributeHorizontalIcon() {
|
|
11788
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12260
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE4, children: [
|
|
11789
12261
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "4", width: "3", height: "8" }),
|
|
11790
12262
|
/* @__PURE__ */ jsx("rect", { x: "6.5", y: "4", width: "3", height: "8" }),
|
|
11791
12263
|
/* @__PURE__ */ jsx("rect", { x: "12", y: "4", width: "3", height: "8" })
|
|
11792
12264
|
] });
|
|
11793
12265
|
}
|
|
11794
12266
|
function DistributeVerticalIcon() {
|
|
11795
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12267
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE4, children: [
|
|
11796
12268
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "1", width: "8", height: "3" }),
|
|
11797
12269
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "6.5", width: "8", height: "3" }),
|
|
11798
12270
|
/* @__PURE__ */ jsx("rect", { x: "4", y: "12", width: "8", height: "3" })
|
|
@@ -11962,7 +12434,7 @@ function applyBooleanOp(adapter, op) {
|
|
|
11962
12434
|
dispatchApplyBatch(adapter, ops, LABEL[op]);
|
|
11963
12435
|
return { kind: "applied", resultIds: newNodes.map((n) => n.id) };
|
|
11964
12436
|
}
|
|
11965
|
-
var
|
|
12437
|
+
var SVG_BASE5 = {
|
|
11966
12438
|
viewBox: "0 0 20 20",
|
|
11967
12439
|
width: 20,
|
|
11968
12440
|
height: 20,
|
|
@@ -11972,7 +12444,7 @@ var SVG_BASE3 = {
|
|
|
11972
12444
|
"aria-hidden": true
|
|
11973
12445
|
};
|
|
11974
12446
|
function UnionIcon() {
|
|
11975
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12447
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11976
12448
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5", fill: "currentColor", stroke: "none" }),
|
|
11977
12449
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5", fill: "currentColor", stroke: "none" }),
|
|
11978
12450
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
@@ -11980,7 +12452,7 @@ function UnionIcon() {
|
|
|
11980
12452
|
] });
|
|
11981
12453
|
}
|
|
11982
12454
|
function IntersectIcon() {
|
|
11983
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12455
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11984
12456
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11985
12457
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
11986
12458
|
/* @__PURE__ */ jsx(
|
|
@@ -11994,7 +12466,7 @@ function IntersectIcon() {
|
|
|
11994
12466
|
] });
|
|
11995
12467
|
}
|
|
11996
12468
|
function SubtractIcon() {
|
|
11997
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12469
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
11998
12470
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
11999
12471
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
12000
12472
|
/* @__PURE__ */ jsx(
|
|
@@ -12008,7 +12480,7 @@ function SubtractIcon() {
|
|
|
12008
12480
|
] });
|
|
12009
12481
|
}
|
|
12010
12482
|
function ExcludeIcon() {
|
|
12011
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12483
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
12012
12484
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
12013
12485
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
12014
12486
|
/* @__PURE__ */ jsx(
|
|
@@ -12023,14 +12495,14 @@ function ExcludeIcon() {
|
|
|
12023
12495
|
] });
|
|
12024
12496
|
}
|
|
12025
12497
|
function DivideIcon() {
|
|
12026
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12498
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
12027
12499
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "10", r: "5" }),
|
|
12028
12500
|
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "10", r: "5" }),
|
|
12029
12501
|
/* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "10", y2: "14" })
|
|
12030
12502
|
] });
|
|
12031
12503
|
}
|
|
12032
12504
|
function CropIcon() {
|
|
12033
|
-
return /* @__PURE__ */ jsxs("svg", { ...
|
|
12505
|
+
return /* @__PURE__ */ jsxs("svg", { ...SVG_BASE5, children: [
|
|
12034
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" }) }) }),
|
|
12035
12507
|
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "10", r: "5" }),
|
|
12036
12508
|
/* @__PURE__ */ jsx(
|
|
@@ -12486,7 +12958,7 @@ function createPaintAction(spec) {
|
|
|
12486
12958
|
}
|
|
12487
12959
|
if (ops.length > 0) {
|
|
12488
12960
|
if (scratch.applyOps) scratch.applyOps(ops, spec.label);
|
|
12489
|
-
else scratch.scene.applyBatch(ops, spec.label, defaultCommitAdapter(scratch.scene));
|
|
12961
|
+
else scratch.scene.applyBatch(ops, spec.label, defaultCommitAdapter(scratch.scene, selection.adapterMethods));
|
|
12490
12962
|
}
|
|
12491
12963
|
scratch.previews.clear();
|
|
12492
12964
|
},
|
|
@@ -12537,14 +13009,23 @@ function mergeAlphaFromPrev(picked, prev) {
|
|
|
12537
13009
|
// src/util/paint.ts
|
|
12538
13010
|
var DEFAULT_FILL_COLOR = "#ffffffff";
|
|
12539
13011
|
var DEFAULT_STROKE_COLOR = "#000000ff";
|
|
12540
|
-
var
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
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);
|
|
12548
13029
|
function solid(color) {
|
|
12549
13030
|
const a = getAlpha01(color);
|
|
12550
13031
|
const rgb = color.length === 9 && color.startsWith("#") ? color.slice(0, 7) : color;
|
|
@@ -13047,7 +13528,7 @@ var ingestAction = {
|
|
|
13047
13528
|
insert,
|
|
13048
13529
|
applyOps: (ops, label) => {
|
|
13049
13530
|
if (applyOps) applyOps(ops, label ?? "Ingest");
|
|
13050
|
-
else scene.applyBatch(ops, label ?? "Ingest",
|
|
13531
|
+
else scene.applyBatch(ops, label ?? "Ingest", defaultCommitAdapter(scene, selection.adapterMethods));
|
|
13051
13532
|
},
|
|
13052
13533
|
scene,
|
|
13053
13534
|
selection,
|
|
@@ -13074,8 +13555,8 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13074
13555
|
escapeAction,
|
|
13075
13556
|
cancelGestureAction,
|
|
13076
13557
|
selectAllAction,
|
|
13077
|
-
deleteAction,
|
|
13078
13558
|
duplicateAction,
|
|
13559
|
+
deleteAction,
|
|
13079
13560
|
groupAction,
|
|
13080
13561
|
ungroupAction,
|
|
13081
13562
|
undoAction,
|
|
@@ -13088,11 +13569,11 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13088
13569
|
reorderForwardAction,
|
|
13089
13570
|
reorderBackwardAction,
|
|
13090
13571
|
alignLeftAction,
|
|
13572
|
+
alignCenterXAction,
|
|
13091
13573
|
alignRightAction,
|
|
13092
13574
|
alignTopAction,
|
|
13093
|
-
alignBottomAction,
|
|
13094
|
-
alignCenterXAction,
|
|
13095
13575
|
alignCenterYAction,
|
|
13576
|
+
alignBottomAction,
|
|
13096
13577
|
distributeHorizontalAction,
|
|
13097
13578
|
distributeVerticalAction,
|
|
13098
13579
|
pathfinderUnionAction,
|
|
@@ -13130,8 +13611,9 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
13130
13611
|
setFillOpacityAction,
|
|
13131
13612
|
setStrokeOpacityAction,
|
|
13132
13613
|
ingestAction,
|
|
13614
|
+
clipboardCutAction,
|
|
13133
13615
|
clipboardCopyAction,
|
|
13134
|
-
|
|
13616
|
+
clipboardPasteAction
|
|
13135
13617
|
];
|
|
13136
13618
|
var KIT_STANDARD_ACTION_IDS = KIT_STANDARD_DESCRIPTORS.map((a) => a.id);
|
|
13137
13619
|
function useStandardActions(opts) {
|
|
@@ -14104,7 +14586,7 @@ function createTimeline(register, id, opts) {
|
|
|
14104
14586
|
let playhead = 0;
|
|
14105
14587
|
let prevPlayhead = -Infinity;
|
|
14106
14588
|
let done = false;
|
|
14107
|
-
let
|
|
14589
|
+
let live2 = true;
|
|
14108
14590
|
let cancelled = false;
|
|
14109
14591
|
let wantPaused = opts.autoplay === false;
|
|
14110
14592
|
let wantScale = 1;
|
|
@@ -14192,12 +14674,12 @@ function createTimeline(register, id, opts) {
|
|
|
14192
14674
|
opts.onDone?.();
|
|
14193
14675
|
}
|
|
14194
14676
|
if (playhead < duration) return false;
|
|
14195
|
-
|
|
14677
|
+
live2 = false;
|
|
14196
14678
|
return true;
|
|
14197
14679
|
};
|
|
14198
14680
|
const onCancel = () => {
|
|
14199
14681
|
cancelled = true;
|
|
14200
|
-
|
|
14682
|
+
live2 = false;
|
|
14201
14683
|
booker?.retract();
|
|
14202
14684
|
};
|
|
14203
14685
|
const base = register({ id, cancelKey: opts.cancelKey, tick, onCancel });
|
|
@@ -14205,11 +14687,11 @@ function createTimeline(register, id, opts) {
|
|
|
14205
14687
|
const rearm = () => {
|
|
14206
14688
|
if (cancelled || playhead >= duration) return;
|
|
14207
14689
|
done = false;
|
|
14208
|
-
if (
|
|
14690
|
+
if (live2) return;
|
|
14209
14691
|
booker?.rearm();
|
|
14210
14692
|
lastVirtual = 0;
|
|
14211
14693
|
offset = playhead;
|
|
14212
|
-
|
|
14694
|
+
live2 = true;
|
|
14213
14695
|
register({ id, cancelKey: opts.cancelKey, keepExisting: true, tick, onCancel });
|
|
14214
14696
|
if (wantPaused) base.pause();
|
|
14215
14697
|
base.setTimeScale(wantScale);
|
|
@@ -14237,8 +14719,8 @@ function createTimeline(register, id, opts) {
|
|
|
14237
14719
|
wantScale = s;
|
|
14238
14720
|
base.setTimeScale(s);
|
|
14239
14721
|
},
|
|
14240
|
-
timeScale: () =>
|
|
14241
|
-
isPaused: () =>
|
|
14722
|
+
timeScale: () => live2 ? base.timeScale() : wantScale,
|
|
14723
|
+
isPaused: () => live2 ? base.isPaused() : wantPaused,
|
|
14242
14724
|
seek(t) {
|
|
14243
14725
|
booker?.retract();
|
|
14244
14726
|
offset = t - lastVirtual;
|
|
@@ -15650,17 +16132,53 @@ function resolveFillPattern(fill) {
|
|
|
15650
16132
|
}
|
|
15651
16133
|
|
|
15652
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;
|
|
15653
16139
|
var cache6 = /* @__PURE__ */ new Map();
|
|
15654
16140
|
var subscribers = /* @__PURE__ */ new Set();
|
|
15655
|
-
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() {
|
|
15656
16148
|
if (typeof Image === "undefined" || typeof createImageBitmap === "undefined") {
|
|
15657
16149
|
throw new Error("weasel imageCache: no DOM image loader in this environment");
|
|
15658
16150
|
}
|
|
16151
|
+
}
|
|
16152
|
+
async function decodeImage(src) {
|
|
15659
16153
|
const img = new Image();
|
|
15660
16154
|
img.crossOrigin = "anonymous";
|
|
15661
16155
|
img.src = src;
|
|
15662
16156
|
await img.decode();
|
|
15663
|
-
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));
|
|
15664
16182
|
};
|
|
15665
16183
|
var loader = defaultLoader;
|
|
15666
16184
|
function notify() {
|
|
@@ -15672,7 +16190,11 @@ function startLoad(src) {
|
|
|
15672
16190
|
loader(src).then(
|
|
15673
16191
|
(bitmap) => {
|
|
15674
16192
|
if (cache6.get(src) !== entry) return;
|
|
15675
|
-
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
|
+
});
|
|
15676
16198
|
notify();
|
|
15677
16199
|
},
|
|
15678
16200
|
() => {
|
|
@@ -15682,11 +16204,54 @@ function startLoad(src) {
|
|
|
15682
16204
|
}
|
|
15683
16205
|
);
|
|
15684
16206
|
}
|
|
15685
|
-
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) {
|
|
15686
16248
|
const entry = cache6.get(src);
|
|
15687
|
-
if (entry)
|
|
15688
|
-
|
|
15689
|
-
|
|
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;
|
|
15690
16255
|
}
|
|
15691
16256
|
function imageStatus(src) {
|
|
15692
16257
|
return cache6.get(src)?.status ?? "idle";
|
|
@@ -16008,6 +16573,21 @@ function pathForShape(d, p) {
|
|
|
16008
16573
|
return { kind: "rect", x: p.x, y: p.y, width: p.width, height: p.height };
|
|
16009
16574
|
}
|
|
16010
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
|
+
}
|
|
16011
16591
|
var IMAGE_PAINTER = {
|
|
16012
16592
|
id: "kit:image",
|
|
16013
16593
|
matches: (node) => {
|
|
@@ -16018,7 +16598,7 @@ var IMAGE_PAINTER = {
|
|
|
16018
16598
|
paint: (node, pose, ctx) => {
|
|
16019
16599
|
const d = node.data;
|
|
16020
16600
|
const p = pose;
|
|
16021
|
-
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));
|
|
16022
16602
|
if (bmp) {
|
|
16023
16603
|
return [{
|
|
16024
16604
|
kind: "image",
|
|
@@ -16027,7 +16607,10 @@ var IMAGE_PAINTER = {
|
|
|
16027
16607
|
y: p.y,
|
|
16028
16608
|
w: p.width,
|
|
16029
16609
|
h: p.height,
|
|
16030
|
-
...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 } : {}
|
|
16031
16614
|
}];
|
|
16032
16615
|
}
|
|
16033
16616
|
const error = ctx?.resolveImage ? false : imageStatus(d.image.src) === "error";
|
|
@@ -16273,9 +16856,13 @@ function scenePickSource(scene, opts = {}) {
|
|
|
16273
16856
|
const n = scene.get(asNodeId(id));
|
|
16274
16857
|
return n ? effectivePose(scene, n) : void 0;
|
|
16275
16858
|
},
|
|
16276
|
-
getParent: (id) => scene.get(asNodeId(id))?.parent ?? null
|
|
16277
|
-
|
|
16278
|
-
|
|
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
|
+
}
|
|
16864
|
+
} : null;
|
|
16865
|
+
const poseOf = getPose ? (node) => getPose(node.id) : chain ? (node) => composeWorldPose(chain, node.id, composition.compose) : (node) => effectivePose(scene, node);
|
|
16279
16866
|
const childrenOf = (id) => scene.childrenOf(id);
|
|
16280
16867
|
const depOf2 = (id) => {
|
|
16281
16868
|
const dep = scene.get(id);
|
|
@@ -16758,6 +17345,14 @@ useTextTool.options = {
|
|
|
16758
17345
|
description: "Raise or lower the range off the baseline, at a smaller size.",
|
|
16759
17346
|
default: void 0
|
|
16760
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
|
+
},
|
|
16761
17356
|
fontFamily: {
|
|
16762
17357
|
kind: "font-family",
|
|
16763
17358
|
name: "Font",
|
|
@@ -16867,44 +17462,9 @@ function anchorCount(s) {
|
|
|
16867
17462
|
return (s.current ? s.current.anchors.length : 0) + s.finishedSubpaths.reduce((n, sp) => n + sp.anchors.length, 0);
|
|
16868
17463
|
}
|
|
16869
17464
|
function buildPath(subpaths, trailing) {
|
|
16870
|
-
const b = new PathBuilder();
|
|
16871
17465
|
const all = [...subpaths];
|
|
16872
17466
|
if (trailing && trailing.anchors.length > 0) all.push(trailing);
|
|
16873
|
-
|
|
16874
|
-
if (sp.anchors.length === 0) continue;
|
|
16875
|
-
const first = sp.anchors[0];
|
|
16876
|
-
b.moveTo(first.x, first.y);
|
|
16877
|
-
for (let i = 1; i < sp.anchors.length; i++) {
|
|
16878
|
-
const prev = sp.anchors[i - 1];
|
|
16879
|
-
const curr = sp.anchors[i];
|
|
16880
|
-
const out = prev.outHandle;
|
|
16881
|
-
const inH = curr.inHandle ?? mirrorHandle(prev, out);
|
|
16882
|
-
if (out || curr.inHandle) {
|
|
16883
|
-
const c1 = out ?? { x: prev.x, y: prev.y };
|
|
16884
|
-
const c2 = inH ?? { x: curr.x, y: curr.y };
|
|
16885
|
-
b.curveTo(c1.x, c1.y, c2.x, c2.y, curr.x, curr.y);
|
|
16886
|
-
} else {
|
|
16887
|
-
b.lineTo(curr.x, curr.y);
|
|
16888
|
-
}
|
|
16889
|
-
}
|
|
16890
|
-
if (sp.closed) {
|
|
16891
|
-
const last = sp.anchors[sp.anchors.length - 1];
|
|
16892
|
-
const first0 = sp.anchors[0];
|
|
16893
|
-
const out = last.outHandle;
|
|
16894
|
-
const inH = first0.inHandle ?? mirrorHandle(last, out);
|
|
16895
|
-
if (out || first0.inHandle) {
|
|
16896
|
-
const c1 = out ?? { x: last.x, y: last.y };
|
|
16897
|
-
const c2 = inH ?? { x: first0.x, y: first0.y };
|
|
16898
|
-
b.curveTo(c1.x, c1.y, c2.x, c2.y, first0.x, first0.y);
|
|
16899
|
-
}
|
|
16900
|
-
b.close();
|
|
16901
|
-
}
|
|
16902
|
-
}
|
|
16903
|
-
return b.build();
|
|
16904
|
-
}
|
|
16905
|
-
function mirrorHandle(anchor, out) {
|
|
16906
|
-
if (!out || anchor.altBroken) return void 0;
|
|
16907
|
-
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));
|
|
16908
17468
|
}
|
|
16909
17469
|
function usePenTool(options) {
|
|
16910
17470
|
const { wrapPath, adapter, autoSelect = true, autoCommitOnClose = true, closeHitRadius = 8, anchorSnapRadius = 8, snapPoint: snapPoint2 } = options;
|
|
@@ -16949,6 +17509,64 @@ function usePenTool(options) {
|
|
|
16949
17509
|
if (optsRef.current.autoSelect) optsRef.current.adapter.setSelection([id]);
|
|
16950
17510
|
resetScratch(s);
|
|
16951
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
|
+
}, []);
|
|
16952
17570
|
const pickUpEndpoint = useCallback((deps, wx, wy) => {
|
|
16953
17571
|
const scratch = scratchRef.current;
|
|
16954
17572
|
if (scratch.current || scratch.finishedSubpaths.length > 0) return false;
|
|
@@ -16971,9 +17589,9 @@ function usePenTool(options) {
|
|
|
16971
17589
|
const radius = optsRef.current.anchorSnapRadius;
|
|
16972
17590
|
if (radius > 0) {
|
|
16973
17591
|
const hit = nearestPathAnchor(pathsNear(deps, x, y, radius), { x, y }, radius, viewScale(deps));
|
|
16974
|
-
if (hit) return { x: hit.x, y: hit.y };
|
|
17592
|
+
if (hit) return { x: hit.x, y: hit.y, hit };
|
|
16975
17593
|
}
|
|
16976
|
-
return snap2(x, y);
|
|
17594
|
+
return { ...snap2(x, y), hit: null };
|
|
16977
17595
|
}, [snap2]);
|
|
16978
17596
|
const withinCloseRadius = useCallback(
|
|
16979
17597
|
(deps, ax, ay, wx, wy) => {
|
|
@@ -16988,7 +17606,7 @@ function usePenTool(options) {
|
|
|
16988
17606
|
id: "pen.placeAnchor",
|
|
16989
17607
|
label: "Pen \u2014 place anchor",
|
|
16990
17608
|
eligible: { capability: "creates-paths" },
|
|
16991
|
-
requires: ["view", "areaSelect", "editAnchors"],
|
|
17609
|
+
requires: ["view", "areaSelect", "editAnchors", "scene", "applyOps"],
|
|
16992
17610
|
invoker: {
|
|
16993
17611
|
timing: "immediate",
|
|
16994
17612
|
run: (deps, params) => {
|
|
@@ -16998,7 +17616,7 @@ function usePenTool(options) {
|
|
|
16998
17616
|
forceRenderRef.current();
|
|
16999
17617
|
return;
|
|
17000
17618
|
}
|
|
17001
|
-
const { x: wx, y: wy } = placeAt(deps, p.pressX, p.pressY);
|
|
17619
|
+
const { x: wx, y: wy, hit } = placeAt(deps, p.pressX, p.pressY);
|
|
17002
17620
|
const scratch = s();
|
|
17003
17621
|
if (scratch.current && scratch.current.anchors.length >= 3) {
|
|
17004
17622
|
const first = scratch.current.anchors[0];
|
|
@@ -17012,8 +17630,10 @@ function usePenTool(options) {
|
|
|
17012
17630
|
return;
|
|
17013
17631
|
}
|
|
17014
17632
|
}
|
|
17633
|
+
const join = joinable(scratch, deps, hit);
|
|
17015
17634
|
if (!scratch.current) scratch.current = { anchors: [], closed: false };
|
|
17016
17635
|
scratch.current.anchors.push({ x: wx, y: wy });
|
|
17636
|
+
if (join) commitJoin(scratch, deps, hit);
|
|
17017
17637
|
forceRenderRef.current();
|
|
17018
17638
|
}
|
|
17019
17639
|
}
|
|
@@ -17045,32 +17665,35 @@ function usePenTool(options) {
|
|
|
17045
17665
|
id: "pen.dragHandle",
|
|
17046
17666
|
label: "Pen \u2014 drag out a bezier handle",
|
|
17047
17667
|
eligible: { capability: "creates-paths" },
|
|
17048
|
-
requires: ["view", "areaSelect", "editAnchors"],
|
|
17668
|
+
requires: ["view", "areaSelect", "editAnchors", "scene", "applyOps"],
|
|
17049
17669
|
invoker: {
|
|
17050
17670
|
timing: "ongoing",
|
|
17051
17671
|
start: (ctx) => {
|
|
17052
17672
|
const scratch = s();
|
|
17053
17673
|
const origin = ctx.drag?.start ?? ctx.world;
|
|
17054
17674
|
const pickedUp = pickUpEndpoint(ctx.deps, origin.x, origin.y);
|
|
17675
|
+
let joinHit = null;
|
|
17055
17676
|
if (!pickedUp) {
|
|
17056
|
-
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;
|
|
17057
17679
|
if (!scratch.current) scratch.current = { anchors: [], closed: false };
|
|
17058
17680
|
scratch.current.anchors.push({ x: ax, y: ay });
|
|
17059
17681
|
}
|
|
17060
17682
|
scratch.draggingHandleAt = scratch.current.anchors.length - 1;
|
|
17061
|
-
applyOutHandle(scratch, ctx.world, ctx.modifiers.shift, optsRef.current.snapPoint);
|
|
17062
17683
|
if (ctx.modifiers.alt) {
|
|
17063
17684
|
scratch.current.anchors[scratch.draggingHandleAt].altBroken = true;
|
|
17064
17685
|
}
|
|
17686
|
+
const linked = !pickedUp;
|
|
17687
|
+
applyOutHandle(scratch, ctx.world, ctx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
17065
17688
|
forceRenderRef.current();
|
|
17066
17689
|
return {
|
|
17067
17690
|
onMove: (moveCtx) => {
|
|
17068
17691
|
const sm = s();
|
|
17069
17692
|
if (sm.draggingHandleAt === null) return;
|
|
17070
|
-
applyOutHandle(sm, moveCtx.world, moveCtx.modifiers.shift, optsRef.current.snapPoint);
|
|
17071
17693
|
if (moveCtx.modifiers.alt && sm.current) {
|
|
17072
17694
|
sm.current.anchors[sm.draggingHandleAt].altBroken = true;
|
|
17073
17695
|
}
|
|
17696
|
+
applyOutHandle(sm, moveCtx.world, moveCtx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
17074
17697
|
forceRenderRef.current();
|
|
17075
17698
|
},
|
|
17076
17699
|
onEnd: (endCtx, reason) => {
|
|
@@ -17087,12 +17710,13 @@ function usePenTool(options) {
|
|
|
17087
17710
|
return;
|
|
17088
17711
|
}
|
|
17089
17712
|
if (se.draggingHandleAt !== null) {
|
|
17090
|
-
applyOutHandle(se, endCtx.world, endCtx.modifiers.shift, optsRef.current.snapPoint);
|
|
17091
17713
|
if (endCtx.modifiers.alt && se.current) {
|
|
17092
17714
|
se.current.anchors[se.draggingHandleAt].altBroken = true;
|
|
17093
17715
|
}
|
|
17716
|
+
applyOutHandle(se, endCtx.world, endCtx.modifiers.shift, linked, optsRef.current.snapPoint);
|
|
17094
17717
|
se.draggingHandleAt = null;
|
|
17095
17718
|
}
|
|
17719
|
+
if (joinHit && se.current) commitJoin(se, ctx.deps, joinHit);
|
|
17096
17720
|
forceRenderRef.current();
|
|
17097
17721
|
}
|
|
17098
17722
|
};
|
|
@@ -17133,7 +17757,7 @@ function usePenTool(options) {
|
|
|
17133
17757
|
}
|
|
17134
17758
|
}
|
|
17135
17759
|
];
|
|
17136
|
-
}, [commit, placeAt, withinCloseRadius, pickUpEndpoint]);
|
|
17760
|
+
}, [commit, placeAt, withinCloseRadius, pickUpEndpoint, joinable, commitJoin]);
|
|
17137
17761
|
return useMemo(() => {
|
|
17138
17762
|
return defineTool({
|
|
17139
17763
|
id: "pen",
|
|
@@ -17191,7 +17815,7 @@ usePenTool.prefs = {
|
|
|
17191
17815
|
}
|
|
17192
17816
|
}
|
|
17193
17817
|
};
|
|
17194
|
-
function applyOutHandle(s, target, shift, snap2) {
|
|
17818
|
+
function applyOutHandle(s, target, shift, linked, snap2) {
|
|
17195
17819
|
if (s.current === null || s.draggingHandleAt === null) return;
|
|
17196
17820
|
const anchor = s.current.anchors[s.draggingHandleAt];
|
|
17197
17821
|
const to = snap2 ? snap2(target) : target;
|
|
@@ -17203,6 +17827,7 @@ function applyOutHandle(s, target, shift, snap2) {
|
|
|
17203
17827
|
dy = c.dy;
|
|
17204
17828
|
}
|
|
17205
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 };
|
|
17206
17831
|
}
|
|
17207
17832
|
function viewScale(deps) {
|
|
17208
17833
|
return deps.view?.get().scale ?? { x: 1, y: 1 };
|
|
@@ -17655,9 +18280,24 @@ function usePencilTool() {
|
|
|
17655
18280
|
}
|
|
17656
18281
|
|
|
17657
18282
|
// src/canvas/defaultDrawOne.ts
|
|
17658
|
-
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) {
|
|
17659
18298
|
const painter = findNodeShape(node);
|
|
17660
|
-
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) : [];
|
|
17661
18301
|
const data = node.data;
|
|
17662
18302
|
if (data?.label && data.text == null) {
|
|
17663
18303
|
const p = pose;
|
|
@@ -18723,11 +19363,11 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf) {
|
|
|
18723
19363
|
const buckets = /* @__PURE__ */ new Map();
|
|
18724
19364
|
for (const l of layers) buckets.set(l.id, []);
|
|
18725
19365
|
const compose2 = adapter.composePose?.bind(adapter);
|
|
18726
|
-
function visit(id, ancestorClips,
|
|
19366
|
+
function visit(id, ancestorClips, parentFrame) {
|
|
18727
19367
|
const node = adapter.getNode(id);
|
|
18728
19368
|
if (!node) return;
|
|
18729
19369
|
const local = adapter.getPose(id);
|
|
18730
|
-
const pose = compose2 &&
|
|
19370
|
+
const pose = compose2 && parentFrame !== null ? compose2(parentFrame, local) : local;
|
|
18731
19371
|
const paints = forLayer === void 0 || node.layer === forLayer;
|
|
18732
19372
|
const self = paints ? drawOne(node, pose, view) : [];
|
|
18733
19373
|
let ownClips = ancestorClips;
|
|
@@ -18751,7 +19391,8 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf) {
|
|
|
18751
19391
|
bucket.push(ownClips.length > 0 ? wrapInClips(self, ownClips) : { kind: "group", children: self });
|
|
18752
19392
|
}
|
|
18753
19393
|
}
|
|
18754
|
-
|
|
19394
|
+
const childFrame = definesFrame(node) ? pose : parentFrame;
|
|
19395
|
+
for (const cid of adapter.getChildren(id)) visit(cid, ownClips, childFrame);
|
|
18755
19396
|
}
|
|
18756
19397
|
for (const rootId of adapter.getChildren(null)) visit(rootId, [], null);
|
|
18757
19398
|
const out = [];
|
|
@@ -19961,191 +20602,613 @@ function wireSceneSlotToScene(slot, scene, alphaFor, colorOverrides) {
|
|
|
19961
20602
|
alphaFor: composeAlphaFor(scene, alphaFor)
|
|
19962
20603
|
};
|
|
19963
20604
|
}
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
|
|
19967
|
-
|
|
19968
|
-
|
|
19969
|
-
let generation2 = 0;
|
|
19970
|
-
function invalidate(id) {
|
|
19971
|
-
const node = getNode(id);
|
|
19972
|
-
if (node) dropPoseKeyedMemoSlots(node);
|
|
19973
|
-
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;
|
|
19974
20610
|
}
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
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;
|
|
19978
20632
|
}
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
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));
|
|
19984
20715
|
},
|
|
19985
|
-
|
|
19986
|
-
|
|
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;
|
|
19987
20723
|
},
|
|
19988
|
-
|
|
19989
|
-
|
|
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);
|
|
19990
20729
|
},
|
|
19991
|
-
|
|
19992
|
-
|
|
20730
|
+
getParent(id) {
|
|
20731
|
+
const n = scene.get(asNodeId(id));
|
|
20732
|
+
return n?.parent ?? null;
|
|
19993
20733
|
},
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
published();
|
|
20734
|
+
definesFrame(id) {
|
|
20735
|
+
const n = scene.get(asNodeId(id));
|
|
20736
|
+
return n === void 0 || definesFrame(n);
|
|
19998
20737
|
},
|
|
19999
|
-
|
|
20000
|
-
if (
|
|
20001
|
-
|
|
20002
|
-
entries.clear();
|
|
20003
|
-
published();
|
|
20738
|
+
getWorldPose(id) {
|
|
20739
|
+
if (!composes) return adapter.getPose(id);
|
|
20740
|
+
return composeWorldPose(adapter, id, composition.compose);
|
|
20004
20741
|
},
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
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);
|
|
20008
20776
|
},
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
return () => {
|
|
20012
|
-
listeners.delete(fn);
|
|
20013
|
-
};
|
|
20777
|
+
setParent(id, parentId) {
|
|
20778
|
+
scene.move(asNodeId(id), parentId === null ? null : asNodeId(parentId));
|
|
20014
20779
|
},
|
|
20015
|
-
|
|
20016
|
-
return
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
const base = {
|
|
20026
|
-
id: spec.id,
|
|
20027
|
-
visible: spec.visible ?? true,
|
|
20028
|
-
locked: spec.locked ?? false
|
|
20029
|
-
};
|
|
20030
|
-
return spec.kind === "user" ? { ...base, kind: "user", name: spec.name ?? spec.id } : { ...base, kind: "system" };
|
|
20031
|
-
}
|
|
20032
|
-
function createScene(options) {
|
|
20033
|
-
if (!options.systemLayers || options.systemLayers.length === 0) {
|
|
20034
|
-
throw new Error("useScene: systemLayers must declare at least one layer");
|
|
20035
|
-
}
|
|
20036
|
-
const state = {
|
|
20037
|
-
nodes: /* @__PURE__ */ new Map(),
|
|
20038
|
-
roots: [],
|
|
20039
|
-
layers: [],
|
|
20040
|
-
layerIndex: /* @__PURE__ */ new Map()
|
|
20041
|
-
};
|
|
20042
|
-
const registry2 = withKitRegistry(options.registry ?? {});
|
|
20043
|
-
const fnFields = createNodeFnFields(registry2);
|
|
20044
|
-
const fnField = (name) => fnFields.find((f) => f.spec.field === name);
|
|
20045
|
-
const dependents = createDependentsIndex();
|
|
20046
|
-
const childDerived = /* @__PURE__ */ new Set();
|
|
20047
|
-
function invalidateDependents(id) {
|
|
20048
|
-
invalidateChildDerivedFrom(state.nodes.get(id)?.parent ?? null);
|
|
20049
|
-
if (dependents.isEmpty()) return;
|
|
20050
|
-
const subtree = [id];
|
|
20051
|
-
descendants(id, subtree);
|
|
20052
|
-
for (const src of subtree) {
|
|
20053
|
-
for (const dep of dependents.transitiveDependentsOf(src)) {
|
|
20054
|
-
const node = state.nodes.get(dep);
|
|
20055
|
-
if (node !== void 0) dropPoseKeyedMemoSlots(node);
|
|
20056
|
-
}
|
|
20057
|
-
}
|
|
20058
|
-
}
|
|
20059
|
-
function invalidateChildDerivedFrom(start) {
|
|
20060
|
-
if (childDerived.size === 0) return;
|
|
20061
|
-
let cursor = start;
|
|
20062
|
-
while (cursor !== null) {
|
|
20063
|
-
const node = state.nodes.get(cursor);
|
|
20064
|
-
if (node === void 0) return;
|
|
20065
|
-
if (childDerived.has(cursor)) {
|
|
20066
|
-
dropPoseKeyedMemoSlots(node);
|
|
20067
|
-
for (const dep of dependents.transitiveDependentsOf(cursor)) {
|
|
20068
|
-
const d = state.nodes.get(dep);
|
|
20069
|
-
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);
|
|
20070
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);
|
|
20071
20810
|
}
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
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
|
+
});
|
|
20122
20933
|
}
|
|
20123
|
-
}
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
let selection = [];
|
|
20128
|
-
const overrides = createPoseOverrides(
|
|
20129
|
-
(id) => state.nodes.get(id),
|
|
20130
|
-
invalidateDependents
|
|
20131
|
-
);
|
|
20132
|
-
const history = createHistory(void 0, {
|
|
20133
|
-
selection: {
|
|
20134
|
-
get: () => selection,
|
|
20135
|
-
set: (ids) => {
|
|
20136
|
-
selection = reachable(ids);
|
|
20137
|
-
notify2();
|
|
20934
|
+
};
|
|
20935
|
+
for (const id of ids) {
|
|
20936
|
+
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
20937
|
+
capture(id, true);
|
|
20138
20938
|
}
|
|
20939
|
+
return { items };
|
|
20139
20940
|
},
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
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;
|
|
20995
|
+
},
|
|
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 };
|
|
21000
|
+
}
|
|
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.
|
|
20149
21212
|
// Return null only when the global registry also misses — the engine
|
|
20150
21213
|
// then places its no-op placeholder. (The engine's own global-registry
|
|
20151
21214
|
// fallback never fires for scene histories: this hook already
|
|
@@ -22018,7 +23081,7 @@ function CanvasView(props) {
|
|
|
22018
23081
|
const viewSelection = selectionProp ?? (selectionOptions ? ownSelection : void 0);
|
|
22019
23082
|
const [internalView, setInternalView] = useState(() => normalizeView(defaultView ?? IDENTITY_VIEW2));
|
|
22020
23083
|
const effectiveView = viewProp ?? internalView;
|
|
22021
|
-
const
|
|
23084
|
+
const live2 = useRef({
|
|
22022
23085
|
view: effectiveView,
|
|
22023
23086
|
bounds,
|
|
22024
23087
|
layers,
|
|
@@ -22032,8 +23095,8 @@ function CanvasView(props) {
|
|
|
22032
23095
|
// surface's otherwise. Filled in below, once the surface's is in hand.
|
|
22033
23096
|
selection: viewSelection ?? ownSelection
|
|
22034
23097
|
});
|
|
22035
|
-
|
|
22036
|
-
...
|
|
23098
|
+
live2.current = {
|
|
23099
|
+
...live2.current,
|
|
22037
23100
|
view: effectiveView,
|
|
22038
23101
|
bounds,
|
|
22039
23102
|
layers,
|
|
@@ -22045,7 +23108,7 @@ function CanvasView(props) {
|
|
|
22045
23108
|
viewSelection
|
|
22046
23109
|
};
|
|
22047
23110
|
const rectAt = useCallback((outer, dims) => {
|
|
22048
|
-
const b =
|
|
23111
|
+
const b = live2.current.bounds;
|
|
22049
23112
|
return typeof b === "function" ? b(outer, dims) : b;
|
|
22050
23113
|
}, []);
|
|
22051
23114
|
const rectNow = useCallback(() => {
|
|
@@ -22053,18 +23116,18 @@ function CanvasView(props) {
|
|
|
22053
23116
|
return rectAt(surface?.view() ?? IDENTITY_VIEW2, surface?.dims() ?? UNMEASURED_DIMS);
|
|
22054
23117
|
}, [registry2, rectAt]);
|
|
22055
23118
|
const cameraAt = useCallback((outer, dims) => {
|
|
22056
|
-
const v =
|
|
23119
|
+
const v = live2.current.view;
|
|
22057
23120
|
return normalizeView(typeof v === "function" ? v(outer, dims) : v);
|
|
22058
23121
|
}, []);
|
|
22059
23122
|
const camera = useCallback(() => {
|
|
22060
23123
|
const surface = registry2?.surface();
|
|
22061
23124
|
return cameraAt(surface?.view() ?? IDENTITY_VIEW2, surface?.dims() ?? UNMEASURED_DIMS);
|
|
22062
23125
|
}, [registry2, cameraAt]);
|
|
22063
|
-
const drawnLayers = useCallback(() => paintedLayers(registry2?.surface()?.layers() ?? [],
|
|
23126
|
+
const drawnLayers = useCallback(() => paintedLayers(registry2?.surface()?.layers() ?? [], live2.current), [registry2]);
|
|
22064
23127
|
const [sceneLayerGate] = useState(createSceneLayerGate);
|
|
22065
|
-
const layerIsPainted = useCallback((layerId) => sceneLayerGate(registry2?.surface()?.layers() ?? [],
|
|
23128
|
+
const layerIsPainted = useCallback((layerId) => sceneLayerGate(registry2?.surface()?.layers() ?? [], live2.current)(layerId), [registry2, sceneLayerGate]);
|
|
22066
23129
|
const setView = useCallback((next) => {
|
|
22067
|
-
const { viewBounds: vb, onViewChange: cb, viewProp: controlled } =
|
|
23130
|
+
const { viewBounds: vb, onViewChange: cb, viewProp: controlled } = live2.current;
|
|
22068
23131
|
const rect = rectNow();
|
|
22069
23132
|
const valid = normalizeView(next);
|
|
22070
23133
|
const clamped = vb ? clampView(valid, vb, { width: rect.w, height: rect.h }) : valid;
|
|
@@ -22107,11 +23170,11 @@ function CanvasView(props) {
|
|
|
22107
23170
|
...createDispatcherPreviewSources(() => dispatcherRef.current)
|
|
22108
23171
|
}), []);
|
|
22109
23172
|
const selection = viewSelection ?? inputs?.selectionApi ?? ownSelection;
|
|
22110
|
-
|
|
23173
|
+
live2.current.selection = selection;
|
|
22111
23174
|
const inputsRef = useRef(inputs);
|
|
22112
23175
|
inputsRef.current = inputs;
|
|
22113
23176
|
const ruleInputs = useCallback(() => ({
|
|
22114
|
-
selection:
|
|
23177
|
+
selection: live2.current.selection.get(),
|
|
22115
23178
|
view: camera(),
|
|
22116
23179
|
action: dispatcherRef.current.getActiveAction()
|
|
22117
23180
|
}), [camera]);
|
|
@@ -22152,553 +23215,169 @@ function CanvasView(props) {
|
|
|
22152
23215
|
world.y,
|
|
22153
23216
|
camera(),
|
|
22154
23217
|
{ width: rect.w, height: rect.h },
|
|
22155
|
-
helpersRef.current,
|
|
22156
|
-
drawnLayers()
|
|
22157
|
-
);
|
|
22158
|
-
if (extra) {
|
|
22159
|
-
const claim = extra.hit;
|
|
22160
|
-
return {
|
|
22161
|
-
kind: `layer:${extra.layerId}`,
|
|
22162
|
-
owner: extra.layerId,
|
|
22163
|
-
strength: claim.strength ?? "shared",
|
|
22164
|
-
...claim.claimedKinds !== void 0 ? { claimedKinds: claim.claimedKinds } : {},
|
|
22165
|
-
...claim.cursor !== void 0 ? { cursor: claim.cursor } : {},
|
|
22166
|
-
...claim.initialScratch !== void 0 ? { payload: claim.initialScratch } : {}
|
|
22167
|
-
};
|
|
22168
|
-
}
|
|
22169
|
-
return inner(world);
|
|
22170
|
-
};
|
|
22171
|
-
}, [getAnchorState, rectNow, registry2, targetScale, camera, drawnLayers]);
|
|
22172
|
-
const classifyTarget = useMemo(() => {
|
|
22173
|
-
const inner = buildClassifyTarget(
|
|
22174
|
-
() => live.current.selection.get(),
|
|
22175
|
-
(wx, wy) => {
|
|
22176
|
-
const i = inputsRef.current;
|
|
22177
|
-
const frame = { ...camera(), layerIsPainted };
|
|
22178
|
-
if (i?.pickBest) return i.pickBest(wx, wy, frame);
|
|
22179
|
-
const ids = i?.pickEvery?.(wx, wy, frame) ?? [];
|
|
22180
|
-
return ids.length > 0 ? ids[ids.length - 1] : null;
|
|
22181
|
-
},
|
|
22182
|
-
(id2) => inputsRef.current?.kindOfNode?.(id2)
|
|
22183
|
-
);
|
|
22184
|
-
return (world) => inner(world);
|
|
22185
|
-
}, [camera, layerIsPainted]);
|
|
22186
|
-
const registration = useMemo(() => ({
|
|
22187
|
-
id,
|
|
22188
|
-
order,
|
|
22189
|
-
interactive,
|
|
22190
|
-
paint,
|
|
22191
|
-
layer: createViewportLayer({
|
|
22192
|
-
id,
|
|
22193
|
-
label: label ?? id,
|
|
22194
|
-
source: drawnLayers,
|
|
22195
|
-
view: cameraAt,
|
|
22196
|
-
bounds: (outer, dims) => rectAt(outer, dims),
|
|
22197
|
-
// The surface half of the envelope passes through; the view half is
|
|
22198
|
-
// this view's, so its layers paint its chrome rather than the
|
|
22199
|
-
// surface's.
|
|
22200
|
-
data: (outer) => ({ ...outer, ...helpersRef.current }),
|
|
22201
|
-
...background !== void 0 ? { background } : {}
|
|
22202
|
-
}),
|
|
22203
|
-
layerIsPainted,
|
|
22204
|
-
target: {
|
|
22205
|
-
dispatcher: dispatcherRef.current,
|
|
22206
|
-
affordanceAt,
|
|
22207
|
-
classifyTarget,
|
|
22208
|
-
clientToWorld: clientToWorldHere,
|
|
22209
|
-
deps: () => ({
|
|
22210
|
-
view: viewApi,
|
|
22211
|
-
ingestion: ingestionApi,
|
|
22212
|
-
// An Escape here cancels what is in flight here.
|
|
22213
|
-
dispatcher: { cancelAll: (reason) => dispatcherRef.current.cancelAll(reason) },
|
|
22214
|
-
// Only a view with its own selection overlays that one; otherwise the
|
|
22215
|
-
// surface's answer stands, wrappers (`selectionMode`) included.
|
|
22216
|
-
...live.current.viewSelection ? { selection: live.current.viewSelection } : {}
|
|
22217
|
-
}),
|
|
22218
|
-
// Action eligibility is chrome-caps evaluated against this view, so a
|
|
22219
|
-
// rule that hides an action in one panel cannot decline a gesture in
|
|
22220
|
-
// another.
|
|
22221
|
-
getRuleCtx: () => inputsRef.current?.chromeCaps?.ruleCtx(ruleInputs())
|
|
22222
|
-
}
|
|
22223
|
-
}), [
|
|
22224
|
-
id,
|
|
22225
|
-
order,
|
|
22226
|
-
interactive,
|
|
22227
|
-
paint,
|
|
22228
|
-
label,
|
|
22229
|
-
background,
|
|
22230
|
-
rectAt,
|
|
22231
|
-
cameraAt,
|
|
22232
|
-
viewApi,
|
|
22233
|
-
affordanceAt,
|
|
22234
|
-
classifyTarget,
|
|
22235
|
-
clientToWorldHere,
|
|
22236
|
-
ruleInputs,
|
|
22237
|
-
ingestionApi,
|
|
22238
|
-
drawnLayers,
|
|
22239
|
-
layerIsPainted
|
|
22240
|
-
]);
|
|
22241
|
-
useEffect(() => {
|
|
22242
|
-
if (!registry2) return;
|
|
22243
|
-
return registry2.register(registration);
|
|
22244
|
-
}, [registry2, registration]);
|
|
22245
|
-
useEffect(() => {
|
|
22246
|
-
registry2?.surface()?.requestRedraw();
|
|
22247
|
-
}, [registry2, effectiveView]);
|
|
22248
|
-
return null;
|
|
22249
|
-
}
|
|
22250
|
-
function ActionsProviderIfRoot({ children }) {
|
|
22251
|
-
const parent = useActionsRegistry();
|
|
22252
|
-
if (parent) return /* @__PURE__ */ jsx(ActionsScope, { children });
|
|
22253
|
-
return /* @__PURE__ */ jsx(ActionsProvider, { children });
|
|
22254
|
-
}
|
|
22255
|
-
function useToolActions(tools) {
|
|
22256
|
-
const registry2 = useActionsRegistry();
|
|
22257
|
-
useEffect(() => {
|
|
22258
|
-
if (!registry2) return;
|
|
22259
|
-
const unregisters = [];
|
|
22260
|
-
for (const tool of [...Object.values(tools.registry), ...tools.ambient]) {
|
|
22261
|
-
for (const action of tool.actions ?? []) {
|
|
22262
|
-
unregisters.push(registry2.register(action));
|
|
22263
|
-
}
|
|
22264
|
-
}
|
|
22265
|
-
return () => {
|
|
22266
|
-
for (const u of unregisters) u();
|
|
22267
|
-
};
|
|
22268
|
-
}, [registry2, tools.registry, tools.ambient]);
|
|
22269
|
-
}
|
|
22270
|
-
function PointerProviderIfRoot({ children }) {
|
|
22271
|
-
const parent = usePointerContext();
|
|
22272
|
-
if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
22273
|
-
return /* @__PURE__ */ jsx(PointerContextProvider, { children });
|
|
22274
|
-
}
|
|
22275
|
-
function PointerPublisher({
|
|
22276
|
-
canvasRef
|
|
22277
|
-
}) {
|
|
22278
|
-
const ctx = usePointerContext();
|
|
22279
|
-
const registry2 = useOptionalViewRegistry();
|
|
22280
|
-
useEffect(() => {
|
|
22281
|
-
if (!ctx) return;
|
|
22282
|
-
const el = canvasRef.current;
|
|
22283
|
-
if (!el) return;
|
|
22284
|
-
const publish = (pointerId, clientX, clientY) => {
|
|
22285
|
-
const target = registry2?.resolver.at(pointerId, clientX, clientY);
|
|
22286
|
-
const rect = el.getBoundingClientRect();
|
|
22287
|
-
const [worldX, worldY] = clientToWorld(
|
|
22288
|
-
clientX,
|
|
22289
|
-
clientY,
|
|
22290
|
-
target?.origin ?? rect,
|
|
22291
|
-
target?.view ?? { x: 0, y: 0, scale: { x: 1, y: 1 } }
|
|
22292
|
-
);
|
|
22293
|
-
ctx.pointerRef.current = { worldX, worldY };
|
|
22294
|
-
};
|
|
22295
|
-
const onMove = (e) => publish(e.pointerId, e.clientX, e.clientY);
|
|
22296
|
-
const onLeave = (e) => {
|
|
22297
|
-
if (e.buttons !== 0) {
|
|
22298
|
-
publish(e.pointerId, e.clientX, e.clientY);
|
|
22299
|
-
return;
|
|
22300
|
-
}
|
|
22301
|
-
ctx.pointerRef.current = null;
|
|
22302
|
-
};
|
|
22303
|
-
const onDocMove = (e) => {
|
|
22304
|
-
if (e.target === el) return;
|
|
22305
|
-
if (e.buttons === 0) return;
|
|
22306
|
-
publish(e.pointerId, e.clientX, e.clientY);
|
|
22307
|
-
};
|
|
22308
|
-
el.addEventListener("pointermove", onMove);
|
|
22309
|
-
el.addEventListener("pointerleave", onLeave);
|
|
22310
|
-
document.addEventListener("pointermove", onDocMove);
|
|
22311
|
-
return () => {
|
|
22312
|
-
el.removeEventListener("pointermove", onMove);
|
|
22313
|
-
el.removeEventListener("pointerleave", onLeave);
|
|
22314
|
-
document.removeEventListener("pointermove", onDocMove);
|
|
22315
|
-
};
|
|
22316
|
-
}, [ctx, canvasRef, registry2]);
|
|
22317
|
-
return null;
|
|
22318
|
-
}
|
|
22319
|
-
function flattenVerts2(poly2) {
|
|
22320
|
-
const flat = new Array(poly2.length * 2);
|
|
22321
|
-
for (let i = 0; i < poly2.length; i++) {
|
|
22322
|
-
flat[i * 2] = poly2[i].x;
|
|
22323
|
-
flat[i * 2 + 1] = poly2[i].y;
|
|
22324
|
-
}
|
|
22325
|
-
return flat;
|
|
22326
|
-
}
|
|
22327
|
-
function polygonContainsRectCenter(poly2, rect) {
|
|
22328
|
-
return pointInPolygon(flattenVerts2(poly2), rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
22329
|
-
}
|
|
22330
|
-
function polygonContainsRect(poly2, rect) {
|
|
22331
|
-
if (poly2.length < 3) return false;
|
|
22332
|
-
const flat = flattenVerts2(poly2);
|
|
22333
|
-
if (!pointInPolygon(flat, rect.x, rect.y)) return false;
|
|
22334
|
-
if (!pointInPolygon(flat, rect.x + rect.width, rect.y)) return false;
|
|
22335
|
-
if (!pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return false;
|
|
22336
|
-
if (!pointInPolygon(flat, rect.x, rect.y + rect.height)) return false;
|
|
22337
|
-
return !anyPolyEdgeCrossesRect(poly2, rect);
|
|
22338
|
-
}
|
|
22339
|
-
function polygonIntersectsRect(poly2, rect) {
|
|
22340
|
-
if (poly2.length < 3) return false;
|
|
22341
|
-
const ab = polygonAabb(poly2);
|
|
22342
|
-
if (ab.x + ab.width < rect.x || ab.x > rect.x + rect.width) return false;
|
|
22343
|
-
if (ab.y + ab.height < rect.y || ab.y > rect.y + rect.height) return false;
|
|
22344
|
-
for (const v of poly2) {
|
|
22345
|
-
if (v.x >= rect.x && v.x <= rect.x + rect.width && v.y >= rect.y && v.y <= rect.y + rect.height) return true;
|
|
22346
|
-
}
|
|
22347
|
-
const flat = flattenVerts2(poly2);
|
|
22348
|
-
if (pointInPolygon(flat, rect.x, rect.y)) return true;
|
|
22349
|
-
if (pointInPolygon(flat, rect.x + rect.width, rect.y)) return true;
|
|
22350
|
-
if (pointInPolygon(flat, rect.x + rect.width, rect.y + rect.height)) return true;
|
|
22351
|
-
if (pointInPolygon(flat, rect.x, rect.y + rect.height)) return true;
|
|
22352
|
-
return anyPolyEdgeCrossesRect(poly2, rect);
|
|
22353
|
-
}
|
|
22354
|
-
function polygonAabb(poly2) {
|
|
22355
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
22356
|
-
for (const v of poly2) {
|
|
22357
|
-
if (v.x < minX) minX = v.x;
|
|
22358
|
-
if (v.y < minY) minY = v.y;
|
|
22359
|
-
if (v.x > maxX) maxX = v.x;
|
|
22360
|
-
if (v.y > maxY) maxY = v.y;
|
|
22361
|
-
}
|
|
22362
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
22363
|
-
}
|
|
22364
|
-
function anyPolyEdgeCrossesRect(poly2, rect) {
|
|
22365
|
-
const r0x = rect.x, r0y = rect.y;
|
|
22366
|
-
const r1x = rect.x + rect.width, r1y = rect.y;
|
|
22367
|
-
const r2x = rect.x + rect.width, r2y = rect.y + rect.height;
|
|
22368
|
-
const r3x = rect.x, r3y = rect.y + rect.height;
|
|
22369
|
-
for (let i = 0, j = poly2.length - 1; i < poly2.length; j = i++) {
|
|
22370
|
-
const a = poly2[j], b = poly2[i];
|
|
22371
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r0x, r0y, r1x, r1y)) return true;
|
|
22372
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r1x, r1y, r2x, r2y)) return true;
|
|
22373
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r2x, r2y, r3x, r3y)) return true;
|
|
22374
|
-
if (segmentsCross(a.x, a.y, b.x, b.y, r3x, r3y, r0x, r0y)) return true;
|
|
22375
|
-
}
|
|
22376
|
-
return false;
|
|
22377
|
-
}
|
|
22378
|
-
|
|
22379
|
-
// src/canvas/sceneAdapter.ts
|
|
22380
|
-
function walkClipAware(scene, poseBounds, nodeTest, poseComposition) {
|
|
22381
|
-
return pickWalk(scenePickSource(scene, poseComposition ? { poseComposition } : {}), {
|
|
22382
|
-
hits: (node, pose) => nodeTest(node, pose),
|
|
22383
|
-
clipAdmits: (clip, _node, pose) => pathIntersectsRect(clip, poseBounds(pose))
|
|
22384
|
-
});
|
|
22385
|
-
}
|
|
22386
|
-
var adapterFallbackIdCounter = 0;
|
|
22387
|
-
function adapterFallbackId() {
|
|
22388
|
-
return `n${(adapterFallbackIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22389
|
-
}
|
|
22390
|
-
var pasteIdCounter = 0;
|
|
22391
|
-
function pasteNodeId() {
|
|
22392
|
-
return `paste-${(pasteIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22393
|
-
}
|
|
22394
|
-
function copyField(value) {
|
|
22395
|
-
if (Array.isArray(value)) return [...value];
|
|
22396
|
-
if (value !== null && typeof value === "object") return { ...value };
|
|
22397
|
-
return value;
|
|
22398
|
-
}
|
|
22399
|
-
function sceneToAdapter(scene, options = {}) {
|
|
22400
|
-
const visibleLayers = () => {
|
|
22401
|
-
const out = /* @__PURE__ */ new Set();
|
|
22402
|
-
for (const layer of scene.layers) {
|
|
22403
|
-
if (layer.visible) out.add(layer.id);
|
|
22404
|
-
}
|
|
22405
|
-
return out;
|
|
22406
|
-
};
|
|
22407
|
-
const sel = options.selection;
|
|
22408
|
-
const getSelection = sel?.getSelection ?? sel?.get ?? (() => []);
|
|
22409
|
-
const setSelection = sel?.setSelection ?? sel?.set ?? (() => {
|
|
22410
|
-
});
|
|
22411
|
-
const d = options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
22412
|
-
const poseBounds = (p) => visualBoundsViaDescriptor(p, d);
|
|
22413
|
-
const translate3 = (p, dx, dy, node) => translatePoseViaDescriptor(
|
|
22414
|
-
p,
|
|
22415
|
-
dx,
|
|
22416
|
-
dy,
|
|
22417
|
-
node === void 0 ? d : poseDescriptorForNode(d, node)
|
|
22418
|
-
);
|
|
22419
|
-
const composition = options.poseComposition;
|
|
22420
|
-
const composes = composition !== void 0 && composition.closure !== "identity";
|
|
22421
|
-
if (composes && options.cascadeContainerPose) {
|
|
22422
|
-
throw new Error(
|
|
22423
|
-
"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."
|
|
22424
|
-
);
|
|
22425
|
-
}
|
|
22426
|
-
const adapter = {
|
|
22427
|
-
getNode(id) {
|
|
22428
|
-
return scene.get(asNodeId(id));
|
|
22429
|
-
},
|
|
22430
|
-
getNodes() {
|
|
22431
|
-
const visible = visibleLayers();
|
|
22432
|
-
const out = [];
|
|
22433
|
-
for (const n of scene.renderOrderNodes()) {
|
|
22434
|
-
if (visible.has(n.layer)) out.push(n);
|
|
22435
|
-
}
|
|
22436
|
-
return out;
|
|
22437
|
-
},
|
|
22438
|
-
getPose(id) {
|
|
22439
|
-
const nid = asNodeId(id);
|
|
22440
|
-
const n = scene.get(nid);
|
|
22441
|
-
if (!n) throw new Error(`sceneToAdapter: unknown node "${id}"`);
|
|
22442
|
-
return effectivePose(scene, n);
|
|
22443
|
-
},
|
|
22444
|
-
getParent(id) {
|
|
22445
|
-
const n = scene.get(asNodeId(id));
|
|
22446
|
-
return n?.parent ?? null;
|
|
22447
|
-
},
|
|
22448
|
-
getWorldPose(id) {
|
|
22449
|
-
if (!composes) return adapter.getPose(id);
|
|
22450
|
-
return composeWorldPose(adapter, id, composition.compose);
|
|
22451
|
-
},
|
|
22452
|
-
// Only present when it would do something: the render walk skips the fold
|
|
22453
|
-
// entirely when this is absent, which is the absolute-pose path.
|
|
22454
|
-
...composes ? { composePose: composition.compose } : {},
|
|
22455
|
-
setPose(id, pose) {
|
|
22456
|
-
if (options.cascadeContainerPose) {
|
|
22457
|
-
const node = scene.get(asNodeId(id));
|
|
22458
|
-
if (node && node.kind === "container") {
|
|
22459
|
-
const g = poseDescriptorForNode(d, node);
|
|
22460
|
-
const before = g.getBounds(node.pose);
|
|
22461
|
-
const after = g.getBounds(pose);
|
|
22462
|
-
const dx = after.x - before.x;
|
|
22463
|
-
const dy = after.y - before.y;
|
|
22464
|
-
if (dx !== 0 || dy !== 0) {
|
|
22465
|
-
const descIds = [];
|
|
22466
|
-
const collect = (rootId) => {
|
|
22467
|
-
for (const cid of scene.childrenOf(asNodeId(rootId))) {
|
|
22468
|
-
descIds.push(cid);
|
|
22469
|
-
collect(cid);
|
|
22470
|
-
}
|
|
22471
|
-
};
|
|
22472
|
-
collect(id);
|
|
22473
|
-
scene.batch("setPose", () => {
|
|
22474
|
-
scene.setPose(asNodeId(id), pose);
|
|
22475
|
-
for (const cid of descIds) {
|
|
22476
|
-
const cn = scene.get(asNodeId(cid));
|
|
22477
|
-
if (!cn) continue;
|
|
22478
|
-
scene.setPose(asNodeId(cid), translate3(cn.pose, dx, dy, cn));
|
|
22479
|
-
}
|
|
22480
|
-
});
|
|
22481
|
-
return;
|
|
22482
|
-
}
|
|
22483
|
-
}
|
|
22484
|
-
}
|
|
22485
|
-
scene.setPose(asNodeId(id), pose);
|
|
22486
|
-
},
|
|
22487
|
-
setParent(id, parentId) {
|
|
22488
|
-
scene.move(asNodeId(id), parentId === null ? null : asNodeId(parentId));
|
|
22489
|
-
},
|
|
22490
|
-
getChildren(id) {
|
|
22491
|
-
if (id === null) return [...scene.roots];
|
|
22492
|
-
return [...scene.childrenOf(asNodeId(id))];
|
|
22493
|
-
},
|
|
22494
|
-
setChildOrder(parentId, ids) {
|
|
22495
|
-
scene.batch("Reorder", () => {
|
|
22496
|
-
for (let i = 0; i < ids.length; i++) {
|
|
22497
|
-
const current = parentId === null ? scene.roots : scene.childrenOf(asNodeId(parentId));
|
|
22498
|
-
if (current[i] === ids[i]) continue;
|
|
22499
|
-
scene.reorder(asNodeId(ids[i]), i);
|
|
22500
|
-
}
|
|
22501
|
-
});
|
|
22502
|
-
},
|
|
22503
|
-
getLayers() {
|
|
22504
|
-
return scene.layers.map((l) => ({ id: l.id, visible: l.visible }));
|
|
22505
|
-
},
|
|
22506
|
-
...options.layouts ? {
|
|
22507
|
-
getLayout: typeof options.layouts === "function" ? options.layouts : (id) => options.layouts[id] ?? null
|
|
22508
|
-
} : {},
|
|
22509
|
-
// Unified applyOps: when a label is supplied the ops are wrapped in a
|
|
22510
|
-
// batch checkpoint (history-aware path used by Move/Resize/Rotate/Insert/
|
|
22511
|
-
// Delete tools). When a journal is active (via scene's getActiveJournal
|
|
22512
|
-
// option), scene.applyBatch routes to the journal instead of recording a
|
|
22513
|
-
// new parent-history entry. Without a label the ops are applied transiently
|
|
22514
|
-
// via applyOpsTo (AreaSelectAdapter contract — no checkpoint).
|
|
22515
|
-
applyOps(ops, label) {
|
|
22516
|
-
if (label !== void 0) {
|
|
22517
|
-
scene.applyBatch(ops, label, this);
|
|
22518
|
-
} else {
|
|
22519
|
-
applyOpsTo(this, ops);
|
|
22520
|
-
}
|
|
22521
|
-
},
|
|
22522
|
-
// Node-mutation surface used by kit InsertOps / DeleteOps (e.g.
|
|
22523
|
-
// useNest wrapping the selection in a new container node, or its
|
|
22524
|
-
// inverse). Re-adds via the full structural spec so the round-trip
|
|
22525
|
-
// survives undo/redo; removes by id.
|
|
22526
|
-
insertNode(node, index) {
|
|
22527
|
-
scene.add({
|
|
22528
|
-
kind: node.kind,
|
|
22529
|
-
layer: node.layer,
|
|
22530
|
-
pose: node.pose,
|
|
22531
|
-
data: node.data,
|
|
22532
|
-
id: node.id,
|
|
22533
|
-
...index !== void 0 ? { index } : {},
|
|
22534
|
-
...node.parent !== null ? { parent: node.parent } : {},
|
|
22535
|
-
// Derivation travels with the node, or undo of a delete brings an
|
|
22536
|
-
// edge back as a static path that never follows its endpoints again.
|
|
22537
|
-
...node.dependsOn !== void 0 ? { dependsOn: node.dependsOn } : {},
|
|
22538
|
-
...fnFieldsOfNode(node)
|
|
22539
|
-
});
|
|
22540
|
-
},
|
|
22541
|
-
removeNode(id) {
|
|
22542
|
-
scene.remove(asNodeId(id));
|
|
22543
|
-
},
|
|
22544
|
-
setData(id, data) {
|
|
22545
|
-
scene.update(asNodeId(id), { data });
|
|
22546
|
-
},
|
|
22547
|
-
getRemovalClosure(ids) {
|
|
22548
|
-
return [...scene.removalClosure(ids.map(asNodeId))];
|
|
22549
|
-
},
|
|
22550
|
-
// AreaSelectAdapter surface — included unconditionally so plain
|
|
22551
|
-
// `useSelectTool(sceneToAdapter(scene, { selection }))` Just Works for the
|
|
22552
|
-
// marquee gesture. `applyOps` (above) dispatches transiently when called
|
|
22553
|
-
// without a label, matching the AreaSelectAdapter contract; `hitTestArea`
|
|
22554
|
-
// does an AABB-vs-AABB scan over `scene.renderOrder()`, reading bounds
|
|
22555
|
-
// through the pose descriptor.
|
|
22556
|
-
getSelection,
|
|
22557
|
-
setSelection,
|
|
22558
|
-
hitTestArea(rect) {
|
|
22559
|
-
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
22560
|
-
const b = poseBounds(pose);
|
|
22561
|
-
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;
|
|
22562
|
-
}, composition);
|
|
22563
|
-
},
|
|
22564
|
-
hitTestLasso(polygon, mode) {
|
|
22565
|
-
if (polygon.length < 3) return [];
|
|
22566
|
-
return walkClipAware(scene, poseBounds, (_n, pose) => {
|
|
22567
|
-
const b = poseBounds(pose);
|
|
22568
|
-
return mode === "centers" ? polygonContainsRectCenter(polygon, b) : mode === "enclosed" ? polygonContainsRect(polygon, b) : polygonIntersectsRect(polygon, b);
|
|
22569
|
-
}, composition);
|
|
22570
|
-
},
|
|
22571
|
-
// commitInsert (gesture-time leaf insert) is opt-in: present only when
|
|
22572
|
-
// `options.commitInsert` is. The full insertNode/removeNode mutators
|
|
22573
|
-
// above are always present so kit-side InsertOp / DeleteOp round-trip
|
|
22574
|
-
// through scene.add / scene.remove regardless.
|
|
22575
|
-
//
|
|
22576
|
-
// Contract: this is a pure factory — it constructs a Node spec but does
|
|
22577
|
-
// NOT touch the scene. `useInsert`'s onEnd dispatches an `InsertOp`
|
|
22578
|
-
// whose apply() calls `adapter.insertNode` → `scene.add`, which is the
|
|
22579
|
-
// sole insertion path. Calling `scene.add` here too would double-insert,
|
|
22580
|
-
// and the second add throws on id collision (silently dropping the
|
|
22581
|
-
// gesture's pointer-up cleanup and breaking the next drag).
|
|
22582
|
-
...options.commitInsert ? {
|
|
22583
|
-
commitInsert: (bounds) => {
|
|
22584
|
-
const created = options.commitInsert(bounds);
|
|
22585
|
-
if (!created) return null;
|
|
22586
|
-
const layer = options.insertLayer ?? "default";
|
|
22587
|
-
const id = asNodeId(created.id ?? adapterFallbackId());
|
|
22588
|
-
return {
|
|
22589
|
-
kind: "leaf",
|
|
22590
|
-
id,
|
|
22591
|
-
layer,
|
|
22592
|
-
pose: created.pose,
|
|
22593
|
-
data: created.data,
|
|
22594
|
-
parent: null
|
|
22595
|
-
};
|
|
22596
|
-
}
|
|
22597
|
-
} : {},
|
|
22598
|
-
// Clipboard seam (`useClipboardOps` / the paste ingestion path).
|
|
22599
|
-
//
|
|
22600
|
-
// `snapshotSelection` captures adapter-shaped node copies; `commitPaste`
|
|
22601
|
-
// is a pure factory like `commitInsert` above — it mints fresh nodes but
|
|
22602
|
-
// NEVER touches the scene. Insertion happens via the hook's InsertOps
|
|
22603
|
-
// (one per created node, parents before children), which is what makes a
|
|
22604
|
-
// whole paste a single undo entry.
|
|
22605
|
-
snapshotSelection(ids) {
|
|
22606
|
-
const idSet = new Set(ids);
|
|
22607
|
-
const taken = /* @__PURE__ */ new Set();
|
|
22608
|
-
const items = [];
|
|
22609
|
-
const poseFor = (n, isRoot) => isRoot && composes ? adapter.getWorldPose(n.id) : n.pose;
|
|
22610
|
-
const capture = (id, isRoot = false) => {
|
|
22611
|
-
if (taken.has(id)) return;
|
|
22612
|
-
const n = scene.get(asNodeId(id));
|
|
22613
|
-
if (!n) return;
|
|
22614
|
-
taken.add(id);
|
|
22615
|
-
if (n.kind === "container") {
|
|
22616
|
-
items.push({
|
|
22617
|
-
kind: "container",
|
|
22618
|
-
id: n.id,
|
|
22619
|
-
layer: n.layer,
|
|
22620
|
-
parent: n.parent,
|
|
22621
|
-
pose: copyField(poseFor(n, isRoot)),
|
|
22622
|
-
data: copyField(n.data),
|
|
22623
|
-
children: [...n.children],
|
|
22624
|
-
...n.clipFromPose ? { clipFromPose: n.clipFromPose } : {}
|
|
22625
|
-
});
|
|
22626
|
-
for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
|
|
22627
|
-
} else {
|
|
22628
|
-
items.push({
|
|
22629
|
-
kind: "leaf",
|
|
22630
|
-
id: n.id,
|
|
22631
|
-
layer: n.layer,
|
|
22632
|
-
parent: n.parent,
|
|
22633
|
-
pose: copyField(poseFor(n, isRoot)),
|
|
22634
|
-
data: copyField(n.data)
|
|
22635
|
-
});
|
|
22636
|
-
}
|
|
22637
|
-
};
|
|
22638
|
-
for (const id of ids) {
|
|
22639
|
-
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
22640
|
-
capture(id, true);
|
|
22641
|
-
}
|
|
22642
|
-
return { items };
|
|
22643
|
-
},
|
|
22644
|
-
commitPaste(clipboard, offset, ctx) {
|
|
22645
|
-
const src = clipboard.items;
|
|
22646
|
-
if (src.length === 0) return [];
|
|
22647
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
22648
|
-
for (const item of src) idMap.set(item.id, asNodeId(pasteNodeId()));
|
|
22649
|
-
const isRoot = (item) => item.parent === null || !idMap.has(item.parent);
|
|
22650
|
-
let dx = offset.dx;
|
|
22651
|
-
let dy = offset.dy;
|
|
22652
|
-
if (ctx?.dropPoint) {
|
|
22653
|
-
let minX = Infinity;
|
|
22654
|
-
let minY = Infinity;
|
|
22655
|
-
for (const item of src) {
|
|
22656
|
-
if (!isRoot(item)) continue;
|
|
22657
|
-
const b = poseBounds(item.pose);
|
|
22658
|
-
if (b.x < minX) minX = b.x;
|
|
22659
|
-
if (b.y < minY) minY = b.y;
|
|
22660
|
-
}
|
|
22661
|
-
if (Number.isFinite(minX) && Number.isFinite(minY)) {
|
|
22662
|
-
dx = ctx.dropPoint.worldX - minX;
|
|
22663
|
-
dy = ctx.dropPoint.worldY - minY;
|
|
22664
|
-
}
|
|
22665
|
-
}
|
|
22666
|
-
const out = [];
|
|
22667
|
-
for (const item of src) {
|
|
22668
|
-
const base = {
|
|
22669
|
-
id: idMap.get(item.id),
|
|
22670
|
-
layer: item.layer,
|
|
22671
|
-
parent: isRoot(item) ? null : idMap.get(item.parent),
|
|
22672
|
-
pose: translate3(copyField(item.pose), dx, dy),
|
|
22673
|
-
data: copyField(item.data)
|
|
23218
|
+
helpersRef.current,
|
|
23219
|
+
drawnLayers()
|
|
23220
|
+
);
|
|
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 } : {}
|
|
22674
23230
|
};
|
|
22675
|
-
if (item.kind === "container") {
|
|
22676
|
-
out.push({
|
|
22677
|
-
...base,
|
|
22678
|
-
kind: "container",
|
|
22679
|
-
children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)),
|
|
22680
|
-
...item.clipFromPose ? { clipFromPose: item.clipFromPose } : {}
|
|
22681
|
-
});
|
|
22682
|
-
} else {
|
|
22683
|
-
out.push({ ...base, kind: "leaf" });
|
|
22684
|
-
}
|
|
22685
23231
|
}
|
|
22686
|
-
return
|
|
22687
|
-
}
|
|
22688
|
-
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
|
|
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())
|
|
22692
23285
|
}
|
|
22693
|
-
}
|
|
22694
|
-
|
|
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;
|
|
22695
23312
|
}
|
|
22696
|
-
function
|
|
22697
|
-
const
|
|
22698
|
-
return
|
|
22699
|
-
|
|
22700
|
-
|
|
22701
|
-
|
|
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;
|
|
22702
23381
|
}
|
|
22703
23382
|
function aabbOfPose(pose, descriptor = AUTO_POSE_DESCRIPTOR) {
|
|
22704
23383
|
return descriptor.getBounds(pose);
|
|
@@ -22731,18 +23410,7 @@ function rotationAboutCenter(pose, descriptor) {
|
|
|
22731
23410
|
// src/canvas/SceneCanvas/useSceneSelectTool.ts
|
|
22732
23411
|
var DEFAULT_PICK_TOLERANCE_PX = 4;
|
|
22733
23412
|
function useSceneSelectTool(args) {
|
|
22734
|
-
const {
|
|
22735
|
-
scene,
|
|
22736
|
-
selection,
|
|
22737
|
-
geometry,
|
|
22738
|
-
selectTool: opts,
|
|
22739
|
-
insertTool,
|
|
22740
|
-
layouts,
|
|
22741
|
-
poseComposition,
|
|
22742
|
-
getView,
|
|
22743
|
-
alphaOf,
|
|
22744
|
-
layerIsPainted
|
|
22745
|
-
} = args;
|
|
23413
|
+
const { scene, adapter, geometry, selectTool: opts, getView, alphaOf, layerIsPainted } = args;
|
|
22746
23414
|
const d = args.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
22747
23415
|
const pickEveryProp = geometry?.pickEvery;
|
|
22748
23416
|
const boundsOfProp = geometry?.boundsOf;
|
|
@@ -22750,62 +23418,6 @@ function useSceneSelectTool(args) {
|
|
|
22750
23418
|
const pickTolerancePx = geometry?.pickTolerancePx ?? DEFAULT_PICK_TOLERANCE_PX;
|
|
22751
23419
|
const moveOptions = opts?.move;
|
|
22752
23420
|
const snap2 = opts?.snap;
|
|
22753
|
-
const commitInsert = insertTool?.create;
|
|
22754
|
-
const insertLayer = insertTool?.layer;
|
|
22755
|
-
const adapter = useMemo(() => {
|
|
22756
|
-
const base = sceneToAdapter(scene, {
|
|
22757
|
-
commitInsert,
|
|
22758
|
-
insertLayer,
|
|
22759
|
-
layouts,
|
|
22760
|
-
poseDescriptor: d,
|
|
22761
|
-
...poseComposition ? { poseComposition } : {}
|
|
22762
|
-
});
|
|
22763
|
-
const framed = poseComposition !== void 0 && poseComposition.closure !== "identity";
|
|
22764
|
-
const collectDescendants = (id, out) => {
|
|
22765
|
-
for (const cid of scene.childrenOf(asNodeId(id))) {
|
|
22766
|
-
out.push(cid);
|
|
22767
|
-
collectDescendants(cid, out);
|
|
22768
|
-
}
|
|
22769
|
-
};
|
|
22770
|
-
return {
|
|
22771
|
-
...base,
|
|
22772
|
-
setPose(id, pose) {
|
|
22773
|
-
const n = scene.get(asNodeId(id));
|
|
22774
|
-
if (framed || !n || n.kind !== "container") {
|
|
22775
|
-
base.setPose(id, pose);
|
|
22776
|
-
return;
|
|
22777
|
-
}
|
|
22778
|
-
const g = poseDescriptorForNode(d, n);
|
|
22779
|
-
const prev = g.getBounds(n.pose);
|
|
22780
|
-
const next = g.getBounds(pose);
|
|
22781
|
-
const dx = next.x - prev.x;
|
|
22782
|
-
const dy = next.y - prev.y;
|
|
22783
|
-
if (dx === 0 && dy === 0) {
|
|
22784
|
-
base.setPose(id, pose);
|
|
22785
|
-
return;
|
|
22786
|
-
}
|
|
22787
|
-
const desc = [];
|
|
22788
|
-
collectDescendants(id, desc);
|
|
22789
|
-
scene.batch("move container", () => {
|
|
22790
|
-
base.setPose(id, pose);
|
|
22791
|
-
for (const cid of desc) {
|
|
22792
|
-
const cn = scene.get(asNodeId(cid));
|
|
22793
|
-
if (!cn) continue;
|
|
22794
|
-
base.setPose(
|
|
22795
|
-
cid,
|
|
22796
|
-
translatePoseViaDescriptor(cn.pose, dx, dy, poseDescriptorForNode(d, cn))
|
|
22797
|
-
);
|
|
22798
|
-
}
|
|
22799
|
-
});
|
|
22800
|
-
},
|
|
22801
|
-
// Selection methods, widened to `string[]` so the synthesized adapter
|
|
22802
|
-
// satisfies `AreaSelectAdapter` (which keeps the kit-internal adapter
|
|
22803
|
-
// contract on plain `string[]`; the brand lives on the public selection
|
|
22804
|
-
// surface, not on adapter shapes).
|
|
22805
|
-
getSelection: () => [...selection.adapterMethods.getSelection()],
|
|
22806
|
-
setSelection: (ids) => selection.adapterMethods.setSelection(ids)
|
|
22807
|
-
};
|
|
22808
|
-
}, [scene, commitInsert, insertLayer, layouts, selection, d, poseComposition]);
|
|
22809
23421
|
const wiredMoveOptions = useMemo(() => {
|
|
22810
23422
|
const merged = { ...moveOptions ?? {} };
|
|
22811
23423
|
if (snap2) {
|
|
@@ -22880,7 +23492,6 @@ function useSceneSelectTool(args) {
|
|
|
22880
23492
|
};
|
|
22881
23493
|
}, [wiredHitBody, adapter]);
|
|
22882
23494
|
return {
|
|
22883
|
-
adapter,
|
|
22884
23495
|
selectTool,
|
|
22885
23496
|
pickEvery: wiredHitBody,
|
|
22886
23497
|
pickBest: wiredPickBest,
|
|
@@ -23337,60 +23948,21 @@ var CLOSE_HINT_RADIUS_PX = 8;
|
|
|
23337
23948
|
function w2s(wx, wy, view) {
|
|
23338
23949
|
return [(wx - view.x) * view.scale.x, (wy - view.y) * view.scale.y];
|
|
23339
23950
|
}
|
|
23340
|
-
function mirror(anchor, out) {
|
|
23341
|
-
if (!out || anchor.altBroken) return void 0;
|
|
23342
|
-
return { x: 2 * anchor.x - out.x, y: 2 * anchor.y - out.y };
|
|
23343
|
-
}
|
|
23344
23951
|
function approximateCircle(cx, cy, r) {
|
|
23345
23952
|
return circlePath(cx, cy, r);
|
|
23346
23953
|
}
|
|
23347
23954
|
function subpathToPath(sp, view) {
|
|
23348
23955
|
if (sp.anchors.length === 0) return null;
|
|
23349
|
-
const
|
|
23350
|
-
|
|
23351
|
-
|
|
23352
|
-
cmds.push(PATH_M);
|
|
23353
|
-
xs.push(sx, sy);
|
|
23354
|
-
for (let i = 1; i < sp.anchors.length; i++) {
|
|
23355
|
-
const prev = sp.anchors[i - 1];
|
|
23356
|
-
const curr = sp.anchors[i];
|
|
23357
|
-
const out = prev.outHandle;
|
|
23358
|
-
const inH = curr.inHandle ?? mirror(prev, out);
|
|
23359
|
-
const [tx, ty] = w2s(curr.x, curr.y, view);
|
|
23360
|
-
if (out || curr.inHandle) {
|
|
23361
|
-
const c1 = out ?? prev;
|
|
23362
|
-
const c2 = inH ?? curr;
|
|
23363
|
-
const [c1x, c1y] = w2s(c1.x, c1.y, view);
|
|
23364
|
-
const [c2x, c2y] = w2s(c2.x, c2.y, view);
|
|
23365
|
-
cmds.push(PATH_C);
|
|
23366
|
-
xs.push(c1x, c1y, c2x, c2y, tx, ty);
|
|
23367
|
-
} else {
|
|
23368
|
-
cmds.push(PATH_L);
|
|
23369
|
-
xs.push(tx, ty);
|
|
23370
|
-
}
|
|
23371
|
-
}
|
|
23372
|
-
if (sp.closed) {
|
|
23373
|
-
const last = sp.anchors[sp.anchors.length - 1];
|
|
23374
|
-
const first = sp.anchors[0];
|
|
23375
|
-
const out = last.outHandle;
|
|
23376
|
-
const inH = first.inHandle ?? mirror(last, out);
|
|
23377
|
-
if (out || first.inHandle) {
|
|
23378
|
-
const c1 = out ?? last;
|
|
23379
|
-
const c2 = inH ?? first;
|
|
23380
|
-
const [c1x, c1y] = w2s(c1.x, c1.y, view);
|
|
23381
|
-
const [c2x, c2y] = w2s(c2.x, c2.y, view);
|
|
23382
|
-
const [tx, ty] = w2s(first.x, first.y, view);
|
|
23383
|
-
cmds.push(PATH_C);
|
|
23384
|
-
xs.push(c1x, c1y, c2x, c2y, tx, ty);
|
|
23385
|
-
}
|
|
23386
|
-
cmds.push(PATH_Z);
|
|
23387
|
-
}
|
|
23388
|
-
return {
|
|
23389
|
-
kind: "polygon",
|
|
23390
|
-
commands: new Uint8Array(cmds),
|
|
23391
|
-
coords: new Float32Array(xs),
|
|
23392
|
-
fillRule: "nonzero"
|
|
23956
|
+
const pt = (p) => {
|
|
23957
|
+
const [x, y] = w2s(p.x, p.y, view);
|
|
23958
|
+
return { x, y };
|
|
23393
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]);
|
|
23394
23966
|
}
|
|
23395
23967
|
function createPenPreviewLayer(opts) {
|
|
23396
23968
|
const style = { ...DEFAULT_STYLE2, ...opts.style ?? {} };
|
|
@@ -23479,10 +24051,10 @@ function createPenPreviewLayer(opts) {
|
|
|
23479
24051
|
path: approximateCircle(hx, hy, HANDLE_DOT_RADIUS_PX),
|
|
23480
24052
|
fill: { fill: "solid", color: style.handleStroke }
|
|
23481
24053
|
});
|
|
23482
|
-
const inSide =
|
|
24054
|
+
const inSide = a.inHandle;
|
|
23483
24055
|
if (inSide) {
|
|
23484
24056
|
const [mx, my] = w2s(inSide.x, inSide.y, view);
|
|
23485
|
-
const
|
|
24057
|
+
const inLine = {
|
|
23486
24058
|
kind: "polygon",
|
|
23487
24059
|
commands: new Uint8Array([PATH_M, PATH_L]),
|
|
23488
24060
|
coords: new Float32Array([ax, ay, mx, my]),
|
|
@@ -23490,7 +24062,7 @@ function createPenPreviewLayer(opts) {
|
|
|
23490
24062
|
};
|
|
23491
24063
|
out.push({
|
|
23492
24064
|
kind: "path",
|
|
23493
|
-
path:
|
|
24065
|
+
path: inLine,
|
|
23494
24066
|
stroke: { paint: { fill: "solid", color: style.handleStroke }, width: 1 }
|
|
23495
24067
|
});
|
|
23496
24068
|
out.push({
|
|
@@ -23706,7 +24278,7 @@ function useBuiltinShapeTools(args) {
|
|
|
23706
24278
|
const { scene, adapter, options } = args;
|
|
23707
24279
|
const seqRef = useRef(0);
|
|
23708
24280
|
const freshId = (prefix) => asNodeId(`${prefix}-${++seqRef.current}`);
|
|
23709
|
-
const nextFill = () =>
|
|
24281
|
+
const nextFill = () => cycleFill(seqRef.current);
|
|
23710
24282
|
const layerOf = () => scene.layers[0]?.id ?? "default";
|
|
23711
24283
|
const makeLeaf = (id, pose, data) => ({
|
|
23712
24284
|
id,
|
|
@@ -23723,6 +24295,15 @@ function useBuiltinShapeTools(args) {
|
|
|
23723
24295
|
const polygon = usePolygonTool();
|
|
23724
24296
|
const star = useStarTool();
|
|
23725
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
|
+
};
|
|
23726
24307
|
const pen = usePenTool({
|
|
23727
24308
|
snapPoint: snapPoint2,
|
|
23728
24309
|
wrapPath: (path, { closed }) => {
|
|
@@ -23731,16 +24312,11 @@ function useBuiltinShapeTools(args) {
|
|
|
23731
24312
|
},
|
|
23732
24313
|
adapter: {
|
|
23733
24314
|
addNode: (carrier) => {
|
|
23734
|
-
const
|
|
23735
|
-
const color = nextFill();
|
|
23736
|
-
const node = makeLeaf(id, carrier.bounds, {
|
|
23737
|
-
path: carrier.path,
|
|
23738
|
-
fill: carrier.closed ? solid(nextFill()) : null,
|
|
23739
|
-
stroke: strokeOf(color, 2)
|
|
23740
|
-
});
|
|
24315
|
+
const node = makePenLeaf(carrier);
|
|
23741
24316
|
adapter.insertNode(node);
|
|
23742
|
-
return String(id);
|
|
24317
|
+
return String(node.id);
|
|
23743
24318
|
},
|
|
24319
|
+
makeNode: (carrier) => makePenLeaf(carrier),
|
|
23744
24320
|
setSelection: (ids) => adapter.setSelection(ids)
|
|
23745
24321
|
}
|
|
23746
24322
|
});
|
|
@@ -24010,6 +24586,19 @@ function useEditAnchorsDepSource(scene, selection, adapter, externalState, optio
|
|
|
24010
24586
|
}
|
|
24011
24587
|
const selectedAnchors = effectiveId ? ext?.getSelectedAnchors() ?? EMPTY_ANCHOR_SELECTION : EMPTY_ANCHOR_SELECTION;
|
|
24012
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
|
+
};
|
|
24013
24602
|
return {
|
|
24014
24603
|
editingId: effectiveId,
|
|
24015
24604
|
setEditingId(id) {
|
|
@@ -24052,24 +24641,10 @@ function useEditAnchorsDepSource(scene, selection, adapter, externalState, optio
|
|
|
24052
24641
|
return { pose: node.pose, data: node.data };
|
|
24053
24642
|
},
|
|
24054
24643
|
applyEdit(id, worldPath, label) {
|
|
24055
|
-
const
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
if (!storage) return;
|
|
24060
|
-
if (storage.kind === "pose") {
|
|
24061
|
-
ad.applyOps([createTransformOp({ id, from: node.pose, to: wp, label })], label);
|
|
24062
|
-
return;
|
|
24063
|
-
}
|
|
24064
|
-
const { pose: newPose, path: aligned } = worldEditToStorage(storage.pose, wp);
|
|
24065
|
-
ad.applyOps(
|
|
24066
|
-
[
|
|
24067
|
-
createTransformOp({ id, from: node.pose, to: newPose, label }),
|
|
24068
|
-
createSetDataOp({ id, from: node.data, to: { ...node.data, path: aligned }, label })
|
|
24069
|
-
],
|
|
24070
|
-
label
|
|
24071
|
-
);
|
|
24072
|
-
}
|
|
24644
|
+
const ops = editOps(id, worldPath, label);
|
|
24645
|
+
if (ops.length > 0) ad.applyOps(ops, label);
|
|
24646
|
+
},
|
|
24647
|
+
editOps
|
|
24073
24648
|
};
|
|
24074
24649
|
});
|
|
24075
24650
|
}
|
|
@@ -24262,7 +24837,7 @@ function useInsertDepSource(scene, adapter, factories) {
|
|
|
24262
24837
|
seq = ++insertSeqRef.current;
|
|
24263
24838
|
id = asNodeId(`kit-${kind}-${seq}`);
|
|
24264
24839
|
}
|
|
24265
|
-
const color =
|
|
24840
|
+
const color = cycleFill(seq);
|
|
24266
24841
|
const fill = solid(color);
|
|
24267
24842
|
const layer = sc.layers[0]?.id ?? "default";
|
|
24268
24843
|
const extent = insertPreviewExtent({ shape: kind, bounds, extras });
|
|
@@ -24358,15 +24933,16 @@ function useDispatcherDepSource(dispatcher) {
|
|
|
24358
24933
|
cancelAll: (reason) => ref.current.cancelAll(reason)
|
|
24359
24934
|
}));
|
|
24360
24935
|
}
|
|
24361
|
-
function useLayoutDepSource(layouts) {
|
|
24362
|
-
const ref = useRef(layouts);
|
|
24363
|
-
ref.current = layouts;
|
|
24936
|
+
function useLayoutDepSource(layouts, dropTarget) {
|
|
24937
|
+
const ref = useRef({ layouts, dropTarget });
|
|
24938
|
+
ref.current = { layouts, dropTarget };
|
|
24364
24939
|
useDepSource("layout", () => ({
|
|
24365
24940
|
getLayout: (containerId) => {
|
|
24366
|
-
const l = ref.current;
|
|
24941
|
+
const l = ref.current.layouts;
|
|
24367
24942
|
if (!l) return null;
|
|
24368
24943
|
return typeof l === "function" ? l(containerId) : l[containerId] ?? null;
|
|
24369
|
-
}
|
|
24944
|
+
},
|
|
24945
|
+
dropTarget: ref.current.dropTarget
|
|
24370
24946
|
}));
|
|
24371
24947
|
}
|
|
24372
24948
|
function useGeometryProjection(projection) {
|
|
@@ -24467,8 +25043,8 @@ var kitImageHandler = {
|
|
|
24467
25043
|
|
|
24468
25044
|
// src/features/ingestion/svgHandler.ts
|
|
24469
25045
|
var SVG_MIME = "image/svg+xml";
|
|
24470
|
-
var
|
|
24471
|
-
var
|
|
25046
|
+
var FALLBACK_WIDTH2 = 300;
|
|
25047
|
+
var FALLBACK_HEIGHT2 = 150;
|
|
24472
25048
|
function isSvgFileItem(item) {
|
|
24473
25049
|
if (item.kind !== "file") return false;
|
|
24474
25050
|
if (item.mime === SVG_MIME) return true;
|
|
@@ -24506,8 +25082,8 @@ var defaultMeasure2 = async (file) => {
|
|
|
24506
25082
|
img.src = url;
|
|
24507
25083
|
await img.decode();
|
|
24508
25084
|
return {
|
|
24509
|
-
width: img.naturalWidth ||
|
|
24510
|
-
height: img.naturalHeight ||
|
|
25085
|
+
width: img.naturalWidth || FALLBACK_WIDTH2,
|
|
25086
|
+
height: img.naturalHeight || FALLBACK_HEIGHT2
|
|
24511
25087
|
};
|
|
24512
25088
|
} finally {
|
|
24513
25089
|
URL.revokeObjectURL(url);
|
|
@@ -25051,6 +25627,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
25051
25627
|
insertNodeFactories,
|
|
25052
25628
|
ingestion,
|
|
25053
25629
|
layouts,
|
|
25630
|
+
layoutDropTarget,
|
|
25054
25631
|
poseComposition,
|
|
25055
25632
|
geometryProjection,
|
|
25056
25633
|
routing,
|
|
@@ -25182,6 +25759,12 @@ function SceneCanvasInner(props, ref) {
|
|
|
25182
25759
|
}, [scene, selectionOptions, selectionMode]);
|
|
25183
25760
|
const internalSelection = useSelection(derivedSelectionOptions);
|
|
25184
25761
|
const baseSelection = selectionProp ?? internalSelection;
|
|
25762
|
+
const baseAdapterMethods = baseSelection.adapterMethods;
|
|
25763
|
+
const readOnlyAdapterMethods = useMemo(() => ({
|
|
25764
|
+
getSelection: () => baseAdapterMethods.getSelection(),
|
|
25765
|
+
setSelection: () => {
|
|
25766
|
+
}
|
|
25767
|
+
}), [baseAdapterMethods]);
|
|
25185
25768
|
const selection = useMemo(() => {
|
|
25186
25769
|
if (selectionMode !== "none") return baseSelection;
|
|
25187
25770
|
const noopSet = () => {
|
|
@@ -25193,9 +25776,10 @@ function SceneCanvasInner(props, ref) {
|
|
|
25193
25776
|
remove: noopSet,
|
|
25194
25777
|
toggle: noopSet,
|
|
25195
25778
|
clear: noopSet,
|
|
25196
|
-
applyClick: noopSet
|
|
25779
|
+
applyClick: noopSet,
|
|
25780
|
+
adapterMethods: readOnlyAdapterMethods
|
|
25197
25781
|
};
|
|
25198
|
-
}, [baseSelection, selectionMode]);
|
|
25782
|
+
}, [baseSelection, selectionMode, readOnlyAdapterMethods]);
|
|
25199
25783
|
const currentSelection = selection.current;
|
|
25200
25784
|
const selectionKinds = useMemo(() => {
|
|
25201
25785
|
if (currentSelection.length === 0) return void 0;
|
|
@@ -25287,9 +25871,20 @@ function SceneCanvasInner(props, ref) {
|
|
|
25287
25871
|
const [sceneLayerGate] = useState(createSceneLayerGate);
|
|
25288
25872
|
const layerIsPainted = useCallback((layerId) => sceneLayerGate(surfaceViewRegistry?.surface()?.layers() ?? [], layerPaintRef.current)(layerId), [surfaceViewRegistry, sceneLayerGate]);
|
|
25289
25873
|
const viewLayerGate = layerVisibility !== void 0 || layerOrder !== void 0 ? layerIsPainted : void 0;
|
|
25290
|
-
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({
|
|
25291
25886
|
scene,
|
|
25292
|
-
|
|
25887
|
+
adapter,
|
|
25293
25888
|
poseDescriptor: descriptor,
|
|
25294
25889
|
geometry,
|
|
25295
25890
|
// The pick tolerance is declared in screen pixels; this is what converts it.
|
|
@@ -25299,10 +25894,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
25299
25894
|
// that once per walk instead of once per candidate.
|
|
25300
25895
|
...alphaFor ? { alphaOf: composedAlphaFor } : {},
|
|
25301
25896
|
...viewLayerGate ? { layerIsPainted: viewLayerGate } : {},
|
|
25302
|
-
selectTool: selectToolWithDefaults
|
|
25303
|
-
...insertTool ? { insertTool } : {},
|
|
25304
|
-
...layouts ? { layouts } : {},
|
|
25305
|
-
...poseComposition ? { poseComposition } : {}
|
|
25897
|
+
selectTool: selectToolWithDefaults
|
|
25306
25898
|
});
|
|
25307
25899
|
const getNodeAtPoint = useMemo(() => {
|
|
25308
25900
|
if (!internalPickEvery) return void 0;
|
|
@@ -25814,6 +26406,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
25814
26406
|
editAnchorsExternalState,
|
|
25815
26407
|
anchorEditingAllowed,
|
|
25816
26408
|
layouts,
|
|
26409
|
+
layoutDropTarget,
|
|
25817
26410
|
poseComposition,
|
|
25818
26411
|
insertNodeFactories,
|
|
25819
26412
|
snapPoint: toolOptions?.snapPoint,
|
|
@@ -26064,6 +26657,7 @@ function StandardActionsRegistrar({
|
|
|
26064
26657
|
anchorEditingAllowed,
|
|
26065
26658
|
poseComposition,
|
|
26066
26659
|
layouts,
|
|
26660
|
+
layoutDropTarget,
|
|
26067
26661
|
insertNodeFactories,
|
|
26068
26662
|
snapPoint: snapPoint2,
|
|
26069
26663
|
canvasRef,
|
|
@@ -26112,7 +26706,7 @@ function StandardActionsRegistrar({
|
|
|
26112
26706
|
useViewportActions({ pan: viewportPanEnabled, zoom: viewportZoom, pinchZoom: viewportPinchZoom });
|
|
26113
26707
|
useAreaSelectDepSource(scene, selection, poseDescriptor, poseComposition, alphaOf);
|
|
26114
26708
|
useNodeAtPointDepSource(pickEvery);
|
|
26115
|
-
useLayoutDepSource(layouts);
|
|
26709
|
+
useLayoutDepSource(layouts, layoutDropTarget);
|
|
26116
26710
|
useInsertDepSource(scene, adapter, insertNodeFactories);
|
|
26117
26711
|
useSnapDepSource(snapPoint2);
|
|
26118
26712
|
useIngestionDepSource(canvasRef, () => currentViewRef.current, ingestionResolveSrc, ingestionSvg, ingestionClipboard);
|
|
@@ -26305,7 +26899,8 @@ function shapeSchema(opts = {}) {
|
|
|
26305
26899
|
letterSpacing: { kind: "number", name: "Tracking", description: "Extra advance per glyph, world units.", default: 0, step: 0.1 },
|
|
26306
26900
|
underline: { kind: "boolean", name: "Underline", description: "Underline the text.", default: false, control: "toggle", short: "U", pair: "Decoration" },
|
|
26307
26901
|
strikethrough: { kind: "boolean", name: "Strikethrough", description: "Strike through the text.", default: false, control: "toggle", short: "S", pair: "Decoration" },
|
|
26308
|
-
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" }] }
|
|
26309
26904
|
}
|
|
26310
26905
|
},
|
|
26311
26906
|
paragraph: {
|
|
@@ -27332,6 +27927,20 @@ function createTextLayer(opts) {
|
|
|
27332
27927
|
}
|
|
27333
27928
|
};
|
|
27334
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
|
+
}
|
|
27335
27944
|
function pointInTextPose(x, y, pose, opts = {}) {
|
|
27336
27945
|
const p = opts.padding ?? 0;
|
|
27337
27946
|
return x >= pose.x - p && x <= pose.x + pose.width + p && y >= pose.y - p && y <= pose.y + pose.height + p;
|
|
@@ -27350,8 +27959,8 @@ function caretIndexAt(x, y, pose) {
|
|
|
27350
27959
|
break;
|
|
27351
27960
|
}
|
|
27352
27961
|
}
|
|
27353
|
-
|
|
27354
|
-
|
|
27962
|
+
if (line.cells.length === 0) return line.srcEnd;
|
|
27963
|
+
const cells = sourceClusters(line.cells);
|
|
27355
27964
|
const visual = cells.map((_, i) => i).sort((a, b) => cells[a].x - cells[b].x);
|
|
27356
27965
|
for (const i of visual) {
|
|
27357
27966
|
const c = cells[i];
|
|
@@ -27384,6 +27993,7 @@ function solidColor(p) {
|
|
|
27384
27993
|
if ("color" in p) return p.color;
|
|
27385
27994
|
return null;
|
|
27386
27995
|
}
|
|
27996
|
+
var TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
27387
27997
|
var EMPTY_STYLE = {
|
|
27388
27998
|
bold: false,
|
|
27389
27999
|
italic: false,
|
|
@@ -27439,6 +28049,8 @@ function styleStateFromElement(el, parent) {
|
|
|
27439
28049
|
const shift = parseFloat(rawShift);
|
|
27440
28050
|
if (Number.isFinite(shift)) next.baselineShift = shift;
|
|
27441
28051
|
}
|
|
28052
|
+
const tt = el.style.textTransform;
|
|
28053
|
+
if (TRANSFORMS.has(tt)) next.textTransform = tt;
|
|
27442
28054
|
if (el.style.fontFamily) next.fontFamily = el.style.fontFamily;
|
|
27443
28055
|
if (el.style.color) next.color = el.style.color;
|
|
27444
28056
|
const ls = el.style.letterSpacing;
|
|
@@ -27452,7 +28064,7 @@ function styleStateFromElement(el, parent) {
|
|
|
27452
28064
|
return next;
|
|
27453
28065
|
}
|
|
27454
28066
|
function styleEquals(a, b) {
|
|
27455
|
-
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;
|
|
27456
28068
|
}
|
|
27457
28069
|
function toRun(text, style) {
|
|
27458
28070
|
const run2 = { text };
|
|
@@ -27468,6 +28080,7 @@ function toRun(text, style) {
|
|
|
27468
28080
|
if (style.script != null) run2.script = style.script;
|
|
27469
28081
|
if (style.baselineShift != null) run2.baselineShift = style.baselineShift;
|
|
27470
28082
|
if (style.fontScale != null) run2.fontScale = style.fontScale;
|
|
28083
|
+
if (style.textTransform != null) run2.textTransform = style.textTransform;
|
|
27471
28084
|
return run2;
|
|
27472
28085
|
}
|
|
27473
28086
|
function domToRuns(parent) {
|
|
@@ -27531,6 +28144,7 @@ function runsToDom(runs, parent) {
|
|
|
27531
28144
|
span.setAttribute("data-baseline-shift", String(run2.baselineShift));
|
|
27532
28145
|
}
|
|
27533
28146
|
if (run2.letterSpacing != null) span.style.letterSpacing = `${run2.letterSpacing}px`;
|
|
28147
|
+
if (run2.textTransform != null) span.style.textTransform = run2.textTransform;
|
|
27534
28148
|
parent.appendChild(span);
|
|
27535
28149
|
}
|
|
27536
28150
|
}
|
|
@@ -27590,7 +28204,8 @@ var STYLE_KEYS = [
|
|
|
27590
28204
|
"stroke",
|
|
27591
28205
|
"script",
|
|
27592
28206
|
"baselineShift",
|
|
27593
|
-
"fontScale"
|
|
28207
|
+
"fontScale",
|
|
28208
|
+
"textTransform"
|
|
27594
28209
|
];
|
|
27595
28210
|
var FLAG_KEYS = /* @__PURE__ */ new Set([
|
|
27596
28211
|
"bold",
|
|
@@ -28231,6 +28846,7 @@ function applyOverlayStyle(el, style) {
|
|
|
28231
28846
|
if (style.underline) decorations.push("underline");
|
|
28232
28847
|
if (style.strikethrough) decorations.push("line-through");
|
|
28233
28848
|
el.style.textDecoration = decorations.length > 0 ? decorations.join(" ") : "none";
|
|
28849
|
+
el.style.textTransform = style.textTransform;
|
|
28234
28850
|
el.style.whiteSpace = style.wrap ? "pre-wrap" : "pre";
|
|
28235
28851
|
el.style.overflowWrap = "normal";
|
|
28236
28852
|
el.style.wordBreak = "normal";
|
|
@@ -28432,7 +29048,8 @@ var OVERRIDES = [
|
|
|
28432
29048
|
"fill",
|
|
28433
29049
|
"script",
|
|
28434
29050
|
"baselineShift",
|
|
28435
|
-
"fontScale"
|
|
29051
|
+
"fontScale",
|
|
29052
|
+
"textTransform"
|
|
28436
29053
|
];
|
|
28437
29054
|
function effectiveRangeStyle(range, style, paint) {
|
|
28438
29055
|
const resolved = resolveTextStyle(style, paint);
|
|
@@ -28440,7 +29057,8 @@ function effectiveRangeStyle(range, style, paint) {
|
|
|
28440
29057
|
const out = {
|
|
28441
29058
|
fontFamily: resolved.fontFamily,
|
|
28442
29059
|
fontSize: resolved.fontSize,
|
|
28443
|
-
letterSpacing: resolved.letterSpacing
|
|
29060
|
+
letterSpacing: resolved.letterSpacing,
|
|
29061
|
+
textTransform: resolved.textTransform
|
|
28444
29062
|
};
|
|
28445
29063
|
if (resolved.fill !== null) out.fill = resolved.fill;
|
|
28446
29064
|
for (const key of FLAGS) {
|
|
@@ -31560,6 +32178,6 @@ function snapPoint(opts) {
|
|
|
31560
32178
|
};
|
|
31561
32179
|
}
|
|
31562
32180
|
|
|
31563
|
-
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 };
|
|
31564
|
-
//# sourceMappingURL=chunk-
|
|
31565
|
-
//# 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
|