@heycys/gallery 0.0.0-stage → 0.1.1

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 (806) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -2
  3. package/THIRD-PARTY-NOTICES.md +3303 -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 +64 -0
  11. package/browser/editor/components/code/index.mjs +327 -0
  12. package/browser/editor/components/code/page.mjs +561 -0
  13. package/browser/editor/components/colors.mjs +50 -0
  14. package/browser/editor/components/columns/index.mjs +104 -0
  15. package/browser/editor/components/columns/page.mjs +310 -0
  16. package/browser/editor/components/details/index.mjs +73 -0
  17. package/browser/editor/components/details/page.mjs +86 -0
  18. package/browser/editor/components/dialog/index.mjs +84 -0
  19. package/browser/editor/components/dialog/page.mjs +201 -0
  20. package/browser/editor/components/divider/index.mjs +48 -0
  21. package/browser/editor/components/embed/icons.mjs +11 -0
  22. package/browser/editor/components/embed/index.mjs +123 -0
  23. package/browser/editor/components/embed/page.mjs +309 -0
  24. package/browser/editor/components/figure/index.mjs +87 -0
  25. package/browser/editor/components/figure/page.mjs +42 -0
  26. package/browser/editor/components/file/icons.mjs +14 -0
  27. package/browser/editor/components/file/index.mjs +77 -0
  28. package/browser/editor/components/file/page.mjs +114 -0
  29. package/browser/editor/components/flowchart/add.mjs +123 -0
  30. package/browser/editor/components/flowchart/index.mjs +246 -0
  31. package/browser/editor/components/flowchart/page.mjs +453 -0
  32. package/browser/editor/components/heading/index.mjs +53 -0
  33. package/browser/editor/components/heading/page.mjs +6 -0
  34. package/browser/editor/components/highlight/index.mjs +63 -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 +48 -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 +179 -0
  43. package/browser/editor/components/installed/page.mjs +523 -0
  44. package/browser/editor/components/installed/served.mjs +35 -0
  45. package/browser/editor/components/kit.mjs +67 -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 +192 -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 +859 -0
  66. package/browser/editor/components/rows.mjs +16 -0
  67. package/browser/editor/components/section/index.mjs +42 -0
  68. package/browser/editor/components/section/page.mjs +141 -0
  69. package/browser/editor/components/set-apart.mjs +24 -0
  70. package/browser/editor/components/sheets/index.mjs +137 -0
  71. package/browser/editor/components/sheets/page.mjs +227 -0
  72. package/browser/editor/components/sheets/pills.mjs +226 -0
  73. package/browser/editor/components/table/grid.mjs +73 -0
  74. package/browser/editor/components/table/index.mjs +308 -0
  75. package/browser/editor/components/table/page.mjs +683 -0
  76. package/browser/editor/components/table/slots.mjs +41 -0
  77. package/browser/editor/components/widget/index.mjs +50 -0
  78. package/browser/editor/page/annotate/anchors.mjs +230 -0
  79. package/browser/editor/page/annotate/card.mjs +102 -0
  80. package/browser/editor/page/annotate/index.mjs +1656 -0
  81. package/browser/editor/page/at-menu/index.mjs +317 -0
  82. package/browser/editor/page/autosave.mjs +1023 -0
  83. package/browser/editor/page/beside.mjs +21 -0
  84. package/browser/editor/page/block-info.mjs +63 -0
  85. package/browser/editor/page/block-menu/colors.mjs +84 -0
  86. package/browser/editor/page/block-menu/index.mjs +217 -0
  87. package/browser/editor/page/blocks.mjs +255 -0
  88. package/browser/editor/page/caret.mjs +682 -0
  89. package/browser/editor/page/changes/bar.mjs +102 -0
  90. package/browser/editor/page/changes/ghost.mjs +206 -0
  91. package/browser/editor/page/changes/gone-words.mjs +94 -0
  92. package/browser/editor/page/changes/index.mjs +447 -0
  93. package/browser/editor/page/changes/words.mjs +27 -0
  94. package/browser/editor/page/chip-fills.mjs +126 -0
  95. package/browser/editor/page/clipboard/import/colors.mjs +192 -0
  96. package/browser/editor/page/clipboard/import/html.mjs +792 -0
  97. package/browser/editor/page/clipboard/import/inline.mjs +232 -0
  98. package/browser/editor/page/clipboard/import/languages.mjs +42 -0
  99. package/browser/editor/page/clipboard/import/markdown.mjs +760 -0
  100. package/browser/editor/page/clipboard/import/math.mjs +100 -0
  101. package/browser/editor/page/clipboard/import/office.mjs +245 -0
  102. package/browser/editor/page/clipboard/import/read.mjs +120 -0
  103. package/browser/editor/page/clipboard/import/rich.mjs +29 -0
  104. package/browser/editor/page/clipboard/import/to-blocks.mjs +290 -0
  105. package/browser/editor/page/clipboard/index.mjs +816 -0
  106. package/browser/editor/page/clipboard/own/blocks.mjs +211 -0
  107. package/browser/editor/page/clipboard/own/chart-keys.mjs +58 -0
  108. package/browser/editor/page/clipboard/own/describe.mjs +124 -0
  109. package/browser/editor/page/clipboard/own/export.mjs +167 -0
  110. package/browser/editor/page/clipboard/own/ids.mjs +39 -0
  111. package/browser/editor/page/clipboard/own/index.mjs +13 -0
  112. package/browser/editor/page/clipboard/own/rules.mjs +44 -0
  113. package/browser/editor/page/clipboard/own/selection.mjs +159 -0
  114. package/browser/editor/page/clipboard/own/writers.mjs +315 -0
  115. package/browser/editor/page/columns/index.mjs +360 -0
  116. package/browser/editor/page/commit.mjs +1813 -0
  117. package/browser/editor/page/conflicts.mjs +249 -0
  118. package/browser/editor/page/convert.mjs +831 -0
  119. package/browser/editor/page/core.mjs +1693 -0
  120. package/browser/editor/page/drag/drop.mjs +307 -0
  121. package/browser/editor/page/drag/index.mjs +351 -0
  122. package/browser/editor/page/drag/preview.mjs +126 -0
  123. package/browser/editor/page/edge-scroll.mjs +50 -0
  124. package/browser/editor/page/editor.mjs +1780 -0
  125. package/browser/editor/page/emoji-picker/emoji-tab.mjs +287 -0
  126. package/browser/editor/page/emoji-picker/glyph-html.mjs +26 -0
  127. package/browser/editor/page/emoji-picker/grid.mjs +81 -0
  128. package/browser/editor/page/emoji-picker/icon-tab.mjs +217 -0
  129. package/browser/editor/page/emoji-picker/icons.mjs +31 -0
  130. package/browser/editor/page/emoji-picker/index.mjs +166 -0
  131. package/browser/editor/page/emoji-picker/styles.mjs +161 -0
  132. package/browser/editor/page/emoji-picker/support.mjs +204 -0
  133. package/browser/editor/page/emoji-picker/upload.mjs +236 -0
  134. package/browser/editor/page/export-report.mjs +23 -0
  135. package/browser/editor/page/fills.mjs +62 -0
  136. package/browser/editor/page/find/index.mjs +107 -0
  137. package/browser/editor/page/flags.mjs +70 -0
  138. package/browser/editor/page/fold/index.mjs +348 -0
  139. package/browser/editor/page/fold/rule.mjs +125 -0
  140. package/browser/editor/page/folded.mjs +23 -0
  141. package/browser/editor/page/format-bar/bar.mjs +449 -0
  142. package/browser/editor/page/format-bar/brush.mjs +77 -0
  143. package/browser/editor/page/format-bar/colors.mjs +46 -0
  144. package/browser/editor/page/format-bar/index.mjs +404 -0
  145. package/browser/editor/page/formula/index.mjs +236 -0
  146. package/browser/editor/page/formula/panel.mjs +336 -0
  147. package/browser/editor/page/formula/symbols.mjs +156 -0
  148. package/browser/editor/page/frame.mjs +74 -0
  149. package/browser/editor/page/frames.mjs +196 -0
  150. package/browser/editor/page/geometry.mjs +402 -0
  151. package/browser/editor/page/gesture.mjs +45 -0
  152. package/browser/editor/page/handle/index.mjs +650 -0
  153. package/browser/editor/page/handle-sizes.mjs +14 -0
  154. package/browser/editor/page/hints/index.mjs +84 -0
  155. package/browser/editor/page/history.mjs +401 -0
  156. package/browser/editor/page/insert/index.mjs +256 -0
  157. package/browser/editor/page/kept.mjs +38 -0
  158. package/browser/editor/page/links/address.mjs +40 -0
  159. package/browser/editor/page/links/index.mjs +866 -0
  160. package/browser/editor/page/links/pages.mjs +131 -0
  161. package/browser/editor/page/links/views.mjs +369 -0
  162. package/browser/editor/page/long-runs.mjs +75 -0
  163. package/browser/editor/page/look.mjs +77 -0
  164. package/browser/editor/page/made.mjs +24 -0
  165. package/browser/editor/page/margin.mjs +373 -0
  166. package/browser/editor/page/markdown/index.mjs +88 -0
  167. package/browser/editor/page/markdown/rules.mjs +33 -0
  168. package/browser/editor/page/marks.mjs +377 -0
  169. package/browser/editor/page/media-menu/index.mjs +158 -0
  170. package/browser/editor/page/menu.mjs +544 -0
  171. package/browser/editor/page/merge.mjs +124 -0
  172. package/browser/editor/page/notices.mjs +142 -0
  173. package/browser/editor/page/offered.mjs +32 -0
  174. package/browser/editor/page/outline/folds.mjs +53 -0
  175. package/browser/editor/page/outline/index.mjs +783 -0
  176. package/browser/editor/page/outline/list.mjs +184 -0
  177. package/browser/editor/page/outline/read.mjs +201 -0
  178. package/browser/editor/page/outline/strip.mjs +287 -0
  179. package/browser/editor/page/outline/tree.mjs +248 -0
  180. package/browser/editor/page/overlay-layer.mjs +227 -0
  181. package/browser/editor/page/overlays.mjs +205 -0
  182. package/browser/editor/page/own.mjs +28 -0
  183. package/browser/editor/page/pads.mjs +71 -0
  184. package/browser/editor/page/page-objects.mjs +105 -0
  185. package/browser/editor/page/panel.mjs +65 -0
  186. package/browser/editor/page/placement.mjs +58 -0
  187. package/browser/editor/page/post.mjs +41 -0
  188. package/browser/editor/page/present-links.mjs +50 -0
  189. package/browser/editor/page/quiet.mjs +48 -0
  190. package/browser/editor/page/resize/index.mjs +327 -0
  191. package/browser/editor/page/select-all/index.mjs +262 -0
  192. package/browser/editor/page/selection/delete.mjs +61 -0
  193. package/browser/editor/page/selection/index.mjs +164 -0
  194. package/browser/editor/page/selection/marquee.mjs +174 -0
  195. package/browser/editor/page/session.mjs +168 -0
  196. package/browser/editor/page/slash-menu/index.mjs +291 -0
  197. package/browser/editor/page/storage.mjs +48 -0
  198. package/browser/editor/page/sync-state.mjs +267 -0
  199. package/browser/editor/page/table/aligns.mjs +29 -0
  200. package/browser/editor/page/table/cell-bar.mjs +141 -0
  201. package/browser/editor/page/table/cells.mjs +527 -0
  202. package/browser/editor/page/table/icons.mjs +40 -0
  203. package/browser/editor/page/table/index.mjs +990 -0
  204. package/browser/editor/page/table/lists.mjs +187 -0
  205. package/browser/editor/page/table/widths.mjs +313 -0
  206. package/browser/editor/page/tex.mjs +32 -0
  207. package/browser/editor/page/text-select/index.mjs +671 -0
  208. package/browser/editor/page/tooltip.mjs +98 -0
  209. package/browser/editor/page/type-grid.mjs +83 -0
  210. package/browser/editor/page/typing/code-edges.mjs +274 -0
  211. package/browser/editor/page/typing/index.mjs +282 -0
  212. package/browser/editor/page/typing/leave.mjs +68 -0
  213. package/browser/editor/page/ui.mjs +160 -0
  214. package/browser/shared/annotations.mjs +569 -0
  215. package/browser/shared/code-folds.mjs +14 -0
  216. package/browser/shared/code-languages.mjs +70 -0
  217. package/browser/shared/code-lines.mjs +30 -0
  218. package/browser/shared/colors.mjs +30 -0
  219. package/browser/shared/component-catalog.mjs +573 -0
  220. package/browser/shared/components/bookmark.mjs +26 -0
  221. package/browser/shared/components/callout.mjs +9 -0
  222. package/browser/shared/components/chart.mjs +20 -0
  223. package/browser/shared/components/code.mjs +37 -0
  224. package/browser/shared/components/columns.mjs +16 -0
  225. package/browser/shared/components/details.mjs +15 -0
  226. package/browser/shared/components/dialog.mjs +28 -0
  227. package/browser/shared/components/diff.mjs +18 -0
  228. package/browser/shared/components/divider.mjs +6 -0
  229. package/browser/shared/components/embed.mjs +25 -0
  230. package/browser/shared/components/figure.mjs +14 -0
  231. package/browser/shared/components/file.mjs +23 -0
  232. package/browser/shared/components/heading.mjs +6 -0
  233. package/browser/shared/components/highlight.mjs +9 -0
  234. package/browser/shared/components/icon.mjs +18 -0
  235. package/browser/shared/components/image.mjs +39 -0
  236. package/browser/shared/components/index.mjs +396 -0
  237. package/browser/shared/components/installed.mjs +232 -0
  238. package/browser/shared/components/list.mjs +146 -0
  239. package/browser/shared/components/math.mjs +49 -0
  240. package/browser/shared/components/mention.mjs +33 -0
  241. package/browser/shared/components/page.mjs +46 -0
  242. package/browser/shared/components/paragraph.mjs +9 -0
  243. package/browser/shared/components/quote.mjs +6 -0
  244. package/browser/shared/components/section.mjs +8 -0
  245. package/browser/shared/components/sheets.mjs +80 -0
  246. package/browser/shared/components/state.mjs +20 -0
  247. package/browser/shared/components/table.mjs +49 -0
  248. package/browser/shared/components/text.mjs +14 -0
  249. package/browser/shared/components/widget.mjs +10 -0
  250. package/browser/shared/copy-ids.mjs +72 -0
  251. package/browser/shared/css-references.mjs +110 -0
  252. package/browser/shared/document-look.mjs +178 -0
  253. package/browser/shared/emoji/custom.mjs +91 -0
  254. package/browser/shared/emoji/data/LICENSE-lucide.txt +43 -0
  255. package/browser/shared/emoji/data/LICENSE-mingcute.txt +202 -0
  256. package/browser/shared/emoji/data/LICENSE-tabler.txt +21 -0
  257. package/browser/shared/emoji/data/README.md +28 -0
  258. package/browser/shared/emoji/data/categories.json +1 -0
  259. package/browser/shared/emoji/data/icons.json +1 -0
  260. package/browser/shared/emoji/data/keywords-en.json +1 -0
  261. package/browser/shared/emoji/data/keywords-zh.json +1 -0
  262. package/browser/shared/emoji/data/names.json +1 -0
  263. package/browser/shared/emoji/data/positions.json +1 -0
  264. package/browser/shared/emoji/data/twemoji-index.json +1 -0
  265. package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
  266. package/browser/shared/emoji/data.mjs +194 -0
  267. package/browser/shared/emoji/emoji.mjs +38 -0
  268. package/browser/shared/emoji/glyphs.mjs +134 -0
  269. package/browser/shared/emoji/icon-value.mjs +91 -0
  270. package/browser/shared/escape.mjs +20 -0
  271. package/browser/shared/export-wrap.mjs +89 -0
  272. package/browser/shared/files.mjs +33 -0
  273. package/browser/shared/html-tree.mjs +59 -0
  274. package/browser/shared/http.mjs +22 -0
  275. package/browser/shared/i18n/en.mjs +1223 -0
  276. package/browser/shared/i18n/index.mjs +132 -0
  277. package/browser/shared/ids.mjs +24 -0
  278. package/browser/shared/import-types.mjs +23 -0
  279. package/browser/shared/inline-spacing.mjs +97 -0
  280. package/browser/shared/layers.mjs +42 -0
  281. package/browser/shared/long-runs.mjs +39 -0
  282. package/browser/shared/media.mjs +162 -0
  283. package/browser/shared/new-names.mjs +67 -0
  284. package/browser/shared/number-reading.mjs +92 -0
  285. package/browser/shared/old-names.mjs +143 -0
  286. package/browser/shared/outline.mjs +194 -0
  287. package/browser/shared/page-address.mjs +140 -0
  288. package/browser/shared/page-links.mjs +86 -0
  289. package/browser/shared/paths.mjs +73 -0
  290. package/browser/shared/preferences.mjs +116 -0
  291. package/browser/shared/preview-server.mjs +247 -0
  292. package/browser/shared/qa-levels.mjs +93 -0
  293. package/browser/shared/reading-place.mjs +91 -0
  294. package/browser/shared/renamed-storage.mjs +220 -0
  295. package/browser/shared/reports.mjs +46 -0
  296. package/browser/shared/search.mjs +85 -0
  297. package/browser/shared/session.mjs +77 -0
  298. package/browser/shared/table-chart.mjs +113 -0
  299. package/browser/shared/table-hides.mjs +120 -0
  300. package/browser/shared/table-slots.mjs +75 -0
  301. package/browser/shared/words.mjs +23 -0
  302. package/browser/shared/workspace-look.mjs +91 -0
  303. package/browser/shared/workspace.mjs +535 -0
  304. package/browser/shared/writing-preferences.mjs +68 -0
  305. package/browser/shared/zoom.mjs +57 -0
  306. package/dist/chunk-2B5E4CDJ.mjs +2 -0
  307. package/dist/chunk-342HXE3Q.mjs +2 -0
  308. package/dist/chunk-3HIRPVLU.mjs +2 -0
  309. package/dist/chunk-43B75J5T.mjs +2 -0
  310. package/dist/chunk-4FS7Z7WR.mjs +2 -0
  311. package/dist/chunk-4K5KC6V2.mjs +2 -0
  312. package/dist/chunk-5L7RYZCL.mjs +2 -0
  313. package/dist/chunk-5MHMDLZD.mjs +4 -0
  314. package/dist/chunk-5ZKJ3H4P.mjs +8 -0
  315. package/dist/chunk-64K7IPLO.mjs +255 -0
  316. package/dist/chunk-6QXGZM36.mjs +8 -0
  317. package/dist/chunk-6RGJOMJP.mjs +16 -0
  318. package/dist/chunk-742D2WWJ.mjs +19 -0
  319. package/dist/chunk-7DTINW6U.mjs +48 -0
  320. package/dist/chunk-A55HN5OK.mjs +2 -0
  321. package/dist/chunk-AGDILNWB.mjs +2 -0
  322. package/dist/chunk-AJ73XJF3.mjs +2 -0
  323. package/dist/chunk-AUZGSZJH.mjs +13 -0
  324. package/dist/chunk-BFE45AKL.mjs +2 -0
  325. package/dist/chunk-BGGDRNYQ.mjs +10 -0
  326. package/dist/chunk-BXO2ZY5P.mjs +2 -0
  327. package/dist/chunk-C564NU2H.mjs +77 -0
  328. package/dist/chunk-CEH4MRCC.mjs +2 -0
  329. package/dist/chunk-CY2FNNAB.mjs +16 -0
  330. package/dist/chunk-DFUDV4LX.mjs +2 -0
  331. package/dist/chunk-DKXG4XDG.mjs +2 -0
  332. package/dist/chunk-E7LWZRXY.mjs +4 -0
  333. package/dist/chunk-EZQ34M3F.mjs +124 -0
  334. package/dist/chunk-EZY3WC6Z.mjs +15 -0
  335. package/dist/chunk-FDLXYYG4.mjs +4 -0
  336. package/dist/chunk-FUKQNOQS.mjs +9 -0
  337. package/dist/chunk-GB5SOYQJ.mjs +7 -0
  338. package/dist/chunk-GRAXKKG4.mjs +2 -0
  339. package/dist/chunk-HYMEEXKN.mjs +4 -0
  340. package/dist/chunk-IVDLMMBD.mjs +2 -0
  341. package/dist/chunk-JPOSLGOC.mjs +6 -0
  342. package/dist/chunk-JVHHHGEA.mjs +3 -0
  343. package/dist/chunk-L3QKH4OK.mjs +26 -0
  344. package/dist/chunk-LAE5NQ4B.mjs +2 -0
  345. package/dist/chunk-LCTHTZIL.mjs +960 -0
  346. package/dist/chunk-LEF6DRCK.mjs +59 -0
  347. package/dist/chunk-LKX55H76.mjs +2 -0
  348. package/dist/chunk-LS4F7JUM.mjs +2 -0
  349. package/dist/chunk-MAFIGVXY.mjs +2 -0
  350. package/dist/chunk-NO5NJCHO.mjs +2 -0
  351. package/dist/chunk-NQPVAU63.mjs +2 -0
  352. package/dist/chunk-NSKUTQ5J.mjs +5 -0
  353. package/dist/chunk-O35FIXRB.mjs +2 -0
  354. package/dist/chunk-P2CAZPDP.mjs +3 -0
  355. package/dist/chunk-PL6GHLGD.mjs +15 -0
  356. package/dist/chunk-QAIHRH3I.mjs +13 -0
  357. package/dist/chunk-QJIR2BLP.mjs +6 -0
  358. package/dist/chunk-QR6A54KJ.mjs +3 -0
  359. package/dist/chunk-QUPLVPB2.mjs +7 -0
  360. package/dist/chunk-R7BNENLY.mjs +4 -0
  361. package/dist/chunk-S6O4UHLC.mjs +2 -0
  362. package/dist/chunk-SGJWQKEI.mjs +2 -0
  363. package/dist/chunk-SYGN7GLU.mjs +2 -0
  364. package/dist/chunk-TMYZB6AV.mjs +2 -0
  365. package/dist/chunk-TX6JH7TR.mjs +2 -0
  366. package/dist/chunk-TYK6VNAB.mjs +10 -0
  367. package/dist/chunk-U3SSPL4K.mjs +8 -0
  368. package/dist/chunk-U6KNPJPR.mjs +2 -0
  369. package/dist/chunk-UALRNVSE.mjs +4 -0
  370. package/dist/chunk-UH4SIXTR.mjs +2 -0
  371. package/dist/chunk-UMHUSOPJ.mjs +2 -0
  372. package/dist/chunk-V3HXVQEM.mjs +2 -0
  373. package/dist/chunk-V5JTW2LH.mjs +27 -0
  374. package/dist/chunk-V5VXUJKG.mjs +2 -0
  375. package/dist/chunk-V6XZ4VIR.mjs +2 -0
  376. package/dist/chunk-VBXMADMP.mjs +2 -0
  377. package/dist/chunk-VNV55RUA.mjs +2 -0
  378. package/dist/chunk-VPD2PS35.mjs +2 -0
  379. package/dist/chunk-W3BGBJQW.mjs +2 -0
  380. package/dist/chunk-W4VKN3GF.mjs +7 -0
  381. package/dist/chunk-W5BXXPDJ.mjs +3 -0
  382. package/dist/chunk-W7XHE4CT.mjs +2 -0
  383. package/dist/chunk-XNROSH2T.mjs +4 -0
  384. package/dist/chunk-XUNHRCOG.mjs +6 -0
  385. package/dist/chunk-Y34NR5JL.mjs +2 -0
  386. package/dist/chunk-Y6RLRPG3.mjs +3 -0
  387. package/dist/chunk-YEY3RSFQ.mjs +2 -0
  388. package/dist/cli.mjs +2 -0
  389. package/dist/server.mjs +2 -0
  390. package/dist/worker.mjs +5 -0
  391. package/kit/charts.js +355 -0
  392. package/kit/code.js +164 -0
  393. package/kit/dialogs.js +157 -0
  394. package/kit/math.js +2004 -0
  395. package/kit/report.css +2932 -0
  396. package/kit/runtime.js +1782 -0
  397. package/kit/sheets.js +123 -0
  398. package/kit/state.js +217 -0
  399. package/kit/tables.js +1499 -0
  400. package/kit/tokens.js +29 -0
  401. package/kit/ui.js +658 -0
  402. package/package.json +41 -4
  403. package/release.json +25 -0
  404. package/sdk/column-widths.d.ts +56 -0
  405. package/sdk/doc-charts.d.ts +82 -0
  406. package/sdk/doc-code.d.ts +28 -0
  407. package/sdk/doc-dialogs.d.ts +4 -0
  408. package/sdk/doc-kit.d.ts +147 -0
  409. package/sdk/doc-math.d.ts +3 -0
  410. package/sdk/doc-sheets.d.ts +15 -0
  411. package/sdk/doc-state.d.ts +41 -0
  412. package/sdk/doc-tables.d.ts +24 -0
  413. package/sdk/doc-tokens.d.ts +22 -0
  414. package/sdk/index.d.ts +30 -0
  415. package/skill/SKILL.md +150 -0
  416. package/skill/agents/openai.yaml +5 -0
  417. package/skill/references/building-components.md +367 -0
  418. package/skill/references/example-report.html +356 -0
  419. package/skill/references/writing-html.md +187 -0
  420. package/skill/sdk/column-widths.d.ts +56 -0
  421. package/skill/sdk/doc-charts.d.ts +82 -0
  422. package/skill/sdk/doc-code.d.ts +28 -0
  423. package/skill/sdk/doc-dialogs.d.ts +4 -0
  424. package/skill/sdk/doc-kit.d.ts +147 -0
  425. package/skill/sdk/doc-math.d.ts +3 -0
  426. package/skill/sdk/doc-sheets.d.ts +15 -0
  427. package/skill/sdk/doc-state.d.ts +41 -0
  428. package/skill/sdk/doc-tables.d.ts +24 -0
  429. package/skill/sdk/doc-tokens.d.ts +22 -0
  430. package/skill/sdk/index.d.ts +30 -0
  431. package/starter/doc.html +21 -0
  432. package/starter/doc.json +5 -0
  433. package/vendor/d3.v7.9.0.min.js +2 -0
  434. package/vendor/katex/auto-render.min.js +1 -0
  435. package/vendor/katex/contrib/mhchem.min.js +1 -0
  436. package/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  437. package/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  438. package/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  439. package/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  440. package/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  441. package/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  442. package/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  443. package/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  444. package/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  445. package/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  446. package/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  447. package/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  448. package/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  449. package/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  450. package/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  451. package/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  452. package/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  453. package/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  454. package/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  455. package/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  456. package/vendor/katex/katex.min.css +1 -0
  457. package/vendor/katex/katex.min.js +1 -0
  458. package/vendor/pdfjs/LICENSE +177 -0
  459. package/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  460. package/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  461. package/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  462. package/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  463. package/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  464. package/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  465. package/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  466. package/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  467. package/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  468. package/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  469. package/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  470. package/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  471. package/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  472. package/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  473. package/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  474. package/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  475. package/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  476. package/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  477. package/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  478. package/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  479. package/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  480. package/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  481. package/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  482. package/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  483. package/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  484. package/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  485. package/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  486. package/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  487. package/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  488. package/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  489. package/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  490. package/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  491. package/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  492. package/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  493. package/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  494. package/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  495. package/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  496. package/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  497. package/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  498. package/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  499. package/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  500. package/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  501. package/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  502. package/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  503. package/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  504. package/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  505. package/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  506. package/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  507. package/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  508. package/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  509. package/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  510. package/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  511. package/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  512. package/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  513. package/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  514. package/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  515. package/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  516. package/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  517. package/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  518. package/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  519. package/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  520. package/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  521. package/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  522. package/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  523. package/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  524. package/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  525. package/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  526. package/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  527. package/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  528. package/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  529. package/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  530. package/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  531. package/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  532. package/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  533. package/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  534. package/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  535. package/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  536. package/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  537. package/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  538. package/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  539. package/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  540. package/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  541. package/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  542. package/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  543. package/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  544. package/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  545. package/vendor/pdfjs/cmaps/H.bcmap +0 -0
  546. package/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  547. package/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  548. package/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  549. package/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  550. package/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  551. package/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  552. package/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  553. package/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  554. package/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  555. package/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  556. package/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  557. package/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  558. package/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  559. package/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  560. package/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  561. package/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  562. package/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  563. package/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  564. package/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  565. package/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  566. package/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  567. package/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  568. package/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  569. package/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  570. package/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  571. package/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  572. package/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  573. package/vendor/pdfjs/cmaps/LICENSE +36 -0
  574. package/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  575. package/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  576. package/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  577. package/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  578. package/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  579. package/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  580. package/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  581. package/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  582. package/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  583. package/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  584. package/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  585. package/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  586. package/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  587. package/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  588. package/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  589. package/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  590. package/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  591. package/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  592. package/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  593. package/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  594. package/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  595. package/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  596. package/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  597. package/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  598. package/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  599. package/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  600. package/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  601. package/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  602. package/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  603. package/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  604. package/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  605. package/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  606. package/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  607. package/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  608. package/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  609. package/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  610. package/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  611. package/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  612. package/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  613. package/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  614. package/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  615. package/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  616. package/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  617. package/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  618. package/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  619. package/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  620. package/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  621. package/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  622. package/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  623. package/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  624. package/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  625. package/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  626. package/vendor/pdfjs/cmaps/V.bcmap +0 -0
  627. package/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  628. package/vendor/pdfjs/legacy/build/pdf.min.mjs +27 -0
  629. package/vendor/pdfjs/legacy/build/pdf.worker.min.mjs +27 -0
  630. package/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  631. package/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  632. package/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  633. package/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  634. package/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  635. package/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  636. package/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  637. package/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  638. package/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  639. package/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  640. package/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  641. package/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
  642. package/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  643. package/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  644. package/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  645. package/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  646. package/vendor/types/@types/d3/LICENSE +21 -0
  647. package/vendor/types/@types/d3/index.d.ts +39 -0
  648. package/vendor/types/@types/d3/package.json +81 -0
  649. package/vendor/types/@types/d3-array/LICENSE +21 -0
  650. package/vendor/types/@types/d3-array/index.d.ts +1118 -0
  651. package/vendor/types/@types/d3-array/package.json +56 -0
  652. package/vendor/types/@types/d3-axis/LICENSE +21 -0
  653. package/vendor/types/@types/d3-axis/index.d.ts +293 -0
  654. package/vendor/types/@types/d3-axis/package.json +47 -0
  655. package/vendor/types/@types/d3-brush/LICENSE +21 -0
  656. package/vendor/types/@types/d3-brush/index.d.ts +255 -0
  657. package/vendor/types/@types/d3-brush/package.json +42 -0
  658. package/vendor/types/@types/d3-chord/LICENSE +21 -0
  659. package/vendor/types/@types/d3-chord/index.d.ts +471 -0
  660. package/vendor/types/@types/d3-chord/package.json +40 -0
  661. package/vendor/types/@types/d3-color/LICENSE +21 -0
  662. package/vendor/types/@types/d3-color/index.d.ts +669 -0
  663. package/vendor/types/@types/d3-color/package.json +55 -0
  664. package/vendor/types/@types/d3-contour/LICENSE +21 -0
  665. package/vendor/types/@types/d3-contour/index.d.ts +222 -0
  666. package/vendor/types/@types/d3-contour/package.json +38 -0
  667. package/vendor/types/@types/d3-delaunay/LICENSE +21 -0
  668. package/vendor/types/@types/d3-delaunay/index.d.ts +316 -0
  669. package/vendor/types/@types/d3-delaunay/package.json +30 -0
  670. package/vendor/types/@types/d3-dispatch/LICENSE +21 -0
  671. package/vendor/types/@types/d3-dispatch/index.d.ts +96 -0
  672. package/vendor/types/@types/d3-dispatch/package.json +51 -0
  673. package/vendor/types/@types/d3-drag/LICENSE +21 -0
  674. package/vendor/types/@types/d3-drag/index.d.ts +395 -0
  675. package/vendor/types/@types/d3-drag/package.json +42 -0
  676. package/vendor/types/@types/d3-dsv/LICENSE +21 -0
  677. package/vendor/types/@types/d3-dsv/index.d.ts +482 -0
  678. package/vendor/types/@types/d3-dsv/package.json +45 -0
  679. package/vendor/types/@types/d3-ease/LICENSE +21 -0
  680. package/vendor/types/@types/d3-ease/index.d.ts +316 -0
  681. package/vendor/types/@types/d3-ease/package.json +40 -0
  682. package/vendor/types/@types/d3-fetch/LICENSE +21 -0
  683. package/vendor/types/@types/d3-fetch/index.d.ts +282 -0
  684. package/vendor/types/@types/d3-fetch/package.json +37 -0
  685. package/vendor/types/@types/d3-force/LICENSE +21 -0
  686. package/vendor/types/@types/d3-force/index.d.ts +899 -0
  687. package/vendor/types/@types/d3-force/package.json +45 -0
  688. package/vendor/types/@types/d3-format/LICENSE +21 -0
  689. package/vendor/types/@types/d3-format/index.d.ts +337 -0
  690. package/vendor/types/@types/d3-format/package.json +45 -0
  691. package/vendor/types/@types/d3-geo/LICENSE +21 -0
  692. package/vendor/types/@types/d3-geo/index.d.ts +1371 -0
  693. package/vendor/types/@types/d3-geo/package.json +48 -0
  694. package/vendor/types/@types/d3-hierarchy/LICENSE +21 -0
  695. package/vendor/types/@types/d3-hierarchy/index.d.ts +928 -0
  696. package/vendor/types/@types/d3-hierarchy/package.json +50 -0
  697. package/vendor/types/@types/d3-interpolate/LICENSE +21 -0
  698. package/vendor/types/@types/d3-interpolate/index.d.ts +387 -0
  699. package/vendor/types/@types/d3-interpolate/package.json +47 -0
  700. package/vendor/types/@types/d3-path/LICENSE +21 -0
  701. package/vendor/types/@types/d3-path/index.d.ts +108 -0
  702. package/vendor/types/@types/d3-path/package.json +41 -0
  703. package/vendor/types/@types/d3-polygon/LICENSE +21 -0
  704. package/vendor/types/@types/d3-polygon/index.d.ts +41 -0
  705. package/vendor/types/@types/d3-polygon/package.json +40 -0
  706. package/vendor/types/@types/d3-quadtree/LICENSE +21 -0
  707. package/vendor/types/@types/d3-quadtree/index.d.ts +204 -0
  708. package/vendor/types/@types/d3-quadtree/package.json +45 -0
  709. package/vendor/types/@types/d3-random/LICENSE +21 -0
  710. package/vendor/types/@types/d3-random/index.d.ts +313 -0
  711. package/vendor/types/@types/d3-random/package.json +41 -0
  712. package/vendor/types/@types/d3-scale/LICENSE +21 -0
  713. package/vendor/types/@types/d3-scale/index.d.ts +2669 -0
  714. package/vendor/types/@types/d3-scale/package.json +53 -0
  715. package/vendor/types/@types/d3-scale-chromatic/LICENSE +21 -0
  716. package/vendor/types/@types/d3-scale-chromatic/index.d.ts +520 -0
  717. package/vendor/types/@types/d3-scale-chromatic/package.json +46 -0
  718. package/vendor/types/@types/d3-selection/LICENSE +21 -0
  719. package/vendor/types/@types/d3-selection/index.d.ts +1210 -0
  720. package/vendor/types/@types/d3-selection/package.json +56 -0
  721. package/vendor/types/@types/d3-shape/LICENSE +21 -0
  722. package/vendor/types/@types/d3-shape/index.d.ts +2792 -0
  723. package/vendor/types/@types/d3-shape/package.json +53 -0
  724. package/vendor/types/@types/d3-time/LICENSE +21 -0
  725. package/vendor/types/@types/d3-time/index.d.ts +697 -0
  726. package/vendor/types/@types/d3-time/package.json +46 -0
  727. package/vendor/types/@types/d3-time-format/LICENSE +21 -0
  728. package/vendor/types/@types/d3-time-format/index.d.ts +202 -0
  729. package/vendor/types/@types/d3-time-format/package.json +40 -0
  730. package/vendor/types/@types/d3-timer/LICENSE +21 -0
  731. package/vendor/types/@types/d3-timer/index.d.ts +68 -0
  732. package/vendor/types/@types/d3-timer/package.json +45 -0
  733. package/vendor/types/@types/d3-transition/LICENSE +21 -0
  734. package/vendor/types/@types/d3-transition/index.d.ts +664 -0
  735. package/vendor/types/@types/d3-transition/package.json +47 -0
  736. package/vendor/types/@types/d3-zoom/LICENSE +21 -0
  737. package/vendor/types/@types/d3-zoom/index.d.ts +611 -0
  738. package/vendor/types/@types/d3-zoom/package.json +48 -0
  739. package/vendor/types/@types/geojson/LICENSE +21 -0
  740. package/vendor/types/@types/geojson/index.d.ts +202 -0
  741. package/vendor/types/@types/geojson/package.json +46 -0
  742. package/vendor/typescript/LICENSE.txt +55 -0
  743. package/vendor/typescript/lib.decorators.d.ts +384 -0
  744. package/vendor/typescript/lib.decorators.legacy.d.ts +22 -0
  745. package/vendor/typescript/lib.dom.d.ts +39429 -0
  746. package/vendor/typescript/lib.dom.iterable.d.ts +571 -0
  747. package/vendor/typescript/lib.es2015.collection.d.ts +147 -0
  748. package/vendor/typescript/lib.es2015.core.d.ts +597 -0
  749. package/vendor/typescript/lib.es2015.d.ts +28 -0
  750. package/vendor/typescript/lib.es2015.generator.d.ts +77 -0
  751. package/vendor/typescript/lib.es2015.iterable.d.ts +605 -0
  752. package/vendor/typescript/lib.es2015.promise.d.ts +81 -0
  753. package/vendor/typescript/lib.es2015.proxy.d.ts +128 -0
  754. package/vendor/typescript/lib.es2015.reflect.d.ts +144 -0
  755. package/vendor/typescript/lib.es2015.symbol.d.ts +46 -0
  756. package/vendor/typescript/lib.es2015.symbol.wellknown.d.ts +326 -0
  757. package/vendor/typescript/lib.es2016.array.include.d.ts +116 -0
  758. package/vendor/typescript/lib.es2016.d.ts +21 -0
  759. package/vendor/typescript/lib.es2016.intl.d.ts +31 -0
  760. package/vendor/typescript/lib.es2017.arraybuffer.d.ts +21 -0
  761. package/vendor/typescript/lib.es2017.d.ts +26 -0
  762. package/vendor/typescript/lib.es2017.date.d.ts +31 -0
  763. package/vendor/typescript/lib.es2017.intl.d.ts +44 -0
  764. package/vendor/typescript/lib.es2017.object.d.ts +49 -0
  765. package/vendor/typescript/lib.es2017.sharedmemory.d.ts +135 -0
  766. package/vendor/typescript/lib.es2017.string.d.ts +45 -0
  767. package/vendor/typescript/lib.es2017.typedarrays.d.ts +53 -0
  768. package/vendor/typescript/lib.es2018.asyncgenerator.d.ts +77 -0
  769. package/vendor/typescript/lib.es2018.asynciterable.d.ts +53 -0
  770. package/vendor/typescript/lib.es2018.d.ts +24 -0
  771. package/vendor/typescript/lib.es2018.intl.d.ts +83 -0
  772. package/vendor/typescript/lib.es2018.promise.d.ts +30 -0
  773. package/vendor/typescript/lib.es2018.regexp.d.ts +37 -0
  774. package/vendor/typescript/lib.es2019.array.d.ts +79 -0
  775. package/vendor/typescript/lib.es2019.d.ts +24 -0
  776. package/vendor/typescript/lib.es2019.intl.d.ts +23 -0
  777. package/vendor/typescript/lib.es2019.object.d.ts +33 -0
  778. package/vendor/typescript/lib.es2019.string.d.ts +37 -0
  779. package/vendor/typescript/lib.es2019.symbol.d.ts +24 -0
  780. package/vendor/typescript/lib.es2020.bigint.d.ts +765 -0
  781. package/vendor/typescript/lib.es2020.d.ts +27 -0
  782. package/vendor/typescript/lib.es2020.date.d.ts +42 -0
  783. package/vendor/typescript/lib.es2020.intl.d.ts +474 -0
  784. package/vendor/typescript/lib.es2020.number.d.ts +28 -0
  785. package/vendor/typescript/lib.es2020.promise.d.ts +47 -0
  786. package/vendor/typescript/lib.es2020.sharedmemory.d.ts +99 -0
  787. package/vendor/typescript/lib.es2020.string.d.ts +44 -0
  788. package/vendor/typescript/lib.es2020.symbol.wellknown.d.ts +41 -0
  789. package/vendor/typescript/lib.es2021.d.ts +23 -0
  790. package/vendor/typescript/lib.es2021.intl.d.ts +166 -0
  791. package/vendor/typescript/lib.es2021.promise.d.ts +48 -0
  792. package/vendor/typescript/lib.es2021.string.d.ts +33 -0
  793. package/vendor/typescript/lib.es2021.weakref.d.ts +78 -0
  794. package/vendor/typescript/lib.es2022.array.d.ts +121 -0
  795. package/vendor/typescript/lib.es2022.d.ts +25 -0
  796. package/vendor/typescript/lib.es2022.error.d.ts +75 -0
  797. package/vendor/typescript/lib.es2022.intl.d.ts +145 -0
  798. package/vendor/typescript/lib.es2022.object.d.ts +26 -0
  799. package/vendor/typescript/lib.es2022.regexp.d.ts +39 -0
  800. package/vendor/typescript/lib.es2022.string.d.ts +25 -0
  801. package/vendor/typescript/lib.es5.d.ts +4601 -0
  802. package/vendor/typescript/package.json +4 -0
  803. package/vendor/typescript/typescript.js +446 -0
  804. package/web/assets/index-Bh74DfsV.css +1 -0
  805. package/web/assets/index-UW0RyFFt.js +13 -0
  806. package/web/index.html +14 -0
@@ -0,0 +1,1656 @@
1
+ import { CARD, RAIL, cardTops, markerLeft, markerTop, newAnnotationId, sameWords, squash, stackItems, tidy } from "../../../shared/annotations.mjs";
2
+ import { t } from "../../../shared/i18n/index.mjs";
3
+ import { SHARES_HEIGHT, standsAsPicture } from "../../components/registry.mjs";
4
+ import { caretAt, editable, select } from "../caret.mjs";
5
+ import { ordered } from "../block-info.mjs";
6
+ import { firstHost, isHost, unitElement } from "../core.mjs";
7
+ import { createFills } from "../fills.mjs";
8
+ import { reveal } from "../folded.mjs";
9
+ import { FOLLOW, task } from "../frames.mjs";
10
+ import { BAR } from "../handle-sizes.mjs";
11
+ import { MARGIN, margin, onMargin, rightRoom, setMargin } from "../margin.mjs";
12
+ import { note, own, provide } from "../overlays.mjs";
13
+ import { sectionOps } from "../page-objects.mjs";
14
+ import { placeBox } from "../placement.mjs";
15
+ import { failure, postAnswer, postJson } from "../post.mjs";
16
+ import { keep, stored } from "../storage.mjs";
17
+ import { addStyle, fadeIn } from "../ui.mjs";
18
+ import { annotatable, blockSelection, locate, textSelection, tighten } from "./anchors.mjs";
19
+ import { HOT, MARK, STYLE, barNode, cardNode, fitField, layerNode, markerNode } from "./card.mjs";
20
+
21
+ // 批注 on the page (docs/editor.md "Annotations"; the workbench's button, docs/workbench.md): the
22
+ // reader's suggestions for a local Agent, each about words of the report or blocks of it, kept by the
23
+ // server in reports/<slug>/annotations.json (editor/annotations.mjs), never in report.html. They run
24
+ // from the editor's start on every page of a session: read from the server once the page is ready, its
25
+ // start never waiting for them, and a page with none has no layer, no observer and no pointer listener.
26
+ // With some, their cards or markers and the marks on their words are drawn from then on.
27
+ //
28
+ // They show in one of two forms, by the room at the column's right (margin.mjs: its `cards`). With
29
+ // room every annotation the page shows stands as a card in the margin, beside what it is about
30
+ // (shared/annotations.mjs cardTops), and its words are edited right there. One stands in front while
31
+ // its field has the focus, or from a press on its marked words or the workbench's list until a press
32
+ // elsewhere (a caret in the words stays in them), pulled back beside its anchor; a new one opens so,
33
+ // and one opened for writing with its anchor's first line out of the window stands in the window. No
34
+ // button stands beside a block meanwhile, as notion-lite has none. In a detail card, where the margin
35
+ // is not the page's, there are none. With no room what follows holds: a marker for each, and one card
36
+ // shown by its words or blocks. The form follows each width at once, a card being written keeping its
37
+ // field, its caret and its words as it goes from one to the other.
38
+ //
39
+ // One is started on purpose: from the format bar (words selected, in a block or across blocks), the
40
+ // button beside a block (handle/, right of its middle line: that block, or all those selected when it
41
+ // is one of them) or Ctrl+Alt+M (the words selected, else the blocks selected, else the caret's block,
42
+ // else the card of the marked words the caret is in). A block that has an annotation takes none of its
43
+ // own (annotated(): no button), its card or marker being the way to what is there, though words in it can be selected and annotated. No selection
44
+ // offers anything by itself. Its card opens with the caret in its field and its words keeping a warm
45
+ // mark (blocks, the fill selected blocks have, in a warm colour: fills.mjs): words have it where the
46
+ // format bar stood (below their last line, by their last character), blocks at the last one's bottom
47
+ // right (at the window's bottom where that is below it), always inside the column. It exists once
48
+ // something is typed in it (white space is nothing): a card left with none leaves nothing, and a saved
49
+ // one emptied goes as its card closes, offered back as its delete button's is (an annotation has words:
50
+ // the user's, 2026-11-06); typing saves it (500ms after the last key, at once when it closes; a text
51
+ // with no words never: the file keeps its last words while the card is open); a press elsewhere, Esc, Enter, Tab or Ctrl+Alt+M close it into a marker
52
+ // in the column's right gutter, at its first line or the middle of its several blocks (right of a
53
+ // section's strip for what is in that section; the place the button beside a block stands on:
54
+ // markerSpot(), which says no where a marker stands already). Closed
55
+ // by a key the caret goes back to its words, or to its blocks where the keys opened it; a card of
56
+ // blocks the pointer opened places none. Resting on a marker (or clicking it) shows its card again
57
+ // where it was written, by its words, which light up, and it stays while the pointer goes over to it.
58
+ // The button at a card's right deletes it, and so does emptying it, or its words or blocks going. One card shows at a time, and starting on the words of an annotation already there shows its
59
+ // card rather than a second one. The workbench's list asks for the page's annotations (rows()) and
60
+ // goes to one (goTo()): what hides it opened, the page scrolled to it, its card shown as resting on its
61
+ // marker shows it, until a press elsewhere or the words leave the window. Markers stack without
62
+ // overlapping (shared/annotations.mjs stackItems), never past the page's end. While the page has one,
63
+ // the page's right side (margin.mjs) is told so: it gives their cards room where it can. Nothing in a
64
+ // detail card's dialog (v1).
65
+ //
66
+ // An annotation goes with its words. The page follows them through the reader's edits: found as they
67
+ // are (as the anchor says them now, or as it said them earlier in this visit: an undo), else where the
68
+ // reader's edit took them (the live range of the words, which grows with what is typed into them and
69
+ // shrinks with what is deleted of them: gone only when nothing of them is left), else as the server
70
+ // finds them (edited in part where they stood). What the page follows is told to the server right
71
+ // before the commit that writes the edit (autosave's `before`: `sync`), so the server's check after it
72
+ // finds the words as they are; the server's own rule, for writes the page did not make (an Agent's),
73
+ // stays strict (shared/annotations.mjs findQuote). When its words or blocks are deleted it is deleted
74
+ // with them: on the page at once (it no longer shows), in the file by the server's check; an undo that
75
+ // brings the words back while the page is open brings it back. Its blocks get their ids through the
76
+ // page's commits (commits.idFor: named with the next one), and until the file holds them the server
77
+ // keeps a new annotation as it was sent (editor/annotations.mjs).
78
+ //
79
+ // Given: slug, token (the map's); isEditing(); picked() (the blocks selected); unpick() (the blocks
80
+ // selected and text selected across blocks let go); wholePage() (the blocks Ctrl+A's top step selects,
81
+ // select-all/: the whole page takes none); crossTexts() (text selected across blocks, text-select/:
82
+ // [{ host, range }] or null); idFor(element) (the id a block has or is to have in the file); changed()
83
+ // (how many there are changed: the workbench hears it); removed(items) (one deleted from its card: the
84
+ // workbench offers it back); say(text, kind).
85
+
86
+ const ROUTE = "/__gallery/edit/annotations";
87
+ const SAVE_AFTER = 500; // typing saves this long after the last key
88
+ const HOVER_IN = 150; // resting on a marker this long shows its card
89
+ const HOVER_OUT = 300; // the card shown by hover goes this long after the pointer left it and its marker (unless it is on its way to the card)
90
+ const REVEAL = 64; // a card pressed in the margin brings what it is about this far into the window, out of it
91
+ const TOP_ROOM = 52; // no marker above this (the page's top)
92
+ const END_ROOM = 16; // nor nearer the page's end than this
93
+ const SAME_LINE = 4; // annotations whose lines are this near stand in one row: the column further right first
94
+ const SYNC_WAIT = 1500; // the most a commit waits for the anchors it changes to be told
95
+ const HISTORY = 12; // the anchors each annotation had in this visit, the latest kept (an undo finds them as they were)
96
+ // Where a card stands by words: the format bar's place (format-bar/bar.mjs GAP_X, GAP_Y: its corner
97
+ // 4px right of the last character, 8px below its line or above the words), and 12px inside the window.
98
+ const GAP_X = 4;
99
+ const GAP_Y = 8;
100
+ const EDGE = 12;
101
+ // Blocks' card at the window's bottom stands above the notices there (notices.mjs, the workbench's
102
+ // toast: 24px up, about 37px tall, the selection's line while blocks are selected), 8px clear.
103
+ const NOTICE_ROOM = 70;
104
+ // The blocks of what takes its height from the tallest beside it: one growing there moves what is below
105
+ // it with no change of the page's height, and says so by its own size.
106
+ const IN_ROW = [...SHARES_HEIGHT].map((kind) => `[data-ge-unit-kind="${kind}"] > *`).join();
107
+
108
+ export function createAnnotate({ slug, token, isEditing, picked, unpick = () => {}, wholePage = () => [], crossTexts, idFor, changed = () => {}, removed = () => {}, say }) {
109
+ const stashKey = `gallery-edit:${slug}:notes`; // what could not be saved (offline), sent by the next page that reads them
110
+ let reading = null; // the first read from the server, under way or done (taken back when it failed)
111
+ let counted = false; // that read is in and what it brought found: the page can say how many it has
112
+ let items = new Map(); // id -> { id, created, updated, text, anchor } in page order, as the server last said, with the page's own changes
113
+ let filed = new Map(); // id -> its text as the file last held it (the server's last answer): what 撤销 brings back of one emptied in its card
114
+ let version = null; // the file's, as last read or written
115
+ const lost = new Map(); // id -> an annotation a check deleted this visit (its words gone): back when an undo brings them back
116
+ const timers = new Map(); // id -> the timer of its save (typed, not saved yet)
117
+ const sending = new Set(); // ids whose save is on its way
118
+ const made = new Map(); // id -> { kind, units, range, excerpt }: made on the page this visit, until its first save names its blocks
119
+ const named = new Map(); // id -> its blocks' elements: saved with the ids the next commit gives them, until they carry them
120
+ const history = new Map(); // id -> the anchors it had this visit ({ kind, blocks, quote, prefix, suffix }), oldest first
121
+ let mutations = 0; // changes of the page's text seen
122
+ const goneAt = new Map(); // id -> the change at which its words were no longer on the page (this visit)
123
+ // id -> { range (its words, live: it follows the reader's edits), units (their blocks' elements),
124
+ // excerpt ({ quote, prefix, suffix } as the page has them now), marker, bar, hidden, folded }
125
+ const views = new Map();
126
+ let layer = null;
127
+ // id -> its card (cardNode), and what the margin last gave it ({ width, left, top, height }; `height`
128
+ // 0: to be fitted and measured): all those standing in the margin, or the one shown.
129
+ const cards = new Map();
130
+ let standing = false; // the cards stand in the margin, else markers and the one card by its words
131
+ let quiet = false; // the form just changed: what comes stands at once, no fade
132
+ let laid = null; // what the margin was last laid out by: { front, left }
133
+ let moves = false; // the margin's cards were laid out once: from now on they move
134
+ let remeasure = true; // all of them to measure again (what may move them: the page's size, its structure), else those in `stale`
135
+ const stale = new Set(); // ids found again since they were measured
136
+ // The annotation whose card shows, or stands in front: { id, focus, hover, keys, located, side, begun,
137
+ // opening, within }: `keys`, opened by the keys, which give the caret back; `located`, shown by
138
+ // the workbench's list; `side`, by its words, "below" or "above" them, kept while it is written;
139
+ // `begun`, new; `opening`, opened for writing and not laid out yet in
140
+ // the margin; `within`, what it is laid out by there instead of its anchor, which is out of the window.
141
+ let shown = null;
142
+ let hot = null; // the id whose words are under the pointer
143
+ let started = false; // the page has drawn once: a marker that comes from then on fades in, those it starts with just stand
144
+ let resolved = false; // the views are found again for what the page shows now
145
+ let everything = true; // all of them to find again (else only those whose blocks `touched` holds)
146
+ const touched = new Set(); // nodes whose text changed since they were last found
147
+ let placed = null; // { side }: where the card was last drawn by words
148
+ let pointer = null; // { x, y, moved }: where the pointer last was over the page, and whether it moved to get there
149
+ let hovering = null; // the id a marker's hover is waiting to show
150
+ let hoverTimer = 0;
151
+ let leaveTimer = 0;
152
+ let saidError = false;
153
+ let marksChanged = true; // what the marks are drawn from changed since they were last drawn
154
+ let painted = null; // the annotation lit when they were
155
+ let heard = false; // the server said what there is
156
+ let roomAsked = null; // what the page's right side was last told (margin.mjs): whether there are any
157
+ let observer = null;
158
+ let resizer = null;
159
+ let watched = null; // the blocks in a row `resizer` looks at
160
+ let unmargin = null;
161
+ const fills = createFills({ kind: "note fill", className: "ge-note-halo", stands: () => Boolean(shown) || (standing && Boolean(hot)) });
162
+ const frames = task("annotate", { run: draw, fail: () => takeDown() });
163
+ const wake = () => frames.wake();
164
+
165
+ // The page's text changed: the anchors in what changed are found again (a block's id given or
166
+ // taken, a structure changed: all of them), and measured; words typed in a line move nothing else
167
+ // (their block's height changing says so: the page's, or that block's in a row, `resizer`).
168
+ function observed(records) {
169
+ let structure = false;
170
+ for (const record of records) {
171
+ if (record.type !== "characterData") remeasure = true;
172
+ if (record.type === "attributes") {
173
+ if (record.attributeName === "data-block-id") everything = true;
174
+ continue; // a disclosure opened, a tab shown: only where things are
175
+ }
176
+ if (record.type === "childList") structure = true;
177
+ touched.add(record.target);
178
+ }
179
+ if (structure) watchRows();
180
+ mutations++;
181
+ resolved = false;
182
+ wake();
183
+ }
184
+
185
+ // ---------- the server ----------
186
+ let chain = Promise.resolve();
187
+ const send = (body) => {
188
+ const run = chain.then(() => postAnswer(ROUTE, token, { slug, ...body }));
189
+ chain = run.catch(() => {});
190
+ return run;
191
+ };
192
+ const fetchAll = () => fetch(`${ROUTE}?slug=${encodeURIComponent(slug)}`, { headers: { "X-Gallery-Token": token }, cache: "no-store" })
193
+ .then((response) => response.json()).catch(() => ({ ok: false, reason: "offline" }));
194
+ const wire = (item) => ({ id: item.id, created: item.created, updated: item.updated, text: item.text, anchor: item.anchor });
195
+
196
+ // What the server says the file holds now: taken in, what the reader is typing kept as typed, what
197
+ // it made and has not saved yet kept, what a check deleted kept aside for an undo.
198
+ function take(answer) {
199
+ if (!answer?.ok) return false;
200
+ heard = true;
201
+ version = answer.version;
202
+ const dropped = new Set(answer.dropped ?? []);
203
+ filed = new Map((answer.items ?? []).map((item) => [item.id, item.text]));
204
+ const next = new Map();
205
+ for (const item of answer.items ?? []) {
206
+ const mine = items.get(item.id);
207
+ if (mine?.anchor && !sameWords(mine.anchor, item.anchor)) remember(item.id, mine.anchor);
208
+ next.set(item.id, mine && (timers.has(item.id) || shown?.id === item.id) ? { ...item, text: mine.text } : item);
209
+ lost.delete(item.id);
210
+ }
211
+ for (const [id, item] of items) {
212
+ if (next.has(id)) continue;
213
+ if (made.has(id) || sending.has(id) || timers.has(id) || (shown?.id === id && writing())) next.set(id, item);
214
+ else if (dropped.has(id)) lost.set(id, { item, at: mutations });
215
+ }
216
+ items = next;
217
+ resolved = false;
218
+ everything = true;
219
+ remeasure = true;
220
+ if (items.size > 0) followText();
221
+ wake();
222
+ changed();
223
+ return true;
224
+ }
225
+
226
+ // What the server holds read, what waits in the browser sent: once, unless it could not be read.
227
+ async function read() {
228
+ if (!take(await fetchAll())) {
229
+ reading = null;
230
+ return;
231
+ }
232
+ await sendStash();
233
+ if (items.size > 0 && !resolved) resolveAll();
234
+ counted = true;
235
+ changed();
236
+ note("annotate", "read");
237
+ }
238
+
239
+ // An anchor it had: found as it was again when an undo brings those words back.
240
+ function remember(id, anchor) {
241
+ if (!anchor || anchor.kind !== "text") return;
242
+ const list = history.get(id) ?? [];
243
+ if (list.some((other) => sameWords(other, anchor))) return;
244
+ list.push(anchor);
245
+ if (list.length > HISTORY) list.shift();
246
+ history.set(id, list);
247
+ }
248
+
249
+ function failed(answer, item) {
250
+ if (answer.reason === "offline") {
251
+ stash(item);
252
+ if (!saidError) say(t("批注没能保存:{0}", t("连不上本地服务")), "error");
253
+ } else if (!saidError) say(t("批注没能保存:{0}", failure(answer, answer.reason ?? "")), "error");
254
+ saidError = true;
255
+ }
256
+
257
+ // What could not be saved waits in the browser, sent the next time they are read (each once, as last
258
+ // typed, with when: the server keeps a newer text). A later save or delete of it that reaches the
259
+ // server takes it out, so an old text never comes back over a newer one.
260
+ const stash = (item) => {
261
+ const waiting = stored(stashKey, [], Array.isArray).filter((other) => other?.id !== item.id);
262
+ keep(stashKey, [...waiting, wire(item)]);
263
+ };
264
+ const unstash = (id) => {
265
+ const waiting = stored(stashKey, [], Array.isArray);
266
+ if (waiting.some((other) => other?.id === id)) keep(stashKey, waiting.filter((other) => other?.id !== id));
267
+ };
268
+ async function sendStash() {
269
+ const waiting = stored(stashKey, [], Array.isArray);
270
+ if (waiting.length === 0) return;
271
+ const answer = await send({ restore: waiting });
272
+ if (answer.ok) {
273
+ keep(stashKey, []);
274
+ take(answer);
275
+ }
276
+ }
277
+
278
+ // The anchor of an annotation's words or blocks as the page has them now, its blocks named (their
279
+ // ids, or those the next commit gives them).
280
+ function anchorOf(id, record = made.get(id)) {
281
+ const view = views.get(id);
282
+ const kind = record?.kind ?? items.get(id)?.anchor?.kind;
283
+ const units = (view?.units ?? record?.units ?? []).filter((unit) => unit.isConnected);
284
+ const blocks = units.map((unit) => idFor(unit)).filter(Boolean);
285
+ if (kind === "text") return { kind: "text", blocks, ...(view?.excerpt ?? record.excerpt) };
286
+ return { kind: "blocks", blocks, quote: record?.excerpt.quote ?? items.get(id)?.anchor?.quote ?? "" };
287
+ }
288
+
289
+ function schedule(id) {
290
+ clearTimeout(timers.get(id));
291
+ timers.set(id, setTimeout(() => save(id), SAVE_AFTER));
292
+ }
293
+
294
+ // Taken away in the file (its delete button, a new one taken back).
295
+ async function remove(id) {
296
+ const answer = await send({ remove: id });
297
+ if (answer.ok) {
298
+ unstash(id);
299
+ take(answer);
300
+ }
301
+ return answer;
302
+ }
303
+
304
+ // Save it now: made (its first save) or changed; never with no words (left so, it goes: close).
305
+ async function save(id) {
306
+ clearTimeout(timers.get(id));
307
+ timers.delete(id);
308
+ const item = items.get(id);
309
+ if (!item) return;
310
+ if (!item.text.trim()) return; // no words: nothing to save, the file keeps its last words
311
+ if (!item.anchor) {
312
+ if (!made.has(id)) return;
313
+ item.anchor = anchorOf(id);
314
+ }
315
+ sending.add(id);
316
+ const answer = await send({ put: wire(item) });
317
+ sending.delete(id);
318
+ if (!answer.ok) return failed(answer, item);
319
+ saidError = false;
320
+ unstash(id);
321
+ const record = made.get(id);
322
+ if (record) named.set(id, views.get(id)?.units ?? record.units);
323
+ made.delete(id);
324
+ take(answer);
325
+ }
326
+
327
+ // ---------- telling the server where the words went ----------
328
+
329
+ // The anchors that followed the reader's edit since the server last heard of them: { id: anchor }, or null.
330
+ function followed() {
331
+ if (items.size === 0) return null;
332
+ if (!resolved) resolveAll();
333
+ const out = {};
334
+ for (const [id, item] of items) {
335
+ if (made.has(id) || sending.has(id) || lost.has(id) || item.anchor?.kind !== "text") continue;
336
+ const view = views.get(id);
337
+ if (!view?.excerpt || view.unknown) continue;
338
+ const blocks = view.units.map((unit) => unit.getAttribute("data-block-id") ?? "");
339
+ if (sameWords({ kind: "text", blocks, ...view.excerpt }, item.anchor)) continue;
340
+ out[id] = anchorOf(id);
341
+ }
342
+ return Object.keys(out).length > 0 ? out : null;
343
+ }
344
+
345
+ /**
346
+ * Tell the server where annotations' words went with the reader's edits (autosave calls it right
347
+ * before a commit goes, so the check after that commit finds them as they are). Resolves once it
348
+ * has heard (SYNC_WAIT at most), or at once when nothing changed.
349
+ */
350
+ function sync({ keepalive = false } = {}) {
351
+ const anchors = followed();
352
+ if (!anchors) return null;
353
+ if (keepalive) {
354
+ postJson(ROUTE, token, { slug, anchors }, { keepalive: true }).catch(() => {});
355
+ return null;
356
+ }
357
+ const told = send({ anchors }).then((answer) => {
358
+ if (!answer.ok) return;
359
+ version = answer.version;
360
+ for (const [id, anchor] of Object.entries(anchors)) {
361
+ const item = items.get(id);
362
+ if (!item) continue;
363
+ remember(id, item.anchor);
364
+ item.anchor = answer.items?.find((each) => each.id === id)?.anchor ?? anchor;
365
+ }
366
+ note("annotate", "anchors told");
367
+ });
368
+ return Promise.race([told, new Promise((resolve) => setTimeout(resolve, SYNC_WAIT))]);
369
+ }
370
+
371
+ /** Save what is typed and not saved yet, now (Ctrl+S, the workbench's save, the page closing: `keepalive`). */
372
+ function flush({ keepalive = false } = {}) {
373
+ // What was typed, and one begun and not saved yet; one with no words saves nothing (save), and one
374
+ // the file has keeps its last words (the page going, no 撤销 could be offered for its going).
375
+ const ids = [...new Set([...timers.keys(), ...[...made.keys()].filter((id) => !sending.has(id))])];
376
+ if (keepalive) {
377
+ sync({ keepalive: true });
378
+ for (const id of ids) {
379
+ clearTimeout(timers.get(id));
380
+ timers.delete(id);
381
+ const item = items.get(id);
382
+ if (!item?.text.trim()) continue;
383
+ if (!item.anchor && made.has(id)) item.anchor = anchorOf(id);
384
+ postJson(ROUTE, token, { slug, put: wire(item) }, { keepalive: true }).catch(() => {});
385
+ }
386
+ return Promise.resolve();
387
+ }
388
+ return Promise.all([...ids.map(save), sync()]).then(() => chain);
389
+ }
390
+
391
+ // ---------- what can be annotated ----------
392
+
393
+ // In a detail card's dialog, where nothing is annotated (v1) and the margin is not the page's.
394
+ const inDialog = (node) => Boolean(node?.closest?.("dialog"));
395
+
396
+ // Text that can be: the body's, a header's lines but its title (v1: none on the title or the page as
397
+ // a whole), not a tab's name, nothing in a detail card's dialog (v1).
398
+ const annotatableText = (host) => host && host.closest("main") && host.getAttribute("data-ge-kind") !== "title" && !host.closest(".doc-title, .doc-sheet-tabs, dialog");
399
+
400
+ // Whether blocks are of what can be, looked at without reading their text: none in a detail card's
401
+ // dialog (v1), nor the whole page (Ctrl+A to the top, v1).
402
+ function annotatableBlocks(list) {
403
+ if (list.length === 0 || list.some(inDialog)) return false;
404
+ if (list.length > 1) {
405
+ const page = wholePage();
406
+ const set = new Set(list);
407
+ if (page.length > 1 && page.every((unit) => set.has(unit))) return false;
408
+ }
409
+ return list.some(annotatable);
410
+ }
411
+
412
+ // Whether a block has an annotation, of its words or of it whole (or one being written): it takes
413
+ // none of its own. Its marker is how to reach what is there; words in it can
414
+ // still be selected and annotated.
415
+ function annotated(unit) {
416
+ if (items.size === 0) return false;
417
+ if (!resolved) resolveAll();
418
+ for (const view of views.values()) if (view.units?.includes(unit)) return true;
419
+ return false;
420
+ }
421
+
422
+ // Whether these blocks can take an annotation, one for them all: blocks that can be, one of them
423
+ // at least with none yet.
424
+ const canBlocks = (list) => annotatableBlocks(list) && list.some((unit) => annotatable(unit) && !annotated(unit));
425
+
426
+ // What is selected now, as an anchor: { kind, units, range, excerpt }, or null where it can take
427
+ // none (white space alone, the title…).
428
+ function selected() {
429
+ const list = picked();
430
+ if (list.length > 0) return canBlocks(list) ? blockSelection(list) : null;
431
+ const texts = crossTexts();
432
+ if (texts?.length) {
433
+ if (texts.some(({ host }) => inDialog(host))) return null;
434
+ const range = document.createRange();
435
+ range.setStart(texts[0].range.startContainer, texts[0].range.startOffset);
436
+ range.setEnd(texts.at(-1).range.endContainer, texts.at(-1).range.endOffset);
437
+ return textSelection(range);
438
+ }
439
+ const selection = getSelection();
440
+ if (!selection?.rangeCount || selection.isCollapsed) return null;
441
+ const range = selection.getRangeAt(0);
442
+ const host = editable(range.commonAncestorContainer) ?? editable(range.startContainer);
443
+ return annotatableText(host) && host.contains(range.endContainer) ? textSelection(range.cloneRange()) : null;
444
+ }
445
+
446
+ // The block the caret is in, as an anchor, or null.
447
+ function caretBlock() {
448
+ const selection = getSelection();
449
+ const host = selection?.rangeCount ? editable(selection.anchorNode) : null;
450
+ const unit = annotatableText(host) ? unitElement(host) : null;
451
+ return unit && canBlocks([unit]) ? blockSelection([unit]) : null;
452
+ }
453
+
454
+ // The annotation whose marked words hold the caret, its id, or null.
455
+ function markedAtCaret() {
456
+ const selection = getSelection();
457
+ if (!selection?.rangeCount) return null;
458
+ for (const [id, view] of views) {
459
+ if (!view.folded && view.range?.isPointInRange(selection.anchorNode, selection.anchorOffset)) return id;
460
+ }
461
+ return null;
462
+ }
463
+
464
+ // ---------- writing ----------
465
+
466
+ // A card's field has the focus (a card being written); `shown.focus` follows it.
467
+ const focused = () => Boolean(document.activeElement?.closest?.(".ge-note-card"));
468
+ const writing = () => Boolean(shown) && focused();
469
+ // A new one with no words (made on the page, not saved yet, nothing typed but white space): no
470
+ // annotation yet: no marker, no count, no row; left so, it leaves nothing (close).
471
+ const drafted = (id) => made.has(id) && !(items.get(id)?.text ?? "").trim();
472
+
473
+ // The annotation already on `selection` (the same words or blocks), or undefined.
474
+ function sameAs(selection) {
475
+ if (!resolved) resolveAll();
476
+ return [...items.values()].find((item) => {
477
+ const view = views.get(item.id);
478
+ const quote = view?.excerpt?.quote ?? item.anchor?.quote;
479
+ return !lost.has(item.id) && view && item.anchor?.kind === selection.kind && squash(quote) === squash(selection.excerpt.quote)
480
+ && selection.units.every((unit) => view.units.includes(unit));
481
+ });
482
+ }
483
+
484
+ // An annotation started on `selection`: its words keep the hot mark, its card the field with the
485
+ // caret; on the words of one already there, its card. A card being written closes first. Blocks
486
+ // selected and text selected across blocks give way to it (the status line that names them goes
487
+ // with them). False when the field could not take the focus (show).
488
+ function begin(selection, { keys = false } = {}) {
489
+ const same = sameAs(selection);
490
+ unpick();
491
+ if (same) return show(same.id, { focus: true, keys });
492
+ if (shown?.focus || focused()) close();
493
+ const id = newAnnotationId();
494
+ made.set(id, { kind: selection.kind, units: selection.units, range: selection.range, excerpt: selection.excerpt });
495
+ const at = new Date().toISOString();
496
+ items.set(id, { id, created: at, updated: at, text: "", anchor: null });
497
+ shown = { id, focus: false, hover: false, begun: true };
498
+ resolved = false;
499
+ followText();
500
+ note("annotate", "begun");
501
+ return show(id, { focus: true, keys });
502
+ }
503
+
504
+ // An annotation's card, saying what the annotation says (what is being typed in it stays as it is).
505
+ function cardOf(id) {
506
+ const card = cards.get(id) ?? makeCard(id);
507
+ const text = items.get(id)?.text ?? "";
508
+ if (card.field.value !== text && document.activeElement !== card.field) {
509
+ card.field.value = text;
510
+ card.height = 0;
511
+ }
512
+ return card;
513
+ }
514
+
515
+ function makeCard(id) {
516
+ const card = { ...cardNode(), width: null, left: null, top: null, height: 0, tall: 0 };
517
+ const { node, field, erase: button } = card;
518
+ node.dataset.note = id;
519
+ cards.set(id, card);
520
+ field.addEventListener("input", (event) => {
521
+ const tall = fitField(node, field);
522
+ if (tall !== card.tall) {
523
+ card.tall = tall;
524
+ card.height = 0;
525
+ wake();
526
+ }
527
+ if (!event.isComposing) typed(id);
528
+ });
529
+ field.addEventListener("compositionend", () => typed(id));
530
+ field.addEventListener("scroll", () => fitField(node, field), { passive: true });
531
+ field.addEventListener("focus", () => focusIn(id));
532
+ // A press elsewhere closes it, in the page or in the workbench (Chrome takes the focus from the field).
533
+ field.addEventListener("blur", () => setTimeout(() => { if (!focused()) close(); }, 0));
534
+ field.addEventListener("keydown", (event) => {
535
+ if (event.isComposing || event.keyCode === 229) return;
536
+ const mod = event.ctrlKey || event.metaKey;
537
+ const closing = (event.key === "Enter" && !event.shiftKey && !mod && !event.altKey) || event.key === "Escape" || (event.key === "Tab" && !mod && !event.altKey)
538
+ || (mod && event.altKey && !event.shiftKey && event.code === "KeyM"); // the keys that opened it close it
539
+ if (closing) {
540
+ event.preventDefault();
541
+ event.stopPropagation();
542
+ backToText();
543
+ }
544
+ });
545
+ // A press in a card edits it, the caret where pressed (at the end of its text for a press beside the
546
+ // field), what it is about brought into the window. A press on its delete button moves nothing: the
547
+ // card stays as it is until the click.
548
+ node.addEventListener("pointerdown", (event) => {
549
+ if (event.button !== 0 || button.contains(event.target)) return;
550
+ if (standing && shown?.id !== id) bringAnchor(id);
551
+ if (event.target !== field) {
552
+ event.preventDefault();
553
+ field.focus({ preventScroll: true });
554
+ field.setSelectionRange(field.value.length, field.value.length);
555
+ }
556
+ });
557
+ button.addEventListener("pointerdown", (event) => event.preventDefault());
558
+ button.addEventListener("click", () => erase(id));
559
+ // Under the pointer a card in the margin lights its words or blocks; one shown by hover stays.
560
+ node.addEventListener("pointerenter", () => {
561
+ clearTimeout(leaveTimer);
562
+ if (standing) lightUp(id);
563
+ });
564
+ node.addEventListener("pointerleave", () => {
565
+ if (standing) lightUp(null);
566
+ else leaveSoon();
567
+ });
568
+ return card;
569
+ }
570
+
571
+ // The annotation under the pointer in the margin.
572
+ function lightUp(id) {
573
+ hot = id;
574
+ paintMarks();
575
+ wake();
576
+ }
577
+
578
+ // What a card pressed is about in the window, where it is out of it (notion-lite's locate: 64px in).
579
+ function bringAnchor(id) {
580
+ const line = views.has(id) ? lineOf(views.get(id)) : null;
581
+ const height = document.documentElement.clientHeight;
582
+ if (line && (line.top < REVEAL || line.top > height - REVEAL)) scrollBy({ top: line.top - REVEAL, behavior: "smooth" });
583
+ }
584
+
585
+ // A field took the focus: its card is the one written, whichever it was.
586
+ function focusIn(id) {
587
+ if (shown && shown.id !== id) close();
588
+ shown ??= { id, focus: false, hover: false };
589
+ shown.focus = true;
590
+ shown.hover = false;
591
+ shown.side ??= placed?.side ?? null; // by its words, on the side it stood when pressed (growing as it is written, it never jumps over them)
592
+ wake();
593
+ }
594
+
595
+ // Its card shown; with `focus`, for writing (the caret at its end). False when the field could not
596
+ // take the focus (a modal dialog over the page): an annotation just begun then goes as it came. The
597
+ // card being written closes first (saved), the field keeping the focus.
598
+ function show(id, { focus = false, hover = false, keys = false } = {}) {
599
+ const item = items.get(id);
600
+ if (!item) return false;
601
+ clearTimeout(leaveTimer);
602
+ clearTimeout(hoverTimer);
603
+ hovering = null;
604
+ if (shown && shown.id !== id && (shown.focus || focused())) close();
605
+ const kept = shown?.id === id ? shown : null;
606
+ shown = { ...kept, id, focus: kept ? kept.focus || focused() : false, hover: kept ? kept.hover && hover : hover, keys: keys || Boolean(kept?.keys) };
607
+ if (focus) shown.opening = true;
608
+ const card = cardOf(id);
609
+ draw();
610
+ if (!focus) return true;
611
+ card.field.focus({ preventScroll: true });
612
+ if (document.activeElement !== card.field) {
613
+ if (drafted(id)) {
614
+ items.delete(id);
615
+ made.delete(id);
616
+ dropView(id);
617
+ shown = null;
618
+ wake();
619
+ }
620
+ return false;
621
+ }
622
+ shown.focus = true; // as the focus event says, also when the field had it already (another card was written)
623
+ shown.hover = false;
624
+ const end = card.field.value.length;
625
+ card.field.setSelectionRange(end, end);
626
+ wake();
627
+ return true;
628
+ }
629
+
630
+ function hide() {
631
+ if (!shown || shown.focus || focused()) return;
632
+ shown = null;
633
+ wake();
634
+ }
635
+
636
+ // What 撤销 puts back of one going at the reader's word (its delete button, emptied and closed): as
637
+ // it is, or, emptied in its card, with the words the file last held; null when the file never had it.
638
+ function backOf(id) {
639
+ const item = items.get(id);
640
+ if (!item?.anchor) return null;
641
+ const text = item.text.trim() ? item.text : filed.get(id);
642
+ return text ? { ...wire(item), text } : null;
643
+ }
644
+
645
+ // An annotation gone at the reader's word, from the page and the file (one never saved leaves nothing).
646
+ function forget(id) {
647
+ const item = items.get(id);
648
+ if (!item) return;
649
+ items.delete(id);
650
+ clearTimeout(timers.get(id));
651
+ timers.delete(id);
652
+ if (!made.has(id) || item.anchor) remove(id);
653
+ made.delete(id);
654
+ unstash(id);
655
+ dropView(id);
656
+ }
657
+
658
+ // The card closes into its marker: what was typed saved at once. One left with no words is none: it
659
+ // goes, from the page and the file, and one the file had is offered back as its delete button's is
660
+ // (`removed`, with its last words). `drop`: it goes whatever it says (its delete button, which offers
661
+ // it back itself). With room it goes back among the others.
662
+ function close({ drop = false } = {}) {
663
+ if (!shown) return;
664
+ const { id } = shown;
665
+ shown = null;
666
+ const item = items.get(id);
667
+ if (item && (drop || !item.text.trim())) {
668
+ const back = drop ? null : backOf(id);
669
+ forget(id);
670
+ if (back) removed([back]);
671
+ } else if (item && (timers.has(id) || !item.anchor)) save(id);
672
+ changed();
673
+ wake();
674
+ }
675
+
676
+ // A card's delete button: its annotation goes at once, and the workbench offers it back (emptied in
677
+ // its card, with the words the file last held). One never saved leaves nothing to bring back.
678
+ function erase(id) {
679
+ const item = items.get(id);
680
+ if (!item) return;
681
+ const was = backOf(id);
682
+ if (shown?.id !== id) {
683
+ forget(id);
684
+ changed();
685
+ wake();
686
+ } else if (focused()) backToText({ drop: true });
687
+ else close({ drop: true });
688
+ if (was) removed([was]);
689
+ }
690
+
691
+ // Esc, Enter, Tab, Ctrl+Alt+M: the caret back in the page, at the end of the words, collapsed, or at
692
+ // the end of the last block where the keys opened the card; of blocks the pointer opened it on there
693
+ // was no caret to give back, and the page stays where it is.
694
+ function backToText({ drop = false } = {}) {
695
+ const view = views.get(shown?.id);
696
+ const range = view?.range;
697
+ const host = range ? editable(range.endContainer) : null;
698
+ if (range && host) {
699
+ host.focus({ preventScroll: true });
700
+ const caret = range.cloneRange();
701
+ caret.collapse(false);
702
+ select(caret);
703
+ } else if (shown?.keys && view?.units?.length) {
704
+ const last = view.units.at(-1);
705
+ const text = isHost(last) ? last : firstHost(last);
706
+ if (text) caretAt(text, "end");
707
+ }
708
+ if (focused()) document.activeElement.blur();
709
+ close({ drop }); // whether or not the field heard its blur (a window without the system's focus)
710
+ }
711
+
712
+ // What was typed: kept and saved soon; not while an input method composes (its letters are no
713
+ // words yet), but when it ends.
714
+ function typed(id) {
715
+ const field = cards.get(id)?.field;
716
+ const item = items.get(id);
717
+ if (!field || !item || item.text === field.value) return;
718
+ const was = drafted(id);
719
+ item.text = field.value;
720
+ item.updated = new Date().toISOString();
721
+ schedule(id);
722
+ changed();
723
+ if (!standing || was !== drafted(id)) wake();
724
+ }
725
+
726
+ // Whether the pointer is over `box` (window coordinates), as last seen.
727
+ const under = (box) => Boolean(pointer && box) && pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom;
728
+ // How far the pointer is from the card (Infinity when it is not over the page, or no card shows).
729
+ function fromCard() {
730
+ const node = cards.get(shown?.id)?.node;
731
+ if (!pointer || !node?.isConnected) return Infinity;
732
+ const box = node.getBoundingClientRect();
733
+ return Math.hypot(Math.max(box.left - pointer.x, 0, pointer.x - box.right), Math.max(box.top - pointer.y, 0, pointer.y - box.bottom));
734
+ }
735
+
736
+ // The card shown by hover goes once the pointer has left it and its marker for HOVER_OUT: not while
737
+ // it is over either again (what a still pointer's events say after the card was drawn), nor while it
738
+ // is on its way from the marker to the card (nearer to it than when last looked: the marker stands
739
+ // in the gutter, the card by its words).
740
+ function leaveSoon() {
741
+ clearTimeout(hoverTimer);
742
+ clearTimeout(leaveTimer);
743
+ hovering = null;
744
+ if (!shown?.hover || shown.focus || focused()) return;
745
+ let was = fromCard();
746
+ leaveTimer = setTimeout(function away() {
747
+ if (!shown?.hover || shown.focus || focused()) return;
748
+ const marker = views.get(shown.id)?.marker;
749
+ const node = cards.get(shown.id)?.node;
750
+ const now = fromCard();
751
+ if ((node?.isConnected && under(node.getBoundingClientRect())) || (marker?.isConnected && under(marker.getBoundingClientRect())) || now < was - 2) {
752
+ was = now;
753
+ leaveTimer = setTimeout(away, HOVER_OUT);
754
+ return;
755
+ }
756
+ hide();
757
+ }, HOVER_OUT);
758
+ }
759
+
760
+ // ---------- markers ----------
761
+ function markerOf(id) {
762
+ const view = views.get(id);
763
+ if (view.marker) return view.marker;
764
+ const node = own(markerNode(id), "note marker", { owner: null, alive: () => views.has(id) });
765
+ node.addEventListener("pointerenter", () => {
766
+ clearTimeout(leaveTimer);
767
+ hot = id;
768
+ paintMarks();
769
+ });
770
+ // Its card shows once the pointer rests on it, and only for a pointer that moved there: a marker
771
+ // that came under a still pointer (the margin drawn again) opens nothing. Resting on another while
772
+ // a card shows by hover switches at once. Never while a card is being written.
773
+ node.addEventListener("pointermove", () => {
774
+ if (!pointer?.moved) return;
775
+ clearTimeout(leaveTimer);
776
+ if (shown?.focus || focused() || shown?.id === id || hovering === id) return;
777
+ clearTimeout(hoverTimer);
778
+ if (shown?.hover) {
779
+ show(id, { hover: true });
780
+ return;
781
+ }
782
+ hovering = id;
783
+ hoverTimer = setTimeout(() => {
784
+ hovering = null;
785
+ if (!shown?.focus && !focused()) show(id, { hover: true });
786
+ }, HOVER_IN);
787
+ });
788
+ node.addEventListener("pointerleave", () => {
789
+ if (hot === id) hot = null;
790
+ paintMarks();
791
+ leaveSoon();
792
+ });
793
+ node.addEventListener("pointerdown", (event) => event.preventDefault()); // the selection stays where it is
794
+ node.addEventListener("click", () => show(id, { focus: true }));
795
+ view.marker = node;
796
+ return node;
797
+ }
798
+
799
+ // An annotation no longer on the page: its marker, bar and card go at once.
800
+ function dropView(id) {
801
+ const view = views.get(id);
802
+ view?.marker?.remove();
803
+ view?.bar?.remove();
804
+ views.delete(id);
805
+ cards.get(id)?.node.remove();
806
+ cards.delete(id);
807
+ laid = null;
808
+ marksChanged = true;
809
+ }
810
+
811
+ // ---------- the workbench's list ----------
812
+
813
+ // Where a view's words or blocks start, as a collapsed range: what page order compares.
814
+ function startOf(view) {
815
+ const at = document.createRange();
816
+ if (view.range?.startContainer.isConnected) at.setStart(view.range.startContainer, view.range.startOffset);
817
+ else at.setStartBefore(view.units.find((unit) => unit.isConnected));
818
+ at.collapse(true);
819
+ return at;
820
+ }
821
+
822
+ // One row per annotation the page shows, in page order: what it says, and its words (a block
823
+ // annotation's, its blocks' as they read now).
824
+ function rows() {
825
+ if (!resolved) resolveAll();
826
+ const listed = [...items.values()].filter((item) => views.has(item.id) && !drafted(item.id))
827
+ .map((item) => ({ item, view: views.get(item.id), at: startOf(views.get(item.id)) }))
828
+ .sort((a, b) => a.at.compareBoundaryPoints(Range.START_TO_START, b.at));
829
+ return listed.map(({ item, view }) => {
830
+ const words = view.range || view.unknown ? view.excerpt?.quote ?? item.anchor?.quote : blockSelection(view.units)?.excerpt.quote;
831
+ return { id: item.id, text: item.text, quote: tidy(words) };
832
+ });
833
+ }
834
+
835
+ // An annotation brought to the reader: what hides it opened, the page scrolled to it (where it is out
836
+ // of the window: to its upper middle, as search's hit), its card in front in the margin until a press
837
+ // elsewhere; with no room, by its words, without the focus, and gone with them when they leave the
838
+ // window (draw): the pointer never came to it to leave it. False where no card stands for it (in a
839
+ // detail card's dialog, which is opened).
840
+ function goTo(id) {
841
+ if (!resolved) resolveAll();
842
+ const view = views.get(id);
843
+ if (!view || drafted(id)) return false;
844
+ const start = view.range?.startContainer ?? view.units[0];
845
+ reveal(start.nodeType === 1 ? start : start.parentElement); // a folded heading's section too (fold/)
846
+ remeasure = true; // what was hidden stands now, before the observer says so
847
+ const line = lineOf(view);
848
+ if (!line) return false;
849
+ if (line.top < TOP_ROOM || line.bottom > innerHeight - EDGE) scrollTo({ top: Math.max(0, scrollY + line.top - innerHeight * 0.35), behavior: "instant" });
850
+ if (!show(id, { hover: !standing }) || !shown) return false;
851
+ shown.located = true;
852
+ return true;
853
+ }
854
+
855
+ // ---------- finding and drawing ----------
856
+
857
+ // Where an annotation's words or blocks are on the page now: { range, units, excerpt } (range null
858
+ // for blocks), `unknown` when it cannot be told, null when they are gone. Words: as they are, by
859
+ // what the page last found them as, by what the file says, by what they were earlier this visit (an
860
+ // undo); else where the reader's edit took them (their live range: typed into, partly deleted); else
861
+ // as the server finds them (edited in part where they stood, or once elsewhere). `live`: the live
862
+ // range may be used (not for what a check deleted: its words came back or they did not).
863
+ function find(id, item, view, { live = true } = {}) {
864
+ const record = made.get(id);
865
+ const kind = item.anchor?.kind ?? record?.kind;
866
+ if (kind === "blocks") {
867
+ if (item.anchor) return locate(item.anchor, named.get(id) ?? null);
868
+ const units = record.units.filter((unit) => unit.isConnected);
869
+ return units.length > 0 ? { range: null, units } : null;
870
+ }
871
+ const candidates = [];
872
+ if (view?.excerpt && view.units?.some((unit) => unit.isConnected)) candidates.push([{ kind: "text", blocks: [], ...view.excerpt }, view.units]);
873
+ if (item.anchor) candidates.push([item.anchor, named.get(id) ?? null]);
874
+ for (const anchor of [...(history.get(id) ?? [])].reverse()) candidates.push([anchor, null]);
875
+ for (const [anchor, elements] of candidates) {
876
+ const found = locate(anchor, elements, { exact: true });
877
+ const words = found?.range && derive(found.range);
878
+ if (words) return words;
879
+ }
880
+ const range = live ? view?.range ?? record?.range : null;
881
+ const words = range && derive(range);
882
+ if (words) return words;
883
+ if (!item.anchor) return null;
884
+ const found = locate(item.anchor, named.get(id) ?? null);
885
+ if (!found || found.unknown) return found;
886
+ return derive(found.range);
887
+ }
888
+
889
+ // A range of words as the page reads an anchor: its blocks, the words trimmed, what they say now.
890
+ function derive(range) {
891
+ const tight = tighten(range);
892
+ const words = tight && textSelection(tight);
893
+ return words && { range: words.range, units: words.units, excerpt: words.excerpt };
894
+ }
895
+
896
+ // Where each annotation is on the page now (those whose blocks the page's changes touched, or all);
897
+ // what no longer is goes from it (a check deletes it in the file); what a check deleted that is
898
+ // here again (an undo) is saved again, as it was.
899
+ function resolveAll() {
900
+ resolved = true;
901
+ const all = everything;
902
+ const changedNodes = [...touched];
903
+ everything = false;
904
+ touched.clear();
905
+ const affected = (view) => !view.units?.length || view.unknown || view.units.some((unit) => !unit.isConnected
906
+ || changedNodes.some((node) => node === unit || unit.contains(node) || node.contains(unit)))
907
+ || (view.range && changedNodes.some((node) => view.range.intersectsNode(node)));
908
+ for (const [id, item] of items) {
909
+ const known = views.get(id);
910
+ if (!all && known && !made.has(id) && !named.has(id) && !affected(known)) continue; // its blocks untouched: where it was
911
+ const found = find(id, item, known);
912
+ // Its blocks carry the ids it was saved with: found by them from now on.
913
+ if (named.has(id) && item.anchor?.blocks.every((block, index) => named.get(id)[index]?.getAttribute("data-block-id") === block)) named.delete(id);
914
+ const view = known ?? {};
915
+ if (!found || (found.units?.length ?? 0) === 0) {
916
+ // A new one with no words, not saved yet, is about nothing now: it goes with them, for no save to keep.
917
+ if (drafted(id) && !item.anchor) {
918
+ forget(id);
919
+ if (shown?.id === id) shown = null;
920
+ continue;
921
+ }
922
+ view.marker?.remove();
923
+ view.bar?.remove();
924
+ views.delete(id);
925
+ marksChanged = true;
926
+ if (!goneAt.has(id)) goneAt.set(id, mutations);
927
+ continue;
928
+ }
929
+ if (found.unknown) views.set(id, { ...view, units: found.units, unknown: true });
930
+ else views.set(id, { ...view, range: found.range, units: found.units, excerpt: found.excerpt ?? null, unknown: false });
931
+ stale.add(id);
932
+ marksChanged = true;
933
+ }
934
+ for (const [id, view] of views) {
935
+ if (items.has(id)) continue;
936
+ view.marker?.remove();
937
+ view.bar?.remove();
938
+ views.delete(id);
939
+ marksChanged = true;
940
+ }
941
+ // Deleted by a check once its words left the page, and they are here again (an undo, before or after
942
+ // the check's word came): it comes back as it was. One whose words the page never lost (an Agent's
943
+ // write, shown once the page is shown again) does not.
944
+ for (const [id, { item }] of lost) {
945
+ if (!goneAt.has(id) || !find(id, item, null, { live: false })) continue;
946
+ lost.delete(id);
947
+ items.set(id, item);
948
+ send({ put: wire(item) }).then((answer) => answer.ok && take(answer));
949
+ resolved = false;
950
+ everything = true;
951
+ note("annotate", "back with its words");
952
+ }
953
+ changed();
954
+ }
955
+
956
+ function ensureLayer() {
957
+ if (!layer?.isConnected) {
958
+ addStyle(STYLE);
959
+ layer = layerNode();
960
+ document.body.append(layer);
961
+ }
962
+ followPage(true);
963
+ return layer;
964
+ }
965
+
966
+ // The outermost closed disclosure `node` is hidden in (in its content, not its title, nor `node`
967
+ // itself: it shows, as its title), or the folded heading whose section hides it (fold/: the
968
+ // outermost, the heading its own title), whichever holds the other; null where neither hides it.
969
+ function closedAround(node) {
970
+ const element = node?.nodeType === 1 ? node : node?.parentElement;
971
+ let found = null;
972
+ for (let details = element?.closest("details:not([open])"); details; details = details.parentElement?.closest("details:not([open])")) {
973
+ if (details !== node && !details.querySelector(":scope > summary")?.contains(element)) found = details;
974
+ }
975
+ let marked = null;
976
+ for (let at = element?.closest("[data-ge-folded]"); at; at = at.parentElement?.closest("[data-ge-folded]")) marked = at;
977
+ const heading = marked && document.querySelector(`[data-ge-id="${CSS.escape(marked.getAttribute("data-ge-folded"))}"]`);
978
+ if (!heading) return found;
979
+ return found && found.contains(heading) ? found : heading;
980
+ }
981
+
982
+ // What stands for a closed disclosure or a folded section (closedAround): the disclosure's title, or
983
+ // the heading itself, which has none.
984
+ const titleOf = (closed) => closed.querySelector(":scope > summary") ?? closed;
985
+
986
+ // The first line of `node` (window coordinates): { top, height, left }: of its first words; where a
987
+ // picture standing as a block comes before them, the picture's top (as the handle's first line,
988
+ // handle/: not its caption, far from there); its box's top where it holds neither.
989
+ function firstLine(node) {
990
+ const first = (each) => (each.nodeType === 3
991
+ ? /\S/.test(each.data) && !each.parentElement?.closest("[data-doc-dev], [aria-hidden='true']")
992
+ : standsAsPicture(each));
993
+ const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, { acceptNode: (each) => (first(each) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP) });
994
+ let top = node;
995
+ for (let text; (text = walker.nextNode());) {
996
+ if (text.nodeType === 1) {
997
+ top = text;
998
+ break;
999
+ }
1000
+ const at = text.data.search(/\S/);
1001
+ const range = document.createRange();
1002
+ range.setStart(text, at);
1003
+ range.setEnd(text, at + 1);
1004
+ const rect = [...range.getClientRects()].find((each) => each.width > 0 && each.height > 0);
1005
+ if (rect) return { top: rect.top, height: rect.height, left: rect.left };
1006
+ }
1007
+ const rect = top.getBoundingClientRect();
1008
+ const lineHeight = parseFloat(getComputedStyle(node).lineHeight) || 26;
1009
+ const height = Math.min(lineHeight, rect.height || lineHeight);
1010
+ return { top: rect.top, height, left: rect.left };
1011
+ }
1012
+
1013
+ // The line a view's marker stands by (window coordinates): { top, height, left, bottom (of all it
1014
+ // holds), box (what it stands for as shown) }, or null when it does not show: its first line, or all
1015
+ // of them for several blocks, from the first's top to the last's bottom, the marker at their middle.
1016
+ // In a closed disclosure (laid out though hidden), its title's line: checked first, as the hidden
1017
+ // text still has boxes; in a folded heading's section (fold/), the heading's.
1018
+ function lineOf(view) {
1019
+ const start = view.range?.startContainer ?? view.units?.find((unit) => unit.isConnected);
1020
+ if (!start?.isConnected) return null;
1021
+ const closed = closedAround(start);
1022
+ if (closed) {
1023
+ const summary = titleOf(closed);
1024
+ if (!summary?.getClientRects().length) return null;
1025
+ const box = summary.getBoundingClientRect();
1026
+ return { ...firstLine(summary), folded: true, box, bottom: box.bottom };
1027
+ }
1028
+ if (view.range) {
1029
+ const rects = [...view.range.getClientRects()].filter((each) => each.width > 0 && each.height > 0);
1030
+ const rect = rects[0];
1031
+ return rect ? { top: rect.top, height: rect.height, left: rect.left, bottom: Math.max(...rects.map((each) => each.bottom)) } : null;
1032
+ }
1033
+ const units = view.units.filter((unit) => unit.isConnected && unit.getClientRects().length);
1034
+ if (units.length === 0) return null;
1035
+ const first = units[0].getBoundingClientRect();
1036
+ const last = units.at(-1).getBoundingClientRect();
1037
+ const left = Math.min(...units.map((unit) => unit.getBoundingClientRect().left));
1038
+ const right = Math.max(...units.map((unit) => unit.getBoundingClientRect().right));
1039
+ const line = units.length > 1 ? { top: first.top, height: last.bottom - first.top, left } : firstLine(units[0]);
1040
+ return { ...line, box: { top: first.top, bottom: last.bottom, left, right }, bottom: last.bottom };
1041
+ }
1042
+
1043
+ // The right edge of the column (of 分栏) a view's words stand in, or the page's: annotations in one
1044
+ // row stand the column further right first, the one beside the margin.
1045
+ function columnRight(view) {
1046
+ const start = view.range?.startContainer ?? view.units?.[0];
1047
+ const element = start?.nodeType === 1 ? start : start?.parentElement;
1048
+ const column = element?.closest(".doc-column");
1049
+ return column ? column.getBoundingClientRect().right : Infinity;
1050
+ }
1051
+
1052
+ const highlights = () => globalThis.CSS?.highlights && globalThis.Highlight;
1053
+
1054
+ // Whether a view's line (lineOf) shows it: what its mark goes by.
1055
+ function seen(view, line) {
1056
+ if (view.hidden !== !line || view.folded !== Boolean(line?.folded)) marksChanged = true;
1057
+ view.hidden = !line;
1058
+ view.folded = Boolean(line?.folded);
1059
+ }
1060
+
1061
+ // The marks, drawn again only when something of them changed: a Highlight made anew repaints every
1062
+ // mark on the page.
1063
+ function paintMarks() {
1064
+ if (!highlights()) return;
1065
+ const hotId = shown?.id ?? hot;
1066
+ if (!marksChanged && painted === hotId) return;
1067
+ marksChanged = false;
1068
+ painted = hotId;
1069
+ const plain = [];
1070
+ const strong = [];
1071
+ for (const [id, view] of views) {
1072
+ if (!view.range || view.hidden || view.folded) continue;
1073
+ (id === hotId ? strong : plain).push(view.range);
1074
+ }
1075
+ if (shown?.id && !views.has(shown.id) && made.get(shown.id)?.range) strong.push(made.get(shown.id).range);
1076
+ CSS.highlights.set(MARK, new Highlight(...plain));
1077
+ CSS.highlights.set(HOT, new Highlight(...strong));
1078
+ for (const [id, view] of views) {
1079
+ view.marker?.toggleAttribute("data-hot", id === hotId);
1080
+ view.bar?.toggleAttribute("data-hot", id === shown?.id || id === hot);
1081
+ }
1082
+ }
1083
+
1084
+ function takeDown() {
1085
+ layer?.remove();
1086
+ layer = null;
1087
+ standing = false;
1088
+ laid = null;
1089
+ moves = false;
1090
+ cards.clear();
1091
+ fills.clear();
1092
+ followPage(false);
1093
+ marksChanged = true;
1094
+ for (const view of views.values()) { view.marker = null; view.bar = null; }
1095
+ if (highlights()) {
1096
+ CSS.highlights.delete(MARK);
1097
+ CSS.highlights.delete(HOT);
1098
+ }
1099
+ }
1100
+
1101
+ // What a card stands by (window coordinates), or null when it does not show: words, their box and
1102
+ // where their last line ends ({ words, top, left, right: the last character's, bottom: the last
1103
+ // line's }); blocks, theirs ({ top, bottom and right: the last one's }); in a closed disclosure (laid
1104
+ // out though hidden), its title as a block; in a folded heading's section, the heading.
1105
+ function anchorBox(target) {
1106
+ const start = target.range?.startContainer ?? target.units?.find((unit) => unit.isConnected);
1107
+ if (!start?.isConnected) return null;
1108
+ const closed = closedAround(start);
1109
+ if (closed) {
1110
+ const summary = titleOf(closed);
1111
+ if (!summary?.getClientRects().length) return null;
1112
+ const box = summary.getBoundingClientRect();
1113
+ return { top: box.top, bottom: box.bottom, right: box.right };
1114
+ }
1115
+ if (target.range) {
1116
+ const rects = [...target.range.getClientRects()].filter((each) => each.width > 0 && each.height > 0);
1117
+ if (rects.length === 0) return null;
1118
+ const box = target.range.getBoundingClientRect();
1119
+ return { words: true, top: box.top, left: box.left, right: rects.at(-1).right, bottom: rects.at(-1).bottom };
1120
+ }
1121
+ const units = target.units.filter((unit) => unit.isConnected && unit.getClientRects().length);
1122
+ if (units.length === 0) return null;
1123
+ const last = units.at(-1).getBoundingClientRect();
1124
+ return { top: units[0].getBoundingClientRect().top, bottom: last.bottom, right: last.right };
1125
+ }
1126
+
1127
+ // Where the card of `size` stands by `box` (anchorBox), inside the column and the window (window
1128
+ // coordinates): words have it where the format bar would stand (placeBox: below their last line, its
1129
+ // left by their last character, above them where the window has no room below); blocks at the last
1130
+ // one's bottom right, or at the window's bottom where that is below it (the visible part of a long
1131
+ // block; above the notices there, the selection's line), or above them where even their top is too
1132
+ // low for it. `side` ("below": by the words' last line, the blocks' bottom or the window's; "above"),
1133
+ // the side it stood on when pressed, is kept while it is written: a card growing with its text grows
1134
+ // from where it stands and never jumps over its words or blocks, held in the window (the caret in
1135
+ // it: typing never scrolls the page).
1136
+ function placeCard(box, size, column, side) {
1137
+ const height = document.documentElement.clientHeight || innerHeight;
1138
+ const inColumn = (left) => Math.max(column.left, Math.min(left, column.right - size.width));
1139
+ const inWindow = (top, room) => Math.max(EDGE, Math.min(top, height - room - size.height));
1140
+ if (box.words) {
1141
+ const origin = { left: box.left, right: box.right, top: box.top, bottom: box.bottom, width: box.right - box.left, height: box.bottom - box.top };
1142
+ const at = placeBox(origin, size, { x: box.right + GAP_X, gap: GAP_Y, margin: EDGE });
1143
+ const top = side === "below" ? inWindow(box.bottom + GAP_Y, EDGE) : side === "above" ? inWindow(box.top - GAP_Y - size.height, EDGE) : at.top;
1144
+ return { left: inColumn(at.left), top, side: side ?? (top >= box.bottom ? "below" : "above") };
1145
+ }
1146
+ const floor = height - NOTICE_ROOM - size.height; // the lowest its top goes in the window
1147
+ const chosen = side ?? (box.top <= floor ? "below" : "above");
1148
+ const top = chosen === "below" ? box.bottom + GAP_Y : box.top - GAP_Y - size.height;
1149
+ return { left: inColumn(Math.min(box.right, column.right) - size.width), top: inWindow(top, NOTICE_ROOM), side: chosen };
1150
+ }
1151
+
1152
+ const pageColumn = () => document.querySelector("main.doc-page") ?? document.querySelector("main");
1153
+ const windowWidth = () => document.documentElement.clientWidth || innerWidth;
1154
+ // The lowest a marker reaches, the page's end being `bottom` (its content's, or the window's when that
1155
+ // is shorter).
1156
+ const pageEnd = (bottom) => Math.max(bottom, document.documentElement.clientHeight) - END_ROOM;
1157
+
1158
+ // Whether a marker stands on the 24px square at `spot` (window coordinates).
1159
+ const markerOn = (spot) => [...views.values()].some((view) => {
1160
+ const box = view.marker?.isConnected ? view.marker.getBoundingClientRect() : null;
1161
+ return Boolean(box) && box.left < spot.left + RAIL.marker && box.right > spot.left && box.top < spot.top + RAIL.marker && box.bottom > spot.top;
1162
+ });
1163
+
1164
+ // Where across the window a marker of `units` stands, the column's right edge being `right`: with any
1165
+ // of them in a section, past its strip, as far out as the pointer finds that (handle-sizes.mjs
1166
+ // BAR.reach), so that each keeps the pointer it is for, and within the column's right room (margin.mjs).
1167
+ function markerAcross(right, units) {
1168
+ const section = units.map((unit) => sectionOps()?.sectionOf(unit)).find(Boolean);
1169
+ return markerLeft(right, windowWidth(), section ? Math.min(section.getBoundingClientRect().right + BAR.reach, right + rightRoom() - RAIL.marker) : 0);
1170
+ }
1171
+
1172
+ // Where a marker of `unit` alone stands (window coordinates): the place of the button beside it
1173
+ // (handle/), which writing leaves a marker on, to the pixel. Null where the block does not show, or
1174
+ // a marker stands there already (stacked down from the block above, a closed disclosure's on its
1175
+ // title, a section's on its heading): the pointer there would be that marker's, so the block has no
1176
+ // button and keeps its handle and the shortcut; or while a card stands in front in the margin, 20px
1177
+ // left of its place at rest (notion-lite's canComment: none while a discussion is selected).
1178
+ function markerSpot(unit) {
1179
+ if (standing && shown) return null;
1180
+ const page = pageColumn();
1181
+ const line = page && lineOf({ range: null, units: [unit] });
1182
+ if (!line) return null;
1183
+ const { top: origin, bottom } = document.documentElement.getBoundingClientRect();
1184
+ const [top] = stackItems([{ ideal: markerTop(line) - origin, height: RAIL.marker, marker: true, active: false }], { top: TOP_ROOM, bottom: pageEnd(bottom - origin) });
1185
+ const spot = { left: markerAcross(page.getBoundingClientRect().right, [unit]), top: top + origin };
1186
+ return markerOn(spot) ? null : spot;
1187
+ }
1188
+
1189
+ // The right side of the page told whether it has any: until the server has said, what the page's
1190
+ // head was told stands.
1191
+ function askRoom() {
1192
+ const wanted = items.size > 0;
1193
+ if ((!heard && !wanted) || wanted === roomAsked) return;
1194
+ roomAsked = wanted;
1195
+ setMargin({ notes: wanted });
1196
+ }
1197
+
1198
+ // A block anchor's bar beside its blocks (beside a closed disclosure's title when they are in it),
1199
+ // `left` across the layer, `origin` the layer's top in the window.
1200
+ function placeBar(id, view, line, left, origin) {
1201
+ if (!line || view.range) {
1202
+ view.bar?.remove();
1203
+ return;
1204
+ }
1205
+ view.bar ??= own(barNode(), "note bar", { alive: () => views.has(id) });
1206
+ if (view.bar.parentNode !== layer) layer.append(view.bar);
1207
+ Object.assign(view.bar.style, { transform: `translate(${Math.round(left)}px, ${Math.round(line.box.top - origin)}px)`, height: `${Math.max(2, Math.round(line.box.bottom - line.box.top))}px` });
1208
+ view.bar.toggleAttribute("data-hot", shown?.id === id || hot === id);
1209
+ }
1210
+
1211
+ // The form changed: what the other left goes, and all of it is placed anew. A card being written keeps
1212
+ // its node, so its field, caret and words; one only looked at goes.
1213
+ function changeForm(stands) {
1214
+ standing = stands;
1215
+ quiet = true;
1216
+ layer.toggleAttribute("data-margin", stands);
1217
+ layer.removeAttribute("data-moves");
1218
+ laid = null;
1219
+ moves = false;
1220
+ remeasure = true;
1221
+ hot = null;
1222
+ placed = null;
1223
+ marksChanged = true;
1224
+ if (shown && !shown.focus && !shown.begun && !focused()) shown = null;
1225
+ if (shown) shown.opening = true;
1226
+ if (stands) for (const view of views.values()) view.marker?.remove();
1227
+ for (const card of cards.values()) Object.assign(card, { width: null, left: null, top: null, height: 0 });
1228
+ }
1229
+
1230
+ // What a card is laid out by, down the layer: its anchor's first line, a new one's further down, or
1231
+ // the place in the window it was given as it opened.
1232
+ const laidBy = (id, view) => (shown?.id === id ? shown.within ?? view.top + (shown.begun ? CARD.draft : 0) : view.top);
1233
+
1234
+ // A card opened for writing stands where the reader is looking: with its place out of the window (a
1235
+ // caret at the end of a long block, blocks selected across screens) it stands as far in as a card
1236
+ // pressed brings its anchor, and the page does not move for it, so that its first key does not either.
1237
+ // `origin`: the layer's top in the window. Returns whether that changed where it is laid out.
1238
+ function standWithin(origin) {
1239
+ const view = views.get(shown.id);
1240
+ if ((view?.top ?? null) === null) return false;
1241
+ const was = shown.within;
1242
+ shown.opening = false;
1243
+ shown.within = null;
1244
+ const top = laidBy(shown.id, view) - CARD.above + origin;
1245
+ if (top < 0 || top > document.documentElement.clientHeight - REVEAL) shown.within = REVEAL + CARD.above - origin;
1246
+ return shown.within !== was;
1247
+ }
1248
+
1249
+ // With room: each annotation the page shows as a card right of the column, beside what it is about
1250
+ // (cardTops). What a key did not move is left alone: only those found again are measured until the
1251
+ // page's size or structure changed, and the cards are laid out only when a place, a height or the one
1252
+ // in front did. Their left is where the column is going, not where a slide has it now.
1253
+ function standCards({ left, column, cards: width }) {
1254
+ const origin = layer.getBoundingClientRect().top;
1255
+ let moved = false;
1256
+ for (const id of remeasure ? [...views.keys()] : [...stale]) {
1257
+ const view = views.get(id);
1258
+ if (!view) continue;
1259
+ const line = lineOf(view);
1260
+ seen(view, line);
1261
+ placeBar(id, view, line, left + column + RAIL.bar, origin);
1262
+ const top = line && !view.units?.some(inDialog) ? line.top - origin : null;
1263
+ if (top !== view.top) moved = true;
1264
+ view.top = top;
1265
+ }
1266
+ remeasure = false;
1267
+ stale.clear();
1268
+ for (const [id, card] of cards) {
1269
+ if ((views.get(id)?.top ?? null) !== null) continue;
1270
+ card.node.remove();
1271
+ cards.delete(id);
1272
+ moved = true;
1273
+ }
1274
+ if (shown && !cards.has(shown.id) && !focused()) shown = null;
1275
+ if (shown?.opening && standWithin(origin)) moved = true;
1276
+ for (const [id, view] of views) {
1277
+ if ((view.top ?? null) === null) continue;
1278
+ const card = cardOf(id);
1279
+ if (card.node.parentNode !== layer) {
1280
+ card.node.style.top = `${Math.round(laidBy(id, view) - CARD.above)}px`;
1281
+ layer.append(card.node);
1282
+ if (moves && !quiet) fadeIn(card.node);
1283
+ }
1284
+ if (card.width !== width) {
1285
+ card.width = width;
1286
+ card.node.style.width = `${width}px`;
1287
+ card.height = 0;
1288
+ }
1289
+ if (!card.height) {
1290
+ card.tall = fitField(card.node, card.field);
1291
+ card.height = card.node.offsetHeight;
1292
+ moved = true;
1293
+ }
1294
+ }
1295
+ const front = shown?.id ?? null;
1296
+ const across = Math.round(left + column + MARGIN.lane);
1297
+ if (!moved && laid?.front === front && laid?.left === across) return;
1298
+ laid = { front, left: across };
1299
+ const order = cardTops([...cards].map(([id, card]) => ({ id, height: card.height, created: items.get(id)?.created ?? "", top: laidBy(id, views.get(id)) })), front);
1300
+ order.forEach(({ id, top }, index) => {
1301
+ const card = cards.get(id);
1302
+ const at = Math.round(top);
1303
+ if (card.top !== at) card.node.style.top = `${(card.top = at)}px`;
1304
+ if (card.left !== across) card.node.style.left = `${(card.left = across)}px`;
1305
+ card.node.toggleAttribute("data-front", id === front);
1306
+ card.node.toggleAttribute("data-last", index === order.length - 1);
1307
+ });
1308
+ if (moves) return;
1309
+ moves = true;
1310
+ requestAnimationFrame(() => layer?.setAttribute("data-moves", ""));
1311
+ }
1312
+
1313
+ // With no room: where each annotation is, its marker in the column's right gutter (stacked without
1314
+ // overlapping); the card shown by its words or blocks.
1315
+ function foldCards(page, starting) {
1316
+ const column = page.getBoundingClientRect();
1317
+ const origin = layer.getBoundingClientRect();
1318
+ const x = (value) => value - origin.left;
1319
+ const y = (value) => value - origin.top;
1320
+ const end = pageEnd(y(document.documentElement.getBoundingClientRect().bottom));
1321
+
1322
+ const stack = [];
1323
+ for (const [id, view] of views) {
1324
+ const line = lineOf(view);
1325
+ seen(view, line);
1326
+ // None for a new one while it is written with no words yet: an annotation once left, or once it has them.
1327
+ if (!line || drafted(id)) {
1328
+ view.marker?.remove();
1329
+ view.bar?.remove();
1330
+ continue;
1331
+ }
1332
+ const marker = markerOf(id);
1333
+ if (marker.parentNode !== layer) {
1334
+ layer.append(marker);
1335
+ marksChanged = true;
1336
+ if (!starting && !quiet) fadeIn(marker);
1337
+ }
1338
+ placeBar(id, view, line, x(column.right + RAIL.bar), origin.top);
1339
+ stack.push({ id, node: marker, left: markerAcross(column.right, (view.units ?? []).filter((unit) => unit.isConnected)), ideal: y(markerTop(line)), marker: true, height: RAIL.marker, active: false, row: { right: columnRight(view), order: stack.length } });
1340
+ }
1341
+ // One row (two columns of 分栏 side by side): the column beside the gutter first.
1342
+ const rows = [...stack].sort((a, b) => a.ideal - b.ideal || a.row.order - b.row.order);
1343
+ for (let start = 0; start < rows.length;) {
1344
+ let stop = start + 1;
1345
+ while (stop < rows.length && rows[stop].ideal - rows[start].ideal <= SAME_LINE) stop++;
1346
+ const group = rows.slice(start, stop);
1347
+ if (group.length > 1 && new Set(group.map((item) => item.row.right)).size > 1) {
1348
+ const top = Math.min(...group.map((item) => item.ideal));
1349
+ group.sort((a, b) => b.row.right - a.row.right || a.row.order - b.row.order).forEach((item, index) => { item.ideal = top + index * 0.001; });
1350
+ }
1351
+ start = stop;
1352
+ }
1353
+ const tops = stackItems(stack, { top: TOP_ROOM, bottom: end });
1354
+ stack.forEach((item, index) => { item.node.style.transform = `translate(${Math.round(x(item.left))}px, ${Math.round(tops[index])}px)`; });
1355
+
1356
+ // The card: the annotation shown (by hover, a click, being written).
1357
+ const target = shown ? views.get(shown.id) ?? made.get(shown.id) : null;
1358
+ let box = target ? anchorBox(target) : null;
1359
+ if (shown && !(shown.focus || focused()) && (!box || (shown.located && (box.bottom < 0 || box.top > document.documentElement.clientHeight)))) {
1360
+ hide();
1361
+ box = null;
1362
+ }
1363
+ for (const [id, each] of cards) {
1364
+ if (id === shown?.id) continue;
1365
+ each.node.remove();
1366
+ cards.delete(id);
1367
+ }
1368
+ if (box) {
1369
+ const card = cardOf(shown.id);
1370
+ const appended = card.node.parentNode !== layer;
1371
+ if (appended) layer.append(card.node);
1372
+ card.node.hidden = false;
1373
+ card.node.style.width = `${Math.round(Math.min(RAIL.card, column.width))}px`;
1374
+ fitField(card.node, card.field);
1375
+ // Fading in as it appears, or shows another annotation.
1376
+ if (appended && !quiet) fadeIn(card.node);
1377
+ const at = placeCard(box, { width: card.node.offsetWidth, height: card.node.offsetHeight }, column, shown?.focus ? shown.side : null);
1378
+ placed = { side: at.side };
1379
+ if (shown?.focus && at.side) shown.side ??= at.side;
1380
+ Object.assign(card.node.style, { left: `${Math.round(x(at.left))}px`, top: `${Math.round(y(at.top))}px` });
1381
+ } else cards.get(shown?.id)?.node.remove();
1382
+ }
1383
+
1384
+ // One frame: the room asked for, then the annotations in the form it gives, the fill and the marks.
1385
+ // With nothing to draw nothing stands: no layer, nothing listening for the pointer.
1386
+ function draw() {
1387
+ if (!isEditing()) {
1388
+ takeDown();
1389
+ return false;
1390
+ }
1391
+ if (!resolved) resolveAll();
1392
+ const starting = !started;
1393
+ started = true;
1394
+ askRoom();
1395
+ if (!shown && views.size === 0) {
1396
+ takeDown();
1397
+ return false;
1398
+ }
1399
+ ensureLayer();
1400
+ const page = pageColumn();
1401
+ if (!page) return false;
1402
+ const room = margin();
1403
+ if (room.cards > 0 !== standing) changeForm(room.cards > 0);
1404
+ if (standing) standCards(room);
1405
+ else foldCards(page, starting);
1406
+ quiet = false;
1407
+
1408
+ // A block anchor's blocks filled while it is shown, or its card in the margin is under the pointer;
1409
+ // those in a closed disclosure, the disclosure; in a folded section, its heading.
1410
+ const filled = views.get(shown?.id ?? (standing ? hot : null));
1411
+ fills.paint(filled && !filled.range && !filled.hidden ? ordered(filled.units.filter((unit) => unit.isConnected).map((unit) => closedAround(unit) ?? unit)) : []);
1412
+ paintMarks();
1413
+ return fills.count > 0 ? FOLLOW : false;
1414
+ }
1415
+
1416
+ // Where the pointer is, and whether it moved to get there (a marker drawn under a still pointer
1417
+ // hears an enter without a move); over marked words, they and their marker are hot (no card: the
1418
+ // words are for editing).
1419
+ let moved = null;
1420
+ function pointerAt(event) {
1421
+ const was = pointer;
1422
+ pointer = { x: event.clientX, y: event.clientY, moved: !was || was.x !== event.clientX || was.y !== event.clientY };
1423
+ if (!event.buttons) layer?.removeAttribute("data-pressed");
1424
+ }
1425
+ // Into the workbench: over nothing of the page (no words hot either).
1426
+ const pointerOut = (event) => {
1427
+ if (event.relatedTarget) return;
1428
+ pointer = null;
1429
+ if (hot) {
1430
+ hot = null;
1431
+ paintMarks();
1432
+ for (const [id, view] of views) view.bar?.toggleAttribute("data-hot", shown?.id === id);
1433
+ }
1434
+ };
1435
+ function pointerMoved(event) {
1436
+ if (event.buttons || event.target?.closest?.(".ge-note-layer")) return;
1437
+ const first = !moved;
1438
+ moved = { x: event.clientX, y: event.clientY };
1439
+ if (first) requestAnimationFrame(hotAt);
1440
+ }
1441
+ // The annotation whose marked words are under this point (window coordinates), or null.
1442
+ function markedAt(x, y) {
1443
+ for (const [id, view] of views) {
1444
+ if (!view.range || view.hidden || view.folded) continue;
1445
+ for (const rect of view.range.getClientRects()) if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) return id;
1446
+ }
1447
+ return null;
1448
+ }
1449
+ function hotAt() {
1450
+ const over = markedAt(moved?.x ?? -1, moved?.y ?? -1);
1451
+ moved = null;
1452
+ if (over !== hot) {
1453
+ hot = over;
1454
+ paintMarks();
1455
+ for (const [id, view] of views) view.bar?.toggleAttribute("data-hot", id === hot || shown?.id === id);
1456
+ }
1457
+ }
1458
+
1459
+ // The annotation whose marked words `event` came down on: what stands over them keeps a press to itself.
1460
+ const markedBy = (event) => (event.target?.closest?.("main") && !inDialog(event.target) ? markedAt(event.clientX, event.clientY) : null);
1461
+ // A press elsewhere takes away a card only looked at: one the pointer never rested on has no leaving.
1462
+ // In the margin it takes the one in front back among the others, but for a press on its marked words
1463
+ // (one being written goes by its field's blur). Begun out of the margin, it leaves the cards out of
1464
+ // the pointer's way until it moves with no button down (`pointerAt`; a release on the scrollbar says
1465
+ // nothing): a selection dragged over them is the page's.
1466
+ const pressed = (event) => {
1467
+ if (event.target?.closest?.(".ge-note-layer")) return;
1468
+ layer?.setAttribute("data-pressed", "");
1469
+ if (!shown || shown.focus) return;
1470
+ if (shown.hover || (standing && markedBy(event) !== shown.id)) hide();
1471
+ };
1472
+ // A press out of the frame, heard as the frame's blur where the page had the focus, or told by the
1473
+ // workbench (`away`): one only in front goes back too (one being written goes by its field's blur).
1474
+ const leftFrame = () => { if (standing) hide(); };
1475
+ // A click on marked words, no words selected by it, brings their card in front in the margin; the
1476
+ // caret stays in them. With no room the words are for editing: no card.
1477
+ const clicked = (event) => {
1478
+ if (!standing || event.target?.closest?.(".ge-note-layer") || !getSelection()?.isCollapsed) return;
1479
+ const id = markedBy(event);
1480
+ if (id && id !== shown?.id) show(id);
1481
+ };
1482
+
1483
+ // The server's word that the file changed (another tab, an Agent's own write).
1484
+ addEventListener("doc-annotations-changed", (event) => {
1485
+ if (reading && (event.detail?.version ?? "") !== version) fetchAll().then(take);
1486
+ });
1487
+ // The card follows the scroll where the window holds it (by words, as the format bar does; at the
1488
+ // window's bottom by a long block). Those in the margin scroll with the page.
1489
+ const scrolled = () => { if (shown && !standing) wake(); };
1490
+ // What may have moved what they stand beside.
1491
+ const pageMoved = () => {
1492
+ remeasure = true;
1493
+ wake();
1494
+ };
1495
+
1496
+ // The page's text followed, from the first annotation on: a page with none has no observer.
1497
+ function followText() {
1498
+ const main = document.querySelector("main");
1499
+ if (observer || !main) return;
1500
+ observer = new MutationObserver(observed);
1501
+ observer.observe(main, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["data-block-id", "open", "hidden", "class"] });
1502
+ }
1503
+
1504
+ // What moves what is drawn, and the pointer over it, listened to while the layer stands.
1505
+ function followPage(wanted) {
1506
+ if (wanted === Boolean(resizer)) return;
1507
+ const listen = wanted ? addEventListener : removeEventListener;
1508
+ listen("resize", pageMoved);
1509
+ listen("scroll", scrolled, { passive: true });
1510
+ listen("mousemove", pointerMoved, { passive: true, capture: true });
1511
+ listen("pointermove", pointerAt, { passive: true, capture: true });
1512
+ listen("pointerout", pointerOut, { passive: true, capture: true });
1513
+ listen("pointerdown", pressed, { passive: true, capture: true });
1514
+ listen("click", clicked, { passive: true, capture: true });
1515
+ listen("blur", leftFrame);
1516
+ if (!wanted) {
1517
+ resizer.disconnect();
1518
+ resizer = null;
1519
+ unmargin();
1520
+ unmargin = null;
1521
+ clearTimeout(hoverTimer);
1522
+ clearTimeout(leaveTimer);
1523
+ hovering = null;
1524
+ return;
1525
+ }
1526
+ const main = document.querySelector("main");
1527
+ resizer = new ResizeObserver(pageMoved);
1528
+ watched = new WeakSet();
1529
+ resizer.observe(document.documentElement);
1530
+ if (main) resizer.observe(main);
1531
+ watchRows();
1532
+ unmargin = onMargin(pageMoved);
1533
+ }
1534
+
1535
+ // The blocks standing in a row, each looked at once (a block in a row added says so as it comes).
1536
+ function watchRows() {
1537
+ if (!resizer) return;
1538
+ for (const block of document.querySelector("main")?.querySelectorAll(IN_ROW) ?? []) {
1539
+ if (watched.has(block)) continue;
1540
+ watched.add(block);
1541
+ resizer.observe(block);
1542
+ }
1543
+ }
1544
+
1545
+ // Once the marks are drawn (a frame after the annotations came), 500ms at most.
1546
+ const drawn = () => new Promise((resolve) => {
1547
+ const timer = setTimeout(resolve, 500);
1548
+ requestAnimationFrame(() => requestAnimationFrame(() => { clearTimeout(timer); resolve(); }));
1549
+ });
1550
+
1551
+ // What the workbench's list and its two actions ask of the page: the rows of the list; going to the
1552
+ // one `id` says; a press in the workbench after that, where the page has no focus to lose; what
1553
+ // 复制批注 hands the workbench; 全部删除, what was typed saved first, so that 撤销 brings all of it back.
1554
+ // 撤销 is the workbench's, by the report's slug (the page shown then may be another's), and then the
1555
+ // page reads them again (`annotate`).
1556
+ const ACTIONS = {
1557
+ async list() {
1558
+ await (reading ??= read());
1559
+ return { ok: true, items: rows() };
1560
+ },
1561
+ async locate(id) {
1562
+ await (reading ??= read());
1563
+ return goTo(id) ? { ok: true } : { ok: false, reason: "missing" };
1564
+ },
1565
+ away() {
1566
+ leftFrame();
1567
+ return { ok: true };
1568
+ },
1569
+ async copy() {
1570
+ await flush();
1571
+ const answer = await fetch(`${ROUTE}?slug=${encodeURIComponent(slug)}&as=copy`, { headers: { "X-Gallery-Token": token }, cache: "no-store" }).then((response) => response.json()).catch(() => ({ ok: false, reason: "offline" }));
1572
+ return answer.ok ? { ok: true, data: { title: answer.title, path: answer.path, items: answer.items } } : { ok: false, reason: answer.reason ?? "error" };
1573
+ },
1574
+ async clear() {
1575
+ if (focused()) document.activeElement.blur();
1576
+ close();
1577
+ await flush();
1578
+ shown = null;
1579
+ made.clear();
1580
+ named.clear();
1581
+ lost.clear();
1582
+ history.clear();
1583
+ const answer = await send({ clear: true });
1584
+ if (!answer.ok) return { ok: false, reason: answer.reason ?? "error" };
1585
+ keep(stashKey, []);
1586
+ items = new Map();
1587
+ take(answer);
1588
+ return { ok: true, slug, removed: answer.removed ?? [] };
1589
+ },
1590
+ };
1591
+
1592
+ /** How many annotations the page has (their words or blocks still there; a new one once it has words). */
1593
+ const count = () => [...items.keys()].filter((id) => views.has(id) && !drafted(id)).length;
1594
+
1595
+ provide("annotations", () => ({
1596
+ items: items.size, found: [...views.keys()].filter((id) => !drafted(id)), lost: [...lost.keys()], card: shown && { id: shown.id, focus: shown.focus, hover: shown.hover }, standing,
1597
+ quotes: Object.fromEntries([...views].map(([id, view]) => [id, view.excerpt?.quote ?? null])),
1598
+ }));
1599
+
1600
+ return {
1601
+ /** Whether `node` is the margin's (a card, a marker): the editor's own keys, presses and clipboard leave it alone. */
1602
+ holds: (node) => Boolean(node?.closest?.(".ge-note-layer")),
1603
+ /** The page is ready: what the server holds is read, in the background. */
1604
+ start() {
1605
+ reading ??= read();
1606
+ },
1607
+ /** Whether these blocks can take an annotation, one for them all (the button beside a block). */
1608
+ canBlocks,
1609
+ /** Where a marker of this block alone stands: where the button beside it stands; null where it has none. */
1610
+ markerSpot,
1611
+ /** An annotation on these blocks (the button beside a block). */
1612
+ annotateBlocks(units) {
1613
+ if (canBlocks(units)) begin(blockSelection(units));
1614
+ },
1615
+ /** Whether what is selected now can be annotated (the format bar's row). */
1616
+ canSelection: () => Boolean(selected()),
1617
+ /** An annotation on what is selected now (the format bar's row). */
1618
+ annotateSelection() {
1619
+ const selection = selected();
1620
+ if (selection) begin(selection);
1621
+ },
1622
+ /** Whether a card is being written (it has the focus, or what was typed waits to be saved): the page is not swapped meanwhile. */
1623
+ writing: () => writing() || timers.size > 0 || sending.size > 0,
1624
+ /** How many annotations the page has, for the workbench's button; undefined until it can say. */
1625
+ count: () => (counted ? count() : undefined),
1626
+ flush,
1627
+ /** Before a commit goes (autosave's `before`): where annotations' words went with the edit it writes, told first (a promise, or null). */
1628
+ sync: () => sync(),
1629
+ /** The session's `annotate`: { refresh } -> { ok, count }: what there is read (`refresh`: read again) and drawn, so that a page is swapped in with its marks. */
1630
+ async command({ refresh = false }) {
1631
+ await (reading ??= read());
1632
+ if (refresh) take(await fetchAll());
1633
+ if (views.size > 0) await drawn();
1634
+ return { ok: true, count: count() };
1635
+ },
1636
+ /** The session's `annotations`: what the workbench asks of them by name (`ACTIONS`). */
1637
+ action: ({ action, id }) => (Object.hasOwn(ACTIONS, action) ? ACTIONS[action](id) : { ok: false, reason: "action" }),
1638
+ /**
1639
+ * Ctrl/⌘+Alt+M in the page: an annotation on the words selected, else on the blocks selected, else
1640
+ * on the caret's block; a caret in marked words, whose block takes none, their card. True when it
1641
+ * did one of these (else the key is the workbench's: a press of its button).
1642
+ */
1643
+ key(event) {
1644
+ if (!(event.ctrlKey || event.metaKey) || !event.altKey || event.shiftKey || event.code !== "KeyM") return false;
1645
+ const selection = selected() ?? caretBlock();
1646
+ const marked = selection ? null : markedAtCaret();
1647
+ if (!selection && !marked) return false;
1648
+ event.preventDefault();
1649
+ if (selection) begin(selection, { keys: true });
1650
+ else show(marked, { focus: true, keys: true });
1651
+ return true;
1652
+ },
1653
+ /** The page goes away: what is typed goes now. */
1654
+ leave: () => flush({ keepalive: true }),
1655
+ };
1656
+ }