@heycys/gallery 0.1.0 → 0.1.2

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 (465) hide show
  1. package/THIRD-PARTY-NOTICES.md +51 -1
  2. package/browser/editor/components/callout/index.mjs +40 -15
  3. package/browser/editor/components/columns/page.mjs +45 -32
  4. package/browser/editor/components/details/index.mjs +2 -1
  5. package/browser/editor/components/dialog/index.mjs +2 -1
  6. package/browser/editor/components/dialog/page.mjs +0 -18
  7. package/browser/editor/components/divider/index.mjs +1 -1
  8. package/browser/editor/components/embed/index.mjs +2 -2
  9. package/browser/editor/components/figure/page.mjs +10 -1
  10. package/browser/editor/components/file/page.mjs +2 -2
  11. package/browser/editor/components/flowchart/add.mjs +7 -6
  12. package/browser/editor/components/flowchart/page.mjs +1 -1
  13. package/browser/editor/components/heading/index.mjs +2 -0
  14. package/browser/editor/components/heading/page.mjs +6 -0
  15. package/browser/editor/components/highlight/index.mjs +4 -20
  16. package/browser/editor/components/icons.mjs +13 -2
  17. package/browser/editor/components/image/crop.mjs +6 -9
  18. package/browser/editor/components/image/page.mjs +2 -2
  19. package/browser/editor/components/installed/index.mjs +82 -29
  20. package/browser/editor/components/installed/page.mjs +146 -34
  21. package/browser/editor/components/installed/served.mjs +6 -5
  22. package/browser/editor/components/instances.mjs +18 -0
  23. package/browser/editor/components/kit.mjs +3 -0
  24. package/browser/editor/components/list/page.mjs +3 -6
  25. package/browser/editor/components/media/page.mjs +44 -14
  26. package/browser/editor/components/pages.mjs +2 -2
  27. package/browser/editor/components/registry.mjs +68 -33
  28. package/browser/editor/components/section/index.mjs +2 -1
  29. package/browser/editor/components/set-apart.mjs +24 -0
  30. package/browser/editor/components/table/index.mjs +64 -5
  31. package/browser/editor/components/table/page.mjs +30 -19
  32. package/browser/editor/page/annotate/anchors.mjs +4 -3
  33. package/browser/editor/page/annotate/card.mjs +3 -6
  34. package/browser/editor/page/annotate/index.mjs +61 -34
  35. package/browser/editor/page/autosave.mjs +357 -38
  36. package/browser/editor/page/beside.mjs +5 -5
  37. package/browser/editor/page/block-menu/colors.mjs +3 -1
  38. package/browser/editor/page/block-menu/index.mjs +8 -6
  39. package/browser/editor/page/blocks.mjs +16 -7
  40. package/browser/editor/page/board/client.mjs +57 -0
  41. package/browser/editor/page/board/hint.mjs +145 -0
  42. package/browser/editor/page/board/history.mjs +119 -0
  43. package/browser/editor/page/board/index.mjs +970 -0
  44. package/browser/editor/page/board/settings.mjs +209 -0
  45. package/browser/editor/page/caret.mjs +5 -2
  46. package/browser/editor/page/changes/bar.mjs +94 -0
  47. package/browser/editor/page/changes/ghost.mjs +206 -0
  48. package/browser/editor/page/changes/gone-words.mjs +94 -0
  49. package/browser/editor/page/changes/index.mjs +447 -0
  50. package/browser/editor/page/changes/words.mjs +27 -0
  51. package/browser/editor/page/clipboard/import/colors.mjs +3 -3
  52. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  53. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  54. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  55. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  56. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  57. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  58. package/browser/editor/page/clipboard/index.mjs +156 -26
  59. package/browser/editor/page/clipboard/own/blocks.mjs +25 -16
  60. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  61. package/browser/editor/page/clipboard/own/describe.mjs +16 -7
  62. package/browser/editor/page/clipboard/own/export.mjs +6 -6
  63. package/browser/editor/page/clipboard/own/selection.mjs +3 -3
  64. package/browser/editor/page/clipboard/own/writers.mjs +0 -11
  65. package/browser/editor/page/columns/index.mjs +48 -46
  66. package/browser/editor/page/commit.mjs +98 -25
  67. package/browser/editor/page/conflicts.mjs +13 -33
  68. package/browser/editor/page/convert.mjs +8 -7
  69. package/browser/editor/page/core.mjs +81 -27
  70. package/browser/editor/page/custom-page.mjs +106 -0
  71. package/browser/editor/page/drag/index.mjs +40 -24
  72. package/browser/editor/page/drag/preview.mjs +9 -4
  73. package/browser/editor/page/editor.mjs +120 -28
  74. package/browser/editor/page/emoji-picker/emoji-tab.mjs +22 -13
  75. package/browser/editor/page/emoji-picker/grid.mjs +7 -5
  76. package/browser/editor/page/emoji-picker/icon-tab.mjs +4 -4
  77. package/browser/editor/page/emoji-picker/icons.mjs +0 -1
  78. package/browser/editor/page/emoji-picker/index.mjs +11 -9
  79. package/browser/editor/page/emoji-picker/styles.mjs +156 -2
  80. package/browser/editor/page/emoji-picker/support.mjs +6 -4
  81. package/browser/editor/page/emoji-picker/upload.mjs +14 -13
  82. package/browser/editor/page/fills.mjs +5 -3
  83. package/browser/editor/page/fold/index.mjs +348 -0
  84. package/browser/editor/page/fold/rule.mjs +125 -0
  85. package/browser/editor/page/folded.mjs +23 -0
  86. package/browser/editor/page/format-bar/bar.mjs +2 -1
  87. package/browser/editor/page/format-bar/index.mjs +1 -1
  88. package/browser/editor/page/geometry.mjs +8 -6
  89. package/browser/editor/page/handle/index.mjs +22 -14
  90. package/browser/editor/page/hints/index.mjs +1 -1
  91. package/browser/editor/page/history.mjs +24 -4
  92. package/browser/editor/page/insert/index.mjs +30 -17
  93. package/browser/editor/page/links/index.mjs +5 -3
  94. package/browser/editor/page/links/pages.mjs +27 -9
  95. package/browser/editor/page/long-runs.mjs +75 -0
  96. package/browser/editor/page/look.mjs +69 -6
  97. package/browser/editor/page/margin.mjs +63 -27
  98. package/browser/editor/page/media-menu/index.mjs +6 -7
  99. package/browser/editor/page/menu.mjs +86 -31
  100. package/browser/editor/page/merge.mjs +24 -0
  101. package/browser/editor/page/notices.mjs +3 -3
  102. package/browser/editor/page/offered.mjs +1 -1
  103. package/browser/editor/page/outline/folds.mjs +11 -10
  104. package/browser/editor/page/outline/index.mjs +131 -45
  105. package/browser/editor/page/outline/list.mjs +23 -11
  106. package/browser/editor/page/outline/read.mjs +1 -1
  107. package/browser/editor/page/outline/strip.mjs +7 -1
  108. package/browser/editor/page/page-objects.mjs +13 -2
  109. package/browser/editor/page/panel.mjs +9 -13
  110. package/browser/editor/page/present-links.mjs +11 -2
  111. package/browser/editor/page/resize/index.mjs +42 -29
  112. package/browser/editor/page/selection/delete.mjs +8 -6
  113. package/browser/editor/page/selection/index.mjs +10 -3
  114. package/browser/editor/page/served.mjs +14 -0
  115. package/browser/editor/page/session.mjs +101 -6
  116. package/browser/editor/page/slash-menu/index.mjs +3 -3
  117. package/browser/editor/page/table/cell-bar.mjs +4 -1
  118. package/browser/editor/page/table/cells.mjs +103 -47
  119. package/browser/editor/page/table/icons.mjs +3 -1
  120. package/browser/editor/page/table/index.mjs +40 -16
  121. package/browser/editor/page/table/pictures.mjs +299 -0
  122. package/browser/editor/page/table/views.mjs +150 -0
  123. package/browser/editor/page/table/widths.mjs +9 -4
  124. package/browser/editor/page/text-select/index.mjs +43 -14
  125. package/browser/editor/page/todo-writes.mjs +75 -0
  126. package/browser/editor/page/tooltip.mjs +1 -1
  127. package/browser/editor/page/type-grid.mjs +4 -3
  128. package/browser/editor/page/typing/code-edges.mjs +63 -17
  129. package/browser/editor/page/typing/index.mjs +65 -9
  130. package/browser/editor/page/typing/leave.mjs +1 -1
  131. package/browser/editor/page/ui.mjs +13 -0
  132. package/browser/shared/annotations.mjs +8 -10
  133. package/browser/shared/block-packs.mjs +404 -0
  134. package/browser/shared/board-places.mjs +224 -0
  135. package/browser/shared/card-size.mjs +59 -0
  136. package/browser/shared/code-folds.mjs +5 -1
  137. package/browser/shared/colors.mjs +17 -14
  138. package/browser/shared/component-catalog.mjs +345 -54
  139. package/browser/shared/component-source.mjs +37 -0
  140. package/browser/shared/components/callout.mjs +4 -2
  141. package/browser/shared/components/index.mjs +72 -9
  142. package/browser/shared/components/installed.mjs +110 -8
  143. package/browser/shared/components/page.mjs +22 -3
  144. package/browser/shared/components/state.mjs +5 -1
  145. package/browser/shared/components/table.mjs +1 -1
  146. package/browser/shared/components/text.mjs +2 -2
  147. package/browser/shared/document-look.mjs +183 -0
  148. package/browser/shared/drafts.mjs +432 -0
  149. package/browser/shared/emoji/data/LICENSE-lucide.txt +43 -0
  150. package/browser/shared/emoji/data/README.md +4 -2
  151. package/browser/shared/emoji/data/categories.json +1 -1
  152. package/browser/shared/emoji/data/keywords-en.json +1 -1
  153. package/browser/shared/emoji/data/keywords-zh.json +1 -1
  154. package/browser/shared/emoji/data/names.json +1 -1
  155. package/browser/shared/emoji/data/positions.json +1 -1
  156. package/browser/shared/emoji/data/twemoji-index.json +1 -1
  157. package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
  158. package/browser/shared/emoji/data.mjs +10 -12
  159. package/browser/shared/emoji/emoji.mjs +8 -7
  160. package/browser/shared/emoji/icon-value.mjs +4 -3
  161. package/browser/shared/extension-sidebar.mjs +126 -0
  162. package/browser/shared/extensions.mjs +79 -0
  163. package/browser/shared/file-table.mjs +101 -0
  164. package/browser/shared/html-tree.mjs +11 -5
  165. package/browser/shared/i18n/en.mjs +374 -35
  166. package/browser/shared/json-layout.mjs +47 -0
  167. package/browser/shared/long-runs.mjs +39 -0
  168. package/browser/shared/net-cache.mjs +133 -0
  169. package/browser/shared/net-keys.mjs +32 -0
  170. package/browser/shared/net-policy.mjs +177 -0
  171. package/browser/shared/new-names.mjs +5 -2
  172. package/browser/shared/outline-shape.mjs +128 -0
  173. package/browser/shared/outline.mjs +458 -0
  174. package/browser/shared/pack-manifest.mjs +77 -0
  175. package/browser/shared/page-address.mjs +6 -3
  176. package/browser/shared/page-cards.mjs +308 -0
  177. package/browser/shared/page-links.mjs +69 -6
  178. package/browser/shared/pages.mjs +388 -0
  179. package/browser/shared/paths.mjs +6 -0
  180. package/browser/shared/preferences.mjs +10 -4
  181. package/browser/shared/preview-server.mjs +3 -7
  182. package/browser/shared/session.mjs +13 -5
  183. package/browser/shared/shipped-blocks.mjs +299 -0
  184. package/browser/shared/table-chart.mjs +33 -1
  185. package/browser/shared/todos.mjs +83 -0
  186. package/browser/shared/user-directory.mjs +24 -0
  187. package/browser/shared/versions.mjs +14 -0
  188. package/browser/shared/web-links.mjs +134 -0
  189. package/browser/shared/workspace-columns.mjs +120 -0
  190. package/browser/shared/workspace-look.mjs +91 -0
  191. package/browser/shared/workspace-name.mjs +154 -0
  192. package/browser/shared/workspace-query.mjs +125 -0
  193. package/browser/shared/workspace-registry.mjs +272 -0
  194. package/browser/shared/workspace.mjs +171 -20
  195. package/browser/shared/zoom.mjs +5 -5
  196. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  197. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  198. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  199. package/dist/chunk-3KJHLHUO.mjs +2 -0
  200. package/dist/chunk-3NJACR4W.mjs +2 -0
  201. package/dist/chunk-4AV456RQ.mjs +2 -0
  202. package/dist/chunk-4OZWXZE5.mjs +5 -0
  203. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  204. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  205. package/dist/chunk-6A53MZYZ.mjs +2 -0
  206. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  207. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  208. package/dist/{chunk-UXUK4PMC.mjs → chunk-6QBHU4DF.mjs} +1 -1
  209. package/dist/{chunk-6U2SLSDN.mjs → chunk-6VTU6KEO.mjs} +1 -1
  210. package/dist/chunk-B4P2CM2J.mjs +3 -0
  211. package/dist/chunk-BPL64HGA.mjs +4 -0
  212. package/dist/chunk-BVEFGE4B.mjs +2 -0
  213. package/dist/chunk-BZ5RAF7L.mjs +2 -0
  214. package/dist/chunk-CRJEG7MT.mjs +2 -0
  215. package/dist/{chunk-B3VTYBYR.mjs → chunk-D4CKRD3V.mjs} +91 -91
  216. package/dist/chunk-D4EURAX4.mjs +4 -0
  217. package/dist/chunk-DQCE7TJY.mjs +5 -0
  218. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  219. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  220. package/dist/{chunk-DB4WKGYF.mjs → chunk-ERLAIBOA.mjs} +2 -2
  221. package/dist/chunk-EURLTOTZ.mjs +4 -0
  222. package/dist/chunk-FJYN7RTH.mjs +4 -0
  223. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  224. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  225. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  226. package/dist/chunk-GY3FAUVM.mjs +2 -0
  227. package/dist/chunk-HLYV7RIH.mjs +2 -0
  228. package/dist/chunk-HMYTM3GT.mjs +15 -0
  229. package/dist/chunk-IA4PSIMD.mjs +2 -0
  230. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  231. package/dist/chunk-JVHHHGEA.mjs +3 -0
  232. package/dist/chunk-KTKHRC6H.mjs +2 -0
  233. package/dist/chunk-L7UP52JO.mjs +2 -0
  234. package/dist/chunk-MHW7ZKOU.mjs +2 -0
  235. package/dist/chunk-N76HWBA3.mjs +10 -0
  236. package/dist/chunk-N7AZZLGT.mjs +548 -0
  237. package/dist/chunk-NAMN3RAF.mjs +2 -0
  238. package/dist/{chunk-YXAUQPVK.mjs → chunk-NFMCP2HI.mjs} +3 -3
  239. package/dist/chunk-NG375ILQ.mjs +2 -0
  240. package/dist/chunk-NVRNLK25.mjs +6 -0
  241. package/dist/chunk-NWFBE2YA.mjs +2 -0
  242. package/dist/chunk-NWR2LT5B.mjs +2 -0
  243. package/dist/chunk-OJKMINIU.mjs +20 -0
  244. package/dist/chunk-OUUP3WKT.mjs +2 -0
  245. package/dist/chunk-OW5NWPOJ.mjs +10 -0
  246. package/dist/chunk-PAREWQGB.mjs +4 -0
  247. package/dist/chunk-POQSE4C2.mjs +2 -0
  248. package/dist/chunk-PUOQ65NC.mjs +2 -0
  249. package/dist/{chunk-Z52HTUW7.mjs → chunk-PVER7LSC.mjs} +2 -2
  250. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  251. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  252. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  253. package/dist/chunk-QKXMPUXC.mjs +2 -0
  254. package/dist/chunk-QOUEFUCH.mjs +16 -0
  255. package/dist/chunk-RFDZXFJF.mjs +13 -0
  256. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  257. package/dist/chunk-RMECZA2K.mjs +2 -0
  258. package/dist/{chunk-EUX7IC5J.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  259. package/dist/chunk-SX4LJYT6.mjs +8 -0
  260. package/dist/chunk-SXHBYZST.mjs +16 -0
  261. package/dist/chunk-TANZRINB.mjs +2 -0
  262. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  263. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  264. package/dist/chunk-UFNYFFKO.mjs +20 -0
  265. package/dist/chunk-UGWYKHBL.mjs +170 -0
  266. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  267. package/dist/chunk-UMHUSOPJ.mjs +2 -0
  268. package/dist/chunk-UNOYVIYR.mjs +2 -0
  269. package/dist/chunk-V6RIYQZZ.mjs +4 -0
  270. package/dist/chunk-VJPFVNNW.mjs +15 -0
  271. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  272. package/dist/chunk-VTKNEYT3.mjs +6 -0
  273. package/dist/chunk-VXACTLO3.mjs +3 -0
  274. package/dist/chunk-W3BGBJQW.mjs +2 -0
  275. package/dist/chunk-YI43RG4P.mjs +2 -0
  276. package/dist/chunk-YQGT4AB2.mjs +6 -0
  277. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  278. package/dist/{chunk-ACOPDCNB.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  279. package/dist/chunk-ZTHICSSF.mjs +2 -0
  280. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  281. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  282. package/dist/cli.mjs +1 -1
  283. package/dist/server.mjs +1 -1
  284. package/dist/{chunk-EVJ3BX3M.mjs → update-fetch.mjs} +1 -1
  285. package/dist/worker.mjs +1 -1
  286. package/kit/board.css +95 -0
  287. package/kit/board.js +250 -0
  288. package/kit/charts.js +5 -71
  289. package/kit/code.js +14 -14
  290. package/kit/math.js +18 -2
  291. package/kit/report.css +500 -151
  292. package/kit/runtime.js +1720 -170
  293. package/kit/state.js +10 -1
  294. package/kit/tables.js +172 -37
  295. package/kit/ui.js +166 -27
  296. package/package.json +2 -1
  297. package/packs/gallery/card/component.json +14 -0
  298. package/packs/gallery/card/example.html +10 -0
  299. package/packs/gallery/card/preview.svg +1 -0
  300. package/packs/gallery/card/style.css +6 -0
  301. package/packs/gallery/card/template.html +6 -0
  302. package/packs/gallery/card/writing.md +14 -0
  303. package/packs/gallery/chart-card/component.json +15 -0
  304. package/packs/gallery/chart-card/example.html +14 -0
  305. package/packs/gallery/chart-card/kit.js +130 -0
  306. package/packs/gallery/chart-card/preview.svg +1 -0
  307. package/packs/gallery/chart-card/style.css +42 -0
  308. package/packs/gallery/chart-card/template.html +13 -0
  309. package/packs/gallery/chart-card/writing.md +20 -0
  310. package/packs/gallery/chart-view/component.json +20 -0
  311. package/packs/gallery/chart-view/example.html +13 -0
  312. package/packs/gallery/chart-view/kit.js +355 -0
  313. package/packs/gallery/chart-view/preview.svg +1 -0
  314. package/packs/gallery/chart-view/style.css +94 -0
  315. package/packs/gallery/chart-view/template.html +3 -0
  316. package/packs/gallery/chart-view/writing.md +22 -0
  317. package/packs/gallery/countdown/component.json +18 -0
  318. package/packs/gallery/countdown/example.html +3 -0
  319. package/packs/gallery/countdown/kit.js +156 -0
  320. package/packs/gallery/countdown/preview.svg +1 -0
  321. package/packs/gallery/countdown/style.css +130 -0
  322. package/packs/gallery/countdown/template.html +3 -0
  323. package/packs/gallery/countdown/writing.md +22 -0
  324. package/packs/gallery/habit/component.json +14 -0
  325. package/packs/gallery/habit/example.html +3 -0
  326. package/packs/gallery/habit/kit.js +295 -0
  327. package/packs/gallery/habit/preview.svg +1 -0
  328. package/packs/gallery/habit/style.css +246 -0
  329. package/packs/gallery/habit/template.html +3 -0
  330. package/packs/gallery/habit/writing.md +21 -0
  331. package/packs/gallery/heatmap/component.json +18 -0
  332. package/packs/gallery/heatmap/example.html +3 -0
  333. package/packs/gallery/heatmap/kit.js +351 -0
  334. package/packs/gallery/heatmap/preview.svg +1 -0
  335. package/packs/gallery/heatmap/style.css +201 -0
  336. package/packs/gallery/heatmap/template.html +3 -0
  337. package/packs/gallery/heatmap/writing.md +21 -0
  338. package/packs/gallery/kanban/component.json +18 -0
  339. package/packs/gallery/kanban/example.html +12 -0
  340. package/packs/gallery/kanban/kit.js +540 -0
  341. package/packs/gallery/kanban/preview.svg +1 -0
  342. package/packs/gallery/kanban/style.css +212 -0
  343. package/packs/gallery/kanban/template.html +3 -0
  344. package/packs/gallery/kanban/writing.md +20 -0
  345. package/packs/gallery/metric/component.json +22 -0
  346. package/packs/gallery/metric/example.html +15 -0
  347. package/packs/gallery/metric/kit.js +248 -0
  348. package/packs/gallery/metric/preview.svg +1 -0
  349. package/packs/gallery/metric/style.css +92 -0
  350. package/packs/gallery/metric/template.html +3 -0
  351. package/packs/gallery/metric/writing.md +23 -0
  352. package/packs/gallery/note/component.json +14 -0
  353. package/packs/gallery/note/example.html +3 -0
  354. package/packs/gallery/note/kit.js +102 -0
  355. package/packs/gallery/note/preview.svg +1 -0
  356. package/packs/gallery/note/style.css +76 -0
  357. package/packs/gallery/note/template.html +3 -0
  358. package/packs/gallery/note/writing.md +18 -0
  359. package/packs/gallery/pack.json +8 -0
  360. package/packs/gallery/recent/component.json +13 -0
  361. package/packs/gallery/recent/example.html +1 -0
  362. package/packs/gallery/recent/kit.js +238 -0
  363. package/packs/gallery/recent/preview.svg +1 -0
  364. package/packs/gallery/recent/style.css +158 -0
  365. package/packs/gallery/recent/template.html +1 -0
  366. package/packs/gallery/recent/writing.md +15 -0
  367. package/packs/gallery/static/component.json +13 -0
  368. package/packs/gallery/static/example.html +11 -0
  369. package/packs/gallery/static/preview.svg +1 -0
  370. package/packs/gallery/static/style.css +138 -0
  371. package/packs/gallery/static/template.html +1 -0
  372. package/packs/gallery/static/writing.md +18 -0
  373. package/packs/gallery/stats/component.json +13 -0
  374. package/packs/gallery/stats/example.html +1 -0
  375. package/packs/gallery/stats/kit.js +166 -0
  376. package/packs/gallery/stats/preview.svg +1 -0
  377. package/packs/gallery/stats/style.css +151 -0
  378. package/packs/gallery/stats/template.html +1 -0
  379. package/packs/gallery/stats/writing.md +11 -0
  380. package/packs/gallery/today/component.json +12 -0
  381. package/packs/gallery/today/example.html +1 -0
  382. package/packs/gallery/today/kit.js +185 -0
  383. package/packs/gallery/today/preview.svg +1 -0
  384. package/packs/gallery/today/style.css +120 -0
  385. package/packs/gallery/today/template.html +1 -0
  386. package/packs/gallery/today/writing.md +14 -0
  387. package/packs/gallery/todos/component.json +19 -0
  388. package/packs/gallery/todos/example.html +3 -0
  389. package/packs/gallery/todos/kit.js +342 -0
  390. package/packs/gallery/todos/preview.svg +1 -0
  391. package/packs/gallery/todos/style.css +267 -0
  392. package/packs/gallery/todos/template.html +3 -0
  393. package/packs/gallery/todos/writing.md +30 -0
  394. package/packs/gallery/weather/component.json +19 -0
  395. package/packs/gallery/weather/example.html +3 -0
  396. package/packs/gallery/weather/kit.js +312 -0
  397. package/packs/gallery/weather/preview.svg +1 -0
  398. package/packs/gallery/weather/style.css +258 -0
  399. package/packs/gallery/weather/template.html +3 -0
  400. package/packs/gallery/weather/writing.md +26 -0
  401. package/packs/gallery-soft/pack.json +8 -0
  402. package/release.json +13 -11
  403. package/sdk/doc-kit.d.ts +404 -7
  404. package/sdk/doc-state.d.ts +2 -0
  405. package/skill/SKILL.md +71 -26
  406. package/skill/references/block-script.md +140 -0
  407. package/skill/references/building-blocks.md +257 -0
  408. package/skill/references/example-report.html +1 -1
  409. package/skill/references/extensions.md +59 -0
  410. package/skill/references/pages-and-data.md +221 -0
  411. package/skill/references/writing-html.md +9 -7
  412. package/skill/sdk/doc-kit.d.ts +404 -7
  413. package/skill/sdk/doc-state.d.ts +2 -0
  414. package/web/assets/index-BK2e_07w.js +13 -0
  415. package/web/assets/index-CWFI83mF.css +1 -0
  416. package/web/index.html +2 -2
  417. package/browser/editor/components/callout/page.mjs +0 -13
  418. package/dist/chunk-2PBALTR3.mjs +0 -57
  419. package/dist/chunk-3LA3EMDE.mjs +0 -15
  420. package/dist/chunk-5FRMCDPY.mjs +0 -16
  421. package/dist/chunk-5LEK2CBS.mjs +0 -2
  422. package/dist/chunk-5OFN5AHC.mjs +0 -2
  423. package/dist/chunk-5SIYS5HN.mjs +0 -2
  424. package/dist/chunk-5TEK3RAD.mjs +0 -2
  425. package/dist/chunk-6CYG3Z4V.mjs +0 -2
  426. package/dist/chunk-7GHT2UCA.mjs +0 -15
  427. package/dist/chunk-7QQBR7PB.mjs +0 -2
  428. package/dist/chunk-ATCJYAR4.mjs +0 -2
  429. package/dist/chunk-C7HWOBIF.mjs +0 -2
  430. package/dist/chunk-E7VYDG2X.mjs +0 -2
  431. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  432. package/dist/chunk-FQGQH4KM.mjs +0 -2
  433. package/dist/chunk-G7TOXONE.mjs +0 -6
  434. package/dist/chunk-GPF4YWWB.mjs +0 -9
  435. package/dist/chunk-GYQV7Y4J.mjs +0 -120
  436. package/dist/chunk-HMHK3EQ6.mjs +0 -2
  437. package/dist/chunk-HQQMJCQJ.mjs +0 -10
  438. package/dist/chunk-IERUOGC3.mjs +0 -6
  439. package/dist/chunk-ITNLYXXV.mjs +0 -3
  440. package/dist/chunk-L4DVONOG.mjs +0 -2
  441. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  442. package/dist/chunk-LC7YNQFG.mjs +0 -2
  443. package/dist/chunk-LHTFLRB6.mjs +0 -4
  444. package/dist/chunk-NFIBI4IP.mjs +0 -26
  445. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  446. package/dist/chunk-O52JMOCZ.mjs +0 -2
  447. package/dist/chunk-P2CAZPDP.mjs +0 -3
  448. package/dist/chunk-QMQUXTU4.mjs +0 -12
  449. package/dist/chunk-QVWVFC5K.mjs +0 -6
  450. package/dist/chunk-QYKCJ7FG.mjs +0 -8
  451. package/dist/chunk-QZY6Q64C.mjs +0 -2
  452. package/dist/chunk-TDQP3DKQ.mjs +0 -10
  453. package/dist/chunk-TX6JH7TR.mjs +0 -2
  454. package/dist/chunk-UNUEWH4S.mjs +0 -2
  455. package/dist/chunk-V4CPWKMZ.mjs +0 -2
  456. package/dist/chunk-VACRB2NE.mjs +0 -3
  457. package/dist/chunk-W63L7367.mjs +0 -7
  458. package/dist/chunk-W7CXPCRV.mjs +0 -6
  459. package/dist/chunk-XINNG4F2.mjs +0 -4
  460. package/dist/chunk-YL2DPWVS.mjs +0 -4
  461. package/dist/chunk-YZ5Z6H4W.mjs +0 -4
  462. package/dist/chunk-ZBIM6ARY.mjs +0 -216
  463. package/skill/references/building-components.md +0 -327
  464. package/web/assets/index-B6Ai3wsa.js +0 -13
  465. package/web/assets/index-SYPC3EZy.css +0 -1
@@ -0,0 +1,447 @@
1
+ import { t } from "../../../shared/i18n/index.mjs";
2
+ import { onPreferences, preference } from "../../../shared/preferences.mjs";
3
+ import { readText, rangeOf } from "../annotate/anchors.mjs";
4
+ import { GONE_WORDS, nestedBlocks } from "../core.mjs";
5
+ import { createFills } from "../fills.mjs";
6
+ import { FOLLOW, task } from "../frames.mjs";
7
+ import { onSettle, own, provide } from "../overlays.mjs";
8
+ import { failure, postAnswer } from "../post.mjs";
9
+ import { addStyle } from "../ui.mjs";
10
+ import { UNDO_SHOWN, createBar } from "./bar.mjs";
11
+ import { GHOST_STYLE, createGhosts } from "./ghost.mjs";
12
+ import { editsBy, goneMark, placeOf, putMark, takeMark } from "./gone-words.mjs";
13
+ import { wordMarks } from "./words.mjs";
14
+
15
+ // The Agent's changes on the page (docs/editor.md "Agent's changes"), as Cursor reviews an Agent's:
16
+ // what an Agent wrote to the report since the reader last kept or reverted it (the server's review,
17
+ // editor/changes.mjs, read at the editor's start and never holding it back, drawn once the page is
18
+ // ready), marked without a change to the file: a block added (or changed with no words to tell, or
19
+ // whose words cannot be compared) a green fill (fills.mjs), words added the 批注's look in green (a
20
+ // CSS highlight under the 批注's own), the words taken out shown where they were, in red struck
21
+ // through (gone-words.mjs: an empty inline mark of the editor's own, its words drawn by CSS, which
22
+ // nothing that reads or writes the block's content takes), the blocks deleted shown where they were,
23
+ // a ghost of them in red struck through in room made for it (ghost.mjs, drawn in the overlay layer:
24
+ // nothing of it in the document). Nothing to rest the pointer on: all of it shows at once. The reader
25
+ // editing a block takes its words taken out away first (the keys, input, an input method, a paste,
26
+ // cut or drop in it, the format bar over it), and they are not drawn in it again until the caret has
27
+ // left it (the review read again then). The bar at the window's bottom (bar.mjs) says how many blocks
28
+ // and offers 撤回 (the server writes the baseline back: the reader's own saved edits stay; undone within
29
+ // UNDO_SHOWN from the bar of the page shown again) and 确认 (no more marks). 显示变更 off (设置),
30
+ // nothing is drawn and a review read is kept at once, quietly. The page reads the review again when
31
+ // its server says it changed (another tab kept it, an Agent wrote more) and 1.5s after a commit of its
32
+ // own while the bar shows. Not on a page opened on its own in a frame (?solo), printed, or presented
33
+ // (the preview's ?present has no editor).
34
+
35
+ const ROUTE = "/__gallery/edit/changes";
36
+ const REREAD_AFTER = 1500; // after a commit of the page's own, the review read again this long after (the reader paused)
37
+ const LEFT_AFTER = 150; // the caret left a block the reader edited: the review read again this long after (once saved)
38
+ const KEEP_TRIES = 3; // a quiet keep refused (the review grew meanwhile) is tried again this often
39
+ // What may edit the block it happens in: its words taken out go first (gone-words.mjs editsBy for a key).
40
+ const EDITS = ["keydown", "beforeinput", "compositionstart", "paste", "cut", "drop", "dragstart"];
41
+
42
+ const STYLE = `
43
+ ::highlight(ge-diff-add) { background-color: var(--ge-diff-add); text-decoration: underline 2px var(--ge-diff-add-line); }
44
+ .ge-diff-del { user-select: none; pointer-events: none; }
45
+ .ge-diff-del::before { content: attr(data-ge-gone-words); border-radius: 3px; background: var(--ge-diff-del); color: var(--ge-diff-del-text); text-decoration: line-through; text-decoration-color: var(--ge-diff-del-text); -webkit-box-decoration-break: clone; box-decoration-break: clone; }
46
+ .ge-diff-del[data-ge-gone-lead]::before { margin-left: .28em; }
47
+ .ge-diff-del[data-ge-gone-tail]::after { content: " "; }
48
+ ${GHOST_STYLE}
49
+ @media print { ::highlight(ge-diff-add) { background-color: transparent; text-decoration: none; } .ge-diff-del, .ge-diff-halo { display: none; } }
50
+ `;
51
+
52
+ const highlights = () => Boolean(globalThis.CSS?.highlights && globalThis.Highlight);
53
+ const holdsSelection = (block) => {
54
+ const selection = getSelection();
55
+ return selection.rangeCount > 0 && (block.contains(selection.anchorNode) || block.contains(selection.focusNode));
56
+ };
57
+
58
+ /**
59
+ * slug, token: the map's; commits(), autosave(): the page's (made later than this); flush(): what
60
+ * waits saved now (as Ctrl+S); say(text, kind); session: sessionStorage (storage.mjs storageOf), where
61
+ * a revert's undo is kept over the page shown again; across: the selection across blocks (text-select/:
62
+ * texts(), redraw(texts)), kept on its words while marks go and come. Returns { start(),
63
+ * committed(receipt), leave() }.
64
+ */
65
+ export function createChanges({ slug, token, commits, autosave, flush, say, session, across = null }) {
66
+ const undoKey = `gallery-edit:${slug}:changes-undo`;
67
+ const shows = () => preference("showChanges");
68
+ let review = null; // the review as last read and drawn: { stamp, counts, added, changed, removed }
69
+ let started = false;
70
+ let left = false;
71
+ let busy = false; // 撤回, 确认 or 撤销 under way
72
+ let reading = read(); // at once: the page's start does not wait for it
73
+ let rereadTimer = 0;
74
+ let keepTries = 0;
75
+ let offering = 0; // until when the bar offers 撤销 of this tab's revert
76
+ const filled = new Set(); // block elements filled green
77
+ const changedBlocks = new Map(); // a text block -> its words in the baseline
78
+ const added = new Map(); // a text block -> the ranges of its words added (the green highlight's)
79
+ let dels = []; // the words taken out shown: { node, block }
80
+ const edited = new Set(); // text blocks the reader edited since their words taken out were drawn
81
+ const ghosts = createGhosts();
82
+ let fills = null;
83
+ const bar = createBar({ revert: () => revert(), accept: () => keep() });
84
+
85
+ function read() {
86
+ return postAnswer(ROUTE, token, { slug, action: "read" });
87
+ }
88
+
89
+ // ---------- finding the blocks it names (in the file now) ----------
90
+ const unitOf = (id) => document.querySelector(`[data-ge-unit="${id}"]`);
91
+ const body = () => document.querySelector("main");
92
+ function find(ref) {
93
+ if (!ref) return null;
94
+ const main = body() ?? document;
95
+ if (ref.title) return main.querySelector('[data-ge-kind="title"]'); // the page's own title, as the editor made it a text block
96
+ if (ref.id) return main.querySelector(`[data-block-id="${CSS.escape(ref.id)}"]`);
97
+ if (typeof ref.hash !== "string") return null;
98
+ const list = commits();
99
+ if (!list) return null;
100
+ let nth = 0;
101
+ for (const node of main.querySelectorAll("[data-ge-unit]")) {
102
+ if (list.fileHash(Number(node.getAttribute("data-ge-unit"))) !== ref.hash) continue;
103
+ if (nth++ === ref.nth) return node;
104
+ }
105
+ return null;
106
+ }
107
+ // A unit's own text blocks in the page's order (the server's: its scope line's first, if a table's).
108
+ function ownBlocks(unit, ref) {
109
+ if (ref.title) return [unit];
110
+ const owner = (block) => {
111
+ const at = block.closest("[data-ge-unit], [data-ge-lead]");
112
+ return at?.hasAttribute("data-ge-lead") && !at.hasAttribute("data-ge-unit") ? unitOf(at.getAttribute("data-ge-lead")) : at;
113
+ };
114
+ const lead = document.querySelector(`[data-ge-lead="${unit.getAttribute("data-ge-unit")}"]`);
115
+ const within = (root) => [root, ...root.querySelectorAll("[data-ge-block]")].filter((block) => block.hasAttribute("data-ge-block") && owner(block) === unit);
116
+ return [...(lead && lead !== unit ? within(lead) : []), ...within(unit)];
117
+ }
118
+ // A text block's words as the server reads them: a list item's own, not the blocks it holds.
119
+ const readBlock = (block) => {
120
+ const held = nestedBlocks(block);
121
+ return readText([block], held.length ? { skip: (child) => held.includes(child) } : undefined);
122
+ };
123
+
124
+ // ---------- drawing ----------
125
+ const frames = task("changes", { run: place, fail: () => keepingAcross(takeDown) });
126
+ // A selection across blocks (text-select/) kept on its words while marks go and come: its ends are
127
+ // points of its own, which a text node joined again (takeMark) would leave off the page, and so its
128
+ // pieces' live ranges are taken first (a text node split, putMark, they follow; one joined, takeMark
129
+ // sets them) and it is drawn again from them.
130
+ function keepingAcross(change) {
131
+ const texts = across?.texts() ?? null;
132
+ change(texts ? texts.map(({ range }) => range) : []);
133
+ if (texts) across.redraw(texts);
134
+ }
135
+ function takeDown(ranges = []) {
136
+ fills?.clear();
137
+ for (const { node } of dels) if (node.isConnected) takeMark(node, ranges);
138
+ dels = [];
139
+ ghosts.clear();
140
+ filled.clear();
141
+ changedBlocks.clear();
142
+ added.clear();
143
+ if (highlights()) CSS.highlights.delete("ge-diff-add");
144
+ }
145
+
146
+ function draw() {
147
+ keepingAcross((ranges) => drawNow(ranges));
148
+ }
149
+ function drawNow(ranges) {
150
+ takeDown(ranges);
151
+ if (!review || !shows()) {
152
+ edited.clear();
153
+ return;
154
+ }
155
+ addStyle(STYLE);
156
+ fills ??= createFills({ kind: "changes", className: "ge-diff-halo", stands: (block) => filled.has(block) });
157
+ for (const { ref } of review.added) {
158
+ const unit = find(ref);
159
+ if (unit) filled.add(unit);
160
+ }
161
+ for (const change of review.changed) {
162
+ const unit = find(change.ref);
163
+ if (!unit) continue;
164
+ if (change.whole) {
165
+ filled.add(unit);
166
+ continue;
167
+ }
168
+ const blocks = ownBlocks(unit, change.ref);
169
+ for (const { nth, base } of change.texts ?? []) {
170
+ if (blocks[nth]) changedBlocks.set(blocks[nth], base);
171
+ }
172
+ }
173
+ // A block the reader edited keeps its words taken out away while the caret is in it.
174
+ for (const block of [...edited]) if (!changedBlocks.has(block) || !holdsSelection(block)) edited.delete(block);
175
+ for (const [block, base] of changedBlocks) markWords(block, base, !edited.has(block));
176
+ paintAdded();
177
+ for (const group of review.removed) {
178
+ const anchor = group.before ? find(group.before) : group.after ? find(group.after) : body();
179
+ if (anchor) ghosts.add(group, anchor, group.before ? "before" : group.after ? "after" : "into");
180
+ }
181
+ if (filled.size || added.size || dels.length || ghosts.count) frames.wake();
182
+ }
183
+
184
+ // A changed text block's marks: the ranges of its words added (live: they follow the reader's
185
+ // typing), and, `taken`, its words taken out put in where they were (from the last, so that each
186
+ // place read before is still where it was: a text node is split only after it).
187
+ function markWords(block, base, taken) {
188
+ const text = readBlock(block);
189
+ const { ins, cuts } = wordMarks(base, text.text);
190
+ added.set(block, ins.map(([from, to]) => rangeOf(text, from, to)).filter(Boolean));
191
+ if (!taken) return;
192
+ for (const { at, text: words } of [...cuts].reverse()) {
193
+ const where = placeOf(text, at);
194
+ if (!where) continue;
195
+ const node = goneMark(words, document, { before: text.text[at - 1], after: text.text[at] });
196
+ const entry = { node, block };
197
+ own(node, "changes", { owner: block, alive: () => block.contains(node) && dels.includes(entry) });
198
+ putMark(node, where);
199
+ dels.push(entry);
200
+ }
201
+ }
202
+
203
+ function paintAdded() {
204
+ if (!highlights()) return;
205
+ const ranges = [...added.values()].flat();
206
+ if (ranges.length === 0) return void CSS.highlights.delete("ge-diff-add");
207
+ const highlight = new Highlight(...ranges);
208
+ highlight.priority = -1; // a 批注's marks over these
209
+ CSS.highlights.set("ge-diff-add", highlight);
210
+ }
211
+
212
+ // Each frame something may have moved: the fills where their blocks are, the ghosts where their rooms are.
213
+ function place() {
214
+ if (!review) return false;
215
+ fills?.paint([...filled].filter((block) => block.isConnected));
216
+ ghosts.place();
217
+ return filled.size || ghosts.count ? FOLLOW : false;
218
+ }
219
+
220
+ // What the page's edits took off the page goes with it (a block deleted, its marks): the words taken
221
+ // out shown in a block deleted or turned into another are taken out of it too, so that undo, which
222
+ // puts the very same element back, puts back none of them.
223
+ // A copy of a mark that a change made (a cell turned into a header cell or back, cells merged or
224
+ // filled, a disclosure's title made a paragraph, a block duplicated: whatever clones a block's
225
+ // content) stands for nothing and goes at once, before the overlays' audit would find it a stray:
226
+ // the review read again after the change's commit draws the words taken out where they are then.
227
+ onSettle(() => {
228
+ const drawn = dels.length > 0;
229
+ for (const block of [...filled]) if (!block.isConnected) filled.delete(block);
230
+ dels = dels.filter((entry) => {
231
+ if (entry.block.isConnected && entry.node.isConnected) return true;
232
+ if (entry.node.parentNode) takeMark(entry.node);
233
+ return false;
234
+ });
235
+ if (drawn) {
236
+ const ours = new Set(dels.map((entry) => entry.node));
237
+ for (const node of document.querySelectorAll(`main ${GONE_WORDS}`)) if (!ours.has(node)) takeMark(node);
238
+ }
239
+ ghosts.settle();
240
+ });
241
+
242
+ // ---------- the reader edits a block holding words taken out ----------
243
+ // They go first, before the edit touches the block (window, capture: before the editor's own
244
+ // listeners): the text either side of each one text node again, the caret where it was, and the
245
+ // block's words added marked anew (a range that ended in the half that went would cover the wrong
246
+ // words). The block is `edited`: drawn without them until the caret leaves it.
247
+ // The blocks an edit may touch: the one it happens in, those the selection reaches, the browser's
248
+ // or the page's across blocks (text-select/, which the browser's then holds none of).
249
+ function blocksOf(event) {
250
+ const selection = getSelection();
251
+ const range = selection.rangeCount ? selection.getRangeAt(0) : null;
252
+ const target = event?.target instanceof Node ? event.target : null;
253
+ const reached = across?.texts()?.map(({ host }) => host) ?? [];
254
+ const hit = new Set();
255
+ for (const { block } of dels) {
256
+ if (hit.has(block)) continue;
257
+ if ((target && block.contains(target)) || (range && range.intersectsNode(block)) || reached.some((host) => host === block || host.contains(block) || block.contains(host))) hit.add(block);
258
+ }
259
+ return hit;
260
+ }
261
+ function takeOut(blocks) {
262
+ if (blocks.size === 0) return;
263
+ keepingAcross((ranges) => {
264
+ for (const entry of dels) if (blocks.has(entry.block) && entry.node.isConnected) takeMark(entry.node, ranges);
265
+ });
266
+ dels = dels.filter((entry) => !blocks.has(entry.block));
267
+ for (const block of blocks) {
268
+ edited.add(block);
269
+ const base = changedBlocks.get(block);
270
+ if (base !== undefined) markWords(block, base, false);
271
+ }
272
+ paintAdded();
273
+ }
274
+ function editing(event) {
275
+ if (dels.length === 0 || (event.type === "keydown" && !editsBy(event))) return;
276
+ takeOut(blocksOf(event));
277
+ }
278
+ // The format bar over words of such a block (its buttons keep the selection where it is).
279
+ function pressed(event) {
280
+ if (dels.length > 0 && event.target instanceof Element && event.target.closest(".ge-format")) takeOut(blocksOf(null));
281
+ }
282
+ // The caret left a block the reader edited: the review read again once what waits is saved, and drawn.
283
+ function moved() {
284
+ if (edited.size === 0) return;
285
+ let leaving = false;
286
+ for (const block of [...edited]) {
287
+ if (block.isConnected && holdsSelection(block)) continue;
288
+ edited.delete(block);
289
+ leaving = true;
290
+ }
291
+ if (leaving) rereadSaved(LEFT_AFTER);
292
+ }
293
+ for (const type of EDITS) addEventListener(type, editing, true);
294
+ addEventListener("pointerdown", pressed, true);
295
+ document.addEventListener("selectionchange", moved);
296
+
297
+ // ---------- what the server says ----------
298
+ function take(answer) {
299
+ if (left) return;
300
+ review = answer?.ok && answer.pending ? answer : null;
301
+ if (review && !shows()) return void keep({ quiet: true });
302
+ if (busy) return;
303
+ if (review) bar.counts(review.counts);
304
+ else if (Date.now() >= offering) bar.hide();
305
+ draw();
306
+ }
307
+ async function reread() {
308
+ clearTimeout(rereadTimer);
309
+ if (left || !started) return;
310
+ reading = read();
311
+ take(await reading);
312
+ }
313
+ // Read again `after` ms from now, once nothing waits to be saved (the reader paused).
314
+ function rereadSaved(after) {
315
+ clearTimeout(rereadTimer);
316
+ const again = () => {
317
+ if (autosave()?.state.waiting > 0) rereadTimer = setTimeout(again, REREAD_AFTER);
318
+ else reread();
319
+ };
320
+ rereadTimer = setTimeout(again, after);
321
+ }
322
+
323
+ // 确认: the review ended; `quiet`, as the setting being off does it (nothing said).
324
+ async function keep({ quiet = false } = {}) {
325
+ if (!review || busy) return;
326
+ const stamp = review.stamp;
327
+ busy = true;
328
+ review = null;
329
+ draw();
330
+ bar.hide();
331
+ const answer = await postAnswer(ROUTE, token, { slug, action: "accept", stamp });
332
+ busy = false;
333
+ if (answer.ok) {
334
+ keepTries = 0;
335
+ return;
336
+ }
337
+ // Refused (the review grew meanwhile, the file was held): read again, and kept again when quiet, a few times at most.
338
+ if (quiet) {
339
+ if (++keepTries > KEEP_TRIES) return;
340
+ } else if (answer.reason === "changed") say(failure(answer, t("Agent 刚刚又改了文档,再看一眼")), "note");
341
+ else say(t("没能确认:{0}", failure(answer, t("预览服务器出错"))), "error");
342
+ reread();
343
+ }
344
+
345
+ // 撤回: what waits is saved first, and nothing may wait for the reader (a conflict); then the server
346
+ // writes the baseline, and the page is shown again as after a write of its own (autosave showOwn: no
347
+ // question of what that write put back of the reader's, which the reader asked for).
348
+ async function revert() {
349
+ if (!review || busy) return;
350
+ const stamp = review.stamp;
351
+ busy = true;
352
+ await flush();
353
+ const state = autosave()?.state;
354
+ if (!state || state.waiting > 0 || state.syncing || state.held > 0 || state.offline) {
355
+ busy = false;
356
+ say(t("还有修改没保存或有冲突待处理,处理完再撤回"), "error");
357
+ return;
358
+ }
359
+ let answer = null;
360
+ const shown = await autosave().showOwn(async () => {
361
+ answer = await postAnswer(ROUTE, token, { slug, action: "revert", stamp });
362
+ if (!answer.ok || answer.none) return false;
363
+ session?.set(undoKey, JSON.stringify({ undo: answer.undo, at: Date.now() }));
364
+ return answer.version || true; // the version it wrote: the page shown again on another asks what that put back
365
+ });
366
+ if (shown) return; // the page goes: the one shown again offers 撤销
367
+ busy = false;
368
+ if (answer?.ok) return void reread(); // nothing was under review any more
369
+ say(answer?.reason === "changed" ? failure(answer, t("Agent 刚刚又改了文档,再看一眼")) : t("没能撤回:{0}", failure(answer ?? {}, t("预览服务器出错"))), answer?.reason === "changed" ? "note" : "error");
370
+ reread();
371
+ }
372
+
373
+ // 撤销 of a revert: the server writes back what the revert wrote over, while the file is still the revert's.
374
+ async function undo(id) {
375
+ if (busy) return;
376
+ busy = true;
377
+ offering = 0;
378
+ session?.remove(undoKey);
379
+ bar.hide();
380
+ await flush();
381
+ let answer = null;
382
+ const shown = await autosave().showOwn(async () => {
383
+ answer = await postAnswer(ROUTE, token, { slug, action: "undo", undo: id });
384
+ return answer.ok ? answer.version || true : false;
385
+ });
386
+ if (shown) return;
387
+ busy = false;
388
+ say(t("没能撤销:{0}", failure(answer ?? {}, t("预览服务器出错"))), "error");
389
+ }
390
+
391
+ // A revert of this tab's, the page shown again since: 撤销 on the bar for what is left of UNDO_SHOWN.
392
+ function undoShown() {
393
+ let kept = null;
394
+ try { kept = JSON.parse(session?.get(undoKey) ?? "null"); } catch {}
395
+ const remaining = kept && typeof kept.undo === "string" ? UNDO_SHOWN - (Date.now() - kept.at) : 0;
396
+ if (remaining <= 0) {
397
+ session?.remove(undoKey);
398
+ return false;
399
+ }
400
+ offering = Date.now() + remaining;
401
+ bar.reverted(remaining, () => undo(kept.undo));
402
+ setTimeout(() => session?.remove(undoKey), remaining);
403
+ return true;
404
+ }
405
+
406
+ // The server's word that the review changed (another tab kept or reverted it, the Agent wrote more).
407
+ const heard = () => {
408
+ clearTimeout(rereadTimer);
409
+ rereadTimer = setTimeout(reread, 100);
410
+ };
411
+ addEventListener("doc-changes-changed", heard);
412
+ // 显示变更 switched (设置, another page of the origin): off, what is under review is kept and nothing drawn; on, read again.
413
+ onPreferences(() => {
414
+ if (!started || left) return;
415
+ if (!shows()) {
416
+ if (review) keep({ quiet: true });
417
+ return;
418
+ }
419
+ reread();
420
+ });
421
+
422
+ provide("changes", () => ({ pending: Boolean(review), counts: review?.counts ?? null, filled: filled.size, words: dels.length, ghosts: ghosts.count, edited: edited.size }));
423
+
424
+ return {
425
+ /** The page is ready: what was read is drawn (the bar, or 撤销 after a revert of this tab's). */
426
+ async start() {
427
+ if (started) return;
428
+ started = true;
429
+ undoShown();
430
+ take(await reading);
431
+ },
432
+ /** A commit of the page's was taken in: while the bar shows, the review is read again once the reader pauses. */
433
+ committed(receipt) {
434
+ if (!receipt?.wrote || !review || !started) return;
435
+ rereadSaved(REREAD_AFTER);
436
+ },
437
+ /** The page goes. */
438
+ leave() {
439
+ left = true;
440
+ clearTimeout(rereadTimer);
441
+ removeEventListener("doc-changes-changed", heard);
442
+ for (const type of EDITS) removeEventListener(type, editing, true);
443
+ removeEventListener("pointerdown", pressed, true);
444
+ document.removeEventListener("selectionchange", moved);
445
+ },
446
+ };
447
+ }
@@ -0,0 +1,27 @@
1
+ import { wordDiff } from "../merge.mjs";
2
+
3
+ // The words an Agent changed in a text block, as the page marks them (docs/editor.md "Agent's
4
+ // changes"): the word diff of the block's text in the baseline against its text on the page
5
+ // (merge.mjs wordDiff), as places in the page's text.
6
+
7
+ /**
8
+ * What `now` (the block's text on the page) has that `base` did not and where `base`'s words were
9
+ * taken out of it: { ins: [[from, to]] (ranges of `now`), cuts: [{ at, text }] (a place in `now`,
10
+ * what was taken out there; one where several runs meet) }.
11
+ */
12
+ export function wordMarks(base, now) {
13
+ const ins = [];
14
+ const cuts = [];
15
+ let at = 0;
16
+ for (const { tag, text } of wordDiff(base, now)) {
17
+ if (tag === "del") {
18
+ const last = cuts.at(-1);
19
+ if (last?.at === at) last.text += text;
20
+ else cuts.push({ at, text });
21
+ continue;
22
+ }
23
+ if (tag === "ins") ins.push([at, at + text.length]);
24
+ at += text.length;
25
+ }
26
+ return { ins, cuts };
27
+ }
@@ -1,8 +1,8 @@
1
1
  // Pasted colors onto the page's nine (feishu-lite model/color-match.ts and css-color.ts, after Feishu
2
2
  // 855470 · bn / mG / WW, 99121 · FB, 768176 CIEDE2000; Notion keeps only its own colors). Pure, no
3
3
  // DOM. A color is given as the CSS text of a style (named, #hex, rgb[a](), hsl[a]()).
4
- // exact a color equal to one of the page's (the kit's light values, doc-text-* and
5
- // doc-highlight-*) is that one: what a page whose styles are not meant (any web page)
4
+ // exact a color equal to one of the page's (the kit's light values: doc-text-*, and
5
+ // doc-highlight-* as a block's fill or a run of words') is that one: what a page whose styles are not meant (any web page)
6
6
  // keeps
7
7
  // nearest for sources whose styles are meant (Word, Google Docs, Notion): text — too dark (HSV
8
8
  // v ≤ 0.3) none, unsaturated (s < 0.08) gray, a pure CSS color by a fixed table, else the
@@ -14,7 +14,7 @@ import { BODY, COLORS } from "../../../../shared/colors.mjs";
14
14
 
15
15
  // The page's light values (src/shared/colors.mjs).
16
16
  const TEXT = COLORS.map(({ name, text }) => [name, text[0]]);
17
- const BACKGROUND = COLORS.map(({ name, fill }) => [name, fill[0]]);
17
+ const BACKGROUND = COLORS.flatMap(({ name, fill, mark }) => [[name, fill[0]], [name, mark[0]]]);
18
18
  const BODY_TEXT = BODY.text[0];
19
19
  const BODY_BACKGROUND = BODY.background[0];
20
20
 
@@ -332,7 +332,11 @@ function equationOf(el) {
332
332
  return el.getAttribute(ATTR.equation) || el.getAttribute(ATTR.inlineEquation) || annotation || /^\$\$([\s\S]+?)\$\$\s*$/.exec(text)?.[1].trim() || /^\$([\s\S]+?)\$$/.exec(text)?.[1].trim();
333
333
  }
334
334
 
335
- function richTextOf(node, { stripText = false, stripSurroundingWhitespace = false, ignoreCodeBlock = false, preserveLineBreaks = false } = {}) {
335
+ // Besides Notion's: a picture in a table's cell (`pictures`) is a token of its own, PICTURE annotated
336
+ // ["img", its address, its alt, "", its width] (inline.mjs's pieces, the width of an <img width>), which
337
+ // a cell of the page shows (to-blocks.mjs richHtml).
338
+ const PICTURE = String.fromCodePoint(0xfffc);
339
+ function richTextOf(node, { stripText = false, stripSurroundingWhitespace = false, ignoreCodeBlock = false, preserveLineBreaks = false, pictures = false } = {}) {
336
340
  const doc = node.ownerDocument;
337
341
  if (!stripText) {
338
342
  // Paragraphs, headings and items as lines with their bullets (the structure kept in one text).
@@ -354,17 +358,18 @@ function richTextOf(node, { stripText = false, stripSurroundingWhitespace = fals
354
358
  all.push(n);
355
359
  for (const child of n.childNodes) walk(child);
356
360
  })(node);
357
- const nodes = all.filter((n) => (n.nodeType !== 3 ? preserveLineBreaks && tagOf(n) === "br" : !(stripText && BLANK_LINES.test(n.data))));
361
+ const picture = (n) => pictures && tagOf(n) === "img" && Boolean(n.getAttribute("src"));
362
+ const nodes = all.filter((n) => (n.nodeType !== 3 ? (preserveLineBreaks && tagOf(n) === "br") || picture(n) : !(stripText && BLANK_LINES.test(n.data))));
358
363
  const tokens = [];
359
364
  const seenEquations = new Set();
360
365
  nodes.forEach((n, i) => {
361
- let text = n.nodeType === 3 ? n.data : "\n";
366
+ let text = n.nodeType === 3 ? n.data : picture(n) ? PICTURE : "\n";
362
367
  if (stripText) text = text.replace(/\[ \] /g, "").replace(/\[x\] /g, "").replace(/^[-*•]\s?/g, "").replace(/(\s)+/g, "$1");
363
368
  if (i === 0 && stripSurroundingWhitespace && !node.hasAttribute?.("confluenceTabSpaces")) text = text.trimStart();
364
369
  text = clean(text);
365
370
  if (i === nodes.length - 1 && stripSurroundingWhitespace) text = text.trimEnd();
366
371
  const up = ancestors(n).filter((a) => a.nodeType === 1);
367
- const annotations = [];
372
+ const annotations = picture(n) ? [["img", n.getAttribute("src"), n.getAttribute("alt") ?? "", "", n.getAttribute("width") ?? ""]] : [];
368
373
  if (!stripText && up.some((a) => HEADINGS.includes(tagOf(a)))) annotations.push(["b"]);
369
374
  if (up.some((a) => tagOf(a) === "strong")) annotations.push(["b"]);
370
375
  if (up.some((a) => tagOf(a) === "em")) annotations.push(["i"]);
@@ -526,7 +531,8 @@ const int = (value, fallback) => {
526
531
  };
527
532
 
528
533
  // 760354 table: row and column spans spread into empty cells; a first row all th a header row, every
529
- // row starting with a th a header column (both: the side with more th's).
534
+ // row starting with a th a header column (both: the side with more th's). Its cells' pictures kept
535
+ // (richTextOf `pictures`, besides Notion's).
530
536
  function tableBlock(node) {
531
537
  const rows = [...node.querySelectorAll("tr")].filter((row) => row.closest("table") === node);
532
538
  let headerRow = false;
@@ -550,7 +556,7 @@ function tableBlock(node) {
550
556
  while (taken[r][c]) c++;
551
557
  const rowSpan = int(cell.getAttribute("rowspan"), 1);
552
558
  const colSpan = int(cell.getAttribute("colspan"), 1);
553
- grid[r][c] = richTextOf(cell, { stripSurroundingWhitespace: true, preserveLineBreaks: true });
559
+ grid[r][c] = richTextOf(cell, { stripSurroundingWhitespace: true, preserveLineBreaks: true, pictures: true });
554
560
  taken[r][c] = true;
555
561
  for (let dr = 0; dr < rowSpan && r + dr < rows.length; dr++) {
556
562
  for (let dc = 0; dc < colSpan; dc++) {
@@ -716,6 +722,23 @@ function blockOf(node) {
716
722
 
717
723
  const blocksOf = (nodes) => nodes.flatMap(blockOf);
718
724
 
725
+ /**
726
+ * Besides Notion's (another program's cells pasted into a table's: page/clipboard/ cellsOf): the
727
+ * HTML's table, when it holds one and no words outside it, as rows of rich text, its cells' as
728
+ * tableBlock reads them (spans spread into empty cells, pictures kept); a table of one column stays
729
+ * one (normalizing makes Notion's paragraphs of it). Null for anything else.
730
+ */
731
+ export function tableRows(root, options = {}) {
732
+ const tables = [...root.querySelectorAll("table")].filter((table) => !table.parentElement?.closest("table"));
733
+ const outside = (n) => (n.nodeType === 3 ? n.data.trim() !== "" : n.nodeType === 1 && tagOf(n) !== "table" && !REMOVED.has(tagOf(n)) && [...n.childNodes].some(outside));
734
+ if (tables.length !== 1 || [...root.childNodes].some(outside)) return null;
735
+ tables[0].classList.add("simple-table"); // kept a table (firstPass)
736
+ normalizeHTML(root, { ...options, inline: false });
737
+ const table = root.querySelector("table");
738
+ const block = table ? tableBlock(table) : null;
739
+ return block ? block.children.map((row) => block.props.table_block_column_order.map((column) => row.props.cells[column] ?? [])) : null;
740
+ }
741
+
719
742
  // ---------- entry points ----------
720
743
 
721
744
  /** 760354 U: the HTML's blocks. */
@@ -27,8 +27,14 @@
27
27
  // and a bare a@b.c (GFM's); $$…$$ amid words a display formula (annotated display too, which
28
28
  // markdown.mjs makes a block of its own in a paragraph).
29
29
  // Besides Notion's annotations, these three: sup (superscript), sub (subscript) and display.
30
+ //
31
+ // Yuque's Markdown (its toolbar's 复制为 Markdown, read as a whole document with `yuque`: yuque.mjs)
32
+ // reads two more: a formula as Yuque writes one, $ … $ with a space inside each $ (Pandoc's rule
33
+ // refuses those), and the colors of a <font style="color: …; background-color: …"> (or a <span> so
34
+ // styled) as the page's nearest (colors.mjs: Yuque's colors are meant, as Word's are).
30
35
 
31
36
  import { addressOf } from "../../links/address.mjs";
37
+ import { backgroundColor, textColor } from "./colors.mjs";
32
38
 
33
39
  const WORD = /[\p{L}\p{N}]/u;
34
40
  const TRAILING = /[.,:;!?'")\]}>,。:;!?、)】」』]+$/u;
@@ -100,6 +106,20 @@ const attribute = (tag, name) => {
100
106
  };
101
107
  const decodeEntities = (text) => text.replace(/&(#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{1,31});/g, (all, name) => entity(name) ?? all);
102
108
 
109
+ // Yuque's Markdown: $ … $ with a space inside each $ a formula (its words trimmed), and a <font> or
110
+ // <span> whose style colors its words those colors, the nearest of the page's (none: its words alone).
111
+ const styleValue = (style, name) => new RegExp(`(?:^|;)\\s*${name}\\s*:\\s*([^;]+)`, "i").exec(style ?? "")?.[1].trim() ?? "";
112
+ const YUQUE_RULES = [
113
+ [/^\$ +([^$\n]*?[^\s$]) +\$(?!\d)/, (m) => ["⁍", ["e", m[1]], false]],
114
+ [/^<(font|span)\s([^<>]*)>([\s\S]*?)<\/\1\s*>/i, (m) => {
115
+ const style = attribute(`<${m[1]} ${m[2]}>`, "style");
116
+ const text = textColor(styleValue(style, "color"), { nearest: true });
117
+ const background = backgroundColor(styleValue(style, "background(?:-color)?"), { nearest: true });
118
+ const colors = [text, background].filter(Boolean).map((name) => ["h", name]);
119
+ return [m[3], colors.length ? colors : null, true];
120
+ }],
121
+ ];
122
+
103
123
  /** A picture's piece: U+FFFC annotated ["img", its address, its alt, its title]. */
104
124
  const picture = (src, alt, title) => ["\u{FFFC}", ["img", src.replace(/^<|>$/g, ""), alt ?? "", title ?? ""], false];
105
125
  const unquoted = (text) => (text ? text.slice(1, -1) : "");
@@ -123,6 +143,7 @@ function documentRules(doc) {
123
143
  }
124
144
  };
125
145
  const rules = [
146
+ ...(doc.yuque ? YUQUE_RULES : []),
126
147
  RULES[0], RULES[1],
127
148
  // $$ … $$ amid words: a display formula (markdown.mjs makes it a block of its own in a paragraph).
128
149
  [/^\$\$(.+?)\$\$/, (m) => ["⁍", [["e", m[1].trim()], ["display"]], false]],
@@ -185,6 +206,17 @@ export const parseInlineMath = (text) => parseInline(text, MATH_RULES);
185
206
  */
186
207
  export const parseInlineMarkdown = (text, doc = null) => parseInline(text, doc ? (doc.rules ??= documentRules(doc)) : RULES); // a document's made once
187
208
 
209
+ // A table cell's pictures, a linked one's too (its link left: the cell's picture is what it shows),
210
+ // ahead of Notion's rules (a paste's Markdown table: 单元格里的图片, which a cell holds).
211
+ const CELL_RULES = [
212
+ [new RegExp(String.raw`^\[!\[([^\]]*)\]${LINK_TARGET}\]${LINK_TARGET}`), (m) => picture(m[2], m[1], unquoted(m[3]))],
213
+ [new RegExp(String.raw`^!\[([^\]]*)\]${LINK_TARGET}`), (m) => picture(m[2], m[1], unquoted(m[3]))],
214
+ ...RULES,
215
+ ];
216
+
217
+ /** A pasted Markdown table's cell as rich text: Notion's rules, and its pictures ![alt](src) (inline.mjs's picture pieces). */
218
+ export const parseCellMarkdown = (text) => parseInline(text, CELL_RULES);
219
+
188
220
  /** HTML entities in text as their characters (&amp; &#8212;), one it does not know as written. */
189
221
  export { decodeEntities };
190
222