@ai-matrx/office 0.8.44 → 0.8.46

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.
@@ -35,6 +35,7 @@ __export(collab_exports, {
35
35
  DOCS_COLLAB_PROTO: () => DOCS_COLLAB_PROTO,
36
36
  DOCS_TRANSFORM_VERSION: () => DOCS_TRANSFORM_VERSION,
37
37
  DOC_LOCAL_KEYS: () => DOC_LOCAL_KEYS,
38
+ REDO_COMMAND: () => REDO_COMMAND,
38
39
  RICH_TEXT_EDITING: () => RICH_TEXT_EDITING,
39
40
  SENDER_VIEW_PARAMS: () => SENDER_VIEW_PARAMS,
40
41
  SHEETS_COLLAB_PROTO: () => SHEETS_COLLAB_PROTO,
@@ -42,6 +43,7 @@ __export(collab_exports, {
42
43
  SHEETS_READABLE_PROTOS: () => SHEETS_READABLE_PROTOS,
43
44
  SHEET_ROOM_EDITOR_PRESETS: () => SHEET_ROOM_EDITOR_PRESETS,
44
45
  SHEET_ROOM_PLUGIN_PACKAGES: () => SHEET_ROOM_PLUGIN_PACKAGES,
46
+ UNDO_COMMAND: () => UNDO_COMMAND,
45
47
  WorkbookCollabSession: () => WorkbookCollabSession,
46
48
  applyDocsOp: () => applyDocsOp,
47
49
  asLoadedDocsSnapshot: () => asLoadedDocsSnapshot,
@@ -57,7 +59,9 @@ __export(collab_exports, {
57
59
  documentOtFactory: () => documentOtFactory,
58
60
  documentUsesOrderedRoom: () => documentUsesOrderedRoom,
59
61
  dropEmptyStyleRetains: () => dropEmptyStyleRetains,
62
+ editorBridgeCell: () => editorBridgeCell,
60
63
  encodeSheetOp: () => encodeSheetOp,
64
+ filterServiceRange: () => filterServiceRange,
61
65
  invertDocsOp: () => invertDocsOp,
62
66
  isCellEditorScratch: () => isCellEditorScratch,
63
67
  isNoopDocsOp: () => isNoopDocsOp,
@@ -75,6 +79,7 @@ __export(collab_exports, {
75
79
  startWorkbookOtRoom: () => startWorkbookOtRoom,
76
80
  stripSenderView: () => stripSenderView,
77
81
  takeCovered: () => takeCovered,
82
+ touchesEditedCell: () => touchesEditedCell,
78
83
  transformDocsOp: () => transformDocsOp,
79
84
  transformSheetOp: () => transformSheetOp,
80
85
  transformTextX: () => transformTextX,
@@ -1249,26 +1254,77 @@ function rebaseUndoStack(items, remote, stack) {
1249
1254
  const first = stack === "undo" ? "undoMutations" : "redoMutations";
1250
1255
  const second = stack === "undo" ? "redoMutations" : "undoMutations";
1251
1256
  let dropped = 0;
1252
- for (let i = items.length - 1; i >= 0; i--) {
1257
+ let R = remote;
1258
+ for (let i = items.length - 1; i >= 0 && !isNoopDocsOp(R); i--) {
1253
1259
  const item = items[i];
1254
1260
  try {
1255
- const a = threadThrough(item[first], remote);
1261
+ const a = threadThrough(item[first], R);
1256
1262
  const b = threadThrough(item[second], a.after);
1257
1263
  if (a.next) item[first] = a.next;
1258
1264
  if (b.next) item[second] = b.next;
1265
+ R = a.after;
1259
1266
  } catch {
1260
1267
  items.splice(i, 1);
1261
1268
  dropped++;
1269
+ R = carryPast(item[first], R);
1262
1270
  }
1263
1271
  }
1264
1272
  return dropped;
1265
1273
  }
1274
+ function carryPast(list, remote) {
1275
+ let R = remote;
1276
+ for (const m of list ?? []) {
1277
+ if (m.id !== RICH_TEXT_EDITING || isNoopDocsOp(R)) continue;
1278
+ const own = m.params.actions;
1279
+ if (touchesPageSetup(own) && touchesPageSetup(R)) continue;
1280
+ R = transformDocsOp(R, own, "right").op;
1281
+ }
1282
+ return R;
1283
+ }
1284
+ var UNDO_COMMAND = "univer.command.undo";
1285
+ var REDO_COMMAND = "univer.command.redo";
1266
1286
  function openDocsOtRoom(o) {
1267
1287
  const { port } = o;
1268
1288
  let session = null;
1269
1289
  let composing = false;
1270
1290
  let queue = [];
1271
1291
  const drainWaiters = [];
1292
+ let replay = null;
1293
+ const noteHistoryStep = (m, inverse) => {
1294
+ const side = m.cmd === UNDO_COMMAND ? "undo" : m.cmd === REDO_COMMAND ? "redo" : null;
1295
+ if (!side) {
1296
+ replay = null;
1297
+ return;
1298
+ }
1299
+ for (const t of port.undoTargets()) {
1300
+ const stacks = undoStacksOf(t.service);
1301
+ const stack = stacks ? (side === "undo" ? stacks.undo : stacks.redo).get(t.unitId) : void 0;
1302
+ const item = stack?.[stack.length - 1];
1303
+ const list = item ? side === "undo" ? item.undoMutations : item.redoMutations : void 0;
1304
+ const k = list ? list.findIndex((x) => x.params === m.params) : -1;
1305
+ if (!item || !list || k < 0) continue;
1306
+ if (k === 0 || !replay || replay.item !== item || replay.side !== side) {
1307
+ const other = (side === "undo" ? item.redoMutations : item.undoMutations)?.[0]?.params ?? {};
1308
+ const { textRanges: _t, layoutState: _l, actions: _a, ...template } = other;
1309
+ replay = { item, side, inverses: [], template };
1310
+ }
1311
+ if (inverse === void 0 || list.some((x) => x.id !== RICH_TEXT_EDITING)) {
1312
+ replay = null;
1313
+ return;
1314
+ }
1315
+ replay.inverses.unshift(inverse);
1316
+ if (k === list.length - 1) {
1317
+ const rewritten = replay.inverses.filter((op) => !isNoopDocsOp(op)).map((op) => ({ id: RICH_TEXT_EDITING, params: { ...replay.template, segmentId: m.params?.segmentId ?? "", actions: op } }));
1318
+ if (rewritten.length) {
1319
+ if (side === "undo") item.redoMutations = rewritten;
1320
+ else item.undoMutations = rewritten;
1321
+ }
1322
+ replay = null;
1323
+ }
1324
+ return;
1325
+ }
1326
+ replay = null;
1327
+ };
1272
1328
  const notice = (n) => {
1273
1329
  try {
1274
1330
  o.onNotice?.(n);
@@ -1366,7 +1422,10 @@ function openDocsOtRoom(o) {
1366
1422
  }
1367
1423
  const p = m.params ?? {};
1368
1424
  const applied = p.actions ?? null;
1369
- if (isNoopDocsOp(applied)) return;
1425
+ if (isNoopDocsOp(applied)) {
1426
+ noteHistoryStep(m, null);
1427
+ return;
1428
+ }
1370
1429
  let op = normalizeDocsOp(structuredClone(applied));
1371
1430
  let inverse;
1372
1431
  try {
@@ -1374,6 +1433,7 @@ function openDocsOtRoom(o) {
1374
1433
  } catch {
1375
1434
  inverse = void 0;
1376
1435
  }
1436
+ noteHistoryStep(m, inverse === void 0 ? void 0 : normalizeDocsOp(inverse));
1377
1437
  if (queue.length) {
1378
1438
  inverse = void 0;
1379
1439
  const next = [];
@@ -2846,6 +2906,18 @@ function transformCell(X, Y4, side) {
2846
2906
  if (styleKeys(xs).length !== styleKeys(kept ?? {}).length) xLost = true;
2847
2907
  }
2848
2908
  }
2909
+ if (hasContent(X) && hasContent(Y4)) {
2910
+ const has = (s) => !!s && typeof s === "object" && "n" in s;
2911
+ const outS = out.s;
2912
+ if (side === "left" && !has(X.s) && has(Y4.s) && X.s !== null) {
2913
+ out.s = { ...outS && typeof outS === "object" ? outS : {}, n: null };
2914
+ } else if (side === "right" && outS && typeof outS === "object" && "n" in outS) {
2915
+ const rest = withoutStyleKeys(outS, /* @__PURE__ */ new Set(["n"]));
2916
+ if (rest) out.s = rest;
2917
+ else delete out.s;
2918
+ xLost = true;
2919
+ }
2920
+ }
2849
2921
  return { entries: Object.keys(out).length ? [out, ...tail] : tail, xLost, yLost };
2850
2922
  }
2851
2923
  function transformCellsOverCells(x, y, side) {
@@ -3934,6 +4006,12 @@ function rebaseSheetUndoStack(items, remote, unitId) {
3934
4006
  function createSheetsOtAdapter(port, hooks = {}) {
3935
4007
  let queue = [];
3936
4008
  let draining = false;
4009
+ const sameOp = (a, b) => JSON.stringify(canonicalKeys(a ?? [])) === JSON.stringify(canonicalKeys(b ?? []));
4010
+ const commutesWithHeld = (op) => queue.every((q) => {
4011
+ const a = transformSheetOp(op, q.op, "left");
4012
+ const b = transformSheetOp(q.op, op, "right");
4013
+ return !("barrier" in a) && !("barrier" in b) && sameOp(a.op, op) && sameOp(b.op, q.op);
4014
+ });
3937
4015
  const lazy = typeof port.beforeUndo === "function";
3938
4016
  let pending = [];
3939
4017
  let pendingBase = 0;
@@ -4006,15 +4084,16 @@ function createSheetsOtAdapter(port, hooks = {}) {
4006
4084
  decode: decodeSheetOp,
4007
4085
  apply(op) {
4008
4086
  if (isNoopSheetOp(op)) return true;
4009
- if (port.busy() || queue.length) {
4010
- queue.push(op);
4087
+ const hold = port.busy() || !!port.defers?.(op) || queue.length > 0 && !commutesWithHeld(op);
4088
+ if (hold) {
4089
+ queue.push({ op, rev: hooks.rev?.() ?? 0 });
4011
4090
  if (!draining) {
4012
4091
  draining = true;
4013
4092
  port.whenIdle(() => {
4014
4093
  draining = false;
4015
4094
  const held = queue;
4016
4095
  queue = [];
4017
- for (const q of held) applyNow(q);
4096
+ for (const q of held) applyNow(q.op);
4018
4097
  });
4019
4098
  }
4020
4099
  return true;
@@ -4039,9 +4118,9 @@ function createSheetsOtAdapter(port, hooks = {}) {
4039
4118
  async hash() {
4040
4119
  if (port.busy() || queue.length) {
4041
4120
  await new Promise((resolve) => {
4042
- const done = setTimeout(resolve, 5e3);
4121
+ const done = queue.length ? null : setTimeout(resolve, 5e3);
4043
4122
  port.whenIdle(() => {
4044
- clearTimeout(done);
4123
+ if (done) clearTimeout(done);
4045
4124
  setTimeout(resolve, 0);
4046
4125
  });
4047
4126
  });
@@ -4052,15 +4131,17 @@ function createSheetsOtAdapter(port, hooks = {}) {
4052
4131
  rebaseLocalOverHeld(local) {
4053
4132
  let op = local;
4054
4133
  const next = [];
4134
+ const overrode = [];
4055
4135
  for (const q of queue) {
4056
- const l2 = transformSheetOp(op, q, "left");
4057
- const q2 = transformSheetOp(q, op, "right");
4136
+ const l2 = transformSheetOp(op, q.op, "left");
4137
+ const q2 = transformSheetOp(q.op, op, "right");
4058
4138
  if ("barrier" in l2 || "barrier" in q2) return "barrier";
4139
+ if (l2.lost.length && q.rev > 0) overrode.push({ rev: q.rev, lost: l2.lost });
4059
4140
  op = l2.op ?? [];
4060
- if (q2.op?.length) next.push(q2.op);
4141
+ if (q2.op?.length) next.push({ op: q2.op, rev: q.rev });
4061
4142
  }
4062
4143
  queue = next;
4063
- return op;
4144
+ return { op, overrode };
4064
4145
  },
4065
4146
  clearHeld() {
4066
4147
  queue = [];
@@ -4071,7 +4152,6 @@ function createSheetsOtAdapter(port, hooks = {}) {
4071
4152
  function openSheetsOtRoom(o) {
4072
4153
  const { port } = o;
4073
4154
  let session = null;
4074
- const adapter = createSheetsOtAdapter(port);
4075
4155
  const notice = (n) => {
4076
4156
  try {
4077
4157
  o.onNotice?.(n);
@@ -4079,6 +4159,29 @@ function openSheetsOtRoom(o) {
4079
4159
  console.warn("[sheets-ot] notice handler failed", err);
4080
4160
  }
4081
4161
  };
4162
+ const adapter = createSheetsOtAdapter(port, { rev: () => session?.rev() ?? 0 });
4163
+ let healing = null;
4164
+ const HEALS = /* @__PURE__ */ new Set(["hash_mismatch", "save_hash_mismatch", "ack_mismatch"]);
4165
+ const screen = () => {
4166
+ try {
4167
+ const s = port.snapshot();
4168
+ return s ? canonicalSheetString(s) : null;
4169
+ } catch {
4170
+ return null;
4171
+ }
4172
+ };
4173
+ const onIncident = (i) => {
4174
+ if (HEALS.has(i.kind) && !healing) healing = { reason: i.kind, before: screen() };
4175
+ o.onIncident?.(i);
4176
+ };
4177
+ const onStatus = (st) => {
4178
+ if (st === "live" && healing) {
4179
+ const h = healing;
4180
+ healing = null;
4181
+ if (h.before !== null && screen() !== h.before) notice({ kind: "resynced", reason: h.reason });
4182
+ } else if (st === "read_only" || st === "closed") healing = null;
4183
+ o.onStatus?.(st);
4184
+ };
4082
4185
  const onLocal = (e) => {
4083
4186
  if (!session) return;
4084
4187
  if (e.kind === "not_shared") {
@@ -4098,6 +4201,7 @@ function openSheetsOtRoom(o) {
4098
4201
  }
4099
4202
  let op = e.mutations;
4100
4203
  let inverse = e.inverse;
4204
+ let overrode = [];
4101
4205
  if (adapter.held()) {
4102
4206
  inverse = void 0;
4103
4207
  const r = adapter.rebaseLocalOverHeld(op);
@@ -4106,10 +4210,11 @@ function openSheetsOtRoom(o) {
4106
4210
  session.rebuild("held_barrier");
4107
4211
  return;
4108
4212
  }
4109
- op = r;
4213
+ op = r.op;
4214
+ overrode = r.overrode;
4110
4215
  if (!op.length) return;
4111
4216
  }
4112
- if (!session.local(op, { ...inverse !== void 0 ? { inverse } : {}, label: e.label ?? SHEETS_EDIT_GROUP })) notice({ kind: "not_live" });
4217
+ if (!session.local(op, { ...inverse !== void 0 ? { inverse } : {}, label: e.label ?? SHEETS_EDIT_GROUP, ...overrode.length ? { overrode } : {} })) notice({ kind: "not_live" });
4113
4218
  };
4114
4219
  const unsubLocal = port.onLocal(onLocal);
4115
4220
  session = (0, import_collab5.openOpSession)({
@@ -4119,8 +4224,8 @@ function openSheetsOtRoom(o) {
4119
4224
  clientId: o.clientId,
4120
4225
  ...o.presence ? { presence: o.presence } : {},
4121
4226
  onNotice: (n) => notice(n),
4122
- ...o.onIncident ? { onIncident: o.onIncident } : {},
4123
- ...o.onStatus ? { onStatus: o.onStatus } : {},
4227
+ onIncident,
4228
+ onStatus,
4124
4229
  ...o.offline ? { offline: o.offline } : {},
4125
4230
  ...o.onOffline ? { onOffline: o.onOffline } : {}
4126
4231
  });
@@ -4213,6 +4318,23 @@ function releaseUnitPluginState(cs, unitId, saved) {
4213
4318
  }
4214
4319
 
4215
4320
  // src/univer/collab/sheets-ot/univer-port.ts
4321
+ function filterServiceRange(service) {
4322
+ return (unitId, subUnitId) => {
4323
+ try {
4324
+ const r = service()?.getFilterModel(unitId, subUnitId)?.getRange();
4325
+ return r ? { startRow: r.startRow, endRow: r.endRow, startColumn: r.startColumn, endColumn: r.endColumn, ...r.rangeType !== void 0 ? { rangeType: r.rangeType } : {} } : null;
4326
+ } catch {
4327
+ return null;
4328
+ }
4329
+ };
4330
+ }
4331
+ function editorBridgeCell(bridge) {
4332
+ return () => {
4333
+ if (!bridge.isVisible()?.visible) return null;
4334
+ const at = bridge.getEditLocation();
4335
+ return at ? { sheetId: at.sheetId, row: at.row, col: at.column } : null;
4336
+ };
4337
+ }
4216
4338
  var COMMAND = 0;
4217
4339
  var MUTATION = 2;
4218
4340
  var VALUES = "sheet.mutation.set-range-values";
@@ -4220,6 +4342,7 @@ var ADD_MERGE = "sheet.mutation.add-worksheet-merge";
4220
4342
  var REMOVE_MERGE = "sheet.mutation.remove-worksheet-merge";
4221
4343
  var SHEET_NAME = "sheet.mutation.set-worksheet-name";
4222
4344
  var INSERT_SHEET = "sheet.mutation.insert-sheet";
4345
+ var REMOVE_SHEET2 = "sheet.mutation.remove-sheet";
4223
4346
  var FILTER_RANGE = "sheet.mutation.set-filter-range";
4224
4347
  var RESHAPE = /* @__PURE__ */ new Set(["sheet.mutation.set-worksheet-row-count", "sheet.mutation.set-worksheet-column-count", "sheet.mutation.remove-sheet", INSERT_SHEET]);
4225
4348
  var UNDO = "univer.command.undo";
@@ -4239,6 +4362,18 @@ function univerSheetsOtPort(o) {
4239
4362
  const tracker = { stack: [], settle: () => settle() };
4240
4363
  let group = null;
4241
4364
  let applying = false;
4365
+ let editorHold = false;
4366
+ const edited = () => {
4367
+ try {
4368
+ return o.cellEditor?.() ?? null;
4369
+ } catch {
4370
+ return null;
4371
+ }
4372
+ };
4373
+ const holding = () => {
4374
+ if (editorHold && !edited()) editorHold = false;
4375
+ return editorHold;
4376
+ };
4242
4377
  const unitId = () => o.workbook()?.getUnitId() ?? null;
4243
4378
  const mine = (params) => {
4244
4379
  const u = unitId();
@@ -4267,7 +4402,7 @@ function univerSheetsOtPort(o) {
4267
4402
  if (!u || !o.filterRange) return null;
4268
4403
  try {
4269
4404
  const r = o.filterRange(u, sheet);
4270
- return r ? { startRow: r.startRow, endRow: r.endRow, startColumn: r.startColumn, endColumn: r.endColumn } : null;
4405
+ return r ? { startRow: r.startRow, endRow: r.endRow, startColumn: r.startColumn, endColumn: r.endColumn, ...r.rangeType !== void 0 ? { rangeType: r.rangeType } : {} } : null;
4271
4406
  } catch {
4272
4407
  return null;
4273
4408
  }
@@ -4283,10 +4418,45 @@ function univerSheetsOtPort(o) {
4283
4418
  }
4284
4419
  }
4285
4420
  };
4421
+ const sweepRemovedSheetRefs = (g) => {
4422
+ const removed = g.mutations.filter((m) => m.id === REMOVE_SHEET2).map((m) => String(m.params.subUnitId ?? ""));
4423
+ const wb = o.workbook();
4424
+ const u = unitId();
4425
+ if (!removed.length || !wb || !u) return;
4426
+ const refSheets = {};
4427
+ for (const [id, name] of Object.entries(g.sheets)) refSheets[name] = id;
4428
+ for (const gone of removed) {
4429
+ const name = g.sheets[gone];
4430
+ if (!name) continue;
4431
+ for (const ws of wb.getSheets()) {
4432
+ const sheet = ws.getSheetId();
4433
+ const cells = {};
4434
+ ws.getCellMatrix?.().forValue((r, c, cell) => {
4435
+ const f = cell?.f;
4436
+ if (typeof f !== "string" || !f.includes(name)) return;
4437
+ const out = rewriteFormula(f, { ownSheet: sheet, refSheets }, { kind: "remove-sheet", sheet: gone });
4438
+ if (typeof out !== "string" && out.changed) (cells[r] ??= {})[c] = { f: out.text, si: null };
4439
+ });
4440
+ if (!Object.keys(cells).length) continue;
4441
+ applying = true;
4442
+ try {
4443
+ cs.syncExecuteCommand(VALUES, { unitId: u, subUnitId: sheet, cellValue: json(cells) });
4444
+ } finally {
4445
+ applying = false;
4446
+ }
4447
+ const done = {};
4448
+ for (const r of Object.keys(cells))
4449
+ for (const c of Object.keys(cells[r])) (done[r] ??= {})[c] = completeCellContent(cells[r][c], json(ws.getCellRaw(Number(r), Number(c)) ?? null));
4450
+ g.mutations.push({ id: VALUES, params: { subUnitId: sheet, cellValue: done } });
4451
+ g.inverses.push(void 0);
4452
+ }
4453
+ }
4454
+ };
4286
4455
  const flush = () => {
4287
4456
  const g = group;
4288
4457
  group = null;
4289
4458
  if (!g) return;
4459
+ if (!g.notShared) sweepRemovedSheetRefs(g);
4290
4460
  if (g.notShared) emit({ kind: "not_shared", mutationId: g.notShared, label: g.label });
4291
4461
  else if (g.mutations.length) {
4292
4462
  const mutations = annotateSheetOps(annotateFormulas(g.mutations, g.sheets, g.names), g.order);
@@ -4298,7 +4468,7 @@ function univerSheetsOtPort(o) {
4298
4468
  };
4299
4469
  function settle() {
4300
4470
  if (tracker.stack.length === 0) flush();
4301
- if (tracker.stack.length === 0 && !group) for (const fn of idle.splice(0)) fn();
4471
+ if (tracker.stack.length === 0 && !group && !holding()) for (const fn of idle.splice(0)) fn();
4302
4472
  }
4303
4473
  const before = cs.beforeCommandExecuted((info, options) => {
4304
4474
  if (info.id === UNDO || info.id === REDO) for (const fn of [...undoWatchers]) fn();
@@ -4312,6 +4482,7 @@ function univerSheetsOtPort(o) {
4312
4482
  const ws = o.workbook()?.getSheetBySheetId(String(p.subUnitId ?? ""));
4313
4483
  if (!ws) return;
4314
4484
  const record = { merges: json(ws.getMergeData() ?? []) };
4485
+ if (!group) record.lone = openGroup(null);
4315
4486
  if (info.id === VALUES) {
4316
4487
  record.cells = /* @__PURE__ */ new Map();
4317
4488
  const cells = p.cellValue ?? {};
@@ -4348,6 +4519,7 @@ function univerSheetsOtPort(o) {
4348
4519
  }
4349
4520
  };
4350
4521
  const after = cs.onCommandExecuted((info) => {
4522
+ if (editorHold && tracker.stack.length === 0 && !group && !edited()) settle();
4351
4523
  if (info.type !== COMMAND) return;
4352
4524
  const at = tracker.stack.lastIndexOf(info.id);
4353
4525
  if (at >= 0) tracker.stack.splice(at, 1);
@@ -4429,7 +4601,7 @@ function univerSheetsOtPort(o) {
4429
4601
  else {
4430
4602
  inv = {};
4431
4603
  if (CONTENT_KEYS.some((k) => k !== "t" && k in done)) Object.assign(inv, fullContent(p));
4432
- if ("s" in done) inv.s = p?.s ?? null;
4604
+ if ("s" in done) inv.s = inverseStyle(p?.s, done.s);
4433
4605
  }
4434
4606
  (invCells[r] ??= {})[c] = inv;
4435
4607
  }
@@ -4467,7 +4639,7 @@ function univerSheetsOtPort(o) {
4467
4639
  };
4468
4640
  const collab = cs.onMutationExecutedForCollab((info, options) => {
4469
4641
  if (applying || flagged(options) || !mine(info.params)) return;
4470
- if (!group) group = openGroup(null);
4642
+ if (!group) group = pre.get(info.params)?.lone ?? openGroup(null);
4471
4643
  const g = group;
4472
4644
  if (!g.notShared) {
4473
4645
  const r = capture(info);
@@ -4548,11 +4720,18 @@ function univerSheetsOtPort(o) {
4548
4720
  listeners.add(listener);
4549
4721
  return () => void listeners.delete(listener);
4550
4722
  },
4723
+ // the editor hold is not "busy": only the ops that touch its cell wait (defers), the rest apply live
4551
4724
  busy: () => tracker.stack.length > 0 || group !== null,
4725
+ defers(op) {
4726
+ const at = edited();
4727
+ if (!at || !op.some((m) => touchesEditedCell(m, at))) return false;
4728
+ editorHold = true;
4729
+ return true;
4730
+ },
4552
4731
  /** The outermost commands still running here (why the port is busy), for logs. */
4553
4732
  running: () => [...tracker.stack, ...group && tracker.stack.length === 0 ? ["(group)"] : []],
4554
4733
  whenIdle(fn) {
4555
- if (tracker.stack.length === 0 && !group) fn();
4734
+ if (tracker.stack.length === 0 && !group && !holding()) fn();
4556
4735
  else idle.push(fn);
4557
4736
  },
4558
4737
  applyRemote(m) {
@@ -4612,6 +4791,55 @@ function univerSheetsOtPort(o) {
4612
4791
  }
4613
4792
  };
4614
4793
  }
4794
+ function inverseStyle(prev, wrote) {
4795
+ if (!prev || typeof prev !== "object") return null;
4796
+ if (!wrote || typeof wrote !== "object") return prev;
4797
+ const out = { ...prev };
4798
+ for (const k of Object.keys(wrote)) if (!(k in out)) out[k] = null;
4799
+ return out;
4800
+ }
4801
+ function touchesEditedCell(m, at) {
4802
+ const p = m.params;
4803
+ const sheet = String(p.subUnitId ?? p.sheetId ?? "");
4804
+ if (sheet !== at.sheetId) return false;
4805
+ if (m.id === VALUES) {
4806
+ const row = p.cellValue?.[at.row];
4807
+ return !!row && Object.prototype.hasOwnProperty.call(row, at.col);
4808
+ }
4809
+ if (m.id === REMOVE_SHEET2) return true;
4810
+ if (m.id === "sheet.mutation.set-worksheet-row-count") return !(Number(p.rowCount) > at.row);
4811
+ if (m.id === "sheet.mutation.set-worksheet-column-count") return !(Number(p.columnCount) > at.col);
4812
+ const axis = axisOpOf(m);
4813
+ if (axis) {
4814
+ const i = axis.axis === "row" ? at.row : at.col;
4815
+ return mapIndex(axis.op, i) !== i;
4816
+ }
4817
+ const ranges = [];
4818
+ let unreadable = false;
4819
+ const walk = (v, depth) => {
4820
+ if (!v || typeof v !== "object" || depth > 6) return;
4821
+ if (Array.isArray(v)) {
4822
+ for (const x of v) walk(x, depth + 1);
4823
+ return;
4824
+ }
4825
+ const o = v;
4826
+ if (["startRow", "endRow", "startColumn", "endColumn"].some((k) => k in o)) {
4827
+ const r = o;
4828
+ if (![r.startRow, r.endRow, r.startColumn, r.endColumn].every((n) => typeof n === "number" && Number.isFinite(n)) && !(r.rangeType && r.rangeType > 0)) unreadable = true;
4829
+ else ranges.push(r);
4830
+ return;
4831
+ }
4832
+ if (typeof o.row === "number" && typeof o.col === "number") ranges.push({ startRow: o.row, endRow: o.row, startColumn: o.col, endColumn: o.col });
4833
+ for (const x of Object.values(o)) walk(x, depth + 1);
4834
+ };
4835
+ walk(p, 0);
4836
+ return unreadable || ranges.some((r) => covers(r, at));
4837
+ }
4838
+ function covers(r, at) {
4839
+ const rows = r.rangeType === 2 || r.rangeType === 3 || r.startRow <= at.row && at.row <= r.endRow;
4840
+ const cols = r.rangeType === 1 || r.rangeType === 3 || r.startColumn <= at.col && at.col <= r.endColumn;
4841
+ return rows && cols;
4842
+ }
4615
4843
  function fullContent(cell) {
4616
4844
  const out = {};
4617
4845
  for (const k of CONTENT_KEYS) out[k] = cell && cell[k] !== void 0 ? cell[k] : null;