mce 0.18.3 → 0.18.4

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.js CHANGED
@@ -42,6 +42,46 @@ var _0_command_default = defineMixin((editor) => {
42
42
  });
43
43
  });
44
44
  //#endregion
45
+ //#region src/utils/arrow.ts
46
+ function rotatePoint(center, point, angleRad) {
47
+ const cos = Math.cos(angleRad);
48
+ const sin = Math.sin(angleRad);
49
+ const dx = point.x - center.x;
50
+ const dy = point.y - center.y;
51
+ return {
52
+ x: center.x + (dx * cos - dy * sin),
53
+ y: center.y + (dx * sin + dy * cos)
54
+ };
55
+ }
56
+ function getArrowPath(p1, p2, options = {}) {
57
+ const { size = 10, angle = 30, startMarker = "none", endMarker = "open", roundValues = true } = options;
58
+ const fmt = (n) => roundValues ? Math.round(n) : n.toFixed(2);
59
+ let d = `M ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(p2.x)} ${fmt(p2.y)}`;
60
+ const theta = Math.atan2(p2.y - p1.y, p2.x - p1.x);
61
+ const angleRad = angle * Math.PI / 180;
62
+ if (endMarker !== "none") {
63
+ const baseWing = {
64
+ x: p2.x - size,
65
+ y: p2.y
66
+ };
67
+ const wing1 = rotatePoint(p2, baseWing, theta + angleRad);
68
+ const wing2 = rotatePoint(p2, baseWing, theta - angleRad);
69
+ if (endMarker === "open") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p2.x)} ${fmt(p2.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)}`;
70
+ else if (endMarker === "filled") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p2.x)} ${fmt(p2.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)} Z`;
71
+ }
72
+ if (startMarker !== "none") {
73
+ const baseWingStart = {
74
+ x: p1.x + size,
75
+ y: p1.y
76
+ };
77
+ const wing1 = rotatePoint(p1, baseWingStart, theta + angleRad);
78
+ const wing2 = rotatePoint(p1, baseWingStart, theta - angleRad);
79
+ if (startMarker === "open") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)}`;
80
+ else if (startMarker === "filled") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)} Z`;
81
+ }
82
+ return d;
83
+ }
84
+ //#endregion
45
85
  //#region src/utils/box.ts
46
86
  function boundingBoxToStyle(box) {
47
87
  const style = {
@@ -386,18 +426,26 @@ var _0_config_default = defineMixin((editor) => {
386
426
  }
387
427
  function importConfig() {
388
428
  return new Promise((resolve) => {
389
- const { onChange, open } = useFileDialog({
429
+ const { onChange, onCancel, open } = useFileDialog({
390
430
  accept: ".json",
391
431
  reset: true,
392
432
  multiple: false
393
433
  });
394
434
  onChange(async (files) => {
395
435
  const file = files?.[0];
396
- if (file) try {
436
+ if (!file) {
437
+ resolve(void 0);
438
+ return;
439
+ }
440
+ try {
397
441
  config.value = JSON.parse(await file.text());
398
442
  resolve(config.value);
399
- } catch {}
443
+ } catch {
444
+ console.warn("[mce] Failed to parse config file");
445
+ resolve(void 0);
446
+ }
400
447
  });
448
+ onCancel(() => resolve(void 0));
401
449
  open();
402
450
  });
403
451
  }
@@ -4852,6 +4900,7 @@ var _0_context_default = defineMixin((editor, options) => {
4852
4900
  const camera = ref(new Camera2D({ internalMode: "front" }));
4853
4901
  const timeline = ref(new Timeline({
4854
4902
  startTime: 0,
4903
+ currentTime: 0,
4855
4904
  endTime: 0,
4856
4905
  loop: true,
4857
4906
  paused: true
@@ -4890,6 +4939,18 @@ var _0_context_default = defineMixin((editor, options) => {
4890
4939
  function setCursor(mode) {
4891
4940
  renderEngine.value.input.setCursor(mode);
4892
4941
  }
4942
+ let exclusiveRenderDepth = 0;
4943
+ async function runExclusiveRender(fn) {
4944
+ const engine = renderEngine.value;
4945
+ if (exclusiveRenderDepth === 0) engine.stop();
4946
+ exclusiveRenderDepth++;
4947
+ try {
4948
+ return await fn();
4949
+ } finally {
4950
+ exclusiveRenderDepth--;
4951
+ if (exclusiveRenderDepth === 0) engine.start();
4952
+ }
4953
+ }
4893
4954
  function getGlobalPointer() {
4894
4955
  const { x = 0, y = 0 } = drawboardPointer.value ?? {};
4895
4956
  return camera.value.toGlobal({
@@ -4951,6 +5012,7 @@ var _0_context_default = defineMixin((editor, options) => {
4951
5012
  fonts,
4952
5013
  assets,
4953
5014
  renderEngine,
5015
+ runExclusiveRender,
4954
5016
  timeline,
4955
5017
  camera,
4956
5018
  drawboardEffect,
@@ -4998,7 +5060,7 @@ var _0_context_default = defineMixin((editor, options) => {
4998
5060
  function onSetDoc() {
4999
5061
  updateNodes();
5000
5062
  }
5001
- watch(selection, (value) => {
5063
+ if (options.debug) watch(selection, (value) => {
5002
5064
  window.$$0 = value[0];
5003
5065
  });
5004
5066
  watch(state, () => {
@@ -5140,6 +5202,14 @@ var en_default = {
5140
5202
  "checkerboard:dot": "Dot checkerboard",
5141
5203
  "panels": "Panels",
5142
5204
  "timeline": "Timeline",
5205
+ "play": "Play",
5206
+ "pause": "Pause",
5207
+ "togglePlay": "Play / Pause",
5208
+ "seekStart": "Go to Start",
5209
+ "seekEnd": "Go to End",
5210
+ "stepBackward": "Previous Frame",
5211
+ "stepForward": "Next Frame",
5212
+ "collapse": "Collapse",
5143
5213
  "statusbar": "Statusbar",
5144
5214
  "creator": "Create node",
5145
5215
  "memoryManager": "Manage memory",
@@ -5262,6 +5332,14 @@ var zh_Hans_default = {
5262
5332
  "panels": "面板",
5263
5333
  "statusbar": "状态栏",
5264
5334
  "timeline": "时间线",
5335
+ "play": "播放",
5336
+ "pause": "暂停",
5337
+ "togglePlay": "播放/暂停",
5338
+ "seekStart": "回到开头",
5339
+ "seekEnd": "跳到结尾",
5340
+ "stepBackward": "后退一帧",
5341
+ "stepForward": "前进一帧",
5342
+ "collapse": "折叠",
5265
5343
  "creator": "创建节点",
5266
5344
  "memoryManager": "管理内存",
5267
5345
  "toolbelt": "工具腰带",
@@ -5323,7 +5401,7 @@ var _0_locale_default = defineMixin((editor, options) => {
5323
5401
  const { locale, localeMessages, fallbackMessages } = messages.value;
5324
5402
  const value = options.t?.(key, fallback) ?? localeMessages?.[key ?? -1] ?? fallbackMessages?.[key ?? -1] ?? localeMessages?.[fallback ?? -1] ?? fallbackMessages?.[fallback ?? -1];
5325
5403
  if (value === void 0) console.warn(`[mce] Not found '${key}' key in '${locale}' locale messages.`);
5326
- return value ?? key ?? fallback;
5404
+ return value ?? key ?? fallback ?? "";
5327
5405
  }
5328
5406
  Object.assign(editor, { t });
5329
5407
  });
@@ -5892,6 +5970,7 @@ var exporter_default = defineMixin((editor) => {
5892
5970
  exporters.delete(name);
5893
5971
  };
5894
5972
  const to = (name, options = {}) => {
5973
+ exportProgress.value = 0;
5895
5974
  const res = exporters.get(name)?.handle({
5896
5975
  ...options,
5897
5976
  onProgress: (progress) => {
@@ -5900,7 +5979,6 @@ var exporter_default = defineMixin((editor) => {
5900
5979
  }
5901
5980
  });
5902
5981
  if (res instanceof Promise) {
5903
- exportProgress.value = 0;
5904
5982
  exporting.value = true;
5905
5983
  return res.finally(() => {
5906
5984
  exporting.value = false;
@@ -6047,6 +6125,15 @@ function parseKey(key) {
6047
6125
  }
6048
6126
  }).filter(Boolean).sort().join("+");
6049
6127
  }
6128
+ var parsedKeyCache = /* @__PURE__ */ new Map();
6129
+ function parseKeyMemo(key) {
6130
+ let value = parsedKeyCache.get(key);
6131
+ if (value === void 0) {
6132
+ value = parseKey(key);
6133
+ parsedKeyCache.set(key, value);
6134
+ }
6135
+ return value;
6136
+ }
6050
6137
  function parseKeyboardEvent(event) {
6051
6138
  if (event.key.toLowerCase() === "capslock") return;
6052
6139
  const { code } = event;
@@ -6112,10 +6199,11 @@ var hotkey_default = defineMixin((editor) => {
6112
6199
  if (isInputEvent(e)) return;
6113
6200
  const eKey = parseKeyboardEvent(e);
6114
6201
  hotkeysData.value.forEach((hotkeyData) => {
6202
+ if (hotkeyData.enabled === false) return;
6115
6203
  const command = hotkeyData.command;
6116
6204
  const hotkey = hotkeys.get(command);
6117
6205
  (Array.isArray(hotkeyData.key) ? hotkeyData.key : [hotkeyData.key]).forEach((key) => {
6118
- if (eKey === parseKey(key) && (!hotkey?.when || hotkey.when(e))) {
6206
+ if (eKey === parseKeyMemo(key) && (!hotkey?.when || hotkey.when(e))) {
6119
6207
  if (hotkey?.preventDefault !== false) {
6120
6208
  e.preventDefault();
6121
6209
  e.stopPropagation();
@@ -6261,7 +6349,7 @@ var mixins = [
6261
6349
  const axisX = /* @__PURE__ */ new Set();
6262
6350
  const axisY = /* @__PURE__ */ new Set();
6263
6351
  snappers.forEach((snapper) => {
6264
- const { xLines, yLines } = snapper.get();
6352
+ const { xLines, yLines } = snapper.getLines();
6265
6353
  xLines?.forEach((v) => axisX.add(v));
6266
6354
  yLines?.forEach((v) => axisY.add(v));
6267
6355
  });
@@ -6307,7 +6395,7 @@ var mixins = [
6307
6395
  });
6308
6396
  }),
6309
6397
  defineMixin((editor) => {
6310
- const { isElement, frames, frameThumbs, log, fonts } = editor;
6398
+ const { isElement, frames, frameThumbs, log, fonts, runExclusiveRender } = editor;
6311
6399
  async function snapshot() {
6312
6400
  frameThumbs.value = frames.value.map(() => ({
6313
6401
  instanceId: -1,
@@ -6325,12 +6413,12 @@ var mixins = [
6325
6413
  data.style ??= {};
6326
6414
  data.style.top = 0;
6327
6415
  data.style.left = 0;
6328
- return await render({
6416
+ return await runExclusiveRender(() => render({
6329
6417
  width: data.style.width,
6330
6418
  height: data.style.height,
6331
6419
  fonts,
6332
6420
  data
6333
- });
6421
+ }));
6334
6422
  }
6335
6423
  async function captureFrameScreenshot(index) {
6336
6424
  const frame = frames.value[index];
@@ -6824,7 +6912,7 @@ var doc_default = definePlugin((editor, options) => {
6824
6912
  },
6825
6913
  {
6826
6914
  command: "newDoc",
6827
- handle: clearDoc
6915
+ handle: newDoc
6828
6916
  },
6829
6917
  {
6830
6918
  command: "openDoc",
@@ -6935,11 +7023,19 @@ var edit_default = definePlugin((editor, options) => {
6935
7023
  exec("delete");
6936
7024
  };
6937
7025
  let locked = false;
7026
+ let lockTimer;
6938
7027
  function getPasteLock() {
6939
7028
  return {
6940
7029
  locked,
6941
- lock: () => locked = true,
6942
- unlock: () => locked = false
7030
+ lock: () => {
7031
+ locked = true;
7032
+ clearTimeout(lockTimer);
7033
+ lockTimer = setTimeout(() => locked = false, 300);
7034
+ },
7035
+ unlock: () => {
7036
+ locked = false;
7037
+ clearTimeout(lockTimer);
7038
+ }
6943
7039
  };
6944
7040
  }
6945
7041
  async function _paste(items) {
@@ -7259,6 +7355,10 @@ var aliases = {
7259
7355
  close: "M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z",
7260
7356
  play: "M8 5.14v14l11-7z",
7261
7357
  pause: "M14 19h4V5h-4M6 19h4V5H6z",
7358
+ skipPrevious: "M6 18V6h2v12zm3.5-6L18 6v12z",
7359
+ skipNext: "M16 18h2V6h-2M6 18l8.5-6L6 6z",
7360
+ stepBackward: "M15.4 7.41L14 6l-6 6 6 6 1.4-1.4L10.83 12z",
7361
+ stepForward: "M8.6 7.41L10 6l6 6-6 6-1.4-1.4L13.17 12z",
7262
7362
  gps: "M12 8a4 4 0 0 1 4 4a4 4 0 0 1-4 4a4 4 0 0 1-4-4a4 4 0 0 1 4-4m-8.95 5H1v-2h2.05C3.5 6.83 6.83 3.5 11 3.05V1h2v2.05c4.17.45 7.5 3.78 7.95 7.95H23v2h-2.05c-.45 4.17-3.78 7.5-7.95 7.95V23h-2v-2.05C6.83 20.5 3.5 17.17 3.05 13M12 5a7 7 0 0 0-7 7a7 7 0 0 0 7 7a7 7 0 0 0 7-7a7 7 0 0 0-7-7",
7263
7363
  arrowRight: "M12.6 12L8 7.4L9.4 6l6 6l-6 6L8 16.6z",
7264
7364
  arrowDown: "M12 14.975q-.2 0-.375-.062T11.3 14.7l-4.6-4.6q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l3.9 3.9l3.9-3.9q.275-.275.7-.275t.7.275t.275.7t-.275.7l-4.6 4.6q-.15.15-.325.213t-.375.062",
@@ -7876,7 +7976,7 @@ var Frame_default = /* @__PURE__ */ defineComponent({
7876
7976
  });
7877
7977
  //#endregion
7878
7978
  //#region src/components/Frames.vue?vue&type=script&setup=true&lang.ts
7879
- var _hoisted_1$28 = { class: "m-frames" };
7979
+ var _hoisted_1$30 = { class: "m-frames" };
7880
7980
  //#endregion
7881
7981
  //#region src/components/Frames.vue
7882
7982
  var Frames_default = /* @__PURE__ */ defineComponent({
@@ -7885,7 +7985,7 @@ var Frames_default = /* @__PURE__ */ defineComponent({
7885
7985
  const { frames, getConfigRef } = useEditor();
7886
7986
  const config = getConfigRef("canvas.frame");
7887
7987
  return (_ctx, _cache) => {
7888
- return openBlock(), createElementBlock("div", _hoisted_1$28, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(frames), (frame, key) => {
7988
+ return openBlock(), createElementBlock("div", _hoisted_1$30, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(frames), (frame, key) => {
7889
7989
  return openBlock(), createBlock(Frame_default, {
7890
7990
  key,
7891
7991
  "model-value": frame,
@@ -8031,7 +8131,7 @@ var history_default = definePlugin((editor) => {
8031
8131
  });
8032
8132
  //#endregion
8033
8133
  //#region src/components/Hover.vue?vue&type=script&setup=true&lang.ts
8034
- var _hoisted_1$27 = ["data-name"];
8134
+ var _hoisted_1$29 = ["data-name"];
8035
8135
  //#endregion
8036
8136
  //#region src/components/Hover.vue
8037
8137
  var Hover_default = /* @__PURE__ */ defineComponent({
@@ -8050,7 +8150,7 @@ var Hover_default = /* @__PURE__ */ defineComponent({
8050
8150
  borderRadius: `${(unref(hoverElement).style.borderRadius ?? 0) * unref(camera).zoom.x}px`,
8051
8151
  ...hoverElementObb.value.toCssStyle()
8052
8152
  })
8053
- }, null, 12, _hoisted_1$27)) : createCommentVNode("", true);
8153
+ }, null, 12, _hoisted_1$29)) : createCommentVNode("", true);
8054
8154
  };
8055
8155
  }
8056
8156
  });
@@ -8100,7 +8200,7 @@ var html_default = definePlugin((editor) => {
8100
8200
  //#endregion
8101
8201
  //#region src/plugins/image.ts
8102
8202
  var image_default = definePlugin((editor) => {
8103
- const { exec, addElement, to, fonts, upload, drawboardEffect } = editor;
8203
+ const { exec, addElement, to, fonts, upload, drawboardEffect, runExclusiveRender } = editor;
8104
8204
  const insertImage = async (url, options) => {
8105
8205
  return addElement(await createImageElement(url), {
8106
8206
  sizeToFit: true,
@@ -8114,7 +8214,7 @@ var image_default = definePlugin((editor) => {
8114
8214
  saveAs: true,
8115
8215
  handle: async (options) => {
8116
8216
  const doc = await to("json", options);
8117
- const canvas = await render({
8217
+ const canvas = await runExclusiveRender(() => render({
8118
8218
  data: doc,
8119
8219
  fonts,
8120
8220
  width: doc.style.width,
@@ -8128,7 +8228,7 @@ var image_default = definePlugin((editor) => {
8128
8228
  pixelGrid: false
8129
8229
  }));
8130
8230
  }
8131
- });
8231
+ }));
8132
8232
  return await new Promise((resolve) => {
8133
8233
  canvas.toBlob((v) => resolve(v), name === "jpg" ? "image/jpeg" : `image/${name}`);
8134
8234
  });
@@ -8215,13 +8315,16 @@ var import_default = definePlugin((editor) => {
8215
8315
  e.preventDefault();
8216
8316
  if (e.dataTransfer) await exec("paste", e.dataTransfer);
8217
8317
  }
8318
+ let dom;
8218
8319
  onMounted(() => {
8219
- drawboardDom.value?.addEventListener("dragover", onDragover);
8220
- drawboardDom.value?.addEventListener("drop", onDrop);
8320
+ dom = drawboardDom.value;
8321
+ dom?.addEventListener("dragover", onDragover);
8322
+ dom?.addEventListener("drop", onDrop);
8221
8323
  });
8222
8324
  onScopeDispose(() => {
8223
- drawboardDom.value?.removeEventListener("dragover", onDragover);
8224
- drawboardDom.value?.removeEventListener("drop", onDrop);
8325
+ dom?.removeEventListener("dragover", onDragover);
8326
+ dom?.removeEventListener("drop", onDrop);
8327
+ dom = void 0;
8225
8328
  });
8226
8329
  }
8227
8330
  };
@@ -8316,10 +8419,10 @@ var Btn_default = /* @__PURE__ */ defineComponent({
8316
8419
  });
8317
8420
  //#endregion
8318
8421
  //#region src/components/Layer.vue?vue&type=script&setup=true&lang.ts
8319
- var _hoisted_1$26 = ["data-id"];
8320
- var _hoisted_2$12 = { class: "m-layer__content" };
8321
- var _hoisted_3$11 = { class: "m-layer__prepend" };
8322
- var _hoisted_4$5 = {
8422
+ var _hoisted_1$28 = ["data-id"];
8423
+ var _hoisted_2$14 = { class: "m-layer__content" };
8424
+ var _hoisted_3$13 = { class: "m-layer__prepend" };
8425
+ var _hoisted_4$7 = {
8323
8426
  key: 0,
8324
8427
  class: "m-layer__name"
8325
8428
  };
@@ -8463,8 +8566,8 @@ var Layer_default = /* @__PURE__ */ defineComponent({
8463
8566
  }, [
8464
8567
  _cache[5] || (_cache[5] = createElementVNode("span", { class: "m-layer__underlay" }, null, -1)),
8465
8568
  _cache[6] || (_cache[6] = createElementVNode("span", { class: "m-layer__overlay" }, null, -1)),
8466
- createElementVNode("div", _hoisted_2$12, [
8467
- createElementVNode("div", _hoisted_3$11, [childrenLength.value ? (openBlock(), createBlock(unref(Icon_default), {
8569
+ createElementVNode("div", _hoisted_2$14, [
8570
+ createElementVNode("div", _hoisted_3$13, [childrenLength.value ? (openBlock(), createBlock(unref(Icon_default), {
8468
8571
  key: 0,
8469
8572
  class: "m-layer__arrow",
8470
8573
  icon: "$arrowRight",
@@ -8475,7 +8578,7 @@ var Layer_default = /* @__PURE__ */ defineComponent({
8475
8578
  class: "m-layer__thumbnail",
8476
8579
  onDblclick: onDblclickThumbnail
8477
8580
  }, [createVNode(unref(Icon_default), { icon: unref(thumbnailIcon) }, null, 8, ["icon"])], 32),
8478
- props.root ? (openBlock(), createElementBlock("div", _hoisted_4$5, toDisplayString(unref(t)("layers")), 1)) : (openBlock(), createElementBlock("div", {
8581
+ props.root ? (openBlock(), createElementBlock("div", _hoisted_4$7, toDisplayString(unref(t)("layers")), 1)) : (openBlock(), createElementBlock("div", {
8479
8582
  key: 1,
8480
8583
  class: "m-layer__name",
8481
8584
  onDblclick: onDblclickName
@@ -8522,7 +8625,7 @@ var Layer_default = /* @__PURE__ */ defineComponent({
8522
8625
  _: 1
8523
8626
  }, 8, ["class"])], 64))], 2)
8524
8627
  ])
8525
- ], 46, _hoisted_1$26), opened.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(childrenLength.value, (i) => {
8628
+ ], 46, _hoisted_1$28), opened.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(childrenLength.value, (i) => {
8526
8629
  return openBlock(), createBlock(_component_MceLayer, {
8527
8630
  key: i,
8528
8631
  node: children.value[childrenLength.value - i],
@@ -8539,8 +8642,8 @@ var Layer_default = /* @__PURE__ */ defineComponent({
8539
8642
  });
8540
8643
  //#endregion
8541
8644
  //#region src/components/Layers.vue?vue&type=script&setup=true&lang.ts
8542
- var _hoisted_1$25 = { class: "m-layers" };
8543
- var _hoisted_2$11 = { class: "m-layers__wrapper" };
8645
+ var _hoisted_1$27 = { class: "m-layers" };
8646
+ var _hoisted_2$13 = { class: "m-layers__wrapper" };
8544
8647
  //#endregion
8545
8648
  //#region src/components/Layers.vue
8546
8649
  var Layers_default = /* @__PURE__ */ defineComponent({
@@ -8574,7 +8677,7 @@ var Layers_default = /* @__PURE__ */ defineComponent({
8574
8677
  layerScrollIntoView();
8575
8678
  });
8576
8679
  return (_ctx, _cache) => {
8577
- return openBlock(), createElementBlock("div", _hoisted_1$25, [createElementVNode("div", _hoisted_2$11, [createVNode(Layer_default, {
8680
+ return openBlock(), createElementBlock("div", _hoisted_1$27, [createElementVNode("div", _hoisted_2$13, [createVNode(Layer_default, {
8578
8681
  root: true,
8579
8682
  node: unref(root),
8580
8683
  opened: true
@@ -8609,13 +8712,13 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
8609
8712
  //#endregion
8610
8713
  //#region src/components/MadeWith.vue
8611
8714
  var _sfc_main = {};
8612
- var _hoisted_1$24 = {
8715
+ var _hoisted_1$26 = {
8613
8716
  class: "m-made-with",
8614
8717
  href: "https://github.com/qq15725/mce",
8615
8718
  target: "_blank"
8616
8719
  };
8617
8720
  function _sfc_render(_ctx, _cache) {
8618
- return openBlock(), createElementBlock("a", _hoisted_1$24, [..._cache[0] || (_cache[0] = [createElementVNode("div", null, "MADE WITH", -1), createElementVNode("div", null, "MCE", -1)])]);
8721
+ return openBlock(), createElementBlock("a", _hoisted_1$26, [..._cache[0] || (_cache[0] = [createElementVNode("div", null, "MADE WITH", -1), createElementVNode("div", null, "MCE", -1)])]);
8619
8722
  }
8620
8723
  var MadeWith_default = /* @__PURE__ */ _plugin_vue_export_helper_default(_sfc_main, [["render", _sfc_render]]);
8621
8724
  //#endregion
@@ -8637,7 +8740,7 @@ var madeWith_default = definePlugin((editor) => {
8637
8740
  });
8638
8741
  //#endregion
8639
8742
  //#region src/components/MemoryManager.vue?vue&type=script&setup=true&lang.ts
8640
- var _hoisted_1$23 = { class: "m-manage-memory" };
8743
+ var _hoisted_1$25 = { class: "m-manage-memory" };
8641
8744
  //#endregion
8642
8745
  //#region src/components/MemoryManager.vue
8643
8746
  var MemoryManager_default = /* @__PURE__ */ defineComponent({
@@ -8670,7 +8773,7 @@ var MemoryManager_default = /* @__PURE__ */ defineComponent({
8670
8773
  });
8671
8774
  onBeforeUnmount(() => timer && clearInterval(timer));
8672
8775
  return (_ctx, _cache) => {
8673
- return openBlock(), createElementBlock("div", _hoisted_1$23, [_cache[0] || (_cache[0] = createElementVNode("div", null, "Total memory used", -1)), createElementVNode("div", null, toDisplayString(humanBytes(used.value)), 1)]);
8776
+ return openBlock(), createElementBlock("div", _hoisted_1$25, [_cache[0] || (_cache[0] = createElementVNode("div", null, "Total memory used", -1)), createElementVNode("div", null, toDisplayString(humanBytes(used.value)), 1)]);
8674
8777
  };
8675
8778
  }
8676
8779
  });
@@ -8794,22 +8897,22 @@ var Overlay_default = /* @__PURE__ */ defineComponent({
8794
8897
  });
8795
8898
  //#endregion
8796
8899
  //#region src/components/shared/Menu.vue?vue&type=script&setup=true&lang.ts
8797
- var _hoisted_1$22 = ["onMouseenter"];
8798
- var _hoisted_2$10 = ["onClick"];
8799
- var _hoisted_3$10 = {
8900
+ var _hoisted_1$24 = ["onMouseenter"];
8901
+ var _hoisted_2$12 = ["onClick"];
8902
+ var _hoisted_3$12 = {
8800
8903
  key: 0,
8801
8904
  class: "m-list-item__checked"
8802
8905
  };
8803
- var _hoisted_4$4 = {
8906
+ var _hoisted_4$6 = {
8804
8907
  key: 1,
8805
8908
  class: "m-list-item__prepend"
8806
8909
  };
8807
- var _hoisted_5$3 = { class: "m-list-item__title" };
8910
+ var _hoisted_5$4 = { class: "m-list-item__title" };
8808
8911
  var _hoisted_6$3 = {
8809
8912
  key: 2,
8810
8913
  class: "m-list-item__kbd"
8811
8914
  };
8812
- var _hoisted_7$2 = {
8915
+ var _hoisted_7$3 = {
8813
8916
  key: 3,
8814
8917
  class: "m-list-item__append"
8815
8918
  };
@@ -8921,18 +9024,18 @@ var Menu_default = /* @__PURE__ */ defineComponent({
8921
9024
  class: normalizeClass(["m-list-item", [item.disabled && "m-list-item--disabled", opened.value === index && "m-list-item--opened"]]),
8922
9025
  onClick: (e) => onClickItem(item, index, e)
8923
9026
  }, [
8924
- hasPrepend.value ? (openBlock(), createElementBlock("div", _hoisted_3$10, [item.checked ? (openBlock(), createBlock(unref(Icon_default), {
9027
+ hasPrepend.value ? (openBlock(), createElementBlock("div", _hoisted_3$12, [item.checked ? (openBlock(), createBlock(unref(Icon_default), {
8925
9028
  key: 0,
8926
9029
  icon: "$check"
8927
9030
  })) : createCommentVNode("", true)])) : createCommentVNode("", true),
8928
- _ctx.$slots.prepend ? (openBlock(), createElementBlock("div", _hoisted_4$4, [renderSlot(_ctx.$slots, "prepend", { item })])) : createCommentVNode("", true),
8929
- createElementVNode("div", _hoisted_5$3, [renderSlot(_ctx.$slots, "title", { item }, () => [createTextVNode(toDisplayString(item.key), 1)])]),
9031
+ _ctx.$slots.prepend ? (openBlock(), createElementBlock("div", _hoisted_4$6, [renderSlot(_ctx.$slots, "prepend", { item })])) : createCommentVNode("", true),
9032
+ createElementVNode("div", _hoisted_5$4, [renderSlot(_ctx.$slots, "title", { item }, () => [createTextVNode(toDisplayString(item.key), 1)])]),
8930
9033
  _ctx.$slots.kbd ? (openBlock(), createElementBlock("div", _hoisted_6$3, [renderSlot(_ctx.$slots, "kbd", { item })])) : createCommentVNode("", true),
8931
- item.children?.length || _ctx.$slots.append ? (openBlock(), createElementBlock("div", _hoisted_7$2, [renderSlot(_ctx.$slots, "append", { item }), item.children?.length ? (openBlock(), createBlock(unref(Icon_default), {
9034
+ item.children?.length || _ctx.$slots.append ? (openBlock(), createElementBlock("div", _hoisted_7$3, [renderSlot(_ctx.$slots, "append", { item }), item.children?.length ? (openBlock(), createBlock(unref(Icon_default), {
8932
9035
  key: 0,
8933
9036
  icon: "$arrowRight"
8934
9037
  })) : createCommentVNode("", true)])) : createCommentVNode("", true)
8935
- ], 10, _hoisted_2$10)], 40, _hoisted_1$22))], 64);
9038
+ ], 10, _hoisted_2$12)], 40, _hoisted_1$24))], 64);
8936
9039
  }), 128)), opened.value > -1 && __props.items?.[opened.value]?.children?.length ? (openBlock(), createBlock(_component_MceMenu, {
8937
9040
  key: 0,
8938
9041
  "open-on-hover": "",
@@ -9442,9 +9545,9 @@ var menu_default = definePlugin((editor, options) => {
9442
9545
  });
9443
9546
  //#endregion
9444
9547
  //#region src/components/Creator.vue?vue&type=script&setup=true&lang.ts
9445
- var _hoisted_1$21 = { class: "m-creator" };
9446
- var _hoisted_2$9 = { class: "m-creator__tree" };
9447
- var _hoisted_3$9 = { class: "m-creator__actions" };
9548
+ var _hoisted_1$23 = { class: "m-creator" };
9549
+ var _hoisted_2$11 = { class: "m-creator__tree" };
9550
+ var _hoisted_3$11 = { class: "m-creator__actions" };
9448
9551
  //#endregion
9449
9552
  //#region src/components/Creator.vue
9450
9553
  var Creator_default = /* @__PURE__ */ defineComponent({
@@ -9515,12 +9618,12 @@ var Creator_default = /* @__PURE__ */ defineComponent({
9515
9618
  })];
9516
9619
  }
9517
9620
  return (_ctx, _cache) => {
9518
- return openBlock(), createElementBlock("div", _hoisted_1$21, [createElementVNode("div", _hoisted_2$9, [(openBlock(true), createElementBlock(Fragment, null, renderList(tree.value, (node, index) => {
9621
+ return openBlock(), createElementBlock("div", _hoisted_1$23, [createElementVNode("div", _hoisted_2$11, [(openBlock(true), createElementBlock(Fragment, null, renderList(tree.value, (node, index) => {
9519
9622
  return openBlock(), createBlock(CreatorNode, {
9520
9623
  key: index,
9521
9624
  node
9522
9625
  }, null, 8, ["node"]);
9523
- }), 128))]), createElementVNode("div", _hoisted_3$9, [createVNode(Btn_default, { onClick: cancel }, {
9626
+ }), 128))]), createElementVNode("div", _hoisted_3$11, [createVNode(Btn_default, { onClick: cancel }, {
9524
9627
  default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("cancel")), 1)]),
9525
9628
  _: 1
9526
9629
  }), createVNode(Btn_default, { onClick: create }, {
@@ -9556,10 +9659,13 @@ var node_default = definePlugin((editor) => {
9556
9659
  //#endregion
9557
9660
  //#region src/plugins/pen.ts
9558
9661
  var pen_default = definePlugin((editor) => {
9559
- const { addElement, renderEngine, activeTool, getGlobalPointer, state } = editor;
9662
+ const { addElement, renderEngine, activeTool, activateTool, getGlobalPointer, camera, selection, state } = editor;
9560
9663
  let el;
9561
9664
  let currentPath;
9562
9665
  let currentLine;
9666
+ let onMove;
9667
+ let onKey;
9668
+ let stopWatch;
9563
9669
  const update = () => {
9564
9670
  if (el && currentPath) {
9565
9671
  el.shape.paths = [{ data: currentPath.toData() }];
@@ -9570,17 +9676,42 @@ var pen_default = definePlugin((editor) => {
9570
9676
  el.style.height = box.height || 1;
9571
9677
  }
9572
9678
  };
9679
+ function finish(close = false) {
9680
+ if (!el || !currentPath) return;
9681
+ const curves = currentPath.currentCurve.curves;
9682
+ if (currentLine && curves[curves.length - 1] === currentLine) curves.pop();
9683
+ if (close && curves.length) currentPath.currentCurve.closePath();
9684
+ const placed = curves.length;
9685
+ const ref = el;
9686
+ if (placed > 0) update();
9687
+ renderEngine.value.off("pointermove", onMove);
9688
+ if (onKey) window.removeEventListener("keydown", onKey);
9689
+ stopWatch?.();
9690
+ stopWatch = void 0;
9691
+ onMove = void 0;
9692
+ onKey = void 0;
9693
+ el = void 0;
9694
+ currentPath = void 0;
9695
+ currentLine = void 0;
9696
+ if (placed > 0) selection.value = [ref];
9697
+ else ref.remove();
9698
+ }
9573
9699
  return {
9574
9700
  name: "mce:pen",
9575
9701
  tools: [{
9576
9702
  name: "pen",
9577
9703
  handle: (start) => {
9578
- if (el) {
9579
- if (currentPath) {
9580
- currentLine = new LineCurve(new Vector2(start.x, start.y), new Vector2(start.x, start.y));
9581
- currentPath.currentCurve.addCurve(currentLine);
9582
- update();
9704
+ if (el && currentPath) {
9705
+ const first = currentPath.currentCurve.curves[0];
9706
+ const threshold = 8 / (camera.value.zoom.x || 1);
9707
+ if (first && currentPath.currentCurve.curves.length > 1 && first.p1.distanceTo(start) < threshold) {
9708
+ finish(true);
9709
+ activateTool(void 0);
9710
+ return;
9583
9711
  }
9712
+ currentLine = new LineCurve(new Vector2(start.x, start.y), new Vector2(start.x, start.y));
9713
+ currentPath.currentCurve.addCurve(currentLine);
9714
+ update();
9584
9715
  return;
9585
9716
  }
9586
9717
  el = addElement({
@@ -9601,7 +9732,7 @@ var pen_default = definePlugin((editor) => {
9601
9732
  currentLine = new LineCurve(new Vector2(start.x, start.y), new Vector2(start.x, start.y));
9602
9733
  currentPath.currentCurve.addCurve(currentLine);
9603
9734
  update();
9604
- const onMove = () => {
9735
+ onMove = () => {
9605
9736
  const move = getGlobalPointer();
9606
9737
  if (currentLine && move) {
9607
9738
  currentLine.p2.x = move.x;
@@ -9610,17 +9741,15 @@ var pen_default = definePlugin((editor) => {
9610
9741
  }
9611
9742
  };
9612
9743
  renderEngine.value.on("pointermove", onMove);
9613
- let stopWatch;
9614
- const cleanup = () => {
9615
- renderEngine.value.off("pointermove", onMove);
9616
- stopWatch?.();
9617
- stopWatch = void 0;
9618
- el = void 0;
9619
- currentPath = void 0;
9620
- currentLine = void 0;
9744
+ onKey = (e) => {
9745
+ if (e.key === "Enter" || e.key === "Escape") {
9746
+ e.preventDefault();
9747
+ finish(false);
9748
+ activateTool(void 0);
9749
+ }
9621
9750
  };
9622
- stopWatch = watch([state, activeTool], cleanup);
9623
- return { end: cleanup };
9751
+ window.addEventListener("keydown", onKey);
9752
+ stopWatch = watch([state, activeTool], () => finish(false));
9624
9753
  }
9625
9754
  }, {
9626
9755
  name: "pencil",
@@ -9669,12 +9798,12 @@ var pen_default = definePlugin((editor) => {
9669
9798
  });
9670
9799
  //#endregion
9671
9800
  //#region src/components/shared/Tooltip.vue?vue&type=script&setup=true&lang.ts
9672
- var _hoisted_1$20 = {
9801
+ var _hoisted_1$22 = {
9673
9802
  key: 0,
9674
9803
  class: "m-tooltip__arrow"
9675
9804
  };
9676
- var _hoisted_2$8 = { class: "m-tooltip__content" };
9677
- var _hoisted_3$8 = {
9805
+ var _hoisted_2$10 = { class: "m-tooltip__content" };
9806
+ var _hoisted_3$10 = {
9678
9807
  key: 0,
9679
9808
  class: "m-tooltip__kbd"
9680
9809
  };
@@ -9717,7 +9846,7 @@ var Tooltip_default = /* @__PURE__ */ defineComponent({
9717
9846
  target: props.target,
9718
9847
  attach: props.attach
9719
9848
  }, createSlots({
9720
- default: withCtx(() => [isActive.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.showArrow ? (openBlock(), createElementBlock("div", _hoisted_1$20)) : createCommentVNode("", true), createElementVNode("div", _hoisted_2$8, [renderSlot(_ctx.$slots, "default"), _ctx.$slots.kbd ? (openBlock(), createElementBlock("div", _hoisted_3$8, [renderSlot(_ctx.$slots, "kbd")])) : createCommentVNode("", true)])], 64)) : createCommentVNode("", true)]),
9849
+ default: withCtx(() => [isActive.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.showArrow ? (openBlock(), createElementBlock("div", _hoisted_1$22)) : createCommentVNode("", true), createElementVNode("div", _hoisted_2$10, [renderSlot(_ctx.$slots, "default"), _ctx.$slots.kbd ? (openBlock(), createElementBlock("div", _hoisted_3$10, [renderSlot(_ctx.$slots, "kbd")])) : createCommentVNode("", true)])], 64)) : createCommentVNode("", true)]),
9721
9850
  _: 2
9722
9851
  }, [_ctx.$slots.activator ? {
9723
9852
  name: "activator",
@@ -9740,13 +9869,13 @@ var Tooltip_default = /* @__PURE__ */ defineComponent({
9740
9869
  });
9741
9870
  //#endregion
9742
9871
  //#region src/components/shared/Ruler.vue?vue&type=script&setup=true&lang.ts
9743
- var _hoisted_1$19 = ["width", "height"];
9744
- var _hoisted_2$7 = [
9872
+ var _hoisted_1$21 = ["width", "height"];
9873
+ var _hoisted_2$9 = [
9745
9874
  "onDblclick",
9746
9875
  "onMousedown",
9747
9876
  "onMousemove"
9748
9877
  ];
9749
- var _hoisted_3$7 = { style: {
9878
+ var _hoisted_3$9 = { style: {
9750
9879
  "font-size": "0.75rem",
9751
9880
  "text-wrap": "nowrap"
9752
9881
  } };
@@ -10017,7 +10146,7 @@ var Ruler_default = /* @__PURE__ */ defineComponent({
10017
10146
  class: "m-ruler__canvas",
10018
10147
  width: props.size,
10019
10148
  height: props.size
10020
- }, null, 8, _hoisted_1$19)], 16)), [[unref(vResizeObserver), unref(resize)]]),
10149
+ }, null, 8, _hoisted_1$21)], 16)), [[unref(vResizeObserver), unref(resize)]]),
10021
10150
  (openBlock(true), createElementBlock(Fragment, null, renderList(lines.value, (item, index) => {
10022
10151
  return openBlock(), createElementBlock("div", {
10023
10152
  key: index,
@@ -10038,7 +10167,7 @@ var Ruler_default = /* @__PURE__ */ defineComponent({
10038
10167
  onMousedown: ($event) => onReflineMousedown($event, index),
10039
10168
  onMousemove: (e) => updateTip(e, item),
10040
10169
  onMouseleave: onLeave
10041
- }, null, 46, _hoisted_2$7);
10170
+ }, null, 46, _hoisted_2$9);
10042
10171
  }), 128)),
10043
10172
  createVNode(Tooltip_default, {
10044
10173
  "model-value": !!tipText.value,
@@ -10046,7 +10175,7 @@ var Ruler_default = /* @__PURE__ */ defineComponent({
10046
10175
  offset: 24,
10047
10176
  location: props.vertical ? "bottom" : "right"
10048
10177
  }, {
10049
- default: withCtx(() => [createElementVNode("div", _hoisted_3$7, toDisplayString(tipText.value), 1)]),
10178
+ default: withCtx(() => [createElementVNode("div", _hoisted_3$9, toDisplayString(tipText.value), 1)]),
10050
10179
  _: 1
10051
10180
  }, 8, [
10052
10181
  "model-value",
@@ -10059,7 +10188,7 @@ var Ruler_default = /* @__PURE__ */ defineComponent({
10059
10188
  });
10060
10189
  //#endregion
10061
10190
  //#region src/components/Rulers.vue?vue&type=script&setup=true&lang.ts
10062
- var _hoisted_1$18 = { class: "m-rulers" };
10191
+ var _hoisted_1$20 = { class: "m-rulers" };
10063
10192
  //#endregion
10064
10193
  //#region src/components/Rulers.vue
10065
10194
  var Rulers_default = /* @__PURE__ */ defineComponent({
@@ -10074,7 +10203,7 @@ var Rulers_default = /* @__PURE__ */ defineComponent({
10074
10203
  const { getConfigRef, camera, selectionAabbInDrawboard } = useEditor();
10075
10204
  const config = getConfigRef("ui.ruler");
10076
10205
  return (_ctx, _cache) => {
10077
- return openBlock(), createElementBlock("div", _hoisted_1$18, [
10206
+ return openBlock(), createElementBlock("div", _hoisted_1$20, [
10078
10207
  createVNode(Ruler_default, {
10079
10208
  modelValue: refLines.value.x,
10080
10209
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => refLines.value.x = $event),
@@ -10131,7 +10260,7 @@ var ruler_default = definePlugin((editor) => {
10131
10260
  x: [],
10132
10261
  y: []
10133
10262
  });
10134
- registerSnapper("ruler", { get: () => {
10263
+ registerSnapper("ruler", { getLines: () => {
10135
10264
  return {
10136
10265
  xLines: refLines.value.x,
10137
10266
  yLines: refLines.value.y
@@ -10188,7 +10317,7 @@ var saveAs_default = definePlugin((editor) => {
10188
10317
  });
10189
10318
  //#endregion
10190
10319
  //#region src/components/shared/Scrollbar.vue?vue&type=script&setup=true&lang.ts
10191
- var _hoisted_1$17 = {
10320
+ var _hoisted_1$19 = {
10192
10321
  ref: "trackTplRef",
10193
10322
  class: "m-scrollbar__track"
10194
10323
  };
@@ -10271,7 +10400,7 @@ var Scrollbar_default = /* @__PURE__ */ defineComponent({
10271
10400
  [props.vertical ? "width" : "height"]: `${props.size}px`,
10272
10401
  [props.vertical ? "top" : "left"]: `${props.offset}px`
10273
10402
  })
10274
- }, [createElementVNode("div", _hoisted_1$17, [createElementVNode("div", {
10403
+ }, [createElementVNode("div", _hoisted_1$19, [createElementVNode("div", {
10275
10404
  ref: "thumbTplRef",
10276
10405
  class: normalizeClass(["m-scrollbar__thumb", { "m-scrollbar__thumb--active": isActive.value }]),
10277
10406
  style: normalizeStyle({
@@ -10287,7 +10416,7 @@ var Scrollbar_default = /* @__PURE__ */ defineComponent({
10287
10416
  });
10288
10417
  //#endregion
10289
10418
  //#region src/components/Scrollbars.vue?vue&type=script&setup=true&lang.ts
10290
- var _hoisted_1$16 = { class: "m-scrollbars" };
10419
+ var _hoisted_1$18 = { class: "m-scrollbars" };
10291
10420
  //#endregion
10292
10421
  //#region src/components/Scrollbars.vue
10293
10422
  var Scrollbars_default = /* @__PURE__ */ defineComponent({
@@ -10300,7 +10429,7 @@ var Scrollbars_default = /* @__PURE__ */ defineComponent({
10300
10429
  const props = __props;
10301
10430
  const { camera, rootAabb } = useEditor();
10302
10431
  return (_ctx, _cache) => {
10303
- return openBlock(), createElementBlock("div", _hoisted_1$16, [createVNode(Scrollbar_default, mergeProps(props, {
10432
+ return openBlock(), createElementBlock("div", _hoisted_1$18, [createVNode(Scrollbar_default, mergeProps(props, {
10304
10433
  modelValue: unref(camera).position.y,
10305
10434
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => unref(camera).position.y = $event),
10306
10435
  vertical: "",
@@ -10356,7 +10485,7 @@ var scroll_default = definePlugin((editor) => {
10356
10485
  x: aabb.left + aabb.width / 2,
10357
10486
  y: aabb.top + aabb.height / 2
10358
10487
  };
10359
- offset.x += -_screenCenter.x;
10488
+ offset.x = -_screenCenter.x;
10360
10489
  offset.y = -_screenCenter.y;
10361
10490
  }
10362
10491
  if (targetPosition.x !== void 0) {
@@ -10435,22 +10564,22 @@ var ScrollToSelection_default = /* @__PURE__ */ defineComponent({
10435
10564
  });
10436
10565
  //#endregion
10437
10566
  //#region src/components/shared/Transform.vue?vue&type=script&setup=true&lang.ts
10438
- var _hoisted_1$15 = ["rx", "ry"];
10439
- var _hoisted_2$6 = { "pointer-events": "none" };
10440
- var _hoisted_3$6 = [
10567
+ var _hoisted_1$17 = ["rx", "ry"];
10568
+ var _hoisted_2$8 = { "pointer-events": "none" };
10569
+ var _hoisted_3$8 = [
10441
10570
  "x",
10442
10571
  "y",
10443
10572
  "width",
10444
10573
  "height",
10445
10574
  "aria-label"
10446
10575
  ];
10447
- var _hoisted_4$3 = [
10576
+ var _hoisted_4$5 = [
10448
10577
  "cx",
10449
10578
  "cy",
10450
10579
  "r",
10451
10580
  "aria-label"
10452
10581
  ];
10453
- var _hoisted_5$2 = [
10582
+ var _hoisted_5$3 = [
10454
10583
  "x",
10455
10584
  "y",
10456
10585
  "width",
@@ -10460,8 +10589,8 @@ var _hoisted_5$2 = [
10460
10589
  "ry"
10461
10590
  ];
10462
10591
  var _hoisted_6$2 = ["transform"];
10463
- var _hoisted_7$1 = { "pointer-events": "all" };
10464
- var _hoisted_8$1 = [
10592
+ var _hoisted_7$2 = { "pointer-events": "all" };
10593
+ var _hoisted_8$2 = [
10465
10594
  "x",
10466
10595
  "y",
10467
10596
  "width",
@@ -10470,11 +10599,11 @@ var _hoisted_8$1 = [
10470
10599
  "cursor",
10471
10600
  "onPointerdown"
10472
10601
  ];
10473
- var _hoisted_9$1 = {
10602
+ var _hoisted_9$2 = {
10474
10603
  "pointer-events": "all",
10475
10604
  class: "m-transform__svg-slot"
10476
10605
  };
10477
- var _hoisted_10$1 = {
10606
+ var _hoisted_10$2 = {
10478
10607
  key: 0,
10479
10608
  class: "m-transform__tip"
10480
10609
  };
@@ -11072,9 +11201,9 @@ var Transform_default = /* @__PURE__ */ defineComponent({
11072
11201
  fill: "none",
11073
11202
  rx: model.value.borderRadius,
11074
11203
  ry: model.value.borderRadius
11075
- }, null, 8, _hoisted_1$15),
11204
+ }, null, 8, _hoisted_1$17),
11076
11205
  createVNode(Diagonal),
11077
- createElementVNode("g", _hoisted_2$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
11206
+ createElementVNode("g", _hoisted_2$8, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
11078
11207
  return openBlock(), createElementBlock(Fragment, { key: index }, [handle.shape ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [handle.shape === "rect" ? (openBlock(), createElementBlock("rect", {
11079
11208
  key: 0,
11080
11209
  x: handle.x,
@@ -11083,14 +11212,14 @@ var Transform_default = /* @__PURE__ */ defineComponent({
11083
11212
  height: handle.height,
11084
11213
  "aria-label": handle.type,
11085
11214
  class: "m-transform__handle"
11086
- }, null, 8, _hoisted_3$6)) : handle.width === handle.height ? (openBlock(), createElementBlock("circle", {
11215
+ }, null, 8, _hoisted_3$8)) : handle.width === handle.height ? (openBlock(), createElementBlock("circle", {
11087
11216
  key: 1,
11088
11217
  cx: handle.x + handle.width / 2,
11089
11218
  cy: handle.y + handle.width / 2,
11090
11219
  r: handle.width / 2,
11091
11220
  "aria-label": handle.type,
11092
11221
  class: "m-transform__handle"
11093
- }, null, 8, _hoisted_4$3)) : (openBlock(), createElementBlock("rect", {
11222
+ }, null, 8, _hoisted_4$5)) : (openBlock(), createElementBlock("rect", {
11094
11223
  key: 2,
11095
11224
  x: handle.x,
11096
11225
  y: handle.y,
@@ -11100,13 +11229,13 @@ var Transform_default = /* @__PURE__ */ defineComponent({
11100
11229
  rx: handle.width / 4,
11101
11230
  ry: handle.height / 4,
11102
11231
  class: "m-transform__handle"
11103
- }, null, 8, _hoisted_5$2))], 64)) : createCommentVNode("", true)], 64);
11232
+ }, null, 8, _hoisted_5$3))], 64)) : createCommentVNode("", true)], 64);
11104
11233
  }), 128)), __props.rotator ? (openBlock(), createElementBlock("g", {
11105
11234
  key: 0,
11106
11235
  transform: `matrix(1, 0, 0, 1, -32, ${model.value.height}) rotate(270 16 16)`,
11107
11236
  class: "m-transform__rotator"
11108
11237
  }, [..._cache[0] || (_cache[0] = [createElementVNode("path", { d: "M22.4789 9.45728L25.9935 12.9942L22.4789 16.5283V14.1032C18.126 14.1502 14.6071 17.6737 14.5675 22.0283H17.05L13.513 25.543L9.97889 22.0283H12.5674C12.6071 16.5691 17.0214 12.1503 22.4789 12.1031L22.4789 9.45728Z" }, null, -1)])], 8, _hoisted_6$2)) : createCommentVNode("", true)]),
11109
- createElementVNode("g", _hoisted_7$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
11238
+ createElementVNode("g", _hoisted_7$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
11110
11239
  return openBlock(), createElementBlock("rect", {
11111
11240
  key: index,
11112
11241
  ref_for: true,
@@ -11120,11 +11249,11 @@ var Transform_default = /* @__PURE__ */ defineComponent({
11120
11249
  class: "m-transform__handle-rect",
11121
11250
  cursor: transforming.value ? "auto" : getCursor(handle.type),
11122
11251
  onPointerdown: (event) => onPointerDown(event, index)
11123
- }, null, 40, _hoisted_8$1);
11252
+ }, null, 40, _hoisted_8$2);
11124
11253
  }), 128))]),
11125
- createElementVNode("g", _hoisted_9$1, [renderSlot(_ctx.$slots, "svg", { box: model.value })])
11254
+ createElementVNode("g", _hoisted_9$2, [renderSlot(_ctx.$slots, "svg", { box: model.value })])
11126
11255
  ], 4)),
11127
- tip.value ? (openBlock(), createElementBlock("div", _hoisted_10$1, toDisplayString(tip.value), 1)) : createCommentVNode("", true)
11256
+ tip.value ? (openBlock(), createElementBlock("div", _hoisted_10$2, toDisplayString(tip.value), 1)) : createCommentVNode("", true)
11128
11257
  ]),
11129
11258
  _: 3
11130
11259
  }, 8, ["class", "style"]);
@@ -11133,7 +11262,7 @@ var Transform_default = /* @__PURE__ */ defineComponent({
11133
11262
  });
11134
11263
  //#endregion
11135
11264
  //#region src/components/shared/Cropper.vue?vue&type=script&setup=true&lang.ts
11136
- var _hoisted_1$14 = { class: "m-cropper" };
11265
+ var _hoisted_1$16 = { class: "m-cropper" };
11137
11266
  //#endregion
11138
11267
  //#region src/components/shared/Cropper.vue
11139
11268
  var Cropper_default = /* @__PURE__ */ defineComponent({
@@ -11332,7 +11461,7 @@ var Cropper_default = /* @__PURE__ */ defineComponent({
11332
11461
  onBeforeMount(() => emit("start"));
11333
11462
  onBeforeUnmount(() => emit("end"));
11334
11463
  return (_ctx, _cache) => {
11335
- return withDirectives((openBlock(), createElementBlock("div", _hoisted_1$14, [
11464
+ return withDirectives((openBlock(), createElementBlock("div", _hoisted_1$16, [
11336
11465
  createElementVNode("div", {
11337
11466
  class: "m-cropper__source",
11338
11467
  style: normalizeStyle(unref(boundingBoxToStyle)(sourceTransform.value))
@@ -11405,8 +11534,757 @@ var ForegroundCropper_default = /* @__PURE__ */ defineComponent({
11405
11534
  }
11406
11535
  });
11407
11536
  //#endregion
11537
+ //#region src/components/PathEditor.vue?vue&type=script&setup=true&lang.ts
11538
+ var _hoisted_1$15 = {
11539
+ ref: "svgTpl",
11540
+ class: "m-path-editor",
11541
+ style: { overflow: "visible" }
11542
+ };
11543
+ var _hoisted_2$7 = ["d"];
11544
+ var _hoisted_3$7 = [
11545
+ "x1",
11546
+ "y1",
11547
+ "x2",
11548
+ "y2"
11549
+ ];
11550
+ var _hoisted_4$4 = [
11551
+ "cx",
11552
+ "cy",
11553
+ "onPointerdown"
11554
+ ];
11555
+ var _hoisted_5$2 = [
11556
+ "x",
11557
+ "y",
11558
+ "onPointerdown",
11559
+ "onDblclick"
11560
+ ];
11561
+ //#endregion
11562
+ //#region src/components/PathEditor.vue
11563
+ var PathEditor_default = /* @__PURE__ */ defineComponent({
11564
+ __name: "PathEditor",
11565
+ props: {
11566
+ element: {},
11567
+ scale: {},
11568
+ offset: {}
11569
+ },
11570
+ emits: ["end"],
11571
+ setup(__props, { emit: __emit }) {
11572
+ const props = __props;
11573
+ const emit = __emit;
11574
+ const editor = useEditor();
11575
+ const { elementSelection } = editor;
11576
+ const el = props.element;
11577
+ const svgRef = useTemplateRef("svgTpl");
11578
+ const lin = {
11579
+ a: el.transform.a,
11580
+ b: el.transform.b,
11581
+ c: el.transform.c,
11582
+ d: el.transform.d
11583
+ };
11584
+ const det = lin.a * lin.d - lin.c * lin.b || 1;
11585
+ function parentInv(g) {
11586
+ const pt = el.getParent()?.globalTransform;
11587
+ return pt ? pt.applyAffineInverse(g) : g;
11588
+ }
11589
+ function toU(g) {
11590
+ const p = parentInv(g);
11591
+ return {
11592
+ x: (lin.d * p.x - lin.c * p.y) / det,
11593
+ y: (-lin.b * p.x + lin.a * p.y) / det
11594
+ };
11595
+ }
11596
+ function applyR(u) {
11597
+ return {
11598
+ x: lin.a * u.x + lin.c * u.y,
11599
+ y: lin.b * u.x + lin.d * u.y
11600
+ };
11601
+ }
11602
+ const paths = ref([]);
11603
+ let originalPaths = [];
11604
+ let lastWritten = "";
11605
+ const selected = ref(null);
11606
+ function arcToCubic(x0, y0, rx, ry, angleDeg, largeArc, sweep, x, y) {
11607
+ rx = Math.abs(rx);
11608
+ ry = Math.abs(ry);
11609
+ if (rx === 0 || ry === 0) return [[
11610
+ x0,
11611
+ y0,
11612
+ x,
11613
+ y,
11614
+ x,
11615
+ y
11616
+ ]];
11617
+ const rad = angleDeg * Math.PI / 180;
11618
+ const cos = Math.cos(rad);
11619
+ const sin = Math.sin(rad);
11620
+ const dx = (x0 - x) / 2;
11621
+ const dy = (y0 - y) / 2;
11622
+ const x1p = cos * dx + sin * dy;
11623
+ const y1p = -sin * dx + cos * dy;
11624
+ const lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry);
11625
+ if (lambda > 1) {
11626
+ const s = Math.sqrt(lambda);
11627
+ rx *= s;
11628
+ ry *= s;
11629
+ }
11630
+ const sign = largeArc !== sweep ? 1 : -1;
11631
+ const num = Math.max(0, rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p);
11632
+ const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
11633
+ const co = sign * Math.sqrt(den === 0 ? 0 : num / den);
11634
+ const cxp = co * rx * y1p / ry;
11635
+ const cyp = -co * ry * x1p / rx;
11636
+ const cx = cos * cxp - sin * cyp + (x0 + x) / 2;
11637
+ const cy = sin * cxp + cos * cyp + (y0 + y) / 2;
11638
+ const ang = (ux, uy, vx, vy) => {
11639
+ const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
11640
+ let a = Math.acos(Math.max(-1, Math.min(1, len === 0 ? 1 : (ux * vx + uy * vy) / len)));
11641
+ if (ux * vy - uy * vx < 0) a = -a;
11642
+ return a;
11643
+ };
11644
+ const theta1 = ang(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
11645
+ let dtheta = ang((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);
11646
+ if (!sweep && dtheta > 0) dtheta -= 2 * Math.PI;
11647
+ if (sweep && dtheta < 0) dtheta += 2 * Math.PI;
11648
+ const segs = Math.max(1, Math.ceil(Math.abs(dtheta) / (Math.PI / 2)));
11649
+ const delta = dtheta / segs;
11650
+ const k = 4 / 3 * Math.tan(delta / 4);
11651
+ const pointAt = (a) => ({
11652
+ x: cx + rx * cos * Math.cos(a) - ry * sin * Math.sin(a),
11653
+ y: cy + rx * sin * Math.cos(a) + ry * cos * Math.sin(a)
11654
+ });
11655
+ const tangentAt = (a) => ({
11656
+ x: -rx * cos * Math.sin(a) - ry * sin * Math.cos(a),
11657
+ y: -rx * sin * Math.sin(a) + ry * cos * Math.cos(a)
11658
+ });
11659
+ const result = [];
11660
+ let a1 = theta1;
11661
+ for (let i = 0; i < segs; i++) {
11662
+ const a2 = a1 + delta;
11663
+ const p1 = pointAt(a1);
11664
+ const p2 = pointAt(a2);
11665
+ const t1 = tangentAt(a1);
11666
+ const t2 = tangentAt(a2);
11667
+ result.push([
11668
+ p1.x + k * t1.x,
11669
+ p1.y + k * t1.y,
11670
+ p2.x - k * t2.x,
11671
+ p2.y - k * t2.y,
11672
+ p2.x,
11673
+ p2.y
11674
+ ]);
11675
+ a1 = a2;
11676
+ }
11677
+ return result;
11678
+ }
11679
+ function expandArcs(cmds) {
11680
+ const out = [];
11681
+ let cx = 0;
11682
+ let cy = 0;
11683
+ let sx = 0;
11684
+ let sy = 0;
11685
+ for (const c of cmds) {
11686
+ const a = c;
11687
+ if (a.type === "A") {
11688
+ for (const s of arcToCubic(cx, cy, a.rx, a.ry, a.angle, a.largeArcFlag, a.sweepFlag, a.x, a.y)) out.push({
11689
+ type: "C",
11690
+ x1: s[0],
11691
+ y1: s[1],
11692
+ x2: s[2],
11693
+ y2: s[3],
11694
+ x: s[4],
11695
+ y: s[5]
11696
+ });
11697
+ cx = a.x;
11698
+ cy = a.y;
11699
+ } else {
11700
+ out.push(c);
11701
+ if (a.type === "M") {
11702
+ sx = a.x;
11703
+ sy = a.y;
11704
+ }
11705
+ if (a.type === "Z") {
11706
+ cx = sx;
11707
+ cy = sy;
11708
+ } else if ("x" in a && "y" in a) {
11709
+ cx = a.x;
11710
+ cy = a.y;
11711
+ }
11712
+ }
11713
+ }
11714
+ return out;
11715
+ }
11716
+ function build() {
11717
+ originalPaths = el.shape.paths?.map((p) => ({ ...p })) ?? [];
11718
+ const parsed = originalPaths.map((p) => new Path2D(p.data));
11719
+ let dx = Infinity;
11720
+ let dy = Infinity;
11721
+ let dr = -Infinity;
11722
+ let db = -Infinity;
11723
+ for (const p of parsed) {
11724
+ const bb = p.getBoundingBox();
11725
+ dx = Math.min(dx, bb.left);
11726
+ dy = Math.min(dy, bb.top);
11727
+ dr = Math.max(dr, bb.left + bb.width);
11728
+ db = Math.max(db, bb.top + bb.height);
11729
+ }
11730
+ const dbw = dr - dx || 1;
11731
+ const dbh = db - dy || 1;
11732
+ const sx = el.size.x || 1;
11733
+ const sy = el.size.y || 1;
11734
+ const toGlobal = (x, y) => {
11735
+ const local = {
11736
+ x: (x - dx) / dbw * sx,
11737
+ y: (y - dy) / dbh * sy
11738
+ };
11739
+ return el.globalTransform.apply(local);
11740
+ };
11741
+ paths.value = parsed.map((p) => {
11742
+ const cmds = expandArcs(svgPathDataToCommands(p.toData()));
11743
+ for (const c of cmds) {
11744
+ const a = c;
11745
+ if ("x" in a && "y" in a) {
11746
+ const g = toGlobal(a.x, a.y);
11747
+ a.x = g.x;
11748
+ a.y = g.y;
11749
+ }
11750
+ if ("x1" in a) {
11751
+ const g = toGlobal(a.x1, a.y1);
11752
+ a.x1 = g.x;
11753
+ a.y1 = g.y;
11754
+ }
11755
+ if ("x2" in a) {
11756
+ const g = toGlobal(a.x2, a.y2);
11757
+ a.x2 = g.x;
11758
+ a.y2 = g.y;
11759
+ }
11760
+ }
11761
+ return cmds;
11762
+ });
11763
+ }
11764
+ build();
11765
+ function toScreen(x, y) {
11766
+ return {
11767
+ x: x * props.scale[0] + props.offset[0],
11768
+ y: y * props.scale[1] + props.offset[1]
11769
+ };
11770
+ }
11771
+ function getField(cmd, field) {
11772
+ if (field === "cp1") return {
11773
+ x: cmd.x1,
11774
+ y: cmd.y1
11775
+ };
11776
+ if (field === "cp2") return {
11777
+ x: cmd.x2,
11778
+ y: cmd.y2
11779
+ };
11780
+ return {
11781
+ x: cmd.x,
11782
+ y: cmd.y
11783
+ };
11784
+ }
11785
+ function setField(cmd, field, x, y) {
11786
+ if (field === "cp1") {
11787
+ cmd.x1 = x;
11788
+ cmd.y1 = y;
11789
+ } else if (field === "cp2") {
11790
+ cmd.x2 = x;
11791
+ cmd.y2 = y;
11792
+ } else {
11793
+ cmd.x = x;
11794
+ cmd.y = y;
11795
+ }
11796
+ }
11797
+ function lerp(a, b, t) {
11798
+ return {
11799
+ x: a.x + (b.x - a.x) * t,
11800
+ y: a.y + (b.y - a.y) * t
11801
+ };
11802
+ }
11803
+ function dist(a, b) {
11804
+ return Math.hypot(a.x - b.x, a.y - b.y);
11805
+ }
11806
+ function cubicAt(p0, p1, p2, p3, t) {
11807
+ const u = 1 - t;
11808
+ const a = u * u * u;
11809
+ const b = 3 * u * u * t;
11810
+ const c = 3 * u * t * t;
11811
+ const d = t * t * t;
11812
+ return {
11813
+ x: a * p0.x + b * p1.x + c * p2.x + d * p3.x,
11814
+ y: a * p0.y + b * p1.y + c * p2.y + d * p3.y
11815
+ };
11816
+ }
11817
+ const anchors = computed(() => {
11818
+ const list = [];
11819
+ paths.value.forEach((cmds, pi) => {
11820
+ cmds.forEach((c, ci) => {
11821
+ const a = c;
11822
+ if ("x" in a && "y" in a) {
11823
+ const s = toScreen(a.x, a.y);
11824
+ list.push({
11825
+ pi,
11826
+ ci,
11827
+ x: s.x,
11828
+ y: s.y,
11829
+ sel: selected.value?.pi === pi && selected.value?.ci === ci
11830
+ });
11831
+ }
11832
+ });
11833
+ });
11834
+ return list;
11835
+ });
11836
+ const controls = computed(() => {
11837
+ const list = [];
11838
+ paths.value.forEach((cmds, pi) => {
11839
+ cmds.forEach((c, ci) => {
11840
+ const a = c;
11841
+ const prev = cmds[ci - 1];
11842
+ if (a.type === "C") {
11843
+ if (prev && "x" in prev) {
11844
+ const s = toScreen(a.x1, a.y1);
11845
+ const p = toScreen(prev.x, prev.y);
11846
+ list.push({
11847
+ pi,
11848
+ ci,
11849
+ field: "cp1",
11850
+ x: s.x,
11851
+ y: s.y,
11852
+ ax: p.x,
11853
+ ay: p.y
11854
+ });
11855
+ }
11856
+ const s2 = toScreen(a.x2, a.y2);
11857
+ const p2 = toScreen(a.x, a.y);
11858
+ list.push({
11859
+ pi,
11860
+ ci,
11861
+ field: "cp2",
11862
+ x: s2.x,
11863
+ y: s2.y,
11864
+ ax: p2.x,
11865
+ ay: p2.y
11866
+ });
11867
+ } else if (a.type === "Q") {
11868
+ const s = toScreen(a.x1, a.y1);
11869
+ const p = toScreen(a.x, a.y);
11870
+ list.push({
11871
+ pi,
11872
+ ci,
11873
+ field: "cp1",
11874
+ x: s.x,
11875
+ y: s.y,
11876
+ ax: p.x,
11877
+ ay: p.y
11878
+ });
11879
+ }
11880
+ });
11881
+ });
11882
+ return list;
11883
+ });
11884
+ const screenPath = computed(() => {
11885
+ let d = "";
11886
+ for (const cmds of paths.value) for (const c of cmds) {
11887
+ const a = c;
11888
+ if (a.type === "M" || a.type === "L" || a.type === "A") {
11889
+ const s = toScreen(a.x, a.y);
11890
+ d += `${a.type === "M" ? "M" : "L"} ${s.x} ${s.y} `;
11891
+ } else if (a.type === "C") {
11892
+ const c1 = toScreen(a.x1, a.y1);
11893
+ const c2 = toScreen(a.x2, a.y2);
11894
+ const e = toScreen(a.x, a.y);
11895
+ d += `C ${c1.x} ${c1.y} ${c2.x} ${c2.y} ${e.x} ${e.y} `;
11896
+ } else if (a.type === "Q") {
11897
+ const cp = toScreen(a.x1, a.y1);
11898
+ const e = toScreen(a.x, a.y);
11899
+ d += `Q ${cp.x} ${cp.y} ${e.x} ${e.y} `;
11900
+ } else if (a.type === "Z") d += "Z ";
11901
+ }
11902
+ return d;
11903
+ });
11904
+ function mapCommandPoints(cmds, fn) {
11905
+ return cmds.map((c) => {
11906
+ const a = { ...c };
11907
+ if ("x" in a && "y" in a) {
11908
+ const p = fn({
11909
+ x: a.x,
11910
+ y: a.y
11911
+ });
11912
+ a.x = p.x;
11913
+ a.y = p.y;
11914
+ }
11915
+ if ("x1" in a) {
11916
+ const p = fn({
11917
+ x: a.x1,
11918
+ y: a.y1
11919
+ });
11920
+ a.x1 = p.x;
11921
+ a.y1 = p.y;
11922
+ }
11923
+ if ("x2" in a) {
11924
+ const p = fn({
11925
+ x: a.x2,
11926
+ y: a.y2
11927
+ });
11928
+ a.x2 = p.x;
11929
+ a.y2 = p.y;
11930
+ }
11931
+ return a;
11932
+ });
11933
+ }
11934
+ function commit() {
11935
+ const uPaths = paths.value.map((cmds) => mapCommandPoints(cmds, toU));
11936
+ let minX = Infinity;
11937
+ let minY = Infinity;
11938
+ let maxX = -Infinity;
11939
+ let maxY = -Infinity;
11940
+ for (const cmds of uPaths) {
11941
+ const bb = new Path2D(svgPathCommandsToData(cmds)).getBoundingBox();
11942
+ minX = Math.min(minX, bb.left);
11943
+ minY = Math.min(minY, bb.top);
11944
+ maxX = Math.max(maxX, bb.left + bb.width);
11945
+ maxY = Math.max(maxY, bb.top + bb.height);
11946
+ }
11947
+ if (!Number.isFinite(minX)) return;
11948
+ const w = Math.max(1, maxX - minX);
11949
+ const h = Math.max(1, maxY - minY);
11950
+ const center = applyR({
11951
+ x: minX + w / 2,
11952
+ y: minY + h / 2
11953
+ });
11954
+ el.style.left = center.x - w / 2;
11955
+ el.style.top = center.y - h / 2;
11956
+ el.style.width = w;
11957
+ el.style.height = h;
11958
+ const datas = uPaths.map((cmds) => svgPathCommandsToData(mapCommandPoints(cmds, (p) => ({
11959
+ x: p.x - minX,
11960
+ y: p.y - minY
11961
+ }))));
11962
+ lastWritten = datas.join("|");
11963
+ el.shape.paths = originalPaths.map((p, i) => ({
11964
+ ...p,
11965
+ data: datas[i] ?? p.data
11966
+ }));
11967
+ }
11968
+ function dragGroup(pi, ci, field) {
11969
+ const cmds = paths.value[pi];
11970
+ const cmd = cmds[ci];
11971
+ if (field !== "xy") return [{
11972
+ cmd,
11973
+ field
11974
+ }];
11975
+ const group = [{
11976
+ cmd,
11977
+ field: "xy"
11978
+ }];
11979
+ if (cmd.type === "C") group.push({
11980
+ cmd,
11981
+ field: "cp2"
11982
+ });
11983
+ const next = cmds[ci + 1];
11984
+ if (next && next.type === "C") group.push({
11985
+ cmd: next,
11986
+ field: "cp1"
11987
+ });
11988
+ return group;
11989
+ }
11990
+ let drag;
11991
+ function startCapture() {
11992
+ editor.root.value.stopCapturing?.();
11993
+ }
11994
+ function onDown(e, pi, ci, field) {
11995
+ e.stopPropagation();
11996
+ e.preventDefault();
11997
+ if (field === "xy") selected.value = {
11998
+ pi,
11999
+ ci
12000
+ };
12001
+ startCapture();
12002
+ const points = dragGroup(pi, ci, field).map(({ cmd, field }) => {
12003
+ const v = getField(cmd, field);
12004
+ return {
12005
+ cmd,
12006
+ field,
12007
+ sx: v.x,
12008
+ sy: v.y
12009
+ };
12010
+ });
12011
+ drag = {
12012
+ cx: e.clientX,
12013
+ cy: e.clientY,
12014
+ points
12015
+ };
12016
+ window.addEventListener("pointermove", onMove);
12017
+ window.addEventListener("pointerup", onUp);
12018
+ }
12019
+ function onMove(e) {
12020
+ if (!drag) return;
12021
+ const dx = (e.clientX - drag.cx) / props.scale[0];
12022
+ const dy = (e.clientY - drag.cy) / props.scale[1];
12023
+ for (const p of drag.points) setField(p.cmd, p.field, p.sx + dx, p.sy + dy);
12024
+ commit();
12025
+ }
12026
+ function onUp() {
12027
+ if (drag) {
12028
+ startCapture();
12029
+ drag = void 0;
12030
+ }
12031
+ window.removeEventListener("pointermove", onMove);
12032
+ window.removeEventListener("pointerup", onUp);
12033
+ }
12034
+ function onInsert(e) {
12035
+ e.stopPropagation();
12036
+ const rect = svgRef.value?.getBoundingClientRect();
12037
+ if (!rect) return;
12038
+ const g = {
12039
+ x: (e.clientX - rect.left - props.offset[0]) / props.scale[0],
12040
+ y: (e.clientY - rect.top - props.offset[1]) / props.scale[1]
12041
+ };
12042
+ let best;
12043
+ paths.value.forEach((cmds, pi) => {
12044
+ cmds.forEach((c, ci) => {
12045
+ const a = c;
12046
+ const prev = cmds[ci - 1];
12047
+ if (!prev || !("x" in prev)) return;
12048
+ if (a.type === "L") {
12049
+ const vx = a.x - prev.x;
12050
+ const vy = a.y - prev.y;
12051
+ const len2 = vx * vx + vy * vy || 1;
12052
+ const t = Math.max(0, Math.min(1, ((g.x - prev.x) * vx + (g.y - prev.y) * vy) / len2));
12053
+ const at = {
12054
+ x: prev.x + t * vx,
12055
+ y: prev.y + t * vy
12056
+ };
12057
+ const d = dist(g, at);
12058
+ if (!best || d < best.dist) best = {
12059
+ pi,
12060
+ ci,
12061
+ kind: "L",
12062
+ at,
12063
+ t,
12064
+ dist: d
12065
+ };
12066
+ } else if (a.type === "C") {
12067
+ const p0 = {
12068
+ x: prev.x,
12069
+ y: prev.y
12070
+ };
12071
+ const p1 = {
12072
+ x: a.x1,
12073
+ y: a.y1
12074
+ };
12075
+ const p2 = {
12076
+ x: a.x2,
12077
+ y: a.y2
12078
+ };
12079
+ const p3 = {
12080
+ x: a.x,
12081
+ y: a.y
12082
+ };
12083
+ for (let i = 1; i < 24; i++) {
12084
+ const t = i / 24;
12085
+ const at = cubicAt(p0, p1, p2, p3, t);
12086
+ const d = dist(g, at);
12087
+ if (!best || d < best.dist) best = {
12088
+ pi,
12089
+ ci,
12090
+ kind: "C",
12091
+ at,
12092
+ t,
12093
+ dist: d
12094
+ };
12095
+ }
12096
+ }
12097
+ });
12098
+ });
12099
+ if (!best || best.dist * props.scale[0] >= 12) return;
12100
+ const cmds = paths.value[best.pi];
12101
+ if (best.kind === "L") cmds.splice(best.ci, 0, {
12102
+ type: "L",
12103
+ x: best.at.x,
12104
+ y: best.at.y
12105
+ });
12106
+ else {
12107
+ const cur = cmds[best.ci];
12108
+ const prev = cmds[best.ci - 1];
12109
+ const t = best.t;
12110
+ const p0 = {
12111
+ x: prev.x,
12112
+ y: prev.y
12113
+ };
12114
+ const p1 = {
12115
+ x: cur.x1,
12116
+ y: cur.y1
12117
+ };
12118
+ const p2 = {
12119
+ x: cur.x2,
12120
+ y: cur.y2
12121
+ };
12122
+ const p3 = {
12123
+ x: cur.x,
12124
+ y: cur.y
12125
+ };
12126
+ const q0 = lerp(p0, p1, t);
12127
+ const q1 = lerp(p1, p2, t);
12128
+ const q2 = lerp(p2, p3, t);
12129
+ const r0 = lerp(q0, q1, t);
12130
+ const r1 = lerp(q1, q2, t);
12131
+ const s = lerp(r0, r1, t);
12132
+ cur.x1 = r1.x;
12133
+ cur.y1 = r1.y;
12134
+ cur.x2 = q2.x;
12135
+ cur.y2 = q2.y;
12136
+ cmds.splice(best.ci, 0, {
12137
+ type: "C",
12138
+ x1: q0.x,
12139
+ y1: q0.y,
12140
+ x2: r0.x,
12141
+ y2: r0.y,
12142
+ x: s.x,
12143
+ y: s.y
12144
+ });
12145
+ }
12146
+ commit();
12147
+ }
12148
+ function toggleSmooth(pi, ci) {
12149
+ const cmds = paths.value[pi];
12150
+ const cur = cmds[ci];
12151
+ const out = cmds[ci + 1];
12152
+ const prev = cmds[ci - 1];
12153
+ if (cur.type === "M") return;
12154
+ const incCurved = cur.type === "C";
12155
+ const outCurved = out && out.type === "C";
12156
+ if (incCurved || outCurved) {
12157
+ if (incCurved) cmds.splice(ci, 1, {
12158
+ type: "L",
12159
+ x: cur.x,
12160
+ y: cur.y
12161
+ });
12162
+ if (outCurved) cmds.splice(ci + 1, 1, {
12163
+ type: "L",
12164
+ x: out.x,
12165
+ y: out.y
12166
+ });
12167
+ } else {
12168
+ if (!prev || !("x" in prev) || !out || !("x" in out)) return;
12169
+ const p = {
12170
+ x: prev.x,
12171
+ y: prev.y
12172
+ };
12173
+ const c = {
12174
+ x: cur.x,
12175
+ y: cur.y
12176
+ };
12177
+ const n = {
12178
+ x: out.x,
12179
+ y: out.y
12180
+ };
12181
+ const tl = dist(p, n) || 1;
12182
+ const tan = {
12183
+ x: (n.x - p.x) / tl,
12184
+ y: (n.y - p.y) / tl
12185
+ };
12186
+ const lenIn = dist(p, c) / 3;
12187
+ const lenOut = dist(c, n) / 3;
12188
+ cmds.splice(ci, 1, {
12189
+ type: "C",
12190
+ x1: p.x + (c.x - p.x) / 3,
12191
+ y1: p.y + (c.y - p.y) / 3,
12192
+ x2: c.x - tan.x * lenIn,
12193
+ y2: c.y - tan.y * lenIn,
12194
+ x: c.x,
12195
+ y: c.y
12196
+ });
12197
+ cmds.splice(ci + 1, 1, {
12198
+ type: "C",
12199
+ x1: c.x + tan.x * lenOut,
12200
+ y1: c.y + tan.y * lenOut,
12201
+ x2: n.x - (n.x - c.x) / 3,
12202
+ y2: n.y - (n.y - c.y) / 3,
12203
+ x: n.x,
12204
+ y: n.y
12205
+ });
12206
+ }
12207
+ commit();
12208
+ }
12209
+ function anchorCount(pi) {
12210
+ return paths.value[pi].filter((c) => "x" in c && "y" in c).length;
12211
+ }
12212
+ function deleteSelected() {
12213
+ const s = selected.value;
12214
+ if (!s) return;
12215
+ if (paths.value[s.pi][s.ci].type === "M" || anchorCount(s.pi) <= 2) return;
12216
+ paths.value[s.pi].splice(s.ci, 1);
12217
+ selected.value = null;
12218
+ commit();
12219
+ }
12220
+ function onKeydown(e) {
12221
+ if (e.key === "Escape") {
12222
+ e.preventDefault();
12223
+ emit("end");
12224
+ } else if ((e.key === "Delete" || e.key === "Backspace") && selected.value) {
12225
+ e.preventDefault();
12226
+ deleteSelected();
12227
+ }
12228
+ }
12229
+ watch(elementSelection, (sel) => {
12230
+ if (sel.length !== 1 || !sel[0]?.equal(el)) emit("end");
12231
+ });
12232
+ watch(() => el.shape.paths?.map((p) => p.data).join("|") ?? "", (cur) => {
12233
+ if (drag || cur === lastWritten) return;
12234
+ selected.value = null;
12235
+ build();
12236
+ });
12237
+ onMounted(() => window.addEventListener("keydown", onKeydown));
12238
+ onBeforeUnmount(() => {
12239
+ window.removeEventListener("keydown", onKeydown);
12240
+ onUp();
12241
+ });
12242
+ return (_ctx, _cache) => {
12243
+ return openBlock(), createElementBlock("svg", _hoisted_1$15, [
12244
+ createElementVNode("path", {
12245
+ class: "m-path-editor__hit",
12246
+ d: screenPath.value,
12247
+ onDblclick: onInsert
12248
+ }, null, 40, _hoisted_2$7),
12249
+ (openBlock(true), createElementBlock(Fragment, null, renderList(controls.value, (c, i) => {
12250
+ return openBlock(), createElementBlock("line", {
12251
+ key: `l${i}`,
12252
+ class: "m-path-editor__handle-line",
12253
+ x1: c.ax,
12254
+ y1: c.ay,
12255
+ x2: c.x,
12256
+ y2: c.y
12257
+ }, null, 8, _hoisted_3$7);
12258
+ }), 128)),
12259
+ (openBlock(true), createElementBlock(Fragment, null, renderList(controls.value, (c, i) => {
12260
+ return openBlock(), createElementBlock("circle", {
12261
+ key: `c${i}`,
12262
+ class: "m-path-editor__control",
12263
+ cx: c.x,
12264
+ cy: c.y,
12265
+ r: "4",
12266
+ onPointerdown: ($event) => onDown($event, c.pi, c.ci, c.field)
12267
+ }, null, 40, _hoisted_4$4);
12268
+ }), 128)),
12269
+ (openBlock(true), createElementBlock(Fragment, null, renderList(anchors.value, (a, i) => {
12270
+ return openBlock(), createElementBlock("rect", {
12271
+ key: `a${i}`,
12272
+ class: normalizeClass(["m-path-editor__anchor", { "m-path-editor__anchor--selected": a.sel }]),
12273
+ x: a.x - 4,
12274
+ y: a.y - 4,
12275
+ width: "8",
12276
+ height: "8",
12277
+ onPointerdown: ($event) => onDown($event, a.pi, a.ci, "xy"),
12278
+ onDblclick: withModifiers(($event) => toggleSmooth(a.pi, a.ci), ["stop"])
12279
+ }, null, 42, _hoisted_5$2);
12280
+ }), 128))
12281
+ ], 512);
12282
+ };
12283
+ }
12284
+ });
12285
+ //#endregion
11408
12286
  //#region src/components/Selection.vue?vue&type=script&setup=true&lang.ts
11409
- var _hoisted_1$13 = { class: "m-selection" };
12287
+ var _hoisted_1$14 = { class: "m-selection" };
11410
12288
  //#endregion
11411
12289
  //#region src/components/Selection.vue
11412
12290
  var Selection_default = /* @__PURE__ */ defineComponent({
@@ -11496,12 +12374,12 @@ var Selection_default = /* @__PURE__ */ defineComponent({
11496
12374
  return false;
11497
12375
  });
11498
12376
  function tip() {
11499
- const obb = elementSelection.value.length === 1 ? elementSelection.value[0].style : selectionObb.value;
12377
+ const obb = elementSelection.value.length === 1 ? elementSelection.value[0].size : selectionObb.value;
11500
12378
  return `${Number(obb.width.toFixed(2))} × ${Number(obb.height.toFixed(2))}`;
11501
12379
  }
11502
12380
  __expose({ transform });
11503
12381
  return (_ctx, _cache) => {
11504
- return openBlock(), createElementBlock("div", _hoisted_1$13, [
12382
+ return openBlock(), createElementBlock("div", _hoisted_1$14, [
11505
12383
  (openBlock(true), createElementBlock(Fragment, null, renderList(parentObbStyles.value, (style, index) => {
11506
12384
  return openBlock(), createElementBlock("div", {
11507
12385
  key: index,
@@ -11529,8 +12407,19 @@ var Selection_default = /* @__PURE__ */ defineComponent({
11529
12407
  default: withCtx((scope) => [renderSlot(_ctx.$slots, "foreground-cropper", normalizeProps(guardReactiveProps(scope)))]),
11530
12408
  _: 3
11531
12409
  }), renderSlot(_ctx.$slots, "default")], 4)) : createCommentVNode("", true),
11532
- transformValue.value.width && transformValue.value.height ? (openBlock(), createBlock(Transform_default, mergeProps({
12410
+ unref(state) === "pathEditing" && unref(elementSelection).length === 1 ? (openBlock(), createBlock(PathEditor_default, {
11533
12411
  key: 3,
12412
+ element: unref(elementSelection)[0],
12413
+ scale: [unref(camera).zoom.x, unref(camera).zoom.y],
12414
+ offset: [-unref(camera).position.x, -unref(camera).position.y],
12415
+ onEnd: _cache[0] || (_cache[0] = ($event) => state.value = void 0)
12416
+ }, null, 8, [
12417
+ "element",
12418
+ "scale",
12419
+ "offset"
12420
+ ])) : createCommentVNode("", true),
12421
+ transformValue.value.width && transformValue.value.height && unref(state) !== "pathEditing" ? (openBlock(), createBlock(Transform_default, mergeProps({
12422
+ key: 4,
11534
12423
  ref: "transformTpl"
11535
12424
  }, transformProps.value, {
11536
12425
  "model-value": transformValue.value,
@@ -11578,9 +12467,11 @@ var selection_default = definePlugin((editor) => {
11578
12467
  case "all":
11579
12468
  selection.value = [...root.value.children];
11580
12469
  break;
11581
- case "inverse":
11582
- selection.value = [];
12470
+ case "inverse": {
12471
+ const current = selection.value;
12472
+ selection.value = root.value.children.filter((node) => !current.some((v) => v.equal(node)));
11583
12473
  break;
12474
+ }
11584
12475
  case "children": {
11585
12476
  const children = selection.value[0]?.children;
11586
12477
  if (children?.length) selection.value = [...children];
@@ -11836,44 +12727,6 @@ var selection_default = definePlugin((editor) => {
11836
12727
  });
11837
12728
  //#endregion
11838
12729
  //#region src/plugins/shape.ts
11839
- function rotatePoint(center, point, angleRad) {
11840
- const cos = Math.cos(angleRad);
11841
- const sin = Math.sin(angleRad);
11842
- const dx = point.x - center.x;
11843
- const dy = point.y - center.y;
11844
- return {
11845
- x: center.x + (dx * cos - dy * sin),
11846
- y: center.y + (dx * sin + dy * cos)
11847
- };
11848
- }
11849
- function getArrowPath(p1, p2, options = {}) {
11850
- const { size = 10, angle = 30, startMarker = "none", endMarker = "open", roundValues = true } = options;
11851
- const fmt = (n) => roundValues ? Math.round(n) : n.toFixed(2);
11852
- let d = `M ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(p2.x)} ${fmt(p2.y)}`;
11853
- const theta = Math.atan2(p2.y - p1.y, p2.x - p1.x);
11854
- const angleRad = angle * Math.PI / 180;
11855
- if (endMarker !== "none") {
11856
- const baseWing = {
11857
- x: p2.x - size,
11858
- y: p2.y
11859
- };
11860
- const wing1 = rotatePoint(p2, baseWing, theta + angleRad);
11861
- const wing2 = rotatePoint(p2, baseWing, theta - angleRad);
11862
- if (endMarker === "open") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p2.x)} ${fmt(p2.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)}`;
11863
- else if (endMarker === "filled") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p2.x)} ${fmt(p2.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)} Z`;
11864
- }
11865
- if (startMarker !== "none") {
11866
- const baseWingStart = {
11867
- x: p1.x + size,
11868
- y: p1.y
11869
- };
11870
- const wing1 = rotatePoint(p1, baseWingStart, theta + angleRad);
11871
- const wing2 = rotatePoint(p1, baseWingStart, theta - angleRad);
11872
- if (startMarker === "open") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)}`;
11873
- else if (startMarker === "filled") d += ` M ${fmt(wing1.x)} ${fmt(wing1.y)} L ${fmt(p1.x)} ${fmt(p1.y)} L ${fmt(wing2.x)} ${fmt(wing2.y)} Z`;
11874
- }
11875
- return d;
11876
- }
11877
12730
  var shape_default = definePlugin((editor) => {
11878
12731
  const { addElement, activateTool } = editor;
11879
12732
  function createHandle(shape) {
@@ -12003,7 +12856,7 @@ var shape_default = definePlugin((editor) => {
12003
12856
  //#endregion
12004
12857
  //#region src/plugins/slice.ts
12005
12858
  var slice_default = definePlugin((editor) => {
12006
- const { activateTool, addElement, t, elementSelection, inEditorIs, to, fonts, drawboardEffect } = editor;
12859
+ const { activateTool, addElement, t, elementSelection, inEditorIs, to, fonts, drawboardEffect, runExclusiveRender } = editor;
12007
12860
  async function exportSlice(options) {
12008
12861
  const el = elementSelection.value[0];
12009
12862
  if (!el || !inEditorIs(el, "Slice")) return;
@@ -12019,7 +12872,7 @@ var slice_default = definePlugin((editor) => {
12019
12872
  },
12020
12873
  meta: { inCanvasIs: "Camera2D" }
12021
12874
  });
12022
- return await render({
12875
+ return await runExclusiveRender(() => render({
12023
12876
  data: doc,
12024
12877
  fonts,
12025
12878
  width: aabb.width,
@@ -12033,7 +12886,7 @@ var slice_default = definePlugin((editor) => {
12033
12886
  pixelGrid: false
12034
12887
  }));
12035
12888
  }
12036
- });
12889
+ }));
12037
12890
  }
12038
12891
  return {
12039
12892
  name: "mce:slice",
@@ -12087,7 +12940,7 @@ var slice_default = definePlugin((editor) => {
12087
12940
  });
12088
12941
  //#endregion
12089
12942
  //#region src/components/SmartGuides.vue?vue&type=script&setup=true&lang.ts
12090
- var _hoisted_1$12 = {
12943
+ var _hoisted_1$13 = {
12091
12944
  key: 0,
12092
12945
  class: "m-smart-guides"
12093
12946
  };
@@ -12099,7 +12952,7 @@ var SmartGuides_default = /* @__PURE__ */ defineComponent({
12099
12952
  setup(__props) {
12100
12953
  const { state } = useEditor();
12101
12954
  return (_ctx, _cache) => {
12102
- return unref(state) === "transforming" || unref(state) === "moving" ? (openBlock(), createElementBlock("div", _hoisted_1$12, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.snapLines, (item, key) => {
12955
+ return unref(state) === "transforming" || unref(state) === "moving" ? (openBlock(), createElementBlock("div", _hoisted_1$13, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.snapLines, (item, key) => {
12103
12956
  return openBlock(), createElementBlock("div", {
12104
12957
  key,
12105
12958
  class: normalizeClass(item.class.map((v) => `m-smart-guides__${v}`)),
@@ -12969,8 +13822,8 @@ var BSTree = class extends BSTreeKV {};
12969
13822
  var smartGuides_default = definePlugin((editor) => {
12970
13823
  const { isNode, isElement, elementSelection, selectionAabb, getAabb, root, camera, viewportAabb, registerSnapper } = editor;
12971
13824
  const snapThreshold = computed(() => Math.max(1, 5 / camera.value.zoom.x));
12972
- const parnet = computed(() => elementSelection.value[0]?.parent ?? root.value);
12973
- const parentBox = computed(() => createBox(parnet.value));
13825
+ const parent = computed(() => elementSelection.value[0]?.parent ?? root.value);
13826
+ const parentBox = computed(() => createBox(parent.value));
12974
13827
  function createBox(node) {
12975
13828
  if (!node) return void 0;
12976
13829
  const box = {};
@@ -13063,7 +13916,7 @@ var smartGuides_default = definePlugin((editor) => {
13063
13916
  const box = createBox(selectionAabb.value);
13064
13917
  if (box) {
13065
13918
  const excluded = new Set(elementSelection.value.map((el) => el.instanceId));
13066
- const { vLines, hLines } = parnet.value.children.filter((node) => {
13919
+ const { vLines, hLines } = parent.value.children.filter((node) => {
13067
13920
  return !excluded.has(node.instanceId) && isElement(node) && viewportAabb.value.overlap(node.globalAabb);
13068
13921
  }).map((node) => createBox(node)).filter(Boolean).reduce((store, box) => {
13069
13922
  [
@@ -13272,7 +14125,7 @@ var smartGuides_default = definePlugin((editor) => {
13272
14125
  y
13273
14126
  };
13274
14127
  }
13275
- registerSnapper("smartGuides", { get: () => {
14128
+ registerSnapper("smartGuides", { getLines: () => {
13276
14129
  const lines = getSnapPoints();
13277
14130
  return {
13278
14131
  xLines: lines.x,
@@ -13323,7 +14176,7 @@ function isLeftTopLine(line) {
13323
14176
  }
13324
14177
  //#endregion
13325
14178
  //#region src/components/SmartSelection.vue?vue&type=script&setup=true&lang.ts
13326
- var _hoisted_1$11 = ["onPointerdown"];
14179
+ var _hoisted_1$12 = ["onPointerdown"];
13327
14180
  //#endregion
13328
14181
  //#region src/components/SmartSelection.vue
13329
14182
  var SmartSelection_default = /* @__PURE__ */ defineComponent({
@@ -13732,7 +14585,7 @@ var SmartSelection_default = /* @__PURE__ */ defineComponent({
13732
14585
  }, [createElementVNode("div", {
13733
14586
  class: "m-smart-selection__ring",
13734
14587
  onPointerdown: ($event) => onRingDrag($event, item)
13735
- }, null, 40, _hoisted_1$11)], 6);
14588
+ }, null, 40, _hoisted_1$12)], 6);
13736
14589
  }), 128)), currentTransform.value.width && currentTransform.value.height ? (openBlock(), createBlock(Transform_default, mergeProps({
13737
14590
  key: 0,
13738
14591
  "model-value": currentTransform.value
@@ -13814,13 +14667,13 @@ var state_default = definePlugin((editor) => {
13814
14667
  });
13815
14668
  //#endregion
13816
14669
  //#region src/components/shared/ProgressIndicator.vue?vue&type=script&setup=true&lang.ts
13817
- var _hoisted_1$10 = { class: "m-progress-indicator" };
13818
- var _hoisted_2$5 = {
14670
+ var _hoisted_1$11 = { class: "m-progress-indicator" };
14671
+ var _hoisted_2$6 = {
13819
14672
  key: 0,
13820
14673
  class: "m-progress-indicator__status"
13821
14674
  };
13822
- var _hoisted_3$5 = { class: "m-progress-indicator__bar" };
13823
- var _hoisted_4$2 = {
14675
+ var _hoisted_3$6 = { class: "m-progress-indicator__bar" };
14676
+ var _hoisted_4$3 = {
13824
14677
  key: 1,
13825
14678
  class: "m-progress-indicator__bar-indeterminate"
13826
14679
  };
@@ -13839,31 +14692,31 @@ var ProgressIndicator_default = /* @__PURE__ */ _plugin_vue_export_helper_defaul
13839
14692
  setup(__props) {
13840
14693
  const progress = useModel(__props, "modelValue");
13841
14694
  return (_ctx, _cache) => {
13842
- return openBlock(), createElementBlock("div", _hoisted_1$10, [__props.label ? (openBlock(), createElementBlock("span", _hoisted_2$5, toDisplayString(__props.label), 1)) : createCommentVNode("", true), createElementVNode("div", _hoisted_3$5, [!__props.indeterminate ? (openBlock(), createElementBlock("div", {
14695
+ return openBlock(), createElementBlock("div", _hoisted_1$11, [__props.label ? (openBlock(), createElementBlock("span", _hoisted_2$6, toDisplayString(__props.label), 1)) : createCommentVNode("", true), createElementVNode("div", _hoisted_3$6, [!__props.indeterminate ? (openBlock(), createElementBlock("div", {
13843
14696
  key: 0,
13844
14697
  class: "m-progress-indicator__bar-fill",
13845
14698
  style: normalizeStyle({ width: `${progress.value * 100}%` })
13846
- }, null, 4)) : (openBlock(), createElementBlock("div", _hoisted_4$2))])]);
14699
+ }, null, 4)) : (openBlock(), createElementBlock("div", _hoisted_4$3))])]);
13847
14700
  };
13848
14701
  }
13849
14702
  }), [["__scopeId", "data-v-cc8ac0cb"]]);
13850
14703
  //#endregion
13851
14704
  //#region src/components/Statusbar.vue?vue&type=script&setup=true&lang.ts
13852
- var _hoisted_1$9 = { class: "m-statusbar" };
13853
- var _hoisted_2$4 = { class: "m-statusbar__main" };
13854
- var _hoisted_3$4 = { class: "m-statusbar__item" };
13855
- var _hoisted_4$1 = { class: "m-statusbar__kbd" };
14705
+ var _hoisted_1$10 = { class: "m-statusbar" };
14706
+ var _hoisted_2$5 = { class: "m-statusbar__main" };
14707
+ var _hoisted_3$5 = { class: "m-statusbar__item" };
14708
+ var _hoisted_4$2 = { class: "m-statusbar__kbd" };
13856
14709
  var _hoisted_5$1 = { class: "m-statusbar__kbd" };
13857
14710
  var _hoisted_6$1 = { class: "m-statusbar__item" };
13858
- var _hoisted_7 = { class: "m-statusbar__kbd" };
13859
- var _hoisted_8 = { class: "m-statusbar__item" };
13860
- var _hoisted_9 = { class: "m-statusbar__item" };
13861
- var _hoisted_10 = { class: "m-statusbar__kbd" };
13862
- var _hoisted_11 = { class: "m-statusbar__item" };
13863
- var _hoisted_12 = { class: "m-statusbar__kbd" };
13864
- var _hoisted_13 = { key: 2 };
13865
- var _hoisted_14 = { class: "m-statusbar__item" };
13866
- var _hoisted_15 = { class: "m-statusbar__item" };
14711
+ var _hoisted_7$1 = { class: "m-statusbar__kbd" };
14712
+ var _hoisted_8$1 = { class: "m-statusbar__item" };
14713
+ var _hoisted_9$1 = { class: "m-statusbar__item" };
14714
+ var _hoisted_10$1 = { class: "m-statusbar__kbd" };
14715
+ var _hoisted_11$1 = { class: "m-statusbar__item" };
14716
+ var _hoisted_12$1 = { class: "m-statusbar__kbd" };
14717
+ var _hoisted_13$1 = { key: 2 };
14718
+ var _hoisted_14$1 = { class: "m-statusbar__item" };
14719
+ var _hoisted_15$1 = { class: "m-statusbar__item" };
13867
14720
  var _hoisted_16 = { class: "m-statusbar__kbd" };
13868
14721
  var _hoisted_17 = { class: "m-statusbar__item" };
13869
14722
  var _hoisted_18 = { class: "m-statusbar__item" };
@@ -13878,19 +14731,19 @@ var Statusbar_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
13878
14731
  setup(__props) {
13879
14732
  const { state, t, getKbd, exporting, exportProgress, selection, isElement } = useEditor();
13880
14733
  return (_ctx, _cache) => {
13881
- return openBlock(), createElementBlock("div", _hoisted_1$9, [createElementVNode("div", _hoisted_2$4, [unref(state) === "typing" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
13882
- createElementVNode("div", _hoisted_3$4, [createElementVNode("span", _hoisted_4$1, toDisplayString(unref(getKbd)("Command")), 1), createElementVNode("span", _hoisted_5$1, toDisplayString(unref(getKbd)("Enter")), 1)]),
14734
+ return openBlock(), createElementBlock("div", _hoisted_1$10, [createElementVNode("div", _hoisted_2$5, [unref(state) === "typing" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
14735
+ createElementVNode("div", _hoisted_3$5, [createElementVNode("span", _hoisted_4$2, toDisplayString(unref(getKbd)("Command")), 1), createElementVNode("span", _hoisted_5$1, toDisplayString(unref(getKbd)("Enter")), 1)]),
13883
14736
  _cache[1] || (_cache[1] = createElementVNode("span", null, "/", -1)),
13884
- createElementVNode("div", _hoisted_6$1, [createElementVNode("span", _hoisted_7, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("commitChanges")), 1)])
14737
+ createElementVNode("div", _hoisted_6$1, [createElementVNode("span", _hoisted_7$1, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("commitChanges")), 1)])
13885
14738
  ], 64)) : unref(state) === "transforming" || unref(state) === "moving" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
13886
- createElementVNode("div", _hoisted_8, [createVNode(unref(Icon_default), { icon: "$mouseRightClick" })]),
14739
+ createElementVNode("div", _hoisted_8$1, [createVNode(unref(Icon_default), { icon: "$mouseRightClick" })]),
13887
14740
  _cache[3] || (_cache[3] = createElementVNode("span", null, "\xA0/\xA0", -1)),
13888
- createElementVNode("div", _hoisted_9, [createElementVNode("span", _hoisted_10, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("cancel")), 1)]),
13889
- unref(state) === "moving" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[2] || (_cache[2] = createElementVNode("div", { class: "m-statusbar__divider" }, null, -1)), createElementVNode("div", _hoisted_11, [createElementVNode("span", _hoisted_12, toDisplayString(unref(getKbd)("Shift")), 1), createElementVNode("span", null, toDisplayString(unref(t)("constrainMovement")), 1)])], 64)) : createCommentVNode("", true)
13890
- ], 64)) : unref(state) ? (openBlock(), createElementBlock("span", _hoisted_13, toDisplayString(unref(t)(unref(state))), 1)) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
13891
- createElementVNode("div", _hoisted_14, [createVNode(unref(Icon_default), { icon: "$mouseLeftClick" }), createElementVNode("span", null, toDisplayString(unref(t)("selectObject")), 1)]),
14741
+ createElementVNode("div", _hoisted_9$1, [createElementVNode("span", _hoisted_10$1, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("cancel")), 1)]),
14742
+ unref(state) === "moving" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[2] || (_cache[2] = createElementVNode("div", { class: "m-statusbar__divider" }, null, -1)), createElementVNode("div", _hoisted_11$1, [createElementVNode("span", _hoisted_12$1, toDisplayString(unref(getKbd)("Shift")), 1), createElementVNode("span", null, toDisplayString(unref(t)("constrainMovement")), 1)])], 64)) : createCommentVNode("", true)
14743
+ ], 64)) : unref(state) ? (openBlock(), createElementBlock("span", _hoisted_13$1, toDisplayString(unref(t)(unref(state))), 1)) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
14744
+ createElementVNode("div", _hoisted_14$1, [createVNode(unref(Icon_default), { icon: "$mouseLeftClick" }), createElementVNode("span", null, toDisplayString(unref(t)("selectObject")), 1)]),
13892
14745
  _cache[5] || (_cache[5] = createElementVNode("span", null, "\xA0+\xA0", -1)),
13893
- createElementVNode("div", _hoisted_15, [createElementVNode("span", _hoisted_16, toDisplayString(unref(getKbd)("Shift")), 1), createElementVNode("span", null, toDisplayString(unref(t)("extend")), 1)]),
14746
+ createElementVNode("div", _hoisted_15$1, [createElementVNode("span", _hoisted_16, toDisplayString(unref(getKbd)("Shift")), 1), createElementVNode("span", null, toDisplayString(unref(t)("extend")), 1)]),
13894
14747
  _cache[6] || (_cache[6] = createElementVNode("div", { class: "m-statusbar__divider" }, null, -1)),
13895
14748
  createElementVNode("div", _hoisted_17, [createVNode(unref(Icon_default), { icon: "$mouseLeftClick" }), createElementVNode("span", null, toDisplayString(unref(t)("selectArea")), 1)]),
13896
14749
  _cache[7] || (_cache[7] = createElementVNode("span", null, "\xA0+\xA0", -1)),
@@ -13958,28 +14811,29 @@ var test_default = definePlugin((editor) => {
13958
14811
  });
13959
14812
  //#endregion
13960
14813
  //#region src/components/timeline/Playhead.vue?vue&type=script&setup=true&lang.ts
13961
- var _hoisted_1$8 = { class: "m-payhead" };
14814
+ var _hoisted_1$9 = { class: "m-payhead" };
13962
14815
  //#endregion
13963
14816
  //#region src/components/timeline/Playhead.vue
13964
14817
  var Playhead_default = /* @__PURE__ */ defineComponent({
13965
14818
  __name: "Playhead",
13966
14819
  setup(__props) {
13967
14820
  return (_ctx, _cache) => {
13968
- return openBlock(), createElementBlock("div", _hoisted_1$8, [..._cache[0] || (_cache[0] = [createElementVNode("header", { class: "m-payhead__header" }, null, -1), createElementVNode("main", { class: "m-payhead__main" }, null, -1)])]);
14821
+ return openBlock(), createElementBlock("div", _hoisted_1$9, [..._cache[0] || (_cache[0] = [createElementVNode("header", { class: "m-payhead__header" }, null, -1), createElementVNode("main", { class: "m-payhead__main" }, null, -1)])]);
13969
14822
  };
13970
14823
  }
13971
14824
  });
13972
14825
  //#endregion
13973
14826
  //#region src/components/timeline/Segment.vue?vue&type=script&setup=true&lang.ts
13974
- var _hoisted_1$7 = {
14827
+ var _hoisted_1$8 = ["onMousedown"];
14828
+ var _hoisted_2$4 = {
13975
14829
  key: 0,
13976
14830
  class: "m-segment__edge m-segment__edge--front"
13977
14831
  };
13978
- var _hoisted_2$3 = {
14832
+ var _hoisted_3$4 = {
13979
14833
  class: "m-segment__node",
13980
14834
  style: { "overflow": "hidden" }
13981
14835
  };
13982
- var _hoisted_3$3 = {
14836
+ var _hoisted_4$1 = {
13983
14837
  key: 1,
13984
14838
  class: "m-segment__edge m-segment__edge--end"
13985
14839
  };
@@ -13991,118 +14845,374 @@ var Segment_default = /* @__PURE__ */ defineComponent({
13991
14845
  node: {},
13992
14846
  endTime: {},
13993
14847
  msPerPx: { default: 1 },
13994
- active: { type: Boolean }
14848
+ active: { type: Boolean },
14849
+ rev: {}
13995
14850
  },
13996
14851
  setup(__props) {
13997
14852
  const props = __props;
14853
+ const { root, currentTime, fps, recomputeTimelineEndTime, selection } = useEditor();
13998
14854
  const { thumbnailName } = useNode(computed(() => props.node));
13999
14855
  const blocks = computed(() => {
14856
+ props.rev;
14000
14857
  const node = props.node;
14001
- if (node instanceof Element2D) return node.children.filter((child) => child instanceof Animation).map((anim) => {
14002
- const box = {
14003
- left: anim.delay / props.msPerPx,
14004
- top: 0,
14005
- width: anim.duration / props.msPerPx,
14006
- height: 0
14007
- };
14008
- if (box.width) box.width = `${box.width}px`;
14009
- else box.width = "100%";
14010
- return {
14011
- name: anim.name,
14012
- style: {
14013
- width: box.width,
14014
- transform: `matrix(1, 0, 0, 1, ${box.left}, ${box.top})`
14015
- }
14016
- };
14017
- });
14018
- return [];
14858
+ const items = [];
14859
+ if (node instanceof Video2D) {
14860
+ const d = node.videoDuration || 0;
14861
+ if (d > 0) items.push({
14862
+ kind: "video",
14863
+ delay: 0,
14864
+ duration: d
14865
+ });
14866
+ }
14867
+ if (node instanceof Element2D) {
14868
+ for (const slot of [
14869
+ "background",
14870
+ "foreground",
14871
+ "fill",
14872
+ "outline"
14873
+ ]) {
14874
+ const tx = node[slot]?.animatedTexture;
14875
+ if (tx?.duration) items.push({
14876
+ kind: "media",
14877
+ delay: 0,
14878
+ duration: tx.duration
14879
+ });
14880
+ }
14881
+ node.children.forEach((child) => {
14882
+ if (child instanceof Animation) items.push({
14883
+ kind: "animation",
14884
+ delay: child.delay,
14885
+ duration: child.duration,
14886
+ anim: child
14887
+ });
14888
+ });
14889
+ }
14890
+ return items;
14019
14891
  });
14892
+ function blockStyle(b) {
14893
+ return {
14894
+ left: `${b.delay / props.msPerPx}px`,
14895
+ width: b.duration > 0 ? `${b.duration / props.msPerPx}px` : "100%"
14896
+ };
14897
+ }
14020
14898
  const style = computed(() => {
14021
14899
  const { duration: _duration = 0, delay = 0 } = props.node;
14022
- const duration = _duration || props.endTime - delay;
14023
- const box = {
14024
- left: delay / props.msPerPx,
14025
- top: 0,
14026
- width: duration / props.msPerPx,
14027
- height: 0
14028
- };
14900
+ let contentEnd = 0;
14901
+ for (const b of blocks.value) contentEnd = Math.max(contentEnd, b.delay + b.duration);
14902
+ const duration = contentEnd || _duration || props.endTime - delay;
14029
14903
  return {
14030
- width: `${box.width}px`,
14031
- transform: `matrix(1, 0, 0, 1, ${box.left}, ${box.top})`
14904
+ transform: `translateX(${delay / props.msPerPx}px)`,
14905
+ width: `${duration / props.msPerPx}px`
14032
14906
  };
14033
14907
  });
14908
+ function collectSnapTargets(exclude) {
14909
+ const targets = [
14910
+ 0,
14911
+ props.endTime,
14912
+ currentTime.value
14913
+ ];
14914
+ root.value.findAll((node) => {
14915
+ if (node === exclude) return false;
14916
+ if (node instanceof Animation) {
14917
+ targets.push(node.delay);
14918
+ targets.push(node.delay + node.duration);
14919
+ }
14920
+ return false;
14921
+ });
14922
+ return targets;
14923
+ }
14924
+ function snap(value, thresholdMs, exclude) {
14925
+ let best = value;
14926
+ let bestDist = thresholdMs;
14927
+ const targets = collectSnapTargets(exclude);
14928
+ for (const t of targets) {
14929
+ const d = Math.abs(t - value);
14930
+ if (d < bestDist) {
14931
+ bestDist = d;
14932
+ best = t;
14933
+ }
14934
+ }
14935
+ if (bestDist === thresholdMs) {
14936
+ const frameMs = 1e3 / fps.value;
14937
+ const f = Math.round(value / frameMs) * frameMs;
14938
+ if (Math.abs(f - value) < thresholdMs) best = f;
14939
+ }
14940
+ return best;
14941
+ }
14942
+ function pickMode(e, resizable) {
14943
+ if (!resizable) return "move";
14944
+ const rect = e.currentTarget.getBoundingClientRect();
14945
+ const localX = e.clientX - rect.left;
14946
+ if (localX < 6) return "resize-left";
14947
+ if (localX > rect.width - 6) return "resize-right";
14948
+ return "move";
14949
+ }
14950
+ function startDrag(e, target, resizable) {
14951
+ e.stopPropagation();
14952
+ selection.value = [props.node];
14953
+ const mode = pickMode(e, resizable);
14954
+ const startX = e.clientX;
14955
+ const initialDelay = target.delay;
14956
+ const initialDuration = resizable ? target.duration || props.endTime - initialDelay : 0;
14957
+ const minDuration = 1e3 / fps.value;
14958
+ const threshold = 8 * props.msPerPx;
14959
+ function onMove(ev) {
14960
+ const deltaMs = (ev.clientX - startX) * props.msPerPx;
14961
+ if (mode === "move") {
14962
+ const t = snap(initialDelay + deltaMs, threshold, target);
14963
+ target.delay = Math.max(0, t);
14964
+ } else if (mode === "resize-left") {
14965
+ const maxDelay = initialDelay + initialDuration - minDuration;
14966
+ const t = snap(initialDelay + deltaMs, threshold, target);
14967
+ const newDelay = Math.max(0, Math.min(maxDelay, t));
14968
+ target.delay = newDelay;
14969
+ target.duration = initialDuration - (newDelay - initialDelay);
14970
+ } else if (mode === "resize-right") {
14971
+ const t = snap(initialDelay + initialDuration + deltaMs, threshold, target);
14972
+ target.duration = Math.max(minDuration, t - initialDelay);
14973
+ }
14974
+ }
14975
+ function onUp() {
14976
+ window.removeEventListener("mousemove", onMove);
14977
+ window.removeEventListener("mouseup", onUp);
14978
+ recomputeTimelineEndTime();
14979
+ }
14980
+ window.addEventListener("mousemove", onMove);
14981
+ window.addEventListener("mouseup", onUp);
14982
+ }
14983
+ function onBlockDown(e, block) {
14984
+ startDrag(e, block.anim ?? props.node, block.kind === "animation");
14985
+ }
14986
+ function onSegmentDown(e) {
14987
+ startDrag(e, props.node, false);
14988
+ }
14034
14989
  return (_ctx, _cache) => {
14035
14990
  return openBlock(), createElementBlock("div", {
14036
14991
  class: normalizeClass(["m-segment", [`m-segment--${(__props.node.meta.inEditorIs ?? "none").toLowerCase()}`, __props.active && `m-segment--active`]]),
14037
- style: normalizeStyle(style.value)
14992
+ style: normalizeStyle(style.value),
14993
+ onMousedown: onSegmentDown
14038
14994
  }, [
14039
14995
  (openBlock(true), createElementBlock(Fragment, null, renderList(blocks.value, (block, index) => {
14040
14996
  return openBlock(), createElementBlock("div", {
14041
14997
  key: index,
14042
- class: "m-segment__block",
14043
- style: normalizeStyle(block.style)
14044
- }, null, 4);
14998
+ class: normalizeClass(["m-segment__block", `m-segment__block--${block.kind}`]),
14999
+ style: normalizeStyle(blockStyle(block)),
15000
+ onMousedown: ($event) => onBlockDown($event, block)
15001
+ }, null, 46, _hoisted_1$8);
14045
15002
  }), 128)),
14046
- __props.active ? (openBlock(), createElementBlock("div", _hoisted_1$7)) : createCommentVNode("", true),
14047
- createElementVNode("span", _hoisted_2$3, toDisplayString(unref(thumbnailName)), 1),
14048
- __props.active ? (openBlock(), createElementBlock("div", _hoisted_3$3)) : createCommentVNode("", true)
14049
- ], 6);
15003
+ __props.active ? (openBlock(), createElementBlock("div", _hoisted_2$4)) : createCommentVNode("", true),
15004
+ createElementVNode("span", _hoisted_3$4, toDisplayString(unref(thumbnailName)), 1),
15005
+ __props.active ? (openBlock(), createElementBlock("div", _hoisted_4$1)) : createCommentVNode("", true)
15006
+ ], 38);
14050
15007
  };
14051
15008
  }
14052
15009
  });
14053
15010
  //#endregion
14054
15011
  //#region src/components/timeline/Track.vue?vue&type=script&setup=true&lang.ts
14055
- var _hoisted_1$6 = { class: "m-track" };
15012
+ var _hoisted_1$7 = { class: "m-track" };
14056
15013
  //#endregion
14057
15014
  //#region src/components/timeline/Track.vue
14058
15015
  var Track_default = /* @__PURE__ */ defineComponent({
14059
15016
  __name: "Track",
14060
15017
  setup(__props) {
14061
15018
  return (_ctx, _cache) => {
14062
- return openBlock(), createElementBlock("div", _hoisted_1$6, [renderSlot(_ctx.$slots, "default")]);
15019
+ return openBlock(), createElementBlock("div", _hoisted_1$7, [renderSlot(_ctx.$slots, "default")]);
14063
15020
  };
14064
15021
  }
14065
15022
  });
14066
15023
  //#endregion
15024
+ //#region src/components/timeline/Trackhead.vue?vue&type=script&setup=true&lang.ts
15025
+ var _hoisted_1$6 = ["data-id"];
15026
+ var _hoisted_2$3 = { class: "m-trackhead__thumbnail" };
15027
+ var _hoisted_3$3 = { key: 1 };
15028
+ //#endregion
14067
15029
  //#region src/components/timeline/Trackhead.vue
14068
15030
  var Trackhead_default = /* @__PURE__ */ defineComponent({
14069
15031
  __name: "Trackhead",
14070
15032
  props: { node: {} },
14071
15033
  setup(__props) {
15034
+ const props = __props;
15035
+ const editor = useEditor();
15036
+ const { selection, isVisible, setVisible, isLock, setLock, hoverElement, isElement } = editor;
15037
+ const { thumbnailIcon, thumbnailName } = useNode(computed(() => props.node), editor);
15038
+ const selected = computed(() => selection.value.some((v) => v.equal(props.node)));
15039
+ const hovering = ref(false);
15040
+ const editing = ref(false);
15041
+ const editValue = ref("");
15042
+ const inputDom = useTemplateRef("inputDom");
15043
+ const showActions = computed(() => {
15044
+ return hovering.value || isLock(props.node) || !isVisible(props.node);
15045
+ });
15046
+ function onMousedown(e) {
15047
+ if (e.button !== 0) return;
15048
+ if (e.shiftKey) {
15049
+ const filtered = selection.value.filter((v) => !v.equal(props.node));
15050
+ selection.value = filtered.length !== selection.value.length ? filtered : [...selection.value, props.node];
15051
+ } else selection.value = [props.node];
15052
+ addDragListener(e, {
15053
+ threshold: 10,
15054
+ move: ({ event }) => {
15055
+ const target = event.composedPath().find((t) => {
15056
+ return t instanceof HTMLElement && t.classList.contains("m-trackhead");
15057
+ });
15058
+ document.querySelectorAll(".m-trackhead--dropping").forEach((el) => el.classList.remove("m-trackhead--dropping"));
15059
+ if (target && target.dataset.id !== props.node.id) target.classList.add("m-trackhead--dropping");
15060
+ },
15061
+ end: ({ event }) => {
15062
+ document.querySelectorAll(".m-trackhead--dropping").forEach((el) => el.classList.remove("m-trackhead--dropping"));
15063
+ const toId = event.composedPath().find((t) => {
15064
+ return t instanceof HTMLElement && t.classList.contains("m-trackhead");
15065
+ })?.dataset.id;
15066
+ if (!toId || toId === props.node.id) return;
15067
+ const parent = props.node.parent;
15068
+ if (!parent) return;
15069
+ const to = parent.children.find((c) => c.id === toId);
15070
+ if (!to) return;
15071
+ let toIndex = to.getIndex() + 1;
15072
+ if (to.getIndex() > props.node.getIndex()) toIndex--;
15073
+ parent.moveChild(props.node, toIndex);
15074
+ }
15075
+ });
15076
+ }
15077
+ function onMouseenter() {
15078
+ if (isElement(props.node)) hoverElement.value = props.node;
15079
+ hovering.value = true;
15080
+ }
15081
+ function onMouseleave() {
15082
+ hoverElement.value = void 0;
15083
+ hovering.value = false;
15084
+ }
15085
+ function onDblclickName(e) {
15086
+ e.stopPropagation();
15087
+ editing.value = true;
15088
+ editValue.value = thumbnailName.value;
15089
+ nextTick().then(() => {
15090
+ inputDom.value?.focus({ preventScroll: true });
15091
+ inputDom.value?.select();
15092
+ });
15093
+ }
15094
+ function onInputBlur() {
15095
+ editing.value = false;
15096
+ if (editValue.value && editValue.value !== thumbnailName.value) props.node.name = editValue.value;
15097
+ }
15098
+ function onInputKeydown(e) {
15099
+ if (e.key === "Enter") inputDom.value?.blur();
15100
+ else if (e.key === "Escape") editing.value = false;
15101
+ }
15102
+ function onToggleLock(e) {
15103
+ e.stopPropagation();
15104
+ setLock(props.node, !isLock(props.node));
15105
+ }
15106
+ function onToggleVisible(e) {
15107
+ e.stopPropagation();
15108
+ setVisible(props.node, !isVisible(props.node));
15109
+ }
14072
15110
  return (_ctx, _cache) => {
14073
15111
  return openBlock(), createElementBlock("div", {
14074
- class: "m-trackhead",
14075
- style: normalizeStyle({ height: `var(--timeline-track-height__${__props.node.meta.inEditorIs}, 22px)` })
14076
- }, toDisplayString(__props.node.meta.inEditorIs), 5);
15112
+ class: normalizeClass(["m-trackhead", [selected.value && "m-trackhead--selected"]]),
15113
+ "data-id": __props.node.id,
15114
+ onMousedown,
15115
+ onMouseenter,
15116
+ onMouseleave
15117
+ }, [
15118
+ createElementVNode("div", _hoisted_2$3, [createVNode(unref(Icon_default), { icon: unref(thumbnailIcon) }, null, 8, ["icon"])]),
15119
+ createElementVNode("div", {
15120
+ class: "m-trackhead__name",
15121
+ onDblclick: onDblclickName
15122
+ }, [editing.value ? withDirectives((openBlock(), createElementBlock("input", {
15123
+ key: 0,
15124
+ ref_key: "inputDom",
15125
+ ref: inputDom,
15126
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => editValue.value = $event),
15127
+ type: "text",
15128
+ class: "m-trackhead__input",
15129
+ spellcheck: "false",
15130
+ autocapitalize: "off",
15131
+ autocorrect: "off",
15132
+ onBlur: onInputBlur,
15133
+ onKeydown: onInputKeydown,
15134
+ onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
15135
+ }, null, 544)), [[vModelText, editValue.value]]) : (openBlock(), createElementBlock("span", _hoisted_3$3, toDisplayString(unref(thumbnailName)), 1))], 32),
15136
+ createElementVNode("div", { class: normalizeClass(["m-trackhead__action", { "m-trackhead__action--show": showActions.value }]) }, [createElementVNode("button", {
15137
+ type: "button",
15138
+ class: normalizeClass(["m-trackhead__btn", { "m-trackhead__btn--show": unref(isLock)(__props.node) }]),
15139
+ onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"])),
15140
+ onClick: onToggleLock
15141
+ }, [createVNode(unref(Icon_default), { icon: unref(isLock)(__props.node) ? "$lock" : "$unlock" }, null, 8, ["icon"])], 34), createElementVNode("button", {
15142
+ type: "button",
15143
+ class: normalizeClass(["m-trackhead__btn", { "m-trackhead__btn--show": !unref(isVisible)(__props.node) }]),
15144
+ onMousedown: _cache[3] || (_cache[3] = withModifiers(() => {}, ["stop"])),
15145
+ onClick: onToggleVisible
15146
+ }, [createVNode(unref(Icon_default), { icon: unref(isVisible)(__props.node) ? "$visible" : "$unvisible" }, null, 8, ["icon"])], 34)], 2)
15147
+ ], 42, _hoisted_1$6);
14077
15148
  };
14078
15149
  }
14079
15150
  });
14080
15151
  //#endregion
14081
15152
  //#region src/components/timeline/Timeline.vue?vue&type=script&setup=true&lang.ts
14082
15153
  var _hoisted_1$5 = { class: "m-timeline__toolbar" };
14083
- var _hoisted_2$2 = { class: "m-timeline__main" };
14084
- var _hoisted_3$2 = { class: "m-timeline__left-wrapper" };
14085
- var _hoisted_4 = { class: "m-timeline__right-wrapper" };
14086
- var _hoisted_5 = { class: "m-timeline__ruler" };
14087
- var _hoisted_6 = { class: "m-timeline__track" };
15154
+ var _hoisted_2$2 = { class: "m-timeline__time" };
15155
+ var _hoisted_3$2 = { class: "m-timeline__time--muted" };
15156
+ var _hoisted_4 = { class: "m-timeline__controls" };
15157
+ var _hoisted_5 = ["title"];
15158
+ var _hoisted_6 = ["title"];
15159
+ var _hoisted_7 = ["title"];
15160
+ var _hoisted_8 = ["title"];
15161
+ var _hoisted_9 = ["title"];
15162
+ var _hoisted_10 = ["title"];
15163
+ var _hoisted_11 = { class: "m-timeline__main" };
15164
+ var _hoisted_12 = { class: "m-timeline__left-wrapper" };
15165
+ var _hoisted_13 = { class: "m-timeline__right-wrapper" };
15166
+ var _hoisted_14 = { class: "m-timeline__ruler" };
15167
+ var _hoisted_15 = { class: "m-timeline__track" };
14088
15168
  //#endregion
14089
15169
  //#region src/components/timeline/Timeline.vue
14090
15170
  var Timeline_default = /* @__PURE__ */ defineComponent({
14091
15171
  __name: "Timeline",
14092
15172
  setup(__props) {
14093
- const { isElement, root, msPerPx, currentTime, timeline, endTime, selection } = useEditor();
14094
- const fps = ref(1e3 / 30);
15173
+ const { isElement, root, msPerPx, currentTime, endTime, selection, paused, fps, exec, t, assets } = useEditor();
14095
15174
  const ruler = useTemplateRef("rulerTpl");
14096
- const paused = ref(true);
14097
15175
  const offset = ref([0, 0]);
15176
+ const tracksRev = ref(0);
15177
+ function bumpTracksRev() {
15178
+ tracksRev.value++;
15179
+ }
15180
+ let pollId;
15181
+ onBeforeMount(() => {
15182
+ assets.on("loaded", bumpTracksRev);
15183
+ pollId = setInterval(bumpTracksRev, 500);
15184
+ });
15185
+ onBeforeUnmount(() => {
15186
+ assets.off("loaded", bumpTracksRev);
15187
+ if (pollId !== void 0) {
15188
+ clearInterval(pollId);
15189
+ pollId = void 0;
15190
+ }
15191
+ });
15192
+ function hasAnimatedContent(el) {
15193
+ if (el.children.some((c) => c instanceof Animation)) return true;
15194
+ return Boolean(el.background?.animatedTexture || el.foreground?.animatedTexture || el.fill?.animatedTexture || el.outline?.animatedTexture);
15195
+ }
14098
15196
  const elements = computed(() => {
15197
+ endTime.value;
15198
+ tracksRev.value;
14099
15199
  return root.value.findAll((node) => {
14100
- if (isElement(node)) {
14101
- if (node.children.some((child) => child instanceof Animation)) return true;
14102
- }
15200
+ if (node instanceof Video2D) return true;
15201
+ if (isElement(node) && hasAnimatedContent(node)) return true;
14103
15202
  return false;
14104
15203
  }).reverse();
14105
15204
  });
15205
+ function pad(n) {
15206
+ return String(Math.max(0, Math.floor(n))).padStart(2, "0");
15207
+ }
15208
+ function formatTime(ms) {
15209
+ const totalFrames = Math.round((Number.isFinite(ms) ? ms : 0) / 1e3 * fps.value);
15210
+ const f = totalFrames % fps.value;
15211
+ const s = Math.floor(totalFrames / fps.value) % 60;
15212
+ return `${pad(Math.floor(totalFrames / fps.value / 60))}:${pad(s)}.${pad(f)}`;
15213
+ }
15214
+ const currentLabel = computed(() => formatTime(currentTime.value));
15215
+ const totalLabel = computed(() => formatTime(endTime.value));
14106
15216
  function onWheel(e) {
14107
15217
  if (e.ctrlKey || e.metaKey) {
14108
15218
  e.preventDefault();
@@ -14131,47 +15241,67 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
14131
15241
  }
14132
15242
  }
14133
15243
  function rulerLabelFormat(f) {
14134
- if (f % 30 === 0) {
14135
- const m = Math.floor(f / 30 / 60);
14136
- const s = Math.floor(f / 30) % 60;
15244
+ const framesPerSec = fps.value;
15245
+ if (f % framesPerSec === 0) {
15246
+ const m = Math.floor(f / framesPerSec / 60);
15247
+ const s = Math.floor(f / framesPerSec) % 60;
14137
15248
  return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
14138
15249
  }
14139
- return `${Math.floor(f % 30)}f`;
14140
- }
14141
- let requestId;
14142
- function play() {
14143
- paused.value = false;
14144
- let prevTime;
14145
- function loop(time) {
14146
- if (prevTime !== void 0 && time !== void 0) timeline.value.addTime(time - prevTime);
14147
- prevTime = time;
14148
- requestId = requestAnimationFrame(loop);
14149
- }
14150
- loop();
15250
+ return `${Math.floor(f % framesPerSec)}f`;
14151
15251
  }
14152
- function pause() {
14153
- paused.value = true;
14154
- if (requestId !== void 0) {
14155
- cancelAnimationFrame(requestId);
14156
- requestId = void 0;
14157
- }
14158
- }
14159
- function toggle() {
14160
- if (paused.value) play();
14161
- else pause();
14162
- }
14163
- onBeforeUnmount(pause);
14164
15252
  return (_ctx, _cache) => {
14165
15253
  return openBlock(), createElementBlock("div", {
14166
15254
  class: "m-timeline",
14167
- onWheel: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
14168
- }, [createElementVNode("div", _hoisted_1$5, [createElementVNode("div", {
14169
- class: "m-timeline__play",
14170
- onClick: toggle
14171
- }, [createVNode(unref(Icon_default), { icon: paused.value ? "$play" : "$pause" }, null, 8, ["icon"])])]), createElementVNode("div", _hoisted_2$2, [createElementVNode("div", {
15255
+ onWheel: _cache[6] || (_cache[6] = withModifiers(() => {}, ["prevent"]))
15256
+ }, [createElementVNode("div", _hoisted_1$5, [
15257
+ createElementVNode("div", _hoisted_2$2, [
15258
+ createElementVNode("span", null, toDisplayString(currentLabel.value), 1),
15259
+ _cache[7] || (_cache[7] = createElementVNode("span", { class: "m-timeline__time-sep" }, "/", -1)),
15260
+ createElementVNode("span", _hoisted_3$2, toDisplayString(totalLabel.value), 1)
15261
+ ]),
15262
+ createElementVNode("div", _hoisted_4, [
15263
+ createElementVNode("button", {
15264
+ class: "m-timeline__btn",
15265
+ type: "button",
15266
+ title: unref(t)("seekStart"),
15267
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(exec)("seekStart"))
15268
+ }, [createVNode(unref(Icon_default), { icon: "$skipPrevious" })], 8, _hoisted_5),
15269
+ createElementVNode("button", {
15270
+ class: "m-timeline__btn",
15271
+ type: "button",
15272
+ title: unref(t)("stepBackward"),
15273
+ onClick: _cache[1] || (_cache[1] = ($event) => unref(exec)("stepBackward"))
15274
+ }, [createVNode(unref(Icon_default), { icon: "$stepBackward" })], 8, _hoisted_6),
15275
+ createElementVNode("button", {
15276
+ class: "m-timeline__btn m-timeline__btn--primary",
15277
+ type: "button",
15278
+ title: unref(paused) ? unref(t)("play") : unref(t)("pause"),
15279
+ onClick: _cache[2] || (_cache[2] = ($event) => unref(exec)("togglePlay"))
15280
+ }, [createVNode(unref(Icon_default), { icon: unref(paused) ? "$play" : "$pause" }, null, 8, ["icon"])], 8, _hoisted_7),
15281
+ createElementVNode("button", {
15282
+ class: "m-timeline__btn",
15283
+ type: "button",
15284
+ title: unref(t)("stepForward"),
15285
+ onClick: _cache[3] || (_cache[3] = ($event) => unref(exec)("stepForward"))
15286
+ }, [createVNode(unref(Icon_default), { icon: "$stepForward" })], 8, _hoisted_8),
15287
+ createElementVNode("button", {
15288
+ class: "m-timeline__btn",
15289
+ type: "button",
15290
+ title: unref(t)("seekEnd"),
15291
+ onClick: _cache[4] || (_cache[4] = ($event) => unref(exec)("seekEnd"))
15292
+ }, [createVNode(unref(Icon_default), { icon: "$skipNext" })], 8, _hoisted_9)
15293
+ ]),
15294
+ _cache[8] || (_cache[8] = createElementVNode("div", { class: "m-timeline__toolbar-spacer" }, null, -1)),
15295
+ createElementVNode("button", {
15296
+ class: "m-timeline__btn",
15297
+ type: "button",
15298
+ title: unref(t)("collapse"),
15299
+ onClick: _cache[5] || (_cache[5] = ($event) => unref(exec)("togglePanel", "timeline"))
15300
+ }, [createVNode(unref(Icon_default), { icon: "$arrowDown" })], 8, _hoisted_10)
15301
+ ]), createElementVNode("div", _hoisted_11, [createElementVNode("div", {
14172
15302
  class: "m-timeline__left",
14173
15303
  onWheel
14174
- }, [createElementVNode("div", _hoisted_3$2, [createElementVNode("div", { style: normalizeStyle({ transform: `translateY(${offset.value[1]}px)` }) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(elements.value, (node, index) => {
15304
+ }, [createElementVNode("div", _hoisted_12, [createElementVNode("div", { style: normalizeStyle({ transform: `translateY(${offset.value[1]}px)` }) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(elements.value, (node, index) => {
14175
15305
  return openBlock(), createBlock(Trackhead_default, {
14176
15306
  key: index,
14177
15307
  node
@@ -14180,17 +15310,17 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
14180
15310
  class: "m-timeline__right",
14181
15311
  onWheel,
14182
15312
  onMousedown
14183
- }, [createElementVNode("div", _hoisted_4, [
14184
- createElementVNode("div", _hoisted_5, [createVNode(Ruler_default, {
15313
+ }, [createElementVNode("div", _hoisted_13, [
15314
+ createElementVNode("div", _hoisted_14, [createVNode(Ruler_default, {
14185
15315
  ref: "rulerTpl",
14186
- zoom: 1 / unref(msPerPx) * fps.value,
15316
+ zoom: 1e3 / unref(msPerPx) / unref(fps),
14187
15317
  unit: 100,
14188
15318
  "unit-fractions": [1, 3],
14189
15319
  style: { "position": "relative" },
14190
15320
  position: -offset.value[0],
14191
15321
  "label-format": rulerLabelFormat
14192
15322
  }, null, 8, ["zoom", "position"])]),
14193
- createElementVNode("div", _hoisted_6, [createElementVNode("div", { style: normalizeStyle({
15323
+ createElementVNode("div", _hoisted_15, [createElementVNode("div", { style: normalizeStyle({
14194
15324
  width: `${unref(endTime) / unref(msPerPx)}px`,
14195
15325
  transform: `translate(${offset.value[0]}px, ${offset.value[1]}px)`
14196
15326
  }) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(elements.value, (node, index) => {
@@ -14199,14 +15329,14 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
14199
15329
  node,
14200
15330
  "ms-per-px": unref(msPerPx),
14201
15331
  "end-time": unref(endTime),
14202
- active: unref(selection).some((v) => v.equal(node)),
14203
- onMousedown: withModifiers(($event) => selection.value = [node], ["stop"])
15332
+ rev: tracksRev.value,
15333
+ active: unref(selection).some((v) => v.equal(node))
14204
15334
  }, null, 8, [
14205
15335
  "node",
14206
15336
  "ms-per-px",
14207
15337
  "end-time",
14208
- "active",
14209
- "onMousedown"
15338
+ "rev",
15339
+ "active"
14210
15340
  ])]),
14211
15341
  _: 2
14212
15342
  }, 1024);
@@ -14219,8 +15349,48 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
14219
15349
  //#endregion
14220
15350
  //#region src/plugins/timeline.ts
14221
15351
  var timeline_default = definePlugin((editor) => {
14222
- const { registerConfig } = editor;
15352
+ const { registerConfig, timeline } = editor;
14223
15353
  const config = registerConfig("ui.timeline", { default: { visible: false } });
15354
+ const paused = ref(true);
15355
+ const fps = ref(30);
15356
+ async function recomputeTimelineEndTime() {
15357
+ await editor.renderEngine.value.nextTick();
15358
+ timeline.value.endTime = editor.root.value ? editor.getTimeRange(editor.root.value).endTime : 0;
15359
+ }
15360
+ Object.assign(editor, {
15361
+ paused,
15362
+ fps,
15363
+ recomputeTimelineEndTime
15364
+ });
15365
+ function play() {
15366
+ paused.value = false;
15367
+ }
15368
+ function pause() {
15369
+ paused.value = true;
15370
+ }
15371
+ function togglePlay() {
15372
+ paused.value = !paused.value;
15373
+ }
15374
+ function seekStart() {
15375
+ const tl = timeline.value;
15376
+ tl.currentTime = tl.startTime;
15377
+ }
15378
+ function seekEnd() {
15379
+ const tl = timeline.value;
15380
+ tl.currentTime = tl.endTime;
15381
+ }
15382
+ function stepBackward() {
15383
+ const tl = timeline.value;
15384
+ const ms = 1e3 / fps.value;
15385
+ const cur = Number.isFinite(tl.currentTime) ? tl.currentTime : tl.startTime;
15386
+ tl.currentTime = Math.max(tl.startTime, cur - ms);
15387
+ }
15388
+ function stepForward() {
15389
+ const tl = timeline.value;
15390
+ const ms = 1e3 / fps.value;
15391
+ const cur = Number.isFinite(tl.currentTime) ? tl.currentTime : tl.startTime;
15392
+ tl.currentTime = Math.min(tl.endTime, cur + ms);
15393
+ }
14224
15394
  return {
14225
15395
  name: "mce:timeline",
14226
15396
  components: [{
@@ -14234,22 +15404,105 @@ var timeline_default = definePlugin((editor) => {
14234
15404
  set: (val) => config.value.visible = val
14235
15405
  })
14236
15406
  }],
14237
- hotkeys: [{
14238
- command: "togglePanel:timeline",
14239
- key: "Alt+2"
14240
- }],
15407
+ commands: [
15408
+ {
15409
+ command: "play",
15410
+ handle: play
15411
+ },
15412
+ {
15413
+ command: "pause",
15414
+ handle: pause
15415
+ },
15416
+ {
15417
+ command: "togglePlay",
15418
+ handle: togglePlay
15419
+ },
15420
+ {
15421
+ command: "seekStart",
15422
+ handle: seekStart
15423
+ },
15424
+ {
15425
+ command: "seekEnd",
15426
+ handle: seekEnd
15427
+ },
15428
+ {
15429
+ command: "stepBackward",
15430
+ handle: stepBackward
15431
+ },
15432
+ {
15433
+ command: "stepForward",
15434
+ handle: stepForward
15435
+ }
15436
+ ],
15437
+ hotkeys: [
15438
+ {
15439
+ command: "togglePanel:timeline",
15440
+ key: "Alt+2"
15441
+ },
15442
+ {
15443
+ command: "togglePlay",
15444
+ key: "Space",
15445
+ preventDefault: true
15446
+ },
15447
+ {
15448
+ command: "seekStart",
15449
+ key: "Home",
15450
+ preventDefault: true
15451
+ },
15452
+ {
15453
+ command: "seekEnd",
15454
+ key: "End",
15455
+ preventDefault: true
15456
+ },
15457
+ {
15458
+ command: "stepBackward",
15459
+ key: "Left",
15460
+ preventDefault: true
15461
+ },
15462
+ {
15463
+ command: "stepForward",
15464
+ key: "Right",
15465
+ preventDefault: true
15466
+ }
15467
+ ],
14241
15468
  setup: () => {
14242
- const { assets, on, off, renderEngine, timeline, getTimeRange, root } = editor;
14243
- async function updateEndTime() {
14244
- await renderEngine.value.nextTick();
14245
- timeline.value.endTime = root.value ? getTimeRange(root.value).endTime : 0;
14246
- if (!config.value.visible) timeline.value.currentTime = timeline.value.endTime;
15469
+ const { assets, on, off } = editor;
15470
+ const updateEndTime = recomputeTimelineEndTime;
15471
+ let requestId;
15472
+ let prevTime;
15473
+ function startRaf() {
15474
+ if (requestId !== void 0) return;
15475
+ if (!Number.isFinite(timeline.value.currentTime)) timeline.value.currentTime = timeline.value.startTime;
15476
+ function loop(time) {
15477
+ if (prevTime !== void 0 && time !== void 0) {
15478
+ const tl = timeline.value;
15479
+ if (tl.endTime > tl.startTime) tl.addTime(time - prevTime);
15480
+ }
15481
+ prevTime = time;
15482
+ requestId = requestAnimationFrame(loop);
15483
+ }
15484
+ loop();
15485
+ }
15486
+ function stopRaf() {
15487
+ if (requestId !== void 0) {
15488
+ cancelAnimationFrame(requestId);
15489
+ requestId = void 0;
15490
+ }
15491
+ prevTime = void 0;
14247
15492
  }
15493
+ watch(() => config.value.visible, (visible) => {
15494
+ paused.value = visible;
15495
+ }, { immediate: true });
15496
+ watch(paused, (p) => {
15497
+ if (p) stopRaf();
15498
+ else startRaf();
15499
+ }, { immediate: true });
14248
15500
  onBeforeMount(() => {
14249
15501
  on("docSet", updateEndTime);
14250
15502
  assets.on("loaded", updateEndTime);
14251
15503
  });
14252
15504
  onScopeDispose(() => {
15505
+ stopRaf();
14253
15506
  off("docSet", updateEndTime);
14254
15507
  assets.off("loaded", updateEndTime);
14255
15508
  });
@@ -14468,6 +15721,7 @@ var transform_default = definePlugin((editor) => {
14468
15721
  const el = els[0];
14469
15722
  if (el.text.isValid()) await exec("startTyping");
14470
15723
  else if (el.foreground.isValid()) state.value = "cropping";
15724
+ else if (el.shape.isValid() && el.shape.paths?.length && !el.shape.svg) state.value = "pathEditing";
14471
15725
  }
14472
15726
  }
14473
15727
  let context;
@@ -14610,8 +15864,8 @@ var transform_default = definePlugin((editor) => {
14610
15864
  newStyle.top = center.y - newStyle.height / 2;
14611
15865
  }
14612
15866
  }
14613
- const scale = newStyle.rotate ? 100 : 1;
14614
- resizeElement(el, Math.max(1, Math.round(newStyle.width * scale) / scale), Math.max(1, Math.round(newStyle.height * scale) / scale), inEditorIs(el, "Frame") ? void 0 : el.shape.isValid() ? { deep: true } : isCorner ? {
15867
+ const roundFactor = newStyle.rotate ? 100 : 1;
15868
+ resizeElement(el, Math.max(1, Math.round(newStyle.width * roundFactor) / roundFactor), Math.max(1, Math.round(newStyle.height * roundFactor) / roundFactor), inEditorIs(el, "Frame") ? void 0 : el.shape.isValid() ? { deep: true } : isCorner ? {
14615
15869
  deep: true,
14616
15870
  textFontSizeToFit: true
14617
15871
  } : {
@@ -14645,7 +15899,7 @@ var transform_default = definePlugin((editor) => {
14645
15899
  };
14646
15900
  const rotate = (deg) => {
14647
15901
  elementSelection.value.forEach((el) => {
14648
- el.style.rotate = (el.style.rotate + deg) % 360;
15902
+ el.style.rotate = ((el.style.rotate + deg) % 360 + 360) % 360;
14649
15903
  });
14650
15904
  };
14651
15905
  const flip = (direction) => {
@@ -15712,7 +16966,7 @@ var plugins = [
15712
16966
  return ~~Math.max(prev, f[key] ?? 0);
15713
16967
  }, prev);
15714
16968
  }, value) ?? value;
15715
- default: if (content.length === 1 && content[0].fragments.length === 1 && content[0].fragments[0][key]) return content[0].fragments[0][key];
16969
+ default: if (content?.length === 1 && content[0].fragments.length === 1 && content[0].fragments[0][key]) return content[0].fragments[0][key];
15716
16970
  }
15717
16971
  }
15718
16972
  return value;
@@ -15977,7 +17231,7 @@ var plugins = [
15977
17231
  },
15978
17232
  {
15979
17233
  command: "isUiVisible",
15980
- handle: (name) => Boolean(getUiConfig(name).visible)
17234
+ handle: (name) => Boolean(getUiConfig(name)?.visible)
15981
17235
  },
15982
17236
  {
15983
17237
  command: "setUiVisible",