@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
@@ -3,21 +3,32 @@
3
3
  // { "name": "喝八杯水", "days": ["2026-11-05", "2026-11-06"] }. The card draws the name in a field of its
4
4
  // own (DocKit.input: written as the reader presses Enter or leaves it, never while an input method
5
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.
6
+ // yet, so that a streak lasts the day it is to be kept), how many days in all, and a button that checks
7
+ // today in, or takes today's check-in back when it is there. Each press and each name is one
8
+ // context.editJSON, one undo step, the JSON written back on one line as it was: what the reader does is
9
+ // in the file, and nothing else is (the streak and the days are worked out as it draws). It draws from
10
+ // the part on every `source` (an undo, an Agent's write) and again as a day begins (the kit's `clock`,
11
+ // "day"), so that "today" moves at midnight. Where nothing takes its writes (an export, a page that has
12
+ // not come to edit yet: context.canEdit() false) the name is read-only and the button a plain line saying
13
+ // whether today is done. Its words are the page's language (the UI's where Gallery says it, else the
14
+ // page's lang).
15
+ //
16
+ // Its face (the pack's design B, in the kit's card tokens), laid out by the card's cells (`data-layout`,
17
+ // by context.size, read again on `layout`): "wide" (4 × 2, the default) a head of the chip, a flame in
18
+ // its warm color, the name (the field) and the days in all after it (共 41 天), the streak, the hero, and
19
+ // 天连续 after it, the button at its right (a pale blue 打卡, a pale green ✓ 已打卡 a press takes back),
20
+ // and this week at the bottom, Monday to Sunday, seven circles (`weekOf`): a day done filled with the
21
+ // accent and ticked, one not done a soft gray with its initial, today ringed in the accent (今), filled
22
+ // and ringed twice once done, the days to come an outline; "narrow" (2 wide) the name alone at the top,
23
+ // a flame and the streak, and the button as wide as the card at the bottom (the name shown as words of
24
+ // up to two lines, the field over them unseen until a press or Tab brings it, so that a long name is
25
+ // never cut to a few characters); "tall" (4 high) the head, the streak and the button, and the last five
26
+ // weeks as a month (`monthOf`), Monday first, the days of the streak the accent's, the days done before
27
+ // it its pale tint, the others bare, today ringed, the days to come an outline, with 最近五周 and how
28
+ // many of them were done (打了 27 天) over it. A habit never
29
+ // checked in (no days at all) says 还没打过卡 where the streak stands (the face's data-fresh), the chip
30
+ // gray and no count of days, until the first check-in. Start a block of your own from it with gallery
31
+ // block new <id> --recipe habit.
21
32
 
22
33
  const WORDS = {
23
34
  zh: {
@@ -27,14 +38,14 @@ const WORDS = {
27
38
  undo: "再按一下,撤回今天的打卡",
28
39
  todayDone: "今天已打卡",
29
40
  todayOpen: "今天还没打卡",
30
- streak: "连续打卡",
41
+ streak: "天连续",
31
42
  unit: () => "天",
32
43
  total: (/** @type {number} */ days) => `共 ${days} 天`,
33
- rate: "近 30 天",
34
- fresh: "还没打过卡,今天开始第 1 天",
35
- rateSaid: (/** @type {number} */ done) => `近 30 天打卡 ${done} 天`,
36
- week: "最近七天",
37
- weekdays: ["日", "一", "二", "三", "四", "五", "六"],
44
+ fresh: ["还没打过卡", "今天开始第 1 天"],
45
+ weeks: "最近五周",
46
+ got: (/** @type {number} */ days) => ["打了 ", String(days), " 天"],
47
+ week: "这一周",
48
+ initials: ["日", "一", "二", "三", "四", "五", "六"],
38
49
  today: "今",
39
50
  day: (/** @type {number} */ month, /** @type {number} */ day, /** @type {boolean} */ done) => `${month}月${day}日${done ? ",已打卡" : ",没打卡"}`,
40
51
  },
@@ -45,15 +56,15 @@ const WORDS = {
45
56
  undo: "Press again to take today's check-in back",
46
57
  todayDone: "Done today",
47
58
  todayOpen: "Not done today",
48
- streak: "Streak",
59
+ streak: "in a row",
49
60
  unit: (/** @type {number} */ days) => (days === 1 ? "day" : "days"),
50
61
  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",
62
+ fresh: ["No check-ins yet", "today can be day 1"],
63
+ weeks: "Last five weeks",
64
+ got: (/** @type {number} */ days) => ["", String(days), ` ${days === 1 ? "day" : "days"} done`],
65
+ week: "This week",
66
+ initials: ["S", "M", "T", "W", "T", "F", "S"],
67
+ today: "", // a week's circle is too small for "Today": its initial, ringed as today
57
68
  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
69
  },
59
70
  };
@@ -88,6 +99,12 @@ function dayAfter(day, count) {
88
99
  return `${moved.getUTCFullYear()}-${two(moved.getUTCMonth() + 1)}-${two(moved.getUTCDate())}`;
89
100
  }
90
101
 
102
+ /** The weekday of a day written YYYY-MM-DD, 0 for Sunday. @param {string} day */
103
+ function weekdayOf(day) {
104
+ const [year, month, date] = day.split("-").map(Number);
105
+ return new Date(Date.UTC(year, month - 1, date)).getUTCDay();
106
+ }
107
+
91
108
  /**
92
109
  * The habit `data` is (context.readJSON's, or the draft context.editJSON hands a change): its name ("" for
93
110
  * none yet) and its days, as they stand; throws saying why it is none, and the block draws nothing over it.
@@ -133,30 +150,30 @@ function toggled(days, today) {
133
150
  }
134
151
 
135
152
  /**
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.
153
+ * The days of the `weeks` weeks that end with the one `today` is in, Monday first, oldest first: each its
154
+ * day (YYYY-MM-DD), its weekday (0 for Sunday), whether it was done, whether it is today, one to come
155
+ * (after today) and one of the streak (`streak`: the days of streakOf, back from today or yesterday).
138
156
  * @param {string[]} days
139
157
  * @param {string} today
158
+ * @param {number} [weeks]
140
159
  */
141
- function weekOf(days, today) {
160
+ function monthOf(days, today, weeks = 5) {
142
161
  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) };
162
+ const streak = streakOf(days, today);
163
+ const last = done.has(today) ? today : dayAfter(today, -1);
164
+ const first = dayAfter(today, -((weekdayOf(today) + 6) % 7) - 7 * (weeks - 1));
165
+ return Array.from({ length: weeks * 7 }, (_, index) => {
166
+ const day = dayAfter(first, index);
167
+ return { day, weekday: weekdayOf(day), done: done.has(day), today: day === today, future: day > today, streak: day <= last && day > dayAfter(last, -streak) };
147
168
  });
148
169
  }
149
170
 
150
171
  /**
151
- * How many of the `span` days that end today were done (the ring's count: 30 days by default).
172
+ * The week `today` is in, Monday to Sunday, as monthOf draws a day.
152
173
  * @param {string[]} days
153
174
  * @param {string} today
154
- * @param {number} [span]
155
175
  */
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
- }
176
+ const weekOf = (days, today) => monthOf(days, today, 1);
160
177
 
161
178
  /**
162
179
  * Today checked in, or taken back: one write (context.editJSON), what a press of the button calls, and
@@ -195,46 +212,52 @@ function made(tag, kind) {
195
212
  return node;
196
213
  }
197
214
 
215
+ const FLAME = '<path d="M12 20.8a6 6 0 0 0 6-6c0-3.6-2.4-5.6-3.6-8.6-.3-.8-1.3-1-1.8-.3-1.4 2-2 3.6-2 5-.9-.6-1.4-1.5-1.6-2.4-.1-.6-.9-.8-1.2-.2C6.9 9.6 6 11.4 6 14.8a6 6 0 0 0 6 6z"/>';
216
+ const CHECK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.5 12.5l4 4 9-9"/></svg>';
217
+
198
218
  DocKit.component({
199
219
  id: "gallery/habit",
200
- on: ["source", "editable", "clock"],
220
+ on: ["source", "editable", "clock", "layout"],
201
221
  mount(host, context) {
202
222
  const words = WORDS[languageOf(document.documentElement)];
203
223
  const face = made("div", "face");
204
224
  const head = made("div", "head");
205
225
  const chip = made("span", "chip");
206
226
  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>';
227
+ chip.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round">${FLAME}</svg>`;
208
228
  const name = made("input", "name");
209
229
  name.type = "text";
210
230
  name.placeholder = words.name;
211
231
  name.spellcheck = false;
212
232
  name.setAttribute("aria-label", words.name);
233
+ // A narrow card's name as words of up to two lines over the field, which stays the one a press or Tab reaches (style.css).
234
+ const label = made("span", "label");
235
+ label.setAttribute("aria-hidden", "true");
213
236
  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.
237
+ head.append(chip, name, label, total);
238
+ // The streak: its count large and its unit after it (a narrow card a flame before it), or, never
239
+ // checked in, a line inviting the first.
240
+ const row = made("div", "row");
218
241
  const streak = made("p", "streak");
219
- const label = made("span", "label");
220
- label.textContent = words.streak;
242
+ const flame = made("span", "flame");
243
+ flame.setAttribute("aria-hidden", "true");
244
+ flame.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${FLAME}</svg>`;
221
245
  const count = made("strong", "count");
222
246
  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);
247
+ streak.append(flame, count, unit);
248
+ const fresh = made("p", "fresh");
249
+ const press = made("button", "check");
250
+ press.type = "button";
251
+ row.append(streak, fresh, press);
252
+ // A tall card's five weeks: what they are and how many were done, over the month.
253
+ const caption = made("div", "caption");
254
+ const captionName = made("span", "caption-name");
255
+ captionName.textContent = words.weeks;
256
+ const captionCount = made("span", "caption-count");
257
+ caption.append(captionName, captionCount);
232
258
  const week = made("ol", "week");
233
259
  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);
260
+ face.append(head, row, caption, week);
238
261
  host.append(face);
239
262
  const named = DocKit.input(name, { signal: context.signal, commit: (value) => rename(context, value) });
240
263
  press.addEventListener("click", () => {
@@ -243,6 +266,9 @@ DocKit.component({
243
266
 
244
267
  const draw = (/** @type {string} */ reason = "", /** @type {unknown} */ detail = null) => {
245
268
  if (reason === "clock" && detail !== "day") return; // a minute moves no day
269
+ const size = context.size;
270
+ const layout = !size ? "wide" : size.w <= 2 ? "narrow" : size.h >= 4 ? "tall" : "wide";
271
+ if (face.dataset.layout !== layout) face.dataset.layout = layout;
246
272
  const data = context.readJSON("habit");
247
273
  if (data === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
248
274
  let habit;
@@ -256,33 +282,46 @@ DocKit.component({
256
282
  const done = habit.days.includes(today);
257
283
  name.readOnly = !writes;
258
284
  named.set(habit.name);
285
+ label.textContent = habit.name || words.name;
286
+ label.toggleAttribute("data-empty", !habit.name);
259
287
  // Where nothing is written, the button is a line saying how today stands: nothing to press.
260
288
  press.disabled = !writes;
261
289
  press.classList.toggle("gallery-habit-done", done);
262
- press.textContent = writes ? (done ? `✓ ${words.checked}` : words.check) : (done ? words.todayDone : words.todayOpen);
290
+ if (writes && done) press.innerHTML = `${CHECK}<span>${words.checked}</span>`;
291
+ else press.textContent = writes ? words.check : (done ? words.todayDone : words.todayOpen);
263
292
  press.title = writes && done ? words.undo : "";
264
293
  press.setAttribute("aria-pressed", String(done));
265
294
  face.toggleAttribute("data-fresh", habit.days.length === 0);
266
295
  const days = streakOf(habit.days, today);
267
296
  count.textContent = String(days);
268
- unit.textContent = words.unit(days);
297
+ unit.textContent = layout === "narrow" ? words.unit(days) : words.streak;
298
+ fresh.replaceChildren(...(layout === "narrow" ? [words.fresh[0]] : [words.fresh[0], document.createElement("br"), words.fresh[1]]));
269
299
  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 }) => {
300
+ const span = layout === "tall" ? 5 : 1;
301
+ const shown = monthOf(habit.days, today, span);
302
+ week.dataset.weeks = String(span);
303
+ const [before, got, after] = words.got(shown.filter((day) => day.done && !day.future).length);
304
+ const bold = document.createElement("b");
305
+ bold.textContent = got;
306
+ captionCount.replaceChildren(before, bold, after);
307
+ const initials = span === 5 ? [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
308
+ const item = made("li", "initial");
309
+ item.setAttribute("aria-hidden", "true");
310
+ item.textContent = words.initials[weekday];
311
+ return item;
312
+ }) : [];
313
+ week.replaceChildren(...initials, ...shown.map(({ day, weekday, done: kept, today: now, future, streak: inStreak }) => {
276
314
  const item = made("li", "day");
277
315
  item.classList.toggle("gallery-habit-kept", kept);
278
- item.classList.toggle("gallery-habit-today", day === today);
316
+ item.classList.toggle("gallery-habit-today", now);
317
+ item.classList.toggle("gallery-habit-future", future);
318
+ item.classList.toggle("gallery-habit-streak-day", inStreak);
279
319
  const [, month, date] = day.split("-").map(Number);
280
320
  item.setAttribute("aria-label", words.day(month, date, kept));
281
321
  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);
322
+ // A week's circle says its day by its initial (今 for today), a month's by its date; a day done of a week, a tick.
323
+ if (span === 1 && kept && !future) item.innerHTML = CHECK;
324
+ else item.textContent = span === 5 ? String(date) : now && words.today ? words.today : words.initials[weekday];
286
325
  return item;
287
326
  }));
288
327
  };
@@ -1 +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>
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="8" fill="#ffffff" stroke="#e6e6e3"/><rect x="28" y="22" width="10" height="10" rx="3" fill="#fdf1dd"/><rect x="42" y="24.5" width="54" height="5" rx="2.5" fill="#37352f"/><rect x="100" y="24.5" width="20" height="5" rx="2.5" fill="#d4d3cf"/><rect x="28" y="42" width="24" height="20" rx="3" fill="#1d1d1b"/><rect x="56" y="56" width="22" height="5" rx="2.5" fill="#a6a5a1"/><rect x="176" y="42" width="36" height="18" rx="5" fill="#ebf2fa"/><rect x="186" y="48.5" width="16" height="5" rx="2.5" fill="#4084cf"/><circle cx="37" cy="88" r="9" fill="#4084cf"/><path d="M33.5 88l2.5 2.5 4.5-5" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><circle cx="66" cy="88" r="9" fill="#4084cf"/><path d="M62.5 88l2.5 2.5 4.5-5" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><circle cx="95" cy="88" r="9" fill="#4084cf"/><path d="M91.5 88l2.5 2.5 4.5-5" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><circle cx="124" cy="88" r="8.4" fill="none" stroke="#4084cf" stroke-width="1.3"/><circle cx="153" cy="88" r="8.6" fill="none" stroke="#ebebe9"/><circle cx="182" cy="88" r="8.6" fill="none" stroke="#ebebe9"/><circle cx="211" cy="88" r="8.6" fill="none" stroke="#ebebe9"/></svg>