@netless/forge-whiteboard 1.2.0-beta.0 → 1.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/changelog.md +1 -1
  2. package/dist/IndexedNavigation.d.ts +5 -0
  3. package/dist/IndexedNavigation.d.ts.map +1 -1
  4. package/dist/Whiteboard.d.ts +7 -2
  5. package/dist/Whiteboard.d.ts.map +1 -1
  6. package/dist/WhiteboardApplication.d.ts.map +1 -1
  7. package/dist/edit/Editor.d.ts.map +1 -1
  8. package/dist/edit/EditorConfig.d.ts +1 -0
  9. package/dist/edit/EditorConfig.d.ts.map +1 -1
  10. package/dist/model/RenderableModel.d.ts +7 -1
  11. package/dist/model/RenderableModel.d.ts.map +1 -1
  12. package/dist/model/renderable/CurveModel.d.ts +0 -2
  13. package/dist/model/renderable/CurveModel.d.ts.map +1 -1
  14. package/dist/model/renderable/ElementModel.d.ts +7 -2
  15. package/dist/model/renderable/ElementModel.d.ts.map +1 -1
  16. package/dist/model/renderable/ImageModel.d.ts +1 -0
  17. package/dist/model/renderable/ImageModel.d.ts.map +1 -1
  18. package/dist/model/renderable/LaserPointerModel.d.ts.map +1 -1
  19. package/dist/model/renderable/PointTextModel.d.ts.map +1 -1
  20. package/dist/tool/CurveTool.d.ts +2 -0
  21. package/dist/tool/CurveTool.d.ts.map +1 -1
  22. package/dist/tool/EraserTool.d.ts.map +1 -1
  23. package/dist/tool/SelectorTool.d.ts +1 -0
  24. package/dist/tool/SelectorTool.d.ts.map +1 -1
  25. package/dist/tool/WhiteboardTool.d.ts +1 -0
  26. package/dist/tool/WhiteboardTool.d.ts.map +1 -1
  27. package/dist/utils/constant.d.ts +1 -0
  28. package/dist/utils/constant.d.ts.map +1 -1
  29. package/dist/whiteboard.esm.js +460 -149
  30. package/dist/whiteboard.esm.js.map +4 -4
  31. package/dist/whiteboard.js +460 -149
  32. package/dist/whiteboard.js.map +4 -4
  33. package/package.json +2 -2
@@ -23278,7 +23278,7 @@ var require_lodash = __commonJS({
23278
23278
  result2.__values__ = wrapper.__values__;
23279
23279
  return result2;
23280
23280
  }
23281
- function chunk7(array, size2, guard) {
23281
+ function chunk8(array, size2, guard) {
23282
23282
  if (guard ? isIterateeCall(array, size2, guard) : size2 === undefined2) {
23283
23283
  size2 = 1;
23284
23284
  } else {
@@ -25150,7 +25150,7 @@ var require_lodash = __commonJS({
25150
25150
  lodash.bindKey = bindKey;
25151
25151
  lodash.castArray = castArray;
25152
25152
  lodash.chain = chain;
25153
- lodash.chunk = chunk7;
25153
+ lodash.chunk = chunk8;
25154
25154
  lodash.compact = compact;
25155
25155
  lodash.concat = concat;
25156
25156
  lodash.cond = cond;
@@ -25640,6 +25640,7 @@ import { v4 as uuidv4 } from "uuid";
25640
25640
  import { log, removeObserver as removeObserver3 } from "@netless/forge-room";
25641
25641
 
25642
25642
  // src/model/renderable/CurveModel.ts
25643
+ var import_lodash = __toESM(require_lodash(), 1);
25643
25644
  import * as Y2 from "yjs";
25644
25645
 
25645
25646
  // ../../node_modules/.pnpm/perfect-freehand@1.2.2/node_modules/perfect-freehand/dist/esm/index.mjs
@@ -25793,6 +25794,12 @@ function ae(e, t = {}) {
25793
25794
  // src/model/renderable/ElementModel.ts
25794
25795
  import * as Y from "yjs";
25795
25796
  import { removeDeepObserver } from "@netless/forge-room";
25797
+
25798
+ // src/utils/constant.ts
25799
+ var elementsUndoOrigin = "elementsUndoOrigin";
25800
+ var backgroundElementsUndoOrigin = "backgroundElementsUndoOrigin";
25801
+
25802
+ // src/model/renderable/ElementModel.ts
25796
25803
  function _defineProperty(e, r, t) {
25797
25804
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
25798
25805
  }
@@ -25822,6 +25829,12 @@ var ElementModel = class _ElementModel {
25822
25829
  get type() {
25823
25830
  return this.root.get("type");
25824
25831
  }
25832
+ get role() {
25833
+ return this.root.get("role") ?? "normal";
25834
+ }
25835
+ get isBackground() {
25836
+ return this.role === "background";
25837
+ }
25825
25838
  get strokeWidth() {
25826
25839
  return this.root.get(_ElementModel.KEYS.strokeWidth);
25827
25840
  }
@@ -25880,6 +25893,9 @@ var ElementModel = class _ElementModel {
25880
25893
  get isPerformanceEnvironment() {
25881
25894
  return this.isPerformanceMode() && this.shouldUseLocalPoints;
25882
25895
  }
25896
+ isAttached() {
25897
+ return this.root.has(_ElementModel.KEYS.uuid);
25898
+ }
25883
25899
  constructor(root, scope, liveCursor, isPerformanceMode) {
25884
25900
  _defineProperty(this, "shadowEmitter", null);
25885
25901
  _defineProperty(this, "root", void 0);
@@ -25965,16 +25981,12 @@ var ElementModel = class _ElementModel {
25965
25981
  }
25966
25982
  }
25967
25983
  bindObserver() {
25968
- const beforeL = this.root._eH?.l?.length ?? -1;
25969
- const beforeDL = this.root._dEH?.l?.length ?? -1;
25970
25984
  removeDeepObserver(this.root, this.handlePropChange);
25971
25985
  this.subBindObserver();
25972
- const afterRemoveL = this.root._eH?.l?.length ?? -1;
25973
- const afterRemoveDL = this.root._dEH?.l?.length ?? -1;
25974
25986
  this.root.observeDeep(this.handlePropChange);
25975
- const afterAddL = this.root._eH?.l?.length ?? -1;
25976
- const afterAddDL = this.root._dEH?.l?.length ?? -1;
25977
- console.log(`[][][] bindObserver uuid=${this.uuid} doc=${!!this.root.doc} _eH: ${beforeL}->${afterRemoveL}->${afterAddL} _dEH: ${beforeDL}->${afterRemoveDL}->${afterAddDL}`);
25987
+ }
25988
+ mutationOrigin() {
25989
+ return elementsUndoOrigin;
25978
25990
  }
25979
25991
  subBindObserver() {
25980
25992
  }
@@ -26013,62 +26025,81 @@ var ElementModel = class _ElementModel {
26013
26025
  this.item.data.uuid = this.uuid;
26014
26026
  this.item.data.index = this.index;
26015
26027
  this.item.data.type = this.root.get("type");
26028
+ this.item.data.role = this.role;
26016
26029
  this.item.data.ownerId = this.ownerId;
26017
26030
  this.item.applyMatrix = false;
26018
26031
  }
26019
26032
  }
26020
26033
  appendPoints(points) {
26021
26034
  if (this.isPerformanceEnvironment) {
26022
- this.appendPointsPerformance(points);
26035
+ return this.appendPointsPerformance(points);
26023
26036
  } else {
26024
- this.appendPointsDirect(points);
26037
+ return this.appendPointsDirect(points);
26025
26038
  }
26026
26039
  }
26040
+ getPointsArray() {
26041
+ const yArray = this.root.get(_ElementModel.KEYS.points);
26042
+ return yArray instanceof Y.Array ? yArray : null;
26043
+ }
26027
26044
  appendPointsDirect(points) {
26028
- this.root.get(_ElementModel.KEYS.points).push(points);
26045
+ const yArray = this.getPointsArray();
26046
+ if (!yArray) {
26047
+ return false;
26048
+ }
26049
+ yArray.push(points);
26050
+ return true;
26029
26051
  }
26030
26052
  appendPointsPerformance(points) {
26053
+ const yArray = this.getPointsArray();
26054
+ if (!yArray) {
26055
+ return false;
26056
+ }
26031
26057
  this.localPoints = this.localPoints.concat(points);
26032
26058
  this.onVectorUpdate();
26033
26059
  if (this.appendPointsTimer) {
26034
26060
  window.clearTimeout(this.appendPointsTimer);
26035
26061
  }
26036
26062
  if (this.localPoints.length % 80 === 0) {
26037
- const yArray = this.root.get(_ElementModel.KEYS.points);
26038
26063
  yArray?.push(this.localPoints.slice(yArray.length));
26039
26064
  }
26040
26065
  this.appendPointsTimer = window.setTimeout(() => {
26041
26066
  this.appendPointsTimer = null;
26042
26067
  if (this.localPoints.length > 0) {
26043
- const yArray = this.root.get(_ElementModel.KEYS.points);
26044
- yArray?.push(this.localPoints.slice(yArray.length));
26068
+ const nextArray = this.getPointsArray();
26069
+ nextArray?.push(this.localPoints.slice(nextArray.length));
26045
26070
  }
26046
26071
  }, 100);
26072
+ return true;
26047
26073
  }
26048
26074
  setPoints(points) {
26049
26075
  if (this.isPerformanceEnvironment) {
26050
- this.setPointsPerformance(points);
26076
+ return this.setPointsPerformance(points);
26051
26077
  } else {
26052
- this.setPointsDirect(points);
26078
+ return this.setPointsDirect(points);
26053
26079
  }
26054
26080
  }
26055
26081
  setPointsDirect(points) {
26056
26082
  if (this.root.doc) {
26083
+ const yArray = this.getPointsArray();
26084
+ if (!yArray) {
26085
+ return false;
26086
+ }
26057
26087
  this.root.doc.transact(() => {
26058
- const yArray = this.root.get(_ElementModel.KEYS.points);
26059
- if (yArray) {
26060
- yArray.delete(0, yArray.length);
26061
- yArray.push(points);
26062
- }
26063
- });
26088
+ yArray.delete(0, yArray.length);
26089
+ yArray.push(points);
26090
+ }, this.mutationOrigin());
26064
26091
  } else {
26065
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
26092
+ const yArray = this.getPointsArray() || new Y.Array();
26066
26093
  yArray.delete(0, yArray.length);
26067
26094
  yArray.push(points);
26068
26095
  this.root.set(_ElementModel.KEYS.points, yArray);
26069
26096
  }
26097
+ return true;
26070
26098
  }
26071
26099
  setPointsPerformance(points) {
26100
+ if (this.root.doc && !this.getPointsArray()) {
26101
+ return false;
26102
+ }
26072
26103
  this.localPoints = points;
26073
26104
  this.onVectorUpdate();
26074
26105
  if (this.setPointsTimer) {
@@ -26076,25 +26107,33 @@ var ElementModel = class _ElementModel {
26076
26107
  }
26077
26108
  this.setPointsTimer = window.setTimeout(() => {
26078
26109
  if (this.root.doc) {
26110
+ const yArray = this.getPointsArray();
26111
+ if (!yArray) {
26112
+ return;
26113
+ }
26079
26114
  this.root.doc.transact(() => {
26080
- const yArray = this.root.get(_ElementModel.KEYS.points);
26081
- if (yArray) {
26082
- yArray.delete(0, yArray.length);
26083
- yArray.push(points);
26084
- }
26085
- });
26115
+ yArray.delete(0, yArray.length);
26116
+ yArray.push(points);
26117
+ }, this.mutationOrigin());
26086
26118
  } else {
26087
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
26119
+ const yArray = this.getPointsArray() || new Y.Array();
26088
26120
  yArray.delete(0, yArray.length);
26089
26121
  yArray.push(points);
26090
26122
  this.root.set(_ElementModel.KEYS.points, yArray);
26091
26123
  }
26092
26124
  }, 100);
26125
+ return true;
26093
26126
  }
26094
26127
  appendPointsMatrix(matrix) {
26095
26128
  const current = new this.scope.Matrix(this.pointsMatrix);
26096
26129
  const next = matrix.appended(current);
26097
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26130
+ if (this.root.doc) {
26131
+ this.root.doc.transact(() => {
26132
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26133
+ }, this.mutationOrigin());
26134
+ } else {
26135
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26136
+ }
26098
26137
  }
26099
26138
  rotateByOffset(angle, centerX, centerY) {
26100
26139
  const current = new this.scope.Matrix(this.pointsMatrix);
@@ -26108,7 +26147,13 @@ var ElementModel = class _ElementModel {
26108
26147
  matrix2.rotate(delta, centerX, centerY);
26109
26148
  next = matrix2.appended(next);
26110
26149
  }
26111
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26150
+ if (this.root.doc) {
26151
+ this.root.doc.transact(() => {
26152
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26153
+ }, this.mutationOrigin());
26154
+ } else {
26155
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26156
+ }
26112
26157
  return next.rotation;
26113
26158
  }
26114
26159
  getStyleKeys() {
@@ -26119,7 +26164,6 @@ var ElementModel = class _ElementModel {
26119
26164
  this.subDispose();
26120
26165
  }
26121
26166
  disposeObserver() {
26122
- console.log(`[][][] disposeObserver uuid=${this.uuid}`);
26123
26167
  removeDeepObserver(this.root, this.handlePropChange);
26124
26168
  }
26125
26169
  };
@@ -26160,6 +26204,7 @@ var EditorConfig = class _EditorConfig {
26160
26204
  constructor() {
26161
26205
  _defineProperty2(this, "resizeModel", () => "eight");
26162
26206
  _defineProperty2(this, "uniformScale", () => false);
26207
+ _defineProperty2(this, "translatable", () => true);
26163
26208
  _defineProperty2(this, "controlPoints", []);
26164
26209
  }
26165
26210
  merge(other) {
@@ -26168,6 +26213,7 @@ var EditorConfig = class _EditorConfig {
26168
26213
  const j = RESIZE_MODEL_LEVEL.findIndex((v) => v === other.resizeModel());
26169
26214
  next.resizeModel = () => RESIZE_MODEL_LEVEL[Math.max(i, j)];
26170
26215
  next.uniformScale = this.uniformScale || other.uniformScale;
26216
+ next.translatable = () => this.translatable() && other.translatable();
26171
26217
  next.controlPoints = [];
26172
26218
  return next;
26173
26219
  }
@@ -26198,7 +26244,7 @@ var CurveModel = class extends ElementModel {
26198
26244
  _defineProperty3(this, "debug", false);
26199
26245
  _defineProperty3(this, "clearLocalPointsWhenYPointsChange", false);
26200
26246
  _defineProperty3(this, "shouldUseLocalPoints", true);
26201
- _defineProperty3(this, "localPointsPick", 6);
26247
+ _defineProperty3(this, "localPointsPick", 4);
26202
26248
  if (!this.root.doc || !this.root.has("type")) {
26203
26249
  this.root.set("type", "curve");
26204
26250
  }
@@ -26210,7 +26256,6 @@ var CurveModel = class extends ElementModel {
26210
26256
  return (a2 + b2) / 2;
26211
26257
  }
26212
26258
  parsePoints(points) {
26213
- const hasRealPressure = points.some((p) => p.length >= 3 && p[2] > 0);
26214
26259
  const viewScale = this.scope.project.view.matrix.scaling.x || 1;
26215
26260
  const taper = this.strokeWidth * 5 / viewScale;
26216
26261
  const streamline = Math.min(0.7, 0.7 * viewScale);
@@ -26219,7 +26264,7 @@ var CurveModel = class extends ElementModel {
26219
26264
  smoothing: 0.7,
26220
26265
  thinning: 0.5,
26221
26266
  streamline,
26222
- simulatePressure: !hasRealPressure,
26267
+ simulatePressure: true,
26223
26268
  start: {
26224
26269
  taper,
26225
26270
  cap: true
@@ -26230,36 +26275,13 @@ var CurveModel = class extends ElementModel {
26230
26275
  }
26231
26276
  });
26232
26277
  }
26233
- isPressureValue(value) {
26234
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
26235
- }
26236
- pointStride(points) {
26237
- if (points.length >= 3 && points.length % 3 === 0) {
26238
- let hasPressureSlot = false;
26239
- for (let i = 2; i < points.length; i += 3) {
26240
- if (!this.isPressureValue(points[i])) {
26241
- return 2;
26242
- }
26243
- hasPressureSlot = true;
26244
- }
26245
- if (hasPressureSlot) {
26246
- return 3;
26247
- }
26248
- }
26249
- return 2;
26250
- }
26251
26278
  matrixedPoints() {
26252
26279
  const points = this.localPoints.length === 0 ? this.points : this.localPoints;
26253
26280
  const matrix = new this.scope.Matrix(this.pointsMatrix);
26254
- const output = [];
26255
- const stride = this.pointStride(points);
26256
- for (let i = 0, len = points.length; i + 1 < len; i += stride) {
26257
- const p = new this.scope.Point(points[i], points[i + 1]);
26258
- const tp = p.transform(matrix);
26259
- const pressure = stride === 3 ? points[i + 2] ?? 0 : 0;
26260
- output.push([tp.x, tp.y, pressure]);
26261
- }
26262
- return output;
26281
+ return (0, import_lodash.chunk)(points, 2).filter((point) => point.length === 2).map((_ref) => {
26282
+ let [x, y] = _ref;
26283
+ return new this.scope.Point(x, y).transform(matrix);
26284
+ }).map((point) => [point.x, point.y]);
26263
26285
  }
26264
26286
  createPath(points) {
26265
26287
  const path = new this.scope.Path();
@@ -26333,12 +26355,11 @@ var CurveModel = class extends ElementModel {
26333
26355
  liveCursorPoint() {
26334
26356
  const yArray = this.root.get(ElementModel.KEYS.points);
26335
26357
  const points = yArray.toArray();
26336
- const stride = this.pointStride(points);
26337
- if (points.length < stride) {
26358
+ if (points.length < 2) {
26338
26359
  return null;
26339
26360
  }
26340
26361
  const len = points.length;
26341
- const point = new this.scope.Point(points[len - stride], points[len - stride + 1]);
26362
+ const point = new this.scope.Point(points[len - 2], points[len - 1]);
26342
26363
  return point.transform(new this.scope.Matrix(this.pointsMatrix));
26343
26364
  }
26344
26365
  onStyleKeyUpdate(key) {
@@ -26439,7 +26460,7 @@ var SelectorModel = class extends ElementModel {
26439
26460
  import * as Y4 from "yjs";
26440
26461
 
26441
26462
  // src/utils/paperjs.ts
26442
- var import_lodash = __toESM(require_lodash(), 1);
26463
+ var import_lodash2 = __toESM(require_lodash(), 1);
26443
26464
  import { metrics } from "@netless/forge-room";
26444
26465
  function _defineProperty5(e, r, t) {
26445
26466
  return (r = _toPropertyKey5(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -26473,7 +26494,7 @@ function serializePath(path) {
26473
26494
  }, []);
26474
26495
  }
26475
26496
  function deserializePath(points, scope, matrix) {
26476
- const segmentGroup = (0, import_lodash.chunk)(points, 6);
26497
+ const segmentGroup = (0, import_lodash2.chunk)(points, 6);
26477
26498
  const path = new scope.Path();
26478
26499
  path.segments = segmentGroup.map((v) => deserializeSegment(v, scope, matrix));
26479
26500
  return path;
@@ -26615,7 +26636,7 @@ var SegmentsModel = class extends ElementModel {
26615
26636
  };
26616
26637
 
26617
26638
  // src/model/renderable/LineModel.ts
26618
- var import_lodash3 = __toESM(require_lodash(), 1);
26639
+ var import_lodash4 = __toESM(require_lodash(), 1);
26619
26640
  import * as Y5 from "yjs";
26620
26641
 
26621
26642
  // src/tool/WhiteboardTool.ts
@@ -26661,7 +26682,14 @@ var WhiteboardTool = class {
26661
26682
  }
26662
26683
  this.pendingDragEvent = null;
26663
26684
  this.shadowEmitter.setActive(true);
26664
- this.onMouseDown(event);
26685
+ try {
26686
+ this.onMouseDown(event);
26687
+ } catch (error) {
26688
+ this.cancelCurrentAction();
26689
+ this.eventAvailable = false;
26690
+ this.shadowEmitter.setActive(false);
26691
+ throw error;
26692
+ }
26665
26693
  });
26666
26694
  _defineProperty7(this, "flushPendingDrag", () => {
26667
26695
  this.dragRafId = 0;
@@ -26669,7 +26697,14 @@ var WhiteboardTool = class {
26669
26697
  this.lastDragTime = performance.now();
26670
26698
  const event = this.pendingDragEvent;
26671
26699
  this.pendingDragEvent = null;
26672
- this.onMouseDrag(event);
26700
+ try {
26701
+ this.onMouseDrag(event);
26702
+ } catch (error) {
26703
+ this.cancelCurrentAction();
26704
+ this.eventAvailable = false;
26705
+ this.shadowEmitter.setActive(false);
26706
+ throw error;
26707
+ }
26673
26708
  }
26674
26709
  });
26675
26710
  _defineProperty7(this, "onMouseDragSelf", (event) => {
@@ -26680,7 +26715,14 @@ var WhiteboardTool = class {
26680
26715
  if (now - this.lastDragTime >= DRAG_FRAME_MS) {
26681
26716
  this.lastDragTime = now;
26682
26717
  this.pendingDragEvent = null;
26683
- this.onMouseDrag(event);
26718
+ try {
26719
+ this.onMouseDrag(event);
26720
+ } catch (error) {
26721
+ this.cancelCurrentAction();
26722
+ this.eventAvailable = false;
26723
+ this.shadowEmitter.setActive(false);
26724
+ throw error;
26725
+ }
26684
26726
  } else {
26685
26727
  this.pendingDragEvent = event;
26686
26728
  if (!this.dragRafId) {
@@ -26697,11 +26739,26 @@ var WhiteboardTool = class {
26697
26739
  this.dragRafId = 0;
26698
26740
  }
26699
26741
  if (this.pendingDragEvent) {
26700
- this.onMouseDrag(this.pendingDragEvent);
26701
- this.pendingDragEvent = null;
26742
+ try {
26743
+ this.onMouseDrag(this.pendingDragEvent);
26744
+ this.pendingDragEvent = null;
26745
+ } catch (error) {
26746
+ this.pendingDragEvent = null;
26747
+ this.cancelCurrentAction();
26748
+ this.eventAvailable = false;
26749
+ this.shadowEmitter.setActive(false);
26750
+ throw error;
26751
+ }
26752
+ }
26753
+ try {
26754
+ this.onMouseUp(event);
26755
+ } catch (error) {
26756
+ this.cancelCurrentAction();
26757
+ this.eventAvailable = false;
26758
+ throw error;
26759
+ } finally {
26760
+ this.shadowEmitter.setActive(false);
26702
26761
  }
26703
- this.onMouseUp(event);
26704
- this.shadowEmitter.setActive(false);
26705
26762
  });
26706
26763
  this.modelGetter = modelGetter;
26707
26764
  this.enableToolEvent = enableToolEvent;
@@ -26712,10 +26769,12 @@ var WhiteboardTool = class {
26712
26769
  this.tool.onMouseDrag = this.onMouseDragSelf;
26713
26770
  this.tool.onMouseUp = this.onMouseUpSelf;
26714
26771
  }
26772
+ cancelCurrentAction() {
26773
+ }
26715
26774
  };
26716
26775
 
26717
26776
  // src/tool/LineTool.ts
26718
- var import_lodash2 = __toESM(require_lodash(), 1);
26777
+ var import_lodash3 = __toESM(require_lodash(), 1);
26719
26778
  function _defineProperty8(e, r, t) {
26720
26779
  return (r = _toPropertyKey8(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
26721
26780
  }
@@ -26764,7 +26823,7 @@ var LineTool = class extends WhiteboardTool {
26764
26823
  const point = path.getPointAt(distance);
26765
26824
  return [point.x, point.y];
26766
26825
  });
26767
- this.elementModel.setPoints((0, import_lodash2.flattenDeep)(points));
26826
+ this.elementModel.setPoints((0, import_lodash3.flattenDeep)(points));
26768
26827
  }
26769
26828
  }
26770
26829
  onMouseUp(_event) {
@@ -26847,7 +26906,7 @@ var LineModel = class extends ElementModel {
26847
26906
  }
26848
26907
  renderLine() {
26849
26908
  const matrix = new this.scope.Matrix(this.pointsMatrix);
26850
- const papperPoints = (0, import_lodash3.chunk)(this.drawPoints, 2).map((item) => {
26909
+ const papperPoints = (0, import_lodash4.chunk)(this.drawPoints, 2).map((item) => {
26851
26910
  return new this.scope.Point(item[0], item[1]).transform(matrix);
26852
26911
  });
26853
26912
  const path = new this.scope.Path();
@@ -26983,7 +27042,7 @@ var LineControlPoint = class {
26983
27042
  const invertedPoint = point.transform(pointsMatrix.inverted());
26984
27043
  const points = this.model["drawPoints"];
26985
27044
  this.position = invertedPoint;
26986
- const clonedPoints = (0, import_lodash3.cloneDeep)(points);
27045
+ const clonedPoints = (0, import_lodash4.cloneDeep)(points);
26987
27046
  clonedPoints[this.options.index * 2] = invertedPoint.x;
26988
27047
  clonedPoints[this.options.index * 2 + 1] = invertedPoint.y;
26989
27048
  this.model.setPoints(clonedPoints);
@@ -27092,7 +27151,6 @@ var PointTextModel = class extends ElementModel {
27092
27151
  if (!this.item) {
27093
27152
  return null;
27094
27153
  }
27095
- console.log("[][][] drawPoints", this.drawPoints);
27096
27154
  const bounds = this.item.internalBounds;
27097
27155
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27098
27156
  const topLeft = new this.scope.Point(this.drawPoints[0], this.drawPoints[1]).transform(matrix);
@@ -27463,11 +27521,8 @@ var RectangleModel = class extends ElementModel {
27463
27521
  }
27464
27522
  };
27465
27523
 
27466
- // src/utils/constant.ts
27467
- var elementsUndoOrigin = "elementsUndoOrigin";
27468
-
27469
27524
  // src/model/renderable/EraserModel.ts
27470
- var import_lodash4 = __toESM(require_lodash(), 1);
27525
+ var import_lodash5 = __toESM(require_lodash(), 1);
27471
27526
  import * as Y9 from "yjs";
27472
27527
  function _defineProperty13(e, r, t) {
27473
27528
  return (r = _toPropertyKey13(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -27536,7 +27591,7 @@ var EraserModel = class extends ElementModel {
27536
27591
  return path;
27537
27592
  }
27538
27593
  parsePoints(points) {
27539
- const groupPoints = (0, import_lodash4.chunk)(points, 2);
27594
+ const groupPoints = (0, import_lodash5.chunk)(points, 2);
27540
27595
  return ae(groupPoints, {
27541
27596
  size: this.strokeWidth,
27542
27597
  smoothing: 0.5,
@@ -27554,7 +27609,7 @@ var EraserModel = class extends ElementModel {
27554
27609
  });
27555
27610
  }
27556
27611
  matrixedPoints() {
27557
- const currentPoints = (0, import_lodash4.chunk)(this.drawPoints, 2).slice(this.sliceBegin);
27612
+ const currentPoints = (0, import_lodash5.chunk)(this.drawPoints, 2).slice(this.sliceBegin);
27558
27613
  return currentPoints.map((_ref) => {
27559
27614
  let [x, y] = _ref;
27560
27615
  return new this.scope.Point(x, y);
@@ -27619,7 +27674,7 @@ var EraserModel = class extends ElementModel {
27619
27674
  };
27620
27675
 
27621
27676
  // src/model/renderable/LaserPointerModel.ts
27622
- var import_lodash5 = __toESM(require_lodash(), 1);
27677
+ var import_lodash6 = __toESM(require_lodash(), 1);
27623
27678
  import * as Y10 from "yjs";
27624
27679
  import { removeObserver as removeObserver2 } from "@netless/forge-room";
27625
27680
  function _defineProperty14(e, r, t) {
@@ -27746,8 +27801,7 @@ var LaserPointerModel = class extends ElementModel {
27746
27801
  matrixedPoints() {
27747
27802
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27748
27803
  const points = this.cachedPoints || this.points;
27749
- console.log("[][][] ,", this.points.length, this.cachedPoints?.length, this.localPoints.length);
27750
- const groupPoints = (0, import_lodash5.chunk)(points, 2).slice(this.sliceBegin);
27804
+ const groupPoints = (0, import_lodash6.chunk)(points, 2).slice(this.sliceBegin);
27751
27805
  return groupPoints.map((_ref) => {
27752
27806
  let [x, y] = _ref;
27753
27807
  return matrix.transform([x, y]);
@@ -27826,7 +27880,7 @@ var WhiteboardPermissions = class extends AbstractApplicationPermissions {
27826
27880
  };
27827
27881
 
27828
27882
  // src/model/renderable/StraightLineModel.ts
27829
- var import_lodash6 = __toESM(require_lodash(), 1);
27883
+ var import_lodash7 = __toESM(require_lodash(), 1);
27830
27884
  import * as Y11 from "yjs";
27831
27885
  function _defineProperty15(e, r, t) {
27832
27886
  return (r = _toPropertyKey15(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -27886,7 +27940,7 @@ var StraightLineModel = class extends ElementModel {
27886
27940
  }
27887
27941
  renderLine() {
27888
27942
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27889
- const papperPoints = (0, import_lodash6.chunk)(this.drawPoints, 2).map((item) => {
27943
+ const papperPoints = (0, import_lodash7.chunk)(this.drawPoints, 2).map((item) => {
27890
27944
  return new this.scope.Point(item[0], item[1]).transform(matrix);
27891
27945
  });
27892
27946
  const path = new this.scope.Path();
@@ -28016,6 +28070,7 @@ var ImageModel = class extends ElementModel {
28016
28070
  this.item = new this.scope.Raster(this.uuid);
28017
28071
  const matrix = new this.scope.Matrix(this.pointsMatrix);
28018
28072
  this.item.matrix = matrix;
28073
+ this.item.data.role = this.role;
28019
28074
  }
28020
28075
  onVectorUpdate() {
28021
28076
  const matrix = new this.scope.Matrix(this.pointsMatrix);
@@ -28044,8 +28099,12 @@ var ImageModel = class extends ElementModel {
28044
28099
  const cfg = new EditorConfig();
28045
28100
  cfg.resizeModel = () => "four-corner";
28046
28101
  cfg.uniformScale = () => true;
28102
+ cfg.translatable = () => !this.isBackground;
28047
28103
  return cfg;
28048
28104
  }
28105
+ mutationOrigin() {
28106
+ return this.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin;
28107
+ }
28049
28108
  liveCursorPoint() {
28050
28109
  return null;
28051
28110
  }
@@ -28128,6 +28187,7 @@ var RenderableModel = class extends EventEmitter {
28128
28187
  flushRenderables() {
28129
28188
  this.emit("elementClear");
28130
28189
  const elements = Array.from(this.elements.values()).map((v) => this.convertToModel(v)).filter((v) => !!v);
28190
+ elements.sort((a2, b2) => this.compareElements(a2, b2));
28131
28191
  this.maxIndex = elements.reduce((r, n) => Math.max(r, n.index), -1);
28132
28192
  const clearIds = [];
28133
28193
  elements.forEach((model) => {
@@ -28189,7 +28249,32 @@ var RenderableModel = class extends EventEmitter {
28189
28249
  initElement(element) {
28190
28250
  element.shadowEmitter = this.shadowEmitter;
28191
28251
  }
28252
+ compareElementRole(roleA, roleB) {
28253
+ const a2 = roleA === "background" ? 0 : 1;
28254
+ const b2 = roleB === "background" ? 0 : 1;
28255
+ return a2 - b2;
28256
+ }
28257
+ compareElements(a2, b2) {
28258
+ const roleCompare = this.compareElementRole(a2.role, b2.role);
28259
+ if (roleCompare !== 0) {
28260
+ return roleCompare;
28261
+ }
28262
+ if (a2.index !== b2.index) {
28263
+ return a2.index - b2.index;
28264
+ }
28265
+ return a2.uuid.localeCompare(b2.uuid);
28266
+ }
28267
+ isBackgroundElement(uuid) {
28268
+ const element = this.elements.get(uuid);
28269
+ if (!element) {
28270
+ return false;
28271
+ }
28272
+ return (element.get("role") ?? "normal") === "background";
28273
+ }
28192
28274
  removeElementItem(uuid) {
28275
+ if (this.isBackgroundElement(uuid)) {
28276
+ return;
28277
+ }
28193
28278
  this.elements.delete(uuid);
28194
28279
  }
28195
28280
  confirmPermission() {
@@ -28200,15 +28285,21 @@ var RenderableModel = class extends EventEmitter {
28200
28285
  return hasPermission;
28201
28286
  }
28202
28287
  createImage(src) {
28288
+ let fit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "legacy";
28289
+ let role = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "normal";
28203
28290
  if (!this.confirmPermission()) {
28204
28291
  return;
28205
28292
  }
28293
+ if (role === "background") {
28294
+ return this.upsertBackgroundImage(src, fit);
28295
+ }
28206
28296
  const yMap = new Y12.Map();
28207
28297
  this.elements.doc?.transact(() => {
28208
28298
  const uuid = this.uuid;
28209
28299
  yMap.set(ElementModel.KEYS.index, ++this.maxIndex);
28210
28300
  yMap.set(ElementModel.KEYS.uuid, uuid);
28211
28301
  yMap.set("type", "image");
28302
+ yMap.set("role", role);
28212
28303
  yMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28213
28304
  this.elements.set(uuid, yMap);
28214
28305
  }, elementsUndoOrigin);
@@ -28216,9 +28307,47 @@ var RenderableModel = class extends EventEmitter {
28216
28307
  model.bindObserver();
28217
28308
  model.root.set("src", src);
28218
28309
  model.ownerId = this.userManager.selfId;
28219
- this.fitImageToViewport(src, model);
28310
+ this.fitImageToViewport(src, model, fit);
28311
+ }
28312
+ upsertBackgroundImage(src, fit) {
28313
+ const backgroundEntries = Array.from(this.elements.entries()).filter((_ref) => {
28314
+ let [, elementMap] = _ref;
28315
+ return elementMap.get("type") === "image" && (elementMap.get("role") ?? "normal") === "background";
28316
+ });
28317
+ let targetMap = backgroundEntries[0]?.[1] ?? null;
28318
+ if (this.elements.doc) {
28319
+ this.elements.doc.transact(() => {
28320
+ backgroundEntries.slice(1).forEach((_ref2) => {
28321
+ let [key] = _ref2;
28322
+ return this.elements.delete(key);
28323
+ });
28324
+ if (!targetMap) {
28325
+ targetMap = new Y12.Map();
28326
+ const uuid = this.uuid;
28327
+ targetMap.set(ElementModel.KEYS.index, -1);
28328
+ targetMap.set(ElementModel.KEYS.uuid, uuid);
28329
+ targetMap.set("type", "image");
28330
+ targetMap.set("role", "background");
28331
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28332
+ this.elements.set(uuid, targetMap);
28333
+ } else {
28334
+ targetMap.set("role", "background");
28335
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28336
+ }
28337
+ }, backgroundElementsUndoOrigin);
28338
+ }
28339
+ if (!targetMap) {
28340
+ return;
28341
+ }
28342
+ const model = this.convertToModel(targetMap);
28343
+ if (!model || !(model instanceof ImageModel)) {
28344
+ return;
28345
+ }
28346
+ model.root.set("src", src);
28347
+ model.ownerId = this.userManager.selfId;
28348
+ this.fitImageToViewport(src, model, fit);
28220
28349
  }
28221
- fitImageToViewport(src, model) {
28350
+ fitImageToViewport(src, model, fit) {
28222
28351
  const center = this.scope.project.view.center;
28223
28352
  const fallbackMatrix = new this.scope.Matrix();
28224
28353
  fallbackMatrix.translate({
@@ -28235,9 +28364,9 @@ var RenderableModel = class extends EventEmitter {
28235
28364
  return;
28236
28365
  }
28237
28366
  const viewportBounds = this.scope.project.view.bounds;
28238
- const maxWidth = viewportBounds.width * 2 / 3;
28239
- const maxHeight = viewportBounds.height * 2 / 3;
28240
- const fitScale = Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
28367
+ const maxWidth = fit === "contain" ? viewportBounds.width : viewportBounds.width * 2 / 3;
28368
+ const maxHeight = fit === "contain" ? viewportBounds.height : viewportBounds.height * 2 / 3;
28369
+ const fitScale = fit === "contain" ? Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight) : Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
28241
28370
  const nextMatrix = new this.scope.Matrix();
28242
28371
  nextMatrix.translate({
28243
28372
  x: center.x,
@@ -28260,7 +28389,7 @@ var RenderableModel = class extends EventEmitter {
28260
28389
  if (model.root.doc) {
28261
28390
  model.root.doc.transact(() => {
28262
28391
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
28263
- }, elementsUndoOrigin);
28392
+ }, model.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin);
28264
28393
  } else {
28265
28394
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
28266
28395
  }
@@ -28520,7 +28649,7 @@ var RenderableModel = class extends EventEmitter {
28520
28649
  if (shadow) {
28521
28650
  yMap.set(ElementModel.KEYS.shadow, "layer");
28522
28651
  }
28523
- yMap.set(ElementModel.KEYS.points, new Y12.Array());
28652
+ yMap.set(ElementModel.KEYS.points, Y12.Array.from([x, y]));
28524
28653
  yMap.set(ElementModel.KEYS.strokeWidth, this.toolbarModel.strokeWidth);
28525
28654
  yMap.set(ElementModel.KEYS.strokeColor, this.toolbarModel.strokeColor);
28526
28655
  yMap.set(ElementModel.KEYS.fillColor, this.toolbarModel.fillColor);
@@ -28528,7 +28657,6 @@ var RenderableModel = class extends EventEmitter {
28528
28657
  this.elements.set(uuid, yMap);
28529
28658
  }, elementsUndoOrigin);
28530
28659
  pointTextModel.bindObserver();
28531
- pointTextModel.setPoints([x, y]);
28532
28660
  pointTextModel.fontSize = this.toolbarModel.fontSize;
28533
28661
  pointTextModel.fontFamily = this.toolbarModel.fontFamily;
28534
28662
  this.initElement(pointTextModel);
@@ -28562,7 +28690,7 @@ var RenderableModel = class extends EventEmitter {
28562
28690
  };
28563
28691
 
28564
28692
  // src/utils/Recognizer.ts
28565
- var import_lodash7 = __toESM(require_lodash(), 1);
28693
+ var import_lodash8 = __toESM(require_lodash(), 1);
28566
28694
 
28567
28695
  // src/utils/UnistrokeRecognizer.js
28568
28696
  function Point(x, y) {
@@ -28809,7 +28937,7 @@ var Recognizer = class {
28809
28937
  let maxX = -Number.MAX_VALUE;
28810
28938
  let minY = Number.MAX_VALUE;
28811
28939
  let maxY = -Number.MAX_VALUE;
28812
- const result = this.dollar.Recognize((0, import_lodash7.chunk)(points, 3).map((v) => {
28940
+ const result = this.dollar.Recognize((0, import_lodash8.chunk)(points, 2).map((v) => {
28813
28941
  minX = Math.min(minX, v[0]);
28814
28942
  maxX = Math.max(maxX, v[0]);
28815
28943
  minY = Math.min(minY, v[1]);
@@ -28859,22 +28987,33 @@ var CurveTool = class extends WhiteboardTool {
28859
28987
  _defineProperty19(this, "flushPendingPoints", () => {
28860
28988
  this.flushRafId = 0;
28861
28989
  if (this.elementModel && this.pendingPoints.length > 0) {
28862
- this.elementModel.appendPoints(this.pendingPoints);
28990
+ const appended = this.elementModel.appendPoints(this.pendingPoints);
28991
+ if (!appended) {
28992
+ this.resetStrokeState();
28993
+ return;
28994
+ }
28863
28995
  this.pendingPoints = [];
28864
28996
  }
28865
28997
  });
28866
28998
  }
28867
- onMouseDown(_event) {
28999
+ resetStrokeState() {
28868
29000
  this.pointCount = 0;
28869
29001
  this.pendingPoints = [];
28870
29002
  if (this.flushRafId) {
28871
29003
  cancelAnimationFrame(this.flushRafId);
28872
29004
  this.flushRafId = 0;
28873
29005
  }
28874
- if (this.elementModel) {
28875
- this.elementModel.dispose();
28876
- }
28877
29006
  this.elementModel = null;
29007
+ }
29008
+ cancelCurrentAction() {
29009
+ this.resetStrokeState();
29010
+ }
29011
+ onMouseDown(_event) {
29012
+ const previousElement = this.elementModel;
29013
+ this.resetStrokeState();
29014
+ if (previousElement) {
29015
+ previousElement.dispose();
29016
+ }
28878
29017
  this.modelGetter().then((model) => {
28879
29018
  if (model) {
28880
29019
  this.elementModel = model.createCurve(true);
@@ -28887,24 +29026,26 @@ var CurveTool = class extends WhiteboardTool {
28887
29026
  }
28888
29027
  const MIN_DISTANCE = 2;
28889
29028
  if (this.elementModel) {
29029
+ if (!this.elementModel.isAttached()) {
29030
+ this.resetStrokeState();
29031
+ return;
29032
+ }
28890
29033
  let lastX = 0;
28891
29034
  let lastY = 0;
28892
- if (this.pendingPoints.length >= 3) {
28893
- lastX = this.pendingPoints[this.pendingPoints.length - 3];
28894
- lastY = this.pendingPoints[this.pendingPoints.length - 2];
29035
+ if (this.pendingPoints.length >= 2) {
29036
+ lastX = this.pendingPoints[this.pendingPoints.length - 2];
29037
+ lastY = this.pendingPoints[this.pendingPoints.length - 1];
28895
29038
  } else {
28896
29039
  const len = this.elementModel.points.length;
28897
- if (len >= 3) {
28898
- lastX = this.elementModel.points[len - 3];
28899
- lastY = this.elementModel.points[len - 2];
29040
+ if (len >= 2) {
29041
+ lastX = this.elementModel.points[len - 2];
29042
+ lastY = this.elementModel.points[len - 1];
28900
29043
  }
28901
29044
  }
28902
29045
  const dist = Math.max(Math.abs(lastX - event.point.x), Math.abs(lastY - event.point.y));
28903
29046
  if (dist >= MIN_DISTANCE) {
28904
29047
  this.pointCount += 1;
28905
- const nativeEvent = event.event;
28906
- const pressure = nativeEvent.pointerType === "pen" && nativeEvent.pressure > 0 ? nativeEvent.pressure : 0;
28907
- this.pendingPoints.push(event.point.x, event.point.y, pressure);
29048
+ this.pendingPoints.push(event.point.x, event.point.y);
28908
29049
  if (!this.flushRafId) {
28909
29050
  this.flushRafId = requestAnimationFrame(this.flushPendingPoints);
28910
29051
  }
@@ -28917,22 +29058,27 @@ var CurveTool = class extends WhiteboardTool {
28917
29058
  this.flushRafId = 0;
28918
29059
  }
28919
29060
  this.flushPendingPoints();
29061
+ if (!this.elementModel || !this.elementModel.isAttached()) {
29062
+ this.resetStrokeState();
29063
+ return;
29064
+ }
29065
+ const currentElement = this.elementModel;
29066
+ const currentUuid = currentElement.uuid;
29067
+ const currentPointCount = this.pointCount;
28920
29068
  this.modelGetter().then((model) => {
28921
29069
  if (!model) {
28922
29070
  return;
28923
29071
  }
28924
- if (this.pointCount < 3 && this.elementModel) {
28925
- if (this.elementModel) {
28926
- model.removeElementItem(this.elementModel.uuid);
28927
- }
29072
+ if (currentPointCount < 3) {
29073
+ model.removeElementItem(currentUuid);
28928
29074
  }
28929
- if (this.elementModel) {
28930
- this.elementModel.shadow = "";
29075
+ if (currentElement.isAttached()) {
29076
+ currentElement.shadow = "";
28931
29077
  }
28932
- if (this.elementModel && event.event.metaKey) {
28933
- const result = this.recognizer.recognize(this.elementModel.points);
29078
+ if (currentElement.isAttached() && event.event.metaKey) {
29079
+ const result = this.recognizer.recognize(currentElement.points);
28934
29080
  if (result) {
28935
- model.removeElementItem(this.elementModel.uuid);
29081
+ model.removeElementItem(currentUuid);
28936
29082
  if (/^rectangle/.test(result.shape)) {
28937
29083
  const rect = model.createRectangle(false);
28938
29084
  rect?.setPoints([result.minX, result.minY, result.maxX, result.maxY]);
@@ -28951,6 +29097,9 @@ var CurveTool = class extends WhiteboardTool {
28951
29097
  }
28952
29098
  }
28953
29099
  }
29100
+ if (this.elementModel === currentElement) {
29101
+ this.resetStrokeState();
29102
+ }
28954
29103
  });
28955
29104
  }
28956
29105
  };
@@ -29148,6 +29297,9 @@ var SelectorTool = class extends WhiteboardTool {
29148
29297
  _defineProperty22(this, "showLiveCursor", false);
29149
29298
  this.selectElementsModel = selectElementsModel;
29150
29299
  }
29300
+ isSelectableItem(item) {
29301
+ return !!item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.data.role !== "background";
29302
+ }
29151
29303
  onMouseDown(event) {
29152
29304
  this.from = null;
29153
29305
  this.to = null;
@@ -29173,7 +29325,7 @@ var SelectorTool = class extends WhiteboardTool {
29173
29325
  this.elementModel.setPoints([rect.topLeft.x, rect.topLeft.y, rect.size.width, rect.size.height]);
29174
29326
  this.selectElementsModel.clearSelectElementForSelf();
29175
29327
  this.scope.project.activeLayer.children.forEach((item) => {
29176
- if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0) {
29328
+ if (this.isSelectableItem(item)) {
29177
29329
  if (rect.contains(item.bounds)) {
29178
29330
  this.selectElements.set(item.data.uuid, item.data.ownerId);
29179
29331
  } else {
@@ -29199,7 +29351,7 @@ var SelectorTool = class extends WhiteboardTool {
29199
29351
  return result;
29200
29352
  }, []);
29201
29353
  const hitResult = this.scope.project.hitTest(event.point);
29202
- if (hitResult && hitResult.item.data.uuid) {
29354
+ if (hitResult && hitResult.item.data.uuid && this.isSelectableItem(hitResult.item)) {
29203
29355
  elements.push({
29204
29356
  elementId: hitResult.item.data.uuid,
29205
29357
  ownerId: hitResult.item.data.ownerId
@@ -29743,7 +29895,7 @@ var Editor = class extends EventEmitter5 {
29743
29895
  };
29744
29896
  this.rootView.style.opacity = "0";
29745
29897
  }
29746
- if (target === this.frame) {
29898
+ if (target === this.frame && this.editorConfig?.translatable()) {
29747
29899
  evt.preventDefault();
29748
29900
  this.editMode = "translate";
29749
29901
  this.lastEditPoint = {
@@ -30048,7 +30200,6 @@ var Editor = class extends EventEmitter5 {
30048
30200
  this.shadowContainer.remove();
30049
30201
  this.shadowScope.project.activeLayer.addChild(this.shadowContainer);
30050
30202
  this.targets.forEach((model) => {
30051
- console.log("[][][] translateShadow model", model.root._dEH);
30052
30203
  model.shadow = this.shadowContainer.data.uuid;
30053
30204
  });
30054
30205
  }
@@ -30929,6 +31080,9 @@ var EraserTool = class extends WhiteboardTool {
30929
31080
  this.elementModel.appendPoints([event.point.x, event.point.y]);
30930
31081
  }
30931
31082
  this.scope.project.activeLayer.children.forEach((item) => {
31083
+ if (item.data.role === "background") {
31084
+ return;
31085
+ }
30932
31086
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
30933
31087
  segments: true,
30934
31088
  stroke: true,
@@ -30939,6 +31093,9 @@ var EraserTool = class extends WhiteboardTool {
30939
31093
  }
30940
31094
  });
30941
31095
  this.shadowScope.project.activeLayer.children.forEach((item) => {
31096
+ if (item.data.role === "background") {
31097
+ return;
31098
+ }
30942
31099
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
30943
31100
  segments: true,
30944
31101
  stroke: true,
@@ -31398,16 +31555,7 @@ var IndexedNavigation = class extends EventEmitter11 {
31398
31555
  return this.pageModel.pageList().filter((id) => /^_i_/.test(id));
31399
31556
  }
31400
31557
  get head() {
31401
- const headId = Object.keys(this.list).find((key) => {
31402
- return this.list[key] && this.list[key].prev === "";
31403
- });
31404
- if (!headId) {
31405
- log3("indexed navigation confusion", {
31406
- list: JSON.stringify(this.list)
31407
- }, "error");
31408
- throw new Error("indexed navigation confusion");
31409
- }
31410
- return headId;
31558
+ return this.ensureValidList().head;
31411
31559
  }
31412
31560
  get lastNodeId() {
31413
31561
  let currentId = this.head;
@@ -31427,7 +31575,8 @@ var IndexedNavigation = class extends EventEmitter11 {
31427
31575
  _defineProperty36(this, "list", {});
31428
31576
  _defineProperty36(this, "hasPermission", void 0);
31429
31577
  _defineProperty36(this, "handleIndexedPageMapUpdate", (_evt) => {
31430
- this.list = this.indexedPageMap.get("list");
31578
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
31579
+ this.ensureValidList("update");
31431
31580
  const needRemoveList = this.pageModel.pageList().filter((v) => /^_i_/.test(v) && Object.keys(this.list).indexOf(v) < 0);
31432
31581
  const needAddList = Object.keys(this.list).filter((v) => this.pageModel.pageList().indexOf(v) < 0);
31433
31582
  this.indexedPageMap.doc.transact(() => {
@@ -31460,11 +31609,156 @@ var IndexedNavigation = class extends EventEmitter11 {
31460
31609
  this.indexedPageMap.set("list", this.list);
31461
31610
  this.idPool.add("_i_");
31462
31611
  } else {
31463
- this.list = this.indexedPageMap.get("list");
31612
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
31464
31613
  Object.keys(this.list).forEach((id) => this.idPool.add(id));
31614
+ this.ensureValidList("constructor");
31465
31615
  }
31466
31616
  this.indexedPageMap.observe(this.handleIndexedPageMapUpdate);
31467
31617
  }
31618
+ normalizeList(list) {
31619
+ if (!list || typeof list !== "object" || Array.isArray(list)) {
31620
+ return null;
31621
+ }
31622
+ const nextList = {};
31623
+ for (const [id, node] of Object.entries(list)) {
31624
+ if (!/^_i_/.test(id) || !node || typeof node !== "object") {
31625
+ return null;
31626
+ }
31627
+ const {
31628
+ prev,
31629
+ next
31630
+ } = node;
31631
+ if (typeof prev !== "string" || typeof next !== "string") {
31632
+ return null;
31633
+ }
31634
+ nextList[id] = {
31635
+ prev,
31636
+ next
31637
+ };
31638
+ }
31639
+ return nextList;
31640
+ }
31641
+ validateList(list) {
31642
+ const ids = Object.keys(list);
31643
+ if (ids.length === 0) {
31644
+ return {
31645
+ valid: false,
31646
+ reason: "empty list"
31647
+ };
31648
+ }
31649
+ const heads = ids.filter((id) => list[id].prev === "");
31650
+ if (heads.length !== 1) {
31651
+ return {
31652
+ valid: false,
31653
+ reason: `expected one head, got ${heads.length}`
31654
+ };
31655
+ }
31656
+ for (const id of ids) {
31657
+ const node = list[id];
31658
+ if (node.prev && !list[node.prev]) {
31659
+ return {
31660
+ valid: false,
31661
+ reason: `missing prev node ${node.prev}`
31662
+ };
31663
+ }
31664
+ if (node.next && !list[node.next]) {
31665
+ return {
31666
+ valid: false,
31667
+ reason: `missing next node ${node.next}`
31668
+ };
31669
+ }
31670
+ if (node.prev && list[node.prev].next !== id) {
31671
+ return {
31672
+ valid: false,
31673
+ reason: `prev link mismatch for ${id}`
31674
+ };
31675
+ }
31676
+ if (node.next && list[node.next].prev !== id) {
31677
+ return {
31678
+ valid: false,
31679
+ reason: `next link mismatch for ${id}`
31680
+ };
31681
+ }
31682
+ }
31683
+ const visited = /* @__PURE__ */ new Set();
31684
+ let currentId = heads[0];
31685
+ while (currentId) {
31686
+ if (visited.has(currentId)) {
31687
+ return {
31688
+ valid: false,
31689
+ reason: `cycle at ${currentId}`
31690
+ };
31691
+ }
31692
+ visited.add(currentId);
31693
+ currentId = list[currentId].next;
31694
+ }
31695
+ if (visited.size !== ids.length) {
31696
+ return {
31697
+ valid: false,
31698
+ reason: "detached nodes"
31699
+ };
31700
+ }
31701
+ return {
31702
+ valid: true,
31703
+ head: heads[0]
31704
+ };
31705
+ }
31706
+ createList(ids) {
31707
+ return ids.reduce((list, id, index) => {
31708
+ list[id] = {
31709
+ prev: ids[index - 1] ?? "",
31710
+ next: ids[index + 1] ?? ""
31711
+ };
31712
+ return list;
31713
+ }, {});
31714
+ }
31715
+ repairList(reason) {
31716
+ const pageIds = this.idList;
31717
+ const ids = pageIds.length > 0 ? pageIds : Object.keys(this.list).filter((id) => /^_i_/.test(id));
31718
+ const nextIds = ids.length > 0 ? ids : ["_i_"];
31719
+ const nextList = this.createList(nextIds);
31720
+ log3("indexed navigation recovered", {
31721
+ reason,
31722
+ pageList: JSON.stringify(this.pageModel.pageList()),
31723
+ list: JSON.stringify(this.list),
31724
+ nextList: JSON.stringify(nextList)
31725
+ }, "warn");
31726
+ const apply = () => {
31727
+ for (const id of nextIds) {
31728
+ if (this.pageModel.pageList().indexOf(id) < 0) {
31729
+ this.pageModel.addPage(id);
31730
+ }
31731
+ }
31732
+ this.list = nextList;
31733
+ this.indexedPageMap.set("list", nextList);
31734
+ };
31735
+ if (this.indexedPageMap.doc) {
31736
+ this.indexedPageMap.doc.transact(apply);
31737
+ } else {
31738
+ apply();
31739
+ }
31740
+ nextIds.forEach((id) => this.idPool.add(id));
31741
+ return nextIds[0];
31742
+ }
31743
+ ensureValidList() {
31744
+ let context = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "read";
31745
+ const normalized = this.normalizeList(this.list);
31746
+ if (!normalized) {
31747
+ return {
31748
+ head: this.repairList(`${context}: invalid list`)
31749
+ };
31750
+ }
31751
+ this.list = normalized;
31752
+ const validation = this.validateList(this.list);
31753
+ if (!validation.valid) {
31754
+ return {
31755
+ head: this.repairList(`${context}: ${validation.reason}`)
31756
+ };
31757
+ }
31758
+ return {
31759
+ head: validation.head
31760
+ };
31761
+ }
31468
31762
  getNextId() {
31469
31763
  const cache = Array.from(this.idPool).filter((id) => this.idList.indexOf(id) < 0);
31470
31764
  if (cache.length > 0) {
@@ -32414,7 +32708,6 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
32414
32708
  this.emitter.emit("elementDeselected", userId);
32415
32709
  return;
32416
32710
  }
32417
- editor.show();
32418
32711
  const targetLayerId = this.userMap(userId).get(WhiteboardKeys.currentPage);
32419
32712
  const selfLayerId = this.userMap(this.userId).get(WhiteboardKeys.currentPage);
32420
32713
  if (targetLayerId !== selfLayerId || !this.layers.has(targetLayerId)) {
@@ -32422,7 +32715,13 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
32422
32715
  }
32423
32716
  const elementModels = elements.map((id) => {
32424
32717
  return this.layers.get(targetLayerId).elementModels.get(id);
32425
- }).filter((v) => !!v);
32718
+ }).filter((model) => !!model && !model.isBackground);
32719
+ if (elementModels.length === 0) {
32720
+ editor.hidden();
32721
+ this.emitter.emit("elementDeselected", userId);
32722
+ return;
32723
+ }
32724
+ editor.show();
32426
32725
  editor.setTargets(elementModels);
32427
32726
  if (elementModels.length === 1) {
32428
32727
  const model = elementModels[0];
@@ -32614,14 +32913,17 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
32614
32913
  this.camera.resetViewMatrixToMain();
32615
32914
  }
32616
32915
  };
32617
- this.emitter.insertImage = (src, pageId) => {
32916
+ this.emitter.insertImage = (src, options) => {
32618
32917
  if (!/https/.test(src)) {
32619
32918
  log4("[@netless/forge-whiteboard] invalid image url, src needs to be in the HTTPS protocol.", {
32620
32919
  src
32621
32920
  }, "warn");
32622
32921
  return;
32623
32922
  }
32624
- let targetPageId = pageId;
32923
+ const normalizedOptions = typeof options === "string" ? {
32924
+ pageId: options
32925
+ } : options ?? {};
32926
+ let targetPageId = normalizedOptions.pageId;
32625
32927
  if (!targetPageId) {
32626
32928
  targetPageId = this.pageModel.getCurrentPage(this.userManager.selfId);
32627
32929
  }
@@ -32629,13 +32931,16 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
32629
32931
  log4("[@netless/forge-whiteboard] page not found", {}, "warn");
32630
32932
  return;
32631
32933
  }
32632
- this.layers.get(targetPageId)?.createImage(src);
32934
+ this.layers.get(targetPageId)?.createImage(src, normalizedOptions.fit ?? "legacy", normalizedOptions.role ?? "normal");
32633
32935
  };
32634
32936
  this.emitter.removeElement = (pageId, elementId) => {
32635
32937
  if (!this.layers.has(pageId)) {
32636
32938
  log4("[@netless/forge-whiteboard] page not found", {}, "warn");
32637
32939
  return;
32638
32940
  }
32941
+ if (this.layers.get(pageId)?.isBackgroundElement(elementId)) {
32942
+ return;
32943
+ }
32639
32944
  this.layers.get(pageId)?.removeElementItem(elementId);
32640
32945
  };
32641
32946
  this.emitter.getViewModel = (userId) => {
@@ -32686,7 +32991,11 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
32686
32991
  if (model) {
32687
32992
  if (model.elements.doc) {
32688
32993
  model.elements.doc.transact(() => {
32689
- model.elements.clear();
32994
+ for (const key of Array.from(model.elements.keys())) {
32995
+ if (!model.isBackgroundElement(key)) {
32996
+ model.elements.delete(key);
32997
+ }
32998
+ }
32690
32999
  }, elementsUndoOrigin);
32691
33000
  } else {
32692
33001
  model.elementModels.clear();
@@ -33098,7 +33407,9 @@ var WhiteboardApplication = class _WhiteboardApplication extends AbstractApplica
33098
33407
  const at = this.findElementIndex(element.data.index, parent);
33099
33408
  for (let i = at; i >= 0; i--) {
33100
33409
  const child = children[i - 1];
33101
- if (!child || child.data.index < element.data.index || child.data.uuid < element.data.uuid) {
33410
+ const childRole = child?.data.role === "background" ? 0 : 1;
33411
+ const elementRole = element.data.role === "background" ? 0 : 1;
33412
+ if (!child || childRole < elementRole || childRole === elementRole && child.data.index < element.data.index || childRole === elementRole && child.data.index === element.data.index && child.data.uuid < element.data.uuid) {
33102
33413
  parent.insertChild(i, element);
33103
33414
  break;
33104
33415
  }