@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
@@ -69,7 +69,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
69
69
  typing.time = at;
70
70
  } else {
71
71
  closeTyping();
72
- typing = { type: "text", element: host, before, after, time: at };
72
+ typing = { type: "text", element: host, before, after, time: at, began: at };
73
73
  }
74
74
  changed(now);
75
75
  }
@@ -83,8 +83,14 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
83
83
  changed(true);
84
84
  }
85
85
 
86
+ // A run of typing closed: a step of its own, unless it went into the block the last step made for
87
+ // what was typed (struct's `typedInto`: a paragraph after a block, after/), begun within a second of it:
88
+ // then it is part of that step, which undo takes away whole, redo putting the words back with it.
86
89
  function closeTyping() {
87
- if (typingChanged()) push(typing);
90
+ const last = undoStack.at(-1);
91
+ if (typingChanged() && last?.typedInto === typing.element && typing.began - last.madeAt < 1000) last.htmlAfter.set(typing.element, typing.after);
92
+ else if (typingChanged()) push(typing);
93
+ if (typing && last?.typedInto) last.typedInto = null;
88
94
  typing = null;
89
95
  }
90
96
 
@@ -151,7 +157,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
151
157
  // An attribute the file gave another value than the page had (`changes`: [element, name, from, to],
152
158
  // `to` null for none; a key of a table a commit gave a pair anew: page/commit.mjs apply), the page's
153
159
  // element set so: the steps that recorded `from` of it record `to` instead, so that an undo or a redo
154
- // puts back what the file has, never the value it changed (a table's 视图 › redone with the key its view
160
+ // puts back what the file has, never the value it changed (a view made from "/" redone with the key its view
155
161
  // no longer carries).
156
162
  function rekeyed(changes) {
157
163
  if (changes.length === 0) return;
@@ -162,6 +168,14 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
162
168
  if (values?.some(([each, value]) => each === name && value === from)) recorded.set(element, values.map(([each, value]) => [each, each === name && value === from ? to : value]));
163
169
  }
164
170
  }
171
+ // A view's table chosen in its 设置 is a step of look (installed/page.mjs readTable): its attributes by element.
172
+ if (step.type === "look") {
173
+ for (const recorded of [step.before, step.after]) {
174
+ step.elements.forEach((each, index) => {
175
+ for (const [element, name, from, to] of changes) if (each === element && recorded[index]?.attributes?.[name] === from) recorded[index].attributes[name] = to;
176
+ });
177
+ }
178
+ }
165
179
  }
166
180
  }
167
181
 
@@ -194,7 +208,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
194
208
  // spans and widths, which its operations change: components/table/page.mjs). `caret`: where the caret
195
209
  // stood in the first text block it touched is kept too, which undo puts back (notion-lite's Enter in the
196
210
  // page's title, enter.js: "撤销后光标回到回车前的位置"; typing/ enterBody).
197
- function struct(parents, touched, mutate, { attributes = [], caret = false } = {}) {
211
+ function struct(parents, touched, mutate, { attributes = [], caret = false, typedInto = null } = {}) {
198
212
  closeTyping();
199
213
  // What the change may leave untidy is tidied after it by the components that know (rows of
200
214
  // columns with a column emptied, lists set side by side: tidy), so what they name is recorded
@@ -246,6 +260,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
246
260
  selectAfter: result.selectAfter ?? null, // what redo selects instead
247
261
  caret: result.caret ?? null, // where redo puts the caret when it selects nothing (tidyEmptied)
248
262
  settle: result.settle ?? null,
263
+ typedInto, madeAt: Date.now(), // the block made for what is typed, whose first run of typing is part of this step (closeTyping)
249
264
  });
250
265
  result.touched.forEach((node) => lastKnown.set(node, ownHTML(node)));
251
266
  note("history", "change");
@@ -1,10 +1,10 @@
1
1
  import { OUTER, UNIT, blocksIn, boxKind, heldIn, newHost, newTail, placeBefore, unitElement, withLeads } from "../core.mjs";
2
2
  import { containerType } from "../convert.mjs";
3
3
  import { begin, done, made } from "../made.mjs";
4
- import { selectsBeside } from "../beside.mjs";
4
+ import { caretBeside } from "../beside.mjs";
5
5
  import { caretAt, nearestOnLine, placeCaret, selectAt } from "../caret.mjs";
6
6
  import { sectionOps } from "../page-objects.mjs";
7
- import { contentOf, endOf, hoverAt, innerBox, measure } from "../geometry.mjs";
7
+ import { endOf, hoverAt, innerBox, measure } from "../geometry.mjs";
8
8
  import { BOXED, HOLDS_ONE, accepts } from "../../components/registry.mjs";
9
9
  import { DIALOG } from "../../components/kit.mjs";
10
10
 
@@ -18,9 +18,9 @@ import { DIALOG } from "../../components/kit.mjs";
18
18
 
19
19
  /**
20
20
  * struct(parents, touched, mutate): the editor's undoable structural change (history.mjs). blocks():
21
- * the block tools (blocks.mjs).
21
+ * the block tools (blocks.mjs). after: the caret after a block (after/).
22
22
  */
23
- export function createInsert({ struct, blocks }) {
23
+ export function createInsert({ struct, blocks, after }) {
24
24
  // An empty paragraph right before the block of a text block, the caret staying where it is.
25
25
  // Returns whether one could go there.
26
26
  function paragraphBefore(host) {
@@ -54,10 +54,10 @@ export function createInsert({ struct, blocks }) {
54
54
  // (geometry.mjs endOf). Under the last block of a box whose end is its own padding (endIsPadding: a
55
55
  // highlight block, a callout, a disclosure's content; the user's, 2026-11-06, as notion-lite's click-blank.js
56
56
  // makes a block only below the page and in a column) it is a gap like any other, below. Right of
57
- // the text area a boxed block (a code block, a callout, a file…: not text) or a row of columns
58
- // stands in, at its height (boxedBeside), that block selected, so Delete takes it and Enter puts a
59
- // paragraph after it; between a narrower block's right edge and its column's, as beside any block
60
- // of no text (below). Elsewhere beside text, the caret goes into the text block on that line (the nearest of those
57
+ // a boxed block (a code block, a callout, a file…: not text) or a row of columns, at its height
58
+ // (boxedBeside), the caret after it, at its bottom right (after/: Backspace deletes it, Enter or a
59
+ // key makes a paragraph after it; the user's, 2026-11-09: a click there selected it before).
60
+ // Elsewhere beside text, the caret goes into the text block on that line (the nearest of those
61
61
  // side by side), where the line is nearest the click (Notion's way); in the gap between two
62
62
  // blocks, or beside a block of no text, into the nearer of the blocks around it that is text, in the
63
63
  // innermost box at that height (boxBeside: the press may be on the page's margin, the block in a
@@ -82,7 +82,7 @@ export function createInsert({ struct, blocks }) {
82
82
  if (end) return newParagraph(end.box, end.after);
83
83
  const top = y + scrollY;
84
84
  const beside = boxedBeside(main, x, y, target);
85
- if (beside) return blocks().select(beside);
85
+ if (beside) return after.place(beside);
86
86
  const host = hoverAt(measure(main, { top, bottom: top }), x + scrollX, top, { reach: Infinity, skip: (unit) => !unit.text });
87
87
  if (host) return caretNear(host, x, y);
88
88
  for (let around = boxBeside(box, x, y); around; around = around === box ? null : around.parentElement.closest("[data-ge-box]")) {
@@ -157,26 +157,24 @@ export function createInsert({ struct, blocks }) {
157
157
  const texts = [unit, ...unit.querySelectorAll("[data-ge-block], [data-ge-new]")].filter((node) => node.isContentEditable && node.getClientRects().length > 0 && !node.closest("details:not([open]) > :not(summary)"));
158
158
  return (last ? texts.at(-1) : texts[0]) ?? null;
159
159
  }
160
- // A boxed block or a row of columns at height `y` with (x, y) right of the text area it stands in
161
- // (the content box of its box: the page's column, a column of 分栏, a section's, a quote's, a
162
- // highlight block's or a callout's, a disclosure's content) and right of the block itself (one
163
- // wider than that area), or null: in the page's margin, or in the padding of the box it stands in;
164
- // never between a narrower block's right edge and its area's (a table, a picture: the user's,
165
- // 2026-11-07, a click there selected it), nor in another text area (the blank box `target` the
166
- // press was on holds the block: a column further right is no place beside one in the column left
167
- // of it). The innermost, but never a block inside a row of columns the click is to the right of too
168
- // (the row is selected whole): past a column's edge is the gap to the next column, whose press is
169
- // the gap's (columns/: it drags, a click alone changes nothing), or past the row, which is selected.
160
+ // A boxed block or a row of columns at height `y` with (x, y) right of it: between its right edge
161
+ // and the text area's it stands in (a table, a picture narrower than its column: the user's,
162
+ // 2026-11-09, the caret after it where a click there once selected it, 2026-11-07, and later went to
163
+ // the nearest text), in the padding of the box it stands in (a highlight block's, a callout's) or in the
164
+ // page's margin; or null. Never in another text area (the blank box `target` the press was on holds the
165
+ // block: a column further right is no place beside one in the column left of it). The innermost,
166
+ // but never a block inside a row of columns the click is to the right of too (the caret goes after
167
+ // the row): past a column's edge is the gap to the next column, whose press is the gap's (columns/:
168
+ // it drags, a click alone changes nothing), or past the row.
170
169
  function boxedBeside(main, x, y, target) {
171
170
  let found = null;
172
171
  const right = (unit) => {
173
172
  const rect = unit.getBoundingClientRect();
174
173
  if (y < rect.top || y > rect.bottom || (target?.contains && !target.contains(unit))) return false;
175
- const box = unit.parentElement;
176
- return x > Math.max(rect.right, contentOf(box.getBoundingClientRect(), getComputedStyle(box)).right);
174
+ return x > rect.right;
177
175
  };
178
176
  for (const unit of main.querySelectorAll(`[data-ge-unit-kind]`)) {
179
- if (!selectsBeside(unit) || !unit.parentElement?.hasAttribute("data-ge-box") || unit.parentElement.closest("details:not([open])")) continue;
177
+ if (!caretBeside(unit) || !unit.parentElement?.hasAttribute("data-ge-box") || unit.parentElement.closest("details:not([open])")) continue;
180
178
  if (!right(unit)) continue;
181
179
  const row = unit.parentElement.closest('[data-ge-unit-kind="columns"]');
182
180
  if (row && main.contains(row) && right(row)) continue;
@@ -86,8 +86,8 @@ const STYLE = `
86
86
  /* A note wraps at the card's width and never widens it (no width of its own: contain). */
87
87
  .ge-pop-note { contain: inline-size; padding: 0 12px 6px 40px; color: var(--ge-tx3); font-size: 12px; line-height: 1.5; }
88
88
  .ge-pop-empty { padding: 8px 12px; color: var(--ge-tx3); }
89
- /* What a row makes, shown beside it under the pointer (a component package's preview.svg). */
90
- .ge-pop-preview { display: block; width: 240px; max-height: 180px; border-radius: 4px; object-fit: contain; background: var(--ge-bg); }
89
+ /* What a row makes, shown beside it under the pointer (a component package's preview.svg), in the middle of the bubble: its description under it wraps wider than the picture. */
90
+ .ge-pop-preview { display: block; width: 240px; max-width: 100%; max-height: 180px; margin: 0 auto; border-radius: 4px; object-fit: contain; object-position: center; background: var(--ge-bg); }
91
91
  .ge-pop-search { position: sticky; top: 0; z-index: 1; padding: 8px 8px 6px; background: var(--ge-bg); }
92
92
  .ge-pop-search input { box-sizing: border-box; width: 100%; height: 28px; margin: 0; padding: 0 8px; border: 0; border-radius: 6px; outline: none; background: var(--ge-hover); color: var(--ge-tx); font: inherit; box-shadow: inset 0 0 0 1px var(--ge-line); }
93
93
  .ge-pop-search input:focus { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ge-tx3) 60%, transparent); }
@@ -145,7 +145,9 @@ export const menuOpen = () => opened.size > 0;
145
145
  * as its rows (FIT; SEARCHING with a search field). around: the rect of the block a handle
146
146
  * (the anchor) stands for; the menu keeps clear of that block: left of it beside the handle, else
147
147
  * below or above it (scrolling inside when only part of it fits), and over it only when the block
148
- * leaves no room at all. side: "right": beside the anchor, right of it (left of it when there is no
148
+ * leaves no room at all; a tall block (more than TALL high: a table, a formula, a highlight, an image)
149
+ * has it beside the handle, left of it or, with no room there,
150
+ * right of it over the block, centered on the handle, never over the handle. side: "right": beside the anchor, right of it (left of it when there is no
149
151
  * room), level with it (a section's bar). Without either the menu stands below the anchor (the
150
152
  * caret), or above it when there is no room below; align: "end", its right edge at the anchor's.
151
153
  * icons: false, rows of words alone (no column for icons: a code block's languages; a row that opens a
@@ -276,6 +278,7 @@ export function createMenu() {
276
278
  }
277
279
 
278
280
  const clamp = (value, low, high) => Math.max(low, Math.min(high, value));
281
+ const TALL = 34; // px: a block taller than this (a line of text is 26px) is a tall one: its menu may stand over it
279
282
 
280
283
  function place() {
281
284
  if (options.inside) return;
@@ -324,11 +327,16 @@ export function createMenu() {
324
327
  const below = innerHeight - margin - (block.bottom + gap);
325
328
  const above = block.top - gap - margin;
326
329
  const beside = anchor.left - gap - width;
327
- if (beside >= margin) {
328
- // Left of the block, centered on its handle.
330
+ // A block of more than a line (a table, a formula, a highlight, an image: taller than TALL) may
331
+ // have the menu over it: beside its handle, right of it where there is no room left of it,
332
+ // rather than below the block, away from the handle.
333
+ const middle = (anchor.top + anchor.bottom) / 2;
334
+ const onRight = block.bottom - block.top > TALL && anchor.right + gap + width <= innerWidth - margin;
335
+ if (beside >= margin || onRight) {
336
+ // Beside the handle (never over it), left first, centered on it.
329
337
  fit(room);
330
- left = beside;
331
- top = clamp((anchor.top + anchor.bottom) / 2 - height / 2, margin, innerHeight - margin - height);
338
+ left = beside >= margin ? beside : anchor.right + gap;
339
+ top = clamp(middle - height / 2, margin, innerHeight - margin - height);
332
340
  } else if (Math.max(below, above) >= Math.min(height, 180)) {
333
341
  // Below the block, else above it; in the larger of the two when it fits in neither.
334
342
  left = clamp(anchor.left, margin, innerWidth - width - margin);
@@ -36,14 +36,10 @@ const variants = () => Object.keys(TYPE_COMPONENT).flatMap((type) => Object.entr
36
36
 
37
37
  /**
38
38
  * The keys of the variants the menus of blocks offer now ("/", 在下方新建, the handle's +, 转换成): every
39
- * one but a view of a table (`readsTables`: a custom block whose needs hold "tables"), which a table's
40
- * 视图 › alone offers, as it puts one in bound to that table (viewKeys): one put in anywhere else would
41
- * only say it has no table to read.
39
+ * one, a view of a table (`readsTables`: a custom block whose needs hold "tables") too, which binds
40
+ * itself to the table above it as it is made (components/installed/page.mjs; page/table/binding.mjs).
42
41
  */
43
- export const variantKeys = () => variants().filter(([, row]) => !row.readsTables).map(([key]) => key);
44
-
45
- /** The keys of the variants that are views of a table, for a table's 视图 › (table/views.mjs). */
46
- export const viewKeys = () => variants().filter(([, row]) => row.readsTables).map(([key]) => key);
42
+ export const variantKeys = () => variants().map(([key]) => key);
47
43
 
48
44
  /** The page objects, in the order they are made. */
49
45
  export const pageObjects = () => Object.values(pages);
@@ -2,7 +2,7 @@ import { t } from "../../shared/i18n/index.mjs";
2
2
  import { addressOn, elsewhere, fromFile, isPageAddress, mayBePage, pageAddress, workbenchHash } from "../../shared/page-address.mjs";
3
3
  import { inTrash, linkedPages, pageLinks } from "../../shared/page-links.mjs";
4
4
  import { entryOfKey } from "../../shared/extension-sidebar.mjs";
5
- import { PROTOCOL, VERSION } from "../../shared/session.mjs";
5
+ import { SHELL, tell } from "./present-shell.mjs";
6
6
 
7
7
  // The links of a page shown in 演示视图 (?present: src/server/routes.mjs), which has no editor and whose
8
8
  // frame the presentation holds (src/workbench/app/features/present/). A link to a document or a block
@@ -16,7 +16,8 @@ import { PROTOCOL, VERSION } from "../../shared/session.mjs";
16
16
  // a click stays theirs: an anchor, a link to save, a link of a target of its own, any click with a key
17
17
  // held. A workbench's address that names a web link of the sidebar, or an entry of an extension's column
18
18
  // (#~<id>/<entry id>, one with an address), is a web address to the presentation: its page goes to a new
19
- // tab; an entry the list does not hold, or one with no address, is said gone as a document is.
19
+ // tab; an entry the list does not hold, or one with no address, is said gone as a document is. (In a draft's presentation, a frame
20
+ // of its workspace's own server, the word goes to the workbench of another origin and a link keeps it around the page: present-shell.mjs.)
20
21
 
21
22
  const own = pageAddress(location.href)?.slug ?? null;
22
23
  const openTab = (address) => window.open(address, "_blank", "noopener");
@@ -24,13 +25,13 @@ const read = (route) => fetch(`/__gallery/workspace/${route}`).then((answer) =>
24
25
 
25
26
  function go({ slug, block }) {
26
27
  const fragment = block ? `#${encodeURIComponent(block)}` : "";
27
- if (slug !== own) location.assign(`/generated/${slug}.html?present${fragment}`);
28
+ if (slug !== own) location.assign(`/generated/${slug}.html?present${SHELL ? `&shell=${encodeURIComponent(SHELL)}` : ""}${fragment}`); // a draft's presentation keeps the workbench around it (present-shell.mjs)
28
29
  else if (block) dispatchEvent(new CustomEvent("doc-jump-request", { detail: { hash: fragment } })); // the kit's jump, a step of the history as a link #<block id> is
29
30
  }
30
31
 
31
32
  function sayGone(name, { trash = [] } = {}) {
32
33
  const text = inTrash(trash, name) ? t("这个页面已删除。") : t("没有找到这个页面。");
33
- window.parent.postMessage({ protocol: PROTOCOL, v: VERSION, type: "notice", slug: own, text }, location.origin);
34
+ tell("notice", { text });
34
35
  }
35
36
 
36
37
  async function follow(address) {
@@ -0,0 +1,63 @@
1
+ import { pageAddress } from "../../shared/page-address.mjs";
2
+ import { KEEPS_KEYS, stepSection } from "../../shared/present-step.mjs";
3
+ import { PROTOCOL, PROTOCOLS, VERSION } from "../../shared/session.mjs";
4
+
5
+ // What a page shown in 演示视图 (?present: src/server/routes.mjs, no editor) says to the presentation around it and
6
+ // hears from it. The presentation of a document of this workspace is a frame of the same origin, which it reaches into (its
7
+ // scroll, its keys, its theme); that of a draft of another workspace (docs/workbench.md "Drafts of other workspaces") is a frame
8
+ // of that workspace's server, named by the page's address (?shell=<the workbench's origin>) as the editor's page names it
9
+ // (session.mjs): this page then asks its own server whether that is a Gallery server of this computer (GET
10
+ // /__gallery/workspace/shell) and talks to it alone once it is, holding what it says until then; one that is not hears nothing.
11
+ // It tells where the page is scrolled (`present-view`: { x, y }, with `ready` once the page can be spoken to) and the keys pressed
12
+ // in it that are the presentation's (`present-key`: { key, ctrlKey, metaKey, altKey, kept (a control holds the key), prevented }),
13
+ // and does what it is asked: `theme` (the workbench's, light or dark), `go-section` (上一节 / 下一节) and `scroll` ({ left, top }: the wheel). Every message is the session
14
+ // protocol's envelope (src/shared/session.mjs).
15
+
16
+ /** The origin of a workbench of another origin showing this page (?shell=<origin>), or null: this one's. */
17
+ export const SHELL = (() => {
18
+ const named = new URLSearchParams(location.search).get("shell");
19
+ return named && named !== location.origin ? named : null;
20
+ })();
21
+
22
+ const vouched = (origin) => fetch(`/__gallery/workspace/shell?origin=${encodeURIComponent(origin)}`, { cache: "no-store" })
23
+ .then((answer) => answer.json()).then((answer) => answer?.shell === true, () => false);
24
+
25
+ let parentOrigin = SHELL ? null : location.origin;
26
+ let held = SHELL ? [] : null;
27
+
28
+ /** Tell the presentation around the page (`window.parent`) something; held until its origin is vouched for, dropped when it is not. */
29
+ export function tell(type, fields = {}) {
30
+ if (window.parent === window) return;
31
+ if (held) return void held.push([type, fields]);
32
+ if (!parentOrigin) return;
33
+ window.parent.postMessage({ protocol: PROTOCOL, v: VERSION, type, slug: pageAddress(location.href)?.slug ?? "", ...fields }, parentOrigin);
34
+ }
35
+
36
+ function bridge() {
37
+ let frame = 0;
38
+ const view = (ready = false) => tell("present-view", { x: scrollX, y: scrollY, ...(ready ? { ready: true } : {}) });
39
+ addEventListener("scroll", () => { frame ||= requestAnimationFrame(() => { frame = 0; view(); }); }, { passive: true });
40
+ addEventListener("keydown", (event) => {
41
+ if (event.isComposing) return;
42
+ tell("present-key", { key: event.key, ctrlKey: event.ctrlKey, metaKey: event.metaKey, altKey: event.altKey, kept: Boolean(event.target?.closest?.(KEEPS_KEYS)), prevented: event.defaultPrevented });
43
+ });
44
+ addEventListener("message", (event) => {
45
+ const message = event.data;
46
+ if (event.source !== window.parent || event.origin !== parentOrigin || !PROTOCOLS.includes(message?.protocol) || message.v !== VERSION || message.type !== "command") return;
47
+ if (message.name === "theme") dispatchEvent(new CustomEvent("doc-theme-request", { detail: { theme: message.theme === "dark" ? "dark" : "light" } })); // the kit's ui.js
48
+ else if (message.name === "go-section") stepSection(window, message.direction > 0 ? 1 : -1);
49
+ else if (message.name === "scroll") scrollBy({ left: Number(message.left) || 0, top: Number(message.top) || 0 }); // the wheel over the presentation's ink
50
+ });
51
+ view(true);
52
+ }
53
+
54
+ if (SHELL && window.parent !== window) {
55
+ vouched(SHELL).then((ok) => {
56
+ const waiting = held;
57
+ held = null;
58
+ if (!ok) return;
59
+ parentOrigin = SHELL;
60
+ for (const [type, fields] of waiting) tell(type, fields);
61
+ bridge();
62
+ });
63
+ }
@@ -5,6 +5,7 @@ import { createFills } from "../fills.mjs";
5
5
  import { FOLDED } from "../folded.mjs";
6
6
  import { describe, note, report } from "../overlays.mjs";
7
7
  import { t } from "../../../shared/i18n/index.mjs";
8
+ import { UNFILLED_ALONE } from "../../components/registry.mjs";
8
9
 
9
10
  // Selected blocks (docs/editor.md, "Selecting blocks"): a set of blocks, none inside another, in
10
11
  // document order. A box selection drawn from empty space gives blocks of one container
@@ -109,10 +110,17 @@ export function createSelection({ info, active, say, closeMenu, schedule }) {
109
110
  // The fills made to match the selection now: those of blocks no longer selected (or no longer on
110
111
  // the page) go at once; each selected block shown gets its fill where it is, reaching past it as
111
112
  // reachOf says, never into a neighbour's (roomOf). One that cannot be drawn takes all the fills
112
- // down (said once): better none than one in the wrong place.
113
+ // down (said once): better none than one in the wrong place. A kind whose own tools show it selected
114
+ // (UNFILLED_ALONE: the image with its picture, its corner grips and toolbar) draws no fill selected alone (the user, 2026-11-09: no pale
115
+ // blue over a picture clicked); among other blocks it is filled as they are.
116
+ const toolsShowIt = () => {
117
+ if (picked.length !== 1 || marked.length !== 0) return false;
118
+ const holds = UNFILLED_ALONE[picked[0].getAttribute?.("data-ge-unit-kind")];
119
+ return holds !== undefined && picked[0].querySelector?.(holds) !== null;
120
+ };
113
121
  function render() {
114
122
  try {
115
- halos.paint(hidden || !active() ? [] : [...picked, ...marked].filter(alive));
123
+ halos.paint(hidden || !active() || toolsShowIt() ? [] : [...picked, ...marked].filter(alive));
116
124
  } catch (error) {
117
125
  unpaint();
118
126
  report("selection fills", error);
@@ -0,0 +1,87 @@
1
+ import { TABLE } from "../../components/kit.mjs";
2
+ import { CHART_SOURCE, TABLE_READERS, VIEW_FROM, nextChartKey } from "../../../shared/table-chart.mjs";
3
+ import { t } from "../../../shared/i18n/index.mjs";
4
+ import { menuRow } from "../page-objects.mjs";
5
+
6
+ // Which table a view reads (docs/editor.md, "Table views"). A view of a table (an instance of a custom
7
+ // block whose needs hold "tables") names its table by a key: VIEW_FROM on the instance, CHART_SOURCE on
8
+ // the table's frame, never by an id or the blocks' order. A view made on the page ("/", 在下方新建, 转换成)
9
+ // is bound to the nearest table above it as it is made, in the same undo step; the table it reads is the
10
+ // reader's to change after that, in the block's 设置 (installed/page.mjs), which lists the document's tables.
11
+ // A table with no key gets one in the same step (the first of t1, t2 … the page does not hold), and so
12
+ // does the second of two an Agent wrote with one key: a view by that key would read the first.
13
+
14
+ const FRAME_WITH_TABLE = (frame) => frame.querySelector("table") !== null && !frame.closest("[data-doc-dev]");
15
+
16
+ /** The tables of the page, in the document's order: their frames (.doc-table-wrap). */
17
+ export const tableFrames = () => [...document.querySelectorAll(TABLE.frame)].filter(FRAME_WITH_TABLE);
18
+
19
+ /** The frame a key names, as the kit reads it (runtime/table-reading.js frameOf) and the checks do: the first that carries it. */
20
+ export const frameOf = (key) => tableFrames().find((frame) => frame.getAttribute(CHART_SOURCE) === key) ?? null;
21
+
22
+ /** The nearest table above `node`, in the document's order, that does not hold it; null where there is none. */
23
+ export function tableAbove(node) {
24
+ let found = null;
25
+ for (const frame of tableFrames()) {
26
+ if (frame.contains(node)) continue;
27
+ if (frame.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING) found = frame;
28
+ }
29
+ return found;
30
+ }
31
+
32
+ // Every key a chart or a view names, or a frame carries.
33
+ const takenKeys = () => new Set([...document.querySelectorAll([CHART_SOURCE, ...TABLE_READERS].map((name) => `[${name}]`).join(", "))]
34
+ .flatMap((node) => [CHART_SOURCE, ...TABLE_READERS].map((name) => node.getAttribute(name)).filter((value) => value !== null)));
35
+
36
+ /** The key `frame` is read by: its own where it names this frame, else a new one (not yet written). */
37
+ export function keyOf(frame) {
38
+ const own = frame.getAttribute(CHART_SOURCE);
39
+ return own !== null && frameOf(own) === frame ? own : nextChartKey(takenKeys());
40
+ }
41
+
42
+ /**
43
+ * `instance` made a view of `frame` (or of none: null takes its key off): its VIEW_FROM set, and the key
44
+ * on the frame where it carried none that names it. Records nothing: the caller's undo step does
45
+ * (`viewFrames`, struct's `attributes`). Returns whether anything changed.
46
+ */
47
+ export function bindView(instance, frame) {
48
+ if (!frame) {
49
+ if (!instance.hasAttribute(VIEW_FROM)) return false;
50
+ instance.removeAttribute(VIEW_FROM);
51
+ return true;
52
+ }
53
+ const key = keyOf(frame);
54
+ let changed = false;
55
+ if (instance.getAttribute(VIEW_FROM) !== key) {
56
+ instance.setAttribute(VIEW_FROM, key);
57
+ changed = true;
58
+ }
59
+ if (frame.getAttribute(CHART_SOURCE) !== key) {
60
+ frame.setAttribute(CHART_SOURCE, key);
61
+ changed = true;
62
+ }
63
+ return changed;
64
+ }
65
+
66
+ /** A view that was made bound to the table above it. */
67
+ export function bindAbove(instance) {
68
+ return bindView(instance, tableAbove(instance));
69
+ }
70
+
71
+ /**
72
+ * What the undo step that makes a block of the menu's key records of the tables' keys (struct's
73
+ * `attributes`): every table's frame when the block reads tables, as the table it is bound to is only
74
+ * known as the block is made; nothing for any other block.
75
+ */
76
+ export const viewFrames = (key) => (menuRow(key)?.readsTables ? tableFrames().map((frame) => [frame, [CHART_SOURCE]]) : []);
77
+
78
+ // A table named for a reader: its place among the document's tables and the words of its first row.
79
+ function labelOf(frame, place) {
80
+ const first = frame.querySelector("tr");
81
+ const words = first ? [...first.cells].map((cell) => cell.textContent.replace(/\s+/g, " ").trim()).filter(Boolean).slice(0, 4).join("、") : "";
82
+ const shown = words.length > 28 ? `${words.slice(0, 27)}…` : words;
83
+ return shown ? t("表格 {0} · {1}", place, shown) : t("表格 {0}", place);
84
+ }
85
+
86
+ /** The document's tables for a list to choose from: [{ frame, label }], the label naming the table by its place and its first row. */
87
+ export const tableChoices = () => tableFrames().map((frame, at) => ({ frame, label: labelOf(frame, at + 1) }));
@@ -37,6 +37,4 @@ export const TABLE_ICONS = {
37
37
  // 一键优化: a magic wand and its sparks; 识别数字列: a number sign.
38
38
  magic: svg('<path d="m3.75 16.25 9-9"/><path d="m11.25 5.75 3 3"/><path d="M15.25 2.5v3M13.75 4h3M5.5 3.25v2M4.5 4.25h2M16 10.75v2M15 11.75h2"/>'),
39
39
  numbers: svg('<path d="M8.25 4 6.75 16M13.75 4l-1.5 12M4.5 8h12M3.75 12.25h12"/>'),
40
- // 视图: three columns of cards, a board drawn from the table.
41
- view: svg('<rect x="3.25" y="3.75" width="3.5" height="7.5" rx="1"/><rect x="8.25" y="3.75" width="3.5" height="12.5" rx="1"/><rect x="13.25" y="3.75" width="3.5" height="5" rx="1"/>'),
42
40
  };
@@ -57,7 +57,7 @@ import { t } from "../../../shared/i18n/index.mjs";
57
57
  // it (the geometry's view), the grips and + points of the rows and columns in sight.
58
58
  // Each change is one undo step; the page writes it as one operation of the table (grid.mjs). The
59
59
  // keys and the cells' bar are cells.mjs's; what a view of the table asks (a custom block reading it:
60
- // set a cell, move, add or take out a row) and 视图 › of its menu, views.mjs's.
60
+ // set a cell, move, add or take out a row), views.mjs's.
61
61
 
62
62
  const STYLE = `
63
63
  .ge-tgrip { display: flex; align-items: center; justify-content: center; cursor: grab; pointer-events: auto; }
@@ -108,15 +108,14 @@ const REACH = { out: 12, in: 8 }; // the pointer this near the table's top (left
108
108
  * active(): whether the tools may show. struct(parents, touched, mutate): the editor's undoable
109
109
  * structural change; look(elements, change, select): a change of classes and sizes as one undo step. selectBlock(unit):
110
110
  * select the table as a block (Esc). resizeTable(event, unit, axis): a drag of the table's width
111
- * ("x") or scroll height ("y") from an armed edge (resize/). say: the status line. cellsOf(data): what
111
+ * ("x") or scroll height ("y") from an armed edge (resize/). say: the status line; settle(): takes it off. cellsOf(data): what
112
112
  * the clipboard holds as rows of cells, { rows, from } (clipboard/ cellsOf), or null. upload(kind,
113
113
  * body): a file sent to the report's folder (POST /__gallery/edit/upload), a picture of a cell's
114
114
  * (pictures.mjs).
115
115
  * typedKey(event): a key that is the "/" or "@" menu's, standing on a cell's text (slash-menu/, at-menu/), comes before the cell's.
116
- * recordStep(host, before): a text step of a text block, what a view's set is (history.mjs). insertBelow(unit, key,
117
- * { attributes, done }): a block of a menu's key put in below a block in one undo step (editor.mjs), what 视图 › makes.
116
+ * recordStep(host, before): a text step of a text block, what a view's set is (history.mjs).
118
117
  */
119
- export function createTableEditing({ active, struct, look, selectBlock, resizeTable = () => false, say, cellsOf = () => null, upload = async () => { throw new Error("upload"); }, typedKey = () => false, recordStep = () => {}, insertBelow = () => {} }) {
118
+ export function createTableEditing({ active, struct, look, selectBlock, resizeTable = () => false, say, settle = () => {}, cellsOf = () => null, upload = async () => { throw new Error("upload"); }, typedKey = () => false, recordStep = () => {} }) {
120
119
  addStyle(STYLE);
121
120
  const menu = createMenu();
122
121
  let hover = null; // { unit, row, column, edges: { row, column } }: the cell under the pointer, and whether it is at the table's left (top) edge (edgesAt)
@@ -268,7 +267,7 @@ export function createTableEditing({ active, struct, look, selectBlock, resizeTa
268
267
  select(null);
269
268
  selectBlock(unit);
270
269
  },
271
- bounds, selectedCells, tableAt, placeOf, cellOf, cellAt, geometry, change, insert, removeRange, duplicate, merge, split, look, struct, say,
270
+ bounds, selectedCells, tableAt, placeOf, cellOf, cellAt, geometry, change, insert, removeRange, duplicate, merge, split, look, struct, say, settle,
272
271
  active,
273
272
  menuOpen: () => menu.isOpen,
274
273
  typedKey,
@@ -927,15 +926,13 @@ export function createTableEditing({ active, struct, look, selectBlock, resizeTa
927
926
 
928
927
  addEventListener("scroll", () => { if (!drag) draw(); }, { passive: true, capture: true });
929
928
 
930
- // What a view of a table asks, and 视图 › (views.mjs).
931
- const views = createViews({ active, change, tableAt, recordStep, insertBelow, drawn: () => draw() });
929
+ // What a view of a table asks (views.mjs).
930
+ createViews({ active, change, tableAt, recordStep, drawn: () => draw() });
932
931
 
933
932
  // The rows of a table the tools manage. 一键优化 is a split row (menu.mjs): its words run it, its ›
934
- // opens two of its parts to run alone, 自动调整列宽 and 识别数字列 (the user's, 2026-11-07); 视图 › last,
935
- // where a custom block reads tables (views.mjs).
933
+ // opens two of its parts to run alone, 自动调整列宽 and 识别数字列 (the user's, 2026-11-07).
936
934
  function toolRows(unit) {
937
935
  const state = tableState(unit);
938
- const view = views.menuRow(unit);
939
936
  return [
940
937
  { label: t("标题行"), icon: ICONS.headerRow, checked: state.headerRow, run: () => headerRow(unit, state) },
941
938
  { label: t("标题列"), icon: ICONS.headerColumn, checked: state.headerColumn, run: () => change(unit, () => setHeaderColumn(unit, !state.headerColumn), state.headerColumn ? t("已取消标题列") : t("已设为标题列")) },
@@ -947,7 +944,6 @@ export function createTableEditing({ active, struct, look, selectBlock, resizeTa
947
944
  ],
948
945
  },
949
946
  { label: t("均分列宽"), icon: ICONS.even, run: () => widths.even(unit) },
950
- ...(view ? [view] : []),
951
947
  ];
952
948
  }
953
949
 
@@ -998,8 +994,8 @@ export function createTableEditing({ active, struct, look, selectBlock, resizeTa
998
994
  * A table's rows of its block menu (notion-lite's 表格 section, under the menu's one title): a
999
995
  * table of the kind these tools manage has 标题行 and 标题列, switches (checked when on); then
1000
996
  * actions, each applied once (no check): 一键优化 › (自动调整列宽, 识别数字列 in its submenu),
1001
- * 均分列宽, 视图 › (where a custom block reads tables: views.mjs); any table in its frame has its
1002
- * size's rows after them (layoutRows). [] for any other block.
997
+ * 均分列宽; any table in its frame has its size's rows after them (layoutRows). [] for any other block.
998
+ * (The table's 对齐 › is the block menu's own row, above 颜色: page/align.mjs, as a text block's.)
1003
999
  */
1004
1000
  menuItems(target) {
1005
1001
  if (!target?.matches?.('[data-ge-unit-kind="table"]')) return [];
@@ -1,11 +1,6 @@
1
- import { TABLE } from "../../components/kit.mjs";
2
1
  import { insertRow, moveRow, removeRows, tableState } from "../../components/table/page.mjs";
3
2
  import { tableOf } from "../../components/table/grid.mjs";
4
3
  import { ownHTML } from "../core.mjs";
5
- import { menuEntry, viewKeys } from "../page-objects.mjs";
6
- import { CHART_SOURCE, TABLE_READERS, VIEW_FROM, nextChartKey } from "../../../shared/table-chart.mjs";
7
- import { t } from "../../../shared/i18n/index.mjs";
8
- import { TABLE_ICONS } from "./icons.mjs";
9
4
 
10
5
  // The views of a table (docs/editor.md, "Table views"; the plan's 阶段 4): an instance of a custom
11
6
  // block whose needs hold "tables", bound to a table of the document by the key its frame carries
@@ -31,25 +26,15 @@ import { TABLE_ICONS } from "./icons.mjs";
31
26
  // are those after its header row: the head's rows, or with no head its body's first row, which a view
32
27
  // reads its columns' names from; no intent puts a row there or takes that one.
33
28
  //
34
- // 视图 › of a table's block menu (menuRow) lists the custom blocks that read tables and that the
35
- // document's menus would offer (their `readsTables` row of the custom blocks' menu variants, which follows
36
- // the 组件列表 switches; page-objects.mjs viewKeys: no other menu lists them, as one put in unbound only
37
- // says it has no table) and puts one in right after the table bound to it, in one undo step: the table's key
38
- // where it names this table (the first frame carrying it, as the kit and the checks read a key), or a new
39
- // one (the first of t1, t2 … the page does not hold) put on its frame in that step (a table made from "/"
40
- // has none; the second of two an Agent wrote with one key is not the one it names). The commit writes
41
- // both, and the server makes sure of the key against the file (source/chart-keys.mjs relinkMade); a key
42
- // it gives the pair anew the page follows, its undo steps too (page/commit.mjs apply, history.mjs rekeyed).
29
+ // Which table a view reads is binding.mjs's: a view made from a menu is bound to the table above it, and
30
+ // its 设置 (components/installed/page.mjs) changes it.
43
31
 
44
32
  /**
45
33
  * active(): whether the page edits now. change(unit, mutate, said, touched): the table tools' one undo
46
34
  * step (index.mjs). tableAt(node): the table unit these tools manage that a node is in. recordStep(host,
47
- * before): a text step of a text block (history.mjs). insertBelow(unit, key, { attributes, done }): a
48
- * block of the menu's `key` put in below a block in one undo step, `attributes` ([element, names]) recorded
49
- * with it and `done(made)` run in it on the new block (editor.mjs insertBelow). drawn(): the tools drawn
50
- * again (a row taken out).
35
+ * before): a text step of a text block (history.mjs). drawn(): the tools drawn again (a row taken out).
51
36
  */
52
- export function createViews({ active, change, tableAt, recordStep, insertBelow, drawn = () => {} }) {
37
+ export function createViews({ active, change, tableAt, recordStep, drawn = () => {} }) {
53
38
  const merged = (rows) => rows.some((row) => [...row.cells].some((cell) => cell.rowSpan !== 1 || cell.colSpan !== 1));
54
39
  const writable = (cell) => cell?.getAttribute("contenteditable") === "true";
55
40
  const holds = (cell, text) => cell.textContent === text && cell.childElementCount === 0;
@@ -114,37 +99,5 @@ export function createViews({ active, change, tableAt, recordStep, insertBelow,
114
99
  if (run(event.detail ?? {})) event.preventDefault();
115
100
  });
116
101
 
117
- // The frame a key names, as the kit reads it (runtime/table-reading.js frameOf) and the checks do: the
118
- // first that carries it, none of a dev block's.
119
- const frameOf = (key) => [...document.querySelectorAll(`[${CHART_SOURCE}]`)].find((node) => node.getAttribute(CHART_SOURCE) === key && node.matches(TABLE.frame) && !node.closest("[data-doc-dev]")) ?? null;
120
- // A view of the block `key` put in right after `unit`, bound to its table: by its key where that names
121
- // it, else by a new one put on it in the same step (a table made from "/" has none; one of two an Agent
122
- // wrote with one key is not the one it names, the first: a view by it would read and change that one).
123
- function insertView(unit, key) {
124
- const own = unit.getAttribute(CHART_SOURCE);
125
- const taken = new Set([...document.querySelectorAll([CHART_SOURCE, ...TABLE_READERS].map((name) => `[${name}]`).join(", "))].flatMap((node) => [CHART_SOURCE, ...TABLE_READERS].map((name) => node.getAttribute(name)).filter((value) => value !== null)));
126
- const bound = own !== null && frameOf(own) === unit ? own : nextChartKey(taken);
127
- insertBelow(unit, key, {
128
- attributes: [[unit, [CHART_SOURCE]]],
129
- done(made) {
130
- made?.setAttribute(VIEW_FROM, bound);
131
- if (bound !== own) unit.setAttribute(CHART_SOURCE, bound);
132
- },
133
- });
134
- }
135
-
136
- return {
137
- /** The 视图 › row of a table's block menu: one for each custom block that reads tables, or null where there is none or the table cannot be read as rows (no frame, a merged cell). */
138
- menuRow(unit) {
139
- const keys = viewKeys();
140
- if (keys.length === 0 || !unit.matches(TABLE.frame) || merged(tableState(unit).rows)) return null;
141
- return {
142
- label: t("视图"), icon: TABLE_ICONS.view,
143
- submenu: () => viewKeys().map((key) => {
144
- const row = menuEntry(key).variant;
145
- return { label: row.label, icon: row.icon, run: () => insertView(unit, key) };
146
- }),
147
- };
148
- },
149
- };
102
+ return {};
150
103
  }