@heycys/gallery 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/browser/editor/components/align.mjs +58 -0
- package/browser/editor/components/code/index.mjs +1 -1
- package/browser/editor/components/embed/page.mjs +1 -1
- package/browser/editor/components/file/page.mjs +4 -4
- package/browser/editor/components/heading/index.mjs +7 -6
- package/browser/editor/components/icons.mjs +3 -0
- package/browser/editor/components/image/index.mjs +2 -0
- package/browser/editor/components/image/page.mjs +172 -90
- package/browser/editor/components/installed/index.mjs +6 -2
- package/browser/editor/components/installed/page.mjs +80 -13
- package/browser/editor/components/media/icons.mjs +13 -8
- package/browser/editor/components/media/page.mjs +5 -3
- package/browser/editor/components/media/toolbar.mjs +12 -3
- package/browser/editor/components/paragraph/index.mjs +4 -3
- package/browser/editor/components/registry.mjs +17 -5
- package/browser/editor/components/table/index.mjs +4 -2
- package/browser/editor/page/after/index.mjs +288 -0
- package/browser/editor/page/align.mjs +91 -0
- package/browser/editor/page/beside.mjs +5 -6
- package/browser/editor/page/block-menu/index.mjs +6 -3
- package/browser/editor/page/blocks.mjs +3 -1
- package/browser/editor/page/board/hint.mjs +64 -27
- package/browser/editor/page/board/index.mjs +160 -71
- package/browser/editor/page/board/motion.mjs +177 -0
- package/browser/editor/page/board/settings.mjs +4 -3
- package/browser/editor/page/clipboard/index.mjs +10 -3
- package/browser/editor/page/clipboard/own/blocks.mjs +3 -4
- package/browser/editor/page/clipboard/own/describe.mjs +6 -2
- package/browser/editor/page/commit.mjs +2 -2
- package/browser/editor/page/core.mjs +3 -1
- package/browser/editor/page/custom-page.mjs +15 -11
- package/browser/editor/page/editor.mjs +31 -14
- package/browser/editor/page/history.mjs +19 -4
- package/browser/editor/page/insert/index.mjs +20 -22
- package/browser/editor/page/menu.mjs +15 -7
- package/browser/editor/page/page-objects.mjs +3 -7
- package/browser/editor/page/present-links.mjs +5 -4
- package/browser/editor/page/present-shell.mjs +63 -0
- package/browser/editor/page/selection/index.mjs +10 -2
- package/browser/editor/page/table/binding.mjs +87 -0
- package/browser/editor/page/table/icons.mjs +0 -2
- package/browser/editor/page/table/index.mjs +10 -14
- package/browser/editor/page/table/views.mjs +5 -52
- package/browser/editor/page/table/widths.mjs +82 -10
- package/browser/editor/page/typing/index.mjs +44 -12
- package/browser/editor/page/typing/leave.mjs +19 -5
- package/browser/shared/block-packs.mjs +23 -8
- package/browser/shared/component-catalog.mjs +15 -8
- package/browser/shared/component-source.mjs +3 -1
- package/browser/shared/components/heading.mjs +2 -1
- package/browser/shared/components/installed.mjs +1 -1
- package/browser/shared/components/page.mjs +2 -0
- package/browser/shared/components/paragraph.mjs +8 -1
- package/browser/shared/components/table.mjs +1 -0
- package/browser/shared/emoji/line-icons.mjs +37 -0
- package/browser/shared/http.mjs +9 -0
- package/browser/shared/i18n/en.mjs +40 -17
- package/browser/shared/outline.mjs +30 -2
- package/browser/shared/pack-manifest.mjs +12 -1
- package/browser/shared/pages.mjs +169 -57
- package/browser/shared/pinyin.mjs +92 -0
- package/browser/shared/present-step.mjs +17 -0
- package/browser/shared/sidebar-head.mjs +70 -0
- package/browser/shared/web-links.mjs +5 -0
- package/dist/chunk-2OHANJ6Q.mjs +2 -0
- package/dist/{chunk-QOUEFUCH.mjs → chunk-3MBRL2PM.mjs} +1 -1
- package/dist/{chunk-NWR2LT5B.mjs → chunk-4OKWZY5E.mjs} +1 -1
- package/dist/{chunk-RMECZA2K.mjs → chunk-5K44HLTZ.mjs} +1 -1
- package/dist/{chunk-NFMCP2HI.mjs → chunk-5XA2ZBAE.mjs} +2 -2
- package/dist/{chunk-OUUP3WKT.mjs → chunk-67N2QJYT.mjs} +1 -1
- package/dist/chunk-6KBYRA2C.mjs +2 -0
- package/dist/{chunk-SX4LJYT6.mjs → chunk-6N3PX4XX.mjs} +1 -1
- package/dist/{chunk-RFDZXFJF.mjs → chunk-7JDX4E65.mjs} +1 -1
- package/dist/chunk-7M2BQQGZ.mjs +2 -0
- package/dist/{chunk-ZUNVAXLH.mjs → chunk-7OG26PGF.mjs} +1 -1
- package/dist/{chunk-B4P2CM2J.mjs → chunk-AHN3NFKJ.mjs} +1 -1
- package/dist/chunk-ALZ6ZENZ.mjs +3 -0
- package/dist/{chunk-UGWYKHBL.mjs → chunk-AR5D57ZD.mjs} +2 -2
- package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
- package/dist/{chunk-HLYV7RIH.mjs → chunk-CMA72MAG.mjs} +1 -1
- package/dist/{chunk-D4CKRD3V.mjs → chunk-DAZL73VK.mjs} +2 -2
- package/dist/{chunk-6VTU6KEO.mjs → chunk-DJISHPUL.mjs} +1 -1
- package/dist/{chunk-BPL64HGA.mjs → chunk-EAIFITAV.mjs} +1 -1
- package/dist/chunk-EP25HDAJ.mjs +2 -0
- package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
- package/dist/{chunk-3KJHLHUO.mjs → chunk-F6HVDZR4.mjs} +1 -1
- package/dist/{chunk-3NJACR4W.mjs → chunk-FDMAXMQA.mjs} +1 -1
- package/dist/chunk-FETSREUN.mjs +5 -0
- package/dist/{chunk-RKPWLGZ3.mjs → chunk-GHOYKMQ3.mjs} +1 -1
- package/dist/chunk-HDWTZ763.mjs +12 -0
- package/dist/{chunk-VTKNEYT3.mjs → chunk-HPOI4ZX7.mjs} +1 -1
- package/dist/{chunk-OW5NWPOJ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
- package/dist/{chunk-V6RIYQZZ.mjs → chunk-ITOH5EGO.mjs} +1 -1
- package/dist/{chunk-ZWPMCK7P.mjs → chunk-IV64FFI4.mjs} +1 -1
- package/dist/chunk-J43Z6LSS.mjs +2 -0
- package/dist/chunk-J5PUIV3Y.mjs +4 -0
- package/dist/{chunk-KTKHRC6H.mjs → chunk-JA7G53PC.mjs} +1 -1
- package/dist/{chunk-NWFBE2YA.mjs → chunk-JLTZF4LA.mjs} +1 -1
- package/dist/{chunk-GY3FAUVM.mjs → chunk-JZT7I2OJ.mjs} +1 -1
- package/dist/{chunk-QKXMPUXC.mjs → chunk-K5XATEOO.mjs} +1 -1
- package/dist/chunk-L2SUIS5S.mjs +2 -0
- package/dist/{chunk-TEZAWDSQ.mjs → chunk-LM4XFQHZ.mjs} +1 -1
- package/dist/{chunk-5ZJA7CXT.mjs → chunk-MN7BZL4R.mjs} +1 -1
- package/dist/chunk-MTYYHM2G.mjs +15 -0
- package/dist/{chunk-HMYTM3GT.mjs → chunk-MXUBKDRO.mjs} +1 -1
- package/dist/{chunk-MHW7ZKOU.mjs → chunk-OEXWRJME.mjs} +1 -1
- package/dist/{chunk-CRJEG7MT.mjs → chunk-OFHXGWOX.mjs} +1 -1
- package/dist/chunk-ORID3AE7.mjs +166 -0
- package/dist/{chunk-32ZUEFZ7.mjs → chunk-PGDE24N5.mjs} +1 -1
- package/dist/{chunk-UNOYVIYR.mjs → chunk-POTA2FV4.mjs} +1 -1
- package/dist/{chunk-N7AZZLGT.mjs → chunk-QE5RG76S.mjs} +18 -17
- package/dist/{chunk-N76HWBA3.mjs → chunk-R2ZDLJYT.mjs} +1 -1
- package/dist/chunk-SWUTTZCN.mjs +2 -0
- package/dist/{chunk-POQSE4C2.mjs → chunk-TR7TWGHB.mjs} +1 -1
- package/dist/chunk-U5XL7D6A.mjs +3 -0
- package/dist/{chunk-PVER7LSC.mjs → chunk-U7NZI6N4.mjs} +2 -2
- package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
- package/dist/chunk-UJQJKNGK.mjs +2 -0
- package/dist/chunk-UYVCQK3H.mjs +22 -0
- package/dist/{chunk-FJYN7RTH.mjs → chunk-VFX2KNHP.mjs} +1 -1
- package/dist/{chunk-UBBRTJ2R.mjs → chunk-VS7JIGJW.mjs} +1 -1
- package/dist/{chunk-NVRNLK25.mjs → chunk-W7665ZEC.mjs} +2 -2
- package/dist/{chunk-NG375ILQ.mjs → chunk-WQDQWCX5.mjs} +1 -1
- package/dist/chunk-YLPSMDIS.mjs +5 -0
- package/dist/{chunk-VXACTLO3.mjs → chunk-YNAWBAYN.mjs} +1 -1
- package/dist/{chunk-ZB4H7ZN4.mjs → chunk-YROX2O22.mjs} +1 -1
- package/dist/{chunk-YQGT4AB2.mjs → chunk-ZDLPUOAG.mjs} +1 -1
- package/dist/chunk-ZQS6H473.mjs +16 -0
- package/dist/{chunk-OJKMINIU.mjs → chunk-ZWWC4KLF.mjs} +15 -15
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/worker.mjs +1 -1
- package/kit/board.css +5 -5
- package/kit/code.js +15 -15
- package/kit/report.css +120 -41
- package/kit/runtime.js +45 -7
- package/kit/tables.js +2 -0
- package/kit/ui.js +2 -0
- package/package.json +1 -1
- package/packs/gallery/chart-view/kit.js +3 -3
- package/packs/gallery/chart-view/writing.md +1 -1
- package/packs/gallery/countdown/component.json +1 -1
- package/packs/gallery/countdown/kit.js +89 -29
- package/packs/gallery/countdown/preview.svg +1 -1
- package/packs/gallery/countdown/style.css +88 -100
- package/packs/gallery/countdown/writing.md +2 -2
- package/packs/gallery/habit/component.json +3 -3
- package/packs/gallery/habit/kit.js +114 -75
- package/packs/gallery/habit/preview.svg +1 -1
- package/packs/gallery/habit/style.css +133 -205
- package/packs/gallery/habit/writing.md +2 -2
- package/packs/gallery/heatmap/component.json +2 -2
- package/packs/gallery/heatmap/kit.js +335 -173
- package/packs/gallery/heatmap/preview.svg +1 -1
- package/packs/gallery/heatmap/style.css +158 -170
- package/packs/gallery/heatmap/writing.md +2 -2
- package/packs/gallery/kanban/kit.js +3 -3
- package/packs/gallery/kanban/writing.md +1 -1
- package/packs/gallery/metric/kit.js +3 -3
- package/packs/gallery/metric/writing.md +1 -1
- package/packs/gallery/note/component.json +3 -3
- package/packs/gallery/note/kit.js +40 -13
- package/packs/gallery/note/preview.svg +1 -1
- package/packs/gallery/note/style.css +51 -30
- package/packs/gallery/note/writing.md +2 -2
- package/packs/gallery/recent/component.json +2 -2
- package/packs/gallery/recent/kit.js +141 -86
- package/packs/gallery/recent/preview.svg +1 -1
- package/packs/gallery/recent/style.css +53 -126
- package/packs/gallery/recent/writing.md +2 -2
- package/packs/gallery/static/component.json +2 -2
- package/packs/gallery/static/preview.svg +1 -1
- package/packs/gallery/static/style.css +99 -66
- package/packs/gallery/static/writing.md +2 -2
- package/packs/gallery/stats/component.json +3 -3
- package/packs/gallery/stats/kit.js +253 -72
- package/packs/gallery/stats/preview.svg +1 -1
- package/packs/gallery/stats/style.css +64 -142
- package/packs/gallery/stats/writing.md +2 -2
- package/packs/gallery/today/component.json +2 -2
- package/packs/gallery/today/kit.js +62 -27
- package/packs/gallery/today/preview.svg +1 -1
- package/packs/gallery/today/style.css +101 -72
- package/packs/gallery/today/writing.md +2 -2
- package/packs/gallery/todos/component.json +1 -1
- package/packs/gallery/todos/kit.js +218 -71
- package/packs/gallery/todos/preview.svg +1 -1
- package/packs/gallery/todos/style.css +170 -223
- package/packs/gallery/todos/writing.md +1 -1
- package/packs/gallery/weather/component.json +2 -2
- package/packs/gallery/weather/kit.js +165 -90
- package/packs/gallery/weather/preview.svg +1 -1
- package/packs/gallery/weather/style.css +100 -218
- package/packs/gallery/weather/writing.md +1 -1
- package/packs/gallery-soft/countdown/component.json +18 -0
- package/packs/gallery-soft/countdown/example.html +3 -0
- package/packs/gallery-soft/countdown/kit.js +206 -0
- package/packs/gallery-soft/countdown/preview.svg +1 -0
- package/packs/gallery-soft/countdown/style.css +211 -0
- package/packs/gallery-soft/countdown/template.html +3 -0
- package/packs/gallery-soft/countdown/writing.md +24 -0
- package/packs/gallery-soft/habit/component.json +14 -0
- package/packs/gallery-soft/habit/example.html +3 -0
- package/packs/gallery-soft/habit/kit.js +366 -0
- package/packs/gallery-soft/habit/preview.svg +1 -0
- package/packs/gallery-soft/habit/style.css +251 -0
- package/packs/gallery-soft/habit/template.html +3 -0
- package/packs/gallery-soft/habit/writing.md +23 -0
- package/packs/gallery-soft/heatmap/component.json +18 -0
- package/packs/gallery-soft/heatmap/example.html +3 -0
- package/packs/gallery-soft/heatmap/kit.js +498 -0
- package/packs/gallery-soft/heatmap/preview.svg +1 -0
- package/packs/gallery-soft/heatmap/style.css +181 -0
- package/packs/gallery-soft/heatmap/template.html +3 -0
- package/packs/gallery-soft/heatmap/writing.md +23 -0
- package/packs/gallery-soft/note/component.json +14 -0
- package/packs/gallery-soft/note/example.html +3 -0
- package/packs/gallery-soft/note/kit.js +162 -0
- package/packs/gallery-soft/note/preview.svg +1 -0
- package/packs/gallery-soft/note/style.css +148 -0
- package/packs/gallery-soft/note/template.html +3 -0
- package/packs/gallery-soft/note/writing.md +20 -0
- package/packs/gallery-soft/recent/component.json +13 -0
- package/packs/gallery-soft/recent/example.html +1 -0
- package/packs/gallery-soft/recent/kit.js +335 -0
- package/packs/gallery-soft/recent/preview.svg +1 -0
- package/packs/gallery-soft/recent/style.css +160 -0
- package/packs/gallery-soft/recent/template.html +1 -0
- package/packs/gallery-soft/recent/writing.md +17 -0
- package/packs/gallery-soft/static/component.json +13 -0
- package/packs/gallery-soft/static/example.html +12 -0
- package/packs/gallery-soft/static/preview.svg +1 -0
- package/packs/gallery-soft/static/style.css +186 -0
- package/packs/gallery-soft/static/template.html +1 -0
- package/packs/gallery-soft/static/writing.md +20 -0
- package/packs/gallery-soft/stats/component.json +13 -0
- package/packs/gallery-soft/stats/example.html +1 -0
- package/packs/gallery-soft/stats/kit.js +341 -0
- package/packs/gallery-soft/stats/preview.svg +1 -0
- package/packs/gallery-soft/stats/style.css +119 -0
- package/packs/gallery-soft/stats/template.html +1 -0
- package/packs/gallery-soft/stats/writing.md +13 -0
- package/packs/gallery-soft/today/component.json +12 -0
- package/packs/gallery-soft/today/example.html +1 -0
- package/packs/gallery-soft/today/kit.js +222 -0
- package/packs/gallery-soft/today/preview.svg +1 -0
- package/packs/gallery-soft/today/style.css +178 -0
- package/packs/gallery-soft/today/template.html +1 -0
- package/packs/gallery-soft/today/writing.md +16 -0
- package/packs/gallery-soft/todos/component.json +19 -0
- package/packs/gallery-soft/todos/example.html +3 -0
- package/packs/gallery-soft/todos/kit.js +527 -0
- package/packs/gallery-soft/todos/preview.svg +1 -0
- package/packs/gallery-soft/todos/style.css +212 -0
- package/packs/gallery-soft/todos/template.html +3 -0
- package/packs/gallery-soft/todos/writing.md +32 -0
- package/packs/gallery-soft/weather/component.json +19 -0
- package/packs/gallery-soft/weather/example.html +3 -0
- package/packs/gallery-soft/weather/kit.js +411 -0
- package/packs/gallery-soft/weather/preview.svg +1 -0
- package/packs/gallery-soft/weather/style.css +185 -0
- package/packs/gallery-soft/weather/template.html +3 -0
- package/packs/gallery-soft/weather/writing.md +28 -0
- package/release.json +9 -9
- package/skill/SKILL.md +12 -12
- package/skill/references/building-blocks.md +4 -4
- package/skill/references/pages-and-data.md +13 -9
- package/skill/references/writing-html.md +2 -0
- package/web/assets/index-BM_K-Auu.css +1 -0
- package/web/assets/index-DRslOBiv.js +15 -0
- package/web/index.html +2 -2
- package/dist/chunk-2NRBY6AJ.mjs +0 -2
- package/dist/chunk-35NQ4PLQ.mjs +0 -2
- package/dist/chunk-4AV456RQ.mjs +0 -2
- package/dist/chunk-5XYHDBJ5.mjs +0 -2
- package/dist/chunk-6A53MZYZ.mjs +0 -2
- package/dist/chunk-6EXH3ORZ.mjs +0 -166
- package/dist/chunk-BVEFGE4B.mjs +0 -2
- package/dist/chunk-D4EURAX4.mjs +0 -4
- package/dist/chunk-DQCE7TJY.mjs +0 -5
- package/dist/chunk-FWNRPMCJ.mjs +0 -12
- package/dist/chunk-FWW4CSAZ.mjs +0 -2
- package/dist/chunk-JVHHHGEA.mjs +0 -3
- package/dist/chunk-PZDLJJTJ.mjs +0 -3
- package/dist/chunk-SXHBYZST.mjs +0 -16
- package/dist/chunk-UFNYFFKO.mjs +0 -20
- package/dist/chunk-UKSHDZZ6.mjs +0 -2
- package/dist/chunk-VJPFVNNW.mjs +0 -15
- package/dist/chunk-VS3ZMVGX.mjs +0 -5
- package/web/assets/index-BK2e_07w.js +0 -13
- package/web/assets/index-CWFI83mF.css +0 -1
|
@@ -1,37 +1,46 @@
|
|
|
1
1
|
// gallery/weather (天气): a city's weather now and for the days ahead, from Open-Meteo (open-meteo.com,
|
|
2
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
|
|
4
|
-
// writes and this block only reads. The city is looked up by name
|
|
5
|
-
// first match, its coordinates), its weather fetched for them
|
|
6
|
-
// highest, lowest and sky for
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
3
|
+
// `unit` (℃ or ℉), are the card's form (the grip at its top right › 设置…), kept in the data part
|
|
4
|
+
// `settings`, which the board writes and this block only reads. The city is looked up by name
|
|
5
|
+
// (geocoding-api.open-meteo.com: its first match, its coordinates), its weather fetched for them
|
|
6
|
+
// (api.open-meteo.com: now, and each day's highest, lowest and sky for six days, today and the five after
|
|
7
|
+
// it), both through context.fetch, the server fetching for it: its needs name the two hosts, and nothing
|
|
8
|
+
// is fetched until the reader allows them (the kit then shows 允许访问 on the card and starts the block
|
|
9
|
+
// again once the reader says yes). Fetched again as its settings change (`source`) and every 30 minutes;
|
|
10
|
+
// the server keeps an answer a minute and gives what it kept when the network is not there, which the
|
|
11
|
+
// card says by its time (更新于 09:05; an answer kept more than 10 minutes, when the network is not there,
|
|
12
|
+
// the head's sign of no network and when it was fetched, 昨天 21:40). In the page's language.
|
|
13
|
+
//
|
|
14
|
+
// Its face (the pack's design B, in the kit's card tokens), laid out by the card's cells (`data-layout`, by
|
|
15
|
+
// context.size, read again on `layout`): a head of the chip (a place's pin), the city and when; the sky's
|
|
16
|
+
// picture (drawn in its own colors: the sun's orange, a cloud's gray, the rain's blue) beside the
|
|
17
|
+
// temperature, the hero, and the sky's words and today's lowest and highest; under them how it feels,
|
|
18
|
+
// the air's humidity and the wind (its direction and speed). A narrow card (2 wide) the picture and the
|
|
19
|
+
// temperature at the bottom and one line under them; a tall one (4 high) the next five days at the
|
|
20
|
+
// bottom, each its weekday, its sky, its lowest, a bar from its lowest to its highest on the scale of all
|
|
21
|
+
// of them, cold to warm, and its highest. What it says while there is nothing to show (no city yet, one
|
|
22
|
+
// not found, waiting for the reader's consent, the network not there, this computer's network setup
|
|
23
|
+
// sending Open-Meteo's names to an address of its own or of a private network, which the server refuses:
|
|
24
|
+
// code "private") is the cards' empty face under the head: what is so, and what to do, faint.
|
|
19
25
|
|
|
20
26
|
const WORDS = {
|
|
21
27
|
zh: {
|
|
22
28
|
title: "天气",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
states: {
|
|
30
|
+
pick: ["写上一个城市", "在卡片右上角的 设置… 里填,比如「杭州」"],
|
|
31
|
+
loading: ["正在取天气…", ""],
|
|
32
|
+
consent: ["允许联网之后显示天气", "数据来自 open-meteo.com"],
|
|
33
|
+
offline: ["现在连不上网,取不到天气", "连上以后会自己再取"],
|
|
34
|
+
private: ["取不到天气", "网络设置把天气服务指向了本地地址"],
|
|
35
|
+
failed: ["没能取到天气", "过一会儿会自己再取"],
|
|
36
|
+
},
|
|
37
|
+
unknown: (/** @type {string} */ city) => [`找不到「${city}」`, "换个写法试试,比如「上海」"],
|
|
31
38
|
at: (/** @type {string} */ time) => `更新于 ${time}`,
|
|
39
|
+
yesterday: (/** @type {string} */ time) => `昨天 ${time}`,
|
|
40
|
+
day: (/** @type {number} */ month, /** @type {number} */ day, /** @type {string} */ time) => `${month}月${day}日 ${time}`,
|
|
32
41
|
feels: (/** @type {string} */ value) => `体感 ${value}`,
|
|
33
|
-
|
|
34
|
-
wind: (/** @type {number} */ value) =>
|
|
42
|
+
winds: ["北风", "东北风", "东风", "东南风", "南风", "西南风", "西风", "西北风"],
|
|
43
|
+
wind: (/** @type {string} */ from, /** @type {number} */ value) => `${from} ${value} km/h`,
|
|
35
44
|
today: "今天",
|
|
36
45
|
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
|
37
46
|
sky: {
|
|
@@ -42,18 +51,21 @@ const WORDS = {
|
|
|
42
51
|
},
|
|
43
52
|
en: {
|
|
44
53
|
title: "Weather",
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
states: {
|
|
55
|
+
pick: ["Name a city", "In Settings… at the card's top right, such as “Berlin”"],
|
|
56
|
+
loading: ["Fetching the weather…", ""],
|
|
57
|
+
consent: ["Allow internet access to show the weather", "From open-meteo.com"],
|
|
58
|
+
offline: ["Offline: no weather now", "It fetches again once online"],
|
|
59
|
+
private: ["No weather", "The network setup sends the weather service to a local address"],
|
|
60
|
+
failed: ["The weather could not be fetched", "It tries again in a while"],
|
|
61
|
+
},
|
|
62
|
+
unknown: (/** @type {string} */ city) => [`No place called “${city}”`, "Try another spelling, such as “Berlin”"],
|
|
53
63
|
at: (/** @type {string} */ time) => `Updated ${time}`,
|
|
64
|
+
yesterday: (/** @type {string} */ time) => `Yesterday ${time}`,
|
|
65
|
+
day: (/** @type {number} */ month, /** @type {number} */ day, /** @type {string} */ time) => `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month - 1]} ${day} ${time}`,
|
|
54
66
|
feels: (/** @type {string} */ value) => `Feels ${value}`,
|
|
55
|
-
|
|
56
|
-
wind: (/** @type {number} */ value) =>
|
|
67
|
+
winds: ["N", "NE", "E", "SE", "S", "SW", "W", "NW"],
|
|
68
|
+
wind: (/** @type {string} */ from, /** @type {number} */ value) => `${from} ${value} km/h`,
|
|
57
69
|
today: "Today",
|
|
58
70
|
weekdays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
59
71
|
sky: {
|
|
@@ -64,6 +76,9 @@ const WORDS = {
|
|
|
64
76
|
},
|
|
65
77
|
};
|
|
66
78
|
|
|
79
|
+
/** How old a kept answer is before the head says it is (with no network): 10 minutes. */
|
|
80
|
+
const STALE = 10 * 60_000;
|
|
81
|
+
|
|
67
82
|
/** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
|
|
68
83
|
function languageOf(/** @type {HTMLElement} */ root) {
|
|
69
84
|
const ui = root.getAttribute("data-ge-language");
|
|
@@ -72,7 +87,7 @@ function languageOf(/** @type {HTMLElement} */ root) {
|
|
|
72
87
|
|
|
73
88
|
/**
|
|
74
89
|
* The sky a WMO weather code says (what Open-Meteo answers with), as { words (its key in WORDS' sky),
|
|
75
|
-
* picture (which of
|
|
90
|
+
* picture (which of the pictures draws it) }; `day` false draws a clear or mostly clear sky with the moon.
|
|
76
91
|
* @param {unknown} code
|
|
77
92
|
* @param {boolean} day
|
|
78
93
|
*/
|
|
@@ -100,13 +115,14 @@ const searchAddress = (city, language) => `https://geocoding-api.open-meteo.com/
|
|
|
100
115
|
|
|
101
116
|
/**
|
|
102
117
|
* 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
|
|
118
|
+
* which the server keeps), in `unit` (℃ or ℉): now, and six days (today and the five after it), by the
|
|
119
|
+
* place's own clock.
|
|
104
120
|
* @param {{ latitude: number, longitude: number }} place
|
|
105
121
|
* @param {string} unit
|
|
106
122
|
*/
|
|
107
123
|
const forecastAddress = (place, unit) => `https://api.open-meteo.com/v1/forecast?latitude=${place.latitude.toFixed(2)}&longitude=${place.longitude.toFixed(2)}`
|
|
108
|
-
+ "¤t=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=
|
|
124
|
+
+ "¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,weather_code,is_day"
|
|
125
|
+
+ `&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days=6${unit === "℉" ? "&temperature_unit=fahrenheit" : ""}`;
|
|
110
126
|
|
|
111
127
|
/**
|
|
112
128
|
* The first place a lookup's answer gives ({ name, latitude, longitude, region }), or null for none.
|
|
@@ -122,60 +138,89 @@ function placeOf(answer) {
|
|
|
122
138
|
const degrees = (value) => (typeof value === "number" && Number.isFinite(value) ? `${Math.round(value)}°`.replace(/^-/, "−") : "–");
|
|
123
139
|
|
|
124
140
|
/**
|
|
125
|
-
* What the card says, in `language`, of `state`: { kind: "pick" } (no city), { kind: "loading",
|
|
126
|
-
* { kind: "unknown", city } (no such place), { kind: "consent", city }, { kind: "offline", city },
|
|
141
|
+
* What the card says, in `language`, of `state` at `now`: { kind: "pick" } (no city), { kind: "loading",
|
|
142
|
+
* city }, { kind: "unknown", city } (no such place), { kind: "consent", city }, { kind: "offline", city },
|
|
127
143
|
* { kind: "private", city } (the server refused the address this computer's resolver gave: one of this
|
|
128
144
|
* 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,
|
|
131
|
-
*
|
|
145
|
+
* { kind: "weather", place, forecast (Open-Meteo's answer), at (when it was fetched, ISO), cached }. Its head
|
|
146
|
+
* (the city, when, and whether that is an answer kept from before the network went: `stale`), and either a
|
|
147
|
+
* note and its hint (the empty face) or the weather: the sky's picture and words, the temperature, today's
|
|
148
|
+
* lowest and highest, the more line's pieces ({ icon, text }), and the five days after today.
|
|
132
149
|
* @param {any} state
|
|
133
150
|
* @param {"zh" | "en"} language
|
|
151
|
+
* @param {Date} [now]
|
|
134
152
|
*/
|
|
135
|
-
function faceOf(state, language) {
|
|
153
|
+
function faceOf(state, language, now = new Date()) {
|
|
136
154
|
const words = WORDS[language];
|
|
137
|
-
const
|
|
155
|
+
const named = state.kind === "pick" || state.kind === "unknown";
|
|
156
|
+
const head = { city: named ? words.title : state.city || state.place?.name || words.title, at: "", stale: false };
|
|
138
157
|
if (state.kind !== "weather") {
|
|
139
|
-
/** @type {Record<string, string>} */
|
|
140
|
-
const
|
|
141
|
-
const note = state.kind === "unknown" ? words.unknown(state.city) :
|
|
142
|
-
return { head, note, hint
|
|
158
|
+
/** @type {Record<string, string[]>} */
|
|
159
|
+
const states = words.states;
|
|
160
|
+
const [note, hint] = state.kind === "unknown" ? words.unknown(state.city) : states[state.kind] ?? states.failed;
|
|
161
|
+
return { head, note, hint, now: null, days: [] };
|
|
143
162
|
}
|
|
144
163
|
const { current = {}, daily = {} } = state.forecast ?? {};
|
|
145
164
|
const sky = skyOf(current.weather_code, current.is_day !== 0);
|
|
146
165
|
const fetched = typeof state.at === "string" ? new Date(state.at) : null;
|
|
147
166
|
const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
if (fetched && !Number.isNaN(fetched.getTime())) {
|
|
168
|
+
const time = `${two(fetched.getHours())}:${two(fetched.getMinutes())}`;
|
|
169
|
+
head.stale = state.cached === true && now.getTime() - fetched.getTime() > STALE;
|
|
170
|
+
const day = (/** @type {Date} */ date) => Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 86_400_000;
|
|
171
|
+
const ago = day(now) - day(fetched);
|
|
172
|
+
head.at = !head.stale ? words.at(time) : ago === 0 ? time : ago === 1 ? words.yesterday(time) : words.day(fetched.getMonth() + 1, fetched.getDate(), time);
|
|
173
|
+
}
|
|
174
|
+
const range = (/** @type {number} */ index) => `${degrees(daily.temperature_2m_min?.[index])} / ${degrees(daily.temperature_2m_max?.[index])}`;
|
|
150
175
|
const more = [
|
|
151
|
-
typeof current.apparent_temperature === "number" ? words.feels(degrees(current.apparent_temperature)) : null,
|
|
152
|
-
typeof current.relative_humidity_2m === "number" ?
|
|
153
|
-
typeof current.wind_speed_10m === "number" ? words.wind(Math.round(current.wind_speed_10m)) : null,
|
|
176
|
+
typeof current.apparent_temperature === "number" ? { icon: "feels", text: words.feels(degrees(current.apparent_temperature)) } : null,
|
|
177
|
+
typeof current.relative_humidity_2m === "number" ? { icon: "humidity", text: `${Math.round(current.relative_humidity_2m)}%` } : null,
|
|
178
|
+
typeof current.wind_speed_10m === "number" ? { icon: "wind", text: words.wind(typeof current.wind_direction_10m === "number" ? words.winds[Math.round(current.wind_direction_10m / 45) % 8] : "", Math.round(current.wind_speed_10m)).trim() } : null,
|
|
154
179
|
].filter((piece) => piece !== null);
|
|
155
180
|
const dates = Array.isArray(daily.time) ? daily.time : [];
|
|
156
|
-
const days = dates.slice(
|
|
181
|
+
const days = dates.slice(1, 6).map((/** @type {string} */ date, /** @type {number} */ at) => {
|
|
182
|
+
const index = at + 1;
|
|
157
183
|
const [year, month, day] = String(date).split("-").map(Number);
|
|
158
|
-
const weekday =
|
|
184
|
+
const weekday = words.weekdays[new Date(Date.UTC(year, month - 1, day)).getUTCDay()] ?? "";
|
|
159
185
|
const [high, low] = [daily.temperature_2m_max?.[index], daily.temperature_2m_min?.[index]];
|
|
160
|
-
|
|
186
|
+
const kind = skyOf(daily.weather_code?.[index]);
|
|
187
|
+
return { weekday, picture: kind.picture, sky: words.sky[kind.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
188
|
});
|
|
162
|
-
|
|
189
|
+
const span = typeof daily.temperature_2m_max?.[0] === "number" && typeof daily.temperature_2m_min?.[0] === "number" ? [daily.temperature_2m_min[0], daily.temperature_2m_max[0]] : null;
|
|
190
|
+
return { head, note: "", hint: "", now: { picture: sky.picture, sky: words.sky[sky.words], temperature: degrees(current.temperature_2m), range: dates.length ? range(0) : "", span, more }, days };
|
|
163
191
|
}
|
|
164
192
|
|
|
165
|
-
// The sky's pictures,
|
|
166
|
-
|
|
167
|
-
const
|
|
193
|
+
// The sky's pictures, each drawn on a box of 64 in its own colors (style.css: the sun's orange, a cloud's
|
|
194
|
+
// gray, the rain's blue, as the instance's custom properties say them in light and dark).
|
|
195
|
+
const cloud = (x = 0, y = 0, scale = 1, tone = "cloud") => `<g transform="translate(${x} ${y}) scale(${scale})" class="gallery-weather-${tone}"><rect x="13" y="34" width="39" height="15" rx="7.5"/><circle cx="24" cy="35" r="8.5"/><circle cx="35.5" cy="30" r="11.5"/><circle cx="45.5" cy="38" r="7.5"/></g>`;
|
|
196
|
+
function sunAt(cx, cy, r) {
|
|
197
|
+
let rays = "";
|
|
198
|
+
for (let i = 0; i < 8; i++) {
|
|
199
|
+
const a = (i * Math.PI) / 4;
|
|
200
|
+
rays += `<line x1="${(cx + (r + 5) * Math.cos(a)).toFixed(1)}" y1="${(cy + (r + 5) * Math.sin(a)).toFixed(1)}" x2="${(cx + (r + 9.5) * Math.cos(a)).toFixed(1)}" y2="${(cy + (r + 9.5) * Math.sin(a)).toFixed(1)}"/>`;
|
|
201
|
+
}
|
|
202
|
+
return `<g class="gallery-weather-sun"><circle cx="${cx}" cy="${cy}" r="${r}"/><g class="gallery-weather-rays" stroke-width="3" stroke-linecap="round">${rays}</g></g>`;
|
|
203
|
+
}
|
|
204
|
+
const moonAt = (cx, cy, r) => `<path class="gallery-weather-moon" d="M${cx + r * 0.35} ${cy - r}a${r} ${r} 0 1 0 ${r * 0.65} ${r * 1.55}a${r * 0.8} ${r * 0.8} 0 0 1 -${r * 0.65} -${r * 1.55}z"/>`;
|
|
168
205
|
const PICTURES = {
|
|
169
|
-
sun:
|
|
170
|
-
moon:
|
|
171
|
-
partly:
|
|
172
|
-
"partly-night":
|
|
173
|
-
cloud:
|
|
174
|
-
fog:
|
|
175
|
-
rain:
|
|
176
|
-
snow:
|
|
177
|
-
thunder:
|
|
206
|
+
sun: sunAt(32, 32, 11),
|
|
207
|
+
moon: moonAt(30, 32, 14),
|
|
208
|
+
partly: sunAt(24, 23, 9) + cloud(5, 5, 0.95),
|
|
209
|
+
"partly-night": moonAt(22, 22, 11) + cloud(5, 5, 0.95),
|
|
210
|
+
cloud: cloud(-6, -8, 0.9, "cloud-2") + cloud(4, 2, 1),
|
|
211
|
+
fog: `${cloud(0, -8, 1)}<g class="gallery-weather-mist" stroke-width="3.2" stroke-linecap="round"><line x1="14" y1="49" x2="50" y2="49"/><line x1="20" y1="56" x2="44" y2="56"/></g>`,
|
|
212
|
+
rain: `${cloud(0, -6, 1)}<g class="gallery-weather-rain" stroke-width="3.2" stroke-linecap="round"><line x1="24" y1="49" x2="21.5" y2="55"/><line x1="33.5" y1="49" x2="31" y2="55"/><line x1="43" y1="49" x2="40.5" y2="55"/></g>`,
|
|
213
|
+
snow: `${cloud(0, -6, 1)}<g class="gallery-weather-snow"><circle cx="23" cy="52" r="2.4"/><circle cx="32.5" cy="56" r="2.4"/><circle cx="42" cy="52" r="2.4"/></g>`,
|
|
214
|
+
thunder: `${cloud(0, -8, 1)}<path class="gallery-weather-bolt" d="M34 40l-7 10h6l-3 9 9-12h-6l3-7z"/>`,
|
|
215
|
+
};
|
|
216
|
+
// The more line's small pictures, of lines, 24 x 24.
|
|
217
|
+
const ICONS = {
|
|
218
|
+
feels: '<path d="M10 13.8V5.5a2 2 0 1 1 4 0v8.3a3.8 3.8 0 1 1-4 0z"/>',
|
|
219
|
+
humidity: '<path d="M12 4s5.5 6 5.5 10a5.5 5.5 0 0 1-11 0C6.5 10 12 4 12 4z"/>',
|
|
220
|
+
wind: '<path d="M3.5 9.5h10a2.8 2.8 0 1 0-2.8-2.8M3.5 14.5h14a2.8 2.8 0 1 1-2.8 2.8M3.5 12h6"/>',
|
|
221
|
+
offline: '<path d="M3.5 3.5l17 17M9 16.2a4.5 4.5 0 0 1 5.5-.5M5.5 12.5a9.5 9.5 0 0 1 3.6-2.3M18.6 12.6a9.5 9.5 0 0 0-3-2.1M2.5 9a14 14 0 0 1 3.5-2.3M21.5 9A14 14 0 0 0 10.6 5.3"/><circle cx="12" cy="19.5" r=".9" fill="currentColor"/>',
|
|
178
222
|
};
|
|
223
|
+
const lines = (/** @type {string} */ drawing) => `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${drawing}</svg>`;
|
|
179
224
|
|
|
180
225
|
/**
|
|
181
226
|
* An element of the face, of `kind` (its class).
|
|
@@ -192,30 +237,43 @@ function made(tag, kind) {
|
|
|
192
237
|
/** A sky's picture, as an svg element. @param {string} picture */
|
|
193
238
|
function pictureOf(picture) {
|
|
194
239
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
195
|
-
svg.setAttribute("viewBox", "0 0
|
|
240
|
+
svg.setAttribute("viewBox", "0 0 64 64");
|
|
196
241
|
svg.setAttribute("aria-hidden", "true");
|
|
197
242
|
svg.setAttribute("class", "gallery-weather-picture");
|
|
198
|
-
svg.innerHTML = PICTURES[/** @type {keyof typeof PICTURES} */ (picture)] ??
|
|
243
|
+
svg.innerHTML = PICTURES[/** @type {keyof typeof PICTURES} */ (picture)] ?? PICTURES.cloud;
|
|
199
244
|
return svg;
|
|
200
245
|
}
|
|
201
246
|
|
|
202
247
|
DocKit.component({
|
|
203
248
|
id: "gallery/weather",
|
|
204
|
-
on: ["source"],
|
|
249
|
+
on: ["source", "layout"],
|
|
205
250
|
mount(host, context) {
|
|
206
251
|
const language = languageOf(document.documentElement);
|
|
207
252
|
const face = made("div", "face");
|
|
208
253
|
host.append(face);
|
|
254
|
+
const layout = () => {
|
|
255
|
+
const size = context.size;
|
|
256
|
+
face.dataset.layout = !size ? "wide" : size.w <= 2 ? "narrow" : size.h >= 4 ? "tall" : "wide";
|
|
257
|
+
};
|
|
209
258
|
const draw = (/** @type {any} */ state) => {
|
|
210
259
|
const said = faceOf(state, language);
|
|
211
260
|
const head = made("p", "head");
|
|
212
261
|
const chip = made("span", "chip");
|
|
213
262
|
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="
|
|
263
|
+
chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11z"/><circle cx="12" cy="10" r="2.3"/></svg>';
|
|
215
264
|
const city = made("span", "city");
|
|
216
265
|
city.textContent = said.head.city;
|
|
217
266
|
const at = made("span", "at");
|
|
218
|
-
|
|
267
|
+
if (said.head.stale) {
|
|
268
|
+
const sign = made("span", "stale");
|
|
269
|
+
sign.innerHTML = lines(ICONS.offline);
|
|
270
|
+
sign.title = said.head.at;
|
|
271
|
+
at.append(sign);
|
|
272
|
+
}
|
|
273
|
+
// The time in words of its own: a narrow card shows the sign of an answer kept from before alone (style.css).
|
|
274
|
+
const when = made("span", "when");
|
|
275
|
+
when.textContent = said.head.at;
|
|
276
|
+
at.append(when);
|
|
219
277
|
head.append(chip, city, at);
|
|
220
278
|
/** @type {HTMLElement[]} */
|
|
221
279
|
const parts = [head];
|
|
@@ -231,52 +289,68 @@ DocKit.component({
|
|
|
231
289
|
words.append(sky, range);
|
|
232
290
|
now.append(pictureOf(said.now.picture), temperature, words);
|
|
233
291
|
const more = made("p", "more");
|
|
234
|
-
more.replaceChildren(...said.now.more.map((
|
|
292
|
+
more.replaceChildren(...said.now.more.map(({ icon, text }) => {
|
|
235
293
|
const span = made("span", "piece");
|
|
236
|
-
span.
|
|
294
|
+
span.innerHTML = lines(ICONS[/** @type {keyof typeof ICONS} */ (icon)]);
|
|
295
|
+
span.append(text);
|
|
237
296
|
return span;
|
|
238
297
|
}));
|
|
239
298
|
const days = made("ol", "days");
|
|
240
|
-
const spans = said.days.flatMap((day) => day.span
|
|
241
|
-
const
|
|
299
|
+
const spans = [...said.days.flatMap((day) => (day.span ? [day.span] : [])), ...(said.now.span ? [said.now.span] : [])];
|
|
300
|
+
const lowest = Math.min(...spans.map(([low]) => low));
|
|
301
|
+
const highest = Math.max(...spans.map(([, high]) => high));
|
|
242
302
|
days.replaceChildren(...said.days.map((day) => {
|
|
243
303
|
const item = made("li", "day");
|
|
244
304
|
const weekday = made("span", "weekday");
|
|
245
305
|
weekday.textContent = day.weekday;
|
|
246
|
-
const range = made("span", "day-range");
|
|
247
306
|
const low = made("span", "low");
|
|
248
307
|
low.textContent = day.low;
|
|
249
308
|
const bar = made("span", "bar");
|
|
250
309
|
bar.setAttribute("aria-hidden", "true");
|
|
251
310
|
if (day.span && highest > lowest) {
|
|
311
|
+
// The day's part of the whole scale, its colors the scale's own there: the gradient as wide as the scale, moved under it.
|
|
312
|
+
const from = (day.span[0] - lowest) / (highest - lowest);
|
|
313
|
+
const to = (day.span[1] - lowest) / (highest - lowest);
|
|
252
314
|
const fill = made("span", "fill");
|
|
253
|
-
fill.style.
|
|
254
|
-
fill.style.
|
|
315
|
+
fill.style.left = `${from * 100}%`;
|
|
316
|
+
fill.style.right = `${(1 - to) * 100}%`;
|
|
317
|
+
const part = Math.max(to - from, 0.01);
|
|
318
|
+
fill.style.backgroundSize = `${100 / part}% 100%`;
|
|
319
|
+
fill.style.backgroundPosition = `${1 - part > 0 ? (from / (1 - part)) * 100 : 0}% 0`;
|
|
255
320
|
bar.append(fill);
|
|
256
321
|
}
|
|
257
322
|
const high = made("span", "high");
|
|
258
323
|
high.textContent = day.high;
|
|
259
|
-
range.append(low, bar, high);
|
|
260
324
|
const picture = pictureOf(day.picture);
|
|
261
325
|
picture.setAttribute("aria-label", day.sky);
|
|
262
326
|
picture.removeAttribute("aria-hidden");
|
|
263
327
|
picture.setAttribute("role", "img");
|
|
264
|
-
item.append(weekday, picture,
|
|
328
|
+
item.append(weekday, picture, low, bar, high);
|
|
265
329
|
return item;
|
|
266
330
|
}));
|
|
267
|
-
|
|
331
|
+
// A narrow card's one line under the temperature: the sky, then today's lowest and highest.
|
|
332
|
+
const line = made("p", "line");
|
|
333
|
+
const lineSky = document.createElement("b");
|
|
334
|
+
lineSky.textContent = said.now.sky;
|
|
335
|
+
const dot = made("span", "dot");
|
|
336
|
+
dot.textContent = "·";
|
|
337
|
+
line.append(lineSky, dot, said.now.range);
|
|
338
|
+
parts.push(now, line, more, days);
|
|
268
339
|
} else {
|
|
340
|
+
const empty = made("div", "empty");
|
|
269
341
|
const note = made("p", "note");
|
|
270
342
|
note.textContent = said.note;
|
|
271
|
-
|
|
343
|
+
empty.append(note);
|
|
272
344
|
if (said.hint) {
|
|
273
345
|
const hint = made("p", "hint");
|
|
274
346
|
hint.textContent = said.hint;
|
|
275
|
-
|
|
347
|
+
empty.append(hint);
|
|
276
348
|
}
|
|
349
|
+
parts.push(empty);
|
|
277
350
|
}
|
|
278
351
|
face.replaceChildren(...parts);
|
|
279
352
|
face.dataset.kind = state.kind;
|
|
353
|
+
layout();
|
|
280
354
|
};
|
|
281
355
|
let asked = 0; // the latest load: an earlier one that answers later draws nothing
|
|
282
356
|
const load = async () => {
|
|
@@ -293,7 +367,7 @@ DocKit.component({
|
|
|
293
367
|
if (!found) return draw({ kind: "unknown", city });
|
|
294
368
|
const answer = await context.fetch(forecastAddress(found, unit));
|
|
295
369
|
if (mine !== asked) return;
|
|
296
|
-
draw({ kind: "weather", city, place: found, forecast: answer.data, at: answer.at });
|
|
370
|
+
draw({ kind: "weather", city, place: found, forecast: answer.data, at: answer.at, cached: answer.cached });
|
|
297
371
|
} catch (error) {
|
|
298
372
|
if (mine !== asked) return;
|
|
299
373
|
const code = /** @type {DocFetchError} */ (error).code;
|
|
@@ -306,6 +380,7 @@ DocKit.component({
|
|
|
306
380
|
return {
|
|
307
381
|
update(reason) {
|
|
308
382
|
if (reason === "source") load();
|
|
383
|
+
else if (reason === "layout") layout();
|
|
309
384
|
},
|
|
310
385
|
};
|
|
311
386
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="8" fill="#ffffff" stroke="#e6e6e3"/><rect x="28" y="22" width="10" height="10" rx="3" fill="#efefed"/><rect x="42" y="24.5" width="18" height="5" rx="2.5" fill="#37352f"/><rect x="64" y="24.5" width="34" height="5" rx="2.5" fill="#d4d3cf"/><g fill="#f3a935" stroke="#f3a935"><circle cx="40" cy="52" r="6"/><path d="M40 41v3M40 60v3M29 52h3M48 52h3M32.5 44.5l2 2M45.5 44.5l-2 2M32.5 59.5l2-2" stroke-width="1.6" stroke-linecap="round"/></g><g fill="#dde2e9"><rect x="38" y="56" width="22" height="9" rx="4.5"/><circle cx="45" cy="57" r="5"/><circle cx="52" cy="54" r="6.5"/></g><rect x="70" y="44" width="34" height="22" rx="3" fill="#1d1d1b"/><rect x="112" y="46" width="16" height="6" rx="3" fill="#37352f"/><rect x="112" y="57" width="26" height="5" rx="2.5" fill="#a6a5a1"/><rect x="28" y="88" width="40" height="5" rx="2.5" fill="#a6a5a1"/><rect x="80" y="88" width="26" height="5" rx="2.5" fill="#a6a5a1"/><rect x="132" y="88" width="54" height="5" rx="2.5" fill="#a6a5a1"/></svg>
|