@netless/forge-slide 1.2.0-beta.3 → 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.
package/dist/slide.js CHANGED
@@ -61112,6 +61112,8 @@ function me(e, t = {}) {
61112
61112
  function ae(e, t = {}) {
61113
61113
  return ce(me(e, t), t);
61114
61114
  }
61115
+ var elementsUndoOrigin = "elementsUndoOrigin";
61116
+ var backgroundElementsUndoOrigin = "backgroundElementsUndoOrigin";
61115
61117
  function _defineProperty(e, r, t) {
61116
61118
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
61117
61119
  }
@@ -61141,6 +61143,12 @@ var ElementModel = class _ElementModel {
61141
61143
  get type() {
61142
61144
  return this.root.get("type");
61143
61145
  }
61146
+ get role() {
61147
+ return this.root.get("role") ?? "normal";
61148
+ }
61149
+ get isBackground() {
61150
+ return this.role === "background";
61151
+ }
61144
61152
  get strokeWidth() {
61145
61153
  return this.root.get(_ElementModel.KEYS.strokeWidth);
61146
61154
  }
@@ -61199,6 +61207,9 @@ var ElementModel = class _ElementModel {
61199
61207
  get isPerformanceEnvironment() {
61200
61208
  return this.isPerformanceMode() && this.shouldUseLocalPoints;
61201
61209
  }
61210
+ isAttached() {
61211
+ return this.root.has(_ElementModel.KEYS.uuid);
61212
+ }
61202
61213
  constructor(root, scope, liveCursor, isPerformanceMode) {
61203
61214
  _defineProperty(this, "shadowEmitter", null);
61204
61215
  _defineProperty(this, "root", void 0);
@@ -61284,16 +61295,12 @@ var ElementModel = class _ElementModel {
61284
61295
  }
61285
61296
  }
61286
61297
  bindObserver() {
61287
- const beforeL = this.root._eH?.l?.length ?? -1;
61288
- const beforeDL = this.root._dEH?.l?.length ?? -1;
61289
61298
  (0, import_forge_room3.removeDeepObserver)(this.root, this.handlePropChange);
61290
61299
  this.subBindObserver();
61291
- const afterRemoveL = this.root._eH?.l?.length ?? -1;
61292
- const afterRemoveDL = this.root._dEH?.l?.length ?? -1;
61293
61300
  this.root.observeDeep(this.handlePropChange);
61294
- const afterAddL = this.root._eH?.l?.length ?? -1;
61295
- const afterAddDL = this.root._dEH?.l?.length ?? -1;
61296
- console.log(`[][][] bindObserver uuid=${this.uuid} doc=${!!this.root.doc} _eH: ${beforeL}->${afterRemoveL}->${afterAddL} _dEH: ${beforeDL}->${afterRemoveDL}->${afterAddDL}`);
61301
+ }
61302
+ mutationOrigin() {
61303
+ return elementsUndoOrigin;
61297
61304
  }
61298
61305
  subBindObserver() {
61299
61306
  }
@@ -61332,62 +61339,81 @@ var ElementModel = class _ElementModel {
61332
61339
  this.item.data.uuid = this.uuid;
61333
61340
  this.item.data.index = this.index;
61334
61341
  this.item.data.type = this.root.get("type");
61342
+ this.item.data.role = this.role;
61335
61343
  this.item.data.ownerId = this.ownerId;
61336
61344
  this.item.applyMatrix = false;
61337
61345
  }
61338
61346
  }
61339
61347
  appendPoints(points) {
61340
61348
  if (this.isPerformanceEnvironment) {
61341
- this.appendPointsPerformance(points);
61349
+ return this.appendPointsPerformance(points);
61342
61350
  } else {
61343
- this.appendPointsDirect(points);
61351
+ return this.appendPointsDirect(points);
61344
61352
  }
61345
61353
  }
61354
+ getPointsArray() {
61355
+ const yArray = this.root.get(_ElementModel.KEYS.points);
61356
+ return yArray instanceof Y.Array ? yArray : null;
61357
+ }
61346
61358
  appendPointsDirect(points) {
61347
- this.root.get(_ElementModel.KEYS.points).push(points);
61359
+ const yArray = this.getPointsArray();
61360
+ if (!yArray) {
61361
+ return false;
61362
+ }
61363
+ yArray.push(points);
61364
+ return true;
61348
61365
  }
61349
61366
  appendPointsPerformance(points) {
61367
+ const yArray = this.getPointsArray();
61368
+ if (!yArray) {
61369
+ return false;
61370
+ }
61350
61371
  this.localPoints = this.localPoints.concat(points);
61351
61372
  this.onVectorUpdate();
61352
61373
  if (this.appendPointsTimer) {
61353
61374
  window.clearTimeout(this.appendPointsTimer);
61354
61375
  }
61355
61376
  if (this.localPoints.length % 80 === 0) {
61356
- const yArray = this.root.get(_ElementModel.KEYS.points);
61357
61377
  yArray?.push(this.localPoints.slice(yArray.length));
61358
61378
  }
61359
61379
  this.appendPointsTimer = window.setTimeout(() => {
61360
61380
  this.appendPointsTimer = null;
61361
61381
  if (this.localPoints.length > 0) {
61362
- const yArray = this.root.get(_ElementModel.KEYS.points);
61363
- yArray?.push(this.localPoints.slice(yArray.length));
61382
+ const nextArray = this.getPointsArray();
61383
+ nextArray?.push(this.localPoints.slice(nextArray.length));
61364
61384
  }
61365
61385
  }, 100);
61386
+ return true;
61366
61387
  }
61367
61388
  setPoints(points) {
61368
61389
  if (this.isPerformanceEnvironment) {
61369
- this.setPointsPerformance(points);
61390
+ return this.setPointsPerformance(points);
61370
61391
  } else {
61371
- this.setPointsDirect(points);
61392
+ return this.setPointsDirect(points);
61372
61393
  }
61373
61394
  }
61374
61395
  setPointsDirect(points) {
61375
61396
  if (this.root.doc) {
61397
+ const yArray = this.getPointsArray();
61398
+ if (!yArray) {
61399
+ return false;
61400
+ }
61376
61401
  this.root.doc.transact(() => {
61377
- const yArray = this.root.get(_ElementModel.KEYS.points);
61378
- if (yArray) {
61379
- yArray.delete(0, yArray.length);
61380
- yArray.push(points);
61381
- }
61382
- });
61402
+ yArray.delete(0, yArray.length);
61403
+ yArray.push(points);
61404
+ }, this.mutationOrigin());
61383
61405
  } else {
61384
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
61406
+ const yArray = this.getPointsArray() || new Y.Array();
61385
61407
  yArray.delete(0, yArray.length);
61386
61408
  yArray.push(points);
61387
61409
  this.root.set(_ElementModel.KEYS.points, yArray);
61388
61410
  }
61411
+ return true;
61389
61412
  }
61390
61413
  setPointsPerformance(points) {
61414
+ if (this.root.doc && !this.getPointsArray()) {
61415
+ return false;
61416
+ }
61391
61417
  this.localPoints = points;
61392
61418
  this.onVectorUpdate();
61393
61419
  if (this.setPointsTimer) {
@@ -61395,25 +61421,33 @@ var ElementModel = class _ElementModel {
61395
61421
  }
61396
61422
  this.setPointsTimer = window.setTimeout(() => {
61397
61423
  if (this.root.doc) {
61424
+ const yArray = this.getPointsArray();
61425
+ if (!yArray) {
61426
+ return;
61427
+ }
61398
61428
  this.root.doc.transact(() => {
61399
- const yArray = this.root.get(_ElementModel.KEYS.points);
61400
- if (yArray) {
61401
- yArray.delete(0, yArray.length);
61402
- yArray.push(points);
61403
- }
61404
- });
61429
+ yArray.delete(0, yArray.length);
61430
+ yArray.push(points);
61431
+ }, this.mutationOrigin());
61405
61432
  } else {
61406
- const yArray = this.root.get(_ElementModel.KEYS.points) || new Y.Array();
61433
+ const yArray = this.getPointsArray() || new Y.Array();
61407
61434
  yArray.delete(0, yArray.length);
61408
61435
  yArray.push(points);
61409
61436
  this.root.set(_ElementModel.KEYS.points, yArray);
61410
61437
  }
61411
61438
  }, 100);
61439
+ return true;
61412
61440
  }
61413
61441
  appendPointsMatrix(matrix) {
61414
61442
  const current = new this.scope.Matrix(this.pointsMatrix);
61415
61443
  const next = matrix.appended(current);
61416
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61444
+ if (this.root.doc) {
61445
+ this.root.doc.transact(() => {
61446
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61447
+ }, this.mutationOrigin());
61448
+ } else {
61449
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61450
+ }
61417
61451
  }
61418
61452
  rotateByOffset(angle, centerX, centerY) {
61419
61453
  const current = new this.scope.Matrix(this.pointsMatrix);
@@ -61427,7 +61461,13 @@ var ElementModel = class _ElementModel {
61427
61461
  matrix2.rotate(delta, centerX, centerY);
61428
61462
  next = matrix2.appended(next);
61429
61463
  }
61430
- this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61464
+ if (this.root.doc) {
61465
+ this.root.doc.transact(() => {
61466
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61467
+ }, this.mutationOrigin());
61468
+ } else {
61469
+ this.root.set(_ElementModel.KEYS.pointsMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
61470
+ }
61431
61471
  return next.rotation;
61432
61472
  }
61433
61473
  getStyleKeys() {
@@ -61438,7 +61478,6 @@ var ElementModel = class _ElementModel {
61438
61478
  this.subDispose();
61439
61479
  }
61440
61480
  disposeObserver() {
61441
- console.log(`[][][] disposeObserver uuid=${this.uuid}`);
61442
61481
  (0, import_forge_room3.removeDeepObserver)(this.root, this.handlePropChange);
61443
61482
  }
61444
61483
  };
@@ -61477,6 +61516,7 @@ var EditorConfig = class _EditorConfig {
61477
61516
  constructor() {
61478
61517
  _defineProperty2(this, "resizeModel", () => "eight");
61479
61518
  _defineProperty2(this, "uniformScale", () => false);
61519
+ _defineProperty2(this, "translatable", () => true);
61480
61520
  _defineProperty2(this, "controlPoints", []);
61481
61521
  }
61482
61522
  merge(other) {
@@ -61485,6 +61525,7 @@ var EditorConfig = class _EditorConfig {
61485
61525
  const j = RESIZE_MODEL_LEVEL.findIndex((v) => v === other.resizeModel());
61486
61526
  next.resizeModel = () => RESIZE_MODEL_LEVEL[Math.max(i, j)];
61487
61527
  next.uniformScale = this.uniformScale || other.uniformScale;
61528
+ next.translatable = () => this.translatable() && other.translatable();
61488
61529
  next.controlPoints = [];
61489
61530
  return next;
61490
61531
  }
@@ -61935,7 +61976,14 @@ var WhiteboardTool = class {
61935
61976
  }
61936
61977
  this.pendingDragEvent = null;
61937
61978
  this.shadowEmitter.setActive(true);
61938
- this.onMouseDown(event);
61979
+ try {
61980
+ this.onMouseDown(event);
61981
+ } catch (error) {
61982
+ this.cancelCurrentAction();
61983
+ this.eventAvailable = false;
61984
+ this.shadowEmitter.setActive(false);
61985
+ throw error;
61986
+ }
61939
61987
  });
61940
61988
  _defineProperty7(this, "flushPendingDrag", () => {
61941
61989
  this.dragRafId = 0;
@@ -61943,7 +61991,14 @@ var WhiteboardTool = class {
61943
61991
  this.lastDragTime = performance.now();
61944
61992
  const event = this.pendingDragEvent;
61945
61993
  this.pendingDragEvent = null;
61946
- this.onMouseDrag(event);
61994
+ try {
61995
+ this.onMouseDrag(event);
61996
+ } catch (error) {
61997
+ this.cancelCurrentAction();
61998
+ this.eventAvailable = false;
61999
+ this.shadowEmitter.setActive(false);
62000
+ throw error;
62001
+ }
61947
62002
  }
61948
62003
  });
61949
62004
  _defineProperty7(this, "onMouseDragSelf", (event) => {
@@ -61954,7 +62009,14 @@ var WhiteboardTool = class {
61954
62009
  if (now - this.lastDragTime >= DRAG_FRAME_MS) {
61955
62010
  this.lastDragTime = now;
61956
62011
  this.pendingDragEvent = null;
61957
- this.onMouseDrag(event);
62012
+ try {
62013
+ this.onMouseDrag(event);
62014
+ } catch (error) {
62015
+ this.cancelCurrentAction();
62016
+ this.eventAvailable = false;
62017
+ this.shadowEmitter.setActive(false);
62018
+ throw error;
62019
+ }
61958
62020
  } else {
61959
62021
  this.pendingDragEvent = event;
61960
62022
  if (!this.dragRafId) {
@@ -61971,11 +62033,26 @@ var WhiteboardTool = class {
61971
62033
  this.dragRafId = 0;
61972
62034
  }
61973
62035
  if (this.pendingDragEvent) {
61974
- this.onMouseDrag(this.pendingDragEvent);
61975
- this.pendingDragEvent = null;
62036
+ try {
62037
+ this.onMouseDrag(this.pendingDragEvent);
62038
+ this.pendingDragEvent = null;
62039
+ } catch (error) {
62040
+ this.pendingDragEvent = null;
62041
+ this.cancelCurrentAction();
62042
+ this.eventAvailable = false;
62043
+ this.shadowEmitter.setActive(false);
62044
+ throw error;
62045
+ }
62046
+ }
62047
+ try {
62048
+ this.onMouseUp(event);
62049
+ } catch (error) {
62050
+ this.cancelCurrentAction();
62051
+ this.eventAvailable = false;
62052
+ throw error;
62053
+ } finally {
62054
+ this.shadowEmitter.setActive(false);
61976
62055
  }
61977
- this.onMouseUp(event);
61978
- this.shadowEmitter.setActive(false);
61979
62056
  });
61980
62057
  this.modelGetter = modelGetter;
61981
62058
  this.enableToolEvent = enableToolEvent;
@@ -61986,6 +62063,8 @@ var WhiteboardTool = class {
61986
62063
  this.tool.onMouseDrag = this.onMouseDragSelf;
61987
62064
  this.tool.onMouseUp = this.onMouseUpSelf;
61988
62065
  }
62066
+ cancelCurrentAction() {
62067
+ }
61989
62068
  };
61990
62069
  var import_lodash3 = __toESM2(require_lodash(), 1);
61991
62070
  function _defineProperty8(e, r, t) {
@@ -62358,7 +62437,6 @@ var PointTextModel = class extends ElementModel {
62358
62437
  if (!this.item) {
62359
62438
  return null;
62360
62439
  }
62361
- console.log("[][][] drawPoints", this.drawPoints);
62362
62440
  const bounds = this.item.internalBounds;
62363
62441
  const matrix = new this.scope.Matrix(this.pointsMatrix);
62364
62442
  const topLeft = new this.scope.Point(this.drawPoints[0], this.drawPoints[1]).transform(matrix);
@@ -62722,7 +62800,6 @@ var RectangleModel = class extends ElementModel {
62722
62800
  onStyleKeyUpdate(_key) {
62723
62801
  }
62724
62802
  };
62725
- var elementsUndoOrigin = "elementsUndoOrigin";
62726
62803
  var import_lodash5 = __toESM2(require_lodash(), 1);
62727
62804
  function _defineProperty13(e, r, t) {
62728
62805
  return (r = _toPropertyKey13(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e;
@@ -62997,7 +63074,6 @@ var LaserPointerModel = class extends ElementModel {
62997
63074
  matrixedPoints() {
62998
63075
  const matrix = new this.scope.Matrix(this.pointsMatrix);
62999
63076
  const points = this.cachedPoints || this.points;
63000
- console.log("[][][] ,", this.points.length, this.cachedPoints?.length, this.localPoints.length);
63001
63077
  const groupPoints = (0, import_lodash6.chunk)(points, 2).slice(this.sliceBegin);
63002
63078
  return groupPoints.map((_ref) => {
63003
63079
  let [x, y] = _ref;
@@ -63259,6 +63335,7 @@ var ImageModel = class extends ElementModel {
63259
63335
  this.item = new this.scope.Raster(this.uuid);
63260
63336
  const matrix = new this.scope.Matrix(this.pointsMatrix);
63261
63337
  this.item.matrix = matrix;
63338
+ this.item.data.role = this.role;
63262
63339
  }
63263
63340
  onVectorUpdate() {
63264
63341
  const matrix = new this.scope.Matrix(this.pointsMatrix);
@@ -63287,8 +63364,12 @@ var ImageModel = class extends ElementModel {
63287
63364
  const cfg = new EditorConfig();
63288
63365
  cfg.resizeModel = () => "four-corner";
63289
63366
  cfg.uniformScale = () => true;
63367
+ cfg.translatable = () => !this.isBackground;
63290
63368
  return cfg;
63291
63369
  }
63370
+ mutationOrigin() {
63371
+ return this.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin;
63372
+ }
63292
63373
  liveCursorPoint() {
63293
63374
  return null;
63294
63375
  }
@@ -63369,6 +63450,7 @@ var RenderableModel = class extends import_eventemitter3.default {
63369
63450
  flushRenderables() {
63370
63451
  this.emit("elementClear");
63371
63452
  const elements = Array.from(this.elements.values()).map((v) => this.convertToModel(v)).filter((v) => !!v);
63453
+ elements.sort((a2, b2) => this.compareElements(a2, b2));
63372
63454
  this.maxIndex = elements.reduce((r, n) => Math.max(r, n.index), -1);
63373
63455
  const clearIds = [];
63374
63456
  elements.forEach((model) => {
@@ -63430,7 +63512,32 @@ var RenderableModel = class extends import_eventemitter3.default {
63430
63512
  initElement(element) {
63431
63513
  element.shadowEmitter = this.shadowEmitter;
63432
63514
  }
63515
+ compareElementRole(roleA, roleB) {
63516
+ const a2 = roleA === "background" ? 0 : 1;
63517
+ const b2 = roleB === "background" ? 0 : 1;
63518
+ return a2 - b2;
63519
+ }
63520
+ compareElements(a2, b2) {
63521
+ const roleCompare = this.compareElementRole(a2.role, b2.role);
63522
+ if (roleCompare !== 0) {
63523
+ return roleCompare;
63524
+ }
63525
+ if (a2.index !== b2.index) {
63526
+ return a2.index - b2.index;
63527
+ }
63528
+ return a2.uuid.localeCompare(b2.uuid);
63529
+ }
63530
+ isBackgroundElement(uuid) {
63531
+ const element = this.elements.get(uuid);
63532
+ if (!element) {
63533
+ return false;
63534
+ }
63535
+ return (element.get("role") ?? "normal") === "background";
63536
+ }
63433
63537
  removeElementItem(uuid) {
63538
+ if (this.isBackgroundElement(uuid)) {
63539
+ return;
63540
+ }
63434
63541
  this.elements.delete(uuid);
63435
63542
  }
63436
63543
  confirmPermission() {
@@ -63441,15 +63548,21 @@ var RenderableModel = class extends import_eventemitter3.default {
63441
63548
  return hasPermission;
63442
63549
  }
63443
63550
  createImage(src) {
63551
+ let fit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "legacy";
63552
+ let role = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "normal";
63444
63553
  if (!this.confirmPermission()) {
63445
63554
  return;
63446
63555
  }
63556
+ if (role === "background") {
63557
+ return this.upsertBackgroundImage(src, fit);
63558
+ }
63447
63559
  const yMap = new Y12.Map();
63448
63560
  this.elements.doc?.transact(() => {
63449
63561
  const uuid = this.uuid;
63450
63562
  yMap.set(ElementModel.KEYS.index, ++this.maxIndex);
63451
63563
  yMap.set(ElementModel.KEYS.uuid, uuid);
63452
63564
  yMap.set("type", "image");
63565
+ yMap.set("role", role);
63453
63566
  yMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
63454
63567
  this.elements.set(uuid, yMap);
63455
63568
  }, elementsUndoOrigin);
@@ -63457,9 +63570,47 @@ var RenderableModel = class extends import_eventemitter3.default {
63457
63570
  model.bindObserver();
63458
63571
  model.root.set("src", src);
63459
63572
  model.ownerId = this.userManager.selfId;
63460
- this.fitImageToViewport(src, model);
63573
+ this.fitImageToViewport(src, model, fit);
63461
63574
  }
63462
- fitImageToViewport(src, model) {
63575
+ upsertBackgroundImage(src, fit) {
63576
+ const backgroundEntries = Array.from(this.elements.entries()).filter((_ref) => {
63577
+ let [, elementMap] = _ref;
63578
+ return elementMap.get("type") === "image" && (elementMap.get("role") ?? "normal") === "background";
63579
+ });
63580
+ let targetMap = backgroundEntries[0]?.[1] ?? null;
63581
+ if (this.elements.doc) {
63582
+ this.elements.doc.transact(() => {
63583
+ backgroundEntries.slice(1).forEach((_ref2) => {
63584
+ let [key] = _ref2;
63585
+ return this.elements.delete(key);
63586
+ });
63587
+ if (!targetMap) {
63588
+ targetMap = new Y12.Map();
63589
+ const uuid = this.uuid;
63590
+ targetMap.set(ElementModel.KEYS.index, -1);
63591
+ targetMap.set(ElementModel.KEYS.uuid, uuid);
63592
+ targetMap.set("type", "image");
63593
+ targetMap.set("role", "background");
63594
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
63595
+ this.elements.set(uuid, targetMap);
63596
+ } else {
63597
+ targetMap.set("role", "background");
63598
+ targetMap.set(ElementModel.KEYS.ownerId, this.userManager.selfId);
63599
+ }
63600
+ }, backgroundElementsUndoOrigin);
63601
+ }
63602
+ if (!targetMap) {
63603
+ return;
63604
+ }
63605
+ const model = this.convertToModel(targetMap);
63606
+ if (!model || !(model instanceof ImageModel)) {
63607
+ return;
63608
+ }
63609
+ model.root.set("src", src);
63610
+ model.ownerId = this.userManager.selfId;
63611
+ this.fitImageToViewport(src, model, fit);
63612
+ }
63613
+ fitImageToViewport(src, model, fit) {
63463
63614
  const center = this.scope.project.view.center;
63464
63615
  const fallbackMatrix = new this.scope.Matrix();
63465
63616
  fallbackMatrix.translate({
@@ -63476,9 +63627,9 @@ var RenderableModel = class extends import_eventemitter3.default {
63476
63627
  return;
63477
63628
  }
63478
63629
  const viewportBounds = this.scope.project.view.bounds;
63479
- const maxWidth = viewportBounds.width * 2 / 3;
63480
- const maxHeight = viewportBounds.height * 2 / 3;
63481
- const fitScale = Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
63630
+ const maxWidth = fit === "contain" ? viewportBounds.width : viewportBounds.width * 2 / 3;
63631
+ const maxHeight = fit === "contain" ? viewportBounds.height : viewportBounds.height * 2 / 3;
63632
+ const fitScale = fit === "contain" ? Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight) : Math.min(maxWidth / naturalWidth, maxHeight / naturalHeight, 1);
63482
63633
  const nextMatrix = new this.scope.Matrix();
63483
63634
  nextMatrix.translate({
63484
63635
  x: center.x,
@@ -63501,7 +63652,7 @@ var RenderableModel = class extends import_eventemitter3.default {
63501
63652
  if (model.root.doc) {
63502
63653
  model.root.doc.transact(() => {
63503
63654
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
63504
- }, elementsUndoOrigin);
63655
+ }, model.isBackground ? backgroundElementsUndoOrigin : elementsUndoOrigin);
63505
63656
  } else {
63506
63657
  model.root.set(ElementModel.KEYS.pointsMatrix, values);
63507
63658
  }
@@ -64091,22 +64242,33 @@ var CurveTool = class extends WhiteboardTool {
64091
64242
  _defineProperty19(this, "flushPendingPoints", () => {
64092
64243
  this.flushRafId = 0;
64093
64244
  if (this.elementModel && this.pendingPoints.length > 0) {
64094
- this.elementModel.appendPoints(this.pendingPoints);
64245
+ const appended = this.elementModel.appendPoints(this.pendingPoints);
64246
+ if (!appended) {
64247
+ this.resetStrokeState();
64248
+ return;
64249
+ }
64095
64250
  this.pendingPoints = [];
64096
64251
  }
64097
64252
  });
64098
64253
  }
64099
- onMouseDown(_event) {
64254
+ resetStrokeState() {
64100
64255
  this.pointCount = 0;
64101
64256
  this.pendingPoints = [];
64102
64257
  if (this.flushRafId) {
64103
64258
  cancelAnimationFrame(this.flushRafId);
64104
64259
  this.flushRafId = 0;
64105
64260
  }
64106
- if (this.elementModel) {
64107
- this.elementModel.dispose();
64108
- }
64109
64261
  this.elementModel = null;
64262
+ }
64263
+ cancelCurrentAction() {
64264
+ this.resetStrokeState();
64265
+ }
64266
+ onMouseDown(_event) {
64267
+ const previousElement = this.elementModel;
64268
+ this.resetStrokeState();
64269
+ if (previousElement) {
64270
+ previousElement.dispose();
64271
+ }
64110
64272
  this.modelGetter().then((model) => {
64111
64273
  if (model) {
64112
64274
  this.elementModel = model.createCurve(true);
@@ -64119,6 +64281,10 @@ var CurveTool = class extends WhiteboardTool {
64119
64281
  }
64120
64282
  const MIN_DISTANCE = 2;
64121
64283
  if (this.elementModel) {
64284
+ if (!this.elementModel.isAttached()) {
64285
+ this.resetStrokeState();
64286
+ return;
64287
+ }
64122
64288
  let lastX = 0;
64123
64289
  let lastY = 0;
64124
64290
  if (this.pendingPoints.length >= 2) {
@@ -64147,22 +64313,27 @@ var CurveTool = class extends WhiteboardTool {
64147
64313
  this.flushRafId = 0;
64148
64314
  }
64149
64315
  this.flushPendingPoints();
64316
+ if (!this.elementModel || !this.elementModel.isAttached()) {
64317
+ this.resetStrokeState();
64318
+ return;
64319
+ }
64320
+ const currentElement = this.elementModel;
64321
+ const currentUuid = currentElement.uuid;
64322
+ const currentPointCount = this.pointCount;
64150
64323
  this.modelGetter().then((model) => {
64151
64324
  if (!model) {
64152
64325
  return;
64153
64326
  }
64154
- if (this.pointCount < 3 && this.elementModel) {
64155
- if (this.elementModel) {
64156
- model.removeElementItem(this.elementModel.uuid);
64157
- }
64327
+ if (currentPointCount < 3) {
64328
+ model.removeElementItem(currentUuid);
64158
64329
  }
64159
- if (this.elementModel) {
64160
- this.elementModel.shadow = "";
64330
+ if (currentElement.isAttached()) {
64331
+ currentElement.shadow = "";
64161
64332
  }
64162
- if (this.elementModel && event.event.metaKey) {
64163
- const result = this.recognizer.recognize(this.elementModel.points);
64333
+ if (currentElement.isAttached() && event.event.metaKey) {
64334
+ const result = this.recognizer.recognize(currentElement.points);
64164
64335
  if (result) {
64165
- model.removeElementItem(this.elementModel.uuid);
64336
+ model.removeElementItem(currentUuid);
64166
64337
  if (/^rectangle/.test(result.shape)) {
64167
64338
  const rect = model.createRectangle(false);
64168
64339
  rect?.setPoints([result.minX, result.minY, result.maxX, result.maxY]);
@@ -64181,6 +64352,9 @@ var CurveTool = class extends WhiteboardTool {
64181
64352
  }
64182
64353
  }
64183
64354
  }
64355
+ if (this.elementModel === currentElement) {
64356
+ this.resetStrokeState();
64357
+ }
64184
64358
  });
64185
64359
  }
64186
64360
  };
@@ -64370,6 +64544,9 @@ var SelectorTool = class extends WhiteboardTool {
64370
64544
  _defineProperty22(this, "showLiveCursor", false);
64371
64545
  this.selectElementsModel = selectElementsModel;
64372
64546
  }
64547
+ isSelectableItem(item) {
64548
+ return !!item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.data.role !== "background";
64549
+ }
64373
64550
  onMouseDown(event) {
64374
64551
  this.from = null;
64375
64552
  this.to = null;
@@ -64395,7 +64572,7 @@ var SelectorTool = class extends WhiteboardTool {
64395
64572
  this.elementModel.setPoints([rect.topLeft.x, rect.topLeft.y, rect.size.width, rect.size.height]);
64396
64573
  this.selectElementsModel.clearSelectElementForSelf();
64397
64574
  this.scope.project.activeLayer.children.forEach((item) => {
64398
- if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0) {
64575
+ if (this.isSelectableItem(item)) {
64399
64576
  if (rect.contains(item.bounds)) {
64400
64577
  this.selectElements.set(item.data.uuid, item.data.ownerId);
64401
64578
  } else {
@@ -64421,7 +64598,7 @@ var SelectorTool = class extends WhiteboardTool {
64421
64598
  return result;
64422
64599
  }, []);
64423
64600
  const hitResult = this.scope.project.hitTest(event.point);
64424
- if (hitResult && hitResult.item.data.uuid) {
64601
+ if (hitResult && hitResult.item.data.uuid && this.isSelectableItem(hitResult.item)) {
64425
64602
  elements.push({
64426
64603
  elementId: hitResult.item.data.uuid,
64427
64604
  ownerId: hitResult.item.data.ownerId
@@ -64948,7 +65125,7 @@ var Editor = class extends import_eventemitter34.default {
64948
65125
  };
64949
65126
  this.rootView.style.opacity = "0";
64950
65127
  }
64951
- if (target === this.frame) {
65128
+ if (target === this.frame && this.editorConfig?.translatable()) {
64952
65129
  evt.preventDefault();
64953
65130
  this.editMode = "translate";
64954
65131
  this.lastEditPoint = {
@@ -65253,7 +65430,6 @@ var Editor = class extends import_eventemitter34.default {
65253
65430
  this.shadowContainer.remove();
65254
65431
  this.shadowScope.project.activeLayer.addChild(this.shadowContainer);
65255
65432
  this.targets.forEach((model) => {
65256
- console.log("[][][] translateShadow model", model.root._dEH);
65257
65433
  model.shadow = this.shadowContainer.data.uuid;
65258
65434
  });
65259
65435
  }
@@ -66114,6 +66290,9 @@ var EraserTool = class extends WhiteboardTool {
66114
66290
  this.elementModel.appendPoints([event.point.x, event.point.y]);
66115
66291
  }
66116
66292
  this.scope.project.activeLayer.children.forEach((item) => {
66293
+ if (item.data.role === "background") {
66294
+ return;
66295
+ }
66117
66296
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
66118
66297
  segments: true,
66119
66298
  stroke: true,
@@ -66124,6 +66303,9 @@ var EraserTool = class extends WhiteboardTool {
66124
66303
  }
66125
66304
  });
66126
66305
  this.shadowScope.project.activeLayer.children.forEach((item) => {
66306
+ if (item.data.role === "background") {
66307
+ return;
66308
+ }
66127
66309
  if (item.data.type && ["selector", "eraser", "laser"].indexOf(item.data.type) < 0 && item.hitTest(event.point, {
66128
66310
  segments: true,
66129
66311
  stroke: true,
@@ -66565,16 +66747,7 @@ var IndexedNavigation = class extends import_eventemitter311.default {
66565
66747
  return this.pageModel.pageList().filter((id) => /^_i_/.test(id));
66566
66748
  }
66567
66749
  get head() {
66568
- const headId = Object.keys(this.list).find((key) => {
66569
- return this.list[key] && this.list[key].prev === "";
66570
- });
66571
- if (!headId) {
66572
- (0, import_forge_room13.log)("indexed navigation confusion", {
66573
- list: JSON.stringify(this.list)
66574
- }, "error");
66575
- throw new Error("indexed navigation confusion");
66576
- }
66577
- return headId;
66750
+ return this.ensureValidList().head;
66578
66751
  }
66579
66752
  get lastNodeId() {
66580
66753
  let currentId = this.head;
@@ -66594,7 +66767,8 @@ var IndexedNavigation = class extends import_eventemitter311.default {
66594
66767
  _defineProperty36(this, "list", {});
66595
66768
  _defineProperty36(this, "hasPermission", void 0);
66596
66769
  _defineProperty36(this, "handleIndexedPageMapUpdate", (_evt) => {
66597
- this.list = this.indexedPageMap.get("list");
66770
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
66771
+ this.ensureValidList("update");
66598
66772
  const needRemoveList = this.pageModel.pageList().filter((v) => /^_i_/.test(v) && Object.keys(this.list).indexOf(v) < 0);
66599
66773
  const needAddList = Object.keys(this.list).filter((v) => this.pageModel.pageList().indexOf(v) < 0);
66600
66774
  this.indexedPageMap.doc.transact(() => {
@@ -66627,11 +66801,156 @@ var IndexedNavigation = class extends import_eventemitter311.default {
66627
66801
  this.indexedPageMap.set("list", this.list);
66628
66802
  this.idPool.add("_i_");
66629
66803
  } else {
66630
- this.list = this.indexedPageMap.get("list");
66804
+ this.list = this.normalizeList(this.indexedPageMap.get("list")) ?? {};
66631
66805
  Object.keys(this.list).forEach((id) => this.idPool.add(id));
66806
+ this.ensureValidList("constructor");
66632
66807
  }
66633
66808
  this.indexedPageMap.observe(this.handleIndexedPageMapUpdate);
66634
66809
  }
66810
+ normalizeList(list) {
66811
+ if (!list || typeof list !== "object" || Array.isArray(list)) {
66812
+ return null;
66813
+ }
66814
+ const nextList = {};
66815
+ for (const [id, node] of Object.entries(list)) {
66816
+ if (!/^_i_/.test(id) || !node || typeof node !== "object") {
66817
+ return null;
66818
+ }
66819
+ const {
66820
+ prev,
66821
+ next
66822
+ } = node;
66823
+ if (typeof prev !== "string" || typeof next !== "string") {
66824
+ return null;
66825
+ }
66826
+ nextList[id] = {
66827
+ prev,
66828
+ next
66829
+ };
66830
+ }
66831
+ return nextList;
66832
+ }
66833
+ validateList(list) {
66834
+ const ids = Object.keys(list);
66835
+ if (ids.length === 0) {
66836
+ return {
66837
+ valid: false,
66838
+ reason: "empty list"
66839
+ };
66840
+ }
66841
+ const heads = ids.filter((id) => list[id].prev === "");
66842
+ if (heads.length !== 1) {
66843
+ return {
66844
+ valid: false,
66845
+ reason: `expected one head, got ${heads.length}`
66846
+ };
66847
+ }
66848
+ for (const id of ids) {
66849
+ const node = list[id];
66850
+ if (node.prev && !list[node.prev]) {
66851
+ return {
66852
+ valid: false,
66853
+ reason: `missing prev node ${node.prev}`
66854
+ };
66855
+ }
66856
+ if (node.next && !list[node.next]) {
66857
+ return {
66858
+ valid: false,
66859
+ reason: `missing next node ${node.next}`
66860
+ };
66861
+ }
66862
+ if (node.prev && list[node.prev].next !== id) {
66863
+ return {
66864
+ valid: false,
66865
+ reason: `prev link mismatch for ${id}`
66866
+ };
66867
+ }
66868
+ if (node.next && list[node.next].prev !== id) {
66869
+ return {
66870
+ valid: false,
66871
+ reason: `next link mismatch for ${id}`
66872
+ };
66873
+ }
66874
+ }
66875
+ const visited = /* @__PURE__ */ new Set();
66876
+ let currentId = heads[0];
66877
+ while (currentId) {
66878
+ if (visited.has(currentId)) {
66879
+ return {
66880
+ valid: false,
66881
+ reason: `cycle at ${currentId}`
66882
+ };
66883
+ }
66884
+ visited.add(currentId);
66885
+ currentId = list[currentId].next;
66886
+ }
66887
+ if (visited.size !== ids.length) {
66888
+ return {
66889
+ valid: false,
66890
+ reason: "detached nodes"
66891
+ };
66892
+ }
66893
+ return {
66894
+ valid: true,
66895
+ head: heads[0]
66896
+ };
66897
+ }
66898
+ createList(ids) {
66899
+ return ids.reduce((list, id, index) => {
66900
+ list[id] = {
66901
+ prev: ids[index - 1] ?? "",
66902
+ next: ids[index + 1] ?? ""
66903
+ };
66904
+ return list;
66905
+ }, {});
66906
+ }
66907
+ repairList(reason) {
66908
+ const pageIds = this.idList;
66909
+ const ids = pageIds.length > 0 ? pageIds : Object.keys(this.list).filter((id) => /^_i_/.test(id));
66910
+ const nextIds = ids.length > 0 ? ids : ["_i_"];
66911
+ const nextList = this.createList(nextIds);
66912
+ (0, import_forge_room13.log)("indexed navigation recovered", {
66913
+ reason,
66914
+ pageList: JSON.stringify(this.pageModel.pageList()),
66915
+ list: JSON.stringify(this.list),
66916
+ nextList: JSON.stringify(nextList)
66917
+ }, "warn");
66918
+ const apply = () => {
66919
+ for (const id of nextIds) {
66920
+ if (this.pageModel.pageList().indexOf(id) < 0) {
66921
+ this.pageModel.addPage(id);
66922
+ }
66923
+ }
66924
+ this.list = nextList;
66925
+ this.indexedPageMap.set("list", nextList);
66926
+ };
66927
+ if (this.indexedPageMap.doc) {
66928
+ this.indexedPageMap.doc.transact(apply);
66929
+ } else {
66930
+ apply();
66931
+ }
66932
+ nextIds.forEach((id) => this.idPool.add(id));
66933
+ return nextIds[0];
66934
+ }
66935
+ ensureValidList() {
66936
+ let context = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "read";
66937
+ const normalized = this.normalizeList(this.list);
66938
+ if (!normalized) {
66939
+ return {
66940
+ head: this.repairList(`${context}: invalid list`)
66941
+ };
66942
+ }
66943
+ this.list = normalized;
66944
+ const validation = this.validateList(this.list);
66945
+ if (!validation.valid) {
66946
+ return {
66947
+ head: this.repairList(`${context}: ${validation.reason}`)
66948
+ };
66949
+ }
66950
+ return {
66951
+ head: validation.head
66952
+ };
66953
+ }
66635
66954
  getNextId() {
66636
66955
  const cache = Array.from(this.idPool).filter((id) => this.idList.indexOf(id) < 0);
66637
66956
  if (cache.length > 0) {
@@ -67563,7 +67882,6 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
67563
67882
  this.emitter.emit("elementDeselected", userId);
67564
67883
  return;
67565
67884
  }
67566
- editor.show();
67567
67885
  const targetLayerId = this.userMap(userId).get(WhiteboardKeys.currentPage);
67568
67886
  const selfLayerId = this.userMap(this.userId).get(WhiteboardKeys.currentPage);
67569
67887
  if (targetLayerId !== selfLayerId || !this.layers.has(targetLayerId)) {
@@ -67571,7 +67889,13 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
67571
67889
  }
67572
67890
  const elementModels = elements.map((id) => {
67573
67891
  return this.layers.get(targetLayerId).elementModels.get(id);
67574
- }).filter((v) => !!v);
67892
+ }).filter((model) => !!model && !model.isBackground);
67893
+ if (elementModels.length === 0) {
67894
+ editor.hidden();
67895
+ this.emitter.emit("elementDeselected", userId);
67896
+ return;
67897
+ }
67898
+ editor.show();
67575
67899
  editor.setTargets(elementModels);
67576
67900
  if (elementModels.length === 1) {
67577
67901
  const model = elementModels[0];
@@ -67763,14 +68087,17 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
67763
68087
  this.camera.resetViewMatrixToMain();
67764
68088
  }
67765
68089
  };
67766
- this.emitter.insertImage = (src, pageId) => {
68090
+ this.emitter.insertImage = (src, options) => {
67767
68091
  if (!/https/.test(src)) {
67768
68092
  (0, import_forge_room.log)("[@netless/forge-whiteboard] invalid image url, src needs to be in the HTTPS protocol.", {
67769
68093
  src
67770
68094
  }, "warn");
67771
68095
  return;
67772
68096
  }
67773
- let targetPageId = pageId;
68097
+ const normalizedOptions = typeof options === "string" ? {
68098
+ pageId: options
68099
+ } : options ?? {};
68100
+ let targetPageId = normalizedOptions.pageId;
67774
68101
  if (!targetPageId) {
67775
68102
  targetPageId = this.pageModel.getCurrentPage(this.userManager.selfId);
67776
68103
  }
@@ -67778,13 +68105,16 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
67778
68105
  (0, import_forge_room.log)("[@netless/forge-whiteboard] page not found", {}, "warn");
67779
68106
  return;
67780
68107
  }
67781
- this.layers.get(targetPageId)?.createImage(src);
68108
+ this.layers.get(targetPageId)?.createImage(src, normalizedOptions.fit ?? "legacy", normalizedOptions.role ?? "normal");
67782
68109
  };
67783
68110
  this.emitter.removeElement = (pageId, elementId) => {
67784
68111
  if (!this.layers.has(pageId)) {
67785
68112
  (0, import_forge_room.log)("[@netless/forge-whiteboard] page not found", {}, "warn");
67786
68113
  return;
67787
68114
  }
68115
+ if (this.layers.get(pageId)?.isBackgroundElement(elementId)) {
68116
+ return;
68117
+ }
67788
68118
  this.layers.get(pageId)?.removeElementItem(elementId);
67789
68119
  };
67790
68120
  this.emitter.getViewModel = (userId) => {
@@ -67835,7 +68165,11 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
67835
68165
  if (model) {
67836
68166
  if (model.elements.doc) {
67837
68167
  model.elements.doc.transact(() => {
67838
- model.elements.clear();
68168
+ for (const key of Array.from(model.elements.keys())) {
68169
+ if (!model.isBackgroundElement(key)) {
68170
+ model.elements.delete(key);
68171
+ }
68172
+ }
67839
68173
  }, elementsUndoOrigin);
67840
68174
  } else {
67841
68175
  model.elementModels.clear();
@@ -68247,7 +68581,9 @@ var WhiteboardApplication = class _WhiteboardApplication extends import_forge_ro
68247
68581
  const at = this.findElementIndex(element.data.index, parent);
68248
68582
  for (let i = at; i >= 0; i--) {
68249
68583
  const child = children[i - 1];
68250
- if (!child || child.data.index < element.data.index || child.data.uuid < element.data.uuid) {
68584
+ const childRole = child?.data.role === "background" ? 0 : 1;
68585
+ const elementRole = element.data.role === "background" ? 0 : 1;
68586
+ 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) {
68251
68587
  parent.insertChild(i, element);
68252
68588
  break;
68253
68589
  }