@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,348 @@
1
+ import { IN_LAYER } from "../../../shared/layers.mjs";
2
+ import { FOLD_ARROW } from "../../../shared/code-folds.mjs";
3
+ import { t } from "../../../shared/i18n/index.mjs";
4
+ import { ELEMENT_ID, UNIT, identityOf, unitsOf, withLeads } from "../core.mjs";
5
+ import { caretAt } from "../caret.mjs";
6
+ import { HEADING } from "../outline/read.mjs";
7
+ import { foldsOf, keepFolds } from "../outline/folds.mjs";
8
+ import { drawAt, shapeOf } from "../overlay-layer.mjs";
9
+ import { own } from "../overlays.mjs";
10
+ import { local } from "../storage.mjs";
11
+ import { shortcut, tooltip } from "../tooltip.mjs";
12
+ import { addStyle, element } from "../ui.mjs";
13
+ import { FOLDED, hiddenByFold, setFoldOpener } from "../folded.mjs";
14
+ import { foldable, headingOf, keyOf, sectionAfter, stepOver, takenWith } from "./rule.mjs";
15
+
16
+ // Folding a heading's section (docs/editor.md, "Folding a heading's section"): a small rounded triangle
17
+ // a gap right of a heading's last line, shown while the pointer is at the heading (always while it is
18
+ // folded), folds what the heading's section is (rule.mjs: the outline's nesting, in its box) out of
19
+ // sight; Ctrl/⌘+Enter in its words does the same (components/heading/page.mjs). A fold is the reader's
20
+ // view, never the file's: no undo step, no commit; kept per report in the browser
21
+ // (gallery-edit:heading-folds, as the outline's folds are), so the page shown again after an Agent's
22
+ // write folds what was folded. While the page lives, what is folded is the headings' identities
23
+ // (data-ge-id: a heading turned into another level, or put back by undo, stays folded); what a fold
24
+ // hides is marked data-ge-folded (the outermost folded heading's identity), shown as nothing.
25
+ //
26
+ // What asks it: the block tools (blocks.mjs) draw the triangles each frame (place, beside the handle:
27
+ // handle.pointed), and take a folded heading's section with it wherever blocks are taken away whole
28
+ // (takenWith: drag/, selection/delete.mjs, clipboard/, block-menu/'s 组成新分区; groupsOf and
29
+ // stepOver: drag/ moveBy; withHidden: a row of columns made around it, columns/ and drag/);
30
+ // selection/ keeps what is hidden out of the selection; typing/ opens a fold before Backspace or
31
+ // Delete would join hidden words; editor.mjs opens the folds around the caret, and
32
+ // every path that opens what hides a block goes through reveal() (page/folded.mjs). One rule opens a fold
33
+ // of itself: a block a change of the page puts into a folded section (or one that was in sight and is not
34
+ // now) opens it, unless the block came onto the page with its heading in that same change (undo
35
+ // putting a folded section back, a folded section dragged).
36
+
37
+ const STORE = "gallery-edit:heading-folds";
38
+ // The button is what a press finds, drawn as nothing but its triangle; 16px (it was 20, filled under the
39
+ // pointer: the user's, 2026-11-07), its triangle's middle 14px past the words as before.
40
+ const SIZE = 16;
41
+ const GAP = 6; // from the end of the heading's last line to the button
42
+ // A press held that began off the triangles (words being selected, a box drawn, a block dragged): the
43
+ // triangles take no pointer until it ends. Under the pointer one stood for the place of the drag: the
44
+ // browser, finding a node outside the heading's editable text, put the selection's moving end at the
45
+ // heading's start (the whole heading selected while the pointer crossed the triangle, the user's
46
+ // 2026-11-07 第 6 条), and it darkened as if it were to be pressed.
47
+ const HELD = "data-ge-fold-held";
48
+ // What a fold hides is display: none !important: none of it shows, whatever display its blocks have
49
+ // of their own (a row of columns' grid, a card's flex, a rule of the report's own).
50
+ const STYLE = `
51
+ [data-ge-editing] [${FOLDED}] { display: none !important; }
52
+ .ge-fold { position: absolute; left: 0; top: 0; z-index: ${IN_LAYER.button}; box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: ${SIZE}px; height: ${SIZE}px; border-radius: 4px; color: var(--ge-ico3); cursor: pointer; pointer-events: auto; user-select: none; }
53
+ html[${HELD}] .ge-fold { pointer-events: none; }
54
+ /* No fill under the pointer, the triangle darker (as the outline's), a short one while pressed (as a code block's arrow). */
55
+ .ge-fold:hover { color: var(--ge-ico2); }
56
+ .ge-fold:active { background: var(--ge-press); }
57
+ /* Open it points down; folded, left, at the heading's words (it stands right of them), turned clockwise. */
58
+ .ge-fold svg { display: block; width: 12px; height: 12px; fill: currentColor; transition: transform .2s ease-out; }
59
+ .ge-fold[aria-expanded="false"] svg { transform: rotate(90deg); }
60
+ @media (prefers-reduced-motion: reduce) { .ge-fold svg { transition: none; } }
61
+ `;
62
+
63
+ /** Where a heading's button stands (window coordinates): 6px right of its last line's end, on that line's middle; null where it shows no words. */
64
+ export function foldRectOf(heading) {
65
+ const range = document.createRange();
66
+ range.selectNodeContents(heading);
67
+ const end = [...range.getClientRects()].filter((rect) => rect.width > 0 && rect.height > 0).at(-1);
68
+ if (!end) return null;
69
+ const left = end.right + GAP;
70
+ const top = (end.top + end.bottom) / 2 - SIZE / 2;
71
+ return { left, top, right: left + SIZE, bottom: top + SIZE };
72
+ }
73
+
74
+ /**
75
+ * slug: the report's. selection: selection/index.mjs (what is hidden leaves it). active(): whether
76
+ * the block tools may show. schedule(): the block tools' next frame.
77
+ */
78
+ export function createFolds({ slug, selection, active, schedule }) {
79
+ addStyle(STYLE);
80
+ const folded = new Set(); // the identities of the headings folded
81
+ let heads = []; // the folded headings on the page at the last apply, in document order
82
+ let hidden = new Set(); // the units hidden at the last apply
83
+ let started = false;
84
+ let observer = null;
85
+ const buttons = new Map(); // heading -> its button
86
+ const holds = (node) => [...buttons.values()].some((button) => button.contains(node));
87
+
88
+ const main = () => document.querySelector("main");
89
+ const idOf = (node) => node?.getAttribute?.(ELEMENT_ID) ?? null;
90
+ const isFolded = (node) => node?.nodeType === 1 && folded.has(idOf(node)) && foldable(node);
91
+ const headings = () => [...(main()?.querySelectorAll(HEADING) ?? [])].filter(foldable);
92
+ // The units a folded heading hides (with those of folds inside it): those of its box marked with it.
93
+ const hiddenBy = (heading) => (heading.parentElement ? unitsOf(heading.parentElement).filter((unit) => unit.getAttribute(FOLDED) === idOf(heading)) : []);
94
+
95
+ function load() {
96
+ const keys = foldsOf(local, slug, STORE);
97
+ if (keys.size === 0) return;
98
+ const all = headings();
99
+ for (const key of keys) {
100
+ const heading = headingOf(key, all);
101
+ if (heading) folded.add(identityOf(heading));
102
+ }
103
+ }
104
+
105
+ // What is folded, kept by the keys the headings have now (only those still on the page).
106
+ function save() {
107
+ if (!started) return;
108
+ const all = headings();
109
+ keepFolds(local, slug, new Set(all.filter((heading) => folded.has(idOf(heading))).map((heading) => keyOf(heading, all))), null, STORE);
110
+ }
111
+
112
+ // What each folded heading hides now: unit -> the outermost folded heading over it. A folded heading
113
+ // with no words is folded no more; one hidden by a fold around it starts no section of its own.
114
+ function wanted(root) {
115
+ const want = new Map();
116
+ const found = [];
117
+ for (const heading of root.querySelectorAll(HEADING)) {
118
+ const id = idOf(heading);
119
+ if (!folded.has(id)) continue;
120
+ if (!foldable(heading)) {
121
+ folded.delete(id);
122
+ continue;
123
+ }
124
+ found.push(heading);
125
+ let under = false;
126
+ for (let node = heading; node && node !== root && !under; node = node.parentElement) under = want.has(node);
127
+ if (under) continue;
128
+ for (const unit of sectionAfter(heading)) want.set(unit, heading);
129
+ }
130
+ return { want, found };
131
+ }
132
+
133
+ /**
134
+ * The marks set again as the folds are now. `added`: the elements a change of the page put in (null:
135
+ * a fold toggled, or the page begun), for the rule that opens a fold: a unit hidden now that was not
136
+ * opens the folds over it, unless it came in with its heading.
137
+ */
138
+ function apply({ added = null } = {}) {
139
+ const root = main();
140
+ if (!root) return;
141
+ const within = (node) => {
142
+ for (let at = node; at && at !== root; at = at.parentElement) if (added.has(at)) return true;
143
+ return false;
144
+ };
145
+ const count = folded.size;
146
+ let result;
147
+ for (let round = 0; round < 16; round++) {
148
+ result = wanted(root);
149
+ if (added === null) break;
150
+ const opened = new Set();
151
+ for (const [unit, heading] of result.want) {
152
+ if (!hidden.has(unit) && !(within(unit) && within(heading))) opened.add(heading);
153
+ }
154
+ if (opened.size === 0) break;
155
+ for (const heading of opened) folded.delete(idOf(heading));
156
+ }
157
+ const marks = new Map();
158
+ for (const [unit, heading] of result.want) for (const part of withLeads(unit)) marks.set(part, idOf(heading));
159
+ for (const node of root.querySelectorAll(`[${FOLDED}]`)) if (!marks.has(node)) node.removeAttribute(FOLDED);
160
+ for (const [node, id] of marks) if (node.getAttribute(FOLDED) !== id) node.setAttribute(FOLDED, id);
161
+ heads = result.found;
162
+ hidden = new Set(result.want.keys());
163
+ if (added !== null && folded.size !== count) save(); // a fold opened of itself
164
+ schedule();
165
+ }
166
+
167
+ // A change of the page: blocks put in or taken out (a container's children, or a node that is or
168
+ // holds a block) apply the folds again; words typed in a heading too (emptied, its fold opens; a
169
+ // heading in a box may now end a section). Words typed elsewhere are nothing to it.
170
+ function changed(records) {
171
+ if (folded.size === 0 && hidden.size === 0) return;
172
+ const added = new Set();
173
+ let again = false;
174
+ for (const record of records) {
175
+ const target = record.target.nodeType === 1 ? record.target : record.target.parentElement;
176
+ if (target?.closest?.(HEADING)) again = true;
177
+ if (record.type !== "childList") continue;
178
+ if (target?.hasAttribute?.("data-ge-box")) again = true;
179
+ for (const node of record.addedNodes) {
180
+ if (node.nodeType !== 1) continue;
181
+ added.add(node);
182
+ if (!again && (node.matches(UNIT) || node.querySelector(UNIT))) again = true;
183
+ }
184
+ if (!again) for (const node of record.removedNodes) if (node.nodeType === 1 && (node.matches(UNIT) || node.querySelector(UNIT))) again = true;
185
+ }
186
+ if (again) apply({ added });
187
+ }
188
+
189
+ // The caret, or an end of a text selection, in what a fold now hides goes to the end of its heading;
190
+ // what is selected of it leaves the selection.
191
+ function stepOut(heading) {
192
+ const picked = selection.picked;
193
+ const kept = picked.filter((node) => !hiddenByFold(node));
194
+ if (kept.length !== picked.length) {
195
+ if (kept.length) selection.select(kept, { quiet: true });
196
+ else selection.clear();
197
+ }
198
+ const text = getSelection();
199
+ if (text?.rangeCount && (hiddenByFold(text.anchorNode) || hiddenByFold(text.focusNode))) caretAt(heading, "end");
200
+ }
201
+
202
+ /** Fold or unfold a heading's section. Whether it is one that folds. */
203
+ function toggle(heading) {
204
+ if (!started || !foldable(heading)) return false;
205
+ const id = identityOf(heading);
206
+ if (!folded.delete(id)) folded.add(id);
207
+ apply();
208
+ if (folded.has(id)) stepOut(heading);
209
+ save();
210
+ return true;
211
+ }
212
+
213
+ /** The folds over `node` opened, outermost first, until it shows. Whether any was. */
214
+ function showAround(node) {
215
+ let opened = false;
216
+ for (let round = 0; round < 16 && hiddenByFold(node); round++) {
217
+ const marked = (node.nodeType === 1 ? node : node.parentElement).closest(`[${FOLDED}]`);
218
+ if (!folded.delete(marked.getAttribute(FOLDED))) marked.removeAttribute(FOLDED); // no fold of this page's
219
+ opened = true;
220
+ apply();
221
+ }
222
+ if (opened) save();
223
+ return opened;
224
+ }
225
+
226
+ // ---------- the buttons ----------
227
+
228
+ // A press held that began off the triangles (HELD): set on the window before any tool sees the
229
+ // press, gone with the press (let go, cancelled, the window left, or a move with no button down
230
+ // where its end was missed). The triangles shown stay shown, as the handle's block stays while a
231
+ // button is down (handle/ onMove), and take the pointer again once it is let go.
232
+ let held = false;
233
+ const hold = (on) => {
234
+ if (held === on) return;
235
+ held = on;
236
+ document.documentElement.toggleAttribute(HELD, on);
237
+ };
238
+ addEventListener("pointerdown", (event) => hold(!holds(event.target)), true);
239
+ for (const type of ["pointerup", "pointercancel"]) addEventListener(type, () => hold(false), true);
240
+ addEventListener("blur", () => hold(false));
241
+ addEventListener("pointermove", (event) => { if (held && event.buttons === 0) hold(false); }, { capture: true, passive: true });
242
+
243
+ function make(heading) {
244
+ const node = element("div", { class: "ge-fold", role: "button", html: FOLD_ARROW });
245
+ tooltip(node, () => (isFolded(heading) ? t("展开") : t("折叠")), shortcut("mod+enter"));
246
+ // No caret moves, no box selection starts, the focus stays (blocks.mjs passes presses on it by).
247
+ node.addEventListener("pointerdown", (event) => event.preventDefault());
248
+ node.addEventListener("click", (event) => {
249
+ event.preventDefault();
250
+ toggle(heading);
251
+ });
252
+ own(node, "heading fold", { owner: heading, alive: () => buttons.get(heading) === node && heading.isConnected && started && active(), expected: () => foldRectOf(heading) });
253
+ return node;
254
+ }
255
+
256
+ function drop(heading) {
257
+ buttons.get(heading)?.remove();
258
+ buttons.delete(heading);
259
+ }
260
+
261
+ /** A frame: a button by each folded heading in sight, and by the heading the pointer is at (`pointed`). */
262
+ function place(pointed) {
263
+ const want = new Set(started ? heads.filter((heading) => heading.isConnected && isFolded(heading)) : []);
264
+ if (started && pointed && foldable(pointed) && !hiddenByFold(pointed)) want.add(pointed);
265
+ for (const heading of [...buttons.keys()]) if (!want.has(heading)) drop(heading);
266
+ for (const heading of want) {
267
+ const shape = shapeOf(heading);
268
+ const rect = shape && foldRectOf(heading);
269
+ if (!rect) {
270
+ drop(heading);
271
+ continue;
272
+ }
273
+ let node = buttons.get(heading);
274
+ if (!node) {
275
+ node = make(heading);
276
+ buttons.set(heading, node);
277
+ }
278
+ const open = !isFolded(heading);
279
+ if (node.getAttribute("aria-expanded") !== String(open)) {
280
+ node.setAttribute("aria-expanded", String(open));
281
+ node.setAttribute("aria-label", open ? t("折叠") : t("展开"));
282
+ }
283
+ drawAt(node, shape.layer, rect.left - shape.origin.left, rect.top - shape.origin.top);
284
+ }
285
+ }
286
+
287
+ function hide() {
288
+ for (const heading of [...buttons.keys()]) drop(heading);
289
+ }
290
+
291
+ const folds = {
292
+ /** Editing begins: what was folded folded again, in the task editing begins in (no frame shows it open). */
293
+ start() {
294
+ if (started) return;
295
+ started = true;
296
+ setFoldOpener(showAround);
297
+ load();
298
+ apply();
299
+ observer = new MutationObserver(changed);
300
+ const root = main();
301
+ if (root) observer.observe(root, { childList: true, subtree: true, characterData: true });
302
+ addEventListener("pagehide", save);
303
+ },
304
+ /** Editing ends: every fold shown open (what is folded is kept). */
305
+ stop() {
306
+ if (!started) return;
307
+ save();
308
+ started = false;
309
+ observer?.disconnect();
310
+ for (const node of document.querySelectorAll(`[${FOLDED}]`)) node.removeAttribute(FOLDED);
311
+ heads = [];
312
+ hidden = new Set();
313
+ hide();
314
+ },
315
+ place,
316
+ hide,
317
+ /** A button shows (the frames go on following the headings). */
318
+ get showing() { return buttons.size > 0; },
319
+ holds,
320
+ toggle,
321
+ isFolded,
322
+ hidden: hiddenByFold,
323
+ showAround,
324
+ /** `units` with what each folded heading among them hides (a section it covers whole instead): rule.mjs takenWith. */
325
+ takenWith: (units) => takenWith(units, { isFolded, hiddenBy }),
326
+ /** A block with what it hides (a folded heading's section, folds inside it too), in document order: what goes into a column with it (columns/ split, drag/ besideTo). */
327
+ withHidden: (unit) => (isFolded(unit) ? [unit, ...hiddenBy(unit)] : [unit]),
328
+ /** The last block of a block and what it hides (a folded heading's last hidden block), for a place after it. */
329
+ lastOf: (unit) => (isFolded(unit) ? hiddenBy(unit).at(-1) ?? unit : unit),
330
+ /** The blocks of `list` as the reader sees them: each shown block, a folded heading with what it hides. */
331
+ groupsOf(list) {
332
+ const groups = [];
333
+ for (const unit of list) {
334
+ if (unit.hasAttribute(FOLDED) && groups.length > 0) groups.at(-1).push(unit);
335
+ else groups.push([unit]);
336
+ }
337
+ return groups;
338
+ },
339
+ /** Where Ctrl+Shift+↑/↓ takes a folded heading (rule.mjs stepOver). */
340
+ stepOver,
341
+ /** What the status line calls `targets` taken for `picked` (named: block-info.mjs's): 标题和折叠的 N 块. */
342
+ namedWith(picked, targets, named) {
343
+ const extra = targets.length - picked.length;
344
+ return extra > 0 && picked.every((node) => targets.includes(node)) ? t("{0}和折叠的 {1} 块", named(picked), extra) : named(targets);
345
+ },
346
+ };
347
+ return folds;
348
+ }
@@ -0,0 +1,125 @@
1
+ import { HEADING, LEFT_OUT, headingText } from "../outline/read.mjs";
2
+ import { UNIT, unitsOf } from "../core.mjs";
3
+ import { CHAPTER, LEADS_CHAPTER } from "../../components/registry.mjs";
4
+ import { FOLDED } from "../folded.mjs";
5
+
6
+ // What folding a heading's section takes (docs/editor.md, "Folding a heading's section"), pure so the
7
+ // unit tests hold it (tests/unit/heading-folds.mjs); the tool that folds is index.mjs. A heading the
8
+ // outline lists, standing straight in a box, folds what follows it in that box up to the first block
9
+ // that ends it: a heading of its level or higher, or a block holding one the outline lists (a section,
10
+ // a tabs area, a row of columns, a disclosure, a highlight block…), the outline's rule of what nests
11
+ // under a heading (outline/tree.mjs treeOf), so the fold and the outline say the same of a section.
12
+
13
+ export { FOLDED };
14
+
15
+ /** A heading's level, 1-4. */
16
+ export const levelOf = (heading) => Number(heading.localName[1]);
17
+
18
+ const kindOf = (node) => node.getAttribute("data-ge-unit-kind");
19
+
20
+ /** Whether a node is a heading that folds: one the outline lists, standing straight in a box, with words. */
21
+ export function foldable(node) {
22
+ return node?.nodeType === 1 && node.matches(HEADING) && node.matches(UNIT) && !node.closest(LEFT_OUT)
23
+ && Boolean(node.parentElement?.hasAttribute("data-ge-box")) && headingText(node) !== "";
24
+ }
25
+
26
+ /** Whether `unit` ends a section of level `level`: a heading of that level or higher, or a block holding one the outline lists. */
27
+ export function ends(unit, level) {
28
+ if (unit.matches(HEADING)) return levelOf(unit) <= level;
29
+ for (const heading of unit.querySelectorAll(HEADING)) {
30
+ if (levelOf(heading) <= level && !heading.closest(LEFT_OUT) && headingText(heading) !== "") return true;
31
+ }
32
+ return false;
33
+ }
34
+
35
+ /** The units after `heading` in its box that are its section (at `level`, its own by default), the one ending it not among them. */
36
+ export function sectionAfter(heading, level = levelOf(heading)) {
37
+ const box = heading.parentElement;
38
+ if (!box) return [];
39
+ const out = [];
40
+ let past = false;
41
+ for (const unit of unitsOf(box)) {
42
+ if (!past) {
43
+ past = unit === heading;
44
+ continue;
45
+ }
46
+ if (ends(unit, level)) break;
47
+ out.push(unit);
48
+ }
49
+ return out;
50
+ }
51
+
52
+ /** Nodes in document order, each once, none inside another. */
53
+ export function inOrder(nodes) {
54
+ const set = [...new Set(nodes)];
55
+ return set.filter((node) => !set.some((other) => other !== node && other.contains(node)))
56
+ .sort((a, b) => (a.compareDocumentPosition(b) & 4 ? -1 : 1)); // 4: DOCUMENT_POSITION_FOLLOWING
57
+ }
58
+
59
+ /**
60
+ * What an operation that takes blocks away whole takes for `units`: each folded heading with what it
61
+ * hides (isFolded(heading), hiddenBy(heading): the units its fold hides, those of folds inside it too),
62
+ * in document order. A folded heading whose box is a chapter (a section) of which it and what it hides
63
+ * are every unit, but for dividers before it, stands for that section: the section is taken.
64
+ */
65
+ export function takenWith(units, { isFolded, hiddenBy }) {
66
+ const out = [];
67
+ for (const unit of units) {
68
+ out.push(unit);
69
+ if (isFolded(unit)) out.push(...hiddenBy(unit));
70
+ }
71
+ for (const unit of [...out]) {
72
+ const box = unit.parentElement;
73
+ if (!isFolded(unit) || !box?.matches(UNIT) || !CHAPTER.has(kindOf(box))) continue;
74
+ const covered = new Set([unit, ...hiddenBy(unit)]);
75
+ const all = unitsOf(box);
76
+ const at = all.indexOf(unit);
77
+ if (all.every((each, index) => covered.has(each) || (index < at && LEADS_CHAPTER.has(kindOf(each))))) out.push(box);
78
+ }
79
+ return inOrder(out);
80
+ }
81
+
82
+ /**
83
+ * Where Ctrl+Shift+↑/↓ takes a folded heading (its section with it): past the next section of its
84
+ * level going down (the block after what it hides, which ends it, and what follows that up to the
85
+ * next block ending a section of its level), past the one before going up (back to the block ending a
86
+ * section of its level, or the box's first block, up to the block before it). `list`: the blocks it
87
+ * moves among, in order. { first, last } of what it goes past, or null where there is none.
88
+ */
89
+ export function stepOver(heading, delta, list = unitsOf(heading.parentElement)) {
90
+ const level = levelOf(heading);
91
+ const at = list.indexOf(heading);
92
+ if (at < 0) return null;
93
+ if (delta > 0) {
94
+ const from = at + 1 + sectionAfter(heading).length;
95
+ if (from >= list.length) return null;
96
+ let to = from;
97
+ while (to + 1 < list.length && !ends(list[to + 1], level)) to++;
98
+ return { first: list[from], last: list[to] };
99
+ }
100
+ if (at === 0) return null;
101
+ let from = at - 1;
102
+ while (from > 0 && !ends(list[from], level)) from--;
103
+ return { first: list[from], last: list[at - 1] };
104
+ }
105
+
106
+ /**
107
+ * The key a folded heading is kept by: `b:<block id>`, or with none `t:<tag>:<words>#<n>`, n its place
108
+ * among the foldable headings of its tag and words (`all`, in document order; with an id or not).
109
+ */
110
+ export function keyOf(heading, all) {
111
+ const id = heading.getAttribute("data-block-id");
112
+ if (id) return `b:${id}`;
113
+ const text = headingText(heading);
114
+ const same = all.filter((each) => each.localName === heading.localName && headingText(each) === text);
115
+ return `t:${heading.localName}:${text}#${Math.max(0, same.indexOf(heading))}`;
116
+ }
117
+
118
+ /** The heading of `all` a key names (keyOf), or null: by its block id, or the n-th of its tag and words, with an id or not. */
119
+ export function headingOf(key, all) {
120
+ if (key.startsWith("b:")) return all.find((each) => each.getAttribute("data-block-id") === key.slice(2)) ?? null;
121
+ const match = /^t:(h[1-4]):([\s\S]*)#(\d+)$/.exec(key);
122
+ if (!match) return null;
123
+ const [, tag, text, n] = match;
124
+ return all.filter((each) => each.localName === tag && headingText(each) === text)[Number(n)] ?? null;
125
+ }
@@ -0,0 +1,23 @@
1
+ // What the other tools ask of the headings' folds (fold/ folds a heading's section, docs/editor.md
2
+ // "Folding a heading's section"): the mark on what a fold hides, whether a node is hidden by one, and
3
+ // reveal(), what every path that brings a block into sight goes through (a link to a block, the
4
+ // outline, a 批注, a conflict's card): the folds over it opened, then what the kit opens.
5
+
6
+ /** What marks a unit (and its leads) a fold hides: the identity (data-ge-id) of the outermost heading folded over it. */
7
+ export const FOLDED = "data-ge-folded";
8
+
9
+ /** Whether a node (or the block it is in) is hidden by a fold. */
10
+ export const hiddenByFold = (node) => Boolean((node?.nodeType === 1 ? node : node?.parentElement)?.closest?.(`[${FOLDED}]`));
11
+
12
+ let opener = null; // the page's fold tool's showAround, once editing begins
13
+
14
+ /** The page's fold tool: what opens the folds over a node (fold/ showAround). */
15
+ export function setFoldOpener(open) {
16
+ opener = open;
17
+ }
18
+
19
+ /** What hides `target` opened: the folds over it first, then what the kit opens (DocKit.reveal: a disclosure, a tab, a detail card). */
20
+ export function reveal(target) {
21
+ opener?.(target);
22
+ globalThis.DocKit?.reveal(target);
23
+ }
@@ -25,7 +25,8 @@ import { t } from "../../../shared/i18n/index.mjs";
25
25
  // Esc and a press elsewhere. "更多" opens its list below it, resting on it or with a click. Resting
26
26
  // on the type row opens what the block can be turned into beside the bar. The bar shows the
27
27
  // selection's state and reports commands; the editor applies them (index.mjs). Swatches use the
28
- // kit's own classes, so they match the text in either theme. The icons are drawn here after Notion's
28
+ // kit's own classes, so they match the text in either theme (a background's, a span, shows the
29
+ // words' fill; the cell bar draws its own as a block's: table/cell-bar.mjs). The icons are drawn here after Notion's
29
30
  // look (20px, thin lines), not taken from Notion's files. The bar
30
31
  // fades in (no fade out); its panels fade in and out quickly and keep inside the window.
31
32
 
@@ -319,7 +319,7 @@ export function createFormatting({ active, say, recordStep, struct, convert, cro
319
319
 
320
320
  // The blocks of the selection across blocks turned into `type` together (one undo step), the
321
321
  // selection drawn again over their text where it is now (notion-lite's placeCaret(keep)); blocks
322
- // that hold no text of the body then (a callout, a disclosure, code) are selected instead.
322
+ // that hold no text of the body then (a disclosure, code) are selected instead.
323
323
  function turnAcross(type) {
324
324
  const turn = crossTurn();
325
325
  if (!turn) return;
@@ -1,5 +1,5 @@
1
1
  import { UNIT, isEmpty, isHost, nestedBlocks, withLeads } from "./core.mjs";
2
- import { boxRole, isBlock, isLead } from "../components/registry.mjs";
2
+ import { boxRole, endIsPadding, isBlock, isLead } from "../components/registry.mjs";
3
3
 
4
4
  // Where the blocks are on the screen (docs/editor.md, "Selecting blocks"). measure() reads the
5
5
  // page once into a tree of boxes and units with their rectangles. hoverAt, atHeight, measuredNode, pick,
@@ -11,7 +11,7 @@ import { boxRole, isBlock, isLead } from "../components/registry.mjs";
11
11
  // of text blocks, the one a click there puts the caret into)
12
12
  // pick the blocks a box selection takes: the ones it touches, lifted to one level
13
13
  // endOf what a click below the last block of a box does: a new paragraph there, or the
14
- // caret into the empty paragraph already at the end
14
+ // caret into the empty paragraph already at the end (none in a box whose end is its padding)
15
15
  // innerBox the innermost box at a click's height, whose blocks are those beside it
16
16
  //
17
17
  // Where dragged blocks would go (resolveDrop) is drag/drop.mjs, from the same tree.
@@ -217,8 +217,8 @@ function boxesOf(el) {
217
217
  return [...el.querySelectorAll("[data-ge-box]")].filter((box) => box.parentElement.closest(`${UNIT}, [data-ge-box]`) === el);
218
218
  }
219
219
 
220
- // A box's rect inside its padding and border.
221
- function contentOf(rect, style) {
220
+ /** A box's rect inside its padding and border (its text area: insert/ boxedBeside too). */
221
+ export function contentOf(rect, style) {
222
222
  const inset = (side) => (parseFloat(style[`padding${side}`]) || 0) + (parseFloat(style[`border${side}Width`]) || 0);
223
223
  return { left: rect.left + inset("Left"), top: rect.top + inset("Top"), right: rect.right - inset("Right"), bottom: rect.bottom - inset("Bottom") };
224
224
  }
@@ -360,10 +360,12 @@ export function pick(tree, rect) {
360
360
  * block, a flow or the page takes a new paragraph at its end ({ box, after }: after its last
361
361
  * block, or at its start); when the last block, followed down through the flows it holds (a
362
362
  * section's, a panel's, a disclosure's), is an empty paragraph, the caret goes there instead
363
- * ({ focus }), so clicking again does not pile them up. Anywhere else, null.
363
+ * ({ focus }), so clicking again does not pile them up. A box whose end is its own padding
364
+ * (endIsPadding: a highlight block, a disclosure's content) takes none: under its last block is a gap
365
+ * like any other (insert/ clickBlank). Anywhere else, null.
364
366
  */
365
367
  export function endOf(box, y) {
366
- if (!FLOWS.has(box.role)) return null;
368
+ if (!FLOWS.has(box.role) || endIsPadding(box.kind)) return null;
367
369
  const last = box.children.at(-1) ?? null;
368
370
  if (last && y <= last.rect.bottom) return null;
369
371
  let deepest = last;
@@ -44,13 +44,12 @@ import { t } from "../../../shared/i18n/index.mjs";
44
44
  // (lineAt, blocks.mjs). The handle and the bar are drawn in the editor's own layer (overlay-layer.mjs:
45
45
  // the one of the block they stand beside), never inside a block, each belonging to the block it stands
46
46
  // beside (overlays.mjs). A press ends when the pointer comes up, is cancelled, or the handle loses the
47
- // pointer capture (then a drag under way is cancelled, never dropped). While the page is edited, its
47
+ // pointer capture (then a drag under way is cancelled, never dropped). On a page the editor edits, its
48
48
  // side gutters (the kit's --doc-page-gutter, --doc-wide-gutter) are at least the room the
49
- // handle takes left of a block, and a little (EDGE): on a narrow window the handle stands in the
50
- // margin, never over the text, as Feishu's and Notion's do; the section bar has the same room on
51
- // the right. A wide window, whose column leaves more than that, does not change.
52
-
53
- const EDGE = ADD.width + HANDLE.width + HANDLE.gap + 12; // the page's gutter while editing: the bar's square, the grip, its gap, and room to breathe (64)
49
+ // handle takes left of a block, and a little (margin.mjs MARGIN.edge, from the page's first paint: the
50
+ // head's MARGIN_STYLE): on a narrow window the handle stands in the margin, never over the text, as
51
+ // Feishu's and Notion's do; the section bar has the same room on the right. A wide window, whose column
52
+ // leaves more than that, does not change.
54
53
 
55
54
  const STYLE = `
56
55
  .ge-handle { position: absolute; left: 0; top: 0; z-index: ${IN_LAYER.handle}; display: flex; align-items: center; height: ${HANDLE.height}px; touch-action: none; }
@@ -84,8 +83,6 @@ const STYLE = `
84
83
  .ge-section-bar::before { content: ""; position: absolute; top: 0; bottom: 0; left: 6px; width: 4px; border-radius: 2px; background: light-dark(rgb(55 53 47 / 14%), rgb(255 255 255 / 14%)); transition: background .08s ease-in; }
85
84
  .ge-section-bar:hover::before { background: light-dark(rgb(55 53 47 / 28%), rgb(255 255 255 / 26%)); }
86
85
  .ge-section-bar[data-active]::before { background: color-mix(in srgb, var(--doc-accent, #2678d8) 60%, transparent); }
87
- /* Room for the handle beside the page's blocks: the page's gutters while editing (resize/ reads --ge-edge-room). */
88
- :root[data-ge-editing] { --ge-edge-room: ${EDGE}px; --doc-page-gutter: var(--ge-edge-room); --doc-wide-gutter: var(--ge-edge-room); }
89
86
  `;
90
87
 
91
88
  // Notion's grip in size (its dragHandle icon at its icons' 20px): 2.5px dots 5px apart across and 6px down (notion-lite's were 5px down, the grip 2px shorter than Notion's: the user, 2026-10-26).
@@ -111,9 +108,11 @@ function emptyLine(host) {
111
108
  * handle as it is, as those tools keep themselves. add(target, where): the "+" clicked, an empty
112
109
  * block "below" the block (or "above" it: Alt) with the "/" menu open in it. notes: 批注 on blocks,
113
110
  * notes.can(units) whether they can take one, notes.at(unit) where the button beside the block stands
114
- * (window coordinates, null where it has none), notes.write(units) the annotation begun.
111
+ * (window coordinates, null where it has none), notes.write(units) the annotation begun. aside(node):
112
+ * a node over a block the 批注 button steps aside for while the pointer is on it (a heading's fold
113
+ * triangle, fold/, which may stand where the button would).
115
114
  */
116
- export function createHandle({ info, active, selection, drag, menu, openMenu, drawing, schedule, keeps = () => false, add = () => {}, notes = { can: () => false, at: () => null, write: () => {} } }) {
115
+ export function createHandle({ info, active, selection, drag, menu, openMenu, drawing, schedule, keeps = () => false, aside = () => false, add = () => {}, notes = { can: () => false, at: () => null, write: () => {} } }) {
117
116
  addStyle(STYLE);
118
117
  const { kindOf, boxKindOf } = info;
119
118
  const grip = element("div", { class: "ge-grip", role: "button", "aria-label": t("打开块菜单") });
@@ -133,7 +132,7 @@ export function createHandle({ info, active, selection, drag, menu, openMenu, dr
133
132
  let barShown = null; // the section the bar stands beside
134
133
  let shown = null; // the block the handle stands beside
135
134
  let noted = null; // the block the 批注 button stands beside
136
- let pointer = null; // { x, y }: where the pointer last moved over the page
135
+ let pointer = null; // { x, y, target }: where the pointer last moved over the page, and what it was on
137
136
  let typing = false; // the handle hides while the reader types, until the pointer moves
138
137
  let still = null; // the page scrolled with the pointer here: the handle hides until it moves away
139
138
  let press = null; // the handle is pressed: { target, x, y }
@@ -273,7 +272,7 @@ export function createHandle({ info, active, selection, drag, menu, openMenu, dr
273
272
  // block's middle line, or null where it has none: a block that takes an annotation, as do those
274
273
  // selected with it, standing beside nothing, nor in anything that does, with its place free.
275
274
  function noteSpot(target) {
276
- const rect = pointer && box(target);
275
+ const rect = pointer && !aside(pointer.target) && box(target);
277
276
  if (!rect || pointer.x <= (rect.left + rect.right) / 2 + (noted === target ? -MIDDLE_SLACK : MIDDLE_SLACK)) return null;
278
277
  if (!pointedAt(target).every((unit) => notes.can([unit])) || !notes.can(notedBlocks(target))) return null;
279
278
  for (let unit = target; unit; unit = unitElement(unit.parentElement)) if (standsBeside(kindOf(unit), unit)) return null;
@@ -416,7 +415,7 @@ export function createHandle({ info, active, selection, drag, menu, openMenu, dr
416
415
  }
417
416
 
418
417
  // The first block of a container whose first line acts for it (its description's `firstLineActs`: a
419
- // highlight block's, after its icon, Notion's callout disableFirstItemHoverMenu; a quote's) shows no
418
+ // highlight block's, after its icon, Notion's callout disableFirstItemHoverMenu; a callout's; a quote's) shows no
420
419
  // handle of its own: the container's stands on its line (a disclosure's is its title row).
421
420
  function firstOfPanel(el) {
422
421
  const panel = el?.parentElement;
@@ -452,7 +451,7 @@ export function createHandle({ info, active, selection, drag, menu, openMenu, dr
452
451
  still = null;
453
452
  if (!active() || event.buttons) return;
454
453
  typing = false;
455
- pointer = { x, y };
454
+ pointer = { x, y, target };
456
455
  // The pointer on the way to a 批注's marker or button finds a section's strip, which stands left of
457
456
  // them. A card at a section's edge is the card's.
458
457
  if (!bar.contains(target)) barOver = target?.closest?.(".ge-note-card") ? null : barAt(x, y);
@@ -586,6 +585,15 @@ export function createHandle({ info, active, selection, drag, menu, openMenu, dr
586
585
  placeBar();
587
586
  placeHandle();
588
587
  },
588
+ /**
589
+ * The block the pointer is at as the reader sees it, for what shows beside it but the handle (a
590
+ * heading's fold triangle, fold/): none while dragging, typing, drawing a box or dragging a gap;
591
+ * of a container whose first line acts for it (a highlight block), that first block.
592
+ */
593
+ get pointed() {
594
+ if (drag.active || drawing() || typing || document.documentElement.hasAttribute("data-ge-gap-drag") || !hovered?.isConnected) return null;
595
+ return pointedAt(hovered).at(-1);
596
+ },
589
597
  /** The handle, the 批注 button or the bar shows (the frames go on). */
590
598
  get showing() { return Boolean(shown || noted || barShown); },
591
599
  /** Whether a node is part of the handle, the 批注 button or the bar. */