@heycys/gallery 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/THIRD-PARTY-NOTICES.md +1 -1
  2. package/browser/editor/components/columns/page.mjs +4 -4
  3. package/browser/editor/components/embed/index.mjs +2 -2
  4. package/browser/editor/components/flowchart/add.mjs +7 -6
  5. package/browser/editor/components/flowchart/page.mjs +1 -1
  6. package/browser/editor/components/icons.mjs +8 -0
  7. package/browser/editor/components/image/crop.mjs +6 -9
  8. package/browser/editor/components/installed/index.mjs +11 -4
  9. package/browser/editor/components/installed/page.mjs +27 -16
  10. package/browser/editor/components/installed/served.mjs +6 -5
  11. package/browser/editor/components/instances.mjs +18 -0
  12. package/browser/editor/components/list/page.mjs +3 -6
  13. package/browser/editor/components/registry.mjs +13 -4
  14. package/browser/editor/components/table/index.mjs +59 -2
  15. package/browser/editor/components/table/page.mjs +1 -1
  16. package/browser/editor/page/annotate/card.mjs +3 -6
  17. package/browser/editor/page/autosave.mjs +310 -37
  18. package/browser/editor/page/board/client.mjs +57 -0
  19. package/browser/editor/page/board/hint.mjs +145 -0
  20. package/browser/editor/page/board/history.mjs +119 -0
  21. package/browser/editor/page/board/index.mjs +970 -0
  22. package/browser/editor/page/board/settings.mjs +209 -0
  23. package/browser/editor/page/caret.mjs +1 -1
  24. package/browser/editor/page/changes/bar.mjs +6 -14
  25. package/browser/editor/page/changes/index.mjs +2 -2
  26. package/browser/editor/page/clipboard/import/html.mjs +29 -6
  27. package/browser/editor/page/clipboard/import/inline.mjs +32 -0
  28. package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
  29. package/browser/editor/page/clipboard/import/read.mjs +6 -1
  30. package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
  31. package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
  32. package/browser/editor/page/clipboard/index.mjs +124 -13
  33. package/browser/editor/page/clipboard/own/blocks.mjs +11 -11
  34. package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
  35. package/browser/editor/page/clipboard/own/selection.mjs +1 -1
  36. package/browser/editor/page/columns/index.mjs +48 -46
  37. package/browser/editor/page/commit.mjs +80 -22
  38. package/browser/editor/page/conflicts.mjs +7 -7
  39. package/browser/editor/page/core.mjs +31 -15
  40. package/browser/editor/page/custom-page.mjs +106 -0
  41. package/browser/editor/page/editor.mjs +51 -14
  42. package/browser/editor/page/emoji-picker/styles.mjs +7 -12
  43. package/browser/editor/page/emoji-picker/upload.mjs +2 -2
  44. package/browser/editor/page/handle/index.mjs +5 -8
  45. package/browser/editor/page/history.mjs +24 -4
  46. package/browser/editor/page/links/index.mjs +5 -3
  47. package/browser/editor/page/links/pages.mjs +27 -9
  48. package/browser/editor/page/look.mjs +54 -3
  49. package/browser/editor/page/margin.mjs +18 -3
  50. package/browser/editor/page/media-menu/index.mjs +6 -7
  51. package/browser/editor/page/menu.mjs +18 -7
  52. package/browser/editor/page/notices.mjs +2 -2
  53. package/browser/editor/page/page-objects.mjs +13 -2
  54. package/browser/editor/page/panel.mjs +4 -9
  55. package/browser/editor/page/present-links.mjs +11 -2
  56. package/browser/editor/page/resize/index.mjs +42 -29
  57. package/browser/editor/page/served.mjs +14 -0
  58. package/browser/editor/page/session.mjs +48 -4
  59. package/browser/editor/page/slash-menu/index.mjs +3 -3
  60. package/browser/editor/page/table/cells.mjs +102 -46
  61. package/browser/editor/page/table/icons.mjs +2 -0
  62. package/browser/editor/page/table/index.mjs +29 -10
  63. package/browser/editor/page/table/pictures.mjs +299 -0
  64. package/browser/editor/page/table/views.mjs +150 -0
  65. package/browser/editor/page/text-select/index.mjs +26 -12
  66. package/browser/editor/page/todo-writes.mjs +75 -0
  67. package/browser/editor/page/typing/index.mjs +53 -3
  68. package/browser/editor/page/ui.mjs +13 -0
  69. package/browser/shared/block-packs.mjs +404 -0
  70. package/browser/shared/board-places.mjs +224 -0
  71. package/browser/shared/card-size.mjs +59 -0
  72. package/browser/shared/colors.mjs +3 -2
  73. package/browser/shared/component-catalog.mjs +323 -46
  74. package/browser/shared/component-source.mjs +37 -0
  75. package/browser/shared/components/index.mjs +12 -3
  76. package/browser/shared/components/installed.mjs +35 -3
  77. package/browser/shared/components/page.mjs +18 -8
  78. package/browser/shared/components/state.mjs +5 -1
  79. package/browser/shared/document-look.mjs +10 -5
  80. package/browser/shared/drafts.mjs +432 -0
  81. package/browser/shared/extension-sidebar.mjs +126 -0
  82. package/browser/shared/extensions.mjs +79 -0
  83. package/browser/shared/file-table.mjs +101 -0
  84. package/browser/shared/i18n/en.mjs +307 -27
  85. package/browser/shared/json-layout.mjs +47 -0
  86. package/browser/shared/net-cache.mjs +133 -0
  87. package/browser/shared/net-keys.mjs +32 -0
  88. package/browser/shared/net-policy.mjs +177 -0
  89. package/browser/shared/outline-shape.mjs +128 -0
  90. package/browser/shared/outline.mjs +331 -67
  91. package/browser/shared/pack-manifest.mjs +77 -0
  92. package/browser/shared/page-address.mjs +6 -3
  93. package/browser/shared/page-cards.mjs +308 -0
  94. package/browser/shared/page-links.mjs +69 -6
  95. package/browser/shared/pages.mjs +388 -0
  96. package/browser/shared/paths.mjs +6 -0
  97. package/browser/shared/preview-server.mjs +3 -7
  98. package/browser/shared/session.mjs +8 -1
  99. package/browser/shared/shipped-blocks.mjs +299 -0
  100. package/browser/shared/table-chart.mjs +33 -1
  101. package/browser/shared/todos.mjs +83 -0
  102. package/browser/shared/user-directory.mjs +24 -0
  103. package/browser/shared/versions.mjs +14 -0
  104. package/browser/shared/web-links.mjs +134 -0
  105. package/browser/shared/workspace-columns.mjs +120 -0
  106. package/browser/shared/workspace-name.mjs +154 -0
  107. package/browser/shared/workspace-query.mjs +125 -0
  108. package/browser/shared/workspace-registry.mjs +272 -0
  109. package/browser/shared/workspace.mjs +171 -20
  110. package/dist/chunk-2NRBY6AJ.mjs +2 -0
  111. package/dist/chunk-32ZUEFZ7.mjs +2 -0
  112. package/dist/chunk-35NQ4PLQ.mjs +2 -0
  113. package/dist/chunk-3KJHLHUO.mjs +2 -0
  114. package/dist/chunk-3NJACR4W.mjs +2 -0
  115. package/dist/chunk-4AV456RQ.mjs +2 -0
  116. package/dist/chunk-4OZWXZE5.mjs +5 -0
  117. package/dist/chunk-5XYHDBJ5.mjs +2 -0
  118. package/dist/chunk-5ZJA7CXT.mjs +2 -0
  119. package/dist/chunk-6A53MZYZ.mjs +2 -0
  120. package/dist/chunk-6EXH3ORZ.mjs +166 -0
  121. package/dist/chunk-6EZ7FNIK.mjs +3 -0
  122. package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
  123. package/dist/{chunk-Y6RLRPG3.mjs → chunk-6VTU6KEO.mjs} +1 -1
  124. package/dist/chunk-B4P2CM2J.mjs +3 -0
  125. package/dist/{chunk-FDLXYYG4.mjs → chunk-BPL64HGA.mjs} +1 -1
  126. package/dist/chunk-BVEFGE4B.mjs +2 -0
  127. package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
  128. package/dist/chunk-CRJEG7MT.mjs +2 -0
  129. package/dist/{chunk-LCTHTZIL.mjs → chunk-D4CKRD3V.mjs} +91 -91
  130. package/dist/chunk-D4EURAX4.mjs +4 -0
  131. package/dist/chunk-DQCE7TJY.mjs +5 -0
  132. package/dist/chunk-DR73Y2UJ.mjs +3 -0
  133. package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
  134. package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
  135. package/dist/chunk-EURLTOTZ.mjs +4 -0
  136. package/dist/chunk-FJYN7RTH.mjs +4 -0
  137. package/dist/chunk-FWNRPMCJ.mjs +12 -0
  138. package/dist/chunk-FWW4CSAZ.mjs +2 -0
  139. package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
  140. package/dist/chunk-GY3FAUVM.mjs +2 -0
  141. package/dist/chunk-HLYV7RIH.mjs +2 -0
  142. package/dist/chunk-HMYTM3GT.mjs +15 -0
  143. package/dist/chunk-IA4PSIMD.mjs +2 -0
  144. package/dist/chunk-JEUK3ZO7.mjs +2 -0
  145. package/dist/chunk-KTKHRC6H.mjs +2 -0
  146. package/dist/chunk-L7UP52JO.mjs +2 -0
  147. package/dist/{chunk-SYGN7GLU.mjs → chunk-MHW7ZKOU.mjs} +1 -1
  148. package/dist/chunk-N76HWBA3.mjs +10 -0
  149. package/dist/chunk-N7AZZLGT.mjs +548 -0
  150. package/dist/chunk-NAMN3RAF.mjs +2 -0
  151. package/dist/{chunk-5ZKJ3H4P.mjs → chunk-NFMCP2HI.mjs} +3 -3
  152. package/dist/chunk-NG375ILQ.mjs +2 -0
  153. package/dist/chunk-NVRNLK25.mjs +6 -0
  154. package/dist/chunk-NWFBE2YA.mjs +2 -0
  155. package/dist/chunk-NWR2LT5B.mjs +2 -0
  156. package/dist/chunk-OJKMINIU.mjs +20 -0
  157. package/dist/{chunk-4FS7Z7WR.mjs → chunk-OUUP3WKT.mjs} +1 -1
  158. package/dist/{chunk-BGGDRNYQ.mjs → chunk-OW5NWPOJ.mjs} +1 -1
  159. package/dist/chunk-PAREWQGB.mjs +4 -0
  160. package/dist/chunk-POQSE4C2.mjs +2 -0
  161. package/dist/chunk-PUOQ65NC.mjs +2 -0
  162. package/dist/{chunk-XNROSH2T.mjs → chunk-PVER7LSC.mjs} +2 -2
  163. package/dist/chunk-PZDLJJTJ.mjs +3 -0
  164. package/dist/chunk-Q47PLKIJ.mjs +15 -0
  165. package/dist/chunk-Q4UEPUWF.mjs +2 -0
  166. package/dist/chunk-QKXMPUXC.mjs +2 -0
  167. package/dist/chunk-QOUEFUCH.mjs +16 -0
  168. package/dist/chunk-RFDZXFJF.mjs +13 -0
  169. package/dist/chunk-RKPWLGZ3.mjs +4 -0
  170. package/dist/chunk-RMECZA2K.mjs +2 -0
  171. package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  172. package/dist/chunk-SX4LJYT6.mjs +8 -0
  173. package/dist/chunk-SXHBYZST.mjs +16 -0
  174. package/dist/chunk-TANZRINB.mjs +2 -0
  175. package/dist/chunk-TEZAWDSQ.mjs +2 -0
  176. package/dist/chunk-UBBRTJ2R.mjs +24 -0
  177. package/dist/chunk-UFNYFFKO.mjs +20 -0
  178. package/dist/chunk-UGWYKHBL.mjs +170 -0
  179. package/dist/chunk-UKSHDZZ6.mjs +2 -0
  180. package/dist/chunk-UNOYVIYR.mjs +2 -0
  181. package/dist/{chunk-E7LWZRXY.mjs → chunk-V6RIYQZZ.mjs} +1 -1
  182. package/dist/chunk-VJPFVNNW.mjs +15 -0
  183. package/dist/chunk-VS3ZMVGX.mjs +5 -0
  184. package/dist/chunk-VTKNEYT3.mjs +6 -0
  185. package/dist/chunk-VXACTLO3.mjs +3 -0
  186. package/dist/chunk-YI43RG4P.mjs +2 -0
  187. package/dist/chunk-YQGT4AB2.mjs +6 -0
  188. package/dist/chunk-ZB4H7ZN4.mjs +2 -0
  189. package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
  190. package/dist/chunk-ZTHICSSF.mjs +2 -0
  191. package/dist/chunk-ZUNVAXLH.mjs +2 -0
  192. package/dist/chunk-ZWPMCK7P.mjs +2 -0
  193. package/dist/cli.mjs +1 -1
  194. package/dist/server.mjs +1 -1
  195. package/dist/{chunk-U6KNPJPR.mjs → update-fetch.mjs} +1 -1
  196. package/dist/worker.mjs +1 -1
  197. package/kit/board.css +95 -0
  198. package/kit/board.js +250 -0
  199. package/kit/charts.js +5 -71
  200. package/kit/code.js +14 -14
  201. package/kit/report.css +255 -17
  202. package/kit/runtime.js +1600 -125
  203. package/kit/state.js +10 -1
  204. package/kit/tables.js +128 -43
  205. package/kit/ui.js +77 -35
  206. package/package.json +2 -1
  207. package/packs/gallery/card/component.json +14 -0
  208. package/packs/gallery/card/example.html +10 -0
  209. package/packs/gallery/card/preview.svg +1 -0
  210. package/packs/gallery/card/style.css +6 -0
  211. package/packs/gallery/card/template.html +6 -0
  212. package/packs/gallery/card/writing.md +14 -0
  213. package/packs/gallery/chart-card/component.json +15 -0
  214. package/packs/gallery/chart-card/example.html +14 -0
  215. package/packs/gallery/chart-card/kit.js +130 -0
  216. package/packs/gallery/chart-card/preview.svg +1 -0
  217. package/packs/gallery/chart-card/style.css +42 -0
  218. package/packs/gallery/chart-card/template.html +13 -0
  219. package/packs/gallery/chart-card/writing.md +20 -0
  220. package/packs/gallery/chart-view/component.json +20 -0
  221. package/packs/gallery/chart-view/example.html +13 -0
  222. package/packs/gallery/chart-view/kit.js +355 -0
  223. package/packs/gallery/chart-view/preview.svg +1 -0
  224. package/packs/gallery/chart-view/style.css +94 -0
  225. package/packs/gallery/chart-view/template.html +3 -0
  226. package/packs/gallery/chart-view/writing.md +22 -0
  227. package/packs/gallery/countdown/component.json +18 -0
  228. package/packs/gallery/countdown/example.html +3 -0
  229. package/packs/gallery/countdown/kit.js +156 -0
  230. package/packs/gallery/countdown/preview.svg +1 -0
  231. package/packs/gallery/countdown/style.css +130 -0
  232. package/packs/gallery/countdown/template.html +3 -0
  233. package/packs/gallery/countdown/writing.md +22 -0
  234. package/packs/gallery/habit/component.json +14 -0
  235. package/packs/gallery/habit/example.html +3 -0
  236. package/packs/gallery/habit/kit.js +295 -0
  237. package/packs/gallery/habit/preview.svg +1 -0
  238. package/packs/gallery/habit/style.css +246 -0
  239. package/packs/gallery/habit/template.html +3 -0
  240. package/packs/gallery/habit/writing.md +21 -0
  241. package/packs/gallery/heatmap/component.json +18 -0
  242. package/packs/gallery/heatmap/example.html +3 -0
  243. package/packs/gallery/heatmap/kit.js +351 -0
  244. package/packs/gallery/heatmap/preview.svg +1 -0
  245. package/packs/gallery/heatmap/style.css +201 -0
  246. package/packs/gallery/heatmap/template.html +3 -0
  247. package/packs/gallery/heatmap/writing.md +21 -0
  248. package/packs/gallery/kanban/component.json +18 -0
  249. package/packs/gallery/kanban/example.html +12 -0
  250. package/packs/gallery/kanban/kit.js +540 -0
  251. package/packs/gallery/kanban/preview.svg +1 -0
  252. package/packs/gallery/kanban/style.css +212 -0
  253. package/packs/gallery/kanban/template.html +3 -0
  254. package/packs/gallery/kanban/writing.md +20 -0
  255. package/packs/gallery/metric/component.json +22 -0
  256. package/packs/gallery/metric/example.html +15 -0
  257. package/packs/gallery/metric/kit.js +248 -0
  258. package/packs/gallery/metric/preview.svg +1 -0
  259. package/packs/gallery/metric/style.css +92 -0
  260. package/packs/gallery/metric/template.html +3 -0
  261. package/packs/gallery/metric/writing.md +23 -0
  262. package/packs/gallery/note/component.json +14 -0
  263. package/packs/gallery/note/example.html +3 -0
  264. package/packs/gallery/note/kit.js +102 -0
  265. package/packs/gallery/note/preview.svg +1 -0
  266. package/packs/gallery/note/style.css +76 -0
  267. package/packs/gallery/note/template.html +3 -0
  268. package/packs/gallery/note/writing.md +18 -0
  269. package/packs/gallery/pack.json +8 -0
  270. package/packs/gallery/recent/component.json +13 -0
  271. package/packs/gallery/recent/example.html +1 -0
  272. package/packs/gallery/recent/kit.js +238 -0
  273. package/packs/gallery/recent/preview.svg +1 -0
  274. package/packs/gallery/recent/style.css +158 -0
  275. package/packs/gallery/recent/template.html +1 -0
  276. package/packs/gallery/recent/writing.md +15 -0
  277. package/packs/gallery/static/component.json +13 -0
  278. package/packs/gallery/static/example.html +11 -0
  279. package/packs/gallery/static/preview.svg +1 -0
  280. package/packs/gallery/static/style.css +138 -0
  281. package/packs/gallery/static/template.html +1 -0
  282. package/packs/gallery/static/writing.md +18 -0
  283. package/packs/gallery/stats/component.json +13 -0
  284. package/packs/gallery/stats/example.html +1 -0
  285. package/packs/gallery/stats/kit.js +166 -0
  286. package/packs/gallery/stats/preview.svg +1 -0
  287. package/packs/gallery/stats/style.css +151 -0
  288. package/packs/gallery/stats/template.html +1 -0
  289. package/packs/gallery/stats/writing.md +11 -0
  290. package/packs/gallery/today/component.json +12 -0
  291. package/packs/gallery/today/example.html +1 -0
  292. package/packs/gallery/today/kit.js +185 -0
  293. package/packs/gallery/today/preview.svg +1 -0
  294. package/packs/gallery/today/style.css +120 -0
  295. package/packs/gallery/today/template.html +1 -0
  296. package/packs/gallery/today/writing.md +14 -0
  297. package/packs/gallery/todos/component.json +19 -0
  298. package/packs/gallery/todos/example.html +3 -0
  299. package/packs/gallery/todos/kit.js +342 -0
  300. package/packs/gallery/todos/preview.svg +1 -0
  301. package/packs/gallery/todos/style.css +267 -0
  302. package/packs/gallery/todos/template.html +3 -0
  303. package/packs/gallery/todos/writing.md +30 -0
  304. package/packs/gallery/weather/component.json +19 -0
  305. package/packs/gallery/weather/example.html +3 -0
  306. package/packs/gallery/weather/kit.js +312 -0
  307. package/packs/gallery/weather/preview.svg +1 -0
  308. package/packs/gallery/weather/style.css +258 -0
  309. package/packs/gallery/weather/template.html +3 -0
  310. package/packs/gallery/weather/writing.md +26 -0
  311. package/packs/gallery-soft/pack.json +8 -0
  312. package/release.json +12 -10
  313. package/sdk/doc-kit.d.ts +396 -5
  314. package/sdk/doc-state.d.ts +2 -0
  315. package/skill/SKILL.md +56 -23
  316. package/skill/references/block-script.md +140 -0
  317. package/skill/references/building-blocks.md +257 -0
  318. package/skill/references/extensions.md +59 -0
  319. package/skill/references/pages-and-data.md +221 -0
  320. package/skill/references/writing-html.md +6 -4
  321. package/skill/sdk/doc-kit.d.ts +396 -5
  322. package/skill/sdk/doc-state.d.ts +2 -0
  323. package/web/assets/index-BK2e_07w.js +13 -0
  324. package/web/assets/index-CWFI83mF.css +1 -0
  325. package/web/index.html +2 -2
  326. package/dist/chunk-3HIRPVLU.mjs +0 -2
  327. package/dist/chunk-5MHMDLZD.mjs +0 -4
  328. package/dist/chunk-64K7IPLO.mjs +0 -255
  329. package/dist/chunk-6QXGZM36.mjs +0 -8
  330. package/dist/chunk-6RGJOMJP.mjs +0 -16
  331. package/dist/chunk-AJ73XJF3.mjs +0 -2
  332. package/dist/chunk-AUZGSZJH.mjs +0 -13
  333. package/dist/chunk-BFE45AKL.mjs +0 -2
  334. package/dist/chunk-BXO2ZY5P.mjs +0 -2
  335. package/dist/chunk-CEH4MRCC.mjs +0 -2
  336. package/dist/chunk-CY2FNNAB.mjs +0 -16
  337. package/dist/chunk-DFUDV4LX.mjs +0 -2
  338. package/dist/chunk-DKXG4XDG.mjs +0 -2
  339. package/dist/chunk-EZQ34M3F.mjs +0 -124
  340. package/dist/chunk-EZY3WC6Z.mjs +0 -15
  341. package/dist/chunk-FUKQNOQS.mjs +0 -9
  342. package/dist/chunk-JPOSLGOC.mjs +0 -6
  343. package/dist/chunk-L3QKH4OK.mjs +0 -26
  344. package/dist/chunk-LAE5NQ4B.mjs +0 -2
  345. package/dist/chunk-LEF6DRCK.mjs +0 -59
  346. package/dist/chunk-NO5NJCHO.mjs +0 -2
  347. package/dist/chunk-NQPVAU63.mjs +0 -2
  348. package/dist/chunk-NSKUTQ5J.mjs +0 -5
  349. package/dist/chunk-O35FIXRB.mjs +0 -2
  350. package/dist/chunk-P2CAZPDP.mjs +0 -3
  351. package/dist/chunk-PL6GHLGD.mjs +0 -15
  352. package/dist/chunk-QJIR2BLP.mjs +0 -6
  353. package/dist/chunk-QUPLVPB2.mjs +0 -7
  354. package/dist/chunk-R7BNENLY.mjs +0 -4
  355. package/dist/chunk-S6O4UHLC.mjs +0 -2
  356. package/dist/chunk-SGJWQKEI.mjs +0 -2
  357. package/dist/chunk-TX6JH7TR.mjs +0 -2
  358. package/dist/chunk-TYK6VNAB.mjs +0 -10
  359. package/dist/chunk-UALRNVSE.mjs +0 -4
  360. package/dist/chunk-V3HXVQEM.mjs +0 -2
  361. package/dist/chunk-V5VXUJKG.mjs +0 -2
  362. package/dist/chunk-VBXMADMP.mjs +0 -2
  363. package/dist/chunk-VNV55RUA.mjs +0 -2
  364. package/dist/chunk-VPD2PS35.mjs +0 -2
  365. package/dist/chunk-W4VKN3GF.mjs +0 -7
  366. package/dist/chunk-W5BXXPDJ.mjs +0 -3
  367. package/dist/chunk-XUNHRCOG.mjs +0 -6
  368. package/skill/references/building-components.md +0 -367
  369. package/web/assets/index-Bh74DfsV.css +0 -1
  370. package/web/assets/index-UW0RyFFt.js +0 -13
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "gallery/weather",
3
+ "apiVersion": 1,
4
+ "version": "1.2.0",
5
+ "name": { "zh": "天气", "en": "Weather" },
6
+ "description": { "zh": "一个城市现在的天气和接下来几天的预报,数据来自 Open-Meteo,要先允许联网", "en": "A city's weather now and for the days ahead, from Open-Meteo, once the reader lets it online" },
7
+ "words": "tianqi qiwen yubao weather forecast temperature 天气 气温 预报 温度 下雨",
8
+ "parts": {
9
+ "settings": { "at": ":scope > script", "edit": "data" }
10
+ },
11
+ "runtime": { "generated": [":scope > .gallery-weather-face"] },
12
+ "places": ["page"],
13
+ "card": { "sizes": [[4, 2], [2, 2], [4, 4]], "resize": true },
14
+ "settings": [
15
+ { "key": "city", "label": { "zh": "城市", "en": "City" }, "type": "text", "default": "", "required": true },
16
+ { "key": "unit", "label": { "zh": "单位", "en": "Unit" }, "type": "select", "options": ["℃", "℉"], "default": "℃" }
17
+ ],
18
+ "needs": ["net:api.open-meteo.com", "net:geocoding-api.open-meteo.com"]
19
+ }
@@ -0,0 +1,3 @@
1
+ <div data-doc-component="gallery/weather">
2
+ <script type="application/json">{"city":"上海","unit":"℃"}</script>
3
+ </div>
@@ -0,0 +1,312 @@
1
+ // gallery/weather (天气): a city's weather now and for the days ahead, from Open-Meteo (open-meteo.com,
2
+ // no key), for a custom page. Its settings, `city` (a name as the reader writes it: 上海, Berlin) and
3
+ // `unit` (℃ or ℉), are the card's form (the grip at its top right › 设置…), kept in the data part `settings`, which the board
4
+ // writes and this block only reads. The city is looked up by name (geocoding-api.open-meteo.com: its
5
+ // first match, its coordinates), its weather fetched for them (api.open-meteo.com: now, and each day's
6
+ // highest, lowest and sky for five days), both through context.fetch, the server fetching for it: its
7
+ // needs name the two hosts, and nothing is fetched until the reader allows them (the kit then shows
8
+ // 允许访问 on the card and starts the block again once the reader says yes). Fetched again as its settings
9
+ // change (`source`) and every 30 minutes; the server keeps an answer a minute and gives what it kept
10
+ // when the network is not there, which the card says by its time (更新于 …). Its face: the city and when,
11
+ // the sky's picture, the temperature large, the sky's words and today's range, how it feels, the air's
12
+ // humidity and the wind, and the days ahead, as much of it as the card's size holds (style.css,
13
+ // container queries); what it says while there is nothing to show (no city yet, one not found, waiting
14
+ // for the reader's consent, the network not there, this computer's network setup sending Open-Meteo's
15
+ // names to an address of its own or of a private network, which the server refuses: code "private")
16
+ // stands in the face's note. In the page's language. Its head is the cards' (the kit's card tokens): the
17
+ // chip, a place's pin, before the city; each day ahead its lowest and highest at either end of a bar, the
18
+ // bar drawn from the lowest to the highest of all the days shown, so that the days read against each other.
19
+
20
+ const WORDS = {
21
+ zh: {
22
+ title: "天气",
23
+ pick: "写上一个城市",
24
+ where: "右上角手柄 › 设置…",
25
+ loading: "正在取天气…",
26
+ unknown: (/** @type {string} */ city) => `找不到「${city}」:换一个写法试试`,
27
+ consent: "允许联网之后显示天气",
28
+ offline: "现在连不上网,取不到天气",
29
+ private: "网络设置把天气服务指向了本地地址,取不到天气",
30
+ failed: "没能取到天气",
31
+ at: (/** @type {string} */ time) => `更新于 ${time}`,
32
+ feels: (/** @type {string} */ value) => `体感 ${value}`,
33
+ humidity: (/** @type {number} */ value) => `湿度 ${value}%`,
34
+ wind: (/** @type {number} */ value) => `风 ${value} km/h`,
35
+ today: "今天",
36
+ weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
37
+ sky: {
38
+ clear: "晴", mostly: "大部晴朗", partly: "多云", overcast: "阴", fog: "雾", drizzle: "毛毛雨", "freezing-drizzle": "冻毛毛雨",
39
+ rain: "雨", "light-rain": "小雨", "heavy-rain": "大雨", "freezing-rain": "冻雨", snow: "雪", "light-snow": "小雪", "heavy-snow": "大雪",
40
+ grains: "雪粒", showers: "阵雨", "snow-showers": "阵雪", thunder: "雷阵雨", hail: "雷阵雨伴冰雹", unknown: "天气未知",
41
+ },
42
+ },
43
+ en: {
44
+ title: "Weather",
45
+ pick: "Name a city",
46
+ where: "The grip at the top right › Settings…",
47
+ loading: "Fetching the weather…",
48
+ unknown: (/** @type {string} */ city) => `No place called “${city}”: try another spelling`,
49
+ consent: "Shown once it may go online",
50
+ offline: "Offline: no weather now",
51
+ private: "The network setup sends the weather service to a local address",
52
+ failed: "The weather could not be fetched",
53
+ at: (/** @type {string} */ time) => `Updated ${time}`,
54
+ feels: (/** @type {string} */ value) => `Feels ${value}`,
55
+ humidity: (/** @type {number} */ value) => `Humidity ${value}%`,
56
+ wind: (/** @type {number} */ value) => `Wind ${value} km/h`,
57
+ today: "Today",
58
+ weekdays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
59
+ sky: {
60
+ clear: "Clear", mostly: "Mostly clear", partly: "Partly cloudy", overcast: "Overcast", fog: "Fog", drizzle: "Drizzle", "freezing-drizzle": "Freezing drizzle",
61
+ rain: "Rain", "light-rain": "Light rain", "heavy-rain": "Heavy rain", "freezing-rain": "Freezing rain", snow: "Snow", "light-snow": "Light snow", "heavy-snow": "Heavy snow",
62
+ grains: "Snow grains", showers: "Showers", "snow-showers": "Snow showers", thunder: "Thunderstorm", hail: "Thunderstorm with hail", unknown: "Unknown",
63
+ },
64
+ },
65
+ };
66
+
67
+ /** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
68
+ function languageOf(/** @type {HTMLElement} */ root) {
69
+ const ui = root.getAttribute("data-ge-language");
70
+ return (ui !== null ? ui === "en" : /^en(?:-|$)/i.test(root.lang)) ? "en" : "zh";
71
+ }
72
+
73
+ /**
74
+ * The sky a WMO weather code says (what Open-Meteo answers with), as { words (its key in WORDS' sky),
75
+ * picture (which of PICTURES draws it) }; `day` false draws a clear or mostly clear sky with the moon.
76
+ * @param {unknown} code
77
+ * @param {boolean} day
78
+ */
79
+ function skyOf(code, day = true) {
80
+ const sun = day ? "sun" : "moon";
81
+ /** @type {Record<number, [string, string]>} */
82
+ const skies = {
83
+ 0: ["clear", sun], 1: ["mostly", sun], 2: ["partly", day ? "partly" : "partly-night"], 3: ["overcast", "cloud"],
84
+ 45: ["fog", "fog"], 48: ["fog", "fog"], 51: ["drizzle", "rain"], 53: ["drizzle", "rain"], 55: ["drizzle", "rain"], 56: ["freezing-drizzle", "rain"], 57: ["freezing-drizzle", "rain"],
85
+ 61: ["light-rain", "rain"], 63: ["rain", "rain"], 65: ["heavy-rain", "rain"], 66: ["freezing-rain", "rain"], 67: ["freezing-rain", "rain"],
86
+ 71: ["light-snow", "snow"], 73: ["snow", "snow"], 75: ["heavy-snow", "snow"], 77: ["grains", "snow"],
87
+ 80: ["showers", "rain"], 81: ["showers", "rain"], 82: ["showers", "rain"], 85: ["snow-showers", "snow"], 86: ["snow-showers", "snow"],
88
+ 95: ["thunder", "thunder"], 96: ["hail", "thunder"], 99: ["hail", "thunder"],
89
+ };
90
+ const [words, picture] = typeof code === "number" && skies[code] ? skies[code] : ["unknown", "cloud"];
91
+ return { words, picture };
92
+ }
93
+
94
+ /**
95
+ * The address a city's name is looked up at (Open-Meteo's geocoding: its first match, in `language`).
96
+ * @param {string} city
97
+ * @param {"zh" | "en"} language
98
+ */
99
+ const searchAddress = (city, language) => `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1&language=${language}&format=json`;
100
+
101
+ /**
102
+ * The address of the weather at a place (its coordinates, to two decimals: one address for one place,
103
+ * which the server keeps), in `unit` (℃ or ℉): now, and five days, by the place's own clock.
104
+ * @param {{ latitude: number, longitude: number }} place
105
+ * @param {string} unit
106
+ */
107
+ const forecastAddress = (place, unit) => `https://api.open-meteo.com/v1/forecast?latitude=${place.latitude.toFixed(2)}&longitude=${place.longitude.toFixed(2)}`
108
+ + "&current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code,is_day"
109
+ + `&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days=5${unit === "℉" ? "&temperature_unit=fahrenheit" : ""}`;
110
+
111
+ /**
112
+ * The first place a lookup's answer gives ({ name, latitude, longitude, region }), or null for none.
113
+ * @param {any} answer
114
+ */
115
+ function placeOf(answer) {
116
+ const found = Array.isArray(answer?.results) ? answer.results[0] : null;
117
+ if (!found || typeof found.latitude !== "number" || typeof found.longitude !== "number") return null;
118
+ return { name: String(found.name ?? ""), latitude: found.latitude, longitude: found.longitude, region: String(found.admin1 ?? found.country ?? "") };
119
+ }
120
+
121
+ /** A temperature as the card writes it: whole degrees, a degree sign, a minus that is one. @param {unknown} value */
122
+ const degrees = (value) => (typeof value === "number" && Number.isFinite(value) ? `${Math.round(value)}°`.replace(/^-/, "−") : "–");
123
+
124
+ /**
125
+ * What the card says, in `language`, of `state`: { kind: "pick" } (no city), { kind: "loading", city },
126
+ * { kind: "unknown", city } (no such place), { kind: "consent", city }, { kind: "offline", city },
127
+ * { kind: "private", city } (the server refused the address this computer's resolver gave: one of this
128
+ * computer or of a private network, its network setup's doing), { kind: "failed", city }, or
129
+ * { kind: "weather", place, forecast (Open-Meteo's answer), at (when it was fetched, ISO) }. Its head
130
+ * (the city, and when), and either a note or the weather: the sky's picture and words, the temperature,
131
+ * today's range, the more line's pieces, and the days ahead.
132
+ * @param {any} state
133
+ * @param {"zh" | "en"} language
134
+ */
135
+ function faceOf(state, language) {
136
+ const words = WORDS[language];
137
+ const head = { city: state.city || state.place?.name || words.title, at: "" };
138
+ if (state.kind !== "weather") {
139
+ /** @type {Record<string, string>} */
140
+ const notes = { pick: words.pick, loading: words.loading, consent: words.consent, offline: words.offline, private: words.private, failed: words.failed };
141
+ const note = state.kind === "unknown" ? words.unknown(state.city) : notes[state.kind] ?? words.failed;
142
+ return { head, note, hint: state.kind === "pick" || state.kind === "unknown" ? words.where : "", now: null, days: [] };
143
+ }
144
+ const { current = {}, daily = {} } = state.forecast ?? {};
145
+ const sky = skyOf(current.weather_code, current.is_day !== 0);
146
+ const fetched = typeof state.at === "string" ? new Date(state.at) : null;
147
+ const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
148
+ head.at = fetched && !Number.isNaN(fetched.getTime()) ? words.at(`${two(fetched.getHours())}:${two(fetched.getMinutes())}`) : "";
149
+ const range = (/** @type {number} */ index) => `${degrees(daily.temperature_2m_max?.[index])} / ${degrees(daily.temperature_2m_min?.[index])}`;
150
+ const more = [
151
+ typeof current.apparent_temperature === "number" ? words.feels(degrees(current.apparent_temperature)) : null,
152
+ typeof current.relative_humidity_2m === "number" ? words.humidity(Math.round(current.relative_humidity_2m)) : null,
153
+ typeof current.wind_speed_10m === "number" ? words.wind(Math.round(current.wind_speed_10m)) : null,
154
+ ].filter((piece) => piece !== null);
155
+ const dates = Array.isArray(daily.time) ? daily.time : [];
156
+ const days = dates.slice(0, 5).map((/** @type {string} */ date, /** @type {number} */ index) => {
157
+ const [year, month, day] = String(date).split("-").map(Number);
158
+ const weekday = index === 0 ? words.today : words.weekdays[new Date(Date.UTC(year, month - 1, day)).getUTCDay()] ?? "";
159
+ const [high, low] = [daily.temperature_2m_max?.[index], daily.temperature_2m_min?.[index]];
160
+ return { weekday, picture: skyOf(daily.weather_code?.[index]).picture, sky: words.sky[skyOf(daily.weather_code?.[index]).words], range: range(index), high: degrees(high), low: degrees(low), span: typeof high === "number" && typeof low === "number" && Number.isFinite(high) && Number.isFinite(low) ? [low, high] : null };
161
+ });
162
+ return { head, note: "", hint: "", now: { picture: sky.picture, sky: words.sky[sky.words], temperature: degrees(current.temperature_2m), range: dates.length ? range(0) : "", more }, days };
163
+ }
164
+
165
+ // The sky's pictures, a 24 x 24 drawing each in the text's color (style.css colors the sun and the rain).
166
+ const SUN = '<circle cx="12" cy="12" r="4.2" class="gallery-weather-sun"/><g class="gallery-weather-sun" stroke-linecap="round" stroke-width="1.8" fill="none"><path d="M12 2.5v2.2M12 19.3v2.2M2.5 12h2.2M19.3 12h2.2M5.3 5.3l1.6 1.6M17.1 17.1l1.6 1.6M5.3 18.7l1.6-1.6M17.1 6.9l1.6-1.6"/></g>';
167
+ const CLOUD = '<path class="gallery-weather-cloud" d="M7.5 19h9.3a4.2 4.2 0 0 0 .5-8.4 5.6 5.6 0 0 0-10.7 1.6A3.4 3.4 0 0 0 7.5 19z"/>';
168
+ const PICTURES = {
169
+ sun: SUN,
170
+ moon: '<path class="gallery-weather-moon" d="M15.6 3.4a8.6 8.6 0 1 0 5 15.2A7.2 7.2 0 0 1 15.6 3.4z"/>',
171
+ partly: '<g transform="translate(-3 -4) scale(.8)">' + SUN + "</g>" + CLOUD,
172
+ "partly-night": '<path class="gallery-weather-moon" d="M9.6 2.6a6.2 6.2 0 1 0 6.2 8.6A5.2 5.2 0 0 1 9.6 2.6z"/>' + CLOUD,
173
+ cloud: CLOUD,
174
+ fog: '<g class="gallery-weather-cloud-line" stroke-linecap="round" stroke-width="1.9" fill="none"><path d="M4 8.5h16M6 12.5h12M4 16.5h16"/></g>',
175
+ rain: '<g transform="translate(0 -3)">' + CLOUD + '</g><g class="gallery-weather-rain" stroke-linecap="round" stroke-width="1.8" fill="none"><path d="M9 18.5l-1 3M13 18.5l-1 3M17 18.5l-1 3"/></g>',
176
+ snow: '<g transform="translate(0 -3)">' + CLOUD + '</g><g class="gallery-weather-snow"><circle cx="8.5" cy="20" r="1.1"/><circle cx="12.5" cy="21.5" r="1.1"/><circle cx="16.5" cy="20" r="1.1"/></g>',
177
+ thunder: '<g transform="translate(0 -3)">' + CLOUD + '</g><path class="gallery-weather-bolt" d="M12.5 14.5l-2.6 4.2h2.4l-1.2 4 3.8-5.4h-2.4l1.4-2.8z"/>',
178
+ };
179
+
180
+ /**
181
+ * An element of the face, of `kind` (its class).
182
+ * @template {keyof HTMLElementTagNameMap} K
183
+ * @param {K} tag
184
+ * @param {string} kind
185
+ */
186
+ function made(tag, kind) {
187
+ const node = document.createElement(tag);
188
+ node.className = `gallery-weather-${kind}`;
189
+ return node;
190
+ }
191
+
192
+ /** A sky's picture, as an svg element. @param {string} picture */
193
+ function pictureOf(picture) {
194
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
195
+ svg.setAttribute("viewBox", "0 0 24 24");
196
+ svg.setAttribute("aria-hidden", "true");
197
+ svg.setAttribute("class", "gallery-weather-picture");
198
+ svg.innerHTML = PICTURES[/** @type {keyof typeof PICTURES} */ (picture)] ?? CLOUD;
199
+ return svg;
200
+ }
201
+
202
+ DocKit.component({
203
+ id: "gallery/weather",
204
+ on: ["source"],
205
+ mount(host, context) {
206
+ const language = languageOf(document.documentElement);
207
+ const face = made("div", "face");
208
+ host.append(face);
209
+ const draw = (/** @type {any} */ state) => {
210
+ const said = faceOf(state, language);
211
+ const head = made("p", "head");
212
+ const chip = made("span", "chip");
213
+ chip.setAttribute("aria-hidden", "true");
214
+ chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11a3 3 0 1 0 6 0 3 3 0 0 0-6 0"/><path d="M17.7 16.7l-4.3 4.2a2 2 0 0 1-2.8 0l-4.3-4.2a8 8 0 1 1 11.4 0z"/></svg>';
215
+ const city = made("span", "city");
216
+ city.textContent = said.head.city;
217
+ const at = made("span", "at");
218
+ at.textContent = said.head.at;
219
+ head.append(chip, city, at);
220
+ /** @type {HTMLElement[]} */
221
+ const parts = [head];
222
+ if (said.now) {
223
+ const now = made("div", "now");
224
+ const temperature = made("span", "temperature");
225
+ temperature.textContent = said.now.temperature;
226
+ const words = made("span", "words");
227
+ const sky = made("span", "sky");
228
+ sky.textContent = said.now.sky;
229
+ const range = made("span", "range");
230
+ range.textContent = said.now.range;
231
+ words.append(sky, range);
232
+ now.append(pictureOf(said.now.picture), temperature, words);
233
+ const more = made("p", "more");
234
+ more.replaceChildren(...said.now.more.map((piece) => {
235
+ const span = made("span", "piece");
236
+ span.textContent = piece;
237
+ return span;
238
+ }));
239
+ const days = made("ol", "days");
240
+ const spans = said.days.flatMap((day) => day.span ?? []);
241
+ const [lowest, highest] = [Math.min(...spans), Math.max(...spans)];
242
+ days.replaceChildren(...said.days.map((day) => {
243
+ const item = made("li", "day");
244
+ const weekday = made("span", "weekday");
245
+ weekday.textContent = day.weekday;
246
+ const range = made("span", "day-range");
247
+ const low = made("span", "low");
248
+ low.textContent = day.low;
249
+ const bar = made("span", "bar");
250
+ bar.setAttribute("aria-hidden", "true");
251
+ if (day.span && highest > lowest) {
252
+ const fill = made("span", "fill");
253
+ fill.style.setProperty("--from", String((day.span[0] - lowest) / (highest - lowest)));
254
+ fill.style.setProperty("--to", String((day.span[1] - lowest) / (highest - lowest)));
255
+ bar.append(fill);
256
+ }
257
+ const high = made("span", "high");
258
+ high.textContent = day.high;
259
+ range.append(low, bar, high);
260
+ const picture = pictureOf(day.picture);
261
+ picture.setAttribute("aria-label", day.sky);
262
+ picture.removeAttribute("aria-hidden");
263
+ picture.setAttribute("role", "img");
264
+ item.append(weekday, picture, range);
265
+ return item;
266
+ }));
267
+ parts.push(now, more, days);
268
+ } else {
269
+ const note = made("p", "note");
270
+ note.textContent = said.note;
271
+ parts.push(note);
272
+ if (said.hint) {
273
+ const hint = made("p", "hint");
274
+ hint.textContent = said.hint;
275
+ parts.push(hint);
276
+ }
277
+ }
278
+ face.replaceChildren(...parts);
279
+ face.dataset.kind = state.kind;
280
+ };
281
+ let asked = 0; // the latest load: an earlier one that answers later draws nothing
282
+ const load = async () => {
283
+ const mine = ++asked;
284
+ const settings = context.readJSON("settings");
285
+ if (settings === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
286
+ const city = typeof settings?.city === "string" ? settings.city.trim() : "";
287
+ const unit = settings?.unit === "℉" ? "℉" : "℃";
288
+ if (!city) return draw({ kind: "pick" });
289
+ draw({ kind: "loading", city });
290
+ try {
291
+ const found = placeOf((await context.fetch(searchAddress(city, language))).data);
292
+ if (mine !== asked) return;
293
+ if (!found) return draw({ kind: "unknown", city });
294
+ const answer = await context.fetch(forecastAddress(found, unit));
295
+ if (mine !== asked) return;
296
+ draw({ kind: "weather", city, place: found, forecast: answer.data, at: answer.at });
297
+ } catch (error) {
298
+ if (mine !== asked) return;
299
+ const code = /** @type {DocFetchError} */ (error).code;
300
+ draw({ kind: code === "consent" || code === "offline" || code === "private" ? code : "failed", city });
301
+ if (!code) context.fail(error); // no refusal of the server's: the block's own mistake, which the kit says
302
+ }
303
+ };
304
+ load();
305
+ context.interval(load, 30 * 60_000);
306
+ return {
307
+ update(reason) {
308
+ if (reason === "source") load();
309
+ },
310
+ };
311
+ },
312
+ });
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="12" fill="#ffffff" stroke="#e7e6e3"/><rect x="30" y="24" width="14" height="14" rx="4.5" fill="#ebf2fa"/><g transform="translate(32 26) scale(.4167)" fill="none" stroke="#2e7cd1" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11a3 3 0 1 0 6 0 3 3 0 0 0-6 0"/><path d="M17.7 16.7l-4.3 4.2a2 2 0 0 1-2.8 0l-4.3-4.2a8 8 0 1 1 11.4 0z"/></g><rect x="50" y="28" width="22" height="6" rx="3" fill="#3b3a37"/><rect x="174" y="29" width="36" height="5" rx="2.5" fill="#c4c2bd"/><circle cx="40" cy="64" r="6.5" fill="#d98a1c"/><path d="M40 52.5v2.5M28.5 64H31M31.9 55.9l1.8 1.8M48.1 55.9l-1.8 1.8" stroke="#d98a1c" stroke-width="2" stroke-linecap="round"/><path d="M40 80h14a6.5 6.5 0 0 0 .8-12.9 8.6 8.6 0 0 0-16.5 2.5A5.2 5.2 0 0 0 40 80z" fill="#a3abb8"/><text x="64" y="80" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="30" font-weight="600" letter-spacing="-.5" fill="#3b3a37">18°</text><rect x="116" y="62" width="26" height="6" rx="3" fill="#3b3a37"/><rect x="116" y="73" width="40" height="5" rx="2.5" fill="#c4c2bd"/><rect x="30" y="93" width="34" height="5" rx="2.5" fill="#c4c2bd"/><circle cx="70" cy="95.5" r="1.3" fill="#c4c2bd"/><rect x="76" y="93" width="32" height="5" rx="2.5" fill="#c4c2bd"/><circle cx="114" cy="95.5" r="1.3" fill="#c4c2bd"/><rect x="120" y="93" width="40" height="5" rx="2.5" fill="#c4c2bd"/></svg>
@@ -0,0 +1,258 @@
1
+ /* Every selector starts at the instance and goes down from it (a step at a time, >, but for the
2
+ pictures' parts), so that nothing here reaches the rest of the page. The face fills its card on a
3
+ custom page (the board draws the frame), in the kit's card tokens: its head (the chip, the city, when it
4
+ was fetched, muted, its end clear of the card's grip); the sky's picture, the temperature large, the
5
+ sky's words and today's range; how it feels, the humidity and the wind, muted, a dot between them; and
6
+ the days ahead, each its lowest and highest at the ends of a bar in the accent drawn against all the
7
+ days shown. As much as the card holds: the instance is a container of both its sides (a 2 x 2 card
8
+ shows the temperature and, under it, the sky's words and its range on one muted line, a 4 x 2 one its
9
+ range and the more line too, a 4 x 4 one the days ahead), the weather now in the middle of the room under
10
+ the head, but where the days ahead show: then it stands under the head, the days right after the more
11
+ line, their rows taller where the card has the room (to 44px), the room left over at the bottom. The
12
+ temperature is the cards' hero size. What it says while there is nothing to show is the cards' empty face: a quiet
13
+ picture (a place's pin) in a soft disc over a muted line, where to set the city under it, in the middle. The sun and
14
+ the rain have colors of their own, light and dark. */
15
+ [data-doc-component="gallery/weather"] {
16
+ container-type: size;
17
+ --gallery-weather-sun: light-dark(#e09a2b, #f2b950);
18
+ --gallery-weather-rain: light-dark(#3a7bd5, #7aa9ec);
19
+ --gallery-weather-cloud: light-dark(#a3abb8, #7c8594);
20
+ > .gallery-weather-face {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: var(--gallery-face-gap);
24
+ box-sizing: border-box;
25
+ height: 100%;
26
+ min-height: 0;
27
+ overflow: hidden;
28
+ color: var(--gallery-text);
29
+ }
30
+ > .gallery-weather-face > p { margin: 0; }
31
+ > .gallery-weather-face > .gallery-weather-head {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 8px;
35
+ flex: none;
36
+ min-width: 0;
37
+ padding-inline-end: var(--gallery-grip-room, 0px);
38
+ font-size: var(--gallery-title-size);
39
+ line-height: var(--gallery-title-line);
40
+ }
41
+ > .gallery-weather-face > .gallery-weather-head > .gallery-weather-chip {
42
+ flex: none;
43
+ display: inline-flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ width: var(--gallery-chip-size);
47
+ height: var(--gallery-chip-size);
48
+ border-radius: var(--gallery-chip-radius);
49
+ background: var(--gallery-accent-soft);
50
+ color: var(--gallery-accent);
51
+ }
52
+ > .gallery-weather-face > .gallery-weather-head > .gallery-weather-chip > svg { width: var(--gallery-chip-icon); height: var(--gallery-chip-icon); }
53
+ > .gallery-weather-face > .gallery-weather-head > .gallery-weather-city {
54
+ flex: 1 1 auto;
55
+ min-width: 0;
56
+ overflow: hidden;
57
+ white-space: nowrap;
58
+ text-overflow: ellipsis;
59
+ font-weight: 600;
60
+ color: var(--gallery-text-strong);
61
+ }
62
+ > .gallery-weather-face > .gallery-weather-head > .gallery-weather-at {
63
+ flex: none;
64
+ margin-inline-end: calc(-1 * var(--gallery-grip-room, 0px)); opacity: calc(1 - var(--gallery-grip-shown, 0)); transition: opacity .12s ease-out;
65
+ font-size: var(--gallery-meta-size);
66
+ color: var(--doc-muted);
67
+ white-space: nowrap;
68
+ }
69
+ > .gallery-weather-face > .gallery-weather-now {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 12px;
73
+ min-width: 0;
74
+ margin-top: auto;
75
+ }
76
+ /* The weather now and its more line, together in the middle of the room under the head. */
77
+ > .gallery-weather-face > .gallery-weather-more { margin-bottom: auto; }
78
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-picture {
79
+ flex: none;
80
+ width: clamp(40px, 13cqi, 52px);
81
+ height: clamp(40px, 13cqi, 52px);
82
+ }
83
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-temperature {
84
+ flex: none;
85
+ font-size: var(--gallery-hero-size);
86
+ line-height: 1;
87
+ font-weight: var(--gallery-numeral-weight);
88
+ letter-spacing: var(--gallery-numeral-tracking);
89
+ color: var(--gallery-text-strong);
90
+ font-variant-numeric: tabular-nums;
91
+ }
92
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 2px;
96
+ min-width: 0;
97
+ font-size: 14px;
98
+ line-height: 1.35;
99
+ }
100
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > * {
101
+ overflow: hidden;
102
+ white-space: nowrap;
103
+ text-overflow: ellipsis;
104
+ }
105
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > .gallery-weather-sky { font-weight: 500; }
106
+ > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > .gallery-weather-range {
107
+ font-size: 13px;
108
+ color: var(--doc-muted);
109
+ font-variant-numeric: tabular-nums;
110
+ }
111
+ /* How it feels, the humidity, the wind: muted, a dot between them, a piece never broken. */
112
+ > .gallery-weather-face > .gallery-weather-more {
113
+ display: flex;
114
+ flex-wrap: wrap;
115
+ align-items: center;
116
+ gap: 2px 0;
117
+ font-size: var(--gallery-meta-size);
118
+ line-height: 1.5;
119
+ color: var(--doc-muted);
120
+ font-variant-numeric: tabular-nums;
121
+ }
122
+ > .gallery-weather-face > .gallery-weather-more > .gallery-weather-piece { white-space: nowrap; }
123
+ > .gallery-weather-face > .gallery-weather-more > .gallery-weather-piece + .gallery-weather-piece::before {
124
+ content: "";
125
+ display: inline-block;
126
+ width: 3px;
127
+ height: 3px;
128
+ margin: 0 8px;
129
+ border-radius: 50%;
130
+ background: currentColor;
131
+ opacity: .6;
132
+ vertical-align: middle;
133
+ }
134
+ > .gallery-weather-face > .gallery-weather-days {
135
+ display: none;
136
+ margin: 0;
137
+ padding: 8px 0 0;
138
+ list-style: none;
139
+ border-top: 1px solid var(--doc-grid);
140
+ }
141
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day {
142
+ display: grid;
143
+ grid-template-columns: minmax(2.6em, 1fr) 22px minmax(0, 3fr);
144
+ align-items: center;
145
+ gap: 10px;
146
+ padding: 3px 0;
147
+ font-size: 13px;
148
+ line-height: 1.4;
149
+ }
150
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day > .gallery-weather-picture { width: 20px; height: 20px; }
151
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day > .gallery-weather-day-range {
152
+ display: grid;
153
+ grid-template-columns: 2.6em minmax(0, 1fr) 2.6em;
154
+ align-items: center;
155
+ gap: 8px;
156
+ font-variant-numeric: tabular-nums;
157
+ }
158
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day > .gallery-weather-day-range > .gallery-weather-low { color: var(--doc-muted); text-align: end; }
159
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day > .gallery-weather-day-range > .gallery-weather-bar {
160
+ position: relative;
161
+ height: 4px;
162
+ border-radius: 2px;
163
+ background: var(--gallery-track);
164
+ }
165
+ > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day > .gallery-weather-day-range > .gallery-weather-bar > .gallery-weather-fill {
166
+ position: absolute;
167
+ top: 0;
168
+ bottom: 0;
169
+ left: calc(var(--from, 0) * 100%);
170
+ right: calc((1 - var(--to, 1)) * 100%);
171
+ min-width: 4px;
172
+ border-radius: 2px;
173
+ background: var(--gallery-accent);
174
+ }
175
+ /* What it says while there is nothing to show: the cards' empty face (as todos' and recent's), a quiet
176
+ picture over a muted line, where to set the city under it, the two in the middle. */
177
+ > .gallery-weather-face > .gallery-weather-note {
178
+ display: flex;
179
+ flex-direction: column;
180
+ align-items: center;
181
+ gap: 8px;
182
+ margin-top: auto;
183
+ padding: 0 8px;
184
+ font-size: 13px;
185
+ line-height: 1.45;
186
+ color: var(--doc-muted);
187
+ text-align: center;
188
+ }
189
+ > .gallery-weather-face > .gallery-weather-note::before {
190
+ content: "";
191
+ flex: none;
192
+ width: 44px;
193
+ height: 44px;
194
+ margin-bottom: 2px;
195
+ background: currentColor;
196
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 44'%3E%3Ccircle cx='22' cy='22' r='22' fill-opacity='.14'/%3E%3Cg transform='translate(11 11) scale(.9167)' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 11a3 3 0 1 0 6 0 3 3 0 0 0-6 0'/%3E%3Cpath d='M17.66 16.66 13.41 20.9a2 2 0 0 1-2.83 0l-4.24-4.24a8 8 0 1 1 11.32 0z'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
197
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 44'%3E%3Ccircle cx='22' cy='22' r='22' fill-opacity='.14'/%3E%3Cg transform='translate(11 11) scale(.9167)' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 11a3 3 0 1 0 6 0 3 3 0 0 0-6 0'/%3E%3Cpath d='M17.66 16.66 13.41 20.9a2 2 0 0 1-2.83 0l-4.24-4.24a8 8 0 1 1 11.32 0z'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
198
+ }
199
+ > .gallery-weather-face > .gallery-weather-note:last-child { margin-bottom: auto; }
200
+ > .gallery-weather-face > .gallery-weather-hint {
201
+ margin: -4px 0 auto;
202
+ font-size: var(--gallery-meta-size);
203
+ color: var(--doc-muted);
204
+ text-align: center;
205
+ opacity: .8;
206
+ }
207
+ .gallery-weather-picture { fill: var(--gallery-weather-cloud); }
208
+ .gallery-weather-picture .gallery-weather-sun { fill: var(--gallery-weather-sun); stroke: var(--gallery-weather-sun); }
209
+ .gallery-weather-picture g.gallery-weather-sun { fill: none; }
210
+ .gallery-weather-picture .gallery-weather-moon { fill: var(--gallery-weather-sun); }
211
+ .gallery-weather-picture .gallery-weather-rain,
212
+ .gallery-weather-picture .gallery-weather-cloud-line { stroke: var(--gallery-weather-rain); }
213
+ .gallery-weather-picture .gallery-weather-cloud-line { stroke: var(--gallery-weather-cloud); }
214
+ .gallery-weather-picture .gallery-weather-snow { fill: var(--gallery-weather-rain); }
215
+ .gallery-weather-picture .gallery-weather-bolt { fill: var(--gallery-weather-sun); }
216
+ }
217
+ /* A narrow card (2 wide): the picture at the left of the temperature and, under the temperature (from its
218
+ left, not the picture's), the sky's words and the range on one muted line, a dot between; no more line;
219
+ narrower still, no range. */
220
+ @container (max-width: 260px) {
221
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-more { display: none; }
222
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now {
223
+ display: grid;
224
+ grid-template-columns: auto minmax(0, 1fr);
225
+ grid-template-areas: "picture temperature" "picture words";
226
+ align-items: center;
227
+ gap: 2px 10px;
228
+ margin-bottom: auto;
229
+ }
230
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-picture { grid-area: picture; }
231
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-temperature { grid-area: temperature; }
232
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words { grid-area: words; flex-direction: row; gap: 0; font-size: 13px; color: var(--doc-muted); }
233
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > .gallery-weather-sky { font-weight: 400; }
234
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > .gallery-weather-range::before { content: "·"; margin: 0 5px; }
235
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-head > .gallery-weather-at { display: none; }
236
+ }
237
+ @container (max-width: 200px) {
238
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now > .gallery-weather-words > .gallery-weather-range { display: none; }
239
+ }
240
+ /* A low card: no more line. */
241
+ @container (max-height: 104px) {
242
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-more { display: none; }
243
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now { margin-bottom: auto; }
244
+ }
245
+ /* A tall card (4 high, or 2 high on a wide window but for its last row's room): the weather now under the
246
+ head, the days ahead right after its more line, their rows sharing the room left (each 44px at the most),
247
+ what is still left at the bottom. */
248
+ @container (min-height: 240px) {
249
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-now { margin-top: 4px; }
250
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-more { margin-bottom: 0; }
251
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-days { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin-top: 4px; }
252
+ [data-doc-component="gallery/weather"] > .gallery-weather-face > .gallery-weather-days > .gallery-weather-day { flex: 1 1 0; max-height: 44px; min-height: 0; }
253
+ }
254
+ /* On a custom page the face is the card's: as high as what the card holds inside its padding. Where no card
255
+ is around it (a document, as gallery block check shows it) it is as high as a 4 x 2 card's face, for the
256
+ instance is a container of its height too and would otherwise have none. */
257
+ [data-doc-component="gallery/weather"] { height: 11em; }
258
+ .gallery-card > [data-doc-component="gallery/weather"] { height: 100%; }
@@ -0,0 +1,3 @@
1
+ <div data-doc-component="gallery/weather">
2
+ <script type="application/json">{}</script>
3
+ </div>
@@ -0,0 +1,26 @@
1
+ # 天气 (gallery/weather)
2
+
3
+ A card for a custom page (its places are `page`): a city's weather now (the sky's picture and words, the temperature, today's highest and lowest, how it feels, the humidity and the wind) and, on a tall card, the next five days. The data comes from Open-Meteo (open-meteo.com), which needs no key. Use it when the user asks for the weather on a page, its home page among them.
4
+
5
+ ## Settings
6
+
7
+ Its two settings are the card's form, which the user opens from the grip at the card's top right › 设置…:
8
+
9
+ | Key | Label | Type | What it is |
10
+ | --- | --- | --- | --- |
11
+ | `city` | 城市 | text | the city, as the user names it: 上海, 杭州, Berlin; looked up by that name, its first match taken |
12
+ | `unit` | 单位 | select | `℃` (the default) or `℉` |
13
+
14
+ They are kept in its data part `settings`, a `<script type="application/json">`, as example.html does: `{"city":"上海","unit":"℃"}` (on one line and compact, as the card's 设置… writes it back). The city is the one setting it cannot show anything without (`required` in its manifest): when the user did not say which, ask them, or take it from what the workspace says of where they live (写作偏好, the documents; a trip is not a home: ask when that is a guess), and tell them in the handoff which you did; never hand over a card with no city unsaid. `gallery page add <slug> gallery/weather --set city=上海` writes it with the add (`--set unit=℉` the unit); for a card already there, write the city in its settings part. `{}` (template.html, what a menu adds) is a card with no city yet, which asks for one (右上角手柄 › 设置…), and `gallery page check` notes it. The block reads them and writes nothing.
15
+
16
+ ## Going online
17
+
18
+ Its needs name the two hosts it reaches, `net:api.open-meteo.com` (the weather) and `net:geocoding-api.open-meteo.com` (a city's name to its coordinates). Gallery fetches nothing for it until the user allows those: the card then shows 允许访问 api.open-meteo.com 等 2 个网站, a press of which opens Gallery's consent dialog, and the card fetches once the user says yes. Tell the user so when you put the card on a page ("点卡片上的「允许访问」"). They can take it back in 设置 › 组件列表 (允许联网). Never write the consent into gallery.json yourself: it is the user's to give.
19
+
20
+ ## How to write it
21
+
22
+ Put it on a custom page as a card of its board, `<section class="gallery-card" data-card-id="…" data-x data-y data-w data-h>`, holding one instance, after `gallery block add weather`, the instance as template.html has it, then its settings as above (`gallery page add <slug> gallery/weather` does the card for you). Sizes: 4 × 2 (the default: now, today's range and the more line), 2 × 2 (the temperature and the sky alone) and 4 × 4 (and the next five days).
23
+
24
+ ## How it looks
25
+
26
+ The city beside a pin's chip and when the weather was fetched (更新于 14:05) at the top, the sky's picture, the temperature large and the sky's words beside it (on a 2 × 2 card under it, with the range, muted), how it feels, the humidity and the wind in a muted line, and on a 4 × 4 card the days ahead right under them, each with its picture and its lowest and highest at the ends of a bar drawn against the whole week; in the kit's card tokens (as every Gallery card: a head of a tinted chip and a title, its end clear of the card's grip), light and dark, the sun and the rain in colors of their own. It is fetched again when its settings change and every 30 minutes. While there is nothing to show it says why where the weather stands: 写上一个城市 (no city), 找不到「…」 (a name Open-Meteo does not know), 允许联网之后显示天气 (waiting for the user's consent, the kit's button over it), 现在连不上网,取不到天气 (no network, and nothing fetched before; Gallery gives the last answer it kept when there is one, its time at the top), 网络设置把天气服务指向了本地地址,取不到天气 (this computer's network setup gives Open-Meteo's names an address of this computer or of a private network, and no proxy of this computer takes them by name: Gallery refuses such an address). The script runs with the rights of the page's own scripts, as any block's does.