@tita-agent/dsh-data-table 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +172 -2
- package/cordis.patch.yml +7 -0
- package/lib/client.js +16766 -0
- package/lib/contract-envelope.d.ts +91 -0
- package/lib/contract-envelope.js +106 -0
- package/lib/contract-layout.d.ts +130 -0
- package/lib/contract-layout.js +138 -0
- package/lib/contract-refs.d.ts +39 -0
- package/lib/contract-refs.js +9 -0
- package/lib/contract-version.d.ts +311 -0
- package/lib/contract-version.js +270 -0
- package/lib/contract-wire.d.ts +147 -0
- package/lib/contract-wire.js +99 -0
- package/lib/contracts.d.ts +12 -0
- package/lib/contracts.js +12 -0
- package/lib/types/audit.d.ts +42 -0
- package/lib/types/audit.js +39 -0
- package/lib/types/client/ProjectImportMenu.d.ts +30 -0
- package/lib/types/client/ProjectImportMenu.js +70 -0
- package/lib/types/client/SheetPanel.d.ts +33 -0
- package/lib/types/client/SheetPanel.js +1766 -0
- package/lib/types/client/SheetToggle.d.ts +18 -0
- package/lib/types/client/SheetToggle.js +26 -0
- package/lib/types/client/index.d.ts +15 -0
- package/lib/types/client/index.js +110 -0
- package/lib/types/client/persistence/changes.d.ts +15 -0
- package/lib/types/client/persistence/changes.js +51 -0
- package/lib/types/client/persistence/ids.d.ts +34 -0
- package/lib/types/client/persistence/ids.js +48 -0
- package/lib/types/client/persistence/merge.d.ts +35 -0
- package/lib/types/client/persistence/merge.js +362 -0
- package/lib/types/client/store.d.ts +61 -0
- package/lib/types/client/store.js +152 -0
- package/lib/types/client/surface-context.d.ts +27 -0
- package/lib/types/client/surface-context.js +104 -0
- package/lib/types/client/surface-control.d.ts +2 -0
- package/lib/types/client/surface-control.js +12 -0
- package/lib/types/client/transport/workbookApi.d.ts +159 -0
- package/lib/types/client/transport/workbookApi.js +246 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.d.ts +5 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.js +7 -0
- package/lib/types/client/univer/WorkbookCanvas.d.ts +40 -0
- package/lib/types/client/univer/WorkbookCanvas.js +87 -0
- package/lib/types/client/univer/WorkbookView.d.ts +3 -0
- package/lib/types/client/univer/WorkbookView.js +2 -0
- package/lib/types/client/univer/loader.d.ts +18 -0
- package/lib/types/client/univer/loader.js +81 -0
- package/lib/types/client/univer/standalone-entry.d.ts +1 -0
- package/lib/types/client/univer/standalone-entry.js +19 -0
- package/lib/types/client/versions/VersionChrome.d.ts +33 -0
- package/lib/types/client/versions/VersionChrome.js +21 -0
- package/lib/types/client/versions/inspect.d.ts +70 -0
- package/lib/types/client/versions/inspect.js +88 -0
- package/lib/types/client/versions/preview-decoration.d.ts +11 -0
- package/lib/types/client/versions/preview-decoration.js +54 -0
- package/lib/types/client/versions/records.d.ts +19 -0
- package/lib/types/client/versions/records.js +70 -0
- package/lib/types/client/workspace-host.d.ts +61 -0
- package/lib/types/client/workspace-host.js +124 -0
- package/lib/types/collaboration-hub.d.ts +31 -0
- package/lib/types/collaboration-hub.js +69 -0
- package/lib/types/contract/ids.d.ts +24 -0
- package/lib/types/contract/ids.js +46 -0
- package/lib/types/contract/mutations.d.ts +25 -0
- package/lib/types/contract/mutations.js +782 -0
- package/lib/types/contract/operations.d.ts +54 -0
- package/lib/types/contract/operations.js +203 -0
- package/lib/types/contract/snapshots.d.ts +176 -0
- package/lib/types/contract/snapshots.js +351 -0
- package/lib/types/contract/univer-api-catalog.d.ts +29 -0
- package/lib/types/contract/univer-api-catalog.js +264 -0
- package/lib/types/errors.d.ts +8 -0
- package/lib/types/errors.js +12 -0
- package/lib/types/http/assets-routes.d.ts +3 -0
- package/lib/types/http/assets-routes.js +169 -0
- package/lib/types/http/collaboration.d.ts +10 -0
- package/lib/types/http/collaboration.js +154 -0
- package/lib/types/http/history.d.ts +15 -0
- package/lib/types/http/history.js +134 -0
- package/lib/types/http/project-files.d.ts +79 -0
- package/lib/types/http/project-files.js +206 -0
- package/lib/types/http/project-scope.d.ts +50 -0
- package/lib/types/http/project-scope.js +52 -0
- package/lib/types/http/routes.d.ts +98 -0
- package/lib/types/http/routes.js +323 -0
- package/lib/types/http/standalone.d.ts +9 -0
- package/lib/types/http/standalone.js +31 -0
- package/lib/types/http/trunk.d.ts +10 -0
- package/lib/types/http/trunk.js +322 -0
- package/lib/types/http/worktree.d.ts +9 -0
- package/lib/types/http/worktree.js +145 -0
- package/lib/types/index.d.ts +18 -0
- package/lib/types/index.js +156 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/invariant.js +15 -0
- package/lib/types/request-params.d.ts +21 -0
- package/lib/types/request-params.js +76 -0
- package/lib/types/store/paths.d.ts +68 -0
- package/lib/types/store/paths.js +148 -0
- package/lib/types/store/project.d.ts +88 -0
- package/lib/types/store/project.js +120 -0
- package/lib/types/store/read.d.ts +110 -0
- package/lib/types/store/read.js +404 -0
- package/lib/types/store/retention.d.ts +27 -0
- package/lib/types/store/retention.js +111 -0
- package/lib/types/store/write.d.ts +22 -0
- package/lib/types/store/write.js +210 -0
- package/lib/types/tools/arguments.d.ts +40 -0
- package/lib/types/tools/arguments.js +130 -0
- package/lib/types/tools/cells.d.ts +50 -0
- package/lib/types/tools/cells.js +298 -0
- package/lib/types/tools/index.d.ts +2 -0
- package/lib/types/tools/index.js +250 -0
- package/lib/types/tools/render.d.ts +47 -0
- package/lib/types/tools/render.js +290 -0
- package/lib/types/tools/univer-api.d.ts +18 -0
- package/lib/types/tools/univer-api.js +198 -0
- package/lib/types/tools/workbook-catalog.d.ts +3 -0
- package/lib/types/tools/workbook-catalog.js +83 -0
- package/lib/types/tools/workbook-edit.d.ts +192 -0
- package/lib/types/tools/workbook-edit.js +912 -0
- package/lib/types/tools/workbook-find.d.ts +11 -0
- package/lib/types/tools/workbook-find.js +89 -0
- package/lib/types/tools/workbook-history.d.ts +2 -0
- package/lib/types/tools/workbook-history.js +59 -0
- package/lib/types/tools/workbook-read.d.ts +21 -0
- package/lib/types/tools/workbook-read.js +204 -0
- package/lib/types/types.d.ts +377 -0
- package/lib/types/types.js +34 -0
- package/lib/types/univer/apply-operation.d.ts +14 -0
- package/lib/types/univer/apply-operation.js +398 -0
- package/lib/types/univer/chunk.d.ts +68 -0
- package/lib/types/univer/chunk.js +16 -0
- package/lib/types/univer/decoration.d.ts +34 -0
- package/lib/types/univer/decoration.js +88 -0
- package/lib/types/univer/diagnostics.d.ts +31 -0
- package/lib/types/univer/diagnostics.js +141 -0
- package/lib/types/univer/engine.d.ts +33 -0
- package/lib/types/univer/engine.js +338 -0
- package/lib/types/univer/entry.d.ts +8 -0
- package/lib/types/univer/entry.js +11 -0
- package/lib/types/univer/facade.d.ts +107 -0
- package/lib/types/univer/facade.js +1 -0
- package/lib/types/univer/fonts.d.ts +202 -0
- package/lib/types/univer/fonts.js +140 -0
- package/lib/types/univer/plugins/collaboration.d.ts +23 -0
- package/lib/types/univer/plugins/collaboration.js +47 -0
- package/lib/types/univer/plugins/export.d.ts +39 -0
- package/lib/types/univer/plugins/export.js +246 -0
- package/lib/types/univer/plugins/history.d.ts +24 -0
- package/lib/types/univer/plugins/history.js +50 -0
- package/lib/types/univer/plugins/version.d.ts +13 -0
- package/lib/types/univer/plugins/version.js +13 -0
- package/lib/types/version/diff.d.ts +36 -0
- package/lib/types/version/diff.js +156 -0
- package/lib/types/version/grouping.d.ts +7 -0
- package/lib/types/version/grouping.js +94 -0
- package/lib/types/version/records.d.ts +17 -0
- package/lib/types/version/records.js +102 -0
- package/lib/types/version/session.d.ts +36 -0
- package/lib/types/version/session.js +89 -0
- package/lib/types/version/summary.d.ts +24 -0
- package/lib/types/version/summary.js +111 -0
- package/lib/types/workbook-manager.d.ts +191 -0
- package/lib/types/workbook-manager.js +935 -0
- package/lib/types/xlsx-export-worker.d.ts +5 -0
- package/lib/types/xlsx-export-worker.js +24 -0
- package/lib/types/xlsx-export.d.ts +21 -0
- package/lib/types/xlsx-export.js +371 -0
- package/lib/types/xlsx-import-worker.d.ts +1 -0
- package/lib/types/xlsx-import-worker.js +19 -0
- package/lib/univer-client.js +690 -0
- package/lib/workbook-page.js +23826 -0
- package/lib/xlsx-export-worker.js +104 -0
- package/lib/xlsx-import-worker.js +46 -0
- package/package.json +106 -3
- package/src/audit.ts +58 -0
- package/src/client/ProjectImportMenu.tsx +100 -0
- package/src/client/SheetPanel.module.css +1578 -0
- package/src/client/SheetPanel.tsx +1934 -0
- package/src/client/SheetToggle.module.css +73 -0
- package/src/client/SheetToggle.tsx +65 -0
- package/src/client/contract.d.ts +19 -0
- package/src/client/index.ts +131 -0
- package/src/client/persistence/changes.ts +58 -0
- package/src/client/persistence/ids.ts +60 -0
- package/src/client/persistence/merge.ts +392 -0
- package/src/client/store.ts +188 -0
- package/src/client/surface-context.ts +123 -0
- package/src/client/surface-control.ts +13 -0
- package/src/client/transport/workbookApi.ts +355 -0
- package/src/client/univer/SheetView.module.css +110 -0
- package/src/client/univer/StandaloneWorkbookPage.module.css +8 -0
- package/src/client/univer/StandaloneWorkbookPage.tsx +15 -0
- package/src/client/univer/WorkbookCanvas.tsx +128 -0
- package/src/client/univer/WorkbookView.module.css +30 -0
- package/src/client/univer/WorkbookView.tsx +3 -0
- package/src/client/univer/loader.ts +90 -0
- package/src/client/univer/react-dom-client.d.ts +10 -0
- package/src/client/univer/standalone-entry.tsx +19 -0
- package/src/client/univer/standalone.css +12 -0
- package/src/client/univer/univer-locales.d.ts +89 -0
- package/src/client/univer/univer-modules.d.ts +26 -0
- package/src/client/versions/VersionChrome.tsx +114 -0
- package/src/client/versions/inspect.ts +138 -0
- package/src/client/versions/preview-decoration.ts +54 -0
- package/src/client/versions/records.ts +78 -0
- package/src/client/workspace-host.ts +147 -0
- package/src/collaboration-hub.ts +83 -0
- package/src/contract/ids.ts +50 -0
- package/src/contract/mutations.ts +791 -0
- package/src/contract/operations.ts +221 -0
- package/src/contract/snapshots.ts +383 -0
- package/src/contract/univer-api-catalog.ts +289 -0
- package/src/css-modules.d.ts +6 -0
- package/src/errors.ts +25 -0
- package/src/http/assets-routes.ts +177 -0
- package/src/http/collaboration.ts +144 -0
- package/src/http/history.ts +151 -0
- package/src/http/project-files.ts +227 -0
- package/src/http/project-scope.ts +77 -0
- package/src/http/routes.ts +364 -0
- package/src/http/standalone.ts +32 -0
- package/src/http/trunk.ts +322 -0
- package/src/http/worktree.ts +119 -0
- package/src/index.ts +165 -0
- package/src/invariant.ts +17 -0
- package/src/request-params.ts +92 -0
- package/src/store/paths.ts +165 -0
- package/src/store/project.ts +137 -0
- package/src/store/read.ts +454 -0
- package/src/store/retention.ts +106 -0
- package/src/store/write.ts +230 -0
- package/src/tools/arguments.ts +128 -0
- package/src/tools/cells.ts +298 -0
- package/src/tools/index.ts +274 -0
- package/src/tools/render.ts +301 -0
- package/src/tools/univer-api.ts +243 -0
- package/src/tools/workbook-catalog.ts +94 -0
- package/src/tools/workbook-edit.ts +987 -0
- package/src/tools/workbook-find.ts +80 -0
- package/src/tools/workbook-history.ts +63 -0
- package/src/tools/workbook-read.ts +210 -0
- package/src/types.ts +432 -0
- package/src/univer/apply-operation.ts +369 -0
- package/src/univer/chunk.ts +77 -0
- package/src/univer/decoration.ts +111 -0
- package/src/univer/diagnostics.ts +136 -0
- package/src/univer/engine.ts +370 -0
- package/src/univer/entry.ts +14 -0
- package/src/univer/facade.ts +91 -0
- package/src/univer/fonts.ts +148 -0
- package/src/univer/overrides.css +33 -0
- package/src/univer/plugins/collaboration.ts +47 -0
- package/src/univer/plugins/export.ts +236 -0
- package/src/univer/plugins/history.ts +49 -0
- package/src/univer/plugins/version.ts +13 -0
- package/src/version/diff.ts +169 -0
- package/src/version/grouping.ts +96 -0
- package/src/version/records.ts +113 -0
- package/src/version/session.ts +90 -0
- package/src/version/summary.ts +117 -0
- package/src/workbook-manager.ts +1111 -0
- package/src/xlsx-export-worker.ts +34 -0
- package/src/xlsx-export.ts +373 -0
- package/src/xlsx-import-worker.ts +30 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applying a workbook operation to the live canvas (docs/issue/0022 P4).
|
|
3
|
+
*
|
|
4
|
+
* This is the *canvas* half of an operation: the Host has already decided the
|
|
5
|
+
* operation is durable and recorded it, and this turns it into Univer commands
|
|
6
|
+
* so the browser sees the same result without reloading a snapshot. The two
|
|
7
|
+
* halves share one vocabulary (`contract/mutations.ts` applies an operation to a
|
|
8
|
+
* snapshot) and must agree: a command this file cannot express is a bug the user
|
|
9
|
+
* sees as "my edit did not appear".
|
|
10
|
+
*
|
|
11
|
+
* Built as a factory because it needs the mount's facade and its sheet resolver,
|
|
12
|
+
* and because `applyingRemote` has to be visible to the engine's mutation guard:
|
|
13
|
+
* a remote operation we apply ourselves is not a user edit.
|
|
14
|
+
*/
|
|
15
|
+
import { APPEND_HEADER_MISMATCH } from "../contract/operations.js";
|
|
16
|
+
import { DEFAULT_SHEET_ROWS, DEFAULT_SHEET_COLUMNS, normalizeCellFormula } from "../contract/snapshots.js";
|
|
17
|
+
export function createOperationApplier(deps) {
|
|
18
|
+
const { sheetsApi, getSheet } = deps;
|
|
19
|
+
let applyingRemote = false;
|
|
20
|
+
const liveSheetMetadata = new Map();
|
|
21
|
+
const coerceAppendValue = (column, value) => {
|
|
22
|
+
if (value === undefined || value === null)
|
|
23
|
+
return null;
|
|
24
|
+
if (column.type === 'number' || column.type === 'currency') {
|
|
25
|
+
const numeric = typeof value === 'number' ? value : Number(value);
|
|
26
|
+
return Number.isFinite(numeric) ? numeric : String(value);
|
|
27
|
+
}
|
|
28
|
+
if (column.type === 'boolean')
|
|
29
|
+
return typeof value === 'boolean' ? value : String(value) === 'true';
|
|
30
|
+
if (column.type === 'date')
|
|
31
|
+
return value instanceof Date ? value.toISOString().slice(0, 10) : String(value);
|
|
32
|
+
return String(value);
|
|
33
|
+
};
|
|
34
|
+
/** Append tabular data with Univer's range mutation APIs. */
|
|
35
|
+
const applyAppendRows = (operation, payload) => {
|
|
36
|
+
const sheetRef = payload.sheetId ?? payload.sheetName;
|
|
37
|
+
const columns = payload.columns ?? [];
|
|
38
|
+
const rows = payload.rows ?? [];
|
|
39
|
+
if (columns.length === 0)
|
|
40
|
+
throw new Error('至少需要一列');
|
|
41
|
+
const headerRowIndex = payload.headerRowIndex ?? 0;
|
|
42
|
+
// Mirrors the Host snapshot check, so a batch can never pass here and fail
|
|
43
|
+
// there. A report sheet whose real header is not row 0 is addressed
|
|
44
|
+
// explicitly instead of being rejected.
|
|
45
|
+
const verifyHeader = payload.verifyHeader !== false;
|
|
46
|
+
let sheet = getSheet(sheetRef);
|
|
47
|
+
if (sheet === null) {
|
|
48
|
+
if (sheetRef === undefined)
|
|
49
|
+
throw new Error('Univer sheet 不存在');
|
|
50
|
+
// Keep an implicitly created sheet addressable by the same stable
|
|
51
|
+
// reference that the Host snapshot uses. This matters when an append is
|
|
52
|
+
// followed by another command in the same Agent batch.
|
|
53
|
+
const generatedSheetId = sheetRef ?? `sheet-${operation.opId.slice(0, 12)}`;
|
|
54
|
+
const activeWorkbook = sheetsApi.getActiveWorkbook();
|
|
55
|
+
if (activeWorkbook === null)
|
|
56
|
+
throw new Error('Univer workbook 未挂载');
|
|
57
|
+
activeWorkbook.create(payload.name ?? sheetRef ?? 'Sheet', DEFAULT_SHEET_ROWS, DEFAULT_SHEET_COLUMNS, { sheet: { id: generatedSheetId } });
|
|
58
|
+
sheet = getSheet(generatedSheetId) ?? getSheet(sheetRef);
|
|
59
|
+
}
|
|
60
|
+
if (sheet === null || sheet.getRange === undefined)
|
|
61
|
+
throw new Error('Univer sheet 创建失败');
|
|
62
|
+
const headerRange = sheet.getRange(headerRowIndex, 0, 1, columns.length);
|
|
63
|
+
const existingHeader = columns.map((_column, index) => sheet?.getRange?.(headerRowIndex, index).getValue?.());
|
|
64
|
+
const hasExistingHeader = existingHeader.some(value => value !== undefined && value !== null && String(value).length > 0);
|
|
65
|
+
if (verifyHeader && hasExistingHeader && columns.some((column, index) => String(existingHeader[index] ?? '') !== column.label)) {
|
|
66
|
+
throw new Error(APPEND_HEADER_MISMATCH);
|
|
67
|
+
}
|
|
68
|
+
if (!hasExistingHeader)
|
|
69
|
+
headerRange.setValues([columns.map(column => column.label)]);
|
|
70
|
+
const nextRow = Math.max(headerRowIndex + 1, (sheet.getLastRow?.() ?? 0) + 1);
|
|
71
|
+
if (rows.length > 0) {
|
|
72
|
+
const values = rows.map(row => columns.map(column => coerceAppendValue(column, row[column.key])));
|
|
73
|
+
sheet.getRange(nextRow, 0, values.length, columns.length).setValues(values);
|
|
74
|
+
}
|
|
75
|
+
if (sheet.getMaxColumns?.() !== undefined && sheet.setColumnCount !== undefined) {
|
|
76
|
+
sheet.setColumnCount(Math.max(sheet.getMaxColumns?.() ?? DEFAULT_SHEET_COLUMNS, columns.length));
|
|
77
|
+
}
|
|
78
|
+
if (sheet.getMaxRows?.() !== undefined && sheet.setRowCount !== undefined) {
|
|
79
|
+
sheet.setRowCount(Math.max(sheet.getMaxRows?.() ?? DEFAULT_SHEET_ROWS, nextRow + rows.length));
|
|
80
|
+
}
|
|
81
|
+
const sheetId = sheet.getSheetId?.();
|
|
82
|
+
if (payload.meta !== undefined && sheetId !== undefined)
|
|
83
|
+
liveSheetMetadata.set(sheetId, payload.meta);
|
|
84
|
+
};
|
|
85
|
+
/** Sort through the open-source sort preset's Facade mixin. */
|
|
86
|
+
const applySortRange = (payload) => {
|
|
87
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
88
|
+
const range = payload.range;
|
|
89
|
+
const sort = payload.sort;
|
|
90
|
+
if (sheet === null || range === undefined || sort === undefined)
|
|
91
|
+
throw new Error('sort_range 参数无效');
|
|
92
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
93
|
+
if (target.sort === undefined)
|
|
94
|
+
throw new Error('当前 Univer Sort Facade 不可用');
|
|
95
|
+
target.sort({ column: sort.column - range.startColumn, ascending: sort.direction !== 'desc' });
|
|
96
|
+
};
|
|
97
|
+
/** Apply the JSON-safe Datasheet style subset through Univer's Facade. */
|
|
98
|
+
const applyCellStyle = async (sheet, range, target, style) => {
|
|
99
|
+
if (style.bg !== undefined) {
|
|
100
|
+
if (range.setBackgroundColor === undefined)
|
|
101
|
+
throw new Error('当前 Univer Facade 不支持背景色');
|
|
102
|
+
range.setBackgroundColor(style.bg);
|
|
103
|
+
}
|
|
104
|
+
if (style.cl !== undefined) {
|
|
105
|
+
if (range.setFontColor === undefined)
|
|
106
|
+
throw new Error('当前 Univer Facade 不支持字体颜色');
|
|
107
|
+
range.setFontColor(style.cl);
|
|
108
|
+
}
|
|
109
|
+
if (style.ff !== undefined) {
|
|
110
|
+
if (range.setFontFamily === undefined)
|
|
111
|
+
throw new Error('当前 Univer Facade 不支持字体');
|
|
112
|
+
range.setFontFamily(style.ff);
|
|
113
|
+
}
|
|
114
|
+
if (style.fs !== undefined) {
|
|
115
|
+
if (range.setFontSize === undefined)
|
|
116
|
+
throw new Error('当前 Univer Facade 不支持字号');
|
|
117
|
+
range.setFontSize(style.fs);
|
|
118
|
+
}
|
|
119
|
+
if (style.bl !== undefined) {
|
|
120
|
+
if (range.setFontWeight === undefined)
|
|
121
|
+
throw new Error('当前 Univer Facade 不支持粗体');
|
|
122
|
+
range.setFontWeight(style.bl ? 'bold' : 'normal');
|
|
123
|
+
}
|
|
124
|
+
if (style.it !== undefined) {
|
|
125
|
+
if (range.setFontStyle === undefined)
|
|
126
|
+
throw new Error('当前 Univer Facade 不支持斜体');
|
|
127
|
+
range.setFontStyle(style.it ? 'italic' : 'normal');
|
|
128
|
+
}
|
|
129
|
+
if (style.ul !== undefined) {
|
|
130
|
+
if (range.setFontLine === undefined)
|
|
131
|
+
throw new Error('当前 Univer Facade 不支持下划线');
|
|
132
|
+
range.setFontLine(style.ul ? 'underline' : 'none');
|
|
133
|
+
}
|
|
134
|
+
if (style.st !== undefined) {
|
|
135
|
+
if (range.setFontLine === undefined)
|
|
136
|
+
throw new Error('当前 Univer Facade 不支持删除线');
|
|
137
|
+
range.setFontLine(style.st ? 'line-through' : 'none');
|
|
138
|
+
}
|
|
139
|
+
if (style.ht !== undefined) {
|
|
140
|
+
if (range.setHorizontalAlignment === undefined)
|
|
141
|
+
throw new Error('当前 Univer Facade 不支持水平对齐');
|
|
142
|
+
const alignment = style.ht === 1 ? 'left' : style.ht === 2 ? 'center' : 'normal';
|
|
143
|
+
range.setHorizontalAlignment(alignment);
|
|
144
|
+
}
|
|
145
|
+
if (style.vt !== undefined) {
|
|
146
|
+
if (range.setVerticalAlignment === undefined)
|
|
147
|
+
throw new Error('当前 Univer Facade 不支持垂直对齐');
|
|
148
|
+
const alignment = style.vt === 1 ? 'top' : style.vt === 2 ? 'middle' : 'bottom';
|
|
149
|
+
range.setVerticalAlignment(alignment);
|
|
150
|
+
}
|
|
151
|
+
if (style.tr !== undefined) {
|
|
152
|
+
if (range.setTextRotation === undefined)
|
|
153
|
+
throw new Error('当前 Univer Facade 不支持文字旋转');
|
|
154
|
+
range.setTextRotation(style.tr);
|
|
155
|
+
}
|
|
156
|
+
// Univer 0.25 exposes number format through the native SetStyleCommand;
|
|
157
|
+
// use the command bridge for this one style that the Facade does not wrap.
|
|
158
|
+
if (style.n !== undefined) {
|
|
159
|
+
await sheetsApi.executeCommand('sheet.command.set-style', {
|
|
160
|
+
unitId: sheetsApi.getActiveWorkbook()?.getId(),
|
|
161
|
+
subUnitId: sheet.getSheetId?.(),
|
|
162
|
+
range: target,
|
|
163
|
+
style: { type: 'n', value: style.n },
|
|
164
|
+
}, { fromCollab: true });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const applyOperation = async (operation) => {
|
|
168
|
+
const workbook = sheetsApi.getActiveWorkbook();
|
|
169
|
+
if (workbook === null)
|
|
170
|
+
throw new Error('Univer workbook 未挂载');
|
|
171
|
+
const payload = operation.payload;
|
|
172
|
+
applyingRemote = true;
|
|
173
|
+
try {
|
|
174
|
+
switch (operation.type) {
|
|
175
|
+
case 'update_cells': {
|
|
176
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
177
|
+
if (sheet === null)
|
|
178
|
+
throw new Error(`Univer sheet 不存在:${payload.sheetId ?? payload.sheetName ?? ''}`);
|
|
179
|
+
const cells = payload.cells ?? [];
|
|
180
|
+
if (cells.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
const unitId = workbook.getId();
|
|
183
|
+
const sheetId = sheet.getSheetId?.();
|
|
184
|
+
if (sheetId === undefined)
|
|
185
|
+
throw new Error('Univer sheet id 缺失');
|
|
186
|
+
const value = {};
|
|
187
|
+
const clearCells = [];
|
|
188
|
+
for (const cell of cells) {
|
|
189
|
+
// The same normalization the snapshot applies: the canvas and the
|
|
190
|
+
// stored snapshot are two views of one workbook.
|
|
191
|
+
const formula = normalizeCellFormula(cell.formula);
|
|
192
|
+
if (cell.value === null && formula === undefined) {
|
|
193
|
+
clearCells.push(cell);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
value[String(cell.row)] ??= {};
|
|
197
|
+
value[String(cell.row)][String(cell.column)] = { v: cell.value, ...(formula === undefined ? {} : { f: formula }) };
|
|
198
|
+
}
|
|
199
|
+
if (Object.keys(value).length > 0) {
|
|
200
|
+
await sheetsApi.executeCommand('sheet.command.set-range-values', { unitId, subUnitId: sheetId, value }, { fromCollab: true });
|
|
201
|
+
}
|
|
202
|
+
// A null value is a content clear rather than a value assignment in
|
|
203
|
+
// Univer. Keep clears grouped after the fast sparse value command.
|
|
204
|
+
for (const cell of clearCells)
|
|
205
|
+
sheet.getRange(cell.row, cell.column).clearContent();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
case 'clear_range': {
|
|
209
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
210
|
+
const range = payload.range;
|
|
211
|
+
if (sheet === null || range === undefined)
|
|
212
|
+
throw new Error('clear_range 参数无效');
|
|
213
|
+
sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1).clearContent();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
case 'set_hyperlink': {
|
|
217
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
218
|
+
const range = payload.range;
|
|
219
|
+
if (sheet === null || range === undefined || payload.url === undefined)
|
|
220
|
+
throw new Error('set_hyperlink 参数无效');
|
|
221
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
222
|
+
if (target.setHyperLink === undefined)
|
|
223
|
+
throw new Error('当前 Univer HyperLink Facade 不可用');
|
|
224
|
+
const current = target.getValue?.();
|
|
225
|
+
const label = payload.label ?? (current === undefined || current === null || String(current).length === 0 ? undefined : String(current));
|
|
226
|
+
await target.setHyperLink(payload.url, label);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
case 'remove_hyperlink': {
|
|
230
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
231
|
+
const range = payload.range;
|
|
232
|
+
if (sheet === null || range === undefined)
|
|
233
|
+
throw new Error('remove_hyperlink 参数无效');
|
|
234
|
+
// DSH's operation contract mirrors set_hyperlink and targets only the
|
|
235
|
+
// top-left cell. A multi-cell range must not remove unrelated links.
|
|
236
|
+
const target = sheet.getRange(range.startRow, range.startColumn);
|
|
237
|
+
if (target.cancelHyperLink === undefined)
|
|
238
|
+
throw new Error('当前 Univer HyperLink Facade 不可用');
|
|
239
|
+
target.cancelHyperLink();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
case 'delete_cells': {
|
|
243
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
244
|
+
const range = payload.range;
|
|
245
|
+
const shift = payload.shift ?? 'clear';
|
|
246
|
+
if (sheet === null || range === undefined)
|
|
247
|
+
throw new Error('delete_cells 参数无效');
|
|
248
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
249
|
+
if (shift === 'clear') {
|
|
250
|
+
target.clearContent();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (target.deleteCells === undefined)
|
|
254
|
+
throw new Error('当前 Univer Facade 不支持结构性删除单元格');
|
|
255
|
+
target.deleteCells(shift === 'left' ? 0 : 1);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
case 'insert_rows': {
|
|
259
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
260
|
+
if (sheet?.insertRows === undefined || payload.range === undefined)
|
|
261
|
+
throw new Error('insert_rows 参数无效');
|
|
262
|
+
sheet.insertRows(payload.range.startRow, payload.count ?? 1);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
case 'delete_rows': {
|
|
266
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
267
|
+
if (sheet?.deleteRows === undefined || payload.range === undefined)
|
|
268
|
+
throw new Error('delete_rows 参数无效');
|
|
269
|
+
sheet.deleteRows(payload.range.startRow, payload.count ?? 1);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
case 'insert_columns': {
|
|
273
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
274
|
+
if (sheet?.insertColumns === undefined || payload.range === undefined)
|
|
275
|
+
throw new Error('insert_columns 参数无效');
|
|
276
|
+
sheet.insertColumns(payload.range.startColumn, payload.count ?? 1);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
case 'delete_columns': {
|
|
280
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
281
|
+
if (sheet?.deleteColumns === undefined || payload.range === undefined)
|
|
282
|
+
throw new Error('delete_columns 参数无效');
|
|
283
|
+
sheet.deleteColumns(payload.range.startColumn, payload.count ?? 1);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
case 'create_sheet':
|
|
287
|
+
workbook.create(payload.name ?? payload.sheetId ?? 'Sheet', DEFAULT_SHEET_ROWS, DEFAULT_SHEET_COLUMNS, {
|
|
288
|
+
sheet: { id: payload.sheetId ?? `sheet-${operation.opId.slice(0, 12)}` },
|
|
289
|
+
});
|
|
290
|
+
return;
|
|
291
|
+
case 'rename_sheet': {
|
|
292
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
293
|
+
if (sheet?.setName === undefined || payload.name === undefined)
|
|
294
|
+
throw new Error('rename_sheet 参数无效');
|
|
295
|
+
sheet.setName(payload.name);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
case 'delete_sheet': {
|
|
299
|
+
if (workbook.deleteSheet === undefined)
|
|
300
|
+
throw new Error('当前 Univer Facade 不支持删除 sheet');
|
|
301
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
302
|
+
if (sheet === null)
|
|
303
|
+
throw new Error(`Univer sheet 不存在:${payload.sheetId ?? payload.sheetName ?? ''}`);
|
|
304
|
+
// Pass the Facade sheet object so a name-based Agent operation is
|
|
305
|
+
// resolved to the actual sheet id before Univer executes the command.
|
|
306
|
+
workbook.deleteSheet(sheet);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
case 'format_cells': {
|
|
310
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
311
|
+
const range = payload.range;
|
|
312
|
+
if (sheet === null || range === undefined || payload.style === undefined)
|
|
313
|
+
throw new Error('format_cells 参数无效');
|
|
314
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
315
|
+
await applyCellStyle(sheet, target, range, payload.style);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
case 'merge_cells': {
|
|
319
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
320
|
+
const range = payload.range ?? payload.merge;
|
|
321
|
+
if (sheet === null || range === undefined)
|
|
322
|
+
throw new Error('merge_cells 参数无效');
|
|
323
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
324
|
+
if (target.merge === undefined)
|
|
325
|
+
throw new Error('当前 Univer Facade 不支持合并单元格');
|
|
326
|
+
target.merge({ defaultMerge: true, isForceMerge: true });
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
case 'unmerge_cells': {
|
|
330
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
331
|
+
const range = payload.range ?? payload.merge;
|
|
332
|
+
if (sheet === null || range === undefined)
|
|
333
|
+
throw new Error('unmerge_cells 参数无效');
|
|
334
|
+
const target = sheet.getRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
335
|
+
if (target.breakApart === undefined)
|
|
336
|
+
throw new Error('当前 Univer Facade 不支持取消合并');
|
|
337
|
+
target.breakApart();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
case 'freeze_panes': {
|
|
341
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
342
|
+
const freeze = payload.freeze;
|
|
343
|
+
if (sheet === null || freeze === undefined || sheet.setFreeze === undefined || sheet.cancelFreeze === undefined)
|
|
344
|
+
throw new Error('freeze_panes 参数无效');
|
|
345
|
+
const rows = Math.max(0, freeze.rows ?? 0);
|
|
346
|
+
const columns = Math.max(0, freeze.columns ?? 0);
|
|
347
|
+
if (rows === 0 && columns === 0)
|
|
348
|
+
sheet.cancelFreeze();
|
|
349
|
+
else
|
|
350
|
+
sheet.setFreeze({ startRow: rows, startColumn: columns, xSplit: columns, ySplit: rows });
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
case 'set_column_width': {
|
|
354
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
355
|
+
const index = payload.range?.startColumn;
|
|
356
|
+
if (sheet === null || index === undefined || payload.size === undefined || sheet.setColumnWidth === undefined)
|
|
357
|
+
throw new Error('set_column_width 参数无效');
|
|
358
|
+
sheet.setColumnWidth(index, payload.size);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
case 'set_row_height': {
|
|
362
|
+
const sheet = getSheet(payload.sheetId ?? payload.sheetName);
|
|
363
|
+
const index = payload.range?.startRow;
|
|
364
|
+
if (sheet === null || index === undefined || payload.size === undefined || sheet.setRowHeight === undefined)
|
|
365
|
+
throw new Error('set_row_height 参数无效');
|
|
366
|
+
sheet.setRowHeight(index, payload.size);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
case 'append_rows':
|
|
370
|
+
applyAppendRows(operation, payload);
|
|
371
|
+
return;
|
|
372
|
+
case 'sort_range':
|
|
373
|
+
applySortRange(payload);
|
|
374
|
+
return;
|
|
375
|
+
case 'batch': {
|
|
376
|
+
for (const child of payload.operations ?? []) {
|
|
377
|
+
if (child.type === 'batch' || child.type === 'snapshot_replace')
|
|
378
|
+
throw new Error('batch 不支持嵌套 batch 或 snapshot_replace');
|
|
379
|
+
// Re-enable the guard after each recursive call. The child call
|
|
380
|
+
// owns the same operation adapter but resets the guard in finally.
|
|
381
|
+
applyingRemote = true;
|
|
382
|
+
await applyOperation({ ...operation, type: child.type, payload: child.payload });
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
case 'snapshot_replace':
|
|
387
|
+
// An arbitrary snapshot has no equivalent single Univer command.
|
|
388
|
+
throw new Error(`operation ${operation.type} 需要重新加载 snapshot`);
|
|
389
|
+
default:
|
|
390
|
+
throw new Error(`未知 workbook operation:${String(operation.type)}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
finally {
|
|
394
|
+
applyingRemote = false;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
return { apply: applyOperation, isApplyingRemote: () => applyingRemote, liveSheetMetadata };
|
|
398
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-chunk contract, declared once (docs/issue/0002 PRD v3.2 §7.2).
|
|
3
|
+
*
|
|
4
|
+
* Two bundles have to agree on this shape without importing each other:
|
|
5
|
+
* `src/univer/entry.ts` builds the ~16MB Univer IIFE, and
|
|
6
|
+
* `src/client/univer/loader.ts` loads it lazily on first sheet render. Keeping the interface *and* the
|
|
7
|
+
* `window` global here means a signature change cannot be made on one side only
|
|
8
|
+
* — which is exactly how the preview's `readOnly` option would otherwise have
|
|
9
|
+
* been silently dropped by the loader.
|
|
10
|
+
*
|
|
11
|
+
* Type-only: this module emits no runtime code, so it is erased from both
|
|
12
|
+
* bundles and cannot pull Univer into the boot bundle.
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-data-table/univer/chunk
|
|
15
|
+
*/
|
|
16
|
+
import type { WorkbookFocusRange, WorkbookLocalChange, WorkbookOperation, WorkbookPresencePayload, WorkbookSelectionRange } from '../types.ts';
|
|
17
|
+
/** Mounted Univer residency: dispose releases the engine. */
|
|
18
|
+
export interface WorkbookMount {
|
|
19
|
+
save(): unknown;
|
|
20
|
+
applyOperation(operation: WorkbookOperation): Promise<void>;
|
|
21
|
+
/** Focus one cell range in the active workbook. */
|
|
22
|
+
focusRange(range: WorkbookFocusRange): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Show a different workbook in the same residency (same Univer instance, new
|
|
25
|
+
* unit). Returns false when the swap was impossible, so the caller can fall
|
|
26
|
+
* back to a full remount. PRD v3.5 §7.2.
|
|
27
|
+
*/
|
|
28
|
+
swapWorkbook(next: object, options?: {
|
|
29
|
+
decoration?: WorkbookDecoration;
|
|
30
|
+
}): boolean;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|
|
33
|
+
/** Which cells a preview paints, and why (docs/issue/0002 PRD v3.3 §7.2). */
|
|
34
|
+
export type WorkbookDecorationTone = 'add' | 'remove' | 'modify';
|
|
35
|
+
export interface WorkbookDecorationCell {
|
|
36
|
+
sheetRef?: string;
|
|
37
|
+
row: number;
|
|
38
|
+
column: number;
|
|
39
|
+
tone: WorkbookDecorationTone;
|
|
40
|
+
}
|
|
41
|
+
export interface WorkbookDecoration {
|
|
42
|
+
cells: WorkbookDecorationCell[];
|
|
43
|
+
/** Cell to select so the viewport lands on what changed. */
|
|
44
|
+
focus?: {
|
|
45
|
+
sheetRef?: string;
|
|
46
|
+
row: number;
|
|
47
|
+
column: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Mount options forwarded to the engine. `readOnly` mounts a preview residency:
|
|
52
|
+
* the snapshot is a past Version, so the canvas reports no edits, drops the
|
|
53
|
+
* editing chrome and turns Univer's workbook-level edit permission off
|
|
54
|
+
* (PRD v3.2 §7.2). `decoration` is the diff overlay (PRD v3.3 §7.2).
|
|
55
|
+
*/
|
|
56
|
+
export interface WorkbookMountOptions {
|
|
57
|
+
readOnly?: boolean;
|
|
58
|
+
decoration?: WorkbookDecoration;
|
|
59
|
+
}
|
|
60
|
+
/** The engine surface the chunk exposes. */
|
|
61
|
+
export interface UniverChunkApi {
|
|
62
|
+
mountWorkbook(container: HTMLElement, workbookData: object, onDirty?: (change?: WorkbookLocalChange) => void, onPresence?: (presence: WorkbookPresencePayload) => void, options?: WorkbookMountOptions, onSelection?: (ranges: readonly WorkbookSelectionRange[]) => void): WorkbookMount;
|
|
63
|
+
}
|
|
64
|
+
declare global {
|
|
65
|
+
interface Window {
|
|
66
|
+
__bestExcelUniver?: UniverChunkApi;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-chunk contract, declared once (docs/issue/0002 PRD v3.2 §7.2).
|
|
3
|
+
*
|
|
4
|
+
* Two bundles have to agree on this shape without importing each other:
|
|
5
|
+
* `src/univer/entry.ts` builds the ~16MB Univer IIFE, and
|
|
6
|
+
* `src/client/univer/loader.ts` loads it lazily on first sheet render. Keeping the interface *and* the
|
|
7
|
+
* `window` global here means a signature change cannot be made on one side only
|
|
8
|
+
* — which is exactly how the preview's `readOnly` option would otherwise have
|
|
9
|
+
* been silently dropped by the loader.
|
|
10
|
+
*
|
|
11
|
+
* Type-only: this module emits no runtime code, so it is erased from both
|
|
12
|
+
* bundles and cannot pull Univer into the boot bundle.
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-data-table/univer/chunk
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The diff overlay and the read-only lock (docs/issue/0022 P4, PRD v3.3 §7.2).
|
|
3
|
+
*
|
|
4
|
+
* Order matters twice over: the tint is written through `SetStyleCommand`, which
|
|
5
|
+
* the workbook's editable permission gates, so it has to land **before**
|
|
6
|
+
* `setEditable(false)`; and the selection has to be made before the lock too, so
|
|
7
|
+
* stepping a Version moves the viewport onto the cell that changed instead of
|
|
8
|
+
* leaving the reader to hunt for it.
|
|
9
|
+
*
|
|
10
|
+
* Fire-and-forget: `mountWorkbook` returns synchronously and the canvas is usable
|
|
11
|
+
* immediately — the lock is a frame or two behind (see {@link lockWorkbook}),
|
|
12
|
+
* and the mutation guard in the engine covers that window.
|
|
13
|
+
*
|
|
14
|
+
* The three tints are the contract with `VersionInspectBar`'s legend: the swatch
|
|
15
|
+
* a reader sees beside `新增 2` is painted with the same hex the engine writes
|
|
16
|
+
* into the cell.
|
|
17
|
+
*/
|
|
18
|
+
import type { WorkbookDecoration } from './chunk.ts';
|
|
19
|
+
import type { SheetsApi, SheetResolver } from './facade.ts';
|
|
20
|
+
/** Tint per change direction; shared with the comparison bar's legend. */
|
|
21
|
+
export declare const DECORATION_TINT: {
|
|
22
|
+
readonly add: "#e6f7ee";
|
|
23
|
+
readonly modify: "#fff5e0";
|
|
24
|
+
readonly remove: "#fdeceb";
|
|
25
|
+
};
|
|
26
|
+
export interface DecorationApplier {
|
|
27
|
+
/** Paint a diff and, for a read-only residency, lock the workbook. */
|
|
28
|
+
apply(plan: WorkbookDecoration | undefined): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function createDecorationApplier(deps: {
|
|
31
|
+
sheetsApi: SheetsApi;
|
|
32
|
+
getSheet: SheetResolver;
|
|
33
|
+
readOnly: boolean;
|
|
34
|
+
}): DecorationApplier;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Tint per change direction; shared with the comparison bar's legend. */
|
|
2
|
+
export const DECORATION_TINT = { add: '#e6f7ee', modify: '#fff5e0', remove: '#fdeceb' };
|
|
3
|
+
/**
|
|
4
|
+
* Univer's permission service re-creates the workbook's permission points
|
|
5
|
+
* **after** the mount, with their defaults — so a lock applied at mount is
|
|
6
|
+
* silently undone, and a preview stayed editable (GUI feedback 2026-09-23:
|
|
7
|
+
* "why can I still type into a comparison?").
|
|
8
|
+
*
|
|
9
|
+
* Measured against the real engine, mounted with `readOnly: true`:
|
|
10
|
+
*
|
|
11
|
+
* t=0 setEditable(false) → canEdit() false (the lock lands)
|
|
12
|
+
* t=100ms canEdit() true ← the service re-added it
|
|
13
|
+
* t=200ms setEditable(false) → canEdit() false (and it stays off)
|
|
14
|
+
*
|
|
15
|
+
* `WorkbookPermissionService._init` iterates the existing sheet units and calls
|
|
16
|
+
* `addPermissionPoint` for each point, and `addPermissionPoint` takes the
|
|
17
|
+
* point's *default* value — `true` for `WorkbookEdit`. That write is what undid
|
|
18
|
+
* the mount-time lock. It happens once, in the first ~100ms; the lock is
|
|
19
|
+
* therefore re-asserted a few times over the first second rather than once.
|
|
20
|
+
*
|
|
21
|
+
* The lock is Univer's own workbook permission — the mechanism a protected
|
|
22
|
+
* sheet uses — and with it off the cell editor refuses to open too
|
|
23
|
+
* (`sheet.operation.set-cell-edit-visible` returns false), so a preview cannot
|
|
24
|
+
* be typed into at all, not merely have its edits refused on commit.
|
|
25
|
+
*
|
|
26
|
+
* Delays rather than animation frames: a frame loop stops while the page is
|
|
27
|
+
* hidden, and the point of these attempts is to land *after* a write that
|
|
28
|
+
* happens on Univer's own schedule.
|
|
29
|
+
*/
|
|
30
|
+
const LOCK_RETRY_DELAYS_MS = [0, 150, 400, 900];
|
|
31
|
+
function lockWorkbook(sheetsApi) {
|
|
32
|
+
const attempt = (index) => {
|
|
33
|
+
try {
|
|
34
|
+
sheetsApi.getActiveWorkbook()?.setEditable?.(false);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Permission point unavailable: the mutation guard in the engine still
|
|
38
|
+
// refuses to report an edit from a read-only residency.
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const next = index + 1;
|
|
42
|
+
if (next < LOCK_RETRY_DELAYS_MS.length) {
|
|
43
|
+
setTimeout(() => attempt(next), LOCK_RETRY_DELAYS_MS[next]);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
attempt(0);
|
|
47
|
+
}
|
|
48
|
+
export function createDecorationApplier(deps) {
|
|
49
|
+
const { sheetsApi, getSheet, readOnly } = deps;
|
|
50
|
+
return {
|
|
51
|
+
apply: (plan) => {
|
|
52
|
+
void (async () => {
|
|
53
|
+
const workbook = sheetsApi.getActiveWorkbook();
|
|
54
|
+
if (plan !== undefined) {
|
|
55
|
+
for (const cell of plan.cells) {
|
|
56
|
+
// Paint every cell the caller sent: which tones exist in the shown
|
|
57
|
+
// snapshot is the caller's decision, because the same diff has a
|
|
58
|
+
// different paintable side depending on which Version is on screen.
|
|
59
|
+
try {
|
|
60
|
+
await getSheet(cell.sheetRef)?.getRange(cell.row, cell.column)?.setBackgroundColor?.(DECORATION_TINT[cell.tone]);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// A cell outside the sheet's bounds: skip it, keep the rest.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const focus = plan?.focus;
|
|
68
|
+
if (focus !== undefined && workbook !== null) {
|
|
69
|
+
try {
|
|
70
|
+
const sheet = getSheet(focus.sheetRef);
|
|
71
|
+
const range = sheet?.getRange(focus.row, focus.column);
|
|
72
|
+
if (sheet !== null && range !== undefined) {
|
|
73
|
+
const sheetId = sheet.getSheetId?.();
|
|
74
|
+
if (sheetId !== undefined)
|
|
75
|
+
workbook.setActiveSheet?.(sheetId);
|
|
76
|
+
workbook.setActiveRange?.(range);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Selection is an affordance, not a correctness requirement.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (readOnly)
|
|
84
|
+
lockWorkbook(sheetsApi);
|
|
85
|
+
})();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { WorkbookLocalChange, WorkbookPresencePayload } from '../types.ts';
|
|
2
|
+
/** Why the current mutation could not become an incremental cell change. */
|
|
3
|
+
export declare function noteCellMutation(_commandId: string, mapped: boolean, detail?: {
|
|
4
|
+
reason?: string;
|
|
5
|
+
keys?: string[];
|
|
6
|
+
}): void;
|
|
7
|
+
/**
|
|
8
|
+
* Publish the outcome of one persisted mutation. This runs for *every*
|
|
9
|
+
* mutation the workbook persists, so a command id we do not map is still
|
|
10
|
+
* visible instead of leaving the probe undefined:
|
|
11
|
+
*
|
|
12
|
+
* __dshLastCellMutation // { commandId, mapped, reason?, keys? }
|
|
13
|
+
*/
|
|
14
|
+
export declare function publishMutationAttempt(commandId: string, mapped: boolean): void;
|
|
15
|
+
export declare function cellKeysOf(cellValue: unknown): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Convert Univer's sparse set-range-values mutation into the small DSH edit
|
|
18
|
+
* contract. Formatting, pasted rich objects, and structural commands return
|
|
19
|
+
* undefined so the browser deliberately falls back to a full snapshot save.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toLocalCellChange(commandInfo: {
|
|
22
|
+
id: string;
|
|
23
|
+
params?: unknown;
|
|
24
|
+
}): WorkbookLocalChange | undefined;
|
|
25
|
+
/** Convert selection/focus commands into a compact ephemeral presence event.
|
|
26
|
+
* Unknown commands are intentionally ignored so UI internals never become
|
|
27
|
+
* durable edits or high-volume network traffic. */
|
|
28
|
+
export declare function toPresence(commandInfo: {
|
|
29
|
+
id: string;
|
|
30
|
+
params?: unknown;
|
|
31
|
+
}): WorkbookPresencePayload | undefined;
|