@bongos/core 1.24.30 → 1.24.31
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 +153 -103
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/api/openapi.json +139 -1
- package/docs/api-reference.md +7 -3
- package/docs/copy-inventory.md +192 -187
- package/docs/copy-registry.json +530 -458
- package/docs/module-api-changelog.md +2 -2
- package/docs/page-inventory.json +15 -35
- package/docs/page-readings.json +226 -263
- package/modules/copy-desk/module.json +1 -1
- package/modules/copy-desk/routes/copy-desk-studio.js +103 -0
- package/modules/copy-desk/tests/copy_no_cms.mjs +2 -2
- package/modules/hall-ui/public/ai-studio.html +1 -1
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/craft-mode.js +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/hall-render.js +62 -86
- package/modules/hall-ui/public/home-city-draw.js +360 -52
- package/modules/hall-ui/public/home-city.css +47 -11
- package/modules/hall-ui/public/home-city.js +156 -40
- package/modules/hall-ui/public/home-gallery.css +129 -0
- package/modules/hall-ui/public/home-gallery.js +349 -0
- package/modules/hall-ui/public/home-studio.css +72 -0
- package/modules/hall-ui/public/home-studio.js +203 -0
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +4 -1
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-settings.js +6 -1
- package/modules/hall-ui/public/market-watch.html +141 -54
- package/modules/hall-ui/public/market-watch.js +54 -0
- package/modules/hall-ui/public/market-watch.states.json +2 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +2 -3
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +9 -1
- package/modules/hall-ui/public/style.css +74 -27
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/home-city.md +123 -0
- package/modules/hall-ui/records/loader.md +1 -1
- package/modules/hall-ui/records/market-watch-settings.md +16 -3
- package/modules/hall-ui/records/market-watch.md +3 -1
- package/modules/lifecycle/home-city.js +58 -10
- package/modules/lifecycle/home-gallery.js +320 -0
- package/modules/lifecycle/migrations/lifecycle_020_work_appreciations.sql +36 -0
- package/modules/lifecycle/module.json +1 -0
- package/modules/lifecycle/routes/home-gallery.js +37 -0
- package/modules/market-watch/routes/settings.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +29 -1
- package/scripts/bongos/leak-scan-allowlist.js +8 -0
- package/scripts/hall-preview/README.md +1 -0
- package/scripts/hall-preview/server.js +10 -0
- package/src/bongos/serve-internal.js +17 -3
- package/src/module-api.js +1 -1
- package/tests/hall_builders_page_pass.mjs +18 -27
- package/tests/hall_loading.mjs +1 -1
- package/tests/hall_nav.mjs +11 -4
- package/tests/hall_page_gate_map.mjs +7 -4
- package/tests/home_city.mjs +200 -5
- package/tests/home_city_widget.mjs +167 -27
- package/tests/home_gallery.mjs +255 -0
- package/tests/home_gallery_widget.mjs +220 -0
- package/tests/home_studio.mjs +169 -0
- package/tests/home_studio_widget.mjs +157 -0
- package/tests/market_watch_pages.mjs +162 -0
- package/tests/nav_permission_atoms.mjs +2 -2
- package/modules/hall-ui/public/market-watch-settings.html +0 -115
- package/modules/hall-ui/public/market-watch-settings.states.json +0 -14
|
@@ -439,3 +439,165 @@ test('MARKET WATCH OFF: both pages show the quiet off line, not an error, and as
|
|
|
439
439
|
assert.equal(p.els['mw-fail'].hidden, true);
|
|
440
440
|
}
|
|
441
441
|
});
|
|
442
|
+
|
|
443
|
+
// ---- the owner's Settings tab (task 1005045) ---------------------------------------
|
|
444
|
+
//
|
|
445
|
+
// Owner decision 2026-11-03 (idea 1001310): one sidebar item. The settings that had their
|
|
446
|
+
// own page at /market-watch-settings are a Settings tab on this page, shown to the owner
|
|
447
|
+
// (archon, cosmetic — the four settings routes keep their own wall, ADR 0016) and to
|
|
448
|
+
// nobody else. These boot the real market-watch.js with the kit and the settings script
|
|
449
|
+
// stood in, so what is asserted is which tabs the page builds for which viewer.
|
|
450
|
+
|
|
451
|
+
function bootDesk({ rank, homeStatus = 200, modules = { enabled: { 'market-watch': true } }, meStatus = 200, shared } = {}) {
|
|
452
|
+
const els = {};
|
|
453
|
+
const el = (id) => (els[id] = els[id] || { id, hidden: true, innerHTML: '', textContent: '', style: {}, value: '', dataset: {}, title: '', addEventListener() {}, querySelectorAll: () => [], setAttribute() {} });
|
|
454
|
+
const listeners = {};
|
|
455
|
+
const fetches = [];
|
|
456
|
+
const tabsMade = [];
|
|
457
|
+
const settingsLoads = [];
|
|
458
|
+
const window = {
|
|
459
|
+
__MODULES__: modules,
|
|
460
|
+
OTBMarketWatch: MW,
|
|
461
|
+
OTBKit: { makeTabs: (opts) => { tabsMade.push(opts); return { activate() {}, active: () => opts.initial || opts.tabs[0].id }; } },
|
|
462
|
+
OTBMarketWatchSettings: { load: () => { settingsLoads.push(1); } },
|
|
463
|
+
__hallReads: shared === undefined ? undefined : { '/me': shared },
|
|
464
|
+
location: { pathname: '/market-watch', search: '', hash: '', assign() {} },
|
|
465
|
+
localStorage: { getItem: () => null, setItem() {} },
|
|
466
|
+
};
|
|
467
|
+
const document = {
|
|
468
|
+
title: 'Market Watch — Hub',
|
|
469
|
+
getElementById: el,
|
|
470
|
+
querySelectorAll: () => [],
|
|
471
|
+
addEventListener: (ev, fn) => { listeners[ev] = fn; },
|
|
472
|
+
};
|
|
473
|
+
const answer = (status, body) => ({ status, ok: status === 200, json: async () => body });
|
|
474
|
+
const ctx = vm.createContext({
|
|
475
|
+
window, document, URLSearchParams, console,
|
|
476
|
+
fetch: async (url) => {
|
|
477
|
+
fetches.push(url);
|
|
478
|
+
if (/\/me$/.test(url)) return answer(meStatus, { builder: { id: '1', rank } });
|
|
479
|
+
if (/market-watch\/home$/.test(url)) return answer(homeStatus, homeStatus === 200 ? home() : { error: { code: 'not_found' } });
|
|
480
|
+
return answer(404, {});
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
vm.runInContext(readFileSync(new URL('../modules/hall-ui/public/market-watch.js', import.meta.url), 'utf8'), ctx);
|
|
484
|
+
return { els: new Proxy(els, { get: (_, id) => el(id) }), fetches, tabsMade, settingsLoads, ready: () => listeners.DOMContentLoaded() };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
test('SETTINGS TAB: the owner gets a Watch tab and a Settings tab, linked by #settings, and the settings load', async () => {
|
|
488
|
+
const p = bootDesk({ rank: 'archon' });
|
|
489
|
+
await p.ready();
|
|
490
|
+
assert.equal(p.els['mw-desk'].hidden, false, 'the desk still draws');
|
|
491
|
+
assert.equal(p.tabsMade.length, 1, 'one tab strip');
|
|
492
|
+
const t = JSON.parse(JSON.stringify(p.tabsMade[0]));
|
|
493
|
+
assert.deepEqual(t.tabs.map((x) => [x.id, x.label, x.panel]), [['watch', 'Watch', 'mw-panel-watch'], ['settings', 'Settings', 'mw-panel-settings']]);
|
|
494
|
+
assert.equal(t.hash, true, '#settings is the address the old URL redirects to');
|
|
495
|
+
assert.equal(t.initial, 'watch', 'the owner lands on the watch, like everyone else');
|
|
496
|
+
assert.equal(p.els['mw-tabs'].hidden, false, 'the strip shows');
|
|
497
|
+
assert.equal(p.settingsLoads.length, 1, 'the settings read is made once');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('SETTINGS TAB: a non-owner sees no tab strip and no settings, and the settings are never read', async () => {
|
|
501
|
+
for (const rank of ['metic', 'thetes', 'xenos', null]) {
|
|
502
|
+
const p = bootDesk({ rank });
|
|
503
|
+
await p.ready();
|
|
504
|
+
assert.equal(p.els['mw-desk'].hidden, false, `${rank}: the desk draws`);
|
|
505
|
+
assert.equal(p.tabsMade.length, 0, `${rank}: no tab strip`);
|
|
506
|
+
assert.equal(p.els['mw-tabs'].hidden, true, `${rank}: the strip stays hidden`);
|
|
507
|
+
assert.equal(p.els['mw-panel-settings'].hidden, true, `${rank}: the settings panel stays hidden`);
|
|
508
|
+
assert.equal(p.settingsLoads.length, 0, `${rank}: the owner-only read is not made`);
|
|
509
|
+
}
|
|
510
|
+
// An unreadable /me is not an owner.
|
|
511
|
+
const blind = bootDesk({ rank: 'archon', meStatus: 500 });
|
|
512
|
+
await blind.ready();
|
|
513
|
+
assert.equal(blind.tabsMade.length, 0, 'an unreadable identity builds no settings tab');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test('SETTINGS TAB: where Market Watch is off there is nothing to set, so no tab (and still nothing asked)', async () => {
|
|
517
|
+
const off = bootDesk({ rank: 'archon', modules: { enabled: { 'market-watch': false } } });
|
|
518
|
+
await off.ready();
|
|
519
|
+
assert.equal(off.tabsMade.length, 0);
|
|
520
|
+
assert.equal(off.fetches.length, 0, 'an off module is not asked, and neither is /me');
|
|
521
|
+
const unmounted = bootDesk({ rank: 'archon', modules: undefined, homeStatus: 404 });
|
|
522
|
+
await unmounted.ready();
|
|
523
|
+
assert.equal(unmounted.els['mw-off'].hidden, false);
|
|
524
|
+
assert.equal(unmounted.tabsMade.length, 0, 'unmounted routes have no settings either');
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('SETTINGS TAB: the page carries every setting the old page did, in a hidden owner panel, and the old page is gone', () => {
|
|
528
|
+
const pub = (f) => new URL(`../modules/hall-ui/public/${f}`, import.meta.url);
|
|
529
|
+
const html = readFileSync(pub('market-watch.html'), 'utf8');
|
|
530
|
+
const panel = html.match(/<section[^>]*id="mw-panel-settings"[^>]*>[\s\S]*?<\/section>\s*<!-- \/mw-panel-settings -->/);
|
|
531
|
+
assert.ok(panel, 'one settings panel, closed by its marker comment');
|
|
532
|
+
assert.match(panel[0].split('>')[0], /\bhidden\b/, 'the panel starts hidden: nobody sees it until the owner check passes');
|
|
533
|
+
for (const id of ['mws-power', 'mws-cap', 'mws-cad', 'mws-weeks', 'mws-key-in', 'mws-key-save', 'mws-key-clear', 'mws-sealed', 'mws-off', 'mws-error']) {
|
|
534
|
+
assert.match(panel[0], new RegExp(`id="${id}"`), `${id} is on the tab`);
|
|
535
|
+
}
|
|
536
|
+
assert.match(panel[0], /href="\/market-watch-rules"/, 'the link to the rules is on the tab');
|
|
537
|
+
// While GET /market-watch/settings is in flight every section below is [hidden], so the
|
|
538
|
+
// panel opens on the self-hiding page loader (records/loader.md) rather than a blank tab.
|
|
539
|
+
const inner = panel[0].slice(panel[0].indexOf('>') + 1).replace(/^\s*(<!--[\s\S]*?-->\s*)*/, '');
|
|
540
|
+
assert.match(inner, /^<p class="hall-loading hall-loading--page hall-loading--wait" role="status">Loading…<\/p>/, 'the settings tab opens on the page loader');
|
|
541
|
+
const after = inner.slice(inner.indexOf('</p>') + 4);
|
|
542
|
+
const kids = [...after.matchAll(/^ {10}<(section|div|p)\b([^>]*)>/gm)];
|
|
543
|
+
assert.ok(kids.length >= 4, `the loader's siblings were read (${kids.length})`);
|
|
544
|
+
for (const [, tag, attrs] of kids) assert.match(attrs, /\bhidden\b/, `a <${tag}> after the loader starts hidden, or the loader would never show`);
|
|
545
|
+
assert.match(html, /<div[^>]*id="mw-tabs"[^>]*hidden/, 'the tab strip starts hidden');
|
|
546
|
+
assert.match(html, /<div[^>]*id="mw-panel-watch"/, 'the watch is its own panel');
|
|
547
|
+
assert.match(html, /market-watch-settings\.css/);
|
|
548
|
+
assert.match(html, /<script src="\/builders\/market-watch-settings\.js"><\/script>\s*<script src="\/builders\/market-watch\.js"><\/script>/,
|
|
549
|
+
'the settings script loads before the page script that calls it');
|
|
550
|
+
for (const f of ['market-watch-settings.html', 'market-watch-settings.states.json']) {
|
|
551
|
+
assert.throws(() => readFileSync(pub(f)), /ENOENT/, `${f} is gone — the settings live on the tab`);
|
|
552
|
+
}
|
|
553
|
+
// The settings script no longer boots itself: it reads only when the page says so.
|
|
554
|
+
const js = readFileSync(pub('market-watch-settings.js'), 'utf8');
|
|
555
|
+
assert.doesNotMatch(js, /addEventListener\('DOMContentLoaded'/, 'no self-boot: a non-owner page never asks for the settings');
|
|
556
|
+
assert.match(js, /window\.OTBMarketWatchSettings = \{ load \}/);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
test('SETTINGS TAB: the old settings URL redirects to the tab, and no gate dispatches to it', () => {
|
|
560
|
+
const serve = readFileSync(new URL('../src/bongos/serve-internal.js', import.meta.url), 'utf8');
|
|
561
|
+
const decl = 'const RETIRED_MW_SETTINGS_RE = ';
|
|
562
|
+
const i = serve.indexOf(decl);
|
|
563
|
+
assert.ok(i > 0, 'the old URL keeps a matcher, so a bookmark still lands');
|
|
564
|
+
const block = serve.slice(i, i + 700);
|
|
565
|
+
assert.ok(block.includes('res.redirect(302,'), 'a temporary redirect, never cached past a re-point');
|
|
566
|
+
assert.ok(block.includes('/market-watch${qs}#settings'), 'landing on the Settings tab, query string kept');
|
|
567
|
+
const rx = eval(serve.slice(i + decl.length, serve.indexOf(';', i))); // eslint-disable-line no-eval
|
|
568
|
+
for (const p of ['/builders/market-watch-settings', '/builders/market-watch-settings/', '/builders/market-watch-settings.html']) {
|
|
569
|
+
assert.ok(rx.test(p), `${p} reaches the redirect`);
|
|
570
|
+
}
|
|
571
|
+
for (const p of ['/builders/market-watch', '/builders/market-watch-rules', '/api/bongos/market-watch/settings', '/builders/market-watch-settingsx']) {
|
|
572
|
+
assert.ok(!rx.test(p), `${p} is not redirected`);
|
|
573
|
+
}
|
|
574
|
+
const mwRe = serve.match(/const MARKET_WATCH_PAGE_RE = (\/[^\n]*?\/);/);
|
|
575
|
+
assert.ok(mwRe, 'the Market Watch page gate is still a literal');
|
|
576
|
+
const gate = eval(mwRe[1]); // eslint-disable-line no-eval
|
|
577
|
+
assert.ok(!gate.test('/builders/market-watch-settings'), 'the page gate no longer names the retired page');
|
|
578
|
+
for (const p of ['/builders/market-watch', '/builders/market-watch-item', '/builders/market-watch-rules']) assert.ok(gate.test(p), p);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test('SETTINGS TAB: the page adopts the shell\'s one /me read instead of asking again', async () => {
|
|
582
|
+
const owner = bootDesk({ rank: 'archon', shared: Promise.resolve({ builder: { id: '1', rank: 'archon' } }) });
|
|
583
|
+
await owner.ready();
|
|
584
|
+
assert.equal(owner.tabsMade.length, 1, 'the shared answer is enough to know the owner');
|
|
585
|
+
assert.ok(!owner.fetches.some((u) => /\/me$/.test(u)), 'no second /me request');
|
|
586
|
+
for (const answer of [{ builder: { id: '2', rank: 'metic' } }, false, null]) {
|
|
587
|
+
const p = bootDesk({ rank: 'archon', shared: Promise.resolve(answer) });
|
|
588
|
+
await p.ready();
|
|
589
|
+
assert.equal(p.tabsMade.length, 0, `${JSON.stringify(answer)}: no tab`);
|
|
590
|
+
assert.ok(!p.fetches.some((u) => /\/me$/.test(u)), 'and no fallback request either');
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test('SETTINGS TAB: a watch that fails to read keeps the owner\'s tab, so the switch and key stay reachable', async () => {
|
|
595
|
+
const p = bootDesk({ rank: 'archon', homeStatus: 500 });
|
|
596
|
+
await p.ready();
|
|
597
|
+
assert.equal(p.els['mw-fail'].hidden, false, 'the failure line shows on the Watch tab');
|
|
598
|
+
assert.equal(p.tabsMade.length, 1, 'the settings are their own read, so the owner still gets them');
|
|
599
|
+
assert.equal(p.settingsLoads.length, 1);
|
|
600
|
+
const member = bootDesk({ rank: 'metic', homeStatus: 500 });
|
|
601
|
+
await member.ready();
|
|
602
|
+
assert.equal(member.tabsMade.length, 0, 'and a non-owner still gets none');
|
|
603
|
+
});
|
|
@@ -94,8 +94,8 @@ test('the hall-preview fixture carries every atom /me resolves', () => {
|
|
|
94
94
|
// "Upgraded to core X" notice after a move; task 1004826: Standing is Accomplishments;
|
|
95
95
|
// task 1004846: the new logo (its bump landed late, in task 1004852); task 1004938: the
|
|
96
96
|
// Market Watch settings nav item and its fuel glyph; task 1005041: the Update pill replaced the
|
|
97
|
-
// core-update banner.
|
|
98
|
-
const SHELL_SHA = '
|
|
97
|
+
// core-update banner; task 1005045: the Market Watch settings item left (one sidebar item).
|
|
98
|
+
const SHELL_SHA = '4a4b9795cdea422c';
|
|
99
99
|
|
|
100
100
|
test('shell.js has not changed without its cache-bust being bumped', () => {
|
|
101
101
|
const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Market Watch settings — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="Turn Market Watch on or off, set its monthly AI cap and how often it runs, see what it spent this month, and set the project's Anthropic key.">
|
|
8
|
-
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-09-23-changelist1">
|
|
12
|
-
<link rel="stylesheet" href="/builders/market-watch-settings.css">
|
|
13
|
-
</head>
|
|
14
|
-
<body data-page="market-watch-settings" data-page-title="Market Watch">
|
|
15
|
-
|
|
16
|
-
<div class="app">
|
|
17
|
-
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
18
|
-
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
19
|
-
<div class="app-body">
|
|
20
|
-
<header class="app-topbar" id="app-topbar"></header>
|
|
21
|
-
<div class="app-content">
|
|
22
|
-
<!-- The page's one h1, for screen readers: no page shows its head (task 1004836). -->
|
|
23
|
-
<div class="page-head page-head--board">
|
|
24
|
-
<h1 class="page-head__h">Market Watch settings</h1>
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
<main id="hall-main">
|
|
28
|
-
<!-- task 1005043: the page loader, shown until any section below is revealed. -->
|
|
29
|
-
<p class="hall-loading hall-loading--page hall-loading--wait" role="status">Loading…</p>
|
|
30
|
-
<!-- What a reader who is not the owner sees. The routes are the wall (403); this is
|
|
31
|
-
only what stands in for an empty gauge. -->
|
|
32
|
-
<section class="scroll" id="mws-sealed" aria-labelledby="mws-sealed-h" hidden>
|
|
33
|
-
<h2 class="scroll__h" id="mws-sealed-h">Owner only</h2>
|
|
34
|
-
<p class="scroll__lede">Only the project owner can see and change Market Watch settings.</p>
|
|
35
|
-
</section>
|
|
36
|
-
|
|
37
|
-
<section class="scroll" id="mws-off" aria-labelledby="mws-off-h" hidden>
|
|
38
|
-
<h2 class="scroll__h" id="mws-off-h">Market Watch is not on this project</h2>
|
|
39
|
-
<p class="scroll__lede">Turn the Market Watch module on under <a href="/modules">Modules</a> first. Its settings appear here once it is running.</p>
|
|
40
|
-
</section>
|
|
41
|
-
|
|
42
|
-
<p class="mws-error" id="mws-error" role="alert" hidden></p>
|
|
43
|
-
|
|
44
|
-
<!-- The fuel gauge (docs/design/mocks/market-watch/settings.html, option B). Drawn
|
|
45
|
-
by market-watch-settings.js from GET /market-watch/settings. -->
|
|
46
|
-
<section class="scroll mws" id="mws-body" aria-label="Market Watch settings" hidden>
|
|
47
|
-
<div class="mws-hero">
|
|
48
|
-
<div class="mws-ring" id="mws-ring">
|
|
49
|
-
<svg viewBox="0 0 100 100" aria-hidden="true">
|
|
50
|
-
<circle class="mws-ring__track" cx="50" cy="50" r="44" pathLength="100"/>
|
|
51
|
-
<circle class="mws-ring__spent" id="mws-arc" cx="50" cy="50" r="44" pathLength="100" stroke-dasharray="0 100"/>
|
|
52
|
-
<circle class="mws-ring__next" id="mws-arc2" cx="50" cy="50" r="44" pathLength="100" stroke-dasharray="0 100"/>
|
|
53
|
-
</svg>
|
|
54
|
-
<div class="mws-ring__c">
|
|
55
|
-
<div>
|
|
56
|
-
<div class="mws-ring__big" id="mws-spent">$0</div>
|
|
57
|
-
<div class="mws-soft" id="mws-of"></div>
|
|
58
|
-
<div class="mws-pill" id="mws-left"></div>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div class="mws-controls">
|
|
64
|
-
<button type="button" class="mws-power" id="mws-power" aria-pressed="false">
|
|
65
|
-
<span id="mws-power-t">Market Watch is off</span><i aria-hidden="true">⏻</i>
|
|
66
|
-
</button>
|
|
67
|
-
|
|
68
|
-
<div class="mws-slide">
|
|
69
|
-
<label class="mws-soft" for="mws-cap">monthly AI cap</label>
|
|
70
|
-
<span class="mws-cap" id="mws-cap-t">$20</span>
|
|
71
|
-
<input type="range" min="5" max="60" step="5" value="20" id="mws-cap">
|
|
72
|
-
<span class="mws-soft" id="mws-hint"></span>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<div class="mws-cad" id="mws-cad" role="group" aria-label="how often it runs">
|
|
76
|
-
<button type="button" class="mws-chip" data-c="weekly">weekly</button>
|
|
77
|
-
<button type="button" class="mws-chip" data-c="biweekly">every 2 weeks</button>
|
|
78
|
-
<button type="button" class="mws-chip" data-c="monthly">monthly</button>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
<div class="mws-lbl" id="mws-weeks-h">the last weeks</div>
|
|
84
|
-
<ol class="mws-weeks" id="mws-weeks" aria-labelledby="mws-weeks-h"></ol>
|
|
85
|
-
<div class="mws-legend" id="mws-legend"></div>
|
|
86
|
-
|
|
87
|
-
<!-- The project's own Anthropic key: write-only. The page only ever learns whether
|
|
88
|
-
one is set; it can replace it or clear it, never read it. -->
|
|
89
|
-
<div class="mws-lbl">anthropic key</div>
|
|
90
|
-
<div class="mws-key" id="mws-key">
|
|
91
|
-
<span class="mws-key__state" id="mws-key-state">not set</span>
|
|
92
|
-
<input type="password" id="mws-key-in" autocomplete="off" spellcheck="false" placeholder="paste the project's key" aria-label="the project's Anthropic key">
|
|
93
|
-
<button type="button" class="btn" id="mws-key-save">Save</button>
|
|
94
|
-
<button type="button" class="btn btn--ghost" id="mws-key-clear" hidden>Clear</button>
|
|
95
|
-
</div>
|
|
96
|
-
<p class="mws-soft mws-key__note" id="mws-key-note">Runs use this key, so the project's Anthropic account pays for them. It is stored encrypted and never shown again.</p>
|
|
97
|
-
</section>
|
|
98
|
-
</main>
|
|
99
|
-
|
|
100
|
-
<footer class="app-foot" id="app-foot"></footer>
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
106
|
-
|
|
107
|
-
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
108
|
-
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
109
|
-
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
110
|
-
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
111
|
-
<script src="/builders/palette.js" defer></script>
|
|
112
|
-
<script src="/builders/hall-kit.js"></script>
|
|
113
|
-
<script src="/builders/market-watch-settings.js"></script>
|
|
114
|
-
</body>
|
|
115
|
-
</html>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_": "MARKET WATCH SETTINGS (task 1004938; design of record docs/design/mocks/market-watch/settings.html, option B, the fuel gauge) through modules/ui-design/kit/render.js. The page reads ONE owner-only route, GET /market-watch/settings, so against the kit's own stub it renders its 'module is off' state (the 404), which the floors must hold too. To look at the populated gauge run it against the hall-preview harness, whose market-watch__settings.json fixture carries the mockup's month (12.40 of 20 spent, six runs, one skipped at the cap):\n\n node scripts/hall-preview/server.js --port 4620 --fixture-me --rank archon\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/market-watch-settings.states.json --base-in http://localhost:4620 --base-out http://localhost:4620",
|
|
3
|
-
"page": "/builders/market-watch-settings",
|
|
4
|
-
"surface": "hall-ui",
|
|
5
|
-
"stub": { "prefix": "/builders/" },
|
|
6
|
-
"modeQuery": false,
|
|
7
|
-
"ignoreRequests": ["/api/(gds|bongos)/"],
|
|
8
|
-
"ignoreConsole": ["/api/(gds|bongos)/", "\\b401\\b", "\\b403\\b", "\\b404\\b", "Unauthorized", "Forbidden", "Not Found", "load failed"],
|
|
9
|
-
"states": {
|
|
10
|
-
"gauge": { "auth": true, "actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300]], "expect": { "visible": ["#mws-body"] } },
|
|
11
|
-
"out": { "auth": false, "actions": [["wait", 600]] }
|
|
12
|
-
},
|
|
13
|
-
"reducedMotion": { "auth": true, "actions": [["wait", 800]] }
|
|
14
|
-
}
|