forma-arch 0.8.0 → 0.9.0
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/lib/docmap.mjs +27 -4
- package/lib/gen.mjs +4 -1
- package/lib/viewer/c4-hologram.html +25 -5
- package/package.json +1 -1
package/lib/docmap.mjs
CHANGED
|
@@ -147,16 +147,36 @@ export function indexByNode(rows, nodes) {
|
|
|
147
147
|
const paths = subtree(n.id)
|
|
148
148
|
if (!paths.length) continue
|
|
149
149
|
const hit = rows.filter((r) => r.refs.some((ref) => paths.some((p) => touches(ref, p))))
|
|
150
|
-
if (hit.length) idx.set(n.id, hit)
|
|
150
|
+
if (hit.length) idx.set(n.id, { rows: hit, cover: coverOf(n, byId, kids, hit, paths) })
|
|
151
151
|
}
|
|
152
152
|
return idx
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// How much of a node the rows actually reach. `done / rows.length` answers "of the capabilities
|
|
156
|
+
// somebody wrote down, how many are finished" — never "how much of this module is finished". A
|
|
157
|
+
// container holding 8 files that a matrix names 3 of, all done, reads 100%: the other 5 are not
|
|
158
|
+
// counted as unfinished, they are not counted at all. So the number ships with its own reach.
|
|
159
|
+
// `whole` means a row names the module itself, i.e. the document IS talking about all of it.
|
|
160
|
+
function coverOf(node, byId, kids, hit, paths) {
|
|
161
|
+
const own = (node.evidence || []).filter((e) => e.type === 'path').map((e) => normRef(e.ref))
|
|
162
|
+
const whole = hit.some((r) => r.refs.some((ref) => own.some((p) => ref === p || p.startsWith(ref + '/'))))
|
|
163
|
+
const leaves = (kids.get(node.id) || []).map((k) => byId.get(k)).filter((k) => k && k.kind === 'leaf')
|
|
164
|
+
const glob = (node.evidence || []).find((e) => e.type === 'glob')
|
|
165
|
+
// Units = the boxes below it, or — for a node whose children are internal detail (a Go package) —
|
|
166
|
+
// the file count its own drift anchor already carries.
|
|
167
|
+
const total = leaves.length || (glob && glob.count) || paths.length
|
|
168
|
+
if (whole) return { named: total, total, whole: true }
|
|
169
|
+
const named = new Set()
|
|
170
|
+
for (const r of hit) for (const ref of r.refs) if (own.some((p) => ref.startsWith(p + '/'))) named.add(ref)
|
|
171
|
+
return { named: Math.min(named.size, total), total, whole: false }
|
|
172
|
+
}
|
|
173
|
+
|
|
155
174
|
// The rows that DESCRIBE a node (past the cap it is only touched by them — see MAX_ROWS).
|
|
156
175
|
export const describingRows = (idx, id) => {
|
|
157
|
-
const
|
|
158
|
-
return
|
|
176
|
+
const e = idx.get(id)
|
|
177
|
+
return e && e.rows.length <= MAX_ROWS ? e.rows : null
|
|
159
178
|
}
|
|
179
|
+
export const coverageFor = (idx, id) => (idx.get(id) || {}).cover || null
|
|
160
180
|
|
|
161
181
|
// Programme state a document states outright: how many of the capabilities living in this node are
|
|
162
182
|
// finished. Derived, never curated — the c4-status.json overlay still overrides it (gen.mjs §WP-A1),
|
|
@@ -165,9 +185,12 @@ export function statusFor(idx, id) {
|
|
|
165
185
|
const rows = describingRows(idx, id)
|
|
166
186
|
if (!rows || rows.some((r) => r.done == null)) return null // no status column: describe only
|
|
167
187
|
const done = rows.filter((r) => r.done).length
|
|
188
|
+
const c = coverageFor(idx, id)
|
|
168
189
|
return {
|
|
169
190
|
status2: done === rows.length ? 'done' : done === 0 ? 'planned' : 'in-progress',
|
|
170
191
|
completion: Math.round((done / rows.length) * 100),
|
|
171
|
-
|
|
192
|
+
// The citation says what it is: a repo document declaring itself finished, not a verification.
|
|
193
|
+
source: `${rows[0].from} (${done}/${rows.length} declared done)`,
|
|
194
|
+
...(c ? { coverage: c } : {}),
|
|
172
195
|
}
|
|
173
196
|
}
|
package/lib/gen.mjs
CHANGED
|
@@ -165,7 +165,10 @@ for (const n of nodes) {
|
|
|
165
165
|
const ds = !n.status2 && n.kind !== 'system' ? statusFor(docIndex, n.id) : null
|
|
166
166
|
// `derived: true` is the marker `check` keys off to know this state must keep re-deriving. A
|
|
167
167
|
// string match on the citation would break the moment anyone rewords it.
|
|
168
|
-
|
|
168
|
+
// `coverage` rides in `verify` (additionalProperties: true there, so no schema churn) and says how
|
|
169
|
+
// much of the node the citation actually reaches — the difference between "3 of 3 rows are done"
|
|
170
|
+
// and "this module is done".
|
|
171
|
+
if (ds) { n.status2 = ds.status2; if (n.completion == null) n.completion = ds.completion; if (!n.verify) n.verify = { source: ds.source, derived: true, ...(ds.coverage ? { coverage: ds.coverage } : {}) } }
|
|
169
172
|
// Code can prove a file EXISTS; it cannot prove the work behind it is finished. Marking every
|
|
170
173
|
// undecorated node done/100 turned a virgin repo into a board reading "10/10 complete" — the
|
|
171
174
|
// exact false green this tool exists to kill. No overlay (§WP-A1), no verdict: `unknown`, and
|
|
@@ -89,7 +89,7 @@ svg.dense .edge.hot{opacity:1;stroke-width:2.6}
|
|
|
89
89
|
#etip{position:absolute;pointer-events:none;display:none;z-index:6;background:var(--detailbg);border:1px solid var(--border);
|
|
90
90
|
color:var(--nm);font-size:10px;letter-spacing:.03em;padding:3px 8px;border-radius:5px;white-space:nowrap;box-shadow:0 3px 12px rgba(0,0,0,.45)}
|
|
91
91
|
#legend{display:flex;gap:13px;font-size:9.5px;color:var(--sec);margin:10px 0 0;flex-wrap:wrap}
|
|
92
|
-
#legend i{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px}
|
|
92
|
+
#legend i{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;box-sizing:border-box}
|
|
93
93
|
.hint{color:var(--ter2)}
|
|
94
94
|
#detail{margin-top:12px;border:1px solid var(--border);border-radius:10px;padding:13px;background:var(--detailbg);display:none}
|
|
95
95
|
#detail h3{color:var(--cyan);font-size:12.5px;margin-bottom:8px;letter-spacing:.05em}
|
|
@@ -165,6 +165,10 @@ html[data-skin="blueprint"] button.on{text-shadow:none}
|
|
|
165
165
|
<div id="stage"><div id="etip"></div><div id="err" style="display:none"></div></div>
|
|
166
166
|
<div id="legend">
|
|
167
167
|
<span><i style="background:#2fe08a"></i><span id="lg-done"></span></span>
|
|
168
|
+
<!-- Same colour, hollow: a green derived from a repo document saying it is finished is not the
|
|
169
|
+
same evidence as a green from a closed issue, and the legend is where a reader learns that
|
|
170
|
+
one mark means two things. -->
|
|
171
|
+
<span><i style="border:1.5px solid #2fe08a"></i><span id="lg-decl"></span></span>
|
|
168
172
|
<span><i style="background:#ffc74d"></i><span id="lg-prog"></span></span>
|
|
169
173
|
<span><i style="background:#3ee7ff"></i><span id="lg-next"></span></span>
|
|
170
174
|
<span><i style="background:#2d5a72"></i><span id="lg-plan"></span></span>
|
|
@@ -185,8 +189,10 @@ var STRINGS={
|
|
|
185
189
|
labels:"LABELS", labelsTitle:"arrow labels (auto-off above 14 arrows)",
|
|
186
190
|
resetLayout:"RESET LAYOUT", resetLayoutTitle:"re-arrange",
|
|
187
191
|
printExport:"PRINT / EXPORT", exportSvg:"Export SVG", exportPng:"Export PNG", exportLayout:"Export layout JSON", print:"Print",
|
|
188
|
-
legDone:"DONE (proven)", legProg:"IN PROGRESS", legNext:"NEXT", legPlan:"PLAN ONLY", legProb:"PROBLEM",
|
|
192
|
+
legDone:"DONE (proven)", legDecl:"DONE (declared)", legProg:"IN PROGRESS", legNext:"NEXT", legPlan:"PLAN ONLY", legProb:"PROBLEM",
|
|
189
193
|
legUnk:"UNKNOWN (no curated state)", stUnk:"?",
|
|
194
|
+
detDecl:"Declared in", detCover:"Coverage", coverWhole:"the document names this module itself",
|
|
195
|
+
coverPart:"{n} of {t} units named by the document",
|
|
190
196
|
legHint:"— animated arrow = active flow · dashed = to build",
|
|
191
197
|
crumbRoot:"Context", crumbLevel:"C4", lvlContext:"CONTEXT",
|
|
192
198
|
lvlNames:{context:"Context",container:"Container",component:"Components",leaf:"Leaves"},
|
|
@@ -205,8 +211,10 @@ var STRINGS={
|
|
|
205
211
|
labels:"ETICHETTE", labelsTitle:"etichette sugli archi (spente sopra i 14 archi)",
|
|
206
212
|
resetLayout:"RESET LAYOUT", resetLayoutTitle:"ridisponi",
|
|
207
213
|
printExport:"STAMPA / ESPORTA", exportSvg:"Esporta SVG", exportPng:"Esporta PNG", exportLayout:"Esporta layout JSON", print:"Stampa",
|
|
208
|
-
legDone:"FATTO (provato)", legProg:"IN CORSO", legNext:"PROSSIMO", legPlan:"SOLO PROGETTO", legProb:"PROBLEMA",
|
|
214
|
+
legDone:"FATTO (provato)", legDecl:"FATTO (dichiarato)", legProg:"IN CORSO", legNext:"PROSSIMO", legPlan:"SOLO PROGETTO", legProb:"PROBLEMA",
|
|
209
215
|
legUnk:"IGNOTO (stato non curato)", stUnk:"?",
|
|
216
|
+
detDecl:"Dichiarato in", detCover:"Copertura", coverWhole:"il documento nomina il modulo stesso",
|
|
217
|
+
coverPart:"{n} unità su {t} nominate dal documento",
|
|
210
218
|
legHint:"— freccia animata = flusso attivo · tratteggio = da costruire",
|
|
211
219
|
crumbRoot:"Contesto", crumbLevel:"C4", lvlContext:"CONTESTO",
|
|
212
220
|
lvlNames:{context:"Contesto",container:"Container",component:"Componenti",leaf:"Foglie"},
|
|
@@ -491,6 +499,13 @@ function echoesName(desc,name){
|
|
|
491
499
|
for(i=0;i<w.length;i++){if(!w[i]||own[w[i]]||DESC_NOISE[w[i]]||/^\d+$/.test(w[i]))continue;return false;}
|
|
492
500
|
return true;
|
|
493
501
|
}
|
|
502
|
+
// Pure, so it is testable without a DOM. Empty when nothing was derived — a curated or a
|
|
503
|
+
// gh-verified state has no document reach to report.
|
|
504
|
+
function coverText(n){
|
|
505
|
+
var c=n&&n.verify&&n.verify.coverage;if(!c)return "";
|
|
506
|
+
if(c.whole)return STR.coverWhole;
|
|
507
|
+
return STR.coverPart.replace("{n}",c.named).replace("{t}",c.total);
|
|
508
|
+
}
|
|
494
509
|
function wrapDesc(desc,cpl,max){
|
|
495
510
|
if(max<1||cpl<2)return [];
|
|
496
511
|
var words=String(desc==null?"":desc).split(" "),all=[],line="",i;
|
|
@@ -525,7 +540,12 @@ function showDetail(n){
|
|
|
525
540
|
+(n.func?'<div class="row"><span class="lbl">'+esc(STR.detFunc)+'</span><span class="func">'+esc(n.func)+'</span></div>':'')
|
|
526
541
|
+(cur&&cur!==n.func?'<div class="row"><span class="lbl">'+esc(STR.detNow)+'</span><span class="now">'+esc(cur)+'</span></div>':'')
|
|
527
542
|
+((n.target&&n.target!=="—")?'<div class="row"><span class="lbl">'+esc(STR.detTgt)+'</span><span class="tgt">'+esc(n.target)+'</span></div>':'')
|
|
528
|
-
|
|
543
|
+
// A number DERIVED from a repo document is a declaration, not a verification: the label says which
|
|
544
|
+
// one, and the coverage says how much of this module the citation actually reaches. "3 of 3 rows
|
|
545
|
+
// declared done" and "this module is done" are not the same sentence when the module holds 22
|
|
546
|
+
// files and the document names 3 of them.
|
|
547
|
+
+(showSrc?'<div class="row"><span class="lbl">'+esc((n.verify&&n.verify.derived)?STR.detDecl:STR.detSrc)+'</span><span class="src">'+esc(src)+'</span></div>':'')
|
|
548
|
+
+(coverText(n)?'<div class="row"><span class="lbl">'+esc(STR.detCover)+'</span><span class="now">'+esc(coverText(n))+'</span></div>':'')
|
|
529
549
|
+((n.issues&&n.issues.length)?'<div class="row"><span class="lbl">'+esc(STR.detIssue)+'</span><span class="now">'+esc(n.issues.join(", "))+'</span></div>':'');
|
|
530
550
|
$("detail").style.display="block";
|
|
531
551
|
var dc=$("dc");if(dc)dc.addEventListener("click",function(){$("detail").style.display="none";});
|
|
@@ -645,7 +665,7 @@ function applyStrings(){
|
|
|
645
665
|
set("blbl",STR.labels);var bl=$("blbl");if(bl)bl.title=STR.labelsTitle;
|
|
646
666
|
set("pexp",STR.printExport);set("pexpSvg",STR.exportSvg);set("pexpPng",STR.exportPng);
|
|
647
667
|
set("pexpLayout",STR.exportLayout);set("pexpPrint",STR.print);
|
|
648
|
-
set("lg-done",STR.legDone);set("lg-prog",STR.legProg);set("lg-next",STR.legNext);
|
|
668
|
+
set("lg-done",STR.legDone);set("lg-decl",STR.legDecl);set("lg-prog",STR.legProg);set("lg-next",STR.legNext);
|
|
649
669
|
set("lg-plan",STR.legPlan);set("lg-prob",STR.legProb);set("lg-unk",STR.legUnk);set("lg-hint",STR.legHint);
|
|
650
670
|
}
|
|
651
671
|
// ---- FEATURE #2: PRINT / EXPORT (all client-side, offline) ----
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forma-arch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Forma — present your architecture instead of slides. An interactive, stack-agnostic C4 explorer (context → container → component → leaf) generated from your code and kept true to it by a deterministic drift check.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|