@instafy/cli 0.2.3 → 0.2.4
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/README.md +4 -0
- package/dist/bind/LICENSE +661 -0
- package/dist/bind/bin/instafy-bind.mjs +542 -0
- package/dist/bind/client/bind-client.js +1219 -0
- package/dist/bind/src/apply.mjs +293 -0
- package/dist/bind/src/bound.mjs +202 -0
- package/dist/bind/src/budget.mjs +58 -0
- package/dist/bind/src/check.mjs +176 -0
- package/dist/bind/src/config.mjs +180 -0
- package/dist/bind/src/controller.mjs +244 -0
- package/dist/bind/src/executors/agent.mjs +336 -0
- package/dist/bind/src/executors/script.mjs +163 -0
- package/dist/bind/src/handler-kit.mjs +150 -0
- package/dist/bind/src/init.mjs +238 -0
- package/dist/bind/src/jobs.mjs +316 -0
- package/dist/bind/src/net.mjs +281 -0
- package/dist/bind/src/page-fetch.mjs +43 -0
- package/dist/bind/src/preview-host.mjs +114 -0
- package/dist/bind/src/protocol.mjs +103 -0
- package/dist/bind/src/records-schema.mjs +580 -0
- package/dist/bind/src/records.mjs +161 -0
- package/dist/bind/src/save-owner.mjs +492 -0
- package/dist/bind/src/schema-lite.mjs +212 -0
- package/dist/bind/src/server.mjs +908 -0
- package/dist/bind/src/sse.mjs +119 -0
- package/dist/bind/src/starters/blank/app.js +17 -0
- package/dist/bind/src/starters/blank/index.html +22 -0
- package/dist/bind/src/starters/checklist/app.js +171 -0
- package/dist/bind/src/starters/checklist/bound.json +29 -0
- package/dist/bind/src/starters/checklist/data.json +26 -0
- package/dist/bind/src/starters/checklist/index.html +23 -0
- package/dist/bind/src/starters/compare/app.js +316 -0
- package/dist/bind/src/starters/compare/bound.json +26 -0
- package/dist/bind/src/starters/compare/data.json +170 -0
- package/dist/bind/src/starters/compare/index.html +23 -0
- package/dist/bind/src/starters/handlers/read_offer.mjs +55 -0
- package/dist/bind/src/starters/handlers/set_check.mjs +41 -0
- package/dist/bind/src/starters/ranked/app.js +166 -0
- package/dist/bind/src/starters/ranked/bound.json +26 -0
- package/dist/bind/src/starters/ranked/data.json +142 -0
- package/dist/bind/src/starters/ranked/index.html +23 -0
- package/dist/bind/src/starters/site.js +888 -0
- package/dist/bind/src/starters/theme.css +492 -0
- package/dist/bind/src/starters/timeline/app.js +143 -0
- package/dist/bind/src/starters/timeline/data.json +41 -0
- package/dist/bind/src/starters/timeline/index.html +22 -0
- package/dist/bind/src/static.mjs +141 -0
- package/dist/bind/src/tokens.mjs +102 -0
- package/dist/bind/src/try.mjs +139 -0
- package/dist/bind/src/web.mjs +191 -0
- package/dist/cli.js +2117 -365
- package/package.json +3 -2
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/* Instafy page theme. One shared look for starter pages (compare, ranked, timeline, checklist, blank).
|
|
2
|
+
* Copied into the site by `instafy bind init <dir> --starter <name>`; it is an ordinary file you may edit.
|
|
3
|
+
* Restyle on request: override --if-* tokens in a <style> after this file first; then add component rules there.
|
|
4
|
+
* Light is the default. Dark follows the OS, or Studio through <html data-theme="light|dark"> (set by
|
|
5
|
+
* __bind/client.js). <html data-theme="dark" data-theme-fixed> keeps a page dark whatever Studio says.
|
|
6
|
+
* SPDX-License-Identifier: AGPL-3.0-only */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
color-scheme: light;
|
|
10
|
+
--if-font: Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
11
|
+
|
|
12
|
+
--if-bg: #FBFAF7;
|
|
13
|
+
--if-surface: #FFFFFF;
|
|
14
|
+
--if-float: #FFFFFF;
|
|
15
|
+
--if-control: #FFFFFF;
|
|
16
|
+
--if-seg-bg: color-mix(in oklch, #F6F3EF 70%, transparent);
|
|
17
|
+
--if-seg-on: #FFFFFF;
|
|
18
|
+
--if-seg-on-line: #D4D0CA;
|
|
19
|
+
--if-hover: #FBFAF7;
|
|
20
|
+
--if-selected: #F6F3EF;
|
|
21
|
+
--if-line: #EBE7E2;
|
|
22
|
+
--if-line-strong: #D4D0CA;
|
|
23
|
+
--if-fg: #0F172A;
|
|
24
|
+
--if-fg-2: #55524C;
|
|
25
|
+
--if-muted: #6F6B64;
|
|
26
|
+
--if-subtle: #B5B0A9;
|
|
27
|
+
--if-accent: #0066AC;
|
|
28
|
+
--if-accent-hover: #004F88;
|
|
29
|
+
--if-accent-press: #003760;
|
|
30
|
+
--if-on-accent: #FFFFFF;
|
|
31
|
+
--if-link: #0066AC;
|
|
32
|
+
--if-focus: #0066AC;
|
|
33
|
+
--if-check: #007ACC;
|
|
34
|
+
--if-field-focus: #7CB0E3;
|
|
35
|
+
--if-btn-bg: #F6F3EF;
|
|
36
|
+
--if-btn-fg: #55524C;
|
|
37
|
+
--if-btn-hover: #EBE7E2;
|
|
38
|
+
--if-warn-icon: #8F7958;
|
|
39
|
+
--if-warn-bg: color-mix(in oklch, #D7B686 10%, #FFFFFF);
|
|
40
|
+
--if-warn-line: color-mix(in oklch, #D7B686 35%, #FFFFFF);
|
|
41
|
+
--if-danger-icon: #EC003F;
|
|
42
|
+
--if-danger-bg: #FFF1F2;
|
|
43
|
+
--if-danger-line: #FFCCD3;
|
|
44
|
+
--if-ok: #007A55;
|
|
45
|
+
--if-toast-bg: #262626;
|
|
46
|
+
--if-toast-fg: #FBFAF7;
|
|
47
|
+
--if-skel: #F6F3EF;
|
|
48
|
+
--if-shadow-card: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);
|
|
49
|
+
--if-shadow-float: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);
|
|
50
|
+
--if-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236F6B64' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
|
|
51
|
+
|
|
52
|
+
--if-s1: 4px;
|
|
53
|
+
--if-s2: 8px;
|
|
54
|
+
--if-s3: 12px;
|
|
55
|
+
--if-s4: 16px;
|
|
56
|
+
--if-s5: 20px;
|
|
57
|
+
--if-s6: 24px;
|
|
58
|
+
--if-s8: 32px;
|
|
59
|
+
--if-r-chip: 6px;
|
|
60
|
+
--if-r-btn: 8px;
|
|
61
|
+
--if-r-row: 12px;
|
|
62
|
+
--if-r-card: 16px;
|
|
63
|
+
--if-r-full: 999px;
|
|
64
|
+
--if-page: 720px;
|
|
65
|
+
--if-page-wide: 1040px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Dark, from the OS (unless Studio or the page says light). Keep this block equal to the next one. */
|
|
69
|
+
@media (prefers-color-scheme: dark) {
|
|
70
|
+
:root:not([data-theme="light"]) {
|
|
71
|
+
color-scheme: dark;
|
|
72
|
+
--if-bg: #141414;
|
|
73
|
+
--if-surface: #1D1D1D;
|
|
74
|
+
--if-float: #262626;
|
|
75
|
+
--if-control: #1A1A1A;
|
|
76
|
+
--if-seg-bg: #1A1A1A;
|
|
77
|
+
--if-seg-on: #303030;
|
|
78
|
+
--if-seg-on-line: rgba(255, 255, 255, .16);
|
|
79
|
+
--if-hover: rgba(255, 255, 255, .075);
|
|
80
|
+
--if-selected: #2A2A2A;
|
|
81
|
+
--if-line: rgba(255, 255, 255, .105);
|
|
82
|
+
--if-line-strong: rgba(255, 255, 255, .13);
|
|
83
|
+
--if-fg: #FBFAF7;
|
|
84
|
+
--if-fg-2: #EBE7E2;
|
|
85
|
+
--if-muted: #B5B0A9;
|
|
86
|
+
--if-subtle: #979189;
|
|
87
|
+
--if-accent: #2A72C6;
|
|
88
|
+
--if-accent-hover: #2161AB;
|
|
89
|
+
--if-accent-press: #1D5596;
|
|
90
|
+
--if-on-accent: #FFFFFF;
|
|
91
|
+
--if-link: #8DC1FF;
|
|
92
|
+
--if-focus: #8DC1FF;
|
|
93
|
+
--if-check: #3794FF;
|
|
94
|
+
--if-field-focus: #7CB0E3;
|
|
95
|
+
--if-btn-bg: #2A2A2A;
|
|
96
|
+
--if-btn-fg: #F6F3EF;
|
|
97
|
+
--if-btn-hover: #333333;
|
|
98
|
+
--if-warn-icon: #E0C49A;
|
|
99
|
+
--if-warn-bg: rgba(224, 196, 154, .10);
|
|
100
|
+
--if-warn-line: rgba(224, 196, 154, .30);
|
|
101
|
+
--if-danger-icon: #FF637E;
|
|
102
|
+
--if-danger-bg: rgba(255, 32, 86, .10);
|
|
103
|
+
--if-danger-line: rgba(255, 32, 86, .30);
|
|
104
|
+
--if-ok: #5EE9B5;
|
|
105
|
+
--if-toast-bg: #2E2E2E;
|
|
106
|
+
--if-toast-fg: #FBFAF7;
|
|
107
|
+
--if-skel: #262626;
|
|
108
|
+
--if-shadow-card: inset 0 1px 0 rgba(255, 255, 255, .045);
|
|
109
|
+
--if-shadow-float: 0 24px 60px -30px rgba(0, 0, 0, .82);
|
|
110
|
+
--if-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B5B0A9' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Dark, from Studio or the page itself (<html data-theme="dark">). */
|
|
115
|
+
:root[data-theme="dark"] {
|
|
116
|
+
color-scheme: dark;
|
|
117
|
+
--if-bg: #141414;
|
|
118
|
+
--if-surface: #1D1D1D;
|
|
119
|
+
--if-float: #262626;
|
|
120
|
+
--if-control: #1A1A1A;
|
|
121
|
+
--if-seg-bg: #1A1A1A;
|
|
122
|
+
--if-seg-on: #303030;
|
|
123
|
+
--if-seg-on-line: rgba(255, 255, 255, .16);
|
|
124
|
+
--if-hover: rgba(255, 255, 255, .075);
|
|
125
|
+
--if-selected: #2A2A2A;
|
|
126
|
+
--if-line: rgba(255, 255, 255, .105);
|
|
127
|
+
--if-line-strong: rgba(255, 255, 255, .13);
|
|
128
|
+
--if-fg: #FBFAF7;
|
|
129
|
+
--if-fg-2: #EBE7E2;
|
|
130
|
+
--if-muted: #B5B0A9;
|
|
131
|
+
--if-subtle: #979189;
|
|
132
|
+
--if-accent: #2A72C6;
|
|
133
|
+
--if-accent-hover: #2161AB;
|
|
134
|
+
--if-accent-press: #1D5596;
|
|
135
|
+
--if-on-accent: #FFFFFF;
|
|
136
|
+
--if-link: #8DC1FF;
|
|
137
|
+
--if-focus: #8DC1FF;
|
|
138
|
+
--if-check: #3794FF;
|
|
139
|
+
--if-field-focus: #7CB0E3;
|
|
140
|
+
--if-btn-bg: #2A2A2A;
|
|
141
|
+
--if-btn-fg: #F6F3EF;
|
|
142
|
+
--if-btn-hover: #333333;
|
|
143
|
+
--if-warn-icon: #E0C49A;
|
|
144
|
+
--if-warn-bg: rgba(224, 196, 154, .10);
|
|
145
|
+
--if-warn-line: rgba(224, 196, 154, .30);
|
|
146
|
+
--if-danger-icon: #FF637E;
|
|
147
|
+
--if-danger-bg: rgba(255, 32, 86, .10);
|
|
148
|
+
--if-danger-line: rgba(255, 32, 86, .30);
|
|
149
|
+
--if-ok: #5EE9B5;
|
|
150
|
+
--if-toast-bg: #2E2E2E;
|
|
151
|
+
--if-toast-fg: #FBFAF7;
|
|
152
|
+
--if-skel: #262626;
|
|
153
|
+
--if-shadow-card: inset 0 1px 0 rgba(255, 255, 255, .045);
|
|
154
|
+
--if-shadow-float: 0 24px 60px -30px rgba(0, 0, 0, .82);
|
|
155
|
+
--if-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B5B0A9' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* ---------- base (zero specificity, so a site's own rules win) ---------- */
|
|
159
|
+
:where(*, *::before, *::after) { box-sizing: border-box; }
|
|
160
|
+
:where(html) { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
|
161
|
+
:where(body) {
|
|
162
|
+
margin: 0;
|
|
163
|
+
background: var(--if-bg);
|
|
164
|
+
color: var(--if-fg-2);
|
|
165
|
+
font: 400 14px/22px var(--if-font);
|
|
166
|
+
-webkit-font-smoothing: antialiased;
|
|
167
|
+
}
|
|
168
|
+
:where(h1, h2, h3, p, ul, ol, dl, dd, figure) { margin: 0; }
|
|
169
|
+
:where(h1, h2, h3) { color: var(--if-fg); }
|
|
170
|
+
:where(h2) { font-size: 16px; line-height: 24px; font-weight: 600; }
|
|
171
|
+
:where(a) { color: var(--if-link); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
|
|
172
|
+
:where(button, input, select, textarea) { font: inherit; color: inherit; }
|
|
173
|
+
:where([hidden]) { display: none !important; }
|
|
174
|
+
:where(svg.icon) { width: 16px; height: 16px; flex: none; vertical-align: -3px; }
|
|
175
|
+
:where(.sr-only) {
|
|
176
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
|
|
177
|
+
clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ---------- page skeleton ---------- */
|
|
181
|
+
.page {
|
|
182
|
+
max-width: var(--if-page);
|
|
183
|
+
margin: 0 auto;
|
|
184
|
+
padding: var(--if-s6) var(--if-s4) var(--if-s8);
|
|
185
|
+
padding-left: max(var(--if-s4), env(safe-area-inset-left));
|
|
186
|
+
padding-right: max(var(--if-s4), env(safe-area-inset-right));
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
gap: var(--if-s6);
|
|
190
|
+
}
|
|
191
|
+
.page.wide { max-width: var(--if-page-wide); }
|
|
192
|
+
.head { display: flex; flex-direction: column; gap: var(--if-s1); }
|
|
193
|
+
.title { font-size: 20px; line-height: 28px; font-weight: 600; color: var(--if-fg); }
|
|
194
|
+
html[data-host-title="shown"] .title {
|
|
195
|
+
position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
|
|
196
|
+
}
|
|
197
|
+
.lede { font-size: 14px; line-height: 22px; color: var(--if-fg-2); }
|
|
198
|
+
.bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--if-s2) var(--if-s3); }
|
|
199
|
+
.bar > .primary:not(:first-child) { margin-left: auto; }
|
|
200
|
+
.bar > .pick-q { flex: 1 1 100%; }
|
|
201
|
+
/* on a phone the one primary button gets its own full-width line, under the sort or filter */
|
|
202
|
+
@media (max-width: 479px) { .bar > .primary { flex: 1 1 100%; margin-left: 0; } }
|
|
203
|
+
.basis { margin-top: var(--if-s1); }
|
|
204
|
+
.stack { display: flex; flex-direction: column; gap: var(--if-s3); }
|
|
205
|
+
.stack.tight { gap: var(--if-s1); }
|
|
206
|
+
.notes { display: flex; flex-direction: column; gap: var(--if-s2); }
|
|
207
|
+
.notes ul { padding-left: var(--if-s4); }
|
|
208
|
+
.name { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--if-fg); }
|
|
209
|
+
.cap { font-size: 12px; line-height: 16px; color: var(--if-muted); }
|
|
210
|
+
.num { font-variant-numeric: tabular-nums; }
|
|
211
|
+
.nowrap { white-space: nowrap; }
|
|
212
|
+
|
|
213
|
+
/* ---------- buttons ---------- */
|
|
214
|
+
.btn {
|
|
215
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--if-s2);
|
|
216
|
+
min-height: 36px; padding: 0 14px;
|
|
217
|
+
border: 0; border-radius: var(--if-r-btn);
|
|
218
|
+
background: var(--if-btn-bg); color: var(--if-btn-fg);
|
|
219
|
+
font: 500 14px/20px var(--if-font); text-decoration: none; white-space: nowrap;
|
|
220
|
+
cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
|
|
221
|
+
transition: color 150ms ease-out, background-color 150ms ease-out, border-color 150ms ease-out, box-shadow 150ms ease-out, transform 150ms ease-out;
|
|
222
|
+
}
|
|
223
|
+
.btn.primary { background: var(--if-accent); color: var(--if-on-accent); }
|
|
224
|
+
.btn.primary:active { background: var(--if-accent-press); transform: translateY(1px) scale(.98); }
|
|
225
|
+
.btn.quiet { background: transparent; color: var(--if-fg-2); }
|
|
226
|
+
/* hover only where a pointer hovers: a tap on a phone never leaves a button in its hover colour */
|
|
227
|
+
@media (hover: hover) {
|
|
228
|
+
.btn:hover { background: var(--if-btn-hover); }
|
|
229
|
+
.btn.primary:hover { background: var(--if-accent-hover); }
|
|
230
|
+
.btn.quiet:hover { background: var(--if-hover); }
|
|
231
|
+
}
|
|
232
|
+
.btn:disabled, .btn[aria-disabled="true"] { opacity: .6; cursor: default; }
|
|
233
|
+
/* busy is not disabled-looking: "Checking…" is the one status people wait for, so it stays readable */
|
|
234
|
+
.btn[aria-busy="true"] { opacity: 1; cursor: progress; }
|
|
235
|
+
.btn[aria-busy="true"]::before {
|
|
236
|
+
content: ""; width: 14px; height: 14px; flex: none; border-radius: 50%;
|
|
237
|
+
border: 1.5px solid currentColor; border-right-color: transparent;
|
|
238
|
+
animation: if-spin .8s linear infinite;
|
|
239
|
+
}
|
|
240
|
+
@keyframes if-spin { to { transform: rotate(360deg); } }
|
|
241
|
+
|
|
242
|
+
/* a text button inside a caption ("2 more over $160 · Show") */
|
|
243
|
+
.link-btn { padding: 0; border: 0; background: none; color: var(--if-link); font: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; cursor: pointer; }
|
|
244
|
+
/* a lone quiet button starts on the text's left edge */
|
|
245
|
+
.actions > .btn.quiet:first-child, .more-btn.btn.quiet:first-child { margin-left: -14px; }
|
|
246
|
+
|
|
247
|
+
/* ---------- trust line: value · source ↗ · age ---------- */
|
|
248
|
+
.trust { font-size: 12px; line-height: 16px; color: var(--if-muted); overflow-wrap: anywhere; }
|
|
249
|
+
.trust .v { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--if-fg); font-variant-numeric: tabular-nums; }
|
|
250
|
+
/* one link pattern (always underlined) at two weights, the map's sheet included: a source in a caption or a "Sources"
|
|
251
|
+
line is a quiet citation (caption colour, a faint underline, the link colour on hover or focus), so trust signals
|
|
252
|
+
never outshout the one primary button; an action ("Open Amazon ↗", "where to check ↗", "Try again") and a link in
|
|
253
|
+
prose are in the link colour */
|
|
254
|
+
.trust a, .cap a, .notes a { color: var(--if-muted); text-decoration: underline; text-decoration-color: var(--if-line-strong); text-decoration-thickness: 1px; text-underline-offset: 2px; }
|
|
255
|
+
.trust a.act, .trust .link-btn, .cap .link-btn { color: var(--if-link); text-decoration-color: currentColor; }
|
|
256
|
+
.trust a:focus-visible, .cap a:focus-visible, .notes a:focus-visible { color: var(--if-link); text-decoration-color: currentColor; }
|
|
257
|
+
@media (hover: hover) {
|
|
258
|
+
.trust a:hover, .cap a:hover, .notes a:hover { color: var(--if-link); text-decoration-color: currentColor; }
|
|
259
|
+
}
|
|
260
|
+
.trust .age, .trust .sp.keep { white-space: nowrap; }
|
|
261
|
+
/* a part the line wrapped before takes its own line, without its dot (site.js measures the drawn lines) */
|
|
262
|
+
.trust .sp.is-wrap, .trust .meta.is-wrap { display: block; }
|
|
263
|
+
.trust .sp.is-wrap > .dot, .trust .meta.is-wrap > .lead { display: none; }
|
|
264
|
+
:where(a) .arr { white-space: nowrap; }
|
|
265
|
+
.trust.is-old .v { color: var(--if-muted); }
|
|
266
|
+
.trust .icon { width: 12px; height: 12px; color: var(--if-danger-icon); vertical-align: -1px; margin-right: 2px; }
|
|
267
|
+
.trust .err { display: block; }
|
|
268
|
+
.table .trust .meta, .facts .trust .meta, .sortv .trust .meta { display: block; }
|
|
269
|
+
/* a phone row's narrow cell: the row's action ("Open Amazon ↗") on its own line, without a dot in front */
|
|
270
|
+
.facts .trust .sp.is-act, .sortv .trust .sp.is-act { display: block; }
|
|
271
|
+
.facts .trust .sp.is-act > .dot, .sortv .trust .sp.is-act > .dot { display: none; }
|
|
272
|
+
.table .trust .lead, .facts .trust .lead, .sortv .trust .lead { display: none; }
|
|
273
|
+
/* a cell's value wraps between words (a long "J-Frame guidance for overpronation" never pushes the last columns out of
|
|
274
|
+
the card); a number keeps its unit on its line through the value's own non-breaking spaces */
|
|
275
|
+
.table .trust .v { white-space: normal; }
|
|
276
|
+
|
|
277
|
+
/* ---------- badge ---------- */
|
|
278
|
+
.badge {
|
|
279
|
+
display: inline-flex; align-items: center; padding: 2px 10px;
|
|
280
|
+
border: 1px solid var(--if-line); border-radius: var(--if-r-full);
|
|
281
|
+
background: var(--if-selected); color: var(--if-fg-2);
|
|
282
|
+
font-size: 12px; line-height: 16px; font-weight: 500; white-space: nowrap; vertical-align: 1px;
|
|
283
|
+
max-width: 100%;
|
|
284
|
+
}
|
|
285
|
+
/* a long badge ("Previous generation (Arahi 8 current)") wraps inside a narrow row instead of pushing the price out */
|
|
286
|
+
.name-line .badge { white-space: normal; }
|
|
287
|
+
|
|
288
|
+
/* ---------- card, facts ---------- */
|
|
289
|
+
.card {
|
|
290
|
+
background: var(--if-surface); border: 1px solid var(--if-line); border-radius: var(--if-r-card);
|
|
291
|
+
padding: var(--if-s4); box-shadow: var(--if-shadow-card);
|
|
292
|
+
display: flex; flex-direction: column; gap: var(--if-s3);
|
|
293
|
+
}
|
|
294
|
+
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--if-s3); }
|
|
295
|
+
.card-head > :first-child { flex: 1 1 auto; min-width: 0; }
|
|
296
|
+
.card-head .sortv { flex: none; max-width: 45%; text-align: right; font-variant-numeric: tabular-nums; }
|
|
297
|
+
.facts { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--if-s2); }
|
|
298
|
+
@media (min-width: 360px) { .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
299
|
+
.facts > div { min-width: 0; }
|
|
300
|
+
.facts dt { font-size: 12px; line-height: 16px; color: var(--if-muted); }
|
|
301
|
+
.facts dd { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--if-fg); }
|
|
302
|
+
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--if-s2); }
|
|
303
|
+
.more { display: flex; flex-direction: column; gap: var(--if-s3); padding-top: var(--if-s3); border-top: 1px solid var(--if-line); }
|
|
304
|
+
|
|
305
|
+
/* ---------- list (one card, rows split by a line) ---------- */
|
|
306
|
+
.list {
|
|
307
|
+
list-style: none; margin: 0; padding: 0;
|
|
308
|
+
background: var(--if-surface); border: 1px solid var(--if-line); border-radius: var(--if-r-card);
|
|
309
|
+
box-shadow: var(--if-shadow-card); overflow: hidden;
|
|
310
|
+
}
|
|
311
|
+
.list > li { padding: var(--if-s3) var(--if-s4); }
|
|
312
|
+
.list > li + li { border-top: 1px solid var(--if-line); }
|
|
313
|
+
.list > section { padding: var(--if-s3) var(--if-s4); }
|
|
314
|
+
.list > section + section { border-top: 1px solid var(--if-line); }
|
|
315
|
+
.list.cards > li { display: flex; flex-direction: column; gap: var(--if-s2); padding: var(--if-s4); }
|
|
316
|
+
|
|
317
|
+
/* ranked */
|
|
318
|
+
.ranked > li.row { display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: var(--if-s3); }
|
|
319
|
+
.rank { font-size: 20px; line-height: 20px; font-weight: 600; color: var(--if-muted); font-variant-numeric: tabular-nums; }
|
|
320
|
+
.row-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
321
|
+
.row-body .why { color: var(--if-fg-2); line-height: 20px; }
|
|
322
|
+
.row-body .more { margin-top: var(--if-s2); }
|
|
323
|
+
.name-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--if-s2); }
|
|
324
|
+
/* ranked: the name and its badge wrap inside .name-wrap; "Details" stays at the end of the first line */
|
|
325
|
+
.name-line:has(> .name-wrap) { flex-wrap: nowrap; align-items: flex-start; }
|
|
326
|
+
.name-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: var(--if-s1) var(--if-s2); min-width: 0; flex: 1; }
|
|
327
|
+
.row-body .more .facts { grid-template-columns: minmax(0, 1fr); }
|
|
328
|
+
/* "Details" sits at the end of the name line; its touch area grows without making the line taller */
|
|
329
|
+
.name-line > .more-btn { margin: -8px -10px -8px auto; min-height: 36px; padding: 0 10px; }
|
|
330
|
+
|
|
331
|
+
/* ---------- table ---------- */
|
|
332
|
+
.table-wrap {
|
|
333
|
+
background: var(--if-surface); border: 1px solid var(--if-line); border-radius: var(--if-r-card);
|
|
334
|
+
box-shadow: var(--if-shadow-card); overflow-x: auto;
|
|
335
|
+
}
|
|
336
|
+
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 14px; line-height: 20px; }
|
|
337
|
+
.table th {
|
|
338
|
+
padding: 10px var(--if-s4); text-align: left; vertical-align: bottom;
|
|
339
|
+
font-size: 12px; line-height: 16px; font-weight: 500; color: var(--if-muted);
|
|
340
|
+
background: var(--if-surface); border-bottom: 1px solid var(--if-line); white-space: nowrap;
|
|
341
|
+
}
|
|
342
|
+
.table td { padding: var(--if-s3) var(--if-s4); vertical-align: top; background: var(--if-surface); color: var(--if-fg); min-width: 112px; max-width: 320px; }
|
|
343
|
+
.table tbody tr + tr td { border-top: 1px solid var(--if-line); }
|
|
344
|
+
/* numbers are tabular but left-aligned, so a value and its caption share one edge */
|
|
345
|
+
.table .num { text-align: left; }
|
|
346
|
+
.table th:first-child, .table td:first-child { position: sticky; left: 0; z-index: 1; min-width: 180px; }
|
|
347
|
+
.table tbody tr:hover td { background: var(--if-hover); }
|
|
348
|
+
.table tbody tr:hover td:first-child { background: color-mix(in oklch, var(--if-surface), var(--if-hover)); }
|
|
349
|
+
.table td.act { text-align: right; white-space: nowrap; width: 1%; }
|
|
350
|
+
.table td.act .actions { justify-content: flex-end; flex-wrap: nowrap; }
|
|
351
|
+
.table tbody tr.more-row td { position: static; padding-top: 0; border-top: 0; }
|
|
352
|
+
|
|
353
|
+
/* selected (a "select" from chat, or ?open=) */
|
|
354
|
+
.is-selected, .table tr.is-selected td, .list > li.is-selected { background: var(--if-selected); }
|
|
355
|
+
.table tbody tr.is-selected:hover td { background: var(--if-selected); }
|
|
356
|
+
.is-highlighted, .table tr.is-highlighted td, .list > li.is-highlighted { background: var(--if-selected); }
|
|
357
|
+
|
|
358
|
+
/* ---------- segmented control (native radios) ---------- */
|
|
359
|
+
.seg {
|
|
360
|
+
display: inline-flex; gap: var(--if-s1); padding: var(--if-s1); margin: 0; border: 0; min-width: 0;
|
|
361
|
+
border-radius: var(--if-r-row); background: var(--if-seg-bg);
|
|
362
|
+
}
|
|
363
|
+
.seg label { position: relative; display: inline-flex; }
|
|
364
|
+
.seg input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
|
|
365
|
+
.seg span {
|
|
366
|
+
display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
|
|
367
|
+
border: 1px solid transparent; border-radius: var(--if-r-btn);
|
|
368
|
+
color: var(--if-muted); font-weight: 500; white-space: nowrap;
|
|
369
|
+
transition: color 150ms ease-out, background-color 150ms ease-out, border-color 150ms ease-out, box-shadow 150ms ease-out;
|
|
370
|
+
}
|
|
371
|
+
.seg label:hover span { color: var(--if-fg); }
|
|
372
|
+
.seg input:checked + span { background: var(--if-seg-on); border-color: var(--if-seg-on-line); color: var(--if-fg); box-shadow: var(--if-shadow-card); }
|
|
373
|
+
.seg input:focus-visible + span { outline: 2px solid var(--if-focus); outline-offset: -2px; }
|
|
374
|
+
|
|
375
|
+
/* ---------- inputs ---------- */
|
|
376
|
+
.field { display: flex; flex-direction: column; gap: var(--if-s2); min-width: 0; }
|
|
377
|
+
.field.inline { flex-direction: row; align-items: center; }
|
|
378
|
+
.field > label, label.label { font-size: 13px; line-height: 18px; font-weight: 500; color: var(--if-fg); }
|
|
379
|
+
.field input, .field select, .field textarea {
|
|
380
|
+
min-height: 38px; padding: 8px 12px;
|
|
381
|
+
background: var(--if-control); color: var(--if-fg);
|
|
382
|
+
border: 1px solid var(--if-line-strong); border-radius: var(--if-r-row);
|
|
383
|
+
font-size: 16px; line-height: 20px;
|
|
384
|
+
transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
|
|
385
|
+
}
|
|
386
|
+
@media (min-width: 640px) { .field input, .field select, .field textarea { font-size: 14px; } }
|
|
387
|
+
.field select { appearance: none; -webkit-appearance: none; padding-right: 36px; background-image: var(--if-chevron); background-repeat: no-repeat; background-position: right 10px center; background-size: 16px; cursor: pointer; }
|
|
388
|
+
.field ::placeholder { color: var(--if-subtle); }
|
|
389
|
+
.field input:focus, .field select:focus, .field textarea:focus {
|
|
390
|
+
outline: none; border-color: var(--if-field-focus);
|
|
391
|
+
box-shadow: 0 0 0 2px color-mix(in oklch, var(--if-field-focus) 40%, transparent);
|
|
392
|
+
}
|
|
393
|
+
/* the keyboard ring is the same strong ring as every other control */
|
|
394
|
+
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--if-focus); outline-offset: 2px; }
|
|
395
|
+
|
|
396
|
+
/* ---------- checkbox rows ---------- */
|
|
397
|
+
.check { display: flex; align-items: flex-start; gap: var(--if-s3); min-height: 44px; padding: var(--if-s3) var(--if-s4); cursor: pointer; }
|
|
398
|
+
.list > li.item { padding: 0; }
|
|
399
|
+
.check input[type="checkbox"] { width: 20px; height: 20px; margin: 0; flex: none; accent-color: var(--if-check); cursor: pointer; }
|
|
400
|
+
.check .text { display: flex; flex-direction: column; gap: 2px; min-width: 0; color: var(--if-fg); }
|
|
401
|
+
.check input:checked ~ .text > .label-text { color: var(--if-muted); text-decoration: line-through; }
|
|
402
|
+
/* a fact without a link is a note inside the row (checklist/app.js); a fact with a link, or an error with "Try
|
|
403
|
+
again", sits under the row, outside its tap target: 2 px under the title with a mouse, clear of the row's 44 px
|
|
404
|
+
target on touch */
|
|
405
|
+
.item:has(> .item-fact, > .err) > .check { padding-bottom: 0; min-height: 0; }
|
|
406
|
+
.item > .err, .item > .item-fact { display: block; padding: 2px var(--if-s4) var(--if-s3) 48px; font-size: 12px; line-height: 16px; }
|
|
407
|
+
.item .item-fact .v { font-size: 12px; line-height: 16px; font-weight: 400; color: var(--if-muted); }
|
|
408
|
+
.err .icon { width: 12px; height: 12px; color: var(--if-danger-icon); vertical-align: -1px; }
|
|
409
|
+
.progress { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--if-fg); }
|
|
410
|
+
.group { display: flex; flex-direction: column; gap: var(--if-s2); }
|
|
411
|
+
.group-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--if-s3); }
|
|
412
|
+
|
|
413
|
+
/* ---------- timeline: one card, a row per day, the day's stops inside its row ---------- */
|
|
414
|
+
.day { display: flex; flex-direction: column; gap: var(--if-s3); }
|
|
415
|
+
.day-head { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
|
|
416
|
+
details.fold { display: flex; flex-direction: column; }
|
|
417
|
+
details.fold > summary {
|
|
418
|
+
display: flex; align-items: center; gap: var(--if-s3); min-height: 44px; cursor: pointer; list-style: none;
|
|
419
|
+
margin: calc(-1 * var(--if-s3)) calc(-1 * var(--if-s4)); padding: var(--if-s3) var(--if-s4);
|
|
420
|
+
}
|
|
421
|
+
details.fold > summary::-webkit-details-marker { display: none; }
|
|
422
|
+
details.fold > summary > .icon { color: var(--if-muted); transform: rotate(90deg); transition: transform 150ms ease-out; }
|
|
423
|
+
details.fold[open] > summary > .icon { transform: rotate(-90deg); }
|
|
424
|
+
details.fold[open] > .stops { margin-top: var(--if-s5); }
|
|
425
|
+
.stops { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--if-s3); }
|
|
426
|
+
.stops > li.stop { display: flex; gap: var(--if-s3); }
|
|
427
|
+
.stops.has-times > li.stop .time { flex: none; width: 48px; font-size: 14px; line-height: 20px; font-weight: 500; color: var(--if-fg); font-variant-numeric: tabular-nums; }
|
|
428
|
+
.stops > li.stop .stop-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
429
|
+
.stops > li.leg { position: relative; margin: calc(-1 * var(--if-s1)) 0; padding: 2px 0 2px var(--if-s4); font-size: 12px; line-height: 16px; color: var(--if-muted); }
|
|
430
|
+
.stops > li.leg::before { content: ""; position: absolute; left: 5px; top: 0; bottom: 0; border-left: 1px solid var(--if-line-strong); }
|
|
431
|
+
.stops.has-times > li.leg { padding-left: 76px; }
|
|
432
|
+
.stops.has-times > li.leg::before { left: 65px; }
|
|
433
|
+
.leg .trust .v { font-size: 12px; line-height: 16px; font-weight: 400; color: var(--if-muted); }
|
|
434
|
+
|
|
435
|
+
/* ---------- notice ---------- */
|
|
436
|
+
.notice {
|
|
437
|
+
display: flex; align-items: flex-start; gap: var(--if-s3);
|
|
438
|
+
padding: var(--if-s3) var(--if-s4); border: 1px solid var(--if-line); border-radius: var(--if-r-row);
|
|
439
|
+
background: var(--if-hover); color: var(--if-fg-2); font-size: 14px; line-height: 22px;
|
|
440
|
+
}
|
|
441
|
+
.notice > .icon { margin-top: 3px; color: var(--if-muted); }
|
|
442
|
+
.notice > .notice-text { flex: 1; min-width: 0; }
|
|
443
|
+
.notice > .btn { margin: -6px -8px -6px 0; }
|
|
444
|
+
.notice.is-warn { background: var(--if-warn-bg); border-color: var(--if-warn-line); }
|
|
445
|
+
.notice.is-warn > .icon { color: var(--if-warn-icon); }
|
|
446
|
+
.notice.is-error { background: var(--if-danger-bg); border-color: var(--if-danger-line); }
|
|
447
|
+
.notice.is-error > .icon { color: var(--if-danger-icon); }
|
|
448
|
+
|
|
449
|
+
/* ---------- toast ---------- */
|
|
450
|
+
.toast {
|
|
451
|
+
position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom) + 16px); transform: translateX(-50%);
|
|
452
|
+
z-index: 50; width: max-content; max-width: min(360px, calc(100vw - 32px));
|
|
453
|
+
padding: 10px 16px; border-radius: var(--if-r-card);
|
|
454
|
+
background: var(--if-toast-bg); color: var(--if-toast-fg); box-shadow: var(--if-shadow-float);
|
|
455
|
+
font-size: 14px; line-height: 20px; pointer-events: none;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* ---------- empty, loading ---------- */
|
|
459
|
+
/* "Ask in chat to add some." only where there is a chat: in Studio */
|
|
460
|
+
.in-studio { display: none; }
|
|
461
|
+
html[data-host-title="shown"] .in-studio { display: inline; }
|
|
462
|
+
.empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--if-s3); padding: var(--if-s4) 0; color: var(--if-fg-2); }
|
|
463
|
+
.skel-rows { display: flex; flex-direction: column; gap: var(--if-s3); }
|
|
464
|
+
.skel { display: block; height: 14px; border-radius: var(--if-r-chip); background: var(--if-skel); animation: if-pulse 1.4s ease-in-out infinite; }
|
|
465
|
+
.skel.w1 { width: 40%; } .skel.w2 { width: 70%; } .skel.w3 { width: 55%; }
|
|
466
|
+
@keyframes if-pulse { 50% { opacity: .55; } }
|
|
467
|
+
|
|
468
|
+
/* ---------- focus ---------- */
|
|
469
|
+
:focus-visible { outline: 2px solid var(--if-focus); outline-offset: 2px; }
|
|
470
|
+
.list > li:focus-visible, .table tr:focus-visible, details.fold > summary:focus-visible { outline: 2px solid var(--if-focus); outline-offset: -2px; }
|
|
471
|
+
.check input:focus-visible { outline-offset: 2px; }
|
|
472
|
+
|
|
473
|
+
/* ---------- touch: every target at least 44 x 44 ---------- */
|
|
474
|
+
@media (pointer: coarse) {
|
|
475
|
+
.btn { min-height: 44px; }
|
|
476
|
+
.name-line > .more-btn { min-height: 44px; margin-top: -12px; margin-bottom: -12px; }
|
|
477
|
+
.seg span { min-height: 36px; }
|
|
478
|
+
.seg label { margin: -4px 0; padding: 4px 0; }
|
|
479
|
+
.field input, .field select, .field textarea { min-height: 44px; }
|
|
480
|
+
.trust a, .cap a, .lede a, .notes a, .facts a, .link-btn {
|
|
481
|
+
display: inline-block; min-width: 44px; padding: 14px 8px; margin: -14px -8px;
|
|
482
|
+
}
|
|
483
|
+
/* a fact with a link under a checkbox row: the link's grown target stays clear of the row's own target (above and
|
|
484
|
+
below); a fact without one keeps the note's 2 px */
|
|
485
|
+
.item:has(> .item-fact, > .err) > .check { min-height: 44px; }
|
|
486
|
+
.item > .item-fact, .item > .err { padding-top: 14px; padding-bottom: 14px; }
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* ---------- motion ---------- */
|
|
490
|
+
@media (prefers-reduced-motion: reduce) {
|
|
491
|
+
*, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
|
|
492
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* Timeline starter: a day plan (itinerary, schedule, project plan). Days in order, stops in order, legs between.
|
|
2
|
+
* You design the content in data.json; keep theme.css and site.js. Restyle only when asked.
|
|
3
|
+
*
|
|
4
|
+
* data.json:
|
|
5
|
+
* title, lede, map? map: the https link of the same trip's map site (the lede then ends in "Map ↗")
|
|
6
|
+
* days: [{n, date: "2026-10-24", place, base?: {name, url}, note? (the day's own note: a warning, a plan B),
|
|
7
|
+
* stops: [{ref: "stop:museum", name, time?: fact, note?, facts?: [fact], sources?: [{name, url}]}],
|
|
8
|
+
* legs: [{from: "<stop ref>", to: "<stop ref>", mode: "walk"|"train"|…, duration: fact}]}]
|
|
9
|
+
* unchecked: ["what was not checked", …]
|
|
10
|
+
* fact: {"v": "10:00", "src": "Opening hours", "url": "https://…", "date": "2026-10-17", "est": false}
|
|
11
|
+
* A time shows only when it has a source; otherwise the order carries the sequence. Five days or fewer are all
|
|
12
|
+
* open; six or more fold, with day 1 (or ?day=<n>) open, and a folded day's caption counts what was not checked.
|
|
13
|
+
* A leg's time is "Estimate" unless it has a source; give an estimate a "url" (a route search) so it ends in "how ↗".
|
|
14
|
+
* A "Not checked" fact with a "url" ends in "where to check ↗". Rows: id="day-<n>", id="stop-<slug>", data-ref.
|
|
15
|
+
* SPDX-License-Identifier: AGPL-3.0-only */
|
|
16
|
+
(function () {
|
|
17
|
+
"use strict";
|
|
18
|
+
var h = site.h;
|
|
19
|
+
var opened = {}; // days this viewer opened or closed: n -> true | false
|
|
20
|
+
var scrolled = false;
|
|
21
|
+
|
|
22
|
+
site.start({
|
|
23
|
+
data: "data.json",
|
|
24
|
+
state: { day: "" },
|
|
25
|
+
select: function (ref, data) {
|
|
26
|
+
var day = (data.days || []).filter(function (d) {
|
|
27
|
+
return "day:" + d.n === ref || (d.stops || []).some(function (s) { return s.ref === ref; });
|
|
28
|
+
})[0];
|
|
29
|
+
if (!day) return null;
|
|
30
|
+
opened[day.n] = true;
|
|
31
|
+
return { day: String(day.n) };
|
|
32
|
+
},
|
|
33
|
+
render: render
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** "25 min walk", "2 h 10 by train". An estimate says "Estimate" in its caption, so no "About" in front. */
|
|
37
|
+
function legText(leg) {
|
|
38
|
+
var v = site.valueText(leg.duration);
|
|
39
|
+
if (!v) return "";
|
|
40
|
+
return v + (leg.mode === "walk" ? " walk" : leg.mode ? " by " + leg.mode : "");
|
|
41
|
+
}
|
|
42
|
+
/** Things on this day that were not checked (facts without a source, legs without a time). */
|
|
43
|
+
function uncheckedCount(day) {
|
|
44
|
+
var n = 0;
|
|
45
|
+
(day.stops || []).forEach(function (s) { (s.facts || []).forEach(function (f) { if (f && !f.src && !f.est) n++; }); });
|
|
46
|
+
(day.legs || []).forEach(function (l) { if (!site.valueText(l.duration)) n++; });
|
|
47
|
+
return n;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function render(data, st) {
|
|
51
|
+
site.head(data);
|
|
52
|
+
var days = data.days || [];
|
|
53
|
+
var fold = days.length >= 6;
|
|
54
|
+
var current = st.day || (days[0] ? String(days[0].n) : "");
|
|
55
|
+
var list = document.getElementById("list");
|
|
56
|
+
list.replaceChildren();
|
|
57
|
+
if (!days.length) {
|
|
58
|
+
list.appendChild(site.empty("No days planned yet.", null, "Ask in chat to add some."));
|
|
59
|
+
} else {
|
|
60
|
+
list.appendChild(h("div", { "class": "list days" }, days.map(function (day) {
|
|
61
|
+
var isOpen = opened[day.n] != null ? opened[day.n] : String(day.n) === current;
|
|
62
|
+
return dayBlock(day, fold, isOpen);
|
|
63
|
+
})));
|
|
64
|
+
}
|
|
65
|
+
var sources = [];
|
|
66
|
+
days.forEach(function (d) {
|
|
67
|
+
(d.stops || []).forEach(function (s) {
|
|
68
|
+
sources = sources.concat(site.sourcesOf([s.time].concat(s.facts || [])));
|
|
69
|
+
(s.sources || []).forEach(function (x) { sources.push(x); });
|
|
70
|
+
});
|
|
71
|
+
(d.legs || []).forEach(function (l) { sources = sources.concat(site.sourcesOf([l.duration])); });
|
|
72
|
+
});
|
|
73
|
+
site.notes(days.length ? data.unchecked : [], sources);
|
|
74
|
+
if (!scrolled) {
|
|
75
|
+
scrolled = true;
|
|
76
|
+
var target = st.day && document.getElementById("day-" + st.day);
|
|
77
|
+
if (target) target.scrollIntoView({ block: "start" });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function dayBlock(day, fold, isOpen) {
|
|
82
|
+
var stops = day.stops || [];
|
|
83
|
+
var title = (day.date ? site.dayDate(day.date) : "Day " + day.n) + (day.place ? " · " + day.place : "");
|
|
84
|
+
var open = uncheckedCount(day);
|
|
85
|
+
var cap = h("p", { "class": "cap" }, stops.length + (stops.length === 1 ? " stop" : " stops"),
|
|
86
|
+
open ? " · " + open + " not checked" : null,
|
|
87
|
+
day.base && day.base.name ? [" · stay: ", site.link(day.base.url, day.base.name)] : null);
|
|
88
|
+
var head = h("div", { "class": "day-head" }, h("h2", { text: title }), cap);
|
|
89
|
+
// the day's own note (a warning, a plan B) above its stops, as the map's plan shows it
|
|
90
|
+
var body = day.note ? h("div", { "class": "stack" }, h("p", { "class": "day-note", text: day.note }), stopList(day)) : stopList(day);
|
|
91
|
+
var section = h("section", { "class": "day", attrs: { id: "day-" + day.n, "data-ref": "day:" + day.n } });
|
|
92
|
+
if (!fold) {
|
|
93
|
+
section.append(head, body);
|
|
94
|
+
return section;
|
|
95
|
+
}
|
|
96
|
+
var details = h("details", { "class": "fold", attrs: { open: isOpen } },
|
|
97
|
+
h("summary", { attrs: { "data-key": "day:" + day.n } }, head, site.icon("chevron")), body);
|
|
98
|
+
details.dataset.shown = isOpen ? "1" : "0";
|
|
99
|
+
details.addEventListener("toggle", function () {
|
|
100
|
+
if ((details.open ? "1" : "0") === details.dataset.shown) return; // our own render, not the viewer
|
|
101
|
+
details.dataset.shown = details.open ? "1" : "0";
|
|
102
|
+
// keep every day as the viewer sees it now, so opening one never folds another away under their finger
|
|
103
|
+
document.querySelectorAll("section.day > details.fold").forEach(function (d) {
|
|
104
|
+
opened[d.parentNode.id.slice(4)] = d.open;
|
|
105
|
+
});
|
|
106
|
+
if (details.open) site.setState({ day: String(day.n) });
|
|
107
|
+
});
|
|
108
|
+
section.appendChild(details);
|
|
109
|
+
return section;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function stopList(day) {
|
|
113
|
+
var stops = day.stops || [];
|
|
114
|
+
var legs = (day.legs || []).slice();
|
|
115
|
+
var timed = stops.some(function (s) { return s.time && s.time.src; });
|
|
116
|
+
var ol = h("ol", { "class": "stops" + (timed ? " has-times" : "") });
|
|
117
|
+
stops.forEach(function (stop) {
|
|
118
|
+
ol.appendChild(stopRow(stop, timed));
|
|
119
|
+
legs.filter(function (l) { return l.from === stop.ref; }).forEach(function (l) { ol.appendChild(legRow(l)); });
|
|
120
|
+
});
|
|
121
|
+
return ol;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function stopRow(stop, timed) {
|
|
125
|
+
var time = stop.time && stop.time.src ? site.valueText(stop.time) : "";
|
|
126
|
+
var facts = (stop.facts || []).map(function (f) { return h("div", null, site.trust(f)); });
|
|
127
|
+
return h("li", { "class": "stop", attrs: site.rowAttrs(stop.ref, "stop") },
|
|
128
|
+
timed ? h("span", { "class": "time", text: time }) : null,
|
|
129
|
+
h("div", { "class": "stop-body" },
|
|
130
|
+
h("span", { "class": "name", text: stop.name }),
|
|
131
|
+
stop.note ? h("p", { "class": "cap", text: stop.note }) : null,
|
|
132
|
+
facts,
|
|
133
|
+
stop.sources && stop.sources.length ? h("p", { "class": "cap" }, stop.sources.map(function (s, i) {
|
|
134
|
+
return [i ? ", " : "", site.link(s.url, s.name)];
|
|
135
|
+
})) : null));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function legRow(leg) {
|
|
139
|
+
var text = legText(leg);
|
|
140
|
+
var fact = Object.assign({}, leg.duration || {}, { v: text });
|
|
141
|
+
return h("li", { "class": "leg" }, text ? site.trust(fact) : h("span", { "class": "trust", text: "Not checked · how long it takes" }));
|
|
142
|
+
}
|
|
143
|
+
})();
|