@heycys/gallery 0.1.2 → 0.2.1

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 (338) 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/icon/page.mjs +6 -4
  8. package/browser/editor/components/icons.mjs +5 -2
  9. package/browser/editor/components/image/index.mjs +2 -0
  10. package/browser/editor/components/image/page.mjs +172 -90
  11. package/browser/editor/components/installed/index.mjs +6 -2
  12. package/browser/editor/components/installed/page.mjs +80 -13
  13. package/browser/editor/components/media/icons.mjs +13 -8
  14. package/browser/editor/components/media/page.mjs +5 -3
  15. package/browser/editor/components/media/toolbar.mjs +12 -3
  16. package/browser/editor/components/paragraph/index.mjs +4 -3
  17. package/browser/editor/components/registry.mjs +17 -5
  18. package/browser/editor/components/table/index.mjs +4 -2
  19. package/browser/editor/page/after/index.mjs +288 -0
  20. package/browser/editor/page/align.mjs +91 -0
  21. package/browser/editor/page/beside.mjs +5 -6
  22. package/browser/editor/page/block-menu/index.mjs +6 -3
  23. package/browser/editor/page/blocks.mjs +3 -1
  24. package/browser/editor/page/board/hint.mjs +64 -27
  25. package/browser/editor/page/board/index.mjs +160 -71
  26. package/browser/editor/page/board/motion.mjs +177 -0
  27. package/browser/editor/page/board/settings.mjs +4 -3
  28. package/browser/editor/page/clipboard/import/read.mjs +20 -2
  29. package/browser/editor/page/clipboard/index.mjs +10 -3
  30. package/browser/editor/page/clipboard/own/blocks.mjs +3 -4
  31. package/browser/editor/page/clipboard/own/describe.mjs +6 -2
  32. package/browser/editor/page/commit.mjs +2 -2
  33. package/browser/editor/page/core.mjs +3 -1
  34. package/browser/editor/page/custom-page.mjs +15 -11
  35. package/browser/editor/page/editor.mjs +31 -14
  36. package/browser/editor/page/history.mjs +19 -4
  37. package/browser/editor/page/insert/index.mjs +20 -22
  38. package/browser/editor/page/menu.mjs +15 -7
  39. package/browser/editor/page/page-objects.mjs +3 -7
  40. package/browser/editor/page/present-links.mjs +5 -4
  41. package/browser/editor/page/present-shell.mjs +63 -0
  42. package/browser/editor/page/selection/index.mjs +10 -2
  43. package/browser/editor/page/table/binding.mjs +87 -0
  44. package/browser/editor/page/table/icons.mjs +0 -2
  45. package/browser/editor/page/table/index.mjs +10 -14
  46. package/browser/editor/page/table/views.mjs +5 -52
  47. package/browser/editor/page/table/widths.mjs +82 -10
  48. package/browser/editor/page/typing/index.mjs +44 -12
  49. package/browser/editor/page/typing/leave.mjs +19 -5
  50. package/browser/shared/block-packs.mjs +23 -8
  51. package/browser/shared/component-catalog.mjs +15 -8
  52. package/browser/shared/component-source.mjs +3 -1
  53. package/browser/shared/components/heading.mjs +2 -1
  54. package/browser/shared/components/installed.mjs +1 -1
  55. package/browser/shared/components/page.mjs +2 -0
  56. package/browser/shared/components/paragraph.mjs +8 -1
  57. package/browser/shared/components/table.mjs +1 -0
  58. package/browser/shared/document-copy.mjs +43 -0
  59. package/browser/shared/drafts.mjs +10 -3
  60. package/browser/shared/emoji/icon-value.mjs +2 -2
  61. package/browser/shared/emoji/line-icons.mjs +37 -0
  62. package/browser/shared/extensions.mjs +83 -7
  63. package/browser/shared/http.mjs +9 -0
  64. package/browser/shared/i18n/en.mjs +50 -17
  65. package/browser/shared/outline-shape.mjs +6 -0
  66. package/browser/shared/outline.mjs +165 -12
  67. package/browser/shared/pack-manifest.mjs +12 -1
  68. package/browser/shared/pages.mjs +169 -57
  69. package/browser/shared/pinyin.mjs +92 -0
  70. package/browser/shared/present-step.mjs +17 -0
  71. package/browser/shared/sidebar-head.mjs +70 -0
  72. package/browser/shared/web-links.mjs +5 -0
  73. package/browser/shared/workspace-columns.mjs +11 -7
  74. package/dist/{chunk-N7AZZLGT.mjs → chunk-2GKGEVMR.mjs} +29 -24
  75. package/dist/chunk-2OHANJ6Q.mjs +2 -0
  76. package/dist/{chunk-HYMEEXKN.mjs → chunk-2RKIHJI2.mjs} +1 -1
  77. package/dist/{chunk-742D2WWJ.mjs → chunk-36FIRJIF.mjs} +2 -2
  78. package/dist/{chunk-BPL64HGA.mjs → chunk-3A5I2GJK.mjs} +1 -1
  79. package/dist/{chunk-RFDZXFJF.mjs → chunk-3OFXMN6N.mjs} +1 -1
  80. package/dist/chunk-46ACXOU5.mjs +2 -0
  81. package/dist/{chunk-PUOQ65NC.mjs → chunk-4AMTKWDO.mjs} +1 -1
  82. package/dist/{chunk-VTKNEYT3.mjs → chunk-4DNHHJSL.mjs} +1 -1
  83. package/dist/chunk-5DPABPBI.mjs +2 -0
  84. package/dist/chunk-5IIH335D.mjs +3 -0
  85. package/dist/chunk-5L26JUYA.mjs +2 -0
  86. package/dist/{chunk-3NJACR4W.mjs → chunk-5MEXZ2UF.mjs} +1 -1
  87. package/dist/{chunk-ZHEHPMBK.mjs → chunk-6MPKCH3P.mjs} +1 -1
  88. package/dist/{chunk-C564NU2H.mjs → chunk-74RPP2M2.mjs} +1 -1
  89. package/dist/chunk-7M2BQQGZ.mjs +2 -0
  90. package/dist/{chunk-VXACTLO3.mjs → chunk-7M7UPWU6.mjs} +1 -1
  91. package/dist/{chunk-ZB4H7ZN4.mjs → chunk-7N2LPG33.mjs} +1 -1
  92. package/dist/{chunk-UBBRTJ2R.mjs → chunk-7XDYK3UC.mjs} +1 -1
  93. package/dist/chunk-AEMVZYZQ.mjs +166 -0
  94. package/dist/{chunk-POQSE4C2.mjs → chunk-AFGUR7SY.mjs} +1 -1
  95. package/dist/{chunk-QKXMPUXC.mjs → chunk-AMDGVDNV.mjs} +1 -1
  96. package/dist/{chunk-KTKHRC6H.mjs → chunk-ASAXY2DJ.mjs} +1 -1
  97. package/dist/{chunk-NVRNLK25.mjs → chunk-BYSF5UMM.mjs} +2 -2
  98. package/dist/{chunk-U3SSPL4K.mjs → chunk-CCSGFCLZ.mjs} +1 -1
  99. package/dist/{chunk-NWFBE2YA.mjs → chunk-CG3UHDKH.mjs} +1 -1
  100. package/dist/chunk-CPS4SRPF.mjs +5 -0
  101. package/dist/{chunk-B4P2CM2J.mjs → chunk-CZ5KJSGK.mjs} +1 -1
  102. package/dist/chunk-DAMVDXR3.mjs +2 -0
  103. package/dist/{chunk-NG375ILQ.mjs → chunk-DH6GMDNN.mjs} +1 -1
  104. package/dist/chunk-DHB7UZ7F.mjs +3 -0
  105. package/dist/{chunk-MHW7ZKOU.mjs → chunk-DKR7LFIY.mjs} +1 -1
  106. package/dist/chunk-DLFZJJEE.mjs +2 -0
  107. package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
  108. package/dist/{chunk-6VTU6KEO.mjs → chunk-FCK6NXON.mjs} +1 -1
  109. package/dist/{chunk-ZWPMCK7P.mjs → chunk-FX42XU6J.mjs} +1 -1
  110. package/dist/chunk-FXQBADMN.mjs +5 -0
  111. package/dist/{chunk-W3BGBJQW.mjs → chunk-G4YP7GVC.mjs} +1 -1
  112. package/dist/{chunk-V6XZ4VIR.mjs → chunk-GWOYMENX.mjs} +1 -1
  113. package/dist/chunk-HATA2W7X.mjs +2 -0
  114. package/dist/{chunk-YI43RG4P.mjs → chunk-HBAUKKQH.mjs} +1 -1
  115. package/dist/{chunk-MAFIGVXY.mjs → chunk-HM4E43VM.mjs} +1 -1
  116. package/dist/{chunk-CRJEG7MT.mjs → chunk-JI6T3A4Y.mjs} +1 -1
  117. package/dist/{chunk-NFMCP2HI.mjs → chunk-JJNRIFND.mjs} +2 -2
  118. package/dist/chunk-JL6R4FG4.mjs +4 -0
  119. package/dist/{chunk-UMHUSOPJ.mjs → chunk-JXLSHVJ2.mjs} +1 -1
  120. package/dist/{chunk-FJYN7RTH.mjs → chunk-KFZBYDIS.mjs} +1 -1
  121. package/dist/{chunk-ZTHICSSF.mjs → chunk-KH24LWAT.mjs} +1 -1
  122. package/dist/{chunk-RKPWLGZ3.mjs → chunk-KUILYI7H.mjs} +1 -1
  123. package/dist/{chunk-RMECZA2K.mjs → chunk-L7W624FD.mjs} +1 -1
  124. package/dist/chunk-LE44MD3O.mjs +4 -0
  125. package/dist/{chunk-4K5KC6V2.mjs → chunk-LV24RY2E.mjs} +1 -1
  126. package/dist/{chunk-43B75J5T.mjs → chunk-MKTX5PGV.mjs} +1 -1
  127. package/dist/{chunk-LKX55H76.mjs → chunk-NAGMRVW4.mjs} +1 -1
  128. package/dist/{chunk-D4CKRD3V.mjs → chunk-NFABH3QW.mjs} +2 -2
  129. package/dist/{chunk-PVER7LSC.mjs → chunk-NIW7F7LU.mjs} +2 -2
  130. package/dist/{chunk-PAREWQGB.mjs → chunk-NWRZXELO.mjs} +1 -1
  131. package/dist/{chunk-OJKMINIU.mjs → chunk-OU3S466R.mjs} +15 -15
  132. package/dist/{chunk-5ZJA7CXT.mjs → chunk-PAFSFVYB.mjs} +1 -1
  133. package/dist/chunk-QCEATQC4.mjs +2 -0
  134. package/dist/{chunk-Y34NR5JL.mjs → chunk-QJ3TFJLQ.mjs} +1 -1
  135. package/dist/{chunk-IA4PSIMD.mjs → chunk-QK3VISCA.mjs} +1 -1
  136. package/dist/{chunk-ZUNVAXLH.mjs → chunk-QXVNUASA.mjs} +1 -1
  137. package/dist/{chunk-GRAXKKG4.mjs → chunk-QZ35UAB4.mjs} +1 -1
  138. package/dist/{chunk-UGWYKHBL.mjs → chunk-S2Y7VI4H.mjs} +2 -2
  139. package/dist/{chunk-OW5NWPOJ.mjs → chunk-SAHS56QM.mjs} +1 -1
  140. package/dist/{chunk-QOUEFUCH.mjs → chunk-SOD23RGL.mjs} +1 -1
  141. package/dist/chunk-TDWJKOLN.mjs +2 -0
  142. package/dist/{chunk-V6RIYQZZ.mjs → chunk-TOE6DPLH.mjs} +1 -1
  143. package/dist/chunk-UGO5N3EI.mjs +15 -0
  144. package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
  145. package/dist/{chunk-NWR2LT5B.mjs → chunk-V5JRSGZF.mjs} +1 -1
  146. package/dist/{chunk-BZ5RAF7L.mjs → chunk-VA3KAQZD.mjs} +1 -1
  147. package/dist/chunk-VG5JEIUQ.mjs +12 -0
  148. package/dist/{chunk-UNOYVIYR.mjs → chunk-VGTMLQWZ.mjs} +1 -1
  149. package/dist/chunk-VKSRM7EY.mjs +22 -0
  150. package/dist/{chunk-YQGT4AB2.mjs → chunk-VNUWREXU.mjs} +1 -1
  151. package/dist/{chunk-AGDILNWB.mjs → chunk-VPVOAJ7X.mjs} +1 -1
  152. package/dist/{chunk-HLYV7RIH.mjs → chunk-WCWYNFDF.mjs} +1 -1
  153. package/dist/chunk-WTEGUPFG.mjs +16 -0
  154. package/dist/{chunk-N76HWBA3.mjs → chunk-XKOHV4H6.mjs} +1 -1
  155. package/dist/{chunk-HMYTM3GT.mjs → chunk-XMKHCUI6.mjs} +1 -1
  156. package/dist/{chunk-OUUP3WKT.mjs → chunk-XQRUG4G3.mjs} +1 -1
  157. package/dist/{chunk-32ZUEFZ7.mjs → chunk-YBB5LWZK.mjs} +1 -1
  158. package/dist/{chunk-SX4LJYT6.mjs → chunk-YFJU3O7G.mjs} +1 -1
  159. package/dist/chunk-YPPJVDQI.mjs +2 -0
  160. package/dist/chunk-ZANMKAWS.mjs +2 -0
  161. package/dist/{chunk-6QBHU4DF.mjs → chunk-ZCP7BTOZ.mjs} +1 -1
  162. package/dist/{chunk-ERLAIBOA.mjs → chunk-ZEY2RLBT.mjs} +2 -2
  163. package/dist/{chunk-SQ6Y2ZMA.mjs → chunk-ZO7SFB2E.mjs} +2 -2
  164. package/dist/cli.mjs +1 -1
  165. package/dist/server.mjs +1 -1
  166. package/dist/worker.mjs +1 -1
  167. package/kit/board.css +5 -5
  168. package/kit/code.js +15 -15
  169. package/kit/math.js +1 -1
  170. package/kit/report.css +123 -44
  171. package/kit/runtime.js +45 -7
  172. package/kit/tables.js +2 -0
  173. package/kit/ui.js +2 -0
  174. package/package.json +1 -1
  175. package/packs/gallery/chart-view/kit.js +3 -3
  176. package/packs/gallery/chart-view/writing.md +1 -1
  177. package/packs/gallery/countdown/component.json +1 -1
  178. package/packs/gallery/countdown/kit.js +89 -29
  179. package/packs/gallery/countdown/preview.svg +1 -1
  180. package/packs/gallery/countdown/style.css +88 -100
  181. package/packs/gallery/countdown/writing.md +2 -2
  182. package/packs/gallery/habit/component.json +3 -3
  183. package/packs/gallery/habit/kit.js +114 -75
  184. package/packs/gallery/habit/preview.svg +1 -1
  185. package/packs/gallery/habit/style.css +133 -205
  186. package/packs/gallery/habit/writing.md +2 -2
  187. package/packs/gallery/heatmap/component.json +2 -2
  188. package/packs/gallery/heatmap/kit.js +335 -173
  189. package/packs/gallery/heatmap/preview.svg +1 -1
  190. package/packs/gallery/heatmap/style.css +158 -170
  191. package/packs/gallery/heatmap/writing.md +2 -2
  192. package/packs/gallery/kanban/kit.js +3 -3
  193. package/packs/gallery/kanban/writing.md +1 -1
  194. package/packs/gallery/metric/kit.js +3 -3
  195. package/packs/gallery/metric/writing.md +1 -1
  196. package/packs/gallery/note/component.json +3 -3
  197. package/packs/gallery/note/kit.js +40 -13
  198. package/packs/gallery/note/preview.svg +1 -1
  199. package/packs/gallery/note/style.css +51 -30
  200. package/packs/gallery/note/writing.md +2 -2
  201. package/packs/gallery/recent/component.json +2 -2
  202. package/packs/gallery/recent/kit.js +141 -86
  203. package/packs/gallery/recent/preview.svg +1 -1
  204. package/packs/gallery/recent/style.css +53 -126
  205. package/packs/gallery/recent/writing.md +2 -2
  206. package/packs/gallery/static/component.json +2 -2
  207. package/packs/gallery/static/preview.svg +1 -1
  208. package/packs/gallery/static/style.css +99 -66
  209. package/packs/gallery/static/writing.md +2 -2
  210. package/packs/gallery/stats/component.json +3 -3
  211. package/packs/gallery/stats/kit.js +253 -72
  212. package/packs/gallery/stats/preview.svg +1 -1
  213. package/packs/gallery/stats/style.css +64 -142
  214. package/packs/gallery/stats/writing.md +2 -2
  215. package/packs/gallery/today/component.json +2 -2
  216. package/packs/gallery/today/kit.js +62 -27
  217. package/packs/gallery/today/preview.svg +1 -1
  218. package/packs/gallery/today/style.css +101 -72
  219. package/packs/gallery/today/writing.md +2 -2
  220. package/packs/gallery/todos/component.json +1 -1
  221. package/packs/gallery/todos/kit.js +218 -71
  222. package/packs/gallery/todos/preview.svg +1 -1
  223. package/packs/gallery/todos/style.css +170 -223
  224. package/packs/gallery/todos/writing.md +1 -1
  225. package/packs/gallery/weather/component.json +2 -2
  226. package/packs/gallery/weather/kit.js +165 -90
  227. package/packs/gallery/weather/preview.svg +1 -1
  228. package/packs/gallery/weather/style.css +100 -218
  229. package/packs/gallery/weather/writing.md +1 -1
  230. package/packs/gallery-soft/countdown/component.json +18 -0
  231. package/packs/gallery-soft/countdown/example.html +3 -0
  232. package/packs/gallery-soft/countdown/kit.js +206 -0
  233. package/packs/gallery-soft/countdown/preview.svg +1 -0
  234. package/packs/gallery-soft/countdown/style.css +211 -0
  235. package/packs/gallery-soft/countdown/template.html +3 -0
  236. package/packs/gallery-soft/countdown/writing.md +24 -0
  237. package/packs/gallery-soft/habit/component.json +14 -0
  238. package/packs/gallery-soft/habit/example.html +3 -0
  239. package/packs/gallery-soft/habit/kit.js +366 -0
  240. package/packs/gallery-soft/habit/preview.svg +1 -0
  241. package/packs/gallery-soft/habit/style.css +251 -0
  242. package/packs/gallery-soft/habit/template.html +3 -0
  243. package/packs/gallery-soft/habit/writing.md +23 -0
  244. package/packs/gallery-soft/heatmap/component.json +18 -0
  245. package/packs/gallery-soft/heatmap/example.html +3 -0
  246. package/packs/gallery-soft/heatmap/kit.js +498 -0
  247. package/packs/gallery-soft/heatmap/preview.svg +1 -0
  248. package/packs/gallery-soft/heatmap/style.css +181 -0
  249. package/packs/gallery-soft/heatmap/template.html +3 -0
  250. package/packs/gallery-soft/heatmap/writing.md +23 -0
  251. package/packs/gallery-soft/note/component.json +14 -0
  252. package/packs/gallery-soft/note/example.html +3 -0
  253. package/packs/gallery-soft/note/kit.js +162 -0
  254. package/packs/gallery-soft/note/preview.svg +1 -0
  255. package/packs/gallery-soft/note/style.css +148 -0
  256. package/packs/gallery-soft/note/template.html +3 -0
  257. package/packs/gallery-soft/note/writing.md +20 -0
  258. package/packs/gallery-soft/recent/component.json +13 -0
  259. package/packs/gallery-soft/recent/example.html +1 -0
  260. package/packs/gallery-soft/recent/kit.js +335 -0
  261. package/packs/gallery-soft/recent/preview.svg +1 -0
  262. package/packs/gallery-soft/recent/style.css +160 -0
  263. package/packs/gallery-soft/recent/template.html +1 -0
  264. package/packs/gallery-soft/recent/writing.md +17 -0
  265. package/packs/gallery-soft/static/component.json +13 -0
  266. package/packs/gallery-soft/static/example.html +12 -0
  267. package/packs/gallery-soft/static/preview.svg +1 -0
  268. package/packs/gallery-soft/static/style.css +186 -0
  269. package/packs/gallery-soft/static/template.html +1 -0
  270. package/packs/gallery-soft/static/writing.md +20 -0
  271. package/packs/gallery-soft/stats/component.json +13 -0
  272. package/packs/gallery-soft/stats/example.html +1 -0
  273. package/packs/gallery-soft/stats/kit.js +341 -0
  274. package/packs/gallery-soft/stats/preview.svg +1 -0
  275. package/packs/gallery-soft/stats/style.css +119 -0
  276. package/packs/gallery-soft/stats/template.html +1 -0
  277. package/packs/gallery-soft/stats/writing.md +13 -0
  278. package/packs/gallery-soft/today/component.json +12 -0
  279. package/packs/gallery-soft/today/example.html +1 -0
  280. package/packs/gallery-soft/today/kit.js +222 -0
  281. package/packs/gallery-soft/today/preview.svg +1 -0
  282. package/packs/gallery-soft/today/style.css +178 -0
  283. package/packs/gallery-soft/today/template.html +1 -0
  284. package/packs/gallery-soft/today/writing.md +16 -0
  285. package/packs/gallery-soft/todos/component.json +19 -0
  286. package/packs/gallery-soft/todos/example.html +3 -0
  287. package/packs/gallery-soft/todos/kit.js +527 -0
  288. package/packs/gallery-soft/todos/preview.svg +1 -0
  289. package/packs/gallery-soft/todos/style.css +212 -0
  290. package/packs/gallery-soft/todos/template.html +3 -0
  291. package/packs/gallery-soft/todos/writing.md +32 -0
  292. package/packs/gallery-soft/weather/component.json +19 -0
  293. package/packs/gallery-soft/weather/example.html +3 -0
  294. package/packs/gallery-soft/weather/kit.js +411 -0
  295. package/packs/gallery-soft/weather/preview.svg +1 -0
  296. package/packs/gallery-soft/weather/style.css +185 -0
  297. package/packs/gallery-soft/weather/template.html +3 -0
  298. package/packs/gallery-soft/weather/writing.md +28 -0
  299. package/release.json +10 -10
  300. package/skill/SKILL.md +15 -14
  301. package/skill/references/building-blocks.md +4 -4
  302. package/skill/references/extensions.md +11 -7
  303. package/skill/references/pages-and-data.md +13 -9
  304. package/skill/references/writing-html.md +2 -0
  305. package/web/assets/index-BHC5ZOMh.js +15 -0
  306. package/web/assets/index-xP0uw3a0.css +1 -0
  307. package/web/index.html +2 -2
  308. package/dist/chunk-2NRBY6AJ.mjs +0 -2
  309. package/dist/chunk-35NQ4PLQ.mjs +0 -2
  310. package/dist/chunk-3KJHLHUO.mjs +0 -2
  311. package/dist/chunk-4AV456RQ.mjs +0 -2
  312. package/dist/chunk-5XYHDBJ5.mjs +0 -2
  313. package/dist/chunk-6A53MZYZ.mjs +0 -2
  314. package/dist/chunk-6EXH3ORZ.mjs +0 -166
  315. package/dist/chunk-BVEFGE4B.mjs +0 -2
  316. package/dist/chunk-D4EURAX4.mjs +0 -4
  317. package/dist/chunk-DQCE7TJY.mjs +0 -5
  318. package/dist/chunk-FWNRPMCJ.mjs +0 -12
  319. package/dist/chunk-FWW4CSAZ.mjs +0 -2
  320. package/dist/chunk-GY3FAUVM.mjs +0 -2
  321. package/dist/chunk-JVHHHGEA.mjs +0 -3
  322. package/dist/chunk-L7UP52JO.mjs +0 -2
  323. package/dist/chunk-NAMN3RAF.mjs +0 -2
  324. package/dist/chunk-PZDLJJTJ.mjs +0 -3
  325. package/dist/chunk-SXHBYZST.mjs +0 -16
  326. package/dist/chunk-TEZAWDSQ.mjs +0 -2
  327. package/dist/chunk-UFNYFFKO.mjs +0 -20
  328. package/dist/chunk-UKSHDZZ6.mjs +0 -2
  329. package/dist/chunk-VJPFVNNW.mjs +0 -15
  330. package/dist/chunk-VS3ZMVGX.mjs +0 -5
  331. package/web/assets/index-BK2e_07w.js +0 -13
  332. package/web/assets/index-CWFI83mF.css +0 -1
  333. /package/dist/{chunk-YEY3RSFQ.mjs → chunk-5MMN3N5J.mjs} +0 -0
  334. /package/dist/{chunk-UH4SIXTR.mjs → chunk-74UW7QSL.mjs} +0 -0
  335. /package/dist/{chunk-W7XHE4CT.mjs → chunk-EKGYYOSP.mjs} +0 -0
  336. /package/dist/{chunk-QAIHRH3I.mjs → chunk-KNZJT4ET.mjs} +0 -0
  337. /package/dist/{chunk-A55HN5OK.mjs → chunk-N3DSBH5S.mjs} +0 -0
  338. /package/dist/{chunk-QR6A54KJ.mjs → chunk-ZW2A6GBW.mjs} +0 -0
@@ -2,84 +2,109 @@
2
2
  // contributions: a column a week (Monday at the top, as a Chinese calendar starts it), a square a day,
3
3
  // darker for a day of more, read through context.workspace.query("activity") (its manifest's needs hold
4
4
  // "workspace": the server's record, .gallery/state/activity.json, a reader's commits and the Agent's
5
- // writes, by day). Its one setting, `words` (按字数显示, the card's form: the grip at its top right › 设置…, kept in its data part
6
- // `settings`, which the board writes and this block only reads), draws the words a day added (its net
7
- // change, a day that took more away than it added drawn as none) instead of how many edits it had. As
8
- // many weeks as the card holds are drawn, the last ending today, at most a year's 53: the squares as big as
9
- // the card's height lets seven of them be (3px apart, 2px where the card is low), at most so big that 53
10
- // weeks fit its width (but 8px, or three fifths of what the height lets, at least, where the height has room
11
- // for it: fewer weeks then); never more than the card holds, so that no day is cut off. It asks for every
12
- // day 53 weeks draw (the record keeps as many), so that a day drawn apart (none counted then) is one before
13
- // the record started, never one of a record older than what it asked; and the line at the top says since
14
- // when it was kept while nothing is counted yet (从 11月8日 开始记录). It reads again whenever the
15
- // workspace says its record changed (the `data` signal, "activity") and as a day begins (the kit's
16
- // clock), and draws again as its card changes size (`resize`, `layout`, and its own ResizeObserver on
17
- // the room the squares have, which hears a change of height too, as a card that got its height after the
18
- // first draw has) or its settings change (`source`). Each square says its day and its count (its title). Where the workspace cannot be read
19
- // (an exported file, where it has no server; a server that does not answer) the line says so. The words
20
- // are the page's language (the UI's where Gallery says it, else the page's lang). Its head is the cards'
21
- // (the kit's card tokens): the chip, a calendar, before its title; its squares the accent's shades, the
22
- // grid hung from the card's left edge under the head, the weekdays' column left out where the squares are
23
- // too small for its words, the months' names no closer than their own width. A wide card with something
24
- // counted shows the figures beside the squares (`summaryOf`'s `figures`, the face's `data-figures`): what
25
- // they count (期间编辑), the edits (or words) in all, large, how many days had any and today's, the period
26
- // then the head's quiet note at its end; the room the squares have is what the figures leave. Less to more
27
- // stands under the squares at their left where the card has the room for it, else at the head's end (left
28
- // out on a card too narrow for it beside the line: style.css).
5
+ // writes, by day). Its one setting, `words` (按字数显示, the card's form: the grip at its top right ›
6
+ // 设置…, kept in its data part `settings`, which the board writes and this block only reads), draws the
7
+ // words a day added (its net change, a day that took more away than it added drawn as none) instead of
8
+ // how many edits it had. It asks for every day 53 weeks draw (the record keeps as many); a day before the
9
+ // record started is a small gray dot, never an empty square, and a day to come is not drawn. It reads again
10
+ // whenever the workspace says its record changed (the `data` signal, "activity") and as a day begins (the
11
+ // kit's clock), and draws again as its card changes size (`layout`, and its own ResizeObserver on the room
12
+ // the squares have) or its settings change (`source`). Where the workspace cannot be read (an exported
13
+ // file, where it has no server; a server that does not answer) the squares are empty and a line over them
14
+ // says why. The words are the page's language (the UI's where Gallery says it, else the page's lang).
15
+ //
16
+ // Its face (the pack's design B, in the kit's card tokens): a head of the chip (a calendar), 编辑热力图
17
+ // and its quiet meta; the squares the hero, five shades of the accent by how a day stands among the days
18
+ // recorded (`levelsOf`: the quarter of them with least, and so on, never by a straight line to the most,
19
+ // so that a busy day does not make every other pale); today's square ringed; the months' names over the
20
+ // weeks they begin in and 一 三 五 beside the rows; under them, less to more. Laid out by the card's cells
21
+ // (`data-layout`, by context.size): "full" (8 × 4, the default, and 12 × 4) squares of 23px, 4px apart,
22
+ // as many weeks as fit (14, 26), and at their left the figures (`summaryOf`): what the weeks are (近 14 周),
23
+ // the edits (or words) in them, large, the average a day, the days with any, the longest run of days in
24
+ // a row and today's, and a bar of how they split between the reader and the Agent; "compact" (4 × 4) squares
25
+ // of 21px over a line of the total and the days with any; "strip" (12 × 3, a card three cells high or
26
+ // less) squares of 14px (smaller where the card is lower), a year of them, the total and the days in the
27
+ // head's meta, and the day the record began said by the legend's dot. A record begun within the week is
28
+ // said so (从 11月11日 开始记录), and an export's figures say the time they are of (截至 …). A pointer on a
29
+ // square shows its day: its edits (or words), and the reader's and the Agent's.
29
30
 
30
31
  const WORDS = {
31
32
  zh: {
32
33
  title: "编辑热力图",
33
- since: (/** @type {string} */ date) => `从 ${date} 开始记录`,
34
- edits: (/** @type {string} */ period, /** @type {number} */ count) => `${period} ${count} 次编辑`,
35
- words: (/** @type {string} */ period, /** @type {string} */ count) => `${period}写了 ${count} 字`,
34
+ weeks: (/** @type {number} */ count) => `近 ${count} 周`,
36
35
  year: "过去一年",
37
- after: (/** @type {string} */ date) => `${date}以来`,
38
- day: (/** @type {string} */ date, /** @type {number} */ edits, /** @type {number} */ words) => (edits === 0 ? `${date}:没有编辑` : `${date}:${edits} 次编辑,${words >= 0 ? "+" : "−"}${Math.abs(words)} 字`),
39
- before: (/** @type {string} */ date) => `${date}:还没开始记录`,
40
- date: (/** @type {Date} */ date) => `${date.getMonth() + 1}月${date.getDate()}日`,
41
- weekday: (/** @type {Date} */ date) => `${date.getMonth() + 1}月${date.getDate()}日 ${["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][date.getDay()]}`,
42
- month: (/** @type {number} */ month) => `${month + 1}月`,
43
- rows: ["一", "", "三", "", "五", "", ""],
44
- yearLabel: "过去一年",
45
- editsLabel: "期间编辑",
46
- wordsLabel: "期间写作",
47
- editsUnit: "次",
36
+ added: "新增",
37
+ editsUnit: "次编辑",
48
38
  wordsUnit: "字",
49
- active: (/** @type {number} */ count) => `${count} 天有编辑`,
50
- todayEdits: (/** @type {number} */ count) => `今天 ${count} 次`,
51
- todayWords: (/** @type {string} */ count) => `今天 ${count} 字`,
39
+ tenThousand: "万字",
40
+ average: (/** @type {string} */ value, /** @type {boolean} */ words) => ["平均每天 ", value, words ? " 字" : " 次"],
41
+ active: "有编辑的天数",
42
+ run: "最长连续",
43
+ today: "今天",
44
+ days: "天",
45
+ times: "次",
46
+ you: "你",
47
+ agent: "Agent",
48
+ activeDays: (/** @type {number} */ count) => `${count} 天有编辑`,
49
+ stripMeta: (/** @type {string} */ period, /** @type {string} */ total, /** @type {number} */ days) => [`${period} ${total}`, `${days} 天`],
50
+ totalEdits: (/** @type {string} */ count) => `${count} 次编辑`,
51
+ byWords: "按字数",
52
+ fresh: "刚开始记录",
53
+ freshSince: (/** @type {string} */ date) => `从 ${date} 开始记录`,
54
+ freshSaid: ["以后每天写了多少,", "都会在右边亮起一格"],
55
+ before: "开始记录之前",
56
+ beforeSince: (/** @type {string} */ date) => `${date} 开始记录之前`,
57
+ upTo: (/** @type {string} */ when) => `截至 ${when}`,
52
58
  less: "少",
53
59
  more: "多",
60
+ tip: (/** @type {string} */ date, /** @type {number} */ edits, /** @type {number} */ words, /** @type {boolean} */ byWords) => `${date} · ${byWords ? `${words >= 0 ? "+" : "−"}${Math.abs(words).toLocaleString("zh-CN")} 字` : `${edits} 次编辑`}`,
61
+ split: (/** @type {number} */ you, /** @type {number} */ agent) => `你 ${you} 次 · Agent ${agent} 次`,
62
+ unrecorded: "还没开始记录",
54
63
  offline: "在 Gallery 里打开时显示",
55
64
  failed: "读不到工作区的编辑记录",
65
+ date: (/** @type {Date} */ date) => `${date.getMonth() + 1}月${date.getDate()}日`,
66
+ weekday: (/** @type {Date} */ date) => `${date.getMonth() + 1}月${date.getDate()}日 ${["周日", "周一", "周二", "周三", "周四", "周五", "周六"][date.getDay()]}`,
67
+ month: (/** @type {number} */ month) => `${month + 1}月`,
68
+ rows: ["一", "", "三", "", "五", "", ""],
56
69
  locale: "zh-CN",
57
70
  },
58
71
  en: {
59
72
  title: "Editing heatmap",
60
- since: (/** @type {string} */ date) => `Recording since ${date}`,
61
- edits: (/** @type {string} */ period, /** @type {number} */ count) => `${count} ${count === 1 ? "edit" : "edits"} ${period}`,
62
- words: (/** @type {string} */ period, /** @type {string} */ count) => `${count} words ${period}`,
63
- year: "in the past year",
64
- after: (/** @type {string} */ date) => `since ${date}`,
65
- day: (/** @type {string} */ date, /** @type {number} */ edits, /** @type {number} */ words) => (edits === 0 ? `${date}: no edits` : `${date}: ${edits} ${edits === 1 ? "edit" : "edits"}, ${words >= 0 ? "+" : "−"}${Math.abs(words)} words`),
66
- before: (/** @type {string} */ date) => `${date}: not recorded yet`,
67
- date: (/** @type {Date} */ date) => `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]} ${date.getDate()}`,
68
- weekday: (/** @type {Date} */ date) => `${["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][date.getDay()]}, ${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]} ${date.getDate()}`,
69
- month: (/** @type {number} */ month) => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month],
70
- rows: ["Mon", "", "Wed", "", "Fri", "", ""],
71
- yearLabel: "The past year",
72
- editsLabel: "Edits",
73
- wordsLabel: "Words written",
73
+ weeks: (/** @type {number} */ count) => `Last ${count} weeks`,
74
+ year: "Past year",
75
+ added: " added",
74
76
  editsUnit: "edits",
75
77
  wordsUnit: "words",
76
- active: (/** @type {number} */ count) => `${count} ${count === 1 ? "day" : "days"} with edits`,
77
- todayEdits: (/** @type {number} */ count) => `${count} today`,
78
- todayWords: (/** @type {string} */ count) => `${count} today`,
78
+ tenThousand: "",
79
+ average: (/** @type {string} */ value, /** @type {boolean} */ words) => ["", value, words ? " words a day" : " a day"],
80
+ active: "Days with edits",
81
+ run: "Longest run",
82
+ today: "Today",
83
+ days: "d",
84
+ times: "",
85
+ you: "You",
86
+ agent: "Agent",
87
+ activeDays: (/** @type {number} */ count) => `${count} ${count === 1 ? "day" : "days"} with edits`,
88
+ stripMeta: (/** @type {string} */ period, /** @type {string} */ total, /** @type {number} */ days) => [`${period}: ${total}`, `${days} ${days === 1 ? "day" : "days"}`],
89
+ totalEdits: (/** @type {string} */ count) => `${count} edits`,
90
+ byWords: "by words",
91
+ fresh: "Just started",
92
+ freshSince: (/** @type {string} */ date) => `Recording since ${date}`,
93
+ freshSaid: ["Each day you write lights a square on the right"],
94
+ before: "Before recording",
95
+ beforeSince: (/** @type {string} */ date) => `Before ${date}`,
96
+ upTo: (/** @type {string} */ when) => `As of ${when}`,
79
97
  less: "Less",
80
98
  more: "More",
99
+ tip: (/** @type {string} */ date, /** @type {number} */ edits, /** @type {number} */ words, /** @type {boolean} */ byWords) => `${date} · ${byWords ? `${words >= 0 ? "+" : "−"}${Math.abs(words).toLocaleString("en")} words` : `${edits} ${edits === 1 ? "edit" : "edits"}`}`,
100
+ split: (/** @type {number} */ you, /** @type {number} */ agent) => `You ${you} · Agent ${agent}`,
101
+ unrecorded: "Not recorded yet",
81
102
  offline: "Shown when opened in Gallery",
82
103
  failed: "The workspace's record of edits could not be read",
104
+ date: (/** @type {Date} */ date) => `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]} ${date.getDate()}`,
105
+ weekday: (/** @type {Date} */ date) => `${["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][date.getDay()]}, ${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]} ${date.getDate()}`,
106
+ month: (/** @type {number} */ month) => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month],
107
+ rows: ["Mon", "", "Wed", "", "Fri", "", ""],
83
108
  locale: "en",
84
109
  },
85
110
  };
@@ -93,6 +118,9 @@ function languageOf(/** @type {HTMLElement} */ root) {
93
118
  const MOST_WEEKS = 53;
94
119
  /** The days of the record it reads: as many as 53 weeks draw at the most (on a Sunday, its first square 370 days back). */
95
120
  const DAYS = MOST_WEEKS * 7;
121
+ /** The weekdays' column and the months' row, in px. */
122
+ const LABELS = 20;
123
+ const MONTHS = 18;
96
124
  const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
97
125
  /** A local day as the record writes it, YYYY-MM-DD. @param {Date} date */
98
126
  const dayOf = (date) => `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}`;
@@ -100,27 +128,38 @@ const dayOf = (date) => `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two
100
128
  const after = (date, count) => new Date(date.getFullYear(), date.getMonth(), date.getDate() + count, 12);
101
129
 
102
130
  /**
103
- * How the squares fit a grid of `width` x `height` px (what the face has for it, its labels' room taken
104
- * off): { cell, gap, weeks, across }. The gap is 3px, or 2px where seven squares of 9px and their gaps would not
105
- * fit the height. The side is no more than seven of them fit the height (never cut off), at most 18px, and
106
- * no bigger than 53 weeks across the width lets, but 8px where the height has room for that, and three
107
- * fifths of what the height lets where that is more (a card far higher than a year's squares need, as on a
108
- * narrow window: fewer weeks, bigger squares), 4px at the least; then as many weeks as fit across, at
109
- * most 53, at least 1. `across` is the gap between the weeks: the gap, but where a year's 53 leave room
110
- * across (a card far wider than high), that room shared between them, at most half a square more, so that
111
- * a wide card's year reaches across it rather than leaving its right side empty.
131
+ * How a card of `size` (context.size: its cells, or null where no card holds it, a document) lays its face
132
+ * out: "grid" two cells high or less (8 × 2: the squares alone, the user's word of 2026-11-09), "strip" three
133
+ * cells high, "compact" four wide or less, else "full".
134
+ * @param {{ w: number, h: number } | null | undefined} size
135
+ * @returns {"full" | "compact" | "strip" | "grid"}
136
+ */
137
+ function layoutOf(size) {
138
+ if (!size) return "full";
139
+ if (size.h <= 2) return "grid";
140
+ if (size.h <= 3) return "strip";
141
+ return size.w <= 4 ? "compact" : "full";
142
+ }
143
+
144
+ /**
145
+ * The squares of a layout in a room of `width` x `height` px (the field's: the weekdays' column and the
146
+ * months' row inside it): { pitch (from one square to the next), cell (a square's side), gap, weeks }.
147
+ * "full" 23px squares 4px apart, "compact" 21px; "strip" and "grid" 14px, 3px apart, smaller where the room is
148
+ * lower (seven of them and the months' row in its height, 4px at the least, 2px apart under 10px); as many
149
+ * weeks as the width holds, 1 to 53.
150
+ * @param {"full" | "compact" | "strip" | "grid"} layout
112
151
  * @param {number} width
113
152
  * @param {number} height
114
153
  */
115
- function fitOf(width, height) {
116
- const gap = Math.floor((height - 18) / 7) >= 9 ? 3 : 2;
117
- const byHeight = Math.floor((height - 6 * gap) / 7);
118
- const byWidth = Math.floor((width - (MOST_WEEKS - 1) * gap) / MOST_WEEKS);
119
- const cell = Math.max(4, Math.min(18, byHeight, Math.max(byWidth, 8, Math.floor(byHeight * 0.6))));
120
- const weeks = Math.max(1, Math.min(MOST_WEEKS, Math.floor((width + gap) / (cell + gap))));
121
- const room = weeks === MOST_WEEKS ? Math.floor((width - MOST_WEEKS * cell) / (MOST_WEEKS - 1)) : gap;
122
- const across = Math.max(gap, Math.min(room, gap + Math.floor(cell / 2)));
123
- return { cell, gap, weeks, across };
154
+ function fitOf(layout, width, height) {
155
+ let pitch = layout === "full" ? 27 : layout === "compact" ? 25 : 17;
156
+ let gap = pitch === 17 ? 3 : 4;
157
+ if ((layout === "strip" || layout === "grid") && Number.isFinite(height)) {
158
+ pitch = Math.max(6, Math.min(17, Math.floor((height - MONTHS + gap) / 7)));
159
+ gap = pitch < 12 ? 2 : 3;
160
+ }
161
+ const weeks = Math.max(1, Math.min(MOST_WEEKS, Math.floor((width - LABELS + gap) / pitch)));
162
+ return { pitch, cell: pitch - gap, gap, weeks };
124
163
  }
125
164
 
126
165
  /**
@@ -143,50 +182,65 @@ function weeksOf(today, weeks) {
143
182
  }
144
183
 
145
184
  /**
146
- * The level of a day's square, 0 (none) to 4 (the most), of `value` (its edits, or the words it added,
147
- * none for a day that took more away) against the most of any day drawn, `most`.
185
+ * The bounds of the levels of `values` (a day's edits, or the words it added, of every day recorded): the
186
+ * values at 38%, 68% and 90% of the days with any, so that each shade holds a share of the days; for fewer
187
+ * than five such days, a quarter, a half and three quarters of the most.
188
+ * @param {number[]} values
189
+ */
190
+ function levelsOf(values) {
191
+ const some = values.filter((value) => value > 0).sort((a, b) => a - b);
192
+ if (some.length < 5) {
193
+ const most = some[some.length - 1] ?? 0;
194
+ return [most / 4, most / 2, (3 * most) / 4];
195
+ }
196
+ return [some[Math.floor(some.length * 0.38)], some[Math.floor(some.length * 0.68)], some[Math.floor(some.length * 0.9)]];
197
+ }
198
+
199
+ /**
200
+ * The level of a day's square, 0 (none) to 4 (the most), of `value` by the bounds `levels` (levelsOf's).
148
201
  * @param {number} value
149
- * @param {number} most
202
+ * @param {number[]} levels
150
203
  */
151
- function levelOf(value, most) {
152
- if (!(value > 0) || !(most > 0)) return 0;
153
- return Math.min(4, Math.max(1, Math.ceil((4 * value) / most)));
204
+ function levelOf(value, levels) {
205
+ if (!(value > 0)) return 0;
206
+ if (value <= levels[0]) return 1;
207
+ if (value <= levels[1]) return 2;
208
+ return value <= levels[2] ? 3 : 4;
154
209
  }
155
210
 
156
211
  /**
157
- * What the card says of the record `days` (workspace.query("activity")'s data: [{ date, edits, words }],
158
- * from the day it started or 53 weeks back to today), in `language`, counting `words` (the setting) or
159
- * edits: the line at the top, and the value of each day by its date (a Map), and since which day (the
160
- * first of them: before it nothing was kept).
161
- * @param {Array<{ date: string, edits: number, words: number }>} days
212
+ * What the card says of the record `days` (workspace.query("activity")'s data: [{ date, edits, words,
213
+ * reader, agent }], from the day it started or 53 weeks back to today) over the `weeks` it draws (`today`,
214
+ * a Date), counting `words` (the setting) or edits: `values` (each day's, by its date), `levels`, `since`
215
+ * (the first day recorded), and the figures: `total` (in the weeks drawn), `average` (a day, of the days
216
+ * recorded in them), `active` (days with any edit), `run` (the longest run of them), `today` (its value),
217
+ * `you` and `agent` (the edits of each), and `fresh` (the record began within the week).
218
+ * @param {Array<{ date: string, edits: number, words: number, reader?: { edits: number }, agent?: { edits: number } }>} days
162
219
  * @param {boolean} words
163
- * @param {"zh" | "en"} language
220
+ * @param {number} weeks
221
+ * @param {Date} today
164
222
  */
165
- function summaryOf(days, words, language) {
166
- const said = WORDS[language];
167
- const values = new Map(days.map((day) => [day.date, words ? Math.max(0, day.words) : day.edits]));
223
+ function summaryOf(days, words, weeks, today) {
224
+ const valueOf = (/** @type {{ edits: number, words: number }} */ day) => (words ? Math.max(0, day.words) : day.edits);
225
+ const values = new Map(days.map((day) => [day.date, valueOf(day)]));
168
226
  const since = days[0]?.date ?? null;
169
- const edits = days.reduce((sum, day) => sum + day.edits, 0);
170
- const sinceDate = since ? new Date(`${since}T12:00:00`) : null;
171
- const period = days.length >= 365 || !sinceDate ? said.year : said.after(said.date(sinceDate));
172
- let line;
173
- if (edits === 0) line = sinceDate ? said.since(said.date(sinceDate)) : "";
174
- else if (words) line = said.words(period, new Intl.NumberFormat(said.locale, { notation: "compact", maximumFractionDigits: 1 }).format(days.reduce((sum, day) => sum + day.words, 0)));
175
- else line = said.edits(period, edits);
176
- // The figures a wide card shows beside its squares: the edits (or the words) in all, how many days had
177
- // any, and today's.
178
- const format = (/** @type {number} */ value) => new Intl.NumberFormat(said.locale, Math.abs(value) >= 100_000 ? { notation: "compact", maximumFractionDigits: 1 } : { maximumFractionDigits: 0 }).format(value);
179
- const written = days.reduce((sum, day) => sum + day.words, 0);
180
- const today = days.find((day) => day.date === dayOf(new Date()));
181
- const signed = (/** @type {number} */ value) => `${value > 0 ? "+" : value < 0 ? "−" : ""}${format(Math.abs(value))}`;
182
- const figures = edits === 0 ? null : {
183
- period: period === said.year ? said.yearLabel : period.replace(/^since /, "Since "),
184
- label: words ? said.wordsLabel : said.editsLabel,
185
- total: words ? format(Math.max(0, written)) : format(edits),
186
- unit: words ? said.wordsUnit : said.editsUnit,
187
- more: [said.active(days.filter((day) => day.edits > 0).length), ...(today && today.edits > 0 ? [words ? said.todayWords(signed(today.words)) : said.todayEdits(today.edits)] : [])],
227
+ const first = dayOf(after(today, -((today.getDay() + 6) % 7) - 7 * (weeks - 1)));
228
+ const shown = days.filter((day) => day.date >= first && day.date <= dayOf(today));
229
+ let run = 0;
230
+ let longest = 0;
231
+ for (const day of shown) {
232
+ run = day.edits > 0 ? run + 1 : 0;
233
+ longest = Math.max(longest, run);
234
+ }
235
+ const total = shown.reduce((sum, day) => sum + valueOf(day), 0);
236
+ const agent = shown.reduce((sum, day) => sum + (day.agent?.edits ?? 0), 0);
237
+ const you = shown.reduce((sum, day) => sum + (day.reader?.edits ?? 0), 0);
238
+ const now = days.find((day) => day.date === dayOf(today));
239
+ return {
240
+ values, since, levels: levelsOf([...values.values()]),
241
+ total, average: shown.length > 0 ? total / shown.length : 0, active: shown.filter((day) => day.edits > 0).length, run: longest,
242
+ today: now ? valueOf(now) : 0, you, agent, fresh: since !== null && since > dayOf(after(today, -7)),
188
243
  };
189
- return { line, values, since, figures };
190
244
  }
191
245
 
192
246
  /**
@@ -205,87 +259,160 @@ function made(tag, kind, text) {
205
259
 
206
260
  DocKit.component({
207
261
  id: "gallery/heatmap",
208
- on: ["data", "source", "resize", "layout", "clock"],
262
+ on: ["data", "source", "layout", "clock"],
209
263
  mount(host, context) {
210
264
  const language = languageOf(document.documentElement);
211
265
  const words = WORDS[language];
266
+ const format = (/** @type {number} */ value) => new Intl.NumberFormat(words.locale, { maximumFractionDigits: 0 }).format(value);
212
267
  const face = made("div", "face");
213
268
  const head = made("div", "head");
214
- const line = made("span", "line");
269
+ const meta = made("span", "line");
215
270
  const chip = made("span", "chip");
216
271
  chip.setAttribute("aria-hidden", "true");
217
- chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path 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"/><path d="M16 3v4M8 3v4M4 11h16"/></svg>';
218
- head.append(chip, made("span", "title", words.title), line);
272
+ chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="5" width="17" height="15.5" rx="3"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg>';
273
+ head.append(chip, made("span", "title", words.title), meta);
219
274
  const body = made("div", "body");
220
- const months = made("div", "months");
221
- const rows = made("div", "rows");
222
- for (const label of words.rows) rows.append(made("span", "row", label));
223
- const grid = made("div", "grid");
224
- const area = made("div", "area");
225
- area.append(months, grid);
226
- // Beside the squares on a wide card: what they count, its edits (or words) large, and a muted line of the days with any and today's.
275
+ // The figures beside the squares (a full card), or the line over them (a compact one).
227
276
  const aside = made("div", "aside");
228
277
  const period = made("span", "label");
229
278
  const total = made("span", "total");
230
279
  const totalNumber = made("span", "number");
231
280
  const totalUnit = made("span", "unit");
232
281
  total.append(totalNumber, totalUnit);
233
- const asideMore = made("span", "more");
234
- aside.append(period, total, asideMore);
235
- body.append(aside, rows, area);
282
+ const average = made("span", "average");
283
+ const facts = made("dl", "facts");
284
+ const who = made("div", "who");
285
+ const split = made("span", "split");
286
+ const youBar = made("i", "you-bar");
287
+ const agentBar = made("i", "agent-bar");
288
+ split.append(youBar, agentBar);
289
+ const whoWords = made("span", "who-words");
290
+ who.append(split, whoWords);
291
+ aside.append(period, total, average, facts, who);
292
+ const field = made("div", "field");
293
+ const area = made("div", "area");
294
+ const months = made("div", "months");
295
+ const rows = made("div", "rows");
296
+ for (const label of words.rows) rows.append(made("span", "row", label));
297
+ const grid = made("div", "grid");
298
+ // The tip stands over the card, in the page's top layer (a popover; where a browser has none, fixed to the
299
+ // window), never inside the card, whose overflow a tip near its edge would widen into a scrollbar.
300
+ const tip = made("div", "tip");
301
+ const popover = typeof tip.showPopover === "function";
302
+ if (popover) tip.popover = "manual";
303
+ else tip.hidden = true;
304
+ tip.setAttribute("aria-hidden", "true");
305
+ area.append(months, rows, grid, tip);
236
306
  const legend = made("div", "legend");
307
+ const note = made("span", "note");
237
308
  legend.append(made("span", "less", words.less), ...[0, 1, 2, 3, 4].map((level) => {
238
309
  const square = made("span", "square");
239
310
  square.dataset.level = String(level);
240
311
  return square;
241
- }), made("span", "more", words.more));
242
- face.append(head, body);
312
+ }), made("span", "more", words.more), note);
313
+ const overlay = made("p", "overlay");
314
+ overlay.hidden = true;
315
+ field.append(area, legend, overlay);
316
+ body.append(aside, field);
317
+ // As wide as the corner a board's grip takes of the content's top right (--gallery-grip-room, a custom
318
+ // property CSS alone resolves), measured where the months' row is the face's first (the squares alone).
319
+ const gripRoom = made("i", "grip-room");
320
+ gripRoom.setAttribute("aria-hidden", "true");
321
+ face.append(head, body, gripRoom);
243
322
  host.append(face);
244
323
  const workspace = context.workspace;
245
324
  if (!workspace) return void context.fail(new Error("its manifest's needs hold \"workspace\": context.workspace is not there"));
246
325
 
247
- /** @type {Array<{ date: string, edits: number, words: number }> | null} */
326
+ /** @type {Array<{ date: string, edits: number, words: number, reader?: { edits: number, words: number }, agent?: { edits: number, words: number } }> | null} */
248
327
  let days = null;
328
+ let at = "";
249
329
  let failure = "";
250
330
  let drawn = "";
331
+ const kv = (/** @type {string} */ name, /** @type {string} */ value, /** @type {string} */ unit) => {
332
+ const row = made("div", "fact");
333
+ const said = made("dd", "fact-value", value);
334
+ if (unit) said.append(made("small", "fact-unit", unit));
335
+ row.append(made("dt", "fact-name", name), said);
336
+ return row;
337
+ };
338
+ const bigOf = (/** @type {number} */ value, /** @type {boolean} */ byWords) => {
339
+ if (!byWords) return [format(value), language === "zh" ? words.editsUnit : ` ${words.editsUnit}`];
340
+ if (language === "zh" && value >= 10_000) return [(value / 10_000).toFixed(1), words.tenThousand];
341
+ return [format(value), language === "zh" ? words.wordsUnit : ` ${words.wordsUnit}`];
342
+ };
251
343
  const draw = () => {
252
344
  const settings = context.readJSON("settings");
253
345
  if (settings === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
254
346
  const byWords = settings !== null && typeof settings === "object" && settings.words === true;
255
- // The room the grid has: the face's, its labels' and its lines' taken off.
347
+ const layout = layoutOf(context.size);
348
+ face.dataset.layout = layout;
256
349
  const today = new Date();
257
- const summary = summaryOf(days ?? [], byWords, language);
258
- // The figures, where there are some (the aside shows them on a card wide enough: style.css).
259
- const figures = failure ? null : summary.figures;
260
- face.toggleAttribute("data-figures", figures !== null);
261
- if (figures) {
262
- period.textContent = figures.label;
263
- totalNumber.textContent = figures.total;
264
- totalUnit.textContent = figures.unit;
265
- asideMore.replaceChildren(...figures.more.map((piece) => made("span", "piece", piece)));
266
- }
267
- // The room the grid has: the face's, its labels' and its lines' taken off (the weekdays' column and the gap after it; the aside, where it shows,
268
- // its margin and the gap after it: a wide card's squares had run 3px past its edge where its weeks filled the room).
269
- const width = body.clientWidth - rows.offsetWidth - 6 - (aside.offsetWidth > 0 ? aside.offsetWidth + 6 + 6 : 0);
270
- const height = body.clientHeight - months.offsetHeight - 4;
271
- const { cell, gap, weeks, across } = fitOf(width, height);
272
- // Less to more: under the squares where the room left under them holds it, else at the head's end.
273
- const shownAside = aside.offsetWidth > 0;
274
- const under = height - (7 * cell + 6 * gap) >= 22;
275
- const place = under ? area : head;
276
- if (legend.parentElement !== place) place.append(legend);
277
- // The head's quiet note: the line, or, where the figures say the rest beside the squares, their period.
278
- line.textContent = failure || (shownAside && figures ? figures.period : summary.line);
279
- const key = JSON.stringify([cell, gap, weeks, across, dayOf(today), byWords, days, failure, language, aside.offsetWidth, under]);
350
+ // The room the squares have: the field's, the legend's taken off (a strip's height says how large they are).
351
+ const room = { width: field.clientWidth || 600, height: field.clientHeight > 0 ? field.clientHeight - legend.offsetHeight : Infinity };
352
+ const { pitch, cell, gap, weeks } = fitOf(layout, room.width, room.height);
353
+ const summary = summaryOf(days ?? [], byWords, weeks, today);
354
+ const exported = context.place === "export" && at;
355
+ const fresh = days !== null && summary.fresh;
356
+ face.toggleAttribute("data-fresh", fresh);
357
+ face.toggleAttribute("data-unread", Boolean(failure));
358
+ const key = JSON.stringify([layout, pitch, weeks, dayOf(today), byWords, days, failure, at]);
280
359
  if (key === drawn) return;
281
360
  drawn = key;
361
+ hide(); // the squares drawn anew: the one it stood over is gone
362
+ // The head's meta.
363
+ const [bigValue, bigUnit] = bigOf(summary.total, byWords);
364
+ const atWhen = exported ? (() => { const when = new Date(at); return `${words.date(when)} ${two(when.getHours())}:${two(when.getMinutes())}`; })() : "";
365
+ if (failure) meta.textContent = "";
366
+ else if (layout === "strip") {
367
+ const [first, second] = words.stripMeta(weeks >= 52 ? words.year : words.weeks(weeks), byWords ? `${bigValue} ${bigUnit.trim()}` : words.totalEdits(format(summary.total)), summary.active);
368
+ const dot = made("span", "dot", "·");
369
+ meta.replaceChildren(first, dot, second);
370
+ } else if (layout === "compact") meta.textContent = words.weeks(weeks) + (byWords ? ` · ${words.byWords}` : "");
371
+ else meta.textContent = fresh ? words.fresh : exported ? words.upTo(atWhen) : byWords ? words.byWords : "";
372
+ // The figures.
373
+ totalNumber.textContent = failure ? "—" : bigValue;
374
+ totalUnit.textContent = failure ? "" : bigUnit;
375
+ if (fresh && summary.since) {
376
+ period.textContent = words.freshSince(words.date(new Date(`${summary.since}T12:00:00`)));
377
+ average.replaceChildren();
378
+ // Its clauses each whole on a line where one fits (style.css), so that it breaks after the comma.
379
+ const said = made("p", "fresh");
380
+ said.append(...words.freshSaid.map((clause) => made("span", "clause", clause)));
381
+ facts.replaceChildren(said);
382
+ } else {
383
+ period.textContent = layout === "compact" ? "" : `${words.weeks(weeks)}${byWords ? words.added : ""}`;
384
+ const [before, value, afterwards] = words.average(byWords ? format(Math.round(summary.average)) : summary.average.toFixed(1), byWords);
385
+ average.replaceChildren(before, made("b", "strong", value), afterwards);
386
+ const todayName = exported ? words.date(new Date(at)) : words.today;
387
+ facts.replaceChildren(
388
+ kv(words.active, String(summary.active), words.days),
389
+ kv(words.run, String(summary.run), words.days),
390
+ kv(todayName, byWords ? format(summary.today) : String(summary.today), byWords ? words.wordsUnit : words.times),
391
+ );
392
+ }
393
+ if (layout === "compact") average.replaceChildren(words.activeDays(summary.active));
394
+ const both = summary.you + summary.agent;
395
+ const youShare = both > 0 ? Math.round((100 * summary.you) / both) : 0;
396
+ youBar.style.flex = String(summary.you || (both ? 0 : 1));
397
+ agentBar.style.flex = String(summary.agent);
398
+ const label = (/** @type {string} */ kind, /** @type {string} */ name, /** @type {number} */ share) => {
399
+ const piece = made("span", kind);
400
+ piece.append(made("i", "swatch"), `${name} `, made("b", "strong", `${share}%`));
401
+ return piece;
402
+ };
403
+ whoWords.replaceChildren(label("you", words.you, youShare), label("agent", words.agent, both > 0 ? 100 - youShare : 0));
404
+ who.hidden = both === 0 || fresh;
405
+ // The legend's note: since when the record is kept, where days before it are drawn.
406
+ const columns = weeksOf(today, weeks);
407
+ const firstDay = columns[0][0].day;
408
+ const before = days !== null && summary.since !== null && summary.since > firstDay;
409
+ note.replaceChildren(...(before && layout !== "compact" ? [made("s", "dot-key"), layout === "strip" ? words.beforeSince(words.date(new Date(`${summary.since}T12:00:00`))) : words.before] : []));
410
+ overlay.hidden = !failure;
411
+ overlay.textContent = failure;
412
+ // The squares.
282
413
  face.style.setProperty("--gallery-heatmap-cell", `${cell}px`);
283
414
  face.style.setProperty("--gallery-heatmap-gap", `${gap}px`);
284
- face.style.setProperty("--gallery-heatmap-across", `${across}px`);
285
415
  face.style.setProperty("--gallery-heatmap-weeks", String(weeks));
286
- face.toggleAttribute("data-small", cell < 6);
287
- const columns = weeksOf(today, weeks);
288
- const most = Math.max(0, ...[...summary.values.values()]);
289
416
  const byDate = new Map((days ?? []).map((item) => [item.date, item]));
290
417
  grid.replaceChildren(...columns.flatMap((column) => column.map(({ date, day, future }) => {
291
418
  const square = made("span", "day");
@@ -293,36 +420,71 @@ DocKit.component({
293
420
  square.classList.add("gallery-heatmap-future");
294
421
  return square;
295
422
  }
423
+ square.dataset.date = day;
424
+ if (day === dayOf(today)) square.classList.add("gallery-heatmap-today");
296
425
  const recorded = days !== null && summary.since !== null && day >= summary.since;
297
426
  if (!recorded) {
298
427
  square.classList.add("gallery-heatmap-before");
299
- square.title = days === null ? words.weekday(date) : words.before(words.weekday(date));
428
+ square.setAttribute("aria-label", `${words.weekday(date)} · ${days === null ? failure : words.unrecorded}`);
300
429
  return square;
301
430
  }
302
431
  const entry = byDate.get(day);
303
- square.dataset.level = String(levelOf(summary.values.get(day) ?? 0, most));
304
- square.title = words.day(words.weekday(date), entry?.edits ?? 0, entry?.words ?? 0);
432
+ square.dataset.level = String(levelOf(summary.values.get(day) ?? 0, summary.levels));
433
+ square.setAttribute("aria-label", `${words.tip(words.weekday(date), entry?.edits ?? 0, entry?.words ?? 0, byWords)}(${words.split(entry?.reader?.edits ?? 0, entry?.agent?.edits ?? 0)})`);
305
434
  return square;
306
435
  })));
307
- // A month's name over the week its first day falls in, none closer to the one before than its own
308
- // width (about 26px: "12月", "Sep") and three weeks.
309
- const apart = Math.max(3, Math.ceil(28 / (cell + across)));
310
- let last = -apart;
311
- months.replaceChildren(...columns.flatMap((column, index) => {
312
- const first = column.find(({ date }) => date.getDate() === 1);
313
- if (!first || index - last < apart) return [];
314
- last = index;
315
- const label = made("span", "month", words.month(first.date.getMonth()));
316
- label.style.setProperty("--gallery-heatmap-week", String(index));
317
- return [label];
436
+ // A month's name over the week its first day falls in (the first week's too, but where the next is
437
+ // fewer than three weeks after it).
438
+ const starts = columns.flatMap((column, index) => (index === 0 || column[0].date.getMonth() !== columns[index - 1][0].date.getMonth() ? [index] : []));
439
+ if (starts.length > 1 && starts[1] < 3) starts.shift();
440
+ months.replaceChildren(...starts.map((index) => {
441
+ const name = made("span", "month", words.month(columns[index][0].date.getMonth()));
442
+ name.style.setProperty("--gallery-heatmap-week", String(index));
443
+ return name;
318
444
  }));
445
+ // The squares alone: the months' row is the card's first, and a name that would reach into the grip's corner is left out.
446
+ if (layout === "grid") {
447
+ const end = face.getBoundingClientRect().right - gripRoom.offsetWidth;
448
+ for (const name of [...months.children]) if (name.getBoundingClientRect().right > end) name.remove();
449
+ }
450
+ };
451
+ // A pointer on a square: its day, its count and whose, over it (under it where the window has no room
452
+ // above), its middle at the square's but held 8px inside the window, the arrow at the square.
453
+ const hide = () => {
454
+ if (popover) {
455
+ if (tip.matches(":popover-open")) tip.hidePopover();
456
+ } else tip.hidden = true;
457
+ };
458
+ const show = (/** @type {Event} */ event) => {
459
+ const square = /** @type {HTMLElement | null} */ (/** @type {HTMLElement} */ (event.target).closest?.(".gallery-heatmap-day[data-date]"));
460
+ if (!square || !grid.contains(square)) return hide();
461
+ const [first, second] = (square.getAttribute("aria-label") ?? "").replace(/)$/, "").split("(");
462
+ tip.replaceChildren(first, ...(second ? [made("small", "tip-split", second)] : []));
463
+ if (popover) {
464
+ if (!tip.matches(":popover-open")) tip.showPopover();
465
+ } else tip.hidden = false;
466
+ const box = square.getBoundingClientRect();
467
+ const width = tip.offsetWidth;
468
+ const height = tip.offsetHeight;
469
+ const left = Math.max(8, Math.min(innerWidth - 8 - width, box.left + box.width / 2 - width / 2));
470
+ const below = box.top - 8 - height < 8;
471
+ tip.style.left = `${Math.round(left)}px`;
472
+ tip.style.top = `${Math.round(below ? box.bottom + 8 : box.top - 8 - height)}px`;
473
+ tip.style.setProperty("--gallery-heatmap-arrow", `${Math.round(Math.max(10, Math.min(width - 10, box.left + box.width / 2 - left)))}px`);
474
+ tip.toggleAttribute("data-below", below);
319
475
  };
476
+ context.listen(grid, "pointerover", show);
477
+ context.listen(grid, "pointerleave", hide);
478
+ // A scroll (the page's, or a card's) moves the square from under it: gone until the pointer is on one again.
479
+ context.listen(document, "scroll", hide, { capture: true, passive: true });
480
+ context.signal?.addEventListener("abort", hide, { once: true });
320
481
  let asking = 0; // the last read asked: an older answer that comes after it is dropped
321
482
  const read = () => {
322
483
  const ticket = ++asking;
323
- workspace.query("activity", { days: DAYS }).then(({ data }) => {
484
+ workspace.query("activity", { days: DAYS }).then(({ data, at: when }) => {
324
485
  if (ticket !== asking || context.signal.aborted) return;
325
486
  days = data;
487
+ at = typeof when === "string" ? when : "";
326
488
  failure = "";
327
489
  draw();
328
490
  }, (/** @type {any} */ error) => {
@@ -334,8 +496,8 @@ DocKit.component({
334
496
  };
335
497
  draw();
336
498
  read();
337
- // The room the squares have, its height too (the kit's `resize` hears its width alone): a card given its
338
- // height after the first draw (a board laid out late) is drawn again as it gets it.
499
+ // The room the squares have, its height too: a card given its height after the first draw (a board laid
500
+ // out late) is drawn again as it gets it.
339
501
  if (typeof ResizeObserver === "function") {
340
502
  const watch = new ResizeObserver(() => draw());
341
503
  watch.observe(body);