@bongos/core 1.21.7 → 1.21.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.
@@ -0,0 +1,364 @@
1
+ // modules/hall-ui/public/street.js — THE STREET, wired (task 1004525, goal
2
+ // 1000125, R03).
3
+ //
4
+ // street-draw.js computes the street as data; this file reads the feeds, hands
5
+ // them to GovStreet.streetModel(), and turns its lists into DOM: SVG paths for
6
+ // the road and the buildings, a span per crate, a name and state line per
7
+ // building, a bracket per group, an <a> door per building and district, a
8
+ // <button> per pin, and an <a> per reading. It owns the light/dark switch, the
9
+ // artboard's fit to the stage, the address (#building= / #district=), and the
10
+ // short matter card a pin opens. No innerHTML anywhere: every node is created,
11
+ // and every docket string lands as text or an attribute.
12
+ //
13
+ // THE FEEDS (all GETs, all gated server-side):
14
+ // /api/bongos/government/docket buildings, pins, lines, readings (task 1004523)
15
+ // /api/bongos/goals the open goals: one district each
16
+ // /api/bongos/goals/rollups each goal's task counts: its buildings
17
+ // /api/bongos/builders/<viewer>/earnings your share this month (after the docket names the viewer)
18
+ // A feed that fails leaves its part out (no pins, no districts, "—" for the
19
+ // share) and the page says the docket could not be read; it never draws sample data.
20
+ //
21
+ // THE SWITCH. The hall's own preference ('hall-theme', else the OS) and the
22
+ // hall's own attribute (html[data-theme]), shared with the city and every
23
+ // hall page. The drawing's faces are drawing data in the mode, so a toggle
24
+ // redraws the street from the last feeds (no refetch).
25
+ //
26
+ // MOTION is CSS only (street.css); nothing here animates. The scale write on
27
+ // resize is layout, not motion.
28
+
29
+ (function () {
30
+ 'use strict';
31
+ var S = window.GovStreet;
32
+ var SVG_NS = 'http://www.w3.org/2000/svg';
33
+ var THEME_KEY = 'hall-theme';
34
+ var SUN = 'M7 1.5V3M7 11v1.5M1.5 7H3M11 7h1.5M3.1 3.1l1 1M9.9 9.9l1 1M3.1 10.9l1-1M9.9 4.1l1-1M9.5 7A2.5 2.5 0 1 1 4.5 7A2.5 2.5 0 1 1 9.5 7Z';
35
+ var MOON = 'M11.5 8.6A5 5 0 0 1 5.4 2.5A5 5 0 1 0 11.5 8.6Z';
36
+ var FEEDS = {
37
+ docket: '/api/bongos/government/docket',
38
+ goals: '/api/bongos/goals',
39
+ rollups: '/api/bongos/goals/rollups',
40
+ earnings: function (id) { return '/api/bongos/builders/' + encodeURIComponent(id) + '/earnings'; },
41
+ };
42
+ // The artboard's street band: the prototype's 900-high board less its 64 px
43
+ // header and 128 px footer, which this page lays out as real rows.
44
+ var BAND_TOP = 64, BAND_H = 708;
45
+ var CARD_W = 390;
46
+
47
+ var $ = function (id) { return document.getElementById(id); };
48
+ var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
49
+ var data = { docket: null, goals: [], rollups: [], earnings: null, loaded: false };
50
+ var view = { open: null, opener: null, model: null, scale: 1, mode: null };
51
+
52
+ // ---------- light / dark ----------
53
+ var chosen = null;
54
+ function storedTheme() {
55
+ try { var v = window.localStorage.getItem(THEME_KEY); if (v === 'dark' || v === 'light') return v; } catch (_) { /* blocked */ }
56
+ return chosen;
57
+ }
58
+ function effectiveTheme() { return storedTheme() || (media && media.matches ? 'dark' : 'light'); }
59
+ function paintSwitch(t) {
60
+ var btn = $('streetMode');
61
+ var label = t === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
62
+ if (btn) { btn.setAttribute('aria-label', label); btn.setAttribute('title', label); }
63
+ var icon = $('streetModeIcon');
64
+ if (icon) icon.setAttribute('d', t === 'dark' ? SUN : MOON);
65
+ var word = $('streetModeWord');
66
+ if (word) word.textContent = t === 'dark' ? 'light' : 'dark';
67
+ }
68
+ function applyTheme() {
69
+ var t = effectiveTheme();
70
+ document.documentElement.setAttribute('data-theme', t);
71
+ paintSwitch(t);
72
+ return t;
73
+ }
74
+ function toggleTheme() {
75
+ var next = effectiveTheme() === 'dark' ? 'light' : 'dark';
76
+ chosen = next;
77
+ try { window.localStorage.setItem(THEME_KEY, next); } catch (_) { /* private mode: this page only */ }
78
+ applyTheme();
79
+ draw();
80
+ }
81
+ function currentMode() {
82
+ var t = document.documentElement.getAttribute('data-theme');
83
+ return t === 'light' || t === 'dark' ? t : effectiveTheme();
84
+ }
85
+
86
+ // ---------- the address ----------
87
+ function here() { return S.parseHash(window.location && window.location.hash); }
88
+
89
+ // ---------- fit the artboard's street band to the stage ----------
90
+ // 1440 x 708 at scale 1 is the prototype exactly. A wider or taller stage
91
+ // grows it, a smaller one shrinks it; a phone keeps it near full size and the
92
+ // street scrolls sideways inside its window, so a pin stays a pin.
93
+ function fit() {
94
+ var win = $('streetWindow'), sizer = $('streetSizer'), board = $('streetBoard');
95
+ if (!win || !sizer || !board) return;
96
+ var W = view.model ? view.model.width : S.ARTBOARD_W;
97
+ var cw = win.clientWidth || window.innerWidth || 1440, ch = win.clientHeight || 708;
98
+ var s = Math.min(cw / W, ch / BAND_H);
99
+ if (cw / W < 0.6) s = Math.min(1, ch / BAND_H);
100
+ s = Math.max(0.4, Math.round(s * 1000) / 1000);
101
+ view.scale = s;
102
+ sizer.style.width = Math.round(W * s) + 'px';
103
+ sizer.style.height = Math.round(BAND_H * s) + 'px';
104
+ board.style.width = W + 'px';
105
+ board.style.setProperty('--street-scale', String(s));
106
+ var svg = $('streetSvg');
107
+ if (svg) { svg.setAttribute('width', String(W)); svg.setAttribute('viewBox', '0 0 ' + W + ' 900'); }
108
+ placeCard();
109
+ }
110
+
111
+ // ---------- the feeds ----------
112
+ function getJson(url) {
113
+ return fetch(url, { credentials: 'same-origin', headers: { accept: 'application/json' } })
114
+ .then(function (r) { return r && r.ok ? r.json() : null; })
115
+ .catch(function () { return null; });
116
+ }
117
+ function load() {
118
+ return Promise.all([getJson(FEEDS.docket), getJson(FEEDS.goals), getJson(FEEDS.rollups)]).then(function (res) {
119
+ data.docket = res[0] && Array.isArray(res[0].matters) ? res[0] : null;
120
+ data.goals = res[1] && Array.isArray(res[1].goals) ? res[1].goals : [];
121
+ data.rollups = res[2] && Array.isArray(res[2].rollups) ? res[2].rollups : [];
122
+ data.loaded = true;
123
+ draw();
124
+ var viewer = data.docket && data.docket.viewer && data.docket.viewer.id;
125
+ if (viewer == null) return null;
126
+ return getJson(FEEDS.earnings(viewer)).then(function (e) {
127
+ data.earnings = e && Array.isArray(e.days) ? e : null;
128
+ draw();
129
+ });
130
+ });
131
+ }
132
+
133
+ // ---------- drawing ----------
134
+ function clear(el) { while (el && el.firstChild) el.removeChild(el.firstChild); }
135
+ function pathEl(p) {
136
+ var el = document.createElementNS(SVG_NS, 'path');
137
+ el.setAttribute('d', p.d);
138
+ el.setAttribute('fill', p.f);
139
+ el.setAttribute('stroke', p.s);
140
+ if (p.dash && p.dash !== 'none') el.setAttribute('stroke-dasharray', p.dash);
141
+ el.setAttribute('stroke-width', String(p.w || 1));
142
+ return el;
143
+ }
144
+ function node(tag, cls, text) {
145
+ var el = document.createElement(tag);
146
+ if (cls) el.className = cls;
147
+ if (text != null) el.textContent = String(text);
148
+ return el;
149
+ }
150
+ function at(el, x, y, w, h) {
151
+ el.style.left = x + 'px';
152
+ el.style.top = y + 'px';
153
+ if (w != null) el.style.width = w + 'px';
154
+ if (h != null) el.style.height = h + 'px';
155
+ return el;
156
+ }
157
+
158
+ function drawPin(p) {
159
+ var el = node('button', 'street-pin street-pin--' + p.state);
160
+ el.setAttribute('type', 'button');
161
+ el.setAttribute('aria-label', p.aria);
162
+ el.setAttribute('aria-expanded', view.open === p.key ? 'true' : 'false');
163
+ el.setAttribute('aria-controls', 'streetCard');
164
+ el.setAttribute('data-matter', p.key);
165
+ el.setAttribute('data-building', p.building);
166
+ at(el, p.x, p.y, 110, p.h);
167
+ var label = node('span', 'street-pin__label', p.label);
168
+ label.setAttribute('title', p.label);
169
+ el.appendChild(label);
170
+ var head = node('span', 'street-pin__head');
171
+ if (p.state === 'needs') head.appendChild(node('span', 'street-pin__ring'));
172
+ if (p.isCount) head.appendChild(node('span', 'street-pin__count', p.count));
173
+ else {
174
+ var svg = document.createElementNS(SVG_NS, 'svg');
175
+ svg.setAttribute('width', '18');
176
+ svg.setAttribute('height', '18');
177
+ svg.setAttribute('aria-hidden', 'true');
178
+ var fill = document.createElementNS(SVG_NS, 'path');
179
+ fill.setAttribute('d', p.gf || '');
180
+ var line = document.createElementNS(SVG_NS, 'path');
181
+ line.setAttribute('d', p.gl || '');
182
+ svg.appendChild(fill);
183
+ svg.appendChild(line);
184
+ head.appendChild(svg);
185
+ }
186
+ el.appendChild(head);
187
+ el.appendChild(node('span', 'street-pin__stem'));
188
+ el.addEventListener('click', function () { openCard(p.key, el); });
189
+ return el;
190
+ }
191
+
192
+ function drawSign(sg) {
193
+ var a = node('a', 'street-sign' + (sg.hot ? ' street-sign--hot' : '') + (sg.here ? ' street-sign--here' : ''));
194
+ a.setAttribute('href', sg.href);
195
+ a.setAttribute('aria-label', sg.aria);
196
+ a.setAttribute('data-reading', sg.key);
197
+ if (sg.here) a.setAttribute('aria-current', 'location');
198
+ var top = node('span', 'street-sign__top');
199
+ top.appendChild(node('span', 'street-sign__dot'));
200
+ top.appendChild(node('span', 'street-label', sg.label));
201
+ a.appendChild(top);
202
+ a.appendChild(node('span', 'street-sign__figure', sg.figure));
203
+ a.appendChild(node('span', 'street-sign__line', sg.line));
204
+ return a;
205
+ }
206
+
207
+ function draw() {
208
+ var mode = currentMode();
209
+ view.mode = mode;
210
+ paintSwitch(mode);
211
+ var m = S.streetModel({ docket: data.docket, goals: data.goals, rollups: data.rollups, earnings: data.earnings, mode: mode, now: Date.now(), here: here() });
212
+ view.model = m;
213
+ var flat = $('streetFlat'), blds = $('streetBlds');
214
+ clear(flat); clear(blds);
215
+ m.flat.forEach(function (p) { flat.appendChild(pathEl(p)); });
216
+ m.blds.forEach(function (b) {
217
+ var g = document.createElementNS(SVG_NS, 'g');
218
+ g.setAttribute('data-key', b.key);
219
+ b.polys.forEach(function (p) { g.appendChild(pathEl(p)); });
220
+ blds.appendChild(g);
221
+ });
222
+
223
+ var crates = $('streetCrates'); clear(crates);
224
+ m.crates.forEach(function (c) { crates.appendChild(at(node('span', 'street-crate'), c.x, c.y)); });
225
+
226
+ var cols = $('streetCols'); clear(cols);
227
+ m.cols.forEach(function (c) {
228
+ var el = at(node('div', 'street-col'), c.x, 596, c.w);
229
+ var name = node('span', 'street-col__name' + (c.strong ? ' street-col__name--strong' : ''), c.name);
230
+ name.setAttribute('title', c.title);
231
+ el.appendChild(name);
232
+ el.appendChild(node('span', 'street-col__state', c.state));
233
+ cols.appendChild(el);
234
+ });
235
+
236
+ var groups = $('streetGroups'); clear(groups);
237
+ m.groups.forEach(function (g) {
238
+ var el = at(node('div', 'street-group'), g.x, 650, g.w);
239
+ el.appendChild(node('span', 'street-group__bracket'));
240
+ el.appendChild(node('span', 'street-label', g.name));
241
+ groups.appendChild(el);
242
+ });
243
+
244
+ var doors = $('streetDoors'); clear(doors);
245
+ m.doors.forEach(function (d) {
246
+ var a = at(node('a', 'street-door' + (d.here ? ' street-door--here' : '')), d.x, d.y, d.w, d.h);
247
+ a.setAttribute('href', d.href);
248
+ a.setAttribute('aria-label', d.aria);
249
+ a.setAttribute('data-door', d.key);
250
+ if (d.here) a.setAttribute('aria-current', 'location');
251
+ doors.appendChild(a);
252
+ });
253
+
254
+ var pins = $('streetPins'); clear(pins);
255
+ m.pins.forEach(function (p) { pins.appendChild(drawPin(p)); });
256
+
257
+ var signs = $('streetSigns'); clear(signs);
258
+ m.signs.forEach(function (sg) { signs.appendChild(drawSign(sg)); });
259
+
260
+ var hint = $('streetHint');
261
+ if (hint) { hint.textContent = m.hint; hint.hidden = !m.hint || !!view.open; }
262
+ var eve = $('streetEvening');
263
+ if (eve) eve.hidden = !(m.evening && !view.open);
264
+ var eveLine = $('streetEveningLine');
265
+ if (eveLine) eveLine.textContent = m.eveningLine;
266
+
267
+ $('hudNeeds').textContent = m.hud.needs;
268
+ $('hudMoving').textContent = m.hud.moving;
269
+ $('hudShare').textContent = m.hud.share;
270
+ var dot = $('hudDot');
271
+ if (dot) dot.className = 'street-hud__dot' + (m.hud.hot ? ' street-hud__dot--hot' : '');
272
+ var board = $('streetBoard');
273
+ if (board) board.setAttribute('aria-label', data.loaded ? m.label : 'The street, reading the docket');
274
+ var home = $('streetHome');
275
+ if (home) home.setAttribute('href', S.HOME_HREF);
276
+
277
+ fit();
278
+ if (view.open) fillCard();
279
+ return m;
280
+ }
281
+
282
+ // ---------- the short matter card ----------
283
+ function fillCard() {
284
+ var cv = S.cardFor(data.docket, view.open, Date.now());
285
+ if (!cv) { closeCard(false); return null; }
286
+ $('streetCardWhere').textContent = cv.where;
287
+ $('streetCardWhen').textContent = cv.when;
288
+ $('streetCardTitle').textContent = cv.title;
289
+ var meta = $('streetCardMeta');
290
+ var metaText = [cv.proposer, cv.tally].filter(Boolean).join(' · ');
291
+ meta.textContent = metaText;
292
+ meta.hidden = !metaText;
293
+ var lines = $('streetCardLines');
294
+ clear(lines);
295
+ cv.lines.forEach(function (t) { lines.appendChild(node('li', '', t)); });
296
+ lines.hidden = !cv.lines.length;
297
+ $('streetCardResult').textContent = cv.result;
298
+ var open = $('streetCardOpen');
299
+ if (cv.href) open.setAttribute('href', cv.href);
300
+ open.hidden = !cv.href;
301
+ placeCard();
302
+ return cv;
303
+ }
304
+ function placeCard() {
305
+ var card = $('streetCard');
306
+ if (!card || !view.open || !view.model) return;
307
+ var pin = null;
308
+ view.model.pins.forEach(function (p) { if (p.key === view.open) pin = p; });
309
+ var win = $('streetWindow'), sizer = $('streetSizer');
310
+ var stageW = (card.parentNode && card.parentNode.clientWidth) || window.innerWidth || 1440;
311
+ var originX = (sizer && sizer.offsetLeft != null ? sizer.offsetLeft : 0) - (win && win.scrollLeft ? win.scrollLeft : 0);
312
+ var cx = pin ? originX + (pin.x + 55) * view.scale : stageW / 2;
313
+ var x = Math.max(16, Math.min(stageW - CARD_W - 16, Math.round(cx - CARD_W / 2)));
314
+ card.style.setProperty('--street-card-x', x + 'px');
315
+ }
316
+ function openCard(key, opener) {
317
+ view.open = key;
318
+ view.opener = opener || null;
319
+ var card = $('streetCard');
320
+ if (!fillCard()) return;
321
+ card.hidden = false;
322
+ draw();
323
+ var close = $('streetCardClose');
324
+ if (close && close.focus) close.focus();
325
+ }
326
+ function closeCard(restore) {
327
+ var was = view.opener;
328
+ view.open = null;
329
+ view.opener = null;
330
+ var card = $('streetCard');
331
+ if (card) card.hidden = true;
332
+ draw();
333
+ // The pins were redrawn, so focus goes back to the new button for the same matter.
334
+ if (restore !== false && was && was.getAttribute) {
335
+ var key = was.getAttribute('data-matter');
336
+ var pins = $('streetPins').children || [];
337
+ for (var i = 0; i < pins.length; i++) {
338
+ if (pins[i].getAttribute('data-matter') === key) { if (pins[i].focus) pins[i].focus(); break; }
339
+ }
340
+ }
341
+ }
342
+ function onKey(e) {
343
+ if (e.key === 'Escape' && view.open) { e.preventDefault(); closeCard(true); }
344
+ }
345
+
346
+ function boot() {
347
+ var btn = $('streetMode');
348
+ if (btn) btn.addEventListener('click', toggleTheme);
349
+ var close = $('streetCardClose');
350
+ if (close) close.addEventListener('click', function () { closeCard(true); });
351
+ window.addEventListener('keydown', onKey);
352
+ window.addEventListener('resize', fit);
353
+ window.addEventListener('hashchange', function () { draw(); });
354
+ var win = $('streetWindow');
355
+ if (win) win.addEventListener('scroll', placeCard);
356
+ if (media && media.addEventListener) media.addEventListener('change', function () { if (!storedTheme()) { applyTheme(); draw(); } });
357
+ applyTheme();
358
+ draw();
359
+ return load();
360
+ }
361
+
362
+ window.GovStreetPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
363
+ window.GovStreetPage.ready = boot();
364
+ }());
@@ -0,0 +1,43 @@
1
+ {
2
+ "_": "THE STREET (task 1004525, goal 1000125) through modules/ui-design/kit/render.js. The page draws GET /government/docket, GET /goals, GET /goals/rollups and the viewer's GET /builders/:id/earnings; against the kit's own stub none exists, so every state renders the bare street (the nine civic buildings, no districts, no pins) with the readings saying the docket could not be read: a real state the floors must hold in. To look at the populated street, run it against the hall-preview harness, whose government__docket.json fixture carries the reference design's seven sample matters (3 need you, 2 moving, 2 settled) and whose goals fixtures carry the open goals:\n\n node scripts/hall-preview/server.js --port 4625 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/street.states.json --base-in http://localhost:4625 --base-out http://localhost:4625\n\nThe page follows the OS mode until the switch is pressed (then the hall's own 'hall-theme' preference), so the kit's dark/light emulation renders both modes.",
3
+ "page": "/builders/street",
4
+ "surface": "hall-ui",
5
+ "stub": { "prefix": "/builders/" },
6
+ "modeQuery": false,
7
+ "ignoreRequests": ["/api/(gds|bongos)/", "fonts\\.g(oogleapis|static)\\.com"],
8
+ "ignoreConsole": ["/api/(gds|bongos)/", "\\b401\\b", "\\b403\\b", "\\b404\\b", "Unauthorized", "Forbidden", "Not Found", "load failed", "fonts\\.g(oogleapis|static)\\.com"],
9
+ "states": {
10
+ "street": {
11
+ "auth": true,
12
+ "_": "The street: the header, the buildings in the order work moves with their names, state lines and group brackets, the pins, and the six readings.",
13
+ "actions": [["wait", 900]],
14
+ "expect": { "visible": ["#streetBoard", "#streetSigns", "#streetMode", "#streetHome"], "href": [["#streetHome", "/builders/city"]] }
15
+ },
16
+ "card": {
17
+ "auth": true,
18
+ "_": "A pin pressed: the matter's short card over the street.",
19
+ "actions": [["wait", 900], ["click", ".street-pin"], ["wait", 400]],
20
+ "expect": { "visible": ["#streetCard", "#streetCardClose"] }
21
+ },
22
+ "switched": {
23
+ "auth": true,
24
+ "_": "The light/dark switch pressed once: the other mode, the street redrawn in it.",
25
+ "actions": [["wait", 600], ["click", "#streetMode"], ["wait", 400]],
26
+ "expect": { "visible": ["#streetBoard", "#streetSigns"] }
27
+ },
28
+ "here": {
29
+ "auth": true,
30
+ "_": "Arrived through a door: the address names the Charter, so its door and the readings that open it are marked.",
31
+ "url": "/builders/street#building=charter",
32
+ "actions": [["wait", 900]],
33
+ "expect": { "visible": ["#streetBoard"] }
34
+ },
35
+ "focus": {
36
+ "auth": true,
37
+ "_": "Keyboard: the wordmark back to the city focused, its ring drawn.",
38
+ "actions": [["wait", 600], ["focus", "#streetHome"], ["wait", 100]],
39
+ "expect": { "activeId": "streetHome" }
40
+ }
41
+ },
42
+ "reducedMotion": { "auth": true, "actions": [["wait", 600]] }
43
+ }
@@ -0,0 +1,14 @@
1
+ # The Governor City's street — /street, the six readings, and the doors to the interiors (task 1004525)
2
+
3
+ **R03 of goal 1000125.** Built 1:1 on the street view of the reference design [`docs/design/mocks/governor-city/Street.dc.html`](../../../docs/design/mocks/governor-city/README.md): one front-on row in the order work moves under five group brackets, a name and one state line under every building, pins with their labels always showing, work queued at Review as crates by its door, the header (wordmark back to the city, needs you, moving, your share this month, the light/dark switch) and the six readings along the bottom. The home city (`/city`, task 1004524) links here from start governing.
4
+
5
+ Files: `street-draw.js` (the prototype's front-on kit and street layout, verbatim, as pure data: `window.GovStreet`, node-require-safe), `street.js` (the page: feeds, nodes, the switch, the address, the short card) and `street.css` (the template's styles on the hall's tokens). It reuses `city-draw.js` for the decision glyphs, the seeded random and `rulesAtWork`, which the city now also uses to light the Rules band.
6
+
7
+ What it settled:
8
+
9
+ - **Standalone, like the city.** No hall sidebar or top bar; it is the second entry in `tests/hall_audit.mjs`'s `STANDALONE_EXEMPT`, with the city's reason. It still loads `style.css` and reads and writes the hall's own `hall-theme`.
10
+ - **The artboard is the street band.** The prototype's 1440 x 900 board less its 64 px header and 128 px footer, which the page lays out as real rows; `street.js` scales the 1440 x 708 band to the window, and on a phone keeps it near full size and lets the street scroll sideways inside its own window, so a pin stays a pin. The header fits the 80 px top-bar floor at 390 and 320 by going to two rows, its three figures as their own sideways strip.
11
+ - **Real data where the prototype had samples.** One district per open goal (up to eight plots; past that seven stand and the eighth is a staked lot that opens the goals page), each goal's tasks drawn in proportion (built, scaffold, the prototype's `ready` slab), Review held while any source that stands in it can be read, its open matters as crates and the dashed "waiting for a tester" count pin, a reading off drawn as a dash and its reason. The full list is the header of `street-draw.js`.
12
+ - **Every building and district is a door** to `/street#building=<key>` or `/street#district=<goal id>`, where task 1004526 builds the interiors; until then the street marks the building the address names. A pin opens the matter's short card (task 1004528 makes it act); its link is offered only for an on-site path.
13
+
14
+ Tests: `tests/hall_street.mjs` holds the kit, the row, the pins, the doors and the crates equal to the prototype by running its own `Component` class beside `street-draw.js` in both modes, then the docket rules and the page itself in a vm. The states file is `public/street.states.json`; render it against the hall-preview harness, whose `government__docket.json` fixture carries the seven sample matters.
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.7",
3
+ "version": "1.21.8",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.7",
9
+ "version": "1.21.8",
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.21.7",
3
+ "version": "1.21.8",
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",
@@ -8794,5 +8794,15 @@
8794
8794
  "id": "1004503",
8795
8795
  "text": "When you start a project, the invite box now waits until your project is actually up, because nobody can join before then. Until it is, one line says when you can invite, and a demo's teammates are kept ready for that moment."
8796
8796
  }
8797
+ ],
8798
+ "1.21.8": [
8799
+ {
8800
+ "id": "1004525",
8801
+ "text": "The city's start governing button now opens the governor's street. Every part of the project's government stands as a building in one row, in the order work moves, with a building for each open goal. A pin with its label stand"
8802
+ },
8803
+ {
8804
+ "id": "1004441",
8805
+ "text": "Checked and signed off on the live site: artists and ideators now read as their own craft on profiles, the builders list and the status page. Their earnings charts show, and builders can choose their main role in Settings."
8806
+ }
8797
8807
  ]
8798
8808
  }
@@ -951,6 +951,14 @@ function mountInternalSurfaces(app) {
951
951
  // NB as at PROJECT_SETTINGS_PAGE_RE: nothing between the `=` and the literal
952
952
  // (tests/hall_page_gate_map.mjs parses this file statically). Prose goes above.
953
953
  const CITY_PAGE_RE = /^\/builders\/city(?:\.html)?\/?$/;
954
+ // /street — the Governor City's street (task 1004525), the city's one door.
955
+ // The same atom for the same reason: its governed read is the same docket.
956
+ // Its other reads (goals, rollups, the viewer's own earnings) are
957
+ // requireBuilder, a wider floor.
958
+ //
959
+ // NB as at PROJECT_SETTINGS_PAGE_RE: nothing between the `=` and the literal
960
+ // (tests/hall_page_gate_map.mjs parses this file statically). Prose goes above.
961
+ const STREET_PAGE_RE = /^\/builders\/street(?:\.html)?\/?$/;
954
962
  // /deploy — the owner's deploy door (task 1003159, ADR 0293). Gated on the
955
963
  // `core.pin.move` atom, the same key its preview and its move both check, so the
956
964
  // shell's reach equals the right to press the button. Archon-floor and system:true,
@@ -1037,6 +1045,10 @@ function mountInternalSurfaces(app) {
1037
1045
  if (CITY_PAGE_RE.test(norm)) {
1038
1046
  return gdsAuth.requireBoardVotePage(req, res, next);
1039
1047
  }
1048
+ // task 1004525: the street, on the same atom (see STREET_PAGE_RE).
1049
+ if (STREET_PAGE_RE.test(norm)) {
1050
+ return gdsAuth.requireBoardVotePage(req, res, next);
1051
+ }
1040
1052
  // ADR 0293: the deploy door, on the same atom its two routes check.
1041
1053
  if (DEPLOY_PAGE_RE.test(norm)) {
1042
1054
  return gdsAuth.requireCorePinPage(req, res, next);
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.7'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.8'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -378,7 +378,15 @@ test('the module doc still records where the 2179 density pass landed', () => {
378
378
  // the hall's own 'hall-theme' preference. Whether it should join the chrome
379
379
  // behind the full-screen switch, as the sky did in task 1004327, is the
380
380
  // owner's call, named in that task's ship notes.
381
- const STANDALONE_EXEMPT = { 'city.html': 'the governor city home: nothing on the page but the prototype home (task 1004524)' };
381
+ // THE STREET (task 1004525) follows the city, for the same reason: the
382
+ // reference design's street view shows nothing on the page but its own header
383
+ // (the wordmark back to the city, needs you, moving, your share, the switch),
384
+ // the street and the six readings. Same terms as the city: style.css and the
385
+ // hall's tokens, the hall's own 'hall-theme' preference, no private palette.
386
+ const STANDALONE_EXEMPT = {
387
+ 'city.html': 'the governor city home: nothing on the page but the prototype home (task 1004524)',
388
+ 'street.html': 'the governor street: nothing on the page but the prototype street view (task 1004525)',
389
+ };
382
390
 
383
391
  test('every hall page loads style.css and shell.js', () => {
384
392
  const missing = [];
@@ -144,6 +144,9 @@ const ORACLE = {
144
144
  // governed read, GET /government/docket, checks board.vote.cast, so the page's
145
145
  // reach equals the right to read the docket it draws.
146
146
  '/builders/city': 'requireBoardVotePage',
147
+ // task 1004525: the street, the city's one door, on the same atom for the
148
+ // same reason (its governed read is the same docket).
149
+ '/builders/street': 'requireBoardVotePage',
147
150
 
148
151
  // Deliberately UNGATED — the landing card, the rank ladder, the reward
149
152
  // schedule and a builder's public portfolio all render for a stranger.