@heycys/gallery 0.1.1 → 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 (370) hide show
  1. package/THIRD-PARTY-NOTICES.md +1 -1
  2. package/browser/editor/components/columns/page.mjs +4 -4
  3. package/browser/editor/components/embed/index.mjs +2 -2
  4. package/browser/editor/components/flowchart/add.mjs +7 -6
  5. package/browser/editor/components/flowchart/page.mjs +1 -1
  6. package/browser/editor/components/icons.mjs +8 -0
  7. package/browser/editor/components/image/crop.mjs +6 -9
  8. package/browser/editor/components/installed/index.mjs +11 -4
  9. package/browser/editor/components/installed/page.mjs +27 -16
  10. package/browser/editor/components/installed/served.mjs +6 -5
  11. package/browser/editor/components/instances.mjs +18 -0
  12. package/browser/editor/components/list/page.mjs +3 -6
  13. package/browser/editor/components/registry.mjs +13 -4
  14. package/browser/editor/components/table/index.mjs +59 -2
  15. package/browser/editor/components/table/page.mjs +1 -1
  16. package/browser/editor/page/annotate/card.mjs +3 -6
  17. package/browser/editor/page/autosave.mjs +310 -37
  18. package/browser/editor/page/board/client.mjs +57 -0
  19. package/browser/editor/page/board/hint.mjs +145 -0
  20. package/browser/editor/page/board/history.mjs +119 -0
  21. package/browser/editor/page/board/index.mjs +970 -0
  22. package/browser/editor/page/board/settings.mjs +209 -0
  23. package/browser/editor/page/caret.mjs +1 -1
  24. package/browser/editor/page/changes/bar.mjs +6 -14
  25. package/browser/editor/page/changes/index.mjs +2 -2
  26. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  27. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  28. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  29. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  30. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  31. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  32. package/browser/editor/page/clipboard/index.mjs +124 -13
  33. package/browser/editor/page/clipboard/own/blocks.mjs +11 -11
  34. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  35. package/browser/editor/page/clipboard/own/selection.mjs +1 -1
  36. package/browser/editor/page/columns/index.mjs +48 -46
  37. package/browser/editor/page/commit.mjs +80 -22
  38. package/browser/editor/page/conflicts.mjs +7 -7
  39. package/browser/editor/page/core.mjs +31 -15
  40. package/browser/editor/page/custom-page.mjs +106 -0
  41. package/browser/editor/page/editor.mjs +51 -14
  42. package/browser/editor/page/emoji-picker/styles.mjs +7 -12
  43. package/browser/editor/page/emoji-picker/upload.mjs +2 -2
  44. package/browser/editor/page/handle/index.mjs +5 -8
  45. package/browser/editor/page/history.mjs +24 -4
  46. package/browser/editor/page/links/index.mjs +5 -3
  47. package/browser/editor/page/links/pages.mjs +27 -9
  48. package/browser/editor/page/look.mjs +54 -3
  49. package/browser/editor/page/margin.mjs +18 -3
  50. package/browser/editor/page/media-menu/index.mjs +6 -7
  51. package/browser/editor/page/menu.mjs +18 -7
  52. package/browser/editor/page/notices.mjs +2 -2
  53. package/browser/editor/page/page-objects.mjs +13 -2
  54. package/browser/editor/page/panel.mjs +4 -9
  55. package/browser/editor/page/present-links.mjs +11 -2
  56. package/browser/editor/page/resize/index.mjs +42 -29
  57. package/browser/editor/page/served.mjs +14 -0
  58. package/browser/editor/page/session.mjs +48 -4
  59. package/browser/editor/page/slash-menu/index.mjs +3 -3
  60. package/browser/editor/page/table/cells.mjs +102 -46
  61. package/browser/editor/page/table/icons.mjs +2 -0
  62. package/browser/editor/page/table/index.mjs +29 -10
  63. package/browser/editor/page/table/pictures.mjs +299 -0
  64. package/browser/editor/page/table/views.mjs +150 -0
  65. package/browser/editor/page/text-select/index.mjs +26 -12
  66. package/browser/editor/page/todo-writes.mjs +75 -0
  67. package/browser/editor/page/typing/index.mjs +53 -3
  68. package/browser/editor/page/ui.mjs +13 -0
  69. package/browser/shared/block-packs.mjs +404 -0
  70. package/browser/shared/board-places.mjs +224 -0
  71. package/browser/shared/card-size.mjs +59 -0
  72. package/browser/shared/colors.mjs +3 -2
  73. package/browser/shared/component-catalog.mjs +323 -46
  74. package/browser/shared/component-source.mjs +37 -0
  75. package/browser/shared/components/index.mjs +12 -3
  76. package/browser/shared/components/installed.mjs +35 -3
  77. package/browser/shared/components/page.mjs +18 -8
  78. package/browser/shared/components/state.mjs +5 -1
  79. package/browser/shared/document-look.mjs +10 -5
  80. package/browser/shared/drafts.mjs +432 -0
  81. package/browser/shared/extension-sidebar.mjs +126 -0
  82. package/browser/shared/extensions.mjs +79 -0
  83. package/browser/shared/file-table.mjs +101 -0
  84. package/browser/shared/i18n/en.mjs +307 -27
  85. package/browser/shared/json-layout.mjs +47 -0
  86. package/browser/shared/net-cache.mjs +133 -0
  87. package/browser/shared/net-keys.mjs +32 -0
  88. package/browser/shared/net-policy.mjs +177 -0
  89. package/browser/shared/outline-shape.mjs +128 -0
  90. package/browser/shared/outline.mjs +331 -67
  91. package/browser/shared/pack-manifest.mjs +77 -0
  92. package/browser/shared/page-address.mjs +6 -3
  93. package/browser/shared/page-cards.mjs +308 -0
  94. package/browser/shared/page-links.mjs +69 -6
  95. package/browser/shared/pages.mjs +388 -0
  96. package/browser/shared/paths.mjs +6 -0
  97. package/browser/shared/preview-server.mjs +3 -7
  98. package/browser/shared/session.mjs +8 -1
  99. package/browser/shared/shipped-blocks.mjs +299 -0
  100. package/browser/shared/table-chart.mjs +33 -1
  101. package/browser/shared/todos.mjs +83 -0
  102. package/browser/shared/user-directory.mjs +24 -0
  103. package/browser/shared/versions.mjs +14 -0
  104. package/browser/shared/web-links.mjs +134 -0
  105. package/browser/shared/workspace-columns.mjs +120 -0
  106. package/browser/shared/workspace-name.mjs +154 -0
  107. package/browser/shared/workspace-query.mjs +125 -0
  108. package/browser/shared/workspace-registry.mjs +272 -0
  109. package/browser/shared/workspace.mjs +171 -20
  110. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  111. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  112. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  113. package/dist/chunk-3KJHLHUO.mjs +2 -0
  114. package/dist/chunk-3NJACR4W.mjs +2 -0
  115. package/dist/chunk-4AV456RQ.mjs +2 -0
  116. package/dist/chunk-4OZWXZE5.mjs +5 -0
  117. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  118. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  119. package/dist/chunk-6A53MZYZ.mjs +2 -0
  120. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  121. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  122. package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
  123. package/dist/{chunk-Y6RLRPG3.mjs → chunk-6VTU6KEO.mjs} +1 -1
  124. package/dist/chunk-B4P2CM2J.mjs +3 -0
  125. package/dist/{chunk-FDLXYYG4.mjs → chunk-BPL64HGA.mjs} +1 -1
  126. package/dist/chunk-BVEFGE4B.mjs +2 -0
  127. package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
  128. package/dist/chunk-CRJEG7MT.mjs +2 -0
  129. package/dist/{chunk-LCTHTZIL.mjs → chunk-D4CKRD3V.mjs} +91 -91
  130. package/dist/chunk-D4EURAX4.mjs +4 -0
  131. package/dist/chunk-DQCE7TJY.mjs +5 -0
  132. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  133. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  134. package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
  135. package/dist/chunk-EURLTOTZ.mjs +4 -0
  136. package/dist/chunk-FJYN7RTH.mjs +4 -0
  137. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  138. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  139. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  140. package/dist/chunk-GY3FAUVM.mjs +2 -0
  141. package/dist/chunk-HLYV7RIH.mjs +2 -0
  142. package/dist/chunk-HMYTM3GT.mjs +15 -0
  143. package/dist/chunk-IA4PSIMD.mjs +2 -0
  144. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  145. package/dist/chunk-KTKHRC6H.mjs +2 -0
  146. package/dist/chunk-L7UP52JO.mjs +2 -0
  147. package/dist/{chunk-SYGN7GLU.mjs → chunk-MHW7ZKOU.mjs} +1 -1
  148. package/dist/chunk-N76HWBA3.mjs +10 -0
  149. package/dist/chunk-N7AZZLGT.mjs +548 -0
  150. package/dist/chunk-NAMN3RAF.mjs +2 -0
  151. package/dist/{chunk-5ZKJ3H4P.mjs → chunk-NFMCP2HI.mjs} +3 -3
  152. package/dist/chunk-NG375ILQ.mjs +2 -0
  153. package/dist/chunk-NVRNLK25.mjs +6 -0
  154. package/dist/chunk-NWFBE2YA.mjs +2 -0
  155. package/dist/chunk-NWR2LT5B.mjs +2 -0
  156. package/dist/chunk-OJKMINIU.mjs +20 -0
  157. package/dist/{chunk-4FS7Z7WR.mjs → chunk-OUUP3WKT.mjs} +1 -1
  158. package/dist/{chunk-BGGDRNYQ.mjs → chunk-OW5NWPOJ.mjs} +1 -1
  159. package/dist/chunk-PAREWQGB.mjs +4 -0
  160. package/dist/chunk-POQSE4C2.mjs +2 -0
  161. package/dist/chunk-PUOQ65NC.mjs +2 -0
  162. package/dist/{chunk-XNROSH2T.mjs → chunk-PVER7LSC.mjs} +2 -2
  163. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  164. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  165. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  166. package/dist/chunk-QKXMPUXC.mjs +2 -0
  167. package/dist/chunk-QOUEFUCH.mjs +16 -0
  168. package/dist/chunk-RFDZXFJF.mjs +13 -0
  169. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  170. package/dist/chunk-RMECZA2K.mjs +2 -0
  171. package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  172. package/dist/chunk-SX4LJYT6.mjs +8 -0
  173. package/dist/chunk-SXHBYZST.mjs +16 -0
  174. package/dist/chunk-TANZRINB.mjs +2 -0
  175. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  176. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  177. package/dist/chunk-UFNYFFKO.mjs +20 -0
  178. package/dist/chunk-UGWYKHBL.mjs +170 -0
  179. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  180. package/dist/chunk-UNOYVIYR.mjs +2 -0
  181. package/dist/{chunk-E7LWZRXY.mjs → chunk-V6RIYQZZ.mjs} +1 -1
  182. package/dist/chunk-VJPFVNNW.mjs +15 -0
  183. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  184. package/dist/chunk-VTKNEYT3.mjs +6 -0
  185. package/dist/chunk-VXACTLO3.mjs +3 -0
  186. package/dist/chunk-YI43RG4P.mjs +2 -0
  187. package/dist/chunk-YQGT4AB2.mjs +6 -0
  188. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  189. package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  190. package/dist/chunk-ZTHICSSF.mjs +2 -0
  191. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  192. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  193. package/dist/cli.mjs +1 -1
  194. package/dist/server.mjs +1 -1
  195. package/dist/{chunk-U6KNPJPR.mjs → update-fetch.mjs} +1 -1
  196. package/dist/worker.mjs +1 -1
  197. package/kit/board.css +95 -0
  198. package/kit/board.js +250 -0
  199. package/kit/charts.js +5 -71
  200. package/kit/code.js +14 -14
  201. package/kit/report.css +255 -17
  202. package/kit/runtime.js +1600 -125
  203. package/kit/state.js +10 -1
  204. package/kit/tables.js +128 -43
  205. package/kit/ui.js +77 -35
  206. package/package.json +2 -1
  207. package/packs/gallery/card/component.json +14 -0
  208. package/packs/gallery/card/example.html +10 -0
  209. package/packs/gallery/card/preview.svg +1 -0
  210. package/packs/gallery/card/style.css +6 -0
  211. package/packs/gallery/card/template.html +6 -0
  212. package/packs/gallery/card/writing.md +14 -0
  213. package/packs/gallery/chart-card/component.json +15 -0
  214. package/packs/gallery/chart-card/example.html +14 -0
  215. package/packs/gallery/chart-card/kit.js +130 -0
  216. package/packs/gallery/chart-card/preview.svg +1 -0
  217. package/packs/gallery/chart-card/style.css +42 -0
  218. package/packs/gallery/chart-card/template.html +13 -0
  219. package/packs/gallery/chart-card/writing.md +20 -0
  220. package/packs/gallery/chart-view/component.json +20 -0
  221. package/packs/gallery/chart-view/example.html +13 -0
  222. package/packs/gallery/chart-view/kit.js +355 -0
  223. package/packs/gallery/chart-view/preview.svg +1 -0
  224. package/packs/gallery/chart-view/style.css +94 -0
  225. package/packs/gallery/chart-view/template.html +3 -0
  226. package/packs/gallery/chart-view/writing.md +22 -0
  227. package/packs/gallery/countdown/component.json +18 -0
  228. package/packs/gallery/countdown/example.html +3 -0
  229. package/packs/gallery/countdown/kit.js +156 -0
  230. package/packs/gallery/countdown/preview.svg +1 -0
  231. package/packs/gallery/countdown/style.css +130 -0
  232. package/packs/gallery/countdown/template.html +3 -0
  233. package/packs/gallery/countdown/writing.md +22 -0
  234. package/packs/gallery/habit/component.json +14 -0
  235. package/packs/gallery/habit/example.html +3 -0
  236. package/packs/gallery/habit/kit.js +295 -0
  237. package/packs/gallery/habit/preview.svg +1 -0
  238. package/packs/gallery/habit/style.css +246 -0
  239. package/packs/gallery/habit/template.html +3 -0
  240. package/packs/gallery/habit/writing.md +21 -0
  241. package/packs/gallery/heatmap/component.json +18 -0
  242. package/packs/gallery/heatmap/example.html +3 -0
  243. package/packs/gallery/heatmap/kit.js +351 -0
  244. package/packs/gallery/heatmap/preview.svg +1 -0
  245. package/packs/gallery/heatmap/style.css +201 -0
  246. package/packs/gallery/heatmap/template.html +3 -0
  247. package/packs/gallery/heatmap/writing.md +21 -0
  248. package/packs/gallery/kanban/component.json +18 -0
  249. package/packs/gallery/kanban/example.html +12 -0
  250. package/packs/gallery/kanban/kit.js +540 -0
  251. package/packs/gallery/kanban/preview.svg +1 -0
  252. package/packs/gallery/kanban/style.css +212 -0
  253. package/packs/gallery/kanban/template.html +3 -0
  254. package/packs/gallery/kanban/writing.md +20 -0
  255. package/packs/gallery/metric/component.json +22 -0
  256. package/packs/gallery/metric/example.html +15 -0
  257. package/packs/gallery/metric/kit.js +248 -0
  258. package/packs/gallery/metric/preview.svg +1 -0
  259. package/packs/gallery/metric/style.css +92 -0
  260. package/packs/gallery/metric/template.html +3 -0
  261. package/packs/gallery/metric/writing.md +23 -0
  262. package/packs/gallery/note/component.json +14 -0
  263. package/packs/gallery/note/example.html +3 -0
  264. package/packs/gallery/note/kit.js +102 -0
  265. package/packs/gallery/note/preview.svg +1 -0
  266. package/packs/gallery/note/style.css +76 -0
  267. package/packs/gallery/note/template.html +3 -0
  268. package/packs/gallery/note/writing.md +18 -0
  269. package/packs/gallery/pack.json +8 -0
  270. package/packs/gallery/recent/component.json +13 -0
  271. package/packs/gallery/recent/example.html +1 -0
  272. package/packs/gallery/recent/kit.js +238 -0
  273. package/packs/gallery/recent/preview.svg +1 -0
  274. package/packs/gallery/recent/style.css +158 -0
  275. package/packs/gallery/recent/template.html +1 -0
  276. package/packs/gallery/recent/writing.md +15 -0
  277. package/packs/gallery/static/component.json +13 -0
  278. package/packs/gallery/static/example.html +11 -0
  279. package/packs/gallery/static/preview.svg +1 -0
  280. package/packs/gallery/static/style.css +138 -0
  281. package/packs/gallery/static/template.html +1 -0
  282. package/packs/gallery/static/writing.md +18 -0
  283. package/packs/gallery/stats/component.json +13 -0
  284. package/packs/gallery/stats/example.html +1 -0
  285. package/packs/gallery/stats/kit.js +166 -0
  286. package/packs/gallery/stats/preview.svg +1 -0
  287. package/packs/gallery/stats/style.css +151 -0
  288. package/packs/gallery/stats/template.html +1 -0
  289. package/packs/gallery/stats/writing.md +11 -0
  290. package/packs/gallery/today/component.json +12 -0
  291. package/packs/gallery/today/example.html +1 -0
  292. package/packs/gallery/today/kit.js +185 -0
  293. package/packs/gallery/today/preview.svg +1 -0
  294. package/packs/gallery/today/style.css +120 -0
  295. package/packs/gallery/today/template.html +1 -0
  296. package/packs/gallery/today/writing.md +14 -0
  297. package/packs/gallery/todos/component.json +19 -0
  298. package/packs/gallery/todos/example.html +3 -0
  299. package/packs/gallery/todos/kit.js +342 -0
  300. package/packs/gallery/todos/preview.svg +1 -0
  301. package/packs/gallery/todos/style.css +267 -0
  302. package/packs/gallery/todos/template.html +3 -0
  303. package/packs/gallery/todos/writing.md +30 -0
  304. package/packs/gallery/weather/component.json +19 -0
  305. package/packs/gallery/weather/example.html +3 -0
  306. package/packs/gallery/weather/kit.js +312 -0
  307. package/packs/gallery/weather/preview.svg +1 -0
  308. package/packs/gallery/weather/style.css +258 -0
  309. package/packs/gallery/weather/template.html +3 -0
  310. package/packs/gallery/weather/writing.md +26 -0
  311. package/packs/gallery-soft/pack.json +8 -0
  312. package/release.json +12 -10
  313. package/sdk/doc-kit.d.ts +396 -5
  314. package/sdk/doc-state.d.ts +2 -0
  315. package/skill/SKILL.md +56 -23
  316. package/skill/references/block-script.md +140 -0
  317. package/skill/references/building-blocks.md +257 -0
  318. package/skill/references/extensions.md +59 -0
  319. package/skill/references/pages-and-data.md +221 -0
  320. package/skill/references/writing-html.md +6 -4
  321. package/skill/sdk/doc-kit.d.ts +396 -5
  322. package/skill/sdk/doc-state.d.ts +2 -0
  323. package/web/assets/index-BK2e_07w.js +13 -0
  324. package/web/assets/index-CWFI83mF.css +1 -0
  325. package/web/index.html +2 -2
  326. package/dist/chunk-3HIRPVLU.mjs +0 -2
  327. package/dist/chunk-5MHMDLZD.mjs +0 -4
  328. package/dist/chunk-64K7IPLO.mjs +0 -255
  329. package/dist/chunk-6QXGZM36.mjs +0 -8
  330. package/dist/chunk-6RGJOMJP.mjs +0 -16
  331. package/dist/chunk-AJ73XJF3.mjs +0 -2
  332. package/dist/chunk-AUZGSZJH.mjs +0 -13
  333. package/dist/chunk-BFE45AKL.mjs +0 -2
  334. package/dist/chunk-BXO2ZY5P.mjs +0 -2
  335. package/dist/chunk-CEH4MRCC.mjs +0 -2
  336. package/dist/chunk-CY2FNNAB.mjs +0 -16
  337. package/dist/chunk-DFUDV4LX.mjs +0 -2
  338. package/dist/chunk-DKXG4XDG.mjs +0 -2
  339. package/dist/chunk-EZQ34M3F.mjs +0 -124
  340. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  341. package/dist/chunk-FUKQNOQS.mjs +0 -9
  342. package/dist/chunk-JPOSLGOC.mjs +0 -6
  343. package/dist/chunk-L3QKH4OK.mjs +0 -26
  344. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  345. package/dist/chunk-LEF6DRCK.mjs +0 -59
  346. package/dist/chunk-NO5NJCHO.mjs +0 -2
  347. package/dist/chunk-NQPVAU63.mjs +0 -2
  348. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  349. package/dist/chunk-O35FIXRB.mjs +0 -2
  350. package/dist/chunk-P2CAZPDP.mjs +0 -3
  351. package/dist/chunk-PL6GHLGD.mjs +0 -15
  352. package/dist/chunk-QJIR2BLP.mjs +0 -6
  353. package/dist/chunk-QUPLVPB2.mjs +0 -7
  354. package/dist/chunk-R7BNENLY.mjs +0 -4
  355. package/dist/chunk-S6O4UHLC.mjs +0 -2
  356. package/dist/chunk-SGJWQKEI.mjs +0 -2
  357. package/dist/chunk-TX6JH7TR.mjs +0 -2
  358. package/dist/chunk-TYK6VNAB.mjs +0 -10
  359. package/dist/chunk-UALRNVSE.mjs +0 -4
  360. package/dist/chunk-V3HXVQEM.mjs +0 -2
  361. package/dist/chunk-V5VXUJKG.mjs +0 -2
  362. package/dist/chunk-VBXMADMP.mjs +0 -2
  363. package/dist/chunk-VNV55RUA.mjs +0 -2
  364. package/dist/chunk-VPD2PS35.mjs +0 -2
  365. package/dist/chunk-W4VKN3GF.mjs +0 -7
  366. package/dist/chunk-W5BXXPDJ.mjs +0 -3
  367. package/dist/chunk-XUNHRCOG.mjs +0 -6
  368. package/skill/references/building-components.md +0 -367
  369. package/web/assets/index-Bh74DfsV.css +0 -1
  370. package/web/assets/index-UW0RyFFt.js +0 -13
@@ -18,8 +18,9 @@ import { SOLO, connectWorkbench } from "./session.mjs";
18
18
  import { lookOfElement } from "../../shared/document-look.mjs";
19
19
  import { belowTargets, convertBlock, convertGroup, groupParents, groupTargets, noTextToCarry, targetsOf, touchedBy, turnInto, turnParents, turnUnit, typeOf } from "./convert.mjs";
20
20
  import { joinCode, rangeAt, rangeOffsets } from "./marks.mjs";
21
- import { addStyle, button, copyAddress, element, layer } from "./ui.mjs";
21
+ import { addStyle, copyAddress, element, layer, textButton } from "./ui.mjs";
22
22
  import { createClipboard } from "./clipboard/index.mjs";
23
+ import { serveFiles } from "./served.mjs";
23
24
  import { createFormatting } from "./format-bar/index.mjs";
24
25
  import { connectHints, hideHint, readOnlyReason, scheduleHint, trackPointer } from "./hints/index.mjs";
25
26
  import { createInsert } from "./insert/index.mjs";
@@ -58,6 +59,7 @@ import { goBackTo, placeIn, unmoved } from "../../shared/reading-place.mjs";
58
59
  import { heldList, holdPages } from "./links/pages.mjs";
59
60
  import { hideBlocks } from "./offered.mjs";
60
61
  import { local, storageOf } from "./storage.mjs";
62
+ import { createTodoWrites } from "./todo-writes.mjs";
61
63
  import { language, t } from "../../shared/i18n/index.mjs";
62
64
 
63
65
  // In-place editing for the live preview (`npm run dev:local` only; never exported).
@@ -268,7 +270,12 @@ const { newParagraph } = insert;
268
270
 
269
271
  // The icon picker's images go into the report's folder (editor/media-store.mjs), where the server serves them.
270
272
  const assetUrl = (file) => mediaUrl("asset", MAP.slug, file);
273
+ serveFiles(assetUrl); // a table cell's pictures (单元格里的图片) show where the server serves them
271
274
  connectPicker({ slug: MAP.slug, token: MAP.token, notify: say });
275
+ // A block of the page checking a to-do of a document (context.workspace.setTodo: ./todo-writes.mjs): written
276
+ // by the workspace's service, this one's too as any write it did not make, which the page merges; no step of
277
+ // its undo (only a custom page keeps one: board/).
278
+ createTodoWrites({ token: MAP.token }).listen();
272
279
  // Whether a block tool may act: the page edits, no reload is on its way and no field of a component is being edited.
273
280
  const active = () => editing && !autosave?.state.reloading && !fieldOpen();
274
281
  // A picture or file sent to the report's folder (kind: "image", "file" or "embed"): the server's answer.
@@ -372,6 +379,7 @@ const clipboard = createClipboard({
372
379
  pageLink: pageLinkOf,
373
380
  titleBold: () => formatting?.armed ?? false, // a node's empty title: what is pasted there bold, as typed
374
381
  showCaret,
382
+ pictures: () => tables?.pictures ?? null, // a table cell's (table/pictures.mjs)
375
383
  });
376
384
 
377
385
  // ---------- editing ----------
@@ -440,17 +448,19 @@ function enter() {
440
448
  conflicts.add(entries.map((entry) => ({ ...entry, key: `r:${entry.key}`, revert: entry.key, kind: entry.written ? "reverted-source" : entry.unit === undefined ? "reverted" : "reverted-unit" })));
441
449
  say(t("AI 的写入把你之前保存的 {0} 处修改改回去了:在左下方选择保留哪一边。", entries.length), "error");
442
450
  },
451
+ listed: () => showLost(), // a block the page before this one made, which someone else's write took out
443
452
  });
444
453
  conflicts = createConflicts({ act: decide, nodeOf: conflictNode, restorable, copyBlock: (node) => clipboard.copyBlocks([node]), say });
445
454
  editing = true;
446
455
  document.documentElement.setAttribute("data-ge-editing", "");
456
+ typing.untitled(); // an empty title says 未命名文档 from the start
447
457
  blocks?.folds.start(); // what was folded, folded again in this task: no frame shows it open (fold/)
448
458
  refresh();
449
459
  for (const page of pageObjects()) page.entered?.();
450
460
  }
451
461
 
452
462
  // What the page says when what a component wrote went by a manifest that changed: given up, the page shown again.
453
- const outdatedNotice = () => t("组件包刚刚变了,这次改动没有保存,页面已按新的组件重新显示。");
463
+ const outdatedNotice = () => t("自定义块刚刚变了,这次改动没有保存,页面已按新的块重新显示。");
454
464
 
455
465
  // What 仍然调整 wrote over someone else's change of what a component writes, by page unit: the step that
456
466
  // gives theirs back (installed/ overwrite), and when its write went in, all of it (commit.mjs `forced`).
@@ -465,9 +475,10 @@ const keepsOverwrite = (asked) => !asked && [...overwrites.values()].some(({ lan
465
475
 
466
476
  // A commit was taken in (commit.mjs): a block merged with someone else's words shows the merge;
467
477
  // what the file refused is flagged and held back, the rest goes on.
468
- function committed({ conflicts: held, merged, outdated, forced = [] }, receipt) {
478
+ function committed({ conflicts: held, merged, outdated, forced = [], rekeyed = [] }, receipt) {
469
479
  agentChanges?.committed(receipt);
470
480
  for (const host of merged) history.merged(host);
481
+ history.rekeyed(rekeyed); // the keys of tables the file gave otherwise, in the steps that recorded them
471
482
  for (const unit of forced) {
472
483
  const written = overwrites.get(unit);
473
484
  if (!written || written.landed > 0) continue;
@@ -485,7 +496,7 @@ function committed({ conflicts: held, merged, outdated, forced = [] }, receipt)
485
496
  conflicts.add(held);
486
497
  const conflicted = held.filter((entry) => !unfit(entry) && !packageChanged(entry)).length;
487
498
  const packaged = held.filter(packageChanged).length;
488
- say(conflicted > 0 ? t("{0} 处修改和文件的最新版本冲突,没有保存:在左下方选择怎么处理。", conflicted) : packaged > 0 ? t("组件包刚刚变了,{0} 处修改没有保存:在左下方选择怎么处理。", packaged) : t("{0} 处内容文档存不下,没有保存:在左下方选择怎么处理。", held.length), "error");
499
+ say(conflicted > 0 ? t("{0} 处修改和文件的最新版本冲突,没有保存:在左下方选择怎么处理。", conflicted) : packaged > 0 ? t("自定义块刚刚变了,{0} 处修改没有保存:在左下方选择怎么处理。", packaged) : t("{0} 处内容文档存不下,没有保存:在左下方选择怎么处理。", held.length), "error");
489
500
  }
490
501
  else if (outdated || merged.length > 0 || receipt.texts?.some((entry) => entry.status === "merged")) {
491
502
  const count = receipt.texts.filter((entry) => entry.status === "merged").length;
@@ -612,6 +623,7 @@ function markChanged(now = false) {
612
623
  conflicts?.reflag();
613
624
  }
614
625
  if (messageKind() === "error") settle(); // the reader is dealing with it
626
+ typing.untitled(); // a title emptied (or filled) by any change says 未命名文档 (or nothing) at once
615
627
  lastChange = Date.now();
616
628
  discrete ||= now;
617
629
  const counting = checkPending;
@@ -691,7 +703,10 @@ function afterConvert(focus, type, said) {
691
703
  * heading) and the new block turns into the type; below any other block, an empty paragraph of its
692
704
  * box does (the first box out that takes one). One undo step.
693
705
  */
694
- function insertBelow(target, type, typed = null) {
706
+ // `also` (a table's 视图 ›: page/table/views.mjs): `attributes` ([element, names]) the step records too, and
707
+ // `done(made)` run in it on the new block (the element where the empty paragraph was), so that what binds
708
+ // the block to the one above it is the same step.
709
+ function insertBelow(target, type, typed = null, also = null) {
695
710
  const splits = isHost(target) && can(target, "split");
696
711
  let after = target;
697
712
  while (!splits && after && !accepts(after.parentElement?.getAttribute("data-ge-box-kind"), "paragraph")) after = after.parentElement?.closest(OUTER);
@@ -709,10 +724,12 @@ function insertBelow(target, type, typed = null) {
709
724
  after.after(fresh);
710
725
  }
711
726
  if (!fresh) return null;
727
+ const prior = fresh.previousSibling; // what the new block stands right after, made where the paragraph was
712
728
  const turned = type === "paragraph" && fresh.localName === "p" ? { focus: null, created: [] } : convertBlock(fresh, type, { fresh: true });
729
+ also?.done?.(fresh.isConnected ? fresh : prior?.nextElementSibling ?? null);
713
730
  const focus = turned.focus ?? fresh;
714
731
  return { touched: splits ? [target, focus] : [focus], focus, created: turned.created };
715
- });
732
+ }, { attributes: also?.attributes ?? [] });
716
733
  if (result) afterConvert(result.focus, type, t("已插入{0}", menuRow(type).label));
717
734
  }
718
735
 
@@ -1160,7 +1177,8 @@ async function newSubpage(title) {
1160
1177
  }
1161
1178
 
1162
1179
  // The report's title as the reader sees it now (while editing, the title block's text).
1163
- const currentTitle = () => (document.querySelector('[data-ge-kind="title"]') ?? document.querySelector("h1.doc-title, .doc-header h1"))?.textContent.replace(/\s+/g, " ").trim() ?? "";
1180
+ const titleNode = () => document.querySelector('[data-ge-kind="title"]') ?? document.querySelector("h1.doc-title, .doc-header h1");
1181
+ const currentTitle = () => titleNode()?.textContent.replace(/\s+/g, " ").trim() ?? "";
1164
1182
 
1165
1183
  // This page, as the list of pages takes it (links/pages.mjs).
1166
1184
  const thisPage = () => ({ slug: MAP.slug, title: currentTitle() });
@@ -1454,8 +1472,20 @@ function handOff() {
1454
1472
  function connect() {
1455
1473
  // The look the page shows (字体与布局: its <html>'s, src/shared/document-look.mjs), which the workbench's
1456
1474
  // 更多 marks as chosen; told again when it changes (the kit's ui.js, doc-look-change).
1457
- workbench = connectWorkbench({ slug: MAP.slug, snapshot: () => ({ title: currentTitle(), icon: pageIcon(), look: lookOfElement(document.documentElement), state: sessionState(), annotations: annotate?.count() }), commands: COMMANDS, modals: modalOps() });
1475
+ // `title`: null for a page with no title at all (the workbench names it by its doc.json); "" an untitled one (未命名文档).
1476
+ workbench = connectWorkbench({ slug: MAP.slug, snapshot: () => ({ title: titleNode() ? currentTitle() : null, icon: pageIcon(), look: lookOfElement(document.documentElement), state: sessionState(), annotations: annotate?.count() }), commands: COMMANDS, modals: modalOps() });
1458
1477
  addEventListener("doc-look-change", () => workbench?.update());
1478
+ // A block's context.workspace.open (the kit's runtime/workspace.js): a document, or a block of one, goes
1479
+ // where a link to it goes (goTo: in the workbench, its address; on its own, the page once saved); a custom
1480
+ // page, the workbench's #@<slug>, or /pages/<slug> once saved.
1481
+ addEventListener("doc-open-request", (event) => {
1482
+ const { slug = null, block = null, page = null } = event.detail ?? {};
1483
+ event.preventDefault();
1484
+ if (page) {
1485
+ if (window.parent !== window && !SOLO) workbench?.open({ hash: `@${page}` });
1486
+ else COMMANDS["prepare-close"]().then(() => location.assign(`/pages/${page}`));
1487
+ } else goTo({ slug, block }, `/generated/${slug}.html${block ? `#${encodeURIComponent(block)}` : ""}`);
1488
+ });
1459
1489
  relayTo(workbench ? workbench.notice : null, workbench ? workbench.noticeRoom : null); // the workbench shows the page's notices, above the page's bottom panels
1460
1490
  handOff();
1461
1491
  // A page on its own in a frame (?solo: the 写作偏好 dialog's, which no session asks to save) is saved
@@ -1514,6 +1544,10 @@ async function start() {
1514
1544
  TAB = newTab();
1515
1545
  session.set(keys.tab, TAB);
1516
1546
  }
1547
+ // What earlier pages of the document committed, made and wrote (this tab's, and those of tabs no longer
1548
+ // open), asked of at once where this page opened on another version of the file than they last knew
1549
+ // (its cards and list said through the workbench).
1550
+ autosave?.opened(open);
1517
1551
  const prefix = pendingKey(MAP.slug, "");
1518
1552
  const left = local.keys().filter((key) => key.startsWith(prefix) && key !== pendingKey(MAP.slug, TAB) && !open.has(key.slice(prefix.length)));
1519
1553
  if (left.length === 0) return;
@@ -1573,7 +1607,7 @@ function showLost() {
1573
1607
  if (block) {
1574
1608
  panelItem(
1575
1609
  element("p", { text: t("文件里的这一块已被修改:复制你的版本,再粘贴到页面上。") }),
1576
- element("div", { class: "ge-actions" }, button(t("复制这一块"), () => { if (!clipboard.copyBlocks([block])) say(t("没能完成,请再试一次。"), "error"); })),
1610
+ element("div", { class: "ge-actions" }, textButton(t("复制这一块"), () => { if (!clipboard.copyBlocks([block])) say(t("没能完成,请再试一次。"), "error"); })),
1577
1611
  );
1578
1612
  continue;
1579
1613
  }
@@ -1581,10 +1615,10 @@ function showLost() {
1581
1615
  panelItem(
1582
1616
  element("p", { text: LOST[item.why] ?? LOST.refused }),
1583
1617
  element("div", { class: "ge-version", text }),
1584
- element("div", { class: "ge-actions" }, button(t("复制文字"), () => navigator.clipboard?.writeText(text).then(() => say(t("已复制。"))))),
1618
+ element("div", { class: "ge-actions" }, textButton(t("复制文字"), () => navigator.clipboard?.writeText(text).then(() => say(t("已复制。"))))),
1585
1619
  );
1586
1620
  }
1587
- panelItem(element("div", { class: "ge-actions" }, button(t("都处理好了,不再保留"), () => {
1621
+ panelItem(element("div", { class: "ge-actions" }, textButton(t("都处理好了,不再保留"), () => {
1588
1622
  local.remove(lostKey(MAP.slug));
1589
1623
  closePanel();
1590
1624
  }, { primary: true })));
@@ -1617,10 +1651,13 @@ function boot() {
1617
1651
  copyLink: SOLO ? null : copyBlockLink, // what ?solo shows (写作偏好) only its dialog opens: no address leads there
1618
1652
  linkAhead: () => holdPages(thisPage),
1619
1653
  });
1620
- resize = createResize({ units, active, look: lookStep, say, owns: (node) => tables?.owns(node) ?? false });
1621
- gaps = createColumnGaps({ active, struct, look: lookStep, say });
1654
+ resize = createResize({ units, active, look: lookStep, say, settle, owns: (node) => tables?.owns(node) ?? false });
1655
+ gaps = createColumnGaps({ active, struct, look: lookStep, say, settle });
1622
1656
  for (const page of pageObjects()) page.install?.(); // the round "+" at a flowchart node's right, renaming Sheet tabs
1623
- tables = createTableEditing({ active, struct, look: lookStep, selectBlock: (unit) => blocks.select(unit), resizeTable: (event, unit, axis) => resize.startTable(event, unit, axis), say, ownCells: (data) => clipboard.ownCells(data), typedKey });
1657
+ tables = createTableEditing({
1658
+ active, struct, look: lookStep, selectBlock: (unit) => blocks.select(unit), resizeTable: (event, unit, axis) => resize.startTable(event, unit, axis), say, cellsOf: (data) => clipboard.cellsOf(data), upload: uploadTo, typedKey,
1659
+ recordStep, insertBelow: (unit, type, also) => insertBelow(unit, type, null, also),
1660
+ });
1624
1661
  selectAll = createSelectAll({ blocks, say, cells: { table: () => tables.selectedTable, clear: () => tables.clear() } });
1625
1662
  createFrame(() => editing);
1626
1663
  createFlags(() => editing);
@@ -64,14 +64,14 @@ export const PICKER_CSS = `
64
64
  .nmm-cell.focus-visible { background: var(--ge-press); }
65
65
  .nmm-cell img { display: block; }
66
66
  .nmm-cell img.notion-custom-emoji { object-fit: contain; color: transparent; }
67
- /* 添加表情符号: the custom group's first cell, a round plus (26, 3 around, a 1px --ge-edge inside, the plus
68
- --ge-ico3); with no result, a button (28 high, a 1px --ge-edge inside, 13px / 500) */
69
- .nmm-cell.nmm-add-cell { width: 26px; height: 26px; margin: 3px; box-sizing: border-box; border-radius: 50%; box-shadow: inset 0 0 0 1px var(--ge-edge); --nmm-icon: var(--ge-ico3); }
67
+ /* 添加表情符号: the custom group's first cell, a round plus (26, 3 around, the plus --ge-ico3, no line round
68
+ it: gray under the pointer as a cell); with no result, a button (28 high, 13px / 500, no line either) */
69
+ .nmm-cell.nmm-add-cell { width: 26px; height: 26px; margin: 3px; box-sizing: border-box; border-radius: 50%; --nmm-icon: var(--ge-ico3); }
70
70
  .nmm-cell.nmm-add-cell.focused { --nmm-icon: var(--ge-ico); }
71
71
  .nmm-empty { padding: 8px 12px; }
72
72
  .nmm-empty-caption { display: flex; align-items: center; height: 20px; padding: 0 4px; font-size: 12px; font-weight: 500; color: var(--ge-tx3); }
73
73
  .nmm-empty.with-add { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
74
- .nmm-cell.nmm-add-button { justify-content: flex-start; gap: 6px; width: max-content; height: 28px; padding: 0 10px 0 8px; box-sizing: border-box; border-radius: 6px; box-shadow: inset 0 0 0 1px var(--ge-edge); font-size: 13px; font-weight: 500; line-height: 1; color: var(--ge-tx); white-space: nowrap; --nmm-icon: var(--ge-ico2); }
74
+ .nmm-cell.nmm-add-button { justify-content: flex-start; gap: 6px; width: max-content; height: 28px; padding: 0 10px 0 8px; box-sizing: border-box; border-radius: 6px; font-size: 13px; font-weight: 500; line-height: 1; color: var(--ge-tx); white-space: nowrap; --nmm-icon: var(--ge-ico2); }
75
75
 
76
76
  /* The category bar: a --ge-line above it, 6 12 8; buttons 28 x 28, icons 18 in --ge-ico3; the group scrolled
77
77
  to on --ge-hover, its icon --ge-ico; a group with no emoji faded to .35 and not to be clicked (自定义 and
@@ -106,8 +106,8 @@ export const PICKER_CSS = `
106
106
 
107
107
  /* The 上传 tab and the 添加表情符号 popup (upload.mjs): the zone an image goes in on --ge-field, a 1px
108
108
  --ge-edge inside it, rounded 8, deeper while a file is dragged over it (--ge-hover, --ge-ico3), never blue;
109
- 保存 a text button (--ge-tx, 500, --ge-hover under the pointer, --ge-tx3 while it cannot be clicked), 取消 /
110
- 返回 --ge-tx2; the checkbox checked --ge-tx2 with a white tick; an error --ge-red */
109
+ 保存 the pale blue primary and 取消 / 返回 the plain text button (look.mjs BUTTONS: .ge-btn, dimmed while it cannot be
110
+ clicked); the checkbox checked --ge-tx2 with a white tick; an error --ge-red */
111
111
  .nmm-upload { display: flex; flex-direction: column; padding: 0 16px; font-size: 14px; color: var(--ge-tx); }
112
112
  .nmm-upload-title { margin-top: 14px; font-size: 14px; font-weight: 600; }
113
113
  .nmm-upload-description { margin-top: 2px; font-size: 12px; color: var(--ge-tx2); }
@@ -142,12 +142,7 @@ export const PICKER_CSS = `
142
142
  .nmm-upload-name-error { margin-top: 10px; font-size: 12px; color: var(--ge-red); }
143
143
  .nmm-upload-name-error[hidden], .nmm-upload [hidden] { display: none; }
144
144
  .nmm-upload-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 16px 0 12px; }
145
- .nmm-upload-secondary, .nmm-upload-save { display: inline-flex; align-items: center; justify-content: center; height: 28px; padding: 0 10px; border-radius: 6px; font-size: 14px; line-height: 1.2; white-space: nowrap; cursor: pointer; user-select: none; }
146
- .nmm-upload-secondary { color: var(--ge-tx2); }
147
- .nmm-upload-secondary:hover { background: var(--ge-hover); color: var(--ge-tx); }
148
- .nmm-upload-save { color: var(--ge-tx); font-weight: 500; }
149
- .nmm-upload-save:not(.disabled):hover { background: var(--ge-hover); }
150
- .nmm-upload-save.disabled { color: var(--ge-tx3); cursor: default; }
145
+ .nmm-upload-secondary, .nmm-upload-save { font-size: 14px; }
151
146
  .nmm-custom-emoji-popup { min-width: 280px; border-radius: 10px; background: var(--ge-bg); box-shadow: var(--ge-shadow-pop); }
152
147
 
153
148
  /* A button reached by Tab: a gray fill, not the browser's dark or blue ring; the checkbox a gray ring */
@@ -76,8 +76,8 @@ export function createUploadForm({ showTitle = false, largeEmoji = false, allowU
76
76
  `<label class="nmm-upload-name" hidden><div class="nmm-upload-name-label">${T.nameLabel}</div>` +
77
77
  `<div class="nmm-upload-input"><input type="text" placeholder="${T.namePlaceholder}" autocomplete="off" spellcheck="false"></div>` +
78
78
  `<div class="nmm-upload-name-error" hidden></div></label>` +
79
- `<div class="nmm-upload-footer"><div role="button" tabindex="0" class="nmm-upload-secondary" data-act="secondary"></div>` +
80
- `<div role="button" tabindex="0" class="nmm-upload-save" data-act="save">${T.save}</div></div>` +
79
+ `<div class="nmm-upload-footer"><div role="button" tabindex="0" class="ge-btn nmm-upload-secondary" data-act="secondary"></div>` +
80
+ `<div role="button" tabindex="0" class="ge-btn nmm-upload-save" data-primary data-act="save">${T.save}</div></div>` +
81
81
  `<input type="file" accept="${ACCEPT}" hidden>`;
82
82
 
83
83
  const zone = el.querySelector('.nmm-upload-zone'), wrap = el.querySelector('.nmm-upload-zone-wrap');
@@ -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).
@@ -148,6 +148,23 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
148
148
  for (const stack of [undoStack, redoStack]) if (stack.includes(step)) stack.splice(stack.indexOf(step), 1);
149
149
  }
150
150
 
151
+ // An attribute the file gave another value than the page had (`changes`: [element, name, from, to],
152
+ // `to` null for none; a key of a table a commit gave a pair anew: page/commit.mjs apply), the page's
153
+ // element set so: the steps that recorded `from` of it record `to` instead, so that an undo or a redo
154
+ // puts back what the file has, never the value it changed (a table's 视图 › redone with the key its view
155
+ // no longer carries).
156
+ function rekeyed(changes) {
157
+ if (changes.length === 0) return;
158
+ for (const step of [...undoStack, ...redoStack]) {
159
+ for (const recorded of [step.attributesBefore, step.attributesAfter]) {
160
+ for (const [element, name, from, to] of changes) {
161
+ const values = recorded?.get(element);
162
+ if (values?.some(([each, value]) => each === name && value === from)) recorded.set(element, values.map(([each, value]) => [each, each === name && value === from ? to : value]));
163
+ }
164
+ }
165
+ }
166
+ }
167
+
151
168
  // What a step puts into a block (`from`: what the step left there, `to`: what it puts back): as
152
169
  // recorded, unless someone else's words were merged into the block since the step; then the step's
153
170
  // change three ways against what the block holds now (null: the two touch the same place).
@@ -174,8 +191,10 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
174
191
  // after undo and redo (a deleted Sheet tab switches the Sheet shown). A step that names no text block
175
192
  // it came from keeps where the caret stood, which undo puts back, as notion-lite's selectionBefore.
176
193
  // `attributes`: [element, [names]] whose attributes the step records too (a table's cells: their
177
- // spans and widths, which its operations change: components/table/page.mjs).
178
- function struct(parents, touched, mutate, { attributes = [] } = {}) {
194
+ // spans and widths, which its operations change: components/table/page.mjs). `caret`: where the caret
195
+ // stood in the first text block it touched is kept too, which undo puts back (notion-lite's Enter in the
196
+ // page's title, enter.js: "撤销后光标回到回车前的位置"; typing/ enterBody).
197
+ function struct(parents, touched, mutate, { attributes = [], caret = false } = {}) {
179
198
  closeTyping();
180
199
  // What the change may leave untidy is tidied after it by the components that know (rows of
181
200
  // columns with a column emptied, lists set side by side: tidy), so what they name is recorded
@@ -189,7 +208,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
189
208
  }
190
209
  // A text block among the parents (a list item whose lists change) keeps its text through undo too.
191
210
  touched = [...new Set([...touched, ...parents.filter(isHost)])];
192
- const place = touched[0] ? null : caretPlace(parents[0]);
211
+ const place = touched[0] && !caret ? null : caretPlace(parents[0]);
193
212
  const before = parents.map(contentOf);
194
213
  const htmlBefore = new Map(touched.map((node) => [node, ownHTML(node)]));
195
214
  const classesBefore = new Map(touched.map((node) => [node, new Set(node.classList)]));
@@ -219,7 +238,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
219
238
  .filter(({ added, removed }) => added.length > 0 || removed.length > 0),
220
239
  attributesBefore,
221
240
  attributesAfter: attributesOf(),
222
- focusBefore: touched[0] ?? place?.host,
241
+ focusBefore: (caret ? place?.host : null) ?? touched[0] ?? place?.host,
223
242
  caretBefore: place?.offsets ?? null,
224
243
  focusAfter: result.focus,
225
244
  select: result.select ?? null,
@@ -381,6 +400,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
381
400
  sourceStep,
382
401
  fold,
383
402
  drop,
403
+ rekeyed,
384
404
  closeTyping,
385
405
  struct,
386
406
  finish,
@@ -9,7 +9,7 @@ import { placeBox } from "../placement.mjs";
9
9
  import { tooltip } from "../tooltip.mjs";
10
10
  import { addStyle, copyAddress, element, fadeIn, mount, swallowNext } from "../ui.mjs";
11
11
  import { typedHref } from "./address.mjs";
12
- import { findPages, heldList, pageRows, placeIn, readPages, titleIn, trashed } from "./pages.mjs";
12
+ import { findPages, heldList, pageRows, placeIn, readPages, titleIn, trashed, webLinkIn } from "./pages.mjs";
13
13
  import { DIALOG_TRIGGER } from "../../components/dialog/index.mjs";
14
14
  import { DIALOG_ICON } from "../../components/icons.mjs";
15
15
  import { VIEWS, closeOffer, closeViews, hasViews, hrefOf, isPageChip, offerViews, openViews, pageChip, refetch, viewNodes, viewOf, viewsHold } from "./views.mjs";
@@ -668,12 +668,14 @@ async function listedPage(address) {
668
668
 
669
669
  /**
670
670
  * Whether a page alone in a tab may leave for a workbench's `hash`, which only the list reads: not for
671
- * one that names no document, said as the workbench says it, the page staying. With no answer it goes,
671
+ * one that names no document, said as the workbench says it, the page staying. A web link of the sidebar,
672
+ * or an entry of an extension's column with an address (#~<id>/<entry id>), is a place the workbench
673
+ * opens (in its content area), so it goes there. With no answer it goes,
672
674
  * for the workbench to say.
673
675
  */
674
676
  export async function leadsToPage(hash) {
675
677
  const list = await readPages(use.own());
676
- if (!list || placeIn(list, `#${hash}`)) return true;
678
+ if (!list || placeIn(list, `#${hash}`) || webLinkIn(list, hash)) return true;
677
679
  use.say(inTrash(list.trash, hash) ? t("这个页面已删除。") : t("没有找到这个页面。"));
678
680
  return false;
679
681
  }
@@ -1,15 +1,21 @@
1
1
  import { iconUrl } from "../../../shared/emoji/icon-value.mjs";
2
2
  import { escapeAttribute } from "../../../shared/escape.mjs";
3
3
  import { addressOn, decoded, hashAddress } from "../../../shared/page-address.mjs";
4
+ import { DOCS, isTop, topOf } from "../../../shared/outline-shape.mjs";
5
+ import { entryOfKey } from "../../../shared/extension-sidebar.mjs";
4
6
  import { inTrash, namedLinks, pageLink, pageLinks } from "../../../shared/page-links.mjs";
5
7
  import { PAGE_ICON } from "../../components/icons.mjs";
8
+ import { t } from "../../../shared/i18n/index.mjs";
6
9
 
7
10
  // The documents of the workspace, as a link's card, a page block's box and "@" look for them
8
11
  // (docs/editor.md, "Links"): one search for the three. The list is read once for each opening from
9
12
  // what the workbench reads itself (GET /__gallery/workspace/outline: no route of its own, nothing
10
- // pushed), in the sidebar's order, each document with the title of the one above it in the sidebar; 写作偏好
11
- // is no page to link to, and this page has the title it shows now, not the one last saved. A list that
12
- // cannot be read is no results, and the field goes on taking addresses.
13
+ // pushed), in the sidebar's order, column after column (栏: shared/outline-shape.mjs), each document with
14
+ // the title of the one above it in the sidebar; 写作偏好 is no page to link to, nor is a web link of the
15
+ // sidebar (kind "link": no file of its own to link to; the documents under one are pages all the same) or a
16
+ // draft of another workspace (kind "draft": its file is that workspace's, which no relative link reaches),
17
+ // and this page has the title it shows now, not the one last saved. A list that cannot be read is no
18
+ // results, and the field goes on taking addresses.
13
19
  //
14
20
  // What a copy writes is an address by a page's link, and a copy cannot wait for an answer: the
15
21
  // clipboard takes a write only in the click itself. So the page holds the list ready, the last one
@@ -31,8 +37,8 @@ let shown = () => null; // this page, as readPages takes it
31
37
 
32
38
  /**
33
39
  * The workspace's documents: { pages: [{ slug, title, icon, parent }] (`parent`: the title of the one
34
- * it is under, or null), links: their links (shared/page-links.mjs), trash: what the trash holds }, or
35
- * null when the list cannot be read. `own`: this page, { slug, title } as it shows it.
40
+ * it is under, or null), links: their links (shared/page-links.mjs), trash: what the trash holds but web links }, or
41
+ * null when the list cannot be read (`webs`: the links of the sidebar's web links, which are no pages). `own`: this page, { slug, title } as it shows it.
36
42
  */
37
43
  export async function readPages(own = null) {
38
44
  const listed = await fetch(OUTLINE).then((answer) => answer.json()).catch(() => null);
@@ -41,13 +47,18 @@ export async function readPages(own = null) {
41
47
  const pages = [];
42
48
  const walk = (parent) => {
43
49
  for (const slug of listed.outline?.children?.[parent] ?? []) {
44
- if (!listed.reports[slug] || listed.reports[slug].instructions) continue;
45
- pages.push({ slug, title: titleOf(slug) || slug, icon: listed.reports[slug].icon ?? null, parent: parent ? titleOf(parent) || parent : null });
50
+ const report = listed.reports[slug];
51
+ if (!report || report.instructions) continue;
52
+ // An untitled document is named 未命名文档, as the workbench names it (its link's words: its page id alone).
53
+ if (report.kind !== "link" && report.kind !== "draft") pages.push({ slug, title: titleOf(slug) || t("未命名文档"), icon: report.icon ?? null, parent: isTop(parent) ? null : titleOf(parent) || t("未命名文档") });
46
54
  walk(slug);
47
55
  }
48
56
  };
49
- walk("");
50
- held = { pages, links: pageLinks(pages.map(({ slug }) => ({ slug, title: titleOf(slug) }))), trash: listed.trash ?? [] };
57
+ for (const { id } of listed.outline?.groups ?? [{ id: DOCS }]) walk(topOf(id));
58
+ // The sidebar's web links, by their links as a workbench's hash holds them: no page to link to, but an address the workbench opens.
59
+ const web = Object.values(listed.reports).filter((report) => report.kind === "link").map(({ slug, title }) => ({ slug, title }));
60
+ // And the extensions' columns (their entries: an address #~<id>/<entry id> the workbench opens too).
61
+ held = { pages, links: pageLinks(pages.map(({ slug }) => ({ slug, title: titleOf(slug) }))), webs: pageLinks(web), extensions: listed.extensions ?? [], trash: (listed.trash ?? []).filter(({ kind }) => kind !== "link") };
51
62
  return held;
52
63
  }
53
64
 
@@ -118,6 +129,13 @@ export function placeIn(list, text) {
118
129
  return page ? { ...page, block: place.block } : null;
119
130
  }
120
131
 
132
+ /**
133
+ * Whether workbench hash `hash` (no #) names a web link of the sidebar, or an entry of an extension's
134
+ * column that has an address (#~<id>/<entry id>): the workbench opens it in its content area, though it
135
+ * is no page to link to.
136
+ */
137
+ export const webLinkIn = (list, hash) => Boolean(hashAddress(decoded(hash), list.webs) || entryOfKey(list.extensions, decoded(hash))?.url);
138
+
121
139
  /** The title of the page `text` is an address of, of `list` or of its trash, or null: what stands for a link to it where its address would be a folder's. */
122
140
  export function titleIn(list, text) {
123
141
  const binned = addressOn(text, pageLinks(list.trash), globalThis.location?.origin)?.slug;
@@ -1,13 +1,15 @@
1
1
  // What every popup of the editor is painted with, written once: Notion's neutral palette, its blue
2
2
  // and its two shadows, light and dark by light-dark() (the page's color-scheme decides). A sheet of
3
- // `:root` custom properties, put in every document the editor draws in (ui.mjs addStyleTo), so a
3
+ // `:root` custom properties and the one button look (BUTTONS), put in every document the editor draws in (ui.mjs addStyleTo), so a
4
4
  // menu, a card, a bar, a notice reads them wherever it stands and none declares a palette of its
5
5
  // own. Values are Notion's own as notion-lite carries them (view/theme/notion-vars.css), which this
6
6
  // editor follows; the page's content colors (--doc-*, the kit's tokens.css) are another matter.
7
7
  //
8
8
  // The blue is two tokens because notion-lite has two, for two jobs: --ge-accent (its
9
- // --c-bluBacAccPri: a primary button, a switch, a chosen icon) and --ge-focus (its --c-butBluFocRin
10
- // and --c-inpBluFocRin: the ring round a focused field).
9
+ // --c-bluBacAccPri: a switch, a chosen icon, a table's tools, the bars that mark a place) and
10
+ // --ge-focus (its --c-butBluFocRin and --c-inpBluFocRin: the ring round a focused field). A button is
11
+ // neither: the one button look is BUTTONS below, whose primary is a pale blue (--ge-btn-primary*), the
12
+ // workbench's, never the solid --ge-accent.
11
13
  //
12
14
  // Two shadows, because Notion has two surfaces: --ge-shadow, a card standing over the text (the
13
15
  // format bar, the formula and link cards, the conflicts, a notice), and --ge-shadow-pop, a menu
@@ -15,6 +17,38 @@
15
17
  // emoji-picker/support.mjs keeps notion-lite's --c-* variables only for the media menu and the image's
16
18
  // alignment bar.
17
19
 
20
+ /**
21
+ * The one look of a button the editor draws on a page (the Agent changes' bar, the conflict card and the lost
22
+ * texts' panel, the crop window, a list's number, the icon picker's upload, the media menu): rules in the
23
+ * palette's sheet, so that every document the editor draws in has them from the first paint and no style
24
+ * repeats them. `.ge-btn` is a text button (ui.mjs textButton): no line and no fill, a gray under the
25
+ * pointer and while the keys are on it, a deeper one pressed; `data-primary` the pale blue (add, confirm,
26
+ * save), with `data-danger` the pale red (a delete); `data-quiet` a small one for looking, not deciding
27
+ * (收起, 定位). `.disabled` and `aria-disabled` are how a button made of a div says it cannot be pressed.
28
+ * `.ge-icon-btn` is the look of an icon button standing alone (a 批注's marker and its delete): no fill under
29
+ * the pointer (the icon goes darker), a gray only while it is pressed. The tools of a bar (the format bar's,
30
+ * the link card's) and a menu's rows are gray under the pointer, as Notion's are. Text fields are not
31
+ * buttons: they keep their thin outline.
32
+ */
33
+ const OFF = ":disabled, .disabled, [aria-disabled=\"true\"]";
34
+ export const BUTTONS = `
35
+ .ge-btn { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; height: 28px; margin: 0; padding: 0 10px; border: 0; border-radius: 6px; box-shadow: none; background: transparent; color: var(--ge-tx); font: inherit; font-weight: 500; line-height: 1; white-space: nowrap; cursor: pointer; user-select: none; transition: background 20ms ease-in; }
36
+ .ge-btn:is(:hover, :focus-visible):not(${OFF}) { outline: none; background: var(--ge-hover); }
37
+ .ge-btn:active:not(${OFF}) { background: var(--ge-press); }
38
+ .ge-btn[data-primary] { background: var(--ge-btn-primary); color: var(--ge-btn-primary-text); }
39
+ .ge-btn[data-primary]:is(:hover, :focus-visible):not(${OFF}) { background: var(--ge-btn-primary-hover); }
40
+ .ge-btn[data-primary]:active:not(${OFF}) { background: var(--ge-btn-primary-press); }
41
+ .ge-btn[data-primary][data-danger] { background: var(--ge-btn-danger); color: var(--ge-btn-danger-text); }
42
+ .ge-btn[data-primary][data-danger]:is(:hover, :focus-visible):not(${OFF}) { background: var(--ge-btn-danger-hover); }
43
+ .ge-btn[data-primary][data-danger]:active:not(${OFF}) { background: var(--ge-btn-danger-press); }
44
+ .ge-btn[data-quiet] { height: 24px; padding: 0 6px; color: var(--ge-tx2); font-weight: 400; }
45
+ .ge-btn[data-quiet]:is(:hover, :focus-visible):not(${OFF}) { color: var(--ge-tx); }
46
+ .ge-btn:is(${OFF}) { opacity: .45; cursor: default; }
47
+ .ge-icon-btn { border: 0; box-shadow: none; background: transparent; cursor: pointer; transition: background 20ms ease-in, color 20ms ease-in; }
48
+ .ge-icon-btn:hover:not(:disabled) { color: var(--ge-ico); }
49
+ .ge-icon-btn:active:not(:disabled) { background: var(--ge-press); }
50
+ `;
51
+
18
52
  export const TOKENS = `
19
53
  :root {
20
54
  --ge-tx: light-dark(#2c2c2b, #f0efed);
@@ -40,6 +74,17 @@ export const TOKENS = `
40
74
  --ge-focus: #2383e2;
41
75
  --ge-red: light-dark(#eb5757, #ff6b6b);
42
76
  --ge-quote: light-dark(rgb(28 19 1 / 16%), rgb(255 255 235 / 16%));
77
+ /* Buttons (BUTTONS below; the user's rule, restated 2026-11-09): a primary is a pale blue fill, always shown, its words the
78
+ deeper blue; a primary that cannot be taken back a pale red; every other button has no fill and no line, a gray under
79
+ the pointer (--ge-hover) and a deeper one pressed (--ge-press). The workbench's own values (--btnPriBg, --btnDanBg: tokens.css). */
80
+ --ge-btn-primary: light-dark(rgb(35 131 226 / 14%), rgb(67 155 255 / 24%));
81
+ --ge-btn-primary-hover: light-dark(rgb(35 131 226 / 22%), rgb(67 155 255 / 34%));
82
+ --ge-btn-primary-press: light-dark(rgb(35 131 226 / 30%), rgb(67 155 255 / 44%));
83
+ --ge-btn-primary-text: light-dark(#1a69bd, #7ab8ff);
84
+ --ge-btn-danger: light-dark(rgb(235 87 87 / 14%), rgb(255 104 92 / 24%));
85
+ --ge-btn-danger-hover: light-dark(rgb(235 87 87 / 22%), rgb(255 104 92 / 34%));
86
+ --ge-btn-danger-press: light-dark(rgb(235 87 87 / 30%), rgb(255 104 92 / 44%));
87
+ --ge-btn-danger-text: light-dark(#c4403c, #ff9a90);
43
88
  --ge-fill: light-dark(rgb(35 131 226 / 14%), rgb(35 131 226 / 28%));
44
89
  --ge-halo: rgb(35 131 226 / 14%);
45
90
  --ge-select: light-dark(rgb(35 131 226 / 28%), rgb(35 131 226 / 45%));
@@ -55,6 +100,11 @@ export const TOKENS = `
55
100
  --ge-toast-bg: light-dark(#0f0f0f, #2f2f2f);
56
101
  /* The quiet gray a heading is lit with for a moment when the outline goes to it (outline/), never a blue. */
57
102
  --ge-outline-flash: light-dark(rgb(33 27 23 / 7%), rgb(255 255 255 / 8%));
103
+ /* The lines a size is dragged by (resize/, columns/), grays like Notion's column resizer (its border.primaryTranslucent), never a
104
+ bright blue (the user, 2026-11-07): where a drag may start, the line being dragged, and that line where it snaps. */
105
+ --ge-guide: light-dark(rgb(55 53 47 / 16%), rgb(255 255 255 / 14%));
106
+ --ge-guide-hot: light-dark(rgb(55 53 47 / 36%), rgb(255 255 255 / 32%));
107
+ --ge-guide-snap: light-dark(rgb(55 53 47 / 62%), rgb(255 255 255 / 58%));
58
108
  /* 批注 (annotate/): a warm mark on the words an annotation is about, its 2px underline leading (a faint fill, so it is never
59
109
  taken for the report's own yellow background), a block anchor's bar; stronger for the one shown. */
60
110
  --ge-note: light-dark(rgb(255 212 0 / 6%), rgb(255 212 0 / 6%));
@@ -74,4 +124,5 @@ export const TOKENS = `
74
124
  --ge-diff-del-fill: light-dark(rgb(223 22 0 / 4.5%), rgb(255 104 92 / 10%));
75
125
  --ge-diff-del-text: light-dark(#cf5148, #ff8f85);
76
126
  }
127
+ ${BUTTONS}
77
128
  `;
@@ -1,5 +1,6 @@
1
1
  import { OLD_OUTLINE_OPEN_KEY, OLD_PREFERENCES_KEY, OUTLINE_OPEN_KEY, PREFERENCES_KEY } from "../../shared/preferences.mjs";
2
2
  import { LOOK_ATTRIBUTES } from "../../shared/document-look.mjs";
3
+ import { ADD, HANDLE } from "./handle-sizes.mjs";
3
4
 
4
5
  // The right side of the page while it is edited (docs/editor.md, "The outline and the right side"): the
5
6
  // outline (目录, outline/) docked at the window's right edge, the cards of 批注 (annotate/) between it and
@@ -52,9 +53,18 @@ import { LOOK_ATTRIBUTES } from "../../shared/document-look.mjs";
52
53
  // column stands where it will stay from the first paint, and by setMargin() after that.
53
54
  // When they move the column, the page hears a resize, so what stands over the text (the format bar, a
54
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.)
55
64
 
56
65
  /**
57
- * 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
58
68
  * to (the block handle's: its 38px, its 12px gap, 2px), the column, the least a full-width column
59
69
  * keeps, the outline in full (its default width, the least and the most it is dragged to), its
60
70
  * list's inset in its panel (`guide`: the column's right room ends there, at the list's edge, its
@@ -65,7 +75,7 @@ import { LOOK_ATTRIBUTES } from "../../shared/document-look.mjs";
65
75
  * clear; one focused comes 20px left, and no button shows then), a card's width, the least it gives up
66
76
  * to, and the room past it (clear of the outline's list: its guide and grip).
67
77
  */
68
- 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 });
69
79
 
70
80
  /**
71
81
  * The outline's width in full for the reader's choice `chosen` (px; 0 or none: the default), within
@@ -116,8 +126,13 @@ export function marginLayout({ width, outline = false, collapsed = false, notes
116
126
 
117
127
  const SLIDE = 200; // ms: the column's slide as the page comes to have 批注 or has none left
118
128
 
119
- /** 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
+ */
120
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); }
121
136
  @media screen {
122
137
  :root[data-ge-margin] {
123
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). */