@bongos/core 1.24.48 → 1.24.50
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 +152 -112
- package/config/branding.neutral.json +2 -1
- package/docs/copy-inventory.md +1108 -1108
- package/docs/copy-registry.json +1557 -1557
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +5 -4
- package/docs/page-readings.json +681 -681
- package/modules/hall-ui/public/ai-studio.html +2 -2
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +1 -1
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +5 -5
- package/modules/hall-ui/public/market-watch-item.html +2 -2
- package/modules/hall-ui/public/market-watch-rules.html +2 -2
- package/modules/hall-ui/public/market-watch.html +2 -2
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/money.html +2 -2
- package/modules/hall-ui/public/my-agents.html +2 -2
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +7 -9
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/welcome-boot.css +3 -4
- package/modules/hall-ui/public/welcome-boot.js +5 -3
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/public-landing/public/account.html +1 -5
- package/modules/public-landing/public/assets/cosmos.css +7 -6
- package/modules/public-landing/public/community.html +1 -5
- package/modules/public-landing/public/connections.html +1 -5
- package/modules/public-landing/public/contact.html +1 -5
- package/modules/public-landing/public/guild.html +1 -5
- package/modules/public-landing/public/index.html +5 -8
- package/modules/public-landing/public/invitations.html +1 -5
- package/modules/public-landing/public/privacy.html +1 -5
- package/modules/public-landing/public/projects.html +1 -5
- package/modules/public-landing/public/terms.html +1 -5
- package/modules/raise/views/gate.html +4 -3
- package/modules/raise/views/nda.html +2 -1
- package/modules/raise/views/page.html +4 -3
- package/modules/status-ui/public/explore.html +14 -4
- package/modules/status-ui/public/index.html +26 -126
- package/modules/status-ui/public/index.states.json +3 -3
- package/modules/status-ui/public/status.js +53 -27
- package/modules/status-ui/public/style.css +640 -833
- package/modules/ui-design/public/brand/apple-touch-icon.png +0 -0
- package/modules/ui-design/public/brand/favicon.svg +1 -0
- package/modules/ui-design/public/brand/mark-body.svg +1 -0
- package/modules/ui-design/public/brand/mark-dark.svg +1 -0
- package/modules/ui-design/public/brand/mark-dot.svg +1 -0
- package/modules/ui-design/public/brand/mark-light.svg +1 -0
- package/modules/ui-design/public/brand/mark-mono.svg +1 -0
- package/modules/ui-design/public/brand/mark.svg +21 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/fitness.js +1 -2
- package/scripts/bongos/gen-brand.js +99 -0
- package/scripts/gds/gen-brand.js +7 -0
- package/scripts/status-mirror-sync.js +44 -45
- package/src/bongos/platform-visibility-gate.js +6 -3
- package/src/bongos/prelaunch-gate.js +6 -3
- package/src/bongos/serve-internal.js +9 -7
- package/src/brand-mark.js +96 -0
- package/src/branding.js +2 -0
- package/src/hall-look.js +7 -7
- package/src/module-api.js +1 -1
- package/tests/brand_mark.mjs +195 -0
- package/tests/cloud_bongos_pack.mjs +5 -2
- package/tests/hall_loading.mjs +8 -10
- package/tests/loading_mark.mjs +7 -13
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/raise.mjs +3 -1
- package/tests/status_page_chrome.mjs +195 -0
- package/tests/welcome_boot.mjs +5 -2
- package/modules/raise/public/favicon.svg +0 -1
- package/modules/raise/public/logo-ink.svg +0 -1
- package/modules/raise/public/logo-night.svg +0 -1
- package/modules/status-ui/public/cursors/arrow.png +0 -0
- package/modules/status-ui/public/cursors/hand-pointer.png +0 -0
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><style>svg{--i:#17181a;--d:#fb9569}@media (prefers-color-scheme:dark){svg{--i:#e2e1e4}}</style><path d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="24" stroke-linecap="round" style="stroke:var(--i)"/><circle cx="100" cy="100" r="50" style="fill:var(--i)"/><circle cx="156" cy="46" r="18" style="fill:var(--d)"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" aria-hidden="true"><path d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="15" stroke-linecap="round" style="stroke:#000000"/><circle cx="100" cy="100" r="55" style="fill:#000000"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="logo"><path d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="15" stroke-linecap="round" style="stroke:#e2e1e4"/><circle cx="100" cy="100" r="55" style="fill:#e2e1e4"/><circle cx="156" cy="46" r="12" style="fill:#fb9569"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" aria-hidden="true"><circle cx="156" cy="46" r="12" style="fill:#000000"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="logo"><path d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="15" stroke-linecap="round" style="stroke:#17181a"/><circle cx="100" cy="100" r="55" style="fill:#17181a"/><circle cx="156" cy="46" r="12" style="fill:#fb9569"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="logo"><path d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="15" stroke-linecap="round" style="stroke:#000000"/><circle cx="100" cy="100" r="55" style="fill:#000000"/><circle cx="156" cy="46" r="12" style="fill:#000000"/></svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="mark">
|
|
2
|
+
<!-- THE Cloud Bongos mark: the one source (task 1005088). An open ring, the
|
|
3
|
+
planet in it, and the moon in the gap. Every page draws it from here:
|
|
4
|
+
- inline, by reference: an svg with viewBox 0 0 200 200 holding
|
|
5
|
+
use href="/modules/ui-design/public/brand/mark.svg#mark"
|
|
6
|
+
(the parts are #mark-ring, #mark-disc and #mark-dot, for a page that animates them);
|
|
7
|
+
- coloured by two custom properties on the referencing element: mark-ink (the ring
|
|
8
|
+
and the planet; currentColor when unset) and mark-dot (the moon; the page's
|
|
9
|
+
accent when unset), each written with the two leading dashes in CSS;
|
|
10
|
+
- server-side (the gates, a look's favicon) through src/brand-mark.js;
|
|
11
|
+
- as files (light, dark, mono, the favicon set) written beside this one by
|
|
12
|
+
scripts/bongos/gen-brand.js from the pack's colours. Never edit those by hand.
|
|
13
|
+
Keep the ids and the two custom properties: src/brand-mark.js and
|
|
14
|
+
tests/brand_mark.mjs read them. An XML comment may not hold two dashes in a row,
|
|
15
|
+
or no browser will read this file. -->
|
|
16
|
+
<g id="mark">
|
|
17
|
+
<path id="mark-ring" d="M137 31 A78 78 0 1 0 170 66" pathLength="100" fill="none" stroke-width="15" stroke-linecap="round" style="stroke:var(--mark-ink,currentColor)"/>
|
|
18
|
+
<circle id="mark-disc" cx="100" cy="100" r="55" style="fill:var(--mark-ink,currentColor)"/>
|
|
19
|
+
<circle id="mark-dot" cx="156" cy="46" r="12" style="fill:var(--mark-dot,var(--accent,currentColor))"/>
|
|
20
|
+
</g>
|
|
21
|
+
</svg>
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.50",
|
|
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.50",
|
|
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.50",
|
|
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
|
@@ -11259,5 +11259,17 @@
|
|
|
11259
11259
|
"id": "1005084",
|
|
11260
11260
|
"text": "The builders' hall now has one button and one text box style everywhere instead of eight button sizes and five box styles; main buttons are the full 46px pill with dark text on the orange, and the setup screen's save button is"
|
|
11261
11261
|
}
|
|
11262
|
+
],
|
|
11263
|
+
"1.24.49": [
|
|
11264
|
+
{
|
|
11265
|
+
"id": "1005088",
|
|
11266
|
+
"text": "The Cloud Bongos logo and the browser-tab icon now come from one file, so every page, the hall, the welcome screen, the gates and the fundraising page show exactly the same mark, and phones get a proper home-screen icon."
|
|
11267
|
+
}
|
|
11268
|
+
],
|
|
11269
|
+
"1.24.50": [
|
|
11270
|
+
{
|
|
11271
|
+
"id": "1005089",
|
|
11272
|
+
"text": "The public status page now looks like the rest of Cloud Bongos: same colours, fonts, logo and panels, with a dark mode, and its off-site backup copy is built from the same source so the two always match."
|
|
11273
|
+
}
|
|
11262
11274
|
]
|
|
11263
11275
|
}
|
|
@@ -574,8 +574,7 @@ const KERNEL_FILES = [
|
|
|
574
574
|
// task 1004421 (ADR 0358): branding.js lays the chosen look over the pack through this
|
|
575
575
|
// file — the same identity/config category, requiring only instance-config and node
|
|
576
576
|
// built-ins, so it joins the roster rather than reading as a kernel→domain leak.
|
|
577
|
-
'src/hall-look.js',
|
|
578
|
-
'src/build-info.js',
|
|
577
|
+
'src/hall-look.js', 'src/brand-mark.js', 'src/build-info.js', // brand-mark: the mark's one source (task 1005088), read by hall-look; same imports
|
|
579
578
|
// task 1003476 (goal 1000072): the project's stale-claim timer — host configuration
|
|
580
579
|
// a module reads rather than hardcodes, exactly the category instance-config.js and
|
|
581
580
|
// branding.js are in. The doorway (a KERNEL_FILE) re-exports it, so leaving it off
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// gen-brand.js — the logo and favicon set, written from the one mark (task 1005088).
|
|
4
|
+
//
|
|
5
|
+
// The mark's one source is a shipped module's public/brand/mark.svg (src/brand-mark.js finds
|
|
6
|
+
// it). Pages that can colour it themselves reference it with <use>; everything that cannot —
|
|
7
|
+
// an <img>, a CSS mask, a browser tab, a home-screen icon — gets a fixed-colour file written
|
|
8
|
+
// here, beside the source, in the neutral pack's colours (config/branding.neutral.json):
|
|
9
|
+
//
|
|
10
|
+
// mark-light.svg the mark in Ink with the accent moon (light grounds)
|
|
11
|
+
// mark-dark.svg the mark in the Night ink with the accent moon (dark grounds)
|
|
12
|
+
// mark-mono.svg the whole mark in one ink (black), for a print or a stamp
|
|
13
|
+
// mark-body.svg the ring and planet only, one ink (the loaders' mask shapes:
|
|
14
|
+
// mark-dot.svg the moon only, one ink the ring breathes, the moon turns)
|
|
15
|
+
// favicon.svg the small cut, Ink by day and the Night ink by night
|
|
16
|
+
// apple-touch-icon.png 180x180, the light mark on the pack's ground (--png; needs a browser)
|
|
17
|
+
//
|
|
18
|
+
// Deterministic text, so `--check` is a clean gate (tests/brand_mark.mjs runs it). The PNG is
|
|
19
|
+
// rendered by the system browser and only written with --png.
|
|
20
|
+
//
|
|
21
|
+
// Usage:
|
|
22
|
+
// node scripts/bongos/gen-brand.js # write the SVG variants
|
|
23
|
+
// node scripts/bongos/gen-brand.js --png # … and render apple-touch-icon.png
|
|
24
|
+
// node scripts/bongos/gen-brand.js --check # write nothing; exit 2 if a variant is stale
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
const { NIGHT_INK, readMarkSource, markParts, markFile, faviconSvg } = require('../../src/brand-mark');
|
|
29
|
+
|
|
30
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
31
|
+
|
|
32
|
+
function brandDir() {
|
|
33
|
+
const modulesDir = path.join(ROOT, 'modules');
|
|
34
|
+
const hit = fs.readdirSync(modulesDir).sort()
|
|
35
|
+
.map((m) => path.join(modulesDir, m, 'public', 'brand'))
|
|
36
|
+
.find((d) => fs.existsSync(path.join(d, 'mark.svg')));
|
|
37
|
+
if (!hit) throw new Error('no module ships public/brand/mark.svg');
|
|
38
|
+
return hit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function packColors() {
|
|
42
|
+
const pack = JSON.parse(fs.readFileSync(path.join(ROOT, 'config', 'branding.neutral.json'), 'utf8'));
|
|
43
|
+
return pack.theme.ui.colors;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// { filename: text } for every SVG variant. Pure given the source and the colours.
|
|
47
|
+
function brandFiles(src, colors) {
|
|
48
|
+
const parts = markParts(src);
|
|
49
|
+
if (!parts) throw new Error('mark.svg is missing #mark-ring, #mark-disc or #mark-dot');
|
|
50
|
+
const mono = '#000000';
|
|
51
|
+
return {
|
|
52
|
+
'mark-light.svg': markFile(parts, { ink: colors.ink, dot: colors.accent, label: 'logo' }),
|
|
53
|
+
'mark-dark.svg': markFile(parts, { ink: NIGHT_INK, dot: colors.accent, label: 'logo' }),
|
|
54
|
+
'mark-mono.svg': markFile(parts, { ink: mono, dot: mono, label: 'logo' }),
|
|
55
|
+
'mark-body.svg': markFile(parts, { ink: mono, dot: mono, only: ['ring', 'disc'] }),
|
|
56
|
+
'mark-dot.svg': markFile(parts, { ink: mono, dot: mono, only: ['dot'] }),
|
|
57
|
+
'favicon.svg': faviconSvg({ ink: colors.ink, accent: colors.accent }, src) + '\n',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function renderTouchIcon(dir, colors) {
|
|
62
|
+
const { launchBrowser } = require('../../modules/ui-design/kit/lib'); // eslint-disable-line global-require
|
|
63
|
+
const svg = fs.readFileSync(path.join(dir, 'mark-light.svg'), 'utf8');
|
|
64
|
+
const browser = await launchBrowser();
|
|
65
|
+
try {
|
|
66
|
+
const page = await browser.newPage({ viewport: { width: 180, height: 180 }, deviceScaleFactor: 1 });
|
|
67
|
+
await page.setContent(`<html><body style="margin:0;background:${colors.bg}">` +
|
|
68
|
+
`<img src="data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}" style="display:block;width:128px;height:128px;margin:26px">` +
|
|
69
|
+
'</body></html>');
|
|
70
|
+
await page.waitForLoadState('load');
|
|
71
|
+
await page.screenshot({ path: path.join(dir, 'apple-touch-icon.png'), omitBackground: false });
|
|
72
|
+
} finally { await browser.close(); }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function main() {
|
|
76
|
+
const args = process.argv.slice(2);
|
|
77
|
+
const dir = brandDir();
|
|
78
|
+
const colors = packColors();
|
|
79
|
+
const files = brandFiles(readMarkSource({ root: ROOT }), colors);
|
|
80
|
+
const stale = [];
|
|
81
|
+
for (const [name, text] of Object.entries(files)) {
|
|
82
|
+
const file = path.join(dir, name);
|
|
83
|
+
const cur = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null;
|
|
84
|
+
if (cur === text) continue;
|
|
85
|
+
stale.push(name);
|
|
86
|
+
if (!args.includes('--check')) fs.writeFileSync(file, text);
|
|
87
|
+
}
|
|
88
|
+
if (args.includes('--check')) {
|
|
89
|
+
if (stale.length) { process.stderr.write(`gen-brand: stale — ${stale.join(', ')}. Run: node scripts/bongos/gen-brand.js\n`); process.exit(2); }
|
|
90
|
+
process.stdout.write('gen-brand: the logo and favicon set match mark.svg\n');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (args.includes('--png')) await renderTouchIcon(dir, colors);
|
|
94
|
+
process.stdout.write(`gen-brand: ${stale.length ? `wrote ${stale.join(', ')}` : 'nothing to write'}${args.includes('--png') ? ' + apple-touch-icon.png' : ''}\n`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (require.main === module) main().catch((e) => { process.stderr.write(`${e.message}\n`); process.exit(1); });
|
|
98
|
+
|
|
99
|
+
module.exports = { brandFiles, packColors };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/gds/gen-brand.js — @bongos-forwarder (task 1004848). Generated by
|
|
3
|
+
// scripts/bongos/codemod-rename-scripts-gds.js; do not edit.
|
|
4
|
+
// The real script is scripts/bongos/gen-brand.js. This forwarder keeps callers that still name
|
|
5
|
+
// the old path working, and warns once so they get repointed. Task 1003706 removes it.
|
|
6
|
+
'use strict';
|
|
7
|
+
require('../bongos/forwarder.js').forward(module, 'gen-brand.js');
|
|
@@ -23,15 +23,19 @@
|
|
|
23
23
|
// mirror, and the publish REFUSES rather than shipping a
|
|
24
24
|
// page whose banner points nowhere.
|
|
25
25
|
//
|
|
26
|
-
// DESIGN: status.js + style.css +
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
26
|
+
// DESIGN: status.js + style.css + the shared sheets and brand files the page
|
|
27
|
+
// links (SHARED_ASSETS, at the same paths) are byte-identical between the server
|
|
28
|
+
// copy and the mirror (#737 parameterized the one differing constant via
|
|
29
|
+
// window.STATUS_API_BASE), so they are PURE COPIES. index.html is built from the
|
|
30
|
+
// SAME functions the server serves it with (task 1005089): the server's brand-token
|
|
31
|
+
// stamp (applyBrandTokens) and its theme splice (themeMarkup — the pack's palette,
|
|
32
|
+
// faces, font links and favicon), so the mirror cannot drift from the live page.
|
|
33
|
+
// On top of that it carries the three off-box additions, injected at stable anchors:
|
|
30
34
|
// 1. <script>window.STATUS_API_BASE = <domains.publicOrigin></script> (head)
|
|
31
35
|
// 2. <div id="offbox-status">…</div> (after <body>)
|
|
32
36
|
// 3. the availability-banner driver <script> (reads domains.statusDataUrl) (before status.js)
|
|
33
37
|
// If any anchor is missing the script THROWS — a silent mis-sync is worse than
|
|
34
|
-
// a failed run.
|
|
38
|
+
// a failed run. tests/status_mirror_sync.mjs holds the mirror equal to the live page.
|
|
35
39
|
//
|
|
36
40
|
// USAGE:
|
|
37
41
|
// node scripts/status-mirror-sync.js --repo-dir <path-to-mirror-checkout> [--push]
|
|
@@ -48,35 +52,25 @@ const { execFileSync } = require('node:child_process');
|
|
|
48
52
|
|
|
49
53
|
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
50
54
|
const SRC = path.join(REPO_ROOT, 'modules', 'status-ui', 'public');
|
|
51
|
-
const { branding, clientBranding
|
|
52
|
-
const {
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// the
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
const { branding, clientBranding } = require('../src/branding');
|
|
56
|
+
const { stampHtml } = require('../src/asset-version');
|
|
57
|
+
// The server's own serving transforms: the mirror is built with them, not with a
|
|
58
|
+
// copy of them, so the off-box page and the live page are one page (task 1005089).
|
|
59
|
+
const { applyBrandTokens, themeMarkup } = require('../src/bongos/serve-internal');
|
|
60
|
+
|
|
61
|
+
// The shared files the page links from other modules, published at the SAME paths
|
|
62
|
+
// so the page's root-absolute hrefs resolve on the mirror's host as on the server.
|
|
63
|
+
const SHARED_ASSETS = [
|
|
64
|
+
'modules/ui-design/public/tokens.css',
|
|
65
|
+
'modules/hall-ui/public/controls.css',
|
|
66
|
+
'modules/ui-design/public/brand',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Stamp instance-identity tokens into the mirror HTML: the server's applyBrandTokens
|
|
70
|
+
// (task 1705, ADR 0062 §3), with every origin INJECTED ('' when absent) so nothing is
|
|
71
|
+
// read ambiently and the transform stays pure and testable.
|
|
61
72
|
function brandTokens(html, brand = {}, publicOrigin = '', buildersOrigin = '') {
|
|
62
|
-
|
|
63
|
-
const product = (brand.identity && brand.identity.productName) || '';
|
|
64
|
-
const tagline = (brand.copy && brand.copy.tagline) || '';
|
|
65
|
-
const cur = (brand.currency && brand.currency.label) || 'credits';
|
|
66
|
-
const Cur = cur ? cur.charAt(0).toUpperCase() + cur.slice(1) : cur;
|
|
67
|
-
const company = (brand.identity && brand.identity.company) || {};
|
|
68
|
-
const esc = escapeHtml; // the one server escaper (task 1004876)
|
|
69
|
-
return applyBrandConditionals(html, { companyName: company.name, companySite: company.site, companyTagline: company.tagline })
|
|
70
|
-
.split('{{worldName}}').join(esc(world))
|
|
71
|
-
.split('{{productName}}').join(esc(product))
|
|
72
|
-
.split('{{Currency}}').join(esc(Cur))
|
|
73
|
-
.split('{{currency}}').join(esc(cur))
|
|
74
|
-
.split('{{tagline}}').join(esc(tagline))
|
|
75
|
-
.split('{{companyName}}').join(esc(company.name))
|
|
76
|
-
.split('{{companySite}}').join(esc(company.site))
|
|
77
|
-
.split('{{companyTagline}}').join(esc(company.tagline))
|
|
78
|
-
.split('{{publicOrigin}}').join(esc(publicOrigin || ''))
|
|
79
|
-
.split('{{buildersOrigin}}').join(esc(buildersOrigin || publicOrigin || ''));
|
|
73
|
+
return applyBrandTokens(html, brand || {}, publicOrigin || '', buildersOrigin || publicOrigin || '', '', '', '');
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
// ---- the three off-box index.html injections -------------------------------
|
|
@@ -152,7 +146,9 @@ function buildMirrorIndex({ srcHtml, apiBase, statusDataUrl, brand }) {
|
|
|
152
146
|
if (html.includes('offbox-status')) {
|
|
153
147
|
throw new Error('modules/status-ui/public/index.html already contains offbox markup — the source should be the droplet (no-banner) copy. Aborting.');
|
|
154
148
|
}
|
|
155
|
-
|
|
149
|
+
// The off-box globals, then the pack's theme exactly as the server splices it —
|
|
150
|
+
// last in <head>, after the page's own sheets, so the pack wins here as it does live.
|
|
151
|
+
html = injectOnce(html, '</head>', headInject(apiBase) + brandingInject(brand) + themeMarkup((brand || {}).theme), 'before');
|
|
156
152
|
html = injectOnce(html, '<body>', BANNER_DIV, 'after');
|
|
157
153
|
// Insert the driver right before the main status.js script tag.
|
|
158
154
|
const scriptAnchor = '<script src="/status.js';
|
|
@@ -188,17 +184,11 @@ if (require.main === module) {
|
|
|
188
184
|
fs.copyFileSync(path.join(SRC, rel), path.join(repoDir, rel));
|
|
189
185
|
console.error(` copied ${rel}`);
|
|
190
186
|
};
|
|
191
|
-
const copyDir = (rel) => {
|
|
192
|
-
const from = path.join(SRC, rel), to = path.join(repoDir, rel);
|
|
193
|
-
if (!fs.existsSync(from)) return;
|
|
194
|
-
fs.rmSync(to, { recursive: true, force: true });
|
|
195
|
-
fs.cpSync(from, to, { recursive: true });
|
|
196
|
-
console.error(` copied ${rel}/`);
|
|
197
|
-
};
|
|
198
187
|
|
|
199
188
|
console.error('[status-mirror-sync] building mirror from modules/status-ui/public/ …');
|
|
200
189
|
const indexHtml = buildMirrorIndex({
|
|
201
|
-
|
|
190
|
+
// the content-hash ?v= stamp the server serves the index with (#521)
|
|
191
|
+
srcHtml: stampHtml(fs.readFileSync(path.join(SRC, 'index.html'), 'utf8'), { assetDir: SRC, urlPrefix: '/' }),
|
|
202
192
|
apiBase: b.domains.publicOrigin,
|
|
203
193
|
statusDataUrl: b.domains.statusDataUrl,
|
|
204
194
|
brand: clientBranding(),
|
|
@@ -207,12 +197,21 @@ if (require.main === module) {
|
|
|
207
197
|
console.error(' wrote index.html (with off-box injections)');
|
|
208
198
|
copyFile('status.js');
|
|
209
199
|
copyFile('style.css');
|
|
210
|
-
|
|
200
|
+
// the retired bronze cursors (task 1005089) leave the mirror too
|
|
201
|
+
fs.rmSync(path.join(repoDir, 'cursors'), { recursive: true, force: true });
|
|
202
|
+
for (const rel of SHARED_ASSETS) {
|
|
203
|
+
const from = path.join(REPO_ROOT, rel), to = path.join(repoDir, rel);
|
|
204
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
205
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
206
|
+
fs.cpSync(from, to, { recursive: true });
|
|
207
|
+
console.error(` copied ${rel}`);
|
|
208
|
+
}
|
|
211
209
|
|
|
212
210
|
// ---- commit + (optionally) push -------------------------------------------
|
|
213
211
|
|
|
214
212
|
const git = (args) => execFileSync('git', ['-C', repoDir, ...args], { encoding: 'utf8' });
|
|
215
|
-
git(['add', 'index.html', 'status.js', 'style.css', '
|
|
213
|
+
git(['add', '-A', '--', 'index.html', 'status.js', 'style.css', 'modules']);
|
|
214
|
+
git(['rm', '-r', '-q', '--cached', '--ignore-unmatch', 'cursors']);
|
|
216
215
|
const status = git(['status', '--porcelain']);
|
|
217
216
|
if (!status.trim()) {
|
|
218
217
|
console.error('[status-mirror-sync] mirror already up to date — nothing to publish.');
|
|
@@ -228,4 +227,4 @@ if (require.main === module) {
|
|
|
228
227
|
console.error(`[status-mirror-sync] published. The Pages host rebuilds ${b.domains.statusOrigin || 'the mirror'} in ~1 min.`);
|
|
229
228
|
}
|
|
230
229
|
|
|
231
|
-
module.exports = { buildMirrorIndex, injectOnce, brandTokens };
|
|
230
|
+
module.exports = { buildMirrorIndex, injectOnce, brandTokens, SHARED_ASSETS };
|
|
@@ -179,6 +179,9 @@ function relativeReturn(raw) {
|
|
|
179
179
|
|
|
180
180
|
// The one server escaper (task 1004876); it also escapes ' , which this copy did not.
|
|
181
181
|
const { escapeHtml: escAttr } = require('../html-escape');
|
|
182
|
+
// The mark, inlined from its one source (task 1005088) so the door still loads nothing;
|
|
183
|
+
// coloured by .mark's --mark-ink / --mark-dot below.
|
|
184
|
+
const { markSvg } = require('../brand-mark');
|
|
182
185
|
|
|
183
186
|
// The door. Self-contained on purpose (the pre-launch gate's shape): it is
|
|
184
187
|
// served to someone who may not see the project, so it pulls no stylesheet, no
|
|
@@ -198,8 +201,8 @@ body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;
|
|
|
198
201
|
background:#edecee;color:#17181a}
|
|
199
202
|
@media(prefers-color-scheme:dark){body{background:#000000;color:#ffffff}}
|
|
200
203
|
.box{width:min(380px,100%);text-align:center}
|
|
201
|
-
.mark{display:block;width:48px;height:48px;margin:0 auto 22px}
|
|
202
|
-
@media(prefers-color-scheme:dark){.mark
|
|
204
|
+
.mark{display:block;width:48px;height:48px;margin:0 auto 22px;--mark-ink:#17181a;--mark-dot:#fb9569}
|
|
205
|
+
@media(prefers-color-scheme:dark){.mark{--mark-ink:#e2e1e4}}
|
|
203
206
|
h1{font-size:20px;font-weight:700;margin:0 0 6px}
|
|
204
207
|
p{margin:0 0 20px;opacity:.72;font-size:14px}
|
|
205
208
|
/* dark ink on the accent fill: the chrome world's salmon carries white at 2.19:1 */
|
|
@@ -207,7 +210,7 @@ a.btn{display:inline-block;padding:11px 22px;border-radius:999px;background:#fb9
|
|
|
207
210
|
font:700 14px/1 inherit;text-decoration:none}
|
|
208
211
|
a.btn:focus{outline:2px solid #a3481a;outline-offset:2px}
|
|
209
212
|
</style></head><body><div class="box">
|
|
210
|
-
|
|
213
|
+
${markSvg({ className: 'mark' })}
|
|
211
214
|
<h1>${safe}</h1>
|
|
212
215
|
<p>This project’s pages are open to its members. Sign in to continue.</p>
|
|
213
216
|
<a class="btn" href="${escAttr(signIn)}">Sign in</a>
|
|
@@ -180,6 +180,9 @@ function relativeReturn(raw) {
|
|
|
180
180
|
|
|
181
181
|
// The one server escaper (task 1004876); it also escapes ' , which this copy did not.
|
|
182
182
|
const { escapeHtml: escAttr } = require('../html-escape');
|
|
183
|
+
// The mark, inlined from its one source (task 1005088) so the form still loads nothing;
|
|
184
|
+
// coloured by .mark's --mark-ink / --mark-dot below.
|
|
185
|
+
const { markSvg } = require('../brand-mark');
|
|
183
186
|
|
|
184
187
|
// The form. Self-contained and token-free on purpose: it is served BEFORE the
|
|
185
188
|
// visitor is allowed to see anything, so it must not pull the branding bundle,
|
|
@@ -198,8 +201,8 @@ body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;
|
|
|
198
201
|
background:#edecee;color:#17181a}
|
|
199
202
|
@media(prefers-color-scheme:dark){body{background:#000000;color:#ffffff}}
|
|
200
203
|
.box{width:min(380px,100%);text-align:center}
|
|
201
|
-
.mark{display:block;width:48px;height:48px;margin:0 auto 22px}
|
|
202
|
-
@media(prefers-color-scheme:dark){.mark
|
|
204
|
+
.mark{display:block;width:48px;height:48px;margin:0 auto 22px;--mark-ink:#17181a;--mark-dot:#fb9569}
|
|
205
|
+
@media(prefers-color-scheme:dark){.mark{--mark-ink:#e2e1e4}}
|
|
203
206
|
h1{font-size:20px;font-weight:700;margin:0 0 6px}
|
|
204
207
|
p{margin:0 0 20px;opacity:.72;font-size:14px}
|
|
205
208
|
form{display:flex;gap:8px}
|
|
@@ -213,7 +216,7 @@ button{padding:11px 20px;border:0;border-radius:999px;background:#fb9569;color:#
|
|
|
213
216
|
.err{margin:14px 0 0;color:#b3261e;font-size:13.5px}
|
|
214
217
|
@media(prefers-color-scheme:dark){.err{color:#ffb4a8}}
|
|
215
218
|
</style></head><body><div class="box">
|
|
216
|
-
|
|
219
|
+
${markSvg({ className: 'mark' })}
|
|
217
220
|
<h1>${name}</h1>
|
|
218
221
|
<p>Not open to the world yet. Enter the password to have a look.</p>
|
|
219
222
|
<form method="POST" action="${SUBMIT_PATH}">
|
|
@@ -133,6 +133,7 @@ function themeMarkup(theme) {
|
|
|
133
133
|
// before anything reads the URL, so the neutral pack's inline `data:image/svg+xml,
|
|
134
134
|
// <svg …>` favicon resolves to exactly the same bytes it does today.
|
|
135
135
|
if (theme.favicon) out += `<link rel="icon" href="${esc(theme.favicon)}">`;
|
|
136
|
+
if (theme.appleTouchIcon) out += `<link rel="apple-touch-icon" href="${esc(theme.appleTouchIcon)}">`;
|
|
136
137
|
return out;
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -834,15 +835,16 @@ function mountInternalSurfaces(app) {
|
|
|
834
835
|
const surface = surfaceOf(req);
|
|
835
836
|
if (!surface) return next();
|
|
836
837
|
// /api/* already handled above; everything else on a surface host serves from
|
|
837
|
-
// the module's static dir.
|
|
838
|
-
//
|
|
839
|
-
// static handler.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
838
|
+
// the module's static dir. Each page ("/" is the index, "/explore[.html]" its
|
|
839
|
+
// sibling; task 1005089) comes from the stamped + branded in-memory copy (asset
|
|
840
|
+
// ?v= = content hash); everything else falls through to the on-disk static handler.
|
|
841
|
+
// The map's keys are fixed at boot (the dir's own .html files), so a slug can only name a shipped page.
|
|
842
|
+
const pm = /^\/(?:([a-z0-9-]+)(?:\.html)?)?$/i.exec(req.path);
|
|
843
|
+
const page = pm && `${pm[1] || 'index'}.html`;
|
|
844
|
+
if ((req.method === 'GET' || req.method === 'HEAD') && page && surface.html[page]) {
|
|
843
845
|
res.set('Cache-Control', 'public, max-age=60');
|
|
844
846
|
res.type('html');
|
|
845
|
-
return res.send(surface.html[
|
|
847
|
+
return res.send(surface.html[page]);
|
|
846
848
|
}
|
|
847
849
|
return surface.static(req, res, next);
|
|
848
850
|
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/brand-mark.js — the Bongos mark for the server, read from its one source (task 1005088).
|
|
2
|
+
//
|
|
3
|
+
// The ring-and-dot mark lives in exactly one file, a shipped module's public/brand/mark.svg.
|
|
4
|
+
// A browser page draws it by reference (<use href="…/brand/mark.svg#mark">); the server needs
|
|
5
|
+
// the markup itself in two places that load nothing — the pre-launch and members-only gates,
|
|
6
|
+
// which are self-contained on purpose — and in a looked hall's favicon, drawn in the look's own
|
|
7
|
+
// colours. This file reads that source and hands those out, and scripts/bongos/gen-brand.js
|
|
8
|
+
// writes the static variants (light, dark, mono, the favicon) from the same functions.
|
|
9
|
+
//
|
|
10
|
+
// The source is found by its SHAPE — any shipped module's public/brand/mark.svg — never by a
|
|
11
|
+
// module key, which core may not name (ADR 0083 §Decision #4), as src/hall-look.js finds the
|
|
12
|
+
// style library. PURE given the source text; the default reader is the only I/O.
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
|
|
18
|
+
// The Night ink: the chrome world's ink on a dark ground, which every dark twin of the mark uses.
|
|
19
|
+
const NIGHT_INK = '#e2e1e4';
|
|
20
|
+
// The two custom properties the source colours itself by, exactly as written there.
|
|
21
|
+
const INK_VAR = 'var(--mark-ink,currentColor)';
|
|
22
|
+
const DOT_VAR = 'var(--mark-dot,var(--accent,currentColor))';
|
|
23
|
+
// The favicon cut: at 16px the display weights blur, so the tab icon draws a heavier ring, a
|
|
24
|
+
// slightly smaller planet and a larger moon. Same geometry, other weights.
|
|
25
|
+
const SMALL_CUT = { ring: { 'stroke-width': '24' }, disc: { r: '50' }, dot: { r: '18' } };
|
|
26
|
+
|
|
27
|
+
let _source = null;
|
|
28
|
+
function findSource(root) {
|
|
29
|
+
const base = root || require('./instance-config').resolveCoreRoot(); // eslint-disable-line global-require
|
|
30
|
+
const modulesDir = path.join(base, 'modules');
|
|
31
|
+
return fs.readdirSync(modulesDir).sort()
|
|
32
|
+
.map((m) => path.join(modulesDir, m, 'public', 'brand', 'mark.svg'))
|
|
33
|
+
.find((f) => fs.existsSync(f)) || null;
|
|
34
|
+
}
|
|
35
|
+
// The source text, read once. null when no shipped module carries it.
|
|
36
|
+
function readMarkSource({ root } = {}) {
|
|
37
|
+
if (_source !== null && !root) return _source;
|
|
38
|
+
let text = '';
|
|
39
|
+
try { const file = findSource(root); text = file ? fs.readFileSync(file, 'utf8') : ''; } catch { text = ''; }
|
|
40
|
+
if (!root) _source = text;
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The three parts as the source writes them, ids dropped (an inline copy must not collide
|
|
45
|
+
// with the source's ids on a page). null when the source is missing or misshapen.
|
|
46
|
+
function markParts(src = readMarkSource()) {
|
|
47
|
+
const pick = (id) => {
|
|
48
|
+
const m = String(src || '').match(new RegExp(`<(path|circle)\\s+id="mark-${id}"([^>]*?)\\s*/>`));
|
|
49
|
+
return m ? `<${m[1]}${m[2]}/>` : null;
|
|
50
|
+
};
|
|
51
|
+
const parts = { ring: pick('ring'), disc: pick('disc'), dot: pick('dot') };
|
|
52
|
+
return parts.ring && parts.disc && parts.dot ? parts : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setAttrs(el, attrs) {
|
|
56
|
+
let out = el;
|
|
57
|
+
for (const [k, v] of Object.entries(attrs || {})) out = out.replace(new RegExp(`\\s${k}="[^"]*"`), ` ${k}="${v}"`);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The parts' markup with the two colours set: each a CSS value (a hex, or a var()).
|
|
62
|
+
function partsMarkup(parts, { ink = INK_VAR, dot = DOT_VAR, cut = null, only = null } = {}) {
|
|
63
|
+
const names = only || ['ring', 'disc', 'dot'];
|
|
64
|
+
return names.map((n) => setAttrs(parts[n], cut && cut[n]).split(INK_VAR).join(ink).split(DOT_VAR).join(dot)).join('');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The mark as inline markup for a server-rendered page. Coloured by --mark-ink / --mark-dot on
|
|
68
|
+
// the element (or its ancestors), exactly as a page's <use> reference is.
|
|
69
|
+
function markSvg({ className = 'mark', src } = {}) {
|
|
70
|
+
const parts = markParts(src === undefined ? readMarkSource() : src);
|
|
71
|
+
if (!parts) return '';
|
|
72
|
+
return `<svg class="${className}" viewBox="0 0 200 200" aria-hidden="true">${partsMarkup(parts)}</svg>`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// A standalone SVG file of the mark in fixed colours (the light, dark and mono logos, and the
|
|
76
|
+
// two mask shapes the loaders cut from it).
|
|
77
|
+
function markFile(parts, { ink, dot, only, label = '' } = {}) {
|
|
78
|
+
const a11y = label ? ` role="img" aria-label="${label}"` : ' aria-hidden="true"';
|
|
79
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"${a11y}>${partsMarkup(parts, { ink, dot, only })}</svg>\n`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The favicon: the small cut, in the pack's ink by day and the Night ink by night.
|
|
83
|
+
function faviconSvg({ ink, accent, night = NIGHT_INK }, src = readMarkSource()) {
|
|
84
|
+
const parts = markParts(src);
|
|
85
|
+
if (!parts) return '';
|
|
86
|
+
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">' +
|
|
87
|
+
`<style>svg{--i:${ink};--d:${accent}}@media (prefers-color-scheme:dark){svg{--i:${night}}}</style>` +
|
|
88
|
+
partsMarkup(parts, { ink: 'var(--i)', dot: 'var(--d)', cut: SMALL_CUT }) + '</svg>';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// An SVG as a data: URI for a <link rel="icon"> (single quotes, # escaped), or '' for ''.
|
|
92
|
+
function svgDataUri(svg) {
|
|
93
|
+
return svg ? 'data:image/svg+xml;utf8,' + svg.replace(/"/g, "'").replace(/#/g, '%23') : '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { NIGHT_INK, readMarkSource, markParts, markSvg, markFile, faviconSvg, svgDataUri };
|
package/src/branding.js
CHANGED
|
@@ -268,6 +268,8 @@ function clientBranding(b = branding()) {
|
|
|
268
268
|
links: Array.isArray(ui.fonts?.links) ? ui.fonts.links : [],
|
|
269
269
|
},
|
|
270
270
|
favicon: ui.favicon || '',
|
|
271
|
+
// the home-screen icon (a 180x180 PNG; task 1005088), '' when the pack names none
|
|
272
|
+
appleTouchIcon: ui.appleTouchIcon || '',
|
|
271
273
|
backdrop: ui.backdrop || '',
|
|
272
274
|
// The look (ADR 0358): which one this hall wears ('' = its own pack), whether
|
|
273
275
|
// its night is tinted (only ever true with a look), and its logo address.
|
package/src/hall-look.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
const fs = require('node:fs');
|
|
25
25
|
const path = require('node:path');
|
|
26
|
+
const { faviconSvg, svgDataUri } = require('./brand-mark');
|
|
26
27
|
|
|
27
28
|
const HEX = /^#[0-9a-f]{6}$/i;
|
|
28
29
|
const LOOK_NAME = /^[a-z][a-z-]{0,39}$/;
|
|
@@ -53,13 +54,10 @@ function readLibraryLook(name, { root } = {}) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
// The favicon a looked hall shows without a logo: the Bongos mark (task 1004846) in the
|
|
56
|
-
// look's own ink with its accent as the moon, and the Night ink twin in dark mode.
|
|
57
|
+
// look's own ink with its accent as the moon, and the Night ink twin in dark mode. Drawn from
|
|
58
|
+
// the mark's one source (task 1005088); the colours are the look's validated hex.
|
|
57
59
|
function lookFavicon(colors) {
|
|
58
|
-
|
|
59
|
-
return "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'>" +
|
|
60
|
-
`<style>.r{stroke:${c('ink')}}.p{fill:${c('ink')}}@media (prefers-color-scheme:dark){.r{stroke:%23e2e1e4}.p{fill:%23e2e1e4}}</style>` +
|
|
61
|
-
"<path class='r' d='M137 31A78 78 0 1 0 170 66' fill='none' stroke-width='24' stroke-linecap='round'/>" +
|
|
62
|
-
`<circle class='p' cx='100' cy='100' r='50'/><circle cx='156' cy='46' r='18' fill='${c('accent')}'/></svg>`;
|
|
60
|
+
return svgDataUri(faviconSvg({ ink: colors.ink, accent: colors.accent }));
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
// What the hall wears: { name, ui } or null for its own pack. PURE given readLook.
|
|
@@ -91,8 +89,10 @@ function applyLook(resolved, readLook = readLibraryLook) {
|
|
|
91
89
|
ui.look = look.name;
|
|
92
90
|
ui.nightTint = project.nightTint !== 'off';
|
|
93
91
|
ui.favicon = lookFavicon(ui.colors);
|
|
92
|
+
// the home-screen icon is the pack's own colours; a hall in other colours goes without
|
|
93
|
+
ui.appleTouchIcon = '';
|
|
94
94
|
}
|
|
95
|
-
if (logo) { ui.logo = logo; ui.favicon = logo; }
|
|
95
|
+
if (logo) { ui.logo = logo; ui.favicon = logo; ui.appleTouchIcon = ''; }
|
|
96
96
|
resolved.theme.ui = ui;
|
|
97
97
|
return resolved;
|
|
98
98
|
}
|
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.50'; // 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');
|