@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
@@ -1,17 +1,20 @@
1
1
  import { isUtf8 } from "node:buffer";
2
2
  import { createHash } from "node:crypto";
3
- import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
3
+ import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { parseFragment, serializeOuter } from "parse5";
7
7
  import { isInstance, parseCompound, parsePart, parseSelector, partOf } from "./components/index.mjs";
8
- import { ATTRIBUTE, NO_TEXT, PACKAGE_ID, PACKAGE_MARK, SLOT, instanceMatch, refusedInCopy, settingState } from "./components/installed.mjs";
8
+ import { ATTRIBUTE, NO_TEXT, PACKAGE_ID, PACKAGE_MARK, SLOT, inDocumentMenus, instanceMatch, refusedInCopy, settingState } from "./components/installed.mjs";
9
9
  import { writeAtomic } from "./files.mjs";
10
10
  import { attr, walk } from "./html-tree.mjs";
11
+ import { HOST, NET, netHostsOf } from "./net-policy.mjs";
11
12
  import { inLanguage, refusal, said, saying, told } from "./i18n/index.mjs";
13
+ import { PACK_FILE, PACK_ID, readPackManifest } from "./pack-manifest.mjs";
12
14
  import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf, markerOf, packageFolders } from "./workspace.mjs";
13
15
 
14
- // 组件包: what the workspace's component packages say of themselves to Gallery, read from
16
+ // 组件包, custom blocks (自定义块) as the UI and the Agent's documents call them: what the workspace's
17
+ // component packages say of themselves to Gallery, read from
15
18
  // <workspace>/components/<scope>/<name>/component.json, plain data, whose code no system runs here. A
16
19
  // package is a description as a kit component's is (src/shared/components/): its id, where its
17
20
  // instances are, its parts and what it leaves on a page, so that what reads a built-in's reads a
@@ -19,19 +22,22 @@ import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf
19
22
  // A manifest offers only FIELDS, no unit, box, host, rank or category: those are the engines', and a
20
23
  // field it does not know keeps its package from loading.
21
24
  //
22
- // The catalog is { packages, problems, settings, stamp }. `packages`, by id, are those that load. A
25
+ // The catalog is { packages, problems, settings, packs, stamp }. `packages`, by id, are those that load. A
23
26
  // package that does not load (a manifest that does not read as one, one for an apiVersion this Gallery
24
27
  // does not run, which counts as disabled, or one the settings turned off) stands in `problems`, by its
25
28
  // id in lowercase, as an instance names it, with why (`problem`, said()'s, in either language by
26
29
  // problemIn), and touches nothing else: its instances show as those of a package that is not there,
27
30
  // and whoever reads them says why. `settings` is what gallery.json says of the components (设置 › 组件列表,
28
- // componentSettings): `hidden` and `disabled`, by the id of a package or the name of a built-in
29
- // component, and `problem` where the file cannot be read as saying anything (settingsNow). A package
30
- // hidden runs as any other and is left out of the lists that offer one, each of which reads its state
31
- // (menuOf, packageStates); one disabled is one that does not load, so that nothing that loads a
31
+ // componentSettings): `hidden`, `disabled`, `shown` and `unlisted`, by the id of a package (`hidden` the name of a
32
+ // built-in component too), `packs` and `packsOff`, the packs' switches (a pack off: none of its blocks offered,
33
+ // as one unlisted), and `problem` where the file cannot be read as saying anything (settingsNow). `packs`, by
34
+ // the scope of a block (a pack: ./pack-manifest.mjs), what its pack.json names it, or null for a scope with
35
+ // none or one that does not read (who lists it says it by its scope).
36
+ // A package the document's menus do not offer (inDocumentMenus: hidden, unlisted, or a custom page's alone and not
37
+ // shown) runs as any other and is left out of those menus, which read its state (menuOf, packageStates); one disabled is one that does not load, so that nothing that loads a
32
38
  // package has a case for it. A stamp is a hash of what files hold: `stamp`, of a package, over what
33
39
  // reaches a page of it (STAMPED and the files `assets` names), of one that does not load over those
34
- // and why, and of the catalog, over its packages', what is hidden and whether gallery.json reads;
40
+ // and why, and of the catalog, over its packages', what is hidden, shown and unlisted and whether gallery.json reads;
35
41
  // `definition`, over the manifest alone, is what a page's commit is to be held to.
36
42
  //
37
43
  // readCatalog reads the disk; what reads a catalog for use (the scan, the checks, a page) asks
@@ -43,14 +49,38 @@ import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf
43
49
  /** The apiVersion this Gallery runs. */
44
50
  export const API_VERSION = 1;
45
51
 
46
- /** The kit's files a package may ask for by id in `assets`, as src/render/assets.mjs names them (the others are on every page). */
52
+ /** The kit's files a package may ask for by id in `assets`, as src/render/assets.mjs names them (the others are on every page, or, the board's, on every custom page). */
47
53
  export const KIT_ASSETS = new Set(["code", "state", "sheets", "tokens", "dialogs", "katex-styles", "katex", "katex-auto-render", "math", "d3", "charts", "tables"]);
48
54
 
49
55
  // What a manifest may hold, a part of one and its `runtime`.
50
- const FIELDS = ["id", "apiVersion", "version", "name", "description", "words", "parts", "runtime", "assets", "markdown", "schema"];
56
+ const FIELDS = ["id", "apiVersion", "version", "name", "description", "words", "parts", "runtime", "assets", "markdown", "schema", "places", "card", "settings", "needs"];
51
57
  const PART_FIELDS = ["at", "edit", "optional", "min", "max"];
52
58
  const RUNTIME_FIELDS = ["generated", "attrs"];
53
59
 
60
+ // Where a block may stand (`places`, notes/plans/custom-blocks-spec-2026-11-07.md section 1): in a
61
+ // document's flow, on a custom page's board as a card. One that says nothing is a document's, as every
62
+ // package before the field was.
63
+ const PLACES = ["document", "page"];
64
+ const DOCUMENT_ONLY = ["document"];
65
+ // What `card` says of a card on a board, in cells of the grid: a row is 12 of them, and a card at most
66
+ // 24 high.
67
+ const CARD_FIELDS = ["sizes", "w", "h", "min", "max", "resize", "fit", "bare"];
68
+ const GRID = { w: 12, h: 24 };
69
+ const FITS = ["fixed", "content"];
70
+ const NO_CARD = { sizes: null, any: false, w: 4, h: null, min: { w: 1, h: 1 }, max: { ...GRID }, resize: false, fit: "fixed", bare: false };
71
+ // A card setting: its key, a JavaScript name as a block reads it from its settings part; its kinds,
72
+ // each a field of the form the board draws; `required`: the card shows nothing until the setting holds a
73
+ // value (a weather card's city), which gallery page check says of a card that has none.
74
+ const SETTING_FIELDS = ["key", "label", "type", "options", "default", "required"];
75
+ const SETTING_KEY = /^[a-z][a-zA-Z0-9]*$/;
76
+ const SETTING_TYPES = ["text", "number", "select", "date", "switch", "document"];
77
+ const DATE = /^\d{4}-\d{2}-\d{2}$/;
78
+ /** The data part a block with settings keeps their values in, JSON, as the board's form writes them. */
79
+ export const SETTINGS_PART = "settings";
80
+ // What a block asks to use: the workspace's data, the keyed tables of its document, the network of a
81
+ // host (lowercase, no port and no path, "*." first for its subdomains: ./net-policy.mjs HOST).
82
+ const NEEDS = ["workspace", "tables"];
83
+
54
84
  // What a package's part may be edited as: its text, its data through the kit's edit, the document's
55
85
  // blocks (a slot, marked data-doc-slot="<name>" in its markup), or not at all.
56
86
  const EDITS = ["text", "data", "flow", "none"];
@@ -149,7 +179,7 @@ const notTooLong = (bytes, name) => {
149
179
  };
150
180
 
151
181
  const onlyFields = (table, fields, where = "") => {
152
- for (const key of Object.keys(table)) if (!fields.includes(key)) fail(said("{0} 不是组件包能写的字段", `${where}${key}`));
182
+ for (const key of Object.keys(table)) if (!fields.includes(key)) fail(said("{0} 不是自定义块的清单能写的字段", `${where}${key}`));
153
183
  };
154
184
  const required = (manifest, fields) => {
155
185
  for (const field of fields) if (manifest[field] === undefined) fail(said("缺少 {0}", field));
@@ -210,7 +240,7 @@ function runtimeOf(runtime, id) {
210
240
  if (!isTable(attrs)) fail(said("{0} 要写成一张表", "runtime.attrs"));
211
241
  for (const names of Object.values(attrs)) {
212
242
  if (!Array.isArray(names) || !names.every(isWords)) fail(said("{0} 要写成一串文字", "runtime.attrs"));
213
- for (const name of names) if (OWN_NAME.test(name)) fail(said("{0} 是 Gallery 自己的名字,组件包不能用", name));
243
+ for (const name of names) if (OWN_NAME.test(name)) fail(said("{0} 是 Gallery 自己的名字,自定义块不能用", name));
214
244
  }
215
245
  const made = (selector) => {
216
246
  const written = scoped(selector, "runtime.generated");
@@ -223,14 +253,136 @@ function runtimeOf(runtime, id) {
223
253
  };
224
254
  }
225
255
 
256
+ const isCount = (value, most) => Number.isInteger(value) && value >= 1 && value <= most;
257
+ const twice = (list) => list.find((value, index) => list.indexOf(value) !== index);
258
+
259
+ // `places`: one place or both, each once.
260
+ function placesOf(places) {
261
+ if (places === undefined) return DOCUMENT_ONLY;
262
+ if (!Array.isArray(places) || places.length === 0 || !places.every((place) => PLACES.includes(place))) fail(said("places 要写成 document、page 中的一个或两个,如 [\"document\"]"));
263
+ if (twice(places)) fail(said("places 里 {0} 写了两次", twice(places)));
264
+ return [...places];
265
+ }
266
+
267
+ // `card`: as a board reads it, every field there (NO_CARD's where the manifest says nothing; `h` null:
268
+ // the fewest whole cells its content takes). With `sizes`, the first is the default, and no w or h is
269
+ // written beside them; a min or a max beside them frees the card (`any`): the sizes are the common ones
270
+ // its 大小 menu lists, any whole size between min and max is its too (resizing by the corner, a place),
271
+ // and each of the sizes stands inside them.
272
+ function cardOf(card) {
273
+ if (card === undefined) return NO_CARD;
274
+ if (!isTable(card)) fail(said("{0} 要写成一张表", "card"));
275
+ onlyFields(card, CARD_FIELDS, "card.");
276
+ const sized = (pair) => Array.isArray(pair) && pair.length === 2 && isCount(pair[0], GRID.w) && isCount(pair[1], GRID.h);
277
+ if (card.sizes !== undefined) {
278
+ if (!Array.isArray(card.sizes) || card.sizes.length === 0 || !card.sizes.every(sized)) fail(said("card.sizes 要写成 [宽, 高] 的一串,宽 1 到 {0} 格,高 1 到 {1} 格", GRID.w, GRID.h));
279
+ const repeated = twice(card.sizes.map((pair) => pair.join("x")));
280
+ if (repeated) fail(said("card.sizes 里 {0} 写了两次", repeated));
281
+ const beside = ["w", "h"].find((field) => card[field] !== undefined);
282
+ if (beside) fail(said("写了 card.sizes,就不再写 card.{0}:第一种大小是默认的", beside));
283
+ }
284
+ for (const axis of ["w", "h"]) if (card[axis] !== undefined && !isCount(card[axis], GRID[axis])) fail(said("{0} 要写成 1 到 {1} 的整数", `card.${axis}`, GRID[axis]));
285
+ const bounds = {};
286
+ for (const bound of ["min", "max"]) {
287
+ const value = card[bound];
288
+ if (value === undefined) continue;
289
+ if (!isTable(value)) fail(said("{0} 要写成 { w, h },单位是格", `card.${bound}`));
290
+ onlyFields(value, ["w", "h"], `card.${bound}.`);
291
+ for (const axis of ["w", "h"]) if (value[axis] !== undefined && !isCount(value[axis], GRID[axis])) fail(said("{0} 要写成 1 到 {1} 的整数", `card.${bound}.${axis}`, GRID[axis]));
292
+ bounds[bound] = value;
293
+ }
294
+ const min = { w: bounds.min?.w ?? 1, h: bounds.min?.h ?? 1 };
295
+ const max = { w: bounds.max?.w ?? GRID.w, h: bounds.max?.h ?? GRID.h };
296
+ const w = card.sizes ? card.sizes[0][0] : card.w ?? NO_CARD.w;
297
+ const h = card.sizes ? card.sizes[0][1] : card.h ?? null;
298
+ const any = Boolean(card.sizes) && (card.min !== undefined || card.max !== undefined);
299
+ if (any) {
300
+ const outside = card.sizes.find(([width, height]) => width < min.w || width > max.w || height < min.h || height > max.h);
301
+ if (outside) fail(said("card.sizes 里 {0} 不在 card.min 到 card.max 之间", outside.join("x")));
302
+ }
303
+ for (const axis of ["w", "h"]) {
304
+ if (min[axis] > max[axis]) fail(said("card.min.{0} 比 card.max.{0} 大", axis));
305
+ const value = { w, h }[axis];
306
+ if (value !== null && !card.sizes && (value < min[axis] || value > max[axis])) fail(said("默认的 card.{0} 是 {1},不在 card.min 到 card.max 之间({2} 到 {3})", axis, value, min[axis], max[axis]));
307
+ }
308
+ for (const flag of ["resize", "bare"]) if (card[flag] !== undefined && typeof card[flag] !== "boolean") fail(said("{0} 要写成 true 或 false", `card.${flag}`));
309
+ if (card.fit !== undefined && !FITS.includes(card.fit)) fail(said("card.fit 只能是 fixed 或 content"));
310
+ return {
311
+ sizes: card.sizes ? card.sizes.map(([width, height]) => [width, height]) : null,
312
+ any, w, h, min, max,
313
+ resize: card.resize ?? false,
314
+ fit: card.fit ?? "fixed",
315
+ bare: card.bare ?? false,
316
+ };
317
+ }
318
+
319
+ // Whether `value` can be the default of a setting of `type` (with its `options`).
320
+ const fitsSetting = (value, { type, options }) => ({
321
+ text: typeof value === "string",
322
+ number: typeof value === "number" && Number.isFinite(value),
323
+ select: options?.includes(value),
324
+ date: typeof value === "string" && DATE.test(value),
325
+ switch: typeof value === "boolean",
326
+ document: typeof value === "string",
327
+ })[type];
328
+
329
+ // `settings`: the fields of a card's form, each key once; a block with any keeps their values in a JSON
330
+ // data part of its own (SETTINGS_PART), which its parts are to name.
331
+ function settingsOf(settings, parts) {
332
+ if (settings === undefined) return [];
333
+ if (!Array.isArray(settings)) fail(said("{0} 要写成一串字段", "settings"));
334
+ const keys = [];
335
+ settings.forEach((setting, index) => {
336
+ const where = `settings[${index}]`;
337
+ if (!isTable(setting)) fail(said("{0} 要写成一张表", where));
338
+ onlyFields(setting, SETTING_FIELDS, `${where}.`);
339
+ if (typeof setting.key !== "string" || !SETTING_KEY.test(setting.key)) fail(said("{0} 的 key 以小写字母开头,只用字母和数字(如 city、startDate)", where));
340
+ if (keys.includes(setting.key)) fail(said("settings 里 key {0} 写了两次", setting.key));
341
+ keys.push(setting.key);
342
+ bothLanguages(setting.label, `${where}.label`);
343
+ if (!SETTING_TYPES.includes(setting.type)) fail(said("{0} 的 type 只能是 {1}", where, SETTING_TYPES.join(", ")));
344
+ if (setting.type === "select") {
345
+ if (!Array.isArray(setting.options) || setting.options.length === 0 || !setting.options.every(isWords)) fail(said("{0} 是 select,要有 options:一串文字", where));
346
+ if (twice(setting.options)) fail(said("{0} 的 options 里 {1} 写了两次", where, twice(setting.options)));
347
+ } else if (setting.options !== undefined) fail(said("{0} 不是 select,不写 options", where));
348
+ if (setting.default !== undefined && !fitsSetting(setting.default, setting)) fail(said("{0} 的 default 和它的 type {1} 不符", where, setting.type));
349
+ if (setting.required !== undefined && typeof setting.required !== "boolean") fail(said("{0} 的 required 要写 true 或 false", where));
350
+ });
351
+ const part = parts[SETTINGS_PART];
352
+ if (settings.length > 0 && !(part?.edit === "data" && tailTags(part.at).every((tag) => tag === "script") && tailTags(part.at).length > 0)) {
353
+ fail(said("写了 settings,parts 里就要有一个名为 settings、edit 为 data 的部分,它是一个 <script type=\"application/json\">,存卡片设置的值"));
354
+ }
355
+ return settings.map((setting) => ({ ...setting }));
356
+ }
357
+
358
+ // `needs`: what a block asks to use, each once.
359
+ function needsOf(needs) {
360
+ if (needs === undefined) return [];
361
+ if (!Array.isArray(needs)) fail(said("{0} 要写成一串文字", "needs"));
362
+ for (const need of needs) {
363
+ if (typeof need === "string" && (NEEDS.includes(need) || (need.startsWith(NET) && HOST.test(need.slice(NET.length))))) continue;
364
+ fail(said("needs 里的 {0} 不认识:只认 workspace、tables 和 net:<域名>(小写的主机名,不带端口和路径,可以以 *. 开头)", typeof need === "string" ? need : JSON.stringify(need)));
365
+ }
366
+ if (twice(needs)) fail(said("needs 里 {0} 写了两次", twice(needs)));
367
+ return [...needs];
368
+ }
369
+
370
+ // What a block that may stand on a custom page cannot hold: a slot, whose blocks are a document's,
371
+ // which no page has the editor and the rendering of.
372
+ function pageParts(places, parts) {
373
+ if (!places.includes("page")) return;
374
+ const slot = Object.entries(parts).find(([, part]) => part.edit === "flow");
375
+ if (slot) fail(said("places 里有 page,就不能有插槽:{0} 的 edit 是 flow,插槽放的是文档的块,自定义页上没有", `parts.${slot[0]}`));
376
+ }
377
+
226
378
  // `assets`: the kit's files a package asks for, by id, and files of its own, by their path in its folder.
227
379
  function assetsOf(assets, folder) {
228
380
  if (assets === undefined) return { kit: [], own: [] };
229
381
  if (!Array.isArray(assets) || !assets.every(isWords)) fail(said("{0} 要写成一串文字", "assets"));
230
382
  const own = assets.filter((asset) => !KIT_ASSETS.has(asset));
231
383
  for (const asset of own) {
232
- if (!OWN_FILE.test(asset)) fail(said("assets 里的 {0} 既不是套件的资源,也不是包里的文件", asset));
233
- if (!ownPath(folder, asset)) fail(said("assets 写了 {0},包里没有这个文件", asset));
384
+ if (!OWN_FILE.test(asset)) fail(said("assets 里的 {0} 既不是套件的资源,也不是块的文件夹里的文件", asset));
385
+ if (!ownPath(folder, asset)) fail(said("assets 写了 {0},块的文件夹里没有这个文件", asset));
234
386
  }
235
387
  return { kit: assets.filter((asset) => KIT_ASSETS.has(asset)), own };
236
388
  }
@@ -245,12 +397,12 @@ function packageOf(id, folder, text) {
245
397
  fail(said("component.json 不是 JSON:{0}", error.message));
246
398
  }
247
399
  if (!isTable(manifest)) fail(said("component.json 写的不是一个对象"));
248
- if (!PACKAGE_ID.test(id)) fail(said("{0} 不能做组件包的 id:范围和名字都只用小写字母、数字和连字符", id));
249
- for (const name of id.split("/")) if (DEVICE_NAMES.has(name)) fail(said("{0} 不能做组件包的 id:{1} 是 Windows 留给设备的名字", id, name));
400
+ if (!PACKAGE_ID.test(id)) fail(said("{0} 不能做自定义块的 id:范围和名字都只用小写字母、数字和连字符", id));
401
+ for (const name of id.split("/")) if (DEVICE_NAMES.has(name)) fail(said("{0} 不能做自定义块的 id:{1} 是 Windows 留给设备的名字", id, name));
250
402
  if (manifest.id !== id) fail(said("清单的 id 是 {0},和它的文件夹 {1} 不一样", String(manifest.id), id));
251
403
  required(manifest, ["apiVersion"]);
252
404
  if (!Number.isInteger(manifest.apiVersion) || manifest.apiVersion < 1) fail(said("apiVersion 要写成一个正整数"));
253
- if (manifest.apiVersion !== API_VERSION) throw refusal(said("这个组件包要 apiVersion {0},这一版 Gallery 只支持 {1}", manifest.apiVersion, API_VERSION), { disabled: true, version: isWords(manifest.version) ? manifest.version : null });
405
+ if (manifest.apiVersion !== API_VERSION) throw refusal(said("这个自定义块要 apiVersion {0},这一版 Gallery 只支持 {1}", manifest.apiVersion, API_VERSION), { disabled: true, version: isWords(manifest.version) ? manifest.version : null });
254
406
  onlyFields(manifest, FIELDS);
255
407
  required(manifest, ["version", "name", "description", "parts"]);
256
408
  if (!isWords(manifest.version)) fail(said("{0} 要写成文字", "version"));
@@ -263,6 +415,11 @@ function packageOf(id, folder, text) {
263
415
  const { kit, own } = assetsOf(manifest.assets, folder);
264
416
  if (manifest.markdown !== undefined && !MARKDOWN.includes(manifest.markdown)) fail(said("markdown 只能是 parts 或 text"));
265
417
  if (manifest.schema !== undefined && !(Number.isInteger(manifest.schema) && manifest.schema >= 1)) fail(said("schema 要写成一个正整数"));
418
+ const places = placesOf(manifest.places);
419
+ pageParts(places, manifest.parts);
420
+ const card = cardOf(manifest.card);
421
+ const settings = settingsOf(manifest.settings, manifest.parts);
422
+ const needs = needsOf(manifest.needs);
266
423
  const template = ownFile(folder, TEMPLATE);
267
424
  if (template) notTooLong(template, TEMPLATE);
268
425
  return {
@@ -278,6 +435,10 @@ function packageOf(id, folder, text) {
278
435
  words: manifest.words ?? "",
279
436
  assets: kit,
280
437
  markdown: manifest.markdown ?? "parts",
438
+ places,
439
+ card,
440
+ settings,
441
+ needs,
281
442
  files: [...new Set([...SERVED.filter((file) => ownPath(folder, file)), ...own])],
282
443
  template: template && wordsOf(template),
283
444
  folder,
@@ -305,10 +466,20 @@ const workspaceRoot = () => findWorkspace().root;
305
466
 
306
467
  // A file edited by hand holds what it holds: of its lists, what can be an id is read, once each, and
307
468
  // the rest is passed over, one that names nothing there doing nothing. An id may be of a package that
308
- // comes back, so it stays. Only a package can be disabled.
469
+ // comes back, so it stays. Only a package can be disabled or unlisted.
309
470
  const NAMED = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-]*)?$/;
310
471
  const idsIn = (list, shape) => [...new Set(Array.isArray(list) ? list.filter((id) => typeof id === "string" && shape.test(id)) : [])];
311
- const settingsIn = (file) => ({ hidden: idsIn(file?.components?.hidden, NAMED), disabled: idsIn(file?.components?.disabled, PACKAGE_ID) });
472
+ // `net`, the reader's consents (a block's card, 设置 › 组件列表): of each package by its id, the hosts of its
473
+ // needs it was allowed to reach, those that can be a needs' host (./net-policy.mjs HOST), once each; a
474
+ // package with none named none.
475
+ const consentsIn = (net) => Object.fromEntries(Object.entries(isTable(net) ? net : {}).filter(([id]) => PACKAGE_ID.test(id)).map(([id, hosts]) => [id, idsIn(hosts, HOST)]).filter(([, hosts]) => hosts.length > 0));
476
+ // `packs`, the packs' switches (设置 › 组件列表's pack cards): { "<pack id>": { "enabled": false } }, by id, a
477
+ // pack it names none of on; what is no pack's id or says no boolean is passed over.
478
+ const switchesIn = (packs) => Object.fromEntries(Object.entries(isTable(packs) ? packs : {}).filter(([id, entry]) => PACK_ID.test(id) && typeof entry?.enabled === "boolean").map(([id, entry]) => [id, entry.enabled]));
479
+ const settingsIn = (file) => {
480
+ const packs = switchesIn(file?.components?.packs);
481
+ return { hidden: idsIn(file?.components?.hidden, NAMED), disabled: idsIn(file?.components?.disabled, PACKAGE_ID), shown: idsIn(file?.components?.shown, PACKAGE_ID), unlisted: idsIn(file?.components?.unlisted, PACKAGE_ID), net: consentsIn(file?.components?.net), packs, packsOff: Object.keys(packs).filter((id) => packs[id] === false).sort() };
482
+ };
312
483
 
313
484
  // gallery.json's text (null: no file) as what it holds, `value`, or `problem`, why it cannot be taken
314
485
  // for a workspace's: what the catalog goes on through (settingsNow) and no writer writes over.
@@ -329,8 +500,11 @@ function fileAt(root) {
329
500
  }
330
501
 
331
502
  /**
332
- * What gallery.json says of the components: { hidden, disabled }, ids of packages and, in `hidden`,
333
- * names of built-in components. One that is not there says nothing, and neither does one that does not
503
+ * What gallery.json says of the components: { hidden, disabled, shown, unlisted, net, packs, packsOff }, ids of packages and, in
504
+ * `hidden`, names of built-in components (inDocumentMenus reads `hidden`, `shown` and `unlisted`; a block
505
+ * unlisted, 设置 › 组件列表's master switch off, is offered by no menu and no library); `net` the hosts
506
+ * each package was allowed to reach, by its id (consentsIn); `packs` each pack's switch named there, by its id (true on,
507
+ * false off), `packsOff` the ids of those off. One that is not there says nothing, and neither does one that does not
334
508
  * read or is another program's: what goes on using a workspace goes by the catalog (readCatalog).
335
509
  */
336
510
  export const componentSettings = (root = workspaceRoot()) => settingsIn(fileAt(root).value);
@@ -339,12 +513,17 @@ export const componentSettings = (root = workspaceRoot()) => settingsIn(fileAt(r
339
513
  const NOT_WRITABLE = ["EPERM", "EACCES", "EBUSY", "EROFS"];
340
514
 
341
515
  /**
342
- * gallery.json with `id` hidden or not and disabled or not, as `hidden` and `disabled` say (true,
343
- * false, or left out for as it is), written at once and with all else it holds kept. A file that does
516
+ * gallery.json with the pack `pack` on or off, as `enabled` says (components.packs, its entry's other fields
517
+ * kept), or with `id` hidden or not, shown or not, disabled or not and unlisted or not, as `hidden`,
518
+ * `shown`, `disabled` and `unlisted` say (true, false, or left out for as it is), written at once and with
519
+ * all else it holds kept; `shown` and `unlisted` are written where they hold an id or were there already
520
+ * (a block no entry names is listed). `net`, the hosts `id` is allowed
521
+ * to reach (a list: its consent, all it names in place of what was there; [] takes it back), written in
522
+ * components.net under its id, which goes with its last host, and components.net with its last id. A file that does
344
523
  * not read, is another program's, changed under this write or cannot be written is refused, and stays
345
524
  * as it is: it is not this write's to mend.
346
525
  */
347
- export async function writeComponentSettings(root, { id, hidden, disabled }) {
526
+ export async function writeComponentSettings(root, { id, hidden, shown, disabled, unlisted, net, pack, enabled }) {
348
527
  const file = layoutOf(root).marker;
349
528
  const text = await fs.readFile(file, "utf8").catch((error) => {
350
529
  if (error.code === "ENOENT") return null;
@@ -353,8 +532,26 @@ export async function writeComponentSettings(root, { id, hidden, disabled }) {
353
532
  const { value, problem } = fileOf(text);
354
533
  if (problem) fail(problem);
355
534
  const named = (list, on) => (on === undefined ? list : on ? [...new Set([...list, id])] : list.filter((other) => other !== id));
356
- const { hidden: wasHidden, disabled: wasDisabled } = settingsIn(value);
357
- const components = { ...(isTable(value?.components) ? value.components : {}), hidden: named(wasHidden, hidden), disabled: named(wasDisabled, disabled) };
535
+ const { hidden: wasHidden, disabled: wasDisabled, shown: wasShown, unlisted: wasUnlisted } = settingsIn(value);
536
+ const before = isTable(value?.components) ? value.components : {};
537
+ const nowShown = named(wasShown, shown);
538
+ const nowUnlisted = named(wasUnlisted, unlisted);
539
+ const components = {
540
+ ...before, hidden: named(wasHidden, hidden), disabled: named(wasDisabled, disabled),
541
+ ...(nowShown.length > 0 || Object.hasOwn(before, "shown") ? { shown: nowShown } : {}),
542
+ ...(nowUnlisted.length > 0 || Object.hasOwn(before, "unlisted") ? { unlisted: nowUnlisted } : {}),
543
+ };
544
+ if (pack !== undefined) {
545
+ const switches = isTable(before.packs) ? before.packs : {};
546
+ components.packs = { ...switches, [pack]: { ...(isTable(switches[pack]) ? switches[pack] : {}), enabled: enabled === true } };
547
+ }
548
+ if (net !== undefined) {
549
+ const consents = { ...(isTable(before.net) ? before.net : {}) };
550
+ if (net.length > 0) consents[id] = [...new Set(net)];
551
+ else delete consents[id];
552
+ if (Object.keys(consents).length > 0) components.net = consents;
553
+ else delete components.net;
554
+ }
358
555
  const written = await writeAtomic(file, `${JSON.stringify({ ...(value ?? WORKSPACE_MARK), components }, null, 2)}\n`, { expect: text }).catch((error) => {
359
556
  if (NOT_WRITABLE.includes(error.code)) fail(said("gallery.json 写不进去:可能是只读的,或被别的程序占着"));
360
557
  throw error;
@@ -368,31 +565,60 @@ const turnedOff = (found) => ({ ...found, off: true, disabled: true, problem: sa
368
565
 
369
566
  const NO_SETTINGS = settingsIn(null);
370
567
 
371
- /** The stamp of a catalog: of what its packages are, those that do not load and why too, of what is hidden and of why gallery.json does not read, neither of which changes a package. */
372
- export const catalogStamp = ({ packages, problems, settings = NO_SETTINGS }) => hashOf((hash) => {
568
+ /** The stamp of a catalog: of what its packages are, those that do not load and why too, of what is hidden, shown and unlisted, the packs off and their names, and of why gallery.json does not read, none of which changes a package. */
569
+ export const catalogStamp = ({ packages, problems, settings = NO_SETTINGS, packs = new Map() }) => hashOf((hash) => {
373
570
  for (const { id, stamp } of [...packages.values(), ...problems.values()].sort(byId)) hash.update(`${id} ${stamp}\n`);
374
571
  hash.update(`hidden ${[...settings.hidden].sort().join(" ")}\n`);
572
+ hash.update(`shown ${[...settings.shown].sort().join(" ")}\n`);
573
+ hash.update(`unlisted ${[...(settings.unlisted ?? [])].sort().join(" ")}\n`);
574
+ hash.update(`net ${consentPairs(settings).join(" ")}\n`);
575
+ hash.update(`packs off ${(settings.packsOff ?? []).join(" ")}\n`);
576
+ for (const [scope, name] of [...packs].sort(([a], [b]) => (a < b ? -1 : 1))) hash.update(`pack ${scope} ${JSON.stringify(name)}\n`);
375
577
  hash.update(`settings ${settings.problem?.message ?? ""}\n`);
376
578
  });
377
579
 
580
+ // The consents of `settings` as "<id>@<host>", sorted: what the stamp and the settings line say of them.
581
+ const consentPairs = ({ net = {} }) => Object.entries(net).flatMap(([id, hosts]) => hosts.map((host) => `${id}@${host}`)).sort();
582
+
378
583
  // The settings a catalog goes by: what gallery.json says or, while it cannot be read as saying
379
584
  // anything, what was in use (`held`, a broken manifest's hold, for a slip by hand), with why (`problem`).
380
585
  function settingsNow(root, held) {
381
586
  const { value, problem } = fileAt(root);
382
- return problem ? { hidden: held.hidden, disabled: held.disabled, problem } : settingsIn(value);
587
+ return problem ? { hidden: held.hidden, disabled: held.disabled, shown: held.shown ?? [], unlisted: held.unlisted ?? [], net: held.net ?? {}, packs: held.packs ?? {}, packsOff: held.packsOff ?? [], problem } : settingsIn(value);
383
588
  }
384
589
 
590
+ // What runs before the catalog reads the blocks of a workspace that holds Gallery's own (the scopes of
591
+ // Gallery's packs, `ownScopes`): (root, ids) -> anything. A server sets it as it starts
592
+ // (shared/shipped-blocks.mjs followOnRead: a copy as it was added brought up to Gallery's first), so that
593
+ // this file imports nothing of the package's paths, reading whichever workspace it is given; a command sets none.
594
+ let beforeRead = null;
595
+ let ownScopes = [];
596
+
597
+ /** `run(root, ids)` runs before each read of a workspace's blocks that finds Gallery's own (`ids`, those of `scopes`, the scopes of Gallery's packs), from now on in this process. */
598
+ export const beforeCatalogRead = (run, scopes = ["gallery"]) => {
599
+ beforeRead = run;
600
+ ownScopes = [...scopes];
601
+ };
602
+
385
603
  /**
386
604
  * The workspace's packages as they are on disk now: the catalog, whatever a package or gallery.json
387
605
  * holds. `held`: the settings of the catalog in use, for a file that does not read; a process with
388
606
  * none has nothing to go by. `on`: a package read as its files say whatever gallery.json says of it,
389
- * for what holds it to them (gallery component check).
607
+ * for what holds it to them (gallery block check). In a server, Gallery's own blocks as they were
608
+ * added are brought up to Gallery's first (beforeCatalogRead).
390
609
  */
391
610
  export function readCatalog(root = workspaceRoot(), held = NO_SETTINGS, { on = null } = {}) {
392
611
  const settings = settingsNow(root, held);
393
612
  const packages = new Map();
394
613
  const problems = new Map();
395
- for (const { id, folder } of packageFolders(root)) {
614
+ const folders = packageFolders(root);
615
+ const own = folders.map(({ id }) => id).filter((id) => ownScopes.includes(id.split("/")[0]));
616
+ if (beforeRead && own.length > 0) {
617
+ try {
618
+ beforeRead(root, own);
619
+ } catch {} // what it could not do leaves the blocks as they are
620
+ }
621
+ for (const { id, folder } of folders) {
396
622
  const found = readPackage(id, folder);
397
623
  (found.problem ? problems : packages).set(found.id, found);
398
624
  }
@@ -402,7 +628,30 @@ export function readCatalog(root = workspaceRoot(), held = NO_SETTINGS, { on = n
402
628
  problems.set(id, turnedOff(packages.get(id)));
403
629
  packages.delete(id);
404
630
  }
405
- return { packages, problems, settings, stamp: catalogStamp({ packages, problems, settings }) };
631
+ const packs = packNames(root, folders.map(({ id }) => id.split("/")[0]));
632
+ return { packages, problems, settings, packs, stamp: catalogStamp({ packages, problems, settings, packs }) };
633
+ }
634
+
635
+ /**
636
+ * What the pack.json of each scope of `scopes` (and of each folder of components/ that holds one) names
637
+ * it: a Map, by scope, of { zh, en }, or null where it has none or one that does not read (a scope of
638
+ * blocks with no pack.json is a pack of its own by its scope's name: ./block-packs.mjs).
639
+ */
640
+ export function packNames(root, scopes = []) {
641
+ const { components } = layoutOf(root);
642
+ let withFile = [];
643
+ try {
644
+ withFile = readdirSync(components).filter((name) => PACK_ID.test(name) && existsSync(path.join(components, name, PACK_FILE)));
645
+ } catch {}
646
+ const names = new Map();
647
+ for (const scope of [...new Set([...scopes, ...withFile])].sort()) {
648
+ let name = null;
649
+ try {
650
+ name = readPackManifest(readFileSync(path.join(components, scope, PACK_FILE), "utf8")).name;
651
+ } catch {}
652
+ names.set(scope, name);
653
+ }
654
+ return names;
406
655
  }
407
656
 
408
657
  const kept = new Map(); // a workspace's root -> the catalog in use
@@ -429,12 +678,18 @@ const bothProblems = (problem) => ({ zh: problemIn("zh", problem), en: problemIn
429
678
  /** Why gallery.json cannot be read, in both languages, as 设置 › 组件列表 says it, or null. */
430
679
  export const settingsProblem = ({ settings }) => (settings.problem ? bothProblems(settings.problem) : null);
431
680
 
432
- // What a package is now: "normal", "hidden", "disabled" (turned off, or for an apiVersion this Gallery
433
- // does not run), or "broken", one that does not load of itself.
434
- const stateOf = (found, settings) => (found.problem ? (found.disabled ? "disabled" : "broken") : settingState(found.id, settings));
681
+ // What a package is now: "normal" (the document's menus offer it: inDocumentMenus), "hidden" (they do
682
+ // not), "disabled" (turned off, or for an apiVersion this Gallery does not run), or "broken", one that
683
+ // does not load of itself.
684
+ const stateOf = (found, settings) => (found.problem ? (found.disabled ? "disabled" : "broken") : settingState(found.id, settings, found.places));
435
685
 
436
- /** What the kit is told of a package: where its instances are, its parts, what it leaves on a page and how far its content's shape goes. */
437
- export const descriptionOf = ({ id, match, parts, runtime, schema }) => ({ id, match, parts, ...(runtime ? { runtime } : {}), ...(schema !== undefined ? { schema } : {}) });
686
+ /**
687
+ * What the kit is told of a package: where its instances are, its parts, what it leaves on a page, how far
688
+ * its content's shape goes, and what it uses (`needs`, where it names any: the kit gives an instance
689
+ * context.workspace for "workspace", context.fetch for a host of the net and context.table() for
690
+ * "tables").
691
+ */
692
+ export const descriptionOf = ({ id, match, parts, runtime, schema, needs }) => ({ id, match, parts, ...(runtime ? { runtime } : {}), ...(schema !== undefined ? { schema } : {}), ...(needs?.length ? { needs } : {}) });
438
693
 
439
694
  /**
440
695
  * What the kit is told of a catalog, each by DocKit.describe, the one way a page learns a package's
@@ -509,7 +764,7 @@ export function instanceMarkup(text, id) {
509
764
  function readTemplate({ id, template, parts }) {
510
765
  if (!template) return {};
511
766
  const read = instanceMarkup(template, id);
512
- if (!read) return { problem: said("template.html 要是这个组件包的一个实例,有自己的结束标签,此外什么都没有") };
767
+ if (!read) return { problem: said("template.html 要是这个自定义块的一个实例,有自己的结束标签,此外什么都没有") };
513
768
  const held = [];
514
769
  walk({ childNodes: [read.root] }, (node) => { if (refusedInCopy(node.tagName, node.attrs)) held.push(node.tagName); });
515
770
  if (held.length > 0) return { problem: said("template.html 里的 <{0}> 进不了新建的块:脚本、事件、样式和 Gallery 自己的标记都不写在模板里", held[0]) };
@@ -520,7 +775,7 @@ function readTemplate({ id, template, parts }) {
520
775
  return short ? { problem: said("template.html 的 {0} 和清单写的数目不符", short[0]) } : { markup: read.markup };
521
776
  }
522
777
 
523
- /** Why no menu offers the template of `found`, said()'s, for `gallery component check` to say, or null: it has none, or one no reason keeps out. */
778
+ /** Why no menu offers the template of `found`, said()'s, for `gallery block check` to say, or null: it has none, or one no reason keeps out. */
524
779
  export const templateProblem = (found) => readTemplate(found).problem ?? null;
525
780
 
526
781
  /** The markup of an instance of `found` as a new one is written, or null: it has no template a menu offers. */
@@ -530,12 +785,15 @@ export const templateOf = (found) => readTemplate(found).markup ?? null;
530
785
  * What the page the editor runs on holds of a catalog: its MAP's `components` (src/editor/service.mjs),
531
786
  * no route and no dynamic import. One entry for each package that loads, by id: { id, version, name:
532
787
  * { zh, en }, description: { zh, en }, words, definition, stamp, template (the markup "/" puts in: one
533
- * instance of it, or null), preview (the address of its picture, or null), state ("normal", or "hidden": a menu offers what is "normal" and reads no setting), markdown, assets (the kit's files it asks for,
788
+ * instance of it, or null), preview (the address of its picture, or null), state ("normal", or "hidden": a menu offers what is "normal" and reads no setting), places (which the page's
789
+ * settingState reads with gallery.json's lists as its event stream says them), needs, markdown, assets (the kit's files it asks for,
534
790
  * by id), tags (packageTags: what loads it in a page that was served without it, the line that tells the
535
- * kit of its parts among them) }. A page knows what it holds of a package's parts from the kit
791
+ * kit of its parts among them), pack ({ id, name }: the pack it is of, its scope, and what its pack.json names
792
+ * it, null where none does: a menu writes the pack's name after a block's in grey where two packs offer
793
+ * blocks of one name) }. A page knows what it holds of a package's parts from the kit
536
794
  * (descriptionsOf), and of one that came after it was served from these tags.
537
795
  */
538
- export const menuOf = ({ packages, settings = NO_SETTINGS }) => [...packages.values()].sort(byId).map((found) => ({
796
+ export const menuOf = ({ packages, settings = NO_SETTINGS, packs = new Map() }) => [...packages.values()].sort(byId).map((found) => ({
539
797
  id: found.id,
540
798
  version: found.version,
541
799
  name: found.name,
@@ -546,28 +804,47 @@ export const menuOf = ({ packages, settings = NO_SETTINGS }) => [...packages.val
546
804
  template: templateOf(found),
547
805
  preview: found.files.includes("preview.svg") ? packageAddress(found, "preview.svg") : null,
548
806
  state: stateOf(found, settings),
807
+ places: found.places,
808
+ needs: found.needs,
549
809
  markdown: found.markdown,
550
810
  assets: found.assets,
551
811
  tags: packageTags(found),
812
+ pack: { id: found.id.split("/")[0], name: packs.get(found.id.split("/")[0]) ?? null },
552
813
  }));
553
814
 
554
815
  /**
555
816
  * The workspace's packages as who lists them says them, by id: { id, version, name, description (what
556
817
  * a manifest says, null where it does not read), state ("normal", "hidden", "disabled" or "broken":
557
818
  * stateOf), reason ({ zh, en }, why it does not load of itself, or null), template (whether it has one
558
- * a menu can put in: with none, none can), hidden and disabled (what gallery.json says of it) }.
819
+ * a menu can put in: with none, none can), places and needs (what its manifest says, null where it does
820
+ * not read), hidden, shown, disabled and unlisted (what gallery.json says of it), allowed (the hosts of its needs
821
+ * the reader allowed it to reach, gallery.json's components.net: its switch 允许联网 is on while it holds
822
+ * every host its needs name) and menu (whether its switch
823
+ * 在文档菜单里显示 is on: inDocumentMenus, whatever `disabled` and `unlisted` say, which are switches of their
824
+ * own: the row's master switch is on while it is not unlisted, nor its pack's) and pack (the pack it is of: its scope) }.
559
825
  */
560
826
  export const packageStates = ({ packages, problems, settings = NO_SETTINGS }) => [...packages.values(), ...problems.values()].sort(byId).map((found) => ({
561
827
  id: found.id,
828
+ pack: found.id.split("/")[0],
562
829
  version: found.version ?? null,
563
830
  name: found.name ?? null,
564
831
  description: found.description ?? null,
565
832
  state: stateOf(found, settings),
566
833
  reason: found.problem && !found.off ? bothProblems(found.problem) : null,
567
834
  template: Boolean(templateOf(found)),
835
+ places: found.places ?? null,
836
+ needs: found.needs ?? null,
568
837
  hidden: settings.hidden.includes(found.id),
838
+ shown: settings.shown.includes(found.id),
569
839
  disabled: settings.disabled.includes(found.id),
840
+ unlisted: (settings.unlisted ?? []).includes(found.id),
841
+ allowed: (settings.net?.[found.id] ?? []).filter((host) => netHostsOf(found.needs).includes(host)),
842
+ menu: inDocumentMenus(found.places, { ...settings, disabled: [], unlisted: [], packsOff: [] }, found.id),
570
843
  }));
571
844
 
572
- /** The message that tells a page of `settings` on its event stream, its two lists by commas: what it offers follows them, whatever it holds. */
573
- export const settingsLine = ({ hidden, disabled }) => `settings ${hidden.join(",")} ${disabled.join(",")}`;
845
+ /**
846
+ * The message that tells a page of `settings` on its event stream, its six lists by commas (hidden,
847
+ * disabled, shown, the consents, "<id>@<host>" each, unlisted, and the packs off): what it offers follows
848
+ * them, whatever it holds, and an instance whose consent changed starts again (the kit's net.js).
849
+ */
850
+ export const settingsLine = (settings) => `settings ${settings.hidden.join(",")} ${settings.disabled.join(",")} ${settings.shown.join(",")} ${consentPairs(settings).join(",")} ${(settings.unlisted ?? []).join(",")} ${(settings.packsOff ?? []).join(",")}`;
@@ -0,0 +1,37 @@
1
+ // What a component that hears `source` hears of attributes (src/render/kit/runtime/kernel.js;
2
+ // docs/render.md "A custom block on a page"): a change of an attribute in a part or a slot that says
3
+ // something of the file, and of a class only where a look class changes, so that what the kit and the
4
+ // editor toggle as they lay the page out is no change of the file's.
5
+
6
+ import { COLOR_NAMES } from "./colors.mjs";
7
+
8
+ /**
9
+ * The attributes that say something of the file in a part or a slot, which a component hearing
10
+ * `source` hears change (runtime/kernel.js): a to-do item checked, a cell's spans, what a table's
11
+ * header and cells say of its sort (the table description's data-sort-*, listed one by one, as an
12
+ * observer takes them). A class is one only where a look class changes (lookChanged); `hidden` and
13
+ * `open` are the reader's, which `reveal` tells.
14
+ */
15
+ export const SOURCE_ATTRIBUTES = ["data-checked", "rowspan", "colspan", "data-sort-status", "data-sort-type", "data-sort-key", "data-sort-default", "data-sort-ignore", "data-sort-value", "data-sort-rank"];
16
+
17
+ /**
18
+ * The classes that are a block's or a cell's look, which a reader sets and the file keeps: a text color
19
+ * or a fill (doc-text-<color>, doc-highlight-<color>), a cell's alignments and its bold (table.mjs's
20
+ * CELL_ALIGN_CLASSES, CELL_VALIGN_CLASSES, CELL_BOLD_CLASS, the editor's CELL_LOOKS). What the kit and
21
+ * the editor toggle as they lay a page out (a table's number and doc-table--auto, a code block's
22
+ * folds) is none, so that no component hears its own layout as a change of the file, and draws again
23
+ * for ever.
24
+ */
25
+ export const LOOK_CLASSES = new Set([
26
+ ...COLOR_NAMES.flatMap((name) => [`doc-text-${name}`, `doc-highlight-${name}`]),
27
+ // table.mjs's CELL_ALIGN_CLASSES, CELL_VALIGN_CLASSES and CELL_BOLD_CLASS, written out: an import of that
28
+ // file would bring its description, prose and all, into the kit's runtime (tests/unit/installed.mjs
29
+ // holds the two lists together).
30
+ "doc-align-left", "doc-align-center", "doc-align-right", "doc-valign-top", "doc-valign-middle", "doc-valign-bottom", "doc-bold",
31
+ ]);
32
+
33
+ /** Whether a class attribute's look classes (LOOK_CLASSES) differ from `before` to `after`, either null for none. */
34
+ export function lookChanged(before, after) {
35
+ const looks = (value) => [...new Set(String(value ?? "").split(/\s+/).filter((name) => LOOK_CLASSES.has(name)))].sort().join(" ");
36
+ return looks(before) !== looks(after);
37
+ }