@heycys/gallery 0.1.1 → 0.2.0

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 (473) hide show
  1. package/THIRD-PARTY-NOTICES.md +2 -2
  2. package/browser/editor/components/align.mjs +58 -0
  3. package/browser/editor/components/code/index.mjs +1 -1
  4. package/browser/editor/components/columns/page.mjs +4 -4
  5. package/browser/editor/components/embed/index.mjs +2 -2
  6. package/browser/editor/components/embed/page.mjs +1 -1
  7. package/browser/editor/components/file/page.mjs +4 -4
  8. package/browser/editor/components/flowchart/add.mjs +7 -6
  9. package/browser/editor/components/flowchart/page.mjs +1 -1
  10. package/browser/editor/components/heading/index.mjs +7 -6
  11. package/browser/editor/components/icons.mjs +11 -0
  12. package/browser/editor/components/image/crop.mjs +6 -9
  13. package/browser/editor/components/image/index.mjs +2 -0
  14. package/browser/editor/components/image/page.mjs +172 -90
  15. package/browser/editor/components/installed/index.mjs +15 -4
  16. package/browser/editor/components/installed/page.mjs +103 -25
  17. package/browser/editor/components/installed/served.mjs +6 -5
  18. package/browser/editor/components/instances.mjs +18 -0
  19. package/browser/editor/components/list/page.mjs +3 -6
  20. package/browser/editor/components/media/icons.mjs +13 -8
  21. package/browser/editor/components/media/page.mjs +5 -3
  22. package/browser/editor/components/media/toolbar.mjs +12 -3
  23. package/browser/editor/components/paragraph/index.mjs +4 -3
  24. package/browser/editor/components/registry.mjs +30 -9
  25. package/browser/editor/components/table/index.mjs +61 -2
  26. package/browser/editor/components/table/page.mjs +1 -1
  27. package/browser/editor/page/after/index.mjs +288 -0
  28. package/browser/editor/page/align.mjs +91 -0
  29. package/browser/editor/page/annotate/card.mjs +3 -6
  30. package/browser/editor/page/autosave.mjs +310 -37
  31. package/browser/editor/page/beside.mjs +5 -6
  32. package/browser/editor/page/block-menu/index.mjs +6 -3
  33. package/browser/editor/page/blocks.mjs +3 -1
  34. package/browser/editor/page/board/client.mjs +57 -0
  35. package/browser/editor/page/board/hint.mjs +182 -0
  36. package/browser/editor/page/board/history.mjs +119 -0
  37. package/browser/editor/page/board/index.mjs +1059 -0
  38. package/browser/editor/page/board/motion.mjs +177 -0
  39. package/browser/editor/page/board/settings.mjs +210 -0
  40. package/browser/editor/page/caret.mjs +1 -1
  41. package/browser/editor/page/changes/bar.mjs +6 -14
  42. package/browser/editor/page/changes/index.mjs +2 -2
  43. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  44. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  45. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  46. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  47. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  48. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  49. package/browser/editor/page/clipboard/index.mjs +133 -15
  50. package/browser/editor/page/clipboard/own/blocks.mjs +13 -14
  51. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  52. package/browser/editor/page/clipboard/own/describe.mjs +6 -2
  53. package/browser/editor/page/clipboard/own/selection.mjs +1 -1
  54. package/browser/editor/page/columns/index.mjs +48 -46
  55. package/browser/editor/page/commit.mjs +80 -22
  56. package/browser/editor/page/conflicts.mjs +7 -7
  57. package/browser/editor/page/core.mjs +34 -16
  58. package/browser/editor/page/custom-page.mjs +110 -0
  59. package/browser/editor/page/editor.mjs +71 -17
  60. package/browser/editor/page/emoji-picker/styles.mjs +7 -12
  61. package/browser/editor/page/emoji-picker/upload.mjs +2 -2
  62. package/browser/editor/page/handle/index.mjs +5 -8
  63. package/browser/editor/page/history.mjs +41 -6
  64. package/browser/editor/page/insert/index.mjs +20 -22
  65. package/browser/editor/page/links/index.mjs +5 -3
  66. package/browser/editor/page/links/pages.mjs +27 -9
  67. package/browser/editor/page/look.mjs +54 -3
  68. package/browser/editor/page/margin.mjs +18 -3
  69. package/browser/editor/page/media-menu/index.mjs +6 -7
  70. package/browser/editor/page/menu.mjs +33 -14
  71. package/browser/editor/page/notices.mjs +2 -2
  72. package/browser/editor/page/page-objects.mjs +9 -2
  73. package/browser/editor/page/panel.mjs +4 -9
  74. package/browser/editor/page/present-links.mjs +15 -5
  75. package/browser/editor/page/present-shell.mjs +63 -0
  76. package/browser/editor/page/resize/index.mjs +42 -29
  77. package/browser/editor/page/selection/index.mjs +10 -2
  78. package/browser/editor/page/served.mjs +14 -0
  79. package/browser/editor/page/session.mjs +48 -4
  80. package/browser/editor/page/slash-menu/index.mjs +3 -3
  81. package/browser/editor/page/table/binding.mjs +87 -0
  82. package/browser/editor/page/table/cells.mjs +102 -46
  83. package/browser/editor/page/table/index.mjs +24 -9
  84. package/browser/editor/page/table/pictures.mjs +299 -0
  85. package/browser/editor/page/table/views.mjs +103 -0
  86. package/browser/editor/page/table/widths.mjs +82 -10
  87. package/browser/editor/page/text-select/index.mjs +26 -12
  88. package/browser/editor/page/todo-writes.mjs +75 -0
  89. package/browser/editor/page/typing/index.mjs +97 -15
  90. package/browser/editor/page/typing/leave.mjs +19 -5
  91. package/browser/editor/page/ui.mjs +13 -0
  92. package/browser/shared/block-packs.mjs +419 -0
  93. package/browser/shared/board-places.mjs +224 -0
  94. package/browser/shared/card-size.mjs +59 -0
  95. package/browser/shared/colors.mjs +3 -2
  96. package/browser/shared/component-catalog.mjs +330 -46
  97. package/browser/shared/component-source.mjs +39 -0
  98. package/browser/shared/components/heading.mjs +2 -1
  99. package/browser/shared/components/index.mjs +12 -3
  100. package/browser/shared/components/installed.mjs +35 -3
  101. package/browser/shared/components/page.mjs +20 -8
  102. package/browser/shared/components/paragraph.mjs +8 -1
  103. package/browser/shared/components/state.mjs +5 -1
  104. package/browser/shared/components/table.mjs +1 -0
  105. package/browser/shared/document-look.mjs +10 -5
  106. package/browser/shared/drafts.mjs +432 -0
  107. package/browser/shared/emoji/line-icons.mjs +37 -0
  108. package/browser/shared/extension-sidebar.mjs +126 -0
  109. package/browser/shared/extensions.mjs +79 -0
  110. package/browser/shared/file-table.mjs +101 -0
  111. package/browser/shared/http.mjs +9 -0
  112. package/browser/shared/i18n/en.mjs +330 -27
  113. package/browser/shared/json-layout.mjs +47 -0
  114. package/browser/shared/net-cache.mjs +133 -0
  115. package/browser/shared/net-keys.mjs +32 -0
  116. package/browser/shared/net-policy.mjs +177 -0
  117. package/browser/shared/outline-shape.mjs +128 -0
  118. package/browser/shared/outline.mjs +359 -67
  119. package/browser/shared/pack-manifest.mjs +88 -0
  120. package/browser/shared/page-address.mjs +6 -3
  121. package/browser/shared/page-cards.mjs +308 -0
  122. package/browser/shared/page-links.mjs +69 -6
  123. package/browser/shared/pages.mjs +500 -0
  124. package/browser/shared/paths.mjs +6 -0
  125. package/browser/shared/pinyin.mjs +92 -0
  126. package/browser/shared/present-step.mjs +17 -0
  127. package/browser/shared/preview-server.mjs +3 -7
  128. package/browser/shared/session.mjs +8 -1
  129. package/browser/shared/shipped-blocks.mjs +299 -0
  130. package/browser/shared/sidebar-head.mjs +70 -0
  131. package/browser/shared/table-chart.mjs +33 -1
  132. package/browser/shared/todos.mjs +83 -0
  133. package/browser/shared/user-directory.mjs +24 -0
  134. package/browser/shared/versions.mjs +14 -0
  135. package/browser/shared/web-links.mjs +139 -0
  136. package/browser/shared/workspace-columns.mjs +120 -0
  137. package/browser/shared/workspace-name.mjs +154 -0
  138. package/browser/shared/workspace-query.mjs +125 -0
  139. package/browser/shared/workspace-registry.mjs +272 -0
  140. package/browser/shared/workspace.mjs +171 -20
  141. package/dist/chunk-2OHANJ6Q.mjs +2 -0
  142. package/dist/chunk-3MBRL2PM.mjs +16 -0
  143. package/dist/chunk-4OKWZY5E.mjs +2 -0
  144. package/dist/chunk-4OZWXZE5.mjs +5 -0
  145. package/dist/chunk-5K44HLTZ.mjs +2 -0
  146. package/dist/{chunk-5ZKJ3H4P.mjs → chunk-5XA2ZBAE.mjs} +3 -3
  147. package/dist/{chunk-4FS7Z7WR.mjs → chunk-67N2QJYT.mjs} +1 -1
  148. package/dist/chunk-6KBYRA2C.mjs +2 -0
  149. package/dist/chunk-6N3PX4XX.mjs +8 -0
  150. package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
  151. package/dist/chunk-7JDX4E65.mjs +13 -0
  152. package/dist/chunk-7M2BQQGZ.mjs +2 -0
  153. package/dist/chunk-7OG26PGF.mjs +2 -0
  154. package/dist/chunk-AHN3NFKJ.mjs +3 -0
  155. package/dist/chunk-ALZ6ZENZ.mjs +3 -0
  156. package/dist/chunk-AR5D57ZD.mjs +170 -0
  157. package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
  158. package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
  159. package/dist/chunk-CMA72MAG.mjs +2 -0
  160. package/dist/{chunk-LCTHTZIL.mjs → chunk-DAZL73VK.mjs} +91 -91
  161. package/dist/{chunk-Y6RLRPG3.mjs → chunk-DJISHPUL.mjs} +1 -1
  162. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  163. package/dist/{chunk-FDLXYYG4.mjs → chunk-EAIFITAV.mjs} +1 -1
  164. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  165. package/dist/chunk-EP25HDAJ.mjs +2 -0
  166. package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
  167. package/dist/chunk-EURLTOTZ.mjs +4 -0
  168. package/dist/chunk-F4ZA6JRZ.mjs +3 -0
  169. package/dist/chunk-F6HVDZR4.mjs +2 -0
  170. package/dist/chunk-FDMAXMQA.mjs +2 -0
  171. package/dist/chunk-FETSREUN.mjs +5 -0
  172. package/dist/chunk-GHOYKMQ3.mjs +4 -0
  173. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  174. package/dist/chunk-HDWTZ763.mjs +12 -0
  175. package/dist/chunk-HPOI4ZX7.mjs +6 -0
  176. package/dist/{chunk-BGGDRNYQ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
  177. package/dist/chunk-IA4PSIMD.mjs +2 -0
  178. package/dist/{chunk-E7LWZRXY.mjs → chunk-ITOH5EGO.mjs} +1 -1
  179. package/dist/chunk-IV64FFI4.mjs +2 -0
  180. package/dist/chunk-J43Z6LSS.mjs +2 -0
  181. package/dist/chunk-J5PUIV3Y.mjs +4 -0
  182. package/dist/chunk-JA7G53PC.mjs +2 -0
  183. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  184. package/dist/chunk-JLTZF4LA.mjs +2 -0
  185. package/dist/chunk-JZT7I2OJ.mjs +2 -0
  186. package/dist/chunk-K5XATEOO.mjs +2 -0
  187. package/dist/chunk-L2SUIS5S.mjs +2 -0
  188. package/dist/chunk-L7UP52JO.mjs +2 -0
  189. package/dist/chunk-LM4XFQHZ.mjs +2 -0
  190. package/dist/chunk-MN7BZL4R.mjs +2 -0
  191. package/dist/chunk-MTYYHM2G.mjs +15 -0
  192. package/dist/chunk-MXUBKDRO.mjs +15 -0
  193. package/dist/chunk-NAMN3RAF.mjs +2 -0
  194. package/dist/{chunk-SYGN7GLU.mjs → chunk-OEXWRJME.mjs} +1 -1
  195. package/dist/chunk-OFHXGWOX.mjs +2 -0
  196. package/dist/chunk-ORID3AE7.mjs +166 -0
  197. package/dist/chunk-PAREWQGB.mjs +4 -0
  198. package/dist/chunk-PGDE24N5.mjs +2 -0
  199. package/dist/chunk-POTA2FV4.mjs +2 -0
  200. package/dist/chunk-PUOQ65NC.mjs +2 -0
  201. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  202. package/dist/chunk-QE5RG76S.mjs +549 -0
  203. package/dist/chunk-R2ZDLJYT.mjs +10 -0
  204. package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  205. package/dist/chunk-SWUTTZCN.mjs +2 -0
  206. package/dist/chunk-TANZRINB.mjs +2 -0
  207. package/dist/chunk-TR7TWGHB.mjs +2 -0
  208. package/dist/chunk-U5XL7D6A.mjs +3 -0
  209. package/dist/{chunk-XNROSH2T.mjs → chunk-U7NZI6N4.mjs} +2 -2
  210. package/dist/chunk-UGWMMEJ5.mjs +15 -0
  211. package/dist/chunk-UJQJKNGK.mjs +2 -0
  212. package/dist/chunk-UYVCQK3H.mjs +22 -0
  213. package/dist/chunk-VFX2KNHP.mjs +4 -0
  214. package/dist/chunk-VS7JIGJW.mjs +24 -0
  215. package/dist/chunk-W7665ZEC.mjs +6 -0
  216. package/dist/chunk-WQDQWCX5.mjs +2 -0
  217. package/dist/chunk-YI43RG4P.mjs +2 -0
  218. package/dist/chunk-YLPSMDIS.mjs +5 -0
  219. package/dist/chunk-YNAWBAYN.mjs +3 -0
  220. package/dist/chunk-YROX2O22.mjs +2 -0
  221. package/dist/chunk-ZDLPUOAG.mjs +6 -0
  222. package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  223. package/dist/chunk-ZQS6H473.mjs +16 -0
  224. package/dist/chunk-ZTHICSSF.mjs +2 -0
  225. package/dist/chunk-ZWWC4KLF.mjs +20 -0
  226. package/dist/cli.mjs +1 -1
  227. package/dist/server.mjs +1 -1
  228. package/dist/{chunk-U6KNPJPR.mjs → update-fetch.mjs} +1 -1
  229. package/dist/worker.mjs +1 -1
  230. package/kit/board.css +95 -0
  231. package/kit/board.js +250 -0
  232. package/kit/charts.js +5 -71
  233. package/kit/code.js +17 -17
  234. package/kit/report.css +335 -18
  235. package/kit/runtime.js +1638 -125
  236. package/kit/state.js +10 -1
  237. package/kit/tables.js +130 -43
  238. package/kit/ui.js +79 -35
  239. package/package.json +2 -1
  240. package/packs/gallery/card/component.json +14 -0
  241. package/packs/gallery/card/example.html +10 -0
  242. package/packs/gallery/card/preview.svg +1 -0
  243. package/packs/gallery/card/style.css +6 -0
  244. package/packs/gallery/card/template.html +6 -0
  245. package/packs/gallery/card/writing.md +14 -0
  246. package/packs/gallery/chart-card/component.json +15 -0
  247. package/packs/gallery/chart-card/example.html +14 -0
  248. package/packs/gallery/chart-card/kit.js +130 -0
  249. package/packs/gallery/chart-card/preview.svg +1 -0
  250. package/packs/gallery/chart-card/style.css +42 -0
  251. package/packs/gallery/chart-card/template.html +13 -0
  252. package/packs/gallery/chart-card/writing.md +20 -0
  253. package/packs/gallery/chart-view/component.json +20 -0
  254. package/packs/gallery/chart-view/example.html +13 -0
  255. package/packs/gallery/chart-view/kit.js +355 -0
  256. package/packs/gallery/chart-view/preview.svg +1 -0
  257. package/packs/gallery/chart-view/style.css +94 -0
  258. package/packs/gallery/chart-view/template.html +3 -0
  259. package/packs/gallery/chart-view/writing.md +22 -0
  260. package/packs/gallery/countdown/component.json +18 -0
  261. package/packs/gallery/countdown/example.html +3 -0
  262. package/packs/gallery/countdown/kit.js +216 -0
  263. package/packs/gallery/countdown/preview.svg +1 -0
  264. package/packs/gallery/countdown/style.css +118 -0
  265. package/packs/gallery/countdown/template.html +3 -0
  266. package/packs/gallery/countdown/writing.md +22 -0
  267. package/packs/gallery/habit/component.json +14 -0
  268. package/packs/gallery/habit/example.html +3 -0
  269. package/packs/gallery/habit/kit.js +334 -0
  270. package/packs/gallery/habit/preview.svg +1 -0
  271. package/packs/gallery/habit/style.css +174 -0
  272. package/packs/gallery/habit/template.html +3 -0
  273. package/packs/gallery/habit/writing.md +21 -0
  274. package/packs/gallery/heatmap/component.json +18 -0
  275. package/packs/gallery/heatmap/example.html +3 -0
  276. package/packs/gallery/heatmap/kit.js +513 -0
  277. package/packs/gallery/heatmap/preview.svg +1 -0
  278. package/packs/gallery/heatmap/style.css +189 -0
  279. package/packs/gallery/heatmap/template.html +3 -0
  280. package/packs/gallery/heatmap/writing.md +21 -0
  281. package/packs/gallery/kanban/component.json +18 -0
  282. package/packs/gallery/kanban/example.html +12 -0
  283. package/packs/gallery/kanban/kit.js +540 -0
  284. package/packs/gallery/kanban/preview.svg +1 -0
  285. package/packs/gallery/kanban/style.css +212 -0
  286. package/packs/gallery/kanban/template.html +3 -0
  287. package/packs/gallery/kanban/writing.md +20 -0
  288. package/packs/gallery/metric/component.json +22 -0
  289. package/packs/gallery/metric/example.html +15 -0
  290. package/packs/gallery/metric/kit.js +248 -0
  291. package/packs/gallery/metric/preview.svg +1 -0
  292. package/packs/gallery/metric/style.css +92 -0
  293. package/packs/gallery/metric/template.html +3 -0
  294. package/packs/gallery/metric/writing.md +23 -0
  295. package/packs/gallery/note/component.json +14 -0
  296. package/packs/gallery/note/example.html +3 -0
  297. package/packs/gallery/note/kit.js +129 -0
  298. package/packs/gallery/note/preview.svg +1 -0
  299. package/packs/gallery/note/style.css +97 -0
  300. package/packs/gallery/note/template.html +3 -0
  301. package/packs/gallery/note/writing.md +18 -0
  302. package/packs/gallery/pack.json +8 -0
  303. package/packs/gallery/recent/component.json +13 -0
  304. package/packs/gallery/recent/example.html +1 -0
  305. package/packs/gallery/recent/kit.js +293 -0
  306. package/packs/gallery/recent/preview.svg +1 -0
  307. package/packs/gallery/recent/style.css +85 -0
  308. package/packs/gallery/recent/template.html +1 -0
  309. package/packs/gallery/recent/writing.md +15 -0
  310. package/packs/gallery/static/component.json +13 -0
  311. package/packs/gallery/static/example.html +11 -0
  312. package/packs/gallery/static/preview.svg +1 -0
  313. package/packs/gallery/static/style.css +171 -0
  314. package/packs/gallery/static/template.html +1 -0
  315. package/packs/gallery/static/writing.md +18 -0
  316. package/packs/gallery/stats/component.json +13 -0
  317. package/packs/gallery/stats/example.html +1 -0
  318. package/packs/gallery/stats/kit.js +347 -0
  319. package/packs/gallery/stats/preview.svg +1 -0
  320. package/packs/gallery/stats/style.css +73 -0
  321. package/packs/gallery/stats/template.html +1 -0
  322. package/packs/gallery/stats/writing.md +11 -0
  323. package/packs/gallery/today/component.json +12 -0
  324. package/packs/gallery/today/example.html +1 -0
  325. package/packs/gallery/today/kit.js +220 -0
  326. package/packs/gallery/today/preview.svg +1 -0
  327. package/packs/gallery/today/style.css +149 -0
  328. package/packs/gallery/today/template.html +1 -0
  329. package/packs/gallery/today/writing.md +14 -0
  330. package/packs/gallery/todos/component.json +19 -0
  331. package/packs/gallery/todos/example.html +3 -0
  332. package/packs/gallery/todos/kit.js +489 -0
  333. package/packs/gallery/todos/preview.svg +1 -0
  334. package/packs/gallery/todos/style.css +214 -0
  335. package/packs/gallery/todos/template.html +3 -0
  336. package/packs/gallery/todos/writing.md +30 -0
  337. package/packs/gallery/weather/component.json +19 -0
  338. package/packs/gallery/weather/example.html +3 -0
  339. package/packs/gallery/weather/kit.js +387 -0
  340. package/packs/gallery/weather/preview.svg +1 -0
  341. package/packs/gallery/weather/style.css +140 -0
  342. package/packs/gallery/weather/template.html +3 -0
  343. package/packs/gallery/weather/writing.md +26 -0
  344. package/packs/gallery-soft/countdown/component.json +18 -0
  345. package/packs/gallery-soft/countdown/example.html +3 -0
  346. package/packs/gallery-soft/countdown/kit.js +206 -0
  347. package/packs/gallery-soft/countdown/preview.svg +1 -0
  348. package/packs/gallery-soft/countdown/style.css +211 -0
  349. package/packs/gallery-soft/countdown/template.html +3 -0
  350. package/packs/gallery-soft/countdown/writing.md +24 -0
  351. package/packs/gallery-soft/habit/component.json +14 -0
  352. package/packs/gallery-soft/habit/example.html +3 -0
  353. package/packs/gallery-soft/habit/kit.js +366 -0
  354. package/packs/gallery-soft/habit/preview.svg +1 -0
  355. package/packs/gallery-soft/habit/style.css +251 -0
  356. package/packs/gallery-soft/habit/template.html +3 -0
  357. package/packs/gallery-soft/habit/writing.md +23 -0
  358. package/packs/gallery-soft/heatmap/component.json +18 -0
  359. package/packs/gallery-soft/heatmap/example.html +3 -0
  360. package/packs/gallery-soft/heatmap/kit.js +498 -0
  361. package/packs/gallery-soft/heatmap/preview.svg +1 -0
  362. package/packs/gallery-soft/heatmap/style.css +181 -0
  363. package/packs/gallery-soft/heatmap/template.html +3 -0
  364. package/packs/gallery-soft/heatmap/writing.md +23 -0
  365. package/packs/gallery-soft/note/component.json +14 -0
  366. package/packs/gallery-soft/note/example.html +3 -0
  367. package/packs/gallery-soft/note/kit.js +162 -0
  368. package/packs/gallery-soft/note/preview.svg +1 -0
  369. package/packs/gallery-soft/note/style.css +148 -0
  370. package/packs/gallery-soft/note/template.html +3 -0
  371. package/packs/gallery-soft/note/writing.md +20 -0
  372. package/packs/gallery-soft/pack.json +8 -0
  373. package/packs/gallery-soft/recent/component.json +13 -0
  374. package/packs/gallery-soft/recent/example.html +1 -0
  375. package/packs/gallery-soft/recent/kit.js +335 -0
  376. package/packs/gallery-soft/recent/preview.svg +1 -0
  377. package/packs/gallery-soft/recent/style.css +160 -0
  378. package/packs/gallery-soft/recent/template.html +1 -0
  379. package/packs/gallery-soft/recent/writing.md +17 -0
  380. package/packs/gallery-soft/static/component.json +13 -0
  381. package/packs/gallery-soft/static/example.html +12 -0
  382. package/packs/gallery-soft/static/preview.svg +1 -0
  383. package/packs/gallery-soft/static/style.css +186 -0
  384. package/packs/gallery-soft/static/template.html +1 -0
  385. package/packs/gallery-soft/static/writing.md +20 -0
  386. package/packs/gallery-soft/stats/component.json +13 -0
  387. package/packs/gallery-soft/stats/example.html +1 -0
  388. package/packs/gallery-soft/stats/kit.js +341 -0
  389. package/packs/gallery-soft/stats/preview.svg +1 -0
  390. package/packs/gallery-soft/stats/style.css +119 -0
  391. package/packs/gallery-soft/stats/template.html +1 -0
  392. package/packs/gallery-soft/stats/writing.md +13 -0
  393. package/packs/gallery-soft/today/component.json +12 -0
  394. package/packs/gallery-soft/today/example.html +1 -0
  395. package/packs/gallery-soft/today/kit.js +222 -0
  396. package/packs/gallery-soft/today/preview.svg +1 -0
  397. package/packs/gallery-soft/today/style.css +178 -0
  398. package/packs/gallery-soft/today/template.html +1 -0
  399. package/packs/gallery-soft/today/writing.md +16 -0
  400. package/packs/gallery-soft/todos/component.json +19 -0
  401. package/packs/gallery-soft/todos/example.html +3 -0
  402. package/packs/gallery-soft/todos/kit.js +527 -0
  403. package/packs/gallery-soft/todos/preview.svg +1 -0
  404. package/packs/gallery-soft/todos/style.css +212 -0
  405. package/packs/gallery-soft/todos/template.html +3 -0
  406. package/packs/gallery-soft/todos/writing.md +32 -0
  407. package/packs/gallery-soft/weather/component.json +19 -0
  408. package/packs/gallery-soft/weather/example.html +3 -0
  409. package/packs/gallery-soft/weather/kit.js +411 -0
  410. package/packs/gallery-soft/weather/preview.svg +1 -0
  411. package/packs/gallery-soft/weather/style.css +185 -0
  412. package/packs/gallery-soft/weather/template.html +3 -0
  413. package/packs/gallery-soft/weather/writing.md +28 -0
  414. package/release.json +12 -10
  415. package/sdk/doc-kit.d.ts +396 -5
  416. package/sdk/doc-state.d.ts +2 -0
  417. package/skill/SKILL.md +56 -23
  418. package/skill/references/block-script.md +140 -0
  419. package/skill/references/building-blocks.md +257 -0
  420. package/skill/references/extensions.md +59 -0
  421. package/skill/references/pages-and-data.md +225 -0
  422. package/skill/references/writing-html.md +8 -4
  423. package/skill/sdk/doc-kit.d.ts +396 -5
  424. package/skill/sdk/doc-state.d.ts +2 -0
  425. package/web/assets/index-BM_K-Auu.css +1 -0
  426. package/web/assets/index-DRslOBiv.js +15 -0
  427. package/web/index.html +2 -2
  428. package/dist/chunk-3HIRPVLU.mjs +0 -2
  429. package/dist/chunk-5MHMDLZD.mjs +0 -4
  430. package/dist/chunk-64K7IPLO.mjs +0 -255
  431. package/dist/chunk-6QXGZM36.mjs +0 -8
  432. package/dist/chunk-6RGJOMJP.mjs +0 -16
  433. package/dist/chunk-AJ73XJF3.mjs +0 -2
  434. package/dist/chunk-AUZGSZJH.mjs +0 -13
  435. package/dist/chunk-BFE45AKL.mjs +0 -2
  436. package/dist/chunk-BXO2ZY5P.mjs +0 -2
  437. package/dist/chunk-CEH4MRCC.mjs +0 -2
  438. package/dist/chunk-CY2FNNAB.mjs +0 -16
  439. package/dist/chunk-DFUDV4LX.mjs +0 -2
  440. package/dist/chunk-DKXG4XDG.mjs +0 -2
  441. package/dist/chunk-EZQ34M3F.mjs +0 -124
  442. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  443. package/dist/chunk-FUKQNOQS.mjs +0 -9
  444. package/dist/chunk-JPOSLGOC.mjs +0 -6
  445. package/dist/chunk-JVHHHGEA.mjs +0 -3
  446. package/dist/chunk-L3QKH4OK.mjs +0 -26
  447. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  448. package/dist/chunk-LEF6DRCK.mjs +0 -59
  449. package/dist/chunk-NO5NJCHO.mjs +0 -2
  450. package/dist/chunk-NQPVAU63.mjs +0 -2
  451. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  452. package/dist/chunk-O35FIXRB.mjs +0 -2
  453. package/dist/chunk-P2CAZPDP.mjs +0 -3
  454. package/dist/chunk-PL6GHLGD.mjs +0 -15
  455. package/dist/chunk-QJIR2BLP.mjs +0 -6
  456. package/dist/chunk-QUPLVPB2.mjs +0 -7
  457. package/dist/chunk-R7BNENLY.mjs +0 -4
  458. package/dist/chunk-S6O4UHLC.mjs +0 -2
  459. package/dist/chunk-SGJWQKEI.mjs +0 -2
  460. package/dist/chunk-TX6JH7TR.mjs +0 -2
  461. package/dist/chunk-TYK6VNAB.mjs +0 -10
  462. package/dist/chunk-UALRNVSE.mjs +0 -4
  463. package/dist/chunk-V3HXVQEM.mjs +0 -2
  464. package/dist/chunk-V5VXUJKG.mjs +0 -2
  465. package/dist/chunk-VBXMADMP.mjs +0 -2
  466. package/dist/chunk-VNV55RUA.mjs +0 -2
  467. package/dist/chunk-VPD2PS35.mjs +0 -2
  468. package/dist/chunk-W4VKN3GF.mjs +0 -7
  469. package/dist/chunk-W5BXXPDJ.mjs +0 -3
  470. package/dist/chunk-XUNHRCOG.mjs +0 -6
  471. package/skill/references/building-components.md +0 -367
  472. package/web/assets/index-Bh74DfsV.css +0 -1
  473. package/web/assets/index-UW0RyFFt.js +0 -13
@@ -0,0 +1,272 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import fs from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { writeAtomic } from "./files.mjs";
7
+ import { userDirectory } from "./user-directory.mjs";
8
+ import { layoutOf, markerOf } from "./workspace.mjs";
9
+
10
+ // The registry of the workspaces used on this machine (notes/plans/workspaces-links-plan-2026-11-08.md
11
+ // §2.3): one file in the user's data folder (../shared/user-directory.mjs "data": %LOCALAPPDATA%\gallery
12
+ // on Windows, ~/Library/Application Support/gallery on macOS, $XDG_STATE_HOME/gallery or
13
+ // ~/.local/state/gallery elsewhere; GALLERY_DATA_DIR in its place, which the tests set), so that a
14
+ // workspace an Agent made in a project's folder is found from any other (gallery workspaces, gallery
15
+ // list --all, and the sidebar's columns of other workspaces):
16
+ // { "version": 1, "workspaces": [{ "root": "<absolute path>", "seen": "<ISO time>" }] }
17
+ // A workspace is registered once a gallery command has found it (src/cli/gallery.mjs: every command
18
+ // but stop and skill; init and workspaces --name once they made it) and when its server starts
19
+ // (src/server/routes.mjs listening), `seen` written again an hour later at the soonest; never one in
20
+ // the system's temporary folder (the tests', a trial's, a check's), nor a folder whose gallery.json is
21
+ // not Gallery's. A path is one entry however it is spelt (upper and lower case alike on Windows). A
22
+ // change is made under the registry's lock (workspaces.json.lock), on the file as it is then, and
23
+ // written whole to a file beside it renamed over it, so that two commands registering at once each
24
+ // keep the other's (updateRegistry). A workspace whose folder
25
+ // is gone stays registered (an external drive comes back) until gallery workspaces --forget forgets it.
26
+ // Each workspace has a short id that never changes while its path does not: ws- and six hex digits of
27
+ // the hash of its path (workspaceId), for the sidebar's places and addresses.
28
+
29
+ export const REGISTRY_FILE = "workspaces.json";
30
+ /** How long a registration's `seen` is believed before a command writes it again. */
31
+ export const SEEN_EVERY = 60 * 60 * 1000;
32
+
33
+ /** The registry's file: <data folder>/workspaces.json. Only a path. */
34
+ export const registryFile = (options = {}) => path.join(userDirectory("data", options), REGISTRY_FILE);
35
+
36
+ /** A workspace's path as one key, however it is spelt: absolute, and lower case on Windows. */
37
+ export const workspaceKey = (root, platform = process.platform) => (platform === "win32" ? path.resolve(root).toLowerCase() : path.resolve(root));
38
+
39
+ /** The workspace's short id: ws- and the first six hex digits of the SHA-256 of its key. */
40
+ export const workspaceId = (root, platform = process.platform) => `ws-${createHash("sha256").update(workspaceKey(root, platform)).digest("hex").slice(0, 6)}`;
41
+
42
+ /** Whether `value` names a workspace's id (ws- and six hex digits). */
43
+ export const isWorkspaceId = (value) => /^ws-[0-9a-f]{6}$/.test(String(value ?? ""));
44
+
45
+ /**
46
+ * The registry as `text` (the file's) holds it: { version: 1, workspaces: [{ root, seen }] }, each
47
+ * path absolute and once (the latest `seen` of two spellings kept), in the file's order; an entry that
48
+ * is not one left out, a file that does not read an empty registry.
49
+ */
50
+ export function registryOf(text, platform = process.platform) {
51
+ let stored = null;
52
+ try {
53
+ stored = JSON.parse(String(text ?? "").replace(/^/, ""));
54
+ } catch {}
55
+ const workspaces = [];
56
+ const at = new Map();
57
+ for (const entry of Array.isArray(stored?.workspaces) ? stored.workspaces : []) {
58
+ if (typeof entry?.root !== "string" || !path.isAbsolute(entry.root)) continue;
59
+ const seen = typeof entry.seen === "string" && Number.isFinite(Date.parse(entry.seen)) ? entry.seen : new Date(0).toISOString();
60
+ const key = workspaceKey(entry.root, platform);
61
+ if (!at.has(key)) {
62
+ at.set(key, workspaces.length);
63
+ workspaces.push({ root: path.resolve(entry.root), seen });
64
+ } else if (Date.parse(seen) > Date.parse(workspaces[at.get(key)].seen)) workspaces[at.get(key)].seen = seen;
65
+ }
66
+ return { version: 1, workspaces };
67
+ }
68
+
69
+ /** The registry as read now (registryOf of its file; none there: an empty one). */
70
+ export function readRegistry({ file = registryFile() } = {}) {
71
+ try {
72
+ return registryOf(readFileSync(file, "utf8"));
73
+ } catch {
74
+ return { version: 1, workspaces: [] };
75
+ }
76
+ }
77
+
78
+ // The registry's lock (workspaces.json.lock beside it): a file one process alone can make ("wx"),
79
+ // removed after; one older than LOCK_STALE was left by a process that ended and is taken over, and so is
80
+ // one held past LOCK_WAIT (a writer holds it for a read and a rename: a command never waits long).
81
+ // EPERM is a lock being removed (Windows: a file whose removal is pending) and a folder that cannot be
82
+ // written as well (Windows' rights, the sandbox of macOS an Agent may run in, which allows writes in its
83
+ // project alone): where no lock is there, LOCK_REFUSED of them in a row are the folder's, thrown at once,
84
+ // so that a command in such a sandbox is not held LOCK_WAIT twice over by every registration.
85
+ const LOCK_STALE = 10_000;
86
+ const LOCK_WAIT = 3_000;
87
+ const LOCK_REFUSED = 3;
88
+ async function withLock(file, task) {
89
+ const lock = `${file}.lock`;
90
+ const mine = `${process.pid} ${Math.random().toString(16).slice(2)}`;
91
+ await fs.mkdir(path.dirname(file), { recursive: true });
92
+ for (let refused = 0, until = Date.now() + LOCK_WAIT; ;) {
93
+ try {
94
+ await fs.writeFile(lock, mine, { flag: "wx" });
95
+ break;
96
+ } catch (error) {
97
+ if (error.code !== "EEXIST" && error.code !== "EPERM") throw error;
98
+ // The lock's age; null where there is none (removed meanwhile, or never made: a folder refusing it).
99
+ const age = await fs.stat(lock).then(({ mtimeMs }) => Date.now() - mtimeMs, (statError) => (statError.code === "ENOENT" ? null : 0));
100
+ refused = error.code === "EPERM" && age === null ? refused + 1 : 0;
101
+ if (refused >= LOCK_REFUSED) throw error; // a folder that cannot be written
102
+ if (Date.now() > until + LOCK_WAIT) throw error; // neither made nor taken over
103
+ if (age !== null && (age > LOCK_STALE || Date.now() > until)) await fs.rm(lock, { force: true }).catch(() => {});
104
+ await new Promise((resolve) => setTimeout(resolve, 10 + Math.random() * 20));
105
+ }
106
+ }
107
+ try {
108
+ return await task();
109
+ } finally {
110
+ if ((await fs.readFile(lock, "utf8").catch(() => null)) === mine) await fs.rm(lock, { force: true }).catch(() => {});
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Change the registry: `change(registry)` gives the registry to write, or null for nothing to write.
116
+ * Under the registry's lock, so that two commands writing at once each make their change on what the
117
+ * other wrote: read, changed and written whole (a file beside it, renamed over it), made again on what
118
+ * the file holds when it changed between the read and the rename all the same (a lock taken over).
119
+ * Resolves to the registry as it stands after.
120
+ */
121
+ export async function updateRegistry(change, { file = registryFile() } = {}) {
122
+ return withLock(file, async () => {
123
+ for (let tries = 0; tries < 8; tries++) {
124
+ const text = await fs.readFile(file, "utf8").catch((error) => (error.code === "ENOENT" ? null : Promise.reject(error)));
125
+ const current = registryOf(text);
126
+ const next = change(current);
127
+ if (!next) return current;
128
+ if (await writeAtomic(file, `${JSON.stringify({ version: 1, workspaces: next.workspaces }, null, 2)}\n`, { expect: text })) return next;
129
+ }
130
+ throw new Error(`${file} kept changing while it was written: try again.`);
131
+ });
132
+ }
133
+
134
+ // The system's temporary folders, each as its path is written and as it really is (macOS's /var is
135
+ // /private/var, its /tmp /private/tmp): os.tmpdir()'s (`tmp`), and /tmp but on Windows, for macOS's
136
+ // os.tmpdir() is a folder of the user's under /var/folders while Agents' trials go to /tmp, which a
137
+ // restart clears.
138
+ function temporaryFolders(tmp, platform) {
139
+ const folders = [];
140
+ for (const folder of [tmp, ...(platform === "win32" ? [] : ["/tmp"])]) {
141
+ folders.push(path.resolve(folder));
142
+ try {
143
+ folders.push(realpathSync.native(folder));
144
+ } catch {}
145
+ }
146
+ return folders;
147
+ }
148
+
149
+ /** Whether `root` is in one of the system's temporary folders (`tmp`, os.tmpdir()'s, and /tmp but on Windows), or is one. */
150
+ export function isTemporary(root, { tmp = os.tmpdir(), platform = process.platform } = {}) {
151
+ const roots = [path.resolve(root)];
152
+ try {
153
+ roots.push(realpathSync.native(root));
154
+ } catch {}
155
+ return temporaryFolders(tmp, platform).some((folder) => {
156
+ const base = workspaceKey(folder, platform);
157
+ return roots.some((each) => {
158
+ const key = workspaceKey(each, platform);
159
+ return key === base || key.startsWith(base.endsWith(path.sep) ? base : `${base}${path.sep}`);
160
+ });
161
+ });
162
+ }
163
+
164
+ /** Whether the folder `root` is a workspace now: its gallery.json there, and Gallery's. */
165
+ export function isWorkspace(root) {
166
+ try {
167
+ return markerOf(readFileSync(layoutOf(root).marker, "utf8")).ours;
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Register the workspace at `root`: added with `seen` now, or its `seen` written again when it is
175
+ * SEEN_EVERY old (`force`: whatever its age). Nothing is written for a folder in the system's
176
+ * temporary folder or one that is no workspace of Gallery's. Resolves to "added", "seen", "fresh" (its
177
+ * `seen` recent: nothing written), "temporary" or "none" (no workspace there).
178
+ */
179
+ export async function registerWorkspace(root, { file = registryFile(), now = Date.now(), tmp = os.tmpdir(), force = false } = {}) {
180
+ const resolved = path.resolve(root);
181
+ if (isTemporary(resolved, { tmp })) return "temporary";
182
+ if (!isWorkspace(resolved)) return "none";
183
+ const key = workspaceKey(resolved);
184
+ const recent = (entry) => {
185
+ const age = now - Date.parse(entry.seen);
186
+ return !force && age >= 0 && age < SEEN_EVERY;
187
+ };
188
+ // Seen within the hour, as most commands find it: read alone, no lock taken.
189
+ const known = readRegistry({ file }).workspaces.find((each) => workspaceKey(each.root) === key);
190
+ if (known && recent(known)) return "fresh";
191
+ let did = "fresh";
192
+ await updateRegistry((registry) => {
193
+ const seen = new Date(now).toISOString();
194
+ const entry = registry.workspaces.find((each) => workspaceKey(each.root) === key);
195
+ if (!entry) {
196
+ did = "added";
197
+ return { version: 1, workspaces: [...registry.workspaces, { root: resolved, seen }] };
198
+ }
199
+ if (recent(entry)) {
200
+ did = "fresh";
201
+ return null;
202
+ }
203
+ did = "seen";
204
+ return { version: 1, workspaces: registry.workspaces.map((each) => (each === entry ? { root: each.root, seen } : each)) };
205
+ }, { file });
206
+ return did;
207
+ }
208
+
209
+ /** registerWorkspace for a command or a server, which never fails for it: a registry that cannot be read or written is passed over ("failed"). */
210
+ export const registerQuietly = (root, options) => registerWorkspace(root, options).catch(() => "failed");
211
+
212
+ /**
213
+ * Forget the registered workspaces `target` names (a path, as the registry holds it in any spelling, or
214
+ * a ws- id): nothing on disk but the registry changes. Resolves to the roots forgotten ([] for none).
215
+ */
216
+ export async function forgetWorkspace(target, { file = registryFile(), cwd = process.cwd() } = {}) {
217
+ const byId = isWorkspaceId(target);
218
+ const key = byId ? null : workspaceKey(path.resolve(cwd, target));
219
+ const named = (entry) => (byId ? workspaceId(entry.root) === target : workspaceKey(entry.root) === key);
220
+ let forgotten = [];
221
+ await updateRegistry((registry) => {
222
+ forgotten = registry.workspaces.filter(named).map((entry) => entry.root);
223
+ return forgotten.length ? { version: 1, workspaces: registry.workspaces.filter((entry) => !named(entry)) } : null;
224
+ }, { file });
225
+ return forgotten;
226
+ }
227
+
228
+ /**
229
+ * The folders a scan passes over by name (upper and lower case alike), besides every dot folder: a
230
+ * package's and a repository's own, and Windows' own on a drive, its recycle bin among them (a workspace
231
+ * thrown away there is no workspace to find).
232
+ */
233
+ export const SCAN_SKIPS = new Set(["node_modules", ".git", "$recycle.bin", "recycler", "system volume information"]);
234
+ /** How deep a scan looks below its folder, and how many folders it reads at most. */
235
+ export const SCAN_DEPTH = 6;
236
+ export const SCAN_LIMIT = 50_000;
237
+
238
+ /**
239
+ * The workspaces in `folder` and below it, `depth` levels down at most (gallery workspaces --scan):
240
+ * each folder whose gallery.json is Gallery's, sorted, none looked in further (a workspace holds
241
+ * documents, not workspaces). Passed over: dot folders, those SCAN_SKIPS names (node_modules, the
242
+ * recycle bin), links (they lead out of it) and folders that cannot be read. Resolves to { found:
243
+ * [root], read: folders read, cut: whether it stopped at `limit` folders }.
244
+ */
245
+ export async function scanForWorkspaces(folder, { depth = SCAN_DEPTH, limit = SCAN_LIMIT } = {}) {
246
+ const found = [];
247
+ let read = 0;
248
+ let cut = false;
249
+ let level = [path.resolve(folder)];
250
+ for (let at = 0; at <= depth && level.length && !cut; at++) {
251
+ const next = [];
252
+ for (const directory of level) {
253
+ if (read >= limit) {
254
+ cut = true;
255
+ break;
256
+ }
257
+ read++;
258
+ if (isWorkspace(directory)) {
259
+ found.push(directory);
260
+ continue;
261
+ }
262
+ if (at === depth) continue;
263
+ const entries = await fs.readdir(directory, { withFileTypes: true }).catch(() => []);
264
+ for (const entry of entries) {
265
+ if (!entry.isDirectory() || entry.name.startsWith(".") || SCAN_SKIPS.has(entry.name.toLowerCase())) continue;
266
+ next.push(path.join(directory, entry.name));
267
+ }
268
+ }
269
+ level = next;
270
+ }
271
+ return { found: found.sort(), read, cut };
272
+ }
@@ -4,6 +4,8 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
4
4
  import fs from "node:fs/promises";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
+ import { DOC_SLUG, earlierId, newPageId, pageId } from "./page-links.mjs";
8
+ import { LINK_FILE, readLinkText } from "./web-links.mjs";
7
9
 
8
10
  // A workspace: the folder of the user's documents (docs/internals.md, "Where things live"). Found in
9
11
  // this order: --workspace <dir> (the gallery command sets GALLERY_WORKSPACE from it before it loads
@@ -11,20 +13,26 @@ import path from "node:path";
11
13
  // one holding a gallery.json that says it is ours ({ "gallery": "workspace", "version": 1 }: another
12
14
  // program's gallery.json is not), else Gallery in the user's Documents folder (~/Documents/Gallery:
13
15
  // documentsFolder() says which folder that is on each system). Its root shows the user's documents,
14
- // gallery.json, preferences.md (写作偏好), exports/ and components/ only:
16
+ // gallery.json, preferences.md (写作偏好), exports/, components/, pages/ and extensions/ only:
15
17
  //
16
18
  // <workspace>/gallery.json, preferences.md, exports/<slug>.html (the offline deliverables)
17
19
  // <workspace>/components/<scope>/<name>/ a component package (./component-catalog.mjs), no document
20
+ // <workspace>/pages/<slug>/page.html, page.json a custom page (./pages.mjs), no document
21
+ // <workspace>/extensions/<id>/sidebar.json the sidebar column an extension provides (./extensions.mjs), no document
18
22
  // <workspace>/<slug>/doc.html, doc.json (report.html, report.json in a document made before
19
23
  // Gallery 0.1: read and written under those names, never renamed), assets/, materials/, annotations.json
24
+ // <workspace>/<slug>/link.json a web link of the sidebar (./web-links.mjs), no document
20
25
  // <workspace>/.gallery/ everything else Gallery keeps: the sidebar's outline, the trash, the emoji
21
26
  // library, the 写作偏好 page (pages/<slug>/), the edit state (state/), qa's pictures, backups, built
22
- // previews, the server's announcement, port and log, and what was adopted of an old layout.
27
+ // previews, the server's announcement, port and log, what was adopted of an old layout, and the
28
+ // scratch copies gallery block try makes a trial on (try/try-<8 hex>/, each gone once its trial ends).
23
29
  //
24
30
  // A document is a folder at the root holding doc.html or report.html, its name a slug that is not
25
31
  // reserved (exports, generated, which the preview's addresses keep, the names Windows keeps for
26
- // devices: a workspace moves between systems) and not a dot name. components/ holds no document, but
27
- // one of that name made before the packages came stays one, and no new one takes it (COMPONENTS_FOLDER).
32
+ // devices: a workspace moves between systems) and not a dot name. components/, pages/ and extensions/
33
+ // hold no document, but one of those names made before they came stays one, and no new one takes it
34
+ // (COMPONENTS_FOLDER, PAGES_FOLDER, EXTENSIONS_FOLDER). A custom page's slug and a document's are one pool: no new
35
+ // document takes a page's slug, nor a new page a document's (slugTaken).
28
36
  // Nothing here writes outside the workspace, and nothing writes an old folder it copies from.
29
37
  // Node's built-ins only: every system reads its layout from here.
30
38
 
@@ -50,12 +58,27 @@ export const RESERVED = new Set(["exports", "generated", ...DEVICE_NAMES]);
50
58
  * name no new document takes (slugTaken, keptName), the next free one coming instead.
51
59
  */
52
60
  export const COMPONENTS_FOLDER = "components";
61
+ /**
62
+ * The folder of a workspace's custom pages (pages/<slug>/: ./pages.mjs). As components/, it hides
63
+ * nothing: a document called so, made before the custom pages came, keeps its folder and stays a
64
+ * document beside them; it is only a name no new document or page takes.
65
+ */
66
+ export const PAGES_FOLDER = "pages";
67
+ /**
68
+ * The folder of a workspace's extensions (extensions/<id>/: ./extensions.mjs), each of which may provide a
69
+ * column of the sidebar (sidebar.json) and holds whatever else the extension keeps. As components/ and
70
+ * pages/, it hides nothing: a document called so, made before extensions came, keeps its folder and stays
71
+ * a document beside them; it is only a name no new document or page takes.
72
+ */
73
+ export const EXTENSIONS_FOLDER = "extensions";
53
74
  /** A component package's manifest, which is what makes a folder of components/ a package. */
54
75
  export const PACKAGE_MANIFEST = "component.json";
55
- /** The folders a document keeps beside its files: what is its own of a folder it shares with the packages. */
76
+ /** The folders a document keeps beside its files: what is its own of a folder it shares with the packages or the custom pages. */
56
77
  export const DOCUMENT_FOLDERS = ["assets", "materials"];
57
- /** Whether a new document may not be called `name`, whatever the workspace holds. */
58
- export const keptName = (name) => RESERVED.has(name) || name === COMPONENTS_FOLDER;
78
+ /** The folders at the root that hold no document but may share their name with one made before them. */
79
+ export const SHARED_FOLDERS = [COMPONENTS_FOLDER, PAGES_FOLDER, EXTENSIONS_FOLDER];
80
+ /** Whether a new document (or custom page) may not be called `name`, whatever the workspace holds. */
81
+ export const keptName = (name) => RESERVED.has(name) || SHARED_FOLDERS.includes(name);
59
82
  /** Whether `name` may be a document's slug. */
60
83
  export const isSlugName = (name) => typeof name === "string" && slugPattern.test(name) && !RESERVED.has(name);
61
84
  /** The slug of the 写作偏好 page made new (one copied from an old layout keeps its own). */
@@ -98,6 +121,15 @@ const markerAt = (file) => {
98
121
  }
99
122
  };
100
123
 
124
+ /**
125
+ * The default workspace, where a command goes when nothing names one and none is found up from the
126
+ * current folder: Gallery in the user's Documents folder, or the folder GALLERY_DEFAULT_WORKSPACE names
127
+ * (the browser scenarios', whose studio is the default workspace for a draft made elsewhere: src/shared/drafts.mjs).
128
+ */
129
+ export const defaultWorkspace = ({ env = process.env, home = os.homedir(), cwd = process.cwd() } = {}) => (env.GALLERY_DEFAULT_WORKSPACE
130
+ ? path.resolve(cwd, env.GALLERY_DEFAULT_WORKSPACE)
131
+ : path.join(documentsFolder({ home, env }), "Gallery"));
132
+
101
133
  /**
102
134
  * Where the workspace is: { root, how } (how: "flag", "env", "found" or "default"). Reads gallery.json
103
135
  * files on the way up, writes nothing (ensureWorkspace makes it).
@@ -109,7 +141,7 @@ export function findWorkspace({ flag = null, env = process.env, cwd = process.cw
109
141
  if (markerAt(path.join(directory, WORKSPACE_FILE)).ours) return { root: directory, how: "found" };
110
142
  if (path.dirname(directory) === directory) break;
111
143
  }
112
- return { root: path.join(documentsFolder({ home, env }), "Gallery"), how: "default" };
144
+ return { root: defaultWorkspace({ env, home, cwd }), how: "default" };
113
145
  }
114
146
 
115
147
  // Where Windows keeps the Documents folder Explorer shows as 文档: the Personal shell folder, as the
@@ -193,14 +225,34 @@ export function layoutOf(root) {
193
225
  preferences: path.join(resolved, "preferences.md"),
194
226
  exports: path.join(resolved, "exports"),
195
227
  components: path.join(resolved, COMPONENTS_FOLDER),
228
+ // The custom pages (./pages.mjs), at the root as the packages are: the user's and the Agent's. Not
229
+ // called pages, which named .gallery/pages/ before (preferencesPage now): code written for that
230
+ // meaning finds no such key and fails, never reading this folder for the 写作偏好 page's.
231
+ customPages: path.join(resolved, PAGES_FOLDER),
232
+ // The extensions (./extensions.mjs: extensions/<id>/sidebar.json), the user's and the Agent's.
233
+ extensions: path.join(resolved, EXTENSIONS_FOLDER),
196
234
  gallery,
197
235
  outline: path.join(gallery, "outline.json"),
236
+ blocks: path.join(gallery, "blocks.json"),
237
+ // What the reader closed of Gallery's own notes, once for the workspace: the home page's first-open
238
+ // note (src/shared/pages.mjs homeHintClosed).
239
+ hints: path.join(gallery, "hints.json"),
240
+ // The keys of the sites the workspace's blocks reach, by host (./net-keys.mjs: gallery block key),
241
+ // never in gallery.json, whose settings every page is told; and what the net route fetched for them
242
+ // (./net-cache.mjs), which may be thrown away.
243
+ keys: path.join(gallery, "keys.json"),
244
+ netCache: path.join(gallery, "cache", "net"),
198
245
  trash: path.join(gallery, "trash"),
199
246
  emojiLibrary: path.join(gallery, "emoji-library"),
200
- pages: path.join(gallery, "pages"),
247
+ // The 写作偏好 page's folder (.gallery/pages/<slug>/: a document of Gallery's own, never a custom page).
248
+ preferencesPage: path.join(gallery, "pages"),
201
249
  state,
250
+ // The workspace's record of edits by day (src/workbench/activity.mjs): what the 编辑热力图 shows; losable.
251
+ activity: path.join(state, "activity.json"),
202
252
  history: path.join(state, "history"),
203
253
  qa: path.join(gallery, "qa"),
254
+ // The scratch copies gallery block try makes a trial on (try-<8 hex>/: ownOrScratch).
255
+ scratch: path.join(gallery, "try"),
204
256
  backups: path.join(gallery, "backups"),
205
257
  built: path.join(gallery, "built"),
206
258
  logs: path.join(gallery, "logs"),
@@ -252,17 +304,81 @@ export function documentFiles(directory) {
252
304
  };
253
305
  }
254
306
 
255
- /** Where document `slug` is: the 写作偏好 page's folder under .gallery/pages/ when it is one, else <root>/<slug>. */
307
+ /** Where document `slug` is: the 写作偏好 page's folder under .gallery/pages/ when it is one, else ownOrScratch's. */
256
308
  export function documentDirectory(slug, root) {
257
- const page = path.join(layoutOf(root).pages, slug);
258
- return existsSync(page) ? page : path.join(path.resolve(root), slug);
309
+ const page = path.join(layoutOf(root).preferencesPage, slug);
310
+ return existsSync(page) ? page : ownOrScratch(slug, root);
311
+ }
312
+
313
+ /**
314
+ * Where document `slug` that is no 写作偏好 page is: <root>/<slug>; or, where no folder at the root has
315
+ * that name, a scratch copy's in .gallery/try/ (gallery block try's, try-<8 hex>: served and edited
316
+ * as any document, listed nowhere, and gone once its trial ends, src/cli/component-try.mjs).
317
+ */
318
+ function ownOrScratch(slug, root) {
319
+ const own = path.join(path.resolve(root), slug);
320
+ const copy = path.join(layoutOf(root).scratch, slug);
321
+ return !existsSync(own) && existsSync(copy) ? copy : own;
259
322
  }
260
323
 
261
- /** Whether `slug` can name no new document: a name kept, or held by a folder at the root or a page. */
324
+ /**
325
+ * Whether `slug` can name no new document or custom page (one pool): a name kept, or held by a folder
326
+ * at the root, the 写作偏好 page or a custom page (pages/<slug>/).
327
+ */
262
328
  export function slugTaken(slug, root) {
263
- return keptName(slug) || existsSync(path.join(path.resolve(root), slug)) || existsSync(path.join(layoutOf(root).pages, slug));
329
+ const layout = layoutOf(root);
330
+ return keptName(slug) || [layout.root, layout.preferencesPage, layout.customPages].some((folder) => existsSync(path.join(folder, slug)));
331
+ }
332
+
333
+ /**
334
+ * The folder names a page id may come of in a workspace, every one that has an address: a folder at
335
+ * the root (a document, a web link, whatever else), a custom page's, the 写作偏好 page's, and the slug
336
+ * of everything in the trash (a document there goes by its id until it is restored).
337
+ */
338
+ export async function pageNames(root) {
339
+ const layout = layoutOf(root);
340
+ const names = new Set();
341
+ await Promise.all([layout.root, layout.preferencesPage, layout.customPages].map(async (folder) => {
342
+ for (const entry of await fs.readdir(folder, { withFileTypes: true }).catch(() => [])) if (entry.isDirectory() && !entry.name.startsWith(".")) names.add(entry.name);
343
+ }));
344
+ for (const { slug } of await listTrash(root)) if (typeof slug === "string") names.add(slug);
345
+ return names;
346
+ }
347
+
348
+ /**
349
+ * The folder name of a new document: doc-<a new page id> (DOC_SLUG), an id that no name of the
350
+ * workspace has (pageNames: the hash id of an old name counts as the id of a doc-<id> one, and the
351
+ * earlier id of a doc-<eight> name, which its old addresses still open), its folder
352
+ * free (slugTaken: the root, the 写作偏好 page and the custom pages; the trash's slugs are among the
353
+ * names). `random` is newPageId's, for the tests.
354
+ * @param {string} root
355
+ * @param {{ random?: () => number }} [options]
356
+ */
357
+ export async function newDocSlug(root, { random = undefined } = {}) {
358
+ const taken = new Set();
359
+ for (const name of await pageNames(root)) for (const id of [pageId(name), earlierId(name)]) if (id !== null) taken.add(id);
360
+ for (;;) {
361
+ const slug = `doc-${newPageId(taken, random)}`;
362
+ if (!slugTaken(slug, root)) return slug;
363
+ taken.add(slug.slice(4));
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Another name of the workspace that has the page id of `slug` (pageNames), or null: what makes a
369
+ * doc-<id> name a document may not take, its id being another's.
370
+ * @param {string} slug
371
+ * @param {string} root
372
+ */
373
+ export async function idHolder(slug, root) {
374
+ const id = pageId(slug);
375
+ for (const name of await pageNames(root)) if (name !== slug && pageId(name) === id) return name;
376
+ return null;
264
377
  }
265
378
 
379
+ /** Whether `slug` is a name the workbench's new documents have (doc-<eight>: DOC_SLUG), whose id is in it. */
380
+ export const isDocSlug = (slug) => DOC_SLUG.test(slug);
381
+
266
382
  // The folders right in `directory`, by name, sorted: no dot name, and no link, which leads out of it.
267
383
  const foldersIn = (directory) => {
268
384
  try {
@@ -325,20 +441,55 @@ export async function listDocumentSlugs(root) {
325
441
  return (await documentsIn(path.resolve(root))).filter((entry) => !entry.instructions).map((entry) => entry.slug);
326
442
  }
327
443
 
444
+ /**
445
+ * The web links of a workspace (./web-links.mjs), by slug, sorted: the folders at its root, named as
446
+ * documents are, holding a link.json that reads as a link and no doc.html or report.html (a document,
447
+ * whatever else it holds).
448
+ */
449
+ export async function listLinkSlugs(root) {
450
+ const directory = path.resolve(root);
451
+ const entries = await fs.readdir(directory, { withFileTypes: true }).catch(() => []);
452
+ const found = await Promise.all(entries
453
+ .filter((entry) => entry.isDirectory() && isSlugName(entry.name))
454
+ .map(async ({ name }) => {
455
+ const folder = path.join(directory, name);
456
+ if (existsSync(path.join(folder, NEW.html)) || existsSync(path.join(folder, OLD.html))) return null;
457
+ return (await readLink(folder)) ? name : null;
458
+ }));
459
+ return found.filter(Boolean).sort();
460
+ }
461
+
462
+ /** The web link of folder `folder` ({ url, title, icon? }), or null where there is none or it does not read. */
463
+ export async function readLink(folder) {
464
+ const text = await fs.readFile(path.join(folder, LINK_FILE), "utf8").catch(() => null);
465
+ return text === null ? null : readLinkText(text);
466
+ }
467
+
328
468
  /** The 写作偏好 page's slug (the first document under .gallery/pages/), or null when there is none yet. */
329
469
  export async function pageSlug(root) {
330
- return (await documentsIn(layoutOf(root).pages))[0]?.slug ?? null;
470
+ return (await documentsIn(layoutOf(root).preferencesPage))[0]?.slug ?? null;
331
471
  }
332
472
 
333
473
  /** The id of an entry of the trash, a folder of .gallery/trash/. */
334
474
  export const trashIdPattern = /^[a-z0-9]+-[a-z0-9]+$/;
335
475
 
336
- /** What the trash holds, the latest first: each folder's entry.json ({ id, slug, title, at, parent, index }). */
476
+ /**
477
+ * What a trash entry holds: a document, a custom page (./pages.mjs) or a web link (./web-links.mjs).
478
+ * An entry written before the custom pages came holds a document.
479
+ */
480
+ export const TRASH_KINDS = ["document", "page", "link"];
481
+
482
+ /**
483
+ * What the trash holds, the latest first: each folder's entry.json ({ id, kind, slug, title, at,
484
+ * parent, index }), `kind` "document" (its folder report/), "page" (its folder page/, parent "" and
485
+ * index -1: a custom page has no place in the outline) or "link" (its folder link/); one written before
486
+ * kinds came is a document's.
487
+ */
337
488
  export async function listTrash(root) {
338
489
  const { trash } = layoutOf(root);
339
490
  const names = await fs.readdir(trash).catch(() => []);
340
491
  const entries = await Promise.all(names.filter((name) => trashIdPattern.test(name)).map((id) => readJson(path.join(trash, id, "entry.json"))));
341
- return entries.filter(Boolean).sort((a, b) => b.at - a.at);
492
+ return entries.filter(Boolean).map((entry) => (TRASH_KINDS.includes(entry.kind) ? entry : { ...entry, kind: "document" })).sort((a, b) => b.at - a.at);
342
493
  }
343
494
 
344
495
  // ---------- copying an old layout ----------
@@ -423,8 +574,8 @@ export async function copyOldDocuments({ from, to, state = undefined }) {
423
574
  if (instructions) {
424
575
  if (await pageSlug(layout.root)) page = "kept";
425
576
  else {
426
- await fs.mkdir(layout.pages, { recursive: true });
427
- await copyTree(folder, path.join(layout.pages, slug));
577
+ await fs.mkdir(layout.preferencesPage, { recursive: true });
578
+ await copyTree(folder, path.join(layout.preferencesPage, slug));
428
579
  page = "copied";
429
580
  }
430
581
  continue;
@@ -514,7 +665,7 @@ export async function adoptOldLayout(root, { devState = null } = {}) {
514
665
  if ((await copyMissing(path.join(layout.root, ".trash"), layout.trash)).length) copied.push("trash");
515
666
  if ((await mergeEmojiLibrary(path.join(layout.root, ".emoji-library"), layout.emojiLibrary)) > 0) copied.push("emoji-library");
516
667
  const oldPage = (await documentsIn(layout.root)).find((entry) => entry.instructions);
517
- if (oldPage && !(await pageSlug(layout.root))) await once("page", oldPage.folder, path.join(layout.pages, oldPage.slug));
668
+ if (oldPage && !(await pageSlug(layout.root))) await once("page", oldPage.folder, path.join(layout.preferencesPage, oldPage.slug));
518
669
 
519
670
  if (devState && (await isFolder(devState))) {
520
671
  const slugs = [...(await listDocumentSlugs(layout.root)), ...(oldPage ? [oldPage.slug] : [])];
@@ -0,0 +1,2 @@
1
+ import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
2
+ import{constants as G}from"node:buffer";import E from"node:fs";import{Transform as H}from"node:stream";import{pipeline as P}from"node:stream/promises";import b from"node:zlib";import{refusal as k,said as M}from"../browser/shared/i18n/index.mjs";var A=67324752,Z=33639248,F=101010256,W=101075792,X=117853008,U=4294967295,T=2048;function v(t=new Date){let o=t.getHours()<<11|t.getMinutes()<<5|Math.floor(t.getSeconds()/2),e=Math.max(1980,t.getFullYear())-1980<<9|t.getMonth()+1<<5|t.getDate();return[o,e]}function tt(t){let[o,e]=v(),c=[],h=[],n=0;for(let a of t){let s=Buffer.isBuffer(a.data)?a.data:Buffer.from(a.data??"","utf8"),r=Buffer.from(a.name,"utf8"),u=s.length>64?b.deflateRawSync(s):null,w=u&&u.length<s.length?u:s,L=w===s?0:8,g=b.crc32(s);if(s.length>=U||n>=U)throw new Error("\u538B\u7F29\u5305\u592A\u5927\uFF08\u8D85\u8FC7 4GB\uFF09");let l=Buffer.alloc(30);l.writeUInt32LE(A,0),l.writeUInt16LE(20,4),l.writeUInt16LE(T,6),l.writeUInt16LE(L,8),l.writeUInt16LE(o,10),l.writeUInt16LE(e,12),l.writeUInt32LE(g,14),l.writeUInt32LE(w.length,18),l.writeUInt32LE(s.length,22),l.writeUInt16LE(r.length,26),l.writeUInt16LE(0,28);let d=Buffer.alloc(46);d.writeUInt32LE(Z,0),d.writeUInt16LE(20,4),d.writeUInt16LE(20,6),d.writeUInt16LE(T,8),d.writeUInt16LE(L,10),d.writeUInt16LE(o,12),d.writeUInt16LE(e,14),d.writeUInt32LE(g,16),d.writeUInt32LE(w.length,20),d.writeUInt32LE(s.length,24),d.writeUInt16LE(r.length,28),d.writeUInt32LE(n,42),c.push(l,r,w),h.push(d,r),n+=l.length+r.length+w.length}if(t.length>=65535)throw new Error("\u538B\u7F29\u5305\u91CC\u7684\u6587\u4EF6\u592A\u591A");let i=Buffer.concat(h),f=Buffer.alloc(22);return f.writeUInt32LE(F,0),f.writeUInt16LE(t.length,8),f.writeUInt16LE(t.length,10),f.writeUInt32LE(i.length,12),f.writeUInt32LE(n,16),Buffer.concat([...c,i,f])}var Y=new TextDecoder("utf-8",{fatal:!0}),j=new TextDecoder("gbk");function q(t,o){if(o&T)return t.toString("utf8");try{return Y.decode(t)}catch{return j.decode(t)}}var et=t=>t.length>=4&&t.readUInt32LE(0)===A,m=()=>new Error("ZIP \u6587\u4EF6\u5DF2\u635F\u574F"),I=(t,o)=>Number(t.readBigUInt64LE(o));function N(t,o){let e=Math.min(o,65557),c=t(o-e,e),h=-1;for(let r=c.length-22;r>=0;r-=1)if(c.readUInt32LE(r)===F){h=r;break}if(h<0)throw new Error("\u4E0D\u662F\u6709\u6548\u7684 ZIP \u6587\u4EF6");let n=c.readUInt16LE(h+10),i=c.readUInt32LE(h+12),f=c.readUInt32LE(h+16);if(n===65535||i===U||f===U){let r=t(o-e+h-20,20);if(r.readUInt32LE(0)!==X)throw m();let u=t(I(r,8),56);if(u.readUInt32LE(0)!==W)throw m();n=I(u,32),i=I(u,40),f=I(u,48)}let a=t(f,i),s=[];for(let r=0,u=0;u<n;u+=1){if(a.readUInt32LE(r)!==Z)throw m();let w=a.readUInt16LE(r+8),L=a.readUInt16LE(r+10),g=a.readUInt32LE(r+20),l=a.readUInt32LE(r+24),d=a.readUInt16LE(r+28),D=a.readUInt16LE(r+30),_=a.readUInt16LE(r+32),R=a.readUInt32LE(r+42),B=q(a.subarray(r+46,r+46+d),w),p=a.subarray(r+46+d,r+46+d+D);for(let x=0;x+4<=p.length;x+=4+p.readUInt16LE(x+2)){if(p.readUInt16LE(x)!==1)continue;let z=x+4;l===U&&(l=I(p,z),z+=8),g===U&&(g=I(p,z),z+=8),R===U&&(R=I(p,z))}if(r+=46+d+D+_,!B.endsWith("/")){if(w&1)throw k(M("{0} \u5DF2\u52A0\u5BC6",B));if(L!==0&&L!==8)throw k(M("{0}\uFF1A\u4E0D\u652F\u6301\u7684\u538B\u7F29\u65B9\u5F0F",B));s.push({name:B,method:L,packedSize:g,size:l,local:R})}}return s}var S=t=>k(M("{0}\uFF1A\u5927\u5C0F\u4E0D\u7B26",t.name),{code:"size"});function C(t,o){try{return b.inflateRawSync(t,{maxOutputLength:Math.min(G.MAX_LENGTH,Math.max(1,o.size))})}catch(e){throw e?.code==="ERR_BUFFER_TOO_LARGE"?S(o):e}}function O(t,o){let e=t(o.local,30);if(e.readUInt32LE(0)!==A)throw m();return o.local+30+e.readUInt16LE(26)+e.readUInt16LE(28)}function nt(t){let o=(e,c)=>{if(e<0||e+c>t.length)throw m();return t.subarray(e,e+c)};return N(o,t.length).map(e=>{let c=o(O(o,e),e.packedSize),h=e.method===0?Buffer.from(c):C(c,e);if(h.length!==e.size)throw S(e);return{name:e.name,data:h}})}function rt(t){let o=E.openSync(t,"r"),e=E.fstatSync(o).size,c=(n,i)=>{if(n<0||n+i>e)throw m();let f=Buffer.alloc(i);for(let a=0;a<i;){let s=E.readSync(o,f,a,i-a,n+a);if(s===0)throw m();a+=s}return f},h;try{h=N(c,e)}catch(n){throw E.closeSync(o),n}return{entries:h,read(n){let i=c(O(c,n),n.packedSize),f=n.method===0?i:C(i,n);if(f.length!==n.size)throw S(n);return f},async extract(n,i){let f=O(c,n);if(n.packedSize===0)await E.promises.writeFile(i,"");else{let a=E.createReadStream(t,{start:f,end:f+n.packedSize-1}),s=0,r=new H({transform(u,w,L){s+=u.length,L(s>n.size?S(n):null,u)}});await P(...n.method===0?[a]:[a,b.createInflateRaw()],r,E.createWriteStream(i))}if((await E.promises.stat(i)).size!==n.size)throw S(n)},close:()=>E.closeSync(o)}}export{tt as a,et as b,nt as c,rt as d};