@bongos/core 1.24.55 → 1.24.56
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/.bongos-core.json +39 -34
- package/docs/copy-inventory.md +16 -16
- package/docs/copy-registry.json +17 -17
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +18 -18
- package/modules/hall-ui/public/approval-queue.css +9 -9
- package/modules/hall-ui/public/atlas.css +5 -1
- package/modules/hall-ui/public/atlas.js +14 -11
- package/modules/hall-ui/public/city-draw.js +14 -14
- package/modules/hall-ui/public/sky-draw.js +24 -6
- package/modules/hall-ui/public/sky-interaction.js +1 -1
- package/modules/hall-ui/public/sky-panel.js +1 -1
- package/modules/hall-ui/public/studio.css +22 -22
- package/modules/hall-ui/public/thinking.css +46 -48
- package/modules/hall-ui/public/tweak-editor.css +18 -18
- package/modules/hall-ui/public/welcome-boot.css +5 -8
- package/modules/hall-ui/public/welcome-boot.js +11 -15
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/hall_catalog_world.mjs +7 -2
- package/tests/hall_city.mjs +5 -4
- package/tests/hall_private_palettes.mjs +148 -0
- package/tests/hall_sky.mjs +29 -32
- package/tests/hall_street.mjs +3 -2
- package/tests/helpers.mjs +25 -1
- package/tests/welcome_boot.mjs +1 -1
- package/tests/welcome_boot_browser.mjs +1 -1
|
@@ -22,39 +22,37 @@
|
|
|
22
22
|
too). Same reason `body` below is qualified: a pack backdrop is written as
|
|
23
23
|
`body{background:…}` and the void must win. */
|
|
24
24
|
body[data-page] .sky-stage {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
/* The sky's palette is the HALL's (task 1005087): the void is the hall's
|
|
26
|
+
ground, the ink ladder is the hall's ink at the template's alphas, and the
|
|
27
|
+
accent is the pack's own, so a re-branded instance's sky wears its colours.
|
|
28
|
+
Dark is the template's look because the hall's dark tier IS the template's
|
|
29
|
+
values (#000 void, #fff ink, #c0c0c0 / #a8a8a8 soft and faint); light
|
|
30
|
+
follows the hall's paper. sky-draw.js reads --ink, --void, --sky-cold,
|
|
31
|
+
--accent and --chart off the stage as colours, so the canvas repaints with
|
|
32
|
+
the toggle and with the pack. */
|
|
33
|
+
--void: var(--bg);
|
|
34
|
+
--ink-dim: color-mix(in srgb, var(--ink-faint) 60%, var(--bg));
|
|
35
|
+
--ink-ghost: color-mix(in srgb, var(--ink) 22%, var(--bg));
|
|
36
|
+
--rule: color-mix(in srgb, var(--ink) 16%, transparent); --rule-soft: color-mix(in srgb, var(--ink) 8%, transparent);
|
|
37
|
+
--glass: color-mix(in srgb, var(--ink) 7.5%, transparent); --glass-strong: color-mix(in srgb, var(--ink) 14%, transparent);
|
|
38
|
+
/* on paper the accent darkens until it reads: the hall's accent-ink does exactly that, and is the accent itself by night */
|
|
39
|
+
--accent: var(--accent-ink); --chart: var(--warn);
|
|
40
|
+
--sky-cold: color-mix(in srgb, var(--ink) 8%, var(--bg)); --sky-drop: rgba(0, 0, 0, .6); --sky-temp: 1;
|
|
41
|
+
--label: 400 11px/1.2 var(--font-mono); --mono: 400 13px/1.4 var(--font-mono);
|
|
42
|
+
--face: var(--font-body);
|
|
33
43
|
}
|
|
34
|
-
/* LIGHT (task 1004500
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
untouched. Written twice for the same reason style.css writes dark twice:
|
|
39
|
-
html[data-theme] once shell.js runs, the media query for first paint.
|
|
40
|
-
sky-draw.js reads these channels (--sky-*-rgb) off the stage, so the canvas
|
|
41
|
-
repaints with the toggle. */
|
|
44
|
+
/* LIGHT (task 1004500): the marks keep their place on paper; only the shadow
|
|
45
|
+
under a card and the canvas's warmth change. Written twice, as style.css
|
|
46
|
+
writes its dark tier: html[data-theme] once shell.js runs, the media query
|
|
47
|
+
for first paint. */
|
|
42
48
|
html[data-theme="light"] body[data-page] .sky-stage {
|
|
43
49
|
color-scheme: light;
|
|
44
|
-
--
|
|
45
|
-
--rule: rgba(23,24,26,.16); --rule-soft: rgba(23,24,26,.08); --glass: rgba(23,24,26,.06); --glass-strong: rgba(23,24,26,.11);
|
|
46
|
-
--accent: #b8501f; --accent-on: #ffffff; --chart: #8f6a0c;
|
|
47
|
-
--sky-ink-rgb: 23,24,26; --sky-void-rgb: 244,243,245; --sky-accent-rgb: 184,80,31; --sky-chart-rgb: 143,106,12;
|
|
48
|
-
--sky-cold: #dcdbde; --sky-cold-rgb: 220,219,222; --sky-drop: rgba(23,24,26,.14); --sky-temp: .42;
|
|
50
|
+
--sky-drop: rgba(0, 0, 0, .14); --sky-temp: .42;
|
|
49
51
|
}
|
|
50
52
|
@media (prefers-color-scheme: light) {
|
|
51
53
|
html:not([data-theme]) body[data-page] .sky-stage {
|
|
52
54
|
color-scheme: light;
|
|
53
|
-
--
|
|
54
|
-
--rule: rgba(23,24,26,.16); --rule-soft: rgba(23,24,26,.08); --glass: rgba(23,24,26,.06); --glass-strong: rgba(23,24,26,.11);
|
|
55
|
-
--accent: #b8501f; --accent-on: #ffffff; --chart: #8f6a0c;
|
|
56
|
-
--sky-ink-rgb: 23,24,26; --sky-void-rgb: 244,243,245; --sky-accent-rgb: 184,80,31; --sky-chart-rgb: 143,106,12;
|
|
57
|
-
--sky-cold: #dcdbde; --sky-cold-rgb: 220,219,222; --sky-drop: rgba(23,24,26,.14); --sky-temp: .42;
|
|
55
|
+
--sky-drop: rgba(0, 0, 0, .14); --sky-temp: .42;
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
html, body { height: 100%; }
|
|
@@ -128,10 +126,10 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
128
126
|
.pill[aria-pressed="true"]::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-right: 8px; vertical-align: 1px; }
|
|
129
127
|
.pill.icon { width: 36px; padding: 0; display: inline-grid; place-items: center; }
|
|
130
128
|
.pill.icon::before { display: none; }
|
|
131
|
-
.spot { outline: 2px solid var(--accent) !important; outline-offset: 4px; box-shadow: 0 0 0 8px
|
|
129
|
+
.spot { outline: 2px solid var(--accent) !important; outline-offset: 4px; box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 12%, transparent), 0 0 40px color-mix(in srgb, var(--accent) 35%, transparent); }
|
|
132
130
|
|
|
133
131
|
.composer { position: fixed; left: 50%; top: calc(76px + env(safe-area-inset-top, 0px)); transform: translateX(-50%); width: min(620px, calc(100% - 32px)); z-index: 6; display: flex; flex-direction: column; gap: 8px; align-items: center; }
|
|
134
|
-
.composer form { display: flex; width: 100%; height: 48px; border-radius: 999px; background:
|
|
132
|
+
.composer form { display: flex; width: 100%; height: 48px; border-radius: 999px; background: color-mix(in srgb, var(--void) 50%, transparent); box-shadow: inset 0 0 0 1px var(--rule); backdrop-filter: blur(6px); align-items: center; padding: 0 6px 0 22px; gap: 8px; }
|
|
135
133
|
.composer form:focus-within { box-shadow: inset 0 0 0 1px var(--ink-faint); }
|
|
136
134
|
.composer input { flex: 1; min-width: 0; height: 100%; background: transparent; border: 0; color: var(--ink); font: 300 17px/1 var(--face); letter-spacing: -.01em; outline: none; }
|
|
137
135
|
.composer input::placeholder { color: var(--ink-dim); }
|
|
@@ -140,12 +138,12 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
140
138
|
.gravity { min-height: 18px; font: var(--mono); font-variant-numeric: tabular-nums; color: var(--ink-dim); text-align: center; }
|
|
141
139
|
.gravity b { font-weight: 400; color: var(--ink-soft); }
|
|
142
140
|
.gravity .hot { color: var(--accent); }
|
|
143
|
-
.receipt { display: flex; align-items: center; gap: 12px; height: 34px; padding: 0 8px 0 16px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--rule-soft); background:
|
|
144
|
-
.receipt .dot { width: 7px; height: 7px; border-radius: 50%; background:
|
|
141
|
+
.receipt { display: flex; align-items: center; gap: 12px; height: 34px; padding: 0 8px 0 16px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--rule-soft); background: color-mix(in srgb, var(--void) 45%, transparent); color: var(--ink-soft); font-size: 13px; }
|
|
142
|
+
.receipt .dot { width: 7px; height: 7px; border-radius: 50%; background: color-mix(in srgb, var(--ink) 50%, var(--void)); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ink) 5%, transparent); }
|
|
145
143
|
.receipt button { border: 0; background: transparent; color: var(--ink-dim); cursor: pointer; height: 26px; padding: 0 8px; border-radius: 999px; font-size: 12px; white-space: nowrap; flex-shrink: 0; }
|
|
146
144
|
.receipt button:hover { color: var(--ink); background: var(--glass); }
|
|
147
145
|
|
|
148
|
-
.glass { background:
|
|
146
|
+
.glass { background: var(--glass); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent), inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 18px 40px var(--sky-drop); backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%); }
|
|
149
147
|
.card { position: fixed; z-index: 7; max-width: 300px; padding: 12px 14px; border-radius: 14px; pointer-events: none; color: var(--ink); font-size: 13.5px; line-height: 1.4; display: flex; flex-direction: column; gap: 5px; opacity: 0; transform: translateY(4px); transition: opacity .12s ease, transform .12s ease; }
|
|
150
148
|
.card.on { opacity: 1; transform: none; }
|
|
151
149
|
.card .k { display: flex; align-items: center; gap: 8px; }
|
|
@@ -159,7 +157,7 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
159
157
|
.menu button { border: 0; background: transparent; color: var(--ink-soft); text-align: left; padding: 8px 10px; border-radius: 8px; font-size: 13.5px; cursor: pointer; }
|
|
160
158
|
.menu button:hover { background: var(--glass-strong); color: var(--ink); }
|
|
161
159
|
|
|
162
|
-
.panel { position: fixed; right: 20px; top: calc(140px + env(safe-area-inset-top, 0px)); bottom: calc(96px + env(safe-area-inset-bottom, 0px)); width: 340px; z-index: 5; display: flex; flex-direction: column; gap: 14px; padding: 22px 24px; border-radius: 16px; background:
|
|
160
|
+
.panel { position: fixed; right: 20px; top: calc(140px + env(safe-area-inset-top, 0px)); bottom: calc(96px + env(safe-area-inset-bottom, 0px)); width: 340px; z-index: 5; display: flex; flex-direction: column; gap: 14px; padding: 22px 24px; border-radius: 16px; background: color-mix(in srgb, var(--void) 62%, transparent); box-shadow: inset 0 0 0 1px var(--rule-soft); backdrop-filter: blur(8px); overflow: auto; }
|
|
163
161
|
.panel .kind { display: flex; align-items: center; gap: 12px; min-height: 28px; }
|
|
164
162
|
.panel .kind .glyph { width: 32px; display: grid; place-items: center; }
|
|
165
163
|
.panel h2 { margin: 0; font-size: 21px; font-weight: 300; line-height: 1.25; letter-spacing: -.02em; text-wrap: balance; }
|
|
@@ -185,7 +183,7 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
185
183
|
.orbit-list .st { font: var(--label); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim); flex-shrink: 0; width: 72px; }
|
|
186
184
|
.orbit-list .st.live { color: var(--accent); }
|
|
187
185
|
|
|
188
|
-
.room { position: fixed; left: 20px; top: calc(140px + env(safe-area-inset-top, 0px)); bottom: calc(96px + env(safe-area-inset-bottom, 0px)); width: 440px; z-index: 5; display: flex; flex-direction: column; gap: 14px; padding: 22px 0 0; border-radius: 16px; background:
|
|
186
|
+
.room { position: fixed; left: 20px; top: calc(140px + env(safe-area-inset-top, 0px)); bottom: calc(96px + env(safe-area-inset-bottom, 0px)); width: 440px; z-index: 5; display: flex; flex-direction: column; gap: 14px; padding: 22px 0 0; border-radius: 16px; background: color-mix(in srgb, var(--void) 62%, transparent); box-shadow: inset 0 0 0 1px var(--rule-soft); backdrop-filter: blur(8px); }
|
|
189
187
|
.room .head { display: flex; align-items: baseline; gap: 14px; padding: 0 24px; }
|
|
190
188
|
.room .head h2 { margin: 0; font-size: 21px; font-weight: 300; letter-spacing: -.02em; }
|
|
191
189
|
.room .head .lbl { margin-left: auto; }
|
|
@@ -203,9 +201,9 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
203
201
|
.empty { padding: 20px 24px; color: var(--ink-dim); font-size: 14px; }
|
|
204
202
|
|
|
205
203
|
.jump { position: fixed; left: 20px; top: calc(76px + env(safe-area-inset-top, 0px)); z-index: 6; }
|
|
206
|
-
.jump input { width: 260px; height: 40px; border-radius: 999px; border: 0; box-shadow: inset 0 0 0 1px var(--rule-soft); background:
|
|
204
|
+
.jump input { width: 260px; height: 40px; border-radius: 999px; border: 0; box-shadow: inset 0 0 0 1px var(--rule-soft); background: color-mix(in srgb, var(--void) 50%, transparent); color: var(--ink); padding: 0 16px; font: 400 14px/1 var(--face); }
|
|
207
205
|
.jump input::placeholder { color: var(--ink-dim); }
|
|
208
|
-
.jump ul { list-style: none; margin: 6px 0 0; padding: 6px; border-radius: 12px; background:
|
|
206
|
+
.jump ul { list-style: none; margin: 6px 0 0; padding: 6px; border-radius: 12px; background: color-mix(in srgb, var(--void) 80%, transparent); box-shadow: inset 0 0 0 1px var(--rule-soft); max-height: 300px; overflow: auto; width: 360px; }
|
|
209
207
|
.jump li button { width: 100%; text-align: left; border: 0; background: transparent; color: var(--ink-soft); padding: 8px 10px; border-radius: 8px; font-size: 13.5px; cursor: pointer; display: flex; gap: 10px; align-items: baseline; }
|
|
210
208
|
.jump li button:hover, .jump li button:focus-visible { background: var(--glass); color: var(--ink); }
|
|
211
209
|
.jump li .lbl { flex-shrink: 0; width: 60px; }
|
|
@@ -213,7 +211,7 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
213
211
|
.tour, .start { position: fixed; left: 50%; bottom: calc(110px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%); width: min(560px, calc(100% - 32px)); z-index: 8; padding: 18px 22px 16px; border-radius: 16px; display: flex; flex-direction: column; gap: 10px; }
|
|
214
212
|
.tour .step { display: flex; align-items: center; gap: 10px; }
|
|
215
213
|
.tour .dots { display: flex; gap: 5px; margin-left: auto; }
|
|
216
|
-
.tour .dots i { width: 5px; height: 5px; border-radius: 50%; background:
|
|
214
|
+
.tour .dots i { width: 5px; height: 5px; border-radius: 50%; background: color-mix(in srgb, var(--ink) 20%, var(--void)); }
|
|
217
215
|
.tour .dots i.on { background: var(--accent); }
|
|
218
216
|
.tour h3, .start h3 { margin: 0; font-size: 19px; font-weight: 300; letter-spacing: -.02em; line-height: 1.25; }
|
|
219
217
|
.tour p, .start p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-soft); }
|
|
@@ -234,7 +232,7 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
234
232
|
.alt { margin-left: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; text-align: right; color: var(--ink-dim); font: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
235
233
|
.alt b { color: var(--ink-soft); font-weight: 400; }
|
|
236
234
|
.hint { color: var(--ink-ghost); }
|
|
237
|
-
.time { display: flex; align-items: center; gap: 8px; width: min(360px, 100%); align-self: center; padding: 3px 4px 3px 4px; border-radius: 999px; background:
|
|
235
|
+
.time { display: flex; align-items: center; gap: 8px; width: min(360px, 100%); align-self: center; padding: 3px 4px 3px 4px; border-radius: 999px; background: color-mix(in srgb, var(--void) 45%, transparent); box-shadow: inset 0 0 0 1px var(--rule-soft); opacity: .8; }
|
|
238
236
|
.time:hover { opacity: 1; }
|
|
239
237
|
.time .play { width: 24px; height: 24px; border-radius: 50%; border: 0; background: var(--glass-strong); color: var(--ink); cursor: pointer; display: grid; place-items: center; font-size: 9px; flex-shrink: 0; }
|
|
240
238
|
.time .play[aria-pressed="true"] { background: var(--accent); color: var(--accent-on); }
|
|
@@ -246,7 +244,7 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
246
244
|
.composer .mode:hover { color: var(--ink); }
|
|
247
245
|
.composer .mode[aria-pressed="true"] { background: var(--glass-strong); color: var(--ink); }
|
|
248
246
|
.composer .mode[aria-pressed="true"]::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-right: 7px; vertical-align: 1px; }
|
|
249
|
-
.steps { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; max-width: 100%; font-size: 13px; color: var(--ink-soft); padding: 5px 8px 5px 14px; border-radius: 999px; background:
|
|
247
|
+
.steps { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; max-width: 100%; font-size: 13px; color: var(--ink-soft); padding: 5px 8px 5px 14px; border-radius: 999px; background: color-mix(in srgb, var(--void) 50%, transparent); box-shadow: inset 0 0 0 1px var(--rule-soft); }
|
|
250
248
|
.steps b { font: var(--label); letter-spacing: .14em; text-transform: uppercase; color: var(--accent); font-weight: 400; }
|
|
251
249
|
.steps .skip { border: 0; background: transparent; color: var(--ink-dim); cursor: pointer; font-size: 12px; height: 24px; padding: 0 8px; border-radius: 999px; }
|
|
252
250
|
.steps .skip:hover { color: var(--ink); background: var(--glass); }
|
|
@@ -255,16 +253,16 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
255
253
|
.mk { display: block; width: 9px; height: 9px; border-radius: 50%; box-sizing: border-box; }
|
|
256
254
|
.mk.star { background: var(--accent); box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 75%, transparent); }
|
|
257
255
|
.mk.star.dim { opacity: .55; box-shadow: 0 0 4px color-mix(in srgb, var(--accent) 40%, transparent); }
|
|
258
|
-
.mk.protostar { width: 7px; height: 7px; background:
|
|
259
|
-
.mk.satellite { width: 6px; height: 6px; background:
|
|
260
|
-
.mk.remnant { width: 12px; height: 12px; background: transparent; border: 1px solid
|
|
261
|
-
.mk.cold { width: 6px; height: 6px; background: var(--sky-cold); border: 1px solid
|
|
262
|
-
.mk.well { width: 14px; height: 14px; background: transparent; border: 1px dashed
|
|
263
|
-
.mk.alive { width: 14px; height: 14px; background: transparent; border: 1px solid
|
|
264
|
-
.mk.settled { width: 12px; height: 12px; background: transparent; border: 1px solid
|
|
265
|
-
.mk.collapsed { width: 9px; height: 9px; background: var(--void); border: 1px solid
|
|
266
|
-
.mk.unseen { width: 12px; height: 12px; background: transparent; border: 1px dashed
|
|
267
|
-
.mk.task { width: 4px; height: 4px; background:
|
|
256
|
+
.mk.protostar { width: 7px; height: 7px; background: color-mix(in srgb, var(--ink) 50%, var(--void)); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ink) 5%, transparent); }
|
|
257
|
+
.mk.satellite { width: 6px; height: 6px; background: color-mix(in srgb, var(--ink) 40%, var(--void)); outline: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); outline-offset: 3px; }
|
|
258
|
+
.mk.remnant { width: 12px; height: 12px; background: transparent; border: 1px solid color-mix(in srgb, var(--ink) 40%, transparent); }
|
|
259
|
+
.mk.cold { width: 6px; height: 6px; background: var(--sky-cold); border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); }
|
|
260
|
+
.mk.well { width: 14px; height: 14px; background: transparent; border: 1px dashed color-mix(in srgb, var(--ink) 35%, transparent); }
|
|
261
|
+
.mk.alive { width: 14px; height: 14px; background: transparent; border: 1px solid color-mix(in srgb, var(--ink) 35%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent), 0 0 10px var(--accent); }
|
|
262
|
+
.mk.settled { width: 12px; height: 12px; background: transparent; border: 1px solid color-mix(in srgb, var(--ink) 50%, transparent); box-shadow: inset 0 0 0 3px var(--void), inset 0 0 0 4px color-mix(in srgb, var(--ink) 35%, transparent), 0 0 8px color-mix(in srgb, var(--ink) 25%, transparent); }
|
|
263
|
+
.mk.collapsed { width: 9px; height: 9px; background: var(--void); border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); box-shadow: 0 0 0 2px var(--void), 0 0 0 3px color-mix(in srgb, var(--ink) 8%, transparent); }
|
|
264
|
+
.mk.unseen { width: 12px; height: 12px; background: transparent; border: 1px dashed color-mix(in srgb, var(--ink) 20%, transparent); }
|
|
265
|
+
.mk.task { width: 4px; height: 4px; background: color-mix(in srgb, var(--ink) 60%, var(--void)); }
|
|
268
266
|
.mk.task.live { background: var(--accent); box-shadow: 0 0 6px var(--accent); }
|
|
269
267
|
.mk.born { width: 10px; height: 10px; background: transparent; border: 1px solid var(--accent); box-shadow: inset 0 0 0 2px var(--void), inset 0 0 0 5px var(--accent); }
|
|
270
268
|
.mk.route { width: 14px; height: 2px; border-radius: 1px; background: var(--chart); box-shadow: 0 0 6px var(--chart); }
|
|
@@ -24,11 +24,11 @@ body[data-page="tweak-editor"] .tw-stage {
|
|
|
24
24
|
--tw-night: #0c1122;
|
|
25
25
|
--tw-moon: #f1e6d2;
|
|
26
26
|
--tw-floor: #0d0d12;
|
|
27
|
-
--tw-lamp:
|
|
28
|
-
--tw-lamp-out:
|
|
27
|
+
--tw-lamp: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
28
|
+
--tw-lamp-out: color-mix(in srgb, var(--accent) 0%, transparent);
|
|
29
29
|
--tw-ink: #ffffff;
|
|
30
|
-
--tw-ink-soft:
|
|
31
|
-
--tw-ink-faint:
|
|
30
|
+
--tw-ink-soft: rgba(255, 255, 255, .75);
|
|
31
|
+
--tw-ink-faint: rgba(255, 255, 255, .66);
|
|
32
32
|
--tw-ink-dim: #8a8a90;
|
|
33
33
|
--tw-glass: rgba(255, 255, 255, .07);
|
|
34
34
|
--tw-bar-back: rgba(7, 8, 13, .84);
|
|
@@ -41,22 +41,22 @@ body[data-page="tweak-editor"] .tw-stage {
|
|
|
41
41
|
--tw-panel-rule: rgba(255, 255, 255, .12);
|
|
42
42
|
--tw-scrim: rgba(4, 5, 9, .88);
|
|
43
43
|
--tw-drop: rgba(0, 0, 0, .55);
|
|
44
|
-
--tw-accent:
|
|
44
|
+
--tw-accent: var(--accent);
|
|
45
45
|
--tw-on-accent: #000000;
|
|
46
|
-
--tw-link: #
|
|
47
|
-
--tw-paper:
|
|
48
|
-
--tw-paper-ink:
|
|
49
|
-
--tw-paper-soft:
|
|
50
|
-
--tw-paper-faint:
|
|
46
|
+
--tw-link: color-mix(in srgb, var(--accent) 55%, #ffffff);
|
|
47
|
+
--tw-paper: var(--bg-card);
|
|
48
|
+
--tw-paper-ink: var(--ink);
|
|
49
|
+
--tw-paper-soft: var(--ink-soft);
|
|
50
|
+
--tw-paper-faint: var(--ink-faint);
|
|
51
51
|
--tw-shared: #4a5bb8;
|
|
52
|
-
--tw-engineer:
|
|
53
|
-
--tw-ok:
|
|
54
|
-
--tw-ok-wash:
|
|
52
|
+
--tw-engineer: var(--accent-deep);
|
|
53
|
+
--tw-ok: var(--ok);
|
|
54
|
+
--tw-ok-wash: var(--ok-weak);
|
|
55
55
|
--tw-on-ok: #ffffff;
|
|
56
|
-
--tw-line-hover:
|
|
57
|
-
--tw-line-focus:
|
|
58
|
-
--tw-face: var(--font-body
|
|
59
|
-
--tw-mono: var(--font-mono
|
|
56
|
+
--tw-line-hover: color-mix(in srgb, var(--ink) 3.5%, transparent);
|
|
57
|
+
--tw-line-focus: color-mix(in srgb, var(--accent) 8%, transparent);
|
|
58
|
+
--tw-face: var(--font-body);
|
|
59
|
+
--tw-mono: var(--font-mono);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/* The stage fills the content box: the hall's content column is a reading
|
|
@@ -399,7 +399,7 @@ html[data-fullscreen="on"] .tw-frame { left: 0; }
|
|
|
399
399
|
display: inline-block;
|
|
400
400
|
padding: 0 6px;
|
|
401
401
|
border-radius: 5px;
|
|
402
|
-
border: 1px solid var(--tw-rule-soft,
|
|
402
|
+
border: 1px solid var(--tw-rule-soft, color-mix(in srgb, var(--ink) 14%, transparent));
|
|
403
403
|
background: var(--tw-line-hover);
|
|
404
404
|
color: var(--tw-paper-soft);
|
|
405
405
|
font-size: .92em;
|
|
@@ -36,10 +36,7 @@ html.wb-open { overflow: hidden; }
|
|
|
36
36
|
10%, 88% { opacity: 1; }
|
|
37
37
|
100% { transform: translate(var(--wb-dx), var(--wb-dy)); opacity: 0; }
|
|
38
38
|
}
|
|
39
|
-
.wb-
|
|
40
|
-
@keyframes wb-beacon { 0%, 60% { opacity: 1; } 61%, 100% { opacity: .15; } }
|
|
41
|
-
.wb-star { animation: wb-twinkle ease-in-out infinite alternate; }
|
|
42
|
-
@keyframes wb-twinkle { from { opacity: .15; } to { opacity: .9; } }
|
|
39
|
+
.wb-star { opacity: .6; }
|
|
43
40
|
.wb-moon { animation: wb-moon 6s ease-in-out infinite alternate; filter: blur(6px); }
|
|
44
41
|
@keyframes wb-moon { from { opacity: .35; } to { opacity: .8; } }
|
|
45
42
|
.wb__cloud { position: absolute; z-index: 2; border-radius: 50%; background: rgba(255, 255, 255, .06); filter: blur(30px); animation: wb-drift linear infinite; pointer-events: none; }
|
|
@@ -75,18 +72,18 @@ html.wb-open { overflow: hidden; }
|
|
|
75
72
|
letter-spacing: -.01em; text-transform: lowercase; white-space: nowrap;
|
|
76
73
|
}
|
|
77
74
|
/* the mark's ink follows the painting: pale by night, dark by day; the moon stays salmon */
|
|
78
|
-
.wb__mark { --mark-ink:
|
|
75
|
+
.wb__mark { --mark-ink: rgba(255, 255, 255, .88); }
|
|
79
76
|
.wb__planet { transform-box: fill-box; transform-origin: center; }
|
|
80
77
|
.wb__moonlet { transform-box: view-box; transform-origin: 100px 100px; }
|
|
81
|
-
.wb--day .wb__mark { --mark-ink:
|
|
78
|
+
.wb--day .wb__mark { --mark-ink: rgba(0, 0, 0, .9); }
|
|
82
79
|
.wb--lit .wb__ring { stroke-dasharray: 100; animation: wb-ring 1.5s cubic-bezier(.6, 0, .2, 1) .5s both; }
|
|
83
|
-
.wb--lit .wb__planet { animation: wb-planet 1s
|
|
80
|
+
.wb--lit .wb__planet { animation: wb-planet 1s var(--ds-ease-arrive) .3s both; }
|
|
84
81
|
.wb--lit .wb__moonlet { animation: wb-orbit 1.6s cubic-bezier(.25, .8, .3, 1) 1.1s both, wb-glow 3.2s ease-in-out 2.7s infinite alternate; }
|
|
85
82
|
.wb--lit .wb__name { animation: wb-name 1.2s ease-out 1.5s both; }
|
|
86
83
|
@keyframes wb-ring { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
|
|
87
84
|
@keyframes wb-planet { from { transform: scale(.35); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
88
85
|
@keyframes wb-orbit { from { transform: rotate(-200deg); opacity: 0; } 25% { opacity: 1; } to { transform: none; opacity: 1; } }
|
|
89
|
-
@keyframes wb-glow { from { filter: drop-shadow(0 0 0
|
|
86
|
+
@keyframes wb-glow { from { filter: drop-shadow(0 0 0 color-mix(in srgb, var(--accent) 0%, transparent)); } to { filter: drop-shadow(0 0 7px color-mix(in srgb, var(--accent) 95%, transparent)); } }
|
|
90
87
|
@keyframes wb-name { from { opacity: 0; letter-spacing: .18em; } to { opacity: 1; letter-spacing: -.01em; } }
|
|
91
88
|
.wb__version { font-family: var(--font-mono); font-size: 12px; color: #a5b4cf; }
|
|
92
89
|
.wb--day .wb__banner { background: rgba(255, 255, 255, .7); border-color: rgba(20, 22, 30, .14); }
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
// ---------- the roles (the owner's mapping) ----------
|
|
75
75
|
// `mode` is the craft mode the pick sets; `href` null means stay on Home.
|
|
76
76
|
var ROLES = [
|
|
77
|
-
{ id: 'owner', label: 'Owner', room: 'City', href: '/city', mode: 'governing', dot: '
|
|
78
|
-
{ id: 'ideator', label: 'Ideator', room: 'Your thinking', href: '/thinking', mode: 'ideating', dot: '
|
|
77
|
+
{ id: 'owner', label: 'Owner', room: 'City', href: '/city', mode: 'governing', dot: 'var(--accent)' },
|
|
78
|
+
{ id: 'ideator', label: 'Ideator', room: 'Your thinking', href: '/thinking', mode: 'ideating', dot: 'var(--warn)' },
|
|
79
79
|
{ id: 'artist', label: 'Artist', room: 'Studio', href: '/studio', mode: 'artistry', dot: '#f472b6' },
|
|
80
80
|
{ id: 'engineer', label: 'Engineer', room: 'Home', href: null, mode: 'building', dot: '#60a5fa' },
|
|
81
81
|
];
|
|
@@ -92,14 +92,13 @@
|
|
|
92
92
|
var TOWERS = [
|
|
93
93
|
{ x: 140, y: 168, w: 62, h: 118, glow: '#60a5fa' },
|
|
94
94
|
{ x: 280, y: 150, w: 58, h: 200, glow: '#22d3ee' },
|
|
95
|
-
{ x: 184, y: 296, w: 100, h: 196, glow: '
|
|
96
|
-
{ x: 636, y: 222, w: 42, h: 140, glow: '#
|
|
97
|
-
{ x: 702, y: 212, w: 84, h: 190, glow: '#
|
|
98
|
-
{ x: 2, y: 196, w: 52, h: 150, glow: '#
|
|
95
|
+
{ x: 184, y: 296, w: 100, h: 196, glow: 'var(--accent)' },
|
|
96
|
+
{ x: 636, y: 222, w: 42, h: 140, glow: 'color-mix(in srgb, var(--sea) 55%, #ffffff)' },
|
|
97
|
+
{ x: 702, y: 212, w: 84, h: 190, glow: 'color-mix(in srgb, var(--sea) 55%, #ffffff)' },
|
|
98
|
+
{ x: 2, y: 196, w: 52, h: 150, glow: 'color-mix(in srgb, var(--sea) 55%, #ffffff)' },
|
|
99
99
|
{ x: 910, y: 410, w: 110, h: 160, glow: '#22d3ee' },
|
|
100
100
|
];
|
|
101
|
-
//
|
|
102
|
-
var BEACONS = [[10, 157], [322, 103], [393, 198], [941, 251]];
|
|
101
|
+
// The hover-car lanes on the right (the masts' blinking lights went in task 1005087).
|
|
103
102
|
var LANES = [
|
|
104
103
|
{ x1: 770, y1: 350, x2: 1040, y2: 362, dur: 5.5 },
|
|
105
104
|
{ x1: 1040, y1: 340, x2: 760, y2: 352, dur: 7 },
|
|
@@ -129,17 +128,14 @@
|
|
|
129
128
|
var rnd = seeded(11);
|
|
130
129
|
var svg = el('svg', { viewBox: '0 0 ' + AW + ' ' + AH, preserveAspectRatio: 'none', 'class': 'wb-motion' }, host);
|
|
131
130
|
if (night) {
|
|
132
|
-
// the moon breathes; stars
|
|
131
|
+
// the moon breathes; the stars stand still (task 1005087: no twinkle, no beacons)
|
|
133
132
|
el('circle', { 'class': 'wb-moon', cx: MOON[0], cy: MOON[1], r: 26, fill: 'rgba(226,232,255,.25)' }, svg);
|
|
134
133
|
for (var i = 0; i < 40; i++) {
|
|
135
134
|
var sx = rnd() * AW, sy = rnd() * 140;
|
|
136
135
|
if (Math.abs(sx - MOON[0]) < 50 && Math.abs(sy - MOON[1]) < 50) continue;
|
|
137
|
-
el('circle', { 'class': 'wb-star', cx: sx.toFixed(1), cy: sy.toFixed(1), r: (0.6 + rnd() * 0.9).toFixed(2), fill: '#e0e7ff',
|
|
138
|
-
|
|
136
|
+
el('circle', { 'class': 'wb-star', cx: sx.toFixed(1), cy: sy.toFixed(1), r: (0.6 + rnd() * 0.9).toFixed(2), fill: '#e0e7ff' }, svg);
|
|
137
|
+
rnd(); rnd(); // the two draws the twinkle timing took, so every star keeps its place
|
|
139
138
|
}
|
|
140
|
-
BEACONS.forEach(function (b, i) {
|
|
141
|
-
el('circle', { 'class': 'wb-beacon', cx: b[0], cy: b[1], r: 3.2, fill: '#ef4444', style: 'animation-delay:' + (-i * 0.45) + 's' }, svg);
|
|
142
|
-
});
|
|
143
139
|
}
|
|
144
140
|
// data pulses climbing the circuit towers (the whole set brightens as the city powers up)
|
|
145
141
|
var pulses = el('g', { 'class': 'wb-pulses' }, svg);
|
|
@@ -392,7 +388,7 @@
|
|
|
392
388
|
}
|
|
393
389
|
|
|
394
390
|
var api = {
|
|
395
|
-
DAY_KEY: DAY_KEY, SIGNIN_KEY: SIGNIN_KEY, ROLES: ROLES, ART: ART, TOWERS: TOWERS,
|
|
391
|
+
DAY_KEY: DAY_KEY, SIGNIN_KEY: SIGNIN_KEY, ROLES: ROLES, ART: ART, TOWERS: TOWERS, LANES: LANES, AW: AW, AH: AH,
|
|
396
392
|
localDay: localDay, firstVisitToday: firstVisitToday, greetingFor: greetingFor, nameOf: nameOf, timeOfDay: timeOfDay,
|
|
397
393
|
artFor: artFor, projectName: projectName, logoHtml: logoHtml, BUILT_ON: BUILT_ON, rolesFor: rolesFor, pulseRuns: pulseRuns,
|
|
398
394
|
onAccess: onAccess, finish: finish, close: close,
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.56",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.56",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.56",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11301,5 +11301,11 @@
|
|
|
11301
11301
|
"id": "1005083",
|
|
11302
11302
|
"text": "There is now a public design-system page at /brand on cloudbongos.com showing every colour, size, corner and shared building block, with light and dark values. It is built automatically from the same files the site uses, so it"
|
|
11303
11303
|
}
|
|
11304
|
+
],
|
|
11305
|
+
"1.24.56": [
|
|
11306
|
+
{
|
|
11307
|
+
"id": "1005087",
|
|
11308
|
+
"text": "The Studio, the thinking sky, the tweak editor, the city, approvals, the atlas and the welcome screen now take their colours and fonts from the project's own branding, so a re-branded project sees them in its own colours; the"
|
|
11309
|
+
}
|
|
11304
11310
|
]
|
|
11305
11311
|
}
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.56'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -168,10 +168,15 @@ test('the shell no longer aliases .mod-chip onto the pill', () => {
|
|
|
168
168
|
|
|
169
169
|
test('atlas: the nine semantic colours stay fixed literals, and nothing else in the sheet is one', () => {
|
|
170
170
|
const css = read('atlas.css');
|
|
171
|
-
for (const t of ['--ab-sun', '--ab-sky', '--ab-coral', '--ab-paper', '--ab-
|
|
171
|
+
for (const t of ['--ab-sun', '--ab-sky', '--ab-coral', '--ab-paper', '--ab-ghost']) {
|
|
172
172
|
assert.match(css, new RegExp(`${t}:#[0-9a-f]{6}`), `${t} is a fixed literal (ADR 0094)`);
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
// task 1005087: the four STATE colours are the hall's status vocabulary — fixed per instance
|
|
175
|
+
// (never the pack's), so ADR 0094's no-scramble invariant still holds, and no longer Tailwind's.
|
|
176
|
+
for (const [t, tok] of [['--ab-good', '--ok'], ['--ab-core', '--warn'], ['--ab-db', '--danger'], ['--ab-danger', '--danger']]) {
|
|
177
|
+
assert.match(css, new RegExp(`${t}:var\\(${tok}\\)`), `${t} is the hall's ${tok}`);
|
|
178
|
+
}
|
|
179
|
+
const rest = decomment(css).replace(/--ab-(sun|sky|coral|paper|ghost):#[0-9a-f]{6}/g, '');
|
|
175
180
|
assert.deepEqual(rest.match(/#[0-9a-f]{3,8}\b/gi) || [], [], 'no other literal colour');
|
|
176
181
|
noAlias(css, 'atlas.css');
|
|
177
182
|
tokensDefined(css, 'atlas.css');
|
package/tests/hall_city.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import path from 'node:path';
|
|
|
25
25
|
import vm from 'node:vm';
|
|
26
26
|
import { createRequire } from 'node:module';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { cityTokens } from './helpers.mjs';
|
|
28
29
|
|
|
29
30
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
30
31
|
const PUB = path.join(ROOT, 'modules/hall-ui/public');
|
|
@@ -66,13 +67,13 @@ test('the nine civic recipes draw exactly the prototype paths, in both modes', (
|
|
|
66
67
|
const a = []; const b = [];
|
|
67
68
|
p.isoInstitution(a, pk, kind, x, y, { fixes: false });
|
|
68
69
|
City.isoInstitution(b, ck, kind, x, y, { fixes: false });
|
|
69
|
-
assert.deepEqual(b, a, `${mode} ${kind}`);
|
|
70
|
+
assert.deepEqual(b, cityTokens(a), `${mode} ${kind}`);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
test('the iso sub-palette is the prototype theme().iso, both modes', () => {
|
|
75
|
-
for (const mode of ['dark', 'light']) assert.deepEqual(City.isoTheme(mode), new Proto({ mode }).theme(mode).iso, mode);
|
|
76
|
+
for (const mode of ['dark', 'light']) assert.deepEqual(City.isoTheme(mode), cityTokens(new Proto({ mode }).theme(mode).iso), mode);
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
test('every work kind the prototype draws is drawn the same way', () => {
|
|
@@ -83,7 +84,7 @@ test('every work kind the prototype draws is drawn the same way', () => {
|
|
|
83
84
|
const a = []; const b = [];
|
|
84
85
|
p.isoWork(a, pk, 6, 2, kind, 58);
|
|
85
86
|
City.isoWork(b, ck, 6, 2, kind, 58);
|
|
86
|
-
assert.deepEqual(b, a, kind);
|
|
87
|
+
assert.deepEqual(b, cityTokens(a), kind);
|
|
87
88
|
}
|
|
88
89
|
});
|
|
89
90
|
|
|
@@ -95,7 +96,7 @@ test('the ground, ring road, lanes and plaza are the prototype home view, path f
|
|
|
95
96
|
const home = p.homeView(T, p.matters(), false);
|
|
96
97
|
const model = City.cityModel({ docket: docket([matter()]), goals: [], rollups: [], mode: 'light' });
|
|
97
98
|
// The prototype's flat list is ground, 4 roads, lanes, plaza, then its 4 plots and their grid.
|
|
98
|
-
assert.deepEqual(model.flat.slice(0, 7), home.flat.slice(0, 7));
|
|
99
|
+
assert.deepEqual(model.flat.slice(0, 7), cityTokens(home.flat.slice(0, 7)));
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
test('with four goals the districts sit on the prototype\'s four plots, in its order', () => {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// tests/hall_private_palettes.mjs — the hall's private-palette rooms mix the shared tokens only
|
|
2
|
+
// (task 1005087, goal 1000138 "One Cloud Bongos design system").
|
|
3
|
+
//
|
|
4
|
+
// The UI audit found Studio, Thinking (the sky), the Tweak editor, City, Approvals, Atlas and the
|
|
5
|
+
// welcome screen each re-typing the salmon and the ink, their own font stacks, and off-the-shelf
|
|
6
|
+
// green / amber / red — so a re-branded instance saw those rooms in Cloud Bongos's colours. This
|
|
7
|
+
// scans every sheet and script of those rooms (comments stripped) and fails on:
|
|
8
|
+
// * a hex, an rgb()/rgba() or a bare [r, g, b] triple equal to one of the pack's colours, the
|
|
9
|
+
// hall's dark-tier inks, the accent's light tint, or an off-the-shelf status green/amber/red;
|
|
10
|
+
// * a font family typed in by name instead of the pack's stacks;
|
|
11
|
+
// * an overshooting (bounce) easing;
|
|
12
|
+
// * the welcome screen's beacon or star twinkle coming back.
|
|
13
|
+
// Black and white (with or without alpha) stay legal: they are scrims, halos and the glass rooms'
|
|
14
|
+
// inks. Each guard was watched go red on the pre-task sheets.
|
|
15
|
+
import { test, after } from 'node:test';
|
|
16
|
+
import assert from 'node:assert/strict';
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
21
|
+
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const kit = require('../modules/ui-design/kit/lib.js');
|
|
24
|
+
|
|
25
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
|
+
const PUB = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
27
|
+
const ROOMS = /^(studio|thinking|sky-|tweak-editor|city|approval-queue|atlas|welcome-boot)/;
|
|
28
|
+
const FILES = fs.readdirSync(PUB).filter((f) => ROOMS.test(f) && /\.(css|js|html)$/.test(f));
|
|
29
|
+
|
|
30
|
+
const strip = (s) => s.replace(/\/\*[\s\S]*?\*\//g, '').replace(/<!--[\s\S]*?-->/g, '').replace(/(^|[^:'"`\\])\/\/[^\n]*/g, '$1');
|
|
31
|
+
|
|
32
|
+
const PACK = Object.values(JSON.parse(fs.readFileSync(path.join(ROOT, 'config', 'branding.neutral.json'), 'utf8')).theme.ui.colors);
|
|
33
|
+
const BRAND = new Set([...PACK,
|
|
34
|
+
// the hall's dark-tier inks and grounds, and the accent's light tint the glass rooms used for links
|
|
35
|
+
'#101012', '#08080a', '#c0c0c0', '#a8a8a8', '#fcbfa4', '#96410f',
|
|
36
|
+
// off-the-shelf status green / amber / red (Tailwind's, and the rooms' own copies of them)
|
|
37
|
+
'#10b981', '#22c55e', '#4ade80', '#16a34a', '#15803d', '#166534', '#1f7a44',
|
|
38
|
+
'#f59e0b', '#fbbf24', '#facc15', '#d97706', '#b45309', '#92400e', '#8f6a0c',
|
|
39
|
+
'#ef4444', '#dc2626', '#b91c1c', '#991b1b', '#f87171', '#bb1f1f',
|
|
40
|
+
].map((h) => h.toLowerCase()));
|
|
41
|
+
const tripleOf = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)).join(',');
|
|
42
|
+
const TRIPLES = new Set([...BRAND].map(tripleOf));
|
|
43
|
+
|
|
44
|
+
function brandLiterals(src) {
|
|
45
|
+
const s = strip(src);
|
|
46
|
+
const hits = [];
|
|
47
|
+
for (const m of s.matchAll(/#[0-9a-f]{6}\b/gi)) if (BRAND.has(m[0].toLowerCase())) hits.push(m[0]);
|
|
48
|
+
for (const m of s.matchAll(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/gi)) if (TRIPLES.has(`${+m[1]},${+m[2]},${+m[3]}`)) hits.push(m[0]);
|
|
49
|
+
for (const m of s.matchAll(/\[\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\]/g)) if (TRIPLES.has(`${+m[1]},${+m[2]},${+m[3]}`)) hits.push(m[0]);
|
|
50
|
+
for (const m of s.matchAll(/--[a-z-]+-rgb\s*:\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/gi)) if (TRIPLES.has(`${+m[1]},${+m[2]},${+m[3]}`)) hits.push(m[0]);
|
|
51
|
+
return hits;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test('the rooms are all here (the scan is not vacuous)', () => {
|
|
55
|
+
for (const f of ['studio.css', 'thinking.css', 'sky-draw.js', 'tweak-editor.css', 'city.css', 'city-draw.js', 'approval-queue.css', 'atlas.css', 'atlas.js', 'welcome-boot.css', 'welcome-boot.js']) {
|
|
56
|
+
assert.ok(FILES.includes(f), `${f} is scanned`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('no hex, rgb or [r, g, b] literal for a brand colour or an off-the-shelf status colour remains', () => {
|
|
61
|
+
const found = {};
|
|
62
|
+
for (const f of FILES) { const h = brandLiterals(fs.readFileSync(path.join(PUB, f), 'utf8')); if (h.length) found[f] = [...new Set(h)]; }
|
|
63
|
+
assert.deepEqual(found, {}, 'mix the shared tokens (the pack\'s, the status vocabulary) instead');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('the scan catches each shape of brand literal, and spares black, white and comments', () => {
|
|
67
|
+
assert.deepEqual(brandLiterals('a { color: #FB9569; }'), ['#FB9569']);
|
|
68
|
+
assert.deepEqual(brandLiterals('a { color: rgba(23, 24, 26, .4); }'), ['rgba(23, 24, 26']);
|
|
69
|
+
assert.deepEqual(brandLiterals('const ACC = [251, 149, 105];'), ['[251, 149, 105]']);
|
|
70
|
+
assert.deepEqual(brandLiterals('--sky-accent-rgb: 251,149,105;'), ['--sky-accent-rgb: 251,149,105']);
|
|
71
|
+
assert.deepEqual(brandLiterals("dot: '#f59e0b'"), ['#f59e0b']);
|
|
72
|
+
assert.deepEqual(brandLiterals('/* was #fb9569 */ a { color: rgba(0, 0, 0, .5); background: #ffffff; }'), []);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('no room types a font family by name: the pack\'s stacks only', () => {
|
|
76
|
+
const hits = [];
|
|
77
|
+
for (const f of FILES.filter((n) => /\.(css|js)$/.test(n))) {
|
|
78
|
+
const s = strip(fs.readFileSync(path.join(PUB, f), 'utf8'));
|
|
79
|
+
if (/Manrope|JetBrains Mono|Segoe UI|Helvetica|Arial\b/.test(s)) hits.push(f);
|
|
80
|
+
}
|
|
81
|
+
assert.deepEqual(hits, [], 'use var(--font-body) / var(--font-display) / var(--font-mono)');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('no bounce: no easing in the rooms overshoots (use --ds-ease / --ds-ease-arrive)', () => {
|
|
85
|
+
const hits = [];
|
|
86
|
+
for (const f of FILES) {
|
|
87
|
+
const s = strip(fs.readFileSync(path.join(PUB, f), 'utf8'));
|
|
88
|
+
for (const m of s.matchAll(/cubic-bezier\(\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\)/g)) {
|
|
89
|
+
if (+m[2] > 1 || +m[4] > 1 || +m[2] < 0 || +m[4] < 0) hits.push(`${f}: ${m[0]}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
assert.deepEqual(hits, []);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('the welcome screen keeps no beacon and no twinkle', () => {
|
|
96
|
+
const css = strip(fs.readFileSync(path.join(PUB, 'welcome-boot.css'), 'utf8'));
|
|
97
|
+
const js = strip(fs.readFileSync(path.join(PUB, 'welcome-boot.js'), 'utf8'));
|
|
98
|
+
assert.doesNotMatch(css, /wb-beacon|wb-twinkle/, 'no beacon blink, no star twinkle in the sheet');
|
|
99
|
+
assert.doesNotMatch(js, /wb-beacon/, 'the script draws no beacon');
|
|
100
|
+
assert.doesNotMatch(js, /\.wb-star[^]*animation-duration/, 'no star carries a twinkle timing');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ── the done-when, in a browser: a re-branded look renders the rooms in its own colours ──
|
|
104
|
+
// The ui-design kit's stub serves the hall under a look from the style library (grove: a
|
|
105
|
+
// sand accent), and every computed colour on each room is checked for the neutral pack's
|
|
106
|
+
// salmon. Skips when the machine has no system Edge or Chrome, as ui_design_kit_fit does.
|
|
107
|
+
let browser = null;
|
|
108
|
+
try { browser = await kit.launchBrowser(); } catch (e) { if (!(e instanceof kit.NoBrowserError)) throw e; }
|
|
109
|
+
after(() => browser && browser.close());
|
|
110
|
+
|
|
111
|
+
test('under the grove look, Thinking, the Tweak editor, Atlas, City and Studio show no salmon in either mode', { skip: !browser && 'no system Edge or Chrome', timeout: 180000 }, async () => {
|
|
112
|
+
const stub = await kit.startStub({ surface: 'hall-ui', prefix: '/builders/', style: 'grove', auth: true });
|
|
113
|
+
try {
|
|
114
|
+
for (const mode of ['dark', 'light']) {
|
|
115
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 860 }, colorScheme: mode });
|
|
116
|
+
await ctx.addInitScript(() => { try { sessionStorage.setItem('hall-craft-asked', '1'); } catch (e) { /* private mode */ } });
|
|
117
|
+
for (const room of ['thinking', 'tweak-editor', 'atlas', 'city', 'studio']) {
|
|
118
|
+
const page = await ctx.newPage();
|
|
119
|
+
await page.goto(`${stub.base}/builders/${room}`, { waitUntil: 'load' });
|
|
120
|
+
await page.waitForTimeout(800);
|
|
121
|
+
const r = await page.evaluate(() => {
|
|
122
|
+
const SALMON = /\b251,\s*149,\s*105\b/;
|
|
123
|
+
const hits = [];
|
|
124
|
+
for (const el of document.querySelectorAll('*')) {
|
|
125
|
+
const cs = getComputedStyle(el);
|
|
126
|
+
for (const k of ['color', 'backgroundColor', 'borderTopColor', 'fill', 'stroke', 'outlineColor', 'boxShadow', 'backgroundImage']) {
|
|
127
|
+
if (SALMON.test(cs[k] || '')) hits.push(`${el.tagName.toLowerCase()} ${k}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { accent: getComputedStyle(document.documentElement).getPropertyValue('--accent').trim().toLowerCase(), hits };
|
|
131
|
+
});
|
|
132
|
+
await page.close();
|
|
133
|
+
assert.notEqual(r.accent, '#fb9569', `${mode} ${room}: the look's accent is applied`);
|
|
134
|
+
assert.deepEqual(r.hits, [], `${mode} ${room}: no salmon left`);
|
|
135
|
+
}
|
|
136
|
+
await ctx.close();
|
|
137
|
+
}
|
|
138
|
+
} finally { stub.stop(); }
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('the sky reads its colours off the stage as tokens, not channels', () => {
|
|
142
|
+
const css = strip(fs.readFileSync(path.join(PUB, 'thinking.css'), 'utf8'));
|
|
143
|
+
assert.doesNotMatch(css, /--sky-[a-z]+-rgb/, 'no rgb channel tokens');
|
|
144
|
+
assert.match(css, /--void:\s*var\(--bg\)/, 'the void is the hall\'s ground');
|
|
145
|
+
assert.match(css, /--accent:\s*var\(--accent-ink\)/, 'the accent is the pack\'s (darkened on paper by the hall\'s own rule)');
|
|
146
|
+
const draw = fs.readFileSync(path.join(PUB, 'sky-draw.js'), 'utf8');
|
|
147
|
+
for (const tok of ['--ink', '--void', '--sky-cold', '--accent', '--chart']) assert.match(draw, new RegExp(`fill\\('${tok}'`), `sky-draw reads ${tok}`);
|
|
148
|
+
});
|