@ai-matrx/office 0.8.38 → 0.8.39
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/CHANGELOG.md +5 -0
- package/dist/collab-sheets-transform.cjs +4 -7
- package/dist/collab-sheets-transform.cjs.map +1 -1
- package/dist/collab-sheets-transform.js +4 -7
- package/dist/collab-sheets-transform.js.map +1 -1
- package/dist/docs.cjs +1 -1
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.js +1 -1
- package/dist/docs.js.map +1 -1
- package/dist/sheets-comment-anchors.cjs +87 -8
- package/dist/sheets-comment-anchors.cjs.map +1 -1
- package/dist/sheets-comment-anchors.js +82 -3
- package/dist/sheets-comment-anchors.js.map +1 -1
- package/dist/univer-collab.cjs +5 -8
- package/dist/univer-collab.cjs.map +1 -1
- package/dist/univer-collab.js +5 -8
- package/dist/univer-collab.js.map +1 -1
- package/package.json +3 -3
|
@@ -70,6 +70,84 @@ function colName(c) {
|
|
|
70
70
|
}
|
|
71
71
|
var sheetCellName = (cell) => `${colName(cell.col)}${cell.row + 1}`;
|
|
72
72
|
|
|
73
|
+
// src/sheets/note-follower.ts
|
|
74
|
+
|
|
75
|
+
var isNoteController = (x) => {
|
|
76
|
+
const o = x;
|
|
77
|
+
return !!o && o._watcherMap instanceof Map && o._disposableMap instanceof Map && typeof o._watch === "function" && !!_optionalChain([o, 'access', _ => _._sheetsNoteModel, 'optionalAccess', _2 => _2.getSheetNotes]);
|
|
78
|
+
};
|
|
79
|
+
function findNoteRefRangeController(injector) {
|
|
80
|
+
const seen = /* @__PURE__ */ new Set();
|
|
81
|
+
const visit = (inj) => {
|
|
82
|
+
if (!inj || seen.has(inj)) return null;
|
|
83
|
+
seen.add(inj);
|
|
84
|
+
const resolved = _optionalChain([inj, 'access', _3 => _3.resolvedDependencyCollection, 'optionalAccess', _4 => _4.resolvedDependencies]);
|
|
85
|
+
if (resolved instanceof Map) {
|
|
86
|
+
for (const list of resolved.values()) for (const x of Array.isArray(list) ? list : [list]) if (isNoteController(x)) return x;
|
|
87
|
+
}
|
|
88
|
+
for (const child of Array.isArray(inj.children) ? inj.children : []) {
|
|
89
|
+
const hit = visit(child);
|
|
90
|
+
if (hit) return hit;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
return visit(injector);
|
|
95
|
+
}
|
|
96
|
+
var NOTE_MUTATIONS = /* @__PURE__ */ new Set(["sheet.mutation.update-note", "sheet.mutation.remove-note", "sheet.mutation.update-note-position"]);
|
|
97
|
+
function installSheetNoteFollower(injector, commands) {
|
|
98
|
+
let ctrl = null;
|
|
99
|
+
let absent = false;
|
|
100
|
+
let warned = false;
|
|
101
|
+
const ensure = (byNote = false) => {
|
|
102
|
+
if (ctrl) return ctrl;
|
|
103
|
+
if (absent && !byNote) return null;
|
|
104
|
+
const found = findNoteRefRangeController(injector);
|
|
105
|
+
absent = !found;
|
|
106
|
+
if (!found) {
|
|
107
|
+
if (byNote && !warned) console.warn("[sheets-ot] cell notes: Univer's note controller was not found; notes follow Univer's own watchers");
|
|
108
|
+
warned ||= byNote;
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
for (const d of found._watcherMap.values()) _optionalChain([d, 'optionalAccess', _5 => _5.dispose, 'optionalCall', _6 => _6()]);
|
|
112
|
+
found._watcherMap.clear();
|
|
113
|
+
found._watch = () => {
|
|
114
|
+
};
|
|
115
|
+
ctrl = found;
|
|
116
|
+
return ctrl;
|
|
117
|
+
};
|
|
118
|
+
const before = commands.beforeCommandExecuted((info) => {
|
|
119
|
+
if (ctrl || info.type !== _core.CommandType.MUTATION) return;
|
|
120
|
+
if (NOTE_MUTATIONS.has(info.id)) ensure(true);
|
|
121
|
+
else if (structuralAxisOp(info.id, _nullishCoalesce(info.params, () => ( {})))) ensure();
|
|
122
|
+
});
|
|
123
|
+
const after = commands.onCommandExecuted((info) => {
|
|
124
|
+
if (info.type !== _core.CommandType.MUTATION) return;
|
|
125
|
+
const p = _nullishCoalesce(info.params, () => ( {}));
|
|
126
|
+
if (!structuralAxisOp(info.id, p) || typeof p.unitId !== "string") return;
|
|
127
|
+
const c = ensure();
|
|
128
|
+
if (!c) return;
|
|
129
|
+
const sheet = String(_nullishCoalesce(p.subUnitId, () => ( "")));
|
|
130
|
+
const notes = c._sheetsNoteModel.getSheetNotes(p.unitId, sheet);
|
|
131
|
+
if (!_optionalChain([notes, 'optionalAccess', _7 => _7.size])) return;
|
|
132
|
+
const moves = [];
|
|
133
|
+
for (const [id, note] of notes) {
|
|
134
|
+
const at = { row: Number(note.row), col: Number(note.col) };
|
|
135
|
+
const to = moveSheetAnchorCell(at, sheet, info.id, p);
|
|
136
|
+
if (to !== at) moves.push({ id, to });
|
|
137
|
+
}
|
|
138
|
+
for (const m of moves) {
|
|
139
|
+
if (m.to) commands.syncExecuteCommand("sheet.mutation.update-note-position", { unitId: p.unitId, sheetId: sheet, noteId: m.id, newPosition: m.to, silent: true }, { onlyLocal: true });
|
|
140
|
+
else commands.syncExecuteCommand("sheet.mutation.remove-note", { unitId: p.unitId, sheetId: sheet, noteId: m.id, silent: true }, { onlyLocal: true });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
dispose: () => {
|
|
145
|
+
before.dispose();
|
|
146
|
+
after.dispose();
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
73
151
|
// src/sheets/comment-anchors.ts
|
|
74
152
|
var SHEET_COMMENT_ANCHORS_RESOURCE = "SHEET_MATRX_COMMENT_ANCHORS_PLUGIN";
|
|
75
153
|
function applyToSheetAnchors(anchors, id, params) {
|
|
@@ -119,7 +197,7 @@ function sheetAnchorsFromJson(bytes) {
|
|
|
119
197
|
if (!ids || typeof ids !== "object") continue;
|
|
120
198
|
for (const [id, c] of Object.entries(ids)) {
|
|
121
199
|
const cell = c;
|
|
122
|
-
if (typeof _optionalChain([cell, 'optionalAccess',
|
|
200
|
+
if (typeof _optionalChain([cell, 'optionalAccess', _8 => _8.row]) === "number" && typeof _optionalChain([cell, 'optionalAccess', _9 => _9.col]) === "number") (out[sheet] ??= {})[id] = { row: cell.row, col: cell.col };
|
|
123
201
|
}
|
|
124
202
|
}
|
|
125
203
|
return out;
|
|
@@ -130,7 +208,7 @@ function sheetAnchorsFromJson(bytes) {
|
|
|
130
208
|
var REMOVED_KEEP = 25;
|
|
131
209
|
function removedBy(before, after, id, params) {
|
|
132
210
|
const sheetId = String(_nullishCoalesce(params.subUnitId, () => ( "")));
|
|
133
|
-
const gone = Object.entries(_nullishCoalesce(before[sheetId], () => ( {}))).filter(([anchorId]) => !_optionalChain([after, 'access',
|
|
211
|
+
const gone = Object.entries(_nullishCoalesce(before[sheetId], () => ( {}))).filter(([anchorId]) => !_optionalChain([after, 'access', _10 => _10[sheetId], 'optionalAccess', _11 => _11[anchorId]])).map(([anchorId, cell]) => ({ id: anchorId, cell }));
|
|
134
212
|
if (!gone.length) return null;
|
|
135
213
|
if (id === "sheet.mutation.remove-sheet") return { sheetId, axis: null, start: 0, count: 0, anchors: gone };
|
|
136
214
|
const op = structuralAxisOp(id, params);
|
|
@@ -141,7 +219,7 @@ var INSTALLED = /* @__PURE__ */ Symbol.for("ai-matrx.office.sheet-comment-anchor
|
|
|
141
219
|
function installSheetCommentAnchors(univer) {
|
|
142
220
|
const holder = univer;
|
|
143
221
|
if (holder[INSTALLED]) return holder[INSTALLED];
|
|
144
|
-
const injector = _optionalChain([holder, 'access',
|
|
222
|
+
const injector = _optionalChain([holder, 'access', _12 => _12.__getInjector, 'optionalCall', _13 => _13()]);
|
|
145
223
|
if (!injector) throw new Error("installSheetCommentAnchors: this Univer exposes no injector");
|
|
146
224
|
const commands = injector.get(_core.ICommandService);
|
|
147
225
|
const resources = injector.get(_core.IResourceManagerService);
|
|
@@ -169,11 +247,11 @@ function installSheetCommentAnchors(univer) {
|
|
|
169
247
|
const toRestore = [];
|
|
170
248
|
const restoreAfterUndo = (unitId, id, params) => {
|
|
171
249
|
const list = removed.get(unitId);
|
|
172
|
-
if (!_optionalChain([list, 'optionalAccess',
|
|
250
|
+
if (!_optionalChain([list, 'optionalAccess', _14 => _14.length])) return;
|
|
173
251
|
const sheetId = String(_nullishCoalesce(params.subUnitId, () => ( "")));
|
|
174
252
|
const op = id === "sheet.mutation.insert-sheet" ? null : structuralAxisOp(id, params);
|
|
175
253
|
const wholeSheet = id === "sheet.mutation.insert-sheet";
|
|
176
|
-
const matches = (r) => wholeSheet ? r.axis === null && r.sheetId === String(_nullishCoalesce(_nullishCoalesce(_optionalChain([params, 'access',
|
|
254
|
+
const matches = (r) => wholeSheet ? r.axis === null && r.sheetId === String(_nullishCoalesce(_nullishCoalesce(_optionalChain([params, 'access', _15 => _15.sheet, 'optionalAccess', _16 => _16.id]), () => ( params.subUnitId)), () => ( ""))) : !!op && op.op.kind === "insert" && r.axis === op.axis && r.sheetId === sheetId && r.start === op.op.at && r.count === op.op.count;
|
|
177
255
|
let at = -1;
|
|
178
256
|
for (let i = list.length - 1; i >= 0 && at < 0; i--) if (matches(list[i])) at = i;
|
|
179
257
|
if (at < 0) return;
|
|
@@ -183,18 +261,19 @@ function installSheetCommentAnchors(univer) {
|
|
|
183
261
|
const flushRestores = () => {
|
|
184
262
|
for (const r of toRestore.splice(0)) {
|
|
185
263
|
for (const a of r.anchors) {
|
|
186
|
-
if (_optionalChain([(_nullishCoalesce(byUnit.get(r.unitId), () => ( {}))), 'access',
|
|
264
|
+
if (_optionalChain([(_nullishCoalesce(byUnit.get(r.unitId), () => ( {}))), 'access', _17 => _17[r.sheetId], 'optionalAccess', _18 => _18[a.id]])) continue;
|
|
187
265
|
commands.syncExecuteCommand(SET_SHEET_COMMENT_ANCHOR, { unitId: r.unitId, subUnitId: r.sheetId, anchorId: a.id, cell: a.cell });
|
|
188
266
|
}
|
|
189
267
|
}
|
|
190
268
|
};
|
|
191
269
|
const disposables = [];
|
|
270
|
+
disposables.push(installSheetNoteFollower(injector, commands));
|
|
192
271
|
disposables.push(
|
|
193
272
|
commands.registerCommand({
|
|
194
273
|
id: SET_SHEET_COMMENT_ANCHOR,
|
|
195
274
|
type: _core.CommandType.MUTATION,
|
|
196
275
|
handler: (_accessor, params) => {
|
|
197
|
-
if (!_optionalChain([params, 'optionalAccess',
|
|
276
|
+
if (!_optionalChain([params, 'optionalAccess', _19 => _19.unitId]) || !params.subUnitId || !params.anchorId) return false;
|
|
198
277
|
apply(params.unitId, SET_SHEET_COMMENT_ANCHOR, params);
|
|
199
278
|
return true;
|
|
200
279
|
}
|
|
@@ -220,7 +299,7 @@ function installSheetCommentAnchors(univer) {
|
|
|
220
299
|
commands.onCommandExecuted((info) => {
|
|
221
300
|
if (info.type !== _core.CommandType.MUTATION || info.id === SET_SHEET_COMMENT_ANCHOR) return;
|
|
222
301
|
const p = info.params;
|
|
223
|
-
const unitId = typeof _optionalChain([p, 'optionalAccess',
|
|
302
|
+
const unitId = typeof _optionalChain([p, 'optionalAccess', _20 => _20.unitId]) === "string" ? p.unitId : null;
|
|
224
303
|
if (!unitId || !byUnit.has(unitId)) return;
|
|
225
304
|
apply(unitId, info.id, p);
|
|
226
305
|
if (undoing > 0) restoreAfterUndo(unitId, info.id, p);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/sheets-comment-anchors.cjs","../src/sheets/comment-anchors.ts","../src/collab/sheets/transform/axis.ts","../src/collab/sheets/comment-anchor-transform.ts"],"names":["next"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/sheets-comment-anchors.cjs","sourcesContent":[null,"/**\n * Cell comment anchors in a workbook (COLLAB-DESIGN §8, unit C9).\n *\n * The anchor is a workbook resource (`SHEET_MATRX_COMMENT_ANCHORS_PLUGIN`): per sheet, anchor id → cell. It is\n * changed only by `matrx.mutation.set-comment-anchor`, which travels in the ordered room like every covered\n * mutation (its transform is `collab/sheets/comment-anchor-transform.ts`), and it follows every row/column\n * insert, remove and move this seat applies — its own, a peer's, an undo — through `moveSheetAnchorCell`, the\n * same function the transform uses, so every seat holds the same cells. A removed row, column or sheet takes the\n * anchor out: the thread in `platform.comments` (part_anchor `anchor:<id>`) is then orphaned, never dropped.\n *\n * `installSheetCommentAnchors(univer)` must run before the workbook unit is created (the resource loads with it).\n */\nimport { CommandType, ICommandService, IResourceManagerService, UndoCommandId, UniverInstanceType } from \"@univerjs/core\";\n\nimport { SET_SHEET_COMMENT_ANCHOR, moveSheetAnchorCell, structuralAxisOp, type SheetAnchorCell } from \"../collab/sheets/comment-anchor-transform\";\n\nexport { SET_SHEET_COMMENT_ANCHOR, sheetCellName, type SheetAnchorCell } from \"../collab/sheets/comment-anchor-transform\";\n\nexport const SHEET_COMMENT_ANCHORS_RESOURCE = \"SHEET_MATRX_COMMENT_ANCHORS_PLUGIN\" as const;\n\n/** Per sheet id: anchor id → cell. */\nexport type SheetAnchors = Record<string, Record<string, SheetAnchorCell>>;\n\nexport interface SetSheetCommentAnchorParams {\n unitId: string;\n subUnitId: string;\n anchorId: string;\n /** null takes the anchor out. */\n cell: SheetAnchorCell | null;\n}\n\n/** Apply one mutation to a workbook's anchors (pure; returns the same object when nothing changed). */\nexport function applyToSheetAnchors(anchors: SheetAnchors, id: string, params: Readonly<Record<string, unknown>>): SheetAnchors {\n if (id === SET_SHEET_COMMENT_ANCHOR) {\n const p = params as unknown as SetSheetCommentAnchorParams;\n const sheet = { ...(anchors[p.subUnitId] ?? {}) };\n if (p.cell) sheet[p.anchorId] = { row: p.cell.row, col: p.cell.col };\n else if (sheet[p.anchorId]) delete sheet[p.anchorId];\n else return anchors;\n const next = { ...anchors, [p.subUnitId]: sheet };\n if (Object.keys(sheet).length === 0) delete next[p.subUnitId];\n return next;\n }\n const sheetId = String(params.subUnitId ?? \"\");\n const sheet = anchors[sheetId];\n if (!sheet) return anchors;\n let changed = false;\n const moved: Record<string, SheetAnchorCell> = {};\n for (const [anchorId, cell] of Object.entries(sheet)) {\n const to = moveSheetAnchorCell(cell, sheetId, id, params);\n if (to !== cell) changed = true;\n if (to) moved[anchorId] = to;\n }\n if (!changed) return anchors;\n const next = { ...anchors, [sheetId]: moved };\n if (Object.keys(moved).length === 0) delete next[sheetId];\n return next;\n}\n\n/** The resource's stored form: keys sorted so every seat writes the same bytes (the state hash reads them). */\nexport function sheetAnchorsToJson(anchors: SheetAnchors | undefined): string {\n if (!anchors) return \"\";\n const sheets = Object.keys(anchors).filter((s) => Object.keys(anchors[s] ?? {}).length > 0).sort();\n if (sheets.length === 0) return \"\";\n const out: Record<string, Record<string, SheetAnchorCell>> = {};\n for (const s of sheets) {\n const ids = Object.keys(anchors[s]!).sort();\n out[s] = Object.fromEntries(ids.map((id) => [id, { row: anchors[s]![id]!.row, col: anchors[s]![id]!.col }]));\n }\n return JSON.stringify(out);\n}\n\nexport function sheetAnchorsFromJson(bytes: string | null | undefined): SheetAnchors {\n if (!bytes) return {};\n try {\n const v = JSON.parse(bytes) as unknown;\n if (!v || typeof v !== \"object\") return {};\n const out: SheetAnchors = {};\n for (const [sheet, ids] of Object.entries(v as Record<string, unknown>)) {\n if (!ids || typeof ids !== \"object\") continue;\n for (const [id, c] of Object.entries(ids as Record<string, unknown>)) {\n const cell = c as SheetAnchorCell;\n if (typeof cell?.row === \"number\" && typeof cell?.col === \"number\") (out[sheet] ??= {})[id] = { row: cell.row, col: cell.col };\n }\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/**\n * What a removal took out, kept on the seat that may undo it (Univer's undo stack is per seat). Undoing a row, column or\n * sheet delete re-creates the cells; the anchors that were inside come back with them (the thread is no longer orphaned).\n */\ninterface RemovedAnchors {\n sheetId: string;\n /** null: the whole sheet was removed. */\n axis: \"row\" | \"col\" | null;\n start: number;\n count: number;\n anchors: Array<{ id: string; cell: SheetAnchorCell }>;\n}\nconst REMOVED_KEEP = 25;\n\n/** Anchors in `before` that `after` no longer has, on the sheet a removal touched. */\nfunction removedBy(before: SheetAnchors, after: SheetAnchors, id: string, params: Readonly<Record<string, unknown>>): RemovedAnchors | null {\n const sheetId = String(params.subUnitId ?? \"\");\n const gone = Object.entries(before[sheetId] ?? {})\n .filter(([anchorId]) => !after[sheetId]?.[anchorId])\n .map(([anchorId, cell]) => ({ id: anchorId, cell }));\n if (!gone.length) return null;\n if (id === \"sheet.mutation.remove-sheet\") return { sheetId, axis: null, start: 0, count: 0, anchors: gone };\n const op = structuralAxisOp(id, params);\n if (!op || op.op.kind !== \"remove\") return null;\n return { sheetId, axis: op.axis, start: op.op.start, count: op.op.count, anchors: gone };\n}\n\nexport interface SheetCommentAnchorsHandle {\n /** The anchors of one workbook now. */\n get(unitId: string): SheetAnchors;\n /** Anchor a cell (a new thread) — or take an anchor out with `cell: null` — through the command service. */\n set(params: SetSheetCommentAnchorParams): boolean;\n /** Called after any change to a workbook's anchors (set, moved, removed, loaded). */\n onChange(listener: (unitId: string) => void): () => void;\n dispose(): void;\n}\n\ntype Injector = { get<T>(token: unknown): T };\nconst INSTALLED = Symbol.for(\"ai-matrx.office.sheet-comment-anchors\");\n\n/** Register the anchor mutation, the resource and the structural listener on one Univer (idempotent). */\nexport function installSheetCommentAnchors(univer: unknown): SheetCommentAnchorsHandle {\n const holder = univer as { [INSTALLED]?: SheetCommentAnchorsHandle; __getInjector?: () => Injector };\n if (holder[INSTALLED]) return holder[INSTALLED]!;\n const injector = holder.__getInjector?.();\n if (!injector) throw new Error(\"installSheetCommentAnchors: this Univer exposes no injector\");\n const commands = injector.get<ICommandService>(ICommandService);\n const resources = injector.get<{ registerPluginResource: (hook: unknown) => { dispose(): void } }>(IResourceManagerService);\n\n const byUnit = new Map<string, SheetAnchors>();\n const listeners = new Set<(unitId: string) => void>();\n const changed = (unitId: string) => {\n for (const l of Array.from(listeners)) {\n try {\n l(unitId);\n } catch {\n /* a listener never stops the editor */\n }\n }\n };\n const removed = new Map<string, RemovedAnchors[]>();\n let undoing = 0;\n const apply = (unitId: string, id: string, params: Record<string, unknown>) => {\n const before = byUnit.get(unitId) ?? {};\n const after = applyToSheetAnchors(before, id, params);\n if (after === before) return;\n byUnit.set(unitId, after);\n const gone = removedBy(before, after, id, params);\n if (gone) removed.set(unitId, [...(removed.get(unitId) ?? []), gone].slice(-REMOVED_KEEP));\n changed(unitId);\n };\n /**\n * An undo re-created cells a removal had taken anchors out of: the anchors to put back once the undo is done. They are\n * set AFTER the undo's own mutations (never inside the insert-row's callback): a seat's room log must read\n * `insert-row, then set-anchor`; the other order replays as \"set the anchor at row 4, then insert a row at 4\", which\n * pushes the anchor one row down on every seat that rebuilds from the log (live: B5 came back as B6).\n */\n const toRestore: Array<{ unitId: string; sheetId: string; anchors: Array<{ id: string; cell: SheetAnchorCell }> }> = [];\n const restoreAfterUndo = (unitId: string, id: string, params: Record<string, unknown>) => {\n const list = removed.get(unitId);\n if (!list?.length) return;\n const sheetId = String(params.subUnitId ?? \"\");\n const op = id === \"sheet.mutation.insert-sheet\" ? null : structuralAxisOp(id, params);\n const wholeSheet = id === \"sheet.mutation.insert-sheet\";\n const matches = (r: RemovedAnchors) =>\n wholeSheet\n ? r.axis === null && r.sheetId === String((params.sheet as { id?: string } | undefined)?.id ?? params.subUnitId ?? \"\")\n : !!op && op.op.kind === \"insert\" && r.axis === op.axis && r.sheetId === sheetId && r.start === op.op.at && r.count === op.op.count;\n let at = -1;\n for (let i = list.length - 1; i >= 0 && at < 0; i--) if (matches(list[i]!)) at = i;\n if (at < 0) return;\n const [hit] = list.splice(at, 1);\n toRestore.push({ unitId, sheetId: hit!.sheetId, anchors: hit!.anchors });\n };\n const flushRestores = () => {\n for (const r of toRestore.splice(0)) {\n for (const a of r.anchors) {\n if ((byUnit.get(r.unitId) ?? {})[r.sheetId]?.[a.id]) continue;\n commands.syncExecuteCommand(SET_SHEET_COMMENT_ANCHOR, { unitId: r.unitId, subUnitId: r.sheetId, anchorId: a.id, cell: a.cell });\n }\n }\n };\n\n const disposables: Array<{ dispose(): void }> = [];\n disposables.push(\n commands.registerCommand({\n id: SET_SHEET_COMMENT_ANCHOR,\n type: CommandType.MUTATION,\n handler: (_accessor: unknown, params?: SetSheetCommentAnchorParams) => {\n if (!params?.unitId || !params.subUnitId || !params.anchorId) return false;\n apply(params.unitId, SET_SHEET_COMMENT_ANCHOR, params as unknown as Record<string, unknown>);\n return true;\n },\n } as never),\n );\n disposables.push(\n resources.registerPluginResource({\n pluginName: SHEET_COMMENT_ANCHORS_RESOURCE,\n businesses: [UniverInstanceType.UNIVER_SHEET],\n toJson: (unitId: string) => sheetAnchorsToJson(byUnit.get(unitId)),\n parseJson: (bytes: string) => sheetAnchorsFromJson(bytes),\n onLoad: (unitId: string, value: SheetAnchors) => {\n byUnit.set(unitId, value ?? {});\n changed(unitId);\n },\n onUnLoad: (unitId: string) => {\n byUnit.delete(unitId);\n changed(unitId);\n },\n }),\n );\n disposables.push(\n commands.onCommandExecuted((info: { id: string; type?: CommandType; params?: unknown }) => {\n if (info.type !== CommandType.MUTATION || info.id === SET_SHEET_COMMENT_ANCHOR) return;\n const p = info.params as Record<string, unknown> | undefined;\n const unitId = typeof p?.unitId === \"string\" ? p.unitId : null;\n if (!unitId || !byUnit.has(unitId)) return;\n apply(unitId, info.id, p!);\n if (undoing > 0) restoreAfterUndo(unitId, info.id, p!);\n }),\n );\n disposables.push(\n commands.beforeCommandExecuted((info: { id: string }) => {\n if (info.id === UndoCommandId) undoing++;\n }),\n );\n disposables.push(\n commands.onCommandExecuted((info: { id: string }) => {\n if (info.id === UndoCommandId && undoing > 0) {\n undoing--;\n if (undoing === 0) flushRestores();\n }\n }),\n );\n\n const handle: SheetCommentAnchorsHandle = {\n get: (unitId) => byUnit.get(unitId) ?? {},\n set: (params) => commands.syncExecuteCommand(SET_SHEET_COMMENT_ANCHOR, params) === true,\n onChange: (l) => {\n listeners.add(l);\n return () => listeners.delete(l);\n },\n dispose: () => {\n for (const d of disposables.splice(0)) d.dispose();\n listeners.clear();\n delete holder[INSTALLED];\n },\n };\n holder[INSTALLED] = handle;\n return handle;\n}\n","/**\n * One-dimensional structural algebra for rows and columns: insert, remove and move, the index map each\n * induces, and S-over-S transforms. Index arithmetic is ours (not Univer's ref-range helpers) because ties\n * need the side rule and overlapping removes must subtract, which no free helper does.\n *\n * Semantics pinned to Univer 1.0.4:\n * - insert(at, n): new rows occupy [at, at+n); old row i >= at moves to i+n (the row AT `at` moves down).\n * - remove(start, n): rows [start, start+n) go; i >= start+n moves to i-n.\n * - move(from, n, to): `moveMatrixArray` — rows [from, from+n) are placed before old row `to`. Forward\n * (to >= from+n): block lands at [to-n, to). Backward (to < from): block lands at [to, to+n). A `to` in\n * [from, from+n] moves nothing (Univer throws for [from, from+n)); we treat it as a no-op.\n */\nimport type { Side } from \"./types\";\n\nexport type Axis = \"row\" | \"col\";\n\nexport type AxisOp =\n | { kind: \"insert\"; at: number; count: number }\n | { kind: \"remove\"; start: number; count: number }\n | { kind: \"move\"; from: number; count: number; to: number };\n\nexport function isNoopMove(op: { from: number; count: number; to: number }): boolean {\n return op.count <= 0 || (op.to >= op.from && op.to <= op.from + op.count);\n}\n\n/** Where the moved block starts after the move. */\nexport function movedBlockStart(op: { from: number; count: number; to: number }): number {\n return op.to > op.from ? op.to - op.count : op.to;\n}\n\n/** Old index → new index, or null when the index is removed. */\nexport function mapIndex(op: AxisOp, i: number): number | null {\n switch (op.kind) {\n case \"insert\":\n return i >= op.at ? i + op.count : i;\n case \"remove\":\n if (i < op.start) return i;\n if (i < op.start + op.count) return null;\n return i - op.count;\n case \"move\": {\n if (isNoopMove(op)) return i;\n const { from, count, to } = op;\n if (i >= from && i < from + count) return movedBlockStart(op) + (i - from);\n if (to > from) return i >= from + count && i < to ? i - count : i;\n return i >= to && i < from ? i + count : i;\n }\n }\n}\n\n/** Contiguous runs of a sorted, de-duplicated index list. */\nexport function runs(indices: Iterable<number>): Array<{ start: number; count: number }> {\n const sorted = [...new Set(indices)].sort((a, b) => a - b);\n const out: Array<{ start: number; count: number }> = [];\n for (const i of sorted) {\n const last = out[out.length - 1];\n if (last && last.start + last.count === i) last.count++;\n else out.push({ start: i, count: 1 });\n }\n return out;\n}\n\n/**\n * A gap (insert position: \"before old row p\") through a move. Gaps strictly inside the block ride with\n * it; any other gap is a gap of the non-block rows. At the move's own target gap an insert lands BEFORE\n * the block (side-independent; `transformAxis(move, insert)` keeps the same order).\n */\nfunction mapGapThroughMove(p: number, mv: { from: number; count: number; to: number }): number {\n const { from, count, to } = mv;\n if (p > from && p < from + count) return movedBlockStart(mv) + (p - from);\n const g = p <= from ? p : p - count; // gap index among the non-block rows\n const tg = to <= from ? to : to - count;\n return g <= tg ? g : g + count;\n}\n\n/**\n * x over y on ONE axis. Returns the list of axis ops that apply after y (empty = x vanished), or null when\n * the pair has no transform (the caller turns that into a barrier).\n */\nexport function transformAxis(x: AxisOp, y: AxisOp, side: Side): AxisOp[] | null {\n if (y.kind === \"move\" && isNoopMove(y)) return [x];\n if (x.kind === \"move\" && isNoopMove(x)) return [];\n switch (x.kind) {\n case \"insert\": {\n if (y.kind === \"insert\") {\n if (x.at < y.at || (x.at === y.at && side === \"left\")) return [x];\n return [{ ...x, at: x.at + y.count }];\n }\n if (y.kind === \"remove\") {\n if (x.at <= y.start) return [x];\n if (x.at >= y.start + y.count) return [{ ...x, at: x.at - y.count }];\n return [{ ...x, at: y.start }]; // an insert inside a removed span survives at its start\n }\n return [{ ...x, at: mapGapThroughMove(x.at, y) }];\n }\n case \"remove\": {\n const kept: number[] = [];\n for (let i = x.start; i < x.start + x.count; i++) {\n const m = mapIndex(y, i);\n if (m !== null) kept.push(m);\n }\n if (y.kind === \"insert\" || y.kind === \"remove\" || y.kind === \"move\") {\n // Removes are emitted highest first so each index is still valid when it applies.\n return runs(kept)\n .reverse()\n .map((r) => ({ kind: \"remove\" as const, start: r.start, count: r.count }));\n }\n return null;\n }\n case \"move\": {\n if (y.kind === \"insert\") {\n const inside = y.at > x.from && y.at < x.from + x.count;\n const from = inside || y.at > x.from ? x.from : x.from + y.count;\n const count = inside ? x.count + y.count : x.count;\n const to = x.to >= y.at ? x.to + y.count : x.to;\n const out = { kind: \"move\" as const, from, count, to };\n return isNoopMove(out) ? [] : [out];\n }\n if (y.kind === \"remove\") {\n const kept: number[] = [];\n for (let i = x.from; i < x.from + x.count; i++) {\n const m = mapIndex(y, i);\n if (m !== null) kept.push(m);\n }\n if (kept.length === 0) return [];\n const r = runs(kept);\n if (r.length !== 1) return null;\n const to = x.to <= y.start ? x.to : x.to >= y.start + y.count ? x.to - y.count : y.start;\n const out = { kind: \"move\" as const, from: r[0]!.start, count: r[0]!.count, to };\n return isNoopMove(out) ? [] : [out];\n }\n // move over move. The same rows moved twice: the later move wins, the earlier one is dropped.\n // Blocks that only partly overlap have no transform yet.\n const overlap = Math.min(x.from + x.count, y.from + y.count) - Math.max(x.from, y.from);\n if (overlap > 0) {\n if (x.from !== y.from || x.count !== y.count) return null;\n if (side === \"right\") return [];\n const out = { kind: \"move\" as const, from: movedBlockStart(y), count: x.count, to: mapGapThroughMove(x.to, y) };\n return isNoopMove(out) ? [] : [out];\n }\n // Disjoint blocks: x's rows through y must stay contiguous; the target gap goes through y.\n const mapped: number[] = [];\n for (let i = x.from; i < x.from + x.count; i++) mapped.push(mapIndex(y, i)!);\n const r = runs(mapped);\n if (r.length !== 1) return null;\n let to: number;\n if (x.to > y.from && x.to < y.from + y.count) to = movedBlockStart(y) + (x.to - y.from);\n else to = mapGapThroughMove(x.to, y) + (x.to === y.to && side === \"right\" ? y.count : 0); // same target: later block first\n const out = { kind: \"move\" as const, from: r[0]!.start, count: r[0]!.count, to };\n if (to > out.from && to < out.from + out.count) return null;\n return isNoopMove(out) ? [] : [out];\n }\n }\n}\n","/**\n * The sheet comment-anchor transform (COLLAB-DESIGN §8 + §10 class R, unit C9). Its own file; the sheet\n * transformer calls it first for every pair (`transformMutation`) and the registry declares its mutation id.\n *\n * A cell comment is anchored by `matrx.mutation.set-comment-anchor` `{ subUnitId, anchorId, cell: {row, col} | null }`\n * (null takes the anchor out). The anchor is a ranged resource keyed by id:\n * - anchor over a structural op on its sheet: the cell moves by the same index arithmetic as cells (`mapIndex`);\n * a cell whose row or column was removed DROPS the anchor (its author is told) and the thread is orphaned;\n * - anchor over the removal of its sheet: dropped the same way;\n * - anchor over the same anchor id: the later commit wins (§6.4), the earlier side is told;\n * - anything else over an anchor, and an anchor over anything else: unchanged (anchors never move content).\n * Every seat moves its stored anchors through each structural op it applies with `moveSheetAnchorCell` — the\n * same function — so a seat that set an anchor before a peer's row insert and a seat that received the anchor\n * after it end with the same cell.\n *\n * Univer's cell notes (`@univerjs/sheets-note`, C5f) are the same kind of record — keyed by note id, anchored to one\n * cell, never moving content — and transform here too (`transformCellNote`, called first). Their params name the\n * sheet `sheetId`. Facets: a full write (`update-note`: content and cell), a move (`update-note-position`: the cell)\n * and a remove (`remove-note`).\n * - a note over a structural op on its sheet: its cell moves (`moveSheetAnchorCell`); a write whose cell was\n * removed is dropped (the note went with its row), a move whose target was removed becomes a remove;\n * - a structural op over a note: a raw structural mutation runs no plugin ref-range handler synchronously (each\n * seat's watcher moves notes a microtask later), so the op carries the fixup the note is owed — its move, or\n * its remove — and every seat (the fold included) holds the same cells the moment it applies the op;\n * - the same note: a remove wins; a full write over a move takes the move's target cell; two structural ops'\n * fixups both stand (each target went through the other op); otherwise a person's write or move beats a\n * structural op's derived one, else the later commit wins;\n * - two different notes written to one cell at once, or a note change addressed by cell instead of id against\n * another note change on its sheet: barrier (the resource keeps one note per cell, by insertion order).\n */\nimport { mapIndex, type AxisOp } from \"./transform/axis\";\nimport type { SheetMutation, Side, TransformResult } from \"./transform/types\";\n\nexport const SET_SHEET_COMMENT_ANCHOR = \"matrx.mutation.set-comment-anchor\";\n\n/** Mutation ids this package defines itself (never emitted by Univer; the census lists them apart). */\nexport const OWN_SHEET_MUTATION_IDS: readonly string[] = [SET_SHEET_COMMENT_ANCHOR];\n\n/**\n * A sheet op that only sets or clears comment anchors: every mutation is `matrx.mutation.set-comment-anchor`. A seat\n * that may comment but not edit sends exactly these (the door `content.collab_append` checks the same rule).\n */\nexport function isSheetsAnchorOnlyOp(op: ReadonlyArray<SheetMutation> | null | undefined): boolean {\n return !!op && op.length > 0 && op.every((m) => m.id === SET_SHEET_COMMENT_ANCHOR);\n}\n\nexport interface SheetAnchorCell {\n row: number;\n col: number;\n}\n\nconst REMOVE_SHEET = \"sheet.mutation.remove-sheet\";\n\ntype R = { startRow: number; endRow: number; startColumn: number; endColumn: number };\n\n/** The row/column op a Univer structural mutation makes, from its params (the same params on every seat). */\nexport function structuralAxisOp(id: string, params: Readonly<Record<string, unknown>>): { axis: \"row\" | \"col\"; op: AxisOp } | null {\n const p = params as Record<string, R | undefined>;\n switch (id) {\n case \"sheet.mutation.insert-row\":\n return p.range ? { axis: \"row\", op: { kind: \"insert\", at: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;\n case \"sheet.mutation.remove-rows\":\n return p.range ? { axis: \"row\", op: { kind: \"remove\", start: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;\n case \"sheet.mutation.insert-col\":\n return p.range ? { axis: \"col\", op: { kind: \"insert\", at: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;\n case \"sheet.mutation.remove-col\":\n return p.range ? { axis: \"col\", op: { kind: \"remove\", start: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;\n case \"sheet.mutation.move-rows\":\n return p.sourceRange && p.targetRange\n ? { axis: \"row\", op: { kind: \"move\", from: p.sourceRange.startRow, count: p.sourceRange.endRow - p.sourceRange.startRow + 1, to: p.targetRange.startRow } }\n : null;\n case \"sheet.mutation.move-columns\":\n return p.sourceRange && p.targetRange\n ? {\n axis: \"col\",\n op: { kind: \"move\", from: p.sourceRange.startColumn, count: p.sourceRange.endColumn - p.sourceRange.startColumn + 1, to: p.targetRange.startColumn },\n }\n : null;\n default:\n return null;\n }\n}\n\n/**\n * Where an anchored cell is after one applied mutation on `sheet`: the same cell, a moved cell, or null when the\n * mutation removed it (its row, its column or its sheet).\n */\nexport function moveSheetAnchorCell(cell: SheetAnchorCell, sheet: string, id: string, params: Readonly<Record<string, unknown>>): SheetAnchorCell | null {\n if (String(params.subUnitId ?? \"\") !== sheet) return cell;\n if (id === REMOVE_SHEET) return null;\n const s = structuralAxisOp(id, params);\n if (!s) return cell;\n if (s.axis === \"row\") {\n const row = mapIndex(s.op, cell.row);\n return row === null ? null : row === cell.row ? cell : { row, col: cell.col };\n }\n const col = mapIndex(s.op, cell.col);\n return col === null ? null : col === cell.col ? cell : { row: cell.row, col };\n}\n\nfunction colName(c: number): string {\n let s = \"\";\n for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + ((n - 1) % 26)) + s;\n return s;\n}\n/** \"B5\" for row 4, column 1. */\nexport const sheetCellName = (cell: SheetAnchorCell) => `${colName(cell.col)}${cell.row + 1}`;\n\nfunction cellOf(m: SheetMutation): SheetAnchorCell | null {\n const c = m.params.cell as SheetAnchorCell | null | undefined;\n return c && typeof c.row === \"number\" && typeof c.col === \"number\" ? c : null;\n}\n\nconst ok = (op: SheetMutation[], lost: string[] = [], dropped: string[] = []): TransformResult => ({ op, lost, dropped });\nconst label = (m: SheetMutation) => {\n const c = cellOf(m);\n return `${String(m.params.subUnitId ?? \"\")}!${c ? sheetCellName(c) : \"?\"} comment`;\n};\n\n// ── Univer cell notes (C5f) ──────────────────────────────────────────────────────────────────────────────────\nexport const UPDATE_NOTE = \"sheet.mutation.update-note\";\nexport const REMOVE_NOTE = \"sheet.mutation.remove-note\";\nexport const UPDATE_NOTE_POSITION = \"sheet.mutation.update-note-position\";\nconst NOTE_IDS = new Set([UPDATE_NOTE, REMOVE_NOTE, UPDATE_NOTE_POSITION]);\nexport const isNoteMutation = (id: string): boolean => NOTE_IDS.has(id);\n\ntype NoteRole = \"write\" | \"move\" | \"remove\";\ninterface NoteRef {\n role: NoteRole;\n sheet: string;\n /** The note id, or null when the op addresses the note by its cell (an undo of a new note removes by cell). */\n id: string | null;\n /** Where the op leaves the note: a write's cell, a move's target; null for a remove. */\n cell: SheetAnchorCell | null;\n}\n\nconst cellAt = (row: unknown, col: unknown): SheetAnchorCell | null => (typeof row === \"number\" && typeof col === \"number\" ? { row, col } : null);\nfunction noteRef(m: SheetMutation): NoteRef {\n const p = m.params as Record<string, any>;\n const sheet = String(p.sheetId ?? \"\");\n if (m.id === UPDATE_NOTE) return { role: \"write\", sheet, id: p.note?.id ? String(p.note.id) : null, cell: cellAt(p.row, p.col) };\n if (m.id === UPDATE_NOTE_POSITION) return { role: \"move\", sheet, id: p.noteId ? String(p.noteId) : null, cell: cellAt(p.newPosition?.row, p.newPosition?.col) };\n return { role: \"remove\", sheet, id: p.noteId ? String(p.noteId) : null, cell: p.noteId ? null : cellAt(p.row, p.col) };\n}\nconst noteDerived = (m: SheetMutation) => m.params.derived === true;\nconst noteLabel = (n: NoteRef) => `${n.sheet}!${n.cell ? sheetCellName(n.cell) : \"?\"} note`;\nconst tellNote = (m: SheetMutation, n: NoteRef) => (noteDerived(m) ? [] : [noteLabel(n)]);\nconst noteBarrier = (reason: string): TransformResult => ({ barrier: true, reason: `note: ${reason}` });\nconst removeNoteOf = (sheet: string, id: string): SheetMutation => ({ id: REMOVE_NOTE, params: { sheetId: sheet, noteId: id, derived: true } });\nconst moveNoteTo = (sheet: string, id: string, cell: SheetAnchorCell): SheetMutation => ({\n id: UPDATE_NOTE_POSITION,\n params: { sheetId: sheet, noteId: id, newPosition: { row: cell.row, col: cell.col }, derived: true },\n});\n/** A mutation's sheet as `moveSheetAnchorCell` reads it (structural ops name `subUnitId`). */\nconst onSheet = (m: SheetMutation, sheet: string) => String(m.params.subUnitId ?? \"\") === sheet;\n\n/** x over y when either is a cell-note mutation; null when neither is. */\nexport function transformCellNote(x: SheetMutation, y: SheetMutation, side: Side): TransformResult | null {\n const xn = isNoteMutation(x.id);\n const yn = isNoteMutation(y.id);\n if (!xn && !yn) return null;\n if (!xn) {\n // x moves content (or not); y is a note. A structural x carries the fixup y's note is owed.\n const n = noteRef(y);\n if (n.role === \"remove\" || !n.cell || !onSheet(x, n.sheet)) return ok([x]);\n if (x.id === REMOVE_SHEET) return n.id ? ok([removeNoteOf(n.sheet, n.id), x]) : ok([x]);\n if (!structuralAxisOp(x.id, x.params)) return ok([x]);\n if (!n.id) return noteBarrier(\"a note without an id against a row or column change\");\n const moved = moveSheetAnchorCell(n.cell, n.sheet, x.id, x.params);\n if (!moved) return ok([x, removeNoteOf(n.sheet, n.id)]);\n return moved === n.cell ? ok([x]) : ok([x, moveNoteTo(n.sheet, n.id, moved)]);\n }\n const n = noteRef(x);\n if (!yn) {\n if (!onSheet(y, n.sheet)) return ok([x]);\n if (y.id === REMOVE_SHEET) return ok([], [], tellNote(x, n));\n if (!structuralAxisOp(y.id, y.params)) return ok([x]); // a note never conflicts with content\n if (n.role === \"write\" && !n.id) return noteBarrier(\"a note without an id against a row or column change\");\n const p = x.params as Record<string, any>;\n const from = cellAt(p.row, p.col);\n const fromMoved = from ? moveSheetAnchorCell(from, n.sheet, y.id, y.params) : null;\n if (n.role === \"remove\") {\n if (n.id) return ok([x]);\n return fromMoved ? ok([{ id: x.id, params: { ...p, row: fromMoved.row, col: fromMoved.col } }]) : ok([]);\n }\n const moved = n.cell ? moveSheetAnchorCell(n.cell, n.sheet, y.id, y.params) : null;\n if (n.role === \"write\") {\n if (!moved) return ok([], [], tellNote(x, n));\n return ok([moved === n.cell ? x : { id: x.id, params: { ...p, row: moved.row, col: moved.col } }]);\n }\n // a move: its target moves; a target that was removed takes the note with it\n if (!moved) return n.id ? ok([removeNoteOf(n.sheet, n.id)], [], tellNote(x, n)) : ok([], [], tellNote(x, n));\n const params: Record<string, any> = { ...p, newPosition: { ...p.newPosition, row: moved.row, col: moved.col } };\n if (fromMoved) Object.assign(params, { row: fromMoved.row, col: fromMoved.col });\n return ok([{ id: x.id, params }]);\n }\n // both are notes\n const m = noteRef(y);\n if (n.sheet !== m.sheet) return ok([x]);\n if (!n.id || !m.id) return noteBarrier(\"a note change addressed by cell against another note change on its sheet\");\n if (n.id !== m.id) {\n const sameCell = n.cell && m.cell && n.cell.row === m.cell.row && n.cell.col === m.cell.col;\n return sameCell ? noteBarrier(\"two notes written to one cell at once\") : ok([x]);\n }\n if (n.role === \"remove\") return ok([x]);\n if (m.role === \"remove\") return ok([], [], tellNote(x, n));\n if (n.role === \"write\" && m.role === \"move\") {\n // a full write over a move takes the move's target cell\n return m.cell ? ok([{ id: x.id, params: { ...x.params, row: m.cell.row, col: m.cell.col } }]) : ok([x]);\n }\n if (n.role === \"move\" && m.role === \"write\") return ok([x]);\n const dx = noteDerived(x);\n const dy = noteDerived(y);\n // two structural ops' fixups: each target already went through the other op's rows and columns\n if (dx && dy) return ok([x]);\n const xWins = dx !== dy ? !dx : side === \"left\";\n return xWins ? ok([x], tellNote(y, m)) : ok([], [], tellNote(x, n));\n}\n\n/** x over y when either is an anchor mutation; null when neither is (the sheet transformer carries on). */\nexport function transformCommentAnchor(x: SheetMutation, y: SheetMutation, side: Side): TransformResult | null {\n const note = transformCellNote(x, y, side); // cell notes are the same kind of record (C5f)\n if (note) return note;\n const xa = x.id === SET_SHEET_COMMENT_ANCHOR;\n const ya = y.id === SET_SHEET_COMMENT_ANCHOR;\n if (!xa && !ya) return null;\n if (!xa) return ok([x]); // an anchor never moves content\n if (ya) {\n // anchors are keyed by sheet and id: the same id on another sheet is another anchor\n if (x.params.anchorId !== y.params.anchorId || x.params.subUnitId !== y.params.subUnitId) return ok([x]);\n return side === \"left\" ? ok([x], [label(y)]) : ok([], [], [label(x)]);\n }\n const cell = cellOf(x);\n if (!cell) return ok([x]); // taking an anchor out commutes with every move\n const moved = moveSheetAnchorCell(cell, String(x.params.subUnitId ?? \"\"), y.id, y.params);\n if (!moved) return ok([], [], [label(x)]);\n return ok([moved === cell ? x : { id: x.id, params: { ...x.params, cell: moved } }]);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/sheets-comment-anchors.cjs","../src/sheets/comment-anchors.ts","../src/collab/sheets/transform/axis.ts","../src/collab/sheets/comment-anchor-transform.ts","../src/sheets/note-follower.ts"],"names":["next","CommandType"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/sheets-comment-anchors.cjs","sourcesContent":[null,"/**\n * Cell comment anchors in a workbook (COLLAB-DESIGN §8, unit C9).\n *\n * The anchor is a workbook resource (`SHEET_MATRX_COMMENT_ANCHORS_PLUGIN`): per sheet, anchor id → cell. It is\n * changed only by `matrx.mutation.set-comment-anchor`, which travels in the ordered room like every covered\n * mutation (its transform is `collab/sheets/comment-anchor-transform.ts`), and it follows every row/column\n * insert, remove and move this seat applies — its own, a peer's, an undo — through `moveSheetAnchorCell`, the\n * same function the transform uses, so every seat holds the same cells. A removed row, column or sheet takes the\n * anchor out: the thread in `platform.comments` (part_anchor `anchor:<id>`) is then orphaned, never dropped.\n *\n * `installSheetCommentAnchors(univer)` must run before the workbook unit is created (the resource loads with it).\n */\nimport { CommandType, ICommandService, IResourceManagerService, UndoCommandId, UniverInstanceType } from \"@univerjs/core\";\n\nimport { SET_SHEET_COMMENT_ANCHOR, moveSheetAnchorCell, structuralAxisOp, type SheetAnchorCell } from \"../collab/sheets/comment-anchor-transform\";\nimport { installSheetNoteFollower } from \"./note-follower\";\n\nexport { SET_SHEET_COMMENT_ANCHOR, sheetCellName, type SheetAnchorCell } from \"../collab/sheets/comment-anchor-transform\";\n\nexport const SHEET_COMMENT_ANCHORS_RESOURCE = \"SHEET_MATRX_COMMENT_ANCHORS_PLUGIN\" as const;\n\n/** Per sheet id: anchor id → cell. */\nexport type SheetAnchors = Record<string, Record<string, SheetAnchorCell>>;\n\nexport interface SetSheetCommentAnchorParams {\n unitId: string;\n subUnitId: string;\n anchorId: string;\n /** null takes the anchor out. */\n cell: SheetAnchorCell | null;\n}\n\n/** Apply one mutation to a workbook's anchors (pure; returns the same object when nothing changed). */\nexport function applyToSheetAnchors(anchors: SheetAnchors, id: string, params: Readonly<Record<string, unknown>>): SheetAnchors {\n if (id === SET_SHEET_COMMENT_ANCHOR) {\n const p = params as unknown as SetSheetCommentAnchorParams;\n const sheet = { ...(anchors[p.subUnitId] ?? {}) };\n if (p.cell) sheet[p.anchorId] = { row: p.cell.row, col: p.cell.col };\n else if (sheet[p.anchorId]) delete sheet[p.anchorId];\n else return anchors;\n const next = { ...anchors, [p.subUnitId]: sheet };\n if (Object.keys(sheet).length === 0) delete next[p.subUnitId];\n return next;\n }\n const sheetId = String(params.subUnitId ?? \"\");\n const sheet = anchors[sheetId];\n if (!sheet) return anchors;\n let changed = false;\n const moved: Record<string, SheetAnchorCell> = {};\n for (const [anchorId, cell] of Object.entries(sheet)) {\n const to = moveSheetAnchorCell(cell, sheetId, id, params);\n if (to !== cell) changed = true;\n if (to) moved[anchorId] = to;\n }\n if (!changed) return anchors;\n const next = { ...anchors, [sheetId]: moved };\n if (Object.keys(moved).length === 0) delete next[sheetId];\n return next;\n}\n\n/** The resource's stored form: keys sorted so every seat writes the same bytes (the state hash reads them). */\nexport function sheetAnchorsToJson(anchors: SheetAnchors | undefined): string {\n if (!anchors) return \"\";\n const sheets = Object.keys(anchors).filter((s) => Object.keys(anchors[s] ?? {}).length > 0).sort();\n if (sheets.length === 0) return \"\";\n const out: Record<string, Record<string, SheetAnchorCell>> = {};\n for (const s of sheets) {\n const ids = Object.keys(anchors[s]!).sort();\n out[s] = Object.fromEntries(ids.map((id) => [id, { row: anchors[s]![id]!.row, col: anchors[s]![id]!.col }]));\n }\n return JSON.stringify(out);\n}\n\nexport function sheetAnchorsFromJson(bytes: string | null | undefined): SheetAnchors {\n if (!bytes) return {};\n try {\n const v = JSON.parse(bytes) as unknown;\n if (!v || typeof v !== \"object\") return {};\n const out: SheetAnchors = {};\n for (const [sheet, ids] of Object.entries(v as Record<string, unknown>)) {\n if (!ids || typeof ids !== \"object\") continue;\n for (const [id, c] of Object.entries(ids as Record<string, unknown>)) {\n const cell = c as SheetAnchorCell;\n if (typeof cell?.row === \"number\" && typeof cell?.col === \"number\") (out[sheet] ??= {})[id] = { row: cell.row, col: cell.col };\n }\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/**\n * What a removal took out, kept on the seat that may undo it (Univer's undo stack is per seat). Undoing a row, column or\n * sheet delete re-creates the cells; the anchors that were inside come back with them (the thread is no longer orphaned).\n */\ninterface RemovedAnchors {\n sheetId: string;\n /** null: the whole sheet was removed. */\n axis: \"row\" | \"col\" | null;\n start: number;\n count: number;\n anchors: Array<{ id: string; cell: SheetAnchorCell }>;\n}\nconst REMOVED_KEEP = 25;\n\n/** Anchors in `before` that `after` no longer has, on the sheet a removal touched. */\nfunction removedBy(before: SheetAnchors, after: SheetAnchors, id: string, params: Readonly<Record<string, unknown>>): RemovedAnchors | null {\n const sheetId = String(params.subUnitId ?? \"\");\n const gone = Object.entries(before[sheetId] ?? {})\n .filter(([anchorId]) => !after[sheetId]?.[anchorId])\n .map(([anchorId, cell]) => ({ id: anchorId, cell }));\n if (!gone.length) return null;\n if (id === \"sheet.mutation.remove-sheet\") return { sheetId, axis: null, start: 0, count: 0, anchors: gone };\n const op = structuralAxisOp(id, params);\n if (!op || op.op.kind !== \"remove\") return null;\n return { sheetId, axis: op.axis, start: op.op.start, count: op.op.count, anchors: gone };\n}\n\nexport interface SheetCommentAnchorsHandle {\n /** The anchors of one workbook now. */\n get(unitId: string): SheetAnchors;\n /** Anchor a cell (a new thread) — or take an anchor out with `cell: null` — through the command service. */\n set(params: SetSheetCommentAnchorParams): boolean;\n /** Called after any change to a workbook's anchors (set, moved, removed, loaded). */\n onChange(listener: (unitId: string) => void): () => void;\n dispose(): void;\n}\n\ntype Injector = { get<T>(token: unknown): T };\nconst INSTALLED = Symbol.for(\"ai-matrx.office.sheet-comment-anchors\");\n\n/** Register the anchor mutation, the resource and the structural listener on one Univer (idempotent). */\nexport function installSheetCommentAnchors(univer: unknown): SheetCommentAnchorsHandle {\n const holder = univer as { [INSTALLED]?: SheetCommentAnchorsHandle; __getInjector?: () => Injector };\n if (holder[INSTALLED]) return holder[INSTALLED]!;\n const injector = holder.__getInjector?.();\n if (!injector) throw new Error(\"installSheetCommentAnchors: this Univer exposes no injector\");\n const commands = injector.get<ICommandService>(ICommandService);\n const resources = injector.get<{ registerPluginResource: (hook: unknown) => { dispose(): void } }>(IResourceManagerService);\n\n const byUnit = new Map<string, SheetAnchors>();\n const listeners = new Set<(unitId: string) => void>();\n const changed = (unitId: string) => {\n for (const l of Array.from(listeners)) {\n try {\n l(unitId);\n } catch {\n /* a listener never stops the editor */\n }\n }\n };\n const removed = new Map<string, RemovedAnchors[]>();\n let undoing = 0;\n const apply = (unitId: string, id: string, params: Record<string, unknown>) => {\n const before = byUnit.get(unitId) ?? {};\n const after = applyToSheetAnchors(before, id, params);\n if (after === before) return;\n byUnit.set(unitId, after);\n const gone = removedBy(before, after, id, params);\n if (gone) removed.set(unitId, [...(removed.get(unitId) ?? []), gone].slice(-REMOVED_KEEP));\n changed(unitId);\n };\n /**\n * An undo re-created cells a removal had taken anchors out of: the anchors to put back once the undo is done. They are\n * set AFTER the undo's own mutations (never inside the insert-row's callback): a seat's room log must read\n * `insert-row, then set-anchor`; the other order replays as \"set the anchor at row 4, then insert a row at 4\", which\n * pushes the anchor one row down on every seat that rebuilds from the log (live: B5 came back as B6).\n */\n const toRestore: Array<{ unitId: string; sheetId: string; anchors: Array<{ id: string; cell: SheetAnchorCell }> }> = [];\n const restoreAfterUndo = (unitId: string, id: string, params: Record<string, unknown>) => {\n const list = removed.get(unitId);\n if (!list?.length) return;\n const sheetId = String(params.subUnitId ?? \"\");\n const op = id === \"sheet.mutation.insert-sheet\" ? null : structuralAxisOp(id, params);\n const wholeSheet = id === \"sheet.mutation.insert-sheet\";\n const matches = (r: RemovedAnchors) =>\n wholeSheet\n ? r.axis === null && r.sheetId === String((params.sheet as { id?: string } | undefined)?.id ?? params.subUnitId ?? \"\")\n : !!op && op.op.kind === \"insert\" && r.axis === op.axis && r.sheetId === sheetId && r.start === op.op.at && r.count === op.op.count;\n let at = -1;\n for (let i = list.length - 1; i >= 0 && at < 0; i--) if (matches(list[i]!)) at = i;\n if (at < 0) return;\n const [hit] = list.splice(at, 1);\n toRestore.push({ unitId, sheetId: hit!.sheetId, anchors: hit!.anchors });\n };\n const flushRestores = () => {\n for (const r of toRestore.splice(0)) {\n for (const a of r.anchors) {\n if ((byUnit.get(r.unitId) ?? {})[r.sheetId]?.[a.id]) continue;\n commands.syncExecuteCommand(SET_SHEET_COMMENT_ANCHOR, { unitId: r.unitId, subUnitId: r.sheetId, anchorId: a.id, cell: a.cell });\n }\n }\n };\n\n const disposables: Array<{ dispose(): void }> = [];\n // Univer's cell notes are the same kind of record: they follow every structural op through the same function (C5g)\n disposables.push(installSheetNoteFollower(injector, commands as never));\n disposables.push(\n commands.registerCommand({\n id: SET_SHEET_COMMENT_ANCHOR,\n type: CommandType.MUTATION,\n handler: (_accessor: unknown, params?: SetSheetCommentAnchorParams) => {\n if (!params?.unitId || !params.subUnitId || !params.anchorId) return false;\n apply(params.unitId, SET_SHEET_COMMENT_ANCHOR, params as unknown as Record<string, unknown>);\n return true;\n },\n } as never),\n );\n disposables.push(\n resources.registerPluginResource({\n pluginName: SHEET_COMMENT_ANCHORS_RESOURCE,\n businesses: [UniverInstanceType.UNIVER_SHEET],\n toJson: (unitId: string) => sheetAnchorsToJson(byUnit.get(unitId)),\n parseJson: (bytes: string) => sheetAnchorsFromJson(bytes),\n onLoad: (unitId: string, value: SheetAnchors) => {\n byUnit.set(unitId, value ?? {});\n changed(unitId);\n },\n onUnLoad: (unitId: string) => {\n byUnit.delete(unitId);\n changed(unitId);\n },\n }),\n );\n disposables.push(\n commands.onCommandExecuted((info: { id: string; type?: CommandType; params?: unknown }) => {\n if (info.type !== CommandType.MUTATION || info.id === SET_SHEET_COMMENT_ANCHOR) return;\n const p = info.params as Record<string, unknown> | undefined;\n const unitId = typeof p?.unitId === \"string\" ? p.unitId : null;\n if (!unitId || !byUnit.has(unitId)) return;\n apply(unitId, info.id, p!);\n if (undoing > 0) restoreAfterUndo(unitId, info.id, p!);\n }),\n );\n disposables.push(\n commands.beforeCommandExecuted((info: { id: string }) => {\n if (info.id === UndoCommandId) undoing++;\n }),\n );\n disposables.push(\n commands.onCommandExecuted((info: { id: string }) => {\n if (info.id === UndoCommandId && undoing > 0) {\n undoing--;\n if (undoing === 0) flushRestores();\n }\n }),\n );\n\n const handle: SheetCommentAnchorsHandle = {\n get: (unitId) => byUnit.get(unitId) ?? {},\n set: (params) => commands.syncExecuteCommand(SET_SHEET_COMMENT_ANCHOR, params) === true,\n onChange: (l) => {\n listeners.add(l);\n return () => listeners.delete(l);\n },\n dispose: () => {\n for (const d of disposables.splice(0)) d.dispose();\n listeners.clear();\n delete holder[INSTALLED];\n },\n };\n holder[INSTALLED] = handle;\n return handle;\n}\n","/**\n * One-dimensional structural algebra for rows and columns: insert, remove and move, the index map each\n * induces, and S-over-S transforms. Index arithmetic is ours (not Univer's ref-range helpers) because ties\n * need the side rule and overlapping removes must subtract, which no free helper does.\n *\n * Semantics pinned to Univer 1.0.4:\n * - insert(at, n): new rows occupy [at, at+n); old row i >= at moves to i+n (the row AT `at` moves down).\n * - remove(start, n): rows [start, start+n) go; i >= start+n moves to i-n.\n * - move(from, n, to): `moveMatrixArray` — rows [from, from+n) are placed before old row `to`. Forward\n * (to >= from+n): block lands at [to-n, to). Backward (to < from): block lands at [to, to+n). A `to` in\n * [from, from+n] moves nothing (Univer throws for [from, from+n)); we treat it as a no-op.\n */\nimport type { Side } from \"./types\";\n\nexport type Axis = \"row\" | \"col\";\n\nexport type AxisOp =\n | { kind: \"insert\"; at: number; count: number }\n | { kind: \"remove\"; start: number; count: number }\n | { kind: \"move\"; from: number; count: number; to: number };\n\nexport function isNoopMove(op: { from: number; count: number; to: number }): boolean {\n return op.count <= 0 || (op.to >= op.from && op.to <= op.from + op.count);\n}\n\n/** Where the moved block starts after the move. */\nexport function movedBlockStart(op: { from: number; count: number; to: number }): number {\n return op.to > op.from ? op.to - op.count : op.to;\n}\n\n/** Old index → new index, or null when the index is removed. */\nexport function mapIndex(op: AxisOp, i: number): number | null {\n switch (op.kind) {\n case \"insert\":\n return i >= op.at ? i + op.count : i;\n case \"remove\":\n if (i < op.start) return i;\n if (i < op.start + op.count) return null;\n return i - op.count;\n case \"move\": {\n if (isNoopMove(op)) return i;\n const { from, count, to } = op;\n if (i >= from && i < from + count) return movedBlockStart(op) + (i - from);\n if (to > from) return i >= from + count && i < to ? i - count : i;\n return i >= to && i < from ? i + count : i;\n }\n }\n}\n\n/** Contiguous runs of a sorted, de-duplicated index list. */\nexport function runs(indices: Iterable<number>): Array<{ start: number; count: number }> {\n const sorted = [...new Set(indices)].sort((a, b) => a - b);\n const out: Array<{ start: number; count: number }> = [];\n for (const i of sorted) {\n const last = out[out.length - 1];\n if (last && last.start + last.count === i) last.count++;\n else out.push({ start: i, count: 1 });\n }\n return out;\n}\n\n/**\n * A gap (insert position: \"before old row p\") through a move. Gaps strictly inside the block ride with\n * it; any other gap is a gap of the non-block rows. At the move's own target gap an insert lands BEFORE\n * the block (side-independent; `transformAxis(move, insert)` keeps the same order).\n */\nfunction mapGapThroughMove(p: number, mv: { from: number; count: number; to: number }): number {\n const { from, count, to } = mv;\n if (p > from && p < from + count) return movedBlockStart(mv) + (p - from);\n const g = p <= from ? p : p - count; // gap index among the non-block rows\n const tg = to <= from ? to : to - count;\n return g <= tg ? g : g + count;\n}\n\n/**\n * x over y on ONE axis. Returns the list of axis ops that apply after y (empty = x vanished), or null when\n * the pair has no transform (the caller turns that into a barrier).\n */\nexport function transformAxis(x: AxisOp, y: AxisOp, side: Side): AxisOp[] | null {\n if (y.kind === \"move\" && isNoopMove(y)) return [x];\n if (x.kind === \"move\" && isNoopMove(x)) return [];\n switch (x.kind) {\n case \"insert\": {\n if (y.kind === \"insert\") {\n if (x.at < y.at || (x.at === y.at && side === \"left\")) return [x];\n return [{ ...x, at: x.at + y.count }];\n }\n if (y.kind === \"remove\") {\n if (x.at <= y.start) return [x];\n if (x.at >= y.start + y.count) return [{ ...x, at: x.at - y.count }];\n return [{ ...x, at: y.start }]; // an insert inside a removed span survives at its start\n }\n return [{ ...x, at: mapGapThroughMove(x.at, y) }];\n }\n case \"remove\": {\n const kept: number[] = [];\n for (let i = x.start; i < x.start + x.count; i++) {\n const m = mapIndex(y, i);\n if (m !== null) kept.push(m);\n }\n if (y.kind === \"insert\" || y.kind === \"remove\" || y.kind === \"move\") {\n // Removes are emitted highest first so each index is still valid when it applies.\n return runs(kept)\n .reverse()\n .map((r) => ({ kind: \"remove\" as const, start: r.start, count: r.count }));\n }\n return null;\n }\n case \"move\": {\n if (y.kind === \"insert\") {\n const inside = y.at > x.from && y.at < x.from + x.count;\n const from = inside || y.at > x.from ? x.from : x.from + y.count;\n const count = inside ? x.count + y.count : x.count;\n const to = x.to >= y.at ? x.to + y.count : x.to;\n const out = { kind: \"move\" as const, from, count, to };\n return isNoopMove(out) ? [] : [out];\n }\n if (y.kind === \"remove\") {\n const kept: number[] = [];\n for (let i = x.from; i < x.from + x.count; i++) {\n const m = mapIndex(y, i);\n if (m !== null) kept.push(m);\n }\n if (kept.length === 0) return [];\n const r = runs(kept);\n if (r.length !== 1) return null;\n const to = x.to <= y.start ? x.to : x.to >= y.start + y.count ? x.to - y.count : y.start;\n const out = { kind: \"move\" as const, from: r[0]!.start, count: r[0]!.count, to };\n return isNoopMove(out) ? [] : [out];\n }\n // move over move. The same rows moved twice: the later move wins, the earlier one is dropped.\n // Blocks that only partly overlap have no transform yet.\n const overlap = Math.min(x.from + x.count, y.from + y.count) - Math.max(x.from, y.from);\n if (overlap > 0) {\n if (x.from !== y.from || x.count !== y.count) return null;\n if (side === \"right\") return [];\n const out = { kind: \"move\" as const, from: movedBlockStart(y), count: x.count, to: mapGapThroughMove(x.to, y) };\n return isNoopMove(out) ? [] : [out];\n }\n // Disjoint blocks: x's rows through y must stay contiguous; the target gap goes through y.\n const mapped: number[] = [];\n for (let i = x.from; i < x.from + x.count; i++) mapped.push(mapIndex(y, i)!);\n const r = runs(mapped);\n if (r.length !== 1) return null;\n let to: number;\n if (x.to > y.from && x.to < y.from + y.count) to = movedBlockStart(y) + (x.to - y.from);\n else to = mapGapThroughMove(x.to, y) + (x.to === y.to && side === \"right\" ? y.count : 0); // same target: later block first\n const out = { kind: \"move\" as const, from: r[0]!.start, count: r[0]!.count, to };\n if (to > out.from && to < out.from + out.count) return null;\n return isNoopMove(out) ? [] : [out];\n }\n }\n}\n","/**\n * The sheet comment-anchor transform (COLLAB-DESIGN §8 + §10 class R, unit C9). Its own file; the sheet\n * transformer calls it first for every pair (`transformMutation`) and the registry declares its mutation id.\n *\n * A cell comment is anchored by `matrx.mutation.set-comment-anchor` `{ subUnitId, anchorId, cell: {row, col} | null }`\n * (null takes the anchor out). The anchor is a ranged resource keyed by id:\n * - anchor over a structural op on its sheet: the cell moves by the same index arithmetic as cells (`mapIndex`);\n * a cell whose row or column was removed DROPS the anchor (its author is told) and the thread is orphaned;\n * - anchor over the removal of its sheet: dropped the same way;\n * - anchor over the same anchor id: the later commit wins (§6.4), the earlier side is told;\n * - anything else over an anchor, and an anchor over anything else: unchanged (anchors never move content).\n * Every seat moves its stored anchors through each structural op it applies with `moveSheetAnchorCell` — the\n * same function — so a seat that set an anchor before a peer's row insert and a seat that received the anchor\n * after it end with the same cell.\n *\n * Univer's cell notes (`@univerjs/sheets-note`, C5f) are the same kind of record — keyed by note id, anchored to one\n * cell, never moving content — and transform here too (`transformCellNote`, called first). Their params name the\n * sheet `sheetId`. Facets: a full write (`update-note`: content and cell), a move (`update-note-position`: the cell)\n * and a remove (`remove-note`).\n * - a note over a structural op on its sheet: its cell moves (`moveSheetAnchorCell`); a write whose cell was\n * removed is dropped (the note went with its row), a move whose target was removed becomes a remove;\n * - a structural op over a note: a raw structural mutation runs no plugin ref-range handler synchronously (each\n * seat's watcher moves notes a microtask later), so the op carries the fixup the note is owed — its move, or\n * its remove — and every seat (the fold included) holds the same cells the moment it applies the op;\n * - the same note: a remove wins; a full write over a move takes the move's target cell; two structural ops'\n * fixups both stand (each target went through the other op); otherwise a person's write or move beats a\n * structural op's derived one, else the later commit wins;\n * - two different notes written to one cell at once, or a note change addressed by cell instead of id against\n * another note change on its sheet: barrier (the resource keeps one note per cell, by insertion order).\n */\nimport { mapIndex, type AxisOp } from \"./transform/axis\";\nimport type { SheetMutation, Side, TransformResult } from \"./transform/types\";\n\nexport const SET_SHEET_COMMENT_ANCHOR = \"matrx.mutation.set-comment-anchor\";\n\n/** Mutation ids this package defines itself (never emitted by Univer; the census lists them apart). */\nexport const OWN_SHEET_MUTATION_IDS: readonly string[] = [SET_SHEET_COMMENT_ANCHOR];\n\n/**\n * A sheet op that only sets or clears comment anchors: every mutation is `matrx.mutation.set-comment-anchor`. A seat\n * that may comment but not edit sends exactly these (the door `content.collab_append` checks the same rule).\n */\nexport function isSheetsAnchorOnlyOp(op: ReadonlyArray<SheetMutation> | null | undefined): boolean {\n return !!op && op.length > 0 && op.every((m) => m.id === SET_SHEET_COMMENT_ANCHOR);\n}\n\nexport interface SheetAnchorCell {\n row: number;\n col: number;\n}\n\nconst REMOVE_SHEET = \"sheet.mutation.remove-sheet\";\n\ntype R = { startRow: number; endRow: number; startColumn: number; endColumn: number };\n\n/** The row/column op a Univer structural mutation makes, from its params (the same params on every seat). */\nexport function structuralAxisOp(id: string, params: Readonly<Record<string, unknown>>): { axis: \"row\" | \"col\"; op: AxisOp } | null {\n const p = params as Record<string, R | undefined>;\n switch (id) {\n case \"sheet.mutation.insert-row\":\n return p.range ? { axis: \"row\", op: { kind: \"insert\", at: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;\n case \"sheet.mutation.remove-rows\":\n return p.range ? { axis: \"row\", op: { kind: \"remove\", start: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;\n case \"sheet.mutation.insert-col\":\n return p.range ? { axis: \"col\", op: { kind: \"insert\", at: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;\n case \"sheet.mutation.remove-col\":\n return p.range ? { axis: \"col\", op: { kind: \"remove\", start: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;\n case \"sheet.mutation.move-rows\":\n return p.sourceRange && p.targetRange\n ? { axis: \"row\", op: { kind: \"move\", from: p.sourceRange.startRow, count: p.sourceRange.endRow - p.sourceRange.startRow + 1, to: p.targetRange.startRow } }\n : null;\n case \"sheet.mutation.move-columns\":\n return p.sourceRange && p.targetRange\n ? {\n axis: \"col\",\n op: { kind: \"move\", from: p.sourceRange.startColumn, count: p.sourceRange.endColumn - p.sourceRange.startColumn + 1, to: p.targetRange.startColumn },\n }\n : null;\n default:\n return null;\n }\n}\n\n/**\n * Where an anchored cell is after one applied mutation on `sheet`: the same cell, a moved cell, or null when the\n * mutation removed it (its row, its column or its sheet).\n */\nexport function moveSheetAnchorCell(cell: SheetAnchorCell, sheet: string, id: string, params: Readonly<Record<string, unknown>>): SheetAnchorCell | null {\n if (String(params.subUnitId ?? \"\") !== sheet) return cell;\n if (id === REMOVE_SHEET) return null;\n const s = structuralAxisOp(id, params);\n if (!s) return cell;\n if (s.axis === \"row\") {\n const row = mapIndex(s.op, cell.row);\n return row === null ? null : row === cell.row ? cell : { row, col: cell.col };\n }\n const col = mapIndex(s.op, cell.col);\n return col === null ? null : col === cell.col ? cell : { row: cell.row, col };\n}\n\nfunction colName(c: number): string {\n let s = \"\";\n for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + ((n - 1) % 26)) + s;\n return s;\n}\n/** \"B5\" for row 4, column 1. */\nexport const sheetCellName = (cell: SheetAnchorCell) => `${colName(cell.col)}${cell.row + 1}`;\n\nfunction cellOf(m: SheetMutation): SheetAnchorCell | null {\n const c = m.params.cell as SheetAnchorCell | null | undefined;\n return c && typeof c.row === \"number\" && typeof c.col === \"number\" ? c : null;\n}\n\nconst ok = (op: SheetMutation[], lost: string[] = [], dropped: string[] = []): TransformResult => ({ op, lost, dropped });\nconst label = (m: SheetMutation) => {\n const c = cellOf(m);\n return `${String(m.params.subUnitId ?? \"\")}!${c ? sheetCellName(c) : \"?\"} comment`;\n};\n\n// ── Univer cell notes (C5f) ──────────────────────────────────────────────────────────────────────────────────\nexport const UPDATE_NOTE = \"sheet.mutation.update-note\";\nexport const REMOVE_NOTE = \"sheet.mutation.remove-note\";\nexport const UPDATE_NOTE_POSITION = \"sheet.mutation.update-note-position\";\nconst NOTE_IDS = new Set([UPDATE_NOTE, REMOVE_NOTE, UPDATE_NOTE_POSITION]);\nexport const isNoteMutation = (id: string): boolean => NOTE_IDS.has(id);\n\ntype NoteRole = \"write\" | \"move\" | \"remove\";\ninterface NoteRef {\n role: NoteRole;\n sheet: string;\n /** The note id, or null when the op addresses the note by its cell (an undo of a new note removes by cell). */\n id: string | null;\n /** Where the op leaves the note: a write's cell, a move's target; null for a remove. */\n cell: SheetAnchorCell | null;\n}\n\nconst cellAt = (row: unknown, col: unknown): SheetAnchorCell | null => (typeof row === \"number\" && typeof col === \"number\" ? { row, col } : null);\nfunction noteRef(m: SheetMutation): NoteRef {\n const p = m.params as Record<string, any>;\n const sheet = String(p.sheetId ?? \"\");\n if (m.id === UPDATE_NOTE) return { role: \"write\", sheet, id: p.note?.id ? String(p.note.id) : null, cell: cellAt(p.row, p.col) };\n if (m.id === UPDATE_NOTE_POSITION) return { role: \"move\", sheet, id: p.noteId ? String(p.noteId) : null, cell: cellAt(p.newPosition?.row, p.newPosition?.col) };\n return { role: \"remove\", sheet, id: p.noteId ? String(p.noteId) : null, cell: p.noteId ? null : cellAt(p.row, p.col) };\n}\nconst noteDerived = (m: SheetMutation) => m.params.derived === true;\nconst noteLabel = (n: NoteRef) => `${n.sheet}!${n.cell ? sheetCellName(n.cell) : \"?\"} note`;\nconst tellNote = (m: SheetMutation, n: NoteRef) => (noteDerived(m) ? [] : [noteLabel(n)]);\nconst noteBarrier = (reason: string): TransformResult => ({ barrier: true, reason: `note: ${reason}` });\nconst removeNoteOf = (sheet: string, id: string): SheetMutation => ({ id: REMOVE_NOTE, params: { sheetId: sheet, noteId: id, derived: true } });\nconst moveNoteTo = (sheet: string, id: string, cell: SheetAnchorCell): SheetMutation => ({\n id: UPDATE_NOTE_POSITION,\n params: { sheetId: sheet, noteId: id, newPosition: { row: cell.row, col: cell.col }, derived: true },\n});\n/** A mutation's sheet as `moveSheetAnchorCell` reads it (structural ops name `subUnitId`). */\nconst onSheet = (m: SheetMutation, sheet: string) => String(m.params.subUnitId ?? \"\") === sheet;\n\n/** x over y when either is a cell-note mutation; null when neither is. */\nexport function transformCellNote(x: SheetMutation, y: SheetMutation, side: Side): TransformResult | null {\n const xn = isNoteMutation(x.id);\n const yn = isNoteMutation(y.id);\n if (!xn && !yn) return null;\n if (!xn) {\n // x moves content (or not); y is a note. A structural x carries the fixup y's note is owed.\n const n = noteRef(y);\n if (n.role === \"remove\" || !n.cell || !onSheet(x, n.sheet)) return ok([x]);\n if (x.id === REMOVE_SHEET) return n.id ? ok([removeNoteOf(n.sheet, n.id), x]) : ok([x]);\n if (!structuralAxisOp(x.id, x.params)) return ok([x]);\n if (!n.id) return noteBarrier(\"a note without an id against a row or column change\");\n const moved = moveSheetAnchorCell(n.cell, n.sheet, x.id, x.params);\n if (!moved) return ok([x, removeNoteOf(n.sheet, n.id)]);\n return moved === n.cell ? ok([x]) : ok([x, moveNoteTo(n.sheet, n.id, moved)]);\n }\n const n = noteRef(x);\n if (!yn) {\n if (!onSheet(y, n.sheet)) return ok([x]);\n if (y.id === REMOVE_SHEET) return ok([], [], tellNote(x, n));\n if (!structuralAxisOp(y.id, y.params)) return ok([x]); // a note never conflicts with content\n if (n.role === \"write\" && !n.id) return noteBarrier(\"a note without an id against a row or column change\");\n const p = x.params as Record<string, any>;\n const from = cellAt(p.row, p.col);\n const fromMoved = from ? moveSheetAnchorCell(from, n.sheet, y.id, y.params) : null;\n if (n.role === \"remove\") {\n if (n.id) return ok([x]);\n return fromMoved ? ok([{ id: x.id, params: { ...p, row: fromMoved.row, col: fromMoved.col } }]) : ok([]);\n }\n const moved = n.cell ? moveSheetAnchorCell(n.cell, n.sheet, y.id, y.params) : null;\n if (n.role === \"write\") {\n if (!moved) return ok([], [], tellNote(x, n));\n return ok([moved === n.cell ? x : { id: x.id, params: { ...p, row: moved.row, col: moved.col } }]);\n }\n // a move: its target moves; a target that was removed takes the note with it\n if (!moved) return n.id ? ok([removeNoteOf(n.sheet, n.id)], [], tellNote(x, n)) : ok([], [], tellNote(x, n));\n const params: Record<string, any> = { ...p, newPosition: { ...p.newPosition, row: moved.row, col: moved.col } };\n if (fromMoved) Object.assign(params, { row: fromMoved.row, col: fromMoved.col });\n return ok([{ id: x.id, params }]);\n }\n // both are notes\n const m = noteRef(y);\n if (n.sheet !== m.sheet) return ok([x]);\n if (!n.id || !m.id) return noteBarrier(\"a note change addressed by cell against another note change on its sheet\");\n if (n.id !== m.id) {\n const sameCell = n.cell && m.cell && n.cell.row === m.cell.row && n.cell.col === m.cell.col;\n return sameCell ? noteBarrier(\"two notes written to one cell at once\") : ok([x]);\n }\n if (n.role === \"remove\") return ok([x]);\n if (m.role === \"remove\") return ok([], [], tellNote(x, n));\n if (n.role === \"write\" && m.role === \"move\") {\n // a full write over a move takes the move's target cell\n return m.cell ? ok([{ id: x.id, params: { ...x.params, row: m.cell.row, col: m.cell.col } }]) : ok([x]);\n }\n if (n.role === \"move\" && m.role === \"write\") return ok([x]);\n const dx = noteDerived(x);\n const dy = noteDerived(y);\n // two structural ops' fixups: each target already went through the other op's rows and columns\n if (dx && dy) return ok([x]);\n const xWins = dx !== dy ? !dx : side === \"left\";\n return xWins ? ok([x], tellNote(y, m)) : ok([], [], tellNote(x, n));\n}\n\n/** x over y when either is an anchor mutation; null when neither is (the sheet transformer carries on). */\nexport function transformCommentAnchor(x: SheetMutation, y: SheetMutation, side: Side): TransformResult | null {\n const note = transformCellNote(x, y, side); // cell notes are the same kind of record (C5f)\n if (note) return note;\n const xa = x.id === SET_SHEET_COMMENT_ANCHOR;\n const ya = y.id === SET_SHEET_COMMENT_ANCHOR;\n if (!xa && !ya) return null;\n if (!xa) return ok([x]); // an anchor never moves content\n if (ya) {\n // anchors are keyed by sheet and id: the same id on another sheet is another anchor\n if (x.params.anchorId !== y.params.anchorId || x.params.subUnitId !== y.params.subUnitId) return ok([x]);\n return side === \"left\" ? ok([x], [label(y)]) : ok([], [], [label(x)]);\n }\n const cell = cellOf(x);\n if (!cell) return ok([x]); // taking an anchor out commutes with every move\n const moved = moveSheetAnchorCell(cell, String(x.params.subUnitId ?? \"\"), y.id, y.params);\n if (!moved) return ok([], [], [label(x)]);\n return ok([moved === cell ? x : { id: x.id, params: { ...x.params, cell: moved } }]);\n}\n","/**\n * Cell notes follow their cells the way our comment anchors do (C5g): every row/column insert, remove and move this\n * seat applies — its own, a peer's, an undo, a fold — moves each note of that sheet through `moveSheetAnchorCell`,\n * the function the note transform uses (`transformCellNote`), synchronously, whatever the op's other-axis extent.\n *\n * Why not Univer's own: `@univerjs/sheets-note` 1.0.4 moves notes on a raw structural mutation through one\n * `RefRangeService.watchRange` watcher per note, and a watcher only moves a note that lies inside the mutation's\n * OTHER-axis range: a peer's column removal over rows 0–6 shifts every cell right of it but leaves a note on row 10\n * where it was, now on another cell. Whether a note sat inside that range depends on the order concurrent ops\n * apply in, so seats ended with one note on different cells (chained seed 20718; session seed 1015: one note on\n * column 5 on one seat and 8 on another). Those watchers are switched off here; the command-time handlers that send\n * a removed or moved note's own mutation stay as they are (those travel). The watchers also key themselves by the\n * note's first cell and are never re-keyed after a silent move, so they leak; switching them off ends that too.\n *\n * The controller is not exported, so it is found by its shape in Univer's injector (Univer and redi are pinned at\n * 1.0.4 / 1.1.3; `a-cell-note-follows-its-cell-on-every-seat.test.ts` fails when the shape changes). A Univer\n * without the note plugin has no notes and nothing to follow.\n */\nimport { CommandType } from \"@univerjs/core\";\n\nimport { moveSheetAnchorCell, structuralAxisOp } from \"../collab/sheets/comment-anchor-transform\";\n\ntype Obj = Record<string, any>;\n\ninterface NoteCommands {\n syncExecuteCommand(id: string, params?: unknown, options?: unknown): unknown;\n beforeCommandExecuted(listener: (info: { id: string; type?: CommandType; params?: unknown }) => void): { dispose(): void };\n onCommandExecuted(listener: (info: { id: string; type?: CommandType; params?: unknown }) => void): { dispose(): void };\n}\n\nconst isNoteController = (x: unknown): x is Obj => {\n const o = x as Obj | null;\n return !!o && o._watcherMap instanceof Map && o._disposableMap instanceof Map && typeof o._watch === \"function\" && !!o._sheetsNoteModel?.getSheetNotes;\n};\n\n/** Univer's SheetsNoteRefRangeController in this injector or a child, by shape; null when the plugin is absent. */\nexport function findNoteRefRangeController(injector: unknown): Obj | null {\n const seen = new Set<unknown>();\n const visit = (inj: Obj | null | undefined): Obj | null => {\n if (!inj || seen.has(inj)) return null;\n seen.add(inj);\n const resolved = inj.resolvedDependencyCollection?.resolvedDependencies;\n if (resolved instanceof Map)\n for (const list of resolved.values()) for (const x of Array.isArray(list) ? list : [list]) if (isNoteController(x)) return x;\n for (const child of Array.isArray(inj.children) ? inj.children : []) {\n const hit = visit(child);\n if (hit) return hit;\n }\n return null;\n };\n return visit(injector as Obj);\n}\n\nconst NOTE_MUTATIONS = new Set([\"sheet.mutation.update-note\", \"sheet.mutation.remove-note\", \"sheet.mutation.update-note-position\"]);\n\n/** Switch Univer's per-note watchers off and move notes through every structural mutation this Univer applies. */\nexport function installSheetNoteFollower(injector: unknown, commands: NoteCommands): { dispose(): void } {\n let ctrl: Obj | null = null;\n // no note plugin seen yet: structural ops stop looking (a scan of the injector per row insert is the cost) until a\n // note mutation shows the plugin is there\n let absent = false;\n let warned = false;\n /** Finds the controller once and turns its watchers off (before the mutation that would reach them runs). */\n const ensure = (byNote = false): Obj | null => {\n if (ctrl) return ctrl;\n if (absent && !byNote) return null;\n const found = findNoteRefRangeController(injector);\n absent = !found;\n if (!found) {\n // a note exists but Univer's controller changed shape: notes fall back to Univer's own watchers (announced)\n if (byNote && !warned) console.warn(\"[sheets-ot] cell notes: Univer's note controller was not found; notes follow Univer's own watchers\");\n warned ||= byNote;\n return null;\n }\n for (const d of found._watcherMap.values()) d?.dispose?.();\n found._watcherMap.clear();\n found._watch = () => {}; // its notes follow through this module instead\n ctrl = found;\n return ctrl;\n };\n const before = commands.beforeCommandExecuted((info) => {\n if (ctrl || info.type !== CommandType.MUTATION) return;\n if (NOTE_MUTATIONS.has(info.id)) ensure(true);\n else if (structuralAxisOp(info.id, (info.params ?? {}) as Obj)) ensure();\n });\n const after = commands.onCommandExecuted((info) => {\n if (info.type !== CommandType.MUTATION) return;\n const p = (info.params ?? {}) as Obj;\n if (!structuralAxisOp(info.id, p) || typeof p.unitId !== \"string\") return;\n const c = ensure();\n if (!c) return;\n const sheet = String(p.subUnitId ?? \"\");\n const notes = c._sheetsNoteModel.getSheetNotes(p.unitId, sheet) as Map<string, Obj> | undefined;\n if (!notes?.size) return;\n const moves: Array<{ id: string; to: { row: number; col: number } | null }> = [];\n for (const [id, note] of notes) {\n const at = { row: Number(note.row), col: Number(note.col) };\n const to = moveSheetAnchorCell(at, sheet, info.id, p);\n if (to !== at) moves.push({ id, to });\n }\n for (const m of moves) {\n if (m.to) commands.syncExecuteCommand(\"sheet.mutation.update-note-position\", { unitId: p.unitId, sheetId: sheet, noteId: m.id, newPosition: m.to, silent: true }, { onlyLocal: true });\n else commands.syncExecuteCommand(\"sheet.mutation.remove-note\", { unitId: p.unitId, sheetId: sheet, noteId: m.id, silent: true }, { onlyLocal: true });\n }\n });\n return {\n dispose: () => {\n before.dispose();\n after.dispose();\n },\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/sheets/comment-anchors.ts
|
|
2
|
-
import { CommandType, ICommandService, IResourceManagerService, UndoCommandId, UniverInstanceType } from "@univerjs/core";
|
|
2
|
+
import { CommandType as CommandType2, ICommandService, IResourceManagerService, UndoCommandId, UniverInstanceType } from "@univerjs/core";
|
|
3
3
|
|
|
4
4
|
// src/collab/sheets/transform/axis.ts
|
|
5
5
|
function isNoopMove(op) {
|
|
@@ -70,6 +70,84 @@ function colName(c) {
|
|
|
70
70
|
}
|
|
71
71
|
var sheetCellName = (cell) => `${colName(cell.col)}${cell.row + 1}`;
|
|
72
72
|
|
|
73
|
+
// src/sheets/note-follower.ts
|
|
74
|
+
import { CommandType } from "@univerjs/core";
|
|
75
|
+
var isNoteController = (x) => {
|
|
76
|
+
const o = x;
|
|
77
|
+
return !!o && o._watcherMap instanceof Map && o._disposableMap instanceof Map && typeof o._watch === "function" && !!o._sheetsNoteModel?.getSheetNotes;
|
|
78
|
+
};
|
|
79
|
+
function findNoteRefRangeController(injector) {
|
|
80
|
+
const seen = /* @__PURE__ */ new Set();
|
|
81
|
+
const visit = (inj) => {
|
|
82
|
+
if (!inj || seen.has(inj)) return null;
|
|
83
|
+
seen.add(inj);
|
|
84
|
+
const resolved = inj.resolvedDependencyCollection?.resolvedDependencies;
|
|
85
|
+
if (resolved instanceof Map) {
|
|
86
|
+
for (const list of resolved.values()) for (const x of Array.isArray(list) ? list : [list]) if (isNoteController(x)) return x;
|
|
87
|
+
}
|
|
88
|
+
for (const child of Array.isArray(inj.children) ? inj.children : []) {
|
|
89
|
+
const hit = visit(child);
|
|
90
|
+
if (hit) return hit;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
return visit(injector);
|
|
95
|
+
}
|
|
96
|
+
var NOTE_MUTATIONS = /* @__PURE__ */ new Set(["sheet.mutation.update-note", "sheet.mutation.remove-note", "sheet.mutation.update-note-position"]);
|
|
97
|
+
function installSheetNoteFollower(injector, commands) {
|
|
98
|
+
let ctrl = null;
|
|
99
|
+
let absent = false;
|
|
100
|
+
let warned = false;
|
|
101
|
+
const ensure = (byNote = false) => {
|
|
102
|
+
if (ctrl) return ctrl;
|
|
103
|
+
if (absent && !byNote) return null;
|
|
104
|
+
const found = findNoteRefRangeController(injector);
|
|
105
|
+
absent = !found;
|
|
106
|
+
if (!found) {
|
|
107
|
+
if (byNote && !warned) console.warn("[sheets-ot] cell notes: Univer's note controller was not found; notes follow Univer's own watchers");
|
|
108
|
+
warned ||= byNote;
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
for (const d of found._watcherMap.values()) d?.dispose?.();
|
|
112
|
+
found._watcherMap.clear();
|
|
113
|
+
found._watch = () => {
|
|
114
|
+
};
|
|
115
|
+
ctrl = found;
|
|
116
|
+
return ctrl;
|
|
117
|
+
};
|
|
118
|
+
const before = commands.beforeCommandExecuted((info) => {
|
|
119
|
+
if (ctrl || info.type !== CommandType.MUTATION) return;
|
|
120
|
+
if (NOTE_MUTATIONS.has(info.id)) ensure(true);
|
|
121
|
+
else if (structuralAxisOp(info.id, info.params ?? {})) ensure();
|
|
122
|
+
});
|
|
123
|
+
const after = commands.onCommandExecuted((info) => {
|
|
124
|
+
if (info.type !== CommandType.MUTATION) return;
|
|
125
|
+
const p = info.params ?? {};
|
|
126
|
+
if (!structuralAxisOp(info.id, p) || typeof p.unitId !== "string") return;
|
|
127
|
+
const c = ensure();
|
|
128
|
+
if (!c) return;
|
|
129
|
+
const sheet = String(p.subUnitId ?? "");
|
|
130
|
+
const notes = c._sheetsNoteModel.getSheetNotes(p.unitId, sheet);
|
|
131
|
+
if (!notes?.size) return;
|
|
132
|
+
const moves = [];
|
|
133
|
+
for (const [id, note] of notes) {
|
|
134
|
+
const at = { row: Number(note.row), col: Number(note.col) };
|
|
135
|
+
const to = moveSheetAnchorCell(at, sheet, info.id, p);
|
|
136
|
+
if (to !== at) moves.push({ id, to });
|
|
137
|
+
}
|
|
138
|
+
for (const m of moves) {
|
|
139
|
+
if (m.to) commands.syncExecuteCommand("sheet.mutation.update-note-position", { unitId: p.unitId, sheetId: sheet, noteId: m.id, newPosition: m.to, silent: true }, { onlyLocal: true });
|
|
140
|
+
else commands.syncExecuteCommand("sheet.mutation.remove-note", { unitId: p.unitId, sheetId: sheet, noteId: m.id, silent: true }, { onlyLocal: true });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
dispose: () => {
|
|
145
|
+
before.dispose();
|
|
146
|
+
after.dispose();
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
73
151
|
// src/sheets/comment-anchors.ts
|
|
74
152
|
var SHEET_COMMENT_ANCHORS_RESOURCE = "SHEET_MATRX_COMMENT_ANCHORS_PLUGIN";
|
|
75
153
|
function applyToSheetAnchors(anchors, id, params) {
|
|
@@ -189,10 +267,11 @@ function installSheetCommentAnchors(univer) {
|
|
|
189
267
|
}
|
|
190
268
|
};
|
|
191
269
|
const disposables = [];
|
|
270
|
+
disposables.push(installSheetNoteFollower(injector, commands));
|
|
192
271
|
disposables.push(
|
|
193
272
|
commands.registerCommand({
|
|
194
273
|
id: SET_SHEET_COMMENT_ANCHOR,
|
|
195
|
-
type:
|
|
274
|
+
type: CommandType2.MUTATION,
|
|
196
275
|
handler: (_accessor, params) => {
|
|
197
276
|
if (!params?.unitId || !params.subUnitId || !params.anchorId) return false;
|
|
198
277
|
apply(params.unitId, SET_SHEET_COMMENT_ANCHOR, params);
|
|
@@ -218,7 +297,7 @@ function installSheetCommentAnchors(univer) {
|
|
|
218
297
|
);
|
|
219
298
|
disposables.push(
|
|
220
299
|
commands.onCommandExecuted((info) => {
|
|
221
|
-
if (info.type !==
|
|
300
|
+
if (info.type !== CommandType2.MUTATION || info.id === SET_SHEET_COMMENT_ANCHOR) return;
|
|
222
301
|
const p = info.params;
|
|
223
302
|
const unitId = typeof p?.unitId === "string" ? p.unitId : null;
|
|
224
303
|
if (!unitId || !byUnit.has(unitId)) return;
|