@bongos/core 1.24.50 → 1.24.52
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 +104 -89
- package/docs/copy-inventory.md +259 -277
- package/docs/copy-registry.json +461 -634
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +35 -2
- package/docs/page-readings.json +518 -547
- package/modules/hall-ui/public/ai-studio.html +1 -0
- package/modules/hall-ui/public/approval-queue.css +3 -11
- package/modules/hall-ui/public/approval-queue.js +13 -20
- package/modules/hall-ui/public/atlas.html +1 -0
- package/modules/hall-ui/public/blockers.html +1 -0
- package/modules/hall-ui/public/board-room.html +1 -0
- package/modules/hall-ui/public/bulk-toolbar.js +5 -1
- package/modules/hall-ui/public/city.html +1 -0
- package/modules/hall-ui/public/collab.html +1 -0
- package/modules/hall-ui/public/commands.html +1 -0
- package/modules/hall-ui/public/controls.css +100 -0
- package/modules/hall-ui/public/copy-desk.css +7 -11
- package/modules/hall-ui/public/copy-desk.html +17 -16
- package/modules/hall-ui/public/copy-desk.js +13 -9
- package/modules/hall-ui/public/deploy.html +1 -0
- package/modules/hall-ui/public/goal-inbox.js +32 -17
- package/modules/hall-ui/public/goal-map.html +1 -0
- package/modules/hall-ui/public/goals-page.js +7 -1
- package/modules/hall-ui/public/goals.html +1 -0
- package/modules/hall-ui/public/government.html +1 -0
- package/modules/hall-ui/public/government.js +10 -2
- package/modules/hall-ui/public/hall-dialog.js +255 -0
- package/modules/hall-ui/public/hall-kit.js +9 -2
- package/modules/hall-ui/public/idea.html +1 -0
- package/modules/hall-ui/public/ideas.html +1 -0
- package/modules/hall-ui/public/index.html +1 -0
- package/modules/hall-ui/public/market-watch-item.html +2 -1
- package/modules/hall-ui/public/market-watch-lib.js +16 -9
- package/modules/hall-ui/public/market-watch-rules.html +1 -0
- package/modules/hall-ui/public/market-watch.css +2 -3
- package/modules/hall-ui/public/market-watch.html +2 -1
- package/modules/hall-ui/public/modules.html +1 -0
- package/modules/hall-ui/public/money.html +1 -0
- package/modules/hall-ui/public/my-agents.html +1 -0
- package/modules/hall-ui/public/oversight.css +3 -33
- package/modules/hall-ui/public/primer.html +1 -0
- package/modules/hall-ui/public/profile.html +1 -0
- package/modules/hall-ui/public/project-settings.html +1 -0
- package/modules/hall-ui/public/roster.html +1 -0
- package/modules/hall-ui/public/settings-ai-keys.js +5 -1
- package/modules/hall-ui/public/settings-passkeys.js +5 -1
- package/modules/hall-ui/public/settings-sessions.js +10 -2
- package/modules/hall-ui/public/settings.html +1 -0
- package/modules/hall-ui/public/settings.js +5 -1
- package/modules/hall-ui/public/street.html +1 -0
- package/modules/hall-ui/public/studio.css +6 -32
- package/modules/hall-ui/public/studio.html +4 -3
- package/modules/hall-ui/public/studio.js +40 -35
- package/modules/hall-ui/public/style.css +2 -8
- package/modules/hall-ui/public/task.html +1 -0
- package/modules/hall-ui/public/task.js +26 -12
- package/modules/hall-ui/public/thinking.html +1 -0
- package/modules/hall-ui/public/tweak-editor.html +1 -0
- package/modules/hall-ui/public/watch.html +1 -0
- package/modules/hall-ui/public/work.html +1 -0
- package/modules/hall-ui/public/work.js +7 -1
- package/modules/status-ui/public/index.html +43 -43
- package/modules/status-ui/public/status.js +26 -27
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/bulk_toolbar.mjs +1 -1
- package/tests/hall_approval_queue.mjs +8 -2
- package/tests/hall_audit.mjs +1 -1
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_collab_world.mjs +9 -6
- package/tests/hall_dialog.mjs +401 -0
- package/tests/hall_error_envelope.mjs +1 -1
- package/tests/hall_oversight_world.mjs +9 -7
- package/tests/hall_record_world.mjs +1 -1
- package/tests/hall_street.mjs +1 -1
- package/tests/hall_studio_home.mjs +1 -1
- package/tests/home_attention_cards.mjs +2 -0
- package/tests/kind_axis_unset.mjs +1 -1
- package/tests/loading_mark.mjs +6 -6
- package/tests/market_watch_pages.mjs +3 -1
- package/tests/status_company_attribution.mjs +1 -1
- package/tests/status_page_wording.mjs +129 -0
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>{{productName}} —
|
|
7
|
-
<meta name="description" content="
|
|
6
|
+
<title>{{productName}} — build status</title>
|
|
7
|
+
<meta name="description" content="How {{worldName}} is being built, in the open: what shipped, what it cost, and who built it.">
|
|
8
8
|
<link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
|
|
9
9
|
<link rel="apple-touch-icon" href="/modules/ui-design/public/brand/apple-touch-icon.png">
|
|
10
10
|
<!-- The faces are the pack's: the server (and the off-box mirror) splice the pack's
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
|
|
31
31
|
<section class="herm" aria-labelledby="herm-title">
|
|
32
32
|
<h1 id="herm-title">{{worldName}}</h1>
|
|
33
|
-
<p class="herm__epigraph">
|
|
34
|
-
<p class="herm__sub">
|
|
33
|
+
<p class="herm__epigraph">Build status, in the open</p>
|
|
34
|
+
<p class="herm__sub">Everything here is built in public. This page shows what shipped, what it cost, and who built it, and it updates every minute.</p>
|
|
35
35
|
<p class="herm__meta">
|
|
36
|
-
<a href="{{publicOrigin}}" target="_blank" rel="noopener">
|
|
36
|
+
<a href="{{publicOrigin}}" target="_blank" rel="noopener">Go to {{productName}} →</a>
|
|
37
37
|
<span class="dot" aria-hidden="true">·</span>
|
|
38
|
-
<span class="herm__updated" id="last-updated">
|
|
38
|
+
<span class="herm__updated" id="last-updated">Checking for updates…</span>
|
|
39
39
|
</p>
|
|
40
40
|
</section>
|
|
41
41
|
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
(public-builders/style.css .hall-nav__*). Built from a SECTIONS constant
|
|
44
44
|
in status.js. Empty until populated — [hidden] keeps the strip from
|
|
45
45
|
flashing as an empty band before chips arrive. -->
|
|
46
|
-
<nav id="status-nav" class="status-nav" aria-label="Sections of the
|
|
46
|
+
<nav id="status-nav" class="status-nav" aria-label="Sections of the page" hidden>
|
|
47
47
|
<div class="status-nav__inner">
|
|
48
48
|
<ul class="status-nav__list" id="status-nav-list"></ul>
|
|
49
49
|
</div>
|
|
@@ -57,22 +57,22 @@
|
|
|
57
57
|
loadHeadline() in status.js, fed by the existing public endpoints
|
|
58
58
|
(recent-shipped, cost-summary, grades). -->
|
|
59
59
|
<section class="tablet tablet--headline" id="headline-tablet" aria-labelledby="headline-heading">
|
|
60
|
-
<h2 id="headline-heading" class="visually-hidden">
|
|
60
|
+
<h2 id="headline-heading" class="visually-hidden">At a glance</h2>
|
|
61
61
|
<div class="headline-grid">
|
|
62
62
|
<div class="stat headline-stat" id="headline-shipped">
|
|
63
63
|
<div class="stat__num" id="headline-shipped-num">—</div>
|
|
64
|
-
<div class="stat__label">
|
|
64
|
+
<div class="stat__label">tasks shipped, last 7 days</div>
|
|
65
65
|
</div>
|
|
66
66
|
<div class="stat headline-stat" id="headline-treasury">
|
|
67
67
|
<div class="stat__num" id="headline-treasury-num">—</div>
|
|
68
|
-
<div class="stat__label" id="headline-treasury-label">of
|
|
68
|
+
<div class="stat__label" id="headline-treasury-label">of this year's budget spent</div>
|
|
69
69
|
<div class="headline-bar" aria-hidden="true">
|
|
70
70
|
<div class="headline-bar__fill" id="headline-treasury-bar" style="width: 0%"></div>
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
|
73
73
|
<div class="stat headline-stat" id="headline-grade">
|
|
74
74
|
<div class="stat__num" id="headline-grade-num">—</div>
|
|
75
|
-
<div class="stat__label">passed
|
|
75
|
+
<div class="stat__label">passed review, last 7 days</div>
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
</section>
|
|
@@ -80,49 +80,49 @@
|
|
|
80
80
|
<section class="tablet" id="leader-tablet" aria-labelledby="leader-heading">
|
|
81
81
|
<div class="tablet__head">
|
|
82
82
|
<div class="tablet__heading">
|
|
83
|
-
<h2 id="leader-heading">
|
|
84
|
-
<p class="tablet__subtitle">
|
|
83
|
+
<h2 id="leader-heading">Top builders</h2>
|
|
84
|
+
<p class="tablet__subtitle">Ranked by the {{currency}} each has earned.</p>
|
|
85
85
|
</div>
|
|
86
86
|
</div>
|
|
87
87
|
<ol id="leaderboard" class="leader">
|
|
88
|
-
<li class="empty">No
|
|
88
|
+
<li class="empty">No builders yet — be the first.</li>
|
|
89
89
|
</ol>
|
|
90
90
|
</section>
|
|
91
91
|
|
|
92
92
|
<section class="tablet" id="foundry-tablet" aria-labelledby="foundry-heading">
|
|
93
93
|
<div class="tablet__head">
|
|
94
94
|
<div class="tablet__heading">
|
|
95
|
-
<h2 id="foundry-heading">
|
|
96
|
-
<p class="tablet__subtitle">
|
|
95
|
+
<h2 id="foundry-heading">How the work is changing</h2>
|
|
96
|
+
<p class="tablet__subtitle">Tasks shipped and the time each took, over the last 90 days. The project as a whole, never one builder.</p>
|
|
97
97
|
</div>
|
|
98
98
|
</div>
|
|
99
99
|
<div class="cost-summary" id="foundry-stats">
|
|
100
|
-
<div class="stat"><div class="stat__num" id="foundry-shipped">—</div><div class="stat__label">
|
|
101
|
-
<div class="stat"><div class="stat__num" id="foundry-builders">—</div><div class="stat__label">
|
|
102
|
-
<div class="stat"><div class="stat__num" id="foundry-duration">—</div><div class="stat__label">
|
|
100
|
+
<div class="stat"><div class="stat__num" id="foundry-shipped">—</div><div class="stat__label">tasks shipped (90 days)</div></div>
|
|
101
|
+
<div class="stat"><div class="stat__num" id="foundry-builders">—</div><div class="stat__label">active builders</div></div>
|
|
102
|
+
<div class="stat"><div class="stat__num" id="foundry-duration">—</div><div class="stat__label">average time per task</div></div>
|
|
103
103
|
</div>
|
|
104
104
|
<div class="foundry__charts" id="foundry-charts"></div>
|
|
105
105
|
<div class="foundry__kinds" id="foundry-kinds"></div>
|
|
106
|
-
<p class="chart-note" id="foundry-empty" hidden>Not enough shipped
|
|
106
|
+
<p class="chart-note" id="foundry-empty" hidden>Not enough tasks have shipped yet to draw a trend.</p>
|
|
107
107
|
</section>
|
|
108
108
|
|
|
109
109
|
<section class="tablet" id="versions-tablet" aria-labelledby="versions-heading">
|
|
110
110
|
<div class="tablet__head">
|
|
111
111
|
<div class="tablet__heading">
|
|
112
|
-
<h2 id="versions-heading">
|
|
113
|
-
<p class="tablet__subtitle">The
|
|
112
|
+
<h2 id="versions-heading">In progress</h2>
|
|
113
|
+
<p class="tablet__subtitle">The versions being built now.</p>
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
116
116
|
<div id="versions" class="versions">
|
|
117
|
-
<p class="empty loading-mark" role="status">
|
|
117
|
+
<p class="empty loading-mark" role="status">Loading the versions…</p>
|
|
118
118
|
</div>
|
|
119
119
|
</section>
|
|
120
120
|
|
|
121
121
|
<section class="tablet" id="cost-tablet" aria-labelledby="cost-heading">
|
|
122
122
|
<div class="tablet__head">
|
|
123
123
|
<div class="tablet__heading">
|
|
124
|
-
<h2 id="cost-heading">
|
|
125
|
-
<p class="tablet__subtitle">
|
|
124
|
+
<h2 id="cost-heading">Spending</h2>
|
|
125
|
+
<p class="tablet__subtitle">What the build has cost, against the value it has produced.</p>
|
|
126
126
|
</div>
|
|
127
127
|
</div>
|
|
128
128
|
<div class="cost-summary">
|
|
@@ -132,21 +132,21 @@
|
|
|
132
132
|
</div>
|
|
133
133
|
<div class="stat">
|
|
134
134
|
<div class="stat__num" id="value-total">—</div>
|
|
135
|
-
<div class="stat__label">value
|
|
135
|
+
<div class="stat__label">value produced</div>
|
|
136
136
|
</div>
|
|
137
137
|
<div class="stat">
|
|
138
138
|
<div class="stat__num" id="cost-budget">5,000</div>
|
|
139
|
-
<div class="stat__label">
|
|
139
|
+
<div class="stat__label">yearly budget</div>
|
|
140
140
|
</div>
|
|
141
141
|
<div class="stat">
|
|
142
142
|
<div class="stat__num" id="cost-percent">—</div>
|
|
143
|
-
<div class="stat__label">of the
|
|
143
|
+
<div class="stat__label">of the budget</div>
|
|
144
144
|
</div>
|
|
145
145
|
</div>
|
|
146
146
|
<div class="chart-wrap">
|
|
147
|
-
<canvas id="cost-chart" aria-label="cumulative {{currency}} spent against cumulative value
|
|
147
|
+
<canvas id="cost-chart" aria-label="cumulative {{currency}} spent against cumulative value produced, day by day" role="img"></canvas>
|
|
148
148
|
</div>
|
|
149
|
-
<p class="chart-note">
|
|
149
|
+
<p class="chart-note">When the value line sits above the spend line, the work is worth more than it cost. <em>The chart always shows the whole project; the filters below change only the breakdown.</em></p>
|
|
150
150
|
<div class="cost-filters" role="group" aria-label="Filter the spend breakdown" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin:6px 0 10px;">
|
|
151
151
|
<div class="tablet__filter">
|
|
152
152
|
<label for="cost-filter-source" class="tablet__filter-label">Source</label>
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
</div>
|
|
173
173
|
<p id="cost-filter-note" class="cost-split" aria-live="polite"></p>
|
|
174
174
|
<div id="cost-categories" class="cost-cats"></div>
|
|
175
|
-
<p class="chart-note">
|
|
175
|
+
<p class="chart-note">Spending by builder. <em>Unattributed</em> is shared cost: the infrastructure and domains the whole project pays for, plus early spending from before costs were tracked per builder. It is never charged to one builder.</p>
|
|
176
176
|
<p id="cost-attribution-split" class="cost-split" aria-live="polite"></p>
|
|
177
177
|
<div id="cost-builders" class="cost-cats"></div>
|
|
178
178
|
</section>
|
|
@@ -180,12 +180,12 @@
|
|
|
180
180
|
<section class="tablet" id="shipped-tablet" aria-labelledby="shipped-heading">
|
|
181
181
|
<div class="tablet__head">
|
|
182
182
|
<div class="tablet__heading">
|
|
183
|
-
<h2 id="shipped-heading">
|
|
184
|
-
<p class="tablet__subtitle">
|
|
183
|
+
<h2 id="shipped-heading">Recently shipped</h2>
|
|
184
|
+
<p class="tablet__subtitle">The newest finished tasks.</p>
|
|
185
185
|
</div>
|
|
186
186
|
<div class="tablet__filter">
|
|
187
187
|
<label for="shipped-range" class="tablet__filter-label">Window</label>
|
|
188
|
-
<select id="shipped-range" class="tablet__filter-select" aria-label="Filter
|
|
188
|
+
<select id="shipped-range" class="tablet__filter-select" aria-label="Filter shipped tasks by date range">
|
|
189
189
|
<option value="7">Last 7 days</option>
|
|
190
190
|
<option value="30" selected>Last 30 days</option>
|
|
191
191
|
<option value="90">Last 90 days</option>
|
|
@@ -194,9 +194,9 @@
|
|
|
194
194
|
</div>
|
|
195
195
|
</div>
|
|
196
196
|
<ul id="shipped" class="shipped">
|
|
197
|
-
<li class="empty">
|
|
197
|
+
<li class="empty">Nothing has shipped yet.</li>
|
|
198
198
|
</ul>
|
|
199
|
-
<nav id="shipped-pager" class="shipped-pager" hidden aria-label="
|
|
199
|
+
<nav id="shipped-pager" class="shipped-pager" hidden aria-label="Recently shipped pages">
|
|
200
200
|
<button type="button" id="shipped-prev" class="btn btn--ghost btn--sm shipped-pager__btn" aria-label="Previous page">‹ earlier page</button>
|
|
201
201
|
<span id="shipped-pageinfo" class="shipped-pager__info" aria-live="polite">page 1 of 1</span>
|
|
202
202
|
<button type="button" id="shipped-next" class="btn btn--ghost btn--sm shipped-pager__btn" aria-label="Next page">later page ›</button>
|
|
@@ -206,30 +206,30 @@
|
|
|
206
206
|
<section class="tablet tablet--wide" id="world-tablet" aria-labelledby="world-heading">
|
|
207
207
|
<div class="tablet__head">
|
|
208
208
|
<div class="tablet__heading">
|
|
209
|
-
<h2 id="world-heading">
|
|
210
|
-
<p class="tablet__subtitle">
|
|
209
|
+
<h2 id="world-heading">Snapshot</h2>
|
|
210
|
+
<p class="tablet__subtitle">{{worldName}} as it looks today, rendered from the live project.</p>
|
|
211
211
|
</div>
|
|
212
212
|
</div>
|
|
213
213
|
<div id="world-snapshot" class="world-snapshot">
|
|
214
|
-
<p class="empty loading-mark" role="status">
|
|
214
|
+
<p class="empty loading-mark" role="status">Loading the latest snapshot…</p>
|
|
215
215
|
</div>
|
|
216
216
|
<!-- #409: snapshot history collapsed behind a toggle. The latest snapshot
|
|
217
217
|
above is the prime view; the timeline of older snapshots is reference
|
|
218
218
|
material that previously ate ~300px of prime real estate. -->
|
|
219
219
|
<details class="world-history" id="world-history">
|
|
220
|
-
<summary class="world-history__summary">Show
|
|
220
|
+
<summary class="world-history__summary">Show earlier snapshots</summary>
|
|
221
221
|
<div id="world-timeline" class="world-timeline" aria-label="World snapshots over time"></div>
|
|
222
|
-
<p class="world-note" id="world-note" hidden>A new
|
|
222
|
+
<p class="world-note" id="world-note" hidden>A new snapshot is taken each time the project changes. Scroll to see how {{worldName}} has grown.</p>
|
|
223
223
|
</details>
|
|
224
224
|
</section>
|
|
225
225
|
|
|
226
226
|
</main>
|
|
227
227
|
|
|
228
228
|
<footer class="foot">
|
|
229
|
-
<p class="foot__seal">
|
|
229
|
+
<p class="foot__seal">{{productName}} build status</p>
|
|
230
230
|
<!-- task 1002853: every company field is optional, so each clause drops whole when its pack field is empty -->
|
|
231
231
|
<!--brand-if:companyName--><p>{{worldName}} is the work of <!--brand-if:companySite--><a href="{{companySite}}" target="_blank" rel="noopener"><!--/brand-if:companySite--><strong>{{companyName}}</strong><!--brand-if:companySite--></a><!--/brand-if:companySite--><!--brand-if:companyTagline-->, {{companyTagline}}<!--/brand-if:companyTagline-->.</p><!--/brand-if:companyName-->
|
|
232
|
-
<p>{{Currency}} are
|
|
232
|
+
<p>{{Currency}} are awarded by review. They measure work done and are never money.</p>
|
|
233
233
|
</footer>
|
|
234
234
|
|
|
235
235
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
@@ -56,10 +56,9 @@ function fmtUsd(n) {
|
|
|
56
56
|
return Math.round(Number(n)).toLocaleString();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function
|
|
59
|
+
function fmtMoney(n) {
|
|
60
60
|
if (n == null) return '—';
|
|
61
|
-
|
|
62
|
-
return `${Math.round(Number(n)).toLocaleString()} dr.`;
|
|
61
|
+
return `$${fmtUsd(n)}`;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
function fmtRelativeTime(iso) {
|
|
@@ -99,13 +98,13 @@ async function fetchJson(path) {
|
|
|
99
98
|
// in ~756px of content width without wrapping.
|
|
100
99
|
|
|
101
100
|
const SECTIONS = [
|
|
102
|
-
{ id: 'headline-tablet', label: '
|
|
103
|
-
{ id: 'leader-tablet', label: '
|
|
104
|
-
{ id: 'foundry-tablet', label: '
|
|
105
|
-
{ id: 'versions-tablet', label: '
|
|
106
|
-
{ id: 'cost-tablet', label: '
|
|
107
|
-
{ id: 'shipped-tablet', label: '
|
|
108
|
-
{ id: 'world-tablet', label: '
|
|
101
|
+
{ id: 'headline-tablet', label: 'Overview' },
|
|
102
|
+
{ id: 'leader-tablet', label: 'Builders' },
|
|
103
|
+
{ id: 'foundry-tablet', label: 'Trends' },
|
|
104
|
+
{ id: 'versions-tablet', label: 'In progress' },
|
|
105
|
+
{ id: 'cost-tablet', label: 'Spending' },
|
|
106
|
+
{ id: 'shipped-tablet', label: 'Shipped' },
|
|
107
|
+
{ id: 'world-tablet', label: 'Snapshot' },
|
|
109
108
|
];
|
|
110
109
|
|
|
111
110
|
let navObserver = null;
|
|
@@ -354,7 +353,7 @@ async function loadVersions() {
|
|
|
354
353
|
if (rows.length === 0) {
|
|
355
354
|
const p = document.createElement('p');
|
|
356
355
|
p.className = 'empty';
|
|
357
|
-
p.textContent = '
|
|
356
|
+
p.textContent = 'Nothing is in progress right now.';
|
|
358
357
|
els.versions.appendChild(p);
|
|
359
358
|
return;
|
|
360
359
|
}
|
|
@@ -497,7 +496,7 @@ function renderCostFilterNote(data) {
|
|
|
497
496
|
const filtered = Number(data.filtered_total_usd) || 0;
|
|
498
497
|
const whole = Number(data.total_usd) || 0;
|
|
499
498
|
const pct = whole > 0 ? Math.round((filtered / whole) * 100) : 0;
|
|
500
|
-
note.textContent = `Breakdown filtered to ${parts.join(' · ')}: ${
|
|
499
|
+
note.textContent = `Breakdown filtered to ${parts.join(' · ')}: ${fmtMoney(filtered)} of ${fmtMoney(whole)} (${pct}% of all spend).`;
|
|
501
500
|
}
|
|
502
501
|
|
|
503
502
|
async function loadCosts() {
|
|
@@ -522,7 +521,7 @@ async function loadCosts() {
|
|
|
522
521
|
if (cats.length === 0) {
|
|
523
522
|
const tag = document.createElement('span');
|
|
524
523
|
tag.className = 'cost-cat';
|
|
525
|
-
tag.textContent = `No ${curLabel()} yet
|
|
524
|
+
tag.textContent = `No ${curLabel()} spent yet.`;
|
|
526
525
|
els.costCats.appendChild(tag);
|
|
527
526
|
} else {
|
|
528
527
|
for (const c of cats) {
|
|
@@ -531,7 +530,7 @@ async function loadCosts() {
|
|
|
531
530
|
const strong = document.createElement('strong');
|
|
532
531
|
strong.textContent = c.category;
|
|
533
532
|
tag.appendChild(strong);
|
|
534
|
-
tag.append(` · ${
|
|
533
|
+
tag.append(` · ${fmtMoney(c.total_usd)}`);
|
|
535
534
|
els.costCats.appendChild(tag);
|
|
536
535
|
}
|
|
537
536
|
}
|
|
@@ -557,7 +556,7 @@ async function loadCosts() {
|
|
|
557
556
|
if (total > 0) {
|
|
558
557
|
const pct = Math.round((unattributed / total) * 100);
|
|
559
558
|
els.costAttributionSplit.textContent =
|
|
560
|
-
`Attributed to builders: ${
|
|
559
|
+
`Attributed to builders: ${fmtMoney(attributed)} · Shared (unattributed): ${fmtMoney(unattributed)} (${pct}%).`;
|
|
561
560
|
} else {
|
|
562
561
|
els.costAttributionSplit.textContent = '';
|
|
563
562
|
}
|
|
@@ -577,14 +576,14 @@ async function loadCosts() {
|
|
|
577
576
|
: (b.display_name || b.github_login || `builder #${b.builder_id}`);
|
|
578
577
|
strong.textContent = label;
|
|
579
578
|
tag.appendChild(strong);
|
|
580
|
-
tag.append(` · ${
|
|
579
|
+
tag.append(` · ${fmtMoney(b.total_usd)}`);
|
|
581
580
|
els.costBuilders.appendChild(tag);
|
|
582
581
|
rendered++;
|
|
583
582
|
}
|
|
584
583
|
if (rendered === 0) {
|
|
585
584
|
const tag = document.createElement('span');
|
|
586
585
|
tag.className = 'cost-cat';
|
|
587
|
-
tag.textContent = `No ${curLabel()}
|
|
586
|
+
tag.textContent = `No ${curLabel()} attributed to a builder yet.`;
|
|
588
587
|
els.costBuilders.appendChild(tag);
|
|
589
588
|
}
|
|
590
589
|
}
|
|
@@ -666,7 +665,7 @@ async function loadCosts() {
|
|
|
666
665
|
borderWidth: 1.5,
|
|
667
666
|
},
|
|
668
667
|
{
|
|
669
|
-
label: 'value
|
|
668
|
+
label: 'value produced',
|
|
670
669
|
data: cumValue,
|
|
671
670
|
fill: false,
|
|
672
671
|
borderColor: t.info,
|
|
@@ -702,7 +701,7 @@ async function loadCosts() {
|
|
|
702
701
|
bodyFont: { family: t.body, size: 13 },
|
|
703
702
|
padding: 10,
|
|
704
703
|
callbacks: {
|
|
705
|
-
label: (c) => `${c.dataset.label}: ${c.parsed.y.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}
|
|
704
|
+
label: (c) => `${c.dataset.label}: ${c.parsed.y.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`,
|
|
706
705
|
},
|
|
707
706
|
},
|
|
708
707
|
},
|
|
@@ -716,7 +715,7 @@ async function loadCosts() {
|
|
|
716
715
|
color: t.inkSoft,
|
|
717
716
|
},
|
|
718
717
|
ticks: {
|
|
719
|
-
callback: (v) => `${v}
|
|
718
|
+
callback: (v) => `${v}`,
|
|
720
719
|
font: { family: t.body, size: 12 },
|
|
721
720
|
color: t.inkSoft,
|
|
722
721
|
},
|
|
@@ -808,7 +807,7 @@ function renderShippedPage() {
|
|
|
808
807
|
if (items.length === 0) {
|
|
809
808
|
const li = document.createElement('li');
|
|
810
809
|
li.className = 'empty';
|
|
811
|
-
li.textContent = '
|
|
810
|
+
li.textContent = 'Nothing shipped in this window.';
|
|
812
811
|
els.shipped.appendChild(li);
|
|
813
812
|
// GDS #191: pad with invisible rows so an empty window still occupies
|
|
814
813
|
// the same vertical space as a full page. Keeps the section height
|
|
@@ -850,7 +849,7 @@ function renderShippedPage() {
|
|
|
850
849
|
const by = t.display_name || t.github_login;
|
|
851
850
|
// task 1004432: an approved page is the artist's; the session that applied it is a helper.
|
|
852
851
|
const helper = t.helper_display_name || t.helper_login;
|
|
853
|
-
meta.textContent = `${by ? `
|
|
852
|
+
meta.textContent = `${by ? `shipped by ${by} · ` : ''}${helper ? `with help from ${helper} · ` : ''}${fmtRelativeTime(t.shipped_at)}`;
|
|
854
853
|
li.appendChild(title);
|
|
855
854
|
if (thumb) li.appendChild(thumb);
|
|
856
855
|
li.appendChild(meta);
|
|
@@ -1063,7 +1062,7 @@ async function loadLeaderboard() {
|
|
|
1063
1062
|
function stampRefreshTime() {
|
|
1064
1063
|
if (!els.lastUpdated) return;
|
|
1065
1064
|
const t = new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
|
|
1066
|
-
els.lastUpdated.textContent = `
|
|
1065
|
+
els.lastUpdated.textContent = `updated at ${t}`;
|
|
1067
1066
|
}
|
|
1068
1067
|
|
|
1069
1068
|
// ---------- the Foundry — ecosystem dev curve (#291, C5) ----------
|
|
@@ -1121,8 +1120,8 @@ async function loadEcosystem() {
|
|
|
1121
1120
|
const shipVals = (data.ship_rate || []).map((r) => Number(r.n));
|
|
1122
1121
|
const durVals = (data.duration_trend || []).map((r) => Number(r.avg_actual));
|
|
1123
1122
|
chartsEl.innerHTML =
|
|
1124
|
-
`<div class="foundry__chart"><div class="foundry__chart-title">
|
|
1125
|
-
`<div class="foundry__chart"><div class="foundry__chart-title">
|
|
1123
|
+
`<div class="foundry__chart"><div class="foundry__chart-title">Tasks shipped per day</div>${foundrySparkline(shipVals, 'ink') || '<p class="empty">no data</p>'}</div>` +
|
|
1124
|
+
`<div class="foundry__chart"><div class="foundry__chart-title">Average time per task (min)</div>${foundrySparkline(durVals, 'info') || '<p class="empty">no data</p>'}</div>`;
|
|
1126
1125
|
|
|
1127
1126
|
const kinds = (data.top_kinds || []).slice(0, 6);
|
|
1128
1127
|
if (kindsEl) {
|
|
@@ -1218,13 +1217,13 @@ function renderStatusDeepCard(kind, id, state) {
|
|
|
1218
1217
|
const label = `#${id}`;
|
|
1219
1218
|
panel.appendChild(mk('p', 'deeplink__kind', kind.toUpperCase()));
|
|
1220
1219
|
if (state.loading) {
|
|
1221
|
-
panel.appendChild(mk('p', 'deeplink__title', `
|
|
1220
|
+
panel.appendChild(mk('p', 'deeplink__title', `Looking up ${label}…`));
|
|
1222
1221
|
} else if (state.notFound) {
|
|
1223
1222
|
panel.appendChild(mk('p', 'deeplink__title', `${label} — no such ${kind}`));
|
|
1224
1223
|
panel.appendChild(mk('p', 'deeplink__note', 'Records number tasks, ideas, and blockers separately from GitHub pull requests — this may be a PR, not a record here.'));
|
|
1225
1224
|
} else if (state.error) {
|
|
1226
1225
|
panel.appendChild(mk('p', 'deeplink__title', `Could not resolve ${label}`));
|
|
1227
|
-
panel.appendChild(mk('p', 'deeplink__note', 'The
|
|
1226
|
+
panel.appendChild(mk('p', 'deeplink__note', 'The server could not be reached. Try again in a moment.'));
|
|
1228
1227
|
} else {
|
|
1229
1228
|
const r = state.ref || {};
|
|
1230
1229
|
panel.appendChild(mk('p', 'deeplink__title', r.title ? `${label} — ${r.title}` : label));
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.52",
|
|
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.52",
|
|
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.52",
|
|
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
|
@@ -11271,5 +11271,17 @@
|
|
|
11271
11271
|
"id": "1005089",
|
|
11272
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
11273
|
}
|
|
11274
|
+
],
|
|
11275
|
+
"1.24.51": [
|
|
11276
|
+
{
|
|
11277
|
+
"id": "1005090",
|
|
11278
|
+
"text": "The public status page now talks plainly: 'Top builders', 'Recently shipped', 'Spending' instead of the old ancient-city words, and costs show in dollars."
|
|
11279
|
+
}
|
|
11280
|
+
],
|
|
11281
|
+
"1.24.52": [
|
|
11282
|
+
{
|
|
11283
|
+
"id": "1005085",
|
|
11284
|
+
"text": "The hall's pop-up questions now use one Cloud Bongos dialog instead of the browser's grey box: every 'are you sure?' says plainly what will happen, and the six home-made pop-ups on different pages now share one look and the sa"
|
|
11285
|
+
}
|
|
11274
11286
|
]
|
|
11275
11287
|
}
|
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.52'; // 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');
|
package/tests/bulk_toolbar.mjs
CHANGED
|
@@ -46,7 +46,7 @@ function stubPage(present) {
|
|
|
46
46
|
const els = {};
|
|
47
47
|
for (const id of present) els[id] = { hidden: true, disabled: false, textContent: '', title: '', listeners: {}, addEventListener(t, fn) { this.listeners[t] = fn; } };
|
|
48
48
|
globalThis.document = { getElementById: (id) => els[id] || null };
|
|
49
|
-
globalThis.window = { confirm: () => true };
|
|
49
|
+
globalThis.window = { OTBDialog: { confirm: async () => true } }; // the branded confirm (task 1005085)
|
|
50
50
|
globalThis.location = loc;
|
|
51
51
|
return els;
|
|
52
52
|
}
|
|
@@ -90,6 +90,9 @@ function bootPanel({ answers = {}, writes = null, mode = 'light' } = {}) {
|
|
|
90
90
|
getElementById: byId,
|
|
91
91
|
createElement: (tag) => augment(makeEl(tag, {})),
|
|
92
92
|
get activeElement() { return boot.focused || null; },
|
|
93
|
+
// the shared dialog (hall-dialog.js, task 1005085) hears its keys on the document
|
|
94
|
+
listeners: {},
|
|
95
|
+
addEventListener(type, fn) { (this.listeners[type] = this.listeners[type] || []).push(fn); },
|
|
93
96
|
};
|
|
94
97
|
const api = {
|
|
95
98
|
// The REAL client's shape: request(method, path, { body }). This fake once
|
|
@@ -110,6 +113,7 @@ function bootPanel({ answers = {}, writes = null, mode = 'light' } = {}) {
|
|
|
110
113
|
win.window = win;
|
|
111
114
|
vm.runInContext(read('brand-holes.js'), ctx, { filename: 'brand-holes.js' });
|
|
112
115
|
vm.runInContext(read('approval-queue-lib.js'), ctx, { filename: 'approval-queue-lib.js' });
|
|
116
|
+
vm.runInContext(read('hall-dialog.js'), ctx, { filename: 'hall-dialog.js' }); // the shared dialog behaviour (task 1005085)
|
|
113
117
|
vm.runInContext(read('approval-queue.js'), ctx, { filename: 'approval-queue.js' });
|
|
114
118
|
const panel = win.OTBApprovalQueue.mount({
|
|
115
119
|
api,
|
|
@@ -124,7 +128,9 @@ function bootPanel({ answers = {}, writes = null, mode = 'light' } = {}) {
|
|
|
124
128
|
const q = (sel) => scrim.querySelector(sel);
|
|
125
129
|
const qa = (sel) => scrim.querySelectorAll(sel);
|
|
126
130
|
const click = async (el) => { assert.ok(el, 'the control exists'); scrim.fire('click', { target: el }); await settle(); await settle(); };
|
|
127
|
-
boot = { body, scrim, q, qa, click, requests, counts, panel, classes, win, focused: null
|
|
131
|
+
boot = { body, scrim, q, qa, click, requests, counts, panel, classes, win, focused: null,
|
|
132
|
+
// a key pressed anywhere in the page reaches the document, where the shared dialog listens
|
|
133
|
+
key: (ev) => (documentObj.listeners.keydown || []).forEach((fn) => fn({ preventDefault() {}, stopPropagation() {}, ...ev })) };
|
|
128
134
|
return boot;
|
|
129
135
|
}
|
|
130
136
|
|
|
@@ -330,7 +336,7 @@ await test('empty, closed with Escape, and focus goes back where it was', async
|
|
|
330
336
|
await b.panel.open(); await settle();
|
|
331
337
|
assert.equal(text(b.q('.aq-title')), 'Nothing waiting');
|
|
332
338
|
assert.match(text(b.q('.aq-frame__note')), /Nothing is waiting for you/);
|
|
333
|
-
b.
|
|
339
|
+
b.key({ key: 'Escape', target: b.scrim });
|
|
334
340
|
assert.equal(b.scrim.hidden, true);
|
|
335
341
|
assert.equal(b.focused, opener);
|
|
336
342
|
assert.ok(!b.classes.has('aq-open'));
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -510,7 +510,7 @@ test('the verdict classes and the scrim exist in the token tier, in both modes',
|
|
|
510
510
|
assert.match(css, /\.ledger__verdict--danger\s*\{[^}]*var\(--danger\)/, 'fell short → --danger');
|
|
511
511
|
assert.match(css, /\.ledger__verdict--none\s*\{[^}]*var\(--ink-soft\)/, 'not graded → --ink-soft');
|
|
512
512
|
assert.match(css, /--scrim:/, 'the modal scrim is a token, not a sheet-local literal');
|
|
513
|
-
assert.match(read('
|
|
513
|
+
assert.match(read('controls.css'), /\.hall-dialog::backdrop \{ background: var\(--scrim\); \}/, 'the one dialog backdrop takes it (task 1005085)');
|
|
514
514
|
});
|
|
515
515
|
|
|
516
516
|
// ---- 10. an outline: none always names its replacement (task 1003496) -----
|
package/tests/hall_city.mjs
CHANGED
|
@@ -288,7 +288,7 @@ test('the city sits in the hall chrome, behind the shell\'s full-screen switch',
|
|
|
288
288
|
// task 1004685 (the living city): gov-living.js, the model under the drawing, and
|
|
289
289
|
// gov-feeds.js, its one reader per page (shared with the shell's tally), load before it
|
|
290
290
|
// task 1004702: the card answers its first ask in place, so the deck's card and sender (and the desk rows under them) load before the page
|
|
291
|
-
assert.deepEqual(scripts, ['dom-utils', 'shell-trail', 'craft-mode', 'shell', 'palette', 'gov-living', 'gov-feeds', 'city-draw', 'hall-kit', 'street-draw', 'street-card', 'street-inside', 'street-desk', 'street-deck', 'city-decide', 'city-switchboard', 'city'], 'the shell first, as on every hall page');
|
|
291
|
+
assert.deepEqual(scripts, ['dom-utils', 'shell-trail', 'craft-mode', 'shell', 'hall-dialog', 'palette', 'gov-living', 'gov-feeds', 'city-draw', 'hall-kit', 'street-draw', 'street-card', 'street-inside', 'street-desk', 'street-deck', 'city-decide', 'city-switchboard', 'city'], 'the shell first, then the shared dialog (task 1005085), as on every hall page');
|
|
292
292
|
// task 1004925: every script but dom-utils.js is deferred, so none holds up the
|
|
293
293
|
// parser; defer runs them in document order, which is why the order above still
|
|
294
294
|
// holds. dom-utils.js stays synchronous: the one-escaper guard
|
|
@@ -53,7 +53,9 @@ test('the page sheets spend only the fifteen, in px, and never opacity', () => {
|
|
|
53
53
|
const cd = read('copy-desk.css');
|
|
54
54
|
const literals = cd.match(/rgba?\([^)]*\)/g) || [];
|
|
55
55
|
assert.deepEqual(literals, [], 'not even the scrim is a literal here any more');
|
|
56
|
-
|
|
56
|
+
// task 1005085: the dialogs are the hall's one dialog, whose backdrop lives in controls.css
|
|
57
|
+
assert.doesNotMatch(cd, /::backdrop/, 'no backdrop of its own');
|
|
58
|
+
assert.match(read('controls.css'), /\.hall-dialog::backdrop \{ background: var\(--scrim\); \}/, 'the shared backdrop spends the token');
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
test('every list is the family\'s row, and the card-in-a-card is gone', () => {
|
|
@@ -335,15 +337,16 @@ test('the filters and the search are the kit\'s bar; the duplicates are paged th
|
|
|
335
337
|
assert.match(read('copy-desk.html'), /src="\/builders\/hall-kit\.js/, 'the page loads the kit');
|
|
336
338
|
});
|
|
337
339
|
|
|
338
|
-
test('the three dialogs are the
|
|
340
|
+
test('the three dialogs are the hall\'s one dialog on a native <dialog>, with the number input in a field', () => {
|
|
339
341
|
const html = read('copy-desk.html');
|
|
340
|
-
// Three since task 1003114 (R03): flag, close, and the proposal composer
|
|
341
|
-
|
|
342
|
+
// Three since task 1003114 (R03): flag, close, and the proposal composer; the
|
|
343
|
+
// shared .hall-dialog since task 1005085, each named by its own title.
|
|
344
|
+
assert.equal((html.match(/<dialog id="cd-(close-dialog|dialog|propose-dialog)" class="hall-dialog cd-dialog" aria-labelledby="cd-(close|dialog|propose)-h">/g) || []).length, 3);
|
|
342
345
|
assert.match(html, /<div class="ov-field ov-field--wide">\s*<label class="ov-field__l" for="cd-close-task">[^<]*<\/label>\s*<input type="number" id="cd-close-task"/, 'the number input sits in the family field, which restates the 30px pill');
|
|
343
346
|
assert.ok(!/<label[^>]*>[^<]*<span class="cd-dialog__hint"/.test(html), 'no hint lives inside a label (the label voice would uppercase it)');
|
|
344
347
|
assert.equal((html.match(/class="ov-form__why"/g) || []).length, 4, 'the four hints are the form\'s own explainer line');
|
|
345
|
-
assert.match(html, /class="btn
|
|
346
|
-
assert.match(html, /class="btn
|
|
348
|
+
assert.match(html, /class="btn" id="cd-submit"/, 'the accent door files the flag');
|
|
349
|
+
assert.match(html, /class="btn" id="cd-propose-submit"/, 'and the proposal composer has its own');
|
|
347
350
|
});
|
|
348
351
|
|
|
349
352
|
test('the proposal composer is prefilled, and says what filing it does and does not do', () => {
|