@heycys/gallery 0.1.2 → 0.2.1

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 (338) 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/embed/page.mjs +1 -1
  5. package/browser/editor/components/file/page.mjs +4 -4
  6. package/browser/editor/components/heading/index.mjs +7 -6
  7. package/browser/editor/components/icon/page.mjs +6 -4
  8. package/browser/editor/components/icons.mjs +5 -2
  9. package/browser/editor/components/image/index.mjs +2 -0
  10. package/browser/editor/components/image/page.mjs +172 -90
  11. package/browser/editor/components/installed/index.mjs +6 -2
  12. package/browser/editor/components/installed/page.mjs +80 -13
  13. package/browser/editor/components/media/icons.mjs +13 -8
  14. package/browser/editor/components/media/page.mjs +5 -3
  15. package/browser/editor/components/media/toolbar.mjs +12 -3
  16. package/browser/editor/components/paragraph/index.mjs +4 -3
  17. package/browser/editor/components/registry.mjs +17 -5
  18. package/browser/editor/components/table/index.mjs +4 -2
  19. package/browser/editor/page/after/index.mjs +288 -0
  20. package/browser/editor/page/align.mjs +91 -0
  21. package/browser/editor/page/beside.mjs +5 -6
  22. package/browser/editor/page/block-menu/index.mjs +6 -3
  23. package/browser/editor/page/blocks.mjs +3 -1
  24. package/browser/editor/page/board/hint.mjs +64 -27
  25. package/browser/editor/page/board/index.mjs +160 -71
  26. package/browser/editor/page/board/motion.mjs +177 -0
  27. package/browser/editor/page/board/settings.mjs +4 -3
  28. package/browser/editor/page/clipboard/import/read.mjs +20 -2
  29. package/browser/editor/page/clipboard/index.mjs +10 -3
  30. package/browser/editor/page/clipboard/own/blocks.mjs +3 -4
  31. package/browser/editor/page/clipboard/own/describe.mjs +6 -2
  32. package/browser/editor/page/commit.mjs +2 -2
  33. package/browser/editor/page/core.mjs +3 -1
  34. package/browser/editor/page/custom-page.mjs +15 -11
  35. package/browser/editor/page/editor.mjs +31 -14
  36. package/browser/editor/page/history.mjs +19 -4
  37. package/browser/editor/page/insert/index.mjs +20 -22
  38. package/browser/editor/page/menu.mjs +15 -7
  39. package/browser/editor/page/page-objects.mjs +3 -7
  40. package/browser/editor/page/present-links.mjs +5 -4
  41. package/browser/editor/page/present-shell.mjs +63 -0
  42. package/browser/editor/page/selection/index.mjs +10 -2
  43. package/browser/editor/page/table/binding.mjs +87 -0
  44. package/browser/editor/page/table/icons.mjs +0 -2
  45. package/browser/editor/page/table/index.mjs +10 -14
  46. package/browser/editor/page/table/views.mjs +5 -52
  47. package/browser/editor/page/table/widths.mjs +82 -10
  48. package/browser/editor/page/typing/index.mjs +44 -12
  49. package/browser/editor/page/typing/leave.mjs +19 -5
  50. package/browser/shared/block-packs.mjs +23 -8
  51. package/browser/shared/component-catalog.mjs +15 -8
  52. package/browser/shared/component-source.mjs +3 -1
  53. package/browser/shared/components/heading.mjs +2 -1
  54. package/browser/shared/components/installed.mjs +1 -1
  55. package/browser/shared/components/page.mjs +2 -0
  56. package/browser/shared/components/paragraph.mjs +8 -1
  57. package/browser/shared/components/table.mjs +1 -0
  58. package/browser/shared/document-copy.mjs +43 -0
  59. package/browser/shared/drafts.mjs +10 -3
  60. package/browser/shared/emoji/icon-value.mjs +2 -2
  61. package/browser/shared/emoji/line-icons.mjs +37 -0
  62. package/browser/shared/extensions.mjs +83 -7
  63. package/browser/shared/http.mjs +9 -0
  64. package/browser/shared/i18n/en.mjs +50 -17
  65. package/browser/shared/outline-shape.mjs +6 -0
  66. package/browser/shared/outline.mjs +165 -12
  67. package/browser/shared/pack-manifest.mjs +12 -1
  68. package/browser/shared/pages.mjs +169 -57
  69. package/browser/shared/pinyin.mjs +92 -0
  70. package/browser/shared/present-step.mjs +17 -0
  71. package/browser/shared/sidebar-head.mjs +70 -0
  72. package/browser/shared/web-links.mjs +5 -0
  73. package/browser/shared/workspace-columns.mjs +11 -7
  74. package/dist/{chunk-N7AZZLGT.mjs → chunk-2GKGEVMR.mjs} +29 -24
  75. package/dist/chunk-2OHANJ6Q.mjs +2 -0
  76. package/dist/{chunk-HYMEEXKN.mjs → chunk-2RKIHJI2.mjs} +1 -1
  77. package/dist/{chunk-742D2WWJ.mjs → chunk-36FIRJIF.mjs} +2 -2
  78. package/dist/{chunk-BPL64HGA.mjs → chunk-3A5I2GJK.mjs} +1 -1
  79. package/dist/{chunk-RFDZXFJF.mjs → chunk-3OFXMN6N.mjs} +1 -1
  80. package/dist/chunk-46ACXOU5.mjs +2 -0
  81. package/dist/{chunk-PUOQ65NC.mjs → chunk-4AMTKWDO.mjs} +1 -1
  82. package/dist/{chunk-VTKNEYT3.mjs → chunk-4DNHHJSL.mjs} +1 -1
  83. package/dist/chunk-5DPABPBI.mjs +2 -0
  84. package/dist/chunk-5IIH335D.mjs +3 -0
  85. package/dist/chunk-5L26JUYA.mjs +2 -0
  86. package/dist/{chunk-3NJACR4W.mjs → chunk-5MEXZ2UF.mjs} +1 -1
  87. package/dist/{chunk-ZHEHPMBK.mjs → chunk-6MPKCH3P.mjs} +1 -1
  88. package/dist/{chunk-C564NU2H.mjs → chunk-74RPP2M2.mjs} +1 -1
  89. package/dist/chunk-7M2BQQGZ.mjs +2 -0
  90. package/dist/{chunk-VXACTLO3.mjs → chunk-7M7UPWU6.mjs} +1 -1
  91. package/dist/{chunk-ZB4H7ZN4.mjs → chunk-7N2LPG33.mjs} +1 -1
  92. package/dist/{chunk-UBBRTJ2R.mjs → chunk-7XDYK3UC.mjs} +1 -1
  93. package/dist/chunk-AEMVZYZQ.mjs +166 -0
  94. package/dist/{chunk-POQSE4C2.mjs → chunk-AFGUR7SY.mjs} +1 -1
  95. package/dist/{chunk-QKXMPUXC.mjs → chunk-AMDGVDNV.mjs} +1 -1
  96. package/dist/{chunk-KTKHRC6H.mjs → chunk-ASAXY2DJ.mjs} +1 -1
  97. package/dist/{chunk-NVRNLK25.mjs → chunk-BYSF5UMM.mjs} +2 -2
  98. package/dist/{chunk-U3SSPL4K.mjs → chunk-CCSGFCLZ.mjs} +1 -1
  99. package/dist/{chunk-NWFBE2YA.mjs → chunk-CG3UHDKH.mjs} +1 -1
  100. package/dist/chunk-CPS4SRPF.mjs +5 -0
  101. package/dist/{chunk-B4P2CM2J.mjs → chunk-CZ5KJSGK.mjs} +1 -1
  102. package/dist/chunk-DAMVDXR3.mjs +2 -0
  103. package/dist/{chunk-NG375ILQ.mjs → chunk-DH6GMDNN.mjs} +1 -1
  104. package/dist/chunk-DHB7UZ7F.mjs +3 -0
  105. package/dist/{chunk-MHW7ZKOU.mjs → chunk-DKR7LFIY.mjs} +1 -1
  106. package/dist/chunk-DLFZJJEE.mjs +2 -0
  107. package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
  108. package/dist/{chunk-6VTU6KEO.mjs → chunk-FCK6NXON.mjs} +1 -1
  109. package/dist/{chunk-ZWPMCK7P.mjs → chunk-FX42XU6J.mjs} +1 -1
  110. package/dist/chunk-FXQBADMN.mjs +5 -0
  111. package/dist/{chunk-W3BGBJQW.mjs → chunk-G4YP7GVC.mjs} +1 -1
  112. package/dist/{chunk-V6XZ4VIR.mjs → chunk-GWOYMENX.mjs} +1 -1
  113. package/dist/chunk-HATA2W7X.mjs +2 -0
  114. package/dist/{chunk-YI43RG4P.mjs → chunk-HBAUKKQH.mjs} +1 -1
  115. package/dist/{chunk-MAFIGVXY.mjs → chunk-HM4E43VM.mjs} +1 -1
  116. package/dist/{chunk-CRJEG7MT.mjs → chunk-JI6T3A4Y.mjs} +1 -1
  117. package/dist/{chunk-NFMCP2HI.mjs → chunk-JJNRIFND.mjs} +2 -2
  118. package/dist/chunk-JL6R4FG4.mjs +4 -0
  119. package/dist/{chunk-UMHUSOPJ.mjs → chunk-JXLSHVJ2.mjs} +1 -1
  120. package/dist/{chunk-FJYN7RTH.mjs → chunk-KFZBYDIS.mjs} +1 -1
  121. package/dist/{chunk-ZTHICSSF.mjs → chunk-KH24LWAT.mjs} +1 -1
  122. package/dist/{chunk-RKPWLGZ3.mjs → chunk-KUILYI7H.mjs} +1 -1
  123. package/dist/{chunk-RMECZA2K.mjs → chunk-L7W624FD.mjs} +1 -1
  124. package/dist/chunk-LE44MD3O.mjs +4 -0
  125. package/dist/{chunk-4K5KC6V2.mjs → chunk-LV24RY2E.mjs} +1 -1
  126. package/dist/{chunk-43B75J5T.mjs → chunk-MKTX5PGV.mjs} +1 -1
  127. package/dist/{chunk-LKX55H76.mjs → chunk-NAGMRVW4.mjs} +1 -1
  128. package/dist/{chunk-D4CKRD3V.mjs → chunk-NFABH3QW.mjs} +2 -2
  129. package/dist/{chunk-PVER7LSC.mjs → chunk-NIW7F7LU.mjs} +2 -2
  130. package/dist/{chunk-PAREWQGB.mjs → chunk-NWRZXELO.mjs} +1 -1
  131. package/dist/{chunk-OJKMINIU.mjs → chunk-OU3S466R.mjs} +15 -15
  132. package/dist/{chunk-5ZJA7CXT.mjs → chunk-PAFSFVYB.mjs} +1 -1
  133. package/dist/chunk-QCEATQC4.mjs +2 -0
  134. package/dist/{chunk-Y34NR5JL.mjs → chunk-QJ3TFJLQ.mjs} +1 -1
  135. package/dist/{chunk-IA4PSIMD.mjs → chunk-QK3VISCA.mjs} +1 -1
  136. package/dist/{chunk-ZUNVAXLH.mjs → chunk-QXVNUASA.mjs} +1 -1
  137. package/dist/{chunk-GRAXKKG4.mjs → chunk-QZ35UAB4.mjs} +1 -1
  138. package/dist/{chunk-UGWYKHBL.mjs → chunk-S2Y7VI4H.mjs} +2 -2
  139. package/dist/{chunk-OW5NWPOJ.mjs → chunk-SAHS56QM.mjs} +1 -1
  140. package/dist/{chunk-QOUEFUCH.mjs → chunk-SOD23RGL.mjs} +1 -1
  141. package/dist/chunk-TDWJKOLN.mjs +2 -0
  142. package/dist/{chunk-V6RIYQZZ.mjs → chunk-TOE6DPLH.mjs} +1 -1
  143. package/dist/chunk-UGO5N3EI.mjs +15 -0
  144. package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
  145. package/dist/{chunk-NWR2LT5B.mjs → chunk-V5JRSGZF.mjs} +1 -1
  146. package/dist/{chunk-BZ5RAF7L.mjs → chunk-VA3KAQZD.mjs} +1 -1
  147. package/dist/chunk-VG5JEIUQ.mjs +12 -0
  148. package/dist/{chunk-UNOYVIYR.mjs → chunk-VGTMLQWZ.mjs} +1 -1
  149. package/dist/chunk-VKSRM7EY.mjs +22 -0
  150. package/dist/{chunk-YQGT4AB2.mjs → chunk-VNUWREXU.mjs} +1 -1
  151. package/dist/{chunk-AGDILNWB.mjs → chunk-VPVOAJ7X.mjs} +1 -1
  152. package/dist/{chunk-HLYV7RIH.mjs → chunk-WCWYNFDF.mjs} +1 -1
  153. package/dist/chunk-WTEGUPFG.mjs +16 -0
  154. package/dist/{chunk-N76HWBA3.mjs → chunk-XKOHV4H6.mjs} +1 -1
  155. package/dist/{chunk-HMYTM3GT.mjs → chunk-XMKHCUI6.mjs} +1 -1
  156. package/dist/{chunk-OUUP3WKT.mjs → chunk-XQRUG4G3.mjs} +1 -1
  157. package/dist/{chunk-32ZUEFZ7.mjs → chunk-YBB5LWZK.mjs} +1 -1
  158. package/dist/{chunk-SX4LJYT6.mjs → chunk-YFJU3O7G.mjs} +1 -1
  159. package/dist/chunk-YPPJVDQI.mjs +2 -0
  160. package/dist/chunk-ZANMKAWS.mjs +2 -0
  161. package/dist/{chunk-6QBHU4DF.mjs → chunk-ZCP7BTOZ.mjs} +1 -1
  162. package/dist/{chunk-ERLAIBOA.mjs → chunk-ZEY2RLBT.mjs} +2 -2
  163. package/dist/{chunk-SQ6Y2ZMA.mjs → chunk-ZO7SFB2E.mjs} +2 -2
  164. package/dist/cli.mjs +1 -1
  165. package/dist/server.mjs +1 -1
  166. package/dist/worker.mjs +1 -1
  167. package/kit/board.css +5 -5
  168. package/kit/code.js +15 -15
  169. package/kit/math.js +1 -1
  170. package/kit/report.css +123 -44
  171. package/kit/runtime.js +45 -7
  172. package/kit/tables.js +2 -0
  173. package/kit/ui.js +2 -0
  174. package/package.json +1 -1
  175. package/packs/gallery/chart-view/kit.js +3 -3
  176. package/packs/gallery/chart-view/writing.md +1 -1
  177. package/packs/gallery/countdown/component.json +1 -1
  178. package/packs/gallery/countdown/kit.js +89 -29
  179. package/packs/gallery/countdown/preview.svg +1 -1
  180. package/packs/gallery/countdown/style.css +88 -100
  181. package/packs/gallery/countdown/writing.md +2 -2
  182. package/packs/gallery/habit/component.json +3 -3
  183. package/packs/gallery/habit/kit.js +114 -75
  184. package/packs/gallery/habit/preview.svg +1 -1
  185. package/packs/gallery/habit/style.css +133 -205
  186. package/packs/gallery/habit/writing.md +2 -2
  187. package/packs/gallery/heatmap/component.json +2 -2
  188. package/packs/gallery/heatmap/kit.js +335 -173
  189. package/packs/gallery/heatmap/preview.svg +1 -1
  190. package/packs/gallery/heatmap/style.css +158 -170
  191. package/packs/gallery/heatmap/writing.md +2 -2
  192. package/packs/gallery/kanban/kit.js +3 -3
  193. package/packs/gallery/kanban/writing.md +1 -1
  194. package/packs/gallery/metric/kit.js +3 -3
  195. package/packs/gallery/metric/writing.md +1 -1
  196. package/packs/gallery/note/component.json +3 -3
  197. package/packs/gallery/note/kit.js +40 -13
  198. package/packs/gallery/note/preview.svg +1 -1
  199. package/packs/gallery/note/style.css +51 -30
  200. package/packs/gallery/note/writing.md +2 -2
  201. package/packs/gallery/recent/component.json +2 -2
  202. package/packs/gallery/recent/kit.js +141 -86
  203. package/packs/gallery/recent/preview.svg +1 -1
  204. package/packs/gallery/recent/style.css +53 -126
  205. package/packs/gallery/recent/writing.md +2 -2
  206. package/packs/gallery/static/component.json +2 -2
  207. package/packs/gallery/static/preview.svg +1 -1
  208. package/packs/gallery/static/style.css +99 -66
  209. package/packs/gallery/static/writing.md +2 -2
  210. package/packs/gallery/stats/component.json +3 -3
  211. package/packs/gallery/stats/kit.js +253 -72
  212. package/packs/gallery/stats/preview.svg +1 -1
  213. package/packs/gallery/stats/style.css +64 -142
  214. package/packs/gallery/stats/writing.md +2 -2
  215. package/packs/gallery/today/component.json +2 -2
  216. package/packs/gallery/today/kit.js +62 -27
  217. package/packs/gallery/today/preview.svg +1 -1
  218. package/packs/gallery/today/style.css +101 -72
  219. package/packs/gallery/today/writing.md +2 -2
  220. package/packs/gallery/todos/component.json +1 -1
  221. package/packs/gallery/todos/kit.js +218 -71
  222. package/packs/gallery/todos/preview.svg +1 -1
  223. package/packs/gallery/todos/style.css +170 -223
  224. package/packs/gallery/todos/writing.md +1 -1
  225. package/packs/gallery/weather/component.json +2 -2
  226. package/packs/gallery/weather/kit.js +165 -90
  227. package/packs/gallery/weather/preview.svg +1 -1
  228. package/packs/gallery/weather/style.css +100 -218
  229. package/packs/gallery/weather/writing.md +1 -1
  230. package/packs/gallery-soft/countdown/component.json +18 -0
  231. package/packs/gallery-soft/countdown/example.html +3 -0
  232. package/packs/gallery-soft/countdown/kit.js +206 -0
  233. package/packs/gallery-soft/countdown/preview.svg +1 -0
  234. package/packs/gallery-soft/countdown/style.css +211 -0
  235. package/packs/gallery-soft/countdown/template.html +3 -0
  236. package/packs/gallery-soft/countdown/writing.md +24 -0
  237. package/packs/gallery-soft/habit/component.json +14 -0
  238. package/packs/gallery-soft/habit/example.html +3 -0
  239. package/packs/gallery-soft/habit/kit.js +366 -0
  240. package/packs/gallery-soft/habit/preview.svg +1 -0
  241. package/packs/gallery-soft/habit/style.css +251 -0
  242. package/packs/gallery-soft/habit/template.html +3 -0
  243. package/packs/gallery-soft/habit/writing.md +23 -0
  244. package/packs/gallery-soft/heatmap/component.json +18 -0
  245. package/packs/gallery-soft/heatmap/example.html +3 -0
  246. package/packs/gallery-soft/heatmap/kit.js +498 -0
  247. package/packs/gallery-soft/heatmap/preview.svg +1 -0
  248. package/packs/gallery-soft/heatmap/style.css +181 -0
  249. package/packs/gallery-soft/heatmap/template.html +3 -0
  250. package/packs/gallery-soft/heatmap/writing.md +23 -0
  251. package/packs/gallery-soft/note/component.json +14 -0
  252. package/packs/gallery-soft/note/example.html +3 -0
  253. package/packs/gallery-soft/note/kit.js +162 -0
  254. package/packs/gallery-soft/note/preview.svg +1 -0
  255. package/packs/gallery-soft/note/style.css +148 -0
  256. package/packs/gallery-soft/note/template.html +3 -0
  257. package/packs/gallery-soft/note/writing.md +20 -0
  258. package/packs/gallery-soft/recent/component.json +13 -0
  259. package/packs/gallery-soft/recent/example.html +1 -0
  260. package/packs/gallery-soft/recent/kit.js +335 -0
  261. package/packs/gallery-soft/recent/preview.svg +1 -0
  262. package/packs/gallery-soft/recent/style.css +160 -0
  263. package/packs/gallery-soft/recent/template.html +1 -0
  264. package/packs/gallery-soft/recent/writing.md +17 -0
  265. package/packs/gallery-soft/static/component.json +13 -0
  266. package/packs/gallery-soft/static/example.html +12 -0
  267. package/packs/gallery-soft/static/preview.svg +1 -0
  268. package/packs/gallery-soft/static/style.css +186 -0
  269. package/packs/gallery-soft/static/template.html +1 -0
  270. package/packs/gallery-soft/static/writing.md +20 -0
  271. package/packs/gallery-soft/stats/component.json +13 -0
  272. package/packs/gallery-soft/stats/example.html +1 -0
  273. package/packs/gallery-soft/stats/kit.js +341 -0
  274. package/packs/gallery-soft/stats/preview.svg +1 -0
  275. package/packs/gallery-soft/stats/style.css +119 -0
  276. package/packs/gallery-soft/stats/template.html +1 -0
  277. package/packs/gallery-soft/stats/writing.md +13 -0
  278. package/packs/gallery-soft/today/component.json +12 -0
  279. package/packs/gallery-soft/today/example.html +1 -0
  280. package/packs/gallery-soft/today/kit.js +222 -0
  281. package/packs/gallery-soft/today/preview.svg +1 -0
  282. package/packs/gallery-soft/today/style.css +178 -0
  283. package/packs/gallery-soft/today/template.html +1 -0
  284. package/packs/gallery-soft/today/writing.md +16 -0
  285. package/packs/gallery-soft/todos/component.json +19 -0
  286. package/packs/gallery-soft/todos/example.html +3 -0
  287. package/packs/gallery-soft/todos/kit.js +527 -0
  288. package/packs/gallery-soft/todos/preview.svg +1 -0
  289. package/packs/gallery-soft/todos/style.css +212 -0
  290. package/packs/gallery-soft/todos/template.html +3 -0
  291. package/packs/gallery-soft/todos/writing.md +32 -0
  292. package/packs/gallery-soft/weather/component.json +19 -0
  293. package/packs/gallery-soft/weather/example.html +3 -0
  294. package/packs/gallery-soft/weather/kit.js +411 -0
  295. package/packs/gallery-soft/weather/preview.svg +1 -0
  296. package/packs/gallery-soft/weather/style.css +185 -0
  297. package/packs/gallery-soft/weather/template.html +3 -0
  298. package/packs/gallery-soft/weather/writing.md +28 -0
  299. package/release.json +10 -10
  300. package/skill/SKILL.md +15 -14
  301. package/skill/references/building-blocks.md +4 -4
  302. package/skill/references/extensions.md +11 -7
  303. package/skill/references/pages-and-data.md +13 -9
  304. package/skill/references/writing-html.md +2 -0
  305. package/web/assets/index-BHC5ZOMh.js +15 -0
  306. package/web/assets/index-xP0uw3a0.css +1 -0
  307. package/web/index.html +2 -2
  308. package/dist/chunk-2NRBY6AJ.mjs +0 -2
  309. package/dist/chunk-35NQ4PLQ.mjs +0 -2
  310. package/dist/chunk-3KJHLHUO.mjs +0 -2
  311. package/dist/chunk-4AV456RQ.mjs +0 -2
  312. package/dist/chunk-5XYHDBJ5.mjs +0 -2
  313. package/dist/chunk-6A53MZYZ.mjs +0 -2
  314. package/dist/chunk-6EXH3ORZ.mjs +0 -166
  315. package/dist/chunk-BVEFGE4B.mjs +0 -2
  316. package/dist/chunk-D4EURAX4.mjs +0 -4
  317. package/dist/chunk-DQCE7TJY.mjs +0 -5
  318. package/dist/chunk-FWNRPMCJ.mjs +0 -12
  319. package/dist/chunk-FWW4CSAZ.mjs +0 -2
  320. package/dist/chunk-GY3FAUVM.mjs +0 -2
  321. package/dist/chunk-JVHHHGEA.mjs +0 -3
  322. package/dist/chunk-L7UP52JO.mjs +0 -2
  323. package/dist/chunk-NAMN3RAF.mjs +0 -2
  324. package/dist/chunk-PZDLJJTJ.mjs +0 -3
  325. package/dist/chunk-SXHBYZST.mjs +0 -16
  326. package/dist/chunk-TEZAWDSQ.mjs +0 -2
  327. package/dist/chunk-UFNYFFKO.mjs +0 -20
  328. package/dist/chunk-UKSHDZZ6.mjs +0 -2
  329. package/dist/chunk-VJPFVNNW.mjs +0 -15
  330. package/dist/chunk-VS3ZMVGX.mjs +0 -5
  331. package/web/assets/index-BK2e_07w.js +0 -13
  332. package/web/assets/index-CWFI83mF.css +0 -1
  333. /package/dist/{chunk-YEY3RSFQ.mjs → chunk-5MMN3N5J.mjs} +0 -0
  334. /package/dist/{chunk-UH4SIXTR.mjs → chunk-74UW7QSL.mjs} +0 -0
  335. /package/dist/{chunk-W7XHE4CT.mjs → chunk-EKGYYOSP.mjs} +0 -0
  336. /package/dist/{chunk-QAIHRH3I.mjs → chunk-KNZJT4ET.mjs} +0 -0
  337. /package/dist/{chunk-A55HN5OK.mjs → chunk-N3DSBH5S.mjs} +0 -0
  338. /package/dist/{chunk-QR6A54KJ.mjs → chunk-ZW2A6GBW.mjs} +0 -0
@@ -3,27 +3,31 @@ import { postAnswer } from "../post.mjs";
3
3
  import { say } from "../notices.mjs";
4
4
  import { addStyle, element } from "../ui.mjs";
5
5
 
6
- // The home page's first-open note (notes/plans/component-pages-plan-2026-11-07.md §5.2; docs/render.md
7
- // "A custom page", **The default home page**): at the top of the home page, while the page map says
8
- // `hint` (src/server/routes.mjs: the home page, its note not yet closed in this workspace), a short
9
- // note on its cards: how to move one (the grip at its top-right: drag it), open its menu (a click on
10
- // the same grip: 大小, 设置, 删除), add one (添加自定义块: the first item of the ··· menu at the top right of the workbench's bar; a page open
11
- // alone in a tab has it at the top right of its board) and take one out (the menu's 删除), and that a new
12
- // card or a new page is the Agent's to make, with a sentence the reader can copy and say to one (its 复制
13
- // button). Its × closes it for good, for the workspace (POST
14
- // /__gallery/pages/home/hint: .gallery/hints.json), whichever browser shows the page next. In the
6
+ // A custom page's first-open note (notes/plans/component-pages-plan-2026-11-07.md §5.2; docs/render.md
7
+ // "A custom page", **Notes of a page**): at the top of a page whose page.json says `hint` (the default home
8
+ // page's), while the page map says `hint` (src/server/routes.mjs: its note not yet closed in this
9
+ // workspace), a short note on its cards: how to move one (the grip at its top-right: drag it), open its
10
+ // menu (a click on the same grip: 大小, 设置, 删除), add one (添加自定义块: the first item of the ··· menu at the
11
+ // top right of the workbench's bar; a page open alone in a tab has it at the top right of its board) and
12
+ // take one out (the menu's 删除), and that a new card or a new page is the Agent's to make, with a sentence
13
+ // the reader can copy and say to one (its 复制 button). Its × closes it for good, for the workspace (POST
14
+ // /__gallery/pages/<slug>/hint: .gallery/hints.json), whichever browser shows the page next. In the
15
15
  // editor's look: grays alone, a quiet fill, no colour; the kit's own light and dark through the
16
16
  // editor's tokens (look.mjs).
17
17
  //
18
- // And its note of new default cards (showNews: the plan's 3.4), on a home page made before them (a phase 2
19
- // home: 问候与日期 and 便签 alone), while the page map says `news`: which default cards it does not hold
20
- // that came since (我的待办, 最近编辑, 工作区统计, 编辑热力图), and that 加到首页 puts them in the empty spots
21
- // as 恢复默认卡片 in the home page's menu in the sidebar does (the board's restoreDefaults: what is missing
22
- // alone, the cards there untouched, one step of the page's undo). A home already made is never changed by
23
- // itself: the reader is told once, at the top of the page, where the first-open note stands, and nothing
24
- // moves on the board until they ask. Its × (or 加到首页) says it for the workspace (POST
25
- // /__gallery/pages/home/hint with { cards: true }), which 恢复默认卡片 from the sidebar does too.
26
-
18
+ // Its note of new default cards (showNews: the plan's 3.4), on a page with the default cards (its page.json's
19
+ // defaultCards) made before some of them, while the page map says `news`: which default cards it does not
20
+ // hold that came since, and that 加到这一页 puts them in the empty spots as 恢复默认卡片 in the page's menu
21
+ // does (the board's restoreDefaults: what is missing alone, the cards there untouched, one step of the
22
+ // page's undo). A page already made is never changed by itself: the reader is told once, at the top of
23
+ // the page, where the first-open note stands, and nothing moves on the board until they ask. Its × (or
24
+ // 加到这一页) says it for the workspace (POST /__gallery/pages/<slug>/hint with { cards: true }), which
25
+ // 恢复默认卡片 does too.
26
+ //
27
+ // And the middle of a blank page (showBlank: the workbench's 新建页面, which no one has written yet, while
28
+ // the page map says `blank`): that an Agent writes a custom page, any page it likes, a board of cards or
29
+ // not, with a request the reader can copy and say to one, as the first-open note's. Nothing of it is the
30
+ // file's: once an Agent writes page.html the page is shown again without it.
27
31
  const STYLE = `
28
32
  .ge-home-hint { box-sizing: border-box; max-width: var(--gallery-board-width, 944px); margin: 0 auto 12px; }
29
33
  .ge-home-hint-box { position: relative; box-sizing: border-box; max-width: 720px; padding: 14px 44px 14px 16px; border-radius: 10px;
@@ -47,6 +51,9 @@ const STYLE = `
47
51
  .ge-home-hint-box .ge-home-hint-add { margin-left: -2px; background: var(--ge-btn-primary); color: var(--ge-btn-primary-text); }
48
52
  .ge-home-hint-box .ge-home-hint-add:hover:not(:disabled) { background: var(--ge-btn-primary-hover); color: var(--ge-btn-primary-text); }
49
53
  .ge-home-hint-box .ge-home-hint-add:disabled { opacity: .5; cursor: default; }
54
+ .ge-page-blank { box-sizing: border-box; display: flex; justify-content: center; padding: max(48px, 18vh) 16px 48px; }
55
+ .ge-page-blank .ge-home-hint-box { width: 100%; max-width: 520px; padding: 20px 20px 18px; }
56
+ .ge-page-blank .ge-home-hint-title { font-size: 16px; }
50
57
  `;
51
58
 
52
59
  const DOTS = '<svg viewBox="0 0 10 16" aria-hidden="true"><circle cx="2.5" cy="2" r="1.25"/><circle cx="7.5" cy="2" r="1.25"/><circle cx="2.5" cy="8" r="1.25"/><circle cx="7.5" cy="8" r="1.25"/><circle cx="2.5" cy="14" r="1.25"/><circle cx="7.5" cy="14" r="1.25"/></svg>';
@@ -70,15 +77,15 @@ export function showNews(map, { restore }) {
70
77
  if (!map?.news?.length || !main) return null;
71
78
  addStyle(STYLE);
72
79
  const named = ({ block, name }) => name?.[language === "en" ? "en" : "zh"] ?? name?.zh ?? block;
73
- const add = element("button", { type: "button", class: "ge-home-hint-copy ge-home-hint-add", text: t("加到首页") });
80
+ const add = element("button", { type: "button", class: "ge-home-hint-copy ge-home-hint-add", text: t("加到这一页") });
74
81
  const close = element("button", { type: "button", class: "ge-home-hint-close", "aria-label": t("关闭这条说明"), title: t("关闭"), html: CLOSE });
75
82
  const box = element("div", { class: "ge-home-hint-box" },
76
- element("p", { class: "ge-home-hint-title", text: t("首页有了新的卡片") }),
83
+ element("p", { class: "ge-home-hint-title", text: t("有了新的默认卡片") }),
77
84
  element("p", { text: map.news.map(named).join(t("、")) }),
78
- element("p", { class: "ge-home-hint-ask", text: t("点「加到首页」放进空位,已有的卡片不动。以后也可以在侧栏里首页的菜单中点「恢复默认卡片」。") }),
85
+ element("p", { class: "ge-home-hint-ask", text: t("点「加到这一页」放进空位,已有的卡片不动。以后也可以在这一页的菜单中点「恢复默认卡片」。") }),
79
86
  element("div", { class: "ge-home-hint-act" }, add),
80
87
  close);
81
- const note = noteOf("ge-home-news", t("首页的新卡片"), box);
88
+ const note = noteOf("ge-home-news", t("新的默认卡片"), box);
82
89
  for (const button of [add, close]) button.addEventListener("mousedown", (event) => event.preventDefault());
83
90
  const told = () => postAnswer(`/__gallery/pages/${map.slug}/hint`, map.token, { cards: true });
84
91
  add.addEventListener("click", async () => {
@@ -86,7 +93,7 @@ export function showNews(map, { restore }) {
86
93
  const answer = await restore();
87
94
  if (!answer?.ok) {
88
95
  add.disabled = false;
89
- return say(t("没能把卡片放到首页"), "error");
96
+ return say(t("没能把卡片放到这一页"), "error");
90
97
  }
91
98
  note.remove();
92
99
  const added = answer.added ?? [];
@@ -104,7 +111,7 @@ export function showNews(map, { restore }) {
104
111
  }
105
112
 
106
113
  /**
107
- * The note shown, on the home page whose map (`map`: slug, token) says `hint`, at the top of the page
114
+ * The note shown, on the page whose map (`map`: slug, token) says `hint`, at the top of the page
108
115
  * (`main.gallery-page`, before what the page holds). `hosted`: the page stands in a workbench, whose top
109
116
  * bar's ··· menu has 添加自定义块 (the words say so), else the page's own at the top right of its board. Returns the
110
117
  * note, or null where none is shown.
@@ -114,16 +121,16 @@ export function showHint(map, { hosted = false } = {}) {
114
121
  if (!map?.hint || !main) return null;
115
122
  addStyle(STYLE);
116
123
  // The sentence offered: one that names Gallery, so that the Agent takes up its skill.
117
- const ask = t("用 Gallery 在首页加一张卡片,记我每天喝了几杯水。别动我摆好的卡片。");
124
+ const ask = t("用 Gallery 在「{0}」这一页加一张卡片,记我每天喝了几杯水。别动我摆好的卡片。", map.title ?? document.title);
118
125
  const copy = element("button", { type: "button", class: "ge-home-hint-copy", text: t("复制") });
119
126
  const close = element("button", { type: "button", class: "ge-home-hint-close", "aria-label": t("关闭这条说明"), title: t("关闭"), html: CLOSE });
120
127
  const box = element("div", { class: "ge-home-hint-box" },
121
- element("p", { class: "ge-home-hint-title", text: t("这是你的首页") }),
128
+ element("p", { class: "ge-home-hint-title", text: t("这一页的卡片可以自己摆") }),
122
129
  element("p", {}, ...filled(hosted ? t("拖卡片右上角的 {0} 移动它,点一下它打开菜单(大小、设置、删除),点页面右上角的 ··· 里的「添加自定义块」加一张。") : t("拖卡片右上角的 {0} 移动它,点一下它打开菜单(大小、设置、删除),点页面右上方的「添加自定义块」加一张。"), [icon(DOTS)])),
123
130
  element("p", { class: "ge-home-hint-ask", text: t("想要新卡片或新页面,跟 Agent 说,比如:") }),
124
131
  element("div", { class: "ge-home-hint-say" }, element("span", { text: ask }), copy),
125
132
  close);
126
- const note = noteOf("", t("首页的说明"), box);
133
+ const note = noteOf("", t("这一页的说明"), box);
127
134
  for (const button of [copy, close]) button.addEventListener("mousedown", (event) => event.preventDefault());
128
135
  copy.addEventListener("click", () => {
129
136
  // The frame given the focus first: in a workbench a press its mousedown keeps from moving the focus
@@ -143,3 +150,33 @@ export function showHint(map, { hosted = false } = {}) {
143
150
  main.prepend(note);
144
151
  return note;
145
152
  }
153
+
154
+ /**
155
+ * The middle of a blank page (the page map's `blank`: the workbench's 新建页面, which no one has written yet):
156
+ * that an Agent writes it, any page it likes, with a request to copy and say to one. Returns the note, or
157
+ * null where none is shown (no blank page, or no main.gallery-page to show it in).
158
+ */
159
+ export function showBlank(map) {
160
+ const main = document.querySelector("main.gallery-page");
161
+ if (!map?.blank || !main) return null;
162
+ addStyle(STYLE);
163
+ const title = map.title ?? document.title;
164
+ const ask = t("用 Gallery 把「{0}」这个自定义页写出来:放上我这周的待办,和离月底还有几天。", title);
165
+ const copy = element("button", { type: "button", class: "ge-home-hint-copy", text: t("复制") });
166
+ const box = element("div", { class: "ge-home-hint-box" },
167
+ element("p", { class: "ge-home-hint-title", text: t("这一页还是空的") }),
168
+ element("p", { text: t("自定义页由 Agent 来写:可以是一页卡片,也可以是它为你做的任何网页。") }),
169
+ element("p", { class: "ge-home-hint-ask", text: t("跟 Agent 说你想在这里放什么,比如:") }),
170
+ element("div", { class: "ge-home-hint-say" }, element("span", { text: ask }), copy));
171
+ const note = noteOf("ge-page-blank", t("空白的自定义页"), box);
172
+ copy.addEventListener("mousedown", (event) => event.preventDefault());
173
+ copy.addEventListener("click", () => {
174
+ window.focus(); // as the first-open note's 复制: Chrome writes no clipboard of a document without the focus
175
+ navigator.clipboard?.writeText(ask).then(() => {
176
+ copy.textContent = t("已复制");
177
+ say(t("已复制这句话,发给 Agent 就行"));
178
+ }, () => say(t("没能复制:浏览器不允许写剪贴板。"), "error"));
179
+ });
180
+ main.append(note);
181
+ return note;
182
+ }
@@ -1,9 +1,8 @@
1
1
  import { GAP, GRID, cellAt, fitHeight, gridFor, moved, pushDown, sizeFor, spanOf } from "../../../shared/board-places.mjs";
2
2
  import { cellsOf } from "../../../shared/card-size.mjs";
3
3
  import { language, t } from "../../../shared/i18n/index.mjs";
4
- import { BLOCK_ICON, svg } from "../../components/icons.mjs";
4
+ import { BLOCK_ICON, SETTINGS_ICON, svg } from "../../components/icons.mjs";
5
5
  import { instanceAround, instanceOf, packageOf } from "../../components/instances.mjs";
6
- import { edgeScroll } from "../edge-scroll.mjs";
7
6
  import { gestureLock } from "../gesture.mjs";
8
7
  import { ICONS, createMenu } from "../menu.mjs";
9
8
  import { say } from "../notices.mjs";
@@ -12,7 +11,8 @@ import { tooltip } from "../tooltip.mjs";
12
11
  import { addStyle, element, mount } from "../ui.mjs";
13
12
  import { createClient } from "./client.mjs";
14
13
  import { createHistory } from "./history.mjs";
15
- import { showHint, showNews } from "./hint.mjs";
14
+ import { createEdgeScroll, createSettle, glide, isGliding, stopGlide } from "./motion.mjs";
15
+ import { showBlank, showHint, showNews } from "./hint.mjs";
16
16
  import { closeSettings, formOpen, openSettings } from "./settings.mjs";
17
17
 
18
18
  // A custom page's board edited (docs/render.md "A custom page"; the plan's §5.3, the spec §6 and §8):
@@ -31,20 +31,33 @@ import { closeSettings, formOpen, openSettings } from "./settings.mjs";
31
31
  // one that moves less, or a key (Enter, Space) on the grip, opens the card's menu: what is dragged
32
32
  // and selected in a card is its block's (DocKit.drag never moves a card). A bare card (its block's
33
33
  // card.bare) shows where it stands by a dashed line of the guides' gray.
34
- // - Moving. The card follows the pointer, and where it would go, the whole cells nearest to it
35
- // (shared/board-places.mjs cellAt), shows as a quiet spot (a fill of its own, no line; no guide
36
- // lines stand where its edge meets another card's); the cards it would meet are drawn pushed down
37
- // under it (pushDown, the service's own rule: DocBoard.draft), never up. Escape, or the pointer taken
38
- // away, puts all back; a drop sends `place`, and the board stands as the service answers. The rows
39
- // above a board's first card are not drawn (DocBoard.skipped): the pointer's row is the drawn one
40
- // plus the rows skipped as the drag began, so that a card let go where it was writes nothing and one
41
- // held above the first row goes above it (drawn at the top, the others under it).
34
+ // - The board while a card is moved or resized (the user's word of 2026-11-09): no landing shadow
35
+ // under it; the board's lattice drawn as faint grey dots, one where the gaps between four cells
36
+ // cross (the board's outer edge too), from the gesture's start to its end (.ge-board-dots, light
37
+ // and dark), and the card snapping to the cells it would land at as it comes within SNAP px of them,
38
+ // each axis on its own: where it snaps is where a drop puts it.
39
+ // - Its motion (./motion.mjs; the user's word of 2026-11-09, an iPhone's home-screen widgets): the card
40
+ // held follows the pointer at once and settles onto the cells it snaps to (or off them) on a spring in
41
+ // SETTLE ms, never a jump; the cards making way, the card let go (to its cells, or back on Escape), a
42
+ // resize's and 大小 ›'s, an undo's and a redo's glide from where they were seen to their new place on
43
+ // that spring (FLIP, GLIDE ms), each starting again from where it is seen when the target changes under
44
+ // it; near the window's top or bottom edge the page scrolls, timed by the frames, faster nearer the edge
45
+ // and never past FASTEST px a second. prefers-reduced-motion: no glide or settling.
46
+ // - Moving. The card follows the pointer, and where it would go is the whole cells nearest to it
47
+ // (shared/board-places.mjs cellAt), which the card itself is drawn at (DocBoard.draft), moved off
48
+ // them by the pointer's offset but within SNAP px of them on an axis, where it stands on them; the
49
+ // cards it would meet are drawn pushed down under it (pushDown, the service's own rule), never up.
50
+ // Escape, or the pointer taken away, puts all back; a drop sends `place`, and the board stands as
51
+ // the service answers. The rows above a board's first card are not drawn (DocBoard.skipped): the
52
+ // pointer's row is the drawn one plus the rows skipped as the drag began, so that a card let go
53
+ // where it was writes nothing and one held above the first row goes above it (drawn at the top, the
54
+ // others under it).
42
55
  // - Resizing. A card whose block says card.resize has a grip at its bottom-right: it follows the
43
- // pointer, the spot showing the size it snaps to, the nearest of card.sizes, else whole cells
44
- // between card.min and card.max (sizeFor), never past the row's end; one of fit content only
45
- // gets wider (its height is its content's); one whose manifest frees it (`any`) reaches every
46
- // whole size. The menu's 大小 › gives the listed sizes (the common ones), in a card of rows of
47
- // words alone, only as wide as they are.
56
+ // pointer, snapping to the size a drop gives (within SNAP px of its width or height), the nearest of
57
+ // card.sizes, else whole cells between card.min and card.max (sizeFor), never past the row's end;
58
+ // one of fit content only gets wider (its height is its content's); one whose manifest frees it
59
+ // (`any`) reaches every whole size. The menu's 大小 › gives the listed sizes (the common ones), in a
60
+ // card of rows of words alone, only as wide as they are.
48
61
  // - The card's menu (the grip's click): 设置… (a block with settings: ./settings.mjs, written by the
49
62
  // service's `settings`), 大小 ›, 删除 (`remove`).
50
63
  // - 添加自定义块: in a workbench (`hosted`) the workbench's top bar has its button, which asks by the
@@ -53,7 +66,8 @@ import { closeSettings, formOpen, openSettings } from "./settings.mjs";
53
66
  // centred, and adds to the page's FIRST board; a page open alone in a tab has the button of its own
54
67
  // at the top right of each board, above it. The library (the service's GET library: the blocks
55
68
  // whose places hold page and are not turned off, with their previews, Gallery's own not yet in the
56
- // workspace too, which the service puts in): the card put at the first free spot by its block's
69
+ // workspace too, which the service puts in), in a section of each pack under its name (no pack's
70
+ // name in grey after a block's): the card put at the first free spot by its block's
57
71
  // default size (`add`); the page is then shown again (the host's hidden-frame swap), which brings
58
72
  // the block's script and style.
59
73
  // - A block's own write (context.edit, the kit's cancellable doc-component-edit): taken here, its
@@ -88,12 +102,14 @@ import { closeSettings, formOpen, openSettings } from "./settings.mjs";
88
102
  const BOARD = ".gallery-board";
89
103
  const CARD = ".gallery-card";
90
104
  const THRESHOLD = 4; // px a press moves before it is a drag
105
+ const SNAP = 14; // px from the place (or the size) a drop gives within which a moved (or resized) card stands on it
106
+ const DOT = 3; // px of room around each of the board's dots while a card is moved or resized (their tile's corner)
91
107
  const GRIP = { size: 28, inset: 6 }; // the grip's box, and its room from the card's top and right edges (inside the card)
92
108
  const FIELDS = "input, textarea, select, [contenteditable]:not([contenteditable=false])";
93
109
 
94
110
  const DOTS = '<svg viewBox="0 0 10 16" aria-hidden="true"><circle cx="2.5" cy="2" r="1.25"/><circle cx="7.5" cy="2" r="1.25"/><circle cx="2.5" cy="8" r="1.25"/><circle cx="7.5" cy="8" r="1.25"/><circle cx="2.5" cy="14" r="1.25"/><circle cx="7.5" cy="14" r="1.25"/></svg>';
95
111
  const CORNER = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M13 6.5 6.5 13M13 10.5 10.5 13"/></svg>';
96
- const GEAR = svg('<circle cx="10" cy="10" r="2.25"/><path d="M10 3.25v1.5M10 15.25v1.5M3.25 10h1.5M15.25 10h1.5M5.2 5.2l1.06 1.06M13.74 13.74l1.06 1.06M5.2 14.8l1.06-1.06M13.74 6.26l1.06-1.06"/>');
112
+ const GEAR = SETTINGS_ICON;
97
113
  const SIZE = svg('<rect x="3.25" y="3.25" width="13.5" height="13.5" rx="2.5"/><path d="M10 3.25v13.5M3.25 10H10"/>');
98
114
  const BLOCK = BLOCK_ICON;
99
115
 
@@ -118,10 +134,21 @@ const STYLE = `
118
134
  .ge-board-add:hover { background: var(--ge-hover); color: var(--ge-tx); }
119
135
  .ge-board-add:active, .ge-board-add[data-pressed] { background: var(--ge-press); color: var(--ge-tx); }
120
136
  .ge-board-add svg { display: block; width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
121
- .gallery-board > .ge-board-spot { box-sizing: border-box; grid-column: calc(var(--gallery-x, 0) + 1) / span var(--gallery-w, 4); grid-row: calc(var(--gallery-y, 0) + 1) / span var(--gallery-h, 2);
122
- border-radius: var(--gallery-card-radius, 12px); background: light-dark(rgb(55 53 47 / 7%), rgb(255 255 255 / 8%)); pointer-events: none; }
123
- .gallery-board > .gallery-card[data-ge-lifted] { z-index: 3; box-shadow: var(--ge-shadow-pop); cursor: grabbing; }
124
- .gallery-board > .gallery-card[data-ge-sizing] { z-index: 3; }
137
+ /* The board's lattice while a card is moved or resized: a faint grey dot where the gaps between four cells cross, under the cards (first in the board, which stands as their containing block meanwhile), placed by dotsOn. */
138
+ .gallery-board[data-ge-dotted] { position: relative; }
139
+ .gallery-board > .ge-board-dots { position: absolute; pointer-events: none; background-image: radial-gradient(circle at ${DOT}px ${DOT}px, light-dark(rgb(55 53 47 / 24%), rgb(255 255 255 / 20%)) 0 1.1px, transparent 1.6px); background-repeat: repeat; animation: ge-board-dots-in .14s ease-out; }
140
+ @keyframes ge-board-dots-in { from { opacity: 0; } }
141
+ @media (prefers-reduced-motion: reduce) { .gallery-board > .ge-board-dots { animation: none; } }
142
+ /* The card held lifts: its own shadow kept as it is, layer for layer, and a soft lift added after it, so
143
+ that only the lift eases in and out (a menu's shadow in its place made each of the card's layers turn
144
+ into another's, the shadow swelling and shrinking on the way: the user, 2026-11-09); a frameless card
145
+ takes the lift alone. One let go stays over the others while it glides to its cells. */
146
+ .gallery-board { --ge-board-lift: 0 10px 28px -10px light-dark(rgb(15 15 15 / 16%), rgb(0 0 0 / 55%)), 0 3px 10px -4px light-dark(rgb(15 15 15 / 8%), rgb(0 0 0 / 30%)); }
147
+ .gallery-board > .gallery-card[data-ge-lifted], .gallery-board > .gallery-card[data-ge-landing] { transition: box-shadow .2s ease-out; }
148
+ .gallery-board > .gallery-card[data-ge-lifted] { z-index: 3; box-shadow: var(--gallery-card-shadow), var(--ge-board-lift); cursor: grabbing; }
149
+ .gallery-board > .gallery-card[data-ge-bare][data-ge-lifted] { box-shadow: var(--ge-board-lift); }
150
+ .gallery-board > .gallery-card[data-ge-sizing], .gallery-board > .gallery-card[data-ge-landing] { z-index: 3; }
151
+ @media (prefers-reduced-motion: reduce) { .gallery-board > .gallery-card[data-ge-lifted], .gallery-board > .gallery-card[data-ge-landing] { transition: none; } }
125
152
  .gallery-board > .gallery-card[data-ge-bare][data-ge-hover] { outline: 1px dashed var(--ge-guide); outline-offset: 2px; }
126
153
  /* The card the grip stands in: a head's quiet note in its corner fades (the kit's --gallery-grip-shown, board.css). */
127
154
  .gallery-board > .gallery-card[data-ge-hover] { --gallery-grip-shown: 1; }
@@ -218,7 +245,7 @@ export function startBoard({ map, reload, hosted = false }) {
218
245
  };
219
246
  const drawn = (card) => globalThis.DocBoard?.drawn(card) ?? cellsOf(card);
220
247
  // How many rows above its first card the board does not draw (the kit's DocBoard.skipped): its drawn row
221
- // r is the file's row r + skipped, which the spot, a dragged card's place and a drop are reckoned by.
248
+ // r is the file's row r + skipped, which a dragged card's place and a drop are reckoned by.
222
249
  const skipped = (board) => globalThis.DocBoard?.skipped?.(board) ?? 0;
223
250
  const draft = (board, cells) => globalThis.DocBoard?.draft(board, cells);
224
251
 
@@ -226,16 +253,20 @@ export function startBoard({ map, reload, hosted = false }) {
226
253
  // has not, or one the page has that it does not name, is someone else's doing: the page is shown again.
227
254
  function took(answer, { own = [] } = {}) {
228
255
  const named = new Set();
229
- for (const card of answer.cards ?? []) {
230
- named.add(card.id);
231
- const node = cardById(card.id);
232
- if (!node) {
233
- if (!own.includes(card.id) && !leaving.has(card.id)) wantSwap(); // one the page took out meanwhile, its remove asked, is its own
234
- continue;
256
+ // Where the page drew a card elsewhere than the service put it, it glides there (a held card follows the pointer).
257
+ glide(allCards(), () => {
258
+ for (const card of answer.cards ?? []) {
259
+ named.add(card.id);
260
+ const node = cardById(card.id);
261
+ if (!node) {
262
+ if (!own.includes(card.id) && !leaving.has(card.id)) wantSwap(); // one the page took out meanwhile, its remove asked, is its own
263
+ continue;
264
+ }
265
+ setCells(node, card);
266
+ node.dataset.geBase = card.base;
235
267
  }
236
- setCells(node, card);
237
- node.dataset.geBase = card.base;
238
- }
268
+ if (!gesture) for (const board of boards()) globalThis.DocBoard?.layout(board);
269
+ }, { still: gesture?.card ?? null, settled: placeAgain });
239
270
  if (allCards().some((card) => !named.has(card.dataset.cardId))) wantSwap();
240
271
  history.know(basesNow());
241
272
  }
@@ -403,7 +434,8 @@ export function startBoard({ map, reload, hosted = false }) {
403
434
  if (resizable(card) && sizes.length > 1) {
404
435
  items.push({
405
436
  label: t("大小"), icon: SIZE, icons: false, compact: true,
406
- submenu: () => sizes.map(([w, h]) => ({ label: `${w} × ${h}`, current: w === cells.w && h === cells.h, run: () => placeCard(card, { ...cells, w, h }) })),
437
+ // By width, then height (the user's word of 2026-11-09; the manifest lists its default first).
438
+ submenu: () => [...sizes].sort(([aw, ah], [bw, bh]) => aw - bw || ah - bh).map(([w, h]) => ({ label: `${w} × ${h}`, current: w === cells.w && h === cells.h, run: () => layOut(board, () => placeCard(card, { ...cells, w, h }), { lift: card }) })),
407
439
  });
408
440
  }
409
441
  if (items.length) items.push({ separator: true });
@@ -435,37 +467,58 @@ export function startBoard({ map, reload, hosted = false }) {
435
467
  }, true);
436
468
 
437
469
  // ---------- moving and resizing ----------
438
- const spot = element("div", { class: "ge-board-spot", "data-doc-dev": "" });
439
- // Near the window's top or bottom edge a drag or a resize scrolls the page (the editor's edge-scroll.mjs
440
- // rule, faster the nearer the edge), each frame, and follows what came into view.
470
+ const dots = element("div", { class: "ge-board-dots", "data-doc-dev": "", "aria-hidden": "true" });
471
+ // The board's dots over all it draws now (its rows grow as a draft puts a card lower), each at a point of
472
+ // its lattice: where the gaps between four cells cross, and as far out on its edges, a DOT px room
473
+ // around each (the tile's corner the gradient draws it in).
474
+ const dotsOn = (board) => {
475
+ const { rect, step } = geometry(board);
476
+ const box = board.getBoundingClientRect();
477
+ const columns = Math.round((rect.right - rect.left + GAP) / step);
478
+ const rows = Math.max(1, Math.round((board.clientHeight + GAP) / step));
479
+ Object.assign(dots.style, {
480
+ left: `${rect.left - box.left - board.clientLeft - GAP / 2 - DOT}px`, top: `${-GAP / 2 - DOT}px`,
481
+ width: `${columns * step + 2 * DOT}px`, height: `${rows * step + 2 * DOT}px`, backgroundSize: `${step}px ${step}px`,
482
+ });
483
+ };
484
+ // Each frame of a gesture: near the window's top or bottom edge the page scrolls (./motion.mjs
485
+ // createEdgeScroll: timed by the frames, faster nearer the edge, never past FASTEST px a second) and the
486
+ // gesture follows what came into view; a card settling onto (or off) what it snaps to is drawn again.
441
487
  let pointer = null;
442
488
  let scrolling = 0;
443
- const scrollNear = () => {
489
+ const edge = createEdgeScroll();
490
+ const frame = () => {
444
491
  scrolling = 0;
445
492
  if (!gesture || !pointer) return;
446
- if (edgeScroll(pointer.clientX, pointer.clientY)) follow(pointer);
447
- scrolling = requestAnimationFrame(scrollNear);
448
- };
449
- // The spot at cells of the file, drawn where its board draws them.
450
- const placeSpot = (board, cells) => {
451
- for (const axis of ["x", "y", "w", "h"]) spot.style.setProperty(`--gallery-${axis}`, String(axis === "y" ? cells.y - skipped(board) : cells[axis]));
493
+ if (edge.frame(pointer.clientY, performance.now())) follow(pointer);
494
+ else if (gesture.settle.gliding()) draw();
495
+ scrolling = requestAnimationFrame(frame);
452
496
  };
497
+ // The cards of `board` laid out by `change` (cells set), those it moves gliding there (./motion.mjs), the grip
498
+ // put again on its card once they stand.
499
+ const layOut = (board, change, options = {}) => glide(cardsIn(board), () => {
500
+ const result = change();
501
+ globalThis.DocBoard?.layout(board);
502
+ return result;
503
+ }, { settled: placeAgain, ...options });
453
504
 
454
505
  function begin(kind, card, event) {
506
+ stopGlide(card); // still gliding from a drop: taken up where its cells are
455
507
  const board = card.parentElement;
456
508
  const file = cellsOf(card);
457
509
  const rect = card.getBoundingClientRect();
458
510
  // skip: the rows the board skipped as the gesture began, which the kit keeps while its draft is shown
459
511
  // (a card held above the first row skips fewer): the pointer's row is reckoned by it throughout.
460
- gesture = { kind, card, board, file, skip: skipped(board), target: { ...file }, grab: { x: event.clientX - rect.left, y: event.clientY - rect.top }, start: { x: event.clientX, y: event.clientY, width: rect.width, height: rect.height, scroll: scrollY } };
512
+ gesture = { kind, card, board, file, skip: skipped(board), target: { ...file }, grab: { x: event.clientX - rect.left, y: event.clientY - rect.top }, start: { x: event.clientX, y: event.clientY, width: rect.width, height: rect.height, scroll: scrollY }, settle: createSettle(SNAP), size: null };
461
513
  document.documentElement.setAttribute(kind === "move" ? "data-ge-dragging" : "data-ge-sizing", "");
462
514
  card.setAttribute(kind === "move" ? "data-ge-lifted" : "data-ge-sizing", "");
463
- placeSpot(board, drawn(card));
464
- board.append(spot);
515
+ board.setAttribute("data-ge-dotted", "");
516
+ board.prepend(dots);
517
+ dotsOn(board);
465
518
  grip.removeAttribute("data-shown");
466
519
  corner.removeAttribute("data-shown");
467
520
  follow(event);
468
- scrolling = requestAnimationFrame(scrollNear);
521
+ scrolling = requestAnimationFrame(frame);
469
522
  }
470
523
  function follow(event) {
471
524
  const { kind, card, board, file, skip, grab, start } = gesture;
@@ -479,7 +532,7 @@ export function startBoard({ map, reload, hosted = false }) {
479
532
  target = { ...file, x: clamp(cellAt(left, cell), 0, 12 - file.w), y: Math.max(0, cellAt(top, cell) + skip) };
480
533
  } else {
481
534
  // The card follows the pointer between the least and the most it can snap to (its listed sizes', or
482
- // its min and max), never past its row's end; the spot shows what it snaps to.
535
+ // its min and max), never past its row's end, standing at the size a drop gives where it is near it.
483
536
  const block = infoOf(card)?.card ?? {};
484
537
  const listed = block.any ? null : block.sizes?.filter(([w]) => file.x + w <= 12); // a card its block frees (any) takes every whole size
485
538
  const least = listed?.length ? { w: Math.min(...listed.map(([w]) => w)), h: Math.min(...listed.map(([, h]) => h)) } : { w: block.min?.w ?? 1, h: block.min?.h ?? 1 };
@@ -490,36 +543,59 @@ export function startBoard({ map, reload, hosted = false }) {
490
543
  const asked = { w: Math.max(1, Math.round((width + GAP) / step)), h: Math.max(1, Math.round((height + GAP) / step)) };
491
544
  const size = listed?.length ? sizeFor({ sizes: listed }, asked) : sizeFor(block, { w: Math.min(asked.w, 12 - file.x), h: asked.h }, 12 - file.x);
492
545
  target = { ...file, w: size.w, h: fits ? file.h : size.h };
493
- card.style.width = `${Math.round(width)}px`;
494
- if (!fits) card.style.height = `${Math.round(height)}px`;
546
+ gesture.size = { width, height, w: spanOf(size.w, cell), h: spanOf(size.h, cell), fits };
495
547
  }
496
548
  if (!cellsSame(target, gesture.target) || !gesture.drafted) {
497
549
  gesture.target = target;
498
550
  gesture.drafted = true;
499
- draft(board, new Map([[card, target]]));
500
- placeSpot(board, drawn(card));
551
+ // The cards it meets make way, gliding (the held card follows the pointer: draw).
552
+ glide(cardsIn(board), () => draft(board, new Map([[card, target]])), { still: card });
501
553
  }
554
+ draw();
555
+ dotsOn(board);
556
+ }
557
+ // The held card drawn where the pointer has it: moved, at the cells it would land at (the draft's), off them
558
+ // by the pointer's offset but where that is within SNAP px; resized, at the pointer's size but where that is
559
+ // within SNAP px of the size a drop gives. Each axis settles onto (or off) what it snaps to (createSettle).
560
+ function draw() {
561
+ const { kind, card, board, grab, settle, size } = gesture;
562
+ const time = performance.now();
502
563
  if (kind === "move") {
564
+ const { rect, step } = geometry(board);
503
565
  const now = drawn(card);
504
- card.style.translate = `${Math.round(event.clientX - grab.x - rect.left - now.x * step)}px ${Math.round(event.clientY - grab.y - rect.top - (now.y - skipped(board)) * step)}px`;
566
+ const cells = { x: now.x * step, y: (now.y - skipped(board)) * step };
567
+ const x = settle.at("x", pointer.clientX - grab.x - rect.left, cells.x, time);
568
+ const y = settle.at("y", pointer.clientY - grab.y - rect.top, cells.y, time);
569
+ card.style.translate = `${Math.round(x - cells.x)}px ${Math.round(y - cells.y)}px`;
570
+ } else {
571
+ card.style.width = `${Math.round(settle.at("w", size.width, size.w, time))}px`;
572
+ if (!size.fits) card.style.height = `${Math.round(settle.at("h", size.height, size.h, time))}px`;
505
573
  }
506
574
  }
507
575
  function finish(drop) {
508
576
  const done = gesture;
509
577
  gesture = null;
510
578
  cancelAnimationFrame(scrolling);
579
+ edge.stop();
511
580
  pointer = null;
512
581
  const { card, board, file, target, kind } = done;
513
582
  document.documentElement.removeAttribute("data-ge-dragging");
514
583
  document.documentElement.removeAttribute("data-ge-sizing");
584
+ card.setAttribute("data-ge-landing", ""); // its lifted shadow eases out (and it stays over the others while it glides)
515
585
  card.removeAttribute("data-ge-lifted");
516
586
  card.removeAttribute("data-ge-sizing");
517
- card.style.translate = "";
518
- card.style.width = "";
519
- card.style.height = "";
520
- spot.remove();
521
- if (drop && !cellsSame(target, file)) placeCard(card, target, { kind });
522
- draft(board, null);
587
+ // Let go: it glides from where it is seen to its cells (dropped) or back to its own (Escape), over the
588
+ // others meanwhile, and they to theirs.
589
+ glide(cardsIn(board), () => {
590
+ card.style.translate = "";
591
+ card.style.width = "";
592
+ card.style.height = "";
593
+ dots.remove();
594
+ board.removeAttribute("data-ge-dotted");
595
+ if (drop && !cellsSame(target, file)) placeCard(card, target, { kind });
596
+ draft(board, null);
597
+ }, { lift: card, settled: placeAgain });
598
+ if (!isGliding(card)) setTimeout(() => { if (!isGliding(card)) card.removeAttribute("data-ge-landing"); }, 220);
523
599
  trySwap();
524
600
  hideSoon(); // the grip comes back where the pointer is
525
601
  }
@@ -722,14 +798,26 @@ export function startBoard({ map, reload, hosted = false }) {
722
798
  say(t("连不上本地服务"), "error");
723
799
  return false;
724
800
  }
725
- const rows = (text = "") => listed.filter((block) => {
726
- const words = `${said(block.name)} ${said(block.description)} ${block.id}`.toLowerCase();
727
- return !text.trim() || words.includes(text.trim().toLowerCase());
728
- }).map((block) => ({
729
- label: said(block.name) || block.id, icon: BLOCK, hint: block.installed ? `${block.size.w} × ${block.size.h}` : t("自带"), ...(block.of ? { of: block.of } : {}),
730
- ...(block.preview || block.description ? { preview: block.preview ? { src: block.preview, says: said(block.description) } : { says: said(block.description) } } : {}),
731
- run: () => addBlock(board, block),
732
- }));
801
+ // A section of each pack (the user's word of 2026-11-09), its name over its blocks, in the service's order
802
+ // (the packs as 设置 › 组件列表 has them, each's blocks by name); what is typed keeps the sections it finds blocks in.
803
+ const rows = (text = "") => {
804
+ const items = [];
805
+ let pack;
806
+ for (const block of listed) {
807
+ const words = `${said(block.name)} ${said(block.description)} ${block.id}`.toLowerCase();
808
+ if (text.trim() && !words.includes(text.trim().toLowerCase())) continue;
809
+ if (block.pack?.id !== pack) {
810
+ pack = block.pack?.id;
811
+ if (pack) items.push({ title: said(block.pack.name) || pack });
812
+ }
813
+ items.push({
814
+ label: said(block.name) || block.id, icon: BLOCK, hint: block.installed ? `${block.size.w} × ${block.size.h}` : t("自带"),
815
+ ...(block.preview || block.description ? { preview: block.preview ? { src: block.preview, says: said(block.description) } : { says: said(block.description) } } : {}),
816
+ run: () => addBlock(board, block),
817
+ });
818
+ }
819
+ return items;
820
+ };
733
821
  button?.setAttribute("data-pressed", "");
734
822
  const closed = () => menu.close();
735
823
  if (!button) addEventListener("blur", closed);
@@ -773,7 +861,7 @@ export function startBoard({ map, reload, hosted = false }) {
773
861
  });
774
862
 
775
863
  // ---------- 恢复默认卡片 ----------
776
- // The home page's default cards it misses put back (the service's restore-defaults), a task as every
864
+ // The default cards a page with them misses put back (the service's restore-defaults), a task as every
777
865
  // op is: one step of undo for all of them (a group of adds), the page shown again with their blocks'
778
866
  // scripts and styles. Gives the service's answer ({ ok, added: [{ card, block, name }], skipped }),
779
867
  // which the workbench says, or its refusal.
@@ -822,7 +910,7 @@ export function startBoard({ map, reload, hosted = false }) {
822
910
  const cards = step.cards.filter(({ id }) => cardById(id));
823
911
  if (cards.length === 0) return false;
824
912
  const to = way === "undo" ? "before" : "after";
825
- for (const one of cards) setCells(cardById(one.id), one[to]);
913
+ layOut(cardById(cards[0].id).parentElement, () => { for (const one of cards) setCells(cardById(one.id), one[to]); });
826
914
  const answer = await send({ op: "arrange", cards: cards.map((one) => ({ card: one.id, ...one[to], base: cardById(one.id).dataset.geBase })) });
827
915
  if (!answer?.ok) return failed(answer) ?? false;
828
916
  took(answer);
@@ -960,10 +1048,11 @@ export function startBoard({ map, reload, hosted = false }) {
960
1048
  watch.observe(document.documentElement, { attributes: true, attributeFilter: ["data-doc-drawn"] });
961
1049
  }
962
1050
  }
963
- // The home page's first-open note, while its map says so, and its note of new default cards (put in as
964
- // 恢复默认卡片 puts them: restoreDefaults).
1051
+ // The page's first-open note, while its map says so, its note of new default cards (put in as
1052
+ // 恢复默认卡片 puts them: restoreDefaults), and the middle of a blank page (an Agent writes it).
965
1053
  if (map.hint) showHint(map, { hosted });
966
1054
  if (map.news) showNews(map, { restore: restoreDefaults });
1055
+ if (map.blank) showBlank(map);
967
1056
  // Its blocks may write from now on (the kit's context.canEdit, editable).
968
1057
  dispatchEvent(new Event("doc-editable"));
969
1058
  return { swap: wantSwap, undo, redo, idle: () => Promise.all([client.idle(), todos.idle()]), restoreDefaults, undoRestore, addCard };