@heycys/gallery 0.1.1 → 0.2.0

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 (473) hide show
  1. package/THIRD-PARTY-NOTICES.md +2 -2
  2. package/browser/editor/components/align.mjs +58 -0
  3. package/browser/editor/components/code/index.mjs +1 -1
  4. package/browser/editor/components/columns/page.mjs +4 -4
  5. package/browser/editor/components/embed/index.mjs +2 -2
  6. package/browser/editor/components/embed/page.mjs +1 -1
  7. package/browser/editor/components/file/page.mjs +4 -4
  8. package/browser/editor/components/flowchart/add.mjs +7 -6
  9. package/browser/editor/components/flowchart/page.mjs +1 -1
  10. package/browser/editor/components/heading/index.mjs +7 -6
  11. package/browser/editor/components/icons.mjs +11 -0
  12. package/browser/editor/components/image/crop.mjs +6 -9
  13. package/browser/editor/components/image/index.mjs +2 -0
  14. package/browser/editor/components/image/page.mjs +172 -90
  15. package/browser/editor/components/installed/index.mjs +15 -4
  16. package/browser/editor/components/installed/page.mjs +103 -25
  17. package/browser/editor/components/installed/served.mjs +6 -5
  18. package/browser/editor/components/instances.mjs +18 -0
  19. package/browser/editor/components/list/page.mjs +3 -6
  20. package/browser/editor/components/media/icons.mjs +13 -8
  21. package/browser/editor/components/media/page.mjs +5 -3
  22. package/browser/editor/components/media/toolbar.mjs +12 -3
  23. package/browser/editor/components/paragraph/index.mjs +4 -3
  24. package/browser/editor/components/registry.mjs +30 -9
  25. package/browser/editor/components/table/index.mjs +61 -2
  26. package/browser/editor/components/table/page.mjs +1 -1
  27. package/browser/editor/page/after/index.mjs +288 -0
  28. package/browser/editor/page/align.mjs +91 -0
  29. package/browser/editor/page/annotate/card.mjs +3 -6
  30. package/browser/editor/page/autosave.mjs +310 -37
  31. package/browser/editor/page/beside.mjs +5 -6
  32. package/browser/editor/page/block-menu/index.mjs +6 -3
  33. package/browser/editor/page/blocks.mjs +3 -1
  34. package/browser/editor/page/board/client.mjs +57 -0
  35. package/browser/editor/page/board/hint.mjs +182 -0
  36. package/browser/editor/page/board/history.mjs +119 -0
  37. package/browser/editor/page/board/index.mjs +1059 -0
  38. package/browser/editor/page/board/motion.mjs +177 -0
  39. package/browser/editor/page/board/settings.mjs +210 -0
  40. package/browser/editor/page/caret.mjs +1 -1
  41. package/browser/editor/page/changes/bar.mjs +6 -14
  42. package/browser/editor/page/changes/index.mjs +2 -2
  43. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  44. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  45. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  46. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  47. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  48. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  49. package/browser/editor/page/clipboard/index.mjs +133 -15
  50. package/browser/editor/page/clipboard/own/blocks.mjs +13 -14
  51. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  52. package/browser/editor/page/clipboard/own/describe.mjs +6 -2
  53. package/browser/editor/page/clipboard/own/selection.mjs +1 -1
  54. package/browser/editor/page/columns/index.mjs +48 -46
  55. package/browser/editor/page/commit.mjs +80 -22
  56. package/browser/editor/page/conflicts.mjs +7 -7
  57. package/browser/editor/page/core.mjs +34 -16
  58. package/browser/editor/page/custom-page.mjs +110 -0
  59. package/browser/editor/page/editor.mjs +71 -17
  60. package/browser/editor/page/emoji-picker/styles.mjs +7 -12
  61. package/browser/editor/page/emoji-picker/upload.mjs +2 -2
  62. package/browser/editor/page/handle/index.mjs +5 -8
  63. package/browser/editor/page/history.mjs +41 -6
  64. package/browser/editor/page/insert/index.mjs +20 -22
  65. package/browser/editor/page/links/index.mjs +5 -3
  66. package/browser/editor/page/links/pages.mjs +27 -9
  67. package/browser/editor/page/look.mjs +54 -3
  68. package/browser/editor/page/margin.mjs +18 -3
  69. package/browser/editor/page/media-menu/index.mjs +6 -7
  70. package/browser/editor/page/menu.mjs +33 -14
  71. package/browser/editor/page/notices.mjs +2 -2
  72. package/browser/editor/page/page-objects.mjs +9 -2
  73. package/browser/editor/page/panel.mjs +4 -9
  74. package/browser/editor/page/present-links.mjs +15 -5
  75. package/browser/editor/page/present-shell.mjs +63 -0
  76. package/browser/editor/page/resize/index.mjs +42 -29
  77. package/browser/editor/page/selection/index.mjs +10 -2
  78. package/browser/editor/page/served.mjs +14 -0
  79. package/browser/editor/page/session.mjs +48 -4
  80. package/browser/editor/page/slash-menu/index.mjs +3 -3
  81. package/browser/editor/page/table/binding.mjs +87 -0
  82. package/browser/editor/page/table/cells.mjs +102 -46
  83. package/browser/editor/page/table/index.mjs +24 -9
  84. package/browser/editor/page/table/pictures.mjs +299 -0
  85. package/browser/editor/page/table/views.mjs +103 -0
  86. package/browser/editor/page/table/widths.mjs +82 -10
  87. package/browser/editor/page/text-select/index.mjs +26 -12
  88. package/browser/editor/page/todo-writes.mjs +75 -0
  89. package/browser/editor/page/typing/index.mjs +97 -15
  90. package/browser/editor/page/typing/leave.mjs +19 -5
  91. package/browser/editor/page/ui.mjs +13 -0
  92. package/browser/shared/block-packs.mjs +419 -0
  93. package/browser/shared/board-places.mjs +224 -0
  94. package/browser/shared/card-size.mjs +59 -0
  95. package/browser/shared/colors.mjs +3 -2
  96. package/browser/shared/component-catalog.mjs +330 -46
  97. package/browser/shared/component-source.mjs +39 -0
  98. package/browser/shared/components/heading.mjs +2 -1
  99. package/browser/shared/components/index.mjs +12 -3
  100. package/browser/shared/components/installed.mjs +35 -3
  101. package/browser/shared/components/page.mjs +20 -8
  102. package/browser/shared/components/paragraph.mjs +8 -1
  103. package/browser/shared/components/state.mjs +5 -1
  104. package/browser/shared/components/table.mjs +1 -0
  105. package/browser/shared/document-look.mjs +10 -5
  106. package/browser/shared/drafts.mjs +432 -0
  107. package/browser/shared/emoji/line-icons.mjs +37 -0
  108. package/browser/shared/extension-sidebar.mjs +126 -0
  109. package/browser/shared/extensions.mjs +79 -0
  110. package/browser/shared/file-table.mjs +101 -0
  111. package/browser/shared/http.mjs +9 -0
  112. package/browser/shared/i18n/en.mjs +330 -27
  113. package/browser/shared/json-layout.mjs +47 -0
  114. package/browser/shared/net-cache.mjs +133 -0
  115. package/browser/shared/net-keys.mjs +32 -0
  116. package/browser/shared/net-policy.mjs +177 -0
  117. package/browser/shared/outline-shape.mjs +128 -0
  118. package/browser/shared/outline.mjs +359 -67
  119. package/browser/shared/pack-manifest.mjs +88 -0
  120. package/browser/shared/page-address.mjs +6 -3
  121. package/browser/shared/page-cards.mjs +308 -0
  122. package/browser/shared/page-links.mjs +69 -6
  123. package/browser/shared/pages.mjs +500 -0
  124. package/browser/shared/paths.mjs +6 -0
  125. package/browser/shared/pinyin.mjs +92 -0
  126. package/browser/shared/present-step.mjs +17 -0
  127. package/browser/shared/preview-server.mjs +3 -7
  128. package/browser/shared/session.mjs +8 -1
  129. package/browser/shared/shipped-blocks.mjs +299 -0
  130. package/browser/shared/sidebar-head.mjs +70 -0
  131. package/browser/shared/table-chart.mjs +33 -1
  132. package/browser/shared/todos.mjs +83 -0
  133. package/browser/shared/user-directory.mjs +24 -0
  134. package/browser/shared/versions.mjs +14 -0
  135. package/browser/shared/web-links.mjs +139 -0
  136. package/browser/shared/workspace-columns.mjs +120 -0
  137. package/browser/shared/workspace-name.mjs +154 -0
  138. package/browser/shared/workspace-query.mjs +125 -0
  139. package/browser/shared/workspace-registry.mjs +272 -0
  140. package/browser/shared/workspace.mjs +171 -20
  141. package/dist/chunk-2OHANJ6Q.mjs +2 -0
  142. package/dist/chunk-3MBRL2PM.mjs +16 -0
  143. package/dist/chunk-4OKWZY5E.mjs +2 -0
  144. package/dist/chunk-4OZWXZE5.mjs +5 -0
  145. package/dist/chunk-5K44HLTZ.mjs +2 -0
  146. package/dist/{chunk-5ZKJ3H4P.mjs → chunk-5XA2ZBAE.mjs} +3 -3
  147. package/dist/{chunk-4FS7Z7WR.mjs → chunk-67N2QJYT.mjs} +1 -1
  148. package/dist/chunk-6KBYRA2C.mjs +2 -0
  149. package/dist/chunk-6N3PX4XX.mjs +8 -0
  150. package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
  151. package/dist/chunk-7JDX4E65.mjs +13 -0
  152. package/dist/chunk-7M2BQQGZ.mjs +2 -0
  153. package/dist/chunk-7OG26PGF.mjs +2 -0
  154. package/dist/chunk-AHN3NFKJ.mjs +3 -0
  155. package/dist/chunk-ALZ6ZENZ.mjs +3 -0
  156. package/dist/chunk-AR5D57ZD.mjs +170 -0
  157. package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
  158. package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
  159. package/dist/chunk-CMA72MAG.mjs +2 -0
  160. package/dist/{chunk-LCTHTZIL.mjs → chunk-DAZL73VK.mjs} +91 -91
  161. package/dist/{chunk-Y6RLRPG3.mjs → chunk-DJISHPUL.mjs} +1 -1
  162. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  163. package/dist/{chunk-FDLXYYG4.mjs → chunk-EAIFITAV.mjs} +1 -1
  164. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  165. package/dist/chunk-EP25HDAJ.mjs +2 -0
  166. package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
  167. package/dist/chunk-EURLTOTZ.mjs +4 -0
  168. package/dist/chunk-F4ZA6JRZ.mjs +3 -0
  169. package/dist/chunk-F6HVDZR4.mjs +2 -0
  170. package/dist/chunk-FDMAXMQA.mjs +2 -0
  171. package/dist/chunk-FETSREUN.mjs +5 -0
  172. package/dist/chunk-GHOYKMQ3.mjs +4 -0
  173. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  174. package/dist/chunk-HDWTZ763.mjs +12 -0
  175. package/dist/chunk-HPOI4ZX7.mjs +6 -0
  176. package/dist/{chunk-BGGDRNYQ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
  177. package/dist/chunk-IA4PSIMD.mjs +2 -0
  178. package/dist/{chunk-E7LWZRXY.mjs → chunk-ITOH5EGO.mjs} +1 -1
  179. package/dist/chunk-IV64FFI4.mjs +2 -0
  180. package/dist/chunk-J43Z6LSS.mjs +2 -0
  181. package/dist/chunk-J5PUIV3Y.mjs +4 -0
  182. package/dist/chunk-JA7G53PC.mjs +2 -0
  183. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  184. package/dist/chunk-JLTZF4LA.mjs +2 -0
  185. package/dist/chunk-JZT7I2OJ.mjs +2 -0
  186. package/dist/chunk-K5XATEOO.mjs +2 -0
  187. package/dist/chunk-L2SUIS5S.mjs +2 -0
  188. package/dist/chunk-L7UP52JO.mjs +2 -0
  189. package/dist/chunk-LM4XFQHZ.mjs +2 -0
  190. package/dist/chunk-MN7BZL4R.mjs +2 -0
  191. package/dist/chunk-MTYYHM2G.mjs +15 -0
  192. package/dist/chunk-MXUBKDRO.mjs +15 -0
  193. package/dist/chunk-NAMN3RAF.mjs +2 -0
  194. package/dist/{chunk-SYGN7GLU.mjs → chunk-OEXWRJME.mjs} +1 -1
  195. package/dist/chunk-OFHXGWOX.mjs +2 -0
  196. package/dist/chunk-ORID3AE7.mjs +166 -0
  197. package/dist/chunk-PAREWQGB.mjs +4 -0
  198. package/dist/chunk-PGDE24N5.mjs +2 -0
  199. package/dist/chunk-POTA2FV4.mjs +2 -0
  200. package/dist/chunk-PUOQ65NC.mjs +2 -0
  201. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  202. package/dist/chunk-QE5RG76S.mjs +549 -0
  203. package/dist/chunk-R2ZDLJYT.mjs +10 -0
  204. package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  205. package/dist/chunk-SWUTTZCN.mjs +2 -0
  206. package/dist/chunk-TANZRINB.mjs +2 -0
  207. package/dist/chunk-TR7TWGHB.mjs +2 -0
  208. package/dist/chunk-U5XL7D6A.mjs +3 -0
  209. package/dist/{chunk-XNROSH2T.mjs → chunk-U7NZI6N4.mjs} +2 -2
  210. package/dist/chunk-UGWMMEJ5.mjs +15 -0
  211. package/dist/chunk-UJQJKNGK.mjs +2 -0
  212. package/dist/chunk-UYVCQK3H.mjs +22 -0
  213. package/dist/chunk-VFX2KNHP.mjs +4 -0
  214. package/dist/chunk-VS7JIGJW.mjs +24 -0
  215. package/dist/chunk-W7665ZEC.mjs +6 -0
  216. package/dist/chunk-WQDQWCX5.mjs +2 -0
  217. package/dist/chunk-YI43RG4P.mjs +2 -0
  218. package/dist/chunk-YLPSMDIS.mjs +5 -0
  219. package/dist/chunk-YNAWBAYN.mjs +3 -0
  220. package/dist/chunk-YROX2O22.mjs +2 -0
  221. package/dist/chunk-ZDLPUOAG.mjs +6 -0
  222. package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  223. package/dist/chunk-ZQS6H473.mjs +16 -0
  224. package/dist/chunk-ZTHICSSF.mjs +2 -0
  225. package/dist/chunk-ZWWC4KLF.mjs +20 -0
  226. package/dist/cli.mjs +1 -1
  227. package/dist/server.mjs +1 -1
  228. package/dist/{chunk-U6KNPJPR.mjs → update-fetch.mjs} +1 -1
  229. package/dist/worker.mjs +1 -1
  230. package/kit/board.css +95 -0
  231. package/kit/board.js +250 -0
  232. package/kit/charts.js +5 -71
  233. package/kit/code.js +17 -17
  234. package/kit/report.css +335 -18
  235. package/kit/runtime.js +1638 -125
  236. package/kit/state.js +10 -1
  237. package/kit/tables.js +130 -43
  238. package/kit/ui.js +79 -35
  239. package/package.json +2 -1
  240. package/packs/gallery/card/component.json +14 -0
  241. package/packs/gallery/card/example.html +10 -0
  242. package/packs/gallery/card/preview.svg +1 -0
  243. package/packs/gallery/card/style.css +6 -0
  244. package/packs/gallery/card/template.html +6 -0
  245. package/packs/gallery/card/writing.md +14 -0
  246. package/packs/gallery/chart-card/component.json +15 -0
  247. package/packs/gallery/chart-card/example.html +14 -0
  248. package/packs/gallery/chart-card/kit.js +130 -0
  249. package/packs/gallery/chart-card/preview.svg +1 -0
  250. package/packs/gallery/chart-card/style.css +42 -0
  251. package/packs/gallery/chart-card/template.html +13 -0
  252. package/packs/gallery/chart-card/writing.md +20 -0
  253. package/packs/gallery/chart-view/component.json +20 -0
  254. package/packs/gallery/chart-view/example.html +13 -0
  255. package/packs/gallery/chart-view/kit.js +355 -0
  256. package/packs/gallery/chart-view/preview.svg +1 -0
  257. package/packs/gallery/chart-view/style.css +94 -0
  258. package/packs/gallery/chart-view/template.html +3 -0
  259. package/packs/gallery/chart-view/writing.md +22 -0
  260. package/packs/gallery/countdown/component.json +18 -0
  261. package/packs/gallery/countdown/example.html +3 -0
  262. package/packs/gallery/countdown/kit.js +216 -0
  263. package/packs/gallery/countdown/preview.svg +1 -0
  264. package/packs/gallery/countdown/style.css +118 -0
  265. package/packs/gallery/countdown/template.html +3 -0
  266. package/packs/gallery/countdown/writing.md +22 -0
  267. package/packs/gallery/habit/component.json +14 -0
  268. package/packs/gallery/habit/example.html +3 -0
  269. package/packs/gallery/habit/kit.js +334 -0
  270. package/packs/gallery/habit/preview.svg +1 -0
  271. package/packs/gallery/habit/style.css +174 -0
  272. package/packs/gallery/habit/template.html +3 -0
  273. package/packs/gallery/habit/writing.md +21 -0
  274. package/packs/gallery/heatmap/component.json +18 -0
  275. package/packs/gallery/heatmap/example.html +3 -0
  276. package/packs/gallery/heatmap/kit.js +513 -0
  277. package/packs/gallery/heatmap/preview.svg +1 -0
  278. package/packs/gallery/heatmap/style.css +189 -0
  279. package/packs/gallery/heatmap/template.html +3 -0
  280. package/packs/gallery/heatmap/writing.md +21 -0
  281. package/packs/gallery/kanban/component.json +18 -0
  282. package/packs/gallery/kanban/example.html +12 -0
  283. package/packs/gallery/kanban/kit.js +540 -0
  284. package/packs/gallery/kanban/preview.svg +1 -0
  285. package/packs/gallery/kanban/style.css +212 -0
  286. package/packs/gallery/kanban/template.html +3 -0
  287. package/packs/gallery/kanban/writing.md +20 -0
  288. package/packs/gallery/metric/component.json +22 -0
  289. package/packs/gallery/metric/example.html +15 -0
  290. package/packs/gallery/metric/kit.js +248 -0
  291. package/packs/gallery/metric/preview.svg +1 -0
  292. package/packs/gallery/metric/style.css +92 -0
  293. package/packs/gallery/metric/template.html +3 -0
  294. package/packs/gallery/metric/writing.md +23 -0
  295. package/packs/gallery/note/component.json +14 -0
  296. package/packs/gallery/note/example.html +3 -0
  297. package/packs/gallery/note/kit.js +129 -0
  298. package/packs/gallery/note/preview.svg +1 -0
  299. package/packs/gallery/note/style.css +97 -0
  300. package/packs/gallery/note/template.html +3 -0
  301. package/packs/gallery/note/writing.md +18 -0
  302. package/packs/gallery/pack.json +8 -0
  303. package/packs/gallery/recent/component.json +13 -0
  304. package/packs/gallery/recent/example.html +1 -0
  305. package/packs/gallery/recent/kit.js +293 -0
  306. package/packs/gallery/recent/preview.svg +1 -0
  307. package/packs/gallery/recent/style.css +85 -0
  308. package/packs/gallery/recent/template.html +1 -0
  309. package/packs/gallery/recent/writing.md +15 -0
  310. package/packs/gallery/static/component.json +13 -0
  311. package/packs/gallery/static/example.html +11 -0
  312. package/packs/gallery/static/preview.svg +1 -0
  313. package/packs/gallery/static/style.css +171 -0
  314. package/packs/gallery/static/template.html +1 -0
  315. package/packs/gallery/static/writing.md +18 -0
  316. package/packs/gallery/stats/component.json +13 -0
  317. package/packs/gallery/stats/example.html +1 -0
  318. package/packs/gallery/stats/kit.js +347 -0
  319. package/packs/gallery/stats/preview.svg +1 -0
  320. package/packs/gallery/stats/style.css +73 -0
  321. package/packs/gallery/stats/template.html +1 -0
  322. package/packs/gallery/stats/writing.md +11 -0
  323. package/packs/gallery/today/component.json +12 -0
  324. package/packs/gallery/today/example.html +1 -0
  325. package/packs/gallery/today/kit.js +220 -0
  326. package/packs/gallery/today/preview.svg +1 -0
  327. package/packs/gallery/today/style.css +149 -0
  328. package/packs/gallery/today/template.html +1 -0
  329. package/packs/gallery/today/writing.md +14 -0
  330. package/packs/gallery/todos/component.json +19 -0
  331. package/packs/gallery/todos/example.html +3 -0
  332. package/packs/gallery/todos/kit.js +489 -0
  333. package/packs/gallery/todos/preview.svg +1 -0
  334. package/packs/gallery/todos/style.css +214 -0
  335. package/packs/gallery/todos/template.html +3 -0
  336. package/packs/gallery/todos/writing.md +30 -0
  337. package/packs/gallery/weather/component.json +19 -0
  338. package/packs/gallery/weather/example.html +3 -0
  339. package/packs/gallery/weather/kit.js +387 -0
  340. package/packs/gallery/weather/preview.svg +1 -0
  341. package/packs/gallery/weather/style.css +140 -0
  342. package/packs/gallery/weather/template.html +3 -0
  343. package/packs/gallery/weather/writing.md +26 -0
  344. package/packs/gallery-soft/countdown/component.json +18 -0
  345. package/packs/gallery-soft/countdown/example.html +3 -0
  346. package/packs/gallery-soft/countdown/kit.js +206 -0
  347. package/packs/gallery-soft/countdown/preview.svg +1 -0
  348. package/packs/gallery-soft/countdown/style.css +211 -0
  349. package/packs/gallery-soft/countdown/template.html +3 -0
  350. package/packs/gallery-soft/countdown/writing.md +24 -0
  351. package/packs/gallery-soft/habit/component.json +14 -0
  352. package/packs/gallery-soft/habit/example.html +3 -0
  353. package/packs/gallery-soft/habit/kit.js +366 -0
  354. package/packs/gallery-soft/habit/preview.svg +1 -0
  355. package/packs/gallery-soft/habit/style.css +251 -0
  356. package/packs/gallery-soft/habit/template.html +3 -0
  357. package/packs/gallery-soft/habit/writing.md +23 -0
  358. package/packs/gallery-soft/heatmap/component.json +18 -0
  359. package/packs/gallery-soft/heatmap/example.html +3 -0
  360. package/packs/gallery-soft/heatmap/kit.js +498 -0
  361. package/packs/gallery-soft/heatmap/preview.svg +1 -0
  362. package/packs/gallery-soft/heatmap/style.css +181 -0
  363. package/packs/gallery-soft/heatmap/template.html +3 -0
  364. package/packs/gallery-soft/heatmap/writing.md +23 -0
  365. package/packs/gallery-soft/note/component.json +14 -0
  366. package/packs/gallery-soft/note/example.html +3 -0
  367. package/packs/gallery-soft/note/kit.js +162 -0
  368. package/packs/gallery-soft/note/preview.svg +1 -0
  369. package/packs/gallery-soft/note/style.css +148 -0
  370. package/packs/gallery-soft/note/template.html +3 -0
  371. package/packs/gallery-soft/note/writing.md +20 -0
  372. package/packs/gallery-soft/pack.json +8 -0
  373. package/packs/gallery-soft/recent/component.json +13 -0
  374. package/packs/gallery-soft/recent/example.html +1 -0
  375. package/packs/gallery-soft/recent/kit.js +335 -0
  376. package/packs/gallery-soft/recent/preview.svg +1 -0
  377. package/packs/gallery-soft/recent/style.css +160 -0
  378. package/packs/gallery-soft/recent/template.html +1 -0
  379. package/packs/gallery-soft/recent/writing.md +17 -0
  380. package/packs/gallery-soft/static/component.json +13 -0
  381. package/packs/gallery-soft/static/example.html +12 -0
  382. package/packs/gallery-soft/static/preview.svg +1 -0
  383. package/packs/gallery-soft/static/style.css +186 -0
  384. package/packs/gallery-soft/static/template.html +1 -0
  385. package/packs/gallery-soft/static/writing.md +20 -0
  386. package/packs/gallery-soft/stats/component.json +13 -0
  387. package/packs/gallery-soft/stats/example.html +1 -0
  388. package/packs/gallery-soft/stats/kit.js +341 -0
  389. package/packs/gallery-soft/stats/preview.svg +1 -0
  390. package/packs/gallery-soft/stats/style.css +119 -0
  391. package/packs/gallery-soft/stats/template.html +1 -0
  392. package/packs/gallery-soft/stats/writing.md +13 -0
  393. package/packs/gallery-soft/today/component.json +12 -0
  394. package/packs/gallery-soft/today/example.html +1 -0
  395. package/packs/gallery-soft/today/kit.js +222 -0
  396. package/packs/gallery-soft/today/preview.svg +1 -0
  397. package/packs/gallery-soft/today/style.css +178 -0
  398. package/packs/gallery-soft/today/template.html +1 -0
  399. package/packs/gallery-soft/today/writing.md +16 -0
  400. package/packs/gallery-soft/todos/component.json +19 -0
  401. package/packs/gallery-soft/todos/example.html +3 -0
  402. package/packs/gallery-soft/todos/kit.js +527 -0
  403. package/packs/gallery-soft/todos/preview.svg +1 -0
  404. package/packs/gallery-soft/todos/style.css +212 -0
  405. package/packs/gallery-soft/todos/template.html +3 -0
  406. package/packs/gallery-soft/todos/writing.md +32 -0
  407. package/packs/gallery-soft/weather/component.json +19 -0
  408. package/packs/gallery-soft/weather/example.html +3 -0
  409. package/packs/gallery-soft/weather/kit.js +411 -0
  410. package/packs/gallery-soft/weather/preview.svg +1 -0
  411. package/packs/gallery-soft/weather/style.css +185 -0
  412. package/packs/gallery-soft/weather/template.html +3 -0
  413. package/packs/gallery-soft/weather/writing.md +28 -0
  414. package/release.json +12 -10
  415. package/sdk/doc-kit.d.ts +396 -5
  416. package/sdk/doc-state.d.ts +2 -0
  417. package/skill/SKILL.md +56 -23
  418. package/skill/references/block-script.md +140 -0
  419. package/skill/references/building-blocks.md +257 -0
  420. package/skill/references/extensions.md +59 -0
  421. package/skill/references/pages-and-data.md +225 -0
  422. package/skill/references/writing-html.md +8 -4
  423. package/skill/sdk/doc-kit.d.ts +396 -5
  424. package/skill/sdk/doc-state.d.ts +2 -0
  425. package/web/assets/index-BM_K-Auu.css +1 -0
  426. package/web/assets/index-DRslOBiv.js +15 -0
  427. package/web/index.html +2 -2
  428. package/dist/chunk-3HIRPVLU.mjs +0 -2
  429. package/dist/chunk-5MHMDLZD.mjs +0 -4
  430. package/dist/chunk-64K7IPLO.mjs +0 -255
  431. package/dist/chunk-6QXGZM36.mjs +0 -8
  432. package/dist/chunk-6RGJOMJP.mjs +0 -16
  433. package/dist/chunk-AJ73XJF3.mjs +0 -2
  434. package/dist/chunk-AUZGSZJH.mjs +0 -13
  435. package/dist/chunk-BFE45AKL.mjs +0 -2
  436. package/dist/chunk-BXO2ZY5P.mjs +0 -2
  437. package/dist/chunk-CEH4MRCC.mjs +0 -2
  438. package/dist/chunk-CY2FNNAB.mjs +0 -16
  439. package/dist/chunk-DFUDV4LX.mjs +0 -2
  440. package/dist/chunk-DKXG4XDG.mjs +0 -2
  441. package/dist/chunk-EZQ34M3F.mjs +0 -124
  442. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  443. package/dist/chunk-FUKQNOQS.mjs +0 -9
  444. package/dist/chunk-JPOSLGOC.mjs +0 -6
  445. package/dist/chunk-JVHHHGEA.mjs +0 -3
  446. package/dist/chunk-L3QKH4OK.mjs +0 -26
  447. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  448. package/dist/chunk-LEF6DRCK.mjs +0 -59
  449. package/dist/chunk-NO5NJCHO.mjs +0 -2
  450. package/dist/chunk-NQPVAU63.mjs +0 -2
  451. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  452. package/dist/chunk-O35FIXRB.mjs +0 -2
  453. package/dist/chunk-P2CAZPDP.mjs +0 -3
  454. package/dist/chunk-PL6GHLGD.mjs +0 -15
  455. package/dist/chunk-QJIR2BLP.mjs +0 -6
  456. package/dist/chunk-QUPLVPB2.mjs +0 -7
  457. package/dist/chunk-R7BNENLY.mjs +0 -4
  458. package/dist/chunk-S6O4UHLC.mjs +0 -2
  459. package/dist/chunk-SGJWQKEI.mjs +0 -2
  460. package/dist/chunk-TX6JH7TR.mjs +0 -2
  461. package/dist/chunk-TYK6VNAB.mjs +0 -10
  462. package/dist/chunk-UALRNVSE.mjs +0 -4
  463. package/dist/chunk-V3HXVQEM.mjs +0 -2
  464. package/dist/chunk-V5VXUJKG.mjs +0 -2
  465. package/dist/chunk-VBXMADMP.mjs +0 -2
  466. package/dist/chunk-VNV55RUA.mjs +0 -2
  467. package/dist/chunk-VPD2PS35.mjs +0 -2
  468. package/dist/chunk-W4VKN3GF.mjs +0 -7
  469. package/dist/chunk-W5BXXPDJ.mjs +0 -3
  470. package/dist/chunk-XUNHRCOG.mjs +0 -6
  471. package/skill/references/building-components.md +0 -367
  472. package/web/assets/index-Bh74DfsV.css +0 -1
  473. package/web/assets/index-UW0RyFFt.js +0 -13
@@ -13,17 +13,20 @@ import { t } from "../../../shared/i18n/index.mjs";
13
13
  // and blocks/grid.css), and between the nodes of a flowchart's row (步骤流程图: the same tool, the
14
14
  // user's of 2026-10-25). With the pointer in the gap between two of them, a thin line stands down
15
15
  // it, and the cursor says it resizes: dragging it moves only the two beside it (ratiosAfterResize:
16
- // snapping to their equal split and to tenths of the row, each keeping its least share), each one's
17
- // share shown at its top right while it lasts; letting go writes the shares as one undo step, Esc
18
- // cancels, and a double click makes the row's parts equal again. A kind of row (KINDS) says how its
19
- // parts are found, measured, shown while dragged and written:
20
- // - a row of columns: the line blue in the row (grey above it, up to its pin); above each gap, and
21
- // right of the last column, a dot turns into a pin that adds a column there (新增分栏). While the
16
+ // snapping to their equal split and to tenths of the row within SNAP px, each keeping its least
17
+ // share), the bottom notice saying the row's shares while it lasts (no badge on the parts: the user,
18
+ // 2026-11-07, who also found the old snap of 1% of the row too strong); letting go writes the shares
19
+ // as one undo step, Esc cancels, and a double click makes the row's parts equal again. The line is
20
+ // a gray, never a bright blue (the guide tokens, look.mjs): faint where a drag may start, darker
21
+ // while it is dragged, darkest where it snaps. A kind of row (KINDS) says how its parts are found,
22
+ // measured, shown while dragged and written:
23
+ // - a row of columns: the line darker in the row (fainter above it, up to its pin); above each gap,
24
+ // and right of the last column, a dot turns into a round + that adds a column there (新增分栏). While the
22
25
  // pointer is over a row, or the caret in it, its columns are tinted, so the reader sees where they
23
26
  // are. A row stacked on a narrow screen has no gaps. Its shares (--doc-column-ratio) are shown
24
27
  // as the columns' flex-grow while dragged, written as one structural step.
25
28
  // - a flowchart's row, as the kit laid it out (its nodes whose cards stand at one height:
26
- // components/flowchart/page.mjs rowsOfFlow): the line grey, blue once pressed (the mock's), the
29
+ // components/flowchart/page.mjs rowsOfFlow): the line faint, darker once pressed, the
27
30
  // arrow under it dimmed while it shows; no pin (a node is added by the "+" at a node's right,
28
31
  // add.mjs). A node keeps the least width the kit gives a card beside others (leastWidth: 116px and
29
32
  // its longest word), so the row never wraps under the pointer. Its shares are each node's weight
@@ -42,26 +45,30 @@ const STYLE = `
42
45
  }
43
46
  ${gestureLock("data-ge-gap", { cursor: "col-resize" })}
44
47
  html[data-ge-gap-drag] { -webkit-user-select: none; user-select: none; }
45
- .ge-gap-line { width: 2px; border-radius: 2px; background: light-dark(#dee0e3, #4b4e53); pointer-events: none; }
46
- .ge-gap-line[data-hot] { background: #336df4; }
47
- .ge-gap-pin { width: 16px; height: 16px; cursor: pointer; pointer-events: auto; }
48
- .ge-gap-pin > svg { position: absolute; display: block; }
49
- .ge-gap-pin > .ge-gap-dot { left: 6px; top: 6.5px; }
50
- .ge-gap-pin > .ge-gap-add { left: -3px; top: -8px; opacity: 0; transform: scale(0); transform-origin: 50% 90%; transition: opacity .15s, transform .15s; }
48
+ .ge-gap-line { width: 2px; border-radius: 1px; background: var(--ge-guide); pointer-events: none; }
49
+ .ge-gap-line[data-hot] { background: var(--ge-guide-hot); }
50
+ .ge-gap-line[data-hot][data-snap] { background: var(--ge-guide-snap); }
51
+ .ge-gap-pin { width: 16px; height: 16px; color: var(--ge-ico3); cursor: pointer; pointer-events: auto; }
52
+ .ge-gap-pin > .ge-gap-dot { position: absolute; left: 6px; top: 6.5px; display: block; }
53
+ .ge-gap-pin > .ge-gap-add { position: absolute; left: -2px; top: -2px; display: grid; place-items: center; box-sizing: border-box; width: 20px; height: 20px; border-radius: 50%;
54
+ background: var(--ge-bg); color: var(--ge-ico); box-shadow: 0 1px 3px light-dark(rgb(25 25 25 / 18%), rgb(0 0 0 / 45%)); opacity: 0; transform: scale(.6); transition: opacity .15s, transform .15s; }
55
+ .ge-gap-pin > .ge-gap-add > svg { display: block; }
51
56
  .ge-gap-pin:hover > .ge-gap-dot { opacity: 0; }
52
57
  .ge-gap-pin:hover > .ge-gap-add { opacity: 1; transform: scale(1); }
53
- .ge-gap-badge { padding: 0 4px; border-radius: 4px; color: #fff; background: rgb(0 0 0 / 30%); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); font: 12px/18px var(--doc-sans-font, var(--doc-font, system-ui, sans-serif)); pointer-events: none; white-space: nowrap; }
54
58
  /* The arrow under a flowchart's gap line (the node's ::after, the kit's) dimmed while the line shows. */
55
59
  ol.doc-flow > li::after { transition: opacity .12s; }
56
60
  ol.doc-flow > li[data-ge-gap-dim]::after { opacity: .15; }
57
61
  @media (prefers-reduced-motion: reduce) { ol.doc-flow > li::after { transition: none; } }
58
62
  `;
59
- // Feishu's dot and pin.
60
- const DOT = '<svg class="ge-gap-dot" width="4" height="3" viewBox="0 0 4 3" aria-hidden="true"><path d="M3.5 1.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z" fill="#BBBFC4"/></svg>';
61
- const PIN = '<svg class="ge-gap-add" width="22" height="24" viewBox="0 0 22 24" aria-hidden="true"><path fill-rule="evenodd" d="M16.153 20.721A10.999 10.999 0 0022 11c0-6.075-4.925-11-11-11S0 4.925 0 11c0 4.213 2.369 7.873 5.847 9.721L11 24l5.153-3.279z" fill="#3370FF"/><path fill-rule="evenodd" d="M10.45 6a.2.2 0 00-.2.2v4.05H6.2a.2.2 0 00-.2.2v1.1c0 .11.09.2.2.2h4.05v4.05c0 .11.09.2.2.2h1.1a.2.2 0 00.2-.2v-4.05h4.05a.2.2 0 00.2-.2v-1.1a.2.2 0 00-.2-.2h-4.05V6.2a.2.2 0 00-.2-.2h-1.1z" fill="#fff"/></svg>';
63
+ // Feishu's dot, in the handle's quietest gray; under the pointer it turns into a round + in the
64
+ // look of the editor's small floating buttons (a white disc, a hairline, a gray +), not Feishu's blue pin.
65
+ const DOT = '<svg class="ge-gap-dot" width="4" height="3" viewBox="0 0 4 3" aria-hidden="true"><path d="M3.5 1.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z" fill="currentColor"/></svg>';
66
+ const PIN = '<span class="ge-gap-add"><svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M6 1.75v8.5M1.75 6h8.5" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg></span>';
62
67
  const TAIL = 20; // right of the last column, the pin's gap (Feishu's last gap)
63
68
  const ABOVE = 30; // the pin stands this far above the row: the gap reaches up to it
64
69
  const DOUBLE = { time: 500, distance: 4 }; // two presses on a gap this soon and this near: a double click (ms, px)
70
+ const SNAP = 5; // px: how near the equal split or a tenth of the row a dragged gap snaps to it
71
+ const LIVE = 60000; // ms: the notice saying the shares stays while the drag lasts (it is taken off when it ends)
65
72
 
66
73
  const percent = (ratios) => ratios.map((ratio) => `${Math.round(100 * ratio)}%`).join(" / ");
67
74
  const same = (a, b) => a.every((ratio, index) => Math.abs(ratio - b[index]) < 0.0005);
@@ -69,16 +76,17 @@ const same = (a, b) => a.every((ratio, index) => Math.abs(ratio - b[index]) < 0.
69
76
  /**
70
77
  * active(): whether the tools may show. struct(parents, touched, mutate): the editor's undoable
71
78
  * structural change (it records the columns' widths). look(elements, changes): the editor's undoable
72
- * change of blocks' looks (a flowchart's nodes' weights, history.mjs lookStep). say: the status line.
79
+ * change of blocks' looks (a flowchart's nodes' weights, history.mjs lookStep). say(text, kind, hold):
80
+ * the bottom notice; settle(): the notice taken off.
73
81
  * What is resized is known by the page objects that say so: `columnOps` (a row's ratios, add(row,
74
82
  * index): a new column there) and `rowOps` (a row of nodes, its shares and weights).
75
83
  */
76
- export function createColumnGaps({ active, struct, look, say }) {
84
+ export function createColumnGaps({ active, struct, look, say, settle = () => {} }) {
77
85
  addStyle(STYLE);
78
86
  const { add, columnsOf, ratiosAfterResize, ratiosOf, setRatios, sized } = columnOps() ?? {};
79
87
  const { leastWidth, rowShares, rowsOfFlow, weightValue } = rowOps() ?? {};
80
88
  let hover = null; // { row, index, last, line, pin }: the gap after part `index` of `row` (last: right of the last column)
81
- let drag = null; // { row, index, start, before, least, ratios, span, x, line, badges }
89
+ let drag = null; // { row, index, start, before, least, ratios, snapped, span, x, line, told }
82
90
 
83
91
  // ---------- the kinds of row ----------
84
92
  // A row: { kind, el: the element holding it (a row of columns, a flowchart), parts: its parts in
@@ -86,6 +94,7 @@ export function createColumnGaps({ active, struct, look, say }) {
86
94
  const KINDS = {
87
95
  columns: {
88
96
  label: "column", // what its overlays are listed as (overlays.mjs)
97
+ live: (ratios) => t("栏宽:{0}", percent(ratios)), // the notice while a gap is dragged
89
98
  containers: kept(() => [...document.querySelectorAll(".doc-columns[data-ge-box]")]),
90
99
  // Its one row, side by side (a stacked one has no gaps).
91
100
  rows: (el) => (getComputedStyle(el).flexDirection.startsWith("column") ? [] : [columnsOf(el)]),
@@ -118,6 +127,7 @@ export function createColumnGaps({ active, struct, look, say }) {
118
127
  },
119
128
  flowchart: {
120
129
  label: "flowchart",
130
+ live: (ratios) => t("节点宽度:{0}", percent(ratios)),
121
131
  containers: kept(() => [...document.querySelectorAll("ol.doc-flow[data-ge-box]:not([data-ge-keep])")]),
122
132
  // Its rows of two nodes or more, laid out in rows by the kit (one node a row has no gaps).
123
133
  rows: (el) => (getComputedStyle(el).flexDirection.startsWith("column") ? [] : rowsOfFlow(el).filter((steps) => steps.length > 1 && steps.every((step) => step.hasAttribute("data-ge-box")))),
@@ -256,7 +266,8 @@ export function createColumnGaps({ active, struct, look, say }) {
256
266
  before: kind.before(row),
257
267
  least: kind.least(row, span),
258
268
  line: hover?.line ?? null,
259
- badges: row.parts.map((part) => own(element("div", { class: "ge-gap-badge", "data-doc-dev": "" }), `${kind.label} share`, { owner: part, alive: () => drag?.row === row && row.el.isConnected })),
269
+ snapped: false,
270
+ told: null, // what the notice says now
260
271
  };
261
272
  hover?.pin?.remove();
262
273
  hover?.line.setAttribute("data-hot", "");
@@ -268,43 +279,34 @@ export function createColumnGaps({ active, struct, look, say }) {
268
279
  }
269
280
 
270
281
  // The two parts beside the gap as the pointer has moved (none until it has: a press alone changes
271
- // nothing), shown, each share on its part.
282
+ // nothing), shown; the row's shares in the bottom notice (said again only when they change), the
283
+ // line darkest where they snapped.
272
284
  function update(x) {
273
285
  const { row, index, start, span, least } = drag;
274
286
  const kind = kindOf(row);
275
- drag.ratios = Math.abs(x - drag.x) < 1 ? start : ratiosAfterResize(start, index, (x - drag.x) / span, least);
287
+ const moved = (x - drag.x) / span;
288
+ drag.ratios = Math.abs(x - drag.x) < 1 ? start : ratiosAfterResize(start, index, moved, least, SNAP / span);
289
+ drag.snapped = Math.abs(x - drag.x) >= 1 && !same(drag.ratios, ratiosAfterResize(start, index, moved, least, 0));
276
290
  if (same(drag.ratios, start)) kind.reset(row, drag.before);
277
291
  else kind.show(row, drag.ratios);
292
+ drag.line?.toggleAttribute("data-snap", drag.snapped);
293
+ const told = drag.told || Math.abs(x - drag.x) >= 1 ? kind.live(drag.ratios) : null; // a press alone says nothing
294
+ if (told && told !== drag.told) say(told, "note", LIVE);
295
+ drag.told = told ?? drag.told;
278
296
  // A row of columns is laid out again at once; a flowchart's by the kit, in a frame of its own
279
- // asked for as the weights are written (after this one's): measured the frame after that.
280
- if (kind.pins) placeBadges();
281
- else requestAnimationFrame(() => requestAnimationFrame(() => { if (drag?.row === row) placeBadges(); }));
282
- }
283
-
284
- // Each share at its part's top right (a node's 10px in from its right edge, across its border: the mock's).
285
- function placeBadges() {
286
- const { row, ratios, badges } = drag;
287
- const layer = layerOf(row.el);
288
- const flowchart = !kindOf(row).pins;
289
- row.parts.forEach((part, at) => {
290
- const rect = part.getBoundingClientRect();
291
- badges[at].textContent = `${Math.round(100 * ratios[at])}%`;
292
- if (badges[at].parentNode !== layer) layer.append(badges[at]);
293
- const size = badges[at].getBoundingClientRect();
294
- const right = flowchart ? rect.right - 10 : rect.right + 2;
295
- const top = flowchart ? rect.top - size.height / 2 : rect.top - 2;
296
- drawBox(badges[at], layer, { left: right - size.width, top, right, bottom: top + size.height });
297
- });
298
- if (hover) place(hover);
297
+ // asked for as the weights are written (after this one's): the line placed again the frame after that.
298
+ if (kind.pins) { if (hover) place(hover); }
299
+ else requestAnimationFrame(() => requestAnimationFrame(() => { if (drag?.row === row && hover) place(hover); }));
299
300
  }
300
301
 
301
302
  function finish(commit) {
302
303
  if (!drag) return;
303
- const { row, start, ratios, badges, before } = drag;
304
+ const { row, start, ratios, before, line, told } = drag;
304
305
  const kind = kindOf(row);
305
306
  drag = null;
306
307
  note(row.kind, commit ? "gap released" : "gap cancelled");
307
- badges.forEach((badge) => badge.remove());
308
+ line?.removeAttribute("data-snap");
309
+ if (told) settle(); // the shares dragged to: what is said next (or nothing) replaces them
308
310
  kind.reset(row, before);
309
311
  row.el.removeAttribute("data-ge-gap-drag");
310
312
  document.documentElement.removeAttribute("data-ge-gap-drag");
@@ -355,6 +357,6 @@ export function createColumnGaps({ active, struct, look, say }) {
355
357
  /** Whether a gap is being dragged (a flowchart's row shows the weights dragged, not yet a step: commits wait). */
356
358
  get dragging() { return Boolean(drag); },
357
359
  /** The editor's UI nodes to keep in the top layer when a modal dialog opens. */
358
- get nodes() { return [...(hover ? [hover.line, ...(hover.pin ? [hover.pin] : [])] : []), ...(drag?.badges ?? [])]; },
360
+ get nodes() { return hover ? [hover.line, ...(hover.pin ? [hover.pin] : [])] : []; },
359
361
  };
360
362
  }
@@ -7,7 +7,7 @@ import { rangeAt, rangeOffsets } from "./marks.mjs";
7
7
  import { pageOfKind, pageOfUnit } from "./page-objects.mjs";
8
8
  import { containerTypeOf, pack, project } from "./sync-state.mjs";
9
9
  import { outsideKey, randomBlockId } from "../../shared/ids.mjs";
10
- import { CHART_DATA, CHART_SOURCE } from "../../shared/table-chart.mjs";
10
+ import { CHART_SOURCE, TABLE_READERS } from "../../shared/table-chart.mjs";
11
11
 
12
12
  // The page's commits (autosave; docs/editor.md, "Commits"). A commit is what changed on the page
13
13
  // since the last one, as collectEdits finds it against the layout and baselines the last commit
@@ -803,12 +803,18 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
803
803
  baselines.set(id, html);
804
804
  return id;
805
805
  };
806
+ // How many blocks holding text blocks of their own (a list item's paragraphs) asLoaded made anew, those
807
+ // in them made anew with them: pathIndex finds a unit's blocks again then.
808
+ let rewrites = 0;
809
+ const holdsBlocks = (host) => host.querySelector("[contenteditable='true']") !== null;
806
810
  // A text block as a load of the page shows the file's (its inline elements of the report's own
807
811
  // atoms, written back as they are: core.mjs prepareBlock), the caret where it stood.
808
812
  const asLoaded = (host, { inner, atoms = [], kind, caps }) => {
809
813
  const selection = host.ownerDocument.getSelection();
810
814
  const place = selection?.rangeCount && host.contains(selection.anchorNode) ? rangeOffsets(host, selection.getRangeAt(0)) : null;
815
+ const held = holdsBlocks(host);
811
816
  prepareBlock(host, { inner, atoms, kind, caps });
817
+ if (held || holdsBlocks(host)) rewrites++;
812
818
  if (place) select(rangeAt(host, place));
813
819
  };
814
820
  // The selection in a text block someone else's words are about to be merged into, as it stands among the
@@ -883,14 +889,26 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
883
889
  const hosts = [...element.querySelectorAll("[contenteditable='true']")].filter((host) => host.parentElement?.closest("[data-ge-unit], [data-ge-unit-kind]") === element);
884
890
  return hosts.length === blocks.length ? hosts : null;
885
891
  };
886
- // The text block of `element` (a unit of the page) that is the file's { self } or { path } one.
887
- const blockElement = (element, block) => {
892
+ // The text block of `element` (a unit of the page) that is the file's { self } or { path } one: the first
893
+ // of its own whose path (pathFrom) is that one. `find`: the unit's text blocks by path (pathIndex), which a
894
+ // caller asking of each of a unit's blocks makes once: found anew for each block, a table of 500 rows had
895
+ // its thousand cells walked, each past its row's 500 siblings, for each of the thousand the answer named,
896
+ // the page standing still for a minute after each commit there.
897
+ const blockElement = (element, block, find = pathIndex(element)) => {
888
898
  // Its own text: the element itself, or the span of its own its description's `title` names (a Sheet tab's name).
889
899
  if (block.self) return element.matches("[contenteditable='true']") ? element : BLOCK_TYPES[blockTypeOf(element)]?.title?.(element) ?? null;
890
900
  if (!Array.isArray(block.path) || block.path[0] !== "unit") return null;
891
- const want = JSON.stringify(block.path);
892
- const owner = (host) => host.parentElement?.closest("[data-ge-unit], [data-ge-unit-kind]");
893
- return [...element.querySelectorAll("[contenteditable='true']")].find((host) => owner(host) === element && JSON.stringify(pathFrom(element, host)) === want) ?? null;
901
+ return find(JSON.stringify(block.path));
902
+ };
903
+ // The text blocks of `element` by path, for asking of many in turn: found once (hostsByPath), and again only
904
+ // once a block holding blocks of its own was made anew as the file gives it (asLoaded's `rewrites`).
905
+ const pathIndex = (element) => {
906
+ let index = null;
907
+ let made = -1;
908
+ return (want) => {
909
+ if (made !== rewrites) [index, made] = [hostsByPath(element), rewrites];
910
+ return index.get(want) ?? null;
911
+ };
894
912
  };
895
913
 
896
914
  /**
@@ -1003,8 +1021,9 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1003
1021
  // of the page's (its words would go over theirs): it keeps the one the page had, but where nobody else wrote.
1004
1022
  const matched = new Set();
1005
1023
  const inOrder = inFileOrder(element, fact.blocks);
1024
+ const find = pathIndex(element);
1006
1025
  for (const [index, block] of fact.blocks.entries()) {
1007
- const host = blockElement(element, block) ?? inOrder?.[index];
1026
+ const host = blockElement(element, block, find) ?? inOrder?.[index];
1008
1027
  if (!host) continue;
1009
1028
  const fresh = blockOf(host) === null;
1010
1029
  const own = registerBlock(host, pending, block, unit);
@@ -1196,7 +1215,8 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1196
1215
  * Take in the answer to a commit (build's `pending`, the endpoint's receipt; `alone`: nobody but
1197
1216
  * this page wrote the file since it showed it, see adopt). Returns { conflicts, merged: hosts now
1198
1217
  * showing a merge, stale: a merge the page could not show, outdated: it runs a manifest that is no
1199
- * more, forced: the page units a change the reader said to make anyway went in for, all it held }.
1218
+ * more, forced: the page units a change the reader said to make anyway went in for, all it held,
1219
+ * rekeyed: the keys of tables it set otherwise on the page, [element, name, old, new | null] }.
1200
1220
  * A conflict of a change sent as the reader said to make it anyway says so (`again`).
1201
1221
  */
1202
1222
  function apply(pending, receipt, { alone = true } = {}) {
@@ -1221,24 +1241,31 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1221
1241
  else if (element) proposals.set(identityOf(element), id);
1222
1242
  }
1223
1243
  for (const [key, hash] of Object.entries(receipt.hashes ?? {})) if (map.units[Number(key)]) map.units[Number(key)].hash = hash;
1224
- // The keys of charts drawn from tables the file gave the blocks the commit made, other than the
1225
- // page chose (source/chart-keys.mjs: one the file had meanwhile): theirs on the page too, so
1226
- // that each card draws the table the file links it to ({ old: new | null }, null: off a table).
1244
+ // The keys of tables the file gave the blocks the commit made and the tables it keyed (a view
1245
+ // made from a menu: the key its frame took, an attribute of the unit), other than the page chose
1246
+ // (source/chart-keys.mjs: one the file had meanwhile): theirs on the page too, so that each card and
1247
+ // view reads the table the file links it to ({ old: new | null }, null: off a table); `rekeyed` says
1248
+ // which it changed ([element, name, old, new | null]), for the undo steps that recorded them (history.mjs).
1249
+ const rekeyed = [];
1227
1250
  if (receipt.chartKeys) {
1228
- for (const element of pending.made.values()) {
1251
+ const keyed = [...pending.units.values()].filter(({ change }) => change?.attribute === CHART_SOURCE).map(({ id }) => unitElement(id)).filter(Boolean);
1252
+ for (const element of [...pending.made.values(), ...keyed]) {
1229
1253
  // A copy not written yet sends its keys again: the file's now.
1230
1254
  const sent = element?.getAttribute(COPY_KEYS);
1231
1255
  if (sent) {
1232
1256
  const keys = Object.fromEntries(Object.entries(JSON.parse(sent)).map(([from, to]) => [from, to !== null && Object.hasOwn(receipt.chartKeys, to) ? receipt.chartKeys[to] : to]));
1233
1257
  element.setAttribute(COPY_KEYS, JSON.stringify(keys));
1234
1258
  }
1235
- for (const node of element ? [element, ...element.querySelectorAll(`[${CHART_DATA}], [${CHART_SOURCE}]`)] : []) {
1236
- for (const name of [CHART_DATA, CHART_SOURCE]) {
1259
+ const names = [CHART_SOURCE, ...TABLE_READERS];
1260
+ for (const node of element ? [element, ...element.querySelectorAll(names.map((name) => `[${name}]`).join(", "))] : []) {
1261
+ for (const name of names) {
1237
1262
  const key = node.getAttribute(name);
1238
1263
  if (key === null || !Object.hasOwn(receipt.chartKeys, key)) continue;
1239
1264
  const next = receipt.chartKeys[key];
1240
1265
  if (typeof next === "string") node.setAttribute(name, next);
1241
1266
  else if (next === null && name === CHART_SOURCE) node.removeAttribute(name);
1267
+ else continue;
1268
+ rekeyed.push([node, name, key, next]);
1242
1269
  }
1243
1270
  }
1244
1271
  }
@@ -1498,13 +1525,14 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1498
1525
  }
1499
1526
  setLayout(next);
1500
1527
  marks();
1501
- return { conflicts, merged, stale, outdated: shownAgain, forced: [...landed] };
1528
+ return { conflicts, merged, stale, outdated: shownAgain, forced: [...landed], rekeyed };
1502
1529
  }
1503
1530
 
1504
1531
  // A text committed: what it replaced (the first time this page committed it), what it is now, and what
1505
1532
  // the commits of it between left it as (`earlier`, the newest TEXTS_KEPT): someone writing back what
1506
1533
  // they read then puts back what the page committed after. Back as it was first, it is asked of what it
1507
1534
  // was between, as a part of what a component writes is (noteWrite): an undo is a commit like any other.
1535
+ // `at`: when it was last committed, which puts the journals of two tabs' pages in order (autosave.mjs).
1508
1536
  function committedText(key, sent) {
1509
1537
  const mine = sources.get(sent.id);
1510
1538
  const was = journal.get(key);
@@ -1512,7 +1540,7 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1512
1540
  if (mine === undefined) return void journal.delete(key);
1513
1541
  const earlier = [...new Set([...(was?.earlier ?? []), ...(was ? [was.mine] : [])])].filter((text) => text !== mine && text !== original).slice(-TEXTS_KEPT);
1514
1542
  if (mine === original && earlier.length === 0) return void journal.delete(key);
1515
- journal.set(key, { key, block: blockRef(sent.id), original, mine, ...(earlier.length > 0 ? { earlier } : {}) });
1543
+ journal.set(key, { key, block: blockRef(sent.id), original, mine, ...(earlier.length > 0 ? { earlier } : {}), at: Date.now() });
1516
1544
  }
1517
1545
 
1518
1546
  // A part of what a unit's component writes, written by a commit (`before`, `after`: the hashes the file
@@ -1626,6 +1654,12 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1626
1654
  /** Whether unit `unit` stands inside one of `units` on the page: what someone else's write took out with it is decided with it. */
1627
1655
  const heldWithin = (unit, units) => units.some((other) => other !== unit && unitsWithin(other).includes(unit));
1628
1656
 
1657
+ /** The unit the page shows with file id `id` (not deleted on the page), or null: a block an earlier load made, still there. */
1658
+ const unitOf = (id) => {
1659
+ const unit = typeof id === "string" ? map.units.findIndex((each, index) => each?.blockId === id && !removed.has(index) && unitElement(index)) : -1;
1660
+ return unit < 0 ? null : unit;
1661
+ };
1662
+
1629
1663
  /** The text of the blocks in a unit the page shows (the reader's words), or "". */
1630
1664
  const unitText = (unit) => {
1631
1665
  const element = unitElement(unit);
@@ -1741,7 +1775,7 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
1741
1775
  }
1742
1776
 
1743
1777
  return {
1744
- build, apply, collect, current, marks, counts, buffer, texts, resolve, committed, unjournal, placedUnits, putBack, heldWithin, unitText, written, carry, dataOfUnit, heldOtherwise, rewrite, issues, removed, pageIds, idFor,
1778
+ build, apply, collect, current, marks, counts, buffer, texts, resolve, committed, unjournal, placedUnits, putBack, heldWithin, unitOf, unitText, written, carry, dataOfUnit, heldOtherwise, rewrite, issues, removed, pageIds, idFor,
1745
1779
  /** The id a unit of the page has in the file, or the one it proposes for it; null for none. */
1746
1780
  fileId: (unit) => map.units[unit]?.blockId ?? map.units[unit]?.proposed ?? null,
1747
1781
  /** The hash a unit of the page has in the file now (its map's, kept by every answer); null for none (changes/ finds the Agent's blocks by it). */
@@ -1803,11 +1837,35 @@ export const nodeHTML = (node) => [node?.title, node?.html, node?.restore?.sourc
1803
1837
  // Where a text block of the page sits in its unit, as the server's scan writes it (pathIn): the
1804
1838
  // element steps from the unit down, each [tag, index among the parent's children of that tag, how
1805
1839
  // many there are]. Only to find the element a path of the file names (blockElement), never to say where a block is.
1806
- function pathFrom(unit, host) {
1807
- const steps = [];
1840
+ // `steps`: each element's step, kept for the next path asked of the same unit (hostsByPath): its parent's
1841
+ // children are counted once, for all of them.
1842
+ function pathFrom(unit, host, steps = new Map()) {
1843
+ const path = [];
1808
1844
  for (let node = host; node && node !== unit; node = node.parentElement) {
1809
- const same = [...(node.parentElement?.children ?? [])].filter((child) => child.localName === node.localName);
1810
- steps.unshift([node.localName, same.indexOf(node), same.length]);
1845
+ if (!steps.has(node)) {
1846
+ const children = node.parentElement?.children ?? [node];
1847
+ const counts = new Map();
1848
+ for (const child of children) {
1849
+ const at = counts.get(child.localName) ?? 0;
1850
+ counts.set(child.localName, at + 1);
1851
+ steps.set(child, [child.localName, node.parentElement ? at : -1]);
1852
+ }
1853
+ for (const child of children) steps.get(child).push(node.parentElement ? counts.get(child.localName) : 0);
1854
+ }
1855
+ path.unshift(steps.get(node));
1856
+ }
1857
+ return ["unit", ...path];
1858
+ }
1859
+
1860
+ // The text blocks of `unit` (a unit of the page) that are its own, no unit's inside it, by their path (pathFrom,
1861
+ // as JSON): the first of each, in the page's order.
1862
+ function hostsByPath(unit) {
1863
+ const steps = new Map();
1864
+ const found = new Map();
1865
+ for (const host of unit.querySelectorAll("[contenteditable='true']")) {
1866
+ if (host.parentElement?.closest("[data-ge-unit], [data-ge-unit-kind]") !== unit) continue;
1867
+ const path = JSON.stringify(pathFrom(unit, host, steps));
1868
+ if (!found.has(path)) found.set(path, host);
1811
1869
  }
1812
- return ["unit", ...steps];
1870
+ return found;
1813
1871
  }
@@ -2,7 +2,7 @@ import { blocksText, partOwner, plainText } from "./core.mjs";
2
2
  import { reveal } from "./folded.mjs";
3
3
  import { wordDiff } from "./merge.mjs";
4
4
  import { PANEL_STYLE, panelGone, panelShown } from "./panel.mjs";
5
- import { addStyle, button, element, mount } from "./ui.mjs";
5
+ import { addStyle, element, mount, textButton } from "./ui.mjs";
6
6
  import { t } from "../../shared/i18n/index.mjs";
7
7
 
8
8
  // The conflicts the page holds back (docs/editor.md, "Conflicts"), each a card in one panel: what
@@ -66,7 +66,7 @@ function describe(entry, restorable) {
66
66
  if (entry.reason === "changed") return { text: t("这一段你和 AI 同时改了同一处。"), both: true, diff: true, actions: [[t("保留我的"), "mine", true], [t("使用最新版本"), "theirs"]] };
67
67
  if (entry.reason === "missing") return { text: t("你修改的这一块已被 AI 删除。"), actions: [...(restorable ? [[t("恢复我的"), "restore", true]] : []), [t("接受删除"), "accept"], copy] };
68
68
  if (entry.reason === "structure") return { text: t("你修改的这一处所在的组件已被 AI 改过结构,没法确定对应的位置。"), actions: [[t("使用最新版本"), "theirs"], copy] };
69
- if (packageChanged(entry)) return { text: t("这个组件的组件包刚刚变了,这处文字不再能修改,你的修改没有写进文件。"), actions: [[t("使用最新版本"), "theirs"], copy] };
69
+ if (packageChanged(entry)) return { text: t("这个自定义块刚刚变了,这处文字不再能修改,你的修改没有写进文件。"), actions: [[t("使用最新版本"), "theirs"], copy] };
70
70
  return { text: t("这一处在文件里找不到唯一对应的位置,没有保存。"), actions: [[t("使用最新版本"), "theirs"], copy] };
71
71
  }
72
72
  if (entry.kind === "unit") {
@@ -123,7 +123,7 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
123
123
  panelShown(panel);
124
124
  }
125
125
  const conflicts = [...cards.values()].every(({ entry }) => !unfit(entry));
126
- const head = element("div", { class: "ge-conflicts-head" }, element("span", { text: conflicts ? t("{0} 处冲突待处理", cards.size) : t("{0} 处内容待处理", cards.size) }), button(t("收起"), () => { panel.hidden = true; }, { "data-quiet": "" }));
126
+ const head = element("div", { class: "ge-conflicts-head" }, element("span", { text: conflicts ? t("{0} 处冲突待处理", cards.size) : t("{0} 处内容待处理", cards.size) }), textButton(t("收起"), () => { panel.hidden = true; }, { quiet: true }));
127
127
  panel.replaceChildren(head, ...[...cards.values()].map(({ node }) => node));
128
128
  }
129
129
 
@@ -139,7 +139,7 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
139
139
  const buttons = element("div", { class: "ge-actions" });
140
140
  const quiet = element("span", { class: "ge-conflict-quiet" }); // looking, not deciding: on the right
141
141
  for (const [label, action, primary] of actions) {
142
- buttons.append(button(label, async () => {
142
+ buttons.append(textButton(label, async () => {
143
143
  if (action === "copy-block") {
144
144
  const node = nodeOf(entry);
145
145
  if (!node?.isConnected || !copyBlock(node)) say(t("没能完成,请再试一次。"), "error");
@@ -166,16 +166,16 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
166
166
  if (diff && mine && theirs) {
167
167
  const legend = element("div", { class: "ge-conflict-label" }, element("span", { class: "ge-chip", "data-mine": "", text: t("你的") }), element("span", { class: "ge-chip", "data-theirs": "", text: t("AI 的") }));
168
168
  const view = element("div", { hidden: "" }, legend, element("div", { class: "ge-version" }));
169
- const toggle = button(t("查看局部差异"), () => {
169
+ const toggle = textButton(t("查看局部差异"), () => {
170
170
  view.hidden = !view.hidden;
171
171
  versions.hidden = !view.hidden;
172
172
  toggle.textContent = view.hidden ? t("查看局部差异") : t("查看两个版本");
173
173
  if (!view.hidden) view.lastElementChild.replaceChildren(...diffNodes(mine, theirs));
174
- }, { "data-quiet": "" });
174
+ }, { quiet: true });
175
175
  quiet.append(toggle);
176
176
  node.append(view);
177
177
  }
178
- if (nodeOf(entry)) quiet.append(button(t("定位"), () => locate(entry.key), { "data-quiet": "" }));
178
+ if (nodeOf(entry)) quiet.append(textButton(t("定位"), () => locate(entry.key), { quiet: true }));
179
179
  if (quiet.childElementCount > 0) buttons.append(quiet);
180
180
  node.append(buttons);
181
181
  return node;
@@ -10,6 +10,7 @@ import { gridOf, tableChange } from "../components/table/grid.mjs";
10
10
  import { HELD_TAGS, heldListType, listType } from "../components/list/index.mjs";
11
11
  import { isFlowchart, isStep } from "../components/flowchart/index.mjs";
12
12
  import { markEmpty } from "../components/placeholder.mjs";
13
+ import { carry } from "../components/align.mjs";
13
14
  import { unread } from "./tex.mjs";
14
15
  import { MENTION, MENTION_ICON, MENTION_PAGE } from "../../shared/components/mention.mjs";
15
16
  import { FLOW_BLOCKS } from "../../shared/components/index.mjs";
@@ -40,7 +41,7 @@ const WRAPPERS = new Set(["UL", "OL", "BLOCKQUOTE"]);
40
41
 
41
42
  // An inline element of the report's own, as the scanner judges it (source/blocks.mjs): its
42
43
  // attributes as the file writes them (not the page's data-ge-* markers).
43
- const isOwnAtom = (node) => ownInline(node.localName, [...node.attributes].filter(({ name }) => !MARKER.test(name)), node.textContent.trim() !== "");
44
+ const isOwnAtom =(node) => ownInline(node.localName, [...node.attributes].filter(({ name }) => !MARKER.test(name)), node.textContent.trim() !== "");
44
45
 
45
46
  // A block's atoms in `root` (its source parsed), in the order of the file, as the scanner lists
46
47
  // them (source/blocks.mjs inlineMode): the outermost formulas, links shown with their icon and inline
@@ -88,6 +89,7 @@ export function prepareBlock(element, { inner, atoms, kind = "flow", caps = [] }
88
89
  const template = element.ownerDocument.createElement("template");
89
90
  template.innerHTML = inner;
90
91
  template.content.querySelectorAll("*").forEach((node) => node.setAttribute("data-ge-src", ""));
92
+ HOSTS[kind]?.pictures?.show(template.content); // a cell's pictures shown where the server serves them
91
93
  const sourceAtoms = atomsIn(template.content, kind === "text" && caps.includes("plain"));
92
94
  const drawnAtoms = sourceAtoms.filter((atom) => atom.matches(ATOM_SELECTOR)); // a formula as the kit drew it
93
95
  // The blocks a list item holds are blocks of their own: only its text before them is rebuilt.
@@ -342,15 +344,16 @@ export function matchesSource(element, inner) {
342
344
  export function serializeInner(element) {
343
345
  const inner = HOSTS[element.getAttribute?.("data-ge-kind")]?.inner;
344
346
  if (inner) return inner(element);
345
- // A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own.
346
- const lists = element.getAttribute?.("data-ge-kind") === "cell";
347
- return serializeNodes(ownNodes(element), lists).replace(/<br>\s*$/, "");
347
+ // A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own. A
348
+ // kind of text block that holds pictures (a cell's: HOSTS `pictures`) writes them; any other none.
349
+ const kind = element.getAttribute?.("data-ge-kind");
350
+ return serializeNodes(ownNodes(element), kind === "cell", HOSTS[kind]?.pictures ?? null).replace(/<br>\s*$/, "");
348
351
  }
349
352
 
350
353
  // A list in a table cell, its items' inline content (a line break holding an empty item's caret left out).
351
- function serializeList(list) {
354
+ function serializeList(list, pictures = null) {
352
355
  const tag = list.tagName.toLowerCase();
353
- const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${serializeNodes([...item.childNodes]).replace(/<br>\s*$/, "")}</li>`);
356
+ const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${serializeNodes([...item.childNodes], false, pictures).replace(/<br>\s*$/, "")}</li>`);
354
357
  return items.length > 0 ? `<${tag}>${items.join("")}</${tag}>` : "";
355
358
  }
356
359
 
@@ -438,7 +441,7 @@ const spaced = (text) => escapeTyped(text).replace(/ *\u00a0[ \u00a0]*/g, " ");
438
441
  // shows where they were, changes/) are no content: left out, the text on either side of one is
439
442
  // written as one text, as it was before the node split it (a space either side of it the one space
440
443
  // it was, never two).
441
- function serializeNodes(nodes, lists = false) {
444
+ function serializeNodes(nodes, lists = false, pictures = null) {
442
445
  let html = "";
443
446
  let text = null; // a text node's, not written yet: the text after an editor's node joins it
444
447
  let across = false; // an editor's node stands between it and what comes next
@@ -456,17 +459,20 @@ function serializeNodes(nodes, lists = false) {
456
459
  if (text !== null) html += spaced(text);
457
460
  text = null;
458
461
  across = false;
459
- html += serializeNode(node, lists);
462
+ html += serializeNode(node, lists, pictures);
460
463
  }
461
464
  return text === null ? html : html + spaced(text);
462
465
  }
463
466
 
464
- function serializeNode(node, lists = false) {
467
+ // `pictures`: the host's (HOSTS `pictures`: write(element) its markup, "" for one of no file, null for no picture).
468
+ function serializeNode(node, lists = false, pictures = null) {
465
469
  // A text node holding nothing but a zero-width space keeps the caret after a mark the editor
466
470
  // made (markdown/, typing shortcuts); it is never content.
467
471
  if (node.nodeType === 3) return node.data === ZWSP ? "" : spaced(node.data);
468
472
  if (node.nodeType !== 1) return "";
469
- if (isList(node)) return lists ? serializeList(node) : "";
473
+ if (isList(node)) return lists ? serializeList(node, pictures) : "";
474
+ const picture = pictures?.write(node) ?? null;
475
+ if (picture !== null) return picture;
470
476
  const atom = node.getAttribute("data-ge-atom");
471
477
  if (atom !== null && ATOM_SOURCE.has(atom)) return ATOM_SOURCE.get(atom);
472
478
  // Elements from the source keep the author's tag and attributes; only elements the browser
@@ -475,7 +481,7 @@ function serializeNode(node, lists = false) {
475
481
  // and their text kept.
476
482
  const fromSource = node.hasAttribute("data-ge-src") && node.tagName in TAGS;
477
483
  const tag = fromSource ? node.tagName.toLowerCase() : TAGS[node.tagName];
478
- const inner = serializeNodes([...node.childNodes]);
484
+ const inner = serializeNodes([...node.childNodes], false, pictures);
479
485
  const ink = node.tagName === "SPAN" ? [...node.classList].filter(isInkClass) : [];
480
486
  if (ink.length > 0) return inner ? `<span class="${ink.join(" ")}">${inner}</span>` : "";
481
487
  // Small text's end part (its Tab: components/paragraph/page.mjs), at the line's right end.
@@ -572,7 +578,11 @@ export function newTail(container, type) {
572
578
  }
573
579
 
574
580
  // New blocks split off `from` are paragraphs, or list items in a list.
575
- const newBlock = (doc, tag, from) => newHost(doc, tag === "li" ? "item" : "paragraph", from);
581
+ // A paragraph split off a paragraph or a heading keeps its alignment and indent (components/align.mjs carry).
582
+ const newBlock = (doc, tag, from) => (tag === "li" ? newHost(doc, "item", from) : carry(from, newHost(doc, "paragraph", from)));
583
+
584
+ /** A new, empty paragraph, as a split makes one outside a list (the page's title's Enter: typing/ enterBody). */
585
+ export const newParagraphHost = (doc) => newBlock(doc, "p", null);
576
586
 
577
587
  // The element a text block made on the page is part of when it is not a block of its own: the
578
588
  // summary of a new disclosure, a new code block (its code's pre, its title's block), the header of a
@@ -720,10 +730,12 @@ const isFormula = (node) => node.matches(".doc-math-inline");
720
730
  * text with the format bar's marks (marks: false drops them, links: false drops links), a formula
721
731
  * of this page as its chip (another one, KaTeX's or the kit's \(…\), as the chip formula(tex) makes,
722
732
  * or else as its TeX source; `formulas` false, each as its source), a link shown with its icon as the
723
- * chip mention(mentionOf(it)) makes (else a link of its title); blocks and line breaks start new
724
- * lines, and everything else gives only its text.
733
+ * chip mention(mentionOf(it)) makes (else a link of its title), an element what picture(element)
734
+ * makes of it (a cell's picture: page/table/pictures.mjs; null nothing, undefined no picture: none
735
+ * given, no picture is one); blocks and line breaks start new lines, and everything else gives only
736
+ * its text.
725
737
  */
726
- export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null } = {}) {
738
+ export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null, picture = null } = {}) {
727
739
  const template = doc.createElement("template");
728
740
  template.innerHTML = html;
729
741
  const lines = [[]];
@@ -777,6 +789,12 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
777
789
  }
778
790
  }
779
791
  if (/^(STYLE|SCRIPT|TEMPLATE|META|TITLE|LINK|NOSCRIPT|BUTTON|SVG)$/i.test(child.tagName) || child.matches('[aria-hidden="true"], .ge-code-gutter, [data-doc-dev]')) continue;
792
+ // A picture, where `picture` takes it (undefined: no picture): what it makes of it, else nothing.
793
+ const made = picture?.(child);
794
+ if (made !== undefined) {
795
+ if (made) push(target, made);
796
+ continue;
797
+ }
780
798
  if (child.tagName === "BR") {
781
799
  if (target) push(target, doc.createTextNode(" "));
782
800
  else newLine();
@@ -794,7 +812,7 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
794
812
  continue;
795
813
  }
796
814
  walk(child, mark);
797
- if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]")) push(target, mark);
815
+ if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]") || (picture && mark.firstElementChild)) push(target, mark);
798
816
  else if (mark.textContent) push(target, doc.createTextNode(mark.textContent));
799
817
  }
800
818
  };