@heycys/gallery 0.0.0-stage → 0.1.0

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