@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,120 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { iconKind } from "./emoji/icon-value.mjs";
4
+ import { isWorkspace, readRegistry, workspaceId, workspaceKey } from "./workspace-registry.mjs";
5
+ import { defaultWorkspace, documentFiles, isSlugName } from "./workspace.mjs";
6
+
7
+ // The other workspaces a workspace's sidebar shows a column of (notes/plans/workspaces-links-plan-2026-11-08.md
8
+ // §7.2; docs/workbench.md "Columns"): every workspace of the machine's registry (./workspace-registry.mjs) but
9
+ // this one, a clash of ids (two registered folders sharing one: never guessed) and one whose folder is no
10
+ // workspace now (an external drive away), in this order: the default workspace (./workspace.mjs
11
+ // defaultWorkspace, GALLERY_DEFAULT_WORKSPACE honoured) first, the rest as they were registered. The outline
12
+ // (./outline.mjs) gives each a column the first time it reads it (a "whole" one, all its documents, laid out
13
+ // as its own sidebar has them) and the place of its documents no column holds; the user's order of the
14
+ // columns is the outline's after that.
15
+ //
16
+ // What a workspace's sidebar is read with (snapshotOf): its documents in its own sidebar's order, the document
17
+ // each is under there (a web link or another workspace's draft passed over upward), and what its doc.json
18
+ // says (title, description, an icon of the emoji or 图标 kind: no picture of another workspace's folder). It is
19
+ // read from its files, never written, and kept SNAPSHOT_KEPT ms at most, so that the reads of a burst (a
20
+ // command and the workbench's read after it, the list of 添加其他 Gallery 项目…) read each folder once while a
21
+ // change there shows at the next read after that; the server drops one at once when that workspace's own
22
+ // server tells of a change (src/workbench/service.mjs, through ./elsewhere.mjs). Reads files only.
23
+
24
+ /** How long a snapshot of another workspace is believed (ms). */
25
+ export const SNAPSHOT_KEPT = 2000;
26
+
27
+ /**
28
+ * The registered workspaces by their ids: a Map of id to the roots registered under it (two for one id: a clash
29
+ * of the hash's six digits, which names neither). `registry`: readRegistry()'s.
30
+ */
31
+ export function idsOf(registry = readRegistry()) {
32
+ const ids = new Map();
33
+ for (const { root } of registry.workspaces) {
34
+ const id = workspaceId(root);
35
+ const roots = ids.get(id) ?? [];
36
+ if (!roots.some((each) => workspaceKey(each) === workspaceKey(root))) roots.push(root);
37
+ ids.set(id, roots);
38
+ }
39
+ return ids;
40
+ }
41
+
42
+ /**
43
+ * The other workspaces whose columns the sidebar of workspace `root` shows: [{ id, root, isDefault }], the
44
+ * default workspace first (when it is registered and not `root`), then the registry's order.
45
+ */
46
+ export function shownWorkspaces(root, { registry = readRegistry(), env = process.env } = {}) {
47
+ const own = workspaceKey(root);
48
+ const home = workspaceKey(defaultWorkspace({ env }));
49
+ const shown = [];
50
+ for (const [id, roots] of idsOf(registry)) {
51
+ if (roots.length !== 1) continue;
52
+ const [each] = roots;
53
+ if (workspaceKey(each) === own || !isWorkspace(each)) continue;
54
+ shown.push({ id, root: each, isDefault: workspaceKey(each) === home });
55
+ }
56
+ return [...shown.filter(({ isDefault }) => isDefault), ...shown.filter(({ isDefault }) => !isDefault)];
57
+ }
58
+
59
+ const oneLine = (value) => (typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "");
60
+ /** A draft's page icon as another workspace's sidebar may show it: an emoji or a glyph of the 图标 tab. */
61
+ export const shownIcon = (value) => (typeof value === "string" && ["emoji", "glyph"].includes(iconKind(value)) ? value : null);
62
+
63
+ const kept = new Map(); // a workspace's key -> { at, value: Promise }
64
+
65
+ /**
66
+ * What the sidebar reads of workspace `root` (another's): { documents: Set of slugs, order: [slug] (its own
67
+ * sidebar's order, column after column, each entry followed by those under it), parentOf: Map of slug to the
68
+ * document it is under there (null at a column's top), facts: Map of slug to { title, description, icon, path
69
+ * (its doc.html, an old one's report.html) } }. `readOwn(root)`: its outline as its own sidebar has it (no
70
+ * column of any other workspace: ./outline.mjs readOutline with others false). Kept SNAPSHOT_KEPT ms.
71
+ */
72
+ export function snapshotOf(root, readOwn, { now = Date.now() } = {}) {
73
+ const key = workspaceKey(root);
74
+ const known = kept.get(key);
75
+ if (known && now - known.at < SNAPSHOT_KEPT) return known.value;
76
+ const value = read(root, readOwn);
77
+ kept.set(key, { at: now, value });
78
+ value.catch(() => kept.delete(key));
79
+ return value;
80
+ }
81
+
82
+ /** Forget what was read of workspace `root` (none: of every workspace), so that the next read reads it again. */
83
+ export function forgetSnapshot(root = null) {
84
+ if (root === null) kept.clear();
85
+ else kept.delete(workspaceKey(root));
86
+ }
87
+
88
+ async function read(root, readOwn) {
89
+ const own = await readOwn(root);
90
+ const documents = new Set();
91
+ const order = [];
92
+ const parentOf = new Map();
93
+ // Its documents alone: its web links, the drafts of others its tree shows and a folder named by no slug are none.
94
+ const isDocument = (key) => !own.links.includes(key) && !key.startsWith("~") && isSlugName(key);
95
+ const parentIn = (child) => {
96
+ for (const [key, list] of Object.entries(own.children)) if (list.includes(child)) return key;
97
+ return null;
98
+ };
99
+ const above = (slug) => {
100
+ let up = parentIn(slug);
101
+ while (up !== null && !(up === "" || up.startsWith(":")) && !isDocument(up)) up = parentIn(up);
102
+ return up === null || up === "" || up.startsWith(":") ? null : up;
103
+ };
104
+ const walk = (key) => (own.children[key] ?? []).forEach((slug) => {
105
+ if (isDocument(slug) && !documents.has(slug)) {
106
+ documents.add(slug);
107
+ order.push(slug);
108
+ parentOf.set(slug, above(slug));
109
+ }
110
+ walk(slug);
111
+ });
112
+ for (const { id } of own.groups) walk(id === "docs" ? "" : `:${id}`);
113
+ const facts = new Map(await Promise.all(order.map(async (slug) => {
114
+ const files = documentFiles(path.join(root, slug));
115
+ const metadata = await fs.readFile(files.json, "utf8").then((text) => JSON.parse(text.replace(/^/, "")), () => null);
116
+ const value = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {};
117
+ return [slug, { title: oneLine(value.title) || slug, description: oneLine(value.description), icon: shownIcon(value.icon), path: files.html }];
118
+ })));
119
+ return { documents, order, parentOf, facts };
120
+ }
@@ -0,0 +1,91 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import { effectiveLook, lookOf, withLookField } from "./document-look.mjs";
4
+ import { writeAtomic } from "./files.mjs";
5
+ import { refusal, said } from "./i18n/index.mjs";
6
+ import { WORKSPACE_MARK, documentDirectory, documentFiles, layoutOf, markerOf } from "./workspace.mjs";
7
+
8
+ // The workspace's default look (字体与布局's 应用到全部文档, src/shared/document-look.mjs): `look` in its
9
+ // gallery.json, beside what the file says of the components, each choice that is not the default
10
+ // kept and the field gone when none is. A document shows its own choices (doc.json's `look`) over it,
11
+ // choice by choice, and a document with none of its own, a new one among them, shows it whole. A
12
+ // workspace without it, an older one's, has every choice at its default. The 写作偏好 page (a doc.json
13
+ // marked "instructions": true) has no look, the workspace's or its own. Here for the server alone: the
14
+ // render, the workbench's service and the preview's watcher read it from disk; a page and the
15
+ // workbench's app are told the look a document shows.
16
+
17
+ // gallery.json as it reads now: what it holds when it is ours (a workspace without one holds nothing),
18
+ // else why it cannot be taken (its components' messages: src/shared/component-catalog.mjs).
19
+ const UNREAD = said("gallery.json 读不出来:改好这个文件后再试");
20
+ const NOT_OURS = said("gallery.json 不是 Gallery 的文件");
21
+ const NOT_WRITABLE = ["EPERM", "EACCES", "EBUSY", "EROFS"];
22
+ function settingsOf(text) {
23
+ if (text === null) return { value: null };
24
+ const { value, ours, reads } = markerOf(text);
25
+ return ours ? { value } : { problem: reads ? NOT_OURS : UNREAD };
26
+ }
27
+ const readText = (file) => {
28
+ try {
29
+ return readFileSync(file, "utf8");
30
+ } catch (error) {
31
+ return error.code === "ENOENT" ? null : undefined;
32
+ }
33
+ };
34
+
35
+ /** The workspace's default look at `root` (lookOf's form), {} for none, for no gallery.json and for one that does not read or is not ours. */
36
+ export function workspaceLook(root) {
37
+ const text = readText(layoutOf(root).marker);
38
+ return text === undefined ? {} : lookOf(settingsOf(text).value?.look);
39
+ }
40
+
41
+ /**
42
+ * What document `slug`'s doc.json says of its look at `root`: { own: its `look` as written (null for
43
+ * none, for a doc.json that does not read and for none at all), instructions: whether it is the 写作偏好
44
+ * page's, reads: whether what it says can be taken, which a doc.json an Agent is writing may not, a
45
+ * moment; a document with no doc.json at all (one made by hand: its doc.html alone) says it has no look
46
+ * of its own, and reads, while one whose folder is gone says nothing }.
47
+ */
48
+ export function ownLookAt(slug, root) {
49
+ const files = documentFiles(documentDirectory(slug, root));
50
+ try {
51
+ const metadata = JSON.parse(readFileSync(files.json, "utf8"));
52
+ const reads = Boolean(metadata) && typeof metadata === "object" && !Array.isArray(metadata);
53
+ return { own: reads ? (metadata.look ?? null) : null, instructions: reads && metadata.instructions === true, reads };
54
+ } catch (error) {
55
+ return { own: null, instructions: false, reads: error.code === "ENOENT" && existsSync(files.html) };
56
+ }
57
+ }
58
+
59
+ /**
60
+ * The look document `slug` of the workspace at `root` shows (lookOf's form, {} for the default): its
61
+ * own over the workspace's (effectiveLook); the workspace's for a doc.json that does not read, none
62
+ * for the 写作偏好 page.
63
+ */
64
+ export function documentLook(slug, root) {
65
+ const { own, instructions } = ownLookAt(slug, root);
66
+ return instructions ? {} : effectiveLook(own, workspaceLook(root));
67
+ }
68
+
69
+ /**
70
+ * gallery.json with `look` as the workspace's default look (lookOf: each default left out, the field
71
+ * gone when all are), written at once and with all else it holds kept, made when there is none. A
72
+ * file that does not read, is another program's, changed under this write or cannot be written is
73
+ * refused (an error that says why: refusal), and stays as it is.
74
+ */
75
+ export async function writeWorkspaceLook(root, look) {
76
+ const file = layoutOf(root).marker;
77
+ const text = await fs.readFile(file, "utf8").catch((error) => {
78
+ if (error.code === "ENOENT") return null;
79
+ throw refusal(UNREAD);
80
+ });
81
+ const { value, problem } = settingsOf(text);
82
+ if (problem) throw refusal(problem);
83
+ if (value === null && Object.keys(lookOf(look)).length === 0) return; // no file, and none needed for the default
84
+ const written = withLookField(value ?? WORKSPACE_MARK, lookOf(look)); // in its place, else last; gone for the default
85
+ if (JSON.stringify(written) === JSON.stringify(value)) return;
86
+ const done = await writeAtomic(file, `${JSON.stringify(written, null, 2)}\n`, { expect: text }).catch((error) => {
87
+ if (NOT_WRITABLE.includes(error.code)) throw refusal(said("gallery.json 写不进去:可能是只读的,或被别的程序占着"));
88
+ throw error;
89
+ });
90
+ if (!done) throw refusal(said("gallery.json 刚刚被改过:再试一次"));
91
+ }
@@ -0,0 +1,154 @@
1
+ import { readFileSync } from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { isDeepStrictEqual } from "node:util";
6
+ import { writeAtomic } from "./files.mjs";
7
+ import { objectAt, ownLine } from "./pages.mjs";
8
+ import { documentsFolder, layoutOf, markerOf, WORKSPACE_MARK } from "./workspace.mjs";
9
+
10
+ // What a workspace is called where Gallery names it beside others (gallery workspaces, gallery list
11
+ // --all, and the sidebar's column of another workspace: notes/plans/workspaces-links-plan-2026-11-08.md
12
+ // §0): its own name, `name` in its gallery.json (gallery init --name, gallery workspaces --name, the
13
+ // user in the sidebar); else its folder's name, climbing past a name that says nothing of the project
14
+ // (GENERIC_NAMES: D:\proj\outputs\gallery is "proj") but never to the user's home, their Documents
15
+ // folder or a drive's root (~/Documents/Gallery stays "Gallery"; a workspace that is a drive's root
16
+ // is called by the drive, "E:"). Two of the same name are told apart by the folder above the one
17
+ // named, which the sidebar writes after the name in grey (workspaceLabels), and where that is called
18
+ // alike too, by the workspace's own path; the path is otherwise shown on hover and in the menus, never
19
+ // as the name, and no workspace is numbered. Reads files only, but writeWorkspaceName.
20
+
21
+ /** The folders' names that say nothing of a project, passed over upward (upper and lower case alike). */
22
+ export const GENERIC_NAMES = ["gallery", "docs", "outputs", "reports", "文档"];
23
+ /** The longest name a workspace is given, in characters. */
24
+ export const NAME_LIMIT = 80;
25
+
26
+ const isGeneric = (name) => GENERIC_NAMES.includes(name.toLowerCase());
27
+ const keyOf = (folder, platform) => (platform === "win32" ? path.resolve(folder).toLowerCase() : path.resolve(folder));
28
+ const oneLine = (value) => (typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "");
29
+ // A folder's name; a drive's root, which has none, by the drive ("E:", "/").
30
+ const folderName = (folder) => path.basename(folder) || folder.replace(/[\\/]+$/, "") || folder;
31
+
32
+ /**
33
+ * What a name given to a workspace is wrong in, or null: empty, longer than NAME_LIMIT characters, or
34
+ * holding a control character (a line break among them).
35
+ */
36
+ export function nameProblem(name) {
37
+ if (typeof name !== "string" || !name.trim()) return "A workspace's name holds a word at least.";
38
+ if (/[\u0000-\u001f\u007f]/.test(name)) return "A workspace's name is one line, with no control character.";
39
+ if ([...name.trim()].length > NAME_LIMIT) return `A workspace's name is ${NAME_LIMIT} characters at most.`;
40
+ return null;
41
+ }
42
+
43
+ /** The workspace's own name at `root` (gallery.json's `name`, on one line), or null: none, or a gallery.json that is not ours or does not read. */
44
+ export function ownName(root, { readText = (file) => readFileSync(file, "utf8") } = {}) {
45
+ let text;
46
+ try {
47
+ text = readText(layoutOf(root).marker);
48
+ } catch {
49
+ return null;
50
+ }
51
+ const { value, ours } = markerOf(text);
52
+ const name = ours ? oneLine(value?.name) : "";
53
+ return name ? [...name].slice(0, NAME_LIMIT).join("") : null;
54
+ }
55
+
56
+ /** The folders no name climbs to: the user's home and their Documents folder (each workspace's name is found against these). */
57
+ export function nameStops({ home = os.homedir(), documents = documentsFolder({ home }) } = {}) {
58
+ return [home, documents].filter(Boolean);
59
+ }
60
+
61
+ /**
62
+ * The folder whose name a workspace at `root` goes by when it has none of its own: `root`, or the
63
+ * nearest folder above it whose name is not generic, climbing no higher than below one of `stops` and
64
+ * never to a drive's root (a folder there keeps its name, generic or not).
65
+ */
66
+ export function namedFolder(root, { stops = [], platform = process.platform } = {}) {
67
+ const stopped = new Set(stops.map((stop) => keyOf(stop, platform)));
68
+ let folder = path.resolve(root);
69
+ for (;;) {
70
+ const parent = path.dirname(folder);
71
+ const atRoot = parent === folder || path.dirname(parent) === parent;
72
+ if (!isGeneric(path.basename(folder)) || atRoot || stopped.has(keyOf(parent, platform))) return folder;
73
+ folder = parent;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * What the workspace at `root` is called: { name, own: whether it is gallery.json's name (else its
79
+ * folder's), folder: the folder named (the workspace's own for a name of its own), above: that
80
+ * folder's parent's name (what tells two of the same name apart), parent: that parent's path }.
81
+ */
82
+ export function workspaceName(root, { stops = [], platform = process.platform, readText } = {}) {
83
+ const own = ownName(root, readText ? { readText } : undefined);
84
+ const folder = own ? path.resolve(root) : namedFolder(root, { stops, platform });
85
+ const parent = path.dirname(folder);
86
+ return { name: own ?? folderName(folder), own: Boolean(own), folder, above: parent === folder ? null : path.basename(parent) || parent, parent };
87
+ }
88
+
89
+ /**
90
+ * The names of several workspaces as the sidebar and the commands write them side by side: each of
91
+ * `named` (workspaceName's, with its `root`, its path) as { root, name, own, above }, `above` null
92
+ * but where another has the same name (upper and lower case alike): then the name of the folder above
93
+ * the one named, and where another of them has that too (two workspaces side by side, or two in one
94
+ * project climbing to its folder), or there is none (a drive's root), the workspace's own path, which
95
+ * no other has.
96
+ */
97
+ export function workspaceLabels(named) {
98
+ const same = (a, b) => a.toLowerCase() === b.toLowerCase();
99
+ return named.map((entry) => {
100
+ const label = { root: entry.root, name: entry.name, own: entry.own, above: null };
101
+ const others = named.filter((other) => other !== entry && same(other.name, entry.name));
102
+ if (!others.length) return label;
103
+ const told = entry.above !== null && !others.some((other) => other.above !== null && same(other.above, entry.above));
104
+ return { ...label, above: told ? entry.above : entry.root };
105
+ });
106
+ }
107
+
108
+ /**
109
+ * gallery.json at `root` with `name` as the workspace's name (namedIn: in place of the one it had,
110
+ * else right after what marks it; all else it holds kept, and the file's own layout with it), written
111
+ * at once; the workspace must be there and its gallery.json ours (ensureWorkspace first). Resolves to
112
+ * { name: as written, before: the name it had, or null }; throws an Error saying why when the name is
113
+ * wrong (nameProblem), the file is not ours or does not read, or it kept changing under the write.
114
+ */
115
+ export async function writeWorkspaceName(root, name) {
116
+ const problem = nameProblem(name);
117
+ if (problem) throw new Error(problem);
118
+ const wanted = name.trim();
119
+ const file = layoutOf(root).marker;
120
+ for (let tries = 0; tries < 3; tries++) {
121
+ const text = await fs.readFile(file, "utf8");
122
+ const { value, ours, reads } = markerOf(text);
123
+ if (!ours || !value || typeof value !== "object" || Array.isArray(value)) throw new Error(reads ? `${file} is not Gallery's.` : `${file} does not read as JSON: mend it first.`);
124
+ const before = oneLine(value.name) || null;
125
+ if (value.name === wanted) return { name: wanted, before };
126
+ let next = null;
127
+ try {
128
+ next = namedIn(text, wanted);
129
+ } catch {}
130
+ // What the text says once written is checked: any doubt, the whole file written as JSON.stringify lays it out.
131
+ if (next === null || !isDeepStrictEqual(markerOf(next).value, { ...value, name: wanted })) {
132
+ const { gallery, version, name: _old, ...rest } = value;
133
+ next = `${JSON.stringify({ gallery: gallery ?? WORKSPACE_MARK.gallery, version: version ?? WORKSPACE_MARK.version, name: wanted, ...rest }, null, 2)}\n`;
134
+ }
135
+ if (await writeAtomic(file, next, { expect: text })) return { name: wanted, before };
136
+ }
137
+ throw new Error(`${file} kept changing while its name was written: try again.`);
138
+ }
139
+
140
+ // gallery.json's text (`text`, Gallery's, a JSON object that reads) with `name` as its name, written
141
+ // into it as it stands, as ./pages.mjs writes pages.homeMade (its line ends, indentation, BOM, the order
142
+ // of its keys and what stands on one line kept): the name it had replaced in its place, else one added
143
+ // right after the mark (`gallery`, or `version` where that comes after it), laid out as that member is
144
+ // (on a line of its own at its indentation, or on its line after it, as tightly).
145
+ function namedIn(text, name) {
146
+ const top = objectAt(text, text.indexOf("{"));
147
+ const had = top.members.findLast(({ key }) => key === "name");
148
+ if (had) return `${text.slice(0, had.value)}${JSON.stringify(name)}${text.slice(had.end)}`;
149
+ const mark = top.members.filter(({ key }) => key === "gallery" || key === "version").at(-1);
150
+ if (!mark) return null;
151
+ const indent = ownLine(text, top.open, mark);
152
+ const gap = indent !== null ? (text.includes("\r\n") ? "\r\n" : "\n") + indent : /[ \t]*$/.exec(text.slice(0, mark.from))[0];
153
+ return `${text.slice(0, mark.end)},${gap}"name":${mark.colon}${JSON.stringify(name)}${text.slice(mark.end)}`;
154
+ }
@@ -0,0 +1,125 @@
1
+ import { CHART_KEY } from "./table-chart.mjs";
2
+
3
+ // What a block reads of the workspace (the spec custom-blocks-spec-2026-11-07.md §3, §4; docs/render.md
4
+ // "A custom block on a page", docs/workbench.md "The workspace's facts"): the kinds of
5
+ // context.workspace.query, how many items each gives, the options each takes, and where the kit asks for
6
+ // them, the one list the kit's runtime (render/kit/runtime/workspace.js), the server's facts
7
+ // (src/workbench/index.mjs) and the event stream's "data <kind>" (src/server/routes.mjs) go by; and how a
8
+ // block checks a to-do of another document (workspace.setTodo: the plan's 5.1), what an export embeds of
9
+ // the workspace (the spec §9). Pure: no DOM, no Node.
10
+
11
+ /** The kinds workspace.query answers: the workspace's facts, and `table`, a keyed table of a document (the plan's 5.2). */
12
+ export const QUERY_KINDS = Object.freeze(["todos", "recent", "stats", "activity", "table"]);
13
+
14
+ /**
15
+ * The kinds of the workspace's facts a change may change, each said to the pages as "data <kind>":
16
+ * `table` whenever a document's text changed (any keyed table of it may have).
17
+ */
18
+ export const FACT_KINDS = Object.freeze(["todos", "recent", "stats", "activity", "table"]);
19
+
20
+ /**
21
+ * The codes workspace.query rejects with (the spec §3), never another: the server's own refusals of a query
22
+ * (kind, options, missing: no such document, or no table of that key in it, or one with merged cells; and
23
+ * unsupported, which no server of this version says) as it says them; offline where there is no server to ask
24
+ * (an export without that kind in its snapshot) or it does not answer; failed where it answered and could
25
+ * not (a read that failed, a request it refused, no JSON).
26
+ */
27
+ export const QUERY_CODES = Object.freeze(["kind", "options", "unsupported", "missing", "offline", "failed"]);
28
+
29
+ /**
30
+ * How many items a query gives where its options say none, and the most it gives: to-dos, documents edited
31
+ * last, days of the record (as many as it keeps, src/workbench/activity.mjs KEEP_DAYS: the 编辑热力图's 53
32
+ * weeks), rows of a table.
33
+ */
34
+ export const QUERY_LIMITS = Object.freeze({ todos: { default: 200, most: 1000 }, recent: { default: 10, most: 50 }, activity: { default: 365, most: 371 }, table: { default: 200, most: 1000 } });
35
+
36
+ /**
37
+ * What an export embeds of the workspace for a document whose blocks read it (the spec §9: its snapshot's
38
+ * kinds beside `net`), each kind's whole answer to so many at most: to-dos (done or not), documents edited
39
+ * last, days of the record, and of the tables its instances asked for (the last `tables` of them), rows each.
40
+ */
41
+ export const SNAPSHOT_LIMITS = Object.freeze({ todos: 200, recent: 50, activity: 371, tables: 20, rows: 1000 });
42
+
43
+ /** Where a page asks for its to-dos to be checked (POST { slug, items: [{ ref, done }] }, the token's: the workspace's service). */
44
+ export const TODO_ROUTE = "/__gallery/workspace/todo";
45
+
46
+ /** What workspace.setTodo asks whoever shows the page (cancelable, on the window: { ask: { slug, ref, done }, answer(promise) }). */
47
+ export const TODO_EVENT = "doc-todo-set";
48
+
49
+ /**
50
+ * The codes workspace.setTodo rejects with, never another: ref (not a to-do's ref as query gave it, or
51
+ * `done` no true or false), changed (the item is not as the ref read it: its words changed, or it is gone:
52
+ * the conflict, which the block reads again after), ambiguous (no block id, the file changed since, and
53
+ * more than one item of its words under its headings), locked (the item is there as read, in a place the
54
+ * document's own page cannot check: a list in a table cell, a block of the document's own), missing (no such document), busy (the file kept
55
+ * changing, or stayed half written), offline (nothing on the page writes the workspace: an exported file,
56
+ * the 演示视图, a page on its own without a token; or the server does not answer), failed (anything else).
57
+ */
58
+ export const TODO_CODES = Object.freeze(["ref", "changed", "ambiguous", "locked", "missing", "busy", "offline", "failed"]);
59
+
60
+ /** How many to-dos one write of a document carries at most (the page merges what waits into one). */
61
+ export const TODO_MOST = 50;
62
+
63
+ const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
64
+ const BLOCK = /^[A-Za-z0-9_-]{1,64}$/;
65
+ const VERSION = /^[a-f0-9]{16}$/;
66
+
67
+ /**
68
+ * `ref` as a write carries it, when it is a to-do's ref as workspace.query("todos") gives it ({ slug,
69
+ * block, text, path, nth, version }: `version` the hash of the file the facts read it from, which may be
70
+ * missing), or null.
71
+ */
72
+ export function todoRef(ref) {
73
+ if (!ref || typeof ref !== "object" || Array.isArray(ref)) return null;
74
+ const { slug, block = null, text, path, nth, version = null } = ref;
75
+ if (typeof slug !== "string" || !SLUG.test(slug) || (block !== null && (typeof block !== "string" || !BLOCK.test(block)))) return null;
76
+ if (typeof text !== "string" || text.length > 4000 || !Array.isArray(path) || path.length > 6 || !path.every((step) => typeof step === "string" && step.length <= 4000)) return null;
77
+ if (!Number.isInteger(nth) || nth < 0 || (version !== null && (typeof version !== "string" || !VERSION.test(version)))) return null;
78
+ return { slug, block, text, path: [...path], nth, version };
79
+ }
80
+
81
+ /** Where a query is asked (GET, ?kind=<kind>&options=<JSON>): the workspace's service. */
82
+ export const QUERY_ROUTE = "/__gallery/workspace/query";
83
+
84
+ /**
85
+ * The global a page the server serves holds QUERY_ROUTE in (src/server/routes.mjs, a script of the
86
+ * server's that no export keeps), which the kit's runtime asks: none there, the workspace is offline.
87
+ */
88
+ export const WORKSPACE_GLOBAL = "__galleryWorkspace";
89
+
90
+ /** What workspace.open asks whoever shows the page to open (a cancelable event on the window, detail { slug, block } or { page }). */
91
+ export const OPEN_EVENT = "doc-open-request";
92
+
93
+ /**
94
+ * The options of a query of `kind` as they are used: { options } (todos: done, slugs, limit; recent:
95
+ * limit; activity: days; every count held to its most), or { problem } (what is wrong, in English).
96
+ */
97
+ export function queryOptions(kind, options) {
98
+ const given = options ?? {};
99
+ if (typeof given !== "object" || Array.isArray(given)) return { problem: "its options are an object" };
100
+ const count = (name, { default: fallback, most }) => {
101
+ const value = given[name];
102
+ if (value === undefined) return fallback;
103
+ if (!Number.isInteger(value) || value < 1) throw new Error(`${name} is a whole number from 1`);
104
+ return Math.min(value, most);
105
+ };
106
+ try {
107
+ if (kind === "todos") {
108
+ if (given.done !== undefined && typeof given.done !== "boolean") return { problem: "done is true or false" };
109
+ if (given.slugs !== undefined && !(Array.isArray(given.slugs) && given.slugs.every((slug) => typeof slug === "string"))) return { problem: "slugs is a list of documents' slugs" };
110
+ return { options: { done: given.done, slugs: given.slugs ?? null, limit: count("limit", QUERY_LIMITS.todos) } };
111
+ }
112
+ if (kind === "recent") return { options: { limit: count("limit", QUERY_LIMITS.recent) } };
113
+ if (kind === "table") {
114
+ if (typeof given.slug !== "string" || !SLUG.test(given.slug)) return { problem: "slug is a document's slug" };
115
+ if (typeof given.key !== "string" || !CHART_KEY.test(given.key)) return { problem: "key is a table's key (data-doc-chart-source)" };
116
+ const where = given.where ?? {};
117
+ if (typeof where !== "object" || Array.isArray(where) || !Object.values(where).every((value) => typeof value === "string")) return { problem: "where maps each column to the words its cells are to hold" };
118
+ return { options: { slug: given.slug, key: given.key, where: { ...where }, limit: count("limit", QUERY_LIMITS.table) } };
119
+ }
120
+ if (kind === "activity") return { options: { days: count("days", QUERY_LIMITS.activity) } };
121
+ return { options: {} };
122
+ } catch (error) {
123
+ return { problem: error.message };
124
+ }
125
+ }