@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,128 @@
1
+ /**
2
+ * Shared Univer canvas used by the embedded Datasheet panel and the
3
+ * standalone workbook page. The workspace owns persistence and sync; this
4
+ * component owns only Univer residency and its imperative editing surface.
5
+ */
6
+
7
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
8
+ import { mountWorkbook, type WorkbookDecoration } from './loader.ts'
9
+ import type { WorkbookFocusRange, WorkbookLocalChange, WorkbookOperation, WorkbookPresencePayload, WorkbookSelectionRange } from '../../types.ts'
10
+ import styles from './WorkbookView.module.css'
11
+
12
+ export interface WorkbookCanvasProps {
13
+ workbookData: object
14
+ /**
15
+ * Preview residency (docs/issue/0002 PRD v3.2 §7.2): the canvas is showing a past
16
+ * Version rather than live content, so it is mounted with Univer's editable
17
+ * permission off and its mutations are never reported as changes. The caller
18
+ * owns the preview state; this component only refuses to fork a Version.
19
+ */
20
+ readOnly?: boolean
21
+ /**
22
+ * Diff overlay for a preview residency: the cells that differ from the
23
+ * baseline, painted by the engine at mount (PRD v3.3 §7.2). Part of the
24
+ * residency, not a live API — a Step jump remounts the canvas, so a stale
25
+ * highlight cannot survive it.
26
+ */
27
+ decoration?: WorkbookDecoration
28
+ onDirty?: (change?: WorkbookLocalChange) => void
29
+ onPresence?: (presence: WorkbookPresencePayload) => void
30
+ onSelectionChange?: (ranges: readonly WorkbookSelectionRange[]) => void
31
+ onReady?: () => void
32
+ }
33
+
34
+ export interface WorkbookCanvasHandle {
35
+ save(): unknown
36
+ applyOperation(operation: WorkbookOperation): Promise<void>
37
+ focusRange(range: WorkbookFocusRange): boolean
38
+ /**
39
+ * Show a different workbook in the same residency. Returns false when the
40
+ * engine could not swap, so the caller can fall back to a remount.
41
+ */
42
+ swapWorkbook(next: object, decoration?: WorkbookDecoration): boolean
43
+ }
44
+
45
+ /** Resident workbook canvas shared by embedded and standalone workspaces. */
46
+ export const WorkbookCanvas = forwardRef<WorkbookCanvasHandle, WorkbookCanvasProps>(
47
+ function WorkbookCanvas({ workbookData, readOnly = false, decoration, onDirty, onPresence, onSelectionChange, onReady }, ref) {
48
+ const containerRef = useRef<HTMLDivElement>(null)
49
+ const handleRef = useRef<WorkbookCanvasHandle | null>(null)
50
+ const [ready, setReady] = useState(false)
51
+ const [failed, setFailed] = useState(false)
52
+ // Read through a ref so the mount effect stays keyed on the residency
53
+ // itself: `workbookData`/`readOnly` are fixed for a residency's lifetime,
54
+ // and the caller switches residency by changing the React `key`.
55
+ const readOnlyRef = useRef(readOnly)
56
+ readOnlyRef.current = readOnly
57
+ const decorationRef = useRef(decoration)
58
+ decorationRef.current = decoration
59
+ const selectionChangeRef = useRef(onSelectionChange)
60
+ selectionChangeRef.current = onSelectionChange
61
+ const readyRef = useRef(onReady)
62
+ readyRef.current = onReady
63
+
64
+ useEffect(() => {
65
+ if (handleRef.current !== null) return
66
+ const element = containerRef.current
67
+ if (element === null) return
68
+ let disposed = false
69
+ let mounted: Awaited<ReturnType<typeof mountWorkbook>> | null = null
70
+ const readOnlyMount = readOnlyRef.current
71
+ void (async () => {
72
+ await new Promise<void>(resolve => requestAnimationFrame(() => resolve()))
73
+ if (disposed) return
74
+ // Univer emits initialization/layout mutations while createUnit is
75
+ // still mounting. They describe the initial projection, not a user
76
+ // edit; forwarding them would mark the workbook dirty immediately and
77
+ // attempt a large snapshot save before the canvas is ready.
78
+ const mount = await mountWorkbook(element, workbookData, change => {
79
+ if (mounted !== null && !readOnlyMount) onDirty?.(change)
80
+ }, presence => {
81
+ if (mounted !== null && !readOnlyMount) onPresence?.(presence)
82
+ }, { readOnly: readOnlyMount, ...(decorationRef.current === undefined ? {} : { decoration: decorationRef.current }) }, ranges => {
83
+ if (mounted !== null && !readOnlyMount) selectionChangeRef.current?.(ranges)
84
+ })
85
+ mounted = mount
86
+ if (disposed) {
87
+ mount.dispose()
88
+ return
89
+ }
90
+ handleRef.current = {
91
+ save: () => mount.save(),
92
+ applyOperation: operation => mount.applyOperation(operation),
93
+ focusRange: range => mount.focusRange(range),
94
+ swapWorkbook: (next, nextDecoration) => mount.swapWorkbook(next, nextDecoration === undefined ? {} : { decoration: nextDecoration }),
95
+ }
96
+ setReady(true)
97
+ readyRef.current?.()
98
+ })().catch(() => {
99
+ if (!disposed) setFailed(true)
100
+ })
101
+ return () => {
102
+ disposed = true
103
+ mounted?.dispose()
104
+ mounted = null
105
+ handleRef.current = null
106
+ }
107
+ // Workbook data is fixed for the residency's lifetime.
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, [])
110
+
111
+ useImperativeHandle(ref, () => ({
112
+ save: () => (handleRef.current?.save() ?? null) as unknown,
113
+ applyOperation: async operation => { await handleRef.current?.applyOperation(operation) },
114
+ focusRange: range => handleRef.current?.focusRange(range) ?? false,
115
+ swapWorkbook: (next, nextDecoration) => handleRef.current?.swapWorkbook(next, nextDecoration) ?? false,
116
+ }))
117
+
118
+ return (
119
+ <div className={styles.view}>
120
+ <div className={styles.body}>
121
+ {!ready && !failed && <div className={styles.hint}>表格加载中…</div>}
122
+ {failed && <div className={styles.hint}>表格渲染失败</div>}
123
+ <div ref={containerRef} className={styles.univer} />
124
+ </div>
125
+ </div>
126
+ )
127
+ },
128
+ )
@@ -0,0 +1,30 @@
1
+ .view {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ min-height: 320px;
6
+ border: 0;
7
+ border-radius: 0;
8
+ overflow: hidden;
9
+ background: var(--tita-surface);
10
+ }
11
+
12
+ .body {
13
+ position: relative;
14
+ flex: 1;
15
+ min-height: 0;
16
+ }
17
+
18
+ .hint {
19
+ padding: 32px 20px;
20
+ font-size: 13px;
21
+ color: var(--tita-ink-muted);
22
+ text-align: center;
23
+ }
24
+
25
+ .univer {
26
+ position: absolute;
27
+ inset: 0;
28
+ height: 100%;
29
+ width: 100%;
30
+ }
@@ -0,0 +1,3 @@
1
+ /** Backwards-compatible alias for the shared WorkbookCanvas. */
2
+ export { WorkbookCanvas as WorkbookView } from './WorkbookCanvas.tsx'
3
+ export type { WorkbookCanvasHandle as WorkbookViewHandle, WorkbookCanvasProps as WorkbookViewProps } from './WorkbookCanvas.tsx'
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Lazy Univer half for dsh-data-table. The heavy Univer engine lives
3
+ * in a separate chunk (`lib/univer-client.js`, served by the node half at
4
+ * `/data-sheet/univer-client.js`); this module loads that chunk on the first
5
+ * sheet render so the ~16MB graph is never in the page's boot bundle.
6
+ * CSV export is engine-free and stays here.
7
+ * @module dsh-data-table/client/univer
8
+ */
9
+
10
+ import type { WorkbookLocalChange, WorkbookPresencePayload, WorkbookSelectionRange } from '../../types.ts'
11
+ import type { UniverChunkApi, WorkbookDecoration, WorkbookMount, WorkbookMountOptions } from '../../univer/chunk.ts'
12
+
13
+ // The chunk contract (mount shape, mount options, and the `window` global) is
14
+ // declared once in `../../univer/chunk.ts`, so the loader and the ~16MB chunk it
15
+ // loads cannot drift apart. Re-exported here because the client half is that
16
+ // contract's public surface for the panel components.
17
+ export type { WorkbookDecoration, WorkbookDecorationCell, WorkbookMount, WorkbookMountOptions } from '../../univer/chunk.ts'
18
+
19
+ /**
20
+ * Engine-chunk candidates, tried in order: the node-half route is the durable
21
+ * source (registered when the dsh server runs the current plugin code); the
22
+ * dist asset is the stopgap for a server that was started before the route
23
+ * existed (each failed source is a dead end, not retried).
24
+ */
25
+ function dataSheetAssetPath(path: string, keepQuery = false): string {
26
+ const transport = (globalThis as { __DSH_TRANSPORT__?: { ownsHost?: boolean } }).__DSH_TRANSPORT__
27
+ if (transport?.ownsHost === true) {
28
+ // The path travels as a query parameter, so a cache-busting query string
29
+ // would become part of the resolved file path; drop it in this mode.
30
+ const [withoutQuery] = keepQuery ? path.split('?') : [path]
31
+ return `/api/data-sheet?path=${encodeURIComponent(withoutQuery ?? path)}`
32
+ }
33
+ return path
34
+ }
35
+
36
+ declare const __DSH_DATA_SHEET_BUILD__: string | undefined
37
+
38
+ /**
39
+ * Build tag appended to the engine chunk URL. The chunk is a plain script the
40
+ * browser caches by URL, so a rebuilt chunk used to keep running in an already
41
+ * open page; a unique query makes every build a different URL. The host
42
+ * transport mode passes the path as a query parameter, so the tag is only
43
+ * added on the direct route (which matches on `pathname` and ignores queries).
44
+ */
45
+ const CHUNK_VERSION = typeof __DSH_DATA_SHEET_BUILD__ === 'string' ? __DSH_DATA_SHEET_BUILD__ : 'dev'
46
+ const chunkPath = `/data-sheet/univer-client.js?v=${encodeURIComponent(CHUNK_VERSION)}`
47
+ const CHUNK_SOURCES = [dataSheetAssetPath(chunkPath, true), '/assets/data-sheet-univer.js']
48
+
49
+ let chunkPromise: Promise<UniverChunkApi> | null = null
50
+
51
+ /** Load the engine chunk once (script tag; shared across concurrent callers). */
52
+ function loadChunk(): Promise<UniverChunkApi> {
53
+ if (window.__bestExcelUniver !== undefined) return Promise.resolve(window.__bestExcelUniver)
54
+ if (chunkPromise !== null) return chunkPromise
55
+ chunkPromise = (async () => {
56
+ for (const src of CHUNK_SOURCES) {
57
+ try {
58
+ await new Promise<void>((resolve, reject) => {
59
+ const el = document.createElement('script')
60
+ el.async = true
61
+ el.src = src
62
+ el.addEventListener('load', () => {
63
+ if (window.__bestExcelUniver === undefined) reject(new Error(`univer chunk loaded without its global: ${src}`))
64
+ else resolve()
65
+ }, { once: true })
66
+ el.addEventListener('error', () => reject(new Error(`univer chunk failed to load: ${src}`)))
67
+ document.head.append(el)
68
+ })
69
+ const api = window.__bestExcelUniver
70
+ if (api === undefined) throw new Error('univer chunk resolved without its global')
71
+ return api
72
+ } catch (error) {
73
+ if (src === CHUNK_SOURCES[CHUNK_SOURCES.length - 1]) throw error
74
+ }
75
+ }
76
+ throw new Error('unreachable')
77
+ })()
78
+ return chunkPromise
79
+ }
80
+
81
+ /**
82
+ * Mount one resident Univer engine into a container with a workbook snapshot.
83
+ * Resolves after the engine chunk arrived (first call pays the fetch/parse).
84
+ * `options.readOnly` mounts a preview residency: the snapshot is a past
85
+ * Version, so no edit is reported and none can be made (PRD v3.2 §7.2).
86
+ */
87
+ export async function mountWorkbook(container: HTMLElement, workbookData: object, onDirty?: (change?: WorkbookLocalChange) => void, onPresence?: (presence: WorkbookPresencePayload) => void, options?: WorkbookMountOptions, onSelection?: (ranges: readonly WorkbookSelectionRange[]) => void): Promise<WorkbookMount> {
88
+ const api = await loadChunk()
89
+ return api.mountWorkbook(container, workbookData, onDirty, onPresence, options, onSelection)
90
+ }
@@ -0,0 +1,10 @@
1
+ declare module 'react-dom/client' {
2
+ import type { ReactNode } from 'react'
3
+
4
+ interface Root {
5
+ render(children: ReactNode): void
6
+ unmount(): void
7
+ }
8
+
9
+ export function createRoot(container: Element | DocumentFragment): Root
10
+ }
@@ -0,0 +1,19 @@
1
+ import { createRoot } from 'react-dom/client'
2
+ import { setDshSessionId } from '../transport/workbookApi.ts'
3
+ import { StandaloneWorkbookPage } from './StandaloneWorkbookPage.tsx'
4
+ import './standalone.css'
5
+
6
+ const root = document.getElementById('data-sheet-workbook-root')
7
+ if (root !== null) {
8
+ const query = new URLSearchParams(window.location.search)
9
+ // The project this page reads: the address that opened it carries the Session id
10
+ // (see `standaloneWorkbookPath`), because a page in its own document — a new tab
11
+ // or the in-app full page — has no sidebar to ask.
12
+ setDshSessionId(query.get('sessionId') ?? undefined)
13
+ const pathMatch = /^\/data-sheet\/workbook\/([^/]+)\/view$/.exec(window.location.pathname)
14
+ const queryId = query.get('workbook')
15
+ const workbookId = pathMatch?.[1] === undefined
16
+ ? (queryId ?? 'main')
17
+ : decodeURIComponent(pathMatch[1])
18
+ createRoot(root).render(<StandaloneWorkbookPage workbookId={workbookId} />)
19
+ }
@@ -0,0 +1,12 @@
1
+ html,
2
+ body,
3
+ #data-sheet-workbook-root {
4
+ width: 100%;
5
+ height: 100%;
6
+ margin: 0;
7
+ overflow: hidden;
8
+ }
9
+
10
+ body {
11
+ background: var(--tita-canvas);
12
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Ambient declarations for the sheets preset family's `lib/es` runtime
3
+ * modules (index + locale packs): the published packages ship no sibling
4
+ * declarations next to the runtime files. Mirrors the typed counterparts —
5
+ * the preset factory signature (index modules) and the default locale object.
6
+ * @module dsh-data-table/client/univer-locales
7
+ */
8
+
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ambient shim for the runtime-only subpath (IPreset is not exported).
10
+ type PresetFactory = (config?: unknown) => any
11
+
12
+ declare module '@univerjs/presets/lib/es/preset-sheets-core/index.js' {
13
+ export const UniverSheetsCorePreset: PresetFactory
14
+ }
15
+ declare module '@univerjs/presets/lib/es/preset-sheets-filter/index.js' {
16
+ export const UniverSheetsFilterPreset: PresetFactory
17
+ }
18
+ declare module '@univerjs/presets/lib/es/preset-sheets-sort/index.js' {
19
+ export const UniverSheetsSortPreset: PresetFactory
20
+ }
21
+ declare module '@univerjs/presets/lib/es/preset-sheets-find-replace/index.js' {
22
+ export const UniverSheetsFindReplacePreset: PresetFactory
23
+ }
24
+ declare module '@univerjs/presets/lib/es/preset-sheets-conditional-formatting/index.js' {
25
+ export const UniverSheetsConditionalFormattingPreset: PresetFactory
26
+ }
27
+ declare module '@univerjs/presets/lib/es/preset-sheets-data-validation/index.js' {
28
+ export const UniverSheetsDataValidationPreset: PresetFactory
29
+ }
30
+ declare module '@univerjs/presets/lib/es/preset-sheets-drawing/index.js' {
31
+ export const UniverSheetsDrawingPreset: PresetFactory
32
+ }
33
+ declare module '@univerjs/presets/lib/es/preset-sheets-hyper-link/index.js' {
34
+ export const UniverSheetsHyperLinkPreset: PresetFactory
35
+ }
36
+ declare module '@univerjs/presets/lib/es/preset-sheets-note/index.js' {
37
+ export const UniverSheetsNotePreset: PresetFactory
38
+ }
39
+ declare module '@univerjs/presets/lib/es/preset-sheets-thread-comment/index.js' {
40
+ export const UniverSheetsThreadCommentPreset: PresetFactory
41
+ }
42
+ declare module '@univerjs/presets/lib/es/preset-sheets-table/index.js' {
43
+ export const UniverSheetsTablePreset: PresetFactory
44
+ }
45
+
46
+ declare module '@univerjs/presets/lib/es/preset-sheets-core/locales/*.js' {
47
+ const locale: Record<string, unknown>
48
+ export default locale
49
+ }
50
+ declare module '@univerjs/presets/lib/es/preset-sheets-filter/locales/*.js' {
51
+ const locale: Record<string, unknown>
52
+ export default locale
53
+ }
54
+ declare module '@univerjs/presets/lib/es/preset-sheets-sort/locales/*.js' {
55
+ const locale: Record<string, unknown>
56
+ export default locale
57
+ }
58
+ declare module '@univerjs/presets/lib/es/preset-sheets-find-replace/locales/*.js' {
59
+ const locale: Record<string, unknown>
60
+ export default locale
61
+ }
62
+ declare module '@univerjs/presets/lib/es/preset-sheets-conditional-formatting/locales/*.js' {
63
+ const locale: Record<string, unknown>
64
+ export default locale
65
+ }
66
+ declare module '@univerjs/presets/lib/es/preset-sheets-data-validation/locales/*.js' {
67
+ const locale: Record<string, unknown>
68
+ export default locale
69
+ }
70
+ declare module '@univerjs/presets/lib/es/preset-sheets-drawing/locales/*.js' {
71
+ const locale: Record<string, unknown>
72
+ export default locale
73
+ }
74
+ declare module '@univerjs/presets/lib/es/preset-sheets-hyper-link/locales/*.js' {
75
+ const locale: Record<string, unknown>
76
+ export default locale
77
+ }
78
+ declare module '@univerjs/presets/lib/es/preset-sheets-note/locales/*.js' {
79
+ const locale: Record<string, unknown>
80
+ export default locale
81
+ }
82
+ declare module '@univerjs/presets/lib/es/preset-sheets-thread-comment/locales/*.js' {
83
+ const locale: Record<string, unknown>
84
+ export default locale
85
+ }
86
+ declare module '@univerjs/presets/lib/es/preset-sheets-table/locales/*.js' {
87
+ const locale: Record<string, unknown>
88
+ export default locale
89
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Ambient types for the deep Univer preset entry. The preset factory is only
3
+ * exported from the es build subpath (the npm exports map types live at a
4
+ * different subpath), so this standalone declaration types the runtime import
5
+ * in `client/univer/loader.ts`. Must stay free of top-level imports to remain an ambient
6
+ * module declaration rather than an augmentation.
7
+ * @module dsh-data-table/client/univer-modules
8
+ */
9
+
10
+ declare module '@univerjs/presets/lib/es/preset-sheets-core/index.js' {
11
+ import type { Plugin, PluginCtor } from '@univerjs/core'
12
+
13
+ export interface UniverSheetsCorePresetOptions {
14
+ container?: HTMLElement
15
+ header?: boolean
16
+ toolbar?: boolean
17
+ formulaBar?: boolean
18
+ statusBarStatistic?: boolean
19
+ footer?: boolean
20
+ contextMenu?: boolean
21
+ disableAutoFocus?: boolean
22
+ }
23
+ export function UniverSheetsCorePreset(
24
+ config?: Partial<UniverSheetsCorePresetOptions>,
25
+ ): { plugins: Array<PluginCtor<Plugin> | [PluginCtor<Plugin>, unknown]> }
26
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The version drawer and the two dialogs that answer from it (docs/issue/0022 P5).
3
+ *
4
+ * This is the chrome half of the history surface: `records.ts` turns a stored
5
+ * Version into the row the list draws, `inspect.ts` produces what the comparison
6
+ * bar needs, and this assembles the Drawer, the 保存版本 dialog and the
7
+ * 恢复/撤销 confirmation out of them. It was inline in `SheetPanel.tsx`, which is
8
+ * pinned at its size for a reason — the panel's job is the canvas and the wire,
9
+ * and the copy and structure of the history UI are not part of that.
10
+ */
11
+ import { Button } from '@tita-agent/ui/components'
12
+ import {
13
+ Drawer,
14
+ DrawerBody,
15
+ DrawerContent,
16
+ DrawerDescription,
17
+ DrawerHeader,
18
+ DrawerTitle,
19
+ } from '@tita-agent/ui/overlays'
20
+ import { Save } from '@tita-agent/ui/icons'
21
+ import {
22
+ RestoreDialog,
23
+ SaveVersionDialog,
24
+ VersionManager,
25
+ type VersionManagerActions,
26
+ type VersionManagerGroup,
27
+ } from '@tita-agent/ui/versions'
28
+ import type { VersionRestoreRequest } from './inspect.ts'
29
+
30
+ export interface VersionChromeProps {
31
+ open: boolean
32
+ onOpenChange: (open: boolean) => void
33
+ /** Name of the artifact, for the drawer's header line. */
34
+ artifactName: string
35
+ /** How many Versions the list holds, and when the newest one landed. */
36
+ versionCount: number
37
+ headLabel: string | undefined
38
+ /** The Version the canvas would restore, or undo, to. */
39
+ restorableId: string | undefined
40
+ loading: boolean
41
+ groups: VersionManagerGroup[]
42
+ actions: VersionManagerActions
43
+ save: {
44
+ open: boolean
45
+ pending: boolean
46
+ /** True while no artifact is open: there is nothing to name. */
47
+ disabled: boolean
48
+ onOpenChange: (open: boolean) => void
49
+ onConfirm: (name: string) => void
50
+ }
51
+ restore: {
52
+ /** The pending confirmation, or null when the dialog is closed. */
53
+ request: VersionRestoreRequest | null
54
+ pending: boolean
55
+ onOpenChange: (open: boolean) => void
56
+ onConfirm: () => void
57
+ }
58
+ }
59
+
60
+ /** Drawer, 保存版本 dialog and 恢复 confirmation, as one piece of chrome. */
61
+ export function VersionChrome({
62
+ open,
63
+ onOpenChange,
64
+ artifactName,
65
+ versionCount,
66
+ headLabel,
67
+ restorableId,
68
+ loading,
69
+ groups,
70
+ actions,
71
+ save,
72
+ restore,
73
+ }: VersionChromeProps): React.ReactElement {
74
+ return (
75
+ <>
76
+ <Drawer open={open} onOpenChange={onOpenChange}>
77
+ <DrawerContent aria-label="版本管理">
78
+ <DrawerHeader>
79
+ <div>
80
+ <DrawerTitle>版本管理</DrawerTitle>
81
+ <DrawerDescription>
82
+ {versionCount === 0
83
+ ? `${artifactName} · 还没有版本`
84
+ : `${artifactName} · ${versionCount} 个版本 · 最近保存 ${headLabel ?? ''}`}
85
+ </DrawerDescription>
86
+ </div>
87
+ <Button variant="outline" size="sm" disabled={save.disabled} onClick={() => save.onOpenChange(true)}>
88
+ <Save size={14} aria-hidden="true" />保存版本
89
+ </Button>
90
+ </DrawerHeader>
91
+ <DrawerBody>
92
+ <VersionManager loading={loading} groups={groups} actions={{ ...actions, restorableId }} />
93
+ </DrawerBody>
94
+ </DrawerContent>
95
+ </Drawer>
96
+ <SaveVersionDialog
97
+ open={save.open}
98
+ onOpenChange={save.onOpenChange}
99
+ pending={save.pending}
100
+ onConfirm={save.onConfirm}
101
+ />
102
+ <RestoreDialog
103
+ open={restore.request !== null}
104
+ onOpenChange={restore.onOpenChange}
105
+ targetLabel={restore.request?.label ?? ''}
106
+ {...(restore.request?.summary === undefined ? {} : { targetSummary: restore.request.summary })}
107
+ {...(restore.request === null ? {} : { mode: restore.request.mode })}
108
+ artifactLabel="工作簿内容"
109
+ pending={restore.pending}
110
+ onConfirm={restore.onConfirm}
111
+ />
112
+ </>
113
+ )
114
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The read-only comparison surface, as view models (docs/issue/0022 P5).
3
+ *
4
+ * `records.ts` translates a stored Version into the row the drawer draws; this
5
+ * takes that row — or a fetched diff — and produces the things the *inspect*
6
+ * half needs: the two pickers' options, the canvas overlay, the counts in the
7
+ * bar, and the revision a baseline id addresses. All of it is pure, which is the
8
+ * point: the panel is 1800 lines of ref-mutating effects, and each of these was
9
+ * a `useMemo` body buried in the middle of it.
10
+ */
11
+ import { versionChangeDirection } from '#contracts'
12
+ import {
13
+ versionOptionLabel,
14
+ versionStepOptionLabel,
15
+ type VersionInspectOption,
16
+ type VersionInspectorSummary,
17
+ type VersionManagerEntry,
18
+ } from '@tita-agent/ui/versions'
19
+ import type { WorkbookChange } from '../../types.ts'
20
+ import type { WorkbookDecoration } from '../univer/loader.ts'
21
+ import { toDecoration } from './preview-decoration.ts'
22
+
23
+ /**
24
+ * One preview residency (docs/issue/0002 PRD v3.2 §7.2). `step` is part of the
25
+ * canvas's React `key` so replaying a Step of the same Version remounts it;
26
+ * `snapshot` is the content the canvas renders read-only.
27
+ */
28
+ export interface WorkbookPreviewState {
29
+ versionId: string
30
+ revision: number
31
+ /** Rebuilt content state; the canvas renders exactly this. */
32
+ snapshot: object
33
+ /** Step index when the preview is a Step replay rather than the Version end. */
34
+ step: number | null
35
+ /**
36
+ * What is on screen vs what it is compared against: a Step against the state
37
+ * before it, a Version against the newer one it followed. Drives the canvas
38
+ * overlay so the reader does not have to find the changed cells (v3.3 §7.2).
39
+ */
40
+ changes: WorkbookChange[]
41
+ /**
42
+ * Which end of the measured range the canvas is showing, so only changes the
43
+ * reader can actually see get tinted.
44
+ */
45
+ changesSide: 'before' | 'after'
46
+ /** True when the changes could not be rebuilt (compressed history). */
47
+ changesUnavailable?: boolean
48
+ /** True when the diff stopped at the server's limit, so the counts are a floor. */
49
+ changesTruncated?: boolean
50
+ }
51
+
52
+ /**
53
+ * The overlay for a residency: identical to what an in-place swap painted, so a
54
+ * remount and a swap show exactly the same thing.
55
+ */
56
+ export function previewDecorationOf(preview: WorkbookPreviewState | null): WorkbookDecoration | undefined {
57
+ return preview === null ? undefined : toDecoration(preview.changes, preview.changesSide)
58
+ }
59
+
60
+ /**
61
+ * Counts + legend for the bar, derived from the same list the canvas tints.
62
+ *
63
+ * The counts carry the same bucket names the engine paints with, so the legend
64
+ * and the grid are read as one thing; `truncated` is carried through because a
65
+ * capped list would otherwise read as the whole comparison.
66
+ */
67
+ export function inspectorSummaryOf(preview: WorkbookPreviewState | null): VersionInspectorSummary {
68
+ const summary: VersionInspectorSummary = { total: 0, add: 0, remove: 0, modify: 0, structure: 0, style: 0 }
69
+ if (preview === null) return summary
70
+ summary.total = preview.changes.length
71
+ if (preview.changesUnavailable === true) summary.unavailable = true
72
+ if (preview.changesTruncated === true) summary.truncated = true
73
+ for (const change of preview.changes) {
74
+ summary[versionChangeDirection({
75
+ kind: change.kind,
76
+ target: '',
77
+ description: '',
78
+ ...(change.row === undefined ? {} : { row: change.row }),
79
+ ...(change.column === undefined ? {} : { column: change.column }),
80
+ ...(change.before === undefined ? {} : { before: change.before }),
81
+ ...(change.after === undefined ? {} : { after: change.after }),
82
+ ...(change.styleOnly === true ? { styleOnly: true } : {}),
83
+ })] += 1
84
+ }
85
+ return summary
86
+ }
87
+
88
+ /**
89
+ * Preview switcher options: every Version the artifact could be swapped onto,
90
+ * newest-first, labelled the way its row was. Built from the timeline so the
91
+ * selector can never offer a Version the list does not show.
92
+ *
93
+ * The Steps give the picker its second level; the shared helper budgets a Step's
94
+ * summary to one menu line.
95
+ */
96
+ export function inspectOptionsOf(entries: readonly VersionManagerEntry[]): VersionInspectOption[] {
97
+ return entries
98
+ .filter(entry => entry.replayable)
99
+ .map(entry => ({
100
+ id: entry.id,
101
+ label: versionOptionLabel({ summary: entry.summary, ...(entry.timestamp === undefined ? { time: entry.time } : { timestamp: entry.timestamp }) }),
102
+ steps: (entry.steps ?? []).map(step => ({
103
+ index: step.index,
104
+ label: versionStepOptionLabel(step),
105
+ ...(step.revision === undefined ? {} : { revision: step.revision }),
106
+ })),
107
+ }))
108
+ }
109
+
110
+ /**
111
+ * Revision a baseline id measures against.
112
+ *
113
+ * A Step is measured at the revision it produced. Falling back to the Version
114
+ * rather than refusing keeps a record written before the field existed usable —
115
+ * it just compares the whole Version.
116
+ */
117
+ export function baselineRevisionOf(
118
+ entry: VersionManagerEntry | undefined,
119
+ fallbackRevision: number,
120
+ step?: number,
121
+ ): number {
122
+ if (entry === undefined) return fallbackRevision
123
+ if (step !== undefined) {
124
+ const found = entry.steps?.find(candidate => candidate.index === step)
125
+ if (found?.revision !== undefined) return found.revision
126
+ }
127
+ return entry.revision ?? fallbackRevision
128
+ }
129
+
130
+ /** What 「恢复」/「撤销这次改动」 posts, once the confirmation dialog is answered. */
131
+ export interface VersionRestoreRequest {
132
+ revision: number
133
+ /** Render-time label, for the dialog and the notice. */
134
+ label: string
135
+ /** Durable summary written into the new Version's record. */
136
+ summary: string
137
+ mode: 'restore' | 'undo'
138
+ }