@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;
@@ -25651,6 +25651,7 @@ var import_forge_room6 = require("@netless/forge-room");
25651
25651
 
25652
25652
  // src/model/renderable/CurveModel.ts
25653
25653
  var Y2 = __toESM(require("yjs"), 1);
25654
+ var import_lodash = __toESM(require_lodash(), 1);
25654
25655
 
25655
25656
  // ../../node_modules/.pnpm/perfect-freehand@1.2.2/node_modules/perfect-freehand/dist/esm/index.mjs
25656
25657
  function $(e, t, u, x = (h) => h) {
@@ -25803,6 +25804,12 @@ function ae(e, t = {}) {
25803
25804
  // src/model/renderable/ElementModel.ts
25804
25805
  var Y = __toESM(require("yjs"), 1);
25805
25806
  var import_forge_room = require("@netless/forge-room");
25807
+
25808
+ // src/utils/constant.ts
25809
+ var elementsUndoOrigin = "elementsUndoOrigin";
25810
+ var backgroundElementsUndoOrigin = "backgroundElementsUndoOrigin";
25811
+
25812
+ // src/model/renderable/ElementModel.ts
25806
25813
  function _defineProperty(e, r, t) {
25807
25814
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
25808
25815
  }
@@ -25832,6 +25839,12 @@ var ElementModel = class _ElementModel {
25832
25839
  get type() {
25833
25840
  return this.root.get("type");
25834
25841
  }
25842
+ get role() {
25843
+ return this.root.get("role") ?? "normal";
25844
+ }
25845
+ get isBackground() {
25846
+ return this.role === "background";
25847
+ }
25835
25848
  get strokeWidth() {
25836
25849
  return this.root.get(_ElementModel.KEYS.strokeWidth);
25837
25850
  }
@@ -25890,6 +25903,9 @@ var ElementModel = class _ElementModel {
25890
25903
  get isPerformanceEnvironment() {
25891
25904
  return this.isPerformanceMode() && this.shouldUseLocalPoints;
25892
25905
  }
25906
+ isAttached() {
25907
+ return this.root.has(_ElementModel.KEYS.uuid);
25908
+ }
25893
25909
  constructor(root, scope, liveCursor, isPerformanceMode) {
25894
25910
  _defineProperty(this, "shadowEmitter", null);
25895
25911
  _defineProperty(this, "root", void 0);
@@ -25975,16 +25991,12 @@ var ElementModel = class _ElementModel {
25975
25991
  }
25976
25992
  }
25977
25993
  bindObserver() {
25978
- const beforeL = this.root._eH?.l?.length ?? -1;
25979
- const beforeDL = this.root._dEH?.l?.length ?? -1;
25980
25994
  (0, import_forge_room.removeDeepObserver)(this.root, this.handlePropChange);
25981
25995
  this.subBindObserver();
25982
- const afterRemoveL = this.root._eH?.l?.length ?? -1;
25983
- const afterRemoveDL = this.root._dEH?.l?.length ?? -1;
25984
25996
  this.root.observeDeep(this.handlePropChange);
25985
- const afterAddL = this.root._eH?.l?.length ?? -1;
25986
- const afterAddDL = this.root._dEH?.l?.length ?? -1;
25987
- console.log(`[][][] bindObserver uuid=${this.uuid} doc=${!!this.root.doc} _eH: ${beforeL}->${afterRemoveL}->${afterAddL} _dEH: ${beforeDL}->${afterRemoveDL}->${afterAddDL}`);
25997
+ }
25998
+ mutationOrigin() {
25999
+ return elementsUndoOrigin;
25988
26000
  }
25989
26001
  subBindObserver() {
25990
26002
  }
@@ -26023,62 +26035,81 @@ var ElementModel = class _ElementModel {
26023
26035
  this.item.data.uuid = this.uuid;
26024
26036
  this.item.data.index = this.index;
26025
26037
  this.item.data.type = this.root.get("type");
26038
+ this.item.data.role = this.role;
26026
26039
  this.item.data.ownerId = this.ownerId;
26027
26040
  this.item.applyMatrix = false;
26028
26041
  }
26029
26042
  }
26030
26043
  appendPoints(points) {
26031
26044
  if (this.isPerformanceEnvironment) {
26032
- this.appendPointsPerformance(points);
26045
+ return this.appendPointsPerformance(points);
26033
26046
  } else {
26034
- this.appendPointsDirect(points);
26047
+ return this.appendPointsDirect(points);
26035
26048
  }
26036
26049
  }
26050
+ getPointsArray() {
26051
+ const yArray = this.root.get(_ElementModel.KEYS.points);
26052
+ return yArray instanceof Y.Array ? yArray : null;
26053
+ }
26037
26054
  appendPointsDirect(points) {
26038
- this.root.get(_ElementModel.KEYS.points).push(points);
26055
+ const yArray = this.getPointsArray();
26056
+ if (!yArray) {
26057
+ return false;
26058
+ }
26059
+ yArray.push(points);
26060
+ return true;
26039
26061
  }
26040
26062
  appendPointsPerformance(points) {
26063
+ const yArray = this.getPointsArray();
26064
+ if (!yArray) {
26065
+ return false;
26066
+ }
26041
26067
  this.localPoints = this.localPoints.concat(points);
26042
26068
  this.onVectorUpdate();
26043
26069
  if (this.appendPointsTimer) {
26044
26070
  window.clearTimeout(this.appendPointsTimer);
26045
26071
  }
26046
26072
  if (this.localPoints.length % 80 === 0) {
26047
- const yArray = this.root.get(_ElementModel.KEYS.points);
26048
26073
  yArray?.push(this.localPoints.slice(yArray.length));
26049
26074
  }
26050
26075
  this.appendPointsTimer = window.setTimeout(() => {
26051
26076
  this.appendPointsTimer = null;
26052
26077
  if (this.localPoints.length > 0) {
26053
- const yArray = this.root.get(_ElementModel.KEYS.points);
26054
- yArray?.push(this.localPoints.slice(yArray.length));
26078
+ const nextArray = this.getPointsArray();
26079
+ nextArray?.push(this.localPoints.slice(nextArray.length));
26055
26080
  }
26056
26081
  }, 100);
26082
+ return true;
26057
26083
  }
26058
26084
  setPoints(points) {
26059
26085
  if (this.isPerformanceEnvironment) {
26060
- this.setPointsPerformance(points);
26086
+ return this.setPointsPerformance(points);
26061
26087
  } else {
26062
- this.setPointsDirect(points);
26088
+ return this.setPointsDirect(points);
26063
26089
  }
26064
26090
  }
26065
26091
  setPointsDirect(points) {
26066
26092
  if (this.root.doc) {
26093
+ const yArray = this.getPointsArray();
26094
+ if (!yArray) {
26095
+ return false;
26096
+ }
26067
26097
  this.root.doc.transact(() => {
26068
- const yArray = this.root.get(_ElementModel.KEYS.points);
26069
- if (yArray) {
26070
- yArray.delete(0, yArray.length);
26071
- yArray.push(points);
26072
- }
26073
- });
26098
+ yArray.delete(0, yArray.length);
26099
+ yArray.push(points);
26100
+ }, this.mutationOrigin());
26074
26101
  } else {
26075
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
26102
+ const yArray = this.getPointsArray() || new Y.Array();
26076
26103
  yArray.delete(0, yArray.length);
26077
26104
  yArray.push(points);
26078
26105
  this.root.set(_ElementModel.KEYS.points, yArray);
26079
26106
  }
26107
+ return true;
26080
26108
  }
26081
26109
  setPointsPerformance(points) {
26110
+ if (this.root.doc && !this.getPointsArray()) {
26111
+ return false;
26112
+ }
26082
26113
  this.localPoints = points;
26083
26114
  this.onVectorUpdate();
26084
26115
  if (this.setPointsTimer) {
@@ -26086,25 +26117,33 @@ var ElementModel = class _ElementModel {
26086
26117
  }
26087
26118
  this.setPointsTimer = window.setTimeout(() => {
26088
26119
  if (this.root.doc) {
26120
+ const yArray = this.getPointsArray();
26121
+ if (!yArray) {
26122
+ return;
26123
+ }
26089
26124
  this.root.doc.transact(() => {
26090
- const yArray = this.root.get(_ElementModel.KEYS.points);
26091
- if (yArray) {
26092
- yArray.delete(0, yArray.length);
26093
- yArray.push(points);
26094
- }
26095
- });
26125
+ yArray.delete(0, yArray.length);
26126
+ yArray.push(points);
26127
+ }, this.mutationOrigin());
26096
26128
  } else {
26097
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
26129
+ const yArray = this.getPointsArray() || new Y.Array();
26098
26130
  yArray.delete(0, yArray.length);
26099
26131
  yArray.push(points);
26100
26132
  this.root.set(_ElementModel.KEYS.points, yArray);
26101
26133
  }
26102
26134
  }, 100);
26135
+ return true;
26103
26136
  }
26104
26137
  appendPointsMatrix(matrix) {
26105
26138
  const current = new this.scope.Matrix(this.pointsMatrix);
26106
26139
  const next = matrix.appended(current);
26107
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26140
+ if (this.root.doc) {
26141
+ this.root.doc.transact(() => {
26142
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26143
+ }, this.mutationOrigin());
26144
+ } else {
26145
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26146
+ }
26108
26147
  }
26109
26148
  rotateByOffset(angle, centerX, centerY) {
26110
26149
  const current = new this.scope.Matrix(this.pointsMatrix);
@@ -26118,7 +26157,13 @@ var ElementModel = class _ElementModel {
26118
26157
  matrix2.rotate(delta, centerX, centerY);
26119
26158
  next = matrix2.appended(next);
26120
26159
  }
26121
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26160
+ if (this.root.doc) {
26161
+ this.root.doc.transact(() => {
26162
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26163
+ }, this.mutationOrigin());
26164
+ } else {
26165
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
26166
+ }
26122
26167
  return next.rotation;
26123
26168
  }
26124
26169
  getStyleKeys() {
@@ -26129,7 +26174,6 @@ var ElementModel = class _ElementModel {
26129
26174
  this.subDispose();
26130
26175
  }
26131
26176
  disposeObserver() {
26132
- console.log(`[][][] disposeObserver uuid=${this.uuid}`);
26133
26177
  (0, import_forge_room.removeDeepObserver)(this.root, this.handlePropChange);
26134
26178
  }
26135
26179
  };
@@ -26170,6 +26214,7 @@ var EditorConfig = class _EditorConfig {
26170
26214
  constructor() {
26171
26215
  _defineProperty2(this, "resizeModel", () => "eight");
26172
26216
  _defineProperty2(this, "uniformScale", () => false);
26217
+ _defineProperty2(this, "translatable", () => true);
26173
26218
  _defineProperty2(this, "controlPoints", []);
26174
26219
  }
26175
26220
  merge(other) {
@@ -26178,6 +26223,7 @@ var EditorConfig = class _EditorConfig {
26178
26223
  const j = RESIZE_MODEL_LEVEL.findIndex((v) => v === other.resizeModel());
26179
26224
  next.resizeModel = () => RESIZE_MODEL_LEVEL[Math.max(i, j)];
26180
26225
  next.uniformScale = this.uniformScale || other.uniformScale;
26226
+ next.translatable = () => this.translatable() && other.translatable();
26181
26227
  next.controlPoints = [];
26182
26228
  return next;
26183
26229
  }
@@ -26208,7 +26254,7 @@ var CurveModel = class extends ElementModel {
26208
26254
  _defineProperty3(this, "debug", false);
26209
26255
  _defineProperty3(this, "clearLocalPointsWhenYPointsChange", false);
26210
26256
  _defineProperty3(this, "shouldUseLocalPoints", true);
26211
- _defineProperty3(this, "localPointsPick", 6);
26257
+ _defineProperty3(this, "localPointsPick", 4);
26212
26258
  if (!this.root.doc || !this.root.has("type")) {
26213
26259
  this.root.set("type", "curve");
26214
26260
  }
@@ -26220,7 +26266,6 @@ var CurveModel = class extends ElementModel {
26220
26266
  return (a2 + b2) / 2;
26221
26267
  }
26222
26268
  parsePoints(points) {
26223
- const hasRealPressure = points.some((p) => p.length >= 3 && p[2] > 0);
26224
26269
  const viewScale = this.scope.project.view.matrix.scaling.x || 1;
26225
26270
  const taper = this.strokeWidth * 5 / viewScale;
26226
26271
  const streamline = Math.min(0.7, 0.7 * viewScale);
@@ -26229,7 +26274,7 @@ var CurveModel = class extends ElementModel {
26229
26274
  smoothing: 0.7,
26230
26275
  thinning: 0.5,
26231
26276
  streamline,
26232
- simulatePressure: !hasRealPressure,
26277
+ simulatePressure: true,
26233
26278
  start: {
26234
26279
  taper,
26235
26280
  cap: true
@@ -26240,36 +26285,13 @@ var CurveModel = class extends ElementModel {
26240
26285
  }
26241
26286
  });
26242
26287
  }
26243
- isPressureValue(value) {
26244
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
26245
- }
26246
- pointStride(points) {
26247
- if (points.length >= 3 && points.length % 3 === 0) {
26248
- let hasPressureSlot = false;
26249
- for (let i = 2; i < points.length; i += 3) {
26250
- if (!this.isPressureValue(points[i])) {
26251
- return 2;
26252
- }
26253
- hasPressureSlot = true;
26254
- }
26255
- if (hasPressureSlot) {
26256
- return 3;
26257
- }
26258
- }
26259
- return 2;
26260
- }
26261
26288
  matrixedPoints() {
26262
26289
  const points = this.localPoints.length === 0 ? this.points : this.localPoints;
26263
26290
  const matrix = new this.scope.Matrix(this.pointsMatrix);
26264
- const output = [];
26265
- const stride = this.pointStride(points);
26266
- for (let i = 0, len = points.length; i + 1 < len; i += stride) {
26267
- const p = new this.scope.Point(points[i], points[i + 1]);
26268
- const tp = p.transform(matrix);
26269
- const pressure = stride === 3 ? points[i + 2] ?? 0 : 0;
26270
- output.push([tp.x, tp.y, pressure]);
26271
- }
26272
- return output;
26291
+ return (0, import_lodash.chunk)(points, 2).filter((point) => point.length === 2).map((_ref) => {
26292
+ let [x, y] = _ref;
26293
+ return new this.scope.Point(x, y).transform(matrix);
26294
+ }).map((point) => [point.x, point.y]);
26273
26295
  }
26274
26296
  createPath(points) {
26275
26297
  const path = new this.scope.Path();
@@ -26343,12 +26365,11 @@ var CurveModel = class extends ElementModel {
26343
26365
  liveCursorPoint() {
26344
26366
  const yArray = this.root.get(ElementModel.KEYS.points);
26345
26367
  const points = yArray.toArray();
26346
- const stride = this.pointStride(points);
26347
- if (points.length < stride) {
26368
+ if (points.length < 2) {
26348
26369
  return null;
26349
26370
  }
26350
26371
  const len = points.length;
26351
- const point = new this.scope.Point(points[len - stride], points[len - stride + 1]);
26372
+ const point = new this.scope.Point(points[len - 2], points[len - 1]);
26352
26373
  return point.transform(new this.scope.Matrix(this.pointsMatrix));
26353
26374
  }
26354
26375
  onStyleKeyUpdate(key) {
@@ -26449,7 +26470,7 @@ var SelectorModel = class extends ElementModel {
26449
26470
  var Y4 = __toESM(require("yjs"), 1);
26450
26471
 
26451
26472
  // src/utils/paperjs.ts
26452
- var import_lodash = __toESM(require_lodash(), 1);
26473
+ var import_lodash2 = __toESM(require_lodash(), 1);
26453
26474
  var import_forge_room2 = require("@netless/forge-room");
26454
26475
  function _defineProperty5(e, r, t) {
26455
26476
  return (r = _toPropertyKey5(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -26483,7 +26504,7 @@ function serializePath(path) {
26483
26504
  }, []);
26484
26505
  }
26485
26506
  function deserializePath(points, scope, matrix) {
26486
- const segmentGroup = (0, import_lodash.chunk)(points, 6);
26507
+ const segmentGroup = (0, import_lodash2.chunk)(points, 6);
26487
26508
  const path = new scope.Path();
26488
26509
  path.segments = segmentGroup.map((v) => deserializeSegment(v, scope, matrix));
26489
26510
  return path;
@@ -26626,7 +26647,7 @@ var SegmentsModel = class extends ElementModel {
26626
26647
 
26627
26648
  // src/model/renderable/LineModel.ts
26628
26649
  var Y5 = __toESM(require("yjs"), 1);
26629
- var import_lodash3 = __toESM(require_lodash(), 1);
26650
+ var import_lodash4 = __toESM(require_lodash(), 1);
26630
26651
 
26631
26652
  // src/tool/WhiteboardTool.ts
26632
26653
  function _defineProperty7(e, r, t) {
@@ -26671,7 +26692,14 @@ var WhiteboardTool = class {
26671
26692
  }
26672
26693
  this.pendingDragEvent = null;
26673
26694
  this.shadowEmitter.setActive(true);
26674
- this.onMouseDown(event);
26695
+ try {
26696
+ this.onMouseDown(event);
26697
+ } catch (error) {
26698
+ this.cancelCurrentAction();
26699
+ this.eventAvailable = false;
26700
+ this.shadowEmitter.setActive(false);
26701
+ throw error;
26702
+ }
26675
26703
  });
26676
26704
  _defineProperty7(this, "flushPendingDrag", () => {
26677
26705
  this.dragRafId = 0;
@@ -26679,7 +26707,14 @@ var WhiteboardTool = class {
26679
26707
  this.lastDragTime = performance.now();
26680
26708
  const event = this.pendingDragEvent;
26681
26709
  this.pendingDragEvent = null;
26682
- this.onMouseDrag(event);
26710
+ try {
26711
+ this.onMouseDrag(event);
26712
+ } catch (error) {
26713
+ this.cancelCurrentAction();
26714
+ this.eventAvailable = false;
26715
+ this.shadowEmitter.setActive(false);
26716
+ throw error;
26717
+ }
26683
26718
  }
26684
26719
  });
26685
26720
  _defineProperty7(this, "onMouseDragSelf", (event) => {
@@ -26690,7 +26725,14 @@ var WhiteboardTool = class {
26690
26725
  if (now - this.lastDragTime >= DRAG_FRAME_MS) {
26691
26726
  this.lastDragTime = now;
26692
26727
  this.pendingDragEvent = null;
26693
- this.onMouseDrag(event);
26728
+ try {
26729
+ this.onMouseDrag(event);
26730
+ } catch (error) {
26731
+ this.cancelCurrentAction();
26732
+ this.eventAvailable = false;
26733
+ this.shadowEmitter.setActive(false);
26734
+ throw error;
26735
+ }
26694
26736
  } else {
26695
26737
  this.pendingDragEvent = event;
26696
26738
  if (!this.dragRafId) {
@@ -26707,11 +26749,26 @@ var WhiteboardTool = class {
26707
26749
  this.dragRafId = 0;
26708
26750
  }
26709
26751
  if (this.pendingDragEvent) {
26710
- this.onMouseDrag(this.pendingDragEvent);
26711
- this.pendingDragEvent = null;
26752
+ try {
26753
+ this.onMouseDrag(this.pendingDragEvent);
26754
+ this.pendingDragEvent = null;
26755
+ } catch (error) {
26756
+ this.pendingDragEvent = null;
26757
+ this.cancelCurrentAction();
26758
+ this.eventAvailable = false;
26759
+ this.shadowEmitter.setActive(false);
26760
+ throw error;
26761
+ }
26762
+ }
26763
+ try {
26764
+ this.onMouseUp(event);
26765
+ } catch (error) {
26766
+ this.cancelCurrentAction();
26767
+ this.eventAvailable = false;
26768
+ throw error;
26769
+ } finally {
26770
+ this.shadowEmitter.setActive(false);
26712
26771
  }
26713
- this.onMouseUp(event);
26714
- this.shadowEmitter.setActive(false);
26715
26772
  });
26716
26773
  this.modelGetter = modelGetter;
26717
26774
  this.enableToolEvent = enableToolEvent;
@@ -26722,10 +26779,12 @@ var WhiteboardTool = class {
26722
26779
  this.tool.onMouseDrag = this.onMouseDragSelf;
26723
26780
  this.tool.onMouseUp = this.onMouseUpSelf;
26724
26781
  }
26782
+ cancelCurrentAction() {
26783
+ }
26725
26784
  };
26726
26785
 
26727
26786
  // src/tool/LineTool.ts
26728
- var import_lodash2 = __toESM(require_lodash(), 1);
26787
+ var import_lodash3 = __toESM(require_lodash(), 1);
26729
26788
  function _defineProperty8(e, r, t) {
26730
26789
  return (r = _toPropertyKey8(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
26731
26790
  }
@@ -26774,7 +26833,7 @@ var LineTool = class extends WhiteboardTool {
26774
26833
  const point = path.getPointAt(distance);
26775
26834
  return [point.x, point.y];
26776
26835
  });
26777
- this.elementModel.setPoints((0, import_lodash2.flattenDeep)(points));
26836
+ this.elementModel.setPoints((0, import_lodash3.flattenDeep)(points));
26778
26837
  }
26779
26838
  }
26780
26839
  onMouseUp(_event) {
@@ -26857,7 +26916,7 @@ var LineModel = class extends ElementModel {
26857
26916
  }
26858
26917
  renderLine() {
26859
26918
  const matrix = new this.scope.Matrix(this.pointsMatrix);
26860
- const papperPoints = (0, import_lodash3.chunk)(this.drawPoints, 2).map((item) => {
26919
+ const papperPoints = (0, import_lodash4.chunk)(this.drawPoints, 2).map((item) => {
26861
26920
  return new this.scope.Point(item[0], item[1]).transform(matrix);
26862
26921
  });
26863
26922
  const path = new this.scope.Path();
@@ -26993,7 +27052,7 @@ var LineControlPoint = class {
26993
27052
  const invertedPoint = point.transform(pointsMatrix.inverted());
26994
27053
  const points = this.model["drawPoints"];
26995
27054
  this.position = invertedPoint;
26996
- const clonedPoints = (0, import_lodash3.cloneDeep)(points);
27055
+ const clonedPoints = (0, import_lodash4.cloneDeep)(points);
26997
27056
  clonedPoints[this.options.index * 2] = invertedPoint.x;
26998
27057
  clonedPoints[this.options.index * 2 + 1] = invertedPoint.y;
26999
27058
  this.model.setPoints(clonedPoints);
@@ -27102,7 +27161,6 @@ var PointTextModel = class extends ElementModel {
27102
27161
  if (!this.item) {
27103
27162
  return null;
27104
27163
  }
27105
- console.log("[][][] drawPoints", this.drawPoints);
27106
27164
  const bounds = this.item.internalBounds;
27107
27165
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27108
27166
  const topLeft = new this.scope.Point(this.drawPoints[0], this.drawPoints[1]).transform(matrix);
@@ -27473,12 +27531,9 @@ var RectangleModel = class extends ElementModel {
27473
27531
  }
27474
27532
  };
27475
27533
 
27476
- // src/utils/constant.ts
27477
- var elementsUndoOrigin = "elementsUndoOrigin";
27478
-
27479
27534
  // src/model/renderable/EraserModel.ts
27480
27535
  var Y9 = __toESM(require("yjs"), 1);
27481
- var import_lodash4 = __toESM(require_lodash(), 1);
27536
+ var import_lodash5 = __toESM(require_lodash(), 1);
27482
27537
  function _defineProperty13(e, r, t) {
27483
27538
  return (r = _toPropertyKey13(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
27484
27539
  }
@@ -27546,7 +27601,7 @@ var EraserModel = class extends ElementModel {
27546
27601
  return path;
27547
27602
  }
27548
27603
  parsePoints(points) {
27549
- const groupPoints = (0, import_lodash4.chunk)(points, 2);
27604
+ const groupPoints = (0, import_lodash5.chunk)(points, 2);
27550
27605
  return ae(groupPoints, {
27551
27606
  size: this.strokeWidth,
27552
27607
  smoothing: 0.5,
@@ -27564,7 +27619,7 @@ var EraserModel = class extends ElementModel {
27564
27619
  });
27565
27620
  }
27566
27621
  matrixedPoints() {
27567
- const currentPoints = (0, import_lodash4.chunk)(this.drawPoints, 2).slice(this.sliceBegin);
27622
+ const currentPoints = (0, import_lodash5.chunk)(this.drawPoints, 2).slice(this.sliceBegin);
27568
27623
  return currentPoints.map((_ref) => {
27569
27624
  let [x, y] = _ref;
27570
27625
  return new this.scope.Point(x, y);
@@ -27630,7 +27685,7 @@ var EraserModel = class extends ElementModel {
27630
27685
 
27631
27686
  // src/model/renderable/LaserPointerModel.ts
27632
27687
  var Y10 = __toESM(require("yjs"), 1);
27633
- var import_lodash5 = __toESM(require_lodash(), 1);
27688
+ var import_lodash6 = __toESM(require_lodash(), 1);
27634
27689
  var import_forge_room4 = require("@netless/forge-room");
27635
27690
  function _defineProperty14(e, r, t) {
27636
27691
  return (r = _toPropertyKey14(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -27756,8 +27811,7 @@ var LaserPointerModel = class extends ElementModel {
27756
27811
  matrixedPoints() {
27757
27812
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27758
27813
  const points = this.cachedPoints || this.points;
27759
- console.log("[][][] ,", this.points.length, this.cachedPoints?.length, this.localPoints.length);
27760
- const groupPoints = (0, import_lodash5.chunk)(points, 2).slice(this.sliceBegin);
27814
+ const groupPoints = (0, import_lodash6.chunk)(points, 2).slice(this.sliceBegin);
27761
27815
  return groupPoints.map((_ref) => {
27762
27816
  let [x, y] = _ref;
27763
27817
  return matrix.transform([x, y]);
@@ -27837,7 +27891,7 @@ var WhiteboardPermissions = class extends import_forge_room5.AbstractApplication
27837
27891
 
27838
27892
  // src/model/renderable/StraightLineModel.ts
27839
27893
  var Y11 = __toESM(require("yjs"), 1);
27840
- var import_lodash6 = __toESM(require_lodash(), 1);
27894
+ var import_lodash7 = __toESM(require_lodash(), 1);
27841
27895
  function _defineProperty15(e, r, t) {
27842
27896
  return (r = _toPropertyKey15(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
27843
27897
  }
@@ -27896,7 +27950,7 @@ var StraightLineModel = class extends ElementModel {
27896
27950
  }
27897
27951
  renderLine() {
27898
27952
  const matrix = new this.scope.Matrix(this.pointsMatrix);
27899
- const papperPoints = (0, import_lodash6.chunk)(this.drawPoints, 2).map((item) => {
27953
+ const papperPoints = (0, import_lodash7.chunk)(this.drawPoints, 2).map((item) => {
27900
27954
  return new this.scope.Point(item[0], item[1]).transform(matrix);
27901
27955
  });
27902
27956
  const path = new this.scope.Path();
@@ -28026,6 +28080,7 @@ var ImageModel = class extends ElementModel {
28026
28080
  this.item = new this.scope.Raster(this.uuid);
28027
28081
  const matrix = new this.scope.Matrix(this.pointsMatrix);
28028
28082
  this.item.matrix = matrix;
28083
+ this.item.data.role = this.role;
28029
28084
  }
28030
28085
  onVectorUpdate() {
28031
28086
  const matrix = new this.scope.Matrix(this.pointsMatrix);
@@ -28054,8 +28109,12 @@ var ImageModel = class extends ElementModel {
28054
28109
  const cfg = new EditorConfig();
28055
28110
  cfg.resizeModel = () => "four-corner";
28056
28111
  cfg.uniformScale = () => true;
28112
+ cfg.translatable = () => !this.isBackground;
28057
28113
  return cfg;
28058
28114
  }
28115
+ mutationOrigin() {
28116
+ return this.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin;
28117
+ }
28059
28118
  liveCursorPoint() {
28060
28119
  return null;
28061
28120
  }
@@ -28138,6 +28197,7 @@ var RenderableModel = class extends import_eventemitter3.default {
28138
28197
  flushRenderables() {
28139
28198
  this.emit("elementClear");
28140
28199
  const elements = Array.from(this.elements.values()).map((v) => this.convertToModel(v)).filter((v) => !!v);
28200
+ elements.sort((a2, b2) => this.compareElements(a2, b2));
28141
28201
  this.maxIndex = elements.reduce((r, n) => Math.max(r, n.index), -1);
28142
28202
  const clearIds = [];
28143
28203
  elements.forEach((model) => {
@@ -28199,7 +28259,32 @@ var RenderableModel = class extends import_eventemitter3.default {
28199
28259
  initElement(element) {
28200
28260
  element.shadowEmitter = this.shadowEmitter;
28201
28261
  }
28262
+ compareElementRole(roleA, roleB) {
28263
+ const a2 = roleA === "background" ? 0 : 1;
28264
+ const b2 = roleB === "background" ? 0 : 1;
28265
+ return a2 - b2;
28266
+ }
28267
+ compareElements(a2, b2) {
28268
+ const roleCompare = this.compareElementRole(a2.role, b2.role);
28269
+ if (roleCompare !== 0) {
28270
+ return roleCompare;
28271
+ }
28272
+ if (a2.index !== b2.index) {
28273
+ return a2.index - b2.index;
28274
+ }
28275
+ return a2.uuid.localeCompare(b2.uuid);
28276
+ }
28277
+ isBackgroundElement(uuid) {
28278
+ const element = this.elements.get(uuid);
28279
+ if (!element) {
28280
+ return false;
28281
+ }
28282
+ return (element.get("role") ?? "normal") === "background";
28283
+ }
28202
28284
  removeElementItem(uuid) {
28285
+ if (this.isBackgroundElement(uuid)) {
28286
+ return;
28287
+ }
28203
28288
  this.elements.delete(uuid);
28204
28289
  }
28205
28290
  confirmPermission() {
@@ -28210,15 +28295,21 @@ var RenderableModel = class extends import_eventemitter3.default {
28210
28295
  return hasPermission;
28211
28296
  }
28212
28297
  createImage(src) {
28298
+ let fit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "legacy";
28299
+ let role = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "normal";
28213
28300
  if (!this.confirmPermission()) {
28214
28301
  return;
28215
28302
  }
28303
+ if (role === "background") {
28304
+ return this.upsertBackgroundImage(src, fit);
28305
+ }
28216
28306
  const yMap = new Y12.Map();
28217
28307
  this.elements.doc?.transact(() => {
28218
28308
  const uuid = this.uuid;
28219
28309
  yMap.set(ElementModel.KEYS.index, ++this.maxIndex);
28220
28310
  yMap.set(ElementModel.KEYS.uuid, uuid);
28221
28311
  yMap.set("type", "image");
28312
+ yMap.set("role", role);
28222
28313
  yMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28223
28314
  this.elements.set(uuid, yMap);
28224
28315
  }, elementsUndoOrigin);
@@ -28226,9 +28317,47 @@ var RenderableModel = class extends import_eventemitter3.default {
28226
28317
  model.bindObserver();
28227
28318
  model.root.set("src", src);
28228
28319
  model.ownerId = this.userManager.selfId;
28229
- this.fitImageToViewport(src, model);
28320
+ this.fitImageToViewport(src, model, fit);
28321
+ }
28322
+ upsertBackgroundImage(src, fit) {
28323
+ const backgroundEntries = Array.from(this.elements.entries()).filter((_ref) => {
28324
+ let [, elementMap] = _ref;
28325
+ return elementMap.get("type") === "image" && (elementMap.get("role") ?? "normal") === "background";
28326
+ });
28327
+ let targetMap = backgroundEntries[0]?.[1] ?? null;
28328
+ if (this.elements.doc) {
28329
+ this.elements.doc.transact(() => {
28330
+ backgroundEntries.slice(1).forEach((_ref2) => {
28331
+ let [key] = _ref2;
28332
+ return this.elements.delete(key);
28333
+ });
28334
+ if (!targetMap) {
28335
+ targetMap = new Y12.Map();
28336
+ const uuid = this.uuid;
28337
+ targetMap.set(ElementModel.KEYS.index, -1);
28338
+ targetMap.set(ElementModel.KEYS.uuid, uuid);
28339
+ targetMap.set("type", "image");
28340
+ targetMap.set("role", "background");
28341
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28342
+ this.elements.set(uuid, targetMap);
28343
+ } else {
28344
+ targetMap.set("role", "background");
28345
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
28346
+ }
28347
+ }, backgroundElementsUndoOrigin);
28348
+ }
28349
+ if (!targetMap) {
28350
+ return;
28351
+ }
28352
+ const model = this.convertToModel(targetMap);
28353
+ if (!model || !(model instanceof ImageModel)) {
28354
+ return;
28355
+ }
28356
+ model.root.set("src", src);
28357
+ model.ownerId = this.userManager.selfId;
28358
+ this.fitImageToViewport(src, model, fit);
28230
28359
  }
28231
- fitImageToViewport(src, model) {
28360
+ fitImageToViewport(src, model, fit) {
28232
28361
  const center = this.scope.project.view.center;
28233
28362
  const fallbackMatrix = new this.scope.Matrix();
28234
28363
  fallbackMatrix.translate({
@@ -28245,9 +28374,9 @@ var RenderableModel = class extends import_eventemitter3.default {
28245
28374
  return;
28246
28375
  }
28247
28376
  const viewportBounds = this.scope.project.view.bounds;
28248
- const maxWidth = viewportBounds.width * 2 / 3;
28249
- const maxHeight = viewportBounds.height * 2 / 3;
28250
- const fitScale = Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
28377
+ const maxWidth = fit === "contain" ? viewportBounds.width : viewportBounds.width * 2 / 3;
28378
+ const maxHeight = fit === "contain" ? viewportBounds.height : viewportBounds.height * 2 / 3;
28379
+ const fitScale = fit === "contain" ? Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight) : Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
28251
28380
  const nextMatrix = new this.scope.Matrix();
28252
28381
  nextMatrix.translate({
28253
28382
  x: center.x,
@@ -28270,7 +28399,7 @@ var RenderableModel = class extends import_eventemitter3.default {
28270
28399
  if (model.root.doc) {
28271
28400
  model.root.doc.transact(() => {
28272
28401
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
28273
- }, elementsUndoOrigin);
28402
+ }, model.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin);
28274
28403
  } else {
28275
28404
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
28276
28405
  }
@@ -28530,7 +28659,7 @@ var RenderableModel = class extends import_eventemitter3.default {
28530
28659
  if (shadow) {
28531
28660
  yMap.set(ElementModel.KEYS.shadow, "layer");
28532
28661
  }
28533
- yMap.set(ElementModel.KEYS.points, new Y12.Array());
28662
+ yMap.set(ElementModel.KEYS.points, Y12.Array.from([x, y]));
28534
28663
  yMap.set(ElementModel.KEYS.strokeWidth, this.toolbarModel.strokeWidth);
28535
28664
  yMap.set(ElementModel.KEYS.strokeColor, this.toolbarModel.strokeColor);
28536
28665
  yMap.set(ElementModel.KEYS.fillColor, this.toolbarModel.fillColor);
@@ -28538,7 +28667,6 @@ var RenderableModel = class extends import_eventemitter3.default {
28538
28667
  this.elements.set(uuid, yMap);
28539
28668
  }, elementsUndoOrigin);
28540
28669
  pointTextModel.bindObserver();
28541
- pointTextModel.setPoints([x, y]);
28542
28670
  pointTextModel.fontSize = this.toolbarModel.fontSize;
28543
28671
  pointTextModel.fontFamily = this.toolbarModel.fontFamily;
28544
28672
  this.initElement(pointTextModel);
@@ -28572,7 +28700,7 @@ var RenderableModel = class extends import_eventemitter3.default {
28572
28700
  };
28573
28701
 
28574
28702
  // src/utils/Recognizer.ts
28575
- var import_lodash7 = __toESM(require_lodash(), 1);
28703
+ var import_lodash8 = __toESM(require_lodash(), 1);
28576
28704
 
28577
28705
  // src/utils/UnistrokeRecognizer.js
28578
28706
  function Point(x, y) {
@@ -28819,7 +28947,7 @@ var Recognizer = class {
28819
28947
  let maxX = -Number.MAX_VALUE;
28820
28948
  let minY = Number.MAX_VALUE;
28821
28949
  let maxY = -Number.MAX_VALUE;
28822
- const result = this.dollar.Recognize((0, import_lodash7.chunk)(points, 3).map((v) => {
28950
+ const result = this.dollar.Recognize((0, import_lodash8.chunk)(points, 2).map((v) => {
28823
28951
  minX = Math.min(minX, v[0]);
28824
28952
  maxX = Math.max(maxX, v[0]);
28825
28953
  minY = Math.min(minY, v[1]);
@@ -28869,22 +28997,33 @@ var CurveTool = class extends WhiteboardTool {
28869
28997
  _defineProperty19(this, "flushPendingPoints", () => {
28870
28998
  this.flushRafId = 0;
28871
28999
  if (this.elementModel && this.pendingPoints.length > 0) {
28872
- this.elementModel.appendPoints(this.pendingPoints);
29000
+ const appended = this.elementModel.appendPoints(this.pendingPoints);
29001
+ if (!appended) {
29002
+ this.resetStrokeState();
29003
+ return;
29004
+ }
28873
29005
  this.pendingPoints = [];
28874
29006
  }
28875
29007
  });
28876
29008
  }
28877
- onMouseDown(_event) {
29009
+ resetStrokeState() {
28878
29010
  this.pointCount = 0;
28879
29011
  this.pendingPoints = [];
28880
29012
  if (this.flushRafId) {
28881
29013
  cancelAnimationFrame(this.flushRafId);
28882
29014
  this.flushRafId = 0;
28883
29015
  }
28884
- if (this.elementModel) {
28885
- this.elementModel.dispose();
28886
- }
28887
29016
  this.elementModel = null;
29017
+ }
29018
+ cancelCurrentAction() {
29019
+ this.resetStrokeState();
29020
+ }
29021
+ onMouseDown(_event) {
29022
+ const previousElement = this.elementModel;
29023
+ this.resetStrokeState();
29024
+ if (previousElement) {
29025
+ previousElement.dispose();
29026
+ }
28888
29027
  this.modelGetter().then((model) => {
28889
29028
  if (model) {
28890
29029
  this.elementModel = model.createCurve(true);
@@ -28897,24 +29036,26 @@ var CurveTool = class extends WhiteboardTool {
28897
29036
  }
28898
29037
  const MIN_DISTANCE = 2;
28899
29038
  if (this.elementModel) {
29039
+ if (!this.elementModel.isAttached()) {
29040
+ this.resetStrokeState();
29041
+ return;
29042
+ }
28900
29043
  let lastX = 0;
28901
29044
  let lastY = 0;
28902
- if (this.pendingPoints.length >= 3) {
28903
- lastX = this.pendingPoints[this.pendingPoints.length - 3];
28904
- lastY = this.pendingPoints[this.pendingPoints.length - 2];
29045
+ if (this.pendingPoints.length >= 2) {
29046
+ lastX = this.pendingPoints[this.pendingPoints.length - 2];
29047
+ lastY = this.pendingPoints[this.pendingPoints.length - 1];
28905
29048
  } else {
28906
29049
  const len = this.elementModel.points.length;
28907
- if (len >= 3) {
28908
- lastX = this.elementModel.points[len - 3];
28909
- lastY = this.elementModel.points[len - 2];
29050
+ if (len >= 2) {
29051
+ lastX = this.elementModel.points[len - 2];
29052
+ lastY = this.elementModel.points[len - 1];
28910
29053
  }
28911
29054
  }
28912
29055
  const dist = Math.max(Math.abs(lastX - event.point.x), Math.abs(lastY - event.point.y));
28913
29056
  if (dist >= MIN_DISTANCE) {
28914
29057
  this.pointCount += 1;
28915
- const nativeEvent = event.event;
28916
- const pressure = nativeEvent.pointerType === "pen" && nativeEvent.pressure > 0 ? nativeEvent.pressure : 0;
28917
- this.pendingPoints.push(event.point.x, event.point.y, pressure);
29058
+ this.pendingPoints.push(event.point.x, event.point.y);
28918
29059
  if (!this.flushRafId) {
28919
29060
  this.flushRafId = requestAnimationFrame(this.flushPendingPoints);
28920
29061
  }
@@ -28927,22 +29068,27 @@ var CurveTool = class extends WhiteboardTool {
28927
29068
  this.flushRafId = 0;
28928
29069
  }
28929
29070
  this.flushPendingPoints();
29071
+ if (!this.elementModel || !this.elementModel.isAttached()) {
29072
+ this.resetStrokeState();
29073
+ return;
29074
+ }
29075
+ const currentElement = this.elementModel;
29076
+ const currentUuid = currentElement.uuid;
29077
+ const currentPointCount = this.pointCount;
28930
29078
  this.modelGetter().then((model) => {
28931
29079
  if (!model) {
28932
29080
  return;
28933
29081
  }
28934
- if (this.pointCount < 3 && this.elementModel) {
28935
- if (this.elementModel) {
28936
- model.removeElementItem(this.elementModel.uuid);
28937
- }
29082
+ if (currentPointCount < 3) {
29083
+ model.removeElementItem(currentUuid);
28938
29084
  }
28939
- if (this.elementModel) {
28940
- this.elementModel.shadow = "";
29085
+ if (currentElement.isAttached()) {
29086
+ currentElement.shadow = "";
28941
29087
  }
28942
- if (this.elementModel && event.event.metaKey) {
28943
- const result = this.recognizer.recognize(this.elementModel.points);
29088
+ if (currentElement.isAttached() && event.event.metaKey) {
29089
+ const result = this.recognizer.recognize(currentElement.points);
28944
29090
  if (result) {
28945
- model.removeElementItem(this.elementModel.uuid);
29091
+ model.removeElementItem(currentUuid);
28946
29092
  if (/^rectangle/.test(result.shape)) {
28947
29093
  const rect = model.createRectangle(false);
28948
29094
  rect?.setPoints([result.minX, result.minY, result.maxX, result.maxY]);
@@ -28961,6 +29107,9 @@ var CurveTool = class extends WhiteboardTool {
28961
29107
  }
28962
29108
  }
28963
29109
  }
29110
+ if (this.elementModel === currentElement) {
29111
+ this.resetStrokeState();
29112
+ }
28964
29113
  });
28965
29114
  }
28966
29115
  };
@@ -29158,6 +29307,9 @@ var SelectorTool = class extends WhiteboardTool {
29158
29307
  _defineProperty22(this, "showLiveCursor", false);
29159
29308
  this.selectElementsModel = selectElementsModel;
29160
29309
  }
29310
+ isSelectableItem(item) {
29311
+ return !!item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.data.role !== "background";
29312
+ }
29161
29313
  onMouseDown(event) {
29162
29314
  this.from = null;
29163
29315
  this.to = null;
@@ -29183,7 +29335,7 @@ var SelectorTool = class extends WhiteboardTool {
29183
29335
  this.elementModel.setPoints([rect.topLeft.x, rect.topLeft.y, rect.size.width, rect.size.height]);
29184
29336
  this.selectElementsModel.clearSelectElementForSelf();
29185
29337
  this.scope.project.activeLayer.children.forEach((item) => {
29186
- if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0) {
29338
+ if (this.isSelectableItem(item)) {
29187
29339
  if (rect.contains(item.bounds)) {
29188
29340
  this.selectElements.set(item.data.uuid, item.data.ownerId);
29189
29341
  } else {
@@ -29209,7 +29361,7 @@ var SelectorTool = class extends WhiteboardTool {
29209
29361
  return result;
29210
29362
  }, []);
29211
29363
  const hitResult = this.scope.project.hitTest(event.point);
29212
- if (hitResult && hitResult.item.data.uuid) {
29364
+ if (hitResult && hitResult.item.data.uuid && this.isSelectableItem(hitResult.item)) {
29213
29365
  elements.push({
29214
29366
  elementId: hitResult.item.data.uuid,
29215
29367
  ownerId: hitResult.item.data.ownerId
@@ -29753,7 +29905,7 @@ var Editor = class extends import_eventemitter35.default {
29753
29905
  };
29754
29906
  this.rootView.style.opacity = "0";
29755
29907
  }
29756
- if (target === this.frame) {
29908
+ if (target === this.frame && this.editorConfig?.translatable()) {
29757
29909
  evt.preventDefault();
29758
29910
  this.editMode = "translate";
29759
29911
  this.lastEditPoint = {
@@ -30058,7 +30210,6 @@ var Editor = class extends import_eventemitter35.default {
30058
30210
  this.shadowContainer.remove();
30059
30211
  this.shadowScope.project.activeLayer.addChild(this.shadowContainer);
30060
30212
  this.targets.forEach((model) => {
30061
- console.log("[][][] translateShadow model", model.root._dEH);
30062
30213
  model.shadow = this.shadowContainer.data.uuid;
30063
30214
  });
30064
30215
  }
@@ -30939,6 +31090,9 @@ var EraserTool = class extends WhiteboardTool {
30939
31090
  this.elementModel.appendPoints([event.point.x, event.point.y]);
30940
31091
  }
30941
31092
  this.scope.project.activeLayer.children.forEach((item) => {
31093
+ if (item.data.role === "background") {
31094
+ return;
31095
+ }
30942
31096
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
30943
31097
  segments: true,
30944
31098
  stroke: true,
@@ -30949,6 +31103,9 @@ var EraserTool = class extends WhiteboardTool {
30949
31103
  }
30950
31104
  });
30951
31105
  this.shadowScope.project.activeLayer.children.forEach((item) => {
31106
+ if (item.data.role === "background") {
31107
+ return;
31108
+ }
30952
31109
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
30953
31110
  segments: true,
30954
31111
  stroke: true,
@@ -31408,16 +31565,7 @@ var IndexedNavigation = class extends import_eventemitter311.default {
31408
31565
  return this.pageModel.pageList().filter((id) => /^_i_/.test(id));
31409
31566
  }
31410
31567
  get head() {
31411
- const headId = Object.keys(this.list).find((key) => {
31412
- return this.list[key] && this.list[key].prev === "";
31413
- });
31414
- if (!headId) {
31415
- (0, import_forge_room12.log)("indexed navigation confusion", {
31416
- list: JSON.stringify(this.list)
31417
- }, "error");
31418
- throw new Error("indexed navigation confusion");
31419
- }
31420
- return headId;
31568
+ return this.ensureValidList().head;
31421
31569
  }
31422
31570
  get lastNodeId() {
31423
31571
  let currentId = this.head;
@@ -31437,7 +31585,8 @@ var IndexedNavigation = class extends import_eventemitter311.default {
31437
31585
  _defineProperty36(this, "list", {});
31438
31586
  _defineProperty36(this, "hasPermission", void 0);
31439
31587
  _defineProperty36(this, "handleIndexedPageMapUpdate", (_evt) => {
31440
- this.list = this.indexedPageMap.get("list");
31588
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
31589
+ this.ensureValidList("update");
31441
31590
  const needRemoveList = this.pageModel.pageList().filter((v) => /^_i_/.test(v) && Object.keys(this.list).indexOf(v) < 0);
31442
31591
  const needAddList = Object.keys(this.list).filter((v) => this.pageModel.pageList().indexOf(v) < 0);
31443
31592
  this.indexedPageMap.doc.transact(() => {
@@ -31470,11 +31619,156 @@ var IndexedNavigation = class extends import_eventemitter311.default {
31470
31619
  this.indexedPageMap.set("list", this.list);
31471
31620
  this.idPool.add("_i_");
31472
31621
  } else {
31473
- this.list = this.indexedPageMap.get("list");
31622
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
31474
31623
  Object.keys(this.list).forEach((id) => this.idPool.add(id));
31624
+ this.ensureValidList("constructor");
31475
31625
  }
31476
31626
  this.indexedPageMap.observe(this.handleIndexedPageMapUpdate);
31477
31627
  }
31628
+ normalizeList(list) {
31629
+ if (!list || typeof list !== "object" || Array.isArray(list)) {
31630
+ return null;
31631
+ }
31632
+ const nextList = {};
31633
+ for (const [id, node] of Object.entries(list)) {
31634
+ if (!/^_i_/.test(id) || !node || typeof node !== "object") {
31635
+ return null;
31636
+ }
31637
+ const {
31638
+ prev,
31639
+ next
31640
+ } = node;
31641
+ if (typeof prev !== "string" || typeof next !== "string") {
31642
+ return null;
31643
+ }
31644
+ nextList[id] = {
31645
+ prev,
31646
+ next
31647
+ };
31648
+ }
31649
+ return nextList;
31650
+ }
31651
+ validateList(list) {
31652
+ const ids = Object.keys(list);
31653
+ if (ids.length === 0) {
31654
+ return {
31655
+ valid: false,
31656
+ reason: "empty list"
31657
+ };
31658
+ }
31659
+ const heads = ids.filter((id) => list[id].prev === "");
31660
+ if (heads.length !== 1) {
31661
+ return {
31662
+ valid: false,
31663
+ reason: `expected one head, got ${heads.length}`
31664
+ };
31665
+ }
31666
+ for (const id of ids) {
31667
+ const node = list[id];
31668
+ if (node.prev && !list[node.prev]) {
31669
+ return {
31670
+ valid: false,
31671
+ reason: `missing prev node ${node.prev}`
31672
+ };
31673
+ }
31674
+ if (node.next && !list[node.next]) {
31675
+ return {
31676
+ valid: false,
31677
+ reason: `missing next node ${node.next}`
31678
+ };
31679
+ }
31680
+ if (node.prev && list[node.prev].next !== id) {
31681
+ return {
31682
+ valid: false,
31683
+ reason: `prev link mismatch for ${id}`
31684
+ };
31685
+ }
31686
+ if (node.next && list[node.next].prev !== id) {
31687
+ return {
31688
+ valid: false,
31689
+ reason: `next link mismatch for ${id}`
31690
+ };
31691
+ }
31692
+ }
31693
+ const visited = /* @__PURE__ */ new Set();
31694
+ let currentId = heads[0];
31695
+ while (currentId) {
31696
+ if (visited.has(currentId)) {
31697
+ return {
31698
+ valid: false,
31699
+ reason: `cycle at ${currentId}`
31700
+ };
31701
+ }
31702
+ visited.add(currentId);
31703
+ currentId = list[currentId].next;
31704
+ }
31705
+ if (visited.size !== ids.length) {
31706
+ return {
31707
+ valid: false,
31708
+ reason: "detached nodes"
31709
+ };
31710
+ }
31711
+ return {
31712
+ valid: true,
31713
+ head: heads[0]
31714
+ };
31715
+ }
31716
+ createList(ids) {
31717
+ return ids.reduce((list, id, index) => {
31718
+ list[id] = {
31719
+ prev: ids[index - 1] ?? "",
31720
+ next: ids[index + 1] ?? ""
31721
+ };
31722
+ return list;
31723
+ }, {});
31724
+ }
31725
+ repairList(reason) {
31726
+ const pageIds = this.idList;
31727
+ const ids = pageIds.length > 0 ? pageIds : Object.keys(this.list).filter((id) => /^_i_/.test(id));
31728
+ const nextIds = ids.length > 0 ? ids : ["_i_"];
31729
+ const nextList = this.createList(nextIds);
31730
+ (0, import_forge_room12.log)("indexed navigation recovered", {
31731
+ reason,
31732
+ pageList: JSON.stringify(this.pageModel.pageList()),
31733
+ list: JSON.stringify(this.list),
31734
+ nextList: JSON.stringify(nextList)
31735
+ }, "warn");
31736
+ const apply = () => {
31737
+ for (const id of nextIds) {
31738
+ if (this.pageModel.pageList().indexOf(id) < 0) {
31739
+ this.pageModel.addPage(id);
31740
+ }
31741
+ }
31742
+ this.list = nextList;
31743
+ this.indexedPageMap.set("list", nextList);
31744
+ };
31745
+ if (this.indexedPageMap.doc) {
31746
+ this.indexedPageMap.doc.transact(apply);
31747
+ } else {
31748
+ apply();
31749
+ }
31750
+ nextIds.forEach((id) => this.idPool.add(id));
31751
+ return nextIds[0];
31752
+ }
31753
+ ensureValidList() {
31754
+ let context = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "read";
31755
+ const normalized = this.normalizeList(this.list);
31756
+ if (!normalized) {
31757
+ return {
31758
+ head: this.repairList(`${context}: invalid list`)
31759
+ };
31760
+ }
31761
+ this.list = normalized;
31762
+ const validation = this.validateList(this.list);
31763
+ if (!validation.valid) {
31764
+ return {
31765
+ head: this.repairList(`${context}: ${validation.reason}`)
31766
+ };
31767
+ }
31768
+ return {
31769
+ head: validation.head
31770
+ };
31771
+ }
31478
31772
  getNextId() {
31479
31773
  const cache = Array.from(this.idPool).filter((id) => this.idList.indexOf(id) < 0);
31480
31774
  if (cache.length > 0) {
@@ -32424,7 +32718,6 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
32424
32718
  this.emitter.emit("elementDeselected", userId);
32425
32719
  return;
32426
32720
  }
32427
- editor.show();
32428
32721
  const targetLayerId = this.userMap(userId).get(WhiteboardKeys.currentPage);
32429
32722
  const selfLayerId = this.userMap(this.userId).get(WhiteboardKeys.currentPage);
32430
32723
  if (targetLayerId !== selfLayerId || !this.layers.has(targetLayerId)) {
@@ -32432,7 +32725,13 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
32432
32725
  }
32433
32726
  const elementModels = elements.map((id) => {
32434
32727
  return this.layers.get(targetLayerId).elementModels.get(id);
32435
- }).filter((v) => !!v);
32728
+ }).filter((model) => !!model && !model.isBackground);
32729
+ if (elementModels.length === 0) {
32730
+ editor.hidden();
32731
+ this.emitter.emit("elementDeselected", userId);
32732
+ return;
32733
+ }
32734
+ editor.show();
32436
32735
  editor.setTargets(elementModels);
32437
32736
  if (elementModels.length === 1) {
32438
32737
  const model = elementModels[0];
@@ -32624,14 +32923,17 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
32624
32923
  this.camera.resetViewMatrixToMain();
32625
32924
  }
32626
32925
  };
32627
- this.emitter.insertImage = (src, pageId) => {
32926
+ this.emitter.insertImage = (src, options) => {
32628
32927
  if (!/https/.test(src)) {
32629
32928
  (0, import_forge_room14.log)("[@netless/forge-whiteboard] invalid image url, src needs to be in the HTTPS protocol.", {
32630
32929
  src
32631
32930
  }, "warn");
32632
32931
  return;
32633
32932
  }
32634
- let targetPageId = pageId;
32933
+ const normalizedOptions = typeof options === "string" ? {
32934
+ pageId: options
32935
+ } : options ?? {};
32936
+ let targetPageId = normalizedOptions.pageId;
32635
32937
  if (!targetPageId) {
32636
32938
  targetPageId = this.pageModel.getCurrentPage(this.userManager.selfId);
32637
32939
  }
@@ -32639,13 +32941,16 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
32639
32941
  (0, import_forge_room14.log)("[@netless/forge-whiteboard] page not found", {}, "warn");
32640
32942
  return;
32641
32943
  }
32642
- this.layers.get(targetPageId)?.createImage(src);
32944
+ this.layers.get(targetPageId)?.createImage(src, normalizedOptions.fit ?? "legacy", normalizedOptions.role ?? "normal");
32643
32945
  };
32644
32946
  this.emitter.removeElement = (pageId, elementId) => {
32645
32947
  if (!this.layers.has(pageId)) {
32646
32948
  (0, import_forge_room14.log)("[@netless/forge-whiteboard] page not found", {}, "warn");
32647
32949
  return;
32648
32950
  }
32951
+ if (this.layers.get(pageId)?.isBackgroundElement(elementId)) {
32952
+ return;
32953
+ }
32649
32954
  this.layers.get(pageId)?.removeElementItem(elementId);
32650
32955
  };
32651
32956
  this.emitter.getViewModel = (userId) => {
@@ -32696,7 +33001,11 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
32696
33001
  if (model) {
32697
33002
  if (model.elements.doc) {
32698
33003
  model.elements.doc.transact(() => {
32699
- model.elements.clear();
33004
+ for (const key of Array.from(model.elements.keys())) {
33005
+ if (!model.isBackgroundElement(key)) {
33006
+ model.elements.delete(key);
33007
+ }
33008
+ }
32700
33009
  }, elementsUndoOrigin);
32701
33010
  } else {
32702
33011
  model.elementModels.clear();
@@ -33108,7 +33417,9 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
33108
33417
  const at = this.findElementIndex(element.data.index, parent);
33109
33418
  for (let i = at; i >= 0; i--) {
33110
33419
  const child = children[i - 1];
33111
- if (!child || child.data.index < element.data.index || child.data.uuid < element.data.uuid) {
33420
+ const childRole = child?.data.role === "background" ? 0 : 1;
33421
+ const elementRole = element.data.role === "background" ? 0 : 1;
33422
+ 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) {
33112
33423
  parent.insertChild(i, element);
33113
33424
  break;
33114
33425
  }