@hankchen/canvas-editor 0.4.0 → 0.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/dist/index.d.ts +447 -152
- package/dist/index.js +415 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -382,11 +382,22 @@ function resolveSnap(box, axes, threshold) {
|
|
|
382
382
|
|
|
383
383
|
// src/spatial/create.ts
|
|
384
384
|
var CREATE_DEAD_ZONE_SCREEN_PX = 4;
|
|
385
|
-
function createRubberBandRect(type, start, current, axes, threshold) {
|
|
385
|
+
function createRubberBandRect(type, start, current, axes, threshold, ratio) {
|
|
386
386
|
const x = Math.min(start.x, current.x);
|
|
387
387
|
const y = Math.min(start.y, current.y);
|
|
388
|
-
const
|
|
389
|
-
const
|
|
388
|
+
const dx = Math.abs(current.x - start.x);
|
|
389
|
+
const dy = Math.abs(current.y - start.y);
|
|
390
|
+
let width = dx;
|
|
391
|
+
let height = dy;
|
|
392
|
+
if (type === "QrCodeLayer") {
|
|
393
|
+
height = width;
|
|
394
|
+
} else if (ratio) {
|
|
395
|
+
if (dx >= dy) {
|
|
396
|
+
height = Math.round(dx * ratio.h / ratio.w);
|
|
397
|
+
} else {
|
|
398
|
+
width = Math.round(dy * ratio.w / ratio.h);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
390
401
|
const resolution = resolveSnapPoints(
|
|
391
402
|
{ vertical: [x, x + width], horizontal: [y, y + height] },
|
|
392
403
|
axes,
|
|
@@ -435,6 +446,38 @@ function resizeSnapPoints(start, handle, dx, dy) {
|
|
|
435
446
|
if (SOUTH_HANDLES.has(handle)) horizontal.push(start.y + start.height + dy);
|
|
436
447
|
return { vertical, horizontal };
|
|
437
448
|
}
|
|
449
|
+
function proportionalDrivingAxis(handle, dx, dy) {
|
|
450
|
+
const corner = handleResizesWidth(handle) && handleResizesHeight(handle);
|
|
451
|
+
if (corner) return Math.abs(dx) >= Math.abs(dy) ? "width" : "height";
|
|
452
|
+
return handleResizesWidth(handle) ? "width" : "height";
|
|
453
|
+
}
|
|
454
|
+
function resizeBoxProportional(start, handle, reference, driving, dx, dy, minSize = RESIZE_MIN_SIZE_PX) {
|
|
455
|
+
const west = WEST_HANDLES.has(handle);
|
|
456
|
+
const north = NORTH_HANDLES.has(handle);
|
|
457
|
+
let width;
|
|
458
|
+
let height;
|
|
459
|
+
if (driving === "width") {
|
|
460
|
+
width = Math.max(west ? start.width - dx : start.width + dx, minSize);
|
|
461
|
+
height = Math.max(Math.round(width * reference.h / reference.w), minSize);
|
|
462
|
+
} else {
|
|
463
|
+
height = Math.max(north ? start.height - dy : start.height + dy, minSize);
|
|
464
|
+
width = Math.max(Math.round(height * reference.w / reference.h), minSize);
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
x: west ? start.x + start.width - width : start.x,
|
|
468
|
+
y: north ? start.y + start.height - height : start.y,
|
|
469
|
+
width,
|
|
470
|
+
height
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function resizeProportionalSnapPoints(start, handle, driving, dx, dy) {
|
|
474
|
+
if (driving === "width") {
|
|
475
|
+
const vertical = WEST_HANDLES.has(handle) ? [start.x + dx] : EAST_HANDLES.has(handle) ? [start.x + start.width + dx] : [];
|
|
476
|
+
return { vertical, horizontal: [] };
|
|
477
|
+
}
|
|
478
|
+
const horizontal = NORTH_HANDLES.has(handle) ? [start.y + dy] : SOUTH_HANDLES.has(handle) ? [start.y + start.height + dy] : [];
|
|
479
|
+
return { vertical: [], horizontal };
|
|
480
|
+
}
|
|
438
481
|
var NO_AXES = { width: false, height: false };
|
|
439
482
|
function resizableAxesAt(doc, path) {
|
|
440
483
|
const layer = resolveLayer(doc, path);
|
|
@@ -602,7 +645,6 @@ function compileRefNode(raw, rawRef, declPath, ctx) {
|
|
|
602
645
|
}
|
|
603
646
|
function compileInlineNode(raw, declPath, ctx) {
|
|
604
647
|
const node = annotationsOf(raw);
|
|
605
|
-
if (raw["additionalProperties"] === true) node.open = true;
|
|
606
648
|
const rawProperties = raw["properties"];
|
|
607
649
|
if (isPlainObject(rawProperties)) {
|
|
608
650
|
const properties = /* @__PURE__ */ new Map();
|
|
@@ -616,6 +658,23 @@ function compileInlineNode(raw, declPath, ctx) {
|
|
|
616
658
|
if (isPlainObject(rawItems)) node.items = compileSchemaNode(rawItems, `${declPath}/items`, ctx);
|
|
617
659
|
return node;
|
|
618
660
|
}
|
|
661
|
+
var OPEN_MAP_DETAIL_SUFFIX = "\u2014\u2014\u5F00\u653E\u6620\u5C04\u5951\u7EA6\u5DF2\u5E9F\u9664\uFF0C\u52A8\u6001\u952E\u8BF7\u7531\u5BBF\u4E3B\u7EC4\u88C5\u6210\u5177\u4F53 properties \u540E\u6CE8\u5165";
|
|
662
|
+
function findOpenMapping(raw, path) {
|
|
663
|
+
if (Array.isArray(raw)) {
|
|
664
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
665
|
+
const hit = findOpenMapping(raw[i], `${path}/${i}`);
|
|
666
|
+
if (hit !== null) return hit;
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
if (!isPlainObject(raw)) return null;
|
|
671
|
+
if (raw["additionalProperties"] === true) return path;
|
|
672
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
673
|
+
const hit = findOpenMapping(value, `${path}/${pointerEscape(key)}`);
|
|
674
|
+
if (hit !== null) return hit;
|
|
675
|
+
}
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
619
678
|
function parseExpressionSchema(raw) {
|
|
620
679
|
if (!isPlainObject(raw)) {
|
|
621
680
|
return { ok: false, reason: "invalid_schema", detail: "schema \u6839\u5FC5\u987B\u662F\u5BF9\u8C61\u58F0\u660E" };
|
|
@@ -631,6 +690,10 @@ function parseExpressionSchema(raw) {
|
|
|
631
690
|
if (!isPlainObject(rawProperties)) {
|
|
632
691
|
return { ok: false, reason: "invalid_schema", detail: "\u6839\u58F0\u660E\u7F3A properties \u5C42\u7EA7\uFF08schema \u5373\u8F7D\u8377\u5F62\u72B6\uFF0C\u5FC5\u987B\u58F0\u660E\u9876\u5C42\u952E\uFF09" };
|
|
633
692
|
}
|
|
693
|
+
const openPath = findOpenMapping(raw, "#");
|
|
694
|
+
if (openPath !== null) {
|
|
695
|
+
return { ok: false, reason: "open_map_unsupported", detail: `"${openPath}" \u58F0\u660E\u4E86 additionalProperties: true${OPEN_MAP_DETAIL_SUFFIX}` };
|
|
696
|
+
}
|
|
634
697
|
const diagnostics = [];
|
|
635
698
|
const schema = compileSchemaNode(raw, "#", { root: raw, diagnostics, activePath: /* @__PURE__ */ new Set() });
|
|
636
699
|
return { ok: true, schema, diagnostics };
|
|
@@ -672,7 +735,7 @@ function schemaNodeAtPath(node, segments) {
|
|
|
672
735
|
function schemaChildEntries(node) {
|
|
673
736
|
if (node.type === "array" || node.items !== void 0) return { entries: [] };
|
|
674
737
|
const entries = node.properties !== void 0 ? [...node.properties].map(([key, child]) => ({ key, node: child })) : [];
|
|
675
|
-
return
|
|
738
|
+
return { entries };
|
|
676
739
|
}
|
|
677
740
|
function resolveRowSchema(root, rowsPath) {
|
|
678
741
|
const segments = rowsPath.split(".");
|
|
@@ -712,6 +775,9 @@ function skipWs(expr, pos) {
|
|
|
712
775
|
while (pos < expr.length && WS_CHARS.includes(expr[pos])) pos += 1;
|
|
713
776
|
return pos;
|
|
714
777
|
}
|
|
778
|
+
function fail(kind, start, end) {
|
|
779
|
+
return { ok: false, error: "expression_syntax_error", kind, at: { start, end } };
|
|
780
|
+
}
|
|
715
781
|
function isCallFragment(expr) {
|
|
716
782
|
let head = 0;
|
|
717
783
|
while (head < expr.length && !HEAD_STOP_CHARS.includes(expr[head])) head += 1;
|
|
@@ -723,26 +789,30 @@ function isCallFragment(expr) {
|
|
|
723
789
|
function parseExpressionCall(expr) {
|
|
724
790
|
let head = 0;
|
|
725
791
|
while (head < expr.length && !HEAD_STOP_CHARS.includes(expr[head])) head += 1;
|
|
726
|
-
if (head === 0) return
|
|
792
|
+
if (head === 0) return fail("invalid_path", 0, expr.length);
|
|
727
793
|
const name = expr.slice(0, head);
|
|
728
794
|
let pos = skipWs(expr, head);
|
|
729
|
-
if (pos >= expr.length || expr[pos] !== "(") return
|
|
795
|
+
if (pos >= expr.length || expr[pos] !== "(") return fail("missing_open_paren", head, expr.length);
|
|
796
|
+
const openParen = pos;
|
|
730
797
|
pos = skipWs(expr, pos + 1);
|
|
731
798
|
const args = [];
|
|
799
|
+
const spans = [];
|
|
732
800
|
if (pos < expr.length && expr[pos] === ")") {
|
|
733
801
|
pos += 1;
|
|
734
802
|
} else {
|
|
735
803
|
for (; ; ) {
|
|
736
|
-
if (pos >= expr.length) return
|
|
804
|
+
if (pos >= expr.length) return fail("unbalanced_paren", openParen, expr.length);
|
|
737
805
|
const arg = parseCallArg(expr, pos);
|
|
738
806
|
if (!arg.ok) return arg;
|
|
739
807
|
args.push(arg.value);
|
|
808
|
+
spans.push({ start: arg.start, end: arg.end });
|
|
740
809
|
pos = skipWs(expr, arg.end);
|
|
741
|
-
if (pos >= expr.length) return
|
|
810
|
+
if (pos >= expr.length) return fail("unbalanced_paren", openParen, expr.length);
|
|
742
811
|
if (expr[pos] === ",") {
|
|
812
|
+
const comma = pos;
|
|
743
813
|
pos = skipWs(expr, pos + 1);
|
|
744
814
|
if (pos < expr.length && expr[pos] === ")") {
|
|
745
|
-
return
|
|
815
|
+
return fail("empty_arg_slot", comma + 1, pos);
|
|
746
816
|
}
|
|
747
817
|
continue;
|
|
748
818
|
}
|
|
@@ -750,26 +820,35 @@ function parseExpressionCall(expr) {
|
|
|
750
820
|
pos += 1;
|
|
751
821
|
break;
|
|
752
822
|
}
|
|
753
|
-
return
|
|
823
|
+
return fail("bad_arg_syntax", pos, pos + 1);
|
|
754
824
|
}
|
|
755
825
|
}
|
|
756
|
-
|
|
757
|
-
if (
|
|
758
|
-
|
|
826
|
+
pos = skipWs(expr, pos);
|
|
827
|
+
if (pos !== expr.length) return fail("trailing_after_call", pos, expr.length);
|
|
828
|
+
if (args[0] !== void 0 && args[0].type !== "path") {
|
|
829
|
+
const span = spans[0];
|
|
830
|
+
return fail("first_arg_not_path", span.start, span.end);
|
|
831
|
+
}
|
|
832
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
833
|
+
const arg = args[i];
|
|
834
|
+
const span = spans[i];
|
|
759
835
|
if (arg.type === "path" && !parseExpressionPath(arg.value).ok) {
|
|
760
|
-
return
|
|
836
|
+
return fail("invalid_path_arg", span.start, span.end);
|
|
761
837
|
}
|
|
762
838
|
}
|
|
763
839
|
return { ok: true, name, args };
|
|
764
840
|
}
|
|
765
841
|
function parseCallArg(expr, pos) {
|
|
766
842
|
const len = expr.length;
|
|
843
|
+
const start = pos;
|
|
767
844
|
const ch = expr[pos];
|
|
768
845
|
if (ch === "'") {
|
|
769
846
|
let cursor = pos + 1;
|
|
770
847
|
let buf = "";
|
|
771
848
|
for (; ; ) {
|
|
772
|
-
if (cursor >= len)
|
|
849
|
+
if (cursor >= len) {
|
|
850
|
+
return { ok: false, error: "expression_syntax_error", kind: "unterminated_string", at: { start, end: len } };
|
|
851
|
+
}
|
|
773
852
|
if (expr[cursor] === "'") {
|
|
774
853
|
if (cursor + 1 < len && expr[cursor + 1] === "'") {
|
|
775
854
|
buf += "'";
|
|
@@ -782,10 +861,9 @@ function parseCallArg(expr, pos) {
|
|
|
782
861
|
buf += expr[cursor];
|
|
783
862
|
cursor += 1;
|
|
784
863
|
}
|
|
785
|
-
return { ok: true, value: { type: "string", value: buf }, end: cursor };
|
|
864
|
+
return { ok: true, value: { type: "string", value: buf }, start, end: cursor };
|
|
786
865
|
}
|
|
787
866
|
if (ch >= "0" && ch <= "9") {
|
|
788
|
-
const start2 = pos;
|
|
789
867
|
const isDigitAt = (index) => {
|
|
790
868
|
const c = expr[index];
|
|
791
869
|
return c !== void 0 && c >= "0" && c <= "9";
|
|
@@ -795,13 +873,14 @@ function parseCallArg(expr, pos) {
|
|
|
795
873
|
pos += 1;
|
|
796
874
|
while (isDigitAt(pos)) pos += 1;
|
|
797
875
|
}
|
|
798
|
-
return { ok: true, value: { type: "number", value: expr.slice(
|
|
876
|
+
return { ok: true, value: { type: "number", value: expr.slice(start, pos) }, start, end: pos };
|
|
799
877
|
}
|
|
800
|
-
const start = pos;
|
|
801
878
|
while (pos < len && expr[pos] !== "," && expr[pos] !== ")") pos += 1;
|
|
802
879
|
const raw = phpTrim(expr.slice(start, pos));
|
|
803
|
-
if (raw === "")
|
|
804
|
-
|
|
880
|
+
if (raw === "") {
|
|
881
|
+
return { ok: false, error: "expression_syntax_error", kind: "empty_arg_slot", at: { start, end: pos } };
|
|
882
|
+
}
|
|
883
|
+
return { ok: true, value: { type: "path", value: raw }, start, end: pos };
|
|
805
884
|
}
|
|
806
885
|
function expressionCallSlotAtEnd(expr) {
|
|
807
886
|
let head = 0;
|
|
@@ -854,12 +933,20 @@ function phpTrim2(value) {
|
|
|
854
933
|
while (end > start && PHP_TRIM_CHARS.includes(value[end - 1])) end -= 1;
|
|
855
934
|
return value.slice(start, end);
|
|
856
935
|
}
|
|
857
|
-
function
|
|
858
|
-
if (expr === "") return "
|
|
936
|
+
function fragmentDiagnostic(expr) {
|
|
937
|
+
if (expr === "") return { kind: "empty_fragment", at: { start: 0, end: 0 } };
|
|
859
938
|
if (isCallFragment(expr)) {
|
|
860
|
-
|
|
939
|
+
const parsed = parseExpressionCall(expr);
|
|
940
|
+
if (parsed.ok) return null;
|
|
941
|
+
return { kind: parsed.kind, at: parsed.at };
|
|
861
942
|
}
|
|
862
|
-
|
|
943
|
+
if (parseExpressionPath(expr).ok) return null;
|
|
944
|
+
return { kind: "invalid_path", at: { start: 0, end: expr.length } };
|
|
945
|
+
}
|
|
946
|
+
function leadingPhpTrim(value) {
|
|
947
|
+
let start = 0;
|
|
948
|
+
while (start < value.length && PHP_TRIM_CHARS.includes(value[start])) start += 1;
|
|
949
|
+
return start;
|
|
863
950
|
}
|
|
864
951
|
function scanExpressionFragments(template) {
|
|
865
952
|
const parts = [];
|
|
@@ -880,8 +967,26 @@ function scanExpressionFragments(template) {
|
|
|
880
967
|
}
|
|
881
968
|
const raw = template.slice(open + 2, close);
|
|
882
969
|
const expr = phpTrim2(raw);
|
|
883
|
-
|
|
884
|
-
|
|
970
|
+
let diagnostic = null;
|
|
971
|
+
const found = fragmentDiagnostic(expr);
|
|
972
|
+
if (found) {
|
|
973
|
+
const exprStart = open + 2 + leadingPhpTrim(raw);
|
|
974
|
+
diagnostic = {
|
|
975
|
+
kind: found.kind,
|
|
976
|
+
start: open,
|
|
977
|
+
end: close + 2,
|
|
978
|
+
at: { start: exprStart + found.at.start, end: exprStart + found.at.end }
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
parts.push({
|
|
982
|
+
kind: "fragment",
|
|
983
|
+
start: open,
|
|
984
|
+
end: close + 2,
|
|
985
|
+
raw,
|
|
986
|
+
expr,
|
|
987
|
+
error: diagnostic !== null ? "expression_syntax_error" : null,
|
|
988
|
+
diagnostic
|
|
989
|
+
});
|
|
885
990
|
pos = close + 2;
|
|
886
991
|
open = template.indexOf("{{", pos);
|
|
887
992
|
}
|
|
@@ -1113,6 +1218,27 @@ function defaultNextTextStyle() {
|
|
|
1113
1218
|
lineHeight: base.shape.lineHeight
|
|
1114
1219
|
};
|
|
1115
1220
|
}
|
|
1221
|
+
var RATIO_PRESETS = [
|
|
1222
|
+
{ id: "free", ratio: null },
|
|
1223
|
+
{ id: "1:1", ratio: { w: 1, h: 1 } },
|
|
1224
|
+
{ id: "4:3", ratio: { w: 4, h: 3 } },
|
|
1225
|
+
{ id: "3:4", ratio: { w: 3, h: 4 } },
|
|
1226
|
+
{ id: "16:9", ratio: { w: 16, h: 9 } },
|
|
1227
|
+
{ id: "9:16", ratio: { w: 9, h: 16 } }
|
|
1228
|
+
];
|
|
1229
|
+
function defaultNextLayerRatio() {
|
|
1230
|
+
return "free";
|
|
1231
|
+
}
|
|
1232
|
+
function ratioValue(id) {
|
|
1233
|
+
const entry = RATIO_PRESETS.find((candidate) => candidate.id === id);
|
|
1234
|
+
return entry?.ratio ? { ...entry.ratio } : null;
|
|
1235
|
+
}
|
|
1236
|
+
function pickNextLayerRatio(raw) {
|
|
1237
|
+
return RATIO_PRESETS.some((candidate) => candidate.id === raw) ? raw : "free";
|
|
1238
|
+
}
|
|
1239
|
+
function defaultSizeForRatio(ratio) {
|
|
1240
|
+
return ratio.w >= ratio.h ? { width: 200, height: Math.round(200 * ratio.h / ratio.w) } : { width: Math.round(200 * ratio.w / ratio.h), height: 200 };
|
|
1241
|
+
}
|
|
1116
1242
|
function pickNextTextStylePatch(raw) {
|
|
1117
1243
|
const out = {};
|
|
1118
1244
|
if (raw === null || typeof raw !== "object") return out;
|
|
@@ -1181,7 +1307,8 @@ function createDefaultLayer(type, textDefaults) {
|
|
|
1181
1307
|
}
|
|
1182
1308
|
}
|
|
1183
1309
|
function createTemplateTable(options) {
|
|
1184
|
-
const
|
|
1310
|
+
const defaultTable = createDefaultLayer("TableLayer");
|
|
1311
|
+
const table = options.shape ? { ...defaultTable, shape: { ...defaultTable.shape, width: options.shape.width, height: options.shape.height } } : defaultTable;
|
|
1185
1312
|
const templateBase = createDefaultLayer("TableRowTemplate");
|
|
1186
1313
|
const cellBase = createDefaultLayer("TableCellLayer");
|
|
1187
1314
|
const contentBase = createDefaultLayer("TextLayer");
|
|
@@ -1614,11 +1741,13 @@ var EditorStore = class {
|
|
|
1614
1741
|
drag: null,
|
|
1615
1742
|
resize: null,
|
|
1616
1743
|
armedCreate: null,
|
|
1744
|
+
armedCreateRowsPath: "",
|
|
1617
1745
|
create: null,
|
|
1618
1746
|
editing: null,
|
|
1619
1747
|
renaming: null,
|
|
1620
1748
|
anchorExpanded: false,
|
|
1621
1749
|
nextTextStyle: defaultNextTextStyle(),
|
|
1750
|
+
nextLayerRatio: defaultNextLayerRatio(),
|
|
1622
1751
|
dataSourceSchema: null,
|
|
1623
1752
|
guides: [],
|
|
1624
1753
|
rulersVisible: true,
|
|
@@ -1650,7 +1779,7 @@ var EditorStore = class {
|
|
|
1650
1779
|
}
|
|
1651
1780
|
/** 打开/替换文档:ui 选择/编辑/重命名会话与双向历史一并重置(新文档不继承旧路径/旧事务);
|
|
1652
1781
|
* 参考线/锁定集合/查找会话/建层武装与画拉会话随当次会话清空(对位轴/文档内容防护/
|
|
1653
|
-
*
|
|
1782
|
+
* 查询词/待命层型不跨文档),标尺显隐/锚点折叠/新层样式/建层比例作为偏好保留 */
|
|
1654
1783
|
openDocument(canvas) {
|
|
1655
1784
|
this.docValue = canvas;
|
|
1656
1785
|
this.undoSteps = [];
|
|
@@ -1662,6 +1791,7 @@ var EditorStore = class {
|
|
|
1662
1791
|
drag: null,
|
|
1663
1792
|
resize: null,
|
|
1664
1793
|
armedCreate: null,
|
|
1794
|
+
armedCreateRowsPath: "",
|
|
1665
1795
|
create: null,
|
|
1666
1796
|
editing: null,
|
|
1667
1797
|
renaming: null,
|
|
@@ -1758,10 +1888,15 @@ var EditorStore = class {
|
|
|
1758
1888
|
this.uiValue = { ...this.uiValue, resize: gesture };
|
|
1759
1889
|
this.notify({ scope: "ui", branch: "resize" });
|
|
1760
1890
|
}
|
|
1761
|
-
/**
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1891
|
+
/**
|
|
1892
|
+
* 建层武装置/解除(null,drag-create 工单 01);标量值等短路(重复武装同型
|
|
1893
|
+
* 不通知)。rowsPath(template-table-drag-create 工单 01):模板表风味载荷
|
|
1894
|
+
* 与层型一体写——同层型同载荷才短路(重复武装同型换载荷要更新),解除
|
|
1895
|
+
* (null)一律清载荷。
|
|
1896
|
+
*/
|
|
1897
|
+
setArmedCreate(type, rowsPath = "") {
|
|
1898
|
+
if (this.uiValue.armedCreate === type && this.uiValue.armedCreateRowsPath === rowsPath) return;
|
|
1899
|
+
this.uiValue = { ...this.uiValue, armedCreate: type, armedCreateRowsPath: type === null ? "" : rowsPath };
|
|
1765
1900
|
this.notify({ scope: "ui", branch: "armedCreate" });
|
|
1766
1901
|
}
|
|
1767
1902
|
/** 画拉橡皮筋会话开始/更新/结束(null);会话对象住 ui 分支,落库在 endCreate 单事务 */
|
|
@@ -1794,6 +1929,15 @@ var EditorStore = class {
|
|
|
1794
1929
|
this.uiValue = { ...this.uiValue, nextTextStyle: next };
|
|
1795
1930
|
this.notify({ scope: "ui", branch: "nextTextStyle" });
|
|
1796
1931
|
}
|
|
1932
|
+
/**
|
|
1933
|
+
* 建层比例槽写入(image-ratio-preset 工单 01:比例预设应用 / prefs 恢复的
|
|
1934
|
+
* 统一写入口):值等短路(重复写同档零通知),仅 ui 通知、不进历史。
|
|
1935
|
+
*/
|
|
1936
|
+
setNextLayerRatio(id) {
|
|
1937
|
+
if (this.uiValue.nextLayerRatio === id) return;
|
|
1938
|
+
this.uiValue = { ...this.uiValue, nextLayerRatio: id };
|
|
1939
|
+
this.notify({ scope: "ui", branch: "nextLayerRatio" });
|
|
1940
|
+
}
|
|
1797
1941
|
/**
|
|
1798
1942
|
* 数据源 schema 声明替换(归一产物或降级 null,校验收口在注入缝不在这里):
|
|
1799
1943
|
* 同引用短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
|
|
@@ -2678,8 +2822,17 @@ var EditorSession = class {
|
|
|
2678
2822
|
*
|
|
2679
2823
|
* 拖不动写不动的同门守卫:无会话、目标层被结构编辑移除(悬空路径)整体
|
|
2680
2824
|
* 空转(不发事务、不发布命中轴)。
|
|
2825
|
+
*
|
|
2826
|
+
* Shift 等比(image-ratio-preset 工单 01,spec 决策 5):仅 ImageLayer 生效
|
|
2827
|
+
* (其余层型 shift 空转走现状——自适应与尺寸耦合面不被触碰),且须双轴皆在
|
|
2828
|
+
* 可缩放面(格内容等强同步面派生轴写不出去,不进等比模式)、起始盒零维空转
|
|
2829
|
+
* (无比例可参照,回落自由)。参照手势起始盒实际宽高比;驱动轴 = 角柄取位
|
|
2830
|
+
* 移较大轴、边中柄取被拖轴;派生维 = round(驱动维 × 短比)(钳后驱动维跟随,
|
|
2831
|
+
* 最小钳位随动);对缘/对角固定照旧;吸附只在驱动轴移动缘候选(派生轴是算
|
|
2832
|
+
* 术产物不独立吸附,钳先于吸附同门)。等比模式双轴都落声明值(派生轴写算
|
|
2833
|
+
* 术积并清 auto 标志),canonicalize 以驱动轴键收口。
|
|
2681
2834
|
*/
|
|
2682
|
-
resizeTo(sceneX, sceneY) {
|
|
2835
|
+
resizeTo(sceneX, sceneY, shift = false) {
|
|
2683
2836
|
const gesture = this.store.ui.resize;
|
|
2684
2837
|
if (!gesture) return;
|
|
2685
2838
|
const doc = this.store.doc;
|
|
@@ -2689,12 +2842,21 @@ var EditorSession = class {
|
|
|
2689
2842
|
const dy = sceneY - gesture.startScene.y;
|
|
2690
2843
|
const widthActive = handleResizesWidth(gesture.handle);
|
|
2691
2844
|
const heightActive = handleResizesHeight(gesture.handle);
|
|
2845
|
+
const axes = resizableAxesAt(doc, gesture.path);
|
|
2846
|
+
const proportionalDriving = shift && current.type === "ImageLayer" && gesture.startBox.width > 0 && gesture.startBox.height > 0 && axes.width && axes.height ? proportionalDrivingAxis(gesture.handle, dx, dy) : null;
|
|
2692
2847
|
const resolution = resolveSnapPoints(
|
|
2693
|
-
resizeSnapPoints(gesture.startBox, gesture.handle, dx, dy),
|
|
2848
|
+
proportionalDriving !== null ? resizeProportionalSnapPoints(gesture.startBox, gesture.handle, proportionalDriving, dx, dy) : resizeSnapPoints(gesture.startBox, gesture.handle, dx, dy),
|
|
2694
2849
|
this.snapAxesForGesture(gesture.path),
|
|
2695
2850
|
snapThresholdScene(this.store.ui.viewport.zoom)
|
|
2696
2851
|
);
|
|
2697
|
-
const target =
|
|
2852
|
+
const target = proportionalDriving !== null ? resizeBoxProportional(
|
|
2853
|
+
gesture.startBox,
|
|
2854
|
+
gesture.handle,
|
|
2855
|
+
{ w: gesture.startBox.width, h: gesture.startBox.height },
|
|
2856
|
+
proportionalDriving,
|
|
2857
|
+
dx + resolution.dx,
|
|
2858
|
+
dy + resolution.dy
|
|
2859
|
+
) : resizeBox(
|
|
2698
2860
|
gesture.startBox,
|
|
2699
2861
|
gesture.handle,
|
|
2700
2862
|
widthActive ? dx + resolution.dx : dx,
|
|
@@ -2705,11 +2867,11 @@ var EditorSession = class {
|
|
|
2705
2867
|
const layer = resolveLayer(draft, gesture.path);
|
|
2706
2868
|
if (!layer) return;
|
|
2707
2869
|
const shape = layer.shape;
|
|
2708
|
-
if (widthActive) {
|
|
2870
|
+
if (widthActive || proportionalDriving !== null) {
|
|
2709
2871
|
shape.width = target.width;
|
|
2710
2872
|
shape.autoWidth = false;
|
|
2711
2873
|
}
|
|
2712
|
-
if (heightActive) {
|
|
2874
|
+
if (heightActive || proportionalDriving !== null) {
|
|
2713
2875
|
shape.height = target.height;
|
|
2714
2876
|
shape.autoHeight = false;
|
|
2715
2877
|
}
|
|
@@ -2717,7 +2879,7 @@ var EditorSession = class {
|
|
|
2717
2879
|
canonicalizeTableSyncInDraft(
|
|
2718
2880
|
draft,
|
|
2719
2881
|
gesture.path,
|
|
2720
|
-
widthActive ? ["shape", "
|
|
2882
|
+
proportionalDriving === "height" || proportionalDriving === null && !widthActive ? ["shape", "height"] : ["shape", "width"],
|
|
2721
2883
|
this.textPolicies
|
|
2722
2884
|
);
|
|
2723
2885
|
const resolvedWidth = layerWidth(layer, this.textPolicies);
|
|
@@ -2752,22 +2914,42 @@ var EditorSession = class {
|
|
|
2752
2914
|
* 分支待命层型,下一次画布按下即开画拉;建层提交或 Esc 即解除——一次性
|
|
2753
2915
|
* 待命态,无粘性工具(spec 决策 2)。重复武装同型值等短路零通知。
|
|
2754
2916
|
*
|
|
2755
|
-
* preset(text-defaults 工单 01
|
|
2756
|
-
*
|
|
2757
|
-
*
|
|
2758
|
-
*
|
|
2917
|
+
* preset(text-defaults 工单 01 + image-ratio-preset 工单 01):层型相关预设 =
|
|
2918
|
+
* 写槽的快捷方式——命中档位先把档位值并入对应偏好槽再武装;未知 id = 跳过
|
|
2919
|
+
* 写槽、武装照常(评审口径统一:「空转」只指写槽,武装层型本身仍合法)。
|
|
2920
|
+
* - TextLayer + 文本预设档:写新层样式槽(v1 只 fontSize);
|
|
2921
|
+
* - ImageLayer + 比例预设档:写建层比例槽('free' 即显式重置档);QR 恒方
|
|
2922
|
+
* 是层型硬钳、无比例槽(spec 豁免面);
|
|
2923
|
+
* - 交叉类型(文本层带比例档/图片层带文本档)各在对方清单查无此档、天然
|
|
2924
|
+
* 跳写槽。
|
|
2759
2925
|
*/
|
|
2760
2926
|
armLayerCreate(type, preset) {
|
|
2761
2927
|
if (preset !== void 0 && type === "TextLayer") {
|
|
2762
2928
|
const entry = TEXT_PRESETS.find((candidate) => candidate.id === preset);
|
|
2763
2929
|
if (entry) this.store.setNextTextStyle({ fontSize: entry.fontSize });
|
|
2764
2930
|
}
|
|
2931
|
+
if (preset !== void 0 && type === "ImageLayer") {
|
|
2932
|
+
const entry = RATIO_PRESETS.find((candidate) => candidate.id === preset);
|
|
2933
|
+
if (entry) this.store.setNextLayerRatio(entry.id);
|
|
2934
|
+
}
|
|
2765
2935
|
this.store.setArmedCreate(type);
|
|
2766
2936
|
}
|
|
2767
|
-
/**
|
|
2937
|
+
/** 解除建层武装(取消入口):仅清待命态(含模板表风味载荷),零副作用。 */
|
|
2768
2938
|
cancelArmLayerCreate() {
|
|
2769
2939
|
this.store.setArmedCreate(null);
|
|
2770
2940
|
}
|
|
2941
|
+
/**
|
|
2942
|
+
* 武装模板表建层(canvas-web-template-table-drag-create 工单 01,spec 决策 3):
|
|
2943
|
+
* 行路径表单确认即武装——rowsPath 随武装进 ui 瞬态载荷(armedCreate 仍为
|
|
2944
|
+
* 'TableLayer'、不新增 LayerType、不进 graph),下一次画拉或死区点击落模板表。
|
|
2945
|
+
* 空串 no-op(内核防御,表单层已拦);重复武装同 rowsPath 值等短路,换
|
|
2946
|
+
* rowsPath 更新载荷。TableLayer 的 G 武装语义(V1 普通表,armLayerCreate 走
|
|
2947
|
+
* 空载荷)不受染指。
|
|
2948
|
+
*/
|
|
2949
|
+
armTemplateTableCreate(rowsPath) {
|
|
2950
|
+
if (rowsPath === "") return;
|
|
2951
|
+
this.store.setArmedCreate("TableLayer", rowsPath);
|
|
2952
|
+
}
|
|
2771
2953
|
/**
|
|
2772
2954
|
* 开始画拉会话:须已武装(未武装空转防御——画布手势态机的分派前置,此守卫
|
|
2773
2955
|
* 兜其余调用来源),与拖动/缩放会话互斥(手势起点一次性定死,beginDrag/
|
|
@@ -2780,8 +2962,10 @@ var EditorSession = class {
|
|
|
2780
2962
|
if (armed === null || this.store.ui.drag || this.store.ui.resize || this.store.ui.create) return false;
|
|
2781
2963
|
if (!this.store.doc) return false;
|
|
2782
2964
|
const point = { x: sceneX, y: sceneY };
|
|
2965
|
+
const templateRowsPath = armed === "TableLayer" && this.store.ui.armedCreateRowsPath !== "" ? this.store.ui.armedCreateRowsPath : void 0;
|
|
2783
2966
|
this.store.setCreate({
|
|
2784
2967
|
type: armed,
|
|
2968
|
+
templateRowsPath,
|
|
2785
2969
|
startScene: point,
|
|
2786
2970
|
currentScene: point,
|
|
2787
2971
|
rect: { x: sceneX, y: sceneY, width: 0, height: 0 }
|
|
@@ -2790,22 +2974,31 @@ var EditorSession = class {
|
|
|
2790
2974
|
}
|
|
2791
2975
|
/**
|
|
2792
2976
|
* 画拉进行中:橡皮筋求位(spatial/create.createRubberBandRect——两点正规化
|
|
2793
|
-
* + QR 钳 height := width + 左右/上下缘各两点进
|
|
2794
|
-
*
|
|
2795
|
-
* ui.snapAxes 走 GuidesOverlay
|
|
2796
|
-
* (snapAxesForGesture(null)
|
|
2797
|
-
*
|
|
2977
|
+
* + QR 钳 height := width / 比例钳长轴驱动 + 左右/上下缘各两点进
|
|
2978
|
+
* resolveSnapPoints,吸附数学零新增)写回会话 rect 供 overlay 直读(虚线
|
|
2979
|
+
* 矩形 + W×H 气泡),命中轴回写 ui.snapAxes 走 GuidesOverlay 既有通道回显
|
|
2980
|
+
* (dragTo 同门)。吸附排除面为空(snapAxesForGesture(null)——新层尚未入库,
|
|
2981
|
+
* 全部可见根层供轴,copyMode 首移同门)。零文档事务:修正只进会话几何,
|
|
2982
|
+
* 落库在 endCreate。
|
|
2983
|
+
*
|
|
2984
|
+
* 建层比例槽(image-ratio-preset 工单 01,spec 决策 3/4):仅 ImageLayer 按槽
|
|
2985
|
+
* 钳制(QR 层不传 ratio、恒方语义不动,两钳互斥;其余层型无槽);Shift 瞬时
|
|
2986
|
+
* 反转槽态——槽非自由按 Shift = 临时自由、槽自由按 Shift = 临时钳 1:1,按住
|
|
2987
|
+
* 生效松开还原(逐帧求位、不写槽、不改武装态,Figma 惯例的手势瞬时语义)。
|
|
2798
2988
|
*/
|
|
2799
|
-
createTo(sceneX, sceneY) {
|
|
2989
|
+
createTo(sceneX, sceneY, shift = false) {
|
|
2800
2990
|
const gesture = this.store.ui.create;
|
|
2801
2991
|
const doc = this.store.doc;
|
|
2802
2992
|
if (!gesture || !doc) return;
|
|
2993
|
+
const slotRatio = gesture.type === "ImageLayer" ? ratioValue(this.store.ui.nextLayerRatio) : null;
|
|
2994
|
+
const ratio = gesture.type === "ImageLayer" ? shift ? slotRatio === null ? { w: 1, h: 1 } : null : slotRatio : null;
|
|
2803
2995
|
const resolution = createRubberBandRect(
|
|
2804
2996
|
gesture.type,
|
|
2805
2997
|
gesture.startScene,
|
|
2806
2998
|
{ x: sceneX, y: sceneY },
|
|
2807
2999
|
this.snapAxesForGesture(null),
|
|
2808
|
-
snapThresholdScene(this.store.ui.viewport.zoom)
|
|
3000
|
+
snapThresholdScene(this.store.ui.viewport.zoom),
|
|
3001
|
+
ratio
|
|
2809
3002
|
);
|
|
2810
3003
|
this.store.setCreate({
|
|
2811
3004
|
...gesture,
|
|
@@ -2819,12 +3012,19 @@ var EditorSession = class {
|
|
|
2819
3012
|
* + priority)。先清会话再落文档(commitTextEdit 同门——退出路径级联时幂等):
|
|
2820
3013
|
* - 死区外:单事务写入「createDefaultLayer 缺省形态 + position 左上角对准
|
|
2821
3014
|
* 橡皮筋矩形(含吸附修正)+ shape.width/height」并 insertRootLayerInDraft
|
|
2822
|
-
* 置顶(priority min−1、push
|
|
3015
|
+
* 置顶(priority min−1、push 数组尾)——尺寸取手势末态钳制结果(含 Shift
|
|
3016
|
+
* 反转末态,image-ratio-preset 工单 01 spec 决策 4);
|
|
2823
3017
|
* - 死区内(点击兜底,位移按 CREATE_DEAD_ZONE_SCREEN_PX 屏幕 px 随 zoom 折算,
|
|
2824
|
-
* ALT_DRAG_DEAD_ZONE_SCREEN_PX
|
|
3018
|
+
* ALT_DRAG_DEAD_ZONE_SCREEN_PX 同门):左上角对准点击点;尺寸按槽派生缺省
|
|
3019
|
+
* 盒(image-ratio-preset 工单 01:ImageLayer 长边 200 定短边,自由 = 工厂
|
|
3020
|
+
* 200×150 现状;忽略 shift——无位移可供等比约束;其余层型保持工厂缺省)。
|
|
2825
3021
|
* 落库后自动选中新层并解除武装;返回新层路径(绑定层消费:文本层据此自动
|
|
2826
3022
|
* 进文本编辑,spec 决策 6)。无会话/无文档空转返回 null。点击兜底不产生
|
|
2827
3023
|
* 零尺寸层(缺省形态自带可见尺寸)。
|
|
3024
|
+
*
|
|
3025
|
+
* 模板表风味(template-table-drag-create 工单 01):会话载荷 templateRowsPath
|
|
3026
|
+
* 非空即分派 createTemplateTable 工厂(死区外 shape = 手势正规化矩形,死区内
|
|
3027
|
+
* 缺省壳 400×120、左上角对准点击点);模板层不自动进文本编辑(文本层专属)。
|
|
2828
3028
|
*/
|
|
2829
3029
|
endCreate() {
|
|
2830
3030
|
const gesture = this.store.ui.create;
|
|
@@ -2838,9 +3038,23 @@ var EditorSession = class {
|
|
|
2838
3038
|
const deadZoneScene = CREATE_DEAD_ZONE_SCREEN_PX / this.store.ui.viewport.zoom;
|
|
2839
3039
|
const clicked = Math.hypot(dx, dy) <= deadZoneScene;
|
|
2840
3040
|
const index = { v: -1 };
|
|
3041
|
+
const templateRowsPath = gesture.templateRowsPath;
|
|
2841
3042
|
this.store.transact((draft) => {
|
|
2842
|
-
const layer =
|
|
3043
|
+
const layer = templateRowsPath !== void 0 ? createTemplateTable(
|
|
3044
|
+
clicked ? { rowsPath: templateRowsPath } : {
|
|
3045
|
+
rowsPath: templateRowsPath,
|
|
3046
|
+
shape: { width: gesture.rect.width, height: gesture.rect.height }
|
|
3047
|
+
}
|
|
3048
|
+
) : createDefaultLayer(gesture.type, this.store.ui.nextTextStyle);
|
|
2843
3049
|
if (clicked) {
|
|
3050
|
+
if (gesture.type === "ImageLayer") {
|
|
3051
|
+
const ratio = ratioValue(this.store.ui.nextLayerRatio);
|
|
3052
|
+
if (ratio) {
|
|
3053
|
+
const size = defaultSizeForRatio(ratio);
|
|
3054
|
+
layer.shape.width = size.width;
|
|
3055
|
+
layer.shape.height = size.height;
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
2844
3058
|
layer.position = { ...layer.position, x: gesture.startScene.x, y: gesture.startScene.y };
|
|
2845
3059
|
} else {
|
|
2846
3060
|
layer.position = { ...layer.position, x: gesture.rect.x, y: gesture.rect.y };
|
|
@@ -3028,19 +3242,23 @@ var EditorSession = class {
|
|
|
3028
3242
|
return {
|
|
3029
3243
|
rulersVisible: ui.rulersVisible,
|
|
3030
3244
|
anchorExpanded: ui.anchorExpanded,
|
|
3031
|
-
nextTextStyle: ui.nextTextStyle
|
|
3245
|
+
nextTextStyle: ui.nextTextStyle,
|
|
3246
|
+
nextLayerRatio: ui.nextLayerRatio
|
|
3032
3247
|
};
|
|
3033
3248
|
}
|
|
3034
3249
|
/**
|
|
3035
3250
|
* 应用偏好(会话建立时一次性,跨会话恢复 prefs 的写入口):整体替换
|
|
3036
3251
|
* rulersVisible/anchorExpanded,不进历史;值等写入经 store 短路零通知。
|
|
3037
3252
|
* 新层样式(text-defaults 工单 01)走宽容读逐字段合并(pickNextTextStylePatch
|
|
3038
|
-
*
|
|
3253
|
+
* 共享谓词——与捕获同门,缺字/类型不符保留现值);建层比例(image-ratio-preset
|
|
3254
|
+
* 工单 01)走宽容读枚举收口(pickNextLayerRatio——非六档枚举值/旧快照缺字段
|
|
3255
|
+
* 回落 'free')。
|
|
3039
3256
|
*/
|
|
3040
3257
|
applyEditorPrefs(prefs) {
|
|
3041
3258
|
this.store.setRulersVisible(prefs.rulersVisible);
|
|
3042
3259
|
this.store.setAnchorExpanded(prefs.anchorExpanded);
|
|
3043
3260
|
this.store.setNextTextStyle(pickNextTextStylePatch(prefs.nextTextStyle));
|
|
3261
|
+
this.store.setNextLayerRatio(pickNextLayerRatio(prefs.nextLayerRatio));
|
|
3044
3262
|
}
|
|
3045
3263
|
// ---- 对齐画布(layer-align-snap 工单 01):图层盒整体对齐画布几何 ----
|
|
3046
3264
|
/**
|
|
@@ -3084,11 +3302,10 @@ var EditorSession = class {
|
|
|
3084
3302
|
* 进入文本编辑:路径必须解析到 TextLayer。会话只进 ui 分支(不进历史),
|
|
3085
3303
|
* live 文本住在绑定层的 textarea,内容层随即跳绘该层文本(防重影)。
|
|
3086
3304
|
*
|
|
3087
|
-
*
|
|
3088
|
-
*
|
|
3089
|
-
*
|
|
3090
|
-
*
|
|
3091
|
-
* 不重锚);换层进入按新层现状重锚。
|
|
3305
|
+
* 内容模式活标志(ADR 0019)初值按进入瞬间 `layer.expression !== null` 取定
|
|
3306
|
+
* (与文档现态一致),此后不再冻结——pill 切换与内核 toggleDataMode 会话期
|
|
3307
|
+
* 都只翻标志,提交去向由退出时现态裁决(见 commitTextEdit)。同路径重复进入
|
|
3308
|
+
* 短路(会话不重建,接管序号延续);换层进入按新层现状取初值。
|
|
3092
3309
|
*/
|
|
3093
3310
|
beginTextEdit(path) {
|
|
3094
3311
|
const layer = this.textLayerAt(path);
|
|
@@ -3104,14 +3321,14 @@ var EditorSession = class {
|
|
|
3104
3321
|
* 提交与随后 blur 竞态),会话清空后后续调用幂等,保证一次退出恰一步历史。
|
|
3105
3322
|
*
|
|
3106
3323
|
* 提交矩阵(canvas-web-expression-editing 工单 01,spec 决策 3,空串判定先于
|
|
3107
|
-
*
|
|
3108
|
-
*
|
|
3109
|
-
*
|
|
3110
|
-
*
|
|
3324
|
+
* 片段判定;对比对象与去向裁决按会话标志**退出时现态**——活标志语义,ADR
|
|
3325
|
+
* 0019,取代旧「进入时快照」):
|
|
3326
|
+
* - 串与文档一致:不进历史(进出编辑零噪声),返回 false——对比对象随退出时
|
|
3327
|
+
* 会话标志取:表达式现态对比 `layer.expression`、静态现态对比 `layer.text`;
|
|
3111
3328
|
* - 空串:删除该图层(复用 deleteLayer 的 splice/重映射语义),任何模式同语义;
|
|
3112
|
-
* -
|
|
3329
|
+
* - 表达式现态且串含 ≥1 语法有效闭合片段(hasValidExpressionFragment):
|
|
3113
3330
|
* updateDataExpression 标记写(保持标记、text 自动重镜像);
|
|
3114
|
-
* -
|
|
3331
|
+
* - 其余(静态现态恒走此支;表达式现态无合法片段回落):updateData 字面写、
|
|
3115
3332
|
* 字面接管解标——含 `{{...}}` 按字面保留,杜绝无片段标记层。
|
|
3116
3333
|
*/
|
|
3117
3334
|
commitTextEdit(text) {
|
|
@@ -3135,8 +3352,10 @@ var EditorSession = class {
|
|
|
3135
3352
|
/**
|
|
3136
3353
|
* 编辑 overlay 的布局描述:与绘制/命中同一套布局策略求出的盒几何与排版字段
|
|
3137
3354
|
* (textarea 以盒左上对位、盒尺寸占位,字号/行高/内边距按场景像素交给 CSS
|
|
3138
|
-
* transform
|
|
3139
|
-
*
|
|
3355
|
+
* transform 缩放)。对当前文档现算(canvas-web-edit-overlay-toolbar 工单 01,
|
|
3356
|
+
* ADR 0019 布局现算):会话期样式/几何权威写实时反映到查询值;verticalAnchorY
|
|
3357
|
+
* 按当前文档文本计算——live 键入只住 textarea 草稿不落文档,键入不触发文档
|
|
3358
|
+
* 变更,锚点天然稳定不跳。非文本层/路径失效返回 null。
|
|
3140
3359
|
*/
|
|
3141
3360
|
textEditLayout(path) {
|
|
3142
3361
|
const layer = this.textLayerAt(path);
|
|
@@ -3213,7 +3432,8 @@ var EditorSession = class {
|
|
|
3213
3432
|
* 原地不动——事务后派生列表在被替换处缩一位,原下标即「下一处」(「替换后
|
|
3214
3433
|
* 自动跳下一处」靠派生天然成立,末处替换则钳位停在尾)。写值经 replaceHitsInDraft
|
|
3215
3434
|
* 与文本编辑提交同门(text 直写 + expression 保持 null)。无文档/空 query/
|
|
3216
|
-
* 无命中空转返回 false
|
|
3435
|
+
* 无命中空转返回 false。命中编辑层时草稿接管(会话内容独占,工单 02——
|
|
3436
|
+
* 替换结果出现在正在编辑的框里)。
|
|
3217
3437
|
*/
|
|
3218
3438
|
replaceOne() {
|
|
3219
3439
|
const doc = this.store.doc;
|
|
@@ -3222,7 +3442,7 @@ var EditorSession = class {
|
|
|
3222
3442
|
const matches = scanTextMatches(doc, query);
|
|
3223
3443
|
const hit = matches[clampFindCursor(this.store.ui.find.cursor, matches.length)];
|
|
3224
3444
|
if (!hit) return false;
|
|
3225
|
-
this.
|
|
3445
|
+
this.transactContent([hit.path], (draft) => {
|
|
3226
3446
|
replaceHitsInDraft(draft, [hit], this.store.ui.find.replacement);
|
|
3227
3447
|
});
|
|
3228
3448
|
return true;
|
|
@@ -3231,7 +3451,8 @@ var EditorSession = class {
|
|
|
3231
3451
|
* 全部替换(查找条「全部替换」按钮):命中(path + 偏移对)在事务前按原文
|
|
3232
3452
|
* 快照一次算定,单事务内按同字段偏移降序应用、替换产物不重扫(replacement
|
|
3233
3453
|
* 含查询串不死循环)——一次调用 = 一步历史,undo 一次全回。返回应用处数
|
|
3234
|
-
* (计数反馈「已替换 N 处」);无文档/空 query 空转返回 0
|
|
3454
|
+
* (计数反馈「已替换 N 处」);无文档/空 query 空转返回 0。命中编辑层时
|
|
3455
|
+
* 草稿接管(会话内容独占,工单 02)。
|
|
3235
3456
|
*/
|
|
3236
3457
|
replaceAll() {
|
|
3237
3458
|
const doc = this.store.doc;
|
|
@@ -3240,7 +3461,7 @@ var EditorSession = class {
|
|
|
3240
3461
|
const hits = scanTextMatches(doc, query);
|
|
3241
3462
|
if (hits.length === 0) return 0;
|
|
3242
3463
|
let applied = 0;
|
|
3243
|
-
this.
|
|
3464
|
+
this.transactContent(hits.map((hit) => hit.path), (draft) => {
|
|
3244
3465
|
applied = replaceHitsInDraft(draft, hits, this.store.ui.find.replacement);
|
|
3245
3466
|
});
|
|
3246
3467
|
return applied;
|
|
@@ -3294,9 +3515,13 @@ var EditorSession = class {
|
|
|
3294
3515
|
* null,值与标记的镜像关系随之解除——标记态文档经字面编辑退化为字面态,
|
|
3295
3516
|
* 不可自动恢复属预期(spec §3.7 兼容面)。
|
|
3296
3517
|
* 表/行/格无数据字段,空转。
|
|
3518
|
+
*
|
|
3519
|
+
* 会话内容独占(工单 02,ADR 0019 内核底线面):命中编辑层的写落文档的同时
|
|
3520
|
+
* 草稿接管(draftSeq 递增,绑定层全量替换 textarea 草稿)——旁门程序化写的
|
|
3521
|
+
* 结果进编辑框草稿而非被退出提交覆盖,任何方向不丢字。
|
|
3297
3522
|
*/
|
|
3298
3523
|
updateData(path, value, options = {}) {
|
|
3299
|
-
this.
|
|
3524
|
+
this.transactContent([path], (draft) => {
|
|
3300
3525
|
const layer = resolveLayer(draft, path);
|
|
3301
3526
|
if (!layer) return;
|
|
3302
3527
|
switch (layer.type) {
|
|
@@ -3327,9 +3552,11 @@ var EditorSession = class {
|
|
|
3327
3552
|
* 字面态空值归一交给 updateData/decode 分支(这里不清值,字面接管语义)。
|
|
3328
3553
|
* - 仅三内容层生效,表/行/格/行模板空转(与 updateData 同门)。
|
|
3329
3554
|
* - updateData 语义不变:字面写解除标记仍成立(spec §1 明示两者并排)。
|
|
3555
|
+
* - 命中编辑层的旁门打标同走草稿接管(draftSeq 递增;会话标志不随动——
|
|
3556
|
+
* 活标志只归 pill/toggleDataMode 翻,见 toggleDataMode)。
|
|
3330
3557
|
*/
|
|
3331
3558
|
updateDataExpression(path, expression, options = {}) {
|
|
3332
|
-
this.
|
|
3559
|
+
this.transactContent([path], (draft) => {
|
|
3333
3560
|
const layer = resolveLayer(draft, path);
|
|
3334
3561
|
if (!layer) return;
|
|
3335
3562
|
switch (layer.type) {
|
|
@@ -3350,6 +3577,63 @@ var EditorSession = class {
|
|
|
3350
3577
|
}
|
|
3351
3578
|
}, options);
|
|
3352
3579
|
}
|
|
3580
|
+
/**
|
|
3581
|
+
* 数据字段取值方式切换(静态 ⇆ 表达式,工单 02 自面板收编内核):表达式现态
|
|
3582
|
+
* → 字面接管(updateData,值保持现镜像);静态现态 → 打标(updateDataExpression,
|
|
3583
|
+
* 初值 = 值字段原文)。各为独立一步历史(无 mergeKey)。三内容层分派各取值
|
|
3584
|
+
* 字段,表/行/格/悬空路径空转。
|
|
3585
|
+
*
|
|
3586
|
+
* 活标志防御路由(ADR 0019):路径即编辑中的层时只翻会话活标志(零文档变更
|
|
3587
|
+
* 零历史步)——会话期内容模式切换的唯一写入口是会话标志,文档面锁定归编辑框
|
|
3588
|
+
* 草稿;面板锁定段/pill 显式调用与宿主旁路调用都安全收敛到同一语义。
|
|
3589
|
+
*/
|
|
3590
|
+
toggleDataMode(path) {
|
|
3591
|
+
const editing = this.store.ui.editing;
|
|
3592
|
+
if (editing && pathsEqual(editing.path, path)) {
|
|
3593
|
+
this.store.setEditing({ ...editing, expression: !editing.expression });
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3596
|
+
const layer = this.store.doc ? resolveLayer(this.store.doc, path) : null;
|
|
3597
|
+
if (!layer) return;
|
|
3598
|
+
switch (layer.type) {
|
|
3599
|
+
case "TextLayer":
|
|
3600
|
+
if (layer.expression !== null) this.updateData(path, layer.text);
|
|
3601
|
+
else this.updateDataExpression(path, layer.text);
|
|
3602
|
+
return;
|
|
3603
|
+
case "ImageLayer":
|
|
3604
|
+
if (layer.expression !== null) this.updateData(path, layer.src ?? "");
|
|
3605
|
+
else this.updateDataExpression(path, layer.src ?? "");
|
|
3606
|
+
return;
|
|
3607
|
+
case "QrCodeLayer":
|
|
3608
|
+
if (layer.expression !== null) this.updateData(path, layer.value);
|
|
3609
|
+
else this.updateDataExpression(path, layer.value);
|
|
3610
|
+
return;
|
|
3611
|
+
default:
|
|
3612
|
+
return;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
/**
|
|
3616
|
+
* 内容写事务的统一形(会话内容独占工单 02):transact 落库 + 命中编辑层的
|
|
3617
|
+
* 草稿接管收口——updateData/updateDataExpression/replaceOne/replaceAll 四个
|
|
3618
|
+
* 旁门写入口同门(接管判定见 takeoverDraftIfNeeded;零变更事务天然短路)。
|
|
3619
|
+
*/
|
|
3620
|
+
transactContent(paths, recipe, options = {}) {
|
|
3621
|
+
const docBefore = this.store.doc;
|
|
3622
|
+
this.store.transact(recipe, options);
|
|
3623
|
+
this.takeoverDraftIfNeeded(paths, docBefore);
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* 草稿接管收口(工单 02):paths 中有编辑层且文档确实变更(引用比较——零变更
|
|
3627
|
+
* 事务 transact 空转不通知,接管序号也不动)时递增会话 draftSeq 并广播
|
|
3628
|
+
* ui.editing。绑定层据此全量替换 textarea 草稿为文档新值并刷新表达式诊断。
|
|
3629
|
+
* commitTextEdit 先清会话再落库,天然不复入接管。
|
|
3630
|
+
*/
|
|
3631
|
+
takeoverDraftIfNeeded(paths, docBefore) {
|
|
3632
|
+
const editing = this.store.ui.editing;
|
|
3633
|
+
if (!editing || this.store.doc === docBefore) return;
|
|
3634
|
+
if (!paths.some((path) => pathsEqual(path, editing.path))) return;
|
|
3635
|
+
this.store.setEditing({ ...editing, draftSeq: (editing.draftSeq ?? 0) + 1 });
|
|
3636
|
+
}
|
|
3353
3637
|
/** 更新画布级字段(未选中图层时面板的宽/高写入口);键由类型收窄为 width/height */
|
|
3354
3638
|
updateCanvasProp(key, value, options = {}) {
|
|
3355
3639
|
this.store.transact((draft) => {
|
|
@@ -3361,12 +3645,14 @@ var EditorSession = class {
|
|
|
3361
3645
|
* 注入数据源 schema 声明(载荷形态,D1 钉定:声明即 compile(canvas, dataset)
|
|
3362
3646
|
* 收到的 data 载荷形状,根上下文候选 = 载荷顶层键)。声明经
|
|
3363
3647
|
* normalizeExpressionSchemaSource 一次性编译收口——根级结构性问题(保留键
|
|
3364
|
-
* row/$ 前缀,前移填充期 reserved_root_key;根非对象/缺 properties
|
|
3365
|
-
*
|
|
3366
|
-
*
|
|
3367
|
-
*
|
|
3368
|
-
*
|
|
3369
|
-
*
|
|
3648
|
+
* row/$ 前缀,前移填充期 reserved_root_key;根非对象/缺 properties 层级)
|
|
3649
|
+
* 与开放映射契约废除(open_map_unsupported,ADR 0020:文档任意层
|
|
3650
|
+
* additionalProperties: true 显式拒绝,动态键归宿主组装成具体 properties 后
|
|
3651
|
+
* 注入)降级为无候选 + console 警告,局部故障($ref 断链/外部指针/环引用/
|
|
3652
|
+
* 目标形态不符)不拒绝:该节点降叶子 + 注入期汇总告警一次,树其余部分照常
|
|
3653
|
+
* 服务,不抛错不弹错(辅助声明不作权威);null/undefined = 清除声明。声明只
|
|
3654
|
+
* 住 store ui 分支:不进 graph、不落 localStorage、不进 wire;openDocument
|
|
3655
|
+
* 换文档不重置,重注入/清除走同一入口。签名与「注入即整体替换」语义不变(D6)。
|
|
3370
3656
|
*/
|
|
3371
3657
|
setDataSourceSchema(raw) {
|
|
3372
3658
|
this.store.setDataSourceSchema(
|
|
@@ -3928,19 +4214,6 @@ var EditorSession = class {
|
|
|
3928
4214
|
});
|
|
3929
4215
|
if (index.v >= 0) this.store.setSelection([...rowPath, "cells", index.v]);
|
|
3930
4216
|
}
|
|
3931
|
-
/**
|
|
3932
|
-
* 新增模板表(「+」选择器入口,spec §2.1):createTemplateTable 缺省形态 +
|
|
3933
|
-
* 置顶 + 自动选中;一次调用 = 一步历史。rowsPath 必填(解码 rows_path_missing
|
|
3934
|
-
* 硬约束的对齐兜底,表单校验前置),空串 no-op。
|
|
3935
|
-
*/
|
|
3936
|
-
addTemplateTable(rowsPath) {
|
|
3937
|
-
if (!this.store.doc || rowsPath === "") return;
|
|
3938
|
-
const index = { v: -1 };
|
|
3939
|
-
this.store.transact((draft) => {
|
|
3940
|
-
index.v = insertRootLayerInDraft(draft, createTemplateTable({ rowsPath }));
|
|
3941
|
-
});
|
|
3942
|
-
if (index.v >= 0) this.store.setSelection(["layers", index.v]);
|
|
3943
|
-
}
|
|
3944
4217
|
/**
|
|
3945
4218
|
* 模板行加格(spec §3.1):addTemplateCellInDraft 重建路径(末格宽/首格行宽、
|
|
3946
4219
|
* 零高度耦合),自动选中新格;一次调用 = 一步历史。非模板态 no-op。
|
|
@@ -4241,6 +4514,29 @@ function classifyWheel(input) {
|
|
|
4241
4514
|
return "pan";
|
|
4242
4515
|
}
|
|
4243
4516
|
|
|
4517
|
+
// src/shared/expressionDiagnostics.ts
|
|
4518
|
+
function expressionDiagnostics(template) {
|
|
4519
|
+
const found = [];
|
|
4520
|
+
for (const part of scanExpressionFragments(template).parts) {
|
|
4521
|
+
if (part.kind === "fragment" && part.diagnostic !== null) found.push(part.diagnostic);
|
|
4522
|
+
}
|
|
4523
|
+
return found;
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
// src/shared/resourcePath.ts
|
|
4527
|
+
function wholeTemplateDiagnostic(template, kind) {
|
|
4528
|
+
return { kind, start: 0, end: template.length, at: { start: 0, end: template.length } };
|
|
4529
|
+
}
|
|
4530
|
+
function resourcePathDiagnostics(template) {
|
|
4531
|
+
const anchored = template.endsWith("\n") ? template.slice(0, -1) : template;
|
|
4532
|
+
const matched = /^\{\{([^{}]*)\}\}$/.exec(anchored);
|
|
4533
|
+
if (matched === null) return [wholeTemplateDiagnostic(template, "resource_not_single_ref")];
|
|
4534
|
+
const path = phpTrim2(matched[1] ?? "");
|
|
4535
|
+
if (path === "") return [wholeTemplateDiagnostic(template, "empty_fragment")];
|
|
4536
|
+
if (path.includes("(")) return [wholeTemplateDiagnostic(template, "resource_function_call")];
|
|
4537
|
+
return [];
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4244
4540
|
// src/shared/expressionFunctions.ts
|
|
4245
4541
|
var EXPRESSION_FUNCTIONS = [
|
|
4246
4542
|
{ name: "date", signature: "date(path[, format])", summary: "\u65E5\u671F\u683C\u5F0F\u5316\uFF08moment \u98CE\u683C token\uFF0C\u9884\u8BBE date/datetime\uFF09" },
|
|
@@ -4272,7 +4568,7 @@ function candidateMeta(node) {
|
|
|
4272
4568
|
}
|
|
4273
4569
|
function childCandidate(entry, prefix) {
|
|
4274
4570
|
const path = prefix === "" ? entry.key : `${prefix}.${entry.key}`;
|
|
4275
|
-
return { path, segment: entry.key, ...candidateMeta(entry.node) };
|
|
4571
|
+
return { path, segment: entry.key, kind: "path", ...candidateMeta(entry.node) };
|
|
4276
4572
|
}
|
|
4277
4573
|
function callCandidatesFor(entry, path, exprLen) {
|
|
4278
4574
|
const extras = [];
|
|
@@ -4283,6 +4579,7 @@ function callCandidatesFor(entry, path, exprLen) {
|
|
|
4283
4579
|
cursorBack: 2,
|
|
4284
4580
|
replaceLen: exprLen,
|
|
4285
4581
|
filterKey: entry.key,
|
|
4582
|
+
kind: "function",
|
|
4286
4583
|
description: "\u65E5\u671F\u683C\u5F0F\u5316",
|
|
4287
4584
|
type: "\u51FD\u6570"
|
|
4288
4585
|
});
|
|
@@ -4293,6 +4590,7 @@ function callCandidatesFor(entry, path, exprLen) {
|
|
|
4293
4590
|
segment: `${fn.name}(${path})`,
|
|
4294
4591
|
replaceLen: exprLen,
|
|
4295
4592
|
filterKey: entry.key,
|
|
4593
|
+
kind: "function",
|
|
4296
4594
|
description: fn.summary,
|
|
4297
4595
|
type: "\u51FD\u6570"
|
|
4298
4596
|
});
|
|
@@ -4310,6 +4608,7 @@ function functionCandidates() {
|
|
|
4310
4608
|
path: fn.signature,
|
|
4311
4609
|
segment: `${fn.name}()`,
|
|
4312
4610
|
cursorBack: 1,
|
|
4611
|
+
kind: "function",
|
|
4313
4612
|
description: fn.summary,
|
|
4314
4613
|
type: "\u51FD\u6570"
|
|
4315
4614
|
}));
|
|
@@ -4325,12 +4624,12 @@ function dateFormatCandidates() {
|
|
|
4325
4624
|
function headCandidates(schema, context, rowSchema, exprLen, withBuckets) {
|
|
4326
4625
|
const view = schemaChildEntries(schema);
|
|
4327
4626
|
const candidates = view.entries.flatMap((entry) => childCandidates(entry, "", exprLen, withBuckets));
|
|
4328
|
-
candidates.push({ path: "$root", segment: "$root" });
|
|
4627
|
+
candidates.push({ path: "$root", segment: "$root", kind: "path" });
|
|
4329
4628
|
if (context === "row") {
|
|
4330
|
-
candidates.push(rowSchema ? { path: "row", segment: "row", ...candidateMeta(rowSchema) } : { path: "row", segment: "row" });
|
|
4331
|
-
candidates.push({ path: "$index", segment: "$index" });
|
|
4629
|
+
candidates.push(rowSchema ? { path: "row", segment: "row", kind: "path", ...candidateMeta(rowSchema) } : { path: "row", segment: "row", kind: "path" });
|
|
4630
|
+
candidates.push({ path: "$index", segment: "$index", kind: "path" });
|
|
4332
4631
|
}
|
|
4333
|
-
return
|
|
4632
|
+
return candidates;
|
|
4334
4633
|
}
|
|
4335
4634
|
function callSlotResult(schema, query) {
|
|
4336
4635
|
const slot = expressionCallSlotAtEnd(query.expr);
|
|
@@ -4359,7 +4658,7 @@ function enumeratePathCandidates(schema, query, expr, allowCallSuggestions) {
|
|
|
4359
4658
|
ok: true,
|
|
4360
4659
|
prefix: "",
|
|
4361
4660
|
partial: "",
|
|
4362
|
-
|
|
4661
|
+
candidates: headCandidates(schema, query.context, query.rowSchema, expr.length, false)
|
|
4363
4662
|
};
|
|
4364
4663
|
}
|
|
4365
4664
|
return { ok: false, reason: "expression_syntax_error" };
|
|
@@ -4389,8 +4688,7 @@ function enumeratePathCandidates(schema, query, expr, allowCallSuggestions) {
|
|
|
4389
4688
|
ok: true,
|
|
4390
4689
|
prefix: "",
|
|
4391
4690
|
partial,
|
|
4392
|
-
candidates: [...view2
|
|
4393
|
-
...view2.open === true ? { open: true } : {}
|
|
4691
|
+
candidates: [...view2, ...fns]
|
|
4394
4692
|
};
|
|
4395
4693
|
}
|
|
4396
4694
|
let base;
|
|
@@ -4420,8 +4718,7 @@ function enumeratePathCandidates(schema, query, expr, allowCallSuggestions) {
|
|
|
4420
4718
|
ok: true,
|
|
4421
4719
|
prefix,
|
|
4422
4720
|
partial,
|
|
4423
|
-
candidates: view.entries.flatMap((entry) => childCandidates(entry, prefix, expr.length, withBuckets))
|
|
4424
|
-
...view.open === true ? { open: true } : {}
|
|
4721
|
+
candidates: view.entries.flatMap((entry) => childCandidates(entry, prefix, expr.length, withBuckets))
|
|
4425
4722
|
};
|
|
4426
4723
|
}
|
|
4427
4724
|
function enumerateExpressionCandidates(schema, query) {
|
|
@@ -4439,7 +4736,7 @@ function isArrayNode(node) {
|
|
|
4439
4736
|
return node.type === "array" || node.items !== void 0;
|
|
4440
4737
|
}
|
|
4441
4738
|
function isScalarNode(node) {
|
|
4442
|
-
return !isArrayNode(node) && node.properties === void 0
|
|
4739
|
+
return !isArrayNode(node) && node.properties === void 0;
|
|
4443
4740
|
}
|
|
4444
4741
|
function childCandidate2(key, node, prefix) {
|
|
4445
4742
|
const candidate = {
|
|
@@ -4459,7 +4756,7 @@ function levelCandidates(base, prefix) {
|
|
|
4459
4756
|
candidates.push(childCandidate2(key, child, prefix));
|
|
4460
4757
|
}
|
|
4461
4758
|
}
|
|
4462
|
-
return
|
|
4759
|
+
return candidates;
|
|
4463
4760
|
}
|
|
4464
4761
|
function enumerateRowsPathCandidates(startSchema, input) {
|
|
4465
4762
|
if (startSchema === null) return null;
|
|
@@ -4475,11 +4772,7 @@ function enumerateRowsPathCandidates(startSchema, input) {
|
|
|
4475
4772
|
}
|
|
4476
4773
|
if (isArrayNode(base) || isScalarNode(base)) return null;
|
|
4477
4774
|
const view = levelCandidates(base, completed.join("."));
|
|
4478
|
-
return {
|
|
4479
|
-
partial,
|
|
4480
|
-
candidates: view.candidates,
|
|
4481
|
-
...view.open === true ? { open: true } : {}
|
|
4482
|
-
};
|
|
4775
|
+
return { partial, candidates: view };
|
|
4483
4776
|
}
|
|
4484
4777
|
function resolveRowsPathStartSchema(doc, path, rootSchema) {
|
|
4485
4778
|
if (rootSchema === null) return null;
|
|
@@ -4598,6 +4891,7 @@ export {
|
|
|
4598
4891
|
PAN_TO_BOX_MARGIN_PX,
|
|
4599
4892
|
PASTE_OFFSET_PX,
|
|
4600
4893
|
PHP_TRIM_CHARS,
|
|
4894
|
+
RATIO_PRESETS,
|
|
4601
4895
|
RESIZE_HANDLES,
|
|
4602
4896
|
RESIZE_HANDLE_HIT_PX,
|
|
4603
4897
|
RESIZE_MIN_SIZE_PX,
|
|
@@ -4627,11 +4921,14 @@ export {
|
|
|
4627
4921
|
createDefaultLayer,
|
|
4628
4922
|
createRubberBandRect,
|
|
4629
4923
|
createTemplateTable,
|
|
4924
|
+
defaultNextLayerRatio,
|
|
4630
4925
|
defaultNextTextStyle,
|
|
4926
|
+
defaultSizeForRatio,
|
|
4631
4927
|
deleteLayerInDraft,
|
|
4632
4928
|
enumerateExpressionCandidates,
|
|
4633
4929
|
enumerateRowsPathCandidates,
|
|
4634
4930
|
expressionCallSlotAtEnd,
|
|
4931
|
+
expressionDiagnostics,
|
|
4635
4932
|
expressionFragmentAtCursor,
|
|
4636
4933
|
fitRect,
|
|
4637
4934
|
fitViewport,
|
|
@@ -4667,22 +4964,28 @@ export {
|
|
|
4667
4964
|
pathStartsWith,
|
|
4668
4965
|
pathsEqual,
|
|
4669
4966
|
phpTrim2 as phpTrim,
|
|
4967
|
+
pickNextLayerRatio,
|
|
4670
4968
|
pickNextTextStylePatch,
|
|
4671
4969
|
prepareRootPaste,
|
|
4970
|
+
proportionalDrivingAxis,
|
|
4971
|
+
ratioValue,
|
|
4672
4972
|
readSpecField,
|
|
4673
4973
|
remapPathAfterSplice,
|
|
4674
4974
|
replaceHitsInDraft,
|
|
4675
4975
|
resizableAxesAt,
|
|
4676
4976
|
resizeBox,
|
|
4977
|
+
resizeBoxProportional,
|
|
4677
4978
|
resizeHandleAt,
|
|
4678
4979
|
resizeHandlePoint,
|
|
4679
4980
|
resizeHandlesAt,
|
|
4981
|
+
resizeProportionalSnapPoints,
|
|
4680
4982
|
resizeSnapPoints,
|
|
4681
4983
|
resolveLayer,
|
|
4682
4984
|
resolveRowSchema,
|
|
4683
4985
|
resolveRowsPathStartSchema,
|
|
4684
4986
|
resolveSnap,
|
|
4685
4987
|
resolveSnapPoints,
|
|
4988
|
+
resourcePathDiagnostics,
|
|
4686
4989
|
rootLayerOf,
|
|
4687
4990
|
scanExpressionFragments,
|
|
4688
4991
|
scanTextMatches,
|