@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,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-way merge for Univer workbook snapshots.
|
|
3
|
+
*
|
|
4
|
+
* The browser keeps the last server snapshot as the base. When a user save
|
|
5
|
+
* races an Agent/user write, this module compares base/local/remote at cell
|
|
6
|
+
* granularity, adopts one-sided changes automatically, and keeps local values
|
|
7
|
+
* for true conflicts while adding a visible conflict style.
|
|
8
|
+
*/
|
|
9
|
+
import { cellAddress } from '#contracts';
|
|
10
|
+
import { cloneSnapshot, normalizeCellFormula, stableJson, toCellValue } from "../../contract/snapshots.js";
|
|
11
|
+
const MERGEABLE_SHEET_KEYS = [
|
|
12
|
+
'name',
|
|
13
|
+
'tabColor',
|
|
14
|
+
'hidden',
|
|
15
|
+
'zoomRatio',
|
|
16
|
+
'defaultColumnWidth',
|
|
17
|
+
'defaultRowHeight',
|
|
18
|
+
'freeze',
|
|
19
|
+
'mergeData',
|
|
20
|
+
'rowData',
|
|
21
|
+
'columnData',
|
|
22
|
+
'showGridlines',
|
|
23
|
+
'rowHeader',
|
|
24
|
+
'columnHeader',
|
|
25
|
+
'rightToLeft',
|
|
26
|
+
'defaultStyle',
|
|
27
|
+
'dataSheetMeta',
|
|
28
|
+
];
|
|
29
|
+
/** Merge local and remote snapshots against the last common base snapshot. */
|
|
30
|
+
export function mergeWorkbookSnapshots(baseSnapshot, localSnapshot, remoteSnapshot) {
|
|
31
|
+
const base = asRecord(baseSnapshot);
|
|
32
|
+
const local = asRecord(localSnapshot);
|
|
33
|
+
const remote = asRecord(remoteSnapshot);
|
|
34
|
+
const merged = cloneSnapshot(local);
|
|
35
|
+
const conflicts = [];
|
|
36
|
+
const conflictStyleMap = {};
|
|
37
|
+
const localStyles = asRecord(merged.styles);
|
|
38
|
+
const remoteStyles = asRecord(remote.styles);
|
|
39
|
+
const remoteStyleMap = new Map();
|
|
40
|
+
const adoptRemoteCell = (cell) => {
|
|
41
|
+
if (cell === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
const next = cloneSnapshot(cell);
|
|
44
|
+
if (typeof next.s === 'string')
|
|
45
|
+
next.s = remapRemoteStyle(next.s);
|
|
46
|
+
return next;
|
|
47
|
+
};
|
|
48
|
+
const remapRemoteStyle = (styleId) => {
|
|
49
|
+
const known = remoteStyleMap.get(styleId);
|
|
50
|
+
if (known !== undefined)
|
|
51
|
+
return known;
|
|
52
|
+
const remoteStyle = remoteStyles[styleId];
|
|
53
|
+
if (remoteStyle === undefined) {
|
|
54
|
+
remoteStyleMap.set(styleId, styleId);
|
|
55
|
+
return styleId;
|
|
56
|
+
}
|
|
57
|
+
const localStyle = localStyles[styleId];
|
|
58
|
+
if (localStyle === undefined || deepEqual(localStyle, remoteStyle)) {
|
|
59
|
+
localStyles[styleId] = cloneSnapshot(remoteStyle);
|
|
60
|
+
remoteStyleMap.set(styleId, styleId);
|
|
61
|
+
return styleId;
|
|
62
|
+
}
|
|
63
|
+
let candidate = `${styleId}-remote`;
|
|
64
|
+
let suffix = 1;
|
|
65
|
+
while (localStyles[candidate] !== undefined)
|
|
66
|
+
candidate = `${styleId}-remote-${suffix++}`;
|
|
67
|
+
localStyles[candidate] = cloneSnapshot(remoteStyle);
|
|
68
|
+
remoteStyleMap.set(styleId, candidate);
|
|
69
|
+
return candidate;
|
|
70
|
+
};
|
|
71
|
+
const baseSheets = asRecord(base.sheets);
|
|
72
|
+
const localSheets = asRecord(local.sheets);
|
|
73
|
+
const remoteSheets = asRecord(remote.sheets);
|
|
74
|
+
const mergedSheets = {};
|
|
75
|
+
const allSheetIds = new Set([...Object.keys(baseSheets), ...Object.keys(localSheets), ...Object.keys(remoteSheets)]);
|
|
76
|
+
for (const sheetId of allSheetIds) {
|
|
77
|
+
const baseSheet = asOptionalRecord(baseSheets[sheetId]);
|
|
78
|
+
const localSheet = asOptionalRecord(localSheets[sheetId]);
|
|
79
|
+
const remoteSheet = asOptionalRecord(remoteSheets[sheetId]);
|
|
80
|
+
if (localSheet === undefined && remoteSheet === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
if (localSheet === undefined) {
|
|
83
|
+
if (baseSheet !== undefined && remoteSheet !== undefined && !deepEqual(remoteSheet, baseSheet)) {
|
|
84
|
+
conflicts.push(sheetConflict(sheetId, remoteSheet, '本地删除 sheet,但远端同时修改了它'));
|
|
85
|
+
}
|
|
86
|
+
// A sheet deleted locally remains deleted. A remote-only sheet is kept.
|
|
87
|
+
if (baseSheet === undefined && remoteSheet !== undefined)
|
|
88
|
+
mergedSheets[sheetId] = cloneAndRemapSheet(remoteSheet, adoptRemoteCell);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (remoteSheet === undefined) {
|
|
92
|
+
if (baseSheet !== undefined && !deepEqual(localSheet, baseSheet)) {
|
|
93
|
+
conflicts.push(sheetConflict(sheetId, localSheet, '远端删除 sheet,但本地同时修改了它'));
|
|
94
|
+
}
|
|
95
|
+
// A sheet deleted remotely is deleted only when local had not changed it.
|
|
96
|
+
if (baseSheet !== undefined && deepEqual(localSheet, baseSheet))
|
|
97
|
+
continue;
|
|
98
|
+
mergedSheets[sheetId] = cloneSnapshot(localSheet);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const result = mergeSheet(sheetId, baseSheet, localSheet, remoteSheet, adoptRemoteCell, localStyles);
|
|
102
|
+
mergedSheets[sheetId] = result.sheet;
|
|
103
|
+
conflicts.push(...result.conflicts);
|
|
104
|
+
Object.assign(conflictStyleMap, result.conflictStyleMap);
|
|
105
|
+
}
|
|
106
|
+
merged.sheets = mergedSheets;
|
|
107
|
+
merged.styles = localStyles;
|
|
108
|
+
const baseOrder = asStringArray(base.sheetOrder);
|
|
109
|
+
const localOrder = asStringArray(local.sheetOrder);
|
|
110
|
+
const remoteOrder = asStringArray(remote.sheetOrder);
|
|
111
|
+
if (deepEqual(localOrder, baseOrder) && !deepEqual(remoteOrder, baseOrder))
|
|
112
|
+
merged.sheetOrder = remoteOrder.filter(id => mergedSheets[id] !== undefined);
|
|
113
|
+
else if (!deepEqual(localOrder, remoteOrder) && !deepEqual(remoteOrder, baseOrder)) {
|
|
114
|
+
conflicts.push({ kind: 'structure', description: '本地和远端同时调整了 sheet 顺序,当前保留本地顺序' });
|
|
115
|
+
}
|
|
116
|
+
merged.sheetOrder = normalizeSheetOrder(asStringArray(merged.sheetOrder), mergedSheets);
|
|
117
|
+
return {
|
|
118
|
+
snapshot: merged,
|
|
119
|
+
conflicts,
|
|
120
|
+
autoMergedCells: countAutoMergedCells(base, local, remote, mergedSheets),
|
|
121
|
+
conflictStyleMap,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Remove temporary conflict styles before a merged snapshot is persisted. */
|
|
125
|
+
export function stripConflictMarkers(snapshot, conflictStyleMap) {
|
|
126
|
+
if (Object.keys(conflictStyleMap).length === 0)
|
|
127
|
+
return snapshot;
|
|
128
|
+
const next = cloneSnapshot(snapshot);
|
|
129
|
+
const sheets = asRecord(next.sheets);
|
|
130
|
+
for (const sheet of Object.values(sheets)) {
|
|
131
|
+
const cellData = asRecord(sheet).cellData;
|
|
132
|
+
if (cellData === undefined)
|
|
133
|
+
continue;
|
|
134
|
+
for (const [rowKey, row] of Object.entries(cellData)) {
|
|
135
|
+
for (const [columnKey, cell] of Object.entries(row)) {
|
|
136
|
+
const marker = typeof cell.s === 'string' ? conflictStyleMap[cell.s] : undefined;
|
|
137
|
+
if (marker === undefined && !Object.prototype.hasOwnProperty.call(conflictStyleMap, cell.s))
|
|
138
|
+
continue;
|
|
139
|
+
if (marker === null)
|
|
140
|
+
delete cell.s;
|
|
141
|
+
else
|
|
142
|
+
cell.s = marker;
|
|
143
|
+
if (Object.keys(cell).length === 1 && cell.v === null)
|
|
144
|
+
delete row[columnKey];
|
|
145
|
+
}
|
|
146
|
+
if (Object.keys(row).length === 0)
|
|
147
|
+
delete cellData[rowKey];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const styles = asRecord(next.styles);
|
|
151
|
+
for (const markerId of Object.keys(conflictStyleMap))
|
|
152
|
+
delete styles[markerId];
|
|
153
|
+
next.styles = styles;
|
|
154
|
+
return next;
|
|
155
|
+
}
|
|
156
|
+
/** Apply ordinary cell edits to the merge base without cloning the workbook. */
|
|
157
|
+
export function applyIncrementalChangesToSnapshot(snapshot, changes) {
|
|
158
|
+
const root = asRecord(snapshot);
|
|
159
|
+
const sheets = asRecord(root.sheets);
|
|
160
|
+
for (const change of changes) {
|
|
161
|
+
const sheet = sheets[change.payload.sheetId] ?? Object.values(sheets).find(candidate => asRecord(candidate).name === change.payload.sheetId);
|
|
162
|
+
if (sheet === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
const cellData = (asRecord(sheet).cellData ??= {});
|
|
165
|
+
for (const update of change.payload.cells) {
|
|
166
|
+
const row = (cellData[String(update.row)] ??= {});
|
|
167
|
+
if (update.value === null && (update.formula === undefined || update.formula.trim().length === 0)) {
|
|
168
|
+
delete row[String(update.column)];
|
|
169
|
+
if (Object.keys(row).length === 0)
|
|
170
|
+
delete cellData[String(update.row)];
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const current = row[String(update.column)] ?? {};
|
|
174
|
+
row[String(update.column)] = { ...current, v: update.value };
|
|
175
|
+
const formula = normalizeCellFormula(update.formula);
|
|
176
|
+
if (formula === undefined)
|
|
177
|
+
delete row[String(update.column)].f;
|
|
178
|
+
else
|
|
179
|
+
row[String(update.column)].f = formula;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Patch a merge base from a replayable operation log; false means fallback. */
|
|
184
|
+
export function applyIncrementalOperationsToSnapshot(snapshot, operations) {
|
|
185
|
+
const changes = [];
|
|
186
|
+
const collect = (operation) => {
|
|
187
|
+
if (operation.type === 'update_cells') {
|
|
188
|
+
const sheetId = operation.payload.sheetId ?? operation.payload.sheetName;
|
|
189
|
+
if (sheetId === undefined || (operation.payload.cells ?? []).length === 0)
|
|
190
|
+
return false;
|
|
191
|
+
changes.push({ type: 'update_cells', payload: { sheetId, cells: operation.payload.cells ?? [] } });
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (operation.type === 'batch') {
|
|
195
|
+
return (operation.payload.operations ?? []).every((child, index) => collect({
|
|
196
|
+
...operation,
|
|
197
|
+
opId: `${operation.opId}:${index}`,
|
|
198
|
+
type: child.type,
|
|
199
|
+
payload: child.payload,
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
};
|
|
204
|
+
if (!operations.every(collect))
|
|
205
|
+
return false;
|
|
206
|
+
applyIncrementalChangesToSnapshot(snapshot, changes);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
function mergeSheet(sheetId, baseSheet, localSheet, remoteSheet, adoptRemoteCell, styles) {
|
|
210
|
+
const sheet = cloneSnapshot(localSheet);
|
|
211
|
+
const conflicts = [];
|
|
212
|
+
const conflictStyleMap = {};
|
|
213
|
+
let autoMergedCells = 0;
|
|
214
|
+
const localRowCount = typeof localSheet.rowCount === 'number' ? localSheet.rowCount : 0;
|
|
215
|
+
const remoteRowCount = typeof remoteSheet.rowCount === 'number' ? remoteSheet.rowCount : 0;
|
|
216
|
+
const localColumnCount = typeof localSheet.columnCount === 'number' ? localSheet.columnCount : 0;
|
|
217
|
+
const remoteColumnCount = typeof remoteSheet.columnCount === 'number' ? remoteSheet.columnCount : 0;
|
|
218
|
+
sheet.rowCount = Math.max(localRowCount, remoteRowCount);
|
|
219
|
+
sheet.columnCount = Math.max(localColumnCount, remoteColumnCount);
|
|
220
|
+
for (const key of MERGEABLE_SHEET_KEYS) {
|
|
221
|
+
const baseValue = baseSheet?.[key];
|
|
222
|
+
const localValue = localSheet[key];
|
|
223
|
+
const remoteValue = remoteSheet[key];
|
|
224
|
+
if (deepEqual(localValue, remoteValue))
|
|
225
|
+
continue;
|
|
226
|
+
if (deepEqual(localValue, baseValue)) {
|
|
227
|
+
if (remoteValue === undefined)
|
|
228
|
+
delete sheet[key];
|
|
229
|
+
else
|
|
230
|
+
sheet[key] = cloneSnapshot(remoteValue);
|
|
231
|
+
}
|
|
232
|
+
else if (deepEqual(remoteValue, baseValue)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
conflicts.push({ kind: 'structure', sheetId, sheetName: sheetName(localSheet, sheetId), description: `sheet 属性 ${key} 在本地和远端均有修改,当前保留本地值` });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const baseCells = asCellData(baseSheet?.cellData);
|
|
240
|
+
const localCells = asCellData(localSheet.cellData);
|
|
241
|
+
const remoteCells = asCellData(remoteSheet.cellData);
|
|
242
|
+
const mergedCells = {};
|
|
243
|
+
const rowKeys = new Set([...Object.keys(baseCells), ...Object.keys(localCells), ...Object.keys(remoteCells)]);
|
|
244
|
+
for (const rowKey of rowKeys) {
|
|
245
|
+
const baseRow = baseCells[rowKey] ?? {};
|
|
246
|
+
const localRow = localCells[rowKey] ?? {};
|
|
247
|
+
const remoteRow = remoteCells[rowKey] ?? {};
|
|
248
|
+
const columnKeys = new Set([...Object.keys(baseRow), ...Object.keys(localRow), ...Object.keys(remoteRow)]);
|
|
249
|
+
for (const columnKey of columnKeys) {
|
|
250
|
+
const baseCell = baseRow[columnKey];
|
|
251
|
+
const localCell = localRow[columnKey];
|
|
252
|
+
const remoteCell = remoteRow[columnKey];
|
|
253
|
+
let selected;
|
|
254
|
+
if (deepEqual(localCell, remoteCell))
|
|
255
|
+
selected = cloneOptional(localCell);
|
|
256
|
+
else if (deepEqual(localCell, baseCell)) {
|
|
257
|
+
selected = adoptRemoteCell(remoteCell);
|
|
258
|
+
autoMergedCells++;
|
|
259
|
+
}
|
|
260
|
+
else if (deepEqual(remoteCell, baseCell)) {
|
|
261
|
+
selected = cloneOptional(localCell);
|
|
262
|
+
autoMergedCells++;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
selected = cloneOptional(localCell) ?? { v: null };
|
|
266
|
+
const markerId = createConflictStyle(styles, selected, sheetId, Number(rowKey), Number(columnKey));
|
|
267
|
+
conflictStyleMap[markerId] = typeof localCell?.s === 'string' ? localCell.s : null;
|
|
268
|
+
selected.s = markerId;
|
|
269
|
+
conflicts.push({
|
|
270
|
+
kind: 'cell',
|
|
271
|
+
sheetId,
|
|
272
|
+
sheetName: sheetName(localSheet, sheetId),
|
|
273
|
+
row: Number(rowKey),
|
|
274
|
+
column: Number(columnKey),
|
|
275
|
+
address: `${sheetName(localSheet, sheetId)}!${cellAddress(Number(rowKey), Number(columnKey))}`,
|
|
276
|
+
baseValue: toCellValue(baseCell),
|
|
277
|
+
localValue: toCellValue(localCell),
|
|
278
|
+
remoteValue: toCellValue(remoteCell),
|
|
279
|
+
description: '本地和远端同时修改了同一单元格,当前保留本地值',
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (selected !== undefined)
|
|
283
|
+
(mergedCells[rowKey] ??= {})[columnKey] = selected;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
sheet.cellData = mergedCells;
|
|
287
|
+
return { sheet, conflicts, autoMergedCells, conflictStyleMap };
|
|
288
|
+
}
|
|
289
|
+
function createConflictStyle(styles, cell, sheetId, row, column) {
|
|
290
|
+
const original = typeof cell.s === 'string' ? asOptionalRecord(styles[cell.s]) ?? {} : {};
|
|
291
|
+
const baseId = `data-sheet-conflict-${sheetId}-${row}-${column}`;
|
|
292
|
+
let id = baseId;
|
|
293
|
+
let suffix = 1;
|
|
294
|
+
while (styles[id] !== undefined)
|
|
295
|
+
id = `${baseId}-${suffix++}`;
|
|
296
|
+
styles[id] = { ...cloneSnapshot(original), bg: '#FFF2CC', cl: '#9C0006' };
|
|
297
|
+
return id;
|
|
298
|
+
}
|
|
299
|
+
function sheetConflict(sheetId, sheet, description) {
|
|
300
|
+
return { kind: 'sheet', sheetId, sheetName: sheetName(sheet, sheetId), description };
|
|
301
|
+
}
|
|
302
|
+
function sheetName(sheet, fallback) {
|
|
303
|
+
return typeof sheet.name === 'string' && sheet.name.length > 0 ? sheet.name : fallback;
|
|
304
|
+
}
|
|
305
|
+
function countAutoMergedCells(base, local, remote, mergedSheets) {
|
|
306
|
+
let count = 0;
|
|
307
|
+
const baseSheets = asRecord(base.sheets);
|
|
308
|
+
const localSheets = asRecord(local.sheets);
|
|
309
|
+
const remoteSheets = asRecord(remote.sheets);
|
|
310
|
+
for (const [sheetId, sheet] of Object.entries(mergedSheets)) {
|
|
311
|
+
const baseCells = asCellData(baseSheets[sheetId]?.cellData);
|
|
312
|
+
const localCells = asCellData(localSheets[sheetId]?.cellData);
|
|
313
|
+
const remoteCells = asCellData(remoteSheets[sheetId]?.cellData);
|
|
314
|
+
const mergedCells = asCellData(sheet.cellData);
|
|
315
|
+
for (const [rowKey, row] of Object.entries(mergedCells)) {
|
|
316
|
+
for (const columnKey of Object.keys(row)) {
|
|
317
|
+
const localCell = localCells[rowKey]?.[columnKey];
|
|
318
|
+
const remoteCell = remoteCells[rowKey]?.[columnKey];
|
|
319
|
+
const baseCell = baseCells[rowKey]?.[columnKey];
|
|
320
|
+
if (!deepEqual(localCell, remoteCell) && (deepEqual(localCell, baseCell) || deepEqual(remoteCell, baseCell)))
|
|
321
|
+
count++;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return count;
|
|
326
|
+
}
|
|
327
|
+
function cloneAndRemapSheet(sheet, adoptRemoteCell) {
|
|
328
|
+
const next = cloneSnapshot(sheet);
|
|
329
|
+
const cellData = asCellData(next.cellData);
|
|
330
|
+
for (const [rowKey, row] of Object.entries(cellData)) {
|
|
331
|
+
for (const [columnKey, cell] of Object.entries(row))
|
|
332
|
+
row[columnKey] = adoptRemoteCell(cell) ?? cell;
|
|
333
|
+
cellData[rowKey] = row;
|
|
334
|
+
}
|
|
335
|
+
next.cellData = cellData;
|
|
336
|
+
return next;
|
|
337
|
+
}
|
|
338
|
+
function normalizeSheetOrder(order, sheets) {
|
|
339
|
+
const result = order.filter(id => sheets[id] !== undefined);
|
|
340
|
+
for (const id of Object.keys(sheets))
|
|
341
|
+
if (!result.includes(id))
|
|
342
|
+
result.push(id);
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
function asRecord(value) {
|
|
346
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
347
|
+
}
|
|
348
|
+
function asOptionalRecord(value) {
|
|
349
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
350
|
+
}
|
|
351
|
+
function asCellData(value) {
|
|
352
|
+
return asRecord(value);
|
|
353
|
+
}
|
|
354
|
+
function asStringArray(value) {
|
|
355
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
356
|
+
}
|
|
357
|
+
function cloneOptional(value) {
|
|
358
|
+
return value === undefined ? undefined : cloneSnapshot(value);
|
|
359
|
+
}
|
|
360
|
+
function deepEqual(left, right) {
|
|
361
|
+
return stableJson(left) === stableJson(right);
|
|
362
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal sheet-panel store for dsh-data-table: the right-column
|
|
3
|
+
* open state. Workbook data lives server-side (node half, snapshot files);
|
|
4
|
+
* this store only tracks the panel toggle.
|
|
5
|
+
* @module dsh-data-table/client/store
|
|
6
|
+
*/
|
|
7
|
+
/** The workbook to reopen when the pane mounts without an explicit target. */
|
|
8
|
+
export declare function lastSelectedWorkbookId(): string;
|
|
9
|
+
/** Record the active workbook; call with an empty string when it is removed. */
|
|
10
|
+
export declare function rememberWorkbook(id: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* The **unretained editing session** the next save joins, per workbook
|
|
13
|
+
* (PRD v3.3 D22).
|
|
14
|
+
*
|
|
15
|
+
* The session is not a saved Version: it keeps accumulating Steps and only
|
|
16
|
+
* becomes one when the idle window closes it or it reaches the Step cap. It has
|
|
17
|
+
* to outlive the React panel, because DSH remounts the pane whenever the
|
|
18
|
+
* conversation tab changes — losing the cursor there was what made "every visit
|
|
19
|
+
* is its own version", i.e. what users read as "leaving the artifact saved a
|
|
20
|
+
* version". localStorage is best-effort; the in-memory map covers a browser
|
|
21
|
+
* that refuses storage.
|
|
22
|
+
*/
|
|
23
|
+
export interface VersionSession {
|
|
24
|
+
/** The Version id the next save joins; `null` when there is nothing open. */
|
|
25
|
+
versionId: string;
|
|
26
|
+
/** Steps already committed into that Version. */
|
|
27
|
+
stepCount: number;
|
|
28
|
+
}
|
|
29
|
+
/** The open session for a workbook, or `null` when there is none. */
|
|
30
|
+
export declare function readVersionSession(workbookId: string): VersionSession | null;
|
|
31
|
+
/** Record (or with `null`, forget) the open session for a workbook. */
|
|
32
|
+
export declare function rememberVersionSession(workbookId: string, session: VersionSession | null): void;
|
|
33
|
+
/** Whether the right column is open. */
|
|
34
|
+
export declare function isSheetOpen(): boolean;
|
|
35
|
+
/** Open/close the right column. */
|
|
36
|
+
export declare function setSheetOpen(value: boolean): void;
|
|
37
|
+
/** React hook: right-column open state. */
|
|
38
|
+
export declare function useSheetOpen(): boolean;
|
|
39
|
+
/** Record whether the pane is showing the Table tab. */
|
|
40
|
+
export declare function setSheetVisible(value: boolean): void;
|
|
41
|
+
/** Whether the Table tab is on screen. */
|
|
42
|
+
export declare function isSheetVisible(): boolean;
|
|
43
|
+
/** React hook: whether the Table tab is on screen. */
|
|
44
|
+
export declare function useSheetVisible(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Last workbook projection kept outside React. DSH unmounts the right pane when
|
|
47
|
+
* the conversation tab changes, so caching here lets a remount paint the
|
|
48
|
+
* previous content immediately instead of showing an empty panel while the
|
|
49
|
+
* request is in flight. The panel still revalidates against the Host, so edits
|
|
50
|
+
* made in another browser tab are not lost.
|
|
51
|
+
*/
|
|
52
|
+
export interface WorkbookProjection {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
snapshot: object;
|
|
56
|
+
revision: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function cachedWorkbookProjection(id: string): WorkbookProjection | null;
|
|
59
|
+
export declare function rememberWorkbookProjection(projection: WorkbookProjection): void;
|
|
60
|
+
/** Drop the cache when the workbook is deleted or fails to load. */
|
|
61
|
+
export declare function forgetWorkbookProjection(id?: string): void;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal sheet-panel store for dsh-data-table: the right-column
|
|
3
|
+
* open state. Workbook data lives server-side (node half, snapshot files);
|
|
4
|
+
* this store only tracks the panel toggle.
|
|
5
|
+
* @module dsh-data-table/client/store
|
|
6
|
+
*/
|
|
7
|
+
import { useSyncExternalStore } from 'react';
|
|
8
|
+
let sheetOpen = false;
|
|
9
|
+
/**
|
|
10
|
+
* Last workbook the user opened. DSH remounts the pane when the conversation
|
|
11
|
+
* tab changes, so the selection has to live outside React; localStorage keeps
|
|
12
|
+
* it across a reload too. Persistence is best-effort: a locked-down browser
|
|
13
|
+
* simply falls back to an in-memory value for this page life.
|
|
14
|
+
*/
|
|
15
|
+
const LAST_WORKBOOK_KEY = 'dsh.data-sheet.lastWorkbookId';
|
|
16
|
+
let lastWorkbookId = '';
|
|
17
|
+
let lastWorkbookLoaded = false;
|
|
18
|
+
function readLastWorkbook() {
|
|
19
|
+
if (lastWorkbookLoaded)
|
|
20
|
+
return lastWorkbookId;
|
|
21
|
+
lastWorkbookLoaded = true;
|
|
22
|
+
try {
|
|
23
|
+
lastWorkbookId = globalThis.localStorage?.getItem(LAST_WORKBOOK_KEY) ?? '';
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
lastWorkbookId = '';
|
|
27
|
+
}
|
|
28
|
+
return lastWorkbookId;
|
|
29
|
+
}
|
|
30
|
+
/** The workbook to reopen when the pane mounts without an explicit target. */
|
|
31
|
+
export function lastSelectedWorkbookId() {
|
|
32
|
+
return readLastWorkbook();
|
|
33
|
+
}
|
|
34
|
+
/** Record the active workbook; call with an empty string when it is removed. */
|
|
35
|
+
export function rememberWorkbook(id) {
|
|
36
|
+
readLastWorkbook();
|
|
37
|
+
if (lastWorkbookId === id)
|
|
38
|
+
return;
|
|
39
|
+
lastWorkbookId = id;
|
|
40
|
+
try {
|
|
41
|
+
if (id === '')
|
|
42
|
+
globalThis.localStorage?.removeItem(LAST_WORKBOOK_KEY);
|
|
43
|
+
else
|
|
44
|
+
globalThis.localStorage?.setItem(LAST_WORKBOOK_KEY, id);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Ignore storage failures; the in-memory value still survives remounts.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const VERSION_SESSION_PREFIX = 'dsh.data-sheet.versionSession.';
|
|
51
|
+
const versionSessions = new Map();
|
|
52
|
+
function versionSessionKey(workbookId) {
|
|
53
|
+
return `${VERSION_SESSION_PREFIX}${workbookId}`;
|
|
54
|
+
}
|
|
55
|
+
/** The open session for a workbook, or `null` when there is none. */
|
|
56
|
+
export function readVersionSession(workbookId) {
|
|
57
|
+
if (workbookId === '')
|
|
58
|
+
return null;
|
|
59
|
+
const cached = versionSessions.get(workbookId);
|
|
60
|
+
if (cached !== undefined)
|
|
61
|
+
return cached;
|
|
62
|
+
try {
|
|
63
|
+
const raw = globalThis.localStorage?.getItem(versionSessionKey(workbookId));
|
|
64
|
+
if (raw === null || raw === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
if (typeof parsed.versionId !== 'string' || parsed.versionId === '')
|
|
68
|
+
return null;
|
|
69
|
+
const session = { versionId: parsed.versionId, stepCount: typeof parsed.stepCount === 'number' && parsed.stepCount > 0 ? parsed.stepCount : 0 };
|
|
70
|
+
versionSessions.set(workbookId, session);
|
|
71
|
+
return session;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Record (or with `null`, forget) the open session for a workbook. */
|
|
78
|
+
export function rememberVersionSession(workbookId, session) {
|
|
79
|
+
if (workbookId === '')
|
|
80
|
+
return;
|
|
81
|
+
if (session === null)
|
|
82
|
+
versionSessions.delete(workbookId);
|
|
83
|
+
else
|
|
84
|
+
versionSessions.set(workbookId, session);
|
|
85
|
+
try {
|
|
86
|
+
if (session === null)
|
|
87
|
+
globalThis.localStorage?.removeItem(versionSessionKey(workbookId));
|
|
88
|
+
else
|
|
89
|
+
globalThis.localStorage?.setItem(versionSessionKey(workbookId), JSON.stringify(session));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Ignore storage failures; the in-memory value still survives remounts.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const listeners = new Set();
|
|
96
|
+
function emit() {
|
|
97
|
+
for (const listener of [...listeners])
|
|
98
|
+
listener();
|
|
99
|
+
}
|
|
100
|
+
function subscribe(listener) {
|
|
101
|
+
listeners.add(listener);
|
|
102
|
+
return () => { listeners.delete(listener); };
|
|
103
|
+
}
|
|
104
|
+
/** Whether the right column is open. */
|
|
105
|
+
export function isSheetOpen() {
|
|
106
|
+
return sheetOpen;
|
|
107
|
+
}
|
|
108
|
+
/** Open/close the right column. */
|
|
109
|
+
export function setSheetOpen(value) {
|
|
110
|
+
sheetOpen = value;
|
|
111
|
+
emit();
|
|
112
|
+
}
|
|
113
|
+
/** React hook: right-column open state. */
|
|
114
|
+
export function useSheetOpen() {
|
|
115
|
+
return useSyncExternalStore(subscribe, () => sheetOpen);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether the Table tab is currently on screen.
|
|
119
|
+
*
|
|
120
|
+
* The workspace is a resident tree that outlives the pane, so its own `open`
|
|
121
|
+
* flag stays true for the life of the page. Anything that must react only to a
|
|
122
|
+
* *visible* table — the window-level Ctrl/Cmd+S shortcut in particular — reads
|
|
123
|
+
* this instead.
|
|
124
|
+
*/
|
|
125
|
+
let sheetVisible = false;
|
|
126
|
+
/** Record whether the pane is showing the Table tab. */
|
|
127
|
+
export function setSheetVisible(value) {
|
|
128
|
+
if (sheetVisible === value)
|
|
129
|
+
return;
|
|
130
|
+
sheetVisible = value;
|
|
131
|
+
emit();
|
|
132
|
+
}
|
|
133
|
+
/** Whether the Table tab is on screen. */
|
|
134
|
+
export function isSheetVisible() {
|
|
135
|
+
return sheetVisible;
|
|
136
|
+
}
|
|
137
|
+
/** React hook: whether the Table tab is on screen. */
|
|
138
|
+
export function useSheetVisible() {
|
|
139
|
+
return useSyncExternalStore(subscribe, () => sheetVisible);
|
|
140
|
+
}
|
|
141
|
+
let cachedProjection = null;
|
|
142
|
+
export function cachedWorkbookProjection(id) {
|
|
143
|
+
return cachedProjection !== null && cachedProjection.id === id ? cachedProjection : null;
|
|
144
|
+
}
|
|
145
|
+
export function rememberWorkbookProjection(projection) {
|
|
146
|
+
cachedProjection = projection;
|
|
147
|
+
}
|
|
148
|
+
/** Drop the cache when the workbook is deleted or fails to load. */
|
|
149
|
+
export function forgetWorkbookProjection(id) {
|
|
150
|
+
if (id === undefined || cachedProjection?.id === id)
|
|
151
|
+
cachedProjection = null;
|
|
152
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SurfaceSelection } from '@tita-agent/dsh-surface-bridge';
|
|
2
|
+
export declare const DATA_SHEET_SURFACE_ID = "data-sheet";
|
|
3
|
+
export interface DataSheetFocusTarget {
|
|
4
|
+
readonly workbookId: string;
|
|
5
|
+
readonly sheetId: string;
|
|
6
|
+
readonly startRow: number;
|
|
7
|
+
readonly startColumn: number;
|
|
8
|
+
readonly endRow: number;
|
|
9
|
+
readonly endColumn: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DataSheetRangeContext extends Omit<DataSheetFocusTarget, 'workbookId'> {
|
|
12
|
+
readonly sheetName: string;
|
|
13
|
+
readonly values: readonly (readonly unknown[])[];
|
|
14
|
+
}
|
|
15
|
+
export interface DataSheetSelectionContext {
|
|
16
|
+
readonly workbookId: string;
|
|
17
|
+
readonly workbookName: string;
|
|
18
|
+
readonly revision: number;
|
|
19
|
+
readonly documentRevision: number;
|
|
20
|
+
readonly ranges: readonly DataSheetRangeContext[];
|
|
21
|
+
}
|
|
22
|
+
/** Keep source-specific focus data opaque to the bridge, but stable in transcript metadata. */
|
|
23
|
+
export declare function dataSheetFocusId(target: DataSheetFocusTarget): string;
|
|
24
|
+
/** Decode a focus id emitted by this source. */
|
|
25
|
+
export declare function dataSheetFocusTargetOf(value: string): DataSheetFocusTarget | undefined;
|
|
26
|
+
/** Project the active Univer ranges into the bridge's shared context vocabulary. */
|
|
27
|
+
export declare function projectDataSheetSelection(input: DataSheetSelectionContext): SurfaceSelection | null;
|