@heycys/gallery 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. package/THIRD-PARTY-NOTICES.md +51 -1
  2. package/browser/editor/components/callout/index.mjs +40 -15
  3. package/browser/editor/components/columns/page.mjs +45 -32
  4. package/browser/editor/components/details/index.mjs +2 -1
  5. package/browser/editor/components/dialog/index.mjs +2 -1
  6. package/browser/editor/components/dialog/page.mjs +0 -18
  7. package/browser/editor/components/divider/index.mjs +1 -1
  8. package/browser/editor/components/embed/index.mjs +2 -2
  9. package/browser/editor/components/figure/page.mjs +10 -1
  10. package/browser/editor/components/file/page.mjs +2 -2
  11. package/browser/editor/components/flowchart/add.mjs +7 -6
  12. package/browser/editor/components/flowchart/page.mjs +1 -1
  13. package/browser/editor/components/heading/index.mjs +2 -0
  14. package/browser/editor/components/heading/page.mjs +6 -0
  15. package/browser/editor/components/highlight/index.mjs +4 -20
  16. package/browser/editor/components/icons.mjs +13 -2
  17. package/browser/editor/components/image/crop.mjs +6 -9
  18. package/browser/editor/components/image/page.mjs +2 -2
  19. package/browser/editor/components/installed/index.mjs +82 -29
  20. package/browser/editor/components/installed/page.mjs +146 -34
  21. package/browser/editor/components/installed/served.mjs +6 -5
  22. package/browser/editor/components/instances.mjs +18 -0
  23. package/browser/editor/components/kit.mjs +3 -0
  24. package/browser/editor/components/list/page.mjs +3 -6
  25. package/browser/editor/components/media/page.mjs +44 -14
  26. package/browser/editor/components/pages.mjs +2 -2
  27. package/browser/editor/components/registry.mjs +68 -33
  28. package/browser/editor/components/section/index.mjs +2 -1
  29. package/browser/editor/components/set-apart.mjs +24 -0
  30. package/browser/editor/components/table/index.mjs +64 -5
  31. package/browser/editor/components/table/page.mjs +30 -19
  32. package/browser/editor/page/annotate/anchors.mjs +4 -3
  33. package/browser/editor/page/annotate/card.mjs +3 -6
  34. package/browser/editor/page/annotate/index.mjs +61 -34
  35. package/browser/editor/page/autosave.mjs +357 -38
  36. package/browser/editor/page/beside.mjs +5 -5
  37. package/browser/editor/page/block-menu/colors.mjs +3 -1
  38. package/browser/editor/page/block-menu/index.mjs +8 -6
  39. package/browser/editor/page/blocks.mjs +16 -7
  40. package/browser/editor/page/board/client.mjs +57 -0
  41. package/browser/editor/page/board/hint.mjs +145 -0
  42. package/browser/editor/page/board/history.mjs +119 -0
  43. package/browser/editor/page/board/index.mjs +970 -0
  44. package/browser/editor/page/board/settings.mjs +209 -0
  45. package/browser/editor/page/caret.mjs +5 -2
  46. package/browser/editor/page/changes/bar.mjs +94 -0
  47. package/browser/editor/page/changes/ghost.mjs +206 -0
  48. package/browser/editor/page/changes/gone-words.mjs +94 -0
  49. package/browser/editor/page/changes/index.mjs +447 -0
  50. package/browser/editor/page/changes/words.mjs +27 -0
  51. package/browser/editor/page/clipboard/import/colors.mjs +3 -3
  52. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  53. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  54. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  55. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  56. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  57. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  58. package/browser/editor/page/clipboard/index.mjs +156 -26
  59. package/browser/editor/page/clipboard/own/blocks.mjs +25 -16
  60. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  61. package/browser/editor/page/clipboard/own/describe.mjs +16 -7
  62. package/browser/editor/page/clipboard/own/export.mjs +6 -6
  63. package/browser/editor/page/clipboard/own/selection.mjs +3 -3
  64. package/browser/editor/page/clipboard/own/writers.mjs +0 -11
  65. package/browser/editor/page/columns/index.mjs +48 -46
  66. package/browser/editor/page/commit.mjs +98 -25
  67. package/browser/editor/page/conflicts.mjs +13 -33
  68. package/browser/editor/page/convert.mjs +8 -7
  69. package/browser/editor/page/core.mjs +81 -27
  70. package/browser/editor/page/custom-page.mjs +106 -0
  71. package/browser/editor/page/drag/index.mjs +40 -24
  72. package/browser/editor/page/drag/preview.mjs +9 -4
  73. package/browser/editor/page/editor.mjs +120 -28
  74. package/browser/editor/page/emoji-picker/emoji-tab.mjs +22 -13
  75. package/browser/editor/page/emoji-picker/grid.mjs +7 -5
  76. package/browser/editor/page/emoji-picker/icon-tab.mjs +4 -4
  77. package/browser/editor/page/emoji-picker/icons.mjs +0 -1
  78. package/browser/editor/page/emoji-picker/index.mjs +11 -9
  79. package/browser/editor/page/emoji-picker/styles.mjs +156 -2
  80. package/browser/editor/page/emoji-picker/support.mjs +6 -4
  81. package/browser/editor/page/emoji-picker/upload.mjs +14 -13
  82. package/browser/editor/page/fills.mjs +5 -3
  83. package/browser/editor/page/fold/index.mjs +348 -0
  84. package/browser/editor/page/fold/rule.mjs +125 -0
  85. package/browser/editor/page/folded.mjs +23 -0
  86. package/browser/editor/page/format-bar/bar.mjs +2 -1
  87. package/browser/editor/page/format-bar/index.mjs +1 -1
  88. package/browser/editor/page/geometry.mjs +8 -6
  89. package/browser/editor/page/handle/index.mjs +22 -14
  90. package/browser/editor/page/hints/index.mjs +1 -1
  91. package/browser/editor/page/history.mjs +24 -4
  92. package/browser/editor/page/insert/index.mjs +30 -17
  93. package/browser/editor/page/links/index.mjs +5 -3
  94. package/browser/editor/page/links/pages.mjs +27 -9
  95. package/browser/editor/page/long-runs.mjs +75 -0
  96. package/browser/editor/page/look.mjs +69 -6
  97. package/browser/editor/page/margin.mjs +63 -27
  98. package/browser/editor/page/media-menu/index.mjs +6 -7
  99. package/browser/editor/page/menu.mjs +86 -31
  100. package/browser/editor/page/merge.mjs +24 -0
  101. package/browser/editor/page/notices.mjs +3 -3
  102. package/browser/editor/page/offered.mjs +1 -1
  103. package/browser/editor/page/outline/folds.mjs +11 -10
  104. package/browser/editor/page/outline/index.mjs +131 -45
  105. package/browser/editor/page/outline/list.mjs +23 -11
  106. package/browser/editor/page/outline/read.mjs +1 -1
  107. package/browser/editor/page/outline/strip.mjs +7 -1
  108. package/browser/editor/page/page-objects.mjs +13 -2
  109. package/browser/editor/page/panel.mjs +9 -13
  110. package/browser/editor/page/present-links.mjs +11 -2
  111. package/browser/editor/page/resize/index.mjs +42 -29
  112. package/browser/editor/page/selection/delete.mjs +8 -6
  113. package/browser/editor/page/selection/index.mjs +10 -3
  114. package/browser/editor/page/served.mjs +14 -0
  115. package/browser/editor/page/session.mjs +101 -6
  116. package/browser/editor/page/slash-menu/index.mjs +3 -3
  117. package/browser/editor/page/table/cell-bar.mjs +4 -1
  118. package/browser/editor/page/table/cells.mjs +103 -47
  119. package/browser/editor/page/table/icons.mjs +3 -1
  120. package/browser/editor/page/table/index.mjs +40 -16
  121. package/browser/editor/page/table/pictures.mjs +299 -0
  122. package/browser/editor/page/table/views.mjs +150 -0
  123. package/browser/editor/page/table/widths.mjs +9 -4
  124. package/browser/editor/page/text-select/index.mjs +43 -14
  125. package/browser/editor/page/todo-writes.mjs +75 -0
  126. package/browser/editor/page/tooltip.mjs +1 -1
  127. package/browser/editor/page/type-grid.mjs +4 -3
  128. package/browser/editor/page/typing/code-edges.mjs +63 -17
  129. package/browser/editor/page/typing/index.mjs +65 -9
  130. package/browser/editor/page/typing/leave.mjs +1 -1
  131. package/browser/editor/page/ui.mjs +13 -0
  132. package/browser/shared/annotations.mjs +8 -10
  133. package/browser/shared/block-packs.mjs +404 -0
  134. package/browser/shared/board-places.mjs +224 -0
  135. package/browser/shared/card-size.mjs +59 -0
  136. package/browser/shared/code-folds.mjs +5 -1
  137. package/browser/shared/colors.mjs +17 -14
  138. package/browser/shared/component-catalog.mjs +345 -54
  139. package/browser/shared/component-source.mjs +37 -0
  140. package/browser/shared/components/callout.mjs +4 -2
  141. package/browser/shared/components/index.mjs +72 -9
  142. package/browser/shared/components/installed.mjs +110 -8
  143. package/browser/shared/components/page.mjs +22 -3
  144. package/browser/shared/components/state.mjs +5 -1
  145. package/browser/shared/components/table.mjs +1 -1
  146. package/browser/shared/components/text.mjs +2 -2
  147. package/browser/shared/document-look.mjs +183 -0
  148. package/browser/shared/drafts.mjs +432 -0
  149. package/browser/shared/emoji/data/LICENSE-lucide.txt +43 -0
  150. package/browser/shared/emoji/data/README.md +4 -2
  151. package/browser/shared/emoji/data/categories.json +1 -1
  152. package/browser/shared/emoji/data/keywords-en.json +1 -1
  153. package/browser/shared/emoji/data/keywords-zh.json +1 -1
  154. package/browser/shared/emoji/data/names.json +1 -1
  155. package/browser/shared/emoji/data/positions.json +1 -1
  156. package/browser/shared/emoji/data/twemoji-index.json +1 -1
  157. package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
  158. package/browser/shared/emoji/data.mjs +10 -12
  159. package/browser/shared/emoji/emoji.mjs +8 -7
  160. package/browser/shared/emoji/icon-value.mjs +4 -3
  161. package/browser/shared/extension-sidebar.mjs +126 -0
  162. package/browser/shared/extensions.mjs +79 -0
  163. package/browser/shared/file-table.mjs +101 -0
  164. package/browser/shared/html-tree.mjs +11 -5
  165. package/browser/shared/i18n/en.mjs +374 -35
  166. package/browser/shared/json-layout.mjs +47 -0
  167. package/browser/shared/long-runs.mjs +39 -0
  168. package/browser/shared/net-cache.mjs +133 -0
  169. package/browser/shared/net-keys.mjs +32 -0
  170. package/browser/shared/net-policy.mjs +177 -0
  171. package/browser/shared/new-names.mjs +5 -2
  172. package/browser/shared/outline-shape.mjs +128 -0
  173. package/browser/shared/outline.mjs +458 -0
  174. package/browser/shared/pack-manifest.mjs +77 -0
  175. package/browser/shared/page-address.mjs +6 -3
  176. package/browser/shared/page-cards.mjs +308 -0
  177. package/browser/shared/page-links.mjs +69 -6
  178. package/browser/shared/pages.mjs +388 -0
  179. package/browser/shared/paths.mjs +6 -0
  180. package/browser/shared/preferences.mjs +10 -4
  181. package/browser/shared/preview-server.mjs +3 -7
  182. package/browser/shared/session.mjs +13 -5
  183. package/browser/shared/shipped-blocks.mjs +299 -0
  184. package/browser/shared/table-chart.mjs +33 -1
  185. package/browser/shared/todos.mjs +83 -0
  186. package/browser/shared/user-directory.mjs +24 -0
  187. package/browser/shared/versions.mjs +14 -0
  188. package/browser/shared/web-links.mjs +134 -0
  189. package/browser/shared/workspace-columns.mjs +120 -0
  190. package/browser/shared/workspace-look.mjs +91 -0
  191. package/browser/shared/workspace-name.mjs +154 -0
  192. package/browser/shared/workspace-query.mjs +125 -0
  193. package/browser/shared/workspace-registry.mjs +272 -0
  194. package/browser/shared/workspace.mjs +171 -20
  195. package/browser/shared/zoom.mjs +5 -5
  196. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  197. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  198. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  199. package/dist/chunk-3KJHLHUO.mjs +2 -0
  200. package/dist/chunk-3NJACR4W.mjs +2 -0
  201. package/dist/chunk-4AV456RQ.mjs +2 -0
  202. package/dist/chunk-4OZWXZE5.mjs +5 -0
  203. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  204. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  205. package/dist/chunk-6A53MZYZ.mjs +2 -0
  206. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  207. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  208. package/dist/{chunk-UXUK4PMC.mjs → chunk-6QBHU4DF.mjs} +1 -1
  209. package/dist/{chunk-6U2SLSDN.mjs → chunk-6VTU6KEO.mjs} +1 -1
  210. package/dist/chunk-B4P2CM2J.mjs +3 -0
  211. package/dist/chunk-BPL64HGA.mjs +4 -0
  212. package/dist/chunk-BVEFGE4B.mjs +2 -0
  213. package/dist/chunk-BZ5RAF7L.mjs +2 -0
  214. package/dist/chunk-CRJEG7MT.mjs +2 -0
  215. package/dist/{chunk-B3VTYBYR.mjs → chunk-D4CKRD3V.mjs} +91 -91
  216. package/dist/chunk-D4EURAX4.mjs +4 -0
  217. package/dist/chunk-DQCE7TJY.mjs +5 -0
  218. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  219. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  220. package/dist/{chunk-DB4WKGYF.mjs → chunk-ERLAIBOA.mjs} +2 -2
  221. package/dist/chunk-EURLTOTZ.mjs +4 -0
  222. package/dist/chunk-FJYN7RTH.mjs +4 -0
  223. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  224. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  225. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  226. package/dist/chunk-GY3FAUVM.mjs +2 -0
  227. package/dist/chunk-HLYV7RIH.mjs +2 -0
  228. package/dist/chunk-HMYTM3GT.mjs +15 -0
  229. package/dist/chunk-IA4PSIMD.mjs +2 -0
  230. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  231. package/dist/chunk-JVHHHGEA.mjs +3 -0
  232. package/dist/chunk-KTKHRC6H.mjs +2 -0
  233. package/dist/chunk-L7UP52JO.mjs +2 -0
  234. package/dist/chunk-MHW7ZKOU.mjs +2 -0
  235. package/dist/chunk-N76HWBA3.mjs +10 -0
  236. package/dist/chunk-N7AZZLGT.mjs +548 -0
  237. package/dist/chunk-NAMN3RAF.mjs +2 -0
  238. package/dist/{chunk-YXAUQPVK.mjs → chunk-NFMCP2HI.mjs} +3 -3
  239. package/dist/chunk-NG375ILQ.mjs +2 -0
  240. package/dist/chunk-NVRNLK25.mjs +6 -0
  241. package/dist/chunk-NWFBE2YA.mjs +2 -0
  242. package/dist/chunk-NWR2LT5B.mjs +2 -0
  243. package/dist/chunk-OJKMINIU.mjs +20 -0
  244. package/dist/chunk-OUUP3WKT.mjs +2 -0
  245. package/dist/chunk-OW5NWPOJ.mjs +10 -0
  246. package/dist/chunk-PAREWQGB.mjs +4 -0
  247. package/dist/chunk-POQSE4C2.mjs +2 -0
  248. package/dist/chunk-PUOQ65NC.mjs +2 -0
  249. package/dist/{chunk-Z52HTUW7.mjs → chunk-PVER7LSC.mjs} +2 -2
  250. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  251. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  252. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  253. package/dist/chunk-QKXMPUXC.mjs +2 -0
  254. package/dist/chunk-QOUEFUCH.mjs +16 -0
  255. package/dist/chunk-RFDZXFJF.mjs +13 -0
  256. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  257. package/dist/chunk-RMECZA2K.mjs +2 -0
  258. package/dist/{chunk-EUX7IC5J.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  259. package/dist/chunk-SX4LJYT6.mjs +8 -0
  260. package/dist/chunk-SXHBYZST.mjs +16 -0
  261. package/dist/chunk-TANZRINB.mjs +2 -0
  262. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  263. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  264. package/dist/chunk-UFNYFFKO.mjs +20 -0
  265. package/dist/chunk-UGWYKHBL.mjs +170 -0
  266. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  267. package/dist/chunk-UMHUSOPJ.mjs +2 -0
  268. package/dist/chunk-UNOYVIYR.mjs +2 -0
  269. package/dist/chunk-V6RIYQZZ.mjs +4 -0
  270. package/dist/chunk-VJPFVNNW.mjs +15 -0
  271. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  272. package/dist/chunk-VTKNEYT3.mjs +6 -0
  273. package/dist/chunk-VXACTLO3.mjs +3 -0
  274. package/dist/chunk-W3BGBJQW.mjs +2 -0
  275. package/dist/chunk-YI43RG4P.mjs +2 -0
  276. package/dist/chunk-YQGT4AB2.mjs +6 -0
  277. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  278. package/dist/{chunk-ACOPDCNB.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  279. package/dist/chunk-ZTHICSSF.mjs +2 -0
  280. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  281. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  282. package/dist/cli.mjs +1 -1
  283. package/dist/server.mjs +1 -1
  284. package/dist/{chunk-EVJ3BX3M.mjs → update-fetch.mjs} +1 -1
  285. package/dist/worker.mjs +1 -1
  286. package/kit/board.css +95 -0
  287. package/kit/board.js +250 -0
  288. package/kit/charts.js +5 -71
  289. package/kit/code.js +14 -14
  290. package/kit/math.js +18 -2
  291. package/kit/report.css +500 -151
  292. package/kit/runtime.js +1720 -170
  293. package/kit/state.js +10 -1
  294. package/kit/tables.js +172 -37
  295. package/kit/ui.js +166 -27
  296. package/package.json +2 -1
  297. package/packs/gallery/card/component.json +14 -0
  298. package/packs/gallery/card/example.html +10 -0
  299. package/packs/gallery/card/preview.svg +1 -0
  300. package/packs/gallery/card/style.css +6 -0
  301. package/packs/gallery/card/template.html +6 -0
  302. package/packs/gallery/card/writing.md +14 -0
  303. package/packs/gallery/chart-card/component.json +15 -0
  304. package/packs/gallery/chart-card/example.html +14 -0
  305. package/packs/gallery/chart-card/kit.js +130 -0
  306. package/packs/gallery/chart-card/preview.svg +1 -0
  307. package/packs/gallery/chart-card/style.css +42 -0
  308. package/packs/gallery/chart-card/template.html +13 -0
  309. package/packs/gallery/chart-card/writing.md +20 -0
  310. package/packs/gallery/chart-view/component.json +20 -0
  311. package/packs/gallery/chart-view/example.html +13 -0
  312. package/packs/gallery/chart-view/kit.js +355 -0
  313. package/packs/gallery/chart-view/preview.svg +1 -0
  314. package/packs/gallery/chart-view/style.css +94 -0
  315. package/packs/gallery/chart-view/template.html +3 -0
  316. package/packs/gallery/chart-view/writing.md +22 -0
  317. package/packs/gallery/countdown/component.json +18 -0
  318. package/packs/gallery/countdown/example.html +3 -0
  319. package/packs/gallery/countdown/kit.js +156 -0
  320. package/packs/gallery/countdown/preview.svg +1 -0
  321. package/packs/gallery/countdown/style.css +130 -0
  322. package/packs/gallery/countdown/template.html +3 -0
  323. package/packs/gallery/countdown/writing.md +22 -0
  324. package/packs/gallery/habit/component.json +14 -0
  325. package/packs/gallery/habit/example.html +3 -0
  326. package/packs/gallery/habit/kit.js +295 -0
  327. package/packs/gallery/habit/preview.svg +1 -0
  328. package/packs/gallery/habit/style.css +246 -0
  329. package/packs/gallery/habit/template.html +3 -0
  330. package/packs/gallery/habit/writing.md +21 -0
  331. package/packs/gallery/heatmap/component.json +18 -0
  332. package/packs/gallery/heatmap/example.html +3 -0
  333. package/packs/gallery/heatmap/kit.js +351 -0
  334. package/packs/gallery/heatmap/preview.svg +1 -0
  335. package/packs/gallery/heatmap/style.css +201 -0
  336. package/packs/gallery/heatmap/template.html +3 -0
  337. package/packs/gallery/heatmap/writing.md +21 -0
  338. package/packs/gallery/kanban/component.json +18 -0
  339. package/packs/gallery/kanban/example.html +12 -0
  340. package/packs/gallery/kanban/kit.js +540 -0
  341. package/packs/gallery/kanban/preview.svg +1 -0
  342. package/packs/gallery/kanban/style.css +212 -0
  343. package/packs/gallery/kanban/template.html +3 -0
  344. package/packs/gallery/kanban/writing.md +20 -0
  345. package/packs/gallery/metric/component.json +22 -0
  346. package/packs/gallery/metric/example.html +15 -0
  347. package/packs/gallery/metric/kit.js +248 -0
  348. package/packs/gallery/metric/preview.svg +1 -0
  349. package/packs/gallery/metric/style.css +92 -0
  350. package/packs/gallery/metric/template.html +3 -0
  351. package/packs/gallery/metric/writing.md +23 -0
  352. package/packs/gallery/note/component.json +14 -0
  353. package/packs/gallery/note/example.html +3 -0
  354. package/packs/gallery/note/kit.js +102 -0
  355. package/packs/gallery/note/preview.svg +1 -0
  356. package/packs/gallery/note/style.css +76 -0
  357. package/packs/gallery/note/template.html +3 -0
  358. package/packs/gallery/note/writing.md +18 -0
  359. package/packs/gallery/pack.json +8 -0
  360. package/packs/gallery/recent/component.json +13 -0
  361. package/packs/gallery/recent/example.html +1 -0
  362. package/packs/gallery/recent/kit.js +238 -0
  363. package/packs/gallery/recent/preview.svg +1 -0
  364. package/packs/gallery/recent/style.css +158 -0
  365. package/packs/gallery/recent/template.html +1 -0
  366. package/packs/gallery/recent/writing.md +15 -0
  367. package/packs/gallery/static/component.json +13 -0
  368. package/packs/gallery/static/example.html +11 -0
  369. package/packs/gallery/static/preview.svg +1 -0
  370. package/packs/gallery/static/style.css +138 -0
  371. package/packs/gallery/static/template.html +1 -0
  372. package/packs/gallery/static/writing.md +18 -0
  373. package/packs/gallery/stats/component.json +13 -0
  374. package/packs/gallery/stats/example.html +1 -0
  375. package/packs/gallery/stats/kit.js +166 -0
  376. package/packs/gallery/stats/preview.svg +1 -0
  377. package/packs/gallery/stats/style.css +151 -0
  378. package/packs/gallery/stats/template.html +1 -0
  379. package/packs/gallery/stats/writing.md +11 -0
  380. package/packs/gallery/today/component.json +12 -0
  381. package/packs/gallery/today/example.html +1 -0
  382. package/packs/gallery/today/kit.js +185 -0
  383. package/packs/gallery/today/preview.svg +1 -0
  384. package/packs/gallery/today/style.css +120 -0
  385. package/packs/gallery/today/template.html +1 -0
  386. package/packs/gallery/today/writing.md +14 -0
  387. package/packs/gallery/todos/component.json +19 -0
  388. package/packs/gallery/todos/example.html +3 -0
  389. package/packs/gallery/todos/kit.js +342 -0
  390. package/packs/gallery/todos/preview.svg +1 -0
  391. package/packs/gallery/todos/style.css +267 -0
  392. package/packs/gallery/todos/template.html +3 -0
  393. package/packs/gallery/todos/writing.md +30 -0
  394. package/packs/gallery/weather/component.json +19 -0
  395. package/packs/gallery/weather/example.html +3 -0
  396. package/packs/gallery/weather/kit.js +312 -0
  397. package/packs/gallery/weather/preview.svg +1 -0
  398. package/packs/gallery/weather/style.css +258 -0
  399. package/packs/gallery/weather/template.html +3 -0
  400. package/packs/gallery/weather/writing.md +26 -0
  401. package/packs/gallery-soft/pack.json +8 -0
  402. package/release.json +13 -11
  403. package/sdk/doc-kit.d.ts +404 -7
  404. package/sdk/doc-state.d.ts +2 -0
  405. package/skill/SKILL.md +71 -26
  406. package/skill/references/block-script.md +140 -0
  407. package/skill/references/building-blocks.md +257 -0
  408. package/skill/references/example-report.html +1 -1
  409. package/skill/references/extensions.md +59 -0
  410. package/skill/references/pages-and-data.md +221 -0
  411. package/skill/references/writing-html.md +9 -7
  412. package/skill/sdk/doc-kit.d.ts +404 -7
  413. package/skill/sdk/doc-state.d.ts +2 -0
  414. package/web/assets/index-BK2e_07w.js +13 -0
  415. package/web/assets/index-CWFI83mF.css +1 -0
  416. package/web/index.html +2 -2
  417. package/browser/editor/components/callout/page.mjs +0 -13
  418. package/dist/chunk-2PBALTR3.mjs +0 -57
  419. package/dist/chunk-3LA3EMDE.mjs +0 -15
  420. package/dist/chunk-5FRMCDPY.mjs +0 -16
  421. package/dist/chunk-5LEK2CBS.mjs +0 -2
  422. package/dist/chunk-5OFN5AHC.mjs +0 -2
  423. package/dist/chunk-5SIYS5HN.mjs +0 -2
  424. package/dist/chunk-5TEK3RAD.mjs +0 -2
  425. package/dist/chunk-6CYG3Z4V.mjs +0 -2
  426. package/dist/chunk-7GHT2UCA.mjs +0 -15
  427. package/dist/chunk-7QQBR7PB.mjs +0 -2
  428. package/dist/chunk-ATCJYAR4.mjs +0 -2
  429. package/dist/chunk-C7HWOBIF.mjs +0 -2
  430. package/dist/chunk-E7VYDG2X.mjs +0 -2
  431. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  432. package/dist/chunk-FQGQH4KM.mjs +0 -2
  433. package/dist/chunk-G7TOXONE.mjs +0 -6
  434. package/dist/chunk-GPF4YWWB.mjs +0 -9
  435. package/dist/chunk-GYQV7Y4J.mjs +0 -120
  436. package/dist/chunk-HMHK3EQ6.mjs +0 -2
  437. package/dist/chunk-HQQMJCQJ.mjs +0 -10
  438. package/dist/chunk-IERUOGC3.mjs +0 -6
  439. package/dist/chunk-ITNLYXXV.mjs +0 -3
  440. package/dist/chunk-L4DVONOG.mjs +0 -2
  441. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  442. package/dist/chunk-LC7YNQFG.mjs +0 -2
  443. package/dist/chunk-LHTFLRB6.mjs +0 -4
  444. package/dist/chunk-NFIBI4IP.mjs +0 -26
  445. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  446. package/dist/chunk-O52JMOCZ.mjs +0 -2
  447. package/dist/chunk-P2CAZPDP.mjs +0 -3
  448. package/dist/chunk-QMQUXTU4.mjs +0 -12
  449. package/dist/chunk-QVWVFC5K.mjs +0 -6
  450. package/dist/chunk-QYKCJ7FG.mjs +0 -8
  451. package/dist/chunk-QZY6Q64C.mjs +0 -2
  452. package/dist/chunk-TDQP3DKQ.mjs +0 -10
  453. package/dist/chunk-TX6JH7TR.mjs +0 -2
  454. package/dist/chunk-UNUEWH4S.mjs +0 -2
  455. package/dist/chunk-V4CPWKMZ.mjs +0 -2
  456. package/dist/chunk-VACRB2NE.mjs +0 -3
  457. package/dist/chunk-W63L7367.mjs +0 -7
  458. package/dist/chunk-W7CXPCRV.mjs +0 -6
  459. package/dist/chunk-XINNG4F2.mjs +0 -4
  460. package/dist/chunk-YL2DPWVS.mjs +0 -4
  461. package/dist/chunk-YZ5Z6H4W.mjs +0 -4
  462. package/dist/chunk-ZBIM6ARY.mjs +0 -216
  463. package/skill/references/building-components.md +0 -327
  464. package/web/assets/index-B6Ai3wsa.js +0 -13
  465. package/web/assets/index-SYPC3EZy.css +0 -1
@@ -0,0 +1,156 @@
1
+ // gallery/countdown (倒计时): the days between today, by the reader's clock, and the date its settings
2
+ // name: how many are left before it, 今天 on the day itself, how many have gone since after it. Its two
3
+ // settings, `title` (the name of the day) and `date` (YYYY-MM-DD), are the card's form (on a custom page
4
+ // the grip at the card's top right › 设置…), kept in the data part `settings`, a script of JSON, which the board writes and this block
5
+ // only reads: it writes nothing. Drawn from that part on every `source` (the form, an Agent's write, an
6
+ // undo) and again as a day begins (the kit's `clock`, "day"), so that the count moves at midnight. A
7
+ // date it cannot read, or none, asks for one; the words are the page's language (the UI's where Gallery
8
+ // says it, else the page's lang). Its head is the cards' (the kit's card tokens): the chip, an hourglass,
9
+ // before the day's name alone (春节, or 倒计时 where it has none), on one line; under it the count's label
10
+ // (还有, 已经过去) over the count, large, and its unit, the family's label over a number; its face says by
11
+ // `data-kind` which of left, today, since or none it shows.
12
+
13
+ const WORDS = {
14
+ zh: {
15
+ left: "还有",
16
+ since: "已经过去",
17
+ today: "就是今天",
18
+ days: (/** @type {number} */ count) => `${count}`,
19
+ unit: () => "天",
20
+ countdown: "倒计时",
21
+ pick: "选一个日期",
22
+ where: "右上角手柄 › 设置…",
23
+ weekdays: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
24
+ date: (/** @type {number} */ year, /** @type {number} */ month, /** @type {number} */ day, /** @type {string} */ weekday) => [`${year}年${month}月${day}日`, weekday],
25
+ },
26
+ en: {
27
+ left: "Days left",
28
+ since: "Days since",
29
+ today: "Today",
30
+ days: (/** @type {number} */ count) => `${count}`,
31
+ unit: (/** @type {number} */ count) => (count === 1 ? "day" : "days"),
32
+ countdown: "Countdown",
33
+ pick: "Pick a date",
34
+ where: "The grip at the top right › Settings…",
35
+ weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
36
+ date: (/** @type {number} */ year, /** @type {number} */ month, /** @type {number} */ day, /** @type {string} */ weekday) => [`${weekday},`, `${["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][month - 1]} ${day}, ${year}`],
37
+ },
38
+ };
39
+
40
+ /** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
41
+ function languageOf(/** @type {HTMLElement} */ root) {
42
+ const ui = root.getAttribute("data-ge-language");
43
+ return (ui !== null ? ui === "en" : /^en(?:-|$)/i.test(root.lang)) ? "en" : "zh";
44
+ }
45
+
46
+ /**
47
+ * A date written YYYY-MM-DD as { year, month, day }, or null for anything else (no such day too:
48
+ * 2027-02-30).
49
+ * @param {unknown} written
50
+ */
51
+ function dateOf(written) {
52
+ const found = typeof written === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(written) : null;
53
+ if (!found) return null;
54
+ const [year, month, day] = found.slice(1).map(Number);
55
+ const date = new Date(Date.UTC(year, month - 1, day));
56
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day ? { year, month, day } : null;
57
+ }
58
+
59
+ /**
60
+ * Where `now` (the reader's clock) stands to the date `written` (YYYY-MM-DD): { kind: "left", days }
61
+ * before it, { kind: "today", days: 0 } on it, { kind: "since", days } after it, by whole days of the
62
+ * calendar (counted in UTC from each day's date, so that a change of the clocks in between changes
63
+ * nothing); { kind: "none" } for a date it cannot read.
64
+ * @param {unknown} written
65
+ * @param {Date} now
66
+ * @returns {{ kind: "left" | "since" | "today", days: number } | { kind: "none" }}
67
+ */
68
+ function countdownOf(written, now) {
69
+ const date = dateOf(written);
70
+ if (!date) return { kind: "none" };
71
+ const days = Math.round((Date.UTC(date.year, date.month - 1, date.day) - Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())) / 86_400_000);
72
+ if (days === 0) return { kind: "today", days: 0 };
73
+ return days > 0 ? { kind: "left", days } : { kind: "since", days: -days };
74
+ }
75
+
76
+ /**
77
+ * What the card says, in `language`, of its `settings` (context.readJSON's, {} where none is set yet)
78
+ * at `now`: its head (the day's name, or 倒计时), the count's label (还有, 已经过去; "" with no count), its
79
+ * count (null on the day and with no date; `big`, the words standing there instead), the count's unit, and
80
+ * its foot, a list of words a line breaks between and never inside (the date, its weekday).
81
+ * @param {any} settings
82
+ * @param {Date} now
83
+ * @param {"zh" | "en"} language
84
+ */
85
+ function faceOf(settings, now, language) {
86
+ const words = WORDS[language];
87
+ const date = dateOf(settings?.date);
88
+ const named = typeof settings?.title === "string" ? settings.title.trim() : "";
89
+ const count = countdownOf(settings?.date, now);
90
+ const head = named || words.countdown;
91
+ if (!date || count.kind === "none") return { head, label: "", count: null, unit: "", big: words.pick, foot: [words.where] };
92
+ const weekday = words.weekdays[new Date(Date.UTC(date.year, date.month - 1, date.day)).getUTCDay()];
93
+ const foot = words.date(date.year, date.month, date.day, weekday);
94
+ if (count.kind === "today") return { head, label: "", count: null, unit: "", big: words.today, foot };
95
+ return { head, label: count.kind === "left" ? words.left : words.since, count: words.days(count.days), unit: words.unit(count.days), big: "", foot };
96
+ }
97
+
98
+ /**
99
+ * An element of the face, of `kind` (its class).
100
+ * @template {keyof HTMLElementTagNameMap} K
101
+ * @param {K} tag
102
+ * @param {string} kind
103
+ */
104
+ function made(tag, kind) {
105
+ const node = document.createElement(tag);
106
+ node.className = `gallery-countdown-${kind}`;
107
+ return node;
108
+ }
109
+
110
+ DocKit.component({
111
+ id: "gallery/countdown",
112
+ on: ["source", "clock"],
113
+ mount(host, context) {
114
+ const language = languageOf(document.documentElement);
115
+ const face = made("div", "face");
116
+ const head = made("p", "head");
117
+ const chip = made("span", "chip");
118
+ chip.setAttribute("aria-hidden", "true");
119
+ chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 7h11M6.5 17h11M6 20v-2a6 6 0 1 1 12 0v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1zM6 4v2a6 6 0 1 0 12 0V4a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z"/></svg>';
120
+ const named = made("span", "name");
121
+ head.append(chip, named);
122
+ const label = made("p", "label");
123
+ const middle = made("p", "count");
124
+ const foot = made("p", "foot");
125
+ face.append(head, label, middle, foot);
126
+ host.append(face);
127
+ const draw = (/** @type {string} */ reason = "", /** @type {unknown} */ detail = null) => {
128
+ if (reason === "clock" && detail !== "day") return; // a minute moves no count
129
+ const settings = context.readJSON("settings");
130
+ if (settings === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
131
+ if (settings === null || typeof settings !== "object" || Array.isArray(settings)) return context.fail(new Error("its settings are no object: { \"title\": \"…\", \"date\": \"YYYY-MM-DD\" }"));
132
+ const at = new Date();
133
+ const now = faceOf(settings, at, language);
134
+ face.dataset.kind = countdownOf(settings.date, at).kind;
135
+ named.textContent = now.head;
136
+ named.title = now.head;
137
+ label.textContent = now.label;
138
+ if (now.count === null) {
139
+ middle.classList.add("gallery-countdown-words");
140
+ middle.textContent = now.big;
141
+ } else {
142
+ middle.classList.remove("gallery-countdown-words");
143
+ const unit = made("span", "unit");
144
+ unit.textContent = now.unit;
145
+ middle.replaceChildren(now.count, unit);
146
+ }
147
+ foot.replaceChildren(...now.foot.flatMap((words, index) => {
148
+ const piece = made("span", "piece");
149
+ piece.textContent = words;
150
+ return index === 0 ? [piece] : [" ", piece];
151
+ }));
152
+ };
153
+ draw();
154
+ return { update: draw };
155
+ },
156
+ });
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="12" fill="#ffffff" stroke="#e7e6e3"/><rect x="30" y="24" width="14" height="14" rx="4.5" fill="#ebf2fa"/><g transform="translate(32 26) scale(.4167)" fill="none" stroke="#2e7cd1" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 7h11M6.5 17h11M6 20v-2a6 6 0 1 1 12 0v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1zM6 4v2a6 6 0 1 0 12 0V4a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z"/></g><rect x="50" y="28" width="24" height="6" rx="3" fill="#3b3a37"/><rect x="30" y="49" width="16" height="5" rx="2.5" fill="#c4c2bd"/><text x="28" y="84" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="34" font-weight="600" letter-spacing="-.5" fill="#3b3a37">89</text><rect x="72" y="74" width="11" height="9" rx="2.5" fill="#c4c2bd"/><rect x="30" y="93" width="82" height="5" rx="2.5" fill="#c4c2bd"/></svg>
@@ -0,0 +1,130 @@
1
+ /* Every selector starts at the instance and goes down a step at a time (>), so that nothing here
2
+ reaches the rest of the page. The face fills its card on a custom page (the board draws the frame), in
3
+ the kit's card tokens: its head at the top (the chip, then the day's name on one line, cut with an
4
+ ellipsis, its end clear of the card's grip), the count's label small and muted over the count, large
5
+ (the cards' hero size), its unit small and muted after it, the two in the middle of the room between the
6
+ head and the date, which is muted at the bottom. On the day itself the words stand in the accent. With
7
+ no date yet, the cards' empty face: a quiet picture (a calendar) in a soft disc over a muted line, where
8
+ to set one under it, in the middle of the room under the head. Light and dark. */
9
+ [data-doc-component="gallery/countdown"] {
10
+ container-type: inline-size;
11
+ > .gallery-countdown-face {
12
+ display: flex;
13
+ flex-direction: column;
14
+ justify-content: flex-start;
15
+ gap: 6px;
16
+ box-sizing: border-box;
17
+ height: 100%;
18
+ min-height: 0;
19
+ color: var(--gallery-text);
20
+ }
21
+ > .gallery-countdown-face > p { margin: 0; }
22
+ > .gallery-countdown-face > .gallery-countdown-head {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 8px;
26
+ min-width: 0;
27
+ padding-inline-end: var(--gallery-grip-room, 0px);
28
+ font-size: var(--gallery-title-size);
29
+ line-height: var(--gallery-title-line);
30
+ font-weight: 600;
31
+ color: var(--gallery-text-strong);
32
+ }
33
+ > .gallery-countdown-face > .gallery-countdown-head > .gallery-countdown-chip {
34
+ flex: none;
35
+ display: inline-flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ width: var(--gallery-chip-size);
39
+ height: var(--gallery-chip-size);
40
+ border-radius: var(--gallery-chip-radius);
41
+ background: var(--gallery-accent-soft);
42
+ color: var(--gallery-accent);
43
+ }
44
+ > .gallery-countdown-face > .gallery-countdown-head > .gallery-countdown-chip > svg { width: var(--gallery-chip-icon); height: var(--gallery-chip-icon); }
45
+ > .gallery-countdown-face > .gallery-countdown-head > .gallery-countdown-name {
46
+ min-width: 0;
47
+ overflow: hidden;
48
+ white-space: nowrap;
49
+ text-overflow: ellipsis;
50
+ }
51
+ /* The count's label over it, the two in the middle of the room between the head and the date. */
52
+ > .gallery-countdown-face > .gallery-countdown-label {
53
+ margin-top: auto;
54
+ font-size: var(--gallery-meta-size);
55
+ line-height: 16px;
56
+ color: var(--doc-muted);
57
+ }
58
+ > .gallery-countdown-face > .gallery-countdown-label:empty { display: none; }
59
+ > .gallery-countdown-face > .gallery-countdown-label:empty + .gallery-countdown-count { margin-top: auto; }
60
+ > .gallery-countdown-face > .gallery-countdown-count {
61
+ margin-top: -4px;
62
+ font-size: var(--gallery-hero-size);
63
+ line-height: 1.1;
64
+ font-weight: var(--gallery-numeral-weight);
65
+ letter-spacing: var(--gallery-numeral-tracking);
66
+ color: var(--gallery-text-strong);
67
+ font-variant-numeric: tabular-nums;
68
+ white-space: nowrap;
69
+ }
70
+ > .gallery-countdown-face > .gallery-countdown-count > .gallery-countdown-unit {
71
+ margin-inline-start: var(--gallery-unit-gap);
72
+ font-size: max(12px, var(--gallery-unit-size));
73
+ font-weight: 500;
74
+ letter-spacing: 0;
75
+ color: var(--doc-muted);
76
+ }
77
+ /* Today, or no date yet: words where the count stands, smaller; the day itself in the accent. */
78
+ > .gallery-countdown-face > .gallery-countdown-count.gallery-countdown-words {
79
+ font-size: min(var(--gallery-figure-size), 13cqi);
80
+ line-height: 1.2;
81
+ letter-spacing: -.01em;
82
+ white-space: normal;
83
+ }
84
+ > .gallery-countdown-face[data-kind="today"] > .gallery-countdown-count { color: var(--gallery-accent); }
85
+ /* No date yet: the cards' empty face (as todos' and recent's), the line and where to set it in the middle. */
86
+ > .gallery-countdown-face[data-kind="none"] { justify-content: flex-start; }
87
+ > .gallery-countdown-face[data-kind="none"] > .gallery-countdown-count {
88
+ display: flex;
89
+ flex-direction: column;
90
+ align-items: center;
91
+ gap: 8px;
92
+ margin-top: auto;
93
+ font-size: 13px;
94
+ line-height: 1.45;
95
+ font-weight: 400;
96
+ letter-spacing: 0;
97
+ color: var(--doc-muted);
98
+ text-align: center;
99
+ }
100
+ > .gallery-countdown-face[data-kind="none"] > .gallery-countdown-count::before {
101
+ content: "";
102
+ flex: none;
103
+ width: 44px;
104
+ height: 44px;
105
+ margin-bottom: 2px;
106
+ background: currentColor;
107
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 44'%3E%3Ccircle cx='22' cy='22' r='22' fill-opacity='.14'/%3E%3Cg transform='translate(11 11) scale(.9167)' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7z'/%3E%3Cpath d='M16 3v4M8 3v4M4 11h16'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
108
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 44'%3E%3Ccircle cx='22' cy='22' r='22' fill-opacity='.14'/%3E%3Cg transform='translate(11 11) scale(.9167)' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7z'/%3E%3Cpath d='M16 3v4M8 3v4M4 11h16'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
109
+ }
110
+ > .gallery-countdown-face[data-kind="none"] > .gallery-countdown-foot { margin-top: 0; margin-bottom: auto; text-align: center; opacity: .8; }
111
+ > .gallery-countdown-face > .gallery-countdown-foot {
112
+ margin-top: auto;
113
+ font-size: var(--gallery-meta-size);
114
+ line-height: 1.45;
115
+ color: var(--doc-muted);
116
+ }
117
+ /* The date and its weekday break between them, never inside either. */
118
+ > .gallery-countdown-face > .gallery-countdown-foot > .gallery-countdown-piece { display: inline-block; }
119
+ }
120
+ /* On a custom page the face is the card's: as high as what the card holds inside its padding, and a
121
+ container of that height too. A narrow card (two cells wide) shows the date alone, its weekday left
122
+ out, which would take a second line its two rows of cells have no room for; a low card's empty face
123
+ says where to set a date in its words alone, the picture over them left out. */
124
+ .gallery-card > [data-doc-component="gallery/countdown"] { height: 100%; container-type: size; }
125
+ @container (max-width: 180px) {
126
+ [data-doc-component="gallery/countdown"] > .gallery-countdown-face:not([data-kind="none"]) > .gallery-countdown-foot > .gallery-countdown-piece ~ .gallery-countdown-piece { display: none; }
127
+ }
128
+ @container (max-height: 150px) {
129
+ [data-doc-component="gallery/countdown"] > .gallery-countdown-face[data-kind="none"] > .gallery-countdown-count::before { display: none; }
130
+ }
@@ -0,0 +1,3 @@
1
+ <div data-doc-component="gallery/countdown">
2
+ <script type="application/json">{}</script>
3
+ </div>
@@ -0,0 +1,22 @@
1
+ # 倒计时 (gallery/countdown)
2
+
3
+ A card for a custom page (its places are `page`): how many days are left until a date, 就是今天 on the day, and how many days have gone since after it. Use it for a day the user waits for or counts from (a holiday, a deadline, an anniversary).
4
+
5
+ ## Settings
6
+
7
+ Its two settings are the card's form, which the user opens from the grip at the card's top right › 设置…:
8
+
9
+ | Key | Label | Type | What it is |
10
+ | --- | --- | --- | --- |
11
+ | `title` | 名称 | text | the name of the day, as 春节 or 项目上线; left empty, the card says 还有 / 已经过去 alone |
12
+ | `date` | 日期 | date | the day, `YYYY-MM-DD` |
13
+
14
+ They are kept in its data part `settings`, a `<script type="application/json">`, as example.html does: `{"title":"春节","date":"2027-02-06"}` (on one line and compact, as the card's 设置… writes it back). Write them there when the user tells you the day, or `gallery page add <slug> gallery/countdown --set date=2027-02-06 --set title=春节` with the add; the date is the one setting it cannot show anything without (`required` in its manifest): ask the user for it when they did not say which day. `{}` (template.html, what a menu adds) is a card with no date yet, which asks for one (右上角手柄 › 设置…), and `gallery page check` notes it. The block reads them and writes nothing.
15
+
16
+ ## How to write it
17
+
18
+ Put it on a custom page as a card of its board, `<section class="gallery-card" data-card-id="…" data-x data-y data-w data-h>`, holding one instance, after `gallery block add countdown`, the instance as template.html has it, then its settings as above. Sizes: 2 × 2 (the default) and 4 × 2.
19
+
20
+ ## How it looks
21
+
22
+ The day's name at the top beside an hourglass's chip, on one line (春节; 倒计时 where it has none), under it 还有 (or 已经过去) over the count of days, large, and the date with its weekday at the bottom, muted; on the day itself 就是今天 in the accent; in the kit's card tokens (as every Gallery card: a head of a tinted chip and a title, its end clear of the card's grip), light and dark. The count moves by itself at midnight (it hears the kit's clock). The script runs with the rights of the page's own scripts, as any block's does.
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "gallery/habit",
3
+ "apiVersion": 1,
4
+ "version": "1.2.0",
5
+ "name": { "zh": "习惯打卡", "en": "Habit tracker" },
6
+ "description": { "zh": "每天打一次卡,显示连续了几天和最近一周,名字在卡片上直接改", "en": "A check-in a day, the streak and the last seven days; the habit's name is edited on the card" },
7
+ "words": "xiguandaka xiguan daka habit streak checkin tracker 习惯 打卡 连续 坚持",
8
+ "parts": {
9
+ "habit": { "at": ":scope > script", "edit": "data" }
10
+ },
11
+ "runtime": { "generated": [":scope > .gallery-habit-face"] },
12
+ "places": ["page"],
13
+ "card": { "sizes": [[4, 2], [2, 2]], "resize": true }
14
+ }
@@ -0,0 +1,3 @@
1
+ <div data-doc-component="gallery/habit">
2
+ <script type="application/json">{"name":"每天读书半小时","days":["2026-10-30","2026-10-31","2026-11-02","2026-11-03","2026-11-04","2026-11-05","2026-11-06"]}</script>
3
+ </div>
@@ -0,0 +1,295 @@
1
+ // gallery/habit (习惯打卡): one habit, checked in once a day. Its data part, a script of JSON, holds the
2
+ // habit's name and the days it was done, each YYYY-MM-DD by the reader's clock, in order:
3
+ // { "name": "喝八杯水", "days": ["2026-11-05", "2026-11-06"] }. The card draws the name in a field of its
4
+ // own (DocKit.input: written as the reader presses Enter or leaves it, never while an input method
5
+ // composes), the streak (the days in a row up to today, or up to yesterday while today is not checked
6
+ // yet, so that a streak lasts the day it is to be kept), how many days in all, the last seven days as
7
+ // dots, where the card has room the last 30 days as a ring (`rateOf`: the days done of them in its
8
+ // middle), and a button that checks today in, or takes today's check-in back when it is there. Each press
9
+ // and each name is one context.editJSON, one undo step, the JSON written back on one line as it was:
10
+ // what the reader does is in the file, and nothing else is (the streak and the dots are worked out as
11
+ // it draws). It draws from the part on every `source` (an undo, an Agent's write) and again as a day
12
+ // begins (the kit's `clock`, "day"), so that "today" moves at midnight. Where nothing takes its writes
13
+ // (an export, a page that has not come to edit yet: context.canEdit() false) the name is read-only and
14
+ // the button a plain line saying whether today is done. Its words are the page's language (the UI's
15
+ // where Gallery says it, else the page's lang). Its head is the cards' (the kit's card tokens): the chip,
16
+ // a flame for the streak, before the name, the days in all its quiet meta at the end; the streak is its
17
+ // label (连续打卡) over its count, large, and its unit. A habit never checked in (no days at all) shows
18
+ // neither the streak nor the ring, two zeros, but a muted line inviting the first (the face's data-fresh),
19
+ // the week and the button as ever; they come back with the first check-in. Start a block of your own from
20
+ // it with gallery block new <id> --recipe habit.
21
+
22
+ const WORDS = {
23
+ zh: {
24
+ name: "给习惯起个名字",
25
+ check: "打卡",
26
+ checked: "已打卡",
27
+ undo: "再按一下,撤回今天的打卡",
28
+ todayDone: "今天已打卡",
29
+ todayOpen: "今天还没打卡",
30
+ streak: "连续打卡",
31
+ unit: () => "天",
32
+ total: (/** @type {number} */ days) => `共 ${days} 天`,
33
+ rate: "近 30 天",
34
+ fresh: "还没打过卡,今天开始第 1 天",
35
+ rateSaid: (/** @type {number} */ done) => `近 30 天打卡 ${done} 天`,
36
+ week: "最近七天",
37
+ weekdays: ["日", "一", "二", "三", "四", "五", "六"],
38
+ today: "今",
39
+ day: (/** @type {number} */ month, /** @type {number} */ day, /** @type {boolean} */ done) => `${month}月${day}日${done ? ",已打卡" : ",没打卡"}`,
40
+ },
41
+ en: {
42
+ name: "Name this habit",
43
+ check: "Check in",
44
+ checked: "Done",
45
+ undo: "Press again to take today's check-in back",
46
+ todayDone: "Done today",
47
+ todayOpen: "Not done today",
48
+ streak: "Streak",
49
+ unit: (/** @type {number} */ days) => (days === 1 ? "day" : "days"),
50
+ total: (/** @type {number} */ days) => `${days} ${days === 1 ? "day" : "days"} in all`,
51
+ rate: "30 days",
52
+ fresh: "No check-ins yet: today can be day 1",
53
+ rateSaid: (/** @type {number} */ done) => `Done ${done} of the last 30 days`,
54
+ week: "The last seven days",
55
+ weekdays: ["S", "M", "T", "W", "T", "F", "S"],
56
+ today: "Today",
57
+ day: (/** @type {number} */ month, /** @type {number} */ day, /** @type {boolean} */ done) => `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month - 1]} ${day}${done ? ", done" : ", not done"}`,
58
+ },
59
+ };
60
+
61
+ /** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
62
+ function languageOf(/** @type {HTMLElement} */ root) {
63
+ const ui = root.getAttribute("data-ge-language");
64
+ return (ui !== null ? ui === "en" : /^en(?:-|$)/i.test(root.lang)) ? "en" : "zh";
65
+ }
66
+
67
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
68
+
69
+ /**
70
+ * A day of the reader's clock as the data writes it, YYYY-MM-DD.
71
+ * @param {Date} date
72
+ */
73
+ function dayOf(date) {
74
+ const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
75
+ return `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}`;
76
+ }
77
+
78
+ /**
79
+ * The day `count` days after `day` (before it for a count below 0), both YYYY-MM-DD, by the calendar
80
+ * (counted in UTC, so that a change of the clocks changes nothing).
81
+ * @param {string} day
82
+ * @param {number} count
83
+ */
84
+ function dayAfter(day, count) {
85
+ const [year, month, date] = day.split("-").map(Number);
86
+ const moved = new Date(Date.UTC(year, month - 1, date + count));
87
+ const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
88
+ return `${moved.getUTCFullYear()}-${two(moved.getUTCMonth() + 1)}-${two(moved.getUTCDate())}`;
89
+ }
90
+
91
+ /**
92
+ * The habit `data` is (context.readJSON's, or the draft context.editJSON hands a change): its name ("" for
93
+ * none yet) and its days, as they stand; throws saying why it is none, and the block draws nothing over it.
94
+ * @param {any} data
95
+ * @returns {{ name: string, days: string[] }}
96
+ */
97
+ function readHabit(data) {
98
+ if (data === null || typeof data !== "object" || Array.isArray(data)) throw new Error("its data is no object: a habit holds { \"name\": \"…\", \"days\": [\"YYYY-MM-DD\", …] }");
99
+ const name = data.name ?? "";
100
+ if (typeof name !== "string") throw new Error("its name is no string");
101
+ const days = data.days ?? [];
102
+ if (!Array.isArray(days) || !days.every((day) => typeof day === "string" && DAY.test(day))) throw new Error("its days are no list of dates written YYYY-MM-DD");
103
+ return { name, days };
104
+ }
105
+
106
+ /**
107
+ * The days in a row that end today, or yesterday while today is not checked yet (a streak lasts the day
108
+ * it is to be kept); 0 when neither was done.
109
+ * @param {string[]} days
110
+ * @param {string} today
111
+ */
112
+ function streakOf(days, today) {
113
+ const done = new Set(days);
114
+ let day = done.has(today) ? today : dayAfter(today, -1);
115
+ let count = 0;
116
+ while (done.has(day)) {
117
+ count += 1;
118
+ day = dayAfter(day, -1);
119
+ }
120
+ return count;
121
+ }
122
+
123
+ /**
124
+ * `days` with `today` checked in, or taken back where it was: in order, each once.
125
+ * @param {string[]} days
126
+ * @param {string} today
127
+ */
128
+ function toggled(days, today) {
129
+ const done = new Set(days);
130
+ if (done.has(today)) done.delete(today);
131
+ else done.add(today);
132
+ return [...done].sort();
133
+ }
134
+
135
+ /**
136
+ * The seven days that end today, the oldest first: each its day (YYYY-MM-DD), its weekday (0 for
137
+ * Sunday) and whether it was done.
138
+ * @param {string[]} days
139
+ * @param {string} today
140
+ */
141
+ function weekOf(days, today) {
142
+ const done = new Set(days);
143
+ return Array.from({ length: 7 }, (_, index) => {
144
+ const day = dayAfter(today, index - 6);
145
+ const [year, month, date] = day.split("-").map(Number);
146
+ return { day, weekday: new Date(Date.UTC(year, month - 1, date)).getUTCDay(), done: done.has(day) };
147
+ });
148
+ }
149
+
150
+ /**
151
+ * How many of the `span` days that end today were done (the ring's count: 30 days by default).
152
+ * @param {string[]} days
153
+ * @param {string} today
154
+ * @param {number} [span]
155
+ */
156
+ function rateOf(days, today, span = 30) {
157
+ const first = dayAfter(today, 1 - span);
158
+ return new Set(days.filter((day) => day >= first && day <= today)).size;
159
+ }
160
+
161
+ /**
162
+ * Today checked in, or taken back: one write (context.editJSON), what a press of the button calls, and
163
+ * the example too. Whether it was taken.
164
+ * @param {DocComponentContext} context
165
+ * @param {string} today
166
+ */
167
+ function pressToday(context, today) {
168
+ return context.editJSON("habit", (data) => {
169
+ data.days = toggled(readHabit(data).days, today);
170
+ });
171
+ }
172
+
173
+ /**
174
+ * The habit named `name`, one write (context.editJSON): what the name's field calls once the reader is
175
+ * done with it. Whether it was taken.
176
+ * @param {DocComponentContext} context
177
+ * @param {string} name
178
+ */
179
+ function rename(context, name) {
180
+ return context.editJSON("habit", (data) => {
181
+ readHabit(data);
182
+ data.name = name.trim();
183
+ });
184
+ }
185
+
186
+ /**
187
+ * An element of the face, of `kind` (its class).
188
+ * @template {keyof HTMLElementTagNameMap} K
189
+ * @param {K} tag
190
+ * @param {string} kind
191
+ */
192
+ function made(tag, kind) {
193
+ const node = document.createElement(tag);
194
+ node.className = `gallery-habit-${kind}`;
195
+ return node;
196
+ }
197
+
198
+ DocKit.component({
199
+ id: "gallery/habit",
200
+ on: ["source", "editable", "clock"],
201
+ mount(host, context) {
202
+ const words = WORDS[languageOf(document.documentElement)];
203
+ const face = made("div", "face");
204
+ const head = made("div", "head");
205
+ const chip = made("span", "chip");
206
+ chip.setAttribute("aria-hidden", "true");
207
+ chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 10.9c2.3-3.3.2-7.8-1-8.9 0 3.4-2.2 5.3-3.7 6.7C5.9 10.1 5 12.3 5 14.3 5 18 8.1 21 12 21s7-3 7-6.7c0-1.7-1.2-4.4-2.3-5.6-2.1 3.4-3.3 3.4-4.7 2.2"/></svg>';
208
+ const name = made("input", "name");
209
+ name.type = "text";
210
+ name.placeholder = words.name;
211
+ name.spellcheck = false;
212
+ name.setAttribute("aria-label", words.name);
213
+ const total = made("span", "total");
214
+ head.append(chip, name, total);
215
+ const press = made("button", "check");
216
+ press.type = "button";
217
+ // The streak: its label over its count, the count large and its unit after it.
218
+ const streak = made("p", "streak");
219
+ const label = made("span", "label");
220
+ label.textContent = words.streak;
221
+ const count = made("strong", "count");
222
+ const unit = made("span", "unit");
223
+ streak.append(label, " ", count, " ", unit);
224
+ // The last 30 days as a ring, its count in the middle (shown where the card has room for it).
225
+ const ring = made("div", "ring");
226
+ ring.setAttribute("role", "img");
227
+ ring.innerHTML = '<svg viewBox="0 0 100 100" aria-hidden="true"><circle class="gallery-habit-track" cx="50" cy="50" r="43"/><circle class="gallery-habit-arc" cx="50" cy="50" r="43" pathLength="100" transform="rotate(-90 50 50)"/></svg>';
228
+ const rate = made("span", "rate");
229
+ const rateLabel = made("span", "rate-label");
230
+ rateLabel.textContent = words.rate;
231
+ ring.append(rate, rateLabel);
232
+ const week = made("ol", "week");
233
+ week.setAttribute("aria-label", words.week);
234
+ // Never checked in: this line where the streak stands, no ring.
235
+ const fresh = made("p", "fresh");
236
+ fresh.textContent = words.fresh;
237
+ face.append(head, ring, streak, fresh, press, week);
238
+ host.append(face);
239
+ const named = DocKit.input(name, { signal: context.signal, commit: (value) => rename(context, value) });
240
+ press.addEventListener("click", () => {
241
+ if (context.canEdit()) pressToday(context, dayOf(new Date()));
242
+ }, { signal: context.signal });
243
+
244
+ const draw = (/** @type {string} */ reason = "", /** @type {unknown} */ detail = null) => {
245
+ if (reason === "clock" && detail !== "day") return; // a minute moves no day
246
+ const data = context.readJSON("habit");
247
+ if (data === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
248
+ let habit;
249
+ try {
250
+ habit = readHabit(data);
251
+ } catch (error) {
252
+ return context.fail(error);
253
+ }
254
+ const today = dayOf(new Date());
255
+ const writes = context.canEdit();
256
+ const done = habit.days.includes(today);
257
+ name.readOnly = !writes;
258
+ named.set(habit.name);
259
+ // Where nothing is written, the button is a line saying how today stands: nothing to press.
260
+ press.disabled = !writes;
261
+ press.classList.toggle("gallery-habit-done", done);
262
+ press.textContent = writes ? (done ? `✓ ${words.checked}` : words.check) : (done ? words.todayDone : words.todayOpen);
263
+ press.title = writes && done ? words.undo : "";
264
+ press.setAttribute("aria-pressed", String(done));
265
+ face.toggleAttribute("data-fresh", habit.days.length === 0);
266
+ const days = streakOf(habit.days, today);
267
+ count.textContent = String(days);
268
+ unit.textContent = words.unit(days);
269
+ total.textContent = habit.days.length > 0 ? words.total(new Set(habit.days).size) : "";
270
+ const lately = rateOf(habit.days, today);
271
+ rate.textContent = String(lately);
272
+ ring.setAttribute("aria-label", words.rateSaid(lately));
273
+ ring.style.setProperty("--gallery-habit-share", String(lately / 30));
274
+ ring.toggleAttribute("data-none", lately === 0);
275
+ week.replaceChildren(...weekOf(habit.days, today).map(({ day, weekday, done: kept }) => {
276
+ const item = made("li", "day");
277
+ item.classList.toggle("gallery-habit-kept", kept);
278
+ item.classList.toggle("gallery-habit-today", day === today);
279
+ const [, month, date] = day.split("-").map(Number);
280
+ item.setAttribute("aria-label", words.day(month, date, kept));
281
+ item.title = words.day(month, date, kept);
282
+ const dot = made("span", "dot");
283
+ const label = made("span", "weekday");
284
+ label.textContent = day === today ? words.today : words.weekdays[weekday];
285
+ item.append(dot, label);
286
+ return item;
287
+ }));
288
+ };
289
+ draw();
290
+ return { update: draw };
291
+ },
292
+ example(host, context) {
293
+ pressToday(context, dayOf(new Date()));
294
+ },
295
+ });
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="12" fill="#ffffff" stroke="#e7e6e3"/><rect x="30" y="24" width="14" height="14" rx="4.5" fill="#ebf2fa"/><g transform="translate(32 26) scale(.4167)" fill="none" stroke="#2e7cd1" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 10.9c2.3-3.3.2-7.8-1-8.9 0 3.4-2.2 5.3-3.7 6.7C5.9 10.1 5 12.3 5 14.3 5 18 8.1 21 12 21s7-3 7-6.7c0-1.7-1.2-4.4-2.3-5.6-2.1 3.4-3.3 3.4-4.7 2.2"/></g><rect x="50" y="28" width="52" height="6" rx="3" fill="#3b3a37"/><rect x="180" y="29" width="26" height="4" rx="2" fill="#c4c2bd"/><circle cx="51" cy="76" r="19" fill="none" stroke="#ecebe8" stroke-width="4.5"/><circle cx="51" cy="76" r="19" fill="none" stroke="#2e7cd1" stroke-width="4.5" stroke-linecap="round" stroke-dasharray="95.5 119.4" transform="rotate(-90 51 76)"/><text x="51" y="79" text-anchor="middle" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="11" font-weight="600" fill="#3b3a37">24</text><rect x="45" y="84" width="12" height="2.5" rx="1.25" fill="#c4c2bd"/><rect x="84" y="47" width="24" height="4" rx="2" fill="#c4c2bd"/><text x="83" y="69" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="18" font-weight="600" letter-spacing="-.5" fill="#3b3a37">6</text><rect x="96" y="63" width="9" height="4" rx="2" fill="#c4c2bd"/><rect x="172" y="51" width="38" height="15" rx="5" fill="#ebf2fa"/><rect x="183" y="56" width="16" height="5" rx="2.5" fill="#2e7cd1"/><circle cx="89" cy="86" r="5.5" fill="#2e7cd1"/><path d="M86.44 86.16l1.76 1.76 3.52-3.68" fill="none" stroke="#fff" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/><rect x="86.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="108" cy="86" r="5.5" fill="#ecebe8"/><rect x="105.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="127" cy="86" r="5.5" fill="#2e7cd1"/><path d="M124.44 86.16l1.76 1.76 3.52-3.68" fill="none" stroke="#fff" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/><rect x="124.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="146" cy="86" r="5.5" fill="#2e7cd1"/><path d="M143.44 86.16l1.76 1.76 3.52-3.68" fill="none" stroke="#fff" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/><rect x="143.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="165" cy="86" r="5.5" fill="#2e7cd1"/><path d="M162.44 86.16l1.76 1.76 3.52-3.68" fill="none" stroke="#fff" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/><rect x="162.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="184" cy="86" r="5.5" fill="#2e7cd1"/><path d="M181.44 86.16l1.76 1.76 3.52-3.68" fill="none" stroke="#fff" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/><rect x="181.5" y="95" width="5" height="2.5" rx="1.25" fill="#c4c2bd"/><circle cx="203" cy="86" r="4.9" fill="none" stroke="#2e7cd1" stroke-width="1.2"/><rect x="200.5" y="95" width="5" height="2.5" rx="1.25" fill="#2e7cd1"/></svg>