@heycys/gallery 0.1.2 → 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 (292) 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/embed/page.mjs +1 -1
  5. package/browser/editor/components/file/page.mjs +4 -4
  6. package/browser/editor/components/heading/index.mjs +7 -6
  7. package/browser/editor/components/icons.mjs +3 -0
  8. package/browser/editor/components/image/index.mjs +2 -0
  9. package/browser/editor/components/image/page.mjs +172 -90
  10. package/browser/editor/components/installed/index.mjs +6 -2
  11. package/browser/editor/components/installed/page.mjs +80 -13
  12. package/browser/editor/components/media/icons.mjs +13 -8
  13. package/browser/editor/components/media/page.mjs +5 -3
  14. package/browser/editor/components/media/toolbar.mjs +12 -3
  15. package/browser/editor/components/paragraph/index.mjs +4 -3
  16. package/browser/editor/components/registry.mjs +17 -5
  17. package/browser/editor/components/table/index.mjs +4 -2
  18. package/browser/editor/page/after/index.mjs +288 -0
  19. package/browser/editor/page/align.mjs +91 -0
  20. package/browser/editor/page/beside.mjs +5 -6
  21. package/browser/editor/page/block-menu/index.mjs +6 -3
  22. package/browser/editor/page/blocks.mjs +3 -1
  23. package/browser/editor/page/board/hint.mjs +64 -27
  24. package/browser/editor/page/board/index.mjs +160 -71
  25. package/browser/editor/page/board/motion.mjs +177 -0
  26. package/browser/editor/page/board/settings.mjs +4 -3
  27. package/browser/editor/page/clipboard/index.mjs +10 -3
  28. package/browser/editor/page/clipboard/own/blocks.mjs +3 -4
  29. package/browser/editor/page/clipboard/own/describe.mjs +6 -2
  30. package/browser/editor/page/commit.mjs +2 -2
  31. package/browser/editor/page/core.mjs +3 -1
  32. package/browser/editor/page/custom-page.mjs +15 -11
  33. package/browser/editor/page/editor.mjs +31 -14
  34. package/browser/editor/page/history.mjs +19 -4
  35. package/browser/editor/page/insert/index.mjs +20 -22
  36. package/browser/editor/page/menu.mjs +15 -7
  37. package/browser/editor/page/page-objects.mjs +3 -7
  38. package/browser/editor/page/present-links.mjs +5 -4
  39. package/browser/editor/page/present-shell.mjs +63 -0
  40. package/browser/editor/page/selection/index.mjs +10 -2
  41. package/browser/editor/page/table/binding.mjs +87 -0
  42. package/browser/editor/page/table/icons.mjs +0 -2
  43. package/browser/editor/page/table/index.mjs +10 -14
  44. package/browser/editor/page/table/views.mjs +5 -52
  45. package/browser/editor/page/table/widths.mjs +82 -10
  46. package/browser/editor/page/typing/index.mjs +44 -12
  47. package/browser/editor/page/typing/leave.mjs +19 -5
  48. package/browser/shared/block-packs.mjs +23 -8
  49. package/browser/shared/component-catalog.mjs +15 -8
  50. package/browser/shared/component-source.mjs +3 -1
  51. package/browser/shared/components/heading.mjs +2 -1
  52. package/browser/shared/components/installed.mjs +1 -1
  53. package/browser/shared/components/page.mjs +2 -0
  54. package/browser/shared/components/paragraph.mjs +8 -1
  55. package/browser/shared/components/table.mjs +1 -0
  56. package/browser/shared/emoji/line-icons.mjs +37 -0
  57. package/browser/shared/http.mjs +9 -0
  58. package/browser/shared/i18n/en.mjs +40 -17
  59. package/browser/shared/outline.mjs +30 -2
  60. package/browser/shared/pack-manifest.mjs +12 -1
  61. package/browser/shared/pages.mjs +169 -57
  62. package/browser/shared/pinyin.mjs +92 -0
  63. package/browser/shared/present-step.mjs +17 -0
  64. package/browser/shared/sidebar-head.mjs +70 -0
  65. package/browser/shared/web-links.mjs +5 -0
  66. package/dist/chunk-2OHANJ6Q.mjs +2 -0
  67. package/dist/{chunk-QOUEFUCH.mjs → chunk-3MBRL2PM.mjs} +1 -1
  68. package/dist/{chunk-NWR2LT5B.mjs → chunk-4OKWZY5E.mjs} +1 -1
  69. package/dist/{chunk-RMECZA2K.mjs → chunk-5K44HLTZ.mjs} +1 -1
  70. package/dist/{chunk-NFMCP2HI.mjs → chunk-5XA2ZBAE.mjs} +2 -2
  71. package/dist/{chunk-OUUP3WKT.mjs → chunk-67N2QJYT.mjs} +1 -1
  72. package/dist/chunk-6KBYRA2C.mjs +2 -0
  73. package/dist/{chunk-SX4LJYT6.mjs → chunk-6N3PX4XX.mjs} +1 -1
  74. package/dist/{chunk-RFDZXFJF.mjs → chunk-7JDX4E65.mjs} +1 -1
  75. package/dist/chunk-7M2BQQGZ.mjs +2 -0
  76. package/dist/{chunk-ZUNVAXLH.mjs → chunk-7OG26PGF.mjs} +1 -1
  77. package/dist/{chunk-B4P2CM2J.mjs → chunk-AHN3NFKJ.mjs} +1 -1
  78. package/dist/chunk-ALZ6ZENZ.mjs +3 -0
  79. package/dist/{chunk-UGWYKHBL.mjs → chunk-AR5D57ZD.mjs} +2 -2
  80. package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
  81. package/dist/{chunk-HLYV7RIH.mjs → chunk-CMA72MAG.mjs} +1 -1
  82. package/dist/{chunk-D4CKRD3V.mjs → chunk-DAZL73VK.mjs} +2 -2
  83. package/dist/{chunk-6VTU6KEO.mjs → chunk-DJISHPUL.mjs} +1 -1
  84. package/dist/{chunk-BPL64HGA.mjs → chunk-EAIFITAV.mjs} +1 -1
  85. package/dist/chunk-EP25HDAJ.mjs +2 -0
  86. package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
  87. package/dist/{chunk-3KJHLHUO.mjs → chunk-F6HVDZR4.mjs} +1 -1
  88. package/dist/{chunk-3NJACR4W.mjs → chunk-FDMAXMQA.mjs} +1 -1
  89. package/dist/chunk-FETSREUN.mjs +5 -0
  90. package/dist/{chunk-RKPWLGZ3.mjs → chunk-GHOYKMQ3.mjs} +1 -1
  91. package/dist/chunk-HDWTZ763.mjs +12 -0
  92. package/dist/{chunk-VTKNEYT3.mjs → chunk-HPOI4ZX7.mjs} +1 -1
  93. package/dist/{chunk-OW5NWPOJ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
  94. package/dist/{chunk-V6RIYQZZ.mjs → chunk-ITOH5EGO.mjs} +1 -1
  95. package/dist/{chunk-ZWPMCK7P.mjs → chunk-IV64FFI4.mjs} +1 -1
  96. package/dist/chunk-J43Z6LSS.mjs +2 -0
  97. package/dist/chunk-J5PUIV3Y.mjs +4 -0
  98. package/dist/{chunk-KTKHRC6H.mjs → chunk-JA7G53PC.mjs} +1 -1
  99. package/dist/{chunk-NWFBE2YA.mjs → chunk-JLTZF4LA.mjs} +1 -1
  100. package/dist/{chunk-GY3FAUVM.mjs → chunk-JZT7I2OJ.mjs} +1 -1
  101. package/dist/{chunk-QKXMPUXC.mjs → chunk-K5XATEOO.mjs} +1 -1
  102. package/dist/chunk-L2SUIS5S.mjs +2 -0
  103. package/dist/{chunk-TEZAWDSQ.mjs → chunk-LM4XFQHZ.mjs} +1 -1
  104. package/dist/{chunk-5ZJA7CXT.mjs → chunk-MN7BZL4R.mjs} +1 -1
  105. package/dist/chunk-MTYYHM2G.mjs +15 -0
  106. package/dist/{chunk-HMYTM3GT.mjs → chunk-MXUBKDRO.mjs} +1 -1
  107. package/dist/{chunk-MHW7ZKOU.mjs → chunk-OEXWRJME.mjs} +1 -1
  108. package/dist/{chunk-CRJEG7MT.mjs → chunk-OFHXGWOX.mjs} +1 -1
  109. package/dist/chunk-ORID3AE7.mjs +166 -0
  110. package/dist/{chunk-32ZUEFZ7.mjs → chunk-PGDE24N5.mjs} +1 -1
  111. package/dist/{chunk-UNOYVIYR.mjs → chunk-POTA2FV4.mjs} +1 -1
  112. package/dist/{chunk-N7AZZLGT.mjs → chunk-QE5RG76S.mjs} +18 -17
  113. package/dist/{chunk-N76HWBA3.mjs → chunk-R2ZDLJYT.mjs} +1 -1
  114. package/dist/chunk-SWUTTZCN.mjs +2 -0
  115. package/dist/{chunk-POQSE4C2.mjs → chunk-TR7TWGHB.mjs} +1 -1
  116. package/dist/chunk-U5XL7D6A.mjs +3 -0
  117. package/dist/{chunk-PVER7LSC.mjs → chunk-U7NZI6N4.mjs} +2 -2
  118. package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
  119. package/dist/chunk-UJQJKNGK.mjs +2 -0
  120. package/dist/chunk-UYVCQK3H.mjs +22 -0
  121. package/dist/{chunk-FJYN7RTH.mjs → chunk-VFX2KNHP.mjs} +1 -1
  122. package/dist/{chunk-UBBRTJ2R.mjs → chunk-VS7JIGJW.mjs} +1 -1
  123. package/dist/{chunk-NVRNLK25.mjs → chunk-W7665ZEC.mjs} +2 -2
  124. package/dist/{chunk-NG375ILQ.mjs → chunk-WQDQWCX5.mjs} +1 -1
  125. package/dist/chunk-YLPSMDIS.mjs +5 -0
  126. package/dist/{chunk-VXACTLO3.mjs → chunk-YNAWBAYN.mjs} +1 -1
  127. package/dist/{chunk-ZB4H7ZN4.mjs → chunk-YROX2O22.mjs} +1 -1
  128. package/dist/{chunk-YQGT4AB2.mjs → chunk-ZDLPUOAG.mjs} +1 -1
  129. package/dist/chunk-ZQS6H473.mjs +16 -0
  130. package/dist/{chunk-OJKMINIU.mjs → chunk-ZWWC4KLF.mjs} +15 -15
  131. package/dist/cli.mjs +1 -1
  132. package/dist/server.mjs +1 -1
  133. package/dist/worker.mjs +1 -1
  134. package/kit/board.css +5 -5
  135. package/kit/code.js +15 -15
  136. package/kit/report.css +120 -41
  137. package/kit/runtime.js +45 -7
  138. package/kit/tables.js +2 -0
  139. package/kit/ui.js +2 -0
  140. package/package.json +1 -1
  141. package/packs/gallery/chart-view/kit.js +3 -3
  142. package/packs/gallery/chart-view/writing.md +1 -1
  143. package/packs/gallery/countdown/component.json +1 -1
  144. package/packs/gallery/countdown/kit.js +89 -29
  145. package/packs/gallery/countdown/preview.svg +1 -1
  146. package/packs/gallery/countdown/style.css +88 -100
  147. package/packs/gallery/countdown/writing.md +2 -2
  148. package/packs/gallery/habit/component.json +3 -3
  149. package/packs/gallery/habit/kit.js +114 -75
  150. package/packs/gallery/habit/preview.svg +1 -1
  151. package/packs/gallery/habit/style.css +133 -205
  152. package/packs/gallery/habit/writing.md +2 -2
  153. package/packs/gallery/heatmap/component.json +2 -2
  154. package/packs/gallery/heatmap/kit.js +335 -173
  155. package/packs/gallery/heatmap/preview.svg +1 -1
  156. package/packs/gallery/heatmap/style.css +158 -170
  157. package/packs/gallery/heatmap/writing.md +2 -2
  158. package/packs/gallery/kanban/kit.js +3 -3
  159. package/packs/gallery/kanban/writing.md +1 -1
  160. package/packs/gallery/metric/kit.js +3 -3
  161. package/packs/gallery/metric/writing.md +1 -1
  162. package/packs/gallery/note/component.json +3 -3
  163. package/packs/gallery/note/kit.js +40 -13
  164. package/packs/gallery/note/preview.svg +1 -1
  165. package/packs/gallery/note/style.css +51 -30
  166. package/packs/gallery/note/writing.md +2 -2
  167. package/packs/gallery/recent/component.json +2 -2
  168. package/packs/gallery/recent/kit.js +141 -86
  169. package/packs/gallery/recent/preview.svg +1 -1
  170. package/packs/gallery/recent/style.css +53 -126
  171. package/packs/gallery/recent/writing.md +2 -2
  172. package/packs/gallery/static/component.json +2 -2
  173. package/packs/gallery/static/preview.svg +1 -1
  174. package/packs/gallery/static/style.css +99 -66
  175. package/packs/gallery/static/writing.md +2 -2
  176. package/packs/gallery/stats/component.json +3 -3
  177. package/packs/gallery/stats/kit.js +253 -72
  178. package/packs/gallery/stats/preview.svg +1 -1
  179. package/packs/gallery/stats/style.css +64 -142
  180. package/packs/gallery/stats/writing.md +2 -2
  181. package/packs/gallery/today/component.json +2 -2
  182. package/packs/gallery/today/kit.js +62 -27
  183. package/packs/gallery/today/preview.svg +1 -1
  184. package/packs/gallery/today/style.css +101 -72
  185. package/packs/gallery/today/writing.md +2 -2
  186. package/packs/gallery/todos/component.json +1 -1
  187. package/packs/gallery/todos/kit.js +218 -71
  188. package/packs/gallery/todos/preview.svg +1 -1
  189. package/packs/gallery/todos/style.css +170 -223
  190. package/packs/gallery/todos/writing.md +1 -1
  191. package/packs/gallery/weather/component.json +2 -2
  192. package/packs/gallery/weather/kit.js +165 -90
  193. package/packs/gallery/weather/preview.svg +1 -1
  194. package/packs/gallery/weather/style.css +100 -218
  195. package/packs/gallery/weather/writing.md +1 -1
  196. package/packs/gallery-soft/countdown/component.json +18 -0
  197. package/packs/gallery-soft/countdown/example.html +3 -0
  198. package/packs/gallery-soft/countdown/kit.js +206 -0
  199. package/packs/gallery-soft/countdown/preview.svg +1 -0
  200. package/packs/gallery-soft/countdown/style.css +211 -0
  201. package/packs/gallery-soft/countdown/template.html +3 -0
  202. package/packs/gallery-soft/countdown/writing.md +24 -0
  203. package/packs/gallery-soft/habit/component.json +14 -0
  204. package/packs/gallery-soft/habit/example.html +3 -0
  205. package/packs/gallery-soft/habit/kit.js +366 -0
  206. package/packs/gallery-soft/habit/preview.svg +1 -0
  207. package/packs/gallery-soft/habit/style.css +251 -0
  208. package/packs/gallery-soft/habit/template.html +3 -0
  209. package/packs/gallery-soft/habit/writing.md +23 -0
  210. package/packs/gallery-soft/heatmap/component.json +18 -0
  211. package/packs/gallery-soft/heatmap/example.html +3 -0
  212. package/packs/gallery-soft/heatmap/kit.js +498 -0
  213. package/packs/gallery-soft/heatmap/preview.svg +1 -0
  214. package/packs/gallery-soft/heatmap/style.css +181 -0
  215. package/packs/gallery-soft/heatmap/template.html +3 -0
  216. package/packs/gallery-soft/heatmap/writing.md +23 -0
  217. package/packs/gallery-soft/note/component.json +14 -0
  218. package/packs/gallery-soft/note/example.html +3 -0
  219. package/packs/gallery-soft/note/kit.js +162 -0
  220. package/packs/gallery-soft/note/preview.svg +1 -0
  221. package/packs/gallery-soft/note/style.css +148 -0
  222. package/packs/gallery-soft/note/template.html +3 -0
  223. package/packs/gallery-soft/note/writing.md +20 -0
  224. package/packs/gallery-soft/recent/component.json +13 -0
  225. package/packs/gallery-soft/recent/example.html +1 -0
  226. package/packs/gallery-soft/recent/kit.js +335 -0
  227. package/packs/gallery-soft/recent/preview.svg +1 -0
  228. package/packs/gallery-soft/recent/style.css +160 -0
  229. package/packs/gallery-soft/recent/template.html +1 -0
  230. package/packs/gallery-soft/recent/writing.md +17 -0
  231. package/packs/gallery-soft/static/component.json +13 -0
  232. package/packs/gallery-soft/static/example.html +12 -0
  233. package/packs/gallery-soft/static/preview.svg +1 -0
  234. package/packs/gallery-soft/static/style.css +186 -0
  235. package/packs/gallery-soft/static/template.html +1 -0
  236. package/packs/gallery-soft/static/writing.md +20 -0
  237. package/packs/gallery-soft/stats/component.json +13 -0
  238. package/packs/gallery-soft/stats/example.html +1 -0
  239. package/packs/gallery-soft/stats/kit.js +341 -0
  240. package/packs/gallery-soft/stats/preview.svg +1 -0
  241. package/packs/gallery-soft/stats/style.css +119 -0
  242. package/packs/gallery-soft/stats/template.html +1 -0
  243. package/packs/gallery-soft/stats/writing.md +13 -0
  244. package/packs/gallery-soft/today/component.json +12 -0
  245. package/packs/gallery-soft/today/example.html +1 -0
  246. package/packs/gallery-soft/today/kit.js +222 -0
  247. package/packs/gallery-soft/today/preview.svg +1 -0
  248. package/packs/gallery-soft/today/style.css +178 -0
  249. package/packs/gallery-soft/today/template.html +1 -0
  250. package/packs/gallery-soft/today/writing.md +16 -0
  251. package/packs/gallery-soft/todos/component.json +19 -0
  252. package/packs/gallery-soft/todos/example.html +3 -0
  253. package/packs/gallery-soft/todos/kit.js +527 -0
  254. package/packs/gallery-soft/todos/preview.svg +1 -0
  255. package/packs/gallery-soft/todos/style.css +212 -0
  256. package/packs/gallery-soft/todos/template.html +3 -0
  257. package/packs/gallery-soft/todos/writing.md +32 -0
  258. package/packs/gallery-soft/weather/component.json +19 -0
  259. package/packs/gallery-soft/weather/example.html +3 -0
  260. package/packs/gallery-soft/weather/kit.js +411 -0
  261. package/packs/gallery-soft/weather/preview.svg +1 -0
  262. package/packs/gallery-soft/weather/style.css +185 -0
  263. package/packs/gallery-soft/weather/template.html +3 -0
  264. package/packs/gallery-soft/weather/writing.md +28 -0
  265. package/release.json +9 -9
  266. package/skill/SKILL.md +12 -12
  267. package/skill/references/building-blocks.md +4 -4
  268. package/skill/references/pages-and-data.md +13 -9
  269. package/skill/references/writing-html.md +2 -0
  270. package/web/assets/index-BM_K-Auu.css +1 -0
  271. package/web/assets/index-DRslOBiv.js +15 -0
  272. package/web/index.html +2 -2
  273. package/dist/chunk-2NRBY6AJ.mjs +0 -2
  274. package/dist/chunk-35NQ4PLQ.mjs +0 -2
  275. package/dist/chunk-4AV456RQ.mjs +0 -2
  276. package/dist/chunk-5XYHDBJ5.mjs +0 -2
  277. package/dist/chunk-6A53MZYZ.mjs +0 -2
  278. package/dist/chunk-6EXH3ORZ.mjs +0 -166
  279. package/dist/chunk-BVEFGE4B.mjs +0 -2
  280. package/dist/chunk-D4EURAX4.mjs +0 -4
  281. package/dist/chunk-DQCE7TJY.mjs +0 -5
  282. package/dist/chunk-FWNRPMCJ.mjs +0 -12
  283. package/dist/chunk-FWW4CSAZ.mjs +0 -2
  284. package/dist/chunk-JVHHHGEA.mjs +0 -3
  285. package/dist/chunk-PZDLJJTJ.mjs +0 -3
  286. package/dist/chunk-SXHBYZST.mjs +0 -16
  287. package/dist/chunk-UFNYFFKO.mjs +0 -20
  288. package/dist/chunk-UKSHDZZ6.mjs +0 -2
  289. package/dist/chunk-VJPFVNNW.mjs +0 -15
  290. package/dist/chunk-VS3ZMVGX.mjs +0 -5
  291. package/web/assets/index-BK2e_07w.js +0 -13
  292. package/web/assets/index-CWFI83mF.css +0 -1
@@ -6,36 +6,49 @@ import { isDeepStrictEqual } from "node:util";
6
6
  import { escapeMarkup, escapeText } from "./escape.mjs";
7
7
  import { writeAtomic } from "./files.mjs";
8
8
  import { laidOut } from "./json-layout.mjs";
9
+ import { readStoredHead, withOutlineLock, writeHead } from "./outline.mjs";
10
+ import { newPageId } from "./page-links.mjs";
11
+ import { headOrder, headPage, headWithPage } from "./sidebar-head.mjs";
9
12
  import { DOCUMENT_FOLDERS, isSlugName, keptName, layoutOf, listTrash, markerOf, slugPattern, slugTaken } from "./workspace.mjs";
10
13
 
11
14
  // A workspace's custom pages (自定义页: notes/plans/component-pages-plan-2026-11-07.md §5, the spec
12
15
  // custom-blocks-spec-2026-11-07.md §5): a page is a folder pages/<slug>/ at the workspace's root, as
13
- // components/ is, holding page.html (the page an Agent writes: its head, its look, and one or more
14
- // boards, <div class="gallery-board">, of cards, each <section class="gallery-card" data-card-id
15
- // data-x data-y data-w data-h> around one instance of a custom block) and page.json ({ title, icon }).
16
- // Only an Agent makes one (gallery page new); the workbench lists them under 新建 and 搜索, shows one
17
- // at #@<slug> and renames or trashes it (src/workbench/service.mjs). A page's slug and a document's
18
- // are one pool (./workspace.mjs slugTaken): no new document takes a page's, nor a new page a
19
- // document's, and `pages` is a name no new document takes. A page writes no script of its own: the
20
- // blocks' scripts and styles are put in by the server as it shows it (src/render/page-html.mjs).
21
- // The home page is pages/home, listed first: the default one (首页, HOME_TITLE, holding a card of each
22
- // of HOME_CARDS) is made once per workspace by src/editor/home-page.mjs, as the server starts and by
23
- // gallery init, and gallery.json's pages.homeMade says it was (markHomeMade); nothing makes it again.
24
- // Node's built-ins only, but ./escape.mjs, ./files.mjs, ./json-layout.mjs and ./workspace.mjs.
16
+ // components/ is, holding page.html (the page an Agent writes, free HTML: its head, its look, its own
17
+ // scripts and frames, and where it wants them one or more boards, <div class="gallery-board">, of
18
+ // cards, each <section class="gallery-card" data-card-id data-x data-y data-w data-h> around one
19
+ // instance of a custom block) and page.json ({ title, icon, defaultCards?, hint? }: pageMetaOf). An Agent
20
+ // makes one (gallery page new), and the workbench's 新建页面 a blank one (blankPageHtml) for an Agent to
21
+ // write; the workbench lists them in the sidebar's head with 新建 and 搜索, in the order the reader drags
22
+ // them to (./sidebar-head.mjs, kept in the outline file), shows one at #@<slug> and renames or trashes
23
+ // it (src/workbench/service.mjs). A page's slug and a document's are one pool (./workspace.mjs
24
+ // slugTaken): no new document takes a page's, nor a new page a document's, and `pages` is a name no new
25
+ // document takes. The blocks' scripts and styles are put in by the server as it shows a page
26
+ // (src/render/page-html.mjs).
27
+ // No page is special by its slug. The default home page (首页, HOME_TITLE, at pages/home, holding a card of
28
+ // each of HOME_CARDS) is a page like any other, made once per workspace by src/editor/home-page.mjs, as
29
+ // the server starts and by gallery init (gallery.json's pages.homeMade says it was: markHomeMade; nothing
30
+ // makes it again), whose page.json says what any page's may: it has the default cards (defaultCards:
31
+ // 恢复默认卡片 in its menu, the note of the default cards it has not got), and its first-open note
32
+ // (hint, until the reader closes it: pageHintClosed).
33
+ // Node's built-ins only, but ./escape.mjs, ./files.mjs, ./json-layout.mjs, ./outline.mjs, ./sidebar-head.mjs
34
+ // and ./workspace.mjs.
25
35
 
26
36
  /** A custom page's two files. */
27
37
  export const PAGE_FILES = { html: "page.html", json: "page.json" };
28
- /** The home page's slug (首页): listed first, never trashed. */
38
+ /** The slug of the default home page (首页), which src/editor/home-page.mjs makes: a page as any other. */
29
39
  export const HOME = "home";
30
40
  /** The default home page's title. */
31
41
  export const HOME_TITLE = "首页";
32
42
  /**
33
- * The blocks of the default home page's cards (the plan's §5.2: 问候与日期, 便签, 我的待办, 最近编辑, 工作区统计,
34
- * 编辑热力图), in the order they are placed, each at the first free spot by its default size (a home of
35
- * 12 x 6 cells, none left empty: shared/board-places.mjs freeSpot); 恢复默认卡片 (the page service's
36
- * restore-defaults) puts back those of them no card of the home page holds.
43
+ * The blocks of the default home page's cards (the plan's §5.2, laid out as the pack Gallery 风格's home page,
44
+ * notes/plans/block-packs-plan-2026-11-09.md §3: 问候与日期, 天气, 倒计时, 习惯打卡, 工作区统计, 我的待办, 编辑热力图,
45
+ * 最近编辑, 便签, 静态卡片), in the order they are placed, each at the first free spot by its size (HOME_SIZES',
46
+ * else its block's default: shared/board-places.mjs freeSpot), a home of 12 x 12 cells, none left empty;
47
+ * 恢复默认卡片 (the page service's restore-defaults) puts back those of them no card of the home page holds.
37
48
  */
38
- export const HOME_CARDS = Object.freeze(["gallery/today", "gallery/note", "gallery/todos", "gallery/recent", "gallery/stats", "gallery/heatmap"]);
49
+ export const HOME_CARDS = Object.freeze(["gallery/today", "gallery/weather", "gallery/countdown", "gallery/habit", "gallery/stats", "gallery/todos", "gallery/heatmap", "gallery/recent", "gallery/note", "gallery/static"]);
50
+ /** The default home page's cards that are not their block's default size, [w, h] each. */
51
+ export const HOME_SIZES = Object.freeze({ "gallery/habit": [2, 2], "gallery/todos": [4, 6], "gallery/note": [4, 4] });
39
52
  /**
40
53
  * The default cards a home page made before the workspace's data came had (the plan's phase 2: 问候与日期
41
54
  * and 便签): what a home whose hints say nothing of its cards was told of, so that it is told once of the
@@ -86,21 +99,51 @@ export async function pageSlugRefused(slug, root) {
86
99
  return null;
87
100
  }
88
101
 
102
+ /**
103
+ * A slug for a new custom page no name of workspace `root` holds (pageSlugRefused): page- and a random id,
104
+ * as a new document's is doc- and one (the workbench's 新建页面, whose title is typed after it is made).
105
+ */
106
+ export async function newPageSlug(root, random = undefined) {
107
+ for (;;) {
108
+ const slug = `page-${newPageId(new Set(), random)}`;
109
+ if (!(await pageSlugRefused(slug, root))) return slug;
110
+ }
111
+ }
112
+
89
113
  const readJson = (file) => fs.readFile(file, "utf8").then((text) => JSON.parse(text.replace(/^/, ""))).catch(() => null);
90
114
 
91
- /** A page.json's fields as a list shows them: { title (the slug when it names none), icon (or null) }. */
115
+ /**
116
+ * A page.json's fields as a list shows them: { title (the slug when it names none), icon (or null),
117
+ * defaultCards (it says `"defaultCards": true`: the page has Gallery's default cards, HOME_CARDS, which
118
+ * 恢复默认卡片 in its menu puts back where it misses them, and its note tells of those that came after
119
+ * it), hint (`"hint": true`: its first-open note on its cards shows until the reader closes it) }.
120
+ * Any page may say either; the default home page says both.
121
+ */
92
122
  export function pageMetaOf(slug, value) {
93
123
  const title = typeof value?.title === "string" && value.title.trim() ? value.title.trim() : slug;
94
124
  const icon = typeof value?.icon === "string" && value.icon.trim() ? value.icon.trim() : null;
95
- return { title, icon };
125
+ return { title, icon, defaultCards: value?.defaultCards === true, hint: value?.hint === true };
96
126
  }
97
127
 
98
- /** Custom page `slug` as a list shows it: { slug, title, icon }, or null when it is not there. */
128
+ /** Custom page `slug` as a list shows it: { slug, title, icon, defaultCards, hint }, or null when it is not there. */
99
129
  export async function readPageMeta(slug, root) {
100
130
  if (!pageExists(slug, root)) return null;
101
131
  return { slug, ...pageMetaOf(slug, await readJson(pageFiles(slug, root).json)) };
102
132
  }
103
133
 
134
+ /** Custom page `slug`'s meta as its page.json says it now (pageMetaOf), read at once; null when it is not there. */
135
+ export function pageMetaNow(slug, root) {
136
+ if (!pageExists(slug, root)) return null;
137
+ let value = null;
138
+ try {
139
+ value = JSON.parse(readFileSync(pageFiles(slug, root).json, "utf8").replace(/^/, ""));
140
+ } catch {}
141
+ return { slug, ...pageMetaOf(slug, value) };
142
+ }
143
+
144
+ /** Whether custom page `slug` has the default cards (its page.json's defaultCards): 恢复默认卡片 runs on it. */
145
+ export const pageDefaults = (slug, root) => pageMetaNow(slug, root)?.defaultCards === true;
146
+
104
147
  /**
105
148
  * What gallery.json says of the custom pages (the spec §2): { order: the sidebar's order of their
106
149
  * slugs, homeMade: whether the default home page was made }; nothing said, or a gallery.json that does
@@ -236,8 +279,9 @@ export function ownLine(text, open, member) {
236
279
  return start > open && /^[ \t]*$/.test(before) ? before : null;
237
280
  }
238
281
 
239
- // .gallery/hints.json: { home: <when it was closed>, homeCards: [the default cards the reader knows of] },
240
- // or {} where there is none or it does not read.
282
+ // .gallery/hints.json: { pages: { <slug>: <when its first-open note was closed> }, homeCards: [the default
283
+ // cards the reader knows of] }, or {} where there is none or it does not read. Its `home` (<when>) is what
284
+ // a Gallery said before every page was alike: the note of pages/home closed, the one page that had one.
241
285
  function hintsOf(root) {
242
286
  try {
243
287
  const value = JSON.parse(readFileSync(layoutOf(root).hints, "utf8"));
@@ -246,19 +290,48 @@ function hintsOf(root) {
246
290
  return {};
247
291
  }
248
292
  }
293
+ const closedOf = (hints) => (hints.pages && typeof hints.pages === "object" && !Array.isArray(hints.pages) ? hints.pages : {});
294
+
295
+ /** What .gallery/hints.json says under `key` (undefined where it says nothing, or does not read). */
296
+ export const hintOf = (root, key) => hintsOf(root)[key];
297
+
298
+ /** .gallery/hints.json with `patch`'s keys written, its others kept. */
299
+ export async function writeHints(root, patch) {
300
+ const { hints, gallery } = layoutOf(root);
301
+ await fs.mkdir(gallery, { recursive: true });
302
+ await writeAtomic(hints, `${JSON.stringify({ ...hintsOf(root), ...patch }, null, 2)}\n`);
303
+ }
249
304
 
250
305
  /**
251
- * Whether the reader closed the home page's first-open note (the plan's §5.2): once for the workspace,
306
+ * Whether the reader closed custom page `slug`'s first-open note (the plan's §5.2): once for the workspace,
252
307
  * whichever browser or port shows it, kept in .gallery/hints.json (what Gallery keeps of its own, never
253
308
  * gallery.json, which is the workspace's settings and wakes every page's watcher as it is written).
254
309
  */
255
- export const homeHintClosed = (root) => typeof hintsOf(root).home === "string";
310
+ export function pageHintClosed(root, slug) {
311
+ const hints = hintsOf(root);
312
+ return typeof closedOf(hints)[slug] === "string" || (slug === HOME && typeof hints.home === "string");
313
+ }
314
+
315
+ /** Whether custom page `slug`'s first-open note shows: its page.json says `hint`, and the reader has not closed it. */
316
+ export const pageHintShown = (slug, root) => pageMetaNow(slug, root)?.hint === true && !pageHintClosed(root, slug);
317
+
318
+ /** Custom page `slug`'s first-open note closed for good (`at`: when, an ISO time), the file's other notes kept. */
319
+ export async function closePageHint(root, slug, at = new Date().toISOString()) {
320
+ const { hints, gallery } = layoutOf(root);
321
+ await fs.mkdir(gallery, { recursive: true });
322
+ const value = hintsOf(root);
323
+ await writeAtomic(hints, `${JSON.stringify({ ...value, pages: { ...closedOf(value), [slug]: at } }, null, 2)}\n`);
324
+ }
325
+
326
+ /** The first-open note of pages/home closed (closePageHint's), and whether it is (pageHintClosed's): the older words for them. */
327
+ export const closeHomeHint = (root, at) => closePageHint(root, HOME, at);
328
+ export const homeHintClosed = (root) => pageHintClosed(root, HOME);
256
329
 
257
330
  /**
258
- * The default cards (of HOME_CARDS) the reader knows of in workspace `root`: those its home page was made
259
- * with, or the reader was told of since (the home page's note of new cards, closed or taken up, and
260
- * 恢复默认卡片 run), kept in .gallery/hints.json as `homeCards`; FIRST_HOME_CARDS where it says none, the
261
- * cards of a home made before the others came.
331
+ * The default cards (of HOME_CARDS) the reader knows of in workspace `root`: those its pages with the
332
+ * default cards were made with, or the reader was told of since (a page's note of new cards, closed or
333
+ * taken up, and 恢复默认卡片 run), kept in .gallery/hints.json as `homeCards`; FIRST_HOME_CARDS where it
334
+ * says none, the cards of a home page made before the others came.
262
335
  */
263
336
  export function homeCardsTold(root) {
264
337
  const told = hintsOf(root).homeCards;
@@ -266,43 +339,65 @@ export function homeCardsTold(root) {
266
339
  }
267
340
 
268
341
  /**
269
- * The default cards the home page's note tells of (the plan's 3.4: a home made before them is not
270
- * changed, and is told once that 恢复默认卡片 brings them): those of HOME_CARDS the reader was not told of
271
- * (homeCardsTold) and no card of the home page holds (`held`: the blocks its cards are). Empty where
272
- * there is none to tell.
342
+ * The default cards a page's note tells of, on a page with the default cards (pageMetaOf's defaultCards;
343
+ * the plan's 3.4: a page made before them is not changed, and is told once that 恢复默认卡片 brings them):
344
+ * those of HOME_CARDS the reader was not told of (homeCardsTold) and no card of the page holds (`held`: the
345
+ * blocks its cards are). Empty where there is none to tell.
273
346
  */
274
347
  export const homeCardsNew = (root, held) => HOME_CARDS.filter((id) => !homeCardsTold(root).includes(id) && !held.includes(id));
275
348
 
276
- /** Every default card told of (HOME_CARDS, kept as hints.json's `homeCards`): the home page's note of new cards says nothing again. */
349
+ /** Every default card told of (HOME_CARDS, kept as hints.json's `homeCards`): a page's note of new cards says nothing again. */
277
350
  export async function tellHomeCards(root) {
278
351
  const { hints, gallery } = layoutOf(root);
279
352
  await fs.mkdir(gallery, { recursive: true });
280
353
  await writeAtomic(hints, `${JSON.stringify({ ...hintsOf(root), homeCards: [...HOME_CARDS] }, null, 2)}\n`);
281
354
  }
282
355
 
283
- /** The home page's first-open note closed for good (`at`: when, an ISO time), the file's other notes kept. */
284
- export async function closeHomeHint(root, at = new Date().toISOString()) {
285
- const { hints, gallery } = layoutOf(root);
286
- await fs.mkdir(gallery, { recursive: true });
287
- await writeAtomic(hints, `${JSON.stringify({ ...hintsOf(root), home: at }, null, 2)}\n`);
288
- }
289
-
290
356
  /**
291
- * Pages in the sidebar's order (the spec §2): home first, then those gallery.json's pages.order
292
- * names, in its order, then the rest by title (then slug). Pure: `pages` [{ slug, title }].
357
+ * Pages in the order a page goes by where the reader gave it no place in the sidebar's head (the spec
358
+ * §2): those gallery.json's pages.order names, in its order, then the rest by title (then slug). Pure:
359
+ * `pages` [{ slug, title }].
293
360
  */
294
361
  export function inPageOrder(pages, order = []) {
295
- const rank = (slug) => (slug === HOME ? -1 : order.includes(slug) ? order.indexOf(slug) : Infinity);
362
+ const rank = (slug) => (order.includes(slug) ? order.indexOf(slug) : Infinity);
296
363
  const collator = new Intl.Collator("zh-CN", { numeric: true });
297
364
  return [...pages].sort((a, b) => rank(a.slug) - rank(b.slug) || collator.compare(a.title, b.title) || (a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0));
298
365
  }
299
366
 
300
- /** The custom pages of workspace `root`, in the sidebar's order: [{ slug, title, icon }]. Folders of pages/ that hold no page.html are passed over. */
367
+ /**
368
+ * Pages in the sidebar's order: as the head's rows go (`head`, the stored head: ./sidebar-head.mjs
369
+ * headOrder), those it names not after them in inPageOrder's (`order`: gallery.json's pages.order). Pure.
370
+ */
371
+ export function inHeadOrder(pages, order = [], head = null) {
372
+ const listed = inPageOrder(pages, order);
373
+ const bySlug = new Map(listed.map((page) => [page.slug, page]));
374
+ return headOrder(head, listed.map(({ slug }) => slug)).map(headPage).filter(Boolean).map((slug) => bySlug.get(slug));
375
+ }
376
+
377
+ /** The custom pages of workspace `root`, in the sidebar's order (inHeadOrder): [{ slug, title, icon, defaultCards, hint }]. Folders of pages/ that hold no page.html are passed over. */
301
378
  export async function listPages(root) {
302
379
  const { customPages: pages } = layoutOf(root);
303
380
  const names = await fs.readdir(pages, { withFileTypes: true }).catch(() => []);
304
381
  const found = await Promise.all(names.filter((entry) => entry.isDirectory() && isSlugName(entry.name)).map((entry) => readPageMeta(entry.name, root)));
305
- return inPageOrder(found.filter(Boolean), pagesSettings(root).order);
382
+ return inHeadOrder(found.filter(Boolean), pagesSettings(root).order, await readStoredHead(root));
383
+ }
384
+
385
+ /** The sidebar's head rows of workspace `root` in their order (./sidebar-head.mjs headOrder): new, search and @<slug> of each of `pages` (listPages'). */
386
+ export async function headOf(root, pages = null) {
387
+ return headOrder(await readStoredHead(root), (pages ?? await listPages(root)).map(({ slug }) => slug));
388
+ }
389
+
390
+ /**
391
+ * Custom page `slug` given its row in the sidebar's head, in the outline file under its lock: right after
392
+ * page `after`'s row, else last (a page made: createPage's). A head that was never stored is stored as
393
+ * the sidebar showed it, so that the page made last stands last.
394
+ */
395
+ export async function placePageInHead(root, slug, after = null) {
396
+ await withOutlineLock(root, async () => {
397
+ const stored = await readStoredHead(root);
398
+ const shown = stored ? null : await headOf(root, (await listPages(root)).filter((page) => page.slug !== slug));
399
+ await writeHead(root, headWithPage(stored, shown, slug, after));
400
+ });
306
401
  }
307
402
 
308
403
  /**
@@ -322,10 +417,14 @@ export async function pageVersionNow(slug, root) {
322
417
 
323
418
  /**
324
419
  * The page.html of a new custom page: its title (in `<title>` only: the page draws no big title, its
325
- * name is the sidebar's, the tab's and the top bar's) and one empty board (`main`), the spec §5's
326
- * structure. `lang`: its page's lang ("zh-CN" by default).
420
+ * name is the sidebar's, the tab's and the top bar's) and its <main class="gallery-page">, holding one
421
+ * empty board (`main`, the spec §5's structure: gallery page new's), or nothing (`blank`: the workbench's
422
+ * 新建页面, a page an Agent is to write, isBlankPage). `lang`: its page's lang ("zh-CN" by default).
327
423
  */
328
- export function newPageHtml(slug, title, { lang = "zh-CN" } = {}) {
424
+ export function newPageHtml(slug, title, { lang = "zh-CN", blank = false } = {}) {
425
+ const inside = blank ? "" : `
426
+ <div class="gallery-board" data-board-id="${FIRST_BOARD}">
427
+ </div>`;
329
428
  return `<!doctype html>
330
429
  <html lang="${escapeMarkup(lang)}">
331
430
  <head>
@@ -338,9 +437,7 @@ export function newPageHtml(slug, title, { lang = "zh-CN" } = {}) {
338
437
  </style>
339
438
  </head>
340
439
  <body>
341
- <main class="gallery-page" data-page-id="${slug}">
342
- <div class="gallery-board" data-board-id="${FIRST_BOARD}">
343
- </div>
440
+ <main class="gallery-page" data-page-id="${slug}">${inside}
344
441
  </main>
345
442
  </body>
346
443
  </html>
@@ -348,18 +445,33 @@ export function newPageHtml(slug, title, { lang = "zh-CN" } = {}) {
348
445
  }
349
446
 
350
447
  /**
351
- * Make custom page `slug` in workspace `root`: pages/<slug>/ with page.html (newPageHtml) and page.json
352
- * ({ title, icon? }). Throws (code EEXIST) when pageSlugRefused refuses the slug; resolves to its files.
448
+ * Whether page.html `html` is a blank page, one no one has written yet: its <body> holds an empty
449
+ * <main class="gallery-page"> and nothing else (comments and blanks aside), as the workbench's 新建页面
450
+ * writes it (newPageHtml's `blank`). The workbench then shows, in its middle, that an Agent writes it.
451
+ */
452
+ export function isBlankPage(html) {
453
+ const body = /<body\b[^>]*>([\s\S]*?)<\/body>/i.exec(String(html ?? "").replace(/<!--[\s\S]*?-->/g, ""))?.[1];
454
+ return body !== undefined && /^\s*<main\b[^>]*\bclass\s*=\s*["']?gallery-page["']?[^>]*>\s*<\/main>\s*$/i.test(body);
455
+ }
456
+
457
+ /**
458
+ * Make custom page `slug` in workspace `root`: pages/<slug>/ with page.html (newPageHtml: one empty
459
+ * board, or none where `blank`) and page.json ({ title, icon?, defaultCards?, hint? }: pageMetaOf), and its
460
+ * row in the sidebar's head right after page `after`'s, else last (placePageInHead; a head that cannot be
461
+ * written leaves it where a page the head names not goes). Throws (code EEXIST) when pageSlugRefused
462
+ * refuses the slug; resolves to its files.
353
463
  */
354
- export async function createPage(slug, title, { root, icon = null, lang } = {}) {
464
+ export async function createPage(slug, title, { root, icon = null, lang, blank = false, defaultCards = false, hint = false, after = null } = {}) {
355
465
  const refused = await pageSlugRefused(slug, root);
356
466
  if (refused) throw Object.assign(new Error(refused), { code: "EEXIST" });
357
467
  const files = pageFiles(slug, root);
358
468
  await fs.mkdir(layoutOf(root).customPages, { recursive: true });
359
469
  await fs.mkdir(files.folder, { recursive: false }); // fails when it was made meanwhile
360
470
  const name = title.replace(/\s+/g, " ").trim();
361
- await fs.writeFile(files.html, newPageHtml(slug, name, { lang }));
362
- await fs.writeFile(files.json, `${JSON.stringify({ title: name, ...(icon ? { icon } : {}) }, null, 2)}\n`);
471
+ await fs.writeFile(files.html, newPageHtml(slug, name, { lang, blank }));
472
+ const meta = { title: name, ...(icon ? { icon } : {}), ...(defaultCards ? { defaultCards: true } : {}), ...(hint ? { hint: true } : {}) };
473
+ await fs.writeFile(files.json, `${JSON.stringify(meta, null, 2)}\n`);
474
+ await placePageInHead(root, slug, after).catch(() => {});
363
475
  return files;
364
476
  }
365
477
 
@@ -0,0 +1,92 @@
1
+ // Pinyin as the "/" menu looks for a block by it (docs/editor.md, "In the menus"; the built-in blocks
2
+ // list their words in registry.mjs: 公式 is gongshi gs). A custom block's words are its manifest's, so
3
+ // that an author writes the pinyin of the whole name there (the check notes a name no word spells:
4
+ // cli/component-check.mjs wantsPinyin); the initials ("djs" for 倒计时, "daojishi") are not asked of
5
+ // anyone: they are made here, from a word that reads as pinyin and from the name itself.
6
+
7
+ // A syllable of pinyin, no tones: an initial or none, then a final. A word that can be read as pinyin
8
+ // is syllables one after another.
9
+ const SYLLABLE = "(?:(?:[zcs]h|[bpmfdtnlgkhjqxrzcsyw])?(?:[iuv]?(?:a(?:i|o|ng?)?|e(?:i|r|ng?)?|o(?:u|ng)?)|i(?:u|ng?)?|u(?:i|n)?|vn?))";
10
+ const ONE_SYLLABLE = new RegExp(`^${SYLLABLE}$`, "i");
11
+ const LONGEST_SYLLABLE = 6; // zhuang
12
+ const HAN = /\p{Script=Han}/u;
13
+
14
+ /**
15
+ * The syllables `word` reads as, fewest first, its apostrophes left out as an input method leaves
16
+ * them, or null for a word that is none. The fewest, since single finals read a word as more syllables
17
+ * than an input method composes it of (fenbiao as fen-bi-a-o, composed fen-biao).
18
+ */
19
+ export function syllablesOf(word) {
20
+ const letters = word.replaceAll("'", "");
21
+ const fewest = [0, ...Array(letters.length).fill(Infinity)];
22
+ const from = Array(letters.length + 1).fill(0);
23
+ for (let end = 1; end <= letters.length; end++) {
24
+ for (let start = Math.max(0, end - LONGEST_SYLLABLE); start < end; start++) {
25
+ if (fewest[start] + 1 < fewest[end] && ONE_SYLLABLE.test(letters.slice(start, end))) {
26
+ fewest[end] = fewest[start] + 1;
27
+ from[end] = start;
28
+ }
29
+ }
30
+ }
31
+ if (!letters || !Number.isFinite(fewest[letters.length])) return null;
32
+ const syllables = [];
33
+ for (let end = letters.length; end > 0; end = from[end]) syllables.unshift(letters.slice(from[end], end));
34
+ return syllables;
35
+ }
36
+
37
+ /** The fewest syllables of pinyin `word` reads as, or null for a word that is none. */
38
+ export const fewestSyllables = (word) => syllablesOf(word)?.length ?? null;
39
+
40
+ /** The first letters of the syllables of a word of pinyin ("daojishi" is "djs"), or null for a word that is none or one syllable. */
41
+ export function initialsOfWord(word) {
42
+ const syllables = syllablesOf(word.toLowerCase());
43
+ return syllables && syllables.length > 1 ? syllables.map((syllable) => syllable[0]).join("") : null;
44
+ }
45
+
46
+ // The characters that open each letter's run in the pinyin order of the platform's collation (阿 a, 八 b …
47
+ // 帀 z: no run for i, u or v), the classic way to find the first letter of a character's reading without a
48
+ // table. Known only where the platform's collation sorts Chinese by pinyin, which it is asked once.
49
+ const OPENERS = [..."阿八嚓哒妸发旮哈讥咔垃妈拿噢妑七呥仨他屲夕丫帀"];
50
+ const LETTERS = [..."abcdefghjklmnopqrstwxyz"];
51
+ let collator;
52
+
53
+ function sorted() {
54
+ if (collator !== undefined) return collator;
55
+ try {
56
+ const found = new Intl.Collator("zh-CN");
57
+ collator = OPENERS.every((each, at) => at === 0 || found.compare(OPENERS[at - 1], each) < 0) && found.compare("计", "时") < 0 ? found : null;
58
+ } catch {
59
+ collator = null;
60
+ }
61
+ return collator;
62
+ }
63
+
64
+ /** The first letter of a Chinese character's pinyin (its commonest reading), or null. */
65
+ function initialOf(character) {
66
+ const found = sorted();
67
+ if (!found || !HAN.test(character)) return null;
68
+ let letter = null;
69
+ for (let at = 0; at < OPENERS.length && found.compare(character, OPENERS[at]) >= 0; at++) letter = LETTERS[at];
70
+ return letter;
71
+ }
72
+
73
+ /** The initials of a name: a Chinese character's first letter of pinyin, a letter or digit as it is ("倒计时" is "djs"), none where the platform cannot tell. */
74
+ export function initialsOfName(name) {
75
+ let initials = "";
76
+ for (const character of name) {
77
+ const letter = initialOf(character) ?? (/[a-z0-9]/i.test(character) ? character.toLowerCase() : null);
78
+ if (letter) initials += letter;
79
+ }
80
+ return initials.length > 1 ? initials : "";
81
+ }
82
+
83
+ /**
84
+ * What "/" matches a custom block by (slash-menu matchScore: each word whole, or by its start): its names,
85
+ * its id and the manifest's words, then the initials of the name and of every word that reads as pinyin,
86
+ * so that 倒计时 is found by djs as 公式 is by gs.
87
+ */
88
+ export function searchWords({ name, id, words }) {
89
+ const given = `${name.zh} ${name.en} ${id} ${words}`.toLowerCase();
90
+ const initials = [initialsOfName(name.zh), ...words.split(/\s+/).filter(Boolean).map(initialsOfWord)].filter(Boolean);
91
+ return [given, ...new Set(initials)].join(" ");
92
+ }
@@ -0,0 +1,17 @@
1
+ // 演示视图 (the workbench's, src/workbench/app/features/present/): the controls the page keeps its keys for (a field to
2
+ // type in, an editable text, a block's instance: the reader's keys in them are theirs, Esc alone is the presentation's, unless the
3
+ // control used it), and 上一节 / 下一节, which the presentation does on a page of its own origin and a draft's page does itself
4
+ // (src/editor/page/present-shell.mjs), for the presentation cannot reach into a page of another origin.
5
+
6
+ export const KEEPS_KEYS = 'input:not([type="checkbox"], [type="radio"], [type="button"], [type="submit"], [type="reset"], [type="file"], [type="color"]), textarea, select, [contenteditable]:not([contenteditable="false"]), [data-doc-component]';
7
+
8
+ /**
9
+ * The report in window `win` scrolled to the next section (`direction` 1) or the one before (-1): the visible h2 and h1 of its main,
10
+ * placed 24px below the window's top; at the last, the page's end, at the first, its top.
11
+ */
12
+ export function stepSection(win, direction) {
13
+ const doc = win.document;
14
+ const tops = [...doc.querySelectorAll("main h2, main h1")].filter((node) => node.getClientRects().length).map((node) => node.getBoundingClientRect().top - 24);
15
+ const target = direction > 0 ? tops.find((top) => top > 4) : tops.filter((top) => top < -4).pop();
16
+ win.scrollTo({ top: target === undefined ? (direction > 0 ? doc.documentElement.scrollHeight : 0) : win.scrollY + target, behavior: "smooth" });
17
+ }
@@ -0,0 +1,70 @@
1
+ // The sidebar's head rows (docs/workbench.md "The sidebar", **Head**; the user's word of 2026-11-09):
2
+ // 新建, 搜索 and each custom page, one list the reader orders by dragging its rows (no row goes into
3
+ // another, none into the tree, no document among them), kept for the workspace in the outline file
4
+ // (.gallery/outline.json's `head`, which ./outline.mjs keeps as it writes the tree, so that every browser
5
+ // shows the order the reader gave). A row's key: "new" (新建), "search" (搜索), or "@<slug>" for a custom
6
+ // page (its address in the workbench, which no slug holds). Pure: the server's (shared/pages.mjs listPages,
7
+ // the workbench service's head) and the workbench's (its sidebar) alike.
8
+
9
+ /** The key of the head's 新建 row. */
10
+ export const HEAD_NEW = "new";
11
+ /** The key of the head's 搜索 row. */
12
+ export const HEAD_SEARCH = "search";
13
+ /** The key of custom page `slug`'s row: @<slug>. */
14
+ export const pageHeadKey = (slug) => `@${slug}`;
15
+ /** The custom page a head key names, or null (new, search). */
16
+ export const headPage = (key) => (typeof key === "string" && key.startsWith("@") ? key.slice(1) : null);
17
+ /** How many keys a stored head keeps at most (those of pages gone stay, so that one restored goes back to its place). */
18
+ export const MOST_HEAD = 200;
19
+
20
+ const KEY = /^(?:new|search|@[a-z0-9]+(?:-[a-z0-9]+)*)$/;
21
+
22
+ /**
23
+ * `value` as a head to store: an array of keys (new, search, @<slug>), each once, MOST_HEAD at most; null
24
+ * for anything else (not an array, a key that is none, one twice, too many).
25
+ */
26
+ export function storedHead(value) {
27
+ if (!Array.isArray(value) || value.length > MOST_HEAD) return null;
28
+ if (!value.every((key) => typeof key === "string" && KEY.test(key))) return null;
29
+ return new Set(value).size === value.length ? [...value] : null;
30
+ }
31
+
32
+ /**
33
+ * The head's rows in their order: the keys `stored` (a head as the outline file keeps it, or null) gives,
34
+ * those of pages that are not there passed over, then what it does not name: 新建 first and 搜索 after it
35
+ * where it names neither (as before any drag), and each page of `pages` (their slugs, in the order a page
36
+ * goes by when the reader gave it none: shared/pages.mjs inPageOrder) after the last row it names, in that
37
+ * order. Always holds new and search once each, and each page of `pages` once.
38
+ */
39
+ export function headOrder(stored, pages = []) {
40
+ const there = new Set(pages);
41
+ const named = (storedHead(stored) ?? []).filter((key) => key === HEAD_NEW || key === HEAD_SEARCH || there.has(headPage(key)));
42
+ if (!named.includes(HEAD_SEARCH)) named.unshift(HEAD_SEARCH);
43
+ if (!named.includes(HEAD_NEW)) named.splice(named.indexOf(HEAD_SEARCH), 0, HEAD_NEW);
44
+ for (const slug of pages) if (!named.includes(pageHeadKey(slug))) named.push(pageHeadKey(slug));
45
+ return named;
46
+ }
47
+
48
+ /**
49
+ * The head with the row `key` moved before the row `before` (null: last), the others as they were: what a
50
+ * drop on the head makes. Unchanged where `key` is not in it or is `before`.
51
+ */
52
+ export function movedInHead(head, key, before) {
53
+ if (!head.includes(key) || key === before) return [...head];
54
+ const rest = head.filter((one) => one !== key);
55
+ const at = before === null ? rest.length : rest.indexOf(before);
56
+ rest.splice(at < 0 ? rest.length : at, 0, key);
57
+ return rest;
58
+ }
59
+
60
+ /**
61
+ * The stored head `stored` (or null, when none is kept yet: `shown`, the head as the sidebar shows it now,
62
+ * stands for it) with page `slug`'s row put right after the row of `after` (a page's slug), else last: a
63
+ * new page's place. Keys of pages gone are kept where they stand.
64
+ */
65
+ export function headWithPage(stored, shown, slug, after = null) {
66
+ const head = (storedHead(stored) ?? storedHead(shown) ?? [HEAD_NEW, HEAD_SEARCH]).filter((key) => key !== pageHeadKey(slug));
67
+ const at = after ? head.indexOf(pageHeadKey(after)) : -1;
68
+ head.splice(at < 0 ? head.length : at + 1, 0, pageHeadKey(slug));
69
+ return head.slice(-MOST_HEAD);
70
+ }
@@ -37,6 +37,11 @@ export function isSiteIcon(value) {
37
37
  */
38
38
  export const WEB_SANDBOX = "allow-scripts allow-popups allow-top-navigation-by-user-activation allow-forms allow-same-origin allow-storage-access-by-user-activation allow-popups-to-escape-sandbox";
39
39
 
40
+ /** Login/error headings are not document names (also used for browser-provided clipboard titles). */
41
+ export function loginTitle(title) {
42
+ return /(?:^|[|·—–-]\s*)(?:钉钉)?统一身份认证\s*$|^(?:(?:登录|登入|sign[ -]?in|log[ -]?in)(?:$|\s*(?:[|·—–-]|to\b))|(?:401|403)(?:\s|$)|unauthorized|access denied|forbidden|无权访问|访问受限)|(?:[|·—–-]\s*)(?:登录|登入|sign[ -]?in|log[ -]?in)\s*$/i.test(title);
43
+ }
44
+
40
45
  const tidy = (value) => (typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "");
41
46
 
42
47
  /**
@@ -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};
@@ -1,5 +1,5 @@
1
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{a as je}from"./chunk-JEUK3ZO7.mjs";import{a as Te,b as Ne,c as Xe,d as Ze,g as Oe,h as et}from"./chunk-PUOQ65NC.mjs";import{g as Qe}from"./chunk-V6RIYQZZ.mjs";import{b as Se}from"./chunk-SXHBYZST.mjs";import{p as Ee}from"./chunk-HMYTM3GT.mjs";import{a as le}from"./chunk-HYMEEXKN.mjs";import{a as ve,f as Ye,p as ze}from"./chunk-FWNRPMCJ.mjs";import T from"node:fs/promises";import he from"node:os";import x from"node:path";import{pathToFileURL as Re}from"node:url";import{instancesOf as ot,isInstance as bt}from"../browser/shared/components/index.mjs";import{ATTRIBUTE as $t,FALLBACK as xt,INSTANCE as de,SLOT as vt,SLOT_PART as tt}from"../browser/shared/components/installed.mjs";import{jsonIn as Et}from"../browser/shared/json-layout.mjs";import{QUERY_ROUTE as St}from"../browser/shared/workspace-query.mjs";import{documentDirectory as jt,documentFiles as Tt,layoutOf as it,listDocumentSlugs as Nt,PACKAGE_MANIFEST as Ot}from"../browser/shared/workspace.mjs";var At="in a browser, under the export's policy",ce=(e=null)=>e?`the instances in ${e}, in a browser under the export's policy`:"the workspace's documents",q="component-check",ae=[360,736,1024],Ae=["light","dark"],Lt=[1024,360],nt=1e4,rt=6e4,se=3e4,Dn=12e4,lt=2e3,Rt=3e3,Dt=60,Mn=15e3,Mt=2,Pt=8,Ct=se/2,_t=/^(?:wasm-)?eval$/,It=new Set(["TargetCloseError","ConnectionClosedError"]),Pn=e=>e.evaluateOnNewDocument(Ft,St);function Ft(e){let t=globalThis.fetch;if(typeof t!="function"||globalThis.__galleryQueries)return;let i={pending:new Map,count:0};globalThis.__galleryQueries=i,globalThis.fetch=function(r,n){let a=t.apply(this,arguments);try{let p=new URL(typeof r=="string"?r:r?.url??String(r),location.href);if(p.pathname===e){let u=++i.count,w=/\/__gallery\/components\/([a-z0-9-]+\/[a-z0-9-]+)\/kit\.js/.exec(new Error().stack??"");i.pending.set(u,{kind:p.searchParams.get("kind"),block:w?w[1]:null,at:Date.now()});let l=()=>{i.pending.delete(u)};Promise.resolve(a).then(l,l)}}catch{}return a}}var oe=e=>String(e?.message??e).split(`
2
+ import{a as je}from"./chunk-JEUK3ZO7.mjs";import{a as Te,b as Ne,c as Xe,d as Ze,g as Oe,h as et}from"./chunk-PUOQ65NC.mjs";import{g as Qe}from"./chunk-ITOH5EGO.mjs";import{b as Se}from"./chunk-ZQS6H473.mjs";import{p as Ee}from"./chunk-MXUBKDRO.mjs";import{a as le}from"./chunk-HYMEEXKN.mjs";import{a as ve,f as Ye,p as ze}from"./chunk-HDWTZ763.mjs";import T from"node:fs/promises";import he from"node:os";import x from"node:path";import{pathToFileURL as Re}from"node:url";import{instancesOf as ot,isInstance as bt}from"../browser/shared/components/index.mjs";import{ATTRIBUTE as $t,FALLBACK as xt,INSTANCE as de,SLOT as vt,SLOT_PART as tt}from"../browser/shared/components/installed.mjs";import{jsonIn as Et}from"../browser/shared/json-layout.mjs";import{QUERY_ROUTE as St}from"../browser/shared/workspace-query.mjs";import{documentDirectory as jt,documentFiles as Tt,layoutOf as it,listDocumentSlugs as Nt,PACKAGE_MANIFEST as Ot}from"../browser/shared/workspace.mjs";var At="in a browser, under the export's policy",ce=(e=null)=>e?`the instances in ${e}, in a browser under the export's policy`:"the workspace's documents",q="component-check",ae=[360,736,1024],Ae=["light","dark"],Lt=[1024,360],nt=1e4,rt=6e4,se=3e4,Dn=12e4,lt=2e3,Rt=3e3,Dt=60,Mn=15e3,Mt=2,Pt=8,Ct=se/2,_t=/^(?:wasm-)?eval$/,It=new Set(["TargetCloseError","ConnectionClosedError"]),Pn=e=>e.evaluateOnNewDocument(Ft,St);function Ft(e){let t=globalThis.fetch;if(typeof t!="function"||globalThis.__galleryQueries)return;let i={pending:new Map,count:0};globalThis.__galleryQueries=i,globalThis.fetch=function(r,n){let a=t.apply(this,arguments);try{let p=new URL(typeof r=="string"?r:r?.url??String(r),location.href);if(p.pathname===e){let u=++i.count,w=/\/__gallery\/components\/([a-z0-9-]+\/[a-z0-9-]+)\/kit\.js/.exec(new Error().stack??"");i.pending.set(u,{kind:p.searchParams.get("kind"),block:w?w[1]:null,at:Date.now()});let l=()=>{i.pending.delete(u)};Promise.resolve(a).then(l,l)}}catch{}return a}}var oe=e=>String(e?.message??e).split(`
3
3
  `)[0],ct=(e,t)=>`<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${e}</title></head><body><main class="doc-page" data-doc-id="${e}">${t}
4
4
  </main></body></html>
5
5
  `,Bt=({example:e,template:t})=>`<p>example.html</p>${e}
@@ -1,2 +1,2 @@
1
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{a,b,c,d,e,f,g,h,i,j}from"./chunk-SXHBYZST.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-FJYN7RTH.mjs";import"./chunk-UBBRTJ2R.mjs";import"./chunk-FWNRPMCJ.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{g as changeLines,c as handedInSource,f as headerChanged,e as inStep,a as naturalize,d as pendingBlockIds,h as readOnlyTexts,j as scriptKept,b as shownSource,i as writingNotes};
2
+ import{a,b,c,d,e,f,g,h,i,j}from"./chunk-ZQS6H473.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-VFX2KNHP.mjs";import"./chunk-VS7JIGJW.mjs";import"./chunk-HDWTZ763.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{g as changeLines,c as handedInSource,f as headerChanged,e as inStep,a as naturalize,d as pendingBlockIds,h as readOnlyTexts,j as scriptKept,b as shownSource,i as writingNotes};
@@ -1,2 +1,2 @@
1
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{d as m,i as g,k as p}from"./chunk-NFMCP2HI.mjs";import{b as a,f as l,i as c}from"./chunk-4OZWXZE5.mjs";import"./chunk-5L7RYZCL.mjs";import{reportsDirectory as i}from"../browser/shared/paths.mjs";import{joinDefaultTree as b,joinedLine as k}from"../browser/shared/drafts.mjs";var D=5e3;async function v(f){let{positional:s,options:u}=l({options:["--no-browser"],argv:f});if(s.length>1)throw new a("Name one document at most.");let{slug:o=null,block:w=null}=s.length?await c(s[0]):{};console.log(`\u5DE5\u4F5C\u533A\uFF1A${i}`);let e=await g({slug:o,baseline:o!==null});if(e.ok||console.error(e.message),e.reason==="missing"){let{HOME:n,pageExists:d}=await import("../browser/shared/pages.mjs"),{missingPageSaid:E,HOME_NOT_MADE:r}=await import("./chunk-2NRBY6AJ.mjs");d(o,i)?console.error(`"${o}" is a custom page, not a document: gallery page open ${o} opens it and gives its address.`):o===n&&await E(o,i)===r&&console.error(r)}if(o!==null&&e.reason!=="missing"){let{roundBegins:n}=await import("./chunk-4AV456RQ.mjs");await n(o,e.ok?{}:{wait:D})}if(o!==null&&e.reason!=="missing"){let n=k(await b(i,o));n&&console.log(n)}e.ok||process.exit(1);let t=await p(o,e,w);for(let n of t)console.log(n);console.log(await m(t,u))}export{v as run};
2
+ import{d as m,i as g,k as p}from"./chunk-5XA2ZBAE.mjs";import{b as a,f as l,i as c}from"./chunk-4OZWXZE5.mjs";import"./chunk-5L7RYZCL.mjs";import{reportsDirectory as i}from"../browser/shared/paths.mjs";import{joinDefaultTree as b,joinedLine as k}from"../browser/shared/drafts.mjs";var D=5e3;async function v(f){let{positional:s,options:u}=l({options:["--no-browser"],argv:f});if(s.length>1)throw new a("Name one document at most.");let{slug:o=null,block:w=null}=s.length?await c(s[0]):{};console.log(`\u5DE5\u4F5C\u533A\uFF1A${i}`);let e=await g({slug:o,baseline:o!==null});if(e.ok||console.error(e.message),e.reason==="missing"){let{HOME:n,pageExists:d}=await import("../browser/shared/pages.mjs"),{missingPageSaid:E,HOME_NOT_MADE:r}=await import("./chunk-UJQJKNGK.mjs");d(o,i)?console.error(`"${o}" is a custom page, not a document: gallery page open ${o} opens it and gives its address.`):o===n&&await E(o,i)===r&&console.error(r)}if(o!==null&&e.reason!=="missing"){let{roundBegins:n}=await import("./chunk-SWUTTZCN.mjs");await n(o,e.ok?{}:{wait:D})}if(o!==null&&e.reason!=="missing"){let n=k(await b(i,o));n&&console.log(n)}e.ok||process.exit(1);let t=await p(o,e,w);for(let n of t)console.log(n);console.log(await m(t,u))}export{v as run};