aicodeman 1.35.0 → 1.41.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 (316) hide show
  1. package/README.md +33 -7
  2. package/dist/attachment-registry.d.ts +6 -0
  3. package/dist/attachment-registry.d.ts.map +1 -1
  4. package/dist/attachment-registry.js +9 -3
  5. package/dist/attachment-registry.js.map +1 -1
  6. package/dist/cli-agent.d.ts +225 -0
  7. package/dist/cli-agent.d.ts.map +1 -0
  8. package/dist/cli-agent.js +767 -0
  9. package/dist/cli-agent.js.map +1 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +8 -32
  12. package/dist/cli.js.map +1 -1
  13. package/dist/codeman-credentials.d.ts +27 -0
  14. package/dist/codeman-credentials.d.ts.map +1 -0
  15. package/dist/codeman-credentials.js +66 -0
  16. package/dist/codeman-credentials.js.map +1 -0
  17. package/dist/config/cli-registry/patterns.d.ts +6 -0
  18. package/dist/config/cli-registry/patterns.d.ts.map +1 -1
  19. package/dist/config/cli-registry/patterns.js +13 -0
  20. package/dist/config/cli-registry/patterns.js.map +1 -1
  21. package/dist/config/cli-registry/schema.d.ts +15 -0
  22. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  23. package/dist/config/cli-registry/schema.js +63 -2
  24. package/dist/config/cli-registry/schema.js.map +1 -1
  25. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  26. package/dist/config/cli-registry/stock.js +180 -7
  27. package/dist/config/cli-registry/stock.js.map +1 -1
  28. package/dist/config/cli-registry/types.d.ts +105 -7
  29. package/dist/config/cli-registry/types.d.ts.map +1 -1
  30. package/dist/deepseek-route-config.d.ts +110 -0
  31. package/dist/deepseek-route-config.d.ts.map +1 -0
  32. package/dist/deepseek-route-config.js +477 -0
  33. package/dist/deepseek-route-config.js.map +1 -0
  34. package/dist/git-clone.d.ts.map +1 -1
  35. package/dist/git-clone.js +4 -0
  36. package/dist/git-clone.js.map +1 -1
  37. package/dist/git-workspace-status.d.ts +31 -5
  38. package/dist/git-workspace-status.d.ts.map +1 -1
  39. package/dist/git-workspace-status.js +49 -29
  40. package/dist/git-workspace-status.js.map +1 -1
  41. package/dist/image-watcher.d.ts.map +1 -1
  42. package/dist/image-watcher.js +6 -2
  43. package/dist/image-watcher.js.map +1 -1
  44. package/dist/mcp-sync-targets.d.ts +34 -0
  45. package/dist/mcp-sync-targets.d.ts.map +1 -0
  46. package/dist/mcp-sync-targets.js +55 -0
  47. package/dist/mcp-sync-targets.js.map +1 -0
  48. package/dist/mcp-sync.d.ts +2 -1
  49. package/dist/mcp-sync.d.ts.map +1 -1
  50. package/dist/mcp-sync.js +62 -1
  51. package/dist/mcp-sync.js.map +1 -1
  52. package/dist/model-config-resolvers.d.ts +29 -0
  53. package/dist/model-config-resolvers.d.ts.map +1 -0
  54. package/dist/model-config-resolvers.js +34 -0
  55. package/dist/model-config-resolvers.js.map +1 -0
  56. package/dist/session-display-model.d.ts +110 -0
  57. package/dist/session-display-model.d.ts.map +1 -0
  58. package/dist/session-display-model.js +154 -0
  59. package/dist/session-display-model.js.map +1 -0
  60. package/dist/session.d.ts +145 -8
  61. package/dist/session.d.ts.map +1 -1
  62. package/dist/session.js +290 -39
  63. package/dist/session.js.map +1 -1
  64. package/dist/subagent-watcher.d.ts.map +1 -1
  65. package/dist/subagent-watcher.js +29 -6
  66. package/dist/subagent-watcher.js.map +1 -1
  67. package/dist/templates/case-template.md +2 -0
  68. package/dist/tmux-manager.d.ts +5 -0
  69. package/dist/tmux-manager.d.ts.map +1 -1
  70. package/dist/tmux-manager.js +47 -22
  71. package/dist/tmux-manager.js.map +1 -1
  72. package/dist/tui/tui-client.d.ts +3 -17
  73. package/dist/tui/tui-client.d.ts.map +1 -1
  74. package/dist/tui/tui-client.js +3 -44
  75. package/dist/tui/tui-client.js.map +1 -1
  76. package/dist/types/session.d.ts +28 -1
  77. package/dist/types/session.d.ts.map +1 -1
  78. package/dist/types/session.js.map +1 -1
  79. package/dist/types/tools.d.ts +1 -1
  80. package/dist/types/tools.d.ts.map +1 -1
  81. package/dist/utils/deepseek-cli-resolver.d.ts +8 -0
  82. package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
  83. package/dist/utils/deepseek-cli-resolver.js +18 -2
  84. package/dist/utils/deepseek-cli-resolver.js.map +1 -1
  85. package/dist/utils/index.d.ts +1 -0
  86. package/dist/utils/index.d.ts.map +1 -1
  87. package/dist/utils/index.js +1 -0
  88. package/dist/utils/index.js.map +1 -1
  89. package/dist/utils/process-exit-wait.d.ts +40 -0
  90. package/dist/utils/process-exit-wait.d.ts.map +1 -0
  91. package/dist/utils/process-exit-wait.js +68 -0
  92. package/dist/utils/process-exit-wait.js.map +1 -0
  93. package/dist/web/launch-defaults.d.ts +22 -0
  94. package/dist/web/launch-defaults.d.ts.map +1 -0
  95. package/dist/web/launch-defaults.js +46 -0
  96. package/dist/web/launch-defaults.js.map +1 -0
  97. package/dist/web/paste-image-gc.d.ts +46 -6
  98. package/dist/web/paste-image-gc.d.ts.map +1 -1
  99. package/dist/web/paste-image-gc.js +106 -32
  100. package/dist/web/paste-image-gc.js.map +1 -1
  101. package/dist/web/public/admin-ui.js.gz +0 -0
  102. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  103. package/dist/web/public/app.6f8552e1.js +43 -0
  104. package/dist/web/public/app.6f8552e1.js.br +0 -0
  105. package/dist/web/public/app.6f8552e1.js.gz +0 -0
  106. package/dist/web/public/approvals-ui.js.gz +0 -0
  107. package/dist/web/public/{constants.d95fade9.js → constants.bc9c1f63.js} +1276 -127
  108. package/dist/web/public/constants.bc9c1f63.js.br +0 -0
  109. package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
  110. package/dist/web/public/cron-ui.js.gz +0 -0
  111. package/dist/web/public/entrance-animations.js +491 -43
  112. package/dist/web/public/entrance-animations.js.br +0 -0
  113. package/dist/web/public/entrance-animations.js.gz +0 -0
  114. package/dist/web/public/git-status-ui.js +55 -9
  115. package/dist/web/public/git-status-ui.js.br +0 -0
  116. package/dist/web/public/git-status-ui.js.gz +0 -0
  117. package/dist/web/public/home-sessions.js +13 -9
  118. package/dist/web/public/home-sessions.js.br +0 -0
  119. package/dist/web/public/home-sessions.js.gz +0 -0
  120. package/dist/web/public/host-wake-ui.js.gz +0 -0
  121. package/dist/web/public/i18n.a9840ea2.js +2 -0
  122. package/dist/web/public/i18n.a9840ea2.js.br +0 -0
  123. package/dist/web/public/i18n.a9840ea2.js.gz +0 -0
  124. package/dist/web/public/{image-input.a216cf9e.js → image-input.e00471c6.js} +21 -9
  125. package/dist/web/public/image-input.e00471c6.js.br +0 -0
  126. package/dist/web/public/image-input.e00471c6.js.gz +0 -0
  127. package/dist/web/public/index.html +250 -59
  128. package/dist/web/public/index.html.br +0 -0
  129. package/dist/web/public/index.html.gz +0 -0
  130. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  131. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  132. package/dist/web/public/{mobile-handlers.23a85db1.js → mobile-handlers.0ef9c311.js} +20 -0
  133. package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
  134. package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
  135. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  136. package/dist/web/public/mobile-overview.js.gz +0 -0
  137. package/dist/web/public/mobile.111b187d.css +1 -0
  138. package/dist/web/public/mobile.111b187d.css.br +0 -0
  139. package/dist/web/public/mobile.111b187d.css.gz +0 -0
  140. package/dist/web/public/{notification-manager.50bb87d8.js → notification-manager.feb82c04.js} +19 -2
  141. package/dist/web/public/notification-manager.feb82c04.js.br +0 -0
  142. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  143. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  144. package/dist/web/public/{panels-ui.6a5898d9.js → panels-ui.71846fcb.js} +4 -4
  145. package/dist/web/public/panels-ui.71846fcb.js.br +0 -0
  146. package/dist/web/public/panels-ui.71846fcb.js.gz +0 -0
  147. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  148. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  149. package/dist/web/public/readmymind-ui.js.gz +0 -0
  150. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  151. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  152. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  153. package/dist/web/public/session-lineage.js +229 -86
  154. package/dist/web/public/session-lineage.js.br +0 -0
  155. package/dist/web/public/session-lineage.js.gz +0 -0
  156. package/dist/web/public/session-ui.f731602c.js +100 -0
  157. package/dist/web/public/session-ui.f731602c.js.br +0 -0
  158. package/dist/web/public/session-ui.f731602c.js.gz +0 -0
  159. package/dist/web/public/settings-ui.4e33390f.js +97 -0
  160. package/dist/web/public/settings-ui.4e33390f.js.br +0 -0
  161. package/dist/web/public/settings-ui.4e33390f.js.gz +0 -0
  162. package/dist/web/public/spreadsheet-preview-worker.js +290 -0
  163. package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
  164. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  165. package/dist/web/public/spreadsheet-preview.af82fa2f.js +555 -0
  166. package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
  167. package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
  168. package/dist/web/public/spreadsheet-xlsx-core.js +1006 -0
  169. package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
  170. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  171. package/dist/web/public/styles.81a72906.css +1 -0
  172. package/dist/web/public/styles.81a72906.css.br +0 -0
  173. package/dist/web/public/styles.81a72906.css.gz +0 -0
  174. package/dist/web/public/{subagent-windows.e6ca799f.js → subagent-windows.07277a58.js} +7 -5
  175. package/dist/web/public/subagent-windows.07277a58.js.br +0 -0
  176. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  177. package/dist/web/public/sw.js +2 -2
  178. package/dist/web/public/sw.js.br +0 -0
  179. package/dist/web/public/sw.js.gz +0 -0
  180. package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
  181. package/dist/web/public/{tab-rail-resize.94cd59f0.js → tab-rail-resize.a5867793.js} +1 -1
  182. package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
  183. package/dist/web/public/{tab-rail-resize.94cd59f0.js.gz → tab-rail-resize.a5867793.js.gz} +0 -0
  184. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +1 -0
  185. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
  186. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  187. package/dist/web/public/terminal-split.js +146 -649
  188. package/dist/web/public/terminal-split.js.br +0 -0
  189. package/dist/web/public/terminal-split.js.gz +0 -0
  190. package/dist/web/public/terminal-tile.js +1688 -0
  191. package/dist/web/public/terminal-tile.js.br +0 -0
  192. package/dist/web/public/terminal-tile.js.gz +0 -0
  193. package/dist/web/public/terminal-ui.b7267005.js +2 -0
  194. package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
  195. package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
  196. package/dist/web/public/tile-grid.js +2895 -0
  197. package/dist/web/public/tile-grid.js.br +0 -0
  198. package/dist/web/public/tile-grid.js.gz +0 -0
  199. package/dist/web/public/ultracode-panel.js.gz +0 -0
  200. package/dist/web/public/ultracode-windows.js +9 -6
  201. package/dist/web/public/ultracode-windows.js.br +0 -0
  202. package/dist/web/public/ultracode-windows.js.gz +0 -0
  203. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  204. package/dist/web/public/vendor/exceljs.min.js +44 -0
  205. package/dist/web/public/vendor/exceljs.min.js.br +0 -0
  206. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  207. package/dist/web/public/vendor/fflate.min.js +1 -0
  208. package/dist/web/public/vendor/fflate.min.js.br +0 -0
  209. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  210. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  211. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  212. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  213. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  214. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  215. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  216. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +4 -0
  217. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
  218. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
  219. package/dist/web/public/vendor/xterm.css.gz +0 -0
  220. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  221. package/dist/web/public/{voice-input.c4b51eb6.js → voice-input.2342c37b.js} +64 -10
  222. package/dist/web/public/voice-input.2342c37b.js.br +0 -0
  223. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  224. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  225. package/dist/web/public/webview-tabs.js +9 -1
  226. package/dist/web/public/webview-tabs.js.br +0 -0
  227. package/dist/web/public/webview-tabs.js.gz +0 -0
  228. package/dist/web/routes/cli-registry-routes.d.ts +23 -4
  229. package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
  230. package/dist/web/routes/cli-registry-routes.js +95 -8
  231. package/dist/web/routes/cli-registry-routes.js.map +1 -1
  232. package/dist/web/routes/file-routes.d.ts +7 -0
  233. package/dist/web/routes/file-routes.d.ts.map +1 -1
  234. package/dist/web/routes/file-routes.js +34 -4
  235. package/dist/web/routes/file-routes.js.map +1 -1
  236. package/dist/web/routes/git-status-routes.d.ts.map +1 -1
  237. package/dist/web/routes/git-status-routes.js +33 -6
  238. package/dist/web/routes/git-status-routes.js.map +1 -1
  239. package/dist/web/routes/mcp-sync-routes.d.ts +7 -2
  240. package/dist/web/routes/mcp-sync-routes.d.ts.map +1 -1
  241. package/dist/web/routes/mcp-sync-routes.js +10 -3
  242. package/dist/web/routes/mcp-sync-routes.js.map +1 -1
  243. package/dist/web/routes/reboot-restore-routes.js +1 -1
  244. package/dist/web/routes/reboot-restore-routes.js.map +1 -1
  245. package/dist/web/routes/session-routes.d.ts +18 -0
  246. package/dist/web/routes/session-routes.d.ts.map +1 -1
  247. package/dist/web/routes/session-routes.js +154 -69
  248. package/dist/web/routes/session-routes.js.map +1 -1
  249. package/dist/web/routes/status-telemetry-routes.d.ts +3 -0
  250. package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
  251. package/dist/web/routes/status-telemetry-routes.js +12 -0
  252. package/dist/web/routes/status-telemetry-routes.js.map +1 -1
  253. package/dist/web/routes/system-routes.d.ts.map +1 -1
  254. package/dist/web/routes/system-routes.js +13 -0
  255. package/dist/web/routes/system-routes.js.map +1 -1
  256. package/dist/web/schemas.d.ts +30 -0
  257. package/dist/web/schemas.d.ts.map +1 -1
  258. package/dist/web/schemas.js +43 -0
  259. package/dist/web/schemas.js.map +1 -1
  260. package/dist/web/server.d.ts +6 -1
  261. package/dist/web/server.d.ts.map +1 -1
  262. package/dist/web/server.js +47 -21
  263. package/dist/web/server.js.map +1 -1
  264. package/dist/web/session-listener-wiring.d.ts +1 -0
  265. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  266. package/dist/web/session-listener-wiring.js +11 -0
  267. package/dist/web/session-listener-wiring.js.map +1 -1
  268. package/package.json +6 -4
  269. package/scripts/postinstall.js +16 -0
  270. package/skills/codeman/SKILL.md +6 -0
  271. package/README.zh-CN.md +0 -1139
  272. package/dist/web/public/app.b9516c7b.js +0 -43
  273. package/dist/web/public/app.b9516c7b.js.br +0 -0
  274. package/dist/web/public/app.b9516c7b.js.gz +0 -0
  275. package/dist/web/public/constants.d95fade9.js.br +0 -0
  276. package/dist/web/public/constants.d95fade9.js.gz +0 -0
  277. package/dist/web/public/i18n.52f5b7cc.js +0 -1
  278. package/dist/web/public/i18n.52f5b7cc.js.br +0 -0
  279. package/dist/web/public/i18n.52f5b7cc.js.gz +0 -0
  280. package/dist/web/public/image-input.a216cf9e.js.br +0 -0
  281. package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
  282. package/dist/web/public/mobile-handlers.23a85db1.js.br +0 -0
  283. package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
  284. package/dist/web/public/mobile.1aa4952d.css +0 -1
  285. package/dist/web/public/mobile.1aa4952d.css.br +0 -0
  286. package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
  287. package/dist/web/public/notification-manager.50bb87d8.js.br +0 -0
  288. package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
  289. package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
  290. package/dist/web/public/panels-ui.6a5898d9.js.gz +0 -0
  291. package/dist/web/public/session-ui.6bddf916.js +0 -89
  292. package/dist/web/public/session-ui.6bddf916.js.br +0 -0
  293. package/dist/web/public/session-ui.6bddf916.js.gz +0 -0
  294. package/dist/web/public/settings-ui.6e798b7b.js +0 -97
  295. package/dist/web/public/settings-ui.6e798b7b.js.br +0 -0
  296. package/dist/web/public/settings-ui.6e798b7b.js.gz +0 -0
  297. package/dist/web/public/styles.2d175c60.css +0 -1
  298. package/dist/web/public/styles.2d175c60.css.br +0 -0
  299. package/dist/web/public/styles.2d175c60.css.gz +0 -0
  300. package/dist/web/public/subagent-windows.e6ca799f.js.br +0 -0
  301. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  302. package/dist/web/public/tab-rail-resize.94cd59f0.js.br +0 -0
  303. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js +0 -1
  304. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.br +0 -0
  305. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
  306. package/dist/web/public/terminal-ui.a4535311.js +0 -2
  307. package/dist/web/public/terminal-ui.a4535311.js.br +0 -0
  308. package/dist/web/public/terminal-ui.a4535311.js.gz +0 -0
  309. package/dist/web/public/upload.html +0 -155
  310. package/dist/web/public/upload.html.br +0 -0
  311. package/dist/web/public/upload.html.gz +0 -0
  312. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js +0 -4
  313. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.br +0 -0
  314. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.gz +0 -0
  315. package/dist/web/public/voice-input.c4b51eb6.js.br +0 -0
  316. package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
@@ -0,0 +1,555 @@
1
+ /**
2
+ * @fileoverview Read-only, virtualized XLSX preview for the file-preview overlay.
3
+ *
4
+ * `CodemanSpreadsheetPreview.open({ container, url, size })` fetches the workbook
5
+ * bytes (same-origin only, `?preview=true` so the server applies its 10 MB
6
+ * preview cap), hands them to spreadsheet-preview-worker.js, and renders only
7
+ * the visible tile of cells. Parsing happens entirely in the browser worker; the
8
+ * server just streams the file through its existing confined raw routes.
9
+ *
10
+ * Every workbook string (cell text, sheet names) is written with `textContent`,
11
+ * never markup. The per-style `<style>` block only emits validated `#rrggbb`
12
+ * colours and a fixed set of keywords. `dispose()` aborts the fetch and
13
+ * terminates the worker; panels-ui.js calls it whenever the overlay is reused
14
+ * or closed.
15
+ *
16
+ * @dependency constants.js (CodemanBase.url for the worker URL under --base-url)
17
+ * @loadorder 16.5 (after image-input.js; only defines a global, used on demand)
18
+ */
19
+
20
+ (function initSpreadsheetPreview(global) {
21
+ 'use strict';
22
+
23
+ const SPREADSHEET_ASSET_VERSION = '911680fac09d';
24
+ const MAX_PREVIEW_BYTES = 10 * 1024 * 1024;
25
+ const DEFAULT_TIMEOUT_MS = 20000;
26
+ const MAX_SCROLL_PX = 8000000;
27
+ const ROW_HEADING_WIDTH = 36;
28
+ const COLUMN_HEADING_HEIGHT = 20;
29
+ const assets = Object.freeze({
30
+ version: SPREADSHEET_ASSET_VERSION,
31
+ workerUrl: `/spreadsheet-preview-worker.js?v=${SPREADSHEET_ASSET_VERSION}`,
32
+ });
33
+
34
+ // What a refusal from the worker says on screen. The core's own messages
35
+ // (spreadsheet-xlsx-core.js) are developer detail, so each error code maps to
36
+ // one sentence with a zh-CN entry in i18n.js, and the raw message goes to the
37
+ // console. Any other code (parse-failed carries ExcelJS's own exception text)
38
+ // shows the generic failure.
39
+ const TOO_LARGE_OR_COMPLEX = 'This workbook is too large or complex to preview.';
40
+ const UNREADABLE = 'This workbook could not be read. The file may be damaged or not a valid .xlsx file.';
41
+ const WORKER_ERROR_TEXT = Object.freeze({
42
+ encrypted: 'This workbook is password-protected or in the old .xls format, so it cannot be previewed.',
43
+ zip64: 'This workbook uses ZIP64, which the preview does not support.',
44
+ malformed: UNREADABLE,
45
+ 'number-format': UNREADABLE,
46
+ 'entry-limit': TOO_LARGE_OR_COMPLEX,
47
+ 'entry-size': TOO_LARGE_OR_COMPLEX,
48
+ 'inflated-size': TOO_LARGE_OR_COMPLEX,
49
+ 'compression-ratio': TOO_LARGE_OR_COMPLEX,
50
+ 'worksheet-limit': TOO_LARGE_OR_COMPLEX,
51
+ 'element-limit': TOO_LARGE_OR_COMPLEX,
52
+ 'row-limit': TOO_LARGE_OR_COMPLEX,
53
+ 'cell-limit': TOO_LARGE_OR_COMPLEX,
54
+ 'merge-limit': TOO_LARGE_OR_COMPLEX,
55
+ 'style-limit': TOO_LARGE_OR_COMPLEX,
56
+ });
57
+
58
+ // The workbook features the preview leaves out, as the core names them
59
+ // (featureForName), in words for the notice bar.
60
+ const FEATURE_LABELS = Object.freeze({
61
+ charts: 'charts',
62
+ drawings: 'drawings',
63
+ pivotTables: 'pivot tables',
64
+ externalLinks: 'external links',
65
+ macros: 'macros',
66
+ });
67
+ const UNSUPPORTED_FORMAT_PREFIX = 'Unsupported number format: ';
68
+
69
+ function translate(text) {
70
+ return global.codemanT?.(text) || text;
71
+ }
72
+
73
+ function own(map, key) {
74
+ return Object.prototype.hasOwnProperty.call(map, key);
75
+ }
76
+
77
+ function workerErrorText(payload) {
78
+ const code = String(payload?.code || '');
79
+ if (payload?.message) console.warn(`Spreadsheet preview: ${code || 'error'}: ${payload.message}`);
80
+ return own(WORKER_ERROR_TEXT, code) ? WORKER_ERROR_TEXT[code] : 'Spreadsheet preview failed';
81
+ }
82
+
83
+ // One notice bar item, translated on its own (the bar is one text node, which
84
+ // the i18n layer could only match whole; the bar itself carries
85
+ // data-i18n-skip). A number format's code is workbook text: it is appended
86
+ // as is, never passed through the translator, which would read a `{…}` in it
87
+ // as a placeholder. A feature word goes through its scoped
88
+ // 'Spreadsheet feature: <word>' key and reads as the plain word when that key
89
+ // has no translation: a bare 'charts' key would also rename a charts/ folder.
90
+ function warningText(warning) {
91
+ const text = String(warning);
92
+ if (text.startsWith(UNSUPPORTED_FORMAT_PREFIX)) {
93
+ return `${translate('Unsupported number format')}: ${text.slice(UNSUPPORTED_FORMAT_PREFIX.length)}`;
94
+ }
95
+ if (own(FEATURE_LABELS, text)) {
96
+ const label = FEATURE_LABELS[text];
97
+ const key = `Spreadsheet feature: ${label}`;
98
+ const translated = translate(key);
99
+ return translated !== key ? translated : label;
100
+ }
101
+ return translate(text);
102
+ }
103
+
104
+ function message(container, text, kind) {
105
+ container.textContent = '';
106
+ const state = document.createElement('div');
107
+ state.className = `spreadsheet-preview-message ${kind || ''}`.trim();
108
+ state.textContent = text;
109
+ container.appendChild(state);
110
+ }
111
+
112
+ function safePreviewUrl(candidate) {
113
+ const url = new URL(candidate, global.location.href);
114
+ if (url.origin !== global.location.origin) throw new Error('Spreadsheet preview must use a same-origin URL');
115
+ url.searchParams.set('preview', 'true');
116
+ return `${url.pathname}${url.search}${url.hash}`;
117
+ }
118
+
119
+ function open(options) {
120
+ const container = options.container;
121
+ const isCurrent = typeof options.isCurrent === 'function' ? options.isCurrent : () => true;
122
+ let disposed = false;
123
+ let worker = null;
124
+ let controller = null;
125
+ let timer = null;
126
+ let metadata = null;
127
+ let activeSheetId = null;
128
+ let latestRequestId = 0;
129
+ let grid = null;
130
+ let spacer = null;
131
+ let cellsLayer = null;
132
+ let headingsLayer = null;
133
+ let emptySheetState = null;
134
+ let resizeObserver = null;
135
+ let latestRange = null;
136
+ let latestAxes = null;
137
+ let scrollFrame = null;
138
+
139
+ const current = () => !disposed && isCurrent();
140
+ const clearTimer = () => {
141
+ if (timer !== null) global.clearTimeout(timer);
142
+ timer = null;
143
+ };
144
+ const fail = (text) => {
145
+ if (!current()) return;
146
+ clearTimer();
147
+ message(container, text || 'Spreadsheet preview failed', 'error');
148
+ };
149
+
150
+ function sheetMetadata() {
151
+ return metadata?.sheets.find((sheet) => String(sheet.id) === String(activeSheetId));
152
+ }
153
+
154
+ // Prefix sums per override list, built once per sheet's axis: renderTile
155
+ // asks for several offsets per cell, so a linear walk over every override
156
+ // (one per row on a sheet with explicit heights) made each tile O(n) per cell.
157
+ const axisDeltas = new WeakMap();
158
+
159
+ function overrideDeltas(overrides, defaultSize) {
160
+ let entry = axisDeltas.get(overrides);
161
+ if (!entry || entry.defaultSize !== defaultSize) {
162
+ const deltas = [];
163
+ let delta = 0;
164
+ for (const [, size] of overrides) {
165
+ delta += size - defaultSize;
166
+ deltas.push(delta);
167
+ }
168
+ entry = { defaultSize, deltas };
169
+ axisDeltas.set(overrides, entry);
170
+ }
171
+ return entry.deltas;
172
+ }
173
+
174
+ function axisOffset(count, defaultSize, overrides, index) {
175
+ const bounded = Math.max(1, Math.min(count + 1, index));
176
+ const list = overrides || [];
177
+ let low = 0;
178
+ let high = list.length;
179
+ while (low < high) {
180
+ const mid = (low + high) >> 1;
181
+ if (list[mid][0] < bounded) low = mid + 1;
182
+ else high = mid;
183
+ }
184
+ return (bounded - 1) * defaultSize + (low ? overrideDeltas(list, defaultSize)[low - 1] : 0);
185
+ }
186
+
187
+ function axisIndex(count, defaultSize, overrides, offset) {
188
+ let low = 1;
189
+ let high = Math.max(1, count);
190
+ while (low < high) {
191
+ const mid = Math.floor((low + high + 1) / 2);
192
+ if (axisOffset(count, defaultSize, overrides, mid) <= offset) low = mid;
193
+ else high = mid - 1;
194
+ }
195
+ return low;
196
+ }
197
+
198
+ // Past MAX_SCROLL_PX the spacer is shorter than the sheet, so only the
199
+ // scroll POSITION is scaled (the scroll range maps onto the sheet's whole
200
+ // range, so the last row stays reachable) and the tile is laid out at real
201
+ // sizes from there. `shift` is the logical offset minus the scroll offset,
202
+ // 0 when the sheet fits; `end` is the bottom (or right) of the spacer.
203
+ function scrollAxis(logical, scroll, viewport, heading) {
204
+ const shown = Math.min(MAX_SCROLL_PX, logical);
205
+ const scrollRange = Math.max(0, heading + shown - viewport);
206
+ const logicalRange = Math.max(0, heading + logical - viewport);
207
+ const virtual =
208
+ logical > shown && scrollRange > 0 ? Math.min(logicalRange, (scroll / scrollRange) * logicalRange) : scroll;
209
+ return { virtual, shift: virtual - scroll, end: heading + shown };
210
+ }
211
+
212
+ function requestTile() {
213
+ if (!current() || !worker || !grid) return;
214
+ const sheet = sheetMetadata();
215
+ if (!sheet || sheet.rows === 0 || sheet.cols === 0) return;
216
+ const viewHeight = grid.clientHeight || 500;
217
+ const viewWidth = grid.clientWidth || 800;
218
+ const y = scrollAxis(
219
+ axisOffset(sheet.rows, sheet.defaultRowHeight, sheet.rowOverrides, sheet.rows + 1),
220
+ grid.scrollTop,
221
+ viewHeight,
222
+ COLUMN_HEADING_HEIGHT
223
+ );
224
+ const x = scrollAxis(
225
+ axisOffset(sheet.cols, sheet.defaultColumnWidth, sheet.columnOverrides, sheet.cols + 1),
226
+ grid.scrollLeft,
227
+ viewWidth,
228
+ ROW_HEADING_WIDTH
229
+ );
230
+ const r1 = Math.max(
231
+ 1,
232
+ axisIndex(
233
+ sheet.rows,
234
+ sheet.defaultRowHeight,
235
+ sheet.rowOverrides,
236
+ Math.max(0, y.virtual - COLUMN_HEADING_HEIGHT)
237
+ ) - 2
238
+ );
239
+ const c1 = Math.max(
240
+ 1,
241
+ axisIndex(
242
+ sheet.cols,
243
+ sheet.defaultColumnWidth,
244
+ sheet.columnOverrides,
245
+ Math.max(0, x.virtual - ROW_HEADING_WIDTH)
246
+ ) - 2
247
+ );
248
+ const r2 = Math.min(
249
+ sheet.rows,
250
+ axisIndex(
251
+ sheet.rows,
252
+ sheet.defaultRowHeight,
253
+ sheet.rowOverrides,
254
+ Math.max(0, y.virtual - COLUMN_HEADING_HEIGHT + viewHeight)
255
+ ) + 2
256
+ );
257
+ const c2 = Math.min(
258
+ sheet.cols,
259
+ axisIndex(
260
+ sheet.cols,
261
+ sheet.defaultColumnWidth,
262
+ sheet.columnOverrides,
263
+ Math.max(0, x.virtual - ROW_HEADING_WIDTH + viewWidth)
264
+ ) + 2
265
+ );
266
+ latestRequestId += 1;
267
+ latestRange = { r1, c1, r2, c2 };
268
+ latestAxes = { y, x };
269
+ worker.postMessage({
270
+ type: 'tile',
271
+ requestId: latestRequestId,
272
+ sheetId: String(activeSheetId),
273
+ range: { r1, c1, r2, c2 },
274
+ });
275
+ }
276
+
277
+ function renderWarnings(tileWarnings) {
278
+ const notice = container.querySelector('.spreadsheet-preview-notice');
279
+ if (!notice) return;
280
+ const warnings = [...(metadata?.warnings || []), ...(tileWarnings || [])];
281
+ notice.hidden = warnings.length === 0;
282
+ const warningLabel = translate('Some workbook features are not shown');
283
+ notice.textContent = warnings.length ? `${warningLabel}: ${warnings.map(warningText).join(', ')}` : '';
284
+ }
285
+
286
+ function pinHeadings() {
287
+ if (!grid || !headingsLayer) return;
288
+ headingsLayer.querySelectorAll('.spreadsheet-row-heading').forEach((heading) => {
289
+ heading.style.left = `${grid.scrollLeft}px`;
290
+ });
291
+ headingsLayer.querySelectorAll('.spreadsheet-column-heading').forEach((heading) => {
292
+ heading.style.top = `${grid.scrollTop}px`;
293
+ });
294
+ }
295
+
296
+ function renderTile(tile) {
297
+ if (!current() || tile.requestId !== latestRequestId || String(tile.sheetId) !== String(activeSheetId)) return;
298
+ const sheet = sheetMetadata();
299
+ if (!sheet || !cellsLayer || !headingsLayer || !latestRange || !latestAxes) return;
300
+ const { y, x } = latestAxes;
301
+ // Sizes are real; a span (a tall merge) is clipped at the spacer's edge so
302
+ // it never grows the scroll area.
303
+ const rowTop = (row) =>
304
+ COLUMN_HEADING_HEIGHT + axisOffset(sheet.rows, sheet.defaultRowHeight, sheet.rowOverrides, row) - y.shift;
305
+ const colLeft = (col) =>
306
+ ROW_HEADING_WIDTH + axisOffset(sheet.cols, sheet.defaultColumnWidth, sheet.columnOverrides, col) - x.shift;
307
+ const rowSpan = (from, to) => Math.max(0, Math.min(rowTop(to + 1), y.end) - rowTop(from));
308
+ const colSpan = (from, to) => Math.max(0, Math.min(colLeft(to + 1), x.end) - colLeft(from));
309
+ cellsLayer.textContent = '';
310
+ headingsLayer.textContent = '';
311
+ const mergeByAnchor = new Map();
312
+ for (const merge of tile.merges || []) {
313
+ const match = /^([A-Z]+)(\d+):([A-Z]+)(\d+)$/i.exec(merge);
314
+ if (!match) continue;
315
+ const column = (letters) =>
316
+ [...letters.toUpperCase()].reduce((value, char) => value * 26 + char.charCodeAt(0) - 64, 0);
317
+ mergeByAnchor.set(`${Number(match[2])}:${column(match[1])}`, {
318
+ r2: Number(match[4]),
319
+ c2: column(match[3]),
320
+ });
321
+ }
322
+ for (const cell of tile.cells.slice(0, 2500)) {
323
+ const merge = mergeByAnchor.get(`${cell.row}:${cell.col}`);
324
+ const height = rowSpan(cell.row, merge?.r2 || cell.row);
325
+ const width = colSpan(cell.col, merge?.c2 || cell.col);
326
+ // A cell clipped to nothing at the spacer's edge (or sized 0 px) is
327
+ // skipped like its heading: padding and border would still draw it as a
328
+ // small box below the spacer and grow the scroll area.
329
+ if (height <= 0 || width <= 0) continue;
330
+ const element = document.createElement('div');
331
+ element.className = `spreadsheet-cell spreadsheet-style-${Number(cell.styleId) || 0}`;
332
+ element.dataset.row = String(cell.row);
333
+ element.dataset.col = String(cell.col);
334
+ element.textContent = String(cell.text ?? '');
335
+ element.style.top = `${rowTop(cell.row)}px`;
336
+ element.style.left = `${colLeft(cell.col)}px`;
337
+ element.style.height = `${height}px`;
338
+ element.style.width = `${width}px`;
339
+ cellsLayer.appendChild(element);
340
+ }
341
+ // Headings take their size from the same axis math as the cells, so custom
342
+ // widths/heights line up; hidden (0 px) rows and columns get no heading and
343
+ // do not count against the heading caps.
344
+ let rowHeadings = 0;
345
+ for (let row = latestRange.r1; row <= latestRange.r2 && rowHeadings < 200; row += 1) {
346
+ const height = rowSpan(row, row);
347
+ if (height <= 0) continue;
348
+ rowHeadings += 1;
349
+ const heading = document.createElement('div');
350
+ heading.className = 'spreadsheet-row-heading';
351
+ heading.textContent = String(row);
352
+ heading.style.top = `${rowTop(row)}px`;
353
+ heading.style.height = `${height}px`;
354
+ heading.style.left = `${grid.scrollLeft}px`;
355
+ headingsLayer.appendChild(heading);
356
+ }
357
+ let columnHeadings = 0;
358
+ for (let col = latestRange.c1; col <= latestRange.c2 && columnHeadings < 100; col += 1) {
359
+ const width = colSpan(col, col);
360
+ if (width <= 0) continue;
361
+ columnHeadings += 1;
362
+ const heading = document.createElement('div');
363
+ heading.className = 'spreadsheet-column-heading';
364
+ let label = '';
365
+ for (let value = col; value > 0; value = Math.floor((value - 1) / 26))
366
+ label = String.fromCharCode(65 + ((value - 1) % 26)) + label;
367
+ heading.textContent = label;
368
+ heading.style.left = `${colLeft(col)}px`;
369
+ heading.style.width = `${width}px`;
370
+ heading.style.top = `${grid.scrollTop}px`;
371
+ headingsLayer.appendChild(heading);
372
+ }
373
+ renderWarnings(tile.warnings);
374
+ }
375
+
376
+ function selectSheet(sheetId) {
377
+ if (!current() || !metadata?.sheets.some((sheet) => String(sheet.id) === String(sheetId))) return;
378
+ activeSheetId = String(sheetId);
379
+ latestRequestId += 1;
380
+ latestRange = null;
381
+ latestAxes = null;
382
+ if (cellsLayer) cellsLayer.textContent = '';
383
+ if (headingsLayer) headingsLayer.textContent = '';
384
+ container.querySelectorAll('[role="tab"]').forEach((tab) => {
385
+ const selected = tab.dataset.sheetId === activeSheetId;
386
+ tab.setAttribute('aria-selected', String(selected));
387
+ tab.tabIndex = selected ? 0 : -1;
388
+ });
389
+ if (grid) {
390
+ grid.scrollTop = 0;
391
+ grid.scrollLeft = 0;
392
+ }
393
+ const sheet = sheetMetadata();
394
+ if (sheet && spacer) {
395
+ const logicalHeight = axisOffset(sheet.rows, sheet.defaultRowHeight, sheet.rowOverrides, sheet.rows + 1);
396
+ const logicalWidth = axisOffset(sheet.cols, sheet.defaultColumnWidth, sheet.columnOverrides, sheet.cols + 1);
397
+ spacer.style.height = `${COLUMN_HEADING_HEIGHT + Math.min(MAX_SCROLL_PX, logicalHeight)}px`;
398
+ spacer.style.width = `${ROW_HEADING_WIDTH + Math.min(MAX_SCROLL_PX, logicalWidth)}px`;
399
+ }
400
+ if (emptySheetState) emptySheetState.hidden = Boolean(sheet?.rows && sheet?.cols);
401
+ renderWarnings([]);
402
+ requestTile();
403
+ }
404
+
405
+ function renderMetadata(nextMetadata) {
406
+ if (!current()) return;
407
+ metadata = nextMetadata;
408
+ container.textContent = '';
409
+ if (!metadata.sheets?.length) {
410
+ message(container, 'This workbook has no visible worksheets.', 'empty');
411
+ return;
412
+ }
413
+ const shell = document.createElement('div');
414
+ shell.className = 'spreadsheet-preview-shell';
415
+ const styleSheet = document.createElement('style');
416
+ styleSheet.textContent = (metadata.styles || [])
417
+ .map((style, id) => {
418
+ const declarations = [];
419
+ if (style.font?.bold) declarations.push('font-weight:700');
420
+ if (style.font?.italic) declarations.push('font-style:italic');
421
+ // Colour and background are emitted together or not at all.
422
+ // The worker already contrast-checked them as a pair; contributing
423
+ // one half would drop the cell back onto the skin's own background.
424
+ if (/^#[a-f0-9]{6}$/i.test(style.font?.color || '') && /^#[a-f0-9]{6}$/i.test(style.fill || '')) {
425
+ declarations.push(`color:${style.font.color}`, `background-color:${style.fill}`);
426
+ }
427
+ if (['left', 'center', 'right'].includes(style.alignment)) declarations.push(`text-align:${style.alignment}`);
428
+ if (style.wrapText) declarations.push('white-space:normal');
429
+ return `.spreadsheet-style-${id}{${declarations.join(';')}}`;
430
+ })
431
+ .join('');
432
+ const tabs = document.createElement('div');
433
+ tabs.className = 'spreadsheet-sheet-tabs';
434
+ tabs.setAttribute('role', 'tablist');
435
+ tabs.setAttribute('data-i18n-skip', '');
436
+ for (const sheet of metadata.sheets) {
437
+ const tab = document.createElement('button');
438
+ tab.type = 'button';
439
+ tab.className = 'spreadsheet-sheet-tab';
440
+ tab.setAttribute('role', 'tab');
441
+ tab.dataset.sheetId = String(sheet.id);
442
+ tab.textContent = sheet.name;
443
+ tab.addEventListener('click', () => selectSheet(sheet.id));
444
+ tabs.appendChild(tab);
445
+ }
446
+ const notice = document.createElement('div');
447
+ notice.className = 'spreadsheet-preview-notice';
448
+ // Written already translated, item by item (renderWarnings), and it ends
449
+ // with workbook text (a number format's code): the observer's t() over
450
+ // the whole line would rewrite a `{name}` or a "Codeman" in that code.
451
+ notice.setAttribute('data-i18n-skip', '');
452
+ notice.hidden = true;
453
+ emptySheetState = document.createElement('div');
454
+ emptySheetState.className = 'spreadsheet-empty-sheet';
455
+ emptySheetState.textContent = 'This worksheet is empty.';
456
+ emptySheetState.hidden = true;
457
+ grid = document.createElement('div');
458
+ grid.className = 'spreadsheet-grid';
459
+ grid.setAttribute('data-i18n-skip', '');
460
+ spacer = document.createElement('div');
461
+ spacer.className = 'spreadsheet-grid-spacer';
462
+ cellsLayer = document.createElement('div');
463
+ cellsLayer.className = 'spreadsheet-cells';
464
+ headingsLayer = document.createElement('div');
465
+ headingsLayer.className = 'spreadsheet-headings';
466
+ grid.append(spacer, cellsLayer, headingsLayer);
467
+ grid.addEventListener(
468
+ 'scroll',
469
+ () => {
470
+ pinHeadings();
471
+ if (scrollFrame !== null) return;
472
+ scrollFrame = global.requestAnimationFrame(() => {
473
+ scrollFrame = null;
474
+ requestTile();
475
+ });
476
+ },
477
+ { passive: true }
478
+ );
479
+ shell.append(styleSheet, tabs, notice, emptySheetState, grid);
480
+ container.appendChild(shell);
481
+ resizeObserver = typeof ResizeObserver === 'function' ? new ResizeObserver(requestTile) : null;
482
+ resizeObserver?.observe(grid);
483
+ selectSheet(metadata.sheets[0].id);
484
+ }
485
+
486
+ function dispose() {
487
+ if (disposed) return;
488
+ disposed = true;
489
+ clearTimer();
490
+ if (scrollFrame !== null) global.cancelAnimationFrame(scrollFrame);
491
+ controller?.abort();
492
+ resizeObserver?.disconnect();
493
+ try {
494
+ worker?.postMessage({ type: 'dispose' });
495
+ worker?.terminate();
496
+ } catch {
497
+ // A worker that failed during startup may already be unavailable.
498
+ }
499
+ worker = null;
500
+ }
501
+
502
+ async function start() {
503
+ if (Number(options.size) > MAX_PREVIEW_BYTES) {
504
+ fail('This workbook is too large to preview (10 MB limit).');
505
+ return;
506
+ }
507
+ message(container, 'Loading spreadsheet…', 'loading');
508
+ let previewUrl;
509
+ try {
510
+ previewUrl = safePreviewUrl(options.url);
511
+ controller = new AbortController();
512
+ // Root-absolute paths ignore <base href>; route through the mount prefix.
513
+ worker = new Worker(global.CodemanBase?.url ? global.CodemanBase.url(assets.workerUrl) : assets.workerUrl);
514
+ const ready = new Promise((resolve, reject) => {
515
+ worker.onerror = () => reject(new Error('Spreadsheet parser failed to start'));
516
+ worker.onmessageerror = () => reject(new Error('Spreadsheet parser message failed'));
517
+ worker.onmessage = (event) => {
518
+ if (event.data?.type === 'ready') resolve();
519
+ };
520
+ });
521
+ const responsePromise = fetch(previewUrl, { signal: controller.signal });
522
+ const [response] = await Promise.all([responsePromise, ready]);
523
+ if (!current()) return;
524
+ if (response.status === 413) throw new Error('This workbook is too large to preview (10 MB limit).');
525
+ if (!response.ok) throw new Error(`Spreadsheet preview failed (${response.status})`);
526
+ const bytes = await response.arrayBuffer();
527
+ if (!current()) return;
528
+ worker.onmessage = (event) => {
529
+ if (!current()) return;
530
+ const payload = event.data || {};
531
+ if (payload.type === 'metadata') {
532
+ clearTimer();
533
+ renderMetadata(payload);
534
+ } else if (payload.type === 'tile') renderTile(payload);
535
+ else if (payload.type === 'error') fail(workerErrorText(payload));
536
+ };
537
+ worker.onerror = () => fail('Spreadsheet parser failed.');
538
+ worker.onmessageerror = () => fail('Spreadsheet parser message failed.');
539
+ timer = global.setTimeout(() => {
540
+ worker?.terminate();
541
+ fail('Spreadsheet preview timed out.');
542
+ }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
543
+ worker.postMessage({ type: 'load', bytes }, [bytes]);
544
+ } catch (error) {
545
+ if (!disposed && error?.name !== 'AbortError') fail(error?.message);
546
+ }
547
+ }
548
+
549
+ void start();
550
+ return Object.freeze({ dispose, selectSheet, resize: requestTile });
551
+ }
552
+
553
+ global.CodemanSpreadsheetPreviewAssets = assets;
554
+ global.CodemanSpreadsheetPreview = Object.freeze({ open, MAX_PREVIEW_BYTES });
555
+ })(window);