@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
@@ -1,14 +1,19 @@
1
1
  import { OLD_OUTLINE_OPEN_KEY, OLD_PREFERENCES_KEY, OUTLINE_OPEN_KEY, PREFERENCES_KEY } from "../../shared/preferences.mjs";
2
+ import { LOOK_ATTRIBUTES } from "../../shared/document-look.mjs";
3
+ import { ADD, HANDLE } from "./handle-sizes.mjs";
2
4
 
3
5
  // The right side of the page while it is edited (docs/editor.md, "The outline and the right side"): the
4
6
  // outline (目录, outline/) docked at the window's right edge, the cards of 批注 (annotate/) between it and
5
7
  // the column, and where the body column goes beside them. The outline is a panel at the window's right
6
8
  // edge, in full (its list, 200px unless the reader dragged its guide wider or narrower: `outlineWidth`;
7
- // the guide 8px in) or as its strip (36px: a short line a heading, its card shown while the pointer is
8
- // on it), as the room allows.
9
+ // the list 8px in, its guide a little further in, close to its top level's titles: outline/list.mjs
10
+ // LIST) or as its strip (36px: a short line a heading, its card shown while the pointer is
11
+ // on it), as the room allows; or as its strip wherever the reader collapsed it (a click on its guide:
12
+ // `collapsed`, the preference outlineCollapsed, until a click on the guide in the strip's card takes it
13
+ // back, which gives the list only where the room allows it).
9
14
  //
10
15
  // One rule places the column: in the middle of what the outline leaves of the page, as far from the
11
- // window's left edge as from the outline (its guide, or its strip). With the outline the rooms at the
16
+ // window's left edge as from the outline (its list's edge, or its strip). With the outline the rooms at the
12
17
  // column's two sides are the same, and they stay the same as the guide is dragged (each gives or takes
13
18
  // half of what the list takes or gives) and as the window is resized; without it the column is
14
19
  // centered in the page as ever. Each room is the edge room at least (64: the block handle at the left,
@@ -48,18 +53,29 @@ import { OLD_OUTLINE_OPEN_KEY, OLD_PREFERENCES_KEY, OUTLINE_OPEN_KEY, PREFERENCE
48
53
  // column stands where it will stay from the first paint, and by setMargin() after that.
49
54
  // When they move the column, the page hears a resize, so what stands over the text (the format bar, a
50
55
  // selection's strips, a card) is placed again.
56
+ //
57
+ // The edge room is also the page's gutters (the kit's --doc-page-gutter and --doc-wide-gutter, page.css),
58
+ // set by MARGIN_STYLE, which the edit service puts in the page's head: the column the kit lets show
59
+ // (data-doc-ready, the frame a workbench swaps the page in on) stands where the editor keeps it, its lines
60
+ // broken as they stay, before the editor has started, the outline off and no 批注 as much as with them.
61
+ // (Until 2026-11-09 the handle put them in once the page edited: in a workbench with its sidebar open, a
62
+ // page 795px wide came in with the kit's 20px gutters, its column 712 wide and 42 in, and moved to 667 and
63
+ // 64 a few frames later, its lines breaking anew: the user's report on 0.1.1.)
51
64
 
52
65
  /**
53
- * The numbers: the edge room (the least room either side of the column keeps), the least it gives up
66
+ * The numbers: the edge room (the least room either side of the column keeps, and the page's gutters:
67
+ * the bar's square left of the block handle's grip, the grip, its gap and room to breathe, 64), the least it gives up
54
68
  * to (the block handle's: its 38px, its 12px gap, 2px), the column, the least a full-width column
55
69
  * keeps, the outline in full (its default width, the least and the most it is dragged to), its
56
- * guide's inset in its panel (the column's right room ends there), how far left of the guide its grip
57
- * reaches, its strip, the hysteresis; how far right of the column a card of 批注 stands (the button beside
70
+ * list's inset in its panel (`guide`: the column's right room ends there, at the list's edge, its
71
+ * guide's hairline LIST.guide further in, outline/list.mjs; the guide stood there
72
+ * until 2026-11-07, the numbers kept so the forms switch at the same widths), how far left of the guide's
73
+ * hairline its grip reaches (and how far short of the list what stands in the column's right room stays), its strip, the hysteresis; how far right of the column a card of 批注 stands (the button beside
58
74
  * a block, past a section's strip, ends 52px out, and a card under the pointer comes 5px left: 3px
59
75
  * clear; one focused comes 20px left, and no button shows then), a card's width, the least it gives up
60
- * to, and the room past it (clear of the outline's guide and grip).
76
+ * to, and the room past it (clear of the outline's list: its guide and grip).
61
77
  */
62
- export const MARGIN = Object.freeze({ edge: 64, start: 52, column: 712, least: 560, full: 200, fullMin: 160, fullMax: 360, guide: 8, grip: 4, strip: 36, hysteresis: 16, lane: 60, card: 308, cardLeast: 240, cardEnd: 20 });
78
+ export const MARGIN = Object.freeze({ edge: ADD.width + HANDLE.width + HANDLE.gap + 12, start: 52, column: 712, least: 560, full: 200, fullMin: 160, fullMax: 360, guide: 8, grip: 4, strip: 36, hysteresis: 16, lane: 60, card: 308, cardLeast: 240, cardEnd: 20 });
63
79
 
64
80
  /**
65
81
  * The outline's width in full for the reader's choice `chosen` (px; 0 or none: the default), within
@@ -71,19 +87,20 @@ export function outlineWidth(chosen, { full = 200, fullMin = 160, fullMax = 360
71
87
 
72
88
  /**
73
89
  * Where the right side's parts go on a page `width` px wide (its window less its scrollbar): `outline`,
74
- * whether the outline is on, `notes`, whether the page has 批注, `full`, its width in full
75
- * (outlineWidth), `wide`, a report read full width, `was`, the layout given last for the same request
76
- * (a form it laid out is kept for `hysteresis` px under the width it needs).
90
+ * whether the outline is on, `collapsed`, whether the reader collapsed it to its strip, `notes`,
91
+ * whether the page has 批注, `full`, its width in full (outlineWidth), `wide`, a report read full
92
+ * width, `was`, the layout given last for the same request (a form it laid out is kept for
93
+ * `hysteresis` px under the width it needs).
77
94
  * Returns { dock: the outline's width at the window's right edge (0, its strip's or its list's),
78
95
  * outline: its form, "full" | "strip" | null, room: what the outline takes of the page's width beside
79
- * the column's rooms (the list's less its guide's inset, the strip's, 0), side: the least room either
96
+ * the column's rooms (the list's less its inset, the strip's, 0), side: the least room either
80
97
  * side of the column keeps, left: the room at the column's left on this page, right: the room right of
81
98
  * it to the window's edge (its own and the outline's), column: its width, cards: the width of a card
82
99
  * of 批注 standing right of it (0: they fold into markers), cardRoom: what the page's rule keeps right of
83
100
  * the column for them (0: none), floor: the least room at its left }. Written with no outside name, so
84
101
  * the page's head can carry it as it is (EARLY_MARGIN).
85
102
  */
86
- export function marginLayout({ width, outline = false, notes = false, wide = false, was = null, edge = 64, start = 52, column = 712, least = 560, full = 200, guide = 8, strip = 36, hysteresis = 16, lane = 60, card = 308, cardLeast = 240, cardEnd = 20 }) {
103
+ export function marginLayout({ width, outline = false, collapsed = false, notes = false, wide = false, was = null, edge = 64, start = 52, column = 712, least = 560, full = 200, guide = 8, strip = 36, hysteresis = 16, lane = 60, card = 308, cardLeast = 240, cardEnd = 20 }) {
87
104
  const body = wide ? least : column;
88
105
  // The column in the middle of what the outline leaves, its rooms `side` at least (narrower only below
89
106
  // that), and left of there as far as a card's room asks, down to `start`, the card giving what is short.
@@ -99,17 +116,23 @@ export function marginLayout({ width, outline = false, notes = false, wide = fal
99
116
  if (!outline) return placed(null, 0, 0, edge);
100
117
  // In full while the list, as wide as the reader gave it, leaves the column the handle's room at both
101
118
  // sides (its rooms the edge room, less down to the handle's where the page is short); a form kept
102
- // under the width it needs (the hysteresis) narrows the column by what is short. Else its strip.
103
- const spare = width + guide - body; // the page beside the column, the list's room counted from its guide
119
+ // under the width it needs (the hysteresis) narrows the column by what is short. Else, or collapsed by
120
+ // the reader, its strip.
121
+ const spare = width + guide - body; // the page beside the column, the list's room counted from its edge (its inset `guide`)
104
122
  const kept = was?.outline === "full" ? hysteresis : 0;
105
- if (spare + kept >= 2 * start + full) return placed("full", full, full - guide, Math.max(start, Math.min(edge, (spare - full) / 2)));
123
+ if (!collapsed && spare + kept >= 2 * start + full) return placed("full", full, full - guide, Math.max(start, Math.min(edge, (spare - full) / 2)));
106
124
  return placed("strip", strip, strip, edge);
107
125
  }
108
126
 
109
127
  const SLIDE = 200; // ms: the column's slide as the page comes to have 批注 or has none left
110
128
 
111
- /** The page's rules for the room: the column placed (and narrowed only when it must), screen only. */
129
+ /**
130
+ * The page's rules for the room: its gutters the edge room, and on screen the column placed (and
131
+ * narrowed only when it must).
132
+ */
112
133
  export const MARGIN_STYLE = `
134
+ /* The handle's room beside the page's blocks, the page's gutters from its first paint (a page the editor edits: this sheet is in the head the edit service serves); resize/ and the workbench's sidebar read --ge-edge-room. */
135
+ :root { --ge-edge-room: ${MARGIN.edge}px; --doc-page-gutter: var(--ge-edge-room); --doc-wide-gutter: var(--ge-edge-room); }
113
136
  @media screen {
114
137
  :root[data-ge-margin] {
115
138
  /* The least room either side of the column keeps (marginLayout's side: the edge room, less where the page is short of it, down to the handle's). */
@@ -143,8 +166,9 @@ const roomCss = ({ room, side, cardRoom = 0, floor = side }) => `:root[data-ge-m
143
166
 
144
167
  // The head's script (EARLY_MARGIN): the outline's room before the page shows, when the report has it
145
168
  // on (`keys.open`: the reports that have it on, shared/preferences.mjs outlineOpen; `slug`: this page's), as
146
- // wide as the reader's preferences say (`keys.preferences`: outlineWidth, the same for every report),
147
- // and the cards' room, when it has 批注 (`notes`, the edit service's word).
169
+ // wide as the reader's preferences say and in the form they chose (`keys.preferences`: outlineWidth,
170
+ // outlineCollapsed, the same for every report), and the cards' room, when it has 批注 (`notes`, the
171
+ // edit service's word).
148
172
  // Never on a page opened on its own in a frame (?solo, the 写作偏好 dialog), which has none.
149
173
  // In the head the page has no content yet, so no scrollbar: its width is read again at
150
174
  // DOMContentLoaded, when the content is in and the kit has not shown it yet (a frame later), unless the
@@ -157,13 +181,17 @@ function earlyMargin(layout, widthOf, numbers, keys, slug, notes, room, early, c
157
181
  const outline = Array.isArray(open) && open.includes(slug);
158
182
  if (!outline && !notes) return;
159
183
  const prefs = JSON.parse(read(keys.preferences, keys.oldPreferences) || "{}") || {};
160
- const asked = { outline, notes, outlineWidth: Number(prefs.outlineWidth) || 0 };
184
+ const asked = { outline, notes, outlineWidth: Number(prefs.outlineWidth) || 0, collapsed: prefs.outlineCollapsed === true };
161
185
  const root = document.documentElement;
162
186
  const sheet = document.createElement("style");
163
187
  sheet.setAttribute("data-doc-dev", "");
164
188
  sheet.setAttribute(room, "");
165
189
  const lay = () => {
166
- const given = layout({ ...numbers, width: root.clientWidth, outline, notes, full: widthOf(asked.outlineWidth, numbers), wide: root.getAttribute("data-doc-width") === "full" });
190
+ // The column the kit's stylesheet gives (a document's look, its 正文宽度 alone, makes it another
191
+ // than 712px: src/shared/document-look.mjs), read as the editor reads it (numbers() below); the kit's
192
+ // stylesheet stands before this script in the head, so it is in.
193
+ const column = parseFloat(getComputedStyle(root).getPropertyValue("--doc-content-max-width")) || numbers.column;
194
+ const given = layout({ ...numbers, column, width: root.clientWidth, outline, collapsed: asked.collapsed, notes, full: widthOf(asked.outlineWidth, numbers), wide: root.getAttribute("data-doc-width") === "full" });
167
195
  sheet.textContent = css(given);
168
196
  sheet.setAttribute(early, JSON.stringify({ given, asked }));
169
197
  };
@@ -186,7 +214,7 @@ export const earlyMarginScript = (slug, { notes = false } = {}) => {
186
214
 
187
215
  // ---------- on the page ----------
188
216
 
189
- const asked = { outline: false, notes: false, outlineWidth: 0 }; // what the outline and 批注 ask for
217
+ const asked = { outline: false, notes: false, outlineWidth: 0, collapsed: false }; // what the outline and 批注 ask for
190
218
  const listeners = new Set();
191
219
  let now = null; // the layout given last
192
220
  let request = null; // what it was given for: { outline, wide } (the outline's width keeps the form laid out)
@@ -311,14 +339,16 @@ function start() {
311
339
  addEventListener("resize", resized);
312
340
  // The page's width changes without the window's too: its scrollbar comes or goes.
313
341
  new ResizeObserver(resized).observe(document.documentElement);
314
- // A report read full width, or no longer.
315
- new MutationObserver(() => apply()).observe(document.documentElement, { attributes: true, attributeFilter: ["data-doc-width"] });
342
+ // A report read full width, or no longer; the document's look changed (its column's width with its
343
+ // 正文宽度, never its 字体大小: the kit's ui.js puts it on the root, src/shared/document-look.mjs).
344
+ new MutationObserver(() => apply()).observe(document.documentElement, { attributes: true, attributeFilter: ["data-doc-width", ...Object.values(LOOK_ATTRIBUTES)] });
316
345
  }
317
346
 
318
347
  /**
319
348
  * Ask for room: { outline } (whether it shows), { outlineWidth } (its width in full as the reader
320
- * chose it, px; 0: the default), { notes } (whether the page has 批注: the column slides when that
321
- * changes it). The layout is given at once and again whenever the page's width changes it.
349
+ * chose it, px; 0: the default), { collapsed } (the reader collapsed it to its strip), { notes }
350
+ * (whether the page has 批注: the column slides when that changes it). The layout is given at once and
351
+ * again whenever the page's width changes it.
322
352
  */
323
353
  export function setMargin(part) {
324
354
  start();
@@ -331,7 +361,13 @@ export function setMargin(part) {
331
361
  /** The layout given last ({ dock, outline, room, side, left, right, column, cards, cardRoom, floor }; see marginLayout). */
332
362
  export const margin = () => now ?? layoutFor(isWide(), null);
333
363
 
334
- /** How far right of the column what stands in its right room may reach, by `layout` (marginLayout): short of the outline's guide and its grip, or of its strip, or of the window's edge. */
364
+ /**
365
+ * Whether the page's room gives the outline its list in full now, the reader's collapse aside: what a
366
+ * click on the guide in the strip's card would bring (the whole width needed, as for any strip going up).
367
+ */
368
+ export const listFits = () => marginLayout({ ...MARGIN, width: pageWidth(), ...numbers(), ...asked, outline: true, collapsed: false, full: outlineWidth(asked.outlineWidth, MARGIN), wide: isWide(), was: null }).outline === "full";
369
+
370
+ /** How far right of the column what stands in its right room may reach, by `layout` (marginLayout): MARGIN.grip short of the outline's list (its guide and grip further in), or of its strip, or of the window's edge. */
335
371
  export const rightRoom = ({ right, room } = margin()) => right - room - MARGIN.grip;
336
372
 
337
373
  /** Call `listener(layout)` whenever the layout changes (the outline's form or width, the column's least rooms, the cards' width). */
@@ -28,15 +28,14 @@ const STYLE = `
28
28
  .nmedia-menu-tab.active::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: 0; border-bottom: 2px solid var(--c-texPri); }
29
29
  .nmedia-menu-body { padding: 12px 12px 10px; }
30
30
  .nmedia-menu-inner { display: flex; justify-content: center; padding: 4px; }
31
- .nmedia-menu-upload { display: flex; align-items: center; justify-content: center; width: 100%; height: 32px; padding: 0 12px; border-radius: 6px; border: 1px solid var(--ca-borPriTra); font-size: 14px; font-weight: 500; line-height: 1.2; cursor: pointer; transition: background 20ms ease-in; }
32
- .nmedia-menu-upload:hover { background: var(--ca-bacIntTra); }
31
+ /* Its buttons are the editor's one button look (look.mjs BUTTONS): the pale blue primary, here what the tab does. */
32
+ .nmedia-menu-upload, .nmedia-menu-embed { width: 100%; font-size: 14px; }
33
33
  .nmedia-menu-link { flex-direction: column; align-items: stretch; gap: 8px; }
34
34
  .nmedia-menu-input { display: flex; align-items: center; gap: 4px; width: 100%; height: 28px; padding: 3px 6px; border-radius: 6px; background: var(--ca-bacSecTra); box-shadow: 0 0 0 1px var(--ca-borPriTra); box-sizing: border-box; }
35
35
  .nmedia-menu-input:focus-within { box-shadow: var(--c-inpBluFocRin); }
36
36
  .nmedia-menu-input input { flex: 1; min-width: 0; border: none; outline: none; background: transparent; padding: 0; font: inherit; font-size: 14px; line-height: 20px; color: var(--c-texPri); }
37
37
  .nmedia-menu-input input::placeholder { color: var(--c-texTer); }
38
- .nmedia-menu-embed { display: flex; align-items: center; justify-content: center; width: 100%; max-width: 300px; margin: 0 auto; height: 28px; padding: 0 8px; border-radius: 6px; background: var(--c-bluBacAccPri); color: #fff; font-size: 14px; font-weight: 500; cursor: pointer; box-sizing: border-box; }
39
- .nmedia-menu-embed:hover { filter: brightness(.94); }
38
+ .nmedia-menu-embed { max-width: 300px; margin: 0 auto; }
40
39
  .nmedia-menu-caption { margin: -6px 0 4px; padding: 4px 0; font-size: 12px; line-height: 1.4; color: var(--c-texSec); text-align: center; }
41
40
  .nmedia-menu.dragover .nmedia-menu-body { background: var(--ca-bluBacSecTra); }
42
41
  .nmedia-menu-choose { margin-top: 8px; }
@@ -76,10 +75,10 @@ export function openMediaMenu(anchor, { tabs, width, upload = {}, link = {}, onU
76
75
  `<div class="nmedia-menu-tab-wrap"><div role="tab" class="nmedia-menu-tab${key === current ? " active" : ""}" aria-selected="${key === current}" data-tab="${key}">${esc(label)}</div></div>`).join("")}</div>` : "")
77
76
  + `<div class="nmedia-menu-body">${current === "upload"
78
77
  ? (upload.primary
79
- ? `<div class="nmedia-menu-inner nmedia-menu-link"><div role="button" tabindex="0" class="nmedia-menu-embed nmedia-menu-choose" data-do="upload">${esc(upload.label ?? t("选择文件"))}</div>${upload.caption ? `<div class="nmedia-menu-caption">${esc(upload.caption)}</div>` : ""}</div>`
80
- : `<div class="nmedia-menu-inner"><div role="button" tabindex="0" class="nmedia-menu-upload" data-do="upload">${esc(upload.label ?? t("上传文件"))}</div></div>`)
78
+ ? `<div class="nmedia-menu-inner nmedia-menu-link"><div role="button" tabindex="0" class="ge-btn nmedia-menu-embed nmedia-menu-choose" data-primary data-do="upload">${esc(upload.label ?? t("选择文件"))}</div>${upload.caption ? `<div class="nmedia-menu-caption">${esc(upload.caption)}</div>` : ""}</div>`
79
+ : `<div class="nmedia-menu-inner"><div role="button" tabindex="0" class="ge-btn nmedia-menu-upload" data-primary data-do="upload">${esc(upload.label ?? t("上传文件"))}</div></div>`)
81
80
  : `<div class="nmedia-menu-inner nmedia-menu-link"><div class="nmedia-menu-input"><input type="url" placeholder="${esc(link.placeholder ?? t("粘贴链接…"))}" autocomplete="off"></div>`
82
- + `<div role="button" tabindex="0" class="nmedia-menu-embed" data-do="embed">${esc(link.button ?? t("嵌入链接"))}</div>`
81
+ + `<div role="button" tabindex="0" class="ge-btn nmedia-menu-embed" data-primary data-do="embed">${esc(link.button ?? t("嵌入链接"))}</div>`
83
82
  + (link.caption ? `<div class="nmedia-menu-caption">${esc(link.caption)}</div>` : "") + "</div>"}</div>`;
84
83
  const input = el.querySelector("input");
85
84
  if (input) {
@@ -1,7 +1,7 @@
1
1
  import { Z } from "../../shared/layers.mjs";
2
2
  import { addStyle, element, mount } from "./ui.mjs";
3
3
  import { hideTooltip, tooltip } from "./tooltip.mjs";
4
- import { svg } from "../components/icons.mjs";
4
+ import { SECTION_ICON, svg } from "../components/icons.mjs";
5
5
  import { t } from "../../shared/i18n/index.mjs";
6
6
 
7
7
  // The editor's menu card, used by the "/" menu (slash-menu/: block types, filtered as the reader
@@ -12,16 +12,33 @@ import { t } from "../../shared/i18n/index.mjs";
12
12
  // the page with a block selected). A press on a row never moves the caret. Colors follow the
13
13
  // report's light or dark theme (light-dark()). A row may open a submenu (Notion's 颜色 ›): a second
14
14
  // card right of it, opened by resting on the row, a click, Enter or →, and closed by ←, by resting
15
- // on another row, or with the menu; while it is open from the keys they move in it.
15
+ // on another row, or with the menu; while it is open from the keys they move in it. A row that both
16
+ // runs and opens a submenu (a split row: a table's 一键优化 ›) is two parts side by side, each with
17
+ // its own grey under the pointer: its words, which a click or Enter runs, and its › at its right,
18
+ // which opens the submenu (resting on it, a click, or →); resting on its words opens nothing.
19
+ //
20
+ // A card is as wide as its rows (Notion's menus: 180px at least), up to FIT.most, past which a row's
21
+ // words are cut by an ellipsis, so a short submenu (一键优化 ›: 自动调整列宽, 识别数字列; 对齐 ›) is no
22
+ // wider than its words; it once took 265px whatever it held. A card whose rows change as it is used
23
+ // keeps the one width its caller gives (`width`: the block menu's grid, the "/" menu, a code block's
24
+ // languages), or SEARCHING when it has a search field and gives none, so it never jumps as rows come
25
+ // and go.
26
+
27
+ const FIT = { least: 180, most: 320 }; // a card with no width given: as wide as its rows, between these
28
+ const SEARCHING = 240; // a card with a search field and no width given: its rows filtered as typed
16
29
 
17
30
  const STYLE = `
18
31
  .ge-pop {
19
- position: fixed; left: 0; top: 0; z-index: ${Z.menu}; width: 265px; max-width: calc(100vw - 16px); max-height: calc(100vh - 24px);
32
+ position: fixed; left: 0; top: 0; z-index: ${Z.menu}; width: max-content; min-width: ${FIT.least}px; max-width: min(${FIT.most}px, calc(100vw - 16px)); max-height: calc(100vh - 24px);
20
33
  overflow-x: hidden; overflow-y: auto; padding: 4px 0; border-radius: 10px; background: var(--ge-bg); color: var(--ge-tx);
21
34
  box-shadow: var(--ge-shadow-pop);
22
35
  font: 14px/1.2 ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", Helvetica, "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
23
36
  user-select: none; scrollbar-width: thin; scrollbar-color: var(--ge-line-strong) transparent; animation: ge-pop-in .12s ease-out;
24
37
  }
38
+ /* A width given (data-sized: opts.width, placed by place()): that width, whatever the rows hold. */
39
+ .ge-pop[data-sized] { min-width: 0; max-width: calc(100vw - 16px); }
40
+ /* A short list of one kind of word (a card's 大小 ›: 4 × 2): only as wide as its rows, no 180px floor. */
41
+ .ge-pop[data-compact] { min-width: 0; }
25
42
  @keyframes ge-pop-in { from { opacity: 0; transform: scale(.98); } }
26
43
  @media (prefers-reduced-motion: reduce) { .ge-pop { animation: none; } }
27
44
  .ge-pop-title { display: flex; align-items: center; gap: 8px; padding: 8px 12px; color: var(--ge-tx2); font-size: 12px; font-weight: 500; line-height: 14px; }
@@ -43,6 +60,7 @@ const STYLE = `
43
60
  words and the hint takes what is left, cut short with an ellipsis where it is too long (an English
44
61
  row's current choice once took the whole row, its label drawn 0px wide). */
45
62
  .ge-pop-label:has(+ .ge-pop-hint) { flex: 1 0 auto; max-width: 100%; }
63
+ .ge-pop-of { color: var(--ge-tx3); }
46
64
  .ge-pop-hint { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--ge-tx3); font-size: 12px; white-space: nowrap; }
47
65
  /* A row of a page (its title, the page it is under as its hint): a long title is what an ellipsis
48
66
  cuts, and the page above keeps its words, up to a part of the row. */
@@ -52,9 +70,21 @@ const STYLE = `
52
70
  .ge-pop-arrow { color: var(--ge-tx3); }
53
71
  .ge-pop-arrow svg { display: block; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
54
72
  .ge-pop-row[aria-expanded="true"] { background: var(--ge-hover); }
73
+ /* A split row (run and submenu both): its words (.ge-pop-main) and its › (.ge-pop-more, 32px wide:
74
+ the › where another submenu row has its own) side by side, 2px apart, each grey on its own: the words
75
+ when the row is highlighted and the pointer is not on the ›, the › under the pointer and while its
76
+ submenu is open. The row itself takes no grey. */
77
+ .ge-pop-row[data-split] { gap: 2px; padding: 0; }
78
+ .ge-pop-row[data-split][data-active], .ge-pop-row[data-split][aria-expanded="true"] { background: transparent; }
79
+ .ge-pop-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; align-self: stretch; padding: 0 8px; border-radius: 6px; }
80
+ .ge-pop-more { flex: none; display: flex; align-items: center; justify-content: center; width: 32px; align-self: stretch; border-radius: 6px; }
81
+ .ge-pop-row[data-split][data-active]:not([data-more]) > .ge-pop-main,
82
+ .ge-pop-row[data-split][data-more] > .ge-pop-more,
83
+ .ge-pop-row[data-split][aria-expanded="true"] > .ge-pop-more { background: var(--ge-hover); }
55
84
  .ge-pop-check svg { display: block; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
56
85
  .ge-pop-sep { height: 1px; margin: 3px 12px 4px; background: var(--ge-line); }
57
- .ge-pop-note { padding: 0 12px 6px 40px; color: var(--ge-tx3); font-size: 12px; line-height: 1.5; }
86
+ /* A note wraps at the card's width and never widens it (no width of its own: contain). */
87
+ .ge-pop-note { contain: inline-size; padding: 0 12px 6px 40px; color: var(--ge-tx3); font-size: 12px; line-height: 1.5; }
58
88
  .ge-pop-empty { padding: 8px 12px; color: var(--ge-tx3); }
59
89
  /* What a row makes, shown beside it under the pointer (a component package's preview.svg). */
60
90
  .ge-pop-preview { display: block; width: 240px; max-height: 180px; border-radius: 4px; object-fit: contain; background: var(--ge-bg); }
@@ -63,7 +93,7 @@ const STYLE = `
63
93
  .ge-pop-search input:focus { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ge-tx3) 60%, transparent); }
64
94
  .ge-pop-search input::placeholder { color: var(--ge-tx3); }
65
95
  /* Rows in a card that is there already (inside: a link card's pages): under what stands in it, as wide as it, no card of their own. */
66
- .ge-pop[data-inside] { position: static; flex: 0 0 100%; width: auto; max-width: none; padding: 4px 0 0; border-radius: 0; background: none; box-shadow: none; animation: none; }
96
+ .ge-pop[data-inside] { position: static; flex: 0 0 100%; width: auto; min-width: 0; max-width: none; padding: 4px 0 0; border-radius: 0; background: none; box-shadow: none; animation: none; }
67
97
  .ge-pop[data-inside] .ge-pop-row { width: 100%; margin-inline: 0; }
68
98
  `;
69
99
 
@@ -78,8 +108,7 @@ export const ICONS = {
78
108
  outer: svg('<rect x="3.25" y="3.25" width="13.5" height="13.5" rx="2.5" stroke-dasharray="2.2 2.2"/><rect x="6.5" y="6.5" width="7" height="7" rx="1.25"/>'),
79
109
  section: svg(`${SECTION_LINES}<path d="M14.5 12v5M12 14.5h5"/>`),
80
110
  dissolve: svg(`${SECTION_LINES}<path d="m12.75 12.75 3.5 3.5M16.25 12.75l-3.5 3.5"/>`), // section's plus, turned 45°
81
- // Blocks gathered into a new section: their lines between brackets.
82
- group: svg('<path d="M6 3.75h-.75a2 2 0 0 0-2 2v8.5a2 2 0 0 0 2 2H6M14 3.75h.75a2 2 0 0 1 2 2v8.5a2 2 0 0 1-2 2H14"/><path d="M7.25 7.75h5.5M7.25 12.25h5.5"/>'),
111
+ group: SECTION_ICON, // blocks gathered into a new section: their lines between brackets (components/icons.mjs)
83
112
  center: svg('<rect x="5.25" y="4.25" width="9.5" height="11.5" rx="1.75"/><path d="M2.75 4v12M17.25 4v12"/>'),
84
113
  full: svg('<rect x="3.25" y="4.25" width="13.5" height="11.5" rx="1.75"/><path d="m6.5 10h7M6.5 10l1.75-1.75M6.5 10l1.75 1.75M13.5 10l-1.75-1.75M13.5 10l-1.75 1.75"/>'),
85
114
  reset: svg('<path d="M4.75 6.75A6 6 0 1 1 4 10.5"/><path d="M4.5 3.5v3.5H8"/>'),
@@ -99,23 +128,29 @@ const opened = new Set(); // the menus open now, whichever part of the page open
99
128
  export const menuOpen = () => opened.size > 0;
100
129
 
101
130
  /**
102
- * items: rows { label, run, icon?, hint?, long?, current?, checked?, disabled?, note?, preview? } (long:
131
+ * items: rows { label, run, icon?, of?, hint?, long?, current?, checked?, disabled?, note?, preview? } (of: what
132
+ * the label is of, grey after it, a block's pack where two rows have one name; long:
103
133
  * a label of any length, a page's title, cut before its hint is; current: the choice
104
134
  * made, checked and not to be chosen again; checked: a switch that is on, checked and clicked to turn
105
- * it off; preview: { src, says }, a picture of what the row makes, where it has one, and a line on it,
135
+ * it off; selected: the choice of a dropdown (a field's list of options), checked as current is but
136
+ * still a row to click, which closes the card with nothing changed; preview: { src, says }, a picture of what the row makes, where it has one, and a line on it,
106
137
  * shown at the row's right while the pointer rests on it, notion-lite's row tooltip) (or { label, icon?, submenu:
107
138
  * () => items, width?, search? } opening a submenu, with a search field at its top when `search` is
108
- * given, as `opts.search` below, and `search.empty` the words shown when nothing is found), titles
139
+ * given, as `opts.search` below, and `search.empty` the words shown when nothing is found; a row
140
+ * with both `run` and `submenu` is a split row: its words run, its › opens the submenu), titles
109
141
  * { title, icon? }, separators { separator: true } and grids { node, cells, columns } (type-grid.mjs: the
110
142
  * node shown as it is, its cells, buttons that act on their own click, highlighted as rows are).
111
- * opts: { label, empty, highlight, onClose, around, side, width } — width: the card's, in px, instead
112
- * of 265 (the block menu: as wide as its grid of types). around: the rect of the block a handle
143
+ * opts: { label, empty, highlight, onClose, around, side, width } — width: the card's, in px, whatever
144
+ * its rows hold (the block menu: as wide as its grid of types), where without it the card is as wide
145
+ * as its rows (FIT; SEARCHING with a search field). around: the rect of the block a handle
113
146
  * (the anchor) stands for; the menu keeps clear of that block: left of it beside the handle, else
114
147
  * below or above it (scrolling inside when only part of it fits), and over it only when the block
115
148
  * leaves no room at all. side: "right": beside the anchor, right of it (left of it when there is no
116
149
  * room), level with it (a section's bar). Without either the menu stands below the anchor (the
117
150
  * caret), or above it when there is no room below; align: "end", its right edge at the anchor's.
118
- * icons: false, rows of words alone (no column for icons: a code block's languages); height: the
151
+ * icons: false, rows of words alone (no column for icons: a code block's languages; a row that opens a
152
+ * submenu says it of its own as `icons: false`, and `compact: true` makes it only as wide as its rows, no
153
+ * 180px floor: a card's 大小 ›); height: the
119
154
  * card's greatest height, in px (its rows scroll inside it past that). stand(size): where the card
120
155
  * stands, its { left, top } for its { width, height }, in place of all of these rules (a pasted
121
156
  * address's 选择视图, by the format bar's rule: placement.mjs besideText). inside: an element the rows
@@ -139,25 +174,32 @@ export function createMenu() {
139
174
  let subKeys = false; // opened from the keys: they move in it
140
175
  let subTimer = 0;
141
176
  let pointedRow = null;
177
+ let pointedMore = false; // the pointer on a split row's › (not on its words)
142
178
 
143
179
  // The pointer over a row highlights it. In a menu that starts with nothing highlighted (the block
144
180
  // menu), leaving the row takes the highlight off again, as :hover would; a row the keys moved to
145
- // stays, and so does the "/" menu's, the row Enter runs.
146
- const point = (row) => {
181
+ // stays, and so does the "/" menu's, the row Enter runs. On a split row, `more`: the pointer is on
182
+ // its › (data-more on the row, which greys the › instead of the words), the part that opens.
183
+ const point = (row, more = false) => {
147
184
  highlight(rows.indexOf(row), false);
148
185
  pointed = true;
149
- if (row === pointedRow) return;
186
+ if (row.split) row.toggleAttribute("data-more", more);
187
+ if (row === pointedRow && more === pointedMore) return;
150
188
  pointedRow = row;
189
+ pointedMore = more;
151
190
  clearTimeout(subTimer);
152
191
  // Notion's: a submenu opens 80ms after the pointer rests on its row (200ms while another is
153
- // open), and goes 250ms after the pointer rests elsewhere.
154
- if (row.submenu && row !== subRow) subTimer = setTimeout(() => openSub(row), subRow ? 200 : 80);
155
- else if (!row.submenu && subRow) subTimer = setTimeout(closeSub, 250);
192
+ // open), and goes 250ms after the pointer rests elsewhere (a split row's words being elsewhere).
193
+ const opens = Boolean(row.submenu) && (!row.split || more);
194
+ if (opens && row !== subRow) subTimer = setTimeout(() => openSub(row), subRow ? 200 : 80);
195
+ else if (!opens && subRow) subTimer = setTimeout(closeSub, 250);
156
196
  };
157
197
  const leave = (row) => {
158
198
  if (pointed && rows[active] === row && options.highlight === false) highlight(-1);
199
+ row.removeAttribute("data-more");
159
200
  if (pointedRow !== row) return;
160
201
  pointedRow = null;
202
+ pointedMore = false;
161
203
  clearTimeout(subTimer); // passing over a row opens nothing
162
204
  if (subRow === row && !subKeys) subTimer = setTimeout(closeSub, 250); // unless the pointer goes into it
163
205
  };
@@ -180,28 +222,36 @@ export function createMenu() {
180
222
  children.push(item.node);
181
223
  } else {
182
224
  const row = element("button", { type: "button", class: "ge-pop-row", role: "menuitem" });
225
+ // A split row's words go in a part of their own, its › in another (CSS above).
226
+ row.split = Boolean(item.run && item.submenu);
227
+ const words = row.split ? element("span", { class: "ge-pop-main" }) : row;
228
+ if (row.split) row.setAttribute("data-split", "");
183
229
  if (item.long) row.setAttribute("data-long", "");
184
- if (options.icons !== false) row.append(element("span", { class: "ge-pop-icon", html: item.icon ?? "" }));
185
- row.append(element("span", { class: "ge-pop-label", text: item.label }));
186
- if (item.hint) row.append(element("span", { class: "ge-pop-hint", text: item.hint }));
230
+ if (options.icons !== false) words.append(element("span", { class: "ge-pop-icon", html: item.icon ?? "" }));
231
+ // A row's pack where two rows have one name (我的待办 · 柔光风格): grey, after the name, in the label.
232
+ words.append(item.of ? element("span", { class: "ge-pop-label" }, item.label, element("span", { class: "ge-pop-of", text: ` · ${item.of}` })) : element("span", { class: "ge-pop-label", text: item.label }));
233
+ if (item.hint) words.append(element("span", { class: "ge-pop-hint", text: item.hint }));
234
+ if (row.split) row.append(words, element("span", { class: "ge-pop-more", html: `<span class="ge-pop-arrow">${ICONS.chevron}</span>` }));
235
+ else if (item.submenu) row.append(element("span", { class: "ge-pop-arrow", html: ICONS.chevron }));
187
236
  if (item.submenu) {
188
- row.append(element("span", { class: "ge-pop-arrow", html: ICONS.chevron }));
189
237
  row.setAttribute("aria-haspopup", "menu");
190
238
  row.submenu = item.submenu;
191
239
  row.subWidth = item.width;
192
240
  row.subSearch = item.search;
241
+ row.subLook = { icons: item.icons, compact: item.compact };
193
242
  }
194
- if (item.current || item.checked) {
195
- row.append(element("span", { class: "ge-pop-check", html: ICONS.check, "aria-label": item.current ? t("当前") : t("已开启") }));
243
+ if (item.current || item.checked || item.selected) {
244
+ row.append(element("span", { class: "ge-pop-check", html: ICONS.check, "aria-label": item.checked ? t("已开启") : t("当前") }));
196
245
  row.setAttribute("data-current", "");
197
- if (item.checked) row.setAttribute("aria-checked", "true");
246
+ if (item.checked || item.selected) row.setAttribute("aria-checked", "true");
198
247
  }
199
248
  row.disabled = Boolean(item.disabled || item.current);
200
249
  row.run = item.run;
201
250
  row.addEventListener("mousedown", (event) => event.preventDefault());
202
- row.addEventListener("pointermove", () => point(row));
251
+ const onMore = (event) => row.split && Boolean(event.target.closest?.(".ge-pop-more"));
252
+ row.addEventListener("pointermove", (event) => point(row, onMore(event)));
203
253
  row.addEventListener("pointerleave", () => leave(row));
204
- row.addEventListener("click", () => run(row));
254
+ row.addEventListener("click", (event) => run(row, onMore(event)));
205
255
  rows.push(row);
206
256
  children.push(row);
207
257
  if (item.note) children.push(element("div", { class: "ge-pop-note", text: item.note }));
@@ -319,6 +369,7 @@ export function createMenu() {
319
369
  const search = row.subSearch && { placeholder: row.subSearch.placeholder, filter: (text) => closing(row.subSearch.filter(text)) };
320
370
  sub.open(items, row.getBoundingClientRect(), {
321
371
  side: "right", gap: 0, height: Math.round(innerHeight * 0.7), highlight: keys, width: row.subWidth, label: row.textContent, search, empty: row.subSearch?.empty, focus: keys,
372
+ ...(row.subLook.icons === false ? { icons: false } : {}), ...(row.subLook.compact ? { compact: true } : {}),
322
373
  onClose: () => {
323
374
  row.removeAttribute("aria-expanded");
324
375
  if (subRow === row) {
@@ -335,9 +386,11 @@ export function createMenu() {
335
386
  sub?.close();
336
387
  }
337
388
 
338
- function run(row) {
389
+ // A row's work: its submenu opened (a split row's only from its ›: `more`), a grid's cell's own
390
+ // click, or the menu closed and its action run (Enter and a click on a split row's words).
391
+ function run(row, more = false) {
339
392
  if (!row || row.disabled) return false;
340
- if (row.submenu) {
393
+ if (row.submenu && (!row.split || more)) {
341
394
  openSub(row, true);
342
395
  return true;
343
396
  }
@@ -391,10 +444,12 @@ export function createMenu() {
391
444
  const menu = {
392
445
  open(items, rect, opts = {}) {
393
446
  close();
394
- options = opts;
447
+ options = opts.search && !opts.width ? { ...opts, width: SEARCHING } : opts;
395
448
  anchor = rect;
396
449
  opened.add(menu);
397
450
  node = element("div", { class: "ge-pop", role: "menu", "aria-label": opts.label ?? t("菜单") });
451
+ if (options.width) node.setAttribute("data-sized", "");
452
+ if (options.compact) node.setAttribute("data-compact", "");
398
453
  if (opts.inside) {
399
454
  node.setAttribute("data-inside", "");
400
455
  opts.inside.append(node);
@@ -30,6 +30,30 @@ export function commonRun(xs, ys) {
30
30
  return [...head, ...middle, ...tail];
31
31
  }
32
32
 
33
+ // A word diff of two texts, the reader's version against the file's (the conflict card's), or an
34
+ // Agent's baseline against the page (changes/): runs of { tag, text }, tag null
35
+ // for words both have, "del" for the reader's alone and "ins" for the file's alone (an ins before
36
+ // the del it stands beside). Words are runs of letters and digits, spaces, single other characters
37
+ // (each Chinese character on its own); at most 1500 words of each (commonRun above finds what is shared).
38
+ export function wordDiff(mine, theirs) {
39
+ const tokens = (text) => (text.match(/[A-Za-z0-9]+|\s+|[^A-Za-z0-9\s]/gu) ?? []).slice(0, 1500);
40
+ const a = tokens(mine);
41
+ const b = tokens(theirs);
42
+ const runs = [];
43
+ const add = (tag, text) => {
44
+ if (runs.at(-1)?.tag === tag) runs.at(-1).text += text;
45
+ else runs.push({ tag, text });
46
+ };
47
+ let [i, j] = [0, 0];
48
+ for (const [x, y] of [...commonRun(a, b), [a.length, b.length]]) {
49
+ while (j < y) add("ins", b[j++]);
50
+ while (i < x) add("del", a[i++]);
51
+ if (x < a.length) add(null, a[i++]);
52
+ j++;
53
+ }
54
+ return runs;
55
+ }
56
+
33
57
  // ---------- merging a block both sides changed ----------
34
58
 
35
59
  // Tokens of a block's inline HTML: a tag, an entity, a word, a run of spaces, or any other single
@@ -1,6 +1,6 @@
1
1
  import { Z } from "../../shared/layers.mjs";
2
2
  import { PANEL_STYLE, onPanels, panelGone, panelRoom, panelShown } from "./panel.mjs";
3
- import { addStyle, button, element, mount } from "./ui.mjs";
3
+ import { addStyle, element, mount, textButton } from "./ui.mjs";
4
4
  import { t } from "../../shared/i18n/index.mjs";
5
5
  import { preference } from "../../shared/preferences.mjs";
6
6
  import { routine } from "./quiet.mjs";
@@ -24,7 +24,7 @@ import { routine } from "./quiet.mjs";
24
24
  // so that the box has the whole window to be as wide as.
25
25
 
26
26
  const STYLE = `
27
- .ge-toast { position: fixed; left: 0; right: 0; bottom: 24px; z-index: ${Z.toast}; display: flex; align-items: center; gap: 10px; box-sizing: border-box; width: fit-content; max-width: min(640px, calc(100vw - 32px)); margin: 0 auto; padding: 8px 14px; border-radius: 8px; background: var(--ge-toast-bg); color: #f0efed; box-shadow: var(--ge-shadow); font: 14px/1.45 var(--doc-font, system-ui, sans-serif); opacity: 0; transform: translateY(10px); pointer-events: none; transition: opacity .18s, transform .18s; }
27
+ .ge-toast { position: fixed; left: 0; right: 0; bottom: 24px; z-index: ${Z.toast}; display: flex; align-items: center; gap: 10px; box-sizing: border-box; width: fit-content; max-width: min(640px, calc(100vw - 32px)); margin: 0 auto; padding: 8px 14px; border-radius: 8px; background: var(--ge-toast-bg); color: #f0efed; box-shadow: var(--ge-shadow); font: 14px/1.45 var(--doc-sans-font, var(--doc-font, system-ui, sans-serif)); opacity: 0; transform: translateY(10px); pointer-events: none; transition: opacity .18s, transform .18s; }
28
28
  .ge-toast[data-show] { opacity: 1; transform: none; }
29
29
  .ge-toast[data-tone="error"]::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: #ff6b6b; }
30
30
  `;
@@ -128,7 +128,7 @@ export function showPanel(title) {
128
128
  closePanel();
129
129
  addStyle(PANEL_STYLE);
130
130
  panel = mount(element("div", { class: "ge-panel", role: "region", "aria-label": title },
131
- element("div", { class: "ge-panel-head" }, element("span", { text: title }), button(t("关闭"), closePanel, { "data-quiet": "" }))));
131
+ element("div", { class: "ge-panel-head" }, element("span", { text: title }), textButton(t("关闭"), closePanel, { quiet: true }))));
132
132
  panelShown(panel);
133
133
  }
134
134
 
@@ -1,6 +1,6 @@
1
1
  import { TYPE_COMPONENT, blocksHidden } from "../components/registry.mjs";
2
2
 
3
- // What the menus that make a block offer of the reader's 设置 › 组件 (docs/workbench.md): the one place
3
+ // What the menus that make a block offer of the reader's 设置 › 组件列表 (docs/workbench.md): the one place
4
4
  // that hears what is hidden, gallery.json's as the page's map holds it (`hidden`), then as its event
5
5
  // stream says it (`doc-settings-changed`), which costs the page nothing. A block type goes with its
6
6
  // component. "/", 在下方新建 (both slash-menu/ typeItems) and the handle's grid (block-menu/) read it; no
@@ -1,23 +1,24 @@
1
1
  // What is folded in the outline, kept for each report (localStorage, so the page the workbench shows
2
2
  // again after an Agent's write, which is a new page, folds what the reader folded): a node's key (a
3
- // heading's block id, a tab's area and tab), the reports folded last kept and the oldest let go.
3
+ // heading's block id, a tab's area and tab), the reports folded last kept and the oldest let go. The
4
+ // headings folded on the page (fold/) are kept the same way under a key of their own (`key`).
4
5
 
5
6
  import { KEYS } from "../../../shared/renamed-storage.mjs";
6
7
 
7
8
  export const FOLDS_KEY = KEYS.outlineFolds;
8
9
  const KEEP = 100; // reports
9
10
 
10
- function read(storage) {
11
+ function read(storage, key) {
11
12
  try {
12
- const value = JSON.parse(storage.get(FOLDS_KEY) ?? "null");
13
+ const value = JSON.parse(storage.get(key) ?? "null");
13
14
  if (value && typeof value === "object" && value.folds && typeof value.folds === "object") return value;
14
15
  } catch {}
15
16
  return { folds: {} };
16
17
  }
17
18
 
18
- /** The keys folded in report `slug` (`storage`: { get, set }, page/storage.mjs). */
19
- export function foldsOf(storage, slug) {
20
- const keys = read(storage).folds[slug];
19
+ /** The keys folded in report `slug` (`storage`: { get, set }, page/storage.mjs), kept under `key`. */
20
+ export function foldsOf(storage, slug, key = FOLDS_KEY) {
21
+ const keys = read(storage, key).folds[slug];
21
22
  return new Set(Array.isArray(keys) ? keys.filter((key) => typeof key === "string") : []);
22
23
  }
23
24
 
@@ -25,14 +26,14 @@ export function foldsOf(storage, slug) {
25
26
  * Keep `folded` for report `slug`: only the keys the page still has (`present`), the report the one
26
27
  * folded last; none folded forgets the report. At most KEEP reports, the oldest going first.
27
28
  */
28
- export function keepFolds(storage, slug, folded, present = null) {
29
- const value = read(storage);
30
- const keys = [...folded].filter((key) => !present || present.has(key));
29
+ export function keepFolds(storage, slug, folded, present = null, key = FOLDS_KEY) {
30
+ const value = read(storage, key);
31
+ const keys = [...folded].filter((each) => !present || present.has(each));
31
32
  delete value.folds[slug]; // put back last: the report folded last
32
33
  if (keys.length) value.folds[slug] = keys;
33
34
  const slugs = Object.keys(value.folds);
34
35
  for (const old of slugs.slice(0, Math.max(0, slugs.length - KEEP))) delete value.folds[old];
35
- storage.set(FOLDS_KEY, JSON.stringify(value));
36
+ storage.set(key, JSON.stringify(value));
36
37
  }
37
38
 
38
39
  /**