@heycys/gallery 0.1.0 → 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 (465) hide show
  1. package/THIRD-PARTY-NOTICES.md +51 -1
  2. package/browser/editor/components/callout/index.mjs +40 -15
  3. package/browser/editor/components/columns/page.mjs +45 -32
  4. package/browser/editor/components/details/index.mjs +2 -1
  5. package/browser/editor/components/dialog/index.mjs +2 -1
  6. package/browser/editor/components/dialog/page.mjs +0 -18
  7. package/browser/editor/components/divider/index.mjs +1 -1
  8. package/browser/editor/components/embed/index.mjs +2 -2
  9. package/browser/editor/components/figure/page.mjs +10 -1
  10. package/browser/editor/components/file/page.mjs +2 -2
  11. package/browser/editor/components/flowchart/add.mjs +7 -6
  12. package/browser/editor/components/flowchart/page.mjs +1 -1
  13. package/browser/editor/components/heading/index.mjs +2 -0
  14. package/browser/editor/components/heading/page.mjs +6 -0
  15. package/browser/editor/components/highlight/index.mjs +4 -20
  16. package/browser/editor/components/icons.mjs +13 -2
  17. package/browser/editor/components/image/crop.mjs +6 -9
  18. package/browser/editor/components/image/page.mjs +2 -2
  19. package/browser/editor/components/installed/index.mjs +82 -29
  20. package/browser/editor/components/installed/page.mjs +146 -34
  21. package/browser/editor/components/installed/served.mjs +6 -5
  22. package/browser/editor/components/instances.mjs +18 -0
  23. package/browser/editor/components/kit.mjs +3 -0
  24. package/browser/editor/components/list/page.mjs +3 -6
  25. package/browser/editor/components/media/page.mjs +44 -14
  26. package/browser/editor/components/pages.mjs +2 -2
  27. package/browser/editor/components/registry.mjs +68 -33
  28. package/browser/editor/components/section/index.mjs +2 -1
  29. package/browser/editor/components/set-apart.mjs +24 -0
  30. package/browser/editor/components/table/index.mjs +64 -5
  31. package/browser/editor/components/table/page.mjs +30 -19
  32. package/browser/editor/page/annotate/anchors.mjs +4 -3
  33. package/browser/editor/page/annotate/card.mjs +3 -6
  34. package/browser/editor/page/annotate/index.mjs +61 -34
  35. package/browser/editor/page/autosave.mjs +357 -38
  36. package/browser/editor/page/beside.mjs +5 -5
  37. package/browser/editor/page/block-menu/colors.mjs +3 -1
  38. package/browser/editor/page/block-menu/index.mjs +8 -6
  39. package/browser/editor/page/blocks.mjs +16 -7
  40. package/browser/editor/page/board/client.mjs +57 -0
  41. package/browser/editor/page/board/hint.mjs +145 -0
  42. package/browser/editor/page/board/history.mjs +119 -0
  43. package/browser/editor/page/board/index.mjs +970 -0
  44. package/browser/editor/page/board/settings.mjs +209 -0
  45. package/browser/editor/page/caret.mjs +5 -2
  46. package/browser/editor/page/changes/bar.mjs +94 -0
  47. package/browser/editor/page/changes/ghost.mjs +206 -0
  48. package/browser/editor/page/changes/gone-words.mjs +94 -0
  49. package/browser/editor/page/changes/index.mjs +447 -0
  50. package/browser/editor/page/changes/words.mjs +27 -0
  51. package/browser/editor/page/clipboard/import/colors.mjs +3 -3
  52. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  53. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  54. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  55. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  56. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  57. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  58. package/browser/editor/page/clipboard/index.mjs +156 -26
  59. package/browser/editor/page/clipboard/own/blocks.mjs +25 -16
  60. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  61. package/browser/editor/page/clipboard/own/describe.mjs +16 -7
  62. package/browser/editor/page/clipboard/own/export.mjs +6 -6
  63. package/browser/editor/page/clipboard/own/selection.mjs +3 -3
  64. package/browser/editor/page/clipboard/own/writers.mjs +0 -11
  65. package/browser/editor/page/columns/index.mjs +48 -46
  66. package/browser/editor/page/commit.mjs +98 -25
  67. package/browser/editor/page/conflicts.mjs +13 -33
  68. package/browser/editor/page/convert.mjs +8 -7
  69. package/browser/editor/page/core.mjs +81 -27
  70. package/browser/editor/page/custom-page.mjs +106 -0
  71. package/browser/editor/page/drag/index.mjs +40 -24
  72. package/browser/editor/page/drag/preview.mjs +9 -4
  73. package/browser/editor/page/editor.mjs +120 -28
  74. package/browser/editor/page/emoji-picker/emoji-tab.mjs +22 -13
  75. package/browser/editor/page/emoji-picker/grid.mjs +7 -5
  76. package/browser/editor/page/emoji-picker/icon-tab.mjs +4 -4
  77. package/browser/editor/page/emoji-picker/icons.mjs +0 -1
  78. package/browser/editor/page/emoji-picker/index.mjs +11 -9
  79. package/browser/editor/page/emoji-picker/styles.mjs +156 -2
  80. package/browser/editor/page/emoji-picker/support.mjs +6 -4
  81. package/browser/editor/page/emoji-picker/upload.mjs +14 -13
  82. package/browser/editor/page/fills.mjs +5 -3
  83. package/browser/editor/page/fold/index.mjs +348 -0
  84. package/browser/editor/page/fold/rule.mjs +125 -0
  85. package/browser/editor/page/folded.mjs +23 -0
  86. package/browser/editor/page/format-bar/bar.mjs +2 -1
  87. package/browser/editor/page/format-bar/index.mjs +1 -1
  88. package/browser/editor/page/geometry.mjs +8 -6
  89. package/browser/editor/page/handle/index.mjs +22 -14
  90. package/browser/editor/page/hints/index.mjs +1 -1
  91. package/browser/editor/page/history.mjs +24 -4
  92. package/browser/editor/page/insert/index.mjs +30 -17
  93. package/browser/editor/page/links/index.mjs +5 -3
  94. package/browser/editor/page/links/pages.mjs +27 -9
  95. package/browser/editor/page/long-runs.mjs +75 -0
  96. package/browser/editor/page/look.mjs +69 -6
  97. package/browser/editor/page/margin.mjs +63 -27
  98. package/browser/editor/page/media-menu/index.mjs +6 -7
  99. package/browser/editor/page/menu.mjs +86 -31
  100. package/browser/editor/page/merge.mjs +24 -0
  101. package/browser/editor/page/notices.mjs +3 -3
  102. package/browser/editor/page/offered.mjs +1 -1
  103. package/browser/editor/page/outline/folds.mjs +11 -10
  104. package/browser/editor/page/outline/index.mjs +131 -45
  105. package/browser/editor/page/outline/list.mjs +23 -11
  106. package/browser/editor/page/outline/read.mjs +1 -1
  107. package/browser/editor/page/outline/strip.mjs +7 -1
  108. package/browser/editor/page/page-objects.mjs +13 -2
  109. package/browser/editor/page/panel.mjs +9 -13
  110. package/browser/editor/page/present-links.mjs +11 -2
  111. package/browser/editor/page/resize/index.mjs +42 -29
  112. package/browser/editor/page/selection/delete.mjs +8 -6
  113. package/browser/editor/page/selection/index.mjs +10 -3
  114. package/browser/editor/page/served.mjs +14 -0
  115. package/browser/editor/page/session.mjs +101 -6
  116. package/browser/editor/page/slash-menu/index.mjs +3 -3
  117. package/browser/editor/page/table/cell-bar.mjs +4 -1
  118. package/browser/editor/page/table/cells.mjs +103 -47
  119. package/browser/editor/page/table/icons.mjs +3 -1
  120. package/browser/editor/page/table/index.mjs +40 -16
  121. package/browser/editor/page/table/pictures.mjs +299 -0
  122. package/browser/editor/page/table/views.mjs +150 -0
  123. package/browser/editor/page/table/widths.mjs +9 -4
  124. package/browser/editor/page/text-select/index.mjs +43 -14
  125. package/browser/editor/page/todo-writes.mjs +75 -0
  126. package/browser/editor/page/tooltip.mjs +1 -1
  127. package/browser/editor/page/type-grid.mjs +4 -3
  128. package/browser/editor/page/typing/code-edges.mjs +63 -17
  129. package/browser/editor/page/typing/index.mjs +65 -9
  130. package/browser/editor/page/typing/leave.mjs +1 -1
  131. package/browser/editor/page/ui.mjs +13 -0
  132. package/browser/shared/annotations.mjs +8 -10
  133. package/browser/shared/block-packs.mjs +404 -0
  134. package/browser/shared/board-places.mjs +224 -0
  135. package/browser/shared/card-size.mjs +59 -0
  136. package/browser/shared/code-folds.mjs +5 -1
  137. package/browser/shared/colors.mjs +17 -14
  138. package/browser/shared/component-catalog.mjs +345 -54
  139. package/browser/shared/component-source.mjs +37 -0
  140. package/browser/shared/components/callout.mjs +4 -2
  141. package/browser/shared/components/index.mjs +72 -9
  142. package/browser/shared/components/installed.mjs +110 -8
  143. package/browser/shared/components/page.mjs +22 -3
  144. package/browser/shared/components/state.mjs +5 -1
  145. package/browser/shared/components/table.mjs +1 -1
  146. package/browser/shared/components/text.mjs +2 -2
  147. package/browser/shared/document-look.mjs +183 -0
  148. package/browser/shared/drafts.mjs +432 -0
  149. package/browser/shared/emoji/data/LICENSE-lucide.txt +43 -0
  150. package/browser/shared/emoji/data/README.md +4 -2
  151. package/browser/shared/emoji/data/categories.json +1 -1
  152. package/browser/shared/emoji/data/keywords-en.json +1 -1
  153. package/browser/shared/emoji/data/keywords-zh.json +1 -1
  154. package/browser/shared/emoji/data/names.json +1 -1
  155. package/browser/shared/emoji/data/positions.json +1 -1
  156. package/browser/shared/emoji/data/twemoji-index.json +1 -1
  157. package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
  158. package/browser/shared/emoji/data.mjs +10 -12
  159. package/browser/shared/emoji/emoji.mjs +8 -7
  160. package/browser/shared/emoji/icon-value.mjs +4 -3
  161. package/browser/shared/extension-sidebar.mjs +126 -0
  162. package/browser/shared/extensions.mjs +79 -0
  163. package/browser/shared/file-table.mjs +101 -0
  164. package/browser/shared/html-tree.mjs +11 -5
  165. package/browser/shared/i18n/en.mjs +374 -35
  166. package/browser/shared/json-layout.mjs +47 -0
  167. package/browser/shared/long-runs.mjs +39 -0
  168. package/browser/shared/net-cache.mjs +133 -0
  169. package/browser/shared/net-keys.mjs +32 -0
  170. package/browser/shared/net-policy.mjs +177 -0
  171. package/browser/shared/new-names.mjs +5 -2
  172. package/browser/shared/outline-shape.mjs +128 -0
  173. package/browser/shared/outline.mjs +458 -0
  174. package/browser/shared/pack-manifest.mjs +77 -0
  175. package/browser/shared/page-address.mjs +6 -3
  176. package/browser/shared/page-cards.mjs +308 -0
  177. package/browser/shared/page-links.mjs +69 -6
  178. package/browser/shared/pages.mjs +388 -0
  179. package/browser/shared/paths.mjs +6 -0
  180. package/browser/shared/preferences.mjs +10 -4
  181. package/browser/shared/preview-server.mjs +3 -7
  182. package/browser/shared/session.mjs +13 -5
  183. package/browser/shared/shipped-blocks.mjs +299 -0
  184. package/browser/shared/table-chart.mjs +33 -1
  185. package/browser/shared/todos.mjs +83 -0
  186. package/browser/shared/user-directory.mjs +24 -0
  187. package/browser/shared/versions.mjs +14 -0
  188. package/browser/shared/web-links.mjs +134 -0
  189. package/browser/shared/workspace-columns.mjs +120 -0
  190. package/browser/shared/workspace-look.mjs +91 -0
  191. package/browser/shared/workspace-name.mjs +154 -0
  192. package/browser/shared/workspace-query.mjs +125 -0
  193. package/browser/shared/workspace-registry.mjs +272 -0
  194. package/browser/shared/workspace.mjs +171 -20
  195. package/browser/shared/zoom.mjs +5 -5
  196. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  197. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  198. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  199. package/dist/chunk-3KJHLHUO.mjs +2 -0
  200. package/dist/chunk-3NJACR4W.mjs +2 -0
  201. package/dist/chunk-4AV456RQ.mjs +2 -0
  202. package/dist/chunk-4OZWXZE5.mjs +5 -0
  203. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  204. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  205. package/dist/chunk-6A53MZYZ.mjs +2 -0
  206. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  207. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  208. package/dist/{chunk-UXUK4PMC.mjs → chunk-6QBHU4DF.mjs} +1 -1
  209. package/dist/{chunk-6U2SLSDN.mjs → chunk-6VTU6KEO.mjs} +1 -1
  210. package/dist/chunk-B4P2CM2J.mjs +3 -0
  211. package/dist/chunk-BPL64HGA.mjs +4 -0
  212. package/dist/chunk-BVEFGE4B.mjs +2 -0
  213. package/dist/chunk-BZ5RAF7L.mjs +2 -0
  214. package/dist/chunk-CRJEG7MT.mjs +2 -0
  215. package/dist/{chunk-B3VTYBYR.mjs → chunk-D4CKRD3V.mjs} +91 -91
  216. package/dist/chunk-D4EURAX4.mjs +4 -0
  217. package/dist/chunk-DQCE7TJY.mjs +5 -0
  218. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  219. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  220. package/dist/{chunk-DB4WKGYF.mjs → chunk-ERLAIBOA.mjs} +2 -2
  221. package/dist/chunk-EURLTOTZ.mjs +4 -0
  222. package/dist/chunk-FJYN7RTH.mjs +4 -0
  223. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  224. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  225. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  226. package/dist/chunk-GY3FAUVM.mjs +2 -0
  227. package/dist/chunk-HLYV7RIH.mjs +2 -0
  228. package/dist/chunk-HMYTM3GT.mjs +15 -0
  229. package/dist/chunk-IA4PSIMD.mjs +2 -0
  230. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  231. package/dist/chunk-JVHHHGEA.mjs +3 -0
  232. package/dist/chunk-KTKHRC6H.mjs +2 -0
  233. package/dist/chunk-L7UP52JO.mjs +2 -0
  234. package/dist/chunk-MHW7ZKOU.mjs +2 -0
  235. package/dist/chunk-N76HWBA3.mjs +10 -0
  236. package/dist/chunk-N7AZZLGT.mjs +548 -0
  237. package/dist/chunk-NAMN3RAF.mjs +2 -0
  238. package/dist/{chunk-YXAUQPVK.mjs → chunk-NFMCP2HI.mjs} +3 -3
  239. package/dist/chunk-NG375ILQ.mjs +2 -0
  240. package/dist/chunk-NVRNLK25.mjs +6 -0
  241. package/dist/chunk-NWFBE2YA.mjs +2 -0
  242. package/dist/chunk-NWR2LT5B.mjs +2 -0
  243. package/dist/chunk-OJKMINIU.mjs +20 -0
  244. package/dist/chunk-OUUP3WKT.mjs +2 -0
  245. package/dist/chunk-OW5NWPOJ.mjs +10 -0
  246. package/dist/chunk-PAREWQGB.mjs +4 -0
  247. package/dist/chunk-POQSE4C2.mjs +2 -0
  248. package/dist/chunk-PUOQ65NC.mjs +2 -0
  249. package/dist/{chunk-Z52HTUW7.mjs → chunk-PVER7LSC.mjs} +2 -2
  250. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  251. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  252. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  253. package/dist/chunk-QKXMPUXC.mjs +2 -0
  254. package/dist/chunk-QOUEFUCH.mjs +16 -0
  255. package/dist/chunk-RFDZXFJF.mjs +13 -0
  256. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  257. package/dist/chunk-RMECZA2K.mjs +2 -0
  258. package/dist/{chunk-EUX7IC5J.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  259. package/dist/chunk-SX4LJYT6.mjs +8 -0
  260. package/dist/chunk-SXHBYZST.mjs +16 -0
  261. package/dist/chunk-TANZRINB.mjs +2 -0
  262. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  263. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  264. package/dist/chunk-UFNYFFKO.mjs +20 -0
  265. package/dist/chunk-UGWYKHBL.mjs +170 -0
  266. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  267. package/dist/chunk-UMHUSOPJ.mjs +2 -0
  268. package/dist/chunk-UNOYVIYR.mjs +2 -0
  269. package/dist/chunk-V6RIYQZZ.mjs +4 -0
  270. package/dist/chunk-VJPFVNNW.mjs +15 -0
  271. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  272. package/dist/chunk-VTKNEYT3.mjs +6 -0
  273. package/dist/chunk-VXACTLO3.mjs +3 -0
  274. package/dist/chunk-W3BGBJQW.mjs +2 -0
  275. package/dist/chunk-YI43RG4P.mjs +2 -0
  276. package/dist/chunk-YQGT4AB2.mjs +6 -0
  277. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  278. package/dist/{chunk-ACOPDCNB.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  279. package/dist/chunk-ZTHICSSF.mjs +2 -0
  280. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  281. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  282. package/dist/cli.mjs +1 -1
  283. package/dist/server.mjs +1 -1
  284. package/dist/{chunk-EVJ3BX3M.mjs → update-fetch.mjs} +1 -1
  285. package/dist/worker.mjs +1 -1
  286. package/kit/board.css +95 -0
  287. package/kit/board.js +250 -0
  288. package/kit/charts.js +5 -71
  289. package/kit/code.js +14 -14
  290. package/kit/math.js +18 -2
  291. package/kit/report.css +500 -151
  292. package/kit/runtime.js +1720 -170
  293. package/kit/state.js +10 -1
  294. package/kit/tables.js +172 -37
  295. package/kit/ui.js +166 -27
  296. package/package.json +2 -1
  297. package/packs/gallery/card/component.json +14 -0
  298. package/packs/gallery/card/example.html +10 -0
  299. package/packs/gallery/card/preview.svg +1 -0
  300. package/packs/gallery/card/style.css +6 -0
  301. package/packs/gallery/card/template.html +6 -0
  302. package/packs/gallery/card/writing.md +14 -0
  303. package/packs/gallery/chart-card/component.json +15 -0
  304. package/packs/gallery/chart-card/example.html +14 -0
  305. package/packs/gallery/chart-card/kit.js +130 -0
  306. package/packs/gallery/chart-card/preview.svg +1 -0
  307. package/packs/gallery/chart-card/style.css +42 -0
  308. package/packs/gallery/chart-card/template.html +13 -0
  309. package/packs/gallery/chart-card/writing.md +20 -0
  310. package/packs/gallery/chart-view/component.json +20 -0
  311. package/packs/gallery/chart-view/example.html +13 -0
  312. package/packs/gallery/chart-view/kit.js +355 -0
  313. package/packs/gallery/chart-view/preview.svg +1 -0
  314. package/packs/gallery/chart-view/style.css +94 -0
  315. package/packs/gallery/chart-view/template.html +3 -0
  316. package/packs/gallery/chart-view/writing.md +22 -0
  317. package/packs/gallery/countdown/component.json +18 -0
  318. package/packs/gallery/countdown/example.html +3 -0
  319. package/packs/gallery/countdown/kit.js +156 -0
  320. package/packs/gallery/countdown/preview.svg +1 -0
  321. package/packs/gallery/countdown/style.css +130 -0
  322. package/packs/gallery/countdown/template.html +3 -0
  323. package/packs/gallery/countdown/writing.md +22 -0
  324. package/packs/gallery/habit/component.json +14 -0
  325. package/packs/gallery/habit/example.html +3 -0
  326. package/packs/gallery/habit/kit.js +295 -0
  327. package/packs/gallery/habit/preview.svg +1 -0
  328. package/packs/gallery/habit/style.css +246 -0
  329. package/packs/gallery/habit/template.html +3 -0
  330. package/packs/gallery/habit/writing.md +21 -0
  331. package/packs/gallery/heatmap/component.json +18 -0
  332. package/packs/gallery/heatmap/example.html +3 -0
  333. package/packs/gallery/heatmap/kit.js +351 -0
  334. package/packs/gallery/heatmap/preview.svg +1 -0
  335. package/packs/gallery/heatmap/style.css +201 -0
  336. package/packs/gallery/heatmap/template.html +3 -0
  337. package/packs/gallery/heatmap/writing.md +21 -0
  338. package/packs/gallery/kanban/component.json +18 -0
  339. package/packs/gallery/kanban/example.html +12 -0
  340. package/packs/gallery/kanban/kit.js +540 -0
  341. package/packs/gallery/kanban/preview.svg +1 -0
  342. package/packs/gallery/kanban/style.css +212 -0
  343. package/packs/gallery/kanban/template.html +3 -0
  344. package/packs/gallery/kanban/writing.md +20 -0
  345. package/packs/gallery/metric/component.json +22 -0
  346. package/packs/gallery/metric/example.html +15 -0
  347. package/packs/gallery/metric/kit.js +248 -0
  348. package/packs/gallery/metric/preview.svg +1 -0
  349. package/packs/gallery/metric/style.css +92 -0
  350. package/packs/gallery/metric/template.html +3 -0
  351. package/packs/gallery/metric/writing.md +23 -0
  352. package/packs/gallery/note/component.json +14 -0
  353. package/packs/gallery/note/example.html +3 -0
  354. package/packs/gallery/note/kit.js +102 -0
  355. package/packs/gallery/note/preview.svg +1 -0
  356. package/packs/gallery/note/style.css +76 -0
  357. package/packs/gallery/note/template.html +3 -0
  358. package/packs/gallery/note/writing.md +18 -0
  359. package/packs/gallery/pack.json +8 -0
  360. package/packs/gallery/recent/component.json +13 -0
  361. package/packs/gallery/recent/example.html +1 -0
  362. package/packs/gallery/recent/kit.js +238 -0
  363. package/packs/gallery/recent/preview.svg +1 -0
  364. package/packs/gallery/recent/style.css +158 -0
  365. package/packs/gallery/recent/template.html +1 -0
  366. package/packs/gallery/recent/writing.md +15 -0
  367. package/packs/gallery/static/component.json +13 -0
  368. package/packs/gallery/static/example.html +11 -0
  369. package/packs/gallery/static/preview.svg +1 -0
  370. package/packs/gallery/static/style.css +138 -0
  371. package/packs/gallery/static/template.html +1 -0
  372. package/packs/gallery/static/writing.md +18 -0
  373. package/packs/gallery/stats/component.json +13 -0
  374. package/packs/gallery/stats/example.html +1 -0
  375. package/packs/gallery/stats/kit.js +166 -0
  376. package/packs/gallery/stats/preview.svg +1 -0
  377. package/packs/gallery/stats/style.css +151 -0
  378. package/packs/gallery/stats/template.html +1 -0
  379. package/packs/gallery/stats/writing.md +11 -0
  380. package/packs/gallery/today/component.json +12 -0
  381. package/packs/gallery/today/example.html +1 -0
  382. package/packs/gallery/today/kit.js +185 -0
  383. package/packs/gallery/today/preview.svg +1 -0
  384. package/packs/gallery/today/style.css +120 -0
  385. package/packs/gallery/today/template.html +1 -0
  386. package/packs/gallery/today/writing.md +14 -0
  387. package/packs/gallery/todos/component.json +19 -0
  388. package/packs/gallery/todos/example.html +3 -0
  389. package/packs/gallery/todos/kit.js +342 -0
  390. package/packs/gallery/todos/preview.svg +1 -0
  391. package/packs/gallery/todos/style.css +267 -0
  392. package/packs/gallery/todos/template.html +3 -0
  393. package/packs/gallery/todos/writing.md +30 -0
  394. package/packs/gallery/weather/component.json +19 -0
  395. package/packs/gallery/weather/example.html +3 -0
  396. package/packs/gallery/weather/kit.js +312 -0
  397. package/packs/gallery/weather/preview.svg +1 -0
  398. package/packs/gallery/weather/style.css +258 -0
  399. package/packs/gallery/weather/template.html +3 -0
  400. package/packs/gallery/weather/writing.md +26 -0
  401. package/packs/gallery-soft/pack.json +8 -0
  402. package/release.json +13 -11
  403. package/sdk/doc-kit.d.ts +404 -7
  404. package/sdk/doc-state.d.ts +2 -0
  405. package/skill/SKILL.md +71 -26
  406. package/skill/references/block-script.md +140 -0
  407. package/skill/references/building-blocks.md +257 -0
  408. package/skill/references/example-report.html +1 -1
  409. package/skill/references/extensions.md +59 -0
  410. package/skill/references/pages-and-data.md +221 -0
  411. package/skill/references/writing-html.md +9 -7
  412. package/skill/sdk/doc-kit.d.ts +404 -7
  413. package/skill/sdk/doc-state.d.ts +2 -0
  414. package/web/assets/index-BK2e_07w.js +13 -0
  415. package/web/assets/index-CWFI83mF.css +1 -0
  416. package/web/index.html +2 -2
  417. package/browser/editor/components/callout/page.mjs +0 -13
  418. package/dist/chunk-2PBALTR3.mjs +0 -57
  419. package/dist/chunk-3LA3EMDE.mjs +0 -15
  420. package/dist/chunk-5FRMCDPY.mjs +0 -16
  421. package/dist/chunk-5LEK2CBS.mjs +0 -2
  422. package/dist/chunk-5OFN5AHC.mjs +0 -2
  423. package/dist/chunk-5SIYS5HN.mjs +0 -2
  424. package/dist/chunk-5TEK3RAD.mjs +0 -2
  425. package/dist/chunk-6CYG3Z4V.mjs +0 -2
  426. package/dist/chunk-7GHT2UCA.mjs +0 -15
  427. package/dist/chunk-7QQBR7PB.mjs +0 -2
  428. package/dist/chunk-ATCJYAR4.mjs +0 -2
  429. package/dist/chunk-C7HWOBIF.mjs +0 -2
  430. package/dist/chunk-E7VYDG2X.mjs +0 -2
  431. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  432. package/dist/chunk-FQGQH4KM.mjs +0 -2
  433. package/dist/chunk-G7TOXONE.mjs +0 -6
  434. package/dist/chunk-GPF4YWWB.mjs +0 -9
  435. package/dist/chunk-GYQV7Y4J.mjs +0 -120
  436. package/dist/chunk-HMHK3EQ6.mjs +0 -2
  437. package/dist/chunk-HQQMJCQJ.mjs +0 -10
  438. package/dist/chunk-IERUOGC3.mjs +0 -6
  439. package/dist/chunk-ITNLYXXV.mjs +0 -3
  440. package/dist/chunk-L4DVONOG.mjs +0 -2
  441. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  442. package/dist/chunk-LC7YNQFG.mjs +0 -2
  443. package/dist/chunk-LHTFLRB6.mjs +0 -4
  444. package/dist/chunk-NFIBI4IP.mjs +0 -26
  445. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  446. package/dist/chunk-O52JMOCZ.mjs +0 -2
  447. package/dist/chunk-P2CAZPDP.mjs +0 -3
  448. package/dist/chunk-QMQUXTU4.mjs +0 -12
  449. package/dist/chunk-QVWVFC5K.mjs +0 -6
  450. package/dist/chunk-QYKCJ7FG.mjs +0 -8
  451. package/dist/chunk-QZY6Q64C.mjs +0 -2
  452. package/dist/chunk-TDQP3DKQ.mjs +0 -10
  453. package/dist/chunk-TX6JH7TR.mjs +0 -2
  454. package/dist/chunk-UNUEWH4S.mjs +0 -2
  455. package/dist/chunk-V4CPWKMZ.mjs +0 -2
  456. package/dist/chunk-VACRB2NE.mjs +0 -3
  457. package/dist/chunk-W63L7367.mjs +0 -7
  458. package/dist/chunk-W7CXPCRV.mjs +0 -6
  459. package/dist/chunk-XINNG4F2.mjs +0 -4
  460. package/dist/chunk-YL2DPWVS.mjs +0 -4
  461. package/dist/chunk-YZ5Z6H4W.mjs +0 -4
  462. package/dist/chunk-ZBIM6ARY.mjs +0 -216
  463. package/skill/references/building-components.md +0 -327
  464. package/web/assets/index-B6Ai3wsa.js +0 -13
  465. package/web/assets/index-SYPC3EZy.css +0 -1
@@ -0,0 +1,404 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { componentSettings, problemIn, writeComponentSettings } from "./component-catalog.mjs";
4
+ import { refusal, said } from "./i18n/index.mjs";
5
+ import { PACK_FILE, PACK_ID, readPackManifest } from "./pack-manifest.mjs";
6
+ import { packsDirectory } from "./paths.mjs";
7
+ import { filesIn, fingerprintOf, fingerprintOfFiles, isSystemFile, recordOf, shippedBlocks, shippedPackIds, versionAfter, writeRecord, writingOrder } from "./shipped-blocks.mjs";
8
+ import { DEVICE_NAMES, PACKAGE_MANIFEST, layoutOf, markerOf, packageFolders } from "./workspace.mjs";
9
+
10
+ // Block packs (自定义块包: notes/plans/block-packs-plan-2026-11-09.md; docs/render.md "Block packs"). A pack
11
+ // is a scope of the workspace's custom blocks, components/<id>/, with its pack.json (./pack-manifest.mjs)
12
+ // and a folder of each of its blocks, <id>/<name>/, the blocks' ids <id>/<name> as ever, so that nothing
13
+ // written of a block changes; a scope with no pack.json (local/, the Agent's own) is a pack of its own,
14
+ // named by its scope. A pack is shared as its folder, or a zip of it (the folder itself, or what it
15
+ // holds, at the zip's top), and installed into a workspace by gallery block pack install (src/cli/component.mjs)
16
+ // or 设置 › 组件列表's 导入 (src/server/component-settings.mjs), which both read it (src/transfer/pack-source.mjs)
17
+ // and hand its files here, checked alike:
18
+ //
19
+ // installPack its files copied in, as gallery block add copies one of Gallery's own: each block's
20
+ // manifest last; .gallery/blocks.json records each block's version and fingerprint, and
21
+ // under `packs` the pack's version, the fingerprint of its own files (pack.json, and what
22
+ // else stands at its top) and its blocks. Installed again (a newer version), a block as it
23
+ // was installed is replaced, one changed since kept as it is (someone's work), a block
24
+ // the new version has not taken out where it is as installed; pack.json the same way.
25
+ // Refused, with why (said()'s, a \`code\`): a file that points out of the pack ("path"),
26
+ // one too large ("size"), a pack.json missing or that does not read ("manifest"), a block
27
+ // folder of no component.json naming it <id>/<name> ("block"), the id of one of Gallery's
28
+ // own packs ("official"), an id a scope of the workspace has that no install put there
29
+ // ("taken": another pack, or blocks someone wrote).
30
+ // removePack a pack installed taken out, its folder and records; never one of Gallery's own (its
31
+ // switch turns it off), nor a scope no install put there ("own": someone's work).
32
+ // workspacePacks the workspace's packs as 设置 › 组件列表 and gallery block pack list say them.
33
+ //
34
+ // Gallery's own packs (src/packs/<id>/: gallery, Gallery 风格; gallery-soft, 柔光风格) are installed the
35
+ // same way as a workspace's server starts and by gallery init (installOfficialPacks): once each, a pack
36
+ // switched off at first where OFF_AT_FIRST says so (gallery.json's components.packs), and again at each
37
+ // start with the same rule, so that a block as it was installed follows Gallery, a block Gallery's pack
38
+ // has come to hold is added, and one taken away by hand stays away (its record says it was there).
39
+ // A pack's switch (components.packs, { "<id>": { "enabled": false } }, none written on) keeps all its
40
+ // blocks out of every menu and library, as each one's master switch does (shared/components/installed.mjs
41
+ // inDocumentMenus); what is written of them shows as ever.
42
+
43
+ /** Gallery's own packs that a workspace has switched off when they are first installed. */
44
+ export const OFF_AT_FIRST = Object.freeze(["gallery-soft"]);
45
+
46
+ /** The environment variable that keeps a start (and gallery init) from installing Gallery's own packs: the tests' workspaces. */
47
+ export const NO_PACKS = "GALLERY_NO_PACKS";
48
+
49
+ /** Whether `env` says to install none of Gallery's own packs (NO_PACKS set, not to "", "0" or "false"). */
50
+ export const packsSkipped = (env = process.env) => !["", "0", "false", undefined].includes(env[NO_PACKS]);
51
+
52
+ const MOST_FILES = 2000;
53
+ const MOST_BYTES = 64 * 1024 * 1024;
54
+ const NAME = /^[a-z0-9][a-z0-9-]*$/;
55
+ const isTable = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
56
+ const fail = (code, message) => {
57
+ throw refusal(message, { code });
58
+ };
59
+
60
+ /** The files of `folder`, [{ name (its path in it, with "/"), data }]: a link is none of them. */
61
+ export function folderFiles(folder) {
62
+ const files = [];
63
+ for (const entry of fs.readdirSync(folder, { recursive: true, withFileTypes: true })) {
64
+ if (!entry.isFile()) continue;
65
+ if (files.length >= MOST_FILES) fail("size", said("块包太大:最多 {0} 个文件、{1} MB", MOST_FILES, MOST_BYTES / 1024 / 1024));
66
+ const name = path.relative(folder, path.join(entry.parentPath, entry.name)).split(path.sep).join("/");
67
+ files.push({ name, data: fs.readFileSync(path.join(folder, ...name.split("/"))) });
68
+ }
69
+ return files;
70
+ }
71
+
72
+ /**
73
+ * A pack's files ([{ name, data }], as a folder or a zip gives them) read as one: { manifest, blocks: Map
74
+ * of each block's folder name to its files, own: the files at its top, pack.json among them }. A pack
75
+ * zipped with its folder (one folder at the top, pack.json in it) is read from that folder; what a
76
+ * system wrote (.DS_Store, __MACOSX/…) is passed over. Refused as installPack says; `refuse(manifest)`, run
77
+ * once its pack.json reads, before its blocks are looked at, refuses what it will.
78
+ */
79
+ export function readPack(given, { refuse = () => {} } = {}) {
80
+ const files = [];
81
+ let bytes = 0;
82
+ for (const { name: written, data } of given) {
83
+ const name = String(written).replace(/\\/g, "/");
84
+ if (name.endsWith("/")) continue; // a folder of a zip
85
+ const parts = name.split("/");
86
+ if (name.startsWith("/") || /^[A-Za-z]:/.test(name) || name.includes("\u0000") || parts.some((part) => part === "" || part === "." || part === "..")) fail("path", said("{0} 指向块包外面,不能装", String(written)));
87
+ if (parts[0] === "__MACOSX" || isSystemFile(parts.at(-1))) continue;
88
+ bytes += data.length;
89
+ files.push({ name, data });
90
+ }
91
+ if (files.length > MOST_FILES || bytes > MOST_BYTES) fail("size", said("块包太大:最多 {0} 个文件、{1} MB", MOST_FILES, MOST_BYTES / 1024 / 1024));
92
+ let inner = files;
93
+ if (!files.some(({ name }) => name === PACK_FILE)) {
94
+ const tops = new Set(files.map(({ name }) => name.split("/")[0]));
95
+ const [top] = tops;
96
+ if (tops.size !== 1 || !files.some(({ name }) => name === `${top}/${PACK_FILE}`)) fail("manifest", said("最上一层没有 pack.json:块包是一个文件夹,最上一层放 pack.json,每个块一个文件夹"));
97
+ inner = files.map(({ name, data }) => ({ name: name.slice(top.length + 1), data }));
98
+ }
99
+ const manifest = readPackManifest(inner.find(({ name }) => name === PACK_FILE).data.toString("utf8"));
100
+ refuse(manifest);
101
+ const blocks = new Map();
102
+ const own = [];
103
+ for (const file of inner) {
104
+ const [first, ...rest] = file.name.split("/");
105
+ if (rest.length === 0) {
106
+ own.push(file);
107
+ continue;
108
+ }
109
+ if (!blocks.has(first)) blocks.set(first, []);
110
+ blocks.get(first).push({ name: rest.join("/"), data: file.data });
111
+ }
112
+ for (const [name, list] of blocks) {
113
+ if (!NAME.test(name) || DEVICE_NAMES.has(name)) fail("block", said("{0} 不是块的文件夹名:只用小写字母、数字和连字符", name));
114
+ const file = list.find((each) => each.name === PACKAGE_MANIFEST);
115
+ if (!file) fail("block", said("{0}/ 里没有 component.json", name));
116
+ let id;
117
+ try {
118
+ id = JSON.parse(file.data.toString("utf8").replace(/^/, "")).id;
119
+ } catch {
120
+ fail("block", said("{0}/component.json 不是 JSON", name));
121
+ }
122
+ if (id !== `${manifest.id}/${name}`) fail("block", said("{0}/component.json 的 id 要写成 {1}", name, `${manifest.id}/${name}`));
123
+ }
124
+ return { manifest, blocks, own };
125
+ }
126
+
127
+ // A block's version, as its manifest says it, or null.
128
+ function versionIn(list) {
129
+ try {
130
+ const version = JSON.parse(list.find(({ name }) => name === PACKAGE_MANIFEST).data.toString("utf8").replace(/^/, "")).version;
131
+ return typeof version === "string" ? version : null;
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
137
+ // The files right at the top of a pack's folder (pack.json and what else stands beside the blocks).
138
+ function topFiles(folder) {
139
+ try {
140
+ return fs.readdirSync(folder, { withFileTypes: true }).filter((entry) => entry.isFile() && !isSystemFile(entry.name)).map((entry) => entry.name).sort();
141
+ } catch {
142
+ return [];
143
+ }
144
+ }
145
+
146
+ // What the top of a pack's folder holds, as a fingerprint, or null for nothing there.
147
+ function ownFingerprint(folder) {
148
+ const names = topFiles(folder);
149
+ return names.length ? fingerprintOfFiles(names.map((name) => ({ name, data: fs.readFileSync(path.join(folder, name)) }))) : null;
150
+ }
151
+
152
+ // Whether a folder holds anything a system did not write there.
153
+ function holds(folder) {
154
+ try {
155
+ return fs.readdirSync(folder).some((name) => !isSystemFile(name));
156
+ } catch {
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // `files` written into `folder`, the files of what was there that they have not taken out first
162
+ // (`replacing`), the manifest last: what makes a folder a block to a running server's watcher.
163
+ function writeFiles(folder, files, { replacing = false, last = PACKAGE_MANIFEST } = {}) {
164
+ const names = files.map(({ name }) => name);
165
+ if (replacing) for (const old of filesIn(folder)) if (!names.includes(old)) fs.rmSync(path.join(folder, ...old.split("/")), { force: true });
166
+ const byName = new Map(files.map((file) => [file.name, file.data]));
167
+ const order = last === PACKAGE_MANIFEST ? writingOrder(names) : [...names.filter((name) => name !== last), ...(names.includes(last) ? [last] : [])];
168
+ for (const name of order) {
169
+ const target = path.join(folder, ...name.split("/"));
170
+ fs.mkdirSync(path.dirname(target), { recursive: true });
171
+ fs.writeFileSync(target, byName.get(name));
172
+ }
173
+ }
174
+
175
+ /**
176
+ * The pack `given` holds ([{ name, data }]: src/transfer/pack-source.mjs reads a folder or a zip into
177
+ * them) installed into the workspace at `root` (above): { id, name, version, from (the version installed
178
+ * before, or null), did ("installed", "updated" or "same"), folder, added, updated, same, kept (blocks
179
+ * changed since they were installed, left as they are), removed (blocks the new version has not), skipped
180
+ * (blocks of Gallery's own taken away by hand, which stay away), ownKept (its pack.json changed since,
181
+ * left) }, each list of block ids. `official`: one of Gallery's own packs (installOfficialPacks), whose
182
+ * id is its own and whose scope may hold its blocks already, as gallery block add put them there.
183
+ */
184
+ export function installPack(root, given, { official = false, shippedIds = shippedPackIds() } = {}) {
185
+ const { manifest, blocks, own } = readPack(given, {
186
+ refuse: ({ id }) => {
187
+ if (!official && shippedIds.includes(id)) fail("official", said("{0} 是 Gallery 自带的块包用的 id:换一个 id 再装", id));
188
+ },
189
+ });
190
+ const { id } = manifest;
191
+ const folder = path.join(layoutOf(root).components, id);
192
+ const record = recordOf(root);
193
+ const packs = isTable(record.packs) ? record.packs : {};
194
+ const was = isTable(packs[id]) ? packs[id] : null;
195
+ if (!official && !was && holds(folder)) fail("taken", said("工作区里已经有 components/{0}:这个名字被别的块包或别人写的块用着,换一个 id,或先把那个文件夹挪走", id));
196
+ const done = { id, name: manifest.name, version: manifest.version, from: was?.version ?? null, folder, added: [], updated: [], same: [], kept: [], removed: [], skipped: [], ownKept: false };
197
+ const names = [...blocks.keys()].sort();
198
+ for (const name of names) {
199
+ const block = `${id}/${name}`;
200
+ const list = blocks.get(name);
201
+ const target = path.join(folder, name);
202
+ const next = fingerprintOfFiles(list);
203
+ const now = fingerprintOf(target);
204
+ const kept = isTable(record[block]) ? record[block] : null;
205
+ if (now === null) {
206
+ if (official && kept) {
207
+ done.skipped.push(block);
208
+ continue;
209
+ }
210
+ writeFiles(target, list);
211
+ done.added.push(block);
212
+ } else if (now === next) done.same.push(block);
213
+ else if (kept && now === kept.fingerprint) {
214
+ writeFiles(target, list, { replacing: true });
215
+ done.updated.push(block);
216
+ } else {
217
+ done.kept.push(block);
218
+ continue;
219
+ }
220
+ record[block] = { version: versionIn(list), fingerprint: next };
221
+ }
222
+ // The blocks of the pack as it was installed that this version has not: out, where they are as installed.
223
+ for (const block of Array.isArray(was?.blocks) ? was.blocks : []) {
224
+ const name = String(block).split("/")[1];
225
+ if (!name || blocks.has(name) || !NAME.test(name)) continue;
226
+ const target = path.join(folder, name);
227
+ const now = fingerprintOf(target);
228
+ if (now !== null && now !== record[block]?.fingerprint) {
229
+ done.kept.push(block);
230
+ continue;
231
+ }
232
+ if (now !== null) {
233
+ fs.rmSync(target, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
234
+ done.removed.push(block);
235
+ }
236
+ delete record[block];
237
+ }
238
+ // Its own files, pack.json last, the same way.
239
+ const ownNow = ownFingerprint(folder);
240
+ const ownNext = fingerprintOfFiles(own);
241
+ if (ownNow !== ownNext) {
242
+ if (ownNow === null || ownNow === was?.fingerprint) {
243
+ const names = own.map(({ name }) => name);
244
+ for (const old of topFiles(folder)) if (!names.includes(old)) fs.rmSync(path.join(folder, old), { force: true });
245
+ fs.mkdirSync(folder, { recursive: true });
246
+ writeFiles(folder, own, { last: PACK_FILE });
247
+ } else done.ownKept = true;
248
+ }
249
+ const changed = done.added.length + done.updated.length + done.removed.length > 0 || (ownNow !== ownNext && !done.ownKept);
250
+ done.did = !was ? "installed" : changed ? "updated" : "same";
251
+ record.packs = { ...packs, [id]: { version: manifest.version, fingerprint: done.ownKept ? was.fingerprint : ownNext, blocks: names.map((name) => `${id}/${name}`), ...(official ? { official: true } : {}) } };
252
+ writeRecord(root, record);
253
+ return done;
254
+ }
255
+
256
+ /**
257
+ * The pack `id` taken out of the workspace at `root`: its folder and what .gallery/blocks.json records of
258
+ * it ({ id, version, blocks }). What documents and pages hold of its blocks stays, shown as blocks that
259
+ * are not there. Refused: one of Gallery's own ("official": its switch in 设置 › 组件列表 turns it off), none
260
+ * there ("missing"), a scope no install put there ("own": blocks someone wrote, theirs to delete).
261
+ */
262
+ export function removePack(root, id, { shippedIds = shippedPackIds() } = {}) {
263
+ if (shippedIds.includes(id)) fail("official", said("{0} 是 Gallery 自带的块包,不能删除:在 设置 › 组件列表 里把它关掉", id));
264
+ const folder = path.join(layoutOf(root).components, id);
265
+ if (!PACK_ID.test(id) || !fs.existsSync(folder)) fail("missing", said("工作区里没有块包 {0}", id));
266
+ const record = recordOf(root);
267
+ const was = isTable(record.packs) && isTable(record.packs[id]) ? record.packs[id] : null;
268
+ if (!was) fail("own", said("components/{0} 不是装进来的块包,里面的块是有人在这个工作区写的:要删就自己删掉这个文件夹", id));
269
+ fs.rmSync(folder, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
270
+ for (const key of Object.keys(record)) if (key.startsWith(`${id}/`)) delete record[key];
271
+ const { [id]: _gone, ...rest } = record.packs;
272
+ writeRecord(root, { ...record, packs: rest });
273
+ return { id, version: was.version ?? null, blocks: Array.isArray(was.blocks) ? was.blocks : [] };
274
+ }
275
+
276
+ // Why, in both languages.
277
+ const both = (problem) => ({ zh: problemIn("zh", problem), en: problemIn("en", problem) });
278
+
279
+ /**
280
+ * The workspace's packs, as 设置 › 组件列表's cards and gallery block pack list say them: each scope of
281
+ * components/ that holds a block or a pack.json, [{ id, name: { zh, en } (its scope's for one with no
282
+ * pack.json), description, version, author, homepage, icon (null where it says none), kind ("official":
283
+ * one of Gallery's own; "installed": put there by an install; "own": a scope of blocks someone wrote
284
+ * here), problem ({ zh, en }: why its pack.json does not read, or null), blocks (how many), enabled (its
285
+ * switch, gallery.json's components.packs), changed (of what was installed, the blocks changed since, and
286
+ * pack.json where it was), newer (of one of Gallery's own that was changed since, Gallery's version of it
287
+ * when that comes after the one installed, which is not put over it; else null) }]: Gallery's own first,
288
+ * then those installed, then the workspace's own, each by id.
289
+ */
290
+ export function workspacePacks(root, { settings = null, shippedIds = shippedPackIds(), directory = packsDirectory } = {}) {
291
+ const { components } = layoutOf(root);
292
+ const record = recordOf(root);
293
+ const installed = isTable(record.packs) ? record.packs : {};
294
+ const blocks = packageFolders(root);
295
+ const scopes = new Set(blocks.map(({ id }) => id.split("/")[0]));
296
+ try {
297
+ for (const name of fs.readdirSync(components)) if (PACK_ID.test(name) && fs.existsSync(path.join(components, name, PACK_FILE))) scopes.add(name);
298
+ } catch {}
299
+ const switches = (settings ?? componentSettings(root)).packs ?? {};
300
+ const listed = [...scopes].map((id) => {
301
+ const folder = path.join(components, id);
302
+ let manifest = null;
303
+ let problem = null;
304
+ if (fs.existsSync(path.join(folder, PACK_FILE))) {
305
+ try {
306
+ manifest = readPackManifest(fs.readFileSync(path.join(folder, PACK_FILE), "utf8"));
307
+ if (manifest.id !== id) {
308
+ problem = said("pack.json 的 id 是 {0},它的文件夹却是 components/{1}", manifest.id, id);
309
+ manifest = null;
310
+ }
311
+ } catch (error) {
312
+ problem = error;
313
+ }
314
+ }
315
+ const was = isTable(installed[id]) ? installed[id] : null;
316
+ const ids = blocks.filter((block) => block.id.startsWith(`${id}/`));
317
+ const changed = was ? ids.filter((block) => isTable(record[block.id]) && fingerprintOf(block.folder) !== record[block.id].fingerprint).map((block) => block.id) : [];
318
+ if (was && ownFingerprint(folder) !== was.fingerprint) changed.push(`${id}/${PACK_FILE}`);
319
+ const official = shippedIds.includes(id);
320
+ // One of Gallery's own: what Gallery's pack.json says of it, where the workspace's has none (its blocks
321
+ // put there one by one, gallery block add's, before a start installed the pack).
322
+ let shipped = null;
323
+ if (official) {
324
+ try {
325
+ shipped = readPackManifest(fs.readFileSync(path.join(directory, id, PACK_FILE), "utf8"));
326
+ } catch {}
327
+ }
328
+ // Gallery's newer version, not put over a change: a changed block of it whose version Gallery's comes after, or a changed pack.json.
329
+ const versionAt = (block) => {
330
+ try {
331
+ return JSON.parse(fs.readFileSync(path.join(components, ...block.split("/"), PACKAGE_MANIFEST), "utf8")).version ?? null;
332
+ } catch {
333
+ return null;
334
+ }
335
+ };
336
+ const ownBlocks = official ? shippedBlocks(directory) : [];
337
+ const behind = official && shipped && changed.some((block) => (block.endsWith(`/${PACK_FILE}`) ? versionAfter(shipped.version, manifest?.version ?? null) : (ownBlocks.some((one) => one.id === block) && versionAfter(ownBlocks.find((one) => one.id === block).version, versionAt(block)))));
338
+ const newer = behind ? shipped.version : null;
339
+ const told = manifest ?? (problem ? null : shipped);
340
+ return {
341
+ id,
342
+ name: told?.name ?? { zh: id, en: id },
343
+ description: told?.description ?? null,
344
+ version: manifest?.version ?? null,
345
+ author: told?.author ?? null,
346
+ homepage: told?.homepage ?? null,
347
+ icon: told?.icon ?? null,
348
+ kind: official ? "official" : was ? "installed" : "own",
349
+ problem: problem ? both(problem) : null,
350
+ blocks: ids.length,
351
+ enabled: switches[id] !== false,
352
+ changed,
353
+ newer,
354
+ };
355
+ });
356
+ const rank = ({ id, kind }) => (kind === "official" ? shippedIds.indexOf(id) : kind === "installed" ? 1000 : 2000);
357
+ return listed.sort((a, b) => rank(a) - rank(b) || (a.id < b.id ? -1 : 1));
358
+ }
359
+
360
+ // What installOfficialPacks tells the log of one pack, a line each.
361
+ function installedLines(root, made, first) {
362
+ const lines = [];
363
+ const where = path.resolve(root);
364
+ if (first) lines.push(`Gallery's pack ${made.id} ${made.version} installed in ${where} (${made.added.length + made.same.length + made.updated.length} blocks)${OFF_AT_FIRST.includes(made.id) ? ", switched off: 设置 › 组件列表 turns it on" : ""}`);
365
+ else {
366
+ if (made.added.length) lines.push(`Gallery's pack ${made.id} in ${where} has new blocks: ${made.added.join(", ")}`);
367
+ if (made.updated.length) lines.push(`Gallery's pack ${made.id} in ${where}, its blocks as they were installed, updated to this Gallery's: ${made.updated.join(", ")}`);
368
+ if (made.removed.length) lines.push(`Gallery's pack ${made.id} in ${where}: ${made.removed.join(", ")} no longer in it, taken out`);
369
+ }
370
+ return lines;
371
+ }
372
+
373
+ /**
374
+ * Gallery's own packs (in `directory`, the package's) installed into the workspace at `root`, as
375
+ * installPack installs any, whenever its server starts and by gallery init: one not installed yet is
376
+ * (one of OFF_AT_FIRST switched off first in gallery.json, where it says nothing of it: a gallery.json that
377
+ * cannot be written leaves that pack for the next start), one installed is brought up to Gallery's.
378
+ * `env`: whose NO_PACKS is read; `log`: told what was done, a line each. Never rejects: { did: "off"
379
+ * (NO_PACKS), "unread" (gallery.json does not read as Gallery's) or "done", packs: [installPack's answer
380
+ * with `first`, or { id, failed }] }.
381
+ */
382
+ export async function installOfficialPacks(root, { directory = packsDirectory, env = process.env, log = () => {} } = {}) {
383
+ if (packsSkipped(env)) return { did: "off", packs: [] };
384
+ let marker = { ours: false };
385
+ try {
386
+ marker = markerOf(fs.readFileSync(layoutOf(root).marker, "utf8"));
387
+ } catch {}
388
+ if (!marker.ours) return { did: "unread", packs: [] };
389
+ const ids = shippedPackIds(directory);
390
+ const packs = [];
391
+ for (const id of ids) {
392
+ try {
393
+ const first = !isTable(recordOf(root).packs?.[id]);
394
+ if (first && OFF_AT_FIRST.includes(id) && componentSettings(root).packs[id] === undefined) await writeComponentSettings(root, { pack: id, enabled: false });
395
+ const made = installPack(root, folderFiles(path.join(directory, id)), { official: true, shippedIds: ids });
396
+ packs.push({ ...made, first });
397
+ for (const line of installedLines(root, made, first)) log(line);
398
+ } catch (error) {
399
+ log(`Gallery's pack ${id} could not be installed in ${path.resolve(root)}: ${error?.message ?? error}`);
400
+ packs.push({ id, failed: error?.message ?? String(error) });
401
+ }
402
+ }
403
+ return { did: "done", packs };
404
+ }
@@ -0,0 +1,224 @@
1
+ // Where the cards of a custom page's board go (notes/plans/custom-blocks-spec-2026-11-07.md §6, §8;
2
+ // docs/render.md "A custom page"): the one set of rules every side places a card by, so that what the
3
+ // page shows while a card is dragged, what the page service writes and what gallery page add writes
4
+ // are the same. The page service (src/editor/page-service.mjs) and gallery page add place and push
5
+ // cards in the file by them; the kit's board script (src/render/kit/board/) draws a board by them,
6
+ // cards of `fit: "content"` as high as what they hold, those under them pushed down; the page's board
7
+ // editing (src/editor/page/board/) shows a drag's spot and the cards it pushes by them before it asks
8
+ // the service. Pure: no DOM, no Node.
9
+ //
10
+ // A card is { id, x, y, w, h } in whole cells of a board 12 columns wide, at most 24 cells high: what the
11
+ // file says, wherever it is drawn. A cell's side is fixed (CELL), as a document's reading width is, so a
12
+ // board is as wide whatever the window (the workbench's sidebar open or folded); where the room holds
13
+ // fewer than twelve of them the board is drawn in as many as it holds (gridFor), 4 at the least, its
14
+ // cards one after another in their order (flow), as wide as they are, which nothing writes: nothing is
15
+ // placed there. Nothing here ever moves a card up: a card pushed down stays where it was pushed, the room
16
+ // it left a room on the board, as on a board things are pinned to.
17
+
18
+ /** The cells of a board's row, and the most a card is high (the manifest's own range, spec §1). */
19
+ export const GRID = Object.freeze({ columns: 12, rows: 24 });
20
+
21
+ /** The pixels between two cells. */
22
+ export const GAP = 16;
23
+
24
+ /**
25
+ * A cell's side in pixels, the same however wide the window (the user's word of 2026-11-09: the cards no
26
+ * larger with the sidebar folded, only further in): twelve of them and their gaps are 944px, which the
27
+ * workbench's content holds on a 1280px laptop with the sidebar open at its default width.
28
+ */
29
+ export const CELL = 64;
30
+
31
+ /** The fewest columns a board is drawn in: where not even these fit their cells are drawn smaller (a phone). */
32
+ export const LEAST = 4;
33
+
34
+ /** The pixels `n` cells take on a board whose cell is `cell` pixels (n cells and the n - 1 gaps between them). */
35
+ export const spanOf = (n, cell, gap = GAP) => n * cell + Math.max(0, n - 1) * gap;
36
+
37
+ /**
38
+ * A board drawn in `room` pixels: { columns, cell, width }, as many whole cells of CELL, GAP apart, as
39
+ * the room holds, from LEAST to 12; where not even LEAST fit, LEAST cells as large as the room lets
40
+ * them be. `width` is what its columns take (the board stands them in its middle).
41
+ */
42
+ export function gridFor(room) {
43
+ const fit = Math.floor((Math.max(0, room) + GAP) / (CELL + GAP));
44
+ const columns = Math.max(LEAST, Math.min(GRID.columns, fit));
45
+ const cell = fit >= LEAST ? CELL : Math.max(0, (room - (LEAST - 1) * GAP) / LEAST);
46
+ return { columns, cell, width: spanOf(columns, cell) };
47
+ }
48
+
49
+ const whole = (value, fallback) => (Number.isInteger(value) ? value : fallback);
50
+
51
+ /** Whether cards `a` and `b` share a cell. */
52
+ export const overlaps = (a, b) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
53
+
54
+ /**
55
+ * Cells `{ x, y, w, h }` held to a board of `columns`: 1 <= w <= columns, 1 <= h <= 24, 0 <= x <=
56
+ * columns - w, 0 <= y; what is no whole number the default, a 4 x 2 card at 0, 0.
57
+ */
58
+ export function onBoard({ x, y, w, h }, columns = GRID.columns) {
59
+ const width = Math.min(columns, Math.max(1, whole(w, 4)));
60
+ const height = Math.min(GRID.rows, Math.max(1, whole(h, 2)));
61
+ return { x: Math.min(columns - width, Math.max(0, whole(x, 0))), y: Math.max(0, whole(y, 0)), w: width, h: height };
62
+ }
63
+
64
+ /**
65
+ * The cards of a board with none sharing a cell, none moved up: `fixed` (ids) stay where they are,
66
+ * and every other card, in the order of its place (top to bottom, left to right, then as given), goes
67
+ * down until it shares no cell with one placed before it, right under the one it met. What the file
68
+ * says of a board that shares no cell is what this gives back. Returns new cards, in the order given.
69
+ * @template {{ id: string, x: number, y: number, w: number, h: number }} Card
70
+ * @param {Card[]} cards
71
+ * @param {Iterable<string>} fixed
72
+ * @returns {Card[]}
73
+ */
74
+ export function settle(cards, fixed = []) {
75
+ const kept = new Set(fixed);
76
+ const placed = cards.filter((card) => kept.has(card.id)).map((card) => ({ ...card }));
77
+ const rest = cards.map((card, index) => ({ card, index })).filter(({ card }) => !kept.has(card.id))
78
+ .sort((a, b) => a.card.y - b.card.y || a.card.x - b.card.x || a.index - b.index);
79
+ for (const { card } of rest) {
80
+ const going = { ...card };
81
+ for (let met = placed.filter((other) => overlaps(going, other)); met.length > 0; met = placed.filter((other) => overlaps(going, other))) {
82
+ going.y = Math.max(...met.map((other) => other.y + other.h));
83
+ }
84
+ placed.push(going);
85
+ }
86
+ const at = new Map(placed.map((card) => [card.id, card]));
87
+ return cards.map((card) => at.get(card.id));
88
+ }
89
+
90
+ /**
91
+ * The cards with card `moved.id` at `moved`'s cells (held to the board), and those it now shares a cell
92
+ * with pushed down under it, and so on (settle): what a drop, a resize and a card put on a board at a
93
+ * place of its own do. A card of no id there joins them (a card added).
94
+ */
95
+ export function pushDown(cards, moved, columns = GRID.columns) {
96
+ const cells = onBoard(moved, columns);
97
+ const next = cards.some((card) => card.id === moved.id)
98
+ ? cards.map((card) => (card.id === moved.id ? { ...card, ...cells } : card))
99
+ : [...cards, { ...moved, ...cells }];
100
+ return settle(next, [moved.id]);
101
+ }
102
+
103
+ /**
104
+ * The first place a card of `w` x `h` cells takes on a board holding `cards`, reading the board as a
105
+ * page is read: the topmost row it fits in, and there the leftmost column, sharing a cell with none.
106
+ * @returns {{ x: number, y: number }}
107
+ */
108
+ export function freeSpot(cards, { w, h }, columns = GRID.columns) {
109
+ const { w: width, h: height } = onBoard({ x: 0, y: 0, w, h }, columns);
110
+ const bottom = cards.reduce((most, card) => Math.max(most, card.y + card.h), 0);
111
+ for (let y = 0; y <= bottom; y++) {
112
+ for (let x = 0; x + width <= columns; x++) {
113
+ if (!cards.some((card) => overlaps({ x, y, w: width, h: height }, card))) return { x, y };
114
+ }
115
+ }
116
+ return { x: 0, y: bottom };
117
+ }
118
+
119
+ /**
120
+ * The size a card of a block takes when it is put on a board (the manifest's `card`, as the catalog
121
+ * reads it: component-catalog.mjs cardOf): the first of its `sizes`, else its `w` and `h`; `h` null
122
+ * where the manifest says none, the fewest whole cells its content takes, which only the page measures
123
+ * (`measured`: 2 until then).
124
+ * @param {{ sizes?: number[][] | null, w?: number, h?: number | null } | null | undefined} card
125
+ * @returns {{ w: number, h: number, measured: boolean }}
126
+ */
127
+ export function defaultSize(card) {
128
+ if (card?.sizes?.length) return { w: card.sizes[0][0], h: card.sizes[0][1], measured: false };
129
+ return { w: card?.w ?? 4, h: card?.h ?? 2, measured: card?.h === null || card?.h === undefined };
130
+ }
131
+
132
+ /** Of `sizes` ([w, h] each), the nearest to `w` x `h` (by the distance of their corners; the first of two as near). */
133
+ export function nearestSize(sizes, { w, h }) {
134
+ let best = sizes[0];
135
+ let far = Infinity;
136
+ for (const size of sizes) {
137
+ const distance = (size[0] - w) ** 2 + (size[1] - h) ** 2;
138
+ if (distance < far) {
139
+ far = distance;
140
+ best = size;
141
+ }
142
+ }
143
+ return { w: best[0], h: best[1] };
144
+ }
145
+
146
+ /**
147
+ * What a card of a block may be resized to, for `w` x `h` asked: the nearest of the manifest's `sizes`
148
+ * where it lists them (unless `any`: a manifest that wrote a min or a max beside them frees the card,
149
+ * its sizes only the common ones its menu lists), else whole cells between its `min` and `max`
150
+ * (1 x 1 and 12 x 24 where it says none), never past the board.
151
+ */
152
+ export function sizeFor(card, { w, h }, columns = GRID.columns) {
153
+ if (card?.sizes?.length && !card.any) {
154
+ const near = nearestSize(card.sizes, { w, h });
155
+ return { w: Math.min(columns, near.w), h: near.h };
156
+ }
157
+ const min = { w: card?.min?.w ?? 1, h: card?.min?.h ?? 1 };
158
+ const max = { w: Math.min(columns, card?.max?.w ?? GRID.columns), h: Math.min(GRID.rows, card?.max?.h ?? GRID.rows) };
159
+ return { w: Math.min(max.w, Math.max(min.w, Math.round(w))), h: Math.min(max.h, Math.max(min.h, Math.round(h))) };
160
+ }
161
+
162
+ /**
163
+ * The cells high a card of `fit: "content"` is drawn, for its content `height` in pixels (what it
164
+ * needs, its frame and padding with it) on a board whose cell is `cell` pixels: the spec's
165
+ * ceil((height + 16) / (cell + 16)), 1 at least and 24 at most.
166
+ */
167
+ export function fitHeight(height, cell, gap = GAP) {
168
+ if (!(cell > 0)) return 1;
169
+ return Math.min(GRID.rows, Math.max(1, Math.ceil((height + gap) / (cell + gap) - 1e-6)));
170
+ }
171
+
172
+ /**
173
+ * The cards of a board drawn in `columns` fewer than twelve (a narrow room: gridFor): one after another
174
+ * in the order of their places (top to bottom, left to right, then as given), each as wide as it is but
175
+ * never wider than the row (`w` held to `columns`) and as high, each at the first spot at or after where
176
+ * the one before it ends, row by row, that shares no cell with those before it (as a grid flows its items
177
+ * with no place of their own: no card goes before one it came after, so a room may be left beside a tall
178
+ * one). What is drawn only, never written: the file's twelve-column places stay as they are, and the
179
+ * board is drawn by them again once the room holds twelve. Returns new cards, in the order given.
180
+ * @template {{ id: string, x: number, y: number, w: number, h: number }} Card
181
+ * @param {Card[]} cards
182
+ * @param {number} columns
183
+ * @returns {Card[]}
184
+ */
185
+ export function flow(cards, columns) {
186
+ const order = cards.map((card, index) => ({ card, index })).sort((a, b) => a.card.y - b.card.y || a.card.x - b.card.x || a.index - b.index);
187
+ const placed = [];
188
+ const at = new Map();
189
+ let row = 0;
190
+ let column = 0;
191
+ for (const { card, index } of order) {
192
+ const w = Math.max(1, Math.min(card.w, columns));
193
+ let x = column;
194
+ let y = row;
195
+ for (;;) {
196
+ if (x + w > columns) {
197
+ x = 0;
198
+ y += 1;
199
+ continue;
200
+ }
201
+ const spot = { x, y, w, h: card.h };
202
+ if (!placed.some((other) => overlaps(spot, other))) break;
203
+ x += 1;
204
+ }
205
+ const going = { ...card, x, y, w };
206
+ placed.push(going);
207
+ at.set(index, going);
208
+ row = y;
209
+ column = x + w;
210
+ }
211
+ return cards.map((_, index) => at.get(index));
212
+ }
213
+
214
+ /** The whole cells `pixels` are nearest to, from a board's edge: what a drag snaps to. */
215
+ export const cellAt = (pixels, cell, gap = GAP) => (cell > 0 ? Math.round(pixels / (cell + gap)) : 0);
216
+
217
+ /** The cards whose cells changed from `before` to `after` (lists of cards), by id: [{ id, x, y, w, h }] as `after` has them. */
218
+ export function moved(before, after) {
219
+ const was = new Map(before.map((card) => [card.id, card]));
220
+ return after.filter((card) => {
221
+ const old = was.get(card.id);
222
+ return !old || old.x !== card.x || old.y !== card.y || old.w !== card.w || old.h !== card.h;
223
+ });
224
+ }