@bongos/core 1.24.38 → 1.24.39
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 +22 -22
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +502 -702
- package/modules/hall-ui/public/street-desk.js +1 -1
- package/modules/hall-ui/public/street-draw.js +16 -5
- package/modules/hall-ui/public/street-inside.js +16 -0
- package/modules/hall-ui/public/street.css +36 -0
- package/modules/hall-ui/public/street.js +29 -16
- package/modules/hall-ui/records/governor-street.md +12 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/hall_matter_card.mjs +14 -7
- package/tests/hall_street.mjs +38 -32
- package/tests/hall_street_depth.mjs +8 -6
- package/tests/hall_street_desk.mjs +8 -5
- package/tests/hall_street_inside.mjs +18 -0
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
|
|
62
62
|
// Every limit of the line rule, in characters unless named otherwise.
|
|
63
63
|
const LIMITS = Object.freeze({
|
|
64
|
-
nameWords:
|
|
64
|
+
nameWords: 14, name: 90, fact: 60, facts: 3, factLen: 48, actions: 2,
|
|
65
65
|
tiles: 3, tileValue: 8, tileWords: 2, tileLabel: 14,
|
|
66
66
|
status: 44, clearLine: 30, clearSub: 40, record: 4, recordLen: 30,
|
|
67
67
|
chips: 5, chip: 24, todayTarget: 10, pips: 12, saveLine: 48,
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
// /goals/rollups) for the districts, and the viewer's earnings for "your share
|
|
21
21
|
// this month". Where the real data had to bend the design, it is said once here
|
|
22
22
|
// and in the ship notes:
|
|
23
|
-
// * Districts
|
|
23
|
+
// * Districts (only with `districts: true`; the page draws none since the
|
|
24
|
+
// owner's 2026-11-04 direction, and centres the civic row). One per OPEN goal, in the order the goals come, between
|
|
24
25
|
// Automation and Review. Up to four keep the prototype's 90 px plots; past
|
|
25
26
|
// four each plot narrows (never under 56 px) and past that the street grows
|
|
26
27
|
// longer than the 1440 px artboard, so the page scrolls it sideways rather
|
|
@@ -330,9 +331,13 @@
|
|
|
330
331
|
// between districts, 18 otherwise), starting at 43. Past MAX_DISTRICTS open
|
|
331
332
|
// goals the first seven stand as districts and the eighth plot is the rest,
|
|
332
333
|
// staked out, a door to the goals page.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
// The street is the governance square (owner, 2026-11-04): the working areas
|
|
335
|
+
// are the city's own buildings, so by default no district stands on the
|
|
336
|
+
// street and the civic row is centred on the artboard. `districts` keeps the
|
|
337
|
+
// prototype's row (the kit's tests hold it to the prototype).
|
|
338
|
+
function layout(goals, districts) {
|
|
339
|
+
const shown = districts ? (goals.length > MAX_DISTRICTS ? goals.slice(0, MAX_DISTRICTS - 1) : goals) : [];
|
|
340
|
+
const more = districts ? goals.length - shown.length : 0;
|
|
336
341
|
const dw = districtWidth(shown.length + (more ? 1 : 0));
|
|
337
342
|
const segs = [];
|
|
338
343
|
SEGS.forEach((s) => {
|
|
@@ -346,6 +351,8 @@
|
|
|
346
351
|
if (i > 0) x += segs[i - 1].g !== s.g ? 30 : (s.d ? 10 : 18);
|
|
347
352
|
s.x = x; x += s.w;
|
|
348
353
|
});
|
|
354
|
+
const spare = ARTBOARD_W - (x + 43);
|
|
355
|
+
if (!districts && spare > 0) segs.forEach((s) => { s.x += Math.floor(spare / 2); });
|
|
349
356
|
return { segs: segs, width: Math.max(ARTBOARD_W, x + 43), districtW: dw };
|
|
350
357
|
}
|
|
351
358
|
|
|
@@ -510,6 +517,10 @@
|
|
|
510
517
|
// living GovLiving.livingModel() over the same feeds, or null (no Leads,
|
|
511
518
|
// no agents, no construction: the deep street with real data only)
|
|
512
519
|
// roster GET /builders/roster body, or null (the people reading)
|
|
520
|
+
// districts true: stand a district per open goal between Automation and
|
|
521
|
+
// Review (the prototype's row). The page leaves it off: the
|
|
522
|
+
// street is the governance square and the working areas are the
|
|
523
|
+
// city's (owner, 2026-11-04)
|
|
513
524
|
function streetModel(input) {
|
|
514
525
|
const opts = input || {};
|
|
515
526
|
const docket = opts.docket && typeof opts.docket === 'object' && Array.isArray(opts.docket.matters) ? opts.docket : null;
|
|
@@ -529,7 +540,7 @@
|
|
|
529
540
|
const goals = (Array.isArray(opts.goals) ? opts.goals : []).filter((g) => g && g.status === 'open' && g.id != null);
|
|
530
541
|
const byGoal = {};
|
|
531
542
|
(Array.isArray(opts.rollups) ? opts.rollups : []).forEach((r) => { if (r && r.goal_id != null) byGoal[String(r.goal_id)] = r; });
|
|
532
|
-
const L = layout(goals);
|
|
543
|
+
const L = layout(goals, opts.districts === true);
|
|
533
544
|
const segs = L.segs, W = L.width;
|
|
534
545
|
const at = {};
|
|
535
546
|
segs.forEach((s) => { at[s.k || ('d:' + s.d)] = s; });
|
|
@@ -725,7 +725,23 @@
|
|
|
725
725
|
return dv;
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
+
// The floor picker (owner, 2026-11-04: a building is a place to work). On a
|
|
729
|
+
// wide window the section is cropped to the building itself (PICKER_CROP: its
|
|
730
|
+
// crown, storeys and ground, without the 800 x 708 section's empty margins)
|
|
731
|
+
// and scaled to the stage height, never under half size (a floor stays a
|
|
732
|
+
// 24 px target) nor over 0.72 (the desk keeps the room). The CSS variables
|
|
733
|
+
// street.js sets on the drawing's box and its column.
|
|
734
|
+
const PICKER_CROP = Object.freeze({ x: 50, y: 120, w: 590, h: 580 });
|
|
735
|
+
function pickerFit(stageH) {
|
|
736
|
+
const c = PICKER_CROP;
|
|
737
|
+
const k = Math.max(0.5, Math.min(0.72, Math.round(((Number(stageH) || 708) - 60) / c.h * 1000) / 1000));
|
|
738
|
+
const px = (n) => Math.round(n * k) + 'px';
|
|
739
|
+
return { k: k, box: { '--inside-art-scale': String(k), '--inside-art-x': px(-c.x), '--inside-art-y': px(-c.y) },
|
|
740
|
+
art: { '--inside-art-w': px(c.w), '--inside-art-h': px(c.h) } };
|
|
741
|
+
}
|
|
742
|
+
|
|
728
743
|
const api = {
|
|
744
|
+
pickerFit: pickerFit, PICKER_CROP: PICKER_CROP,
|
|
729
745
|
crown: crown, workItem: workItem, floorSymbol: floorSymbol, symbolPath: symbolPath, buildingView: buildingView, districtView: districtView,
|
|
730
746
|
civicInfo: civicInfo, floorFor: floorFor, holderOf: holderOf, recordOf: recordOf, buildingModel: buildingModel,
|
|
731
747
|
districtInfo: districtInfo, districtModel: districtModel,
|
|
@@ -530,6 +530,37 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
|
|
|
530
530
|
.inside-name:focus { outline: none; }
|
|
531
531
|
.inside-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
|
|
532
532
|
|
|
533
|
+
/* THE HALL IS FOR WORK (owner, 2026-11-04; task 1005075). The street's own
|
|
534
|
+
header strip and the six readings along the bottom repeat what the shell's
|
|
535
|
+
top bar and the buildings already say, and inside a building they squeezed
|
|
536
|
+
the floor you came to work on into a corner. Both go. */
|
|
537
|
+
.street-top, .street-signs { display: none; }
|
|
538
|
+
|
|
539
|
+
/* Inside a building: the drawing is the floor picker, at half size in its own
|
|
540
|
+
column (a floor stays a 24 px target); the chosen floor's desk takes the rest
|
|
541
|
+
of the window and scrolls on its own. The building's own facts (who holds it,
|
|
542
|
+
how it decides, its record) follow the desk instead of standing above it. */
|
|
543
|
+
@media (min-width: 900px) {
|
|
544
|
+
.inside--work { display: block; overflow: hidden; }
|
|
545
|
+
.inside--work .inside-sizer { width: auto; height: 100%; margin: 0; overflow: visible; }
|
|
546
|
+
.inside--work .inside-board { position: static; width: auto; height: 100%; transform: none; display: grid;
|
|
547
|
+
grid-template-columns: var(--inside-art-w, 400px) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
|
|
548
|
+
column-gap: 40px; padding: 0 32px 0 16px; box-sizing: border-box; animation: none; }
|
|
549
|
+
.inside--work .inside-head { position: static; grid-column: 1 / -1; padding: 14px 0 6px 16px; }
|
|
550
|
+
.inside--work .inside-art { position: relative; width: var(--inside-art-w, 400px); height: var(--inside-art-h, 354px); grid-row: 2; align-self: start; overflow: hidden; }
|
|
551
|
+
.inside--work .inside-art__box { width: 800px; transform: translate(var(--inside-art-x, 0), var(--inside-art-y, 0)) scale(var(--inside-art-scale, 1)); }
|
|
552
|
+
.inside--work .inside-panel { position: static; grid-row: 2; width: auto; max-width: 880px; height: 100%; padding: 4px 4px 32px 0; box-sizing: border-box; gap: 20px; }
|
|
553
|
+
.inside--work .inside-title { order: 0; flex-direction: row; align-items: baseline; gap: 14px; flex-wrap: wrap; }
|
|
554
|
+
.inside--work .inside-name { font-size: 28px; }
|
|
555
|
+
.inside--work .inside-holds { font-size: 14px; }
|
|
556
|
+
.inside--work .inside-open { order: 1; }
|
|
557
|
+
.inside--work .govx { order: 2; }
|
|
558
|
+
.inside--work .inside-gov { order: 3; }
|
|
559
|
+
.inside--work .inside-record { order: 4; }
|
|
560
|
+
.inside--work .inside-open__head .inside-plate { width: 44px; height: 44px; border-radius: 10px; }
|
|
561
|
+
.inside--work .inside-open__title { font-size: 24px; }
|
|
562
|
+
}
|
|
563
|
+
|
|
533
564
|
/* narrow: the drawing in its own sideways strip, the panel below it */
|
|
534
565
|
@media (max-width: 899px) {
|
|
535
566
|
.inside { display: block; overflow-x: hidden; overflow-y: auto; }
|
|
@@ -543,6 +574,11 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
|
|
|
543
574
|
.inside-dhead { order: 1; }
|
|
544
575
|
.inside-legend { order: 3; flex-direction: column; gap: 8px; }
|
|
545
576
|
.inside-panel { order: 3; overflow: visible; }
|
|
577
|
+
/* the floor's desk straight under the name, the building's facts after it */
|
|
578
|
+
.inside-open { order: 1; }
|
|
579
|
+
.govx { order: 2; }
|
|
580
|
+
.inside-gov { order: 3; }
|
|
581
|
+
.inside-record { order: 4; }
|
|
546
582
|
.inside-name { font-size: 32px; }
|
|
547
583
|
.inside-gov__row { flex-wrap: wrap; row-gap: 4px; }
|
|
548
584
|
.desk-row__main { flex-wrap: wrap; row-gap: 8px; }
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
constitution: '/api/bongos/government/constitution',
|
|
78
78
|
uat: function (id) { return '/api/bongos/done-when/' + encodeURIComponent(id) + '/uat'; },
|
|
79
79
|
};
|
|
80
|
-
var NARROW = 900;
|
|
80
|
+
var NARROW = 900, FOCUS_W = 1120; // FOCUS_W: the civic row and its pins, which a wide window fits
|
|
81
81
|
var I = window.GovInside;
|
|
82
82
|
// The artboard's street band: the prototype's 900-high board less its 64 px
|
|
83
83
|
// header and 128 px footer, which this page lays out as real rows, and less
|
|
@@ -159,13 +159,10 @@
|
|
|
159
159
|
function here() { return S.parseHash(window.location && window.location.hash); }
|
|
160
160
|
|
|
161
161
|
// ---------- fit the artboard's street band to the stage ----------
|
|
162
|
-
// 1440 x 708 at scale 1 is the prototype exactly. A
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// width) gives the spare height to the foot of the page, under the readings, so
|
|
167
|
-
// the street sits on them with no empty sky above it (task 1004718). The window
|
|
168
|
-
// gives the height back whenever a building's inside is open.
|
|
162
|
+
// 1440 x 708 at scale 1 is the prototype exactly. A wide window fits the civic
|
|
163
|
+
// row (FOCUS_W), crops the haze either side and centres the street in the stage
|
|
164
|
+
// (owner, 2026-11-04); a phone keeps it near full size and scrolls it sideways
|
|
165
|
+
// from the first building, so a pin stays a pin.
|
|
169
166
|
function fit() {
|
|
170
167
|
var win = $('streetWindow'), inside = $('streetInside');
|
|
171
168
|
var insideOn = !!(inside && !inside.hidden);
|
|
@@ -175,23 +172,29 @@
|
|
|
175
172
|
if (!win || !sizer || !board) return;
|
|
176
173
|
var W = view.model ? view.model.width : S.ARTBOARD_W;
|
|
177
174
|
var cw = win.clientWidth || window.innerWidth || 1440, ch = win.clientHeight || 708;
|
|
178
|
-
var
|
|
179
|
-
|
|
175
|
+
var wide = cw >= NARROW;
|
|
176
|
+
var s = wide ? Math.min(cw / FOCUS_W, ch / BAND_H) : Math.min(cw / W, ch / BAND_H);
|
|
177
|
+
if (cw / W < 0.6 && !wide) s = Math.min(1, ch / BAND_H);
|
|
180
178
|
s = Math.max(0.4, Math.round(s * 1000) / 1000);
|
|
181
179
|
view.scale = s;
|
|
182
|
-
|
|
180
|
+
var sizerW = wide ? Math.min(Math.round(W * s), cw) : Math.round(W * s);
|
|
181
|
+
sizer.style.width = sizerW + 'px';
|
|
183
182
|
sizer.style.height = Math.round(BAND_H * s) + 'px';
|
|
184
|
-
|
|
183
|
+
board.style.left = wide ? Math.round((sizerW - W * s) / 2) + 'px' : '';
|
|
184
|
+
if (win.style) win.style.overflowX = wide ? 'hidden' : '';
|
|
185
185
|
board.style.width = W + 'px';
|
|
186
186
|
board.style.setProperty('--street-scale', String(s));
|
|
187
187
|
var svg = $('streetSvg');
|
|
188
188
|
if (svg) { svg.setAttribute('width', String(W)); svg.setAttribute('viewBox', '0 0 ' + W + ' 900'); }
|
|
189
|
+
var doors = $('streetDoors'), first = doors && (doors.firstElementChild || (doors.children && doors.children[0]));
|
|
190
|
+
if (!wide && !insideOn && !view.scrolled && first) { win.scrollLeft = Math.max(0, Math.round((parseFloat(first.style.left) || 0) * s) - 16); view.scrolled = true; }
|
|
189
191
|
placeCard();
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
// The interior
|
|
193
|
-
//
|
|
194
|
-
//
|
|
194
|
+
// The interior: a building on a wide window is laid out, its drawing the floor
|
|
195
|
+
// picker (GovInside.pickerFit) beside the desk; a district's 1440 x 708 board
|
|
196
|
+
// scales to the window like the street; under NARROW px the board is a column
|
|
197
|
+
// and only the drawing scales, in its own sideways strip, never under 0.6.
|
|
195
198
|
function fitInside() {
|
|
196
199
|
var box = $('streetInside'), sizer = $('insideSizer'), board = $('insideBoard'), art = $('insideArt'), artBox = $('insideArtBox'), span = $('insideArtSpan');
|
|
197
200
|
if (!box || !sizer || !board || !art || !artBox || box.hidden) return;
|
|
@@ -207,6 +210,14 @@
|
|
|
207
210
|
if (span) { span.style.width = Math.round(artW * a) + 'px'; span.style.height = Math.round(708 * a) + 'px'; }
|
|
208
211
|
return;
|
|
209
212
|
}
|
|
213
|
+
var work = !(view.inside && view.inside.kind === 'district');
|
|
214
|
+
if (box.classList) box.classList.toggle('inside--work', work);
|
|
215
|
+
if (work) {
|
|
216
|
+
var f = I.pickerFit(ch);
|
|
217
|
+
sizer.style.width = ''; sizer.style.height = ''; board.style.setProperty('--inside-scale', '1');
|
|
218
|
+
[[artBox, f.box], [art, f.art]].forEach(function (e) { Object.keys(e[1]).forEach(function (k) { e[0].style.setProperty(k, e[1][k]); }); });
|
|
219
|
+
if (span) { span.style.width = '0px'; span.style.height = '0px'; } return;
|
|
220
|
+
}
|
|
210
221
|
var s = Math.max(0.4, Math.round(Math.min(cw / 1440, ch / 708) * 1000) / 1000);
|
|
211
222
|
sizer.style.width = Math.round(1440 * s) + 'px';
|
|
212
223
|
sizer.style.height = Math.round(708 * s) + 'px';
|
|
@@ -292,7 +303,9 @@
|
|
|
292
303
|
// GovStreetDepth.page's (street-depth.js); this file hands it the street's
|
|
293
304
|
// data, its card opener and a redraw, and keeps the street's own wiring.
|
|
294
305
|
var X = window.GovStreetDepth.page;
|
|
295
|
-
|
|
306
|
+
// a pin goes into its building, the matter a desk row there (owner, 2026-11-04: no pop-ups)
|
|
307
|
+
function goPin(key, el, keys) { var b = el && el.getAttribute && el.getAttribute('data-building'); if (b) window.location.hash = 'building=' + b; else openCard(key, el, keys); }
|
|
308
|
+
function drawPin(p) { return X.pin(p, { open: view.open, openCard: goPin }); }
|
|
296
309
|
function drawSign(sg, tile) { return X.sign(sg, tile); }
|
|
297
310
|
function drawDepth(dm, W) { return X.depth(dm, W); }
|
|
298
311
|
// the living model arrives after the street's own reads: the street and the deck draw it
|
|
@@ -106,3 +106,15 @@ Owner decision 2026-10-04: the street should read at a glance without words, so
|
|
|
106
106
|
- **The evening view is unchanged** ("nothing waits on you." and its line still show when nothing needs you).
|
|
107
107
|
|
|
108
108
|
Tests: `tests/hall_street.mjs` asserts the model has no hint field, the module exports no `HINT`, and none of the street's four files carries the line; putting the markup or the constant back reds it.
|
|
109
|
+
|
|
110
|
+
## The hall is for work: the street as a square, the building as a place to work (owner, 2026-11-04)
|
|
111
|
+
|
|
112
|
+
The owner's direction: the builders' hall is where you go to do things, not to look at them. No menu on menu, no pop-ups (go to the place), no page header or footer that a web app does not need. On the street that meant:
|
|
113
|
+
|
|
114
|
+
- **The street is the governance square.** The working areas are the city's own buildings, so no district stands on the street: `streetModel()` draws one only with `districts: true` (the kit's tests still hold that row to the prototype), and without it the civic row is centred on the artboard. A wide window scales the street to the row (`FOCUS_W`, 1120), not the whole 1440 artboard, so the buildings are as large as the height allows and the haze either side is cropped; a phone still scrolls it sideways and opens at the first building.
|
|
115
|
+
- **The header strip and the six readings are gone** (`.street-top`, `.street-signs` are not displayed; the markup stays, so the counts, the deck link and the readings still compute). The shell's top bar names the page and the buildings carry their own state lines.
|
|
116
|
+
- **A pin takes you into its building** (`#building=<key>`), where each matter is a desk row you act on. It no longer opens the short card; the card is reached only by a pin with no building, and retiring it is a follow-up.
|
|
117
|
+
- **Inside a building the drawing is the floor picker.** On a wide window (`.inside--work`) the interior is laid out, not scaled as one 1440 x 708 picture: the section is cropped to the building (`CROP` in `fitInside`, 590 x 580 of the 800 x 708 section), at 0.5 to 0.72 so a floor stays a 24 px target, in its own column; the chosen floor's desk takes the rest of the window and scrolls on its own. The building's name sits on one line, then the desk, then the government layer, who holds it and how it decides, then the record. A phone keeps its column with the same order. A district keeps its scaled board.
|
|
118
|
+
- **A desk row is one full line.** The board's 20 / 26 / 24 caps were sized for a desk in a corner of the interior; `LIMITS` is now name 90 (14 words), fact 60, facts 48.
|
|
119
|
+
|
|
120
|
+
Tests: `tests/hall_street.mjs` (nine doors and no district on the page, a pin goes inside and fills no card, the row scaled and centred, a phone opening at the first building), `tests/hall_street_inside.mjs` (the cropped picker, never under half size, the board not scaled, a district untouched), `tests/hall_street_depth.mjs` (with no plot to fly from every agent rides the road), `tests/hall_street_desk.mjs` (the new limits).
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.39",
|
|
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.39",
|
|
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.39",
|
|
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
|
@@ -11191,5 +11191,11 @@
|
|
|
11191
11191
|
"id": "1005071",
|
|
11192
11192
|
"text": "The hall sidebar is now seven labelled sections (Work, Make, Community, You, Govern, Run the project, Help), and every craft can reach Help."
|
|
11193
11193
|
}
|
|
11194
|
+
],
|
|
11195
|
+
"1.24.39": [
|
|
11196
|
+
{
|
|
11197
|
+
"id": "1005077",
|
|
11198
|
+
"text": "The governor street is now a work-first square: the working areas left the street, the extra header and footer are gone, a pin takes you straight into its building, and inside a building the drawing is a compact floor picker s"
|
|
11199
|
+
}
|
|
11194
11200
|
]
|
|
11195
11201
|
}
|
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.39'; // 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');
|
|
@@ -290,9 +290,16 @@ const BASE = (d) => ({
|
|
|
290
290
|
'/api/bongos/government/constitution': { board: { membership: 'rank:metic+', pass_rule: 'unanimous', window_minutes: 1440, close_early_on_full_turnout: true } },
|
|
291
291
|
});
|
|
292
292
|
|
|
293
|
-
|
|
293
|
+
// A pin goes into its building now (owner, 2026-11-04: no pop-ups); the card
|
|
294
|
+
// still opens for a pin with no building, so these tests open it directly.
|
|
295
|
+
const openCard = (p) => {
|
|
296
|
+
const pin = p.byId.get('streetPins').children[0];
|
|
297
|
+
p.window.GovStreetPage.openCard(pin.attrs['data-matter'], pin, pin.attrs['data-matters'].split(' '));
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
test('the full card: the ballot is read, then the seats, counsel and acts are drawn as text', async () => {
|
|
294
301
|
const p = await bootPage({ feeds: BASE(docket([matter({ title: '<b>x</b>' })])) });
|
|
295
|
-
p
|
|
302
|
+
openCard(p);
|
|
296
303
|
await settle(); await settle();
|
|
297
304
|
assert.ok(p.calls.some((c) => c.url === '/api/bongos/government/board/items'), 'the room is read when a sitting opens');
|
|
298
305
|
const body = p.byId.get('streetCardBody');
|
|
@@ -308,7 +315,7 @@ test('vote for: one POST to the vote route, a toast, the docket re-read, and the
|
|
|
308
315
|
const feeds = BASE((() => { const d0 = docket([matter()]); return () => (reads++ === 0 ? d0 : after); })());
|
|
309
316
|
feeds['/api/bongos/government/docket'] = (() => { const d0 = docket([matter()]); return () => (reads++ === 0 ? d0 : after); })();
|
|
310
317
|
const p = await bootPage({ feeds, posts: { 'POST /api/bongos/government/board/items/41/votes': { ok: true, vote: {}, already_voted: false, karma_delta: 1 } } });
|
|
311
|
-
p
|
|
318
|
+
openCard(p);
|
|
312
319
|
await settle(); await settle();
|
|
313
320
|
byAct(p.byId.get('streetCardBody'), 'for').fire('click');
|
|
314
321
|
await settle(); await settle(); await settle();
|
|
@@ -324,7 +331,7 @@ test('vote for: one POST to the vote route, a toast, the docket re-read, and the
|
|
|
324
331
|
|
|
325
332
|
test('object: "no" opens the desk row\'s one-tap reasons, and a tap puts the reason and the part on the record', async () => {
|
|
326
333
|
const p = await bootPage({ feeds: BASE(docket([matter()])), posts: { 'POST /api/bongos/government/board/items/41/votes': { ok: true, vote: {}, item_closed: true, outcome: 'returned' } } });
|
|
327
|
-
p
|
|
334
|
+
openCard(p);
|
|
328
335
|
await settle(); await settle();
|
|
329
336
|
const body = () => p.byId.get('streetCardBody');
|
|
330
337
|
assert.equal(byAct(body(), 'object').textContent, D.ACT_WORDS.object, 'the desk row\'s word');
|
|
@@ -341,7 +348,7 @@ test('object: "no" opens the desk row\'s one-tap reasons, and a tap puts the rea
|
|
|
341
348
|
|
|
342
349
|
test('a refused act says why and records nothing; abstain sends nothing at all', async () => {
|
|
343
350
|
const p = await bootPage({ feeds: BASE(docket([matter()])), posts: { 'POST /api/bongos/government/board/items/41/votes': { __status: 403, error: { code: 'not_board_member' } } } });
|
|
344
|
-
p
|
|
351
|
+
openCard(p);
|
|
345
352
|
await settle(); await settle();
|
|
346
353
|
byAct(p.byId.get('streetCardBody'), 'for').fire('click');
|
|
347
354
|
await settle(); await settle();
|
|
@@ -361,7 +368,7 @@ test('release: the override is approved, the card closes, Release rises, and wit
|
|
|
361
368
|
feeds['/api/bongos/government/docket'] = () => (reads++ === 0 ? docket([ov]) : docket([]));
|
|
362
369
|
const p = await bootPage({ feeds, posts: { 'POST /api/bongos/override-requests/9/decide': { ok: true } } });
|
|
363
370
|
assert.equal(p.byId.get('streetEvening').hidden, true);
|
|
364
|
-
p
|
|
371
|
+
openCard(p);
|
|
365
372
|
byAct(p.byId.get('streetCardBody'), 'release').fire('click');
|
|
366
373
|
await settle(); await settle(); await settle();
|
|
367
374
|
assert.deepEqual(p.calls.filter((c) => c.method !== 'GET').map((c) => [c.method, c.url, c.body]), [['POST', '/api/bongos/override-requests/9/decide', { decision: 'approve' }]]);
|
|
@@ -476,7 +483,7 @@ test('more expands the same card in place: the full text, who and when, and the
|
|
|
476
483
|
const { d } = SOURCE_FIXTURES();
|
|
477
484
|
d.matters = d.matters.filter((m) => m.source === 'blockers');
|
|
478
485
|
const p = await bootPage({ feeds: { ...BASE(d) } });
|
|
479
|
-
p
|
|
486
|
+
openCard(p);
|
|
480
487
|
await settle(); await settle();
|
|
481
488
|
const body = () => p.byId.get('streetCardBody');
|
|
482
489
|
const toggle = () => byAct(body(), 'more');
|
package/tests/hall_street.mjs
CHANGED
|
@@ -72,7 +72,9 @@ function docket(matters = [], over = {}) {
|
|
|
72
72
|
}
|
|
73
73
|
const goal = (id, status = 'open', title = `Goal ${id}`) => ({ id: String(id), title, status });
|
|
74
74
|
const rollup = (id, c) => ({ goal_id: String(id), done: 0, active: 0, ready: 0, blocked: 0, backlog: 0, cut: 0, ...c });
|
|
75
|
-
|
|
75
|
+
// The kit is held to the prototype's row, districts and all (`districts: true`); the
|
|
76
|
+
// page itself draws the civic row alone (owner, 2026-11-04), pinned further down.
|
|
77
|
+
const model = (over = {}) => S.streetModel({ docket: docket(), goals: [], rollups: [], mode: 'dark', now: NOW, districts: true, ...over });
|
|
76
78
|
|
|
77
79
|
// ── the drawing is the prototype's ───────────────────────────────────────────
|
|
78
80
|
|
|
@@ -299,7 +301,7 @@ test('the collision pass places in tier order: when only one of two can show, th
|
|
|
299
301
|
});
|
|
300
302
|
|
|
301
303
|
test('no docket: no pins, the readings say so, and nothing is invented', () => {
|
|
302
|
-
const m = S.streetModel({ docket: null, goals: [goal(1)], rollups: [], mode: 'dark', now: NOW });
|
|
304
|
+
const m = S.streetModel({ docket: null, goals: [goal(1)], rollups: [], mode: 'dark', now: NOW, districts: true });
|
|
303
305
|
assert.equal(m.pins.length, 0);
|
|
304
306
|
assert.equal(m.crates.length, 0);
|
|
305
307
|
assert.ok(m.signs.every((s) => s.figure === '—' && !s.hot && s.line === S.FALLBACK_LINE));
|
|
@@ -617,32 +619,30 @@ test('the page reads the four feeds and draws the docket\'s pins, the readings a
|
|
|
617
619
|
assert.equal(p.byId.get('hudMoving').textContent, '2');
|
|
618
620
|
assert.equal(p.byId.get('hudShare').textContent, '$24');
|
|
619
621
|
assert.equal(p.byId.get('hudDot').className, 'street-hud__dot street-hud__dot--hot');
|
|
620
|
-
assert.equal(p.byId.get('streetDoors').children.length,
|
|
622
|
+
assert.equal(p.byId.get('streetDoors').children.length, 9, 'the nine civic buildings, each a door; no district stands on the street (owner, 2026-11-04)');
|
|
623
|
+
assert.ok(p.byId.get('streetDoors').children.every((a) => !/district=/.test(a.attrs.href)), 'no door to a working area');
|
|
621
624
|
assert.equal(p.byId.get('streetHome').attrs.href, '/builders/city');
|
|
622
625
|
});
|
|
623
626
|
|
|
624
|
-
|
|
625
|
-
|
|
627
|
+
// Owner, 2026-11-04: no pop-ups, go to the place. A pin takes you into its
|
|
628
|
+
// building, where each matter is a desk row you act on; it opens no card.
|
|
629
|
+
test('a pin takes you into its building and opens no card', async () => {
|
|
630
|
+
const p = await bootPage({ feeds: FEEDS([matter({ id: 'board:1', building: 'charter', title: 'A vote', href: '/builders/board-room' })]) });
|
|
626
631
|
const pin = p.byId.get('streetPins').children[0];
|
|
632
|
+
assert.equal(pin.attrs['data-building'], 'charter');
|
|
627
633
|
pin.fire('click');
|
|
628
|
-
|
|
629
|
-
assert.equal(
|
|
630
|
-
assert.
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
assert.equal(card.hidden, true);
|
|
637
|
-
assert.equal(e.prevented, true, 'the press is used, so nothing else answers it');
|
|
638
|
-
assert.equal(p.focused.el.attrs['data-matter'], 'board:1');
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
test('the card pager: a pin with several matters opens the most important with "1 of N", and previous/next reach every matter there', async () => {
|
|
634
|
+
assert.equal(p.window.location.hash, 'building=charter', 'the address names the building, so its inside opens');
|
|
635
|
+
assert.equal(p.byId.get('streetCardTitle').textContent, '', 'no card is filled');
|
|
636
|
+
assert.notEqual(p.byId.get('streetPins').children[0].attrs['aria-expanded'], 'true', 'and the pin does not say one is open');
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// The card itself still works for the code paths that open it (a pin with no
|
|
640
|
+
// building); its pager is held here by opening it directly.
|
|
641
|
+
test('the card pager: a card over several matters opens the most important with "1 of N", and previous/next reach every matter there', async () => {
|
|
642
642
|
const ms = [matter({ id: 'blockers:1', state: 'moving', title: 'Third' }), matter({ id: 'blockers:2', state: 'needs', title: 'First' }), matter({ id: 'blockers:3', state: 'needs', title: 'Second' }), matter({ id: 'blockers:4', state: 'settled', title: 'Fourth' })];
|
|
643
643
|
const p = await bootPage({ feeds: FEEDS(ms) });
|
|
644
644
|
const pin = p.byId.get('streetPins').children.find((s) => s.attrs['data-building'] === 'planning');
|
|
645
|
-
pin.
|
|
645
|
+
p.window.GovStreetPage.openCard(pin.attrs['data-matter'], pin, pin.attrs['data-matters'].split(' '));
|
|
646
646
|
const body = p.byId.get('streetCardBody');
|
|
647
647
|
const pager = () => body.children[0];
|
|
648
648
|
const btn = (dir) => pager().children.find((c) => c.attrs['data-act'] === 'page-' + dir);
|
|
@@ -664,7 +664,8 @@ test('the card pager: a pin with several matters opens the most important with "
|
|
|
664
664
|
assert.equal(p.focused.el.attrs['data-building'], 'planning', 'Escape returns focus to the pin');
|
|
665
665
|
// one matter: no pager
|
|
666
666
|
const q = await bootPage({ feeds: FEEDS([matter({ id: 'board:1', building: 'charter' })]) });
|
|
667
|
-
q.byId.get('streetPins').children[0]
|
|
667
|
+
const one = q.byId.get('streetPins').children[0];
|
|
668
|
+
q.window.GovStreetPage.openCard(one.attrs['data-matter'], one, one.attrs['data-matters'].split(' '));
|
|
668
669
|
assert.ok(!q.byId.get('streetCardBody').children.some((c) => c.className === 'mcard-pager'), 'one matter, no pager');
|
|
669
670
|
});
|
|
670
671
|
|
|
@@ -737,17 +738,22 @@ test('arriving through a door marks that building, and a failed docket says so',
|
|
|
737
738
|
assert.equal(q.byId.get('hudShare').textContent, '—');
|
|
738
739
|
});
|
|
739
740
|
|
|
740
|
-
//
|
|
741
|
-
//
|
|
742
|
-
|
|
741
|
+
// Owner, 2026-11-04 (supersedes task 1004718's foot): the readings left the
|
|
742
|
+
// page, so a wide window scales the street to its civic row, not the whole
|
|
743
|
+
// artboard, crops the haze either side, and centres the street in its stage.
|
|
744
|
+
test('a wide window scales the street to its civic row and centres it; a phone scrolls it from the first building', async () => {
|
|
743
745
|
const p = await bootPage({ feeds: FEEDS([]), width: 1440 });
|
|
744
|
-
const win = p.byId.get('streetWindow'), sizer = p.byId.get('streetSizer');
|
|
745
|
-
win.clientHeight =
|
|
746
|
-
p.key('resize', {});
|
|
747
|
-
const h = parseFloat(sizer.style.height);
|
|
748
|
-
assert.ok(h < 900, 'the street is width-bound here');
|
|
749
|
-
assert.equal(win.style.flex, '0 0 ' + h + 'px', 'the window shrinks to the street, so the readings sit right under it');
|
|
750
|
-
win.clientHeight = 300;
|
|
746
|
+
const win = p.byId.get('streetWindow'), sizer = p.byId.get('streetSizer'), board = p.byId.get('streetBoard');
|
|
747
|
+
win.clientWidth = 1376; win.clientHeight = 844;
|
|
751
748
|
p.key('resize', {});
|
|
752
|
-
|
|
749
|
+
const s = Number(board.style['--street-scale']);
|
|
750
|
+
assert.ok(s > 1376 / 1440, 'larger than the whole artboard would allow');
|
|
751
|
+
assert.equal(win.style.flex, '', 'the window keeps the stage, so the street sits centred in it');
|
|
752
|
+
assert.equal(win.style.overflowX, 'hidden', 'no sideways scroll on a wide window');
|
|
753
|
+
assert.equal(parseFloat(sizer.style.width), 1376, 'the sizer is the window');
|
|
754
|
+
assert.ok(parseFloat(board.style.left) < 0, 'the artboard is centred, its haze cropped either side');
|
|
755
|
+
const q = await bootPage({ feeds: FEEDS([]), width: 390 });
|
|
756
|
+
const qwin = q.byId.get('streetWindow');
|
|
757
|
+
assert.equal(qwin.style.overflowX, '', 'a phone scrolls the street sideways');
|
|
758
|
+
assert.ok(qwin.scrollLeft > 0, 'starting at the first building, not the empty haze before it');
|
|
753
759
|
});
|
|
@@ -70,7 +70,7 @@ function feeds(over = {}) {
|
|
|
70
70
|
function living(f) { return L.livingModel({ ...f, now: NOW }); }
|
|
71
71
|
function street(over = {}, mode = 'dark') {
|
|
72
72
|
const f = feeds(over);
|
|
73
|
-
return S.streetModel({ docket: f.docket, goals: f.goals.goals, rollups: f.rollups.rollups, mode, now: NOW, depth: true, living: f.living === null ? null : living(f), roster: f.roster });
|
|
73
|
+
return S.streetModel({ docket: f.docket, goals: f.goals.goals, rollups: f.rollups.rollups, mode, now: NOW, depth: true, living: f.living === null ? null : living(f), roster: f.roster, districts: true });
|
|
74
74
|
}
|
|
75
75
|
const openGoals = () => fx('goals').goals.filter((g) => g.status === 'open');
|
|
76
76
|
|
|
@@ -338,7 +338,7 @@ test('an overlay handed in changes nothing; the Leads not read, no Lead, capsule
|
|
|
338
338
|
const c = street({ leads: null }).depth;
|
|
339
339
|
assert.deepEqual([c.plates.length, c.capsules.length, c.sites.length], [0, 0, 0], 'no Leads read: no plate, no capsule, no build-out going up');
|
|
340
340
|
// without the living model at all the deep street still draws its real skyline and windows
|
|
341
|
-
const none = S.streetModel({ docket: fx('government__docket'), goals: fx('goals').goals, rollups: fx('goals__rollups').rollups, mode: 'dark', now: NOW, depth: true, living: null, roster: null }).depth;
|
|
341
|
+
const none = S.streetModel({ docket: fx('government__docket'), goals: fx('goals').goals, rollups: fx('goals__rollups').rollups, mode: 'dark', now: NOW, depth: true, living: null, roster: null, districts: true }).depth;
|
|
342
342
|
assert.deepEqual(none.clusters, a.clusters);
|
|
343
343
|
assert.deepEqual(none.windows, street({ leads: null, docket: fx('government__docket') }).depth.windows, 'no living model and the hub as it is light the same, the docket\'s own');
|
|
344
344
|
assert.equal(none.agents, 0);
|
|
@@ -361,7 +361,7 @@ test('street-depth.js: its model half is pure, and the two drawing files require
|
|
|
361
361
|
const before = globalThis.GovStreetDepth;
|
|
362
362
|
delete globalThis.GovStreetDepth;
|
|
363
363
|
try {
|
|
364
|
-
const flat = S.streetModel({ docket: fx('government__docket'), goals: openGoals(), rollups: fx('goals__rollups').rollups, mode: 'dark', now: NOW, depth: true });
|
|
364
|
+
const flat = S.streetModel({ docket: fx('government__docket'), goals: openGoals(), rollups: fx('goals__rollups').rollups, mode: 'dark', now: NOW, depth: true, districts: true });
|
|
365
365
|
assert.equal(flat.depth, undefined);
|
|
366
366
|
} finally { globalThis.GovStreetDepth = before; }
|
|
367
367
|
});
|
|
@@ -500,9 +500,11 @@ test('the page draws the living street: the skyline, a pod per claim, the plates
|
|
|
500
500
|
assert.equal(cls('streetDepthFar', 'street-depth-sil__face').length, 1);
|
|
501
501
|
const claims = fx('tasks__claimable').active_claims.length;
|
|
502
502
|
assert.equal(cls('streetDepthSky', 'street-depth-pod').length + cls('streetDepthFront', 'street-depth-pod').length, claims, 'one pod per agent');
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
503
|
+
// The page stands no district (owner, 2026-11-04), so no claim flies a skylane
|
|
504
|
+
// from its goal's plot: every agent rides the road (the kit's skylanes are held
|
|
505
|
+
// with districts on, above).
|
|
506
|
+
assert.equal(cls('streetDepthSky', 'street-depth-pod').length, 0, 'no plot to fly from');
|
|
507
|
+
assert.equal(cls('streetDepthFront', 'street-depth-pod--road').length, claims, 'every agent on the road');
|
|
506
508
|
assert.equal(cls('streetDepthFront', 'street-depth-plate').length, 9);
|
|
507
509
|
assert.equal(all('streetDepthFront').concat(all('streetDepthSky')).filter((e) => 'data-proto' in e.attrs).length, 0, 'nothing is marked prototype');
|
|
508
510
|
assert.equal(cls('streetDepthFront', 'street-depth-jib').length, 1, 'the live site\'s crane');
|
|
@@ -105,11 +105,14 @@ test('fit() is the board\'s fit(), with a word cap on top', () => {
|
|
|
105
105
|
for (const n of [5, 12, 20]) for (const w of [1, 2, 3]) assert.ok(D.fit(LONG, n, w).length <= n, 'never longer than its cap');
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
// A row's name and fact were the board's 20 / 26 / 24 while the desk was a
|
|
109
|
+
// corner of the interior; the desk takes the window's width now (owner,
|
|
110
|
+
// 2026-11-04), so a row is one full line: name 90 (14 words), fact 60, facts 48.
|
|
111
|
+
test('the limits: a row is one full line (name 90, fact 60, facts 48, three), status 44, record 30 (four), three tiles, ten pips', () => {
|
|
109
112
|
const L = D.LIMITS;
|
|
110
|
-
assert.deepEqual([L.name, L.nameWords, L.fact, L.factLen, L.facts, L.actions, L.status, L.recordLen, L.record, L.tiles, L.tileWords, L.todayTarget], [
|
|
113
|
+
assert.deepEqual([L.name, L.nameWords, L.fact, L.factLen, L.facts, L.actions, L.status, L.recordLen, L.record, L.tiles, L.tileWords, L.todayTarget], [90, 14, 60, 48, 3, 2, 44, 30, 4, 3, 2, 10]);
|
|
111
114
|
const src = fs.readFileSync(path.join(ROOT, 'docs/design/mocks/governor-city/Desks.dc.html'), 'utf8');
|
|
112
|
-
for (const pin of ['
|
|
115
|
+
for (const pin of ['.slice(0, 3)', "this.fit('nothing needs you · ' + F.quiet, 44)", "this.fit(t + ' · now', 30)", '.slice(0, 4)', 'const target = 10']) assert.ok(src.includes(pin), `the board says ${pin}`);
|
|
113
116
|
});
|
|
114
117
|
|
|
115
118
|
test('THE LINE RULE: no row, tile or status any docket produces breaks it, on every floor of every building', () => {
|
|
@@ -140,8 +143,8 @@ test('breaks() names every way a desk can break the rule (so the rule test can f
|
|
|
140
143
|
const good = D.deskModel({ key: 'people', floor: 4, docket: dk, now: NOW });
|
|
141
144
|
assert.deepEqual(D.breaks(good), []);
|
|
142
145
|
const bad = j(good);
|
|
143
|
-
bad.rows[0].name = '
|
|
144
|
-
bad.rows[0].fact = 'x'.repeat(
|
|
146
|
+
bad.rows[0].name = Array(16).fill('word').join(' ');
|
|
147
|
+
bad.rows[0].fact = 'x'.repeat(61);
|
|
145
148
|
bad.rows[0].actions = [{ label: 'a' }, { label: 'b' }, { label: 'c' }];
|
|
146
149
|
bad.rows[0].facts = ['a', 'b', 'c', 'd'];
|
|
147
150
|
bad.tiles = [{ v: '0', l: 'need you' }, { v: '3', l: 'three whole words' }, { v: '4', l: 'ok' }, { v: '5', l: 'ok' }];
|
|
@@ -555,6 +555,24 @@ test('the markup carries the interior: one persistent lift, two flip blocks, the
|
|
|
555
555
|
assert.match(html, /<button type="button" class="inside-textbtn" id="insideAmend"/);
|
|
556
556
|
});
|
|
557
557
|
|
|
558
|
+
// Owner, 2026-11-04: a building is a place to work. On a wide window the
|
|
559
|
+
// drawing is a floor picker cropped to the building, at half size or more (a
|
|
560
|
+
// floor stays a 24 px target), and the desk has the rest of the window; the
|
|
561
|
+
// whole interior is no longer one scaled picture. A district keeps its board.
|
|
562
|
+
test('inside a building the drawing is a cropped floor picker, never under half size, and the board is not scaled', async () => {
|
|
563
|
+
const p = await bootPage({ hash: '#building=planning', feeds: FEEDS([]) });
|
|
564
|
+
const box = p.byId.get('insideArtBox'), art = p.byId.get('insideArt'), board = p.byId.get('insideBoard');
|
|
565
|
+
const k = Number(box.style['--inside-art-scale']);
|
|
566
|
+
assert.ok(k >= 0.5 && k <= 0.72, `picker scale ${k}`);
|
|
567
|
+
assert.equal(board.style['--inside-scale'], '1', 'the interior is laid out, not shrunk as one picture');
|
|
568
|
+
assert.equal(art.style['--inside-art-w'], Math.round(590 * k) + 'px', 'cropped to the building, not the 800 px section');
|
|
569
|
+
assert.equal(box.style['--inside-art-x'], Math.round(-50 * k) + 'px');
|
|
570
|
+
assert.ok(47 * k >= 24, 'a floor stays a 24 px target');
|
|
571
|
+
const d = await bootPage({ hash: '#district=1', feeds: FEEDS([]) });
|
|
572
|
+
assert.equal(d.byId.get('insideArtBox').style['--inside-art-scale'], '1', 'a district keeps its whole board');
|
|
573
|
+
assert.equal(d.byId.get('insideArtBox').style['--inside-art-x'], undefined, 'and is not cropped');
|
|
574
|
+
});
|
|
575
|
+
|
|
558
576
|
test('a building address draws the interior in place of the street, from the docket', async () => {
|
|
559
577
|
const ms = [matter({ id: 'access_requests:1', source: 'access_requests', building: 'people', state: 'needs', title: 'Admit Kai' })];
|
|
560
578
|
const p = await bootPage({ hash: '#building=people', feeds: FEEDS(ms) });
|