@bongos/core 1.24.6 → 1.24.8
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 +106 -46
- 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 +13 -1
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0371-auto-pickup-applies-a-submitted-tweak-on-the-runner.md +1 -0
- package/docs/api/openapi.json +264 -4
- package/docs/api-reference.md +7 -3
- package/docs/copy-inventory.md +41 -13
- package/docs/copy-registry.json +319 -35
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +40 -4
- package/docs/page-readings.json +69 -9
- package/modules/government/decision-board.js +5 -0
- package/modules/government/decision-rules.js +3 -0
- package/modules/government/routes/government.js +2 -0
- package/modules/hall-ui/public/market-watch-rules-lib.js +207 -0
- package/modules/hall-ui/public/market-watch-rules.css +68 -0
- package/modules/hall-ui/public/market-watch-rules.html +54 -0
- package/modules/hall-ui/public/market-watch-rules.js +129 -0
- package/modules/hall-ui/public/market-watch-rules.states.json +17 -0
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/project-settings.js +51 -1
- package/modules/lifecycle/autopickup.js +26 -9
- package/modules/lifecycle/db-claims.js +1 -1
- package/modules/lifecycle/routes/project-settings.js +71 -10
- package/modules/market-watch/matter-acts.js +59 -0
- package/modules/market-watch/module.json +5 -4
- package/modules/market-watch/proposals.js +214 -0
- package/modules/market-watch/research.js +49 -8
- package/modules/market-watch/routes/rules.js +202 -0
- package/modules/market-watch/rules-logic.js +264 -0
- package/modules/market-watch/rules-view.js +165 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/government-walk.js +18 -2
- package/scripts/bongos/run-unit-tests.js +3 -0
- package/src/bongos/module-scope-map.js +2 -0
- package/src/bongos/project-settings.js +59 -15
- package/src/bongos/serve-internal.js +2 -1
- package/src/module-api.js +1 -1
- package/tests/autopickup_cap.mjs +203 -11
- package/tests/government_act_alone_buildings.mjs +3 -0
- package/tests/government_decision_rules.mjs +4 -1
- package/tests/governor_matter_acts.mjs +5 -1
- package/tests/hall_page_gate_map.mjs +3 -0
- package/tests/market_watch_module.mjs +1 -0
- package/tests/market_watch_rules.mjs +496 -0
- package/tests/market_watch_rules_db.mjs +133 -0
- package/tests/project_settings_page.mjs +8 -5
package/docs/page-readings.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"adr": "0341 D3",
|
|
6
6
|
"reread": "node scripts/bongos/page-reader.js --stale",
|
|
7
7
|
"how": ["Generated by node scripts/bongos/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
|
|
8
|
-
"counts": {"pages":
|
|
8
|
+
"counts": {"pages":49,"lines":5511,"placed":1700,"shared":435,"unplaced":3376,"omitted_data":3777},
|
|
9
9
|
"pages": [
|
|
10
10
|
{
|
|
11
11
|
"id": "builders:ai-studio",
|
|
@@ -1794,6 +1794,66 @@
|
|
|
1794
1794
|
{"key":"L0066","section":"What are you doing now?","text":"Ask me again next session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":675,"string_id":"fa86bf403775"}
|
|
1795
1795
|
]
|
|
1796
1796
|
},
|
|
1797
|
+
{
|
|
1798
|
+
"id": "builders:market-watch-rules",
|
|
1799
|
+
"surface": "builders",
|
|
1800
|
+
"title": "Market Watch rules",
|
|
1801
|
+
"states_read": ["in","scoring","names","propose","out"],
|
|
1802
|
+
"files_hash": "5146acc85101e657",
|
|
1803
|
+
"reading_hash": "8fab4258348366b7",
|
|
1804
|
+
"counts": {"lines":47,"placed":13,"shared":8,"unplaced":26},
|
|
1805
|
+
"omitted": {"data":45},
|
|
1806
|
+
"notes": [],
|
|
1807
|
+
"lines": [
|
|
1808
|
+
{"key":"L0001","section":"skip link","text":"Skip to content","placement":"shared","file":"modules/hall-ui/public/shell.js","line":868,"string_id":"87cee78ca134"},
|
|
1809
|
+
{"key":"L0002","section":"top bar","text":"Market Watch rules","placement":"shared","file":"modules/hall-ui/public/market-watch-rules.html","line":15,"string_id":"d069f67f3ac6"},
|
|
1810
|
+
{"key":"L0003","section":"top bar","text":"Jump to…","placement":"shared","file":"modules/hall-ui/public/shell.js","line":540,"string_id":"eba610d25523"},
|
|
1811
|
+
{"key":"L0004","section":"top bar","text":"Ctrl K","placement":"shared"},
|
|
1812
|
+
{"key":"L0005","section":"top bar","text":"Building","placement":"shared"},
|
|
1813
|
+
{"key":"L0006","section":"top bar","text":"Artistry","placement":"shared"},
|
|
1814
|
+
{"key":"L0007","section":"top bar","text":"Ideating","placement":"shared"},
|
|
1815
|
+
{"key":"L0008","section":"page top","text":"v3 · now","placement":"unplaced"},
|
|
1816
|
+
{"key":"L0009","section":"page top","text":"in use","placement":"unplaced"},
|
|
1817
|
+
{"key":"L0010","section":"page top","text":"v1","placement":"unplaced"},
|
|
1818
|
+
{"key":"L0011","section":"page top","text":"v2","placement":"unplaced"},
|
|
1819
|
+
{"key":"L0012","section":"page top","text":"v3","placement":"unplaced"},
|
|
1820
|
+
{"key":"L0013","section":"page top","text":"v4?","placement":"unplaced"},
|
|
1821
|
+
{"key":"L0014","section":"page top","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","occurrences":2},
|
|
1822
|
+
{"key":"L0015","section":"{…}","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","state":"propose","occurrences":8},
|
|
1823
|
+
{"key":"L0016","section":"{…}","text":"propose a change","placement":"placed","file":"modules/hall-ui/public/market-watch-rules-lib.js","line":127,"string_id":"cc7d093fdd53","state":"propose","occurrences":4},
|
|
1824
|
+
{"key":"L0017","section":"{…}","text":"change","placement":"unplaced","state":"propose"},
|
|
1825
|
+
{"key":"L0018","section":"{…}","text":"add a source","placement":"unplaced","state":"propose"},
|
|
1826
|
+
{"key":"L0019","section":"{…}","text":"remove one","placement":"unplaced","state":"propose"},
|
|
1827
|
+
{"key":"L0020","section":"{…}","text":"why","placement":"unplaced","state":"propose"},
|
|
1828
|
+
{"key":"L0021","section":"{…}","text":"one line","placement":"placed","file":"modules/hall-ui/public/market-watch-rules-lib.js","line":137,"string_id":"973f396ac861","attr":"placeholder","state":"propose"},
|
|
1829
|
+
{"key":"L0022","section":"{…}","text":"send the proposal","placement":"placed","file":"modules/hall-ui/public/market-watch-rules-lib.js","line":160,"string_id":"fe17d62b8a85","state":"propose"},
|
|
1830
|
+
{"key":"L0023","section":"{…}","text":"cancel","placement":"unplaced","state":"propose"},
|
|
1831
|
+
{"key":"L0024","section":"{…}","text":"Open {…}","placement":"placed","file":"modules/hall-ui/public/goals.js","line":84,"string_id":"f6f68ba322be","state":"names","occurrences":3},
|
|
1832
|
+
{"key":"L0025","section":"{…}","text":"+ 54 more","placement":"unplaced","state":"names"},
|
|
1833
|
+
{"key":"L0026","section":"{…}","text":"HUMAN DECISION","placement":"unplaced","state":"names","occurrences":3},
|
|
1834
|
+
{"key":"L0027","section":"{…}","text":"proposal from {…}","placement":"unplaced","state":"names","occurrences":3},
|
|
1835
|
+
{"key":"L0028","section":"{…}","text":"Approve","placement":"unplaced","state":"names","occurrences":3},
|
|
1836
|
+
{"key":"L0029","section":"{…}","text":"takes effect now","placement":"unplaced","state":"names"},
|
|
1837
|
+
{"key":"L0030","section":"{…}","text":"Decline","placement":"unplaced","state":"names","occurrences":3},
|
|
1838
|
+
{"key":"L0031","section":"{…}","text":"stays in history","placement":"unplaced","state":"names","occurrences":3},
|
|
1839
|
+
{"key":"L0032","section":"{…}","text":"competitors","placement":"unplaced","state":"scoring"},
|
|
1840
|
+
{"key":"L0033","section":"{…}","text":"partners","placement":"unplaced","state":"scoring"},
|
|
1841
|
+
{"key":"L0034","section":"{…}","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","state":"scoring"},
|
|
1842
|
+
{"key":"L0035","section":"{…}","text":"becomes v4","placement":"unplaced","state":"scoring","occurrences":2},
|
|
1843
|
+
{"key":"L0036","section":"cost of one run, by step","text":"cost of one run, by step","placement":"placed","file":"modules/hall-ui/public/market-watch-rules.html","line":32,"string_id":"c02d0024fe78"},
|
|
1844
|
+
{"key":"L0037","section":"cost of one run, by step","text":"{…}: no run has cost anything yet, so these are $5.40 from the planning figures","placement":"unplaced"},
|
|
1845
|
+
{"key":"L0038","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":516,"string_id":"1883ad7f07fc"},
|
|
1846
|
+
{"key":"L0039","section":"cost of one run, by step","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":670,"string_id":"fd1903586e8e","state":"out"},
|
|
1847
|
+
{"key":"L0040","section":"What are you doing now?","text":"What are you doing now?","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":671,"string_id":"60853319f290","state":"out"},
|
|
1848
|
+
{"key":"L0041","section":"What are you doing now?","text":"Pick one. You can switch any time from the top bar.","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":672,"string_id":"4f87d5b17b54","state":"out"},
|
|
1849
|
+
{"key":"L0042","section":"What are you doing now?","text":"Building","placement":"unplaced","state":"out"},
|
|
1850
|
+
{"key":"L0043","section":"What are you doing now?","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","state":"out","occurrences":3},
|
|
1851
|
+
{"key":"L0044","section":"What are you doing now?","text":"Artistry","placement":"unplaced","state":"out"},
|
|
1852
|
+
{"key":"L0045","section":"What are you doing now?","text":"Ideating","placement":"unplaced","state":"out"},
|
|
1853
|
+
{"key":"L0046","section":"What are you doing now?","text":"Turn thoughts into ideas.","placement":"unplaced","state":"out"},
|
|
1854
|
+
{"key":"L0047","section":"What are you doing now?","text":"Ask me again next session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":675,"string_id":"fa86bf403775","state":"out"}
|
|
1855
|
+
]
|
|
1856
|
+
},
|
|
1797
1857
|
{
|
|
1798
1858
|
"id": "builders:modules",
|
|
1799
1859
|
"surface": "builders",
|
|
@@ -2595,9 +2655,9 @@
|
|
|
2595
2655
|
"surface": "builders",
|
|
2596
2656
|
"title": "Project settings",
|
|
2597
2657
|
"states_read": ["default"],
|
|
2598
|
-
"files_hash": "
|
|
2599
|
-
"reading_hash": "
|
|
2600
|
-
"counts": {"lines":38,"placed":
|
|
2658
|
+
"files_hash": "39656ae681066cb4",
|
|
2659
|
+
"reading_hash": "4f7a29f2c2f0ad7c",
|
|
2660
|
+
"counts": {"lines":38,"placed":20,"shared":8,"unplaced":10},
|
|
2601
2661
|
"omitted": {"data":31},
|
|
2602
2662
|
"notes": [],
|
|
2603
2663
|
"lines": [
|
|
@@ -2609,7 +2669,7 @@
|
|
|
2609
2669
|
{"key":"L0006","section":"top bar","text":"Artistry","placement":"shared"},
|
|
2610
2670
|
{"key":"L0007","section":"top bar","text":"Ideating","placement":"shared"},
|
|
2611
2671
|
{"key":"L0008","section":"The fundamentals","text":"The fundamentals","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":39,"string_id":"cb7c7879eeba"},
|
|
2612
|
-
{"key":"L0009","section":"The fundamentals","text":"
|
|
2672
|
+
{"key":"L0009","section":"The fundamentals","text":"Seven things every project settles. Four of them are knobs you can turn here; the other three are decided in committed configuration, and each says which file decides it.","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":40,"string_id":"f7da8748453d"},
|
|
2613
2673
|
{"key":"L0010","section":"The fundamentals","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":9},
|
|
2614
2674
|
{"key":"L0011","section":"The fundamentals","text":"How tasks ship","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":136,"string_id":"76f344dd4727"},
|
|
2615
2675
|
{"key":"L0012","section":"The fundamentals","text":"Save","placement":"unplaced","occurrences":4},
|
|
@@ -2617,14 +2677,14 @@
|
|
|
2617
2677
|
{"key":"L0014","section":"The fundamentals","text":"Raise after","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":115,"string_id":"4de232a05888"},
|
|
2618
2678
|
{"key":"L0015","section":"The fundamentals","text":"days ({…}–{…})","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":115,"string_id":"0e2bef756977"},
|
|
2619
2679
|
{"key":"L0016","section":"The fundamentals","text":"offered","placement":"unplaced","occurrences":2},
|
|
2620
|
-
{"key":"L0017","section":"The fundamentals","text":"New category label","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":
|
|
2621
|
-
{"key":"L0018","section":"The fundamentals","text":"Add","placement":"
|
|
2680
|
+
{"key":"L0017","section":"The fundamentals","text":"New category label","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":193,"string_id":"87f09d983f6c","attr":"placeholder"},
|
|
2681
|
+
{"key":"L0018","section":"The fundamentals","text":"Add","placement":"placed","file":"modules/hall-ui/public/ai-studio.js","line":469,"string_id":"e4da002ae135"},
|
|
2622
2682
|
{"key":"L0019","section":"The fundamentals","text":"committed config","placement":"unplaced"},
|
|
2623
2683
|
{"key":"L0020","section":"The fundamentals","text":"not yet real","placement":"unplaced"},
|
|
2624
2684
|
{"key":"L0021","section":"The fundamentals","text":"not available ({…})","placement":"unplaced"},
|
|
2625
2685
|
{"key":"L0022","section":"Danger zone","text":"Danger zone","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":56,"string_id":"13b22e48d9fb"},
|
|
2626
|
-
{"key":"L0023","section":"Danger zone","text":"This project is hosted on {…}, and that is where it is taken offline or removed. Both links below open this project’s page there.","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":
|
|
2627
|
-
{"key":"L0024","section":"Danger zone","text":"Manage this project on {…}","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":
|
|
2686
|
+
{"key":"L0023","section":"Danger zone","text":"This project is hosted on {…}, and that is where it is taken offline or removed. Both links below open this project’s page there.","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":350,"string_id":"e97b2d549f10"},
|
|
2687
|
+
{"key":"L0024","section":"Danger zone","text":"Manage this project on {…}","placement":"placed","file":"modules/hall-ui/public/project-settings.js","line":351,"string_id":"4e261fd15bd5"},
|
|
2628
2688
|
{"key":"L0025","section":"Danger zone","text":"Take it offline","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":59,"string_id":"da9da6e866bd"},
|
|
2629
2689
|
{"key":"L0026","section":"Danger zone","text":"— stops the project and its nightly backups but keeps everything: its database, its backups and your repository on GitHub. You can bring it back.","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":59,"string_id":"61ebc5d684fd"},
|
|
2630
2690
|
{"key":"L0027","section":"Danger zone","text":"Disconnect from Bongos entirely","placement":"placed","file":"modules/hall-ui/public/project-settings.html","line":60,"string_id":"d3efd513694b"},
|
|
@@ -253,6 +253,11 @@ const MATTER_ACTIONS = Object.freeze({
|
|
|
253
253
|
treasury: Object.freeze({
|
|
254
254
|
set_budget: Object.freeze({ builder_id: 'id', monthly_usd: 'usd' }),
|
|
255
255
|
}),
|
|
256
|
+
// task 1004941: a Market Watch proposal put to the Rules sitting, approved on a
|
|
257
|
+
// pass by the market-watch module's act (modules/market-watch/matter-acts.js)
|
|
258
|
+
rules: Object.freeze({
|
|
259
|
+
market_watch_proposal: Object.freeze({ proposal_id: 'id' }),
|
|
260
|
+
}),
|
|
256
261
|
});
|
|
257
262
|
const MATTER_ACT_POINT = 'governor.matterAct';
|
|
258
263
|
const MAX_BUDGET_USD = 100000;
|
|
@@ -68,6 +68,9 @@ const AREAS = Object.freeze([
|
|
|
68
68
|
'PATCH /project-settings/ship-mode',
|
|
69
69
|
'PATCH /project-settings/tweak-autopickup',
|
|
70
70
|
'PATCH /work-categories/:id',
|
|
71
|
+
// task 1004941: the owner approving a Market Watch proposal (a passed
|
|
72
|
+
// matter approves it through the market_watch_proposal act instead)
|
|
73
|
+
'POST /market-watch/proposals/:id/decide',
|
|
71
74
|
]) }),
|
|
72
75
|
Object.freeze({ key: 'disputes', name: 'Disputes', gate: 'security.report.adjudicate',
|
|
73
76
|
today: 'whoever may judge reports', acts: Object.freeze([
|
|
@@ -131,6 +131,8 @@ const governmentPort = {
|
|
|
131
131
|
// the constitution in force. And the founding band's read: per stage, what it
|
|
132
132
|
// is waiting on. A pass is announced as `board.genesis_stage.passed`.
|
|
133
133
|
openGenesisStageClose: board.openGenesisStageClose,
|
|
134
|
+
// task 1004941: POST /government/board/matters' own openMatter, for a module's proposal (callers gate on board.item.open)
|
|
135
|
+
openMatter: board.openMatter,
|
|
134
136
|
// task 1004427 (BV2.PS13): the genesis charter + legislation choice, filed as
|
|
135
137
|
// an ordinary amendment under the constitution in force — for the genesis
|
|
136
138
|
// home, which renders the stages from GET /government/constitution's
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// market-watch-rules-lib.js — the Market Watch rules page's markup, pure (task 1004941).
|
|
2
|
+
//
|
|
3
|
+
// Built to docs/design/mocks/market-watch/rules.html, option C, the pipeline map: a
|
|
4
|
+
// version slider (v1 to the version in use, plus the proposed next one), the five steps
|
|
5
|
+
// as a row of nodes (searches → sources → names → scoring → brief), the tapped step's
|
|
6
|
+
// rules in plain words with its open proposals, and the cost of one run by step.
|
|
7
|
+
//
|
|
8
|
+
// Every string here comes from the server's GET /market-watch/rules and much of it is
|
|
9
|
+
// member-typed (reasons, names, searches), so every one goes through `esc`; nothing is
|
|
10
|
+
// inserted as HTML. Node-require-safe: no document, no fetch, so a test renders the real
|
|
11
|
+
// markup from a real view.
|
|
12
|
+
(function (root) {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const ASK = () => (root && root.OTBAsk) || (typeof require === 'function' ? require('./hall-ask.js') : null);
|
|
16
|
+
const money = (n) => `$${(Number(n) || 0).toFixed(2)}`;
|
|
17
|
+
|
|
18
|
+
// The slider's stops: every version that was in use, then the proposed next one.
|
|
19
|
+
function stops(view) {
|
|
20
|
+
const out = (view.history || []).map((h) => ({
|
|
21
|
+
version: h.version, kind: h.status === 'active' ? 'now' : 'past', summary: h.summary, changed: h.changed || [], rules: h.rules, entry: h,
|
|
22
|
+
}));
|
|
23
|
+
if (view.next) {
|
|
24
|
+
out.push({
|
|
25
|
+
version: view.next.version,
|
|
26
|
+
kind: 'next',
|
|
27
|
+
summary: view.next.applied === 1 ? 'if you approve the proposal' : `if you approve all ${view.next.applied} proposals`,
|
|
28
|
+
changed: view.next.changed || [],
|
|
29
|
+
rules: view.next.rules,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stopLabel(stop) {
|
|
36
|
+
if (!stop) return '';
|
|
37
|
+
return `v${stop.version}${stop.kind === 'now' ? ' · now' : stop.kind === 'next' ? ' · proposed' : ''}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sliderHtml(view, at, esc) {
|
|
41
|
+
const s = stops(view);
|
|
42
|
+
if (!s.length) return '';
|
|
43
|
+
const i = Math.max(0, Math.min(s.length - 1, at));
|
|
44
|
+
const cur = s[i];
|
|
45
|
+
const pill = cur.kind === 'now' ? 'in use' : cur.summary || '';
|
|
46
|
+
const ticks = s.map((x) => `<span>v${esc(x.version)}${x.kind === 'next' ? '?' : ''}</span>`).join('');
|
|
47
|
+
return `<div class="mwr-time">`
|
|
48
|
+
+ `<span class="mwr-mono" id="mwr-vl">${esc(stopLabel(cur))}</span>`
|
|
49
|
+
+ `<input type="range" id="mwr-v" min="0" max="${s.length - 1}" value="${i}" aria-label="rules version"${s.length < 2 ? ' disabled' : ''}>`
|
|
50
|
+
+ `<span class="mwr-pill${cur.kind === 'now' ? ' mwr-pill--hot' : ''}" id="mwr-vs">${esc(pill)}</span>`
|
|
51
|
+
+ `</div><div class="mwr-ticks" aria-hidden="true">${ticks}</div>`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The five nodes. `changed` marks the steps the slider's version changed.
|
|
55
|
+
function pipelineHtml(view, { open, changed = [] }, esc) {
|
|
56
|
+
return (view.steps || []).map((s, i) => {
|
|
57
|
+
const cls = `mwr-node${changed.includes(s.key) ? ' mwr-node--changed' : ''}`;
|
|
58
|
+
const badge = s.open ? `<span class="mwr-badge" aria-label="${esc(s.open)} open proposals">${esc(s.open)}</span>` : '';
|
|
59
|
+
const node = `<button type="button" class="${cls}" data-step="${esc(s.key)}" aria-pressed="${s.key === open}">`
|
|
60
|
+
+ `<span class="mwr-node__k">${esc(s.label)}</span><span class="mwr-node__v">${esc(s.value)}</span>`
|
|
61
|
+
+ `<span class="mwr-node__s">${esc(s.sub)}</span>${badge}</button>`;
|
|
62
|
+
return node + (i < view.steps.length - 1 ? '<span class="mwr-arrow" aria-hidden="true">→</span>' : '');
|
|
63
|
+
}).join('');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A step's rules, as the chosen version has them.
|
|
67
|
+
function rulesBodyHtml(step, rules, view, esc) {
|
|
68
|
+
const r = rules || {};
|
|
69
|
+
const rows = (list) => (list && list.length
|
|
70
|
+
? `<ul class="mwr-rows">${list.map((x) => `<li class="mwr-row"><span class="mwr-grow">${esc(x)}</span></li>`).join('')}</ul>`
|
|
71
|
+
: '<p class="mwr-empty">Nothing here yet.</p>');
|
|
72
|
+
if (step === 'searches') return rows(r.search_queries);
|
|
73
|
+
if (step === 'sources') return rows(r.sources);
|
|
74
|
+
if (step === 'brief') return r.instructions ? `<pre class="mwr-pre">${esc(r.instructions)}</pre>` : '<p class="mwr-empty">No brief yet.</p>';
|
|
75
|
+
if (step === 'scoring') {
|
|
76
|
+
const kinds = Object.keys(r.rubric || {}).sort();
|
|
77
|
+
if (!kinds.length) return '<p class="mwr-empty">Nothing is scored yet.</p>';
|
|
78
|
+
return kinds.map((kind) => `<p class="mwr-sub">${esc(kind === 'competitor' ? 'competitors' : kind === 'partner' ? 'partners' : kind)}</p>`
|
|
79
|
+
+ `<ul class="mwr-rows">${(r.rubric[kind] || []).map((d) => {
|
|
80
|
+
const w = Number.isInteger(d.weight) ? d.weight : 1;
|
|
81
|
+
return `<li class="mwr-row" title="${esc(d.definition || '')}"><span class="mwr-grow">${esc(d.label || d.key)}</span><span class="mwr-mono">×${esc(w)}</span></li>`;
|
|
82
|
+
}).join('')}</ul>`).join('');
|
|
83
|
+
}
|
|
84
|
+
// names: the people's list, not a rules field
|
|
85
|
+
const step3 = (view.steps || []).find((s) => s.key === 'names');
|
|
86
|
+
const top = (step3 && step3.rules && step3.rules.top) || [];
|
|
87
|
+
if (!top.length) return '<p class="mwr-empty">Nothing is watched yet.</p>';
|
|
88
|
+
const more = step3.value - top.length;
|
|
89
|
+
return `<ul class="mwr-rows">${top.map((t) => `<li class="mwr-row"><span class="mwr-grow">${esc(t.name)}</span>`
|
|
90
|
+
+ `<span class="mwr-pill${t.overall != null && t.overall >= 6 ? ' mwr-pill--hot' : ''}">${t.overall == null ? '' : esc(t.overall)}${t.kind === 'competitor' ? '' : ` · ${esc(t.kind)}`}</span></li>`).join('')}`
|
|
91
|
+
+ `${more > 0 ? `<li class="mwr-row mwr-faint">+ ${esc(more)} more</li>` : ''}</ul>`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// One open proposal. A card only for a reader who can act on it (the owner decides;
|
|
95
|
+
// where the government decides, anyone who may open a matter can send it to the vote);
|
|
96
|
+
// everyone else reads it as one line.
|
|
97
|
+
function proposalHtml(p, view, esc) {
|
|
98
|
+
const d = view.decide || {};
|
|
99
|
+
const nextV = view.active ? view.active.version + 1 : 1;
|
|
100
|
+
const q = /[?]$/.test(p.summary) ? p.summary : `${p.summary}?`;
|
|
101
|
+
const where = `proposal from ${p.by || 'a member'}`;
|
|
102
|
+
if (p.at_vote) {
|
|
103
|
+
return `<li class="mwr-row mwr-prop"><span class="mwr-dot" aria-hidden="true"></span><span class="mwr-grow">${esc(q)} <span class="mwr-faint">· ${esc(p.reason)}</span></span><span class="mwr-pill">at a vote</span></li>`;
|
|
104
|
+
}
|
|
105
|
+
const opts = [];
|
|
106
|
+
if (d.viewer_may_decide) {
|
|
107
|
+
opts.push({ label: 'Approve', result: p.kind === 'rule_change' ? `becomes v${nextV}` : 'takes effect now', rec: true, attrs: { 'data-act': 'approve', 'data-id': p.id } });
|
|
108
|
+
}
|
|
109
|
+
if (d.may_put_to_vote) opts.push({ label: 'Put to a vote', result: d.who ? `decided by ${d.who}` : 'the government decides', attrs: { 'data-act': 'vote', 'data-id': p.id } });
|
|
110
|
+
if (d.viewer_may_decide) opts.push({ label: 'Decline', result: 'stays in history', attrs: { 'data-act': 'decline', 'data-id': p.id } });
|
|
111
|
+
const ask = ASK();
|
|
112
|
+
if (!opts.length || !ask) {
|
|
113
|
+
return `<li class="mwr-row mwr-prop"><span class="mwr-dot" aria-hidden="true"></span><span class="mwr-grow">${esc(q)} <span class="mwr-faint">· ${esc(p.by || 'a member')}: ${esc(p.reason)}</span></span><span class="mwr-pill">waiting</span></li>`;
|
|
114
|
+
}
|
|
115
|
+
return ask.html({ tag: 'li', where, q, said: p.reason, row: true, opts, attrs: { 'data-proposal': p.id } });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function panelHtml(view, step, stop, esc) {
|
|
119
|
+
const s = (view.steps || []).find((x) => x.key === step);
|
|
120
|
+
if (!s) return '';
|
|
121
|
+
const props = (view.proposals || []).filter((p) => p.step === step);
|
|
122
|
+
// who we watch is not versioned with the rules, so the slider does not date it
|
|
123
|
+
const viewing = stop && stop.kind !== 'now' && step !== 'names' ? `<p class="mwr-faint mwr-viewing">as of ${esc(stopLabel(stop))}</p>` : '';
|
|
124
|
+
return `<h2 class="mwr-panel__h">${esc(s.title)}</h2>${viewing}`
|
|
125
|
+
+ rulesBodyHtml(step, stop ? stop.rules : null, view, esc)
|
|
126
|
+
+ (props.length ? `<ul class="mwr-props">${props.map((p) => proposalHtml(p, view, esc)).join('')}</ul>` : '')
|
|
127
|
+
+ `<div class="mwr-propose"><button type="button" class="mwr-btn" data-act="propose" data-step="${esc(step)}" aria-expanded="false">propose a change</button></div>`
|
|
128
|
+
+ '<div class="mwr-form-slot" id="mwr-form-slot"></div>';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The form for one step's change. Selects carry only values the server's own check
|
|
132
|
+
// accepts; the server checks again.
|
|
133
|
+
function formHtml(view, step, esc) {
|
|
134
|
+
const active = (view.history || []).find((h) => h.status === 'active');
|
|
135
|
+
const r = (active && active.rules) || {};
|
|
136
|
+
const opt = (v, label) => `<option value="${esc(v)}">${esc(label)}</option>`;
|
|
137
|
+
const reason = '<label class="mwr-field"><span>why</span><input name="reason" maxlength="500" required placeholder="one line"></label>';
|
|
138
|
+
let body = '';
|
|
139
|
+
if (step === 'searches' || step === 'sources') {
|
|
140
|
+
const list = step === 'searches' ? (r.search_queries || []) : (r.sources || []);
|
|
141
|
+
body = `<label class="mwr-field"><span>change</span><select name="op">${opt('add', step === 'searches' ? 'add a search' : 'add a source')}${list.length ? opt('remove', 'remove one') : ''}</select></label>`
|
|
142
|
+
+ `<label class="mwr-field" data-for="add"><span>${step === 'searches' ? 'search for' : 'read'}</span><input name="text" maxlength="300"></label>`
|
|
143
|
+
+ (list.length ? `<label class="mwr-field" data-for="remove" hidden><span>remove</span><select name="pick">${list.map((x) => opt(x, x)).join('')}</select></label>` : '');
|
|
144
|
+
} else if (step === 'names') {
|
|
145
|
+
body = `<label class="mwr-field"><span>name</span><input name="name" maxlength="200" required></label>`
|
|
146
|
+
+ `<label class="mwr-field"><span>kind</span><select name="kind">${opt('competitor', 'competitor')}${opt('partner', 'partner')}${opt('topic', 'topic')}</select></label>`
|
|
147
|
+
+ '<label class="mwr-field"><span>link</span><input name="url" type="url" maxlength="500" placeholder="https://"></label>'
|
|
148
|
+
+ '<label class="mwr-field"><span>why watch it</span><input name="why" maxlength="500"></label>';
|
|
149
|
+
} else if (step === 'scoring') {
|
|
150
|
+
const dims = [];
|
|
151
|
+
for (const kind of Object.keys(r.rubric || {}).sort()) {
|
|
152
|
+
for (const d of r.rubric[kind] || []) dims.push(opt(`${kind}|${d.key}`, `${d.label || d.key} (${kind})`));
|
|
153
|
+
}
|
|
154
|
+
body = `<label class="mwr-field"><span>score</span><select name="dim">${dims.join('')}</select></label>`
|
|
155
|
+
+ `<label class="mwr-field"><span>counts</span><select name="weight">${[0, 1, 2, 3].map((w) => opt(w, w ? `×${w}` : 'not at all')).join('')}</select></label>`;
|
|
156
|
+
} else if (step === 'brief') {
|
|
157
|
+
body = `<label class="mwr-field mwr-field--wide"><span>the brief</span><textarea name="text" rows="4" maxlength="4000">${esc(r.instructions || '')}</textarea></label>`;
|
|
158
|
+
}
|
|
159
|
+
return `<form class="mwr-form" data-step="${esc(step)}">${body}${reason}`
|
|
160
|
+
+ '<div class="mwr-form__go"><button type="submit" class="mwr-btn mwr-btn--go">send the proposal</button><button type="button" class="mwr-btn" data-act="cancel">cancel</button></div></form>';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The form's fields → the { step, change, reason } the server takes.
|
|
164
|
+
function proposalFromForm(step, f) {
|
|
165
|
+
const v = (k) => (f[k] == null ? '' : String(f[k]));
|
|
166
|
+
let change;
|
|
167
|
+
if (step === 'searches' || step === 'sources') change = v('op') === 'remove' ? { op: 'remove', text: v('pick') } : { op: 'add', text: v('text') };
|
|
168
|
+
else if (step === 'names') change = { op: 'add', name: v('name'), kind: v('kind'), url: v('url'), why: v('why') };
|
|
169
|
+
else if (step === 'scoring') {
|
|
170
|
+
const [kind, key] = v('dim').split('|');
|
|
171
|
+
change = { op: 'weight', kind, key, weight: Number(v('weight')) };
|
|
172
|
+
} else change = { op: 'set', text: v('text') };
|
|
173
|
+
return { step, change, reason: v('reason') };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function costHtml(cost, esc) {
|
|
177
|
+
if (!cost) return '';
|
|
178
|
+
const rows = cost.rows.map((r) => `<li class="mwr-row"><span class="mwr-grow">${esc(r.label)}</span><span class="mwr-mono">${esc(money(r.usd))}</span></li>`).join('');
|
|
179
|
+
const note = cost.basis === 'run'
|
|
180
|
+
? `run ${esc(cost.run && cost.run.n != null ? cost.run.n : '')} cost ${esc(money(cost.total))}${cost.run && cost.run.cost_log_usd != null ? ` (cost log ${esc(money(cost.run.cost_log_usd))})` : ''}; the split by step is an estimate`
|
|
181
|
+
: `estimate: no run has cost anything yet, so these are ${esc(money(cost.total))} from the planning figures`;
|
|
182
|
+
return `<ul class="mwr-rows">${rows}</ul><p class="mwr-note">${note}</p>`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const ERRORS = Object.freeze({
|
|
186
|
+
owner_only: 'Only the project owner can decide this.',
|
|
187
|
+
decided_by_sitting: 'The government decides this one. Put it to a vote.',
|
|
188
|
+
already_proposed: 'That change is already waiting.',
|
|
189
|
+
already_there: 'The rules already have that.',
|
|
190
|
+
not_there: 'The rules do not have that.',
|
|
191
|
+
no_change: 'That is how the rules already are.',
|
|
192
|
+
reason_required: 'Say why in one line.',
|
|
193
|
+
too_many_open: 'You have twenty proposals waiting. Wait for some to be decided.',
|
|
194
|
+
url_required: 'Add the link to its site.',
|
|
195
|
+
bad_url: 'That link does not look right.',
|
|
196
|
+
name_required: 'Give it a name.',
|
|
197
|
+
text_required: 'Write something first.',
|
|
198
|
+
no_longer_applies: 'The rules changed since this was proposed. Decline it and propose again.',
|
|
199
|
+
no_government: 'This project has no government; the owner decides.',
|
|
200
|
+
already_at_vote: 'It is already at a vote.',
|
|
201
|
+
});
|
|
202
|
+
const errorText = (code, fallback) => ERRORS[code] || fallback || 'That did not work. Try again.';
|
|
203
|
+
|
|
204
|
+
const lib = { stops, stopLabel, sliderHtml, pipelineHtml, rulesBodyHtml, proposalHtml, panelHtml, formHtml, proposalFromForm, costHtml, errorText };
|
|
205
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = lib;
|
|
206
|
+
if (root) root.OTBMarketWatchRules = lib;
|
|
207
|
+
})(typeof window !== 'undefined' ? window : null);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* market-watch-rules.css — the Market Watch rules page (task 1004941), built to
|
|
2
|
+
docs/design/mocks/market-watch/rules.html, option C (the pipeline map), on the hall's
|
|
3
|
+
own tokens rather than the mockup's literal colours and fonts. Everything is under the
|
|
4
|
+
`mwr-` prefix; the proposal card is the hall's ask card (hall-ask.css). */
|
|
5
|
+
|
|
6
|
+
.mwr { max-width: 1000px; margin: 0 auto; padding: 4px 0 48px; color: var(--ink); }
|
|
7
|
+
.mwr-mono { font-family: var(--font-mono); font-size: 12.5px; }
|
|
8
|
+
.mwr-faint { color: var(--ink-faint); }
|
|
9
|
+
.mwr-empty { color: var(--ink-soft); margin: 8px 0; }
|
|
10
|
+
|
|
11
|
+
/* the version slider */
|
|
12
|
+
.mwr-time { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
|
|
13
|
+
.mwr-time .mwr-mono { flex: none; white-space: nowrap; }
|
|
14
|
+
.mwr-time input { flex: 1; min-width: 0; height: 24px; margin: 0; accent-color: var(--accent); }
|
|
15
|
+
.mwr-ticks { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-faint); padding: 0 4px; }
|
|
16
|
+
.mwr-pill { font-size: 12px; padding: 2px 9px; border-radius: 999px; border: 1px solid var(--border); color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 50%; }
|
|
17
|
+
.mwr-pill--hot { background: var(--accent-tint); border-color: transparent; color: var(--accent-ink); }
|
|
18
|
+
|
|
19
|
+
/* the pipeline: five steps you tap */
|
|
20
|
+
.mwr-pipe { display: flex; align-items: stretch; overflow-x: auto; padding: 14px 8px 14px 2px; margin-top: 8px; scrollbar-width: thin; }
|
|
21
|
+
.mwr-node { flex: none; width: 150px; border: 1px solid var(--border); border-radius: 18px; padding: 12px; background: var(--bg-card); color: var(--ink); cursor: pointer; text-align: left; position: relative; display: flex; flex-direction: column; gap: 4px; font: inherit; }
|
|
22
|
+
.mwr-node:hover { border-color: var(--border-strong); }
|
|
23
|
+
.mwr-node:focus-visible { outline: 2px solid var(--focus, var(--accent)); outline-offset: 3px; }
|
|
24
|
+
.mwr-node__k { font-size: 12px; color: var(--ink-faint); }
|
|
25
|
+
.mwr-node__v { font-size: 22px; font-weight: 300; letter-spacing: -0.02em; }
|
|
26
|
+
.mwr-node__s { font-size: 12.5px; color: var(--ink-soft); }
|
|
27
|
+
.mwr-node[aria-pressed="true"] { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }
|
|
28
|
+
.mwr-node--changed { background: var(--accent-tint); }
|
|
29
|
+
.mwr-badge { position: absolute; top: -7px; right: -7px; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--accent); color: var(--accent-on); font-size: 11px; font-weight: 600; display: grid; place-items: center; box-sizing: border-box; }
|
|
30
|
+
.mwr-arrow { flex: none; width: 28px; display: grid; place-items: center; color: var(--ink-faint); line-height: 1; }
|
|
31
|
+
/* on a phone the steps stack, and the arrow between them points down */
|
|
32
|
+
@media (max-width: 640px) {
|
|
33
|
+
.mwr-pipe { flex-direction: column; overflow-x: visible; padding-right: 8px; }
|
|
34
|
+
.mwr-node { width: 100%; flex-direction: row; align-items: baseline; gap: 10px; }
|
|
35
|
+
.mwr-node__v { font-size: 18px; }
|
|
36
|
+
.mwr-arrow { width: auto; height: 20px; font-size: 0; }
|
|
37
|
+
.mwr-arrow::after { content: "\2193"; font-size: 15px; line-height: 20px; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* the tapped step */
|
|
41
|
+
.mwr-panel { border: 1px solid var(--border); border-radius: 20px; padding: 16px; margin-top: 8px; background: var(--bg-card); }
|
|
42
|
+
.mwr-panel__h { margin: 0 0 8px; font-weight: 300; font-size: 21px; letter-spacing: -0.02em; }
|
|
43
|
+
.mwr-viewing { margin: -4px 0 8px; font-size: 13px; }
|
|
44
|
+
.mwr-sub { margin: 12px 0 4px; font-size: 12px; letter-spacing: 0.06em; color: var(--ink-faint); }
|
|
45
|
+
.mwr-pre { white-space: pre-wrap; font-family: var(--font-mono); font-size: 12.5px; color: var(--ink-soft); margin: 0; }
|
|
46
|
+
.mwr-rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule-soft, var(--border)); }
|
|
47
|
+
.mwr-row { display: flex; align-items: center; gap: 12px; min-height: 46px; padding: 8px 2px; border-bottom: 1px solid var(--rule-soft, var(--border)); white-space: nowrap; overflow: hidden; }
|
|
48
|
+
.mwr-grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
49
|
+
.mwr-props { list-style: none; margin: 14px 0 0; padding: 0; }
|
|
50
|
+
.mwr-prop .mwr-pill { flex: none; max-width: none; }
|
|
51
|
+
.mwr-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
|
|
52
|
+
.mwr-propose { margin-top: 12px; }
|
|
53
|
+
|
|
54
|
+
/* buttons and the proposal form */
|
|
55
|
+
.mwr-btn { border: 1px solid var(--border-strong); background: transparent; color: var(--ink); border-radius: 999px; padding: 7px 14px; cursor: pointer; font: inherit; font-weight: 500; font-size: 14px; white-space: nowrap; }
|
|
56
|
+
.mwr-btn:hover { background: var(--hask-chip-pick, var(--bg-frame)); }
|
|
57
|
+
.mwr-btn--go { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
|
|
58
|
+
.mwr-btn--go:hover { background: var(--accent); filter: brightness(1.05); }
|
|
59
|
+
.mwr-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px 12px; margin-top: 12px; }
|
|
60
|
+
.mwr-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--ink-soft); min-width: 0; }
|
|
61
|
+
.mwr-field--wide { grid-column: 1 / -1; }
|
|
62
|
+
.mwr-field[hidden] { display: none; }
|
|
63
|
+
.mwr-field input, .mwr-field select, .mwr-field textarea { font: inherit; font-size: 14px; color: var(--ink); background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; min-width: 0; box-sizing: border-box; width: 100%; }
|
|
64
|
+
.mwr-form__go { grid-column: 1 / -1; display: flex; gap: 8px; flex-wrap: wrap; }
|
|
65
|
+
|
|
66
|
+
/* the cost of one run */
|
|
67
|
+
.mwr-lbl { font-size: 12px; letter-spacing: 0.06em; color: var(--ink-faint); margin: 26px 0 8px; font-weight: 400; }
|
|
68
|
+
.mwr-note { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
@@ -0,0 +1,54 @@
|
|
|
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 rules — {{worldName}}</title>
|
|
7
|
+
<meta name="description" content="How the weekly market watch works, step by step, in plain words: what it searches, what it reads, who it watches, how it scores and what it writes. Propose a change with a reason.">
|
|
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-07-02-shell1">
|
|
12
|
+
<link rel="stylesheet" href="/builders/hall-ask.css">
|
|
13
|
+
<link rel="stylesheet" href="/builders/market-watch-rules.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body data-page="market-watch-rules" data-page-title="Market Watch rules">
|
|
16
|
+
|
|
17
|
+
<div class="app">
|
|
18
|
+
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
19
|
+
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
20
|
+
<div class="app-body">
|
|
21
|
+
<header class="app-topbar" id="app-topbar"></header>
|
|
22
|
+
<div class="app-content">
|
|
23
|
+
<!-- The page's one h1, for screen readers: no page shows its head (task 1004836). -->
|
|
24
|
+
<div class="page-head page-head--room">
|
|
25
|
+
<h1 class="page-head__h">Market Watch rules</h1>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<main id="hall-main" class="mwr">
|
|
29
|
+
<div id="mwr-slider"></div>
|
|
30
|
+
<div class="mwr-pipe" id="mwr-pipe" aria-label="the watch, step by step"></div>
|
|
31
|
+
<section class="mwr-panel" id="mwr-panel" aria-live="polite"><p class="mwr-empty">Loading the watch rules…</p></section>
|
|
32
|
+
<h2 class="mwr-lbl">cost of one run, by step</h2>
|
|
33
|
+
<div id="mwr-cost"></div>
|
|
34
|
+
</main>
|
|
35
|
+
|
|
36
|
+
<footer class="app-foot" id="app-foot"></footer>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
42
|
+
|
|
43
|
+
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
44
|
+
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
45
|
+
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
46
|
+
<script src="/builders/shell.js?v=2026-10-25-escape"></script>
|
|
47
|
+
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
48
|
+
<script src="/builders/palette.js" defer></script>
|
|
49
|
+
<script src="/builders/hall-kit.js"></script>
|
|
50
|
+
<script src="/builders/hall-ask.js"></script>
|
|
51
|
+
<script src="/builders/market-watch-rules-lib.js"></script>
|
|
52
|
+
<script src="/builders/market-watch-rules.js"></script>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|