@bongos/core 1.19.468 → 1.19.470
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 +94 -44
- package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +169 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +42 -50
- package/docs/copy-registry.json +167 -271
- package/docs/file-map.md +5 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/research/task-network-map/a-every-task-p2.png +0 -0
- package/docs/research/task-network-map/a-every-task-p40.png +0 -0
- package/docs/research/task-network-map/b-dependency-forest-p2.png +0 -0
- package/docs/research/task-network-map/b-dependency-forest-p40.png +0 -0
- package/docs/research/task-network-map/c-goals-p2.png +0 -0
- package/docs/research/task-network-map/c-goals-p40.png +0 -0
- package/docs/research/task-network-map/d-joined-by-goal.png +0 -0
- package/docs/research/task-network-map-readability.md +191 -0
- package/modules/hall-ui/public/atlas.css +96 -88
- package/modules/hall-ui/public/atlas.html +43 -20
- package/modules/hall-ui/public/atlas.js +118 -20
- package/modules/hall-ui/public/atlas.states.json +52 -1
- package/modules/hall-ui/public/modules.css +67 -86
- package/modules/hall-ui/public/modules.html +46 -35
- package/modules/hall-ui/public/modules.js +195 -103
- package/modules/hall-ui/public/modules.states.json +48 -1
- package/modules/hall-ui/public/people.css +3 -2
- package/modules/hall-ui/public/people.html +2 -2
- package/modules/hall-ui/public/people.js +5 -4
- package/modules/hall-ui/public/people.states.json +1 -1
- package/modules/hall-ui/public/settings.js +1 -1
- package/modules/hall-ui/public/style.css +12 -11
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/spike-task-map.js +730 -0
- package/src/bongos/serve-internal.js +20 -8
- package/src/module-api.js +1 -1
- package/tests/hall_catalog_world.mjs +181 -0
- package/tests/hall_page_gate_map.mjs +4 -2
- package/tests/hall_reading_world.mjs +2 -2
- package/tests/hall_roster_world.mjs +40 -12
- package/tests/spike_task_map.mjs +0 -0
- package/modules/hall-ui/public/scouting.html +0 -69
- package/modules/hall-ui/public/scouting.states.json +0 -59
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
// atlas.js — the Repo Atlas renderer (ADR 0094, idea #457; restyled task 1737
|
|
2
|
-
// Reads the server-generated /builders/atlas.json
|
|
3
|
-
// scripts/gds/gen-atlas.js on every deploy) and draws an
|
|
4
|
-
// hover-connective, click-for-detail map of the repository. Two
|
|
5
|
-
// .md/context layer and the module truth.
|
|
1
|
+
// atlas.js — the Repo Atlas renderer (ADR 0094, idea #457; restyled task 1737;
|
|
2
|
+
// redesigned task 1003472). Reads the server-generated /builders/atlas.json
|
|
3
|
+
// (produced by scripts/gds/gen-atlas.js on every deploy) and draws an
|
|
4
|
+
// isometric, hover-connective, click-for-detail map of the repository. Two
|
|
5
|
+
// views: the .md/context layer and the module truth. Under the map, the ISLAND
|
|
6
|
+
// INDEX renders the same nodes as rows a keyboard or a phone can open — the
|
|
7
|
+
// scene is a hover surface, so without the index a third of the hall's readers
|
|
8
|
+
// could not reach a single detail.
|
|
6
9
|
//
|
|
7
10
|
// Vanilla, no framework (the hall convention). The SEMANTIC node/edge palette
|
|
8
11
|
// (TOKENS/EDGE_STYLE) is fixed — never instance-pack-driven, so a pack can't
|
|
@@ -26,6 +29,10 @@
|
|
|
26
29
|
monolith: { c: TOKENS.db, w: 3, dash: 0 }, ghost: { c: TOKENS.ghost, w: 1.8, dash: '5 5' },
|
|
27
30
|
seam: { c: TOKENS.sun, w: 2, dash: 0 },
|
|
28
31
|
};
|
|
32
|
+
// The in-scene badge kinds map onto the shell's status pills in the HTML
|
|
33
|
+
// detail (good → ok, the rest by name), so a badge reads the same on the
|
|
34
|
+
// island and in its card.
|
|
35
|
+
var PILL = { good: 'ok', warn: 'warn', danger: 'danger', info: 'info' };
|
|
29
36
|
|
|
30
37
|
// Theme-dependent, NON-semantic scene values — resolved from the .atlas-page
|
|
31
38
|
// scoped vars at render time (they flip with html[data-theme]).
|
|
@@ -58,6 +65,10 @@
|
|
|
58
65
|
var emptyEl = document.getElementById('empty');
|
|
59
66
|
var popup = document.getElementById('popup');
|
|
60
67
|
var popupBody = document.getElementById('popupBody');
|
|
68
|
+
var hintEl = document.getElementById('hint');
|
|
69
|
+
var indexEl = document.getElementById('ab-index');
|
|
70
|
+
var indexRows = document.getElementById('ab-index-rows');
|
|
71
|
+
var indexN = document.getElementById('ab-index-n');
|
|
61
72
|
|
|
62
73
|
var DATA = null, view = 'context', selected = null, nodeEls = {}, edgeEls = [], adj = {};
|
|
63
74
|
|
|
@@ -70,6 +81,7 @@
|
|
|
70
81
|
function ptsOf(a) { return a.map(function (p) { return r1(p.x) + ',' + r1(p.y); }).join(' '); }
|
|
71
82
|
function cx(n) { return n.gx + n.w / 2; }
|
|
72
83
|
function cy(n) { return n.gy + n.d / 2; }
|
|
84
|
+
function nodeById(id) { return DATA.views[view].nodes.filter(function (x) { return x.id === id; })[0]; }
|
|
73
85
|
|
|
74
86
|
function nodeFill(n) {
|
|
75
87
|
if (view === 'context') return { hub: TOKENS.sun, nested: TOKENS.sky, docs: TOKENS.paper, db: TOKENS.coral, generated: TOKENS.sky }[n.kind] || TOKENS.sky;
|
|
@@ -210,7 +222,8 @@
|
|
|
210
222
|
});
|
|
211
223
|
|
|
212
224
|
drawLegend(d);
|
|
213
|
-
|
|
225
|
+
drawIndex(d);
|
|
226
|
+
captionEl.innerHTML = esc(d.blurb || '') + '<span class="sub">' + esc(d.sub || '') + '</span>';
|
|
214
227
|
}
|
|
215
228
|
|
|
216
229
|
function buildNode(n) {
|
|
@@ -256,23 +269,25 @@
|
|
|
256
269
|
for (var k in nodeEls) nodeEls[k].classList.remove('lit');
|
|
257
270
|
}
|
|
258
271
|
|
|
272
|
+
// The detail of one island: the same markup in the map's card and in the
|
|
273
|
+
// index row. Facts are the shell's pills and chips; a neighbour is a door
|
|
274
|
+
// (a .ab-jump button that opens that island) rather than a dead chip.
|
|
259
275
|
function detailHTML(n) {
|
|
260
|
-
var
|
|
261
|
-
var b = n.badge ? '<span class="ab-badge ab-b-' + n.badge.k + '">' + n.badge.t + '</span>' : '';
|
|
276
|
+
var b = n.badge ? '<span class="fact-pill fact-pill--' + (PILL[n.badge.k] || 'quiet') + '">' + esc(n.badge.t) + '</span>' : '';
|
|
262
277
|
var html = '<div class="ab-ptitle">' + esc(n.label) + ' ' + b + '</div>';
|
|
263
278
|
html += '<div class="ab-ppath">' + esc(n.path || '') + '</div>';
|
|
264
|
-
if (n.metric) html += '<div class="ab-chips"><span class="
|
|
279
|
+
if (n.metric) html += '<div class="ab-chips"><span class="fact-chip">' + esc(n.metric) + '</span></div>';
|
|
265
280
|
html += '<p class="ab-pdesc">' + esc(n.desc || '') + '</p>';
|
|
266
281
|
if (n.provenance) {
|
|
267
|
-
var provLabel = n.provenance === 'core' ? '
|
|
268
|
-
html += '<div class="ab-chips"><span class="
|
|
269
|
-
(n.author ? '<span class="
|
|
270
|
-
(n.origin ? '<span class="
|
|
282
|
+
var provLabel = n.provenance === 'core' ? 'core-loaded' : 'built here';
|
|
283
|
+
html += '<div class="ab-chips"><span class="fact-pill' + (n.provenance === 'core' ? ' fact-pill--info' : '') + '">' + esc(provLabel) + '</span>' +
|
|
284
|
+
(n.author ? '<span class="ov-fact">by <b>' + esc(n.author) + '</b></span>' : '') +
|
|
285
|
+
(n.origin ? '<span class="ov-fact">from <b>' + esc(n.origin) + '</b></span>' : '') +
|
|
271
286
|
'</div>';
|
|
272
287
|
}
|
|
273
|
-
if (n.provides && n.provides.length) html += '<div class="ab-prow">Provides (seams)</div><div class="ab-chips">' + n.provides.map(function (p) { return '<span class="
|
|
274
|
-
var conns = (adj[n.id] || []).map(function (e) { var o = e.a === n.id ? e.b : e.a; var on =
|
|
275
|
-
if (conns.length) html += '<div class="ab-prow">Connects to</div><div class="ab-chips">' + conns.map(function (cc) { return '<
|
|
288
|
+
if (n.provides && n.provides.length) html += '<div class="ab-prow">Provides (seams)</div><div class="ab-chips">' + n.provides.map(function (p) { return '<span class="fact-chip">' + esc(p) + '</span>'; }).join('') + '</div>';
|
|
289
|
+
var conns = (adj[n.id] || []).map(function (e) { var o = e.a === n.id ? e.b : e.a; var on = nodeById(o); return on ? { id: on.id, label: on.label } : null; }).filter(Boolean);
|
|
290
|
+
if (conns.length) html += '<div class="ab-prow">Connects to</div><div class="ab-chips">' + conns.map(function (cc) { return '<button type="button" class="fact-chip ab-jump" data-node="' + esc(cc.id) + '">' + esc(cc.label) + '</button>'; }).join('') + '</div>';
|
|
276
291
|
return html;
|
|
277
292
|
}
|
|
278
293
|
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
|
@@ -300,25 +315,108 @@
|
|
|
300
315
|
}).join('');
|
|
301
316
|
}
|
|
302
317
|
|
|
318
|
+
// ---- the island index --------------------------------------------------
|
|
319
|
+
// Every island of the current view as a dense row: its colour, its label, its
|
|
320
|
+
// sub and metric, its badge as a pill. The title is a disclosure button; the
|
|
321
|
+
// body it opens is detailHTML laid inline, so the same facts read in place on
|
|
322
|
+
// a phone. Hovering a row lights the island on the map.
|
|
323
|
+
function drawIndex(d) {
|
|
324
|
+
if (!indexRows) return;
|
|
325
|
+
var rows = d.nodes.slice().sort(function (a, b) {
|
|
326
|
+
var fa = a.fixed ? 0 : 1, fb = b.fixed ? 0 : 1;
|
|
327
|
+
if (fa !== fb) return fa - fb;
|
|
328
|
+
return String(a.label).localeCompare(String(b.label));
|
|
329
|
+
});
|
|
330
|
+
indexRows.innerHTML = rows.map(function (n, i) {
|
|
331
|
+
var fill = nodeFill(n);
|
|
332
|
+
var sw = '<span class="ab-ix__sw' + (n.kind === 'ghost' ? ' ab-ix__sw--ghost' : '') + '" style="background:' + fill + ';color:' + fill + '" aria-hidden="true"></span>';
|
|
333
|
+
var facts = [];
|
|
334
|
+
if (n.badge) facts.push('<span class="fact-pill fact-pill--' + (PILL[n.badge.k] || 'quiet') + '">' + esc(n.badge.t) + '</span>');
|
|
335
|
+
if (n.metric) facts.push('<span class="fact-chip">' + esc(n.metric) + '</span>');
|
|
336
|
+
if (n.path) facts.push('<span class="ov-mono">' + esc(n.path) + '</span>');
|
|
337
|
+
return '<li class="ov-row ov-row--dense ab-ix" data-node="' + esc(n.id) + '">' +
|
|
338
|
+
'<div class="ov-row__main">' +
|
|
339
|
+
'<button type="button" class="ov-row__title ab-ix__t" aria-expanded="false" aria-controls="ab-ix-' + i + '">' + sw + esc(n.label) + (n.sub ? '<span class="ov-mono">' + esc(n.sub) + '</span>' : '') + '</button>' +
|
|
340
|
+
(facts.length ? '<div class="ov-row__facts">' + facts.join('') + '</div>' : '') +
|
|
341
|
+
'<div class="ab-ix__body" id="ab-ix-' + i + '" hidden></div>' +
|
|
342
|
+
'</div></li>';
|
|
343
|
+
}).join('');
|
|
344
|
+
if (indexN) indexN.textContent = String(rows.length);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Open one index row (fold the others), fill it with the island's detail,
|
|
348
|
+
// and light the island on the map while it is open.
|
|
349
|
+
function openIndexRow(id, scroll) {
|
|
350
|
+
var rows = indexRows.querySelectorAll('.ab-ix');
|
|
351
|
+
var target = null;
|
|
352
|
+
for (var i = 0; i < rows.length; i++) {
|
|
353
|
+
var r = rows[i], hit = r.getAttribute('data-node') === id;
|
|
354
|
+
var t = r.querySelector('.ab-ix__t'), body = r.querySelector('.ab-ix__body');
|
|
355
|
+
t.setAttribute('aria-expanded', hit ? 'true' : 'false');
|
|
356
|
+
body.hidden = !hit;
|
|
357
|
+
if (hit) { target = r; body.innerHTML = detailHTML(nodeById(id)); }
|
|
358
|
+
else body.innerHTML = '';
|
|
359
|
+
}
|
|
360
|
+
if (target) {
|
|
361
|
+
if (indexEl && !indexEl.open) indexEl.open = true;
|
|
362
|
+
selected = id; lightNode(id);
|
|
363
|
+
if (scroll) target.scrollIntoView({ block: 'nearest' });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function closeIndexRows() {
|
|
367
|
+
var rows = indexRows.querySelectorAll('.ab-ix');
|
|
368
|
+
for (var i = 0; i < rows.length; i++) {
|
|
369
|
+
rows[i].querySelector('.ab-ix__t').setAttribute('aria-expanded', 'false');
|
|
370
|
+
var body = rows[i].querySelector('.ab-ix__body'); body.hidden = true; body.innerHTML = '';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
303
374
|
function setView(v) {
|
|
304
375
|
view = v; selected = null;
|
|
305
|
-
var btns = document.querySelectorAll('.ab-
|
|
306
|
-
for (var i = 0; i < btns.length; i++) btns[i].
|
|
376
|
+
var btns = document.querySelectorAll('.ab-views [data-view]');
|
|
377
|
+
for (var i = 0; i < btns.length; i++) btns[i].setAttribute('aria-selected', btns[i].getAttribute('data-view') === v ? 'true' : 'false');
|
|
307
378
|
render();
|
|
308
379
|
}
|
|
309
380
|
|
|
310
381
|
function wire() {
|
|
311
|
-
var btns = document.querySelectorAll('.ab-
|
|
382
|
+
var btns = document.querySelectorAll('.ab-views [data-view]');
|
|
312
383
|
for (var i = 0; i < btns.length; i++) btns[i].addEventListener('click', function () { setView(this.getAttribute('data-view')); });
|
|
313
384
|
document.getElementById('popupClose').addEventListener('click', function (ev) { ev.stopPropagation(); closePopup(); });
|
|
314
385
|
svg.addEventListener('click', function () { closePopup(); });
|
|
315
|
-
|
|
386
|
+
document.addEventListener('keydown', function (ev) { if (ev.key === 'Escape' && popup.classList.contains('show')) closePopup(); });
|
|
387
|
+
// A neighbour chip in the map's card opens that island's card.
|
|
388
|
+
popup.addEventListener('click', function (ev) {
|
|
389
|
+
var jump = ev.target.closest('.ab-jump'); if (!jump) return;
|
|
390
|
+
ev.stopPropagation();
|
|
391
|
+
var n = nodeById(jump.getAttribute('data-node')); if (n && nodeEls[n.id]) openPopup(n, nodeEls[n.id]);
|
|
392
|
+
});
|
|
393
|
+
// The index: the title opens the row; a neighbour chip inside it opens that
|
|
394
|
+
// row; hovering a row lights the island.
|
|
395
|
+
indexRows.addEventListener('click', function (ev) {
|
|
396
|
+
var jump = ev.target.closest('.ab-jump');
|
|
397
|
+
if (jump) { openIndexRow(jump.getAttribute('data-node'), true); return; }
|
|
398
|
+
var t = ev.target.closest('.ab-ix__t'); if (!t) return;
|
|
399
|
+
var row = t.closest('.ab-ix'), id = row.getAttribute('data-node');
|
|
400
|
+
if (t.getAttribute('aria-expanded') === 'true') { closeIndexRows(); selected = null; clearLight(); }
|
|
401
|
+
else { closePopup(); openIndexRow(id, false); }
|
|
402
|
+
});
|
|
403
|
+
indexRows.addEventListener('mouseover', function (ev) {
|
|
404
|
+
var row = ev.target.closest('.ab-ix'); if (row && !selected) lightNode(row.getAttribute('data-node'));
|
|
405
|
+
});
|
|
406
|
+
indexRows.addEventListener('mouseout', function (ev) {
|
|
407
|
+
var row = ev.target.closest('.ab-ix'); if (row && !selected) clearLight();
|
|
408
|
+
});
|
|
409
|
+
window.addEventListener('resize', function () { if (selected && nodeEls[selected] && popup.classList.contains('show')) { var n = nodeById(selected); if (n) openPopup(n, nodeEls[selected]); } });
|
|
410
|
+
// On a phone the map is a thumbnail and the index is the read.
|
|
411
|
+
if (indexEl && window.matchMedia && window.matchMedia('(max-width: 640px)').matches) indexEl.open = true;
|
|
316
412
|
}
|
|
317
413
|
|
|
318
414
|
function showEmpty() {
|
|
319
415
|
emptyEl.classList.add('show'); svg.style.display = 'none';
|
|
320
416
|
captionEl.textContent = 'The map has not been generated on this deploy yet.';
|
|
321
417
|
footEl.textContent = '';
|
|
418
|
+
if (hintEl) hintEl.hidden = true;
|
|
419
|
+
if (indexEl) indexEl.hidden = true;
|
|
322
420
|
}
|
|
323
421
|
|
|
324
422
|
// Re-render when the shell flips the theme — the scene's non-semantic values
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "Repo atlas — the self-drawing module map. Its SEMANTIC node palette is fixed by ADR 0094 and is not the token tier's business; everything around it (
|
|
2
|
+
"_": "Repo atlas — the self-drawing module map. Its SEMANTIC node palette is fixed by ADR 0094 and is not the token tier's business; everything around it (the panel, the view switch, the caption, the detail card, the legend, the island index) is chrome and rides the world. Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once; the redesign (task 1003472) added the `truth`, `detail` and `index` states. Populated look: the hall-preview harness serves modules/hall-ui/public/atlas.json when scripts/gds/gen-atlas.js has written it (the file is generated on deploy and gitignored, so run the generator first).",
|
|
3
3
|
"page": "/builders/atlas",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -44,6 +44,57 @@
|
|
|
44
44
|
]
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
+
"truth": {
|
|
48
|
+
"auth": true,
|
|
49
|
+
"actions": [
|
|
50
|
+
[
|
|
51
|
+
"wait",
|
|
52
|
+
900
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
"clickIfVisible",
|
|
56
|
+
".ab-views [data-view=\"modules\"]"
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
"wait",
|
|
60
|
+
500
|
|
61
|
+
]
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"detail": {
|
|
65
|
+
"auth": true,
|
|
66
|
+
"actions": [
|
|
67
|
+
[
|
|
68
|
+
"wait",
|
|
69
|
+
900
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"dispatchClick",
|
|
73
|
+
"#svg .ab-node"
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"wait",
|
|
77
|
+
300
|
|
78
|
+
]
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"index": {
|
|
82
|
+
"auth": true,
|
|
83
|
+
"actions": [
|
|
84
|
+
[
|
|
85
|
+
"wait",
|
|
86
|
+
900
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"dispatchClick",
|
|
90
|
+
"#ab-index-rows .ab-ix:first-child .ab-ix__t"
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
"wait",
|
|
94
|
+
300
|
|
95
|
+
]
|
|
96
|
+
]
|
|
97
|
+
},
|
|
47
98
|
"out": {
|
|
48
99
|
"auth": false
|
|
49
100
|
}
|
|
@@ -1,93 +1,74 @@
|
|
|
1
|
-
/* modules.css —
|
|
2
|
-
|
|
3
|
-
the
|
|
4
|
-
|
|
1
|
+
/* modules.css — what a MODULE row has that no other oversight row does (task
|
|
2
|
+
1003472, goal 1000081). The panel, the filter bar, the figure band, the group
|
|
3
|
+
heads, the row, the toggle and the foot line are all the shell's or the
|
|
4
|
+
family's (oversight.css); this sheet adds the clamped description, the
|
|
5
|
+
per-row details disclosure and the lines it opens onto.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.mod-card
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
ROWS, NOT CARDS — the call task 1003472 asked for. Modules was the one hall
|
|
8
|
+
page already rendering its record's description, so a card looked like the
|
|
9
|
+
case where prose earns a box. Rendered against the real catalog it is the
|
|
10
|
+
opposite: the descriptions run from 120 to 1,200 characters, so the 280px
|
|
11
|
+
auto-fill grid stood a 500px card beside a 120px one, the toggle (the row's
|
|
12
|
+
one control) sat at a different height in every column, and a builder
|
|
13
|
+
scanning for a NAME and a STATE read paragraphs to find either. A row puts
|
|
14
|
+
every title on one x, every switch on another, clamps the prose to two lines
|
|
15
|
+
and opens the rest on request. The card grammar (.mod-grid/.mod-card), the
|
|
16
|
+
boxed .mod-browse field and the .mod-chip alias are gone. */
|
|
17
|
+
|
|
18
|
+
/* The filter bar closes on a hairline, the goals panel's rule: the count and
|
|
19
|
+
the figures under it read as the panel's content, not as more controls. */
|
|
20
|
+
#mod-filters { margin: 0 0 12px; padding: 0 0 6px; border-bottom: var(--line) solid var(--rule-soft); }
|
|
21
|
+
#mod-filters .filters { margin-bottom: 10px; }
|
|
22
|
+
.mod-count { margin: 0 0 14px; font-family: var(--font-mono); font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--ink-faint); }
|
|
23
|
+
.mod-count:empty { display: none; }
|
|
24
|
+
|
|
25
|
+
/* One group after another: the second head carries the family's air above it,
|
|
26
|
+
and the first row after a head starts flush (oversight.css draws the rule). */
|
|
27
|
+
.mod-panel .ov-group + .ov-group { margin-top: var(--sp-md); }
|
|
28
|
+
.mod-loading { padding: 10px 0; font-size: 13.5px; color: var(--ink-soft); }
|
|
29
|
+
|
|
30
|
+
/* The description is the sub line, clamped to two lines of the measure so
|
|
31
|
+
twenty rows scan as twenty rows. The details disclosure under it opens the
|
|
32
|
+
rest; while it is open the clamp lifts, so the prose is never shown twice. */
|
|
33
|
+
.mod-row__desc {
|
|
34
|
+
display: -webkit-box;
|
|
35
|
+
-webkit-box-orient: vertical;
|
|
36
|
+
-webkit-line-clamp: 2;
|
|
37
|
+
overflow: hidden;
|
|
19
38
|
}
|
|
20
|
-
.mod-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.mod-card__actions { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; }
|
|
35
|
-
button.mod-toggle {
|
|
36
|
-
font-family: var(--font-body);
|
|
39
|
+
.mod-row:has(.mod-more[open]) .mod-row__desc { display: block; -webkit-line-clamp: unset; overflow: visible; }
|
|
40
|
+
|
|
41
|
+
/* The disclosure is a text control in a 13px facts line: inline-block with the
|
|
42
|
+
family's padding trick so it clears the 24px floor on both axes. No marker
|
|
43
|
+
— the word says what it does, and the open state says so too. */
|
|
44
|
+
.mod-more { margin: 8px 0 0; }
|
|
45
|
+
.mod-more > summary {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
list-style: none;
|
|
48
|
+
min-height: 24px;
|
|
49
|
+
min-width: 24px;
|
|
50
|
+
padding: 3px 4px;
|
|
51
|
+
margin: -3px -4px;
|
|
52
|
+
line-height: 18px;
|
|
37
53
|
font-size: 12.5px;
|
|
38
|
-
|
|
39
|
-
padding: 5px 16px;
|
|
40
|
-
min-width: 78px;
|
|
41
|
-
border-radius: var(--radius-pill);
|
|
42
|
-
border: var(--line) solid var(--rule);
|
|
43
|
-
background: var(--bg-card);
|
|
44
|
-
color: var(--ink-soft);
|
|
54
|
+
color: var(--accent-ink);
|
|
45
55
|
cursor: pointer;
|
|
46
56
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
background: transparent;
|
|
57
|
-
color: var(--ink-soft);
|
|
58
|
-
cursor: pointer;
|
|
59
|
-
}
|
|
60
|
-
button.mod-submit:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
61
|
-
/* Italic was this sheet's alone — nothing else in the hall sets it, and an
|
|
62
|
-
empty state is a plain statement, not an aside (task 1003307). */
|
|
63
|
-
.mod-empty { color: var(--ink-faint); font-size: 13px; padding: 10px 0; }
|
|
64
|
-
.mod-note { font-size: 12px; color: var(--ink-faint); margin-top: 4px; }
|
|
57
|
+
.mod-more > summary::-webkit-details-marker { display: none; }
|
|
58
|
+
.mod-more > summary:hover { text-decoration: underline; text-underline-offset: 3px; }
|
|
59
|
+
.mod-more > summary::before { content: "details"; }
|
|
60
|
+
.mod-more[open] > summary::before { content: "fewer details"; }
|
|
61
|
+
/* The lines the disclosure opens onto are the family's key/value lines; the
|
|
62
|
+
list of what a module contributes wraps as chips on its own line. */
|
|
63
|
+
.mod-more .ov-lines { margin-top: 8px; }
|
|
64
|
+
.mod-more .ov-lines .ov-lines__v { text-align: left; margin-left: 0; }
|
|
65
|
+
.mod-more .ov-lines .ov-lines__v .fact-chip { margin: 0 6px 4px 0; }
|
|
65
66
|
|
|
66
|
-
/* Catalog browse bar (task 1001772) — one filter over both grids. */
|
|
67
|
-
.mod-browse { margin: 0 0 16px; }
|
|
68
|
-
.mod-browse__label {
|
|
69
|
-
display: block;
|
|
70
|
-
font-size: 12px;
|
|
71
|
-
font-weight: 600;
|
|
72
|
-
color: var(--ink-soft);
|
|
73
|
-
margin-bottom: 6px;
|
|
74
|
-
}
|
|
75
|
-
.mod-browse__input {
|
|
76
|
-
font-family: var(--font-body);
|
|
77
|
-
font-size: 13.5px;
|
|
78
|
-
width: 100%;
|
|
79
|
-
max-width: 420px;
|
|
80
|
-
padding: 8px 12px;
|
|
81
|
-
border-radius: var(--radius-sm);
|
|
82
|
-
border: var(--line) solid var(--rule);
|
|
83
|
-
background: var(--bg-card);
|
|
84
|
-
color: var(--ink);
|
|
85
|
-
}
|
|
86
67
|
/* The marker on an inferred credit — quiet, but hoverable for the reason. */
|
|
87
|
-
.mod-inferred {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
68
|
+
.mod-inferred { border: 0; text-decoration: none; cursor: help; color: var(--ink-faint); margin-left: 1px; }
|
|
69
|
+
|
|
70
|
+
/* The right column holds the switch and, on a module built here, the submit
|
|
71
|
+
door under it. The switch is the family's grant toggle: enabled reads in ink,
|
|
72
|
+
disabled in the faint ink, the accent only on hover. A viewer below the floor
|
|
73
|
+
sees the state as a pill, not a dead control. */
|
|
74
|
+
.mod-row__right .btn-ghost { white-space: nowrap; }
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
12
|
+
<!-- task 1003472: the catalog rides the oversight family's row (a titled row
|
|
13
|
+
with facts and one action on the right), so the family sheet loads first
|
|
14
|
+
and modules.css holds only what a module row has that no other row does. -->
|
|
15
|
+
<link rel="stylesheet" href="/builders/oversight.css">
|
|
12
16
|
<link rel="stylesheet" href="/builders/modules.css">
|
|
13
17
|
</head>
|
|
14
18
|
<body data-page="modules" data-page-title="Modules">
|
|
@@ -19,48 +23,53 @@
|
|
|
19
23
|
<div class="app-body">
|
|
20
24
|
<header class="app-topbar" id="app-topbar"></header>
|
|
21
25
|
<div class="app-content">
|
|
22
|
-
<!--
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
page's own name is the least interesting thing on it. -->
|
|
26
|
+
<!-- The board page head (task 1003307): the label voice, because on a
|
|
27
|
+
working surface the page's own name is the least interesting thing on
|
|
28
|
+
it. The sub says what the page IS; the numbers moved into the panel's
|
|
29
|
+
figure band (task 1003472), where a number belongs. -->
|
|
27
30
|
<div class="page-head page-head--board">
|
|
28
31
|
<h1 class="page-head__h">Modules</h1>
|
|
29
|
-
<p class="page-head__sub" id="modules-sub">
|
|
32
|
+
<p class="page-head__sub" id="modules-sub">Everything this instance can run: what the core ships, what this project built, and who keeps each one true as the core advances.</p>
|
|
30
33
|
</div>
|
|
31
34
|
|
|
32
35
|
<main id="hall-main">
|
|
33
|
-
<!--
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
carries its author + origin credit (§2/§3: credit now, money later). -->
|
|
44
|
-
<section class="scroll" id="core-scroll" aria-labelledby="core-h">
|
|
36
|
+
<!-- task 1003472: ONE panel. The search and the state lens sit on a
|
|
37
|
+
hairline at the top, the figure band under them, then two runs of
|
|
38
|
+
rows under group heads — shipped with the core, built by this
|
|
39
|
+
project. Before this the page was a boxed search on the ground over
|
|
40
|
+
three panels, the middle two holding 280px card grids whose height
|
|
41
|
+
varied tenfold with the description, so the one control per record
|
|
42
|
+
(the toggle) landed at a different height on every card. A record
|
|
43
|
+
carrying this many facts is a row (docs/design/hall-direction-v2.md
|
|
44
|
+
section 6; the archetype at docs/design/mocks/hall/goals/v3.html). -->
|
|
45
|
+
<section class="scroll mod-panel" id="core-scroll" aria-labelledby="core-h">
|
|
45
46
|
<h2 id="core-h" class="scroll__h">Module catalog</h2>
|
|
46
|
-
<p class="scroll__lede">
|
|
47
|
-
<div
|
|
48
|
-
|
|
47
|
+
<p class="scroll__lede">The shared repository the core ships and the modules this project built for itself, each credited to its author and the project it came from. Everyone can see the state; the switch is rank-gated.</p>
|
|
48
|
+
<div id="mod-filters"></div>
|
|
49
|
+
<p class="mod-count" id="mod-count" role="status" aria-live="polite"></p>
|
|
50
|
+
<div class="ov-figs" id="mod-figs" aria-label="Catalog at a glance"></div>
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
<!-- Shipped with the core — the catalog this instance opts into
|
|
53
|
+
(ADR 0107 §6: accepted modules land default-off). -->
|
|
54
|
+
<div class="ov-group" id="core-group">
|
|
55
|
+
<div class="ov-group__head">
|
|
56
|
+
<span class="ov-group__name">Shipped with the core</span>
|
|
57
|
+
<span class="ov-group__n" id="core-n"></span>
|
|
58
|
+
</div>
|
|
59
|
+
<ul class="ov-rows" id="core-grid"><li class="mod-loading">Loading the catalog…</li></ul>
|
|
60
|
+
</div>
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
62
|
+
<!-- Built by this project — the pool `bongos module submit` draws from
|
|
63
|
+
(ADR 0107 §1/§9). The submit door is on the row, beside the module
|
|
64
|
+
it files, so the third panel this page used to carry is gone. -->
|
|
65
|
+
<div class="ov-group" id="own-group">
|
|
66
|
+
<div class="ov-group__head">
|
|
67
|
+
<span class="ov-group__name">Built by this project</span>
|
|
68
|
+
<span class="ov-group__n" id="own-n"></span>
|
|
69
|
+
</div>
|
|
70
|
+
<ul class="ov-rows" id="own-grid"></ul>
|
|
71
|
+
<p class="ov-foot" id="submit-lede">A module built here can be submitted upstream for review, so it can join the shared catalog with your name and this project credited.</p>
|
|
72
|
+
</div>
|
|
64
73
|
</section>
|
|
65
74
|
</main>
|
|
66
75
|
|
|
@@ -75,6 +84,8 @@
|
|
|
75
84
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
76
85
|
<script src="/builders/palette.js" defer></script>
|
|
77
86
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
78
|
-
<script src="/builders/
|
|
87
|
+
<script src="/builders/brand-apply.js"></script>
|
|
88
|
+
<script src="/builders/hall-kit.js"></script>
|
|
89
|
+
<script src="/builders/modules.js?v=2026-09-02-rows"></script>
|
|
79
90
|
</body>
|
|
80
91
|
</html>
|