@heycys/gallery 0.0.0-stage → 0.1.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 (785) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -2
  3. package/THIRD-PARTY-NOTICES.md +3253 -0
  4. package/assets/writing-preferences.md +107 -0
  5. package/bin/gallery.mjs +10 -0
  6. package/browser/editor/components/bookmark/icons.mjs +6 -0
  7. package/browser/editor/components/bookmark/index.mjs +80 -0
  8. package/browser/editor/components/bookmark/page.mjs +147 -0
  9. package/browser/editor/components/builders.mjs +19 -0
  10. package/browser/editor/components/callout/index.mjs +39 -0
  11. package/browser/editor/components/callout/page.mjs +13 -0
  12. package/browser/editor/components/code/index.mjs +327 -0
  13. package/browser/editor/components/code/page.mjs +561 -0
  14. package/browser/editor/components/colors.mjs +50 -0
  15. package/browser/editor/components/columns/index.mjs +104 -0
  16. package/browser/editor/components/columns/page.mjs +297 -0
  17. package/browser/editor/components/details/index.mjs +72 -0
  18. package/browser/editor/components/details/page.mjs +86 -0
  19. package/browser/editor/components/dialog/index.mjs +83 -0
  20. package/browser/editor/components/dialog/page.mjs +219 -0
  21. package/browser/editor/components/divider/index.mjs +48 -0
  22. package/browser/editor/components/embed/icons.mjs +11 -0
  23. package/browser/editor/components/embed/index.mjs +123 -0
  24. package/browser/editor/components/embed/page.mjs +309 -0
  25. package/browser/editor/components/figure/index.mjs +87 -0
  26. package/browser/editor/components/figure/page.mjs +33 -0
  27. package/browser/editor/components/file/icons.mjs +14 -0
  28. package/browser/editor/components/file/index.mjs +77 -0
  29. package/browser/editor/components/file/page.mjs +114 -0
  30. package/browser/editor/components/flowchart/add.mjs +123 -0
  31. package/browser/editor/components/flowchart/index.mjs +246 -0
  32. package/browser/editor/components/flowchart/page.mjs +453 -0
  33. package/browser/editor/components/heading/index.mjs +51 -0
  34. package/browser/editor/components/highlight/index.mjs +79 -0
  35. package/browser/editor/components/icon/index.mjs +57 -0
  36. package/browser/editor/components/icon/page.mjs +286 -0
  37. package/browser/editor/components/icons.mjs +45 -0
  38. package/browser/editor/components/image/crop.mjs +189 -0
  39. package/browser/editor/components/image/index.mjs +104 -0
  40. package/browser/editor/components/image/page.mjs +385 -0
  41. package/browser/editor/components/image/viewer.mjs +213 -0
  42. package/browser/editor/components/installed/index.mjs +133 -0
  43. package/browser/editor/components/installed/page.mjs +422 -0
  44. package/browser/editor/components/installed/served.mjs +35 -0
  45. package/browser/editor/components/kit.mjs +64 -0
  46. package/browser/editor/components/list/index.mjs +194 -0
  47. package/browser/editor/components/list/join.mjs +68 -0
  48. package/browser/editor/components/list/numbers.mjs +107 -0
  49. package/browser/editor/components/list/page.mjs +632 -0
  50. package/browser/editor/components/math/index.mjs +88 -0
  51. package/browser/editor/components/math/page.mjs +236 -0
  52. package/browser/editor/components/math/tex-logo.mjs +6 -0
  53. package/browser/editor/components/media/icons.mjs +19 -0
  54. package/browser/editor/components/media/page.mjs +162 -0
  55. package/browser/editor/components/media/parts.mjs +60 -0
  56. package/browser/editor/components/media/size.mjs +20 -0
  57. package/browser/editor/components/media/toolbar.mjs +37 -0
  58. package/browser/editor/components/media/write.mjs +7 -0
  59. package/browser/editor/components/pages.mjs +46 -0
  60. package/browser/editor/components/paragraph/index.mjs +43 -0
  61. package/browser/editor/components/paragraph/page.mjs +52 -0
  62. package/browser/editor/components/placeholder-bar.mjs +17 -0
  63. package/browser/editor/components/placeholder.mjs +128 -0
  64. package/browser/editor/components/quote/index.mjs +33 -0
  65. package/browser/editor/components/registry.mjs +833 -0
  66. package/browser/editor/components/rows.mjs +16 -0
  67. package/browser/editor/components/section/index.mjs +41 -0
  68. package/browser/editor/components/section/page.mjs +141 -0
  69. package/browser/editor/components/sheets/index.mjs +137 -0
  70. package/browser/editor/components/sheets/page.mjs +227 -0
  71. package/browser/editor/components/sheets/pills.mjs +226 -0
  72. package/browser/editor/components/table/grid.mjs +73 -0
  73. package/browser/editor/components/table/index.mjs +306 -0
  74. package/browser/editor/components/table/page.mjs +672 -0
  75. package/browser/editor/components/table/slots.mjs +41 -0
  76. package/browser/editor/components/widget/index.mjs +50 -0
  77. package/browser/editor/page/annotate/anchors.mjs +229 -0
  78. package/browser/editor/page/annotate/card.mjs +102 -0
  79. package/browser/editor/page/annotate/index.mjs +1629 -0
  80. package/browser/editor/page/at-menu/index.mjs +317 -0
  81. package/browser/editor/page/autosave.mjs +977 -0
  82. package/browser/editor/page/beside.mjs +21 -0
  83. package/browser/editor/page/block-info.mjs +63 -0
  84. package/browser/editor/page/block-menu/colors.mjs +82 -0
  85. package/browser/editor/page/block-menu/index.mjs +215 -0
  86. package/browser/editor/page/blocks.mjs +246 -0
  87. package/browser/editor/page/caret.mjs +679 -0
  88. package/browser/editor/page/chip-fills.mjs +126 -0
  89. package/browser/editor/page/clipboard/import/colors.mjs +192 -0
  90. package/browser/editor/page/clipboard/import/html.mjs +792 -0
  91. package/browser/editor/page/clipboard/import/inline.mjs +232 -0
  92. package/browser/editor/page/clipboard/import/languages.mjs +42 -0
  93. package/browser/editor/page/clipboard/import/markdown.mjs +760 -0
  94. package/browser/editor/page/clipboard/import/math.mjs +100 -0
  95. package/browser/editor/page/clipboard/import/office.mjs +245 -0
  96. package/browser/editor/page/clipboard/import/read.mjs +120 -0
  97. package/browser/editor/page/clipboard/import/rich.mjs +29 -0
  98. package/browser/editor/page/clipboard/import/to-blocks.mjs +290 -0
  99. package/browser/editor/page/clipboard/index.mjs +797 -0
  100. package/browser/editor/page/clipboard/own/blocks.mjs +202 -0
  101. package/browser/editor/page/clipboard/own/chart-keys.mjs +58 -0
  102. package/browser/editor/page/clipboard/own/describe.mjs +115 -0
  103. package/browser/editor/page/clipboard/own/export.mjs +167 -0
  104. package/browser/editor/page/clipboard/own/ids.mjs +39 -0
  105. package/browser/editor/page/clipboard/own/index.mjs +13 -0
  106. package/browser/editor/page/clipboard/own/rules.mjs +44 -0
  107. package/browser/editor/page/clipboard/own/selection.mjs +159 -0
  108. package/browser/editor/page/clipboard/own/writers.mjs +326 -0
  109. package/browser/editor/page/columns/index.mjs +360 -0
  110. package/browser/editor/page/commit.mjs +1798 -0
  111. package/browser/editor/page/conflicts.mjs +269 -0
  112. package/browser/editor/page/convert.mjs +830 -0
  113. package/browser/editor/page/core.mjs +1655 -0
  114. package/browser/editor/page/drag/drop.mjs +307 -0
  115. package/browser/editor/page/drag/index.mjs +335 -0
  116. package/browser/editor/page/drag/preview.mjs +121 -0
  117. package/browser/editor/page/edge-scroll.mjs +50 -0
  118. package/browser/editor/page/editor.mjs +1725 -0
  119. package/browser/editor/page/emoji-picker/emoji-tab.mjs +278 -0
  120. package/browser/editor/page/emoji-picker/glyph-html.mjs +26 -0
  121. package/browser/editor/page/emoji-picker/grid.mjs +79 -0
  122. package/browser/editor/page/emoji-picker/icon-tab.mjs +217 -0
  123. package/browser/editor/page/emoji-picker/icons.mjs +32 -0
  124. package/browser/editor/page/emoji-picker/index.mjs +164 -0
  125. package/browser/editor/page/emoji-picker/styles.mjs +2 -0
  126. package/browser/editor/page/emoji-picker/support.mjs +202 -0
  127. package/browser/editor/page/emoji-picker/upload.mjs +235 -0
  128. package/browser/editor/page/export-report.mjs +23 -0
  129. package/browser/editor/page/fills.mjs +60 -0
  130. package/browser/editor/page/find/index.mjs +107 -0
  131. package/browser/editor/page/flags.mjs +70 -0
  132. package/browser/editor/page/format-bar/bar.mjs +448 -0
  133. package/browser/editor/page/format-bar/brush.mjs +77 -0
  134. package/browser/editor/page/format-bar/colors.mjs +46 -0
  135. package/browser/editor/page/format-bar/index.mjs +404 -0
  136. package/browser/editor/page/formula/index.mjs +236 -0
  137. package/browser/editor/page/formula/panel.mjs +336 -0
  138. package/browser/editor/page/formula/symbols.mjs +156 -0
  139. package/browser/editor/page/frame.mjs +74 -0
  140. package/browser/editor/page/frames.mjs +196 -0
  141. package/browser/editor/page/geometry.mjs +400 -0
  142. package/browser/editor/page/gesture.mjs +45 -0
  143. package/browser/editor/page/handle/index.mjs +639 -0
  144. package/browser/editor/page/handle-sizes.mjs +14 -0
  145. package/browser/editor/page/hints/index.mjs +84 -0
  146. package/browser/editor/page/history.mjs +401 -0
  147. package/browser/editor/page/insert/index.mjs +243 -0
  148. package/browser/editor/page/kept.mjs +38 -0
  149. package/browser/editor/page/links/address.mjs +40 -0
  150. package/browser/editor/page/links/index.mjs +866 -0
  151. package/browser/editor/page/links/pages.mjs +131 -0
  152. package/browser/editor/page/links/views.mjs +369 -0
  153. package/browser/editor/page/look.mjs +65 -0
  154. package/browser/editor/page/made.mjs +24 -0
  155. package/browser/editor/page/margin.mjs +352 -0
  156. package/browser/editor/page/markdown/index.mjs +88 -0
  157. package/browser/editor/page/markdown/rules.mjs +33 -0
  158. package/browser/editor/page/marks.mjs +377 -0
  159. package/browser/editor/page/media-menu/index.mjs +158 -0
  160. package/browser/editor/page/menu.mjs +500 -0
  161. package/browser/editor/page/merge.mjs +100 -0
  162. package/browser/editor/page/notices.mjs +142 -0
  163. package/browser/editor/page/offered.mjs +32 -0
  164. package/browser/editor/page/outline/folds.mjs +52 -0
  165. package/browser/editor/page/outline/index.mjs +697 -0
  166. package/browser/editor/page/outline/list.mjs +172 -0
  167. package/browser/editor/page/outline/read.mjs +201 -0
  168. package/browser/editor/page/outline/strip.mjs +281 -0
  169. package/browser/editor/page/outline/tree.mjs +248 -0
  170. package/browser/editor/page/overlay-layer.mjs +227 -0
  171. package/browser/editor/page/overlays.mjs +205 -0
  172. package/browser/editor/page/own.mjs +28 -0
  173. package/browser/editor/page/pads.mjs +71 -0
  174. package/browser/editor/page/page-objects.mjs +105 -0
  175. package/browser/editor/page/panel.mjs +64 -0
  176. package/browser/editor/page/placement.mjs +58 -0
  177. package/browser/editor/page/post.mjs +41 -0
  178. package/browser/editor/page/present-links.mjs +50 -0
  179. package/browser/editor/page/quiet.mjs +48 -0
  180. package/browser/editor/page/resize/index.mjs +327 -0
  181. package/browser/editor/page/select-all/index.mjs +262 -0
  182. package/browser/editor/page/selection/delete.mjs +59 -0
  183. package/browser/editor/page/selection/index.mjs +157 -0
  184. package/browser/editor/page/selection/marquee.mjs +174 -0
  185. package/browser/editor/page/session.mjs +117 -0
  186. package/browser/editor/page/slash-menu/index.mjs +291 -0
  187. package/browser/editor/page/storage.mjs +48 -0
  188. package/browser/editor/page/sync-state.mjs +267 -0
  189. package/browser/editor/page/table/aligns.mjs +29 -0
  190. package/browser/editor/page/table/cell-bar.mjs +138 -0
  191. package/browser/editor/page/table/cells.mjs +527 -0
  192. package/browser/editor/page/table/icons.mjs +40 -0
  193. package/browser/editor/page/table/index.mjs +985 -0
  194. package/browser/editor/page/table/lists.mjs +187 -0
  195. package/browser/editor/page/table/widths.mjs +308 -0
  196. package/browser/editor/page/tex.mjs +32 -0
  197. package/browser/editor/page/text-select/index.mjs +656 -0
  198. package/browser/editor/page/tooltip.mjs +98 -0
  199. package/browser/editor/page/type-grid.mjs +82 -0
  200. package/browser/editor/page/typing/code-edges.mjs +228 -0
  201. package/browser/editor/page/typing/index.mjs +276 -0
  202. package/browser/editor/page/typing/leave.mjs +68 -0
  203. package/browser/editor/page/ui.mjs +160 -0
  204. package/browser/shared/annotations.mjs +571 -0
  205. package/browser/shared/code-folds.mjs +10 -0
  206. package/browser/shared/code-languages.mjs +70 -0
  207. package/browser/shared/code-lines.mjs +30 -0
  208. package/browser/shared/colors.mjs +28 -0
  209. package/browser/shared/component-catalog.mjs +559 -0
  210. package/browser/shared/components/bookmark.mjs +26 -0
  211. package/browser/shared/components/callout.mjs +7 -0
  212. package/browser/shared/components/chart.mjs +20 -0
  213. package/browser/shared/components/code.mjs +37 -0
  214. package/browser/shared/components/columns.mjs +16 -0
  215. package/browser/shared/components/details.mjs +15 -0
  216. package/browser/shared/components/dialog.mjs +28 -0
  217. package/browser/shared/components/diff.mjs +18 -0
  218. package/browser/shared/components/divider.mjs +6 -0
  219. package/browser/shared/components/embed.mjs +25 -0
  220. package/browser/shared/components/figure.mjs +14 -0
  221. package/browser/shared/components/file.mjs +23 -0
  222. package/browser/shared/components/heading.mjs +6 -0
  223. package/browser/shared/components/highlight.mjs +9 -0
  224. package/browser/shared/components/icon.mjs +18 -0
  225. package/browser/shared/components/image.mjs +39 -0
  226. package/browser/shared/components/index.mjs +342 -0
  227. package/browser/shared/components/installed.mjs +162 -0
  228. package/browser/shared/components/list.mjs +146 -0
  229. package/browser/shared/components/math.mjs +49 -0
  230. package/browser/shared/components/mention.mjs +33 -0
  231. package/browser/shared/components/page.mjs +37 -0
  232. package/browser/shared/components/paragraph.mjs +9 -0
  233. package/browser/shared/components/quote.mjs +6 -0
  234. package/browser/shared/components/section.mjs +8 -0
  235. package/browser/shared/components/sheets.mjs +80 -0
  236. package/browser/shared/components/state.mjs +20 -0
  237. package/browser/shared/components/table.mjs +49 -0
  238. package/browser/shared/components/text.mjs +14 -0
  239. package/browser/shared/components/widget.mjs +10 -0
  240. package/browser/shared/copy-ids.mjs +72 -0
  241. package/browser/shared/css-references.mjs +110 -0
  242. package/browser/shared/emoji/custom.mjs +91 -0
  243. package/browser/shared/emoji/data/LICENSE-mingcute.txt +202 -0
  244. package/browser/shared/emoji/data/LICENSE-tabler.txt +21 -0
  245. package/browser/shared/emoji/data/README.md +26 -0
  246. package/browser/shared/emoji/data/categories.json +1 -0
  247. package/browser/shared/emoji/data/icons.json +1 -0
  248. package/browser/shared/emoji/data/keywords-en.json +1 -0
  249. package/browser/shared/emoji/data/keywords-zh.json +1 -0
  250. package/browser/shared/emoji/data/names.json +1 -0
  251. package/browser/shared/emoji/data/positions.json +1 -0
  252. package/browser/shared/emoji/data/twemoji-index.json +1 -0
  253. package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
  254. package/browser/shared/emoji/data.mjs +196 -0
  255. package/browser/shared/emoji/emoji.mjs +37 -0
  256. package/browser/shared/emoji/glyphs.mjs +134 -0
  257. package/browser/shared/emoji/icon-value.mjs +90 -0
  258. package/browser/shared/escape.mjs +20 -0
  259. package/browser/shared/export-wrap.mjs +89 -0
  260. package/browser/shared/files.mjs +33 -0
  261. package/browser/shared/html-tree.mjs +53 -0
  262. package/browser/shared/http.mjs +22 -0
  263. package/browser/shared/i18n/en.mjs +1164 -0
  264. package/browser/shared/i18n/index.mjs +132 -0
  265. package/browser/shared/ids.mjs +24 -0
  266. package/browser/shared/import-types.mjs +23 -0
  267. package/browser/shared/inline-spacing.mjs +97 -0
  268. package/browser/shared/layers.mjs +42 -0
  269. package/browser/shared/media.mjs +162 -0
  270. package/browser/shared/new-names.mjs +64 -0
  271. package/browser/shared/number-reading.mjs +92 -0
  272. package/browser/shared/old-names.mjs +143 -0
  273. package/browser/shared/page-address.mjs +140 -0
  274. package/browser/shared/page-links.mjs +86 -0
  275. package/browser/shared/paths.mjs +73 -0
  276. package/browser/shared/preferences.mjs +110 -0
  277. package/browser/shared/preview-server.mjs +247 -0
  278. package/browser/shared/qa-levels.mjs +93 -0
  279. package/browser/shared/reading-place.mjs +91 -0
  280. package/browser/shared/renamed-storage.mjs +220 -0
  281. package/browser/shared/reports.mjs +46 -0
  282. package/browser/shared/search.mjs +85 -0
  283. package/browser/shared/session.mjs +76 -0
  284. package/browser/shared/table-chart.mjs +113 -0
  285. package/browser/shared/table-hides.mjs +120 -0
  286. package/browser/shared/table-slots.mjs +75 -0
  287. package/browser/shared/words.mjs +23 -0
  288. package/browser/shared/workspace.mjs +535 -0
  289. package/browser/shared/writing-preferences.mjs +68 -0
  290. package/browser/shared/zoom.mjs +57 -0
  291. package/dist/chunk-2B5E4CDJ.mjs +2 -0
  292. package/dist/chunk-2PBALTR3.mjs +57 -0
  293. package/dist/chunk-342HXE3Q.mjs +2 -0
  294. package/dist/chunk-3LA3EMDE.mjs +15 -0
  295. package/dist/chunk-43B75J5T.mjs +2 -0
  296. package/dist/chunk-4K5KC6V2.mjs +2 -0
  297. package/dist/chunk-5FRMCDPY.mjs +16 -0
  298. package/dist/chunk-5L7RYZCL.mjs +2 -0
  299. package/dist/chunk-5LEK2CBS.mjs +2 -0
  300. package/dist/chunk-5OFN5AHC.mjs +2 -0
  301. package/dist/chunk-5SIYS5HN.mjs +2 -0
  302. package/dist/chunk-5TEK3RAD.mjs +2 -0
  303. package/dist/chunk-6CYG3Z4V.mjs +2 -0
  304. package/dist/chunk-6U2SLSDN.mjs +3 -0
  305. package/dist/chunk-742D2WWJ.mjs +19 -0
  306. package/dist/chunk-7GHT2UCA.mjs +15 -0
  307. package/dist/chunk-7QQBR7PB.mjs +2 -0
  308. package/dist/chunk-A55HN5OK.mjs +2 -0
  309. package/dist/chunk-ACOPDCNB.mjs +2 -0
  310. package/dist/chunk-AGDILNWB.mjs +2 -0
  311. package/dist/chunk-ATCJYAR4.mjs +2 -0
  312. package/dist/chunk-B3VTYBYR.mjs +960 -0
  313. package/dist/chunk-C564NU2H.mjs +77 -0
  314. package/dist/chunk-C7HWOBIF.mjs +2 -0
  315. package/dist/chunk-DB4WKGYF.mjs +7 -0
  316. package/dist/chunk-E7VYDG2X.mjs +2 -0
  317. package/dist/chunk-EUX7IC5J.mjs +48 -0
  318. package/dist/chunk-EVJ3BX3M.mjs +2 -0
  319. package/dist/chunk-EZY3WC6Z.mjs +15 -0
  320. package/dist/chunk-FQGQH4KM.mjs +2 -0
  321. package/dist/chunk-G7TOXONE.mjs +6 -0
  322. package/dist/chunk-GPF4YWWB.mjs +9 -0
  323. package/dist/chunk-GRAXKKG4.mjs +2 -0
  324. package/dist/chunk-GYQV7Y4J.mjs +120 -0
  325. package/dist/chunk-HMHK3EQ6.mjs +2 -0
  326. package/dist/chunk-HQQMJCQJ.mjs +10 -0
  327. package/dist/chunk-HYMEEXKN.mjs +4 -0
  328. package/dist/chunk-IERUOGC3.mjs +6 -0
  329. package/dist/chunk-ITNLYXXV.mjs +3 -0
  330. package/dist/chunk-L4DVONOG.mjs +2 -0
  331. package/dist/chunk-LAE5NQ4B.mjs +2 -0
  332. package/dist/chunk-LC7YNQFG.mjs +2 -0
  333. package/dist/chunk-LHTFLRB6.mjs +4 -0
  334. package/dist/chunk-LKX55H76.mjs +2 -0
  335. package/dist/chunk-MAFIGVXY.mjs +2 -0
  336. package/dist/chunk-NFIBI4IP.mjs +26 -0
  337. package/dist/chunk-NSKUTQ5J.mjs +5 -0
  338. package/dist/chunk-O52JMOCZ.mjs +2 -0
  339. package/dist/chunk-P2CAZPDP.mjs +3 -0
  340. package/dist/chunk-QAIHRH3I.mjs +13 -0
  341. package/dist/chunk-QMQUXTU4.mjs +12 -0
  342. package/dist/chunk-QR6A54KJ.mjs +3 -0
  343. package/dist/chunk-QVWVFC5K.mjs +6 -0
  344. package/dist/chunk-QYKCJ7FG.mjs +8 -0
  345. package/dist/chunk-QZY6Q64C.mjs +2 -0
  346. package/dist/chunk-TDQP3DKQ.mjs +10 -0
  347. package/dist/chunk-TMYZB6AV.mjs +2 -0
  348. package/dist/chunk-TX6JH7TR.mjs +2 -0
  349. package/dist/chunk-U3SSPL4K.mjs +8 -0
  350. package/dist/chunk-UH4SIXTR.mjs +2 -0
  351. package/dist/chunk-UNUEWH4S.mjs +2 -0
  352. package/dist/chunk-UXUK4PMC.mjs +27 -0
  353. package/dist/chunk-V4CPWKMZ.mjs +2 -0
  354. package/dist/chunk-V6XZ4VIR.mjs +2 -0
  355. package/dist/chunk-VACRB2NE.mjs +3 -0
  356. package/dist/chunk-W63L7367.mjs +7 -0
  357. package/dist/chunk-W7CXPCRV.mjs +6 -0
  358. package/dist/chunk-W7XHE4CT.mjs +2 -0
  359. package/dist/chunk-XINNG4F2.mjs +4 -0
  360. package/dist/chunk-Y34NR5JL.mjs +2 -0
  361. package/dist/chunk-YEY3RSFQ.mjs +2 -0
  362. package/dist/chunk-YL2DPWVS.mjs +4 -0
  363. package/dist/chunk-YXAUQPVK.mjs +8 -0
  364. package/dist/chunk-YZ5Z6H4W.mjs +4 -0
  365. package/dist/chunk-Z52HTUW7.mjs +4 -0
  366. package/dist/chunk-ZBIM6ARY.mjs +216 -0
  367. package/dist/cli.mjs +2 -0
  368. package/dist/server.mjs +2 -0
  369. package/dist/worker.mjs +5 -0
  370. package/kit/charts.js +355 -0
  371. package/kit/code.js +164 -0
  372. package/kit/dialogs.js +157 -0
  373. package/kit/math.js +1988 -0
  374. package/kit/report.css +2821 -0
  375. package/kit/runtime.js +1707 -0
  376. package/kit/sheets.js +123 -0
  377. package/kit/state.js +217 -0
  378. package/kit/tables.js +1449 -0
  379. package/kit/tokens.js +29 -0
  380. package/kit/ui.js +561 -0
  381. package/package.json +41 -4
  382. package/release.json +25 -0
  383. package/sdk/column-widths.d.ts +56 -0
  384. package/sdk/doc-charts.d.ts +82 -0
  385. package/sdk/doc-code.d.ts +28 -0
  386. package/sdk/doc-dialogs.d.ts +4 -0
  387. package/sdk/doc-kit.d.ts +141 -0
  388. package/sdk/doc-math.d.ts +3 -0
  389. package/sdk/doc-sheets.d.ts +15 -0
  390. package/sdk/doc-state.d.ts +41 -0
  391. package/sdk/doc-tables.d.ts +24 -0
  392. package/sdk/doc-tokens.d.ts +22 -0
  393. package/sdk/index.d.ts +30 -0
  394. package/skill/SKILL.md +138 -0
  395. package/skill/agents/openai.yaml +5 -0
  396. package/skill/references/building-components.md +327 -0
  397. package/skill/references/example-report.html +356 -0
  398. package/skill/references/writing-html.md +187 -0
  399. package/skill/sdk/column-widths.d.ts +56 -0
  400. package/skill/sdk/doc-charts.d.ts +82 -0
  401. package/skill/sdk/doc-code.d.ts +28 -0
  402. package/skill/sdk/doc-dialogs.d.ts +4 -0
  403. package/skill/sdk/doc-kit.d.ts +141 -0
  404. package/skill/sdk/doc-math.d.ts +3 -0
  405. package/skill/sdk/doc-sheets.d.ts +15 -0
  406. package/skill/sdk/doc-state.d.ts +41 -0
  407. package/skill/sdk/doc-tables.d.ts +24 -0
  408. package/skill/sdk/doc-tokens.d.ts +22 -0
  409. package/skill/sdk/index.d.ts +30 -0
  410. package/starter/doc.html +21 -0
  411. package/starter/doc.json +5 -0
  412. package/vendor/d3.v7.9.0.min.js +2 -0
  413. package/vendor/katex/auto-render.min.js +1 -0
  414. package/vendor/katex/contrib/mhchem.min.js +1 -0
  415. package/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  416. package/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  417. package/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  418. package/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  419. package/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  420. package/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  421. package/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  422. package/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  423. package/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  424. package/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  425. package/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  426. package/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  427. package/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  428. package/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  429. package/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  430. package/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  431. package/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  432. package/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  433. package/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  434. package/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  435. package/vendor/katex/katex.min.css +1 -0
  436. package/vendor/katex/katex.min.js +1 -0
  437. package/vendor/pdfjs/LICENSE +177 -0
  438. package/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  439. package/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  440. package/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  441. package/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  442. package/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  443. package/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  444. package/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  445. package/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  446. package/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  447. package/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  448. package/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  449. package/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  450. package/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  451. package/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  452. package/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  453. package/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  454. package/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  455. package/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  456. package/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  457. package/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  458. package/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  459. package/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  460. package/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  461. package/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  462. package/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  463. package/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  464. package/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  465. package/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  466. package/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  467. package/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  468. package/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  469. package/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  470. package/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  471. package/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  472. package/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  473. package/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  474. package/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  475. package/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  476. package/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  477. package/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  478. package/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  479. package/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  480. package/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  481. package/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  482. package/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  483. package/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  484. package/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  485. package/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  486. package/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  487. package/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  488. package/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  489. package/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  490. package/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  491. package/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  492. package/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  493. package/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  494. package/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  495. package/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  496. package/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  497. package/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  498. package/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  499. package/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  500. package/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  501. package/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  502. package/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  503. package/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  504. package/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  505. package/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  506. package/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  507. package/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  508. package/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  509. package/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  510. package/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  511. package/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  512. package/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  513. package/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  514. package/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  515. package/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  516. package/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  517. package/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  518. package/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  519. package/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  520. package/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  521. package/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  522. package/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  523. package/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  524. package/vendor/pdfjs/cmaps/H.bcmap +0 -0
  525. package/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  526. package/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  527. package/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  528. package/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  529. package/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  530. package/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  531. package/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  532. package/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  533. package/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  534. package/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  535. package/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  536. package/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  537. package/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  538. package/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  539. package/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  540. package/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  541. package/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  542. package/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  543. package/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  544. package/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  545. package/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  546. package/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  547. package/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  548. package/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  549. package/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  550. package/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  551. package/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  552. package/vendor/pdfjs/cmaps/LICENSE +36 -0
  553. package/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  554. package/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  555. package/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  556. package/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  557. package/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  558. package/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  559. package/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  560. package/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  561. package/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  562. package/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  563. package/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  564. package/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  565. package/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  566. package/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  567. package/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  568. package/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  569. package/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  570. package/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  571. package/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  572. package/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  573. package/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  574. package/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  575. package/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  576. package/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  577. package/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  578. package/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  579. package/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  580. package/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  581. package/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  582. package/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  583. package/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  584. package/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  585. package/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  586. package/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  587. package/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  588. package/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  589. package/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  590. package/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  591. package/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  592. package/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  593. package/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  594. package/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  595. package/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  596. package/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  597. package/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  598. package/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  599. package/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  600. package/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  601. package/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  602. package/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  603. package/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  604. package/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  605. package/vendor/pdfjs/cmaps/V.bcmap +0 -0
  606. package/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  607. package/vendor/pdfjs/legacy/build/pdf.min.mjs +27 -0
  608. package/vendor/pdfjs/legacy/build/pdf.worker.min.mjs +27 -0
  609. package/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  610. package/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  611. package/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  612. package/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  613. package/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  614. package/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  615. package/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  616. package/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  617. package/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  618. package/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  619. package/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  620. package/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
  621. package/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  622. package/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  623. package/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  624. package/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  625. package/vendor/types/@types/d3/LICENSE +21 -0
  626. package/vendor/types/@types/d3/index.d.ts +39 -0
  627. package/vendor/types/@types/d3/package.json +81 -0
  628. package/vendor/types/@types/d3-array/LICENSE +21 -0
  629. package/vendor/types/@types/d3-array/index.d.ts +1118 -0
  630. package/vendor/types/@types/d3-array/package.json +56 -0
  631. package/vendor/types/@types/d3-axis/LICENSE +21 -0
  632. package/vendor/types/@types/d3-axis/index.d.ts +293 -0
  633. package/vendor/types/@types/d3-axis/package.json +47 -0
  634. package/vendor/types/@types/d3-brush/LICENSE +21 -0
  635. package/vendor/types/@types/d3-brush/index.d.ts +255 -0
  636. package/vendor/types/@types/d3-brush/package.json +42 -0
  637. package/vendor/types/@types/d3-chord/LICENSE +21 -0
  638. package/vendor/types/@types/d3-chord/index.d.ts +471 -0
  639. package/vendor/types/@types/d3-chord/package.json +40 -0
  640. package/vendor/types/@types/d3-color/LICENSE +21 -0
  641. package/vendor/types/@types/d3-color/index.d.ts +669 -0
  642. package/vendor/types/@types/d3-color/package.json +55 -0
  643. package/vendor/types/@types/d3-contour/LICENSE +21 -0
  644. package/vendor/types/@types/d3-contour/index.d.ts +222 -0
  645. package/vendor/types/@types/d3-contour/package.json +38 -0
  646. package/vendor/types/@types/d3-delaunay/LICENSE +21 -0
  647. package/vendor/types/@types/d3-delaunay/index.d.ts +316 -0
  648. package/vendor/types/@types/d3-delaunay/package.json +30 -0
  649. package/vendor/types/@types/d3-dispatch/LICENSE +21 -0
  650. package/vendor/types/@types/d3-dispatch/index.d.ts +96 -0
  651. package/vendor/types/@types/d3-dispatch/package.json +51 -0
  652. package/vendor/types/@types/d3-drag/LICENSE +21 -0
  653. package/vendor/types/@types/d3-drag/index.d.ts +395 -0
  654. package/vendor/types/@types/d3-drag/package.json +42 -0
  655. package/vendor/types/@types/d3-dsv/LICENSE +21 -0
  656. package/vendor/types/@types/d3-dsv/index.d.ts +482 -0
  657. package/vendor/types/@types/d3-dsv/package.json +45 -0
  658. package/vendor/types/@types/d3-ease/LICENSE +21 -0
  659. package/vendor/types/@types/d3-ease/index.d.ts +316 -0
  660. package/vendor/types/@types/d3-ease/package.json +40 -0
  661. package/vendor/types/@types/d3-fetch/LICENSE +21 -0
  662. package/vendor/types/@types/d3-fetch/index.d.ts +282 -0
  663. package/vendor/types/@types/d3-fetch/package.json +37 -0
  664. package/vendor/types/@types/d3-force/LICENSE +21 -0
  665. package/vendor/types/@types/d3-force/index.d.ts +899 -0
  666. package/vendor/types/@types/d3-force/package.json +45 -0
  667. package/vendor/types/@types/d3-format/LICENSE +21 -0
  668. package/vendor/types/@types/d3-format/index.d.ts +337 -0
  669. package/vendor/types/@types/d3-format/package.json +45 -0
  670. package/vendor/types/@types/d3-geo/LICENSE +21 -0
  671. package/vendor/types/@types/d3-geo/index.d.ts +1371 -0
  672. package/vendor/types/@types/d3-geo/package.json +48 -0
  673. package/vendor/types/@types/d3-hierarchy/LICENSE +21 -0
  674. package/vendor/types/@types/d3-hierarchy/index.d.ts +928 -0
  675. package/vendor/types/@types/d3-hierarchy/package.json +50 -0
  676. package/vendor/types/@types/d3-interpolate/LICENSE +21 -0
  677. package/vendor/types/@types/d3-interpolate/index.d.ts +387 -0
  678. package/vendor/types/@types/d3-interpolate/package.json +47 -0
  679. package/vendor/types/@types/d3-path/LICENSE +21 -0
  680. package/vendor/types/@types/d3-path/index.d.ts +108 -0
  681. package/vendor/types/@types/d3-path/package.json +41 -0
  682. package/vendor/types/@types/d3-polygon/LICENSE +21 -0
  683. package/vendor/types/@types/d3-polygon/index.d.ts +41 -0
  684. package/vendor/types/@types/d3-polygon/package.json +40 -0
  685. package/vendor/types/@types/d3-quadtree/LICENSE +21 -0
  686. package/vendor/types/@types/d3-quadtree/index.d.ts +204 -0
  687. package/vendor/types/@types/d3-quadtree/package.json +45 -0
  688. package/vendor/types/@types/d3-random/LICENSE +21 -0
  689. package/vendor/types/@types/d3-random/index.d.ts +313 -0
  690. package/vendor/types/@types/d3-random/package.json +41 -0
  691. package/vendor/types/@types/d3-scale/LICENSE +21 -0
  692. package/vendor/types/@types/d3-scale/index.d.ts +2669 -0
  693. package/vendor/types/@types/d3-scale/package.json +53 -0
  694. package/vendor/types/@types/d3-scale-chromatic/LICENSE +21 -0
  695. package/vendor/types/@types/d3-scale-chromatic/index.d.ts +520 -0
  696. package/vendor/types/@types/d3-scale-chromatic/package.json +46 -0
  697. package/vendor/types/@types/d3-selection/LICENSE +21 -0
  698. package/vendor/types/@types/d3-selection/index.d.ts +1210 -0
  699. package/vendor/types/@types/d3-selection/package.json +56 -0
  700. package/vendor/types/@types/d3-shape/LICENSE +21 -0
  701. package/vendor/types/@types/d3-shape/index.d.ts +2792 -0
  702. package/vendor/types/@types/d3-shape/package.json +53 -0
  703. package/vendor/types/@types/d3-time/LICENSE +21 -0
  704. package/vendor/types/@types/d3-time/index.d.ts +697 -0
  705. package/vendor/types/@types/d3-time/package.json +46 -0
  706. package/vendor/types/@types/d3-time-format/LICENSE +21 -0
  707. package/vendor/types/@types/d3-time-format/index.d.ts +202 -0
  708. package/vendor/types/@types/d3-time-format/package.json +40 -0
  709. package/vendor/types/@types/d3-timer/LICENSE +21 -0
  710. package/vendor/types/@types/d3-timer/index.d.ts +68 -0
  711. package/vendor/types/@types/d3-timer/package.json +45 -0
  712. package/vendor/types/@types/d3-transition/LICENSE +21 -0
  713. package/vendor/types/@types/d3-transition/index.d.ts +664 -0
  714. package/vendor/types/@types/d3-transition/package.json +47 -0
  715. package/vendor/types/@types/d3-zoom/LICENSE +21 -0
  716. package/vendor/types/@types/d3-zoom/index.d.ts +611 -0
  717. package/vendor/types/@types/d3-zoom/package.json +48 -0
  718. package/vendor/types/@types/geojson/LICENSE +21 -0
  719. package/vendor/types/@types/geojson/index.d.ts +202 -0
  720. package/vendor/types/@types/geojson/package.json +46 -0
  721. package/vendor/typescript/LICENSE.txt +55 -0
  722. package/vendor/typescript/lib.decorators.d.ts +384 -0
  723. package/vendor/typescript/lib.decorators.legacy.d.ts +22 -0
  724. package/vendor/typescript/lib.dom.d.ts +39429 -0
  725. package/vendor/typescript/lib.dom.iterable.d.ts +571 -0
  726. package/vendor/typescript/lib.es2015.collection.d.ts +147 -0
  727. package/vendor/typescript/lib.es2015.core.d.ts +597 -0
  728. package/vendor/typescript/lib.es2015.d.ts +28 -0
  729. package/vendor/typescript/lib.es2015.generator.d.ts +77 -0
  730. package/vendor/typescript/lib.es2015.iterable.d.ts +605 -0
  731. package/vendor/typescript/lib.es2015.promise.d.ts +81 -0
  732. package/vendor/typescript/lib.es2015.proxy.d.ts +128 -0
  733. package/vendor/typescript/lib.es2015.reflect.d.ts +144 -0
  734. package/vendor/typescript/lib.es2015.symbol.d.ts +46 -0
  735. package/vendor/typescript/lib.es2015.symbol.wellknown.d.ts +326 -0
  736. package/vendor/typescript/lib.es2016.array.include.d.ts +116 -0
  737. package/vendor/typescript/lib.es2016.d.ts +21 -0
  738. package/vendor/typescript/lib.es2016.intl.d.ts +31 -0
  739. package/vendor/typescript/lib.es2017.arraybuffer.d.ts +21 -0
  740. package/vendor/typescript/lib.es2017.d.ts +26 -0
  741. package/vendor/typescript/lib.es2017.date.d.ts +31 -0
  742. package/vendor/typescript/lib.es2017.intl.d.ts +44 -0
  743. package/vendor/typescript/lib.es2017.object.d.ts +49 -0
  744. package/vendor/typescript/lib.es2017.sharedmemory.d.ts +135 -0
  745. package/vendor/typescript/lib.es2017.string.d.ts +45 -0
  746. package/vendor/typescript/lib.es2017.typedarrays.d.ts +53 -0
  747. package/vendor/typescript/lib.es2018.asyncgenerator.d.ts +77 -0
  748. package/vendor/typescript/lib.es2018.asynciterable.d.ts +53 -0
  749. package/vendor/typescript/lib.es2018.d.ts +24 -0
  750. package/vendor/typescript/lib.es2018.intl.d.ts +83 -0
  751. package/vendor/typescript/lib.es2018.promise.d.ts +30 -0
  752. package/vendor/typescript/lib.es2018.regexp.d.ts +37 -0
  753. package/vendor/typescript/lib.es2019.array.d.ts +79 -0
  754. package/vendor/typescript/lib.es2019.d.ts +24 -0
  755. package/vendor/typescript/lib.es2019.intl.d.ts +23 -0
  756. package/vendor/typescript/lib.es2019.object.d.ts +33 -0
  757. package/vendor/typescript/lib.es2019.string.d.ts +37 -0
  758. package/vendor/typescript/lib.es2019.symbol.d.ts +24 -0
  759. package/vendor/typescript/lib.es2020.bigint.d.ts +765 -0
  760. package/vendor/typescript/lib.es2020.d.ts +27 -0
  761. package/vendor/typescript/lib.es2020.date.d.ts +42 -0
  762. package/vendor/typescript/lib.es2020.intl.d.ts +474 -0
  763. package/vendor/typescript/lib.es2020.number.d.ts +28 -0
  764. package/vendor/typescript/lib.es2020.promise.d.ts +47 -0
  765. package/vendor/typescript/lib.es2020.sharedmemory.d.ts +99 -0
  766. package/vendor/typescript/lib.es2020.string.d.ts +44 -0
  767. package/vendor/typescript/lib.es2020.symbol.wellknown.d.ts +41 -0
  768. package/vendor/typescript/lib.es2021.d.ts +23 -0
  769. package/vendor/typescript/lib.es2021.intl.d.ts +166 -0
  770. package/vendor/typescript/lib.es2021.promise.d.ts +48 -0
  771. package/vendor/typescript/lib.es2021.string.d.ts +33 -0
  772. package/vendor/typescript/lib.es2021.weakref.d.ts +78 -0
  773. package/vendor/typescript/lib.es2022.array.d.ts +121 -0
  774. package/vendor/typescript/lib.es2022.d.ts +25 -0
  775. package/vendor/typescript/lib.es2022.error.d.ts +75 -0
  776. package/vendor/typescript/lib.es2022.intl.d.ts +145 -0
  777. package/vendor/typescript/lib.es2022.object.d.ts +26 -0
  778. package/vendor/typescript/lib.es2022.regexp.d.ts +39 -0
  779. package/vendor/typescript/lib.es2022.string.d.ts +25 -0
  780. package/vendor/typescript/lib.es5.d.ts +4601 -0
  781. package/vendor/typescript/package.json +4 -0
  782. package/vendor/typescript/typescript.js +446 -0
  783. package/web/assets/index-B6Ai3wsa.js +13 -0
  784. package/web/assets/index-SYPC3EZy.css +1 -0
  785. package/web/index.html +14 -0
@@ -0,0 +1,977 @@
1
+ import { TEXTS_KEPT, createCommits, nodeHTML, writtenHTML } from "./commit.mjs";
2
+ import { randomRequestId } from "../../shared/ids.mjs";
3
+ import { viewKey } from "../../shared/session.mjs";
4
+ import { postJson } from "./post.mjs";
5
+ import { unpack } from "./sync-state.mjs";
6
+
7
+ // Autosave (docs/editor.md, "Autosave"): the page is always editing and commits what changed
8
+ // (commit.mjs) by itself. A change waits for a pause in typing (IDLE), at most LONGEST while the
9
+ // reader keeps typing; a discrete operation (a block deleted, moved, split or converted, a
10
+ // formatting step, undo) and the end of an input method's composition commit at once, and Ctrl+S
11
+ // commits whatever waits. One commit is on its way at a time; what changes meanwhile goes with the
12
+ // next. A commit that does not get through is tried again, later and later, as it was: the file
13
+ // may have taken it (only the answer was lost), and one built anew from the page would name what
14
+ // it wrote as still to be written (the reader's later words lost, or a conflict with them).
15
+ //
16
+ // Nothing is lost when the page goes away before a commit is answered: what is not in the file is
17
+ // kept in localStorage (the crash buffer): the commit whose answer never came, the commit that
18
+ // would carry everything else (what is held for the reader too), and the text of blocks no commit
19
+ // can carry yet; with a commit whose answer is not known, what the page knows and the page itself
20
+ // (stateKey), so that the next page takes up exactly where this one was. A page opening on the report sends what a closed or crashed page left there
21
+ // (recover), and lists what the file did not take (lostKey) until the reader has dealt with it.
22
+ // Commits name everything by identity and the blocks made on the page by ids the page proposes,
23
+ // so sending one again, from the buffer or after a lost answer, writes it once. Closing the page
24
+ // tries a last commit (keepalive) when none is on its way, but that is only a try; the buffer is
25
+ // what counts.
26
+ //
27
+ // The file changing by someone else (the Agent, another tab) is not a question for the reader:
28
+ // commits merge with it on the server, and once nothing is left that the file does not have
29
+ // (settled) and the reader pauses, the page reloads to show it, where the reader was
30
+ // (reloadView). Only when that write put texts the reader committed back as they were (an Agent
31
+ // writing the whole file from what it read earlier) does the page ask first: it keeps the texts it
32
+ // committed (commits.committed(), and those of its earlier loads in sessionStorage), the blocks it
33
+ // made and moved this load (commits.placedUnits()) and what its components wrote (commits.written(),
34
+ // those of earlier loads too), and the server says which the file now has as before (POST
35
+ // /__gallery/edit/reverted): a text as it was, a block it made gone, a block it moved back in the unit
36
+ // it was in, a part as before a write of the page's.
37
+
38
+ const IDLE = 1600;
39
+ const LONGEST = 8000;
40
+ const RETRY = [1000, 2000, 5000, 10000, 30000];
41
+ const QUIET = 1500; // how long the reader has changed nothing before the page reloads by itself
42
+ const STORE_EVERY = 1000; // the crash buffer while typing, at most this often
43
+ const JOURNAL_FOR = 60 * 60 * 1000; // how long a committed text is checked for being put back
44
+ const OWN_FOR = 5000; // how long the live-reload channel may still announce a version this page wrote
45
+
46
+ // A committed text as the revert check names it, whichever load named it: the report's title, a block of
47
+ // a unit by the unit's id (or what named the unit, with none) and its place in it, any other by what
48
+ // names it but the words it had.
49
+ const sameText = (block) => JSON.stringify(block?.title ? "title" : block?.unit ? [block.unit.id ?? block.unit.blockId ?? [block.unit.key, block.unit.open], block.self ? true : block.path ?? null] : { ...block, before: undefined });
50
+
51
+ /**
52
+ * map: the page's map (slug, version, token); commits: createCommits(); blocked(): commits wait
53
+ * (an input method is composing); changedAt(): when the reader last changed anything; holding(asked):
54
+ * the reader is in the middle of something (a press, a drag), so the page does not reload by
55
+ * itself (`asked`: the page asked to be shown again itself, reloadSoon, where someone else's write
56
+ * alone came before); refresh(): the state changed; applied(result, receipt): a commit was taken in;
57
+ * failed(reason, message): a commit that cannot go through as it is (not a lost connection);
58
+ * warn("storage"): the crash buffer could not be written; storage: localStorage
59
+ * (get/set/remove/keys); tab(): this tab's id (a duplicated tab takes a new one); notice(): what the
60
+ * page says after it reloads by itself (a merge it just made), or null; session: sessionStorage
61
+ * (get/set/remove); reverted(entries): texts, blocks and components' writes of the reader's that
62
+ * someone else's write put back, for the reader to decide on (decide) before the page shows that
63
+ * write (a component's with `written`: the parts, by name, and the hashes the file holds of them);
64
+ * before(): called right
65
+ * before a commit goes, a promise it waits for (or null): what goes with the edit elsewhere is told
66
+ * first (where the words of the reader's 批注 went: annotate/ sync).
67
+ */
68
+ export function createAutosave({ map, commits, blocked, changedAt, holding = () => false, refresh, applied, failed, warn = () => {}, storage, tab, notice = () => null, session = null, reverted = () => {}, before = () => null }) {
69
+ let known = map.version; // the file's version as the page last knew it
70
+ // Versions this page's commits wrote (and the one it opened on), in order, and when: the
71
+ // live-reload channel announces them too, a little later, in the order they were written. Only
72
+ // for a while, and only in that order: a version is a text, not a write, and someone else writing
73
+ // the file back to one of them (later, or after a newer one of the page's was announced) is a
74
+ // write of theirs.
75
+ const own = [{ version: map.version, at: Date.now() }];
76
+ let heardUpTo = 0; // the last of them announced so far (its place in own)
77
+ let flight = null; // the commit on its way: { request, pending }
78
+ let parked = null; // a commit sent whose outcome is not known (no answer, busy): { request, pending }, sent again as it is
79
+ let buffer = null; // the request the crash buffer holds now (leave() sends that very one)
80
+ let chain = Promise.resolve(); // the page's writes, one after another (send)
81
+ let again = false;
82
+ let timer = 0;
83
+ let first = 0; // when the oldest change still waiting was made
84
+ let failures = 0;
85
+ let retryAt = 0;
86
+ let offline = false;
87
+ let stopped = null; // why commits stopped (a refusal that trying again will not change)
88
+ let foreign = false; // someone else wrote the file since the page showed it
89
+ let asked = false; // the page asked to be shown again itself (reloadSoon)
90
+ let reloading = false;
91
+ let closed = false; // the page is done with (close)
92
+ let leaving = false; // the page goes away (leave), until it is shown again from the back/forward cache
93
+ let storedAt = 0;
94
+ let flightAt = 0; // when the commit on its way went
95
+ let buffered = false; // the crash buffer holds something
96
+ let unstored = false; // the crash buffer could not be written
97
+ let storedFrom = null; // what the page held (collect()'s result) when the buffer was last written with nothing on its way; null: write it anew
98
+ const events = []; // [version, when]: what the live-reload channel announced while a commit was on its way
99
+ // Texts committed by this page's earlier loads (journalKey), still checked for a while, each under a
100
+ // key of its own (`c<n>`: an earlier load's keys name its blocks, as this one's name others); what
101
+ // their components wrote is the commits' own again (carry), by instance.
102
+ const journalKey = `gallery-edit:${map.slug}:journal`;
103
+ let carried = [];
104
+ try {
105
+ const kept = (JSON.parse(session?.get(journalKey) ?? "[]") ?? []).filter((entry) => entry && Date.now() - entry.time < JOURNAL_FOR);
106
+ carried = kept.filter((entry) => entry.instance === undefined).map((entry, index) => ({ ...entry, key: `c${index}` }));
107
+ commits.carry(kept.filter((entry) => entry.instance !== undefined).map(({ instance, parts, time }) => ({ id: instance, parts, time })));
108
+ } catch {}
109
+ session?.remove(journalKey);
110
+ const reverts = new Map(); // key -> entry: put back by someone else, waiting for the reader
111
+ let checking = false;
112
+ const waiters = [];
113
+ const bufferKey = () => pendingKey(map.slug, tab());
114
+ const keptKey = () => stateKey(map.slug, tab());
115
+
116
+ // How many changes wait, and how many blocks no commit can carry yet, as last counted
117
+ // (collectEdits is not free): on each change, after each commit. `counted`: what they were
118
+ // counted from (collect()'s result), to know whether they still hold (commits.current).
119
+ let count = 0;
120
+ let drafts = 0;
121
+ let counted = null;
122
+ const recount = (collected = commits.collect()) => {
123
+ ({ waiting: count, drafts } = commits.counts(collected));
124
+ counted = collected;
125
+ };
126
+ const waiting = () => count;
127
+
128
+ // ---------- scheduling ----------
129
+ function arm(due) {
130
+ clearTimeout(timer);
131
+ timer = setTimeout(run, Math.max(0, due - Date.now(), retryAt - Date.now()));
132
+ }
133
+
134
+ /**
135
+ * A change was made (`collected`: collectEdits' result, when the caller has it): commit after a
136
+ * pause, or at once (`now`) for a discrete operation.
137
+ */
138
+ function changed(now = false, collected = undefined) {
139
+ if (closed) return;
140
+ recount(collected);
141
+ if (stopped || reloading) return;
142
+ if (count === 0 && !now) {
143
+ store({ collected: counted }); // nothing waits (typed and deleted again): nothing to keep but what is on its way
144
+ return;
145
+ }
146
+ const at = Date.now();
147
+ if (!first) first = at;
148
+ arm(now ? at : Math.min(at + IDLE, first + LONGEST));
149
+ if (!buffered || at - storedAt > STORE_EVERY) store({ collected: counted }); // the first change at once, then now and then
150
+ }
151
+
152
+ /** Commit whatever waits, now; resolves with "saved", "nothing", "offline" or "error". */
153
+ function flush() {
154
+ return new Promise((resolve) => {
155
+ waiters.push(resolve);
156
+ retryAt = 0;
157
+ if (flight) again = true;
158
+ else {
159
+ clearTimeout(timer);
160
+ run();
161
+ }
162
+ });
163
+ }
164
+
165
+ const settle = (outcome) => waiters.splice(0).forEach((resolve) => resolve(outcome));
166
+
167
+ // A commit that throws on the page (a bug) stops autosave and says so, rather than leaving
168
+ // everything waiting without a word.
169
+ async function run() {
170
+ try {
171
+ await commit();
172
+ } catch (error) {
173
+ flight = null;
174
+ stopped = "error";
175
+ failed("error", String(error?.message ?? error));
176
+ refresh();
177
+ settle("error");
178
+ throw error;
179
+ }
180
+ }
181
+
182
+ async function commit() {
183
+ timer = 0;
184
+ if (closed) return;
185
+ if (flight) {
186
+ again = true;
187
+ return;
188
+ }
189
+ if (stopped || reloading) return settle("error");
190
+ if (blocked()) return arm(Date.now() + 300); // the composition's end is a change: the commit goes after the pause
191
+ // A commit whose outcome is not known goes again as it went, before anything newer is built.
192
+ // Otherwise it is built from what the page holds now, which the count and the crash buffer read
193
+ // too (one collect for the three); nothing is built when nothing waited as last counted and
194
+ // nothing changed since (commits.current: a flush with nothing to save, as when the page is left).
195
+ const idle = !parked && count === 0 && commits.current(counted);
196
+ const collected = parked ? undefined : idle ? counted : commits.collect();
197
+ const built = parked ?? (idle ? null : commits.build({ collected }));
198
+ first = 0;
199
+ if (!built) {
200
+ recount(collected);
201
+ store({ collected });
202
+ refresh();
203
+ settle(offline ? "offline" : "nothing");
204
+ maybeReload();
205
+ return;
206
+ }
207
+ flight = parked ?? { request: { ...built.request, known, request: randomRequestId() }, pending: built.pending };
208
+ flightAt = Date.now();
209
+ parked = null;
210
+ store({ collected });
211
+ refresh();
212
+ let receipt;
213
+ try {
214
+ await before();
215
+ } catch {}
216
+ try {
217
+ receipt = await send(flight.request);
218
+ } catch {
219
+ receipt = { ok: false, reason: "offline" };
220
+ }
221
+ const sent = flight;
222
+ flight = null;
223
+ if (closed) return; // the page is gone: nothing to take in
224
+ let after; // with the answer taken in: what the page holds now (anew: the page changed meanwhile), counted and kept
225
+ if (receipt.ok) {
226
+ failures = 0;
227
+ retryAt = 0;
228
+ offline = false;
229
+ known = receipt.version;
230
+ if (receipt.wrote !== false) own.push({ version: receipt.version, at: Date.now() }); // one that changed nothing wrote no version
231
+ if (receipt.foreign) foreign = true;
232
+ applied(commits.apply(sent.pending, receipt, { alone: !foreign }), receipt);
233
+ after = commits.collect();
234
+ recount(after);
235
+ for (const [version, at] of events.splice(0)) heard(version, false, at);
236
+ // Someone else wrote the file before this commit: what that put back is asked now, as when
237
+ // the live-reload channel tells of a write (heard).
238
+ if (receipt.foreign) checkReverted().then((answered) => answered !== null && maybeReload());
239
+ } else if (receipt.reason === "offline" || receipt.reason === "busy" || receipt.reason === "server") {
240
+ parked = sent;
241
+ offline = receipt.reason === "offline";
242
+ failures += 1;
243
+ retryAt = Date.now() + RETRY[Math.min(failures, RETRY.length) - 1];
244
+ } else {
245
+ parked = sent; // kept in the crash buffer: a page opening on the report sends it
246
+ stopped = receipt.reason;
247
+ failed(receipt.reason, receipt.message);
248
+ }
249
+ store({ collected: after });
250
+ refresh();
251
+ if (stopped) return settle("error");
252
+ if (!receipt.ok) {
253
+ settle(offline ? "offline" : "busy");
254
+ arm(retryAt);
255
+ return;
256
+ }
257
+ if (again || waiters.length > 0) {
258
+ again = false;
259
+ if (waiting() > 0) return commit();
260
+ }
261
+ if (waiting() > 0) arm(first ? Math.min(first + LONGEST, Date.now() + IDLE) : Date.now() + IDLE);
262
+ else settle("saved");
263
+ maybeReload();
264
+ }
265
+
266
+ // Every write of the page goes through here, one at a time: commits, and the reader's decision
267
+ // on a text someone else put back.
268
+ function send(request) {
269
+ const run = chain.then(() => postCommit(map, request));
270
+ chain = run.catch(() => {});
271
+ return run;
272
+ }
273
+
274
+ // ---------- the crash buffer ----------
275
+ // What the file may not have: the commit on its way or whose answer never came (`sent`), the
276
+ // commit of everything that differs from what the last answer left (held conflicts and blocks the
277
+ // file refused too: sent again, what the file does not take is listed for the reader), and the
278
+ // texts no commit carries (`keep`: blocks no commit can carry yet, texts someone else's write put
279
+ // back). Nothing at all: no buffer. While a commit's answer is not known (sent again later, on its
280
+ // way for a while, or the page going away: `leaving`), what the page knows and the page itself go
281
+ // along too (stateKey). From leave() on the page is going away, and so is every store after it as
282
+ // it goes (visibilitychange's flush, sending again the commit whose answer never came). `collected`:
283
+ // collect()'s result, when the caller has it from the same task.
284
+ //
285
+ // With nothing on its way, the buffer is written from the page and what the commits know of it
286
+ // (and what someone else put back): when none of these changed since it was last written
287
+ // (commits.current), it holds this already and is left as it is. A page that is left stores three
288
+ // times in a row (prepare-close; pagehide; visibilitychange's flush), each a pass over every block.
289
+ function store({ collected = undefined } = {}) {
290
+ if (closed) return;
291
+ storedAt = Date.now();
292
+ const sent = flight?.request ?? parked?.request ?? null;
293
+ if (!sent && !unstored && storedFrom !== null && commits.current(storedFrom)) return;
294
+ const from = collected ?? commits.collect();
295
+ const { request: built, drafts: texts, typed = [] } = commits.buffer(from);
296
+ storedFrom = sent ? null : from;
297
+ // Kept with the buffer as it is now or not at all: taken up as it was earlier, it would miss
298
+ // what the page did since (the buffer's commit has it).
299
+ let kept = false;
300
+ if (sent && (parked || leaving || (flight && Date.now() - flightAt > STORE_EVERY))) {
301
+ try {
302
+ kept = storage.set(keptKey(), JSON.stringify({ sent: sent.request, foreign, ...commits.keep((flight ?? parked).pending) })) !== false;
303
+ } catch {}
304
+ }
305
+ if (!kept) storage.remove(keptKey()); // the next page goes on from the buffer (rebase)
306
+ const request = built && { ...built, known: null, request: randomRequestId() };
307
+ buffer = request;
308
+ // Words typed in a block a commit makes have no commit to go with: listed, unless one is on its
309
+ // way, when the page's texts below say what the next page does not show of them.
310
+ const keep = [...[...texts, ...(sent ? [] : typed)].map((text) => ({ why: "draft", text })), ...[...reverts.values()].map((entry) => ({ why: "reverted", html: entry.mine, ...writtenIn(entry.id, entry.data) }))];
311
+ buffered = Boolean(sent || request || keep.length > 0);
312
+ if (!buffered) {
313
+ storage.remove(bufferKey());
314
+ unstored = false;
315
+ return;
316
+ }
317
+ // With a commit whose answer is not known, what follows it is rebuilt on its answer by the next
318
+ // page (recover: rebase), as well as it can be: the texts of the page's blocks go along, and
319
+ // what the page then opened on the file does not show is listed (checkLeft).
320
+ const page = sent ? commits.texts() : undefined;
321
+ const stored = storage.set(bufferKey(), JSON.stringify({
322
+ v: 2, time: Date.now(), sent: sent && { ...sent, known: null }, request, keep, ...(page ? { page } : {}),
323
+ })) !== false;
324
+ if (!stored && !unstored) warn("storage");
325
+ unstored = !stored;
326
+ }
327
+
328
+ /**
329
+ * The page goes away: keep what is not in the file, and what it committed for the next load to ask
330
+ * of (keepJournal), and try to commit it (the answer is never read): the very request the buffer
331
+ * holds, so that the next page sending it again gets its answer (the commit is not applied twice).
332
+ */
333
+ function leave() {
334
+ // Closed: the page that shows the report now (a workbench's next one) has the buffer, its own.
335
+ if (closed) return;
336
+ leaving = true;
337
+ store();
338
+ keepJournal();
339
+ if (flight || parked || stopped || waiting() === 0 || !buffer) return; // what is on its way is in the buffer
340
+ postCommit(map, buffer, { keepalive: true }).catch(() => {});
341
+ }
342
+
343
+ // ---------- someone else's changes ----------
344
+ /**
345
+ * The live-reload channel says the file is now `version` (`anyway`: it changed, whatever the
346
+ * version, as when this page sent what a closed page left), `at` when it said so.
347
+ */
348
+ function heard(version, anyway = false, at = Date.now()) {
349
+ if (closed) return;
350
+ // It may be the commit on its way, or one whose answer was lost: known once that one is answered
351
+ // (as of when it was announced).
352
+ if ((flight || parked) && !anyway) return void events.push([version, at]);
353
+ if (!anyway) {
354
+ if (!version) return;
355
+ // The next of the page's own writes to be announced (one before the last announced was
356
+ // written back by someone else).
357
+ const index = own.findIndex((write, place) => place >= heardUpTo && write.version === version);
358
+ if (index >= 0 && (version === known || at - own[index].at < OWN_FOR)) {
359
+ heardUpTo = index;
360
+ return;
361
+ }
362
+ }
363
+ foreign = true;
364
+ refresh();
365
+ // What that write put back of the page's is asked at once, whatever else waits: the reader
366
+ // hears of it now, not once all else is settled.
367
+ checkReverted().then((answered) => answered !== null && maybeReload());
368
+ maybeReload();
369
+ }
370
+
371
+ // Whether everything the page shows is in the file: nothing waits, nothing is on its way or
372
+ // waits to be sent again, nothing is held for the reader, no block waits for the reader to finish
373
+ // it (a title still empty). Only then may the page reload by itself.
374
+ const settled = () => !flight && !parked && waiting() === 0 && drafts === 0 && commits.issues.size === 0 && reverts.size === 0;
375
+
376
+ // What someone else's write put back of what the page committed (POST /__gallery/edit/reverted):
377
+ // texts as they were before, blocks it made taken out, blocks it moved back where they were, parts
378
+ // components wrote as they were before. Each one is held for the reader (reverted) until decided.
379
+ // Resolves with whether the server answered; null while another check runs (that one asks again
380
+ // when it is done).
381
+ let recheck = false;
382
+ async function checkReverted() {
383
+ if (checking) {
384
+ recheck = true;
385
+ return null;
386
+ }
387
+ const texts = journal();
388
+ const units = commits.placedUnits();
389
+ const instances = commits.written();
390
+ if (texts.length === 0 && units.length === 0 && instances.length === 0) return true;
391
+ checking = true;
392
+ let answer = null;
393
+ try {
394
+ const response = await postJson("/__gallery/edit/reverted", map.token, { slug: map.slug, opening: map.version, texts: texts.map(({ key, block, original, earlier }) => ({ key, block, original, ...(earlier ? { earlier } : {}) })), units, instances: instances.map(({ key, id, parts }) => ({ key, id, parts })), pageIds: commits.pageIds() });
395
+ answer = await response.json();
396
+ } catch {}
397
+ checking = false;
398
+ if (closed) return false;
399
+ const back = answer?.ok ? [
400
+ ...answer.reverted.map(({ key, theirs }) => ({ ...texts.find((entry) => entry.key === key), theirs })).filter((entry) => entry.key),
401
+ // A block put back: by its unit, with what it holds (the reader's words, to copy).
402
+ ...(answer.units ?? []).filter(({ key }) => units.some((unit) => unit.key === key)).map(({ key, gone }) => ({ key: `p${key}`, unit: key, gone: Boolean(gone), mine: commits.unitText(key) })),
403
+ // Parts a component wrote put back: by its unit, with the hashes the file holds of them (`written`)
404
+ // and what the page holds of them (`data`, the reader's, kept with the crash buffer); none the page
405
+ // holds as the file does, however the file spells it (heldOtherwise).
406
+ ...(answer.parts ?? []).map(({ key, parts, markup }) => {
407
+ const entry = instances.find((instance) => instance.key === key);
408
+ return { entry, parts: entry && parts && typeof parts === "object" ? commits.heldOtherwise(entry.key, parts, markup) : {} };
409
+ }).filter(({ entry, parts }) => entry && Object.keys(parts).length > 0)
410
+ .map(({ entry, parts }) => {
411
+ const data = commits.dataOfUnit(entry.key, Object.keys(parts)) ?? {};
412
+ return { key: `s${entry.key}`, unit: entry.key, id: entry.id, written: parts, data, mine: Object.values(data).join("\n") };
413
+ }),
414
+ ].filter((entry) => !reverts.has(entry.key)) : [];
415
+ if (back.length > 0) {
416
+ for (const entry of back) reverts.set(entry.key, entry);
417
+ storedFrom = null; // the buffer keeps their words too
418
+ store();
419
+ refresh();
420
+ reverted(back);
421
+ }
422
+ if (recheck) {
423
+ recheck = false;
424
+ return checkReverted();
425
+ }
426
+ return Boolean(answer?.ok);
427
+ }
428
+
429
+ // Once everything is in the file and the reader pauses, show the file as it is, where the reader
430
+ // was; unless it put back texts or blocks the reader committed (checkReverted, asked once more
431
+ // right before). What is not in the file yet is settled by a commit or by the reader (each ends by
432
+ // asking again), so only a pause is waited for here.
433
+ let reloadTimer = 0;
434
+ async function maybeReload() {
435
+ clearTimeout(reloadTimer);
436
+ if (!foreign || reloading || checking || stopped || closed || !settled()) return;
437
+ const quiet = Date.now() - changedAt();
438
+ if (blocked() || holding(asked) || quiet < QUIET) {
439
+ reloadTimer = setTimeout(maybeReload, Math.max(300, QUIET - quiet));
440
+ return;
441
+ }
442
+ const answered = await checkReverted();
443
+ if (answered === null) return; // another check runs, and asks again when it is done
444
+ if (!answered) return void (reloadTimer = setTimeout(maybeReload, 2000));
445
+ // Anything may have happened meanwhile (a change, a commit and its conflicts, something put back): ask again.
446
+ if (!settled() || blocked() || holding(asked) || Date.now() - changedAt() < QUIET) return void (reloadTimer = setTimeout(maybeReload, 300));
447
+ reloading = true;
448
+ storage.remove(bufferKey());
449
+ storage.remove(keptKey());
450
+ storedFrom = null; // written anew if anything stores after all
451
+ keepJournal();
452
+ refresh();
453
+ reloadView(map.slug, null, notice());
454
+ }
455
+
456
+ // The texts committed, this load's and earlier ones', one of each block (sameText): what an earlier
457
+ // load committed of a block a later one committed too is where that text started, and what it was
458
+ // between (two of one block are no one question: the server's answer would name neither); back as it
459
+ // was first, it is asked of what it was between.
460
+ const journal = () => {
461
+ const now = commits.committed().map((entry) => ({ ...entry, time: Date.now() }));
462
+ const texts = new Map();
463
+ for (const entry of [...carried, ...now]) {
464
+ const name = sameText(entry.block);
465
+ const was = texts.get(name);
466
+ if (!was) {
467
+ texts.set(name, entry);
468
+ continue;
469
+ }
470
+ const earlier = [...new Set([...(was.earlier ?? []), was.mine, entry.original, ...(entry.earlier ?? [])])].filter((text) => text !== entry.mine && text !== was.original).slice(-TEXTS_KEPT);
471
+ const joined = { ...entry, original: was.original, earlier };
472
+ if (earlier.length === 0) delete joined.earlier;
473
+ if (entry.mine === was.original && earlier.length === 0) texts.delete(name);
474
+ else texts.set(name, joined);
475
+ }
476
+ return [...texts.values()];
477
+ };
478
+
479
+ // What the page committed (journal()) and what its components wrote (commits.written()), kept for the
480
+ // next load of the page in this tab, which asks of them as its own: when it is shown again, and when
481
+ // the page goes away (a reload of the reader's, another document opened in a workbench).
482
+ function keepJournal() {
483
+ const kept = [
484
+ ...journal().map((entry) => ({ ...entry, time: entry.time ?? Date.now() })),
485
+ ...commits.written().map(({ id, parts, time }) => ({ instance: id, parts, time })),
486
+ ];
487
+ if (kept.length > 0) session?.set(journalKey, JSON.stringify(kept));
488
+ else session?.remove(journalKey);
489
+ }
490
+
491
+ /**
492
+ * The reader decided on a text, block or component's write someone else put back: `mine` commits it
493
+ * again over theirs, otherwise theirs stays. Once every one is decided, the page shows the file.
494
+ */
495
+ async function decide(key, mine) {
496
+ const entry = reverts.get(key);
497
+ if (!entry) return;
498
+ if (entry.unit !== undefined) {
499
+ // A block put back: kept (made again, or moved again where the page has it), or the file's. Parts a
500
+ // component wrote: written again over the file's (its hashes of them), or the file's.
501
+ if (entry.written) commits.rewrite(entry.unit, entry.written, mine);
502
+ else commits.putBack(entry.unit, { gone: entry.gone, mine });
503
+ reverts.delete(key);
504
+ storedFrom = null;
505
+ if (mine) changed(true);
506
+ store();
507
+ refresh();
508
+ if (reverts.size === 0) maybeReload();
509
+ return true;
510
+ }
511
+ if (mine) {
512
+ const texts = [{ key, block: { ...entry.block, before: entry.theirs }, parts: [{ keep: true, html: entry.mine }] }];
513
+ const receipt = await send({ opening: map.version, known: null, texts, units: [], touch: [], request: randomRequestId() }).catch(() => null);
514
+ if (!receipt?.ok || receipt.texts?.[0]?.status === "conflict") return false;
515
+ }
516
+ reverts.delete(key);
517
+ storedFrom = null;
518
+ carried = carried.filter((other) => sameText(other.block) !== sameText(entry.block));
519
+ if (!mine) commits.unjournal(key);
520
+ store();
521
+ refresh();
522
+ if (reverts.size === 0) maybeReload();
523
+ return true;
524
+ }
525
+
526
+ /** Show the file as it is once nothing waits (the reader gave a change up for the file's, or what the page is made of changed). */
527
+ function reloadSoon() {
528
+ foreign = true;
529
+ asked = true;
530
+ maybeReload();
531
+ }
532
+
533
+ addEventListener("online", () => {
534
+ retryAt = 0;
535
+ if ((count > 0 || parked) && !flight) arm(Date.now());
536
+ });
537
+ addEventListener("pageshow", (event) => {
538
+ if (event.persisted) leaving = false;
539
+ });
540
+
541
+ /**
542
+ * The page is done with (it goes away for good, or a test lets it go): nothing more is committed,
543
+ * stored, tried again or reloaded, and an answer still on its way is not taken in.
544
+ */
545
+ function close() {
546
+ closed = true;
547
+ clearTimeout(timer);
548
+ clearTimeout(reloadTimer);
549
+ }
550
+
551
+ return {
552
+ changed,
553
+ flush,
554
+ leave,
555
+ heard,
556
+ store,
557
+ decide,
558
+ reloadSoon,
559
+ close,
560
+ get state() {
561
+ return { waiting: waiting(), syncing: Boolean(flight), offline, stopped, foreign, reloading, held: commits.issues.size + reverts.size, drafts, unstored };
562
+ },
563
+ };
564
+ }
565
+
566
+ // A commit sent: the server's answer, or { ok: false, reason: "server" } when it failed or answered
567
+ // no JSON (a lost connection rejects: the caller reads that as offline).
568
+ async function postCommit(map, request, { keepalive = false } = {}) {
569
+ const response = await postJson("/__gallery/edit/commit", map.token, { slug: map.slug, ...request }, { keepalive });
570
+ if (response.status >= 500) return { ok: false, reason: "server" };
571
+ return response.json().catch(() => ({ ok: false, reason: "server" }));
572
+ }
573
+
574
+ // ---------- reloading where the reader was ----------
575
+
576
+
577
+ // What a tab keeps in storage, by kind: gallery-edit:<slug>:<kind>:<tab>.
578
+ const tabKey = (kind, slug, tab) => `gallery-edit:${slug}:${kind}:${tab}`;
579
+
580
+ /** Where a tab keeps what waits to be saved (the crash buffer). */
581
+ export const pendingKey = (slug, tab) => tabKey("pending", slug, tab);
582
+
583
+ /** Where it keeps, with a commit whose answer is not known, what the page knows and the page (createCommits' keep). */
584
+ export const stateKey = (slug, tab) => tabKey("state", slug, tab);
585
+ const PENDING_KEY = /^gallery-edit:([^:]+):pending:(.+)$/;
586
+ /** The state key of the tab a crash buffer's key (a pendingKey) belongs to. */
587
+ const stateKeyOf = (key) => {
588
+ const [, slug, tab] = PENDING_KEY.exec(key);
589
+ return stateKey(slug, tab);
590
+ };
591
+
592
+ /**
593
+ * Where a page keeps what it could not put back into the file (recover): the reader's words, listed
594
+ * until the reader says they are dealt with. [{ why, html | text, time }]
595
+ */
596
+ export const lostKey = (slug) => `gallery-edit:${slug}:lost`;
597
+
598
+ /**
599
+ * Where a page keeps the texts of the blocks a page it recovered showed (one with a commit whose
600
+ * answer it never learned): the page that opens next lists those it does not show (checkLeft).
601
+ */
602
+ const checkKey = (slug) => `gallery-edit:${slug}:check`;
603
+
604
+ function keepCheck(storage, slug, texts) {
605
+ let list = [];
606
+ try {
607
+ list = JSON.parse(storage.get(checkKey(slug)) ?? "[]") ?? [];
608
+ } catch {}
609
+ return storage.set(checkKey(slug), JSON.stringify([...list, ...texts].slice(-2000))) !== false;
610
+ }
611
+
612
+ /**
613
+ * Once recover() is done and the page shows the file: the texts a page it recovered showed that this
614
+ * one does not (`shown`: its blocks' texts) are listed under lostKey, whatever the rebuilt commits
615
+ * missed (a safety net: no word goes without a trace). Returns how many.
616
+ */
617
+ export function checkLeft(storage, slug, shown) {
618
+ let texts = [];
619
+ try {
620
+ texts = JSON.parse(storage.get(checkKey(slug)) ?? "[]") ?? [];
621
+ } catch {}
622
+ if (texts.length === 0) return 0;
623
+ const words = (text) => String(text).replace(/\s+/g, " ").trim();
624
+ // What the page shows, and what is listed already (a block not finished, kept as a draft).
625
+ const listed = readLost(storage, slug).map((item) => item.text ?? String(item.html ?? "").replace(/<[^>]*>/g, ""));
626
+ const here = words([...shown, ...listed].join("\n"));
627
+ const missing = [...new Set(texts.map(words))].filter((text) => text && !here.includes(text));
628
+ if (missing.length > 0 && !keepLost(storage, slug, missing.map((text) => ({ why: "rebase", text })))) return 0;
629
+ storage.remove(checkKey(slug));
630
+ return missing.length;
631
+ }
632
+
633
+ /** What lostKey holds. */
634
+ export function readLost(storage, slug) {
635
+ try {
636
+ const list = JSON.parse(storage.get(lostKey(slug)) ?? "[]");
637
+ return Array.isArray(list) ? list : [];
638
+ } catch {
639
+ return [];
640
+ }
641
+ }
642
+
643
+ // Add to what lostKey holds (the newest 100); false when it could not be written.
644
+ function keepLost(storage, slug, items) {
645
+ const time = Date.now();
646
+ return storage.set(lostKey(slug), JSON.stringify([...readLost(storage, slug), ...items.map((item) => ({ ...item, time }))].slice(-100))) !== false;
647
+ }
648
+
649
+ /**
650
+ * Reload the page to show the file as it is now, keeping where the reader was (`view`, or now):
651
+ * the scroll position, the block the caret was in (by its data-block-id) with the caret's offset in
652
+ * its text and what the editor keeps besides (keepWith); `notice` is said once the page is back.
653
+ */
654
+ export function reloadView(slug, view = null, notice = null) {
655
+ keep(slug, { ...(view ?? viewNow()), notice });
656
+ }
657
+
658
+ /** Where the reader is now, as reloadView keeps it: { x, y, unit, self, offset, and what keepWith gives }. */
659
+ export function viewNow() {
660
+ const selection = getSelection();
661
+ const anchor = selection.rangeCount ? selection.anchorNode : null;
662
+ const host = (anchor?.nodeType === 1 ? anchor : anchor?.parentElement)?.closest?.("[contenteditable='true']") ?? null; // an empty block's caret stands in the block itself
663
+ const unit = host?.closest("[data-block-id]");
664
+ let offset = null;
665
+ if (host && unit) {
666
+ const head = document.createRange();
667
+ head.setStart(host, 0);
668
+ head.setEnd(selection.anchorNode, selection.anchorOffset);
669
+ offset = head.toString().length;
670
+ }
671
+ return { x: scrollX, y: scrollY, unit: unit?.getAttribute("data-block-id") ?? null, self: unit === host, offset, ...kept() };
672
+ }
673
+
674
+ let kept = () => ({});
675
+ /** What a page shown again takes over besides where the reader was: `read()`, its fields (the detail cards open). */
676
+ export function keepWith(read) {
677
+ kept = read;
678
+ }
679
+
680
+ let reloader = () => location.reload();
681
+ /**
682
+ * How the page is shown again: by default it reloads; in a workbench, the workbench loads the
683
+ * report out of sight and puts it in this page's place once it is drawn (editor.mjs, session.mjs).
684
+ */
685
+ export function setReloader(reload) {
686
+ reloader = reload;
687
+ }
688
+
689
+ function keep(slug, view) {
690
+ try {
691
+ sessionStorage.setItem(viewKey(slug), JSON.stringify(view));
692
+ } catch {}
693
+ reloader();
694
+ }
695
+
696
+ /** Where the reader was before reloadView: { x, y, unit, self, offset, notice, and what keepWith gave } once, or null. */
697
+ export function takeView(slug) {
698
+ try {
699
+ const view = JSON.parse(sessionStorage.getItem(viewKey(slug)) ?? "null");
700
+ sessionStorage.removeItem(viewKey(slug));
701
+ return view && typeof view.y === "number" ? view : null;
702
+ } catch {
703
+ return null;
704
+ }
705
+ }
706
+
707
+ // ---------- recovering what a page left ----------
708
+
709
+ const partsHTML = (text) => (text?.parts ?? []).map((part) => part.html).filter(Boolean).join("\n");
710
+ // A commit sent again is answered once it is known to have gone in or not: not when the server
711
+ // could not be reached, was busy or failed (tried again by the next page). A request it can never
712
+ // take (`body`) is answered too: what it carried is listed for the reader.
713
+ const answered = (receipt) => receipt.ok || receipt.reason === "body";
714
+ // Whether the file gave up something of a commit for its package's manifest having changed: nothing
715
+ // for the reader to decide, and nothing to list (a page goes by the manifest there is now).
716
+ const outdatedIn = (receipt) => Boolean(receipt?.units?.some((answer) => answer.status === "conflict" && answer.reason === "definition"));
717
+ // A node as compared between two commits (their keys differ).
718
+ const shape = (node) => JSON.stringify(node, (name, value) => (name === "key" ? undefined : value));
719
+
720
+ // What of `request` the file did not take (answered `receipt`): the reader's words, to list.
721
+ function refusedOf(request, receipt) {
722
+ if (!receipt.ok) {
723
+ return [...(request.texts ?? []).map((text) => ({ why: "refused", html: partsHTML(text) })), ...(request.units ?? []).map((op) => ({ why: "refused", html: op.insert ? nodeHTML(op.insert) : writtenHTML(op) }))]
724
+ .filter((item) => item.html);
725
+ }
726
+ const texts = new Map((request.texts ?? []).map((text) => [text.key, text]));
727
+ const ops = new Map((request.units ?? []).map((op) => [op.key, op]));
728
+ const out = [];
729
+ for (const answer of receipt.texts ?? []) {
730
+ const html = answer.status === "conflict" ? partsHTML(texts.get(answer.key)) : "";
731
+ if (html) out.push({ why: answer.reason === "missing" ? "missing" : "changed", html });
732
+ }
733
+ for (const answer of receipt.units ?? []) {
734
+ // What a manifest that changed refused is given up (outdatedIn), nothing the reader keeps.
735
+ const op = answer.status === "conflict" && answer.reason !== "definition" ? ops.get(answer.key) : null;
736
+ // A block made on the page, one turned into another type (its text goes with the conversion), or what a component wrote.
737
+ const html = op?.insert ? nodeHTML(op.insert) : writtenHTML(op) ?? "";
738
+ if (html) out.push({ why: op.insert ? "insert" : answer.reason === "missing" ? "missing" : "changed", html, ...writtenIn(op?.unit?.id ?? op?.unit?.blockId, op?.source) });
739
+ }
740
+ return out;
741
+ }
742
+
743
+ // Of a component's write the file did not take, the block it was written in (its id) and the parts it
744
+ // wrote (`parts`, by name), for the reader to copy as that block holding them (editor.mjs showLost).
745
+ const writtenIn = (unit, parts) => (typeof unit === "string" && parts && typeof parts === "object" ? { unit, parts } : {});
746
+
747
+ /**
748
+ * The commit a page left built from the same start as one whose answer it never learned (`sent`,
749
+ * sent again first and answered `answer`), made to go on from what that one wrote. A text both
750
+ * carry starts from what the file took of it. A block `sent` made (a split-off part, a block made
751
+ * on the page, the blocks in it) is not made again: it is in the file, by the id the answer gives,
752
+ * and what the page did to it since goes as done to that block: its text as an edit of what `sent`
753
+ * wrote, a move as a move. Blocks added into or taken out of one it made, or its blocks put in
754
+ * another order, have no start the file knows: what it holds is listed for the reader instead.
755
+ * Returns { request (null: nothing left), dropped }.
756
+ */
757
+ function rebase(request, sent, answer) {
758
+ if (!request || !sent || !answer?.ok) return { request, dropped: [] };
759
+ const answers = new Map([...(answer.texts ?? []), ...(answer.units ?? [])].map((entry) => [entry.key, entry]));
760
+ const took = (key) => answers.get(key) && answers.get(key).status !== "conflict";
761
+ const fileId = (key, proposed) => answer.made?.[key] ?? proposed;
762
+ // Proposed id -> { id (the file's), node (as `sent` carried it), place (of an insert, as sent) }.
763
+ const made = new Map();
764
+ const note = (node) => {
765
+ if (node?.blockId) made.set(node.blockId, { id: fileId(node.key, node.blockId), node });
766
+ node?.children?.forEach(note);
767
+ };
768
+ for (const text of sent.texts ?? []) {
769
+ if (!took(text.key)) continue;
770
+ for (const part of text.parts ?? []) if (!part.keep && part.blockId) made.set(part.blockId, { id: fileId(part.key, part.blockId), node: { html: part.html } });
771
+ }
772
+ for (const op of sent.units ?? []) {
773
+ if (!op.insert || !took(op.key)) continue;
774
+ note(op.insert);
775
+ if (op.insert.blockId) made.get(op.insert.blockId).place = shape(op.place);
776
+ }
777
+ const dropped = [];
778
+ const extra = []; // edits of blocks `sent` made
779
+ let serial = 0;
780
+ const ref = (id) => ({ key: `r${serial++}`, id });
781
+ // The text of a block `sent` made, as an edit of what it wrote.
782
+ const edit = (entry, html) => {
783
+ if (html !== undefined && html !== entry.node.html) extra.push({ key: `r${serial++}`, block: { unit: ref(entry.id), self: true, before: entry.node.html ?? "" }, parts: [{ keep: true, html }] });
784
+ };
785
+ // A block made on the page as this commit carries it, against what `sent` made of it: its text and
786
+ // its blocks' texts as edits; false when its blocks differ (added, taken out, in another order).
787
+ const follow = (node, entry) => {
788
+ const now = (node.children ?? []).map((child) => child.blockId ?? null);
789
+ const then = (entry.node.children ?? []).map((child) => child.blockId ?? null);
790
+ if (now.join() !== then.join() || now.some((id) => id === null || !made.has(id))) return false;
791
+ if (node.title !== entry.node.title) return false;
792
+ edit(entry, node.html);
793
+ return (node.children ?? []).every((child) => follow(child, made.get(child.blockId)));
794
+ };
795
+
796
+ const before = new Map((sent.texts ?? []).filter((text) => took(text.key)).map((text) => [text.key, text]));
797
+ const texts = [];
798
+ for (const text of request.texts ?? []) {
799
+ const parts = (text.parts ?? []).filter((part) => {
800
+ if (part.keep || !made.has(part.blockId)) return true;
801
+ edit(made.get(part.blockId), part.html);
802
+ return false;
803
+ });
804
+ const earlier = before.get(text.key);
805
+ if (!earlier) {
806
+ texts.push({ ...text, parts });
807
+ continue;
808
+ }
809
+ const sentHTML = earlier.parts.find((part) => part.keep)?.html;
810
+ const mine = parts.find((part) => part.keep)?.html;
811
+ if (parts.length === 1 && mine !== undefined && mine === sentHTML) continue; // nothing since
812
+ const entry = answers.get(text.key);
813
+ const start = entry.status === "merged" ? entry.html : sentHTML;
814
+ texts.push({ ...text, parts, block: { ...text.block, before: start ?? text.block.before } });
815
+ }
816
+ const units = [];
817
+ for (const op of request.units ?? []) {
818
+ const entry = op.insert?.blockId ? made.get(op.insert.blockId) : null;
819
+ if (!entry) {
820
+ units.push(op);
821
+ continue;
822
+ }
823
+ if (!follow(op.insert, entry)) {
824
+ dropped.push({ why: "rebase", html: nodeHTML(op.insert) });
825
+ continue;
826
+ }
827
+ if (entry.place !== undefined && shape(op.place) !== entry.place) units.push({ key: op.key, unit: ref(entry.id), move: op.place });
828
+ }
829
+ texts.push(...extra);
830
+ // The units both name, as `sent` left them (as a page takes in an answer): the ids the file gave
831
+ // them and their hashes now, so what this one does to them is not taken for a change of someone
832
+ // else's (a unit's numbers in the page's map are its key in both).
833
+ const known = (ref) => {
834
+ if (!ref || typeof ref !== "object") return;
835
+ if (typeof ref.key === "number") {
836
+ const id = answer.ids?.[ref.key];
837
+ const hash = answer.hashes?.[ref.key];
838
+ if (id) ref.id = id;
839
+ if (hash) ref.hash = hash;
840
+ }
841
+ for (const value of Object.values(ref)) if (value && typeof value === "object") known(value);
842
+ };
843
+ const ops = structuredClone(units);
844
+ const edited = structuredClone(texts);
845
+ ops.forEach(known);
846
+ edited.forEach(known);
847
+ for (const op of ops) if (typeof op.hash === "string" && answer.hashes?.[op.unit?.key]) op.hash = answer.hashes[op.unit.key];
848
+ texts.splice(0, texts.length, ...edited);
849
+ units.splice(0, units.length, ...ops);
850
+ // Changed, it is another commit: an id of its own, the same each time it is made from this one.
851
+ const changed = JSON.stringify([texts, units]) !== JSON.stringify([request.texts ?? [], request.units ?? []]);
852
+ const next = texts.length + units.length > 0 ? { ...request, texts, units, ...(changed && request.request ? { request: `${request.request.slice(0, 38)}r` } : {}) } : null;
853
+ return { request: next, dropped };
854
+ }
855
+
856
+ /**
857
+ * A page that went away with a commit whose answer it never learned (`answer`, now known), taken
858
+ * up: what it knew and the page as it was (`bundle`, stateKey) in a document of their own
859
+ * (`parse`), with that answer taken in. { commits, alone }, or null when it cannot be (the bundle
860
+ * unreadable).
861
+ */
862
+ function rehydrate(bundle, answer, parse) {
863
+ try {
864
+ const { document, value } = unpack(bundle, parse);
865
+ let layout = value.layout;
866
+ const commits = createCommits({ root: document, map: value.map, baselines: value.baselines, getLayout: () => layout, setLayout: (next) => (layout = next), restore: value });
867
+ const alone = !bundle.foreign && !answer.foreign;
868
+ commits.apply(value.pending, answer, { alone });
869
+ return { commits, alone };
870
+ } catch {
871
+ return null;
872
+ }
873
+ }
874
+
875
+ // What the file did not take of what a page taken up sent (its commits' issues): the reader's words, to list.
876
+ const heldOf = (commits) => [...commits.issues.values()].filter((issue) => issue.mine)
877
+ .map((issue) => ({ why: issue.kind === "insert" ? "insert" : issue.reason === "missing" ? "missing" : "changed", html: issue.mine, ...writtenIn(issue.kind === "unit" ? commits.fileId(issue.unit) : null, issue.change?.source) }));
878
+
879
+ /**
880
+ * Send what closed or crashed pages of this report left in the crash buffer under `keys`
881
+ * (pendingKey: this tab's own after a reload, those of tabs that are gone), oldest first: a commit
882
+ * whose answer never came first, as it was, then the rest of what waited. What the file does not
883
+ * take, and the texts no commit carried, go to lostKey before the buffer goes; a buffer whose
884
+ * commits are not answered (the server unreachable, busy) stays for the next page. Resolves with
885
+ * { sent: whether the file changed, lost: how many texts were listed, offline: the server could
886
+ * not be reached, left: a buffer stays for a later page (the server busy or failing, the list not
887
+ * written), outdated: what a component wrote was given up, its package's manifest having changed }.
888
+ */
889
+ export async function recover({ map, storage, keys, parse = (html) => new DOMParser().parseFromString(html, "text/html") }) {
890
+ keys = keys.filter((key) => PENDING_KEY.test(key) && storage.get(key) !== null);
891
+ if (keys.length === 0) return { sent: false, lost: 0, offline: false };
892
+ const entries = keys.map((key) => {
893
+ const raw = storage.get(key) ?? "";
894
+ try {
895
+ const entry = JSON.parse(raw);
896
+ if (entry?.v === 2) return { key, time: entry.time, sent: entry.sent ?? null, request: entry.request ?? null, keep: Array.isArray(entry.keep) ? entry.keep : [], page: Array.isArray(entry.page) ? entry.page : [] };
897
+ } catch {}
898
+ return { key, time: 0, unreadable: raw };
899
+ }).sort((a, b) => a.time - b.time);
900
+ const send = (request) => postCommit(map, request);
901
+ let sent = false;
902
+ let lost = 0;
903
+ let outdated = false;
904
+ for (const entry of entries) {
905
+ const found = entry.unreadable ? [{ why: "unreadable", text: entry.unreadable.slice(0, 20000) }] : [];
906
+ if (!entry.unreadable) {
907
+ try {
908
+ let answer = null;
909
+ if (entry.sent) {
910
+ answer = await send(entry.sent);
911
+ if (!answered(answer)) return { sent, lost, offline: false, left: true }; // kept for the next page
912
+ if (answer.ok && answer.version !== map.version) sent = true;
913
+ outdated ||= outdatedIn(answer);
914
+ }
915
+ // What followed it: when the page went with what it knew, the page is taken up and goes on as
916
+ // it would have, commit after commit until nothing is left (one may wait for another: an
917
+ // item's conversion for its lists to go), each kept in the buffer, with what the page knows
918
+ // then, before it goes (a page that cannot send it sends that very one); what the file did
919
+ // not take in the end is listed. Otherwise the rest is rebuilt on the answer (rebase).
920
+ let bundle = null;
921
+ try {
922
+ bundle = answer?.ok ? JSON.parse(storage.get(stateKeyOf(entry.key)) ?? "null") : null;
923
+ } catch {}
924
+ const taken = bundle && entry.sent && bundle.sent === entry.sent.request ? rehydrate(bundle, answer, parse) : null;
925
+ if (!taken && entry.sent) found.push(...refusedOf(entry.sent, answer));
926
+ let request = null;
927
+ if (taken) {
928
+ for (let round = 0; round < 8; round++) {
929
+ let built = null;
930
+ try {
931
+ built = taken.commits.build({ held: round === 0 }); // what was held for the reader goes once more
932
+ } catch {}
933
+ if (!built) break;
934
+ const next = { ...built.request, known: null, request: randomRequestId() };
935
+ let state = null;
936
+ try {
937
+ state = JSON.stringify({ sent: next.request, foreign: !taken.alone, ...taken.commits.keep(built.pending) });
938
+ } catch {}
939
+ if (storage.set(entry.key, JSON.stringify({ v: 2, time: entry.time, sent: next, request: null, keep: entry.keep, page: entry.page })) === false) return { sent, lost, offline: false, left: true };
940
+ if (!state || storage.set(stateKeyOf(entry.key), state) === false) storage.remove(stateKeyOf(entry.key));
941
+ const reply = await send(next);
942
+ if (!answered(reply)) return { sent, lost, offline: false, left: true };
943
+ if (reply.ok && reply.version !== map.version) sent = true;
944
+ outdated ||= outdatedIn(reply);
945
+ if (!reply.ok) {
946
+ found.push(...refusedOf(next, reply));
947
+ break;
948
+ }
949
+ taken.commits.apply(built.pending, reply, { alone: taken.alone && !reply.foreign });
950
+ }
951
+ found.push(...heldOf(taken.commits));
952
+ storage.remove(stateKeyOf(entry.key));
953
+ } else {
954
+ const rebased = rebase(entry.request, entry.sent, answer);
955
+ request = rebased.request;
956
+ found.push(...rebased.dropped);
957
+ }
958
+ if (request) {
959
+ const second = await send(request);
960
+ if (!answered(second)) return { sent, lost, offline: false, left: true };
961
+ if (second.ok && second.version !== map.version) sent = true;
962
+ outdated ||= outdatedIn(second);
963
+ found.push(...refusedOf(request, second));
964
+ }
965
+ } catch {
966
+ return { sent, lost, offline: true }; // kept for the next page
967
+ }
968
+ found.push(...entry.keep.filter((item) => item?.html || item?.text));
969
+ }
970
+ if (found.length > 0 && !keepLost(storage, map.slug, found)) return { sent, lost, offline: false, left: true }; // the buffer stays
971
+ if (entry.page?.length > 0 && !keepCheck(storage, map.slug, entry.page)) return { sent, lost, offline: false, left: true };
972
+ lost += found.length;
973
+ storage.remove(entry.key);
974
+ storage.remove(stateKeyOf(entry.key));
975
+ }
976
+ return { sent, lost, offline: false, outdated };
977
+ }