@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.
Files changed (267) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -2
  3. package/cordis.patch.yml +7 -0
  4. package/lib/client.js +16766 -0
  5. package/lib/contract-envelope.d.ts +91 -0
  6. package/lib/contract-envelope.js +106 -0
  7. package/lib/contract-layout.d.ts +130 -0
  8. package/lib/contract-layout.js +138 -0
  9. package/lib/contract-refs.d.ts +39 -0
  10. package/lib/contract-refs.js +9 -0
  11. package/lib/contract-version.d.ts +311 -0
  12. package/lib/contract-version.js +270 -0
  13. package/lib/contract-wire.d.ts +147 -0
  14. package/lib/contract-wire.js +99 -0
  15. package/lib/contracts.d.ts +12 -0
  16. package/lib/contracts.js +12 -0
  17. package/lib/types/audit.d.ts +42 -0
  18. package/lib/types/audit.js +39 -0
  19. package/lib/types/client/ProjectImportMenu.d.ts +30 -0
  20. package/lib/types/client/ProjectImportMenu.js +70 -0
  21. package/lib/types/client/SheetPanel.d.ts +33 -0
  22. package/lib/types/client/SheetPanel.js +1766 -0
  23. package/lib/types/client/SheetToggle.d.ts +18 -0
  24. package/lib/types/client/SheetToggle.js +26 -0
  25. package/lib/types/client/index.d.ts +15 -0
  26. package/lib/types/client/index.js +110 -0
  27. package/lib/types/client/persistence/changes.d.ts +15 -0
  28. package/lib/types/client/persistence/changes.js +51 -0
  29. package/lib/types/client/persistence/ids.d.ts +34 -0
  30. package/lib/types/client/persistence/ids.js +48 -0
  31. package/lib/types/client/persistence/merge.d.ts +35 -0
  32. package/lib/types/client/persistence/merge.js +362 -0
  33. package/lib/types/client/store.d.ts +61 -0
  34. package/lib/types/client/store.js +152 -0
  35. package/lib/types/client/surface-context.d.ts +27 -0
  36. package/lib/types/client/surface-context.js +104 -0
  37. package/lib/types/client/surface-control.d.ts +2 -0
  38. package/lib/types/client/surface-control.js +12 -0
  39. package/lib/types/client/transport/workbookApi.d.ts +159 -0
  40. package/lib/types/client/transport/workbookApi.js +246 -0
  41. package/lib/types/client/univer/StandaloneWorkbookPage.d.ts +5 -0
  42. package/lib/types/client/univer/StandaloneWorkbookPage.js +7 -0
  43. package/lib/types/client/univer/WorkbookCanvas.d.ts +40 -0
  44. package/lib/types/client/univer/WorkbookCanvas.js +87 -0
  45. package/lib/types/client/univer/WorkbookView.d.ts +3 -0
  46. package/lib/types/client/univer/WorkbookView.js +2 -0
  47. package/lib/types/client/univer/loader.d.ts +18 -0
  48. package/lib/types/client/univer/loader.js +81 -0
  49. package/lib/types/client/univer/standalone-entry.d.ts +1 -0
  50. package/lib/types/client/univer/standalone-entry.js +19 -0
  51. package/lib/types/client/versions/VersionChrome.d.ts +33 -0
  52. package/lib/types/client/versions/VersionChrome.js +21 -0
  53. package/lib/types/client/versions/inspect.d.ts +70 -0
  54. package/lib/types/client/versions/inspect.js +88 -0
  55. package/lib/types/client/versions/preview-decoration.d.ts +11 -0
  56. package/lib/types/client/versions/preview-decoration.js +54 -0
  57. package/lib/types/client/versions/records.d.ts +19 -0
  58. package/lib/types/client/versions/records.js +70 -0
  59. package/lib/types/client/workspace-host.d.ts +61 -0
  60. package/lib/types/client/workspace-host.js +124 -0
  61. package/lib/types/collaboration-hub.d.ts +31 -0
  62. package/lib/types/collaboration-hub.js +69 -0
  63. package/lib/types/contract/ids.d.ts +24 -0
  64. package/lib/types/contract/ids.js +46 -0
  65. package/lib/types/contract/mutations.d.ts +25 -0
  66. package/lib/types/contract/mutations.js +782 -0
  67. package/lib/types/contract/operations.d.ts +54 -0
  68. package/lib/types/contract/operations.js +203 -0
  69. package/lib/types/contract/snapshots.d.ts +176 -0
  70. package/lib/types/contract/snapshots.js +351 -0
  71. package/lib/types/contract/univer-api-catalog.d.ts +29 -0
  72. package/lib/types/contract/univer-api-catalog.js +264 -0
  73. package/lib/types/errors.d.ts +8 -0
  74. package/lib/types/errors.js +12 -0
  75. package/lib/types/http/assets-routes.d.ts +3 -0
  76. package/lib/types/http/assets-routes.js +169 -0
  77. package/lib/types/http/collaboration.d.ts +10 -0
  78. package/lib/types/http/collaboration.js +154 -0
  79. package/lib/types/http/history.d.ts +15 -0
  80. package/lib/types/http/history.js +134 -0
  81. package/lib/types/http/project-files.d.ts +79 -0
  82. package/lib/types/http/project-files.js +206 -0
  83. package/lib/types/http/project-scope.d.ts +50 -0
  84. package/lib/types/http/project-scope.js +52 -0
  85. package/lib/types/http/routes.d.ts +98 -0
  86. package/lib/types/http/routes.js +323 -0
  87. package/lib/types/http/standalone.d.ts +9 -0
  88. package/lib/types/http/standalone.js +31 -0
  89. package/lib/types/http/trunk.d.ts +10 -0
  90. package/lib/types/http/trunk.js +322 -0
  91. package/lib/types/http/worktree.d.ts +9 -0
  92. package/lib/types/http/worktree.js +145 -0
  93. package/lib/types/index.d.ts +18 -0
  94. package/lib/types/index.js +156 -0
  95. package/lib/types/invariant.d.ts +15 -0
  96. package/lib/types/invariant.js +15 -0
  97. package/lib/types/request-params.d.ts +21 -0
  98. package/lib/types/request-params.js +76 -0
  99. package/lib/types/store/paths.d.ts +68 -0
  100. package/lib/types/store/paths.js +148 -0
  101. package/lib/types/store/project.d.ts +88 -0
  102. package/lib/types/store/project.js +120 -0
  103. package/lib/types/store/read.d.ts +110 -0
  104. package/lib/types/store/read.js +404 -0
  105. package/lib/types/store/retention.d.ts +27 -0
  106. package/lib/types/store/retention.js +111 -0
  107. package/lib/types/store/write.d.ts +22 -0
  108. package/lib/types/store/write.js +210 -0
  109. package/lib/types/tools/arguments.d.ts +40 -0
  110. package/lib/types/tools/arguments.js +130 -0
  111. package/lib/types/tools/cells.d.ts +50 -0
  112. package/lib/types/tools/cells.js +298 -0
  113. package/lib/types/tools/index.d.ts +2 -0
  114. package/lib/types/tools/index.js +250 -0
  115. package/lib/types/tools/render.d.ts +47 -0
  116. package/lib/types/tools/render.js +290 -0
  117. package/lib/types/tools/univer-api.d.ts +18 -0
  118. package/lib/types/tools/univer-api.js +198 -0
  119. package/lib/types/tools/workbook-catalog.d.ts +3 -0
  120. package/lib/types/tools/workbook-catalog.js +83 -0
  121. package/lib/types/tools/workbook-edit.d.ts +192 -0
  122. package/lib/types/tools/workbook-edit.js +912 -0
  123. package/lib/types/tools/workbook-find.d.ts +11 -0
  124. package/lib/types/tools/workbook-find.js +89 -0
  125. package/lib/types/tools/workbook-history.d.ts +2 -0
  126. package/lib/types/tools/workbook-history.js +59 -0
  127. package/lib/types/tools/workbook-read.d.ts +21 -0
  128. package/lib/types/tools/workbook-read.js +204 -0
  129. package/lib/types/types.d.ts +377 -0
  130. package/lib/types/types.js +34 -0
  131. package/lib/types/univer/apply-operation.d.ts +14 -0
  132. package/lib/types/univer/apply-operation.js +398 -0
  133. package/lib/types/univer/chunk.d.ts +68 -0
  134. package/lib/types/univer/chunk.js +16 -0
  135. package/lib/types/univer/decoration.d.ts +34 -0
  136. package/lib/types/univer/decoration.js +88 -0
  137. package/lib/types/univer/diagnostics.d.ts +31 -0
  138. package/lib/types/univer/diagnostics.js +141 -0
  139. package/lib/types/univer/engine.d.ts +33 -0
  140. package/lib/types/univer/engine.js +338 -0
  141. package/lib/types/univer/entry.d.ts +8 -0
  142. package/lib/types/univer/entry.js +11 -0
  143. package/lib/types/univer/facade.d.ts +107 -0
  144. package/lib/types/univer/facade.js +1 -0
  145. package/lib/types/univer/fonts.d.ts +202 -0
  146. package/lib/types/univer/fonts.js +140 -0
  147. package/lib/types/univer/plugins/collaboration.d.ts +23 -0
  148. package/lib/types/univer/plugins/collaboration.js +47 -0
  149. package/lib/types/univer/plugins/export.d.ts +39 -0
  150. package/lib/types/univer/plugins/export.js +246 -0
  151. package/lib/types/univer/plugins/history.d.ts +24 -0
  152. package/lib/types/univer/plugins/history.js +50 -0
  153. package/lib/types/univer/plugins/version.d.ts +13 -0
  154. package/lib/types/univer/plugins/version.js +13 -0
  155. package/lib/types/version/diff.d.ts +36 -0
  156. package/lib/types/version/diff.js +156 -0
  157. package/lib/types/version/grouping.d.ts +7 -0
  158. package/lib/types/version/grouping.js +94 -0
  159. package/lib/types/version/records.d.ts +17 -0
  160. package/lib/types/version/records.js +102 -0
  161. package/lib/types/version/session.d.ts +36 -0
  162. package/lib/types/version/session.js +89 -0
  163. package/lib/types/version/summary.d.ts +24 -0
  164. package/lib/types/version/summary.js +111 -0
  165. package/lib/types/workbook-manager.d.ts +191 -0
  166. package/lib/types/workbook-manager.js +935 -0
  167. package/lib/types/xlsx-export-worker.d.ts +5 -0
  168. package/lib/types/xlsx-export-worker.js +24 -0
  169. package/lib/types/xlsx-export.d.ts +21 -0
  170. package/lib/types/xlsx-export.js +371 -0
  171. package/lib/types/xlsx-import-worker.d.ts +1 -0
  172. package/lib/types/xlsx-import-worker.js +19 -0
  173. package/lib/univer-client.js +690 -0
  174. package/lib/workbook-page.js +23826 -0
  175. package/lib/xlsx-export-worker.js +104 -0
  176. package/lib/xlsx-import-worker.js +46 -0
  177. package/package.json +106 -3
  178. package/src/audit.ts +58 -0
  179. package/src/client/ProjectImportMenu.tsx +100 -0
  180. package/src/client/SheetPanel.module.css +1578 -0
  181. package/src/client/SheetPanel.tsx +1934 -0
  182. package/src/client/SheetToggle.module.css +73 -0
  183. package/src/client/SheetToggle.tsx +65 -0
  184. package/src/client/contract.d.ts +19 -0
  185. package/src/client/index.ts +131 -0
  186. package/src/client/persistence/changes.ts +58 -0
  187. package/src/client/persistence/ids.ts +60 -0
  188. package/src/client/persistence/merge.ts +392 -0
  189. package/src/client/store.ts +188 -0
  190. package/src/client/surface-context.ts +123 -0
  191. package/src/client/surface-control.ts +13 -0
  192. package/src/client/transport/workbookApi.ts +355 -0
  193. package/src/client/univer/SheetView.module.css +110 -0
  194. package/src/client/univer/StandaloneWorkbookPage.module.css +8 -0
  195. package/src/client/univer/StandaloneWorkbookPage.tsx +15 -0
  196. package/src/client/univer/WorkbookCanvas.tsx +128 -0
  197. package/src/client/univer/WorkbookView.module.css +30 -0
  198. package/src/client/univer/WorkbookView.tsx +3 -0
  199. package/src/client/univer/loader.ts +90 -0
  200. package/src/client/univer/react-dom-client.d.ts +10 -0
  201. package/src/client/univer/standalone-entry.tsx +19 -0
  202. package/src/client/univer/standalone.css +12 -0
  203. package/src/client/univer/univer-locales.d.ts +89 -0
  204. package/src/client/univer/univer-modules.d.ts +26 -0
  205. package/src/client/versions/VersionChrome.tsx +114 -0
  206. package/src/client/versions/inspect.ts +138 -0
  207. package/src/client/versions/preview-decoration.ts +54 -0
  208. package/src/client/versions/records.ts +78 -0
  209. package/src/client/workspace-host.ts +147 -0
  210. package/src/collaboration-hub.ts +83 -0
  211. package/src/contract/ids.ts +50 -0
  212. package/src/contract/mutations.ts +791 -0
  213. package/src/contract/operations.ts +221 -0
  214. package/src/contract/snapshots.ts +383 -0
  215. package/src/contract/univer-api-catalog.ts +289 -0
  216. package/src/css-modules.d.ts +6 -0
  217. package/src/errors.ts +25 -0
  218. package/src/http/assets-routes.ts +177 -0
  219. package/src/http/collaboration.ts +144 -0
  220. package/src/http/history.ts +151 -0
  221. package/src/http/project-files.ts +227 -0
  222. package/src/http/project-scope.ts +77 -0
  223. package/src/http/routes.ts +364 -0
  224. package/src/http/standalone.ts +32 -0
  225. package/src/http/trunk.ts +322 -0
  226. package/src/http/worktree.ts +119 -0
  227. package/src/index.ts +165 -0
  228. package/src/invariant.ts +17 -0
  229. package/src/request-params.ts +92 -0
  230. package/src/store/paths.ts +165 -0
  231. package/src/store/project.ts +137 -0
  232. package/src/store/read.ts +454 -0
  233. package/src/store/retention.ts +106 -0
  234. package/src/store/write.ts +230 -0
  235. package/src/tools/arguments.ts +128 -0
  236. package/src/tools/cells.ts +298 -0
  237. package/src/tools/index.ts +274 -0
  238. package/src/tools/render.ts +301 -0
  239. package/src/tools/univer-api.ts +243 -0
  240. package/src/tools/workbook-catalog.ts +94 -0
  241. package/src/tools/workbook-edit.ts +987 -0
  242. package/src/tools/workbook-find.ts +80 -0
  243. package/src/tools/workbook-history.ts +63 -0
  244. package/src/tools/workbook-read.ts +210 -0
  245. package/src/types.ts +432 -0
  246. package/src/univer/apply-operation.ts +369 -0
  247. package/src/univer/chunk.ts +77 -0
  248. package/src/univer/decoration.ts +111 -0
  249. package/src/univer/diagnostics.ts +136 -0
  250. package/src/univer/engine.ts +370 -0
  251. package/src/univer/entry.ts +14 -0
  252. package/src/univer/facade.ts +91 -0
  253. package/src/univer/fonts.ts +148 -0
  254. package/src/univer/overrides.css +33 -0
  255. package/src/univer/plugins/collaboration.ts +47 -0
  256. package/src/univer/plugins/export.ts +236 -0
  257. package/src/univer/plugins/history.ts +49 -0
  258. package/src/univer/plugins/version.ts +13 -0
  259. package/src/version/diff.ts +169 -0
  260. package/src/version/grouping.ts +96 -0
  261. package/src/version/records.ts +113 -0
  262. package/src/version/session.ts +90 -0
  263. package/src/version/summary.ts +117 -0
  264. package/src/workbook-manager.ts +1111 -0
  265. package/src/xlsx-export-worker.ts +34 -0
  266. package/src/xlsx-export.ts +373 -0
  267. package/src/xlsx-import-worker.ts +30 -0
@@ -0,0 +1,311 @@
1
+ /**
2
+ * 版本模型:数据产物共享的领域模型。
3
+ *
4
+ * 包含四段:版本与步骤的记录形状(version model)、表格地址与冲突载荷
5
+ * (spreadsheet addresses)、自动检查点的保留策略(retention)、版本时间线与撤销目标
6
+ * (version tier)。
7
+ *
8
+ * 为什么必须共享:数据产物的版本面板读同一份 `VersionChange` 语义、同一套冲突载荷和时间线排序。
9
+ *
10
+ * 依赖 `./refs.js` 的 `LineageContext`。
11
+ */
12
+ import type { LineageContext } from './contract-refs.js';
13
+ /**
14
+ * The unified Version/Revision/Step/Event vocabulary for versioned data artifacts.
15
+ */
16
+ export type ArtifactKind = 'workbook';
17
+ /**
18
+ * `committed` is the saved state; `temporary` workbook versions are previews of pending
19
+ * work and never surface as user versions.
20
+ */
21
+ export type VersionStatus = 'open' | 'committed' | 'restored' | 'superseded';
22
+ export type VersionActor = 'user' | 'agent';
23
+ /**
24
+ * Version tier (PRD v3 §4). `named` Versions are the ones a person or an Agent
25
+ * deliberately created — a manual save, one Agent tool call, or a restore.
26
+ * `auto` checkpoints are what an editing session produces on its own; they stay
27
+ * fully usable but are collapsed out of the default list so a user can see the
28
+ * versions they actually meant to make. Absent on records written before v3 and
29
+ * therefore read as `named`.
30
+ */
31
+ export type VersionKind = 'named' | 'auto';
32
+ /** Only `content` advances the content revision stream and forms Versions. */
33
+ export type RevisionKind = 'content' | 'derived' | 'metadata';
34
+ /** A user-meaningful content version. */
35
+ export interface VersionRecord {
36
+ versionId: string;
37
+ artifactKind: ArtifactKind;
38
+ artifactId: string;
39
+ /** Display number (`V1`, `V2`) scoped to one artifact; never reused. */
40
+ number: number;
41
+ startRevision: number;
42
+ endRevision: number;
43
+ parentVersionId?: string;
44
+ restoredFromVersionId?: string;
45
+ supersededByVersionId?: string;
46
+ status: VersionStatus;
47
+ /** v3 tier; absent on pre-v3 records, which are read as `named` (§4). */
48
+ kind?: VersionKind;
49
+ actor: VersionActor;
50
+ summary: string;
51
+ intent?: string;
52
+ stepCount: number;
53
+ /** Stable hash of the content state at `endRevision`. */
54
+ contentHash: string;
55
+ createdAt: string;
56
+ committedAt?: string;
57
+ lineage?: LineageContext;
58
+ /** False once the low-level log was compacted past its checkpoint (§6.2). */
59
+ replayable: boolean;
60
+ }
61
+ /** One replayable action inside a Version. */
62
+ export interface VersionStepRecord {
63
+ versionId: string;
64
+ index: number;
65
+ opId: string;
66
+ operationType: string;
67
+ summary: string;
68
+ actor: 'user' | 'agent' | 'runtime';
69
+ timestamp: string;
70
+ replayable: boolean;
71
+ }
72
+ export type VersionChangeKind = 'cell' | 'sheet' | 'structure' | 'variable' | 'metadata';
73
+ /** One line of a text diff. */
74
+ export interface TextDiffLine {
75
+ kind: 'add' | 'remove' | 'same';
76
+ text: string;
77
+ }
78
+ /**
79
+ * One difference between two versions, plugin-neutral.
80
+ *
81
+ * The extra position fields exist so the shared diff view can group and hunk
82
+ * the changes the way `git diff` does, instead of rendering one card per
83
+ * change (PRD v3.3 §7.2):
84
+ *
85
+ * - `group` is the section a change belongs to — a sheet for a workbook or another grouped artifact. The view renders one tab per group when there is
86
+ * more than one.
87
+ * - `row`/`column` are 0-based coordinates, when the change has a position.
88
+ * Adjacent changes collapse into one hunk and the header names the row range.
89
+ */
90
+ export interface VersionChange {
91
+ kind: VersionChangeKind;
92
+ target: string;
93
+ description: string;
94
+ group?: string;
95
+ row?: number;
96
+ column?: number;
97
+ before?: unknown;
98
+ after?: unknown;
99
+ /**
100
+ * The cell's value is the same on both sides — only its formatting moved.
101
+ *
102
+ * Formatting a range makes Univer write a style id into every cell it covers,
103
+ * so a diff that reads the raw cell objects reports the range as changed even
104
+ * when nothing in it was edited. A reader counting differences wants content,
105
+ * so formatting is reported as 样式 rather than as a modification (GUI
106
+ * feedback 2026-09-19: 473 styled-empty cells against 11 real edits).
107
+ */
108
+ styleOnly?: boolean;
109
+ /** Line-level diff, present when the change is textual source. */
110
+ lines?: TextDiffLine[];
111
+ }
112
+ /** How a change reads in a diff: insertion, deletion, replacement, or 样式. */
113
+ export type VersionChangeDirection = 'add' | 'remove' | 'modify' | 'structure' | 'style';
114
+ /** Classify one change from what it carries, so every surface agrees. */
115
+ export declare function versionChangeDirection(change: VersionChange): VersionChangeDirection;
116
+ /**
117
+ * Spreadsheet column letters for a 0-based column index: `0 → A`, `25 → Z`,
118
+ * `26 → AA`.
119
+ *
120
+ * The single column-letter implementation: the Table plugin, the shared diff
121
+ * view and other components all used to spell addresses their own way, and the
122
+ * user-visible result was `Sheet1!R3C2` in version summaries and `R3C2` in the
123
+ * comparison gutter — while a spreadsheet address is `Sheet1!C3`.
124
+ */
125
+ export declare function columnLabel(index: number): string;
126
+ /** A1-style cell reference without a sheet: `C3`. */
127
+ export declare function cellAddress(row: number, column: number): string;
128
+ /**
129
+ * A1-style reference with the sheet it lives in: `Sheet1!C3`, or
130
+ * `Sheet1!A1:B3` when the change spans a range.
131
+ *
132
+ * A single cell stays `Sheet1!C3` rather than the degenerate `C3:C3`: an
133
+ * address is what the user reads in the formula bar, and the range form is only
134
+ * meaningful when it actually spans more than one cell.
135
+ */
136
+ export declare function formatCellReference(ref: {
137
+ sheet?: string;
138
+ row: number;
139
+ column: number;
140
+ endRow?: number;
141
+ endColumn?: number;
142
+ }): string;
143
+ /** A single conflicting change between the caller's base and the current state. */
144
+ export interface ConflictEntry {
145
+ kind: VersionChangeKind | 'content';
146
+ target: string;
147
+ description: string;
148
+ base?: unknown;
149
+ local?: unknown;
150
+ remote?: unknown;
151
+ }
152
+ /**
153
+ * The single 409 body both plugins return (PRD §5.1): the caller learns what it
154
+ * was based on, what the artifact is now, and which changes conflict, so the UI
155
+ * can offer "重新读取 / 查看差异" rather than a bare error string.
156
+ */
157
+ export interface ConflictPayload {
158
+ code: 'REVISION_CONFLICT';
159
+ artifactKind: ArtifactKind;
160
+ artifactId: string;
161
+ baseRevision: number;
162
+ currentRevision: number;
163
+ /** Transport cursor, when the plugin distinguishes it from the content revision. */
164
+ changeCursor?: number;
165
+ conflictSet: ConflictEntry[];
166
+ message: string;
167
+ nextAction: string;
168
+ retryable: boolean;
169
+ }
170
+ export declare function conflictPayload(input: {
171
+ artifactKind: ArtifactKind;
172
+ artifactId: string;
173
+ baseRevision: number;
174
+ currentRevision: number;
175
+ changeCursor?: number;
176
+ conflictSet?: ConflictEntry[];
177
+ message?: string;
178
+ nextAction?: string;
179
+ }): ConflictPayload;
180
+ /**
181
+ * Version boundary policy shared by both plugins (PRD v3.1 §4).
182
+ *
183
+ * A Version collects **many Steps**; it is not one Version per interaction. The
184
+ * value below is a *session* window, not a typing debounce: while a user keeps
185
+ * working inside it every edit joins the same open Version, and that Version
186
+ * closes only when a real trigger fires —
187
+ *
188
+ * 1. the user presses 保存版本,
189
+ * 2. an Agent tool call commits,
190
+ * 3. the user leaves the artifact (the client sends `boundary: 'close'`),
191
+ * 4. the user has been idle for this whole window, so the session ended,
192
+ * 5. the Step safety cap is reached.
193
+ *
194
+ * It used to be 2s, which turned every thinking pause into its own Version and
195
+ * made the list read as "one Version per keystroke burst".
196
+ */
197
+ export declare const VERSION_IDLE_CLOSE_MS: number;
198
+ /**
199
+ * How many Steps one Version may hold before it is closed and a new Version
200
+ * starts (PRD v3.3 D21).
201
+ *
202
+ * This is the *only* automatic boundary besides the idle window, so it is the
203
+ * user-visible one: 20 steps is roughly "a burst of work I would call one
204
+ * version". It used to be 50, which in practice meant a session never hit the
205
+ * cap and therefore never became a saved Version while the tab stayed open.
206
+ */
207
+ export declare const VERSION_MAX_STEPS = 20;
208
+ /** Newest auto checkpoints kept regardless of age (PRD v3.1 §6.2, D3). */
209
+ export declare const AUTO_CHECKPOINT_KEEP = 100;
210
+ export interface RetentionCandidate {
211
+ versionId?: string;
212
+ id?: string;
213
+ number: number;
214
+ status: VersionStatus;
215
+ kind?: VersionKind;
216
+ /** ISO timestamp; the retention rule buckets by calendar day. */
217
+ createdAt?: string;
218
+ timestamp?: string;
219
+ restoredFromVersionId?: string;
220
+ parentVersionId?: string;
221
+ /** Present when the record can replay from its own start state. */
222
+ startContentHash?: string;
223
+ }
224
+ /**
225
+ * Which auto checkpoints a store may drop (PRD v3.1 §6.2, D3).
226
+ *
227
+ * Kept, always: every `named` Version, the newest {@link AUTO_CHECKPOINT_KEEP}
228
+ * auto checkpoints, the newest auto checkpoint of every older calendar day, the
229
+ * line head / open Version, and anything a surviving Version still references
230
+ * (restore origin or parent), so pruning can never break a row that remains.
231
+ *
232
+ * `versions` may be in any order; the decision only uses identity, tier and
233
+ * time. The caller deletes the returned records and any content they alone own.
234
+ */
235
+ export declare function selectAutoCheckpointsToPrune<T extends RetentionCandidate>(versions: T[], options?: {
236
+ keep?: number;
237
+ }): T[];
238
+ /** Pre-v3 records carry no `kind`; they are user/Agent-meaningful Versions. */
239
+ export declare function versionKindOf(version: {
240
+ kind?: VersionKind;
241
+ }): VersionKind;
242
+ /** Minimal shape the timeline ordering needs. */
243
+ export interface VersionTimelineKey {
244
+ number: number;
245
+ timestamp?: string;
246
+ createdAt?: string;
247
+ }
248
+ /**
249
+ * Newest-first comparator. Every list in the drawer reads as one timeline;
250
+ * Version numbers are handed out in creation order, so they break ties when a
251
+ * record has no usable timestamp.
252
+ */
253
+ export declare function compareVersionsNewestFirst(left: VersionTimelineKey, right: VersionTimelineKey): number;
254
+ /**
255
+ * The one flat timeline the drawer renders (PRD v3.3 §7.3).
256
+ *
257
+ * The rule is deliberately the **same shape as the storage retention rule**
258
+ * (`selectAutoCheckpointsToPrune`), so the list can never hide a Version the
259
+ * store kept, nor offer one it dropped. A Version is listed when any of these
260
+ * holds:
261
+ *
262
+ * 1. it is `named` — a manual save, one Agent call, or a restore. Never hidden;
263
+ * 2. it is among the newest {@link AUTO_CHECKPOINT_KEEP} Versions;
264
+ * 3. it is the **last Version of its calendar day**.
265
+ *
266
+ * (2) and (3) are what make a user's own editing history visible: in the Data
267
+ * Table every browser save is an `auto` checkpoint, so hiding `auto` outright
268
+ * hid the per-day work the user remembered making. (3) keeps a long-running
269
+ * artifact to roughly one row per working day once it falls out of the recent
270
+ * window, while (2) keeps everything recent restorable.
271
+ *
272
+ * The result is ordered strictly newest-first; callers render `今天 / 昨天 /
273
+ * 更早` separators and exclude `open` Versions, which are still being written.
274
+ */
275
+ export declare function versionTimeline<T extends VersionTimelineKey & {
276
+ kind?: VersionKind;
277
+ }>(versions: T[], options?: {
278
+ keep?: number;
279
+ }): T[];
280
+ /** `今天 / 昨天 / 更早` list separator (PRD v3 P3). */
281
+ export declare function versionDayLabel(timestamp: string | undefined, nowMs?: number): string | undefined;
282
+ /**
283
+ * Target of "撤销这次改动" (PRD v3 §6): the newest Version older than `entry`
284
+ * that is still on the current line. `undefined` means this Version has no
285
+ * reachable predecessor, and the drawer hides the action rather than offering a
286
+ * restore to nothing.
287
+ *
288
+ * Records expose their identity as `versionId` (persisted) or `id` (UI rows),
289
+ * so both shapes are accepted.
290
+ */
291
+ export interface VersionIdentity {
292
+ versionId?: string;
293
+ id?: string;
294
+ number: number;
295
+ status: VersionStatus;
296
+ }
297
+ export declare function versionIdentityOf(value: {
298
+ versionId?: string;
299
+ id?: string;
300
+ }): string | undefined;
301
+ /**
302
+ * Display number of the Version a restore reinstated. Persisted records carry
303
+ * the target id, and only the surrounding list knows its number, so the label
304
+ * `由 V3 恢复` is resolved here once instead of in every plugin.
305
+ */
306
+ export declare function restoredFromNumberOf<T extends {
307
+ versionId: string;
308
+ number: number;
309
+ restoredFromVersionId?: string;
310
+ }>(version: T, all: T[]): number | undefined;
311
+ export declare function undoTargetOf<T extends VersionIdentity>(versions: T[], entry: VersionIdentity): T | undefined;
@@ -0,0 +1,270 @@
1
+ /** Classify one change from what it carries, so every surface agrees. */
2
+ export function versionChangeDirection(change) {
3
+ // Formatting is checked first: its before/after are equal, so every rule
4
+ // below would call it a replacement.
5
+ if (change.styleOnly === true)
6
+ return 'style';
7
+ if (change.kind !== 'cell' && change.kind !== 'variable')
8
+ return 'structure';
9
+ const hasBefore = change.before !== undefined && change.before !== null && String(change.before) !== '';
10
+ const hasAfter = change.after !== undefined && change.after !== null && String(change.after) !== '';
11
+ if (!hasBefore && hasAfter)
12
+ return 'add';
13
+ if (hasBefore && !hasAfter)
14
+ return 'remove';
15
+ return 'modify';
16
+ }
17
+ /* ------------------------------------------------- spreadsheet addresses */
18
+ /**
19
+ * Spreadsheet column letters for a 0-based column index: `0 → A`, `25 → Z`,
20
+ * `26 → AA`.
21
+ *
22
+ * The single column-letter implementation: the Table plugin, the shared diff
23
+ * view and other components all used to spell addresses their own way, and the
24
+ * user-visible result was `Sheet1!R3C2` in version summaries and `R3C2` in the
25
+ * comparison gutter — while a spreadsheet address is `Sheet1!C3`.
26
+ */
27
+ export function columnLabel(index) {
28
+ let value = Math.max(0, Math.floor(index)) + 1;
29
+ let label = '';
30
+ while (value > 0) {
31
+ const remainder = (value - 1) % 26;
32
+ label = String.fromCharCode(65 + remainder) + label;
33
+ value = Math.floor((value - 1) / 26);
34
+ }
35
+ return label;
36
+ }
37
+ /** A1-style cell reference without a sheet: `C3`. */
38
+ export function cellAddress(row, column) {
39
+ return `${columnLabel(column)}${Math.max(0, Math.floor(row)) + 1}`;
40
+ }
41
+ /**
42
+ * A1-style reference with the sheet it lives in: `Sheet1!C3`, or
43
+ * `Sheet1!A1:B3` when the change spans a range.
44
+ *
45
+ * A single cell stays `Sheet1!C3` rather than the degenerate `C3:C3`: an
46
+ * address is what the user reads in the formula bar, and the range form is only
47
+ * meaningful when it actually spans more than one cell.
48
+ */
49
+ export function formatCellReference(ref) {
50
+ const address = cellAddress(ref.row, ref.column);
51
+ const spans = ref.endRow !== undefined && ref.endColumn !== undefined
52
+ && (ref.endRow !== ref.row || ref.endColumn !== ref.column);
53
+ const range = spans ? `${address}:${cellAddress(ref.endRow, ref.endColumn)}` : address;
54
+ const sheet = ref.sheet?.trim();
55
+ return sheet === undefined || sheet === '' ? range : `${sheet}!${range}`;
56
+ }
57
+ export function conflictPayload(input) {
58
+ return {
59
+ code: 'REVISION_CONFLICT',
60
+ artifactKind: input.artifactKind,
61
+ artifactId: input.artifactId,
62
+ baseRevision: input.baseRevision,
63
+ currentRevision: input.currentRevision,
64
+ ...(input.changeCursor === undefined ? {} : { changeCursor: input.changeCursor }),
65
+ conflictSet: input.conflictSet ?? [],
66
+ message: input.message ?? `内容已变化:请求基于 revision ${input.baseRevision},当前是 ${input.currentRevision}`,
67
+ nextAction: input.nextAction ?? '重新读取最新版本后重试;需要保留本地修改时先查看差异',
68
+ retryable: true,
69
+ };
70
+ }
71
+ /**
72
+ * Version boundary policy shared by both plugins (PRD v3.1 §4).
73
+ *
74
+ * A Version collects **many Steps**; it is not one Version per interaction. The
75
+ * value below is a *session* window, not a typing debounce: while a user keeps
76
+ * working inside it every edit joins the same open Version, and that Version
77
+ * closes only when a real trigger fires —
78
+ *
79
+ * 1. the user presses 保存版本,
80
+ * 2. an Agent tool call commits,
81
+ * 3. the user leaves the artifact (the client sends `boundary: 'close'`),
82
+ * 4. the user has been idle for this whole window, so the session ended,
83
+ * 5. the Step safety cap is reached.
84
+ *
85
+ * It used to be 2s, which turned every thinking pause into its own Version and
86
+ * made the list read as "one Version per keystroke burst".
87
+ */
88
+ export const VERSION_IDLE_CLOSE_MS = 10 * 60_000;
89
+ /**
90
+ * How many Steps one Version may hold before it is closed and a new Version
91
+ * starts (PRD v3.3 D21).
92
+ *
93
+ * This is the *only* automatic boundary besides the idle window, so it is the
94
+ * user-visible one: 20 steps is roughly "a burst of work I would call one
95
+ * version". It used to be 50, which in practice meant a session never hit the
96
+ * cap and therefore never became a saved Version while the tab stayed open.
97
+ */
98
+ export const VERSION_MAX_STEPS = 20;
99
+ /* --------------------------------------- auto checkpoint retention (v3.1) */
100
+ /** Newest auto checkpoints kept regardless of age (PRD v3.1 §6.2, D3). */
101
+ export const AUTO_CHECKPOINT_KEEP = 100;
102
+ function candidateId(value) {
103
+ return value.versionId ?? value.id;
104
+ }
105
+ function candidateTime(value) {
106
+ return value.createdAt ?? value.timestamp;
107
+ }
108
+ function calendarDay(iso) {
109
+ if (iso === undefined)
110
+ return undefined;
111
+ const at = Date.parse(iso);
112
+ if (!Number.isFinite(at))
113
+ return undefined;
114
+ const date = new Date(at);
115
+ return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
116
+ }
117
+ /**
118
+ * Which auto checkpoints a store may drop (PRD v3.1 §6.2, D3).
119
+ *
120
+ * Kept, always: every `named` Version, the newest {@link AUTO_CHECKPOINT_KEEP}
121
+ * auto checkpoints, the newest auto checkpoint of every older calendar day, the
122
+ * line head / open Version, and anything a surviving Version still references
123
+ * (restore origin or parent), so pruning can never break a row that remains.
124
+ *
125
+ * `versions` may be in any order; the decision only uses identity, tier and
126
+ * time. The caller deletes the returned records and any content they alone own.
127
+ */
128
+ export function selectAutoCheckpointsToPrune(versions, options = {}) {
129
+ const keep = options.keep ?? AUTO_CHECKPOINT_KEEP;
130
+ const auto = versions
131
+ .filter(version => versionKindOf(version) === 'auto')
132
+ .sort((left, right) => right.number - left.number);
133
+ const candidates = new Set();
134
+ // Older days keep their last checkpoint, so a long-running session still has
135
+ // one row per working day instead of a hard cliff at exactly `keep`.
136
+ const seenDays = new Set(auto.slice(0, Math.max(0, keep))
137
+ .map(version => calendarDay(candidateTime(version)))
138
+ .filter((day) => day !== undefined));
139
+ for (const version of auto.slice(Math.max(0, keep))) {
140
+ const day = calendarDay(candidateTime(version));
141
+ if (day !== undefined && !seenDays.has(day)) {
142
+ seenDays.add(day);
143
+ continue;
144
+ }
145
+ candidates.add(version);
146
+ }
147
+ if (candidates.size === 0)
148
+ return [];
149
+ // Nothing that survives may point at a pruned record: a restore origin and a
150
+ // parent are both reasons to keep a checkpoint that would otherwise go.
151
+ const survivors = versions.filter(version => !candidates.has(version));
152
+ const protectedIds = new Set();
153
+ for (const version of survivors) {
154
+ if (version.restoredFromVersionId !== undefined)
155
+ protectedIds.add(version.restoredFromVersionId);
156
+ // The parent is only load-bearing while a record has no start state of its
157
+ // own to replay from; otherwise protecting it would retain a whole chain.
158
+ if (version.startContentHash === undefined && version.parentVersionId !== undefined) {
159
+ protectedIds.add(version.parentVersionId);
160
+ }
161
+ }
162
+ if (protectedIds.size > 0) {
163
+ for (const version of [...candidates]) {
164
+ const id = candidateId(version);
165
+ if (id !== undefined && protectedIds.has(id))
166
+ candidates.delete(version);
167
+ }
168
+ }
169
+ return [...candidates];
170
+ }
171
+ /* ------------------------------------------------- version tier (v3, §4/§7) */
172
+ /** Pre-v3 records carry no `kind`; they are user/Agent-meaningful Versions. */
173
+ export function versionKindOf(version) {
174
+ return version.kind === 'auto' ? 'auto' : 'named';
175
+ }
176
+ /**
177
+ * Newest-first comparator. Every list in the drawer reads as one timeline;
178
+ * Version numbers are handed out in creation order, so they break ties when a
179
+ * record has no usable timestamp.
180
+ */
181
+ export function compareVersionsNewestFirst(left, right) {
182
+ const leftAt = left.timestamp ?? left.createdAt;
183
+ const rightAt = right.timestamp ?? right.createdAt;
184
+ const leftMs = leftAt === undefined ? Number.NaN : Date.parse(leftAt);
185
+ const rightMs = rightAt === undefined ? Number.NaN : Date.parse(rightAt);
186
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs)
187
+ return rightMs - leftMs;
188
+ return right.number - left.number;
189
+ }
190
+ /**
191
+ * The one flat timeline the drawer renders (PRD v3.3 §7.3).
192
+ *
193
+ * The rule is deliberately the **same shape as the storage retention rule**
194
+ * (`selectAutoCheckpointsToPrune`), so the list can never hide a Version the
195
+ * store kept, nor offer one it dropped. A Version is listed when any of these
196
+ * holds:
197
+ *
198
+ * 1. it is `named` — a manual save, one Agent call, or a restore. Never hidden;
199
+ * 2. it is among the newest {@link AUTO_CHECKPOINT_KEEP} Versions;
200
+ * 3. it is the **last Version of its calendar day**.
201
+ *
202
+ * (2) and (3) are what make a user's own editing history visible: in the Data
203
+ * Table every browser save is an `auto` checkpoint, so hiding `auto` outright
204
+ * hid the per-day work the user remembered making. (3) keeps a long-running
205
+ * artifact to roughly one row per working day once it falls out of the recent
206
+ * window, while (2) keeps everything recent restorable.
207
+ *
208
+ * The result is ordered strictly newest-first; callers render `今天 / 昨天 /
209
+ * 更早` separators and exclude `open` Versions, which are still being written.
210
+ */
211
+ export function versionTimeline(versions, options = {}) {
212
+ const keep = Math.max(0, options.keep ?? AUTO_CHECKPOINT_KEEP);
213
+ const ordered = [...versions].sort(compareVersionsNewestFirst);
214
+ const listed = new Set();
215
+ // 1. deliberate Versions, always.
216
+ for (const version of ordered) {
217
+ if (versionKindOf(version) === 'named')
218
+ listed.add(version);
219
+ }
220
+ // 2. the newest `keep` Versions of any kind.
221
+ for (const version of ordered.slice(0, keep))
222
+ listed.add(version);
223
+ // 3. the last Version of every calendar day, walking back from the newest so
224
+ // the *last* one of each day is the one that survives.
225
+ const seenDays = new Set();
226
+ for (const version of ordered) {
227
+ const day = calendarDay(version.timestamp ?? version.createdAt);
228
+ if (day === undefined || seenDays.has(day))
229
+ continue;
230
+ seenDays.add(day);
231
+ listed.add(version);
232
+ }
233
+ return ordered.filter(version => listed.has(version));
234
+ }
235
+ /** `今天 / 昨天 / 更早` list separator (PRD v3 P3). */
236
+ export function versionDayLabel(timestamp, nowMs = Date.now()) {
237
+ if (timestamp === undefined)
238
+ return undefined;
239
+ const value = Date.parse(timestamp);
240
+ if (!Number.isFinite(value))
241
+ return undefined;
242
+ const startOfDay = (ms) => {
243
+ const date = new Date(ms);
244
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
245
+ };
246
+ const days = Math.round((startOfDay(nowMs) - startOfDay(value)) / 86_400_000);
247
+ if (days <= 0)
248
+ return '今天';
249
+ return days === 1 ? '昨天' : '更早';
250
+ }
251
+ export function versionIdentityOf(value) {
252
+ return value.versionId ?? value.id;
253
+ }
254
+ /**
255
+ * Display number of the Version a restore reinstated. Persisted records carry
256
+ * the target id, and only the surrounding list knows its number, so the label
257
+ * `由 V3 恢复` is resolved here once instead of in every plugin.
258
+ */
259
+ export function restoredFromNumberOf(version, all) {
260
+ if (version.restoredFromVersionId === undefined)
261
+ return undefined;
262
+ return all.find(candidate => candidate.versionId === version.restoredFromVersionId)?.number;
263
+ }
264
+ export function undoTargetOf(versions, entry) {
265
+ const entryId = versionIdentityOf(entry);
266
+ const older = versions
267
+ .filter(candidate => candidate.number < entry.number && versionIdentityOf(candidate) !== entryId)
268
+ .sort((left, right) => right.number - left.number);
269
+ return older.find(candidate => candidate.status !== 'superseded') ?? older[0];
270
+ }