@bongos/core 1.21.41 → 1.21.43
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 +64 -64
- package/docs/copy-inventory.md +10 -11
- package/docs/copy-registry.json +11 -20
- package/docs/design/craft-mode-direction.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +4 -2
- package/docs/page-readings.json +3467 -3507
- package/modules/autonomy/governor-docket.js +6 -1
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/city.html +3 -3
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/craft-mode.js +10 -15
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/drachmae.html +3 -3
- package/modules/hall-ui/public/fleet.html +3 -3
- package/modules/hall-ui/public/gate.html +3 -3
- package/modules/hall-ui/public/goal-map.html +3 -3
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/ranks.html +3 -3
- package/modules/hall-ui/public/roadmap.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/sessions.html +3 -3
- package/modules/hall-ui/public/settings.html +3 -3
- package/modules/hall-ui/public/street-card.css +13 -0
- package/modules/hall-ui/public/street-card.js +61 -9
- package/modules/hall-ui/public/street-card.states.json +72 -0
- package/modules/hall-ui/public/street.html +3 -3
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/style.css +4 -22
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/thinking.html +3 -3
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/records/craft-mode.md +5 -5
- package/modules/hall-ui/records/governor-street.md +1 -0
- 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/craft_mode.mjs +27 -10
- package/tests/craft_mode_looks.mjs +5 -5
- package/tests/craft_mode_nav.mjs +7 -7
- package/tests/governor_docket.mjs +3 -1
- package/tests/hall_matter_card.mjs +71 -0
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/page_notes_panel.mjs +1 -1
package/tests/craft_mode_nav.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// tests/craft_mode_nav.mjs — the hall nav follows the craft mode (task 1004493,
|
|
2
2
|
// goal 1000095 WA6).
|
|
3
3
|
//
|
|
4
|
-
// Each craft mode shows only the tabs its craft needs;
|
|
5
|
-
//
|
|
4
|
+
// Each craft mode shows only the tabs its craft needs; no mode
|
|
5
|
+
// shows every tab, exactly as before. The owner's rule (2026-10-01): some tabs
|
|
6
6
|
// stay in EVERY mode, Builders first among them, written down as ONE shared list
|
|
7
7
|
// that each mode's list adds to; on 2026-10-03 Collab and Profile joined it. These cases boot the REAL shell-trail.js +
|
|
8
8
|
// craft-mode.js + shell.js and read the sidebar the shell actually rendered, so a
|
|
@@ -157,15 +157,15 @@ test('Government shows only in Governing, and Governing is the shortest list', (
|
|
|
157
157
|
assert.equal(Math.min(...lengths), lengths[CRAFTS.indexOf('governing')], 'outside its own Government group, Governing shows the fewest tabs');
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
-
test('NO MODE
|
|
160
|
+
test('NO MODE IS TODAY\'S NAV: every tab the gate allows, nothing marked — and a stored Legacy is no mode', () => {
|
|
161
161
|
const none = boot();
|
|
162
162
|
none.signIn();
|
|
163
163
|
const all = none.showing();
|
|
164
164
|
assert.deepEqual(all, NAV_IDS, 'an archon with no mode sees every tab');
|
|
165
165
|
const legacy = boot({ session: { 'hall-craft': 'legacy' } });
|
|
166
166
|
legacy.signIn();
|
|
167
|
-
assert.
|
|
168
|
-
assert.
|
|
167
|
+
assert.equal(legacy.root.dataset.craft, undefined, 'Legacy was removed (task 1004567), so it reads as no mode');
|
|
168
|
+
assert.deepEqual(legacy.showing(), all, 'and shows exactly the same');
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
test('each mode shows its own tabs, and switching updates the nav live', () => {
|
|
@@ -178,8 +178,8 @@ test('each mode shows its own tabs, and switching updates the nav live', () => {
|
|
|
178
178
|
for (const id of ['roster', 'collab', 'profile']) assert.ok(p.showing().includes(id), `${id} stays in ${m}`);
|
|
179
179
|
assert.equal(p.headShowing('government'), m === 'governing', `the Government head shows only where its tabs do (${m})`);
|
|
180
180
|
}
|
|
181
|
-
p.craft.choose('
|
|
182
|
-
assert.deepEqual(p.showing(),
|
|
181
|
+
p.craft.choose('building');
|
|
182
|
+
assert.deepEqual(new Set(p.showing()), new Set(p.craft.tabsFor('building')), 'back to Building, its tabs return without a reload');
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
test('a mode never shows a tab the gate hid, and a later gate pass keeps the mode', () => {
|
|
@@ -399,7 +399,9 @@ test('the fence: a matter only when it needs a governor, and only a fence manage
|
|
|
399
399
|
const off = autonomyDocket.fenceDocketMatters({ enabled: false, paused_reason: 'payments touched', goals: [{ goal_id: 1 }], updated_by: 3 }, true);
|
|
400
400
|
assert.equal(off.length, 1);
|
|
401
401
|
assert.equal(off[0].title, 'Unattended building is switched off');
|
|
402
|
-
assert.equal(off[0].where, 'automation · the switch ·
|
|
402
|
+
assert.equal(off[0].where, 'automation · the switch', 'the kicker is building · floor only (task 1004568)');
|
|
403
|
+
assert.equal(off[0].detail, 'payments touched', 'the reason rides in detail: the fact, and the more');
|
|
404
|
+
assert.equal(autonomyDocket.fenceDocketMatters({ enabled: false, goals: [] }, true)[0].detail, undefined, 'no reason, no detail');
|
|
403
405
|
assert.equal(off[0].state, 'needs');
|
|
404
406
|
assert.equal(autonomyDocket.fenceDocketMatters({ enabled: false, goals: [] }, false)[0].state, 'settled');
|
|
405
407
|
assert.equal(autonomyDocket.fenceDocketMatters({ enabled: true, goals: [] }, true)[0].title, 'Unattended building is on, but no goal is allowed');
|
|
@@ -507,6 +507,77 @@ test('the docket hands a matter its detail only as text, bounded, and only when
|
|
|
507
507
|
}
|
|
508
508
|
});
|
|
509
509
|
|
|
510
|
+
// ── the compact card sizes to its words (task 1004568) ─────────────────────
|
|
511
|
+
|
|
512
|
+
const allText = (n) => [n._text || ''].concat((n.children || []).map(allText)).join('\n');
|
|
513
|
+
|
|
514
|
+
test('a very long reason stays within the compact card and is whole under more; the kicker is building · floor only', () => {
|
|
515
|
+
const docketJs = require(path.join(ROOT, 'modules/government/docket.js'));
|
|
516
|
+
const fence = require(path.join(ROOT, 'modules/autonomy/governor-docket.js'));
|
|
517
|
+
const reason = 'lars: stop runner for performance evaluation (2026-10-02) while the grader is rebuilt, then switch it back on once the new numbers are in and the owner has read them';
|
|
518
|
+
// the whole path: the autonomy module's matter, through the docket's normaliser, into the card
|
|
519
|
+
const raw = fence.fenceDocketMatters({ enabled: false, paused_reason: reason, goals: [], updated_by: 3, updated_at: '2026-10-02T10:00:00.000Z' }, true)[0];
|
|
520
|
+
const m = docketJs.normalizeMatter(raw, { source: 'autobongos_fence', building: 'automation' });
|
|
521
|
+
assert.equal(m.where, 'automation · the switch', 'the reason is not joined into the kicker');
|
|
522
|
+
const d = docket([m]);
|
|
523
|
+
const cv = D.cardFor(m.id, d, { now: NOW });
|
|
524
|
+
assert.equal(cv.where, 'automation · the switch');
|
|
525
|
+
assert.ok(cv.fact.length <= C.LINE.fact && /…$/.test(cv.fact), 'the fact is one line, cut, and says so: ' + cv.fact);
|
|
526
|
+
assert.ok(reason.startsWith(cv.fact.replace(/…$/, '')), 'the fact is the reason, cut');
|
|
527
|
+
assert.equal(reason.charAt(cv.fact.length - 1), ' ', 'never mid-word');
|
|
528
|
+
const { document } = fakeDom();
|
|
529
|
+
const shut = document.createElement('div');
|
|
530
|
+
C.render(document, shut, cv, { act() {}, openForm() {}, ask() {}, more() {}, pick() {} }, { more: false });
|
|
531
|
+
assert.ok(!allText(shut).includes(reason), 'the compact card does not carry the whole reason');
|
|
532
|
+
assert.equal(walk(shut, (n) => n.className === 'mcard-fact').length, 1, 'one fact line');
|
|
533
|
+
const open = document.createElement('div');
|
|
534
|
+
C.render(document, open, cv, { act() {}, openForm() {}, ask() {}, more() {}, pick() {} }, { more: true });
|
|
535
|
+
assert.ok(allText(open).includes(reason), 'more shows the reason whole');
|
|
536
|
+
// no reason, no fact
|
|
537
|
+
const bare = docketJs.normalizeMatter(fence.fenceDocketMatters({ enabled: false, goals: [] }, true)[0], { source: 'autobongos_fence', building: 'automation' });
|
|
538
|
+
assert.equal(D.cardFor(bare.id, docket([bare]), { now: NOW }).fact, '');
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test('every part of the compact card is held to its line: the kicker, the title, the listed lines, a voice\'s reason', () => {
|
|
542
|
+
const long = (w) => Array.from({ length: 40 }, (_, i) => w + i).join(' ');
|
|
543
|
+
const m = matter({ id: 'blockers:9', source: 'blockers', building: 'review', rule: 'decide', where: long('where'), title: long('title'), detail: long('detail'), ref: { type: 'blocker', id: '9' } });
|
|
544
|
+
const cv = C.model({ docket: docket([m]), key: m.id, now: NOW });
|
|
545
|
+
for (const [k, n] of [['where', C.LINE.where], ['title', C.LINE.title], ['fact', C.LINE.fact]]) {
|
|
546
|
+
assert.ok(cv[k].length <= n && /…$/.test(cv[k]), k + ' is cut: ' + cv[k]);
|
|
547
|
+
const whole = long(k === 'fact' ? 'detail' : k);
|
|
548
|
+
assert.ok(whole.startsWith(cv[k].replace(/…$/, '')), k + ' keeps its own words');
|
|
549
|
+
assert.equal(whole.charAt(cv[k].length - 1), ' ', k + ' cut at a word, not inside one');
|
|
550
|
+
}
|
|
551
|
+
const words = cv.more.text.map((x) => x.words);
|
|
552
|
+
assert.ok(words.includes(long('title')), 'the whole title is under more');
|
|
553
|
+
assert.ok(words.includes(long('where').toLowerCase()), 'the whole kicker is under more');
|
|
554
|
+
assert.ok(words.includes(long('detail')), 'the whole detail is under more');
|
|
555
|
+
// the queue's listed lines and an objection's reason
|
|
556
|
+
const lines = C.compact({ where: '', title: '', fact: '', lines: [long('line')], voices: [{ side: 'against', by: 'Kai', words: long('because') }], more: null });
|
|
557
|
+
assert.ok(lines.lines[0].length <= C.LINE.line && lines.voices[0].words.length <= C.LINE.reason);
|
|
558
|
+
assert.deepEqual(lines.more.text.map((x) => x.words).sort(), [long('because'), long('line')].sort(), 'both whole under more');
|
|
559
|
+
// a text "more" already holds, shortened by its own bound, is not added twice
|
|
560
|
+
const huge = long('w').repeat(40);
|
|
561
|
+
assert.ok(huge.length > 4000, 'longer than more keeps');
|
|
562
|
+
const twice = C.compact({ where: '', title: huge, fact: '', lines: [], voices: [], more: { text: [{ label: '', words: huge.slice(0, 3999).trim() + '…' }], who: '', when: '', history: [] } });
|
|
563
|
+
assert.equal(twice.more.text.length, 1, 'no duplicate under more');
|
|
564
|
+
// short words are left alone, and add nothing to more
|
|
565
|
+
const short = C.compact({ where: 'review · blockers', title: 'Pick a host', fact: 'waits on you', lines: ['one'], voices: [], more: null });
|
|
566
|
+
assert.deepEqual([short.where, short.title, short.fact, short.lines, short.more], ['review · blockers', 'Pick a host', 'waits on you', ['one'], null]);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('the card has no fixed height: no min-height on its parts, every one-line part is held to one line', () => {
|
|
570
|
+
const css = read('street-card.css') + read('street.css');
|
|
571
|
+
const rules = [...css.matchAll(/([^{}]*street-card[^{}]*)\{([^}]*)\}/g)];
|
|
572
|
+
for (const [, sel, body] of rules) if (!/__close/.test(sel)) assert.doesNotMatch(body, /(^|;)\s*(min-height|height)\s*:/, 'no fixed height on ' + sel.trim());
|
|
573
|
+
const card = read('street-card.css');
|
|
574
|
+
for (const sel of ['.street-card__head .street-label', '.mcard-fact', '.street-card__lines li']) {
|
|
575
|
+
const body = card.split(sel + ' {')[1];
|
|
576
|
+
assert.ok(body && /white-space: nowrap/.test(body.split('}')[0]) && /text-overflow: ellipsis/.test(body.split('}')[0]), sel + ' is one line');
|
|
577
|
+
}
|
|
578
|
+
assert.match(card, /\.street-card__title \{[^}]*-webkit-line-clamp: 3/);
|
|
579
|
+
});
|
|
580
|
+
|
|
510
581
|
// ── the Board Room names the new subjects ───────────────────────────────────
|
|
511
582
|
|
|
512
583
|
test('the Board Room labels a building\'s matter as that building\'s matter, and a who-decides change as its own amendment', () => {
|
|
@@ -635,7 +635,7 @@ try {
|
|
|
635
635
|
assert.match(HTML, /<body data-page="tweak-editor"[^>]*data-fullscreen-switch/);
|
|
636
636
|
assert.equal((HTML.match(/<h1\b/g) || []).length, 1);
|
|
637
637
|
assert.ok(HTML.indexOf('tweak-editor-lib.js') < HTML.indexOf('tweak-editor.js?'), 'the lib loads first');
|
|
638
|
-
assert.match(HTML, /shell\.js\?v=2026-10-04-
|
|
638
|
+
assert.match(HTML, /shell\.js\?v=2026-10-04-nolegacy/);
|
|
639
639
|
assert.equal(/fonts\.googleapis\.com\/css/.test(HTML), false);
|
|
640
640
|
for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-open-more', 'tw-divider', 'tw-openwrap', 'tw-open-menu', 'tw-open-tab', 'tw-open-split', 'tw-frame', 'tw-frame-view', 'tw-split-close', 'tw-download', 'tw-upload', 'tw-file', 'tw-submit', 'tw-paper', 'tw-doc', 'tw-notice', 'tw-done', 'tw-take', 'tw-pick', 'tw-picks', 'tw-refused', 'tw-refused-title', 'tw-refused-list', 'tw-refused-next', 'tw-refused-download', 'tw-refused-close']) {
|
|
641
641
|
assert.ok(new RegExp(`id="${id}"`).test(HTML), `#${id} is in the markup`);
|
|
@@ -79,7 +79,7 @@ test('THE PALETTE: in the top bar in Artistry alone, right before the switch, an
|
|
|
79
79
|
assert.deepEqual(p.tools().map((t) => t.id), ['craft-tool-palette']);
|
|
80
80
|
assert.equal(p.tools()[0].nextSibling, p.sw, 'it sits right before the mode switch');
|
|
81
81
|
assert.equal(p.tools()[0].getAttribute('aria-label'), 'Write feedback on this page');
|
|
82
|
-
for (const m of ['building', 'ideating', 'governing'
|
|
82
|
+
for (const m of ['building', 'ideating', 'governing']) {
|
|
83
83
|
p.craft.choose(m);
|
|
84
84
|
assert.equal(p.tools().length, 0, `no palette in ${m}`);
|
|
85
85
|
}
|