@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
@@ -1,6 +1,7 @@
1
1
  import { MEDIA_CLASS } from "../../../shared/components/index.mjs";
2
+ import { IN_LAYER } from "../../../shared/layers.mjs";
2
3
  import { SPAN, SPAN_SIZE, UPLOAD_LIMITS, sizeLimit } from "../../../shared/media.mjs";
3
- import { gestureLock } from "../../page/gesture.mjs";
4
+ import { cancelOnEscape, endsGesture, gestureLock } from "../../page/gesture.mjs";
4
5
  import { openCrop } from "./crop.mjs";
5
6
  import { openViewer } from "./viewer.mjs";
6
7
  import { svg } from "../icons.mjs";
@@ -25,33 +26,68 @@ import { zoomable } from "../../../shared/zoom.mjs";
25
26
  // a click (../media/page.mjs): a file (an image, 10 MB at most) goes to the report's folder, a link's picture is
26
27
  // downloaded there by the server (an export never reaches the web); either is one undo step. The
27
28
  // picture shows where the server serves the folder; what the file is stays in data-ge-src.
28
- // With the pointer on a picture, Notion's toolbar stands at its top right (对齐, 标题, 展开, 更多:
29
- // the block menu) and a handle at each side; dragging one resizes the picture (a share of the reading
30
- // width, at most the width it stands in, both sides at once while centered; snapping to the whole of
31
- // that width, a share of 1: styles/sizes.css), one undo step on
32
- // letting go (Esc puts it back). 标题 shows its caption (empty ones show only then) with the caret
33
- // in it; Enter there makes a paragraph after the image. A double-click on the picture (or on any
34
- // other picture that opens large: a table's, a card's), or 展开, shows it alone over the page (a
35
- // click or Esc closes it). The block menu has 替换图片, 标题, 对齐 ›
36
- // and 恢复原始大小 (menuItems). A picture pasted (an image file on the clipboard) becomes an image
37
- // below the block the caret is in (`acceptsFile`, `fromFiles`).
29
+ // A click on a picture selects its image (`click`); while it is the one block selected (however it
30
+ // was: the click, its handle, the keys, a picture just put in) a short gray L stands at each of the
31
+ // picture's corners (the user's, 2026-11-09, feishu-lite's four corner handles drawn as Ls) and the
32
+ // toolbar at its top right, moved in clear of them (对齐, 标题, 展开, 更多: the block menu), both
33
+ // placed in the block tools' frames (`blockTools` follow, ../../page/blocks.mjs). Dragging a corner
34
+ // resizes the picture, its shape kept (feishu-lite's: by how far the pointer moves across, the left
35
+ // corners the other way, twice while centered), a share of the reading width, at most the width it
36
+ // stands in, snapping to the whole of that width (a share of 1: styles/sizes.css), one undo step on
37
+ // letting go (Esc puts it back). An L is a gray line with a thin edge of the page's color around it, so it
38
+ // reads on a black picture in light mode and a white one in dark mode (the old side bars, a gray of
39
+ // the page's ink, did not). 标题 shows its caption (empty ones show only then) with the caret in it;
40
+ // Enter there makes a paragraph after the image. A double-click on the picture (or on any other picture
41
+ // that opens large: a table's, a card's), or 展开, shows it alone over the page (a click or Esc closes
42
+ // it). The block menu has what the toolbar has not: 同步宽度到全局 first (every other image of the
43
+ // page as wide as this one, one undo step), 替换图片, 恢复原始大小, 裁剪图片 and 下载 (menuItems). A
44
+ // picture pasted (an image file on the clipboard) becomes an image below the block the caret is in
45
+ // (`acceptsFile`, `fromFiles`).
38
46
 
39
47
  const MAX_BYTES = UPLOAD_LIMITS.image;
40
48
  // 裁剪图片's row: a crop mark, drawn as the menus' other icons are.
41
49
  const CROP_ICON = svg('<path d="M6 2.75V13a1 1 0 0 0 1 1h10.25"/><path d="M2.75 6H13a1 1 0 0 1 1 1v10.25"/>');
50
+ // 同步宽度到全局's row: a width (an arrow both ways) over two pictures as wide as each other.
51
+ const SYNC_ICON = svg('<path d="M4 4.25h12M6 2.5 4 4.25 6 6M14 2.5l2 1.75L14 6"/><rect x="3.75" y="8.25" width="12.5" height="3.5" rx="1"/><rect x="3.75" y="14" width="12.5" height="3.5" rx="1"/>');
52
+
53
+ // A corner's grip: a box of GRIP px, the picture's corner AT px in from the box's outer corner, an L
54
+ // whose lines run ARM px along the picture's two edges from its corner, centered a little outside them.
55
+ const GRIP = 24;
56
+ const AT = 8;
57
+ const ARM = 12;
58
+ const L_PATH = `M${AT - 1} ${AT - 1 + ARM}V${AT - 1}h${ARM}`; // drawn for the top left; the others mirror it
59
+ // The corners: which way each mirrors the top left's L, and its cursor.
60
+ const CORNERS = {
61
+ nw: { x: "left", y: "top", cursor: "nwse" },
62
+ ne: { x: "right", y: "top", cursor: "nesw" },
63
+ sw: { x: "left", y: "bottom", cursor: "nesw" },
64
+ se: { x: "right", y: "bottom", cursor: "nwse" },
65
+ };
66
+ const INSET = 16; // how far the toolbar stands from the picture's top and from its right edge, the same each way (the user's, 2026-11-09: the top as far as the right): clear of the L's arms and of the top right grip's box, which reaches GRIP - AT = 16px in
67
+ const GAP = 6; // how far it stands above the picture when it does not fit in
42
68
 
43
69
  const STYLE = `
44
70
  /* The placeholder of an image with no picture yet (Notion's 605415): every empty block's, served with the page (../placeholder.mjs). */
45
71
  [data-ge-editing] .doc-image > img { cursor: default; }
46
- /* The handles (Notion's 681598); the toolbar is the media blocks' (../media/toolbar.mjs). */
47
- .ge-layer > .ge-image-handle { pointer-events: auto; }
72
+ /* The toolbar is the media blocks' (../media/toolbar.mjs). */
48
73
  .ge-image-align { display: flex; align-items: center; padding: 2px; border-radius: 6px; background: var(--c-popBac); box-shadow: var(--c-shaOutLg); }
49
74
  .ge-image-align-sep { width: 1px; height: 16px; margin: 0 2px; background: var(--ca-borSecTra); }
50
- .ge-image-handle { display: flex; align-items: center; justify-content: center; width: 15px; cursor: col-resize; }
51
- /* A plain gray bar, no border (the embed's corners the same). */
52
- .ge-image-handle::after { content: ""; width: 4px; height: 40px; max-height: 50%; border-radius: 4px; background: light-dark(rgb(55 53 47 / 35%), rgb(255 255 255 / 38%)); transition: background 120ms ease; }
53
- .ge-image-handle:hover::after, html[data-ge-image-resize] .ge-image-handle::after { background: light-dark(rgb(55 53 47 / 60%), rgb(255 255 255 / 62%)); }
54
- ${gestureLock("data-ge-image-resize", { cursor: "col-resize" })}
75
+ /* The corners' grips: a short gray L, round at its ends, no edge round it (the user, 2026-11-09): a
76
+ neutral gray that reads on a black picture and a white one alike (4.5 to 1 on each), with a soft
77
+ shadow for a picture of its own gray; darker (lighter) under the pointer and while it is dragged.
78
+ No border, no fill. */
79
+ .ge-layer > .ge-image-grip { pointer-events: auto; z-index: ${IN_LAYER.button}; } /* over the image's fill: it is selected */
80
+ .ge-image-grip { width: ${GRIP}px; height: ${GRIP}px; --ge-grip-line: #767676; filter: drop-shadow(0 0 2px rgb(0 0 0 / 42%)); }
81
+ .ge-image-grip[data-cursor="nwse"] { cursor: nwse-resize; }
82
+ .ge-image-grip[data-cursor="nesw"] { cursor: nesw-resize; }
83
+ .ge-image-grip svg { display: block; width: ${GRIP}px; height: ${GRIP}px; fill: none; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
84
+ .ge-image-grip[data-corner="ne"] svg { transform: scaleX(-1); }
85
+ .ge-image-grip[data-corner="sw"] svg { transform: scaleY(-1); }
86
+ .ge-image-grip[data-corner="se"] svg { transform: scale(-1); }
87
+ .ge-image-grip-line { stroke: var(--ge-grip-line); stroke-width: 3; transition: stroke 120ms ease; }
88
+ .ge-image-grip:hover, .ge-image-grip[data-on] { --ge-grip-line: light-dark(#4f4d49, #d3d1cb); }
89
+ ${gestureLock('data-ge-image-resize="nwse"', { cursor: "nwse-resize", select: true })}
90
+ ${gestureLock('data-ge-image-resize="nesw"', { cursor: "nesw-resize", select: true })}
55
91
  `;
56
92
 
57
93
  const ALIGNS = [["left", t("左对齐"), "alignLeft"], ["center", t("居中对齐"), "alignCenter"], ["right", t("右对齐"), "alignRight"]];
@@ -156,103 +192,110 @@ export function create({ pages, assetUrl }) {
156
192
  if (replacing && !quiet) say(t("已替换图片 · Ctrl+Z 撤销"));
157
193
  }
158
194
 
159
- // ---------- the toolbar and the handles ----------
195
+ // ---------- the toolbar and the corners' grips ----------
160
196
 
161
- let hovered = null; // the image the pointer is on
162
- let resizing = null; // { figure, side, x, start, room, before, centered }
197
+ let shown = null; // the image whose tools show: the one block selected
198
+ let resizing = null; // { figure, corner, x, start, room, page, before, centered }
199
+ let aligning = null; // the 对齐 popover open, a press in which keeps the image selected
163
200
  const bar = mediaBar(t("图片操作"));
164
- const tool = (label, tip, icon, run) => toolButton({ label, tip, icon, run, alive: () => hovered?.isConnected });
201
+ const tool = (label, tip, icon, run) => toolButton({ label, tip, icon, run, alive: () => shown?.isConnected });
165
202
  const tools = {
166
- align: tool(t("对齐方式"), t("设置区块对齐"), () => TOOL_ICONS[{ left: "alignLeft", center: "alignCenter", right: "alignRight" }[sideOf(hovered)]], (button) => alignPopup(hovered, button)),
167
- caption: tool(t("标题@caption"), t("添加标题"), TOOL_ICONS.caption, () => media.caption(hovered)),
168
- expand: tool(t("展开"), t("全屏查看"), TOOL_ICONS.expand, () => view(hovered)),
203
+ align: tool(t("对齐方式"), t("设置区块对齐"), () => TOOL_ICONS[{ left: "alignLeft", center: "alignCenter", right: "alignRight" }[sideOf(shown)]], (button) => alignPopup(shown, button)),
204
+ caption: tool(t("标题@caption"), t("添加标题"), TOOL_ICONS.caption, () => media.caption(shown)),
205
+ expand: tool(t("展开"), t("全屏查看"), TOOL_ICONS.expand, () => view(shown)),
169
206
  more: tool(t("更多"), t("打开区块操作菜单"), TOOL_ICONS.more, (button) => {
170
207
  const rect = button.getBoundingClientRect();
171
- openMenu(hovered, { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom });
208
+ openMenu(shown, { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom });
172
209
  }),
173
210
  };
174
211
  bar.append(...Object.values(tools));
175
- const handles = { left: element("div", { class: "ge-image-handle", "data-side": "left" }), right: element("div", { class: "ge-image-handle", "data-side": "right" }) };
212
+ const lines = `<svg viewBox="0 0 ${GRIP} ${GRIP}" aria-hidden="true"><path class="ge-image-grip-line" d="${L_PATH}"/></svg>`;
213
+ const grips = Object.fromEntries(Object.entries(CORNERS).map(([corner, { cursor }]) => {
214
+ const grip = element("div", { class: "ge-image-grip", "data-corner": corner, "data-cursor": cursor, "aria-hidden": "true" });
215
+ grip.innerHTML = lines;
216
+ return [corner, grip];
217
+ }));
218
+ const nodes = () => [bar, ...Object.values(grips)];
176
219
 
177
- // The toolbar and the handles over the picture of `figure`, or away.
220
+ // The toolbar and the grips over the picture of `figure`, or away. The toolbar stands at the
221
+ // picture's top right, INSET from its top and from its right edge; on a picture too narrow to hold
222
+ // it with that room each side, above the picture, its right edge on the picture's.
178
223
  function show(figure) {
179
- hovered = figure;
224
+ shown = figure;
180
225
  const img = figure && imageOf(figure);
181
226
  const shape = img && shapeOf(img);
182
227
  if (!shape) {
183
- hovered = null;
184
- for (const node of [bar, handles.left, handles.right]) node.remove();
228
+ shown = null;
229
+ for (const node of nodes()) node.remove();
185
230
  return;
186
231
  }
187
- const alive = () => hovered === figure && figure.isConnected;
188
- for (const node of [bar, handles.left, handles.right]) own(node, "image tools", { owner: figure, alive });
232
+ const alive = () => shown === figure && figure.isConnected;
233
+ for (const node of nodes()) own(node, "image tools", { owner: figure, alive });
189
234
  for (const button of Object.values(tools)) button.draw();
190
- const { rect } = shape;
235
+ const { rect, layer } = shape;
191
236
  tools.align.style.display = figure.style.getPropertyValue(SPAN).trim() === "1" ? "none" : ""; // a picture as wide as it can be has no side
192
- if (bar.parentNode !== shape.layer) shape.layer.append(bar); // in the page before its width is measured
193
- drawAt(bar, shape.layer, rect.right - 4 - bar.offsetWidth, rect.top + 4);
194
- for (const [side, handle] of Object.entries(handles)) {
195
- handle.style.height = `${Math.round(rect.bottom - rect.top)}px`;
196
- drawAt(handle, shape.layer, side === "left" ? rect.left : rect.right - 15, rect.top);
237
+ if (bar.parentNode !== layer) layer.append(bar); // in the page before its width is measured
238
+ const fits = rect.right - rect.left >= bar.offsetWidth + 2 * INSET;
239
+ drawAt(bar, layer, fits ? rect.right - INSET - bar.offsetWidth : rect.right - bar.offsetWidth, fits ? rect.top + INSET : rect.top - AT - GAP - bar.offsetHeight);
240
+ for (const [corner, grip] of Object.entries(grips)) {
241
+ const { x, y } = CORNERS[corner];
242
+ drawAt(grip, layer, x === "left" ? rect.left - AT : rect.right - GRIP + AT, y === "top" ? rect.top - AT : rect.bottom - GRIP + AT);
197
243
  }
198
244
  }
199
245
 
200
- document.addEventListener("mousemove", (event) => {
201
- if (resizing || [bar, handles.left, handles.right].some((node) => node.contains(event.target))) return;
202
- const figure = active() && !keptByComponent(event.target) ? event.target.closest?.("main figure.doc-image") ?? null : null;
203
- if (figure !== hovered) show(figure && imageOf(figure) ? figure : null);
204
- }, { passive: true, capture: true });
205
- addEventListener("scroll", () => { if (hovered && !resizing) show(hovered); }, { passive: true, capture: true });
206
- document.documentElement.addEventListener("mouseleave", () => { if (!resizing) show(null); });
207
- // The hovered picture taken off the page by an undo, a redo or a commit: the tools go at once, not at the next pointer move.
208
- onSettle(() => { if (hovered && !hovered.isConnected && !resizing) show(null); });
246
+ // The image the tools stand for, from the blocks selected (the block tools' frame): one image with
247
+ // its picture, not one a component keeps; while a corner is dragged, the one dragged.
248
+ function follow(picked) {
249
+ if (resizing) return show(resizing.figure);
250
+ const [one] = picked.length === 1 ? picked : [];
251
+ const figure = one?.matches?.("figure.doc-image") && imageOf(one) && active() && !keptByComponent(one) ? one : null;
252
+ if (figure || shown) show(figure);
253
+ }
254
+ // The picture taken off the page by an undo, a redo or a commit: the tools go at once, not at the next frame.
255
+ onSettle(() => { if (shown && !shown.isConnected && !resizing) show(null); });
209
256
 
210
- // Dragging a handle: the picture's width as a share of the reading width, at most the width it
211
- // stands in (1 when it reaches that), live; one undo step on letting go.
212
- for (const [side, handle] of Object.entries(handles)) {
213
- handle.addEventListener("pointerdown", (event) => {
214
- if (event.button !== 0 || !hovered) return;
257
+ // Dragging a corner: the picture's width as a share of the reading width, at most the width it
258
+ // stands in (1 when it reaches that), live, its height following (its shape kept); feishu-lite's
259
+ // rule (resize.ts): by how far the pointer moves across, the left corners the other way, twice while
260
+ // centered (it grows on both sides). One undo step on letting go; Esc, or the press cancelled, puts it back.
261
+ for (const [corner, grip] of Object.entries(grips)) {
262
+ grip.addEventListener("pointerdown", (event) => {
263
+ if (event.button !== 0 || !shown || resizing) return;
215
264
  event.preventDefault();
216
- const figure = hovered;
217
- const room = columnRoom(figure);
218
- resizing = { figure, side, x: event.clientX, start: imageOf(figure).getBoundingClientRect().width, room, page: readingRoom(figure), before: figure.style.getPropertyValue(SPAN), centered: sideOf(figure) === "center" };
219
- handle.setPointerCapture?.(event.pointerId);
220
- document.documentElement.setAttribute("data-ge-image-resize", "");
265
+ const figure = shown;
266
+ resizing = { figure, corner, x: event.clientX, start: imageOf(figure).getBoundingClientRect().width, room: columnRoom(figure), page: readingRoom(figure), before: figure.style.getPropertyValue(SPAN), centered: sideOf(figure) === "center" };
267
+ grip.setPointerCapture?.(event.pointerId);
268
+ grip.setAttribute("data-on", "");
269
+ document.documentElement.setAttribute("data-ge-image-resize", CORNERS[corner].cursor);
221
270
  });
222
- handle.addEventListener("pointermove", (event) => {
223
- if (!resizing) return;
271
+ grip.addEventListener("pointermove", (event) => {
272
+ if (resizing?.corner !== corner) return;
224
273
  const { figure, x, start, room, page, centered } = resizing;
225
- const delta = (event.clientX - x) * (side === "right" ? 1 : -1) * (centered ? 2 : 1);
274
+ const delta = (event.clientX - x) * (CORNERS[corner].x === "right" ? 1 : -1) * (centered ? 2 : 1);
226
275
  const width = Math.max(Math.min(room, page * SPAN_SIZE.min), Math.min(room, start + delta));
227
276
  figure.style.setProperty(SPAN, room - width <= SNAP ? "1" : String(Math.round((width / page) * 1000) / 1000));
228
277
  show(figure);
229
278
  });
230
- const end = (keep) => {
231
- if (!resizing) return;
232
- const { figure, before } = resizing;
233
- resizing = null;
234
- document.documentElement.removeAttribute("data-ge-image-resize");
235
- const after = figure.style.getPropertyValue(SPAN);
236
- if (before) figure.style.setProperty(SPAN, before);
237
- else figure.style.removeProperty(SPAN);
238
- if (keep && after !== before && look(figure, { sizes: { [SPAN]: after || null } })) say(t("图片宽度 {0}% · Ctrl+Z 撤销", Math.round(Number(after) * 100)));
239
- show(figure);
240
- };
241
- handle.addEventListener("pointerup", () => end(true));
242
- handle.addEventListener("pointercancel", () => end(false));
243
- handle.addEventListener("lostpointercapture", () => end(true));
279
+ grip.addEventListener("pointerup", () => endResize(true));
280
+ grip.addEventListener("pointercancel", () => endResize(false));
281
+ grip.addEventListener("lostpointercapture", () => endResize(true));
244
282
  }
245
- document.addEventListener("keydown", (event) => {
246
- if (event.key !== "Escape" || !resizing) return;
247
- event.preventDefault();
248
- event.stopPropagation();
283
+ // The drag over: the width it had back on the page, then (`keep`) the one dragged to as one undo step.
284
+ function endResize(keep) {
285
+ if (!resizing) return;
249
286
  const { figure, before } = resizing;
250
287
  resizing = null;
288
+ for (const grip of Object.values(grips)) grip.removeAttribute("data-on");
251
289
  document.documentElement.removeAttribute("data-ge-image-resize");
290
+ const after = figure.style.getPropertyValue(SPAN);
252
291
  if (before) figure.style.setProperty(SPAN, before);
253
292
  else figure.style.removeProperty(SPAN);
293
+ if (keep && after !== before && look(figure, { sizes: { [SPAN]: after || null } })) say(t("图片宽度 {0}% · Ctrl+Z 撤销", Math.round(Number(after) * 100)));
254
294
  show(figure);
255
- }, true);
295
+ }
296
+ // Esc gives the drag up and goes no further (the image stays selected); so do the window losing the focus and the page hidden.
297
+ cancelOnEscape({ active: () => Boolean(resizing), cancel: () => endResize(false) });
298
+ endsGesture({ active: () => Boolean(resizing), cancel: () => endResize(false), owns: () => false });
256
299
 
257
300
  // 对齐: Notion's small popover of the three sides, under its button.
258
301
  function alignPopup(figure, button) {
@@ -270,11 +313,29 @@ export function create({ pages, assetUrl }) {
270
313
  });
271
314
  const rect = button.getBoundingClientRect();
272
315
  const popup = openPopup({ origin: { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom, width: rect.width, height: rect.height }, placement: "bottom", align: "center", gap: 8, content: el });
316
+ aligning = el;
273
317
  }
274
318
 
275
319
  function align(figure, side) {
276
320
  if (look(figure, sideLook(side))) say(t("已{0} · Ctrl+Z 撤销", ALIGNS.find(([each]) => each === side)[1]));
277
- if (hovered === figure) show(figure);
321
+ if (shown === figure) show(figure);
322
+ }
323
+
324
+ // 同步宽度到全局: every other image of the page (with its picture, not one a component keeps) as wide
325
+ // as this one shows, one undo step (undo selects this one again), written as one change. The width is
326
+ // what the picture shows at as a share of the reading width (styles/sizes.css), so a picture with no
327
+ // width of its own, or held narrower by its column, passes on the width it is seen at; the whole
328
+ // reading width is 1. This one is left as it is.
329
+ const others = (figure) => [...document.querySelectorAll("main figure.doc-image")].filter((each) => each !== figure && imageOf(each) && each.matches("[data-ge-unit], [data-ge-new]") && !keptByComponent(each));
330
+ function syncWidths(figure) {
331
+ const img = imageOf(figure);
332
+ if (!img?.isConnected) return;
333
+ const page = readingRoom(figure);
334
+ const width = img.getBoundingClientRect().width;
335
+ const share = page - width <= SNAP ? "1" : String(Math.max(SPAN_SIZE.min, Math.round((width / page) * 1000) / 1000));
336
+ const targets = others(figure).filter((each) => each.style.getPropertyValue(SPAN).trim() !== share);
337
+ if (targets.length === 0 || !look(targets, { sizes: { [SPAN]: share } }, figure)) return say(t("页面上的图片已经都是这个宽度。"));
338
+ say(t("已将 {0} 张图片设为同一宽度 · Ctrl+Z 撤销", targets.length));
278
339
  }
279
340
 
280
341
  // ---------- the viewer ----------
@@ -339,18 +400,18 @@ export function create({ pages, assetUrl }) {
339
400
  }, true);
340
401
  // ---------- the block menu, pasting ----------
341
402
 
342
- /** An image's rows in its block menu. */
403
+ /**
404
+ * An image's rows in its block menu: what its toolbar has not (标题, 对齐 and 展开 are there, and the
405
+ * toolbar shows whenever the image is the one block selected, as it is while its menu is open):
406
+ * 同步宽度到全局 first (where the page has another picture), 替换图片, 恢复原始大小, 裁剪图片, 下载.
407
+ */
343
408
  function menuItems(target) {
344
409
  if (!target?.matches?.("figure.doc-image") || !imageOf(target)) return [];
345
- const rows = [
346
- { label: t("替换图片"), icon: ICONS.reset, run: () => setTimeout(() => pick(target)) },
347
- { label: t("标题@caption"), icon: TOOL_ICONS.caption, run: () => setTimeout(() => media.caption(target)) },
348
- { label: t("对齐"), icon: TOOL_ICONS[{ left: "alignLeft", center: "alignCenter", right: "alignRight" }[sideOf(target)]], width: 180,
349
- submenu: () => ALIGNS.map(([side, label, name]) => ({ label, icon: TOOL_ICONS[name], current: side === sideOf(target), run: () => align(target, side) })) },
350
- ];
410
+ const rows = [];
411
+ if (others(target).length > 0) rows.push({ label: t("同步宽度到全局"), icon: SYNC_ICON, run: () => syncWidths(target) });
412
+ rows.push({ label: t("替换图片"), icon: ICONS.reset, run: () => setTimeout(() => pick(target)) });
351
413
  if (target.style.getPropertyValue(SPAN)) rows.push({ label: t("恢复原始大小"), icon: ICONS.reset, run: () => { if (look(target, { sizes: { [SPAN]: null } })) say(t("已恢复原始大小 · Ctrl+Z 撤销")); } });
352
414
  rows.push({ label: t("裁剪图片"), icon: CROP_ICON, run: () => setTimeout(() => crop(target)) });
353
- rows.push({ label: t("全屏查看"), icon: TOOL_ICONS.expand, run: () => view(target) });
354
415
  rows.push({ label: t("下载"), icon: TOOL_ICONS.download, run: () => download(imageOf(target)) });
355
416
  return rows;
356
417
  }
@@ -380,6 +441,27 @@ export function create({ pages, assetUrl }) {
380
441
  pick(figure);
381
442
  return true;
382
443
  },
383
- get nodes() { return [bar, handles.left, handles.right].filter((node) => node.isConnected); },
444
+ /**
445
+ * A click on an image's picture selects the image (its toolbar and grips then show: `blockTools`),
446
+ * the second of a double-click too (its press let go of it; the double-click itself is the
447
+ * viewer's). Returns whether it took the click.
448
+ */
449
+ click(event) {
450
+ const img = event.target;
451
+ const figure = img?.localName === "img" ? img.parentElement : null;
452
+ if (event.button !== 0 || !figure?.matches("main figure.doc-image") || imageOf(figure) !== img || !active() || keptByComponent(figure)) return false;
453
+ media.select(figure);
454
+ return true;
455
+ },
456
+ /** The toolbar and the grips as a part of the block tools: placed in their frames by the blocks selected (`follow`), a press on them keeping the selection (`holds`). */
457
+ blockTools: () => [{
458
+ follow,
459
+ holds: (node) => nodes().some((each) => each.contains(node)) || Boolean(aligning?.isConnected && aligning.contains(node)),
460
+ hide() {
461
+ endResize(false);
462
+ show(null);
463
+ },
464
+ }],
465
+ get nodes() { return nodes().filter((node) => node.isConnected); },
384
466
  };
385
467
  }
@@ -1,7 +1,8 @@
1
1
  import { escapeText } from "../../../shared/escape.mjs";
2
2
  import { ATTRIBUTE, FALLBACK, INSTANCE, PACKAGE_ID, SLOT, SLOT_PART, componentAt, heldBy, instanceMatch, refusedInCopy, slotsIn } from "../../../shared/components/installed.mjs";
3
3
  import { wordsIn } from "../../page/clipboard/own/writers.mjs";
4
- import { svg } from "../icons.mjs";
4
+ import { BLOCK_ICON } from "../icons.mjs";
5
+ import { CHART_KEY, VIEW_FROM } from "../../../shared/table-chart.mjs";
5
6
  import { t } from "../../../shared/i18n/index.mjs";
6
7
 
7
8
  // 组件: the one kind of block every component package is to the engines, the host of a package's
@@ -86,8 +87,8 @@ function elementOf(html, doc = globalThis.document) {
86
87
  // The most a description carries of one piece of markup (blocks.mjs `string`).
87
88
  const CARRIED = 1e6;
88
89
 
89
- // A block of four corners and a notch, a piece that fits something else.
90
- export const ICON = svg('<path d="M4 6.75A1.75 1.75 0 0 1 5.75 5H8a2 2 0 0 1 4 0h2.25A1.75 1.75 0 0 1 16 6.75V9a2 2 0 0 1 0 4v1.25A1.75 1.75 0 0 1 14.25 16H5.75A1.75 1.75 0 0 1 4 14.25z"/>');
90
+ // The mark of any custom block with no picture of its own: a card of text (icons.mjs).
91
+ export const ICON = BLOCK_ICON;
91
92
 
92
93
  export default {
93
94
  name: "installed",
@@ -102,12 +103,16 @@ export default {
102
103
  sealed: INSTANCE,
103
104
  units: {
104
105
  installed: {
105
- name: t("组件@block"),
106
+ name: t("自定义块@block"),
106
107
  icon: ICON,
107
108
  match: [{ rank: 0, tag: ["div", "section"], attr: ATTRIBUTE }],
108
109
  boxed: true,
109
110
  chapter: true,
110
111
  copy: "itself",
112
+ // The key of the table a view reads (VIEW_FROM), which a view's 设置 changes on an instance the file
113
+ // has (the commit writes it in its start tag, as a table's CHART_SOURCE); one made on the page says it
114
+ // by `extras` below.
115
+ attributes: { [VIEW_FROM]: (value) => CHART_KEY.test(value) },
111
116
  },
112
117
  },
113
118
  boxes: {
@@ -130,11 +135,17 @@ export default {
130
135
  // Its markup is the file's, from its template or what was carried (`data-ge-copy`, its slots
131
136
  // empty); what its slots hold goes apart, blocks made on the page as any (`children`, each in the
132
137
  // slot `boxKeys` names), so that the commit's answer names each, as for a block made in a column.
138
+ // A view of a table (a menu made it of its template, bound to the table above it, or a paste carried one) says the key
139
+ // of the table it reads as the page has it (`from`, its VIEW_FROM, which a paste may have given it
140
+ // anew: clipboard/own/chart-keys.mjs), which the file writes on it (file.mjs source); one carried
141
+ // with none on it says null (a view pasted into another document, its key taken off there), so
142
+ // that the file has none either, whatever its markup carried.
133
143
  extras(element, { nodes }) {
134
144
  const held = slotsIn(element).filter((slot) => slot.hasAttribute("data-ge-box")).flatMap((slot) => (nodes?.(slot) ?? []).map((node) => [slot.getAttribute(SLOT), node]));
135
145
  return {
136
146
  package: element.getAttribute(ATTRIBUTE),
137
147
  ...(element.hasAttribute("data-ge-copy") ? { html: element.getAttribute("data-ge-copy") } : {}),
148
+ ...(element.hasAttribute(VIEW_FROM) ? { from: element.getAttribute(VIEW_FROM) } : element.hasAttribute("data-ge-copy") ? { from: null } : {}),
138
149
  ...(slotsIn(element).length > 0 ? { children: held.map(([, node]) => node), boxKeys: held.map(([key]) => key) } : {}),
139
150
  };
140
151
  },
@@ -1,6 +1,9 @@
1
1
  import { ARRIVING, ATTRIBUTE, DATA_MOST, DATA_SHOWN, FALLBACK, FIELDS, INSTANCE, NO_TEXT, PACKAGE_MARK, SCHEMA, SLOT, SLOT_PART, componentAt, fitsDataScript, heldBy, instanceMatch, isDataScript, noDataPart, refusedInData, schemaAhead, slotsIn } from "../../../shared/components/installed.mjs";
2
2
  import { referencedIds } from "../../../shared/copy-ids.mjs";
3
+ import { jsonIn } from "../../../shared/json-layout.mjs";
4
+ import { CHART_SOURCE, VIEW_FROM } from "../../../shared/table-chart.mjs";
3
5
  import { language, t } from "../../../shared/i18n/index.mjs";
6
+ import { searchWords } from "../../../shared/pinyin.mjs";
4
7
  import { caretAt, editable } from "../../page/caret.mjs";
5
8
  import { htmlToBlocks, parseClipboardHTML } from "../../page/clipboard/import/html.mjs";
6
9
  import { toBlocks } from "../../page/clipboard/import/to-blocks.mjs";
@@ -8,8 +11,11 @@ import { build as buildBlock } from "../../page/clipboard/own/blocks.mjs";
8
11
  import { copyOf } from "../../page/clipboard/own/describe.mjs";
9
12
  import { ownIds } from "../../page/clipboard/own/ids.mjs";
10
13
  import { firstHost, newHost, unitElement } from "../../page/core.mjs";
14
+ import { openSettings } from "../../page/board/settings.mjs";
15
+ import { bindAbove, keyOf, tableChoices } from "../../page/table/binding.mjs";
11
16
  import { addStyle } from "../../page/ui.mjs";
12
17
  import { accepts, capsFor } from "../registry.mjs";
18
+ import { SETTINGS_ICON } from "../icons.mjs";
13
19
  import { ICON } from "./index.mjs";
14
20
  import { followCatalog, followSettings, isBehind } from "./served.mjs";
15
21
 
@@ -301,9 +307,6 @@ function keepSlots(node, from, next) {
301
307
  console.error(`[DocKit] ${instance.getAttribute(ATTRIBUTE)}: a slot left its instance: it is put back, its blocks the document's`);
302
308
  }
303
309
 
304
- // A package's picture where a menu shows a mark.
305
- const pictureOf = ({ preview }) => (preview ? `<img src="${preview}" alt="">` : null);
306
-
307
310
  // A slot the file has empty (an Agent's): what a disclosure's content says (details/page.mjs), a click
308
311
  // on it making a paragraph there (insert/ clickBlank).
309
312
  const SLOT_EMPTY = `
@@ -312,7 +315,7 @@ const SLOT_EMPTY = `
312
315
  `;
313
316
 
314
317
  /** An instance on the page: what its events are, what Enter does in its words, and what the menus make of its package. */
315
- export function create({ map, newParagraph, active, sourceStep, struct }) {
318
+ export function create({ map, newParagraph, active, sourceStep, struct, look }) {
316
319
  held = map;
317
320
  addStyle(SLOT_EMPTY);
318
321
  // Whether a key in an instance, on no field of it, is undo or redo: a button or what a component draws
@@ -360,7 +363,73 @@ export function create({ map, newParagraph, active, sourceStep, struct }) {
360
363
  sourceStep(changed.map(([part, was]) => [part, was]));
361
364
  return true;
362
365
  }
366
+ // ---------- 设置 ----------
367
+ // An instance's 设置 (the handle's menu; the board's form of a custom page, page/board/settings.mjs, whose
368
+ // menu cards a choice is made in): the fields its manifest's `settings` lists, kept in its part
369
+ // `settings` (a script of JSON, what `context.editJSON` writes: one undo step, a change laid out as the
370
+ // part was), and for a view of a table (needs "tables") which table it reads, kept in its VIEW_FROM and on
371
+ // the table's frame (binding.mjs), one undo step too. Null where it has none: no field, or no part to keep
372
+ // them in, and no table to choose; an instance that does not run or that is past its manifest has no form.
373
+ const TABLE_FIELD = "__table"; // the form's field for the table: no setting of the manifest's
374
+ const settingsIn = (part) => {
375
+ try {
376
+ const value = JSON.parse(part?.textContent?.trim() || "{}");
377
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
378
+ } catch {
379
+ return {};
380
+ }
381
+ };
382
+ function formOf(instance) {
383
+ if (!active() || instance.hasAttribute(FALLBACK) || ahead(instance)) return null;
384
+ const entry = entryOf(instance.getAttribute(ATTRIBUTE));
385
+ if (!entry) return null;
386
+ const part = dataParts(instance).find(([name]) => name === "settings")?.[1] ?? null;
387
+ const fields = part ? entry.settings ?? [] : [];
388
+ const reads = entry.needs?.includes("tables") ?? false;
389
+ return fields.length > 0 || reads ? { entry, part, fields, reads } : null;
390
+ }
391
+ // The values of the manifest's fields put in the part, one `source` step, as editJSON writes them.
392
+ function writeSettings(instance, part, values) {
393
+ write({
394
+ parts: [{ name: "settings", part }],
395
+ run({ settings: draft }) {
396
+ const text = draft.textContent ?? "";
397
+ const current = settingsIn(draft);
398
+ draft.textContent = text.trim() ? jsonIn(text, { ...current, ...values }) : JSON.stringify({ ...current, ...values });
399
+ return true;
400
+ },
401
+ }, instance);
402
+ }
403
+ // The table a view reads put on it, as one undo step: its key on both, a new one on a table with none.
404
+ function readTable(instance, frame) {
405
+ const key = frame ? keyOf(frame) : null;
406
+ const keyed = frame && frame.getAttribute(CHART_SOURCE) !== key;
407
+ look([instance, ...(keyed ? [frame] : [])], [{ attributes: { [VIEW_FROM]: key } }, ...(keyed ? [{ attributes: { [CHART_SOURCE]: key } }] : [])]);
408
+ }
409
+ function openFormOf(instance) {
410
+ const form = formOf(instance);
411
+ if (!form) return;
412
+ const choices = form.reads ? tableChoices() : [];
413
+ const bound = form.reads ? choices.findIndex(({ frame }) => frame.getAttribute(CHART_SOURCE) === instance.getAttribute(VIEW_FROM) && instance.hasAttribute(VIEW_FROM)) : -1;
414
+ const fields = [
415
+ ...(form.reads ? [{ key: TABLE_FIELD, label: t("表格"), type: "choice", choices: [["", t("(未选择)")], ...choices.map(({ label }, at) => [String(at), label])] }] : []),
416
+ ...form.fields,
417
+ ];
418
+ const box = instance.getBoundingClientRect();
419
+ const top = Math.min(Math.max(box.top, 8), innerHeight - 40);
420
+ openSettings({
421
+ fields, name: form.entry.name,
422
+ values: { ...(form.part ? settingsIn(form.part) : {}), ...(form.reads ? { [TABLE_FIELD]: bound >= 0 ? String(bound) : "" } : {}) },
423
+ anchor: { left: box.left, right: box.right, top, bottom: top + 8, width: box.width, height: 8 },
424
+ save({ [TABLE_FIELD]: table, ...values }) {
425
+ if (table !== undefined) readTable(instance, table === "" ? null : choices[Number(table)]?.frame ?? null);
426
+ if (Object.keys(values).length > 0 && form.part?.isConnected) writeSettings(instance, form.part, values);
427
+ },
428
+ });
429
+ }
363
430
  return {
431
+ /** An instance's own rows in its block menu: 设置…, where it has a form (formOf). */
432
+ menuItems: (unit) => (formOf(unit) ? [{ label: t("设置…"), icon: SETTINGS_ICON, run: () => openFormOf(unit) }] : []),
364
433
  keepsKeys: (node, event) => componentAt(node) && !editable(node) && !pageUndo(node, event),
365
434
  undoHere: (node) => componentAt(node) && !editable(node),
366
435
  keys: {
@@ -398,22 +467,34 @@ export function create({ map, newParagraph, active, sourceStep, struct }) {
398
467
  // insertTypedBlock: the caret in its first text part; with none, in an empty paragraph after it, as
399
468
  // for a divider). Its slots are boxes made on the page at once, what the template has in each made
400
469
  // blocks of the page's (an empty paragraph where it has none), so that the reader writes in one
401
- // before the instance is saved, and Enter in a text part goes into the slot after it.
402
- menuVariants: () => Object.fromEntries((map.components ?? []).filter(({ template, state }) => template && state === "normal").map((entry) => [entry.id, {
403
- label: entry.name[language],
404
- icon: pictureOf(entry) ?? ICON,
405
- words: `${entry.name.zh} ${entry.name.en} ${entry.id} ${entry.words}`.toLowerCase(),
406
- sample: true,
407
- preview: { src: entry.preview, says: entry.description[language] },
408
- create({ doc, put: place, paragraph }) {
409
- bring(entry);
410
- const instance = newInstance(doc, entry.template);
411
- slotsMade(doc, instance, (slot) => blocksOfTemplate(doc, slot));
412
- ownIds(doc, [instance]);
413
- place(instance);
414
- return editableParts(instance)[0] ?? firstHost(instance) ?? place(paragraph());
415
- },
416
- }])),
470
+ // before the instance is saved, and Enter in a text part goes into the slot after it. One whose needs
471
+ // hold "tables" is a view of a table (`readsTables`), which binds itself to the nearest table above it as
472
+ // it is made (VIEW_FROM: page/table/binding.mjs). Where two offered have one name (the same block
473
+ // of two packs: 我的待办 of Gallery 风格 and of 柔光风格), each row says its pack's name after it in grey
474
+ // (`of`: the menu's ge-pop-of).
475
+ menuVariants: () => {
476
+ const offered = (map.components ?? []).filter(({ template, state }) => template && state === "normal");
477
+ const counts = new Map();
478
+ for (const entry of offered) counts.set(entry.name[language], (counts.get(entry.name[language]) ?? 0) + 1);
479
+ return Object.fromEntries(offered.map((entry) => [entry.id, {
480
+ label: entry.name[language],
481
+ ...(counts.get(entry.name[language]) > 1 ? { of: entry.pack?.name?.[language] ?? entry.id.split("/")[0] } : {}),
482
+ icon: ICON,
483
+ words: searchWords(entry),
484
+ sample: true,
485
+ readsTables: entry.needs?.includes("tables") ?? false,
486
+ preview: { src: entry.preview, says: entry.description[language] },
487
+ create({ doc, put: place, paragraph }) {
488
+ bring(entry);
489
+ const instance = newInstance(doc, entry.template);
490
+ slotsMade(doc, instance, (slot) => blocksOfTemplate(doc, slot));
491
+ ownIds(doc, [instance]);
492
+ place(instance);
493
+ if (entry.needs?.includes("tables")) bindAbove(instance); // a view reads the nearest table above it; its 设置 changes that
494
+ return editableParts(instance)[0] ?? firstHost(instance) ?? place(paragraph());
495
+ },
496
+ }]));
497
+ },
417
498
  /**
418
499
  * Whether a copy of an instance's unit stands for it: not where it has slots, whose blocks that copy
419
500
  * would write whole, with no ids the page knows; such an instance is copied by what it carries alone
@@ -447,11 +528,8 @@ export function create({ map, newParagraph, active, sourceStep, struct }) {
447
528
  if (next) caretAt(next, "start");
448
529
  else if (next === null) selection.removeAllRanges();
449
530
  },
450
- /** Its package's picture, or the mark of any component. */
451
- iconOf: (unit) => {
452
- const entry = entryOf(unit.getAttribute(ATTRIBUTE));
453
- return (entry && pictureOf(entry)) ?? ICON;
454
- },
531
+ /** The mark of a custom block: the one every custom block has in a menu, its picture shown beside its row instead. */
532
+ iconOf: () => ICON,
455
533
  /**
456
534
  * What of an instance its component writes, part by part, as the file is to hold it (null: nothing
457
535
  * here does): its parts as the kit's dehydrate leaves them, what it and its component leave on them