@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
@@ -12,9 +12,9 @@ import { asTitle, flowchartFromList, flowchartFromPanel, itemFromStep, listFromF
12
12
 
13
13
  // Turning blocks into other types on the page (DOM only; tested with jsdom): text, small text
14
14
  // (小字, the kit's .doc-note), four heading levels, bulleted, numbered and to-do lists and flow charts (流程图), disclosures
15
- // (折叠列表), callouts (提示块), highlight panels (高亮块: a container with the opening summary's
16
- // look, whose blocks keep their own types, lists and headings among them; a block turned into one, or
17
- // into a disclosure, goes into it as it is), code blocks, quotes and
15
+ // (折叠列表), highlight panels (高亮块: a container with the opening summary's look, whose blocks keep
16
+ // their own types, lists and headings among them) and callouts (提示块: the same kind of container, its
17
+ // look its own; a block turned into either, or into a disclosure, goes into it as it is), code blocks, quotes and
18
18
  // dividers; the types are the components' (src/editor/components/, registry.mjs), and so is how a
19
19
  // type that is no text block is made (its `create`). The "/" menu,
20
20
  // Markdown shortcuts, the block menu and its "在下方新建一块" all come here (editor.mjs:
@@ -48,7 +48,8 @@ const ownAttribute = (host, name) => Boolean(ATTRIBUTES[host.getAttribute("data-
48
48
 
49
49
  // The types a block, or a group of blocks, goes into as it is (each keeping its type: `takesAsIs`): a
50
50
  // highlight block (notion-lite's callout, whose body is its children: transformBlocks moves the blocks
51
- // into a new one) and, as the reader asked, a disclosure (into its content; its title left empty to type).
51
+ // into a new one), a callout (the same kind of block) and, as the reader asked, a disclosure (into its
52
+ // content; its title left empty to type).
52
53
  const TAKES_AS_IS = new Set(Object.keys(typesWith("takesAsIs")));
53
54
 
54
55
  // Whether a box takes the block of a type: what its role accepts, and none of a type that does not
@@ -181,7 +182,7 @@ export function containerType(unit) {
181
182
  const contentOf = (unit) => BLOCK_TYPES[containerType(unit)]?.content?.(unit) ?? unit;
182
183
 
183
184
  // The containers that turn into one another, what they hold carried along (Notion's: HOLDERS): a
184
- // disclosure, a quote, a highlight block. Each is the kind of box it is, as well as of block.
185
+ // disclosure, a quote, a highlight block, a callout. Each is the kind of box it is, as well as of block.
185
186
 
186
187
  // The blocks a disclosure, quote or highlight block holds (a highlight block's icon not among them).
187
188
  const heldBy = (unit) => heldIn(contentOf(unit));
@@ -272,8 +273,8 @@ function standIn(host, type) {
272
273
  return standsFor(next, host);
273
274
  }
274
275
 
275
- // Whether a block already is the text block of a type: its tag, and the class that tells a
276
- // callout from a div; of the paragraph types, the one its class makes it.
276
+ // Whether a block already is the text block of a type: its tag, and its class where its type has
277
+ // one; of the paragraph types, the one its class makes it.
277
278
  function isOfType(host, type) {
278
279
  const spec = BLOCK_TYPES[type];
279
280
  if (host.localName !== spec.tag) return false;
@@ -40,7 +40,7 @@ const WRAPPERS = new Set(["UL", "OL", "BLOCKQUOTE"]);
40
40
 
41
41
  // An inline element of the report's own, as the scanner judges it (source/blocks.mjs): its
42
42
  // attributes as the file writes them (not the page's data-ge-* markers).
43
- const isOwnAtom = (node) => ownInline(node.localName, [...node.attributes].filter(({ name }) => !MARKER.test(name)), node.textContent.trim() !== "");
43
+ const isOwnAtom =(node) => ownInline(node.localName, [...node.attributes].filter(({ name }) => !MARKER.test(name)), node.textContent.trim() !== "");
44
44
 
45
45
  // A block's atoms in `root` (its source parsed), in the order of the file, as the scanner lists
46
46
  // them (source/blocks.mjs inlineMode): the outermost formulas, links shown with their icon and inline
@@ -88,6 +88,7 @@ export function prepareBlock(element, { inner, atoms, kind = "flow", caps = [] }
88
88
  const template = element.ownerDocument.createElement("template");
89
89
  template.innerHTML = inner;
90
90
  template.content.querySelectorAll("*").forEach((node) => node.setAttribute("data-ge-src", ""));
91
+ HOSTS[kind]?.pictures?.show(template.content); // a cell's pictures shown where the server serves them
91
92
  const sourceAtoms = atomsIn(template.content, kind === "text" && caps.includes("plain"));
92
93
  const drawnAtoms = sourceAtoms.filter((atom) => atom.matches(ATOM_SELECTOR)); // a formula as the kit drew it
93
94
  // The blocks a list item holds are blocks of their own: only its text before them is rebuilt.
@@ -342,15 +343,16 @@ export function matchesSource(element, inner) {
342
343
  export function serializeInner(element) {
343
344
  const inner = HOSTS[element.getAttribute?.("data-ge-kind")]?.inner;
344
345
  if (inner) return inner(element);
345
- // A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own.
346
- const lists = element.getAttribute?.("data-ge-kind") === "cell";
347
- return ownNodes(element).map((node) => serializeNode(node, lists)).join("").replace(/<br>\s*$/, "");
346
+ // A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own. A
347
+ // kind of text block that holds pictures (a cell's: HOSTS `pictures`) writes them; any other none.
348
+ const kind = element.getAttribute?.("data-ge-kind");
349
+ return serializeNodes(ownNodes(element), kind === "cell", HOSTS[kind]?.pictures ?? null).replace(/<br>\s*$/, "");
348
350
  }
349
351
 
350
352
  // A list in a table cell, its items' inline content (a line break holding an empty item's caret left out).
351
- function serializeList(list) {
353
+ function serializeList(list, pictures = null) {
352
354
  const tag = list.tagName.toLowerCase();
353
- const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${[...item.childNodes].map((child) => serializeNode(child)).join("").replace(/<br>\s*$/, "")}</li>`);
355
+ const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${serializeNodes([...item.childNodes], false, pictures).replace(/<br>\s*$/, "")}</li>`);
354
356
  return items.length > 0 ? `<${tag}>${items.join("")}</${tag}>` : "";
355
357
  }
356
358
 
@@ -383,11 +385,16 @@ export function textEnd(element) {
383
385
 
384
386
  const ownNodes = (element) => [...element.childNodes].slice(0, textEnd(element));
385
387
 
386
- /** A block's own content as HTML (innerHTML, without the blocks a list item holds). */
388
+ /**
389
+ * A block's own content as HTML (innerHTML, without the blocks a list item holds, nor the words an
390
+ * Agent took out that the page shows in it: GONE_WORDS), what undo and redo record of it.
391
+ */
387
392
  export function ownHTML(element) {
388
- if (nestedBlocks(element).length === 0) return element.innerHTML;
393
+ const shown = element.querySelector(GONE_WORDS);
394
+ if (nestedBlocks(element).length === 0 && !shown) return element.innerHTML;
389
395
  const copy = element.ownerDocument.createElement("div");
390
396
  copy.append(...ownNodes(element).map((node) => node.cloneNode(true)));
397
+ if (shown) copy.querySelectorAll(GONE_WORDS).forEach((node) => node.remove());
391
398
  return copy.innerHTML;
392
399
  }
393
400
 
@@ -429,12 +436,42 @@ export const parentItem = (item) => item?.tagName === "LI" && item.parentElement
429
436
  // edits becomes a space too.
430
437
  const spaced = (text) => escapeTyped(text).replace(/ *\u00a0[ \u00a0]*/g, " ");
431
438
 
432
- function serializeNode(node, lists = false) {
439
+ // The editor's own nodes in a text block (data-doc-dev: the words an Agent took out, which the page
440
+ // shows where they were, changes/) are no content: left out, the text on either side of one is
441
+ // written as one text, as it was before the node split it (a space either side of it the one space
442
+ // it was, never two).
443
+ function serializeNodes(nodes, lists = false, pictures = null) {
444
+ let html = "";
445
+ let text = null; // a text node's, not written yet: the text after an editor's node joins it
446
+ let across = false; // an editor's node stands between it and what comes next
447
+ for (const node of nodes) {
448
+ if (node.nodeType === 1 && node.hasAttribute("data-doc-dev")) {
449
+ across = text !== null;
450
+ continue;
451
+ }
452
+ if (node.nodeType === 3 && node.data !== ZWSP) {
453
+ if (text !== null && !across) html += spaced(text);
454
+ text = text !== null && across ? text + node.data : node.data;
455
+ across = false;
456
+ continue;
457
+ }
458
+ if (text !== null) html += spaced(text);
459
+ text = null;
460
+ across = false;
461
+ html += serializeNode(node, lists, pictures);
462
+ }
463
+ return text === null ? html : html + spaced(text);
464
+ }
465
+
466
+ // `pictures`: the host's (HOSTS `pictures`: write(element) its markup, "" for one of no file, null for no picture).
467
+ function serializeNode(node, lists = false, pictures = null) {
433
468
  // A text node holding nothing but a zero-width space keeps the caret after a mark the editor
434
469
  // made (markdown/, typing shortcuts); it is never content.
435
470
  if (node.nodeType === 3) return node.data === ZWSP ? "" : spaced(node.data);
436
471
  if (node.nodeType !== 1) return "";
437
- if (isList(node)) return lists ? serializeList(node) : "";
472
+ if (isList(node)) return lists ? serializeList(node, pictures) : "";
473
+ const picture = pictures?.write(node) ?? null;
474
+ if (picture !== null) return picture;
438
475
  const atom = node.getAttribute("data-ge-atom");
439
476
  if (atom !== null && ATOM_SOURCE.has(atom)) return ATOM_SOURCE.get(atom);
440
477
  // Elements from the source keep the author's tag and attributes; only elements the browser
@@ -443,7 +480,7 @@ function serializeNode(node, lists = false) {
443
480
  // and their text kept.
444
481
  const fromSource = node.hasAttribute("data-ge-src") && node.tagName in TAGS;
445
482
  const tag = fromSource ? node.tagName.toLowerCase() : TAGS[node.tagName];
446
- const inner = [...node.childNodes].map((child) => serializeNode(child)).join("");
483
+ const inner = serializeNodes([...node.childNodes], false, pictures);
447
484
  const ink = node.tagName === "SPAN" ? [...node.classList].filter(isInkClass) : [];
448
485
  if (ink.length > 0) return inner ? `<span class="${ink.join(" ")}">${inner}</span>` : "";
449
486
  // Small text's end part (its Tab: components/paragraph/page.mjs), at the line's right end.
@@ -498,8 +535,9 @@ export function isEmpty(element) {
498
535
  const siblingTag = (element) => element.tagName === "LI" ? "li" : "p";
499
536
 
500
537
  /**
501
- * A text block made on the page: data-ge-new, with its type and unit kind. Inside a component it
502
- * leaves the frame to the component, as `from` (the block it was split off) does.
538
+ * A text block made on the page: data-ge-new, with its type and unit kind, a text block of the flow
539
+ * (every type's that is one: a callout's words, its own kind until 2026-11-07, are a paragraph of it
540
+ * now). Inside a component it leaves the frame to the component, as `from` (the block it was split off) does.
503
541
  */
504
542
  export function newHost(doc, type, from = null) {
505
543
  const spec = BLOCK_TYPES[type];
@@ -509,9 +547,8 @@ export function newHost(doc, type, from = null) {
509
547
  block.setAttribute("data-ge-new", "");
510
548
  block.setAttribute("data-ge-type", type);
511
549
  block.setAttribute("data-ge-unit-kind", spec.kind);
512
- const kind = spec.host ?? "flow";
513
- block.setAttribute("data-ge-kind", kind);
514
- block.setAttribute("data-ge-caps", capsFor(kind));
550
+ block.setAttribute("data-ge-kind", "flow");
551
+ block.setAttribute("data-ge-caps", capsFor("flow"));
515
552
  if (from?.hasAttribute("data-ge-framed")) block.setAttribute("data-ge-framed", "");
516
553
  return block;
517
554
  }
@@ -542,6 +579,9 @@ export function newTail(container, type) {
542
579
  // New blocks split off `from` are paragraphs, or list items in a list.
543
580
  const newBlock = (doc, tag, from) => newHost(doc, tag === "li" ? "item" : "paragraph", from);
544
581
 
582
+ /** A new, empty paragraph, as a split makes one outside a list (the page's title's Enter: typing/ enterBody). */
583
+ export const newParagraphHost = (doc) => newBlock(doc, "p", null);
584
+
545
585
  // The element a text block made on the page is part of when it is not a block of its own: the
546
586
  // summary of a new disclosure, a new code block (its code's pre, its title's block), the header of a
547
587
  // new detail card.
@@ -688,10 +728,12 @@ const isFormula = (node) => node.matches(".doc-math-inline");
688
728
  * text with the format bar's marks (marks: false drops them, links: false drops links), a formula
689
729
  * of this page as its chip (another one, KaTeX's or the kit's \(…\), as the chip formula(tex) makes,
690
730
  * or else as its TeX source; `formulas` false, each as its source), a link shown with its icon as the
691
- * chip mention(mentionOf(it)) makes (else a link of its title); blocks and line breaks start new
692
- * lines, and everything else gives only its text.
731
+ * chip mention(mentionOf(it)) makes (else a link of its title), an element what picture(element)
732
+ * makes of it (a cell's picture: page/table/pictures.mjs; null nothing, undefined no picture: none
733
+ * given, no picture is one); blocks and line breaks start new lines, and everything else gives only
734
+ * its text.
693
735
  */
694
- export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null } = {}) {
736
+ export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null, picture = null } = {}) {
695
737
  const template = doc.createElement("template");
696
738
  template.innerHTML = html;
697
739
  const lines = [[]];
@@ -745,6 +787,12 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
745
787
  }
746
788
  }
747
789
  if (/^(STYLE|SCRIPT|TEMPLATE|META|TITLE|LINK|NOSCRIPT|BUTTON|SVG)$/i.test(child.tagName) || child.matches('[aria-hidden="true"], .ge-code-gutter, [data-doc-dev]')) continue;
790
+ // A picture, where `picture` takes it (undefined: no picture): what it makes of it, else nothing.
791
+ const made = picture?.(child);
792
+ if (made !== undefined) {
793
+ if (made) push(target, made);
794
+ continue;
795
+ }
748
796
  if (child.tagName === "BR") {
749
797
  if (target) push(target, doc.createTextNode(" "));
750
798
  else newLine();
@@ -762,7 +810,7 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
762
810
  continue;
763
811
  }
764
812
  walk(child, mark);
765
- if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]")) push(target, mark);
813
+ if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]") || (picture && mark.firstElementChild)) push(target, mark);
766
814
  else if (mark.textContent) push(target, doc.createTextNode(mark.textContent));
767
815
  }
768
816
  };
@@ -872,6 +920,9 @@ export function mergeInto(into, from, { anyKind = false } = {}) {
872
920
  // with its own.
873
921
 
874
922
  export const ELEMENT_ID = "data-ge-id";
923
+
924
+ /** The words an Agent took out of a text block, shown where they were (changes/): never its content. */
925
+ export const GONE_WORDS = "[data-ge-gone-words]";
875
926
  const ELEMENT_ID_PREFIX = Math.random().toString(36).slice(2, 6); // a page's own, as formulas' keys
876
927
  let elementIds = 0;
877
928
 
@@ -927,10 +978,10 @@ export const inTable = (node) => /^(TR|TD|TH)$/.test(node?.tagName ?? "") && nod
927
978
  export function unitElement(node) {
928
979
  let found = node?.closest?.(UNIT_OR_LEAD) ?? null;
929
980
  while (inTable(found)) found = found.parentElement?.closest(UNIT_OR_LEAD) ?? null;
930
- // Text that is part of a block made on the page (a new disclosure's title, a new code block's
931
- // code) stands for that block.
981
+ // Text made on the page that is part of a block (a new code block's code, a media block's new
982
+ // caption) stands for that block: the part's own block, not the one around it.
932
983
  const part = found && !found.hasAttribute("data-ge-unit") ? partOf(found) : null;
933
- if (part) found = (part.matches("pre, .doc-code") ? part : part.parentElement).closest(UNIT);
984
+ if (part) found = part.closest(UNIT);
934
985
  if (!found?.hasAttribute("data-ge-lead")) return found;
935
986
  const id = found.getAttribute("data-ge-lead");
936
987
  let next = found.nextElementSibling;
@@ -1180,12 +1231,12 @@ export function snapshotLayout(root) {
1180
1231
  }
1181
1232
 
1182
1233
  // Whether a text block may be written with no text: it is of a type whose component says so (its
1183
- // `empty`: a paragraph, a heading, a list item, a callout; the server's emptyLeaf reads the same)
1234
+ // `empty`: a paragraph, a heading, a list item; the server's emptyLeaf reads the same)
1184
1235
  // and can be emptied (can "empty"), an item holding blocks too, its line kept over them (keepLine). A
1185
1236
  // field that may be emptied (a cell, a caption) is no block written empty; a new code block is
1186
1237
  // written empty by its type (describeNode).
1187
1238
  const clearable = (element) => can(element, "empty");
1188
- /** Whether an empty text block stays, written with no text (a paragraph, a heading, a list item, a callout, a code block). */
1239
+ /** Whether an empty text block stays, written with no text (a paragraph, a heading, a list item, a code block). */
1189
1240
  const keepsWhenEmpty = (element) => BLOCK_TYPES[textTypeOf(element)]?.empty === true && clearable(element);
1190
1241
 
1191
1242
  /** Whether a text block may go: it can be removed, is not kept (data-ge-keep) and holds no blocks (it would show no line of its own). */
@@ -1208,8 +1259,11 @@ const leaves = (node, emptied) => gone(node) || emptied.has(node);
1208
1259
  // The text block of a block: the block itself, or where its component says (a code block's code).
1209
1260
  const textOf = (node) => BLOCK_TYPES[blockTypeOf(node)]?.textHost?.(node) ?? node;
1210
1261
 
1211
- // What a component's description is given to say what goes with a block (`extras`).
1262
+ // What a component's description is given to say what goes with a block (`extras`). `nodes(box)`: the
1263
+ // blocks of a box of its own as the commit writes them (nodeOf), a block written whole holding those of
1264
+ // boxes of its own apart from its markup (an instance's slots: installed/index.mjs).
1212
1265
  const HELPERS = { sizeValue, serializeInner, unitsOf };
1266
+ const helpersFor = (emptied) => ({ ...HELPERS, nodes: (box) => unitsOf(box).map((child) => nodeOf(child, emptied)).filter(Boolean) });
1213
1267
 
1214
1268
  /**
1215
1269
  * The first element in `root` (in document order, `root` itself left out) carrying each value of
@@ -1351,7 +1405,7 @@ function describeNode(element, emptied) {
1351
1405
  // (`content`: a disclosure's content region), its title (`title`), its id (`keyed`: a detail
1352
1406
  // card's), when it is written at all (`written`: a row once each of its columns has something, a
1353
1407
  // detail card while a link opens it; else once it holds something).
1354
- const extras = spec.extras?.(element, HELPERS) ?? {};
1408
+ const extras = spec.extras?.(element, helpersFor(emptied)) ?? {};
1355
1409
  if (spec.bare) return spec.written?.(element, []) === false ? null : { type, ...extras };
1356
1410
  if (!spec.box) {
1357
1411
  const text = textOf(element);
@@ -0,0 +1,106 @@
1
+ import { startBoard } from "./board/index.mjs";
2
+ import { relayTo } from "./notices.mjs";
3
+ import { connectWorkbench } from "./session.mjs";
4
+
5
+ // A custom page inside a workbench (src/server/routes.mjs serves it at /pages/<slug> with this module;
6
+ // docs/workbench.md "Custom pages"): no document editor, the session, by which the workbench shows it
7
+ // as it shows a document's page, and its boards' editing (./board/: the cards moved, resized, added,
8
+ // taken out, their settings and their blocks' writes, through the page service, by the page map the
9
+ // server writes in its head), which shows the page again only once nothing of it waits. Its session's
10
+ // slug is "@<slug>", the page's address in the workbench (#@<slug>), which no document's slug can be.
11
+ // It answers the session's own commands (theme, keys, scroll-top: src/editor/page/session.mjs),
12
+ // prepare-close (once the board's ops sent are answered: each is written at once, nothing else waits),
13
+ // add-card (the top bar's 添加自定义块: the library of the page's first board, under the button's place that
14
+ // the workbench sends) and, on the home page, restore-defaults (恢复默认卡片, the sidebar's: the board's, one step of its
15
+ // undo; its answer { ok, added, skipped } the workbench says) and undo-restore ({ cards }: that step
16
+ // undone while it is the board's last, the workbench's 撤销),
17
+ // and sends ready, reload (its live script heard that page.html or page.json changed, or a block it
18
+ // holds: the workbench loads it again in a hidden frame and swaps it in once drawn), open (a link to a
19
+ // document or another page, or a block's context.workspace.open: doc-open-request) and key. Opened on its own (not in a frame of the workbench), it has no
20
+ // session: its board edits all the same, and the live script reloads it.
21
+
22
+ const slug = decodeURIComponent(location.pathname.split("/")[2] ?? "");
23
+ const IDLE = { editing: false, pending: false, saving: false, offline: false, conflicts: 0, unfit: 0, changed: false };
24
+ // Nothing stands over a custom page as a modal.
25
+ const NO_MODALS = { open: () => [], listen() {}, closeTop: () => false };
26
+
27
+ let board = null;
28
+ const session = connectWorkbench({
29
+ slug: `@${slug}`,
30
+ snapshot: () => ({ title: document.title, icon: null, state: IDLE }),
31
+ commands: {
32
+ "prepare-close": async () => {
33
+ await board?.idle();
34
+ return { ok: true };
35
+ },
36
+ // 添加自定义块 of the workbench's top bar: the library opens in the page, at `anchor` (the button's place in
37
+ // this frame's coordinates; none: in the middle of the window), the first board's.
38
+ "add-card": async ({ anchor } = {}) => (board ? board.addCard({ anchor }) : { ok: false, reason: "board" }),
39
+ ...(slug === "home" ? {
40
+ "restore-defaults": async () => (board ? board.restoreDefaults() : { ok: false, reason: "board" }),
41
+ "undo-restore": async ({ cards }) => (board ? board.undoRestore(cards) : { ok: false, reason: "board" }),
42
+ } : {}),
43
+ },
44
+ modals: NO_MODALS,
45
+ });
46
+
47
+ // The page map (routes.mjs: its slug, its token, the version it was served at, its blocks): the boards edit.
48
+ let map = null;
49
+ try {
50
+ map = JSON.parse(document.querySelector("script[data-ge-page]")?.textContent ?? "null");
51
+ } catch {}
52
+ board = map?.token ? startBoard({ map, reload: () => (session ? session.reload() : location.reload()), hosted: Boolean(session) }) : null;
53
+ // The live script (routes.mjs) shows the page again through the workbench, which swaps a new page in
54
+ // once drawn: no reload seen; once the board's own writes, a drag, a form or a field typed in are done.
55
+ if (board) window.__galleryEditReload = () => board.swap();
56
+
57
+ if (session) {
58
+ // Its notices (a card someone else changed) are the workbench's toasts, as a document's page's are.
59
+ relayTo(session.notice, session.noticeRoom);
60
+ if (!board) window.__galleryEditReload = () => session.reload();
61
+
62
+ // A block's context.workspace.open (the kit's runtime/workspace.js: 我的待办's item, 最近编辑's
63
+ // document): a document, at its block where it names one, or a custom page, opened in the workbench.
64
+ addEventListener("doc-open-request", (event) => {
65
+ const { slug = null, block = null, page = null } = event.detail ?? {};
66
+ event.preventDefault();
67
+ session.open(page ? { hash: `@${page}` } : { target: slug, block });
68
+ });
69
+
70
+ // A link to a document or a page of the workbench goes there in the workbench, never in this frame:
71
+ // a workbench's address (#<link>, #@<slug>, /#…), a document's page (/generated/<slug>.html) or its
72
+ // file (../<slug>/doc.html), a custom page's served path (/pages/<slug>, the page alone that gallery
73
+ // page open gives, or written relative to this one: "reading" from /pages/home). Another site's
74
+ // opens in a tab of its own.
75
+ addEventListener("click", (event) => {
76
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
77
+ const link = event.target instanceof Element ? event.target.closest("a[href]") : null;
78
+ if (!link || link.hasAttribute("download")) return;
79
+ const raw = link.getAttribute("href") ?? "";
80
+ // A place in this page (an element of that id) is the browser's to go to.
81
+ if (raw.startsWith("#") && raw.length > 1 && document.getElementById(decodeURIComponent(raw.slice(1)))) return;
82
+ let url;
83
+ try {
84
+ url = new URL(raw, location.href);
85
+ } catch {
86
+ return;
87
+ }
88
+ if (url.origin !== location.origin) {
89
+ event.preventDefault();
90
+ window.open(url.href, "_blank", "noopener");
91
+ return;
92
+ }
93
+ const custom = /^\/pages\/([a-z0-9-]+)\/?$/.exec(url.pathname)?.[1];
94
+ const page = /^\/generated\/([a-z0-9-]+)\.html$/.exec(url.pathname)?.[1] ?? /^\/([a-z0-9-]+)\/(?:doc|report)\.html$/.exec(url.pathname)?.[1];
95
+ if (custom) {
96
+ event.preventDefault();
97
+ session.open({ hash: `@${custom}` });
98
+ } else if (page) {
99
+ event.preventDefault();
100
+ session.open({ target: page, block: url.hash.slice(1) || null });
101
+ } else if ((url.pathname === "/" || raw.startsWith("#")) && url.hash.length > 1) {
102
+ event.preventDefault();
103
+ session.open({ hash: decodeURIComponent(url.hash.slice(1)) });
104
+ }
105
+ });
106
+ }
@@ -28,6 +28,8 @@ import { t } from "../../../shared/i18n/index.mjs";
28
28
  // cancels, and so does anything that ends the press another way (the handle losing the pointer
29
29
  // capture, the window losing the focus, the page hidden): never a drop. Ending is idempotent, and
30
30
  // the line, tint and copy belong to their drag (overlays.mjs). Ctrl+Shift+arrows move selected blocks standing next to each other by one place (moveBy).
31
+ // A folded heading goes with what it hides, or as the section it covers whole (fold/ takenWith); its
32
+ // copy shows what is in sight, and Ctrl+Shift+↑/↓ take it a section of its level at a time.
31
33
 
32
34
  const STYLE = `
33
35
  ${gestureLock("data-ge-drag", { cursor: "grabbing", select: true })}
@@ -43,9 +45,9 @@ const STYLE = `
43
45
  * info: block-info.mjs. selection: selection/index.mjs. struct(parents, touched, mutate): the
44
46
  * editor's undoable structural change (history.mjs). say: the status line. closeMenu(): the block
45
47
  * menu closes. schedule(): the block tools' next frame (blocks.mjs), which calls step() while a
46
- * drag lasts.
48
+ * drag lasts. folds: the headings' folds (fold/).
47
49
  */
48
- export function createDrag({ info, selection, struct, say, closeMenu, schedule }) {
50
+ export function createDrag({ info, selection, struct, say, closeMenu, schedule, folds }) {
49
51
  addStyle(STYLE);
50
52
  const { addColumn, columnsOf, rowAround } = columnOps() ?? {};
51
53
  const { kindOf, nameOf, named, fixedText, boxKindOf } = info;
@@ -105,7 +107,8 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
105
107
  }
106
108
 
107
109
  // Put blocks beside a block (drop.mjs `aside`): { target, side }, the two side by side in a new
108
- // row of columns; { row, index, whole }, a new column of that row at `index` holding them, or,
110
+ // row of columns (a folded heading's column taking what it hides: fold/ withHidden); { row, index,
111
+ // whole }, a new column of that row at `index` holding them, or,
109
112
  // when they are all of one column of it (`whole`), that column moved there. One undo step that
110
113
  // keeps them selected; a container they leave empty goes, as when they move (and a row left
111
114
  // with one column gives its blocks back: history.mjs).
@@ -122,7 +125,7 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
122
125
  const leftovers = leftoversOf(targets, aside.row ?? aside.target);
123
126
  const parents = [...new Set([...targets.map((target) => target.parentNode), aside.row ?? aside.target.parentElement, ...leftovers.map((node) => node.parentNode)].filter(Boolean))];
124
127
  struct(parents, [], () => {
125
- const { created } = aside.row ? addColumn(aside.row, aside.index, parts) : rowAround(aside.target, [parts], aside.side);
128
+ const { created } = aside.row ? addColumn(aside.row, aside.index, parts) : rowAround(folds.withHidden(aside.target), [parts], aside.side);
126
129
  leftovers.forEach((node) => node.remove());
127
130
  return { touched: [], focus: null, select: targets, reselect: true, created };
128
131
  });
@@ -134,30 +137,40 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
134
137
  */
135
138
  function moveBy(targets, delta, { keepFocus = false } = {}) {
136
139
  closeMenu();
137
- targets = ordered([targets].flat().filter(alive));
140
+ const picked = ordered([targets].flat().filter(alive));
141
+ targets = folds.takenWith(picked);
138
142
  if (targets.length === 0) return false;
139
143
  const reason = targets.map(fixedText).find(Boolean);
140
144
  if (reason) {
141
145
  say(reason);
142
146
  return false;
143
147
  }
144
- // The blocks to move among: not a box's lead (a highlight block's icon stays first in it).
148
+ const name = folds.namedWith(picked, targets, named);
149
+ // The blocks to move among: not a box's lead (a highlight block's icon stays first in it), as the
150
+ // reader sees them: a folded heading and what it hides one (fold/ groupsOf).
145
151
  const boxKind = boxKindOf(targets[0].parentElement);
146
152
  const list = unitsOf(targets[0].parentElement).filter((unit) => !isLead(boxKind, kindOf(unit)));
147
- const first = list.indexOf(targets[0]);
148
- if (!targets.every((target, index) => list[first + index] === target)) {
153
+ const groups = folds.groupsOf(list);
154
+ const first = groups.findIndex((group) => group[0] === targets[0]);
155
+ let last = first;
156
+ let count = first < 0 ? 0 : groups[first].length;
157
+ while (first >= 0 && count < targets.length && last + 1 < groups.length) count += groups[++last].length;
158
+ if (first < 0 || !groups.slice(first, last + 1).flat().every((unit, index) => targets[index] === unit) || count !== targets.length) {
149
159
  say(t("选中的块不相邻,不能一起上下移动;可以拖动其中一块的手柄,把它们一起移到别处。"));
150
160
  return false;
151
161
  }
152
- const neighbour = list[delta < 0 ? first - 1 : first + targets.length];
153
- if (!neighbour) {
154
- say(list.length === targets.length ? t("这里只有{0};可以拖动手柄移到别处。", targets.length === 1 ? t("这一个{0}", nameOf(targets[0])) : t("这几块")) : t("{0}已经在{1}。", named(targets), delta < 0 ? t("最前面") : t("最后面")));
162
+ // A folded heading alone goes a section of its level at a time (fold/ stepOver); others a block in sight at a time.
163
+ const alone = first === last && folds.isFolded(targets[0]);
164
+ const step = alone ? folds.stepOver(targets[0], delta, list) : null;
165
+ const over = alone ? step && [step.first, step.last] : groups[delta < 0 ? first - 1 : last + 1];
166
+ if (!over) {
167
+ say(groups.length === last - first + 1 ? t("这里只有{0};可以拖动手柄移到别处。", targets.length === 1 ? t("这一个{0}", nameOf(targets[0])) : t("这几块")) : t("{0}已经在{1}。", name, delta < 0 ? t("最前面") : t("最后面")));
155
168
  return false;
156
169
  }
157
- moveTo(targets, targets[0].parentElement, delta < 0 ? withLeads(neighbour)[0] : neighbour.nextSibling);
170
+ moveTo(targets, targets[0].parentElement, delta < 0 ? withLeads(over[0])[0] : over.at(-1).nextSibling);
158
171
  if (!keepFocus) selection.select(targets, { quiet: true });
159
172
  targets[delta < 0 ? 0 : targets.length - 1].scrollIntoView({ block: "nearest", inline: "nearest" });
160
- say(t("已移动{0} · Ctrl+Z 撤销", named(targets)));
173
+ say(t("已移动{0} · Ctrl+Z 撤销", name));
161
174
  return true;
162
175
  }
163
176
 
@@ -165,7 +178,8 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
165
178
  // the drag started (a block that cannot move says why).
166
179
  function start(target, x, y) {
167
180
  const picked = selection.picked;
168
- const targets = picked.length > 1 && picked.includes(target) ? [...picked] : [target];
181
+ const chosen = picked.length > 1 && picked.includes(target) ? [...picked] : [target];
182
+ const targets = folds.takenWith(chosen); // a folded heading with what it hides (fold/)
169
183
  const reason = targets.map(fixedText).find(Boolean);
170
184
  if (reason) {
171
185
  say(reason);
@@ -174,7 +188,7 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
174
188
  closeMenu();
175
189
  selection.select(targets, { quiet: true });
176
190
  const session = {
177
- targets, target, from: target.parentElement, x, y, drop: null, rest: null, opened: new Set(),
191
+ targets, target, from: target.parentElement, x, y, drop: null, rest: null, opened: new Set(), name: folds.namedWith(chosen, targets, named),
178
192
  line: element("div", { class: "ge-drop-line", hidden: "" }),
179
193
  tint: element("div", { class: "ge-drop-tint", hidden: "" }),
180
194
  preview: null,
@@ -184,12 +198,12 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
184
198
  // The line and the tint go in the layer of where the blocks would go
185
199
  // (overlay-layer.mjs), when there is one; the copy follows the pointer in the window.
186
200
  for (const kind of ["line", "tint"]) own(session[kind], `drag ${kind}`, { owner: target, alive });
187
- session.preview = createPreview(targets, x, y, { owner: target, alive });
201
+ session.preview = createPreview(targets.filter((node) => !folds.hidden(node)), x, y, { owner: target, alive }); // what is in sight
188
202
  drag = session;
189
203
  note("drag", `start ${targets.map(describe).join(", ")}`);
190
204
  targets.flatMap(withLeads).forEach((part) => part.setAttribute("data-ge-dragging", ""));
191
205
  document.documentElement.setAttribute("data-ge-drag", "");
192
- say(t("正在移动{0} · 拖到要放的位置后松开 · 停在选项卡上切换,停在折叠块标题上展开 · Esc 取消", named(targets)), "prompt");
206
+ say(t("正在移动{0} · 拖到要放的位置后松开 · 停在选项卡上切换,停在折叠块标题上展开 · Esc 取消", session.name), "prompt");
193
207
  update();
194
208
  schedule();
195
209
  return true;
@@ -219,8 +233,9 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
219
233
  : items ? accepts(box.kind, "item") || accepts(box.kind, "list")
220
234
  : steps ? accepts(box.kind, "step") || (accepts(box.kind, "flowchart") && box.kind !== "nest" && !box.el.closest("ol.doc-flow"))
221
235
  : [...kinds].every((kind) => accepts(box.kind, kind)));
222
- // Blocks a column takes may go beside a block, into a column (分栏).
223
- const aside = !tabs && [...kinds].every((kind) => accepts("column", kind)) && ((unit) => accepts("column", unit.kind));
236
+ // Blocks a column takes may go beside a block, into a column (分栏); beside a folded heading when a
237
+ // column takes what it hides too, which goes into its column with it (besideTo).
238
+ const aside = !tabs && [...kinds].every((kind) => accepts("column", kind)) && ((unit) => folds.withHidden(unit.el).every((node) => accepts("column", kindOf(node))));
224
239
  return resolveDrop(drag.tree, x + scrollX, y + scrollY, { takes, dragged: (unit) => targets.includes(unit.el), count: targets.length, items, aside, edge: scrollX });
225
240
  }
226
241
 
@@ -272,7 +287,7 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
272
287
  // Releasing (commit) or cancelling: the blocks go where the line showed, or stay.
273
288
  function end(commit) {
274
289
  if (!drag) return;
275
- const { targets, target, drop, line, tint, preview, opened } = drag;
290
+ const { targets, target, drop, line, tint, preview, opened, name } = drag;
276
291
  clearTimeout(drag.rest?.timer);
277
292
  drag = null;
278
293
  line.remove();
@@ -293,16 +308,17 @@ export function createDrag({ info, selection, struct, say, closeMenu, schedule }
293
308
  besideTo(targets, drop.aside);
294
309
  selection.select(targets, { quiet: true });
295
310
  target.scrollIntoView({ block: "nearest", inline: "nearest" });
296
- say(t("{0} · 拖动栏间的竖线调宽度 · Ctrl+Z 撤销", drop.aside.whole ? t("已移动这一栏") : drop.aside.row ? t("已把{0}放进新的一栏", named(targets)) : t("已把{0}并排分栏", named(targets))));
311
+ say(t("{0} · 拖动栏间的竖线调宽度 · Ctrl+Z 撤销", drop.aside.whole ? t("已移动这一栏") : drop.aside.row ? t("已把{0}放进新的一栏", name) : t("已把{0}并排分栏", name)));
297
312
  return;
298
313
  }
299
314
  const others = unitsOf(drop.container).filter((unit) => !targets.includes(unit) && extentOf(unit));
300
- // The place is never before a box's lead (a highlight block's icon): drop.mjs.
301
- const reference = drop.slot < others.length ? withLeads(others[drop.slot])[0] : others.length > 0 ? others.at(-1).nextSibling : null;
315
+ // The place is never before a box's lead (a highlight block's icon): drop.mjs. At the end, after
316
+ // what the last block in sight hides too (a folded heading's section: fold/), never between them.
317
+ const reference = drop.slot < others.length ? withLeads(others[drop.slot])[0] : others.length > 0 ? folds.lastOf(others.at(-1)).nextSibling : null;
302
318
  const emptied = moveTo(targets, drop.nest ?? drop.container, drop.nest ? null : reference);
303
319
  selection.select(targets, { quiet: true });
304
320
  target.scrollIntoView({ block: "nearest", inline: "nearest" });
305
- say(t("已移动{0}{1}{2} · Ctrl+Z 撤销", named(targets), drop.nest ? t("到上一项下面") : "", emptied ? t("(原来的外层已空,一并去掉)") : ""));
321
+ say(t("已移动{0}{1}{2} · Ctrl+Z 撤销", name, drop.nest ? t("到上一项下面") : "", emptied ? t("(原来的外层已空,一并去掉)") : ""));
306
322
  }
307
323
 
308
324
  // The window losing the focus, or the page going out of sight, ends a drag: cancelled.
@@ -11,8 +11,12 @@ import { element, mount } from "../ui.mjs";
11
11
  // (theirs stay theirs, a chart's gradients among them). The page's own rules are brought in as one
12
12
  // stylesheet (made once, applied at once: no unstyled first frame), and each copy stands in a frame
13
13
  // of its ancestors as the page has them (shallow copies, display: contents), so a list item keeps its
14
- // marker and its number, and a section's paragraph its section's look. At most MAX blocks, each at
15
- // most TALL of the window (cut, fading out at the bottom).
14
+ // marker and its number, and a section's paragraph its section's look. A frame draws nothing of its
15
+ // own (visibility: hidden, the copy visible): display: contents leaves an element's ::before and
16
+ // ::after and a <details>'s own summary drawn (Chrome's "▶ 详细信息" for one without a summary, an
17
+ // ancestor item's marker, a flowchart step's number, each at the window's left or top), which would
18
+ // follow the pointer (the user's report of 2026-11-06). At most MAX blocks, each at most TALL of the
19
+ // window (cut, fading out at the bottom).
16
20
 
17
21
  const MAX = 12;
18
22
  const TALL = 0.6;
@@ -72,9 +76,9 @@ function copyOf(part, rect, top) {
72
76
  }
73
77
  const cut = rect.height > innerHeight * TALL;
74
78
  if (cut) copy.classList.add("ge-cut");
75
- // Its own size, as it was on the page: no share of a row or of its box (the window is the box here); inline, so the page's rules for the block cannot undo it.
79
+ // Its own size, as it was on the page: no share of a row or of its box (the window is the box here); inline, so the page's rules for the block cannot undo it. Visible, in frames that are not.
76
80
  Object.assign(copy.style, {
77
- position: "absolute", margin: "0", maxWidth: "none", minWidth: "0", maxHeight: "none", flex: "none",
81
+ position: "absolute", margin: "0", maxWidth: "none", minWidth: "0", maxHeight: "none", flex: "none", visibility: "visible",
78
82
  left: `${rect.left}px`, top: `${rect.top}px`, width: `${rect.width}px`,
79
83
  height: `${cut ? Math.round(innerHeight * TALL) : rect.height}px`,
80
84
  });
@@ -82,6 +86,7 @@ function copyOf(part, rect, top) {
82
86
  for (let node = part.parentElement; node && node !== top.parentElement; node = node.parentElement) {
83
87
  const shell = clean(node.cloneNode(false));
84
88
  shell.style.display = "contents";
89
+ shell.style.visibility = "hidden"; // a frame lends the copy its rules and draws nothing of its own
85
90
  shell.append(outer);
86
91
  outer = shell;
87
92
  }