@heycys/gallery 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/THIRD-PARTY-NOTICES.md +1 -1
  2. package/browser/editor/components/columns/page.mjs +4 -4
  3. package/browser/editor/components/embed/index.mjs +2 -2
  4. package/browser/editor/components/flowchart/add.mjs +7 -6
  5. package/browser/editor/components/flowchart/page.mjs +1 -1
  6. package/browser/editor/components/icons.mjs +8 -0
  7. package/browser/editor/components/image/crop.mjs +6 -9
  8. package/browser/editor/components/installed/index.mjs +11 -4
  9. package/browser/editor/components/installed/page.mjs +27 -16
  10. package/browser/editor/components/installed/served.mjs +6 -5
  11. package/browser/editor/components/instances.mjs +18 -0
  12. package/browser/editor/components/list/page.mjs +3 -6
  13. package/browser/editor/components/registry.mjs +13 -4
  14. package/browser/editor/components/table/index.mjs +59 -2
  15. package/browser/editor/components/table/page.mjs +1 -1
  16. package/browser/editor/page/annotate/card.mjs +3 -6
  17. package/browser/editor/page/autosave.mjs +310 -37
  18. package/browser/editor/page/board/client.mjs +57 -0
  19. package/browser/editor/page/board/hint.mjs +145 -0
  20. package/browser/editor/page/board/history.mjs +119 -0
  21. package/browser/editor/page/board/index.mjs +970 -0
  22. package/browser/editor/page/board/settings.mjs +209 -0
  23. package/browser/editor/page/caret.mjs +1 -1
  24. package/browser/editor/page/changes/bar.mjs +6 -14
  25. package/browser/editor/page/changes/index.mjs +2 -2
  26. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  27. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  28. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  29. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  30. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  31. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  32. package/browser/editor/page/clipboard/index.mjs +124 -13
  33. package/browser/editor/page/clipboard/own/blocks.mjs +11 -11
  34. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  35. package/browser/editor/page/clipboard/own/selection.mjs +1 -1
  36. package/browser/editor/page/columns/index.mjs +48 -46
  37. package/browser/editor/page/commit.mjs +80 -22
  38. package/browser/editor/page/conflicts.mjs +7 -7
  39. package/browser/editor/page/core.mjs +31 -15
  40. package/browser/editor/page/custom-page.mjs +106 -0
  41. package/browser/editor/page/editor.mjs +51 -14
  42. package/browser/editor/page/emoji-picker/styles.mjs +7 -12
  43. package/browser/editor/page/emoji-picker/upload.mjs +2 -2
  44. package/browser/editor/page/handle/index.mjs +5 -8
  45. package/browser/editor/page/history.mjs +24 -4
  46. package/browser/editor/page/links/index.mjs +5 -3
  47. package/browser/editor/page/links/pages.mjs +27 -9
  48. package/browser/editor/page/look.mjs +54 -3
  49. package/browser/editor/page/margin.mjs +18 -3
  50. package/browser/editor/page/media-menu/index.mjs +6 -7
  51. package/browser/editor/page/menu.mjs +18 -7
  52. package/browser/editor/page/notices.mjs +2 -2
  53. package/browser/editor/page/page-objects.mjs +13 -2
  54. package/browser/editor/page/panel.mjs +4 -9
  55. package/browser/editor/page/present-links.mjs +11 -2
  56. package/browser/editor/page/resize/index.mjs +42 -29
  57. package/browser/editor/page/served.mjs +14 -0
  58. package/browser/editor/page/session.mjs +48 -4
  59. package/browser/editor/page/slash-menu/index.mjs +3 -3
  60. package/browser/editor/page/table/cells.mjs +102 -46
  61. package/browser/editor/page/table/icons.mjs +2 -0
  62. package/browser/editor/page/table/index.mjs +29 -10
  63. package/browser/editor/page/table/pictures.mjs +299 -0
  64. package/browser/editor/page/table/views.mjs +150 -0
  65. package/browser/editor/page/text-select/index.mjs +26 -12
  66. package/browser/editor/page/todo-writes.mjs +75 -0
  67. package/browser/editor/page/typing/index.mjs +53 -3
  68. package/browser/editor/page/ui.mjs +13 -0
  69. package/browser/shared/block-packs.mjs +404 -0
  70. package/browser/shared/board-places.mjs +224 -0
  71. package/browser/shared/card-size.mjs +59 -0
  72. package/browser/shared/colors.mjs +3 -2
  73. package/browser/shared/component-catalog.mjs +323 -46
  74. package/browser/shared/component-source.mjs +37 -0
  75. package/browser/shared/components/index.mjs +12 -3
  76. package/browser/shared/components/installed.mjs +35 -3
  77. package/browser/shared/components/page.mjs +18 -8
  78. package/browser/shared/components/state.mjs +5 -1
  79. package/browser/shared/document-look.mjs +10 -5
  80. package/browser/shared/drafts.mjs +432 -0
  81. package/browser/shared/extension-sidebar.mjs +126 -0
  82. package/browser/shared/extensions.mjs +79 -0
  83. package/browser/shared/file-table.mjs +101 -0
  84. package/browser/shared/i18n/en.mjs +307 -27
  85. package/browser/shared/json-layout.mjs +47 -0
  86. package/browser/shared/net-cache.mjs +133 -0
  87. package/browser/shared/net-keys.mjs +32 -0
  88. package/browser/shared/net-policy.mjs +177 -0
  89. package/browser/shared/outline-shape.mjs +128 -0
  90. package/browser/shared/outline.mjs +331 -67
  91. package/browser/shared/pack-manifest.mjs +77 -0
  92. package/browser/shared/page-address.mjs +6 -3
  93. package/browser/shared/page-cards.mjs +308 -0
  94. package/browser/shared/page-links.mjs +69 -6
  95. package/browser/shared/pages.mjs +388 -0
  96. package/browser/shared/paths.mjs +6 -0
  97. package/browser/shared/preview-server.mjs +3 -7
  98. package/browser/shared/session.mjs +8 -1
  99. package/browser/shared/shipped-blocks.mjs +299 -0
  100. package/browser/shared/table-chart.mjs +33 -1
  101. package/browser/shared/todos.mjs +83 -0
  102. package/browser/shared/user-directory.mjs +24 -0
  103. package/browser/shared/versions.mjs +14 -0
  104. package/browser/shared/web-links.mjs +134 -0
  105. package/browser/shared/workspace-columns.mjs +120 -0
  106. package/browser/shared/workspace-name.mjs +154 -0
  107. package/browser/shared/workspace-query.mjs +125 -0
  108. package/browser/shared/workspace-registry.mjs +272 -0
  109. package/browser/shared/workspace.mjs +171 -20
  110. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  111. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  112. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  113. package/dist/chunk-3KJHLHUO.mjs +2 -0
  114. package/dist/chunk-3NJACR4W.mjs +2 -0
  115. package/dist/chunk-4AV456RQ.mjs +2 -0
  116. package/dist/chunk-4OZWXZE5.mjs +5 -0
  117. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  118. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  119. package/dist/chunk-6A53MZYZ.mjs +2 -0
  120. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  121. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  122. package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
  123. package/dist/{chunk-Y6RLRPG3.mjs → chunk-6VTU6KEO.mjs} +1 -1
  124. package/dist/chunk-B4P2CM2J.mjs +3 -0
  125. package/dist/{chunk-FDLXYYG4.mjs → chunk-BPL64HGA.mjs} +1 -1
  126. package/dist/chunk-BVEFGE4B.mjs +2 -0
  127. package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
  128. package/dist/chunk-CRJEG7MT.mjs +2 -0
  129. package/dist/{chunk-LCTHTZIL.mjs → chunk-D4CKRD3V.mjs} +91 -91
  130. package/dist/chunk-D4EURAX4.mjs +4 -0
  131. package/dist/chunk-DQCE7TJY.mjs +5 -0
  132. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  133. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  134. package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
  135. package/dist/chunk-EURLTOTZ.mjs +4 -0
  136. package/dist/chunk-FJYN7RTH.mjs +4 -0
  137. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  138. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  139. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  140. package/dist/chunk-GY3FAUVM.mjs +2 -0
  141. package/dist/chunk-HLYV7RIH.mjs +2 -0
  142. package/dist/chunk-HMYTM3GT.mjs +15 -0
  143. package/dist/chunk-IA4PSIMD.mjs +2 -0
  144. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  145. package/dist/chunk-KTKHRC6H.mjs +2 -0
  146. package/dist/chunk-L7UP52JO.mjs +2 -0
  147. package/dist/{chunk-SYGN7GLU.mjs → chunk-MHW7ZKOU.mjs} +1 -1
  148. package/dist/chunk-N76HWBA3.mjs +10 -0
  149. package/dist/chunk-N7AZZLGT.mjs +548 -0
  150. package/dist/chunk-NAMN3RAF.mjs +2 -0
  151. package/dist/{chunk-5ZKJ3H4P.mjs → chunk-NFMCP2HI.mjs} +3 -3
  152. package/dist/chunk-NG375ILQ.mjs +2 -0
  153. package/dist/chunk-NVRNLK25.mjs +6 -0
  154. package/dist/chunk-NWFBE2YA.mjs +2 -0
  155. package/dist/chunk-NWR2LT5B.mjs +2 -0
  156. package/dist/chunk-OJKMINIU.mjs +20 -0
  157. package/dist/{chunk-4FS7Z7WR.mjs → chunk-OUUP3WKT.mjs} +1 -1
  158. package/dist/{chunk-BGGDRNYQ.mjs → chunk-OW5NWPOJ.mjs} +1 -1
  159. package/dist/chunk-PAREWQGB.mjs +4 -0
  160. package/dist/chunk-POQSE4C2.mjs +2 -0
  161. package/dist/chunk-PUOQ65NC.mjs +2 -0
  162. package/dist/{chunk-XNROSH2T.mjs → chunk-PVER7LSC.mjs} +2 -2
  163. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  164. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  165. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  166. package/dist/chunk-QKXMPUXC.mjs +2 -0
  167. package/dist/chunk-QOUEFUCH.mjs +16 -0
  168. package/dist/chunk-RFDZXFJF.mjs +13 -0
  169. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  170. package/dist/chunk-RMECZA2K.mjs +2 -0
  171. package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  172. package/dist/chunk-SX4LJYT6.mjs +8 -0
  173. package/dist/chunk-SXHBYZST.mjs +16 -0
  174. package/dist/chunk-TANZRINB.mjs +2 -0
  175. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  176. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  177. package/dist/chunk-UFNYFFKO.mjs +20 -0
  178. package/dist/chunk-UGWYKHBL.mjs +170 -0
  179. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  180. package/dist/chunk-UNOYVIYR.mjs +2 -0
  181. package/dist/{chunk-E7LWZRXY.mjs → chunk-V6RIYQZZ.mjs} +1 -1
  182. package/dist/chunk-VJPFVNNW.mjs +15 -0
  183. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  184. package/dist/chunk-VTKNEYT3.mjs +6 -0
  185. package/dist/chunk-VXACTLO3.mjs +3 -0
  186. package/dist/chunk-YI43RG4P.mjs +2 -0
  187. package/dist/chunk-YQGT4AB2.mjs +6 -0
  188. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  189. package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  190. package/dist/chunk-ZTHICSSF.mjs +2 -0
  191. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  192. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  193. package/dist/cli.mjs +1 -1
  194. package/dist/server.mjs +1 -1
  195. package/dist/{chunk-U6KNPJPR.mjs → update-fetch.mjs} +1 -1
  196. package/dist/worker.mjs +1 -1
  197. package/kit/board.css +95 -0
  198. package/kit/board.js +250 -0
  199. package/kit/charts.js +5 -71
  200. package/kit/code.js +14 -14
  201. package/kit/report.css +255 -17
  202. package/kit/runtime.js +1600 -125
  203. package/kit/state.js +10 -1
  204. package/kit/tables.js +128 -43
  205. package/kit/ui.js +77 -35
  206. package/package.json +2 -1
  207. package/packs/gallery/card/component.json +14 -0
  208. package/packs/gallery/card/example.html +10 -0
  209. package/packs/gallery/card/preview.svg +1 -0
  210. package/packs/gallery/card/style.css +6 -0
  211. package/packs/gallery/card/template.html +6 -0
  212. package/packs/gallery/card/writing.md +14 -0
  213. package/packs/gallery/chart-card/component.json +15 -0
  214. package/packs/gallery/chart-card/example.html +14 -0
  215. package/packs/gallery/chart-card/kit.js +130 -0
  216. package/packs/gallery/chart-card/preview.svg +1 -0
  217. package/packs/gallery/chart-card/style.css +42 -0
  218. package/packs/gallery/chart-card/template.html +13 -0
  219. package/packs/gallery/chart-card/writing.md +20 -0
  220. package/packs/gallery/chart-view/component.json +20 -0
  221. package/packs/gallery/chart-view/example.html +13 -0
  222. package/packs/gallery/chart-view/kit.js +355 -0
  223. package/packs/gallery/chart-view/preview.svg +1 -0
  224. package/packs/gallery/chart-view/style.css +94 -0
  225. package/packs/gallery/chart-view/template.html +3 -0
  226. package/packs/gallery/chart-view/writing.md +22 -0
  227. package/packs/gallery/countdown/component.json +18 -0
  228. package/packs/gallery/countdown/example.html +3 -0
  229. package/packs/gallery/countdown/kit.js +156 -0
  230. package/packs/gallery/countdown/preview.svg +1 -0
  231. package/packs/gallery/countdown/style.css +130 -0
  232. package/packs/gallery/countdown/template.html +3 -0
  233. package/packs/gallery/countdown/writing.md +22 -0
  234. package/packs/gallery/habit/component.json +14 -0
  235. package/packs/gallery/habit/example.html +3 -0
  236. package/packs/gallery/habit/kit.js +295 -0
  237. package/packs/gallery/habit/preview.svg +1 -0
  238. package/packs/gallery/habit/style.css +246 -0
  239. package/packs/gallery/habit/template.html +3 -0
  240. package/packs/gallery/habit/writing.md +21 -0
  241. package/packs/gallery/heatmap/component.json +18 -0
  242. package/packs/gallery/heatmap/example.html +3 -0
  243. package/packs/gallery/heatmap/kit.js +351 -0
  244. package/packs/gallery/heatmap/preview.svg +1 -0
  245. package/packs/gallery/heatmap/style.css +201 -0
  246. package/packs/gallery/heatmap/template.html +3 -0
  247. package/packs/gallery/heatmap/writing.md +21 -0
  248. package/packs/gallery/kanban/component.json +18 -0
  249. package/packs/gallery/kanban/example.html +12 -0
  250. package/packs/gallery/kanban/kit.js +540 -0
  251. package/packs/gallery/kanban/preview.svg +1 -0
  252. package/packs/gallery/kanban/style.css +212 -0
  253. package/packs/gallery/kanban/template.html +3 -0
  254. package/packs/gallery/kanban/writing.md +20 -0
  255. package/packs/gallery/metric/component.json +22 -0
  256. package/packs/gallery/metric/example.html +15 -0
  257. package/packs/gallery/metric/kit.js +248 -0
  258. package/packs/gallery/metric/preview.svg +1 -0
  259. package/packs/gallery/metric/style.css +92 -0
  260. package/packs/gallery/metric/template.html +3 -0
  261. package/packs/gallery/metric/writing.md +23 -0
  262. package/packs/gallery/note/component.json +14 -0
  263. package/packs/gallery/note/example.html +3 -0
  264. package/packs/gallery/note/kit.js +102 -0
  265. package/packs/gallery/note/preview.svg +1 -0
  266. package/packs/gallery/note/style.css +76 -0
  267. package/packs/gallery/note/template.html +3 -0
  268. package/packs/gallery/note/writing.md +18 -0
  269. package/packs/gallery/pack.json +8 -0
  270. package/packs/gallery/recent/component.json +13 -0
  271. package/packs/gallery/recent/example.html +1 -0
  272. package/packs/gallery/recent/kit.js +238 -0
  273. package/packs/gallery/recent/preview.svg +1 -0
  274. package/packs/gallery/recent/style.css +158 -0
  275. package/packs/gallery/recent/template.html +1 -0
  276. package/packs/gallery/recent/writing.md +15 -0
  277. package/packs/gallery/static/component.json +13 -0
  278. package/packs/gallery/static/example.html +11 -0
  279. package/packs/gallery/static/preview.svg +1 -0
  280. package/packs/gallery/static/style.css +138 -0
  281. package/packs/gallery/static/template.html +1 -0
  282. package/packs/gallery/static/writing.md +18 -0
  283. package/packs/gallery/stats/component.json +13 -0
  284. package/packs/gallery/stats/example.html +1 -0
  285. package/packs/gallery/stats/kit.js +166 -0
  286. package/packs/gallery/stats/preview.svg +1 -0
  287. package/packs/gallery/stats/style.css +151 -0
  288. package/packs/gallery/stats/template.html +1 -0
  289. package/packs/gallery/stats/writing.md +11 -0
  290. package/packs/gallery/today/component.json +12 -0
  291. package/packs/gallery/today/example.html +1 -0
  292. package/packs/gallery/today/kit.js +185 -0
  293. package/packs/gallery/today/preview.svg +1 -0
  294. package/packs/gallery/today/style.css +120 -0
  295. package/packs/gallery/today/template.html +1 -0
  296. package/packs/gallery/today/writing.md +14 -0
  297. package/packs/gallery/todos/component.json +19 -0
  298. package/packs/gallery/todos/example.html +3 -0
  299. package/packs/gallery/todos/kit.js +342 -0
  300. package/packs/gallery/todos/preview.svg +1 -0
  301. package/packs/gallery/todos/style.css +267 -0
  302. package/packs/gallery/todos/template.html +3 -0
  303. package/packs/gallery/todos/writing.md +30 -0
  304. package/packs/gallery/weather/component.json +19 -0
  305. package/packs/gallery/weather/example.html +3 -0
  306. package/packs/gallery/weather/kit.js +312 -0
  307. package/packs/gallery/weather/preview.svg +1 -0
  308. package/packs/gallery/weather/style.css +258 -0
  309. package/packs/gallery/weather/template.html +3 -0
  310. package/packs/gallery/weather/writing.md +26 -0
  311. package/packs/gallery-soft/pack.json +8 -0
  312. package/release.json +12 -10
  313. package/sdk/doc-kit.d.ts +396 -5
  314. package/sdk/doc-state.d.ts +2 -0
  315. package/skill/SKILL.md +56 -23
  316. package/skill/references/block-script.md +140 -0
  317. package/skill/references/building-blocks.md +257 -0
  318. package/skill/references/extensions.md +59 -0
  319. package/skill/references/pages-and-data.md +221 -0
  320. package/skill/references/writing-html.md +6 -4
  321. package/skill/sdk/doc-kit.d.ts +396 -5
  322. package/skill/sdk/doc-state.d.ts +2 -0
  323. package/web/assets/index-BK2e_07w.js +13 -0
  324. package/web/assets/index-CWFI83mF.css +1 -0
  325. package/web/index.html +2 -2
  326. package/dist/chunk-3HIRPVLU.mjs +0 -2
  327. package/dist/chunk-5MHMDLZD.mjs +0 -4
  328. package/dist/chunk-64K7IPLO.mjs +0 -255
  329. package/dist/chunk-6QXGZM36.mjs +0 -8
  330. package/dist/chunk-6RGJOMJP.mjs +0 -16
  331. package/dist/chunk-AJ73XJF3.mjs +0 -2
  332. package/dist/chunk-AUZGSZJH.mjs +0 -13
  333. package/dist/chunk-BFE45AKL.mjs +0 -2
  334. package/dist/chunk-BXO2ZY5P.mjs +0 -2
  335. package/dist/chunk-CEH4MRCC.mjs +0 -2
  336. package/dist/chunk-CY2FNNAB.mjs +0 -16
  337. package/dist/chunk-DFUDV4LX.mjs +0 -2
  338. package/dist/chunk-DKXG4XDG.mjs +0 -2
  339. package/dist/chunk-EZQ34M3F.mjs +0 -124
  340. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  341. package/dist/chunk-FUKQNOQS.mjs +0 -9
  342. package/dist/chunk-JPOSLGOC.mjs +0 -6
  343. package/dist/chunk-L3QKH4OK.mjs +0 -26
  344. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  345. package/dist/chunk-LEF6DRCK.mjs +0 -59
  346. package/dist/chunk-NO5NJCHO.mjs +0 -2
  347. package/dist/chunk-NQPVAU63.mjs +0 -2
  348. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  349. package/dist/chunk-O35FIXRB.mjs +0 -2
  350. package/dist/chunk-P2CAZPDP.mjs +0 -3
  351. package/dist/chunk-PL6GHLGD.mjs +0 -15
  352. package/dist/chunk-QJIR2BLP.mjs +0 -6
  353. package/dist/chunk-QUPLVPB2.mjs +0 -7
  354. package/dist/chunk-R7BNENLY.mjs +0 -4
  355. package/dist/chunk-S6O4UHLC.mjs +0 -2
  356. package/dist/chunk-SGJWQKEI.mjs +0 -2
  357. package/dist/chunk-TX6JH7TR.mjs +0 -2
  358. package/dist/chunk-TYK6VNAB.mjs +0 -10
  359. package/dist/chunk-UALRNVSE.mjs +0 -4
  360. package/dist/chunk-V3HXVQEM.mjs +0 -2
  361. package/dist/chunk-V5VXUJKG.mjs +0 -2
  362. package/dist/chunk-VBXMADMP.mjs +0 -2
  363. package/dist/chunk-VNV55RUA.mjs +0 -2
  364. package/dist/chunk-VPD2PS35.mjs +0 -2
  365. package/dist/chunk-W4VKN3GF.mjs +0 -7
  366. package/dist/chunk-W5BXXPDJ.mjs +0 -3
  367. package/dist/chunk-XUNHRCOG.mjs +0 -6
  368. package/skill/references/building-components.md +0 -367
  369. package/web/assets/index-Bh74DfsV.css +0 -1
  370. package/web/assets/index-UW0RyFFt.js +0 -13
@@ -28,15 +28,14 @@ const STYLE = `
28
28
  .nmedia-menu-tab.active::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: 0; border-bottom: 2px solid var(--c-texPri); }
29
29
  .nmedia-menu-body { padding: 12px 12px 10px; }
30
30
  .nmedia-menu-inner { display: flex; justify-content: center; padding: 4px; }
31
- .nmedia-menu-upload { display: flex; align-items: center; justify-content: center; width: 100%; height: 32px; padding: 0 12px; border-radius: 6px; border: 1px solid var(--ca-borPriTra); font-size: 14px; font-weight: 500; line-height: 1.2; cursor: pointer; transition: background 20ms ease-in; }
32
- .nmedia-menu-upload:hover { background: var(--ca-bacIntTra); }
31
+ /* Its buttons are the editor's one button look (look.mjs BUTTONS): the pale blue primary, here what the tab does. */
32
+ .nmedia-menu-upload, .nmedia-menu-embed { width: 100%; font-size: 14px; }
33
33
  .nmedia-menu-link { flex-direction: column; align-items: stretch; gap: 8px; }
34
34
  .nmedia-menu-input { display: flex; align-items: center; gap: 4px; width: 100%; height: 28px; padding: 3px 6px; border-radius: 6px; background: var(--ca-bacSecTra); box-shadow: 0 0 0 1px var(--ca-borPriTra); box-sizing: border-box; }
35
35
  .nmedia-menu-input:focus-within { box-shadow: var(--c-inpBluFocRin); }
36
36
  .nmedia-menu-input input { flex: 1; min-width: 0; border: none; outline: none; background: transparent; padding: 0; font: inherit; font-size: 14px; line-height: 20px; color: var(--c-texPri); }
37
37
  .nmedia-menu-input input::placeholder { color: var(--c-texTer); }
38
- .nmedia-menu-embed { display: flex; align-items: center; justify-content: center; width: 100%; max-width: 300px; margin: 0 auto; height: 28px; padding: 0 8px; border-radius: 6px; background: var(--c-bluBacAccPri); color: #fff; font-size: 14px; font-weight: 500; cursor: pointer; box-sizing: border-box; }
39
- .nmedia-menu-embed:hover { filter: brightness(.94); }
38
+ .nmedia-menu-embed { max-width: 300px; margin: 0 auto; }
40
39
  .nmedia-menu-caption { margin: -6px 0 4px; padding: 4px 0; font-size: 12px; line-height: 1.4; color: var(--c-texSec); text-align: center; }
41
40
  .nmedia-menu.dragover .nmedia-menu-body { background: var(--ca-bluBacSecTra); }
42
41
  .nmedia-menu-choose { margin-top: 8px; }
@@ -76,10 +75,10 @@ export function openMediaMenu(anchor, { tabs, width, upload = {}, link = {}, onU
76
75
  `<div class="nmedia-menu-tab-wrap"><div role="tab" class="nmedia-menu-tab${key === current ? " active" : ""}" aria-selected="${key === current}" data-tab="${key}">${esc(label)}</div></div>`).join("")}</div>` : "")
77
76
  + `<div class="nmedia-menu-body">${current === "upload"
78
77
  ? (upload.primary
79
- ? `<div class="nmedia-menu-inner nmedia-menu-link"><div role="button" tabindex="0" class="nmedia-menu-embed nmedia-menu-choose" data-do="upload">${esc(upload.label ?? t("选择文件"))}</div>${upload.caption ? `<div class="nmedia-menu-caption">${esc(upload.caption)}</div>` : ""}</div>`
80
- : `<div class="nmedia-menu-inner"><div role="button" tabindex="0" class="nmedia-menu-upload" data-do="upload">${esc(upload.label ?? t("上传文件"))}</div></div>`)
78
+ ? `<div class="nmedia-menu-inner nmedia-menu-link"><div role="button" tabindex="0" class="ge-btn nmedia-menu-embed nmedia-menu-choose" data-primary data-do="upload">${esc(upload.label ?? t("选择文件"))}</div>${upload.caption ? `<div class="nmedia-menu-caption">${esc(upload.caption)}</div>` : ""}</div>`
79
+ : `<div class="nmedia-menu-inner"><div role="button" tabindex="0" class="ge-btn nmedia-menu-upload" data-primary data-do="upload">${esc(upload.label ?? t("上传文件"))}</div></div>`)
81
80
  : `<div class="nmedia-menu-inner nmedia-menu-link"><div class="nmedia-menu-input"><input type="url" placeholder="${esc(link.placeholder ?? t("粘贴链接…"))}" autocomplete="off"></div>`
82
- + `<div role="button" tabindex="0" class="nmedia-menu-embed" data-do="embed">${esc(link.button ?? t("嵌入链接"))}</div>`
81
+ + `<div role="button" tabindex="0" class="ge-btn nmedia-menu-embed" data-primary data-do="embed">${esc(link.button ?? t("嵌入链接"))}</div>`
83
82
  + (link.caption ? `<div class="nmedia-menu-caption">${esc(link.caption)}</div>` : "") + "</div>"}</div>`;
84
83
  const input = el.querySelector("input");
85
84
  if (input) {
@@ -37,6 +37,8 @@ const STYLE = `
37
37
  }
38
38
  /* A width given (data-sized: opts.width, placed by place()): that width, whatever the rows hold. */
39
39
  .ge-pop[data-sized] { min-width: 0; max-width: calc(100vw - 16px); }
40
+ /* A short list of one kind of word (a card's 大小 ›: 4 × 2): only as wide as its rows, no 180px floor. */
41
+ .ge-pop[data-compact] { min-width: 0; }
40
42
  @keyframes ge-pop-in { from { opacity: 0; transform: scale(.98); } }
41
43
  @media (prefers-reduced-motion: reduce) { .ge-pop { animation: none; } }
42
44
  .ge-pop-title { display: flex; align-items: center; gap: 8px; padding: 8px 12px; color: var(--ge-tx2); font-size: 12px; font-weight: 500; line-height: 14px; }
@@ -58,6 +60,7 @@ const STYLE = `
58
60
  words and the hint takes what is left, cut short with an ellipsis where it is too long (an English
59
61
  row's current choice once took the whole row, its label drawn 0px wide). */
60
62
  .ge-pop-label:has(+ .ge-pop-hint) { flex: 1 0 auto; max-width: 100%; }
63
+ .ge-pop-of { color: var(--ge-tx3); }
61
64
  .ge-pop-hint { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--ge-tx3); font-size: 12px; white-space: nowrap; }
62
65
  /* A row of a page (its title, the page it is under as its hint): a long title is what an ellipsis
63
66
  cuts, and the page above keeps its words, up to a part of the row. */
@@ -125,10 +128,12 @@ const opened = new Set(); // the menus open now, whichever part of the page open
125
128
  export const menuOpen = () => opened.size > 0;
126
129
 
127
130
  /**
128
- * items: rows { label, run, icon?, hint?, long?, current?, checked?, disabled?, note?, preview? } (long:
131
+ * items: rows { label, run, icon?, of?, hint?, long?, current?, checked?, disabled?, note?, preview? } (of: what
132
+ * the label is of, grey after it, a block's pack where two rows have one name; long:
129
133
  * a label of any length, a page's title, cut before its hint is; current: the choice
130
134
  * made, checked and not to be chosen again; checked: a switch that is on, checked and clicked to turn
131
- * it off; preview: { src, says }, a picture of what the row makes, where it has one, and a line on it,
135
+ * it off; selected: the choice of a dropdown (a field's list of options), checked as current is but
136
+ * still a row to click, which closes the card with nothing changed; preview: { src, says }, a picture of what the row makes, where it has one, and a line on it,
132
137
  * shown at the row's right while the pointer rests on it, notion-lite's row tooltip) (or { label, icon?, submenu:
133
138
  * () => items, width?, search? } opening a submenu, with a search field at its top when `search` is
134
139
  * given, as `opts.search` below, and `search.empty` the words shown when nothing is found; a row
@@ -143,7 +148,9 @@ export const menuOpen = () => opened.size > 0;
143
148
  * leaves no room at all. side: "right": beside the anchor, right of it (left of it when there is no
144
149
  * room), level with it (a section's bar). Without either the menu stands below the anchor (the
145
150
  * caret), or above it when there is no room below; align: "end", its right edge at the anchor's.
146
- * icons: false, rows of words alone (no column for icons: a code block's languages); height: the
151
+ * icons: false, rows of words alone (no column for icons: a code block's languages; a row that opens a
152
+ * submenu says it of its own as `icons: false`, and `compact: true` makes it only as wide as its rows, no
153
+ * 180px floor: a card's 大小 ›); height: the
147
154
  * card's greatest height, in px (its rows scroll inside it past that). stand(size): where the card
148
155
  * stands, its { left, top } for its { width, height }, in place of all of these rules (a pasted
149
156
  * address's 选择视图, by the format bar's rule: placement.mjs besideText). inside: an element the rows
@@ -221,7 +228,8 @@ export function createMenu() {
221
228
  if (row.split) row.setAttribute("data-split", "");
222
229
  if (item.long) row.setAttribute("data-long", "");
223
230
  if (options.icons !== false) words.append(element("span", { class: "ge-pop-icon", html: item.icon ?? "" }));
224
- words.append(element("span", { class: "ge-pop-label", text: item.label }));
231
+ // A row's pack where two rows have one name (我的待办 · 柔光风格): grey, after the name, in the label.
232
+ words.append(item.of ? element("span", { class: "ge-pop-label" }, item.label, element("span", { class: "ge-pop-of", text: ` · ${item.of}` })) : element("span", { class: "ge-pop-label", text: item.label }));
225
233
  if (item.hint) words.append(element("span", { class: "ge-pop-hint", text: item.hint }));
226
234
  if (row.split) row.append(words, element("span", { class: "ge-pop-more", html: `<span class="ge-pop-arrow">${ICONS.chevron}</span>` }));
227
235
  else if (item.submenu) row.append(element("span", { class: "ge-pop-arrow", html: ICONS.chevron }));
@@ -230,11 +238,12 @@ export function createMenu() {
230
238
  row.submenu = item.submenu;
231
239
  row.subWidth = item.width;
232
240
  row.subSearch = item.search;
241
+ row.subLook = { icons: item.icons, compact: item.compact };
233
242
  }
234
- if (item.current || item.checked) {
235
- row.append(element("span", { class: "ge-pop-check", html: ICONS.check, "aria-label": item.current ? t("当前") : t("已开启") }));
243
+ if (item.current || item.checked || item.selected) {
244
+ row.append(element("span", { class: "ge-pop-check", html: ICONS.check, "aria-label": item.checked ? t("已开启") : t("当前") }));
236
245
  row.setAttribute("data-current", "");
237
- if (item.checked) row.setAttribute("aria-checked", "true");
246
+ if (item.checked || item.selected) row.setAttribute("aria-checked", "true");
238
247
  }
239
248
  row.disabled = Boolean(item.disabled || item.current);
240
249
  row.run = item.run;
@@ -360,6 +369,7 @@ export function createMenu() {
360
369
  const search = row.subSearch && { placeholder: row.subSearch.placeholder, filter: (text) => closing(row.subSearch.filter(text)) };
361
370
  sub.open(items, row.getBoundingClientRect(), {
362
371
  side: "right", gap: 0, height: Math.round(innerHeight * 0.7), highlight: keys, width: row.subWidth, label: row.textContent, search, empty: row.subSearch?.empty, focus: keys,
372
+ ...(row.subLook.icons === false ? { icons: false } : {}), ...(row.subLook.compact ? { compact: true } : {}),
363
373
  onClose: () => {
364
374
  row.removeAttribute("aria-expanded");
365
375
  if (subRow === row) {
@@ -439,6 +449,7 @@ export function createMenu() {
439
449
  opened.add(menu);
440
450
  node = element("div", { class: "ge-pop", role: "menu", "aria-label": opts.label ?? t("菜单") });
441
451
  if (options.width) node.setAttribute("data-sized", "");
452
+ if (options.compact) node.setAttribute("data-compact", "");
442
453
  if (opts.inside) {
443
454
  node.setAttribute("data-inside", "");
444
455
  opts.inside.append(node);
@@ -1,6 +1,6 @@
1
1
  import { Z } from "../../shared/layers.mjs";
2
2
  import { PANEL_STYLE, onPanels, panelGone, panelRoom, panelShown } from "./panel.mjs";
3
- import { addStyle, button, element, mount } from "./ui.mjs";
3
+ import { addStyle, element, mount, textButton } from "./ui.mjs";
4
4
  import { t } from "../../shared/i18n/index.mjs";
5
5
  import { preference } from "../../shared/preferences.mjs";
6
6
  import { routine } from "./quiet.mjs";
@@ -128,7 +128,7 @@ export function showPanel(title) {
128
128
  closePanel();
129
129
  addStyle(PANEL_STYLE);
130
130
  panel = mount(element("div", { class: "ge-panel", role: "region", "aria-label": title },
131
- element("div", { class: "ge-panel-head" }, element("span", { text: title }), button(t("关闭"), closePanel, { "data-quiet": "" }))));
131
+ element("div", { class: "ge-panel-head" }, element("span", { text: title }), textButton(t("关闭"), closePanel, { quiet: true }))));
132
132
  panelShown(panel);
133
133
  }
134
134
 
@@ -31,8 +31,19 @@ export function menuEntry(key) {
31
31
  /** The row of a key of a menu of blocks: its type's, or its variant's; null for neither. */
32
32
  export const menuRow = (key) => TYPE_INFO[key] ?? menuEntry(key).variant;
33
33
 
34
- /** The keys of every variant there is now. */
35
- export const variantKeys = () => Object.keys(TYPE_COMPONENT).flatMap((type) => Object.keys(pageOfType(type)?.menuVariants?.() ?? {}).map((name) => `${type}:${name}`));
34
+ // Every variant there is now: [key, row].
35
+ const variants = () => Object.keys(TYPE_COMPONENT).flatMap((type) => Object.entries(pageOfType(type)?.menuVariants?.() ?? {}).map(([name, row]) => [`${type}:${name}`, row]));
36
+
37
+ /**
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.
42
+ */
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);
36
47
 
37
48
  /** The page objects, in the order they are made. */
38
49
  export const pageObjects = () => Object.values(pages);
@@ -3,8 +3,10 @@ import { Z } from "../../shared/layers.mjs";
3
3
  // The editor's bottom panels: the conflicts' (conflicts.mjs, left) and the lost texts' (notices.mjs,
4
4
  // right) are the same card, and the bar of the Agent's changes (changes/bar.mjs, in the middle) is its
5
5
  // buttons in a row: a small muted head with a red dot, items under it, the reader's words as
6
- // a quote, one filled primary button, the rest outlined or, looking only, quiet text. No notice covers
7
- // one: while one stands, the toast stands above it (notices.mjs, and the workbench's, told the room).
6
+ // a quote, one pale blue primary button, the rest with no line and no fill (gray under the pointer) or, looking
7
+ // only, quiet text: all of them the editor's one button look (look.mjs BUTTONS, ui.mjs textButton), none of
8
+ // their own here. No notice covers one: while one stands, the toast stands above it (notices.mjs, and the
9
+ // workbench's, told the room).
8
10
 
9
11
  const PANELS = ".ge-conflicts:not([hidden]), .ge-panel, .ge-changes:not([hidden])";
10
12
 
@@ -55,11 +57,4 @@ export const PANEL_STYLE = `
55
57
  :is(.ge-conflict, .ge-panel-item) p + .ge-version { margin-top: 6px; }
56
58
  :is(.ge-conflicts, .ge-panel) .ge-version { margin: 0; padding: 0 0 0 10px; border-left: 2px solid var(--ge-quote); overflow-wrap: anywhere; }
57
59
  :is(.ge-conflicts, .ge-panel) .ge-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 12px; }
58
- :is(.ge-conflicts, .ge-panel) button { height: 28px; padding: 0 10px; border: 1px solid var(--ge-edge); border-radius: 6px; background: transparent; color: inherit; font: inherit; font-weight: 500; white-space: nowrap; cursor: pointer; }
59
- :is(.ge-conflicts, .ge-panel) button:hover:not(:disabled) { background: var(--ge-hover); }
60
- :is(.ge-conflicts, .ge-panel) button[data-primary] { border-color: transparent; background: var(--ge-accent); color: #fff; }
61
- :is(.ge-conflicts, .ge-panel) button[data-primary]:hover:not(:disabled) { background: var(--ge-accent-hover); }
62
- :is(.ge-conflicts, .ge-panel) button[data-quiet] { height: 24px; padding: 0 6px; border-color: transparent; color: var(--ge-tx2); font-weight: 400; }
63
- :is(.ge-conflicts, .ge-panel) button[data-quiet]:hover:not(:disabled) { color: var(--ge-tx); background: transparent; }
64
- :is(.ge-conflicts, .ge-panel) button:disabled { opacity: .45; cursor: default; }
65
60
  `;
@@ -1,6 +1,7 @@
1
1
  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
+ import { entryOfKey } from "../../shared/extension-sidebar.mjs";
4
5
  import { PROTOCOL, VERSION } from "../../shared/session.mjs";
5
6
 
6
7
  // The links of a page shown in 演示视图 (?present: src/server/routes.mjs), which has no editor and whose
@@ -13,7 +14,9 @@ import { PROTOCOL, VERSION } from "../../shared/session.mjs";
13
14
  // it is, with a word through it (the workbench's notice), and on another port what the list does not
14
15
  // hold (addressOn) is another program's, a web address like any. What the kit or the browser does with
15
16
  // a click stays theirs: an anchor, a link to save, a link of a target of its own, any click with a key
16
- // held.
17
+ // held. A workbench's address that names a web link of the sidebar, or an entry of an extension's column
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.
17
20
 
18
21
  const own = pageAddress(location.href)?.slug ?? null;
19
22
  const openTab = (address) => window.open(address, "_blank", "noopener");
@@ -34,7 +37,13 @@ async function follow(address) {
34
37
  if (!mayBePage(address)) return void openTab(address);
35
38
  const listed = await read("outline");
36
39
  const place = addressOn(address, listed?.reports ? pageLinks(linkedPages(listed.reports)) : null, location.origin);
37
- if (place) go(place);
40
+ const web = place && listed?.reports?.[place.slug]?.kind === "link" ? listed.reports[place.slug] : null;
41
+ const entry = !place && !elsewhere(address, location.origin) ? entryOfKey(listed?.extensions, workbenchHash(address) ?? "") : null;
42
+ if (web) {
43
+ if (/^https?:/.test(web.url ?? "")) openTab(web.url);
44
+ else sayGone(web.slug, listed);
45
+ } else if (place) go(place);
46
+ else if (entry?.url) openTab(entry.url);
38
47
  else if (isPageAddress(address) && !elsewhere(address, location.origin)) sayGone(pageAddress(address)?.slug ?? workbenchHash(address), listed ?? undefined);
39
48
  else openTab(address);
40
49
  }
@@ -11,8 +11,9 @@ import { t } from "../../../shared/i18n/index.mjs";
11
11
 
12
12
  // Resizing chart cards and tables in the preview ("Sizes" in the kit's styles/sizes.css). With the pointer on the
13
13
  // right edge of a card or table, or on its bottom edge (a chart's plot height, a table's scroll
14
- // height), the cursor changes; dragging shows a blue line where the edge goes and the size that
15
- // makes, and letting go applies it as one undo step (Esc cancels). Widths are shares of the width
14
+ // height), the cursor changes; dragging shows a gray line where the edge goes (darker where it
15
+ // snaps) and the bottom notice says the size that makes while the drag lasts (no label beside the
16
+ // line: the user, 2026-11-07), and letting go applies it as one undo step (Esc cancels). Widths are shares of the width
16
17
  // the block stands in: the reading width, or its column's. A card's width snaps to the middle and
17
18
  // to the end; a centered card grows on both sides. A table's
18
19
  // width moves its own right end by as much as the pointer moves, also when the table is wider than
@@ -23,17 +24,19 @@ import { t } from "../../../shared/i18n/index.mjs";
23
24
  // height of its frame: shorter than its code, the code scrolls inside it, and the edge snaps to the
24
25
  // height that shows all of it (no height of its own). Letting go where the drag started changes
25
26
  // nothing. Anything that ends the press another way (the capture lost, the window losing the
26
- // focus, the page hidden) cancels it; the lines and the label belong to the drag (overlays.mjs). A
27
- // code block's bottom edge has no label: the line alone (where it snaps, the status line says after).
27
+ // focus, the page hidden) cancels it; the lines belong to the drag (overlays.mjs), and the notice it
28
+ // put up goes with it. Snapping takes the edge within SNAP (6px) of a place, little enough that a
29
+ // size near one can still be had (14px before 2026-11-07, which the user found too strong).
28
30
 
29
31
  const STYLE = `
30
32
  ${gestureLock("data-ge-resize=\"x\"", { cursor: "ew-resize" })}
31
33
  ${gestureLock("data-ge-resize=\"y\"", { cursor: "ns-resize" })}
32
- .ge-resize-line { position: fixed; left: 0; top: 0; z-index: ${Z.guide}; border-radius: 2px; background: rgb(151 197 241); pointer-events: none; }
33
- .ge-resize-label { position: fixed; left: 0; top: 0; z-index: ${Z.label}; padding: 2px 7px; border-radius: 5px; background: var(--doc-accent, #2678d8); color: #fff; font: 12px/18px var(--doc-sans-font, var(--doc-font, system-ui, sans-serif)); white-space: nowrap; pointer-events: none; }
34
+ .ge-resize-line { position: fixed; left: 0; top: 0; z-index: ${Z.guide}; border-radius: 1px; background: var(--ge-guide-hot); pointer-events: none; }
35
+ .ge-resize-line[data-snap] { background: var(--ge-guide-snap); }
34
36
  `;
35
37
  const GRAB = { inside: 3, outside: 8 }; // how near an edge the pointer takes it, inside and outside the block (px)
36
- const SNAP = 14; // px
38
+ const SNAP = 6; // px
39
+ const LIVE = 60000; // ms: the notice saying the size dragged to stays while the drag lasts (it is taken off when it ends)
37
40
  const MIN_WIDTH = 160; // px: the least a card's edge is dragged to (a table's is its columns' floors)
38
41
  // The sizes (name and range) are each component's description's (registry SIZES): the same ones the
39
42
  // server checks a size by.
@@ -45,11 +48,12 @@ const heightRange = (kind) => range(kind, heightOf(kind));
45
48
 
46
49
  /**
47
50
  * units: Map(id -> { kind }) from the edit map. active(): whether resizing may start.
48
- * look(element, { sizes }, select): the editor's undoable change of sizes. say(text): the status line.
51
+ * look(element, { sizes }, select): the editor's undoable change of sizes. say(text, kind, hold): the
52
+ * bottom notice; settle(): the notice taken off.
49
53
  * owns(node): whether a table's right and bottom edges are the table tools' (table/widths.mjs: a
50
54
  * click arms one, a drag then is this tool's, `startTable`).
51
55
  */
52
- export function createResize({ units, active, look, say, owns = () => false }) {
56
+ export function createResize({ units, active, look, say, settle = () => {}, owns = () => false }) {
53
57
  addStyle(STYLE);
54
58
  let hover = null; // { node, axis } under the pointer
55
59
  let drag = null;
@@ -134,12 +138,12 @@ export function createResize({ units, active, look, say, owns = () => false }) {
134
138
  const snaps = [{ x: table.press, label: t("原宽度"), keep: true }, { x: table.press + (box.width - table.width), label: t("铺满正文") }];
135
139
  const snap = snaps.find((candidate) => Math.abs(candidate.x - edge) <= SNAP);
136
140
  if (snap) edge = snap.x;
137
- if (snap?.keep) return { edge, label: snap.label, value: drag.before };
141
+ if (snap?.keep) return { edge, label: snap.label, value: drag.before, snapped: true };
138
142
  // Rounded up, so the frame is never narrower than where it was let go (at the floors, say).
139
143
  const share = Math.ceil(Math.max(range("table", SPAN).min, width(edge) / box.width) * 1000) / 1000;
140
144
  const label = snap?.label ?? (share > 1 ? t("超宽表 · 表宽 {0}px,横向滚动", Math.round(width(edge)))
141
145
  : edge <= narrowest + 0.5 ? t("最窄 · {0}%", Math.round(share * 100)) : `${Math.round(share * 100)}%`);
142
- return { edge, label, value: Math.abs(share - 1) < 0.005 ? "1" : String(Math.min(share, range("table", SPAN).max)) };
146
+ return { edge, label, value: Math.abs(share - 1) < 0.005 ? "1" : String(Math.min(share, range("table", SPAN).max)), snapped: Boolean(snap) };
143
147
  }
144
148
 
145
149
  // Where the edge may go and where it snaps: back where it started (no change), the middle (a
@@ -158,13 +162,13 @@ export function createResize({ units, active, look, say, owns = () => false }) {
158
162
  let edge = Math.min(Math.max(x, near), far);
159
163
  const snap = snaps.find((candidate) => Math.abs(candidate.x - edge) <= SNAP);
160
164
  if (snap) edge = snap.x;
161
- if (snap?.keep) return { edge, label: snap.label, value: drag.before };
165
+ if (snap?.keep) return { edge, label: snap.label, value: drag.before, snapped: true };
162
166
  const share = Math.round(Math.max(range(kind, SPAN).min, shareAt(edge)) * 1000) / 1000;
163
167
  const whole = Math.abs(share - 1) < 0.005;
164
168
  const label = snap?.label ?? `${Math.round(share * 100)}%${centered ? t(" · 居中") : ""}`;
165
169
  // A card at the whole width is the default.
166
170
  const value = whole ? (kind === "figure" && !centered ? null : "1") : String(Math.min(share, range(kind, SPAN).max));
167
- return { edge, label, value };
171
+ return { edge, label, value, snapped: Boolean(snap) };
168
172
  }
169
173
 
170
174
  // ---------- heights ----------
@@ -184,8 +188,8 @@ export function createResize({ units, active, look, say, owns = () => false }) {
184
188
  const header = rect.height - code.getBoundingClientRect().height;
185
189
  const whole = rect.height + (code.scrollHeight - code.clientHeight); // all its lines, no scrolling
186
190
  const height = Math.min(Math.max(y - rect.top, Math.min(whole, header + heightRange(kind).min)), whole);
187
- if (before !== null && Math.abs(rect.top + height - rect.bottom) <= SNAP / 2) return { edge: rect.bottom, label: t("原高度"), value: before };
188
- if (whole - height <= SNAP) return { edge: rect.top + whole, label: t("显示全部代码"), value: null };
191
+ if (before !== null && Math.abs(rect.top + height - rect.bottom) <= SNAP / 2) return { edge: rect.bottom, label: t("原高度"), value: before, snapped: true };
192
+ if (whole - height <= SNAP) return { edge: rect.top + whole, label: t("显示全部代码"), value: null, snapped: true };
189
193
  const frame = Math.round(height - header);
190
194
  return { edge: rect.top + header + frame, label: t("高 {0}px · 其余滚动查看", frame), value: `${frame}px` };
191
195
  }
@@ -194,19 +198,20 @@ export function createResize({ units, active, look, say, owns = () => false }) {
194
198
  const drawn = drawnHeight(svg);
195
199
  const scale = svg.getBoundingClientRect().height / drawn || 1;
196
200
  const floor = 160; // the least a chart is drawn at (a softer floor than the one a size is checked by)
197
- if (Math.abs(rect.bottom - y) <= SNAP) return { edge: rect.bottom, label: t("原高度"), value: before };
201
+ if (Math.abs(rect.bottom - y) <= SNAP) return { edge: rect.bottom, label: t("原高度"), value: before, snapped: true };
198
202
  // Level with a card beside it (in another column of its row): the bottoms meet.
199
203
  const beside = drag.beside.find((bottom) => Math.abs(bottom - y) <= SNAP);
200
204
  const height = Math.round(Math.min(Math.max(drawn + ((beside ?? y) - rect.bottom) / scale, floor), heightRange(kind).max));
201
205
  const edge = rect.bottom + (height - drawn) * scale;
202
- return { edge, label: beside !== undefined && Math.abs(edge - beside) < 1.5 ? t("与旁边对齐 · 图表高 {0}px", height) : t("图表高 {0}px", height), value: `${height}px` };
206
+ const level = beside !== undefined && Math.abs(edge - beside) < 1.5;
207
+ return { edge, label: level ? t("与旁边对齐 · 图表高 {0}px", height) : t("图表高 {0}px", height), value: `${height}px`, snapped: level };
203
208
  }
204
209
  const all = node.scrollHeight + (node.offsetHeight - node.clientHeight);
205
210
  let height = Math.min(Math.max(y - rect.top, heightRange(kind).min), all);
206
- if (Math.abs(rect.top + height - rect.bottom) <= SNAP / 2) return { edge: rect.bottom, label: t("原高度"), value: drag.before };
211
+ if (Math.abs(rect.top + height - rect.bottom) <= SNAP / 2) return { edge: rect.bottom, label: t("原高度"), value: drag.before, snapped: true };
207
212
  if (height >= all - 2) {
208
213
  const fits = all <= scrollHeight(node); // the default scroll area shows them all
209
- return { edge: rect.top + all, label: t("显示全部行"), value: fits ? null : "none" };
214
+ return { edge: rect.top + all, label: t("显示全部行"), value: fits ? null : "none", snapped: true };
210
215
  }
211
216
  height = Math.round(height);
212
217
  return { edge: rect.top + height, label: t("高 {0}px", height), value: `${height}px` };
@@ -231,6 +236,7 @@ export function createResize({ units, active, look, say, owns = () => false }) {
231
236
  const rect = node.getBoundingClientRect();
232
237
  drag = {
233
238
  node, axis, kind, name, before, rect,
239
+ press: { x: event.clientX, y: event.clientY },
234
240
  box: contentBox(node.parentElement),
235
241
  // A card's height snaps level with the cards beside it: the bottoms of those in the other columns of its row.
236
242
  beside: axis === "y" && kind === "figure" ? besideBottoms(node) : [],
@@ -243,19 +249,22 @@ export function createResize({ units, active, look, say, owns = () => false }) {
243
249
  press: event.clientX,
244
250
  atEnd: node.scrollLeft + node.clientWidth >= node.scrollWidth - 2,
245
251
  } : null,
246
- label: kind === "code" ? null : element("div", { class: "ge-resize-label" }),
247
252
  result: null,
253
+ told: null, // what the notice says now
248
254
  };
249
255
  const session = drag;
250
256
  session.lines = (session.centered ? [0, 1] : [0]).map(() => element("div", { class: "ge-resize-line" }));
251
- for (const part of [session.label, ...session.lines].filter(Boolean)) mount(own(part, "resize", { owner: node, alive: () => drag === session }));
257
+ for (const part of session.lines) mount(own(part, "resize", { owner: node, alive: () => drag === session }));
252
258
  note("resize", `start ${axis}`);
253
259
  document.documentElement.setPointerCapture?.(event.pointerId);
254
260
  update(event.clientX, event.clientY);
255
261
  }
256
262
 
263
+ // What is being resized, in the notice's words: 卡片 and 宽度, 表格 and 高度, 代码块 and 高度.
264
+ const what = (kind, axis) => [kind === "figure" ? t("卡片") : kind === "code" ? t("代码块") : t("表格"), axis === "x" ? t("宽度") : t("高度")];
265
+
257
266
  function update(x, y) {
258
- const { axis, rect, box, lines, label } = drag;
267
+ const { axis, kind, rect, box, lines } = drag;
259
268
  drag.result = axis === "x" ? widthAt(x) : heightAt(y);
260
269
  const { edge } = drag.result;
261
270
  if (axis === "x") {
@@ -264,24 +273,28 @@ export function createResize({ units, active, look, say, owns = () => false }) {
264
273
  width: "2px", height: `${Math.round(rect.height)}px`,
265
274
  transform: `translate(${Math.round((index ? mirror : edge) - 1)}px, ${Math.round(rect.top)}px)`,
266
275
  }));
267
- if (label) label.style.transform = `translate(${Math.round(Math.min(edge + 10, innerWidth - label.offsetWidth - 8))}px, ${Math.round(Math.max(8, y - 30))}px)`;
268
276
  } else {
269
277
  Object.assign(lines[0].style, { width: `${Math.round(rect.width)}px`, height: "2px", transform: `translate(${Math.round(rect.left)}px, ${Math.round(edge - 1)}px)` });
270
- if (label) label.style.transform = `translate(${Math.round(Math.min(x + 12, innerWidth - label.offsetWidth - 8))}px, ${Math.round(Math.min(edge + 8, innerHeight - 28))}px)`;
271
278
  }
272
- if (label) label.textContent = drag.result.label;
279
+ lines.forEach((line) => line.toggleAttribute("data-snap", Boolean(drag.result.snapped)));
280
+ // The size in the bottom notice once the pointer has moved (a press alone says nothing), said again
281
+ // only when it changes (a workbench is told each time).
282
+ if (!drag.told && Math.hypot(x - drag.press.x, y - drag.press.y) < 2) return;
283
+ const told = t("{0}{1}:{2}", ...what(kind, axis), drag.result.label);
284
+ if (told !== drag.told) say(told, "note", LIVE);
285
+ drag.told = told;
273
286
  }
274
287
 
275
288
  function finish(commit) {
276
289
  if (!drag) return;
277
- const { node, axis, kind, name, before, lines, label, result, table } = drag;
290
+ const { node, axis, kind, name, lines, result, table, told } = drag;
278
291
  note("resize", commit ? "released" : "cancelled");
279
292
  drag = null;
280
293
  lines.forEach((line) => line.remove());
281
- label?.remove();
294
+ if (told) settle(); // the size dragged to: what is said next (or nothing) replaces it
282
295
  setHover(null);
283
296
  if (commit && result && look(node, { sizes: { [name]: result.value } }, null)) {
284
- say(t("已调整{0}{1}:{2} · Ctrl+Z 撤销", kind === "figure" ? t("卡片") : kind === "code" ? t("代码块") : t("表格"), axis === "x" ? t("宽度") : t("高度"), result.label));
297
+ say(t("已调整{0}{1}:{2} · Ctrl+Z 撤销", ...what(kind, axis), result.label));
285
298
  // Scrolled to its end, a table stays so: its new end shows at the frame's edge, ready for the
286
299
  // next drag (after tables.js has laid it out again, on the next frame).
287
300
  if (table?.atEnd) requestAnimationFrame(() => requestAnimationFrame(() => { node.scrollLeft = node.scrollWidth; }));
@@ -322,6 +335,6 @@ export function createResize({ units, active, look, say, owns = () => false }) {
322
335
  setHover(null);
323
336
  },
324
337
  /** The editor's UI nodes to keep in the top layer when a modal dialog opens. */
325
- get nodes() { return drag ? [...drag.lines, drag.label].filter(Boolean) : []; },
338
+ get nodes() { return drag ? [...drag.lines] : []; },
326
339
  };
327
340
  }
@@ -0,0 +1,14 @@
1
+ // Where the page shows a file of the report's folder (assets/<file>): the server's asset route,
2
+ // which editor.mjs gives (serveFiles) once it knows the report. A picture in a table cell's text is
3
+ // shown there (components/table/ cellPicture). No imports: a component's index.mjs reads it while
4
+ // the registry is still being made.
5
+
6
+ let fileUrl = (path) => path;
7
+
8
+ /** Where the page shows a file of the report's folder from now on: `urlOf(path)`. */
9
+ export const serveFiles = (urlOf) => {
10
+ fileUrl = urlOf;
11
+ };
12
+
13
+ /** Where the page shows `path`, a file of the report's folder (assets/<file>). */
14
+ export const servedFile = (path) => fileUrl(path);
@@ -5,7 +5,15 @@ import { lookOf, lookOfElement } from "../../shared/document-look.mjs";
5
5
  // tells the workbench its state, never the report's content. Nothing happens when the page is not
6
6
  // embedded. The protocol is src/shared/session.mjs; how a workbench uses it, docs/workbench.md.
7
7
  //
8
- // A message counts only from the parent window, of this origin and protocol. A command names the
8
+ // A workbench of another workspace shows a draft of this one from its own origin (docs/workbench.md
9
+ // "Drafts of other workspaces"), naming that origin in the frame's address (?shell=<origin>): the page
10
+ // then asks its own server whether that is a Gallery server of this computer (GET
11
+ // /__gallery/workspace/shell: on the registry, running, its token and process as it announces them),
12
+ // and talks to it alone once it is, holding what it says until then; one that is not hears nothing and
13
+ // is heard by nothing, so that no other page of this computer drives a document by framing it. Going,
14
+ // such a page says `bye` (the workbench of another origin cannot see it go).
15
+ //
16
+ // A message counts only from the parent window, of this origin (or the shell's, vouched for) and protocol. A command names the
9
17
  // report it is meant for: one for another report (the iframe went to another page meanwhile) is
10
18
  // refused. Every command gets exactly one reply, once it is done. Besides the editor's commands the
11
19
  // session itself answers five: `theme` (show the report light or dark, or as the reader chose, without
@@ -14,10 +22,20 @@ import { lookOf, lookOfElement } from "../../shared/document-look.mjs";
14
22
  // `scroll-top` (back to the top when scrolled down) and `close-card` (the detail card on top closes as a
15
23
  // press on its backdrop closes it: the workbench asks on a press on its own bars, or Esc there, while
16
24
  // the page says a card is open, `card` in what it sends). A card stands in the middle of the
17
- // workbench's window, not of the frame (centreCards, below).
25
+ // workbench's window, not of the frame (centreCards, below). A block's 允许访问 (doc-net-consent, the kit's)
26
+ // is sent as `consent`.
18
27
 
19
28
  /** A page opened in a frame on its own (?solo), not as the workbench's report: no session, its own notices. */
20
29
  export const SOLO = new URLSearchParams(location.search).has("solo");
30
+ /** The origin of a workbench of another workspace showing this page (?shell=<origin>), or null: this one's. */
31
+ const SHELL = (() => {
32
+ const named = new URLSearchParams(location.search).get("shell");
33
+ return named && named !== location.origin ? named : null;
34
+ })();
35
+
36
+ // Whether this page's server vouches for the workbench at `origin` (GET /__gallery/workspace/shell).
37
+ const vouched = (origin) => fetch(`/__gallery/workspace/shell?origin=${encodeURIComponent(origin)}`, { cache: "no-store" })
38
+ .then((answer) => answer.json()).then((answer) => answer?.shell === true, () => false);
21
39
 
22
40
  /**
23
41
  * slug: the report's; snapshot(): { title, state } (state: STATE_FLAGS of src/shared/session.mjs);
@@ -38,11 +56,28 @@ export const SOLO = new URLSearchParams(location.search).has("solo");
38
56
  export function connectWorkbench({ slug, snapshot, commands, modals }) {
39
57
  if (window.parent === window || SOLO) return null;
40
58
  centreCards(modals);
59
+ // The workbench's origin: this one's, or the shell's once this page's server vouched for it (null till
60
+ // then: what the page says is held, and is dropped when it does not).
61
+ let parentOrigin = SHELL ? null : location.origin;
62
+ let held = SHELL ? [] : null;
63
+ if (SHELL) {
64
+ vouched(SHELL).then((ok) => {
65
+ const waiting = held;
66
+ held = null;
67
+ if (!ok) return;
68
+ parentOrigin = SHELL;
69
+ for (const [type, fields, protocols] of waiting) send(type, fields, protocols);
70
+ // A workbench of another origin sees no pagehide of this page: it is told.
71
+ addEventListener("pagehide", () => send("bye", {}), { once: true });
72
+ });
73
+ }
41
74
  // The id the workbench speaks (src/shared/session.mjs, OLD_PROTOCOL): null until its first command,
42
75
  // and meanwhile every message goes under both, so that a workbench of either version hears it.
43
76
  let spoken = null;
44
77
  const send = (type, fields, protocols = spoken ? [spoken] : [PROTOCOL, OLD_PROTOCOL]) => {
45
- for (const protocol of protocols) window.parent.postMessage({ protocol, v: VERSION, type, slug, ...fields }, location.origin);
78
+ if (held) return void held.push([type, fields, protocols]);
79
+ if (!parentOrigin) return;
80
+ for (const protocol of protocols) window.parent.postMessage({ protocol, v: VERSION, type, slug, ...fields }, parentOrigin);
46
81
  };
47
82
  let last = null;
48
83
  const update = () => {
@@ -64,6 +99,15 @@ export function connectWorkbench({ slug, snapshot, commands, modals }) {
64
99
  event.preventDefault();
65
100
  send("key", { key });
66
101
  });
102
+ // A block's card asks the reader to let it online (its 允许访问 button: the kit's net.js): the workbench
103
+ // opens its consent dialog, the one 设置 › 组件列表 opens, and writes what the reader says; the page
104
+ // hears it on its event stream, and the kit starts the instance again.
105
+ addEventListener("doc-net-consent", (event) => {
106
+ const { block, hosts } = event.detail ?? {};
107
+ if (typeof block !== "string") return;
108
+ event.preventDefault();
109
+ send("consent", { block, hosts: Array.isArray(hosts) ? hosts : [] });
110
+ });
67
111
  const own = {
68
112
  async theme({ theme = null }) {
69
113
  if (theme !== null && theme !== "light" && theme !== "dark") return { ok: false, reason: "theme" };
@@ -97,7 +141,7 @@ export function connectWorkbench({ slug, snapshot, commands, modals }) {
97
141
 
98
142
  addEventListener("message", async (event) => {
99
143
  const message = event.data;
100
- if (event.source !== window.parent || event.origin !== location.origin) return;
144
+ if (event.source !== window.parent || !parentOrigin || event.origin !== parentOrigin) return;
101
145
  if (!PROTOCOLS.includes(message?.protocol) || message.v !== VERSION || message.type !== "command") return;
102
146
  spoken = message.protocol;
103
147
  const run = Object.hasOwn(all, message.name) ? all[message.name] : null;
@@ -42,12 +42,12 @@ const INLINE = {
42
42
  };
43
43
 
44
44
  // Groups of the unfiltered menu: 基本区块, 高级区块 (media, code, tabs and formula blocks: what
45
- // holds more than text; the types' own `menu`, registry.mjs MENU_GROUPS), 组件 (what the variants
46
- // are now: the workspace's), and what is no block under 内联.
45
+ // holds more than text; the types' own `menu`, registry.mjs MENU_GROUPS), 自定义块 (what the variants
46
+ // are now: the workspace's custom blocks, plan §4.1's name for the group 组件 was), and what is no block under 内联.
47
47
  const SECTIONS = [
48
48
  { title: t("基本区块"), types: () => MENU_GROUPS.basic },
49
49
  { title: t("高级区块"), types: () => MENU_GROUPS.advanced },
50
- { title: t("组件@group"), types: variantKeys },
50
+ { title: t("自定义块"), types: variantKeys },
51
51
  ];
52
52
 
53
53
  const infoOf = (key) => INLINE[key] ?? menuRow(key);