ccqa 1.47.3 → 1.48.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/dist/bin/ccqa.mjs +142 -21
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -22806,6 +22806,9 @@ const HTML_BODY = `
|
|
|
22806
22806
|
<button class="fchip" id="persp-chip-manuallyverified" data-f="manuallyVerified" aria-pressed="false" type="button"><span data-i18n="perspectives.rerun.state.manuallyVerified">Manually verified</span><span class="fcount"></span></button>
|
|
22807
22807
|
<button class="fchip" id="persp-chip-verified" data-f="verified" aria-pressed="false" type="button"><span data-i18n="perspectives.rerun.state.verified">Verified</span><span class="fcount"></span></button>
|
|
22808
22808
|
</div>
|
|
22809
|
+
<!-- Not a chip: the chips pick which cases to show out of what the
|
|
22810
|
+
project verifies, and this changes what that set is. -->
|
|
22811
|
+
<label class="ftoggle"><input type="checkbox" id="persp-show-disabled"><span data-i18n="perspectives.filter.showDisabled">Show disabled</span><span class="fcount" id="persp-disabled-count"></span></label>
|
|
22809
22812
|
<div class="spacer"></div>
|
|
22810
22813
|
<span class="muted persp-head" id="persp-deploy-head" hidden></span>
|
|
22811
22814
|
<div class="sw-wrap" id="persp-profile-wrap">
|
|
@@ -23278,6 +23281,8 @@ const CSS = `
|
|
|
23278
23281
|
amber as the drift badges (dr-found), not fail-red. */
|
|
23279
23282
|
.spec-card.drift-found { border-left: 3px solid var(--amber); }
|
|
23280
23283
|
.spec-card.passed { border-left: 3px solid var(--pass); }
|
|
23284
|
+
/* Says which card the link landed on; the left rail still carries the verdict. */
|
|
23285
|
+
.spec-card.linked { box-shadow: 0 0 0 2px var(--accent); }
|
|
23281
23286
|
.spec-card-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; }
|
|
23282
23287
|
.spec-card-head .name { font-weight: 600; font-size: 15px; }
|
|
23283
23288
|
.spec-card-head .slug { font-family: var(--mono); font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
@@ -23621,6 +23626,10 @@ const CSS = `
|
|
|
23621
23626
|
/* The Perspectives toolbar carries search + two filter-chip groups + the
|
|
23622
23627
|
profile selector, so it wraps instead of overflowing on a narrow window. */
|
|
23623
23628
|
#view-perspectives .toolbar { flex-wrap: wrap; }
|
|
23629
|
+
.ftoggle { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); cursor: pointer; }
|
|
23630
|
+
.ftoggle input { margin: 0; cursor: pointer; }
|
|
23631
|
+
.chip.off { background: var(--surface-2); color: var(--muted-2); border-color: var(--border); }
|
|
23632
|
+
.ftoggle .fcount { margin-left: 2px; font-variant-numeric: tabular-nums; color: var(--muted-2); }
|
|
23624
23633
|
.proj-menu.right { left: auto; right: 0; }
|
|
23625
23634
|
.d-note { margin-top: 12px; max-width: 900px; }
|
|
23626
23635
|
|
|
@@ -23746,7 +23755,7 @@ const CLIENT_JS = `
|
|
|
23746
23755
|
// Every status a run can be in, from the contract that defines them, so the
|
|
23747
23756
|
// runs filter offers exactly what a row's badge can say.
|
|
23748
23757
|
var RUN_STATUSES = ${JSON.stringify(RunStatusSchema.options)};
|
|
23749
|
-
var state = { token: "", project: "", profile: "default", detailRunId: "", jobPollToken: 0, runsLoadToken: 0, spendLoadToken: 0 };
|
|
23758
|
+
var state = { token: "", project: "", profile: "default", detailRunId: "", detailCaseKey: null, detailScrolled: false, jobPollToken: 0, runsLoadToken: 0, spendLoadToken: 0 };
|
|
23750
23759
|
var knownProfiles = [];
|
|
23751
23760
|
var TOKEN_KEY = "ccqa-hub-token";
|
|
23752
23761
|
var LANG_KEY = "ccqa-hub-lang";
|
|
@@ -23859,6 +23868,9 @@ const CLIENT_JS = `
|
|
|
23859
23868
|
"perspectives.filter.all": "All", "perspectives.filter.deterministic": "Deterministic",
|
|
23860
23869
|
"perspectives.filter.live": "Live",
|
|
23861
23870
|
"perspectives.filter.group.mode": "Mode",
|
|
23871
|
+
"perspectives.filter.showDisabled": "Show disabled",
|
|
23872
|
+
"detail.caseNotInRun": "{case} is not in this run.",
|
|
23873
|
+
"perspectives.filter.disabled": "disabled",
|
|
23862
23874
|
"perspectives.col.verdict": "Verdict", "perspectives.col.audit": "Audit",
|
|
23863
23875
|
"perspectives.col.run": "Execution",
|
|
23864
23876
|
"perspectives.audit.state.due": "Audit due",
|
|
@@ -24083,6 +24095,9 @@ const CLIENT_JS = `
|
|
|
24083
24095
|
"perspectives.filter.all": "すべて", "perspectives.filter.deterministic": "決定的",
|
|
24084
24096
|
"perspectives.filter.live": "ライブ",
|
|
24085
24097
|
"perspectives.filter.group.mode": "モード",
|
|
24098
|
+
"perspectives.filter.showDisabled": "無効も表示",
|
|
24099
|
+
"detail.caseNotInRun": "{case} はこの実行に含まれていません。",
|
|
24100
|
+
"perspectives.filter.disabled": "無効",
|
|
24086
24101
|
"perspectives.col.verdict": "判定", "perspectives.col.audit": "監査",
|
|
24087
24102
|
"perspectives.col.run": "実行",
|
|
24088
24103
|
"perspectives.audit.state.due": "監査待ち",
|
|
@@ -24691,6 +24706,32 @@ const CLIENT_JS = `
|
|
|
24691
24706
|
document.querySelector(".nav-jobs").classList.toggle("disabled", gated);
|
|
24692
24707
|
}
|
|
24693
24708
|
|
|
24709
|
+
// --- pure: run hash ------------------------------------------------------
|
|
24710
|
+
// Lifted out and run by perspectives-visibility.test.ts: the pattern lives
|
|
24711
|
+
// inside a template literal, where a lost backslash fails silently.
|
|
24712
|
+
|
|
24713
|
+
/** A run, optionally opened on one case. */
|
|
24714
|
+
function runCaseHref(runId, caseKey) {
|
|
24715
|
+
var base = "#/runs/" + encodeURIComponent(runId);
|
|
24716
|
+
return caseKey ? base + "?case=" + encodeURIComponent(caseKey) : base;
|
|
24717
|
+
}
|
|
24718
|
+
|
|
24719
|
+
/** The run id and case a #/runs/... hash names, or null when it names no run. */
|
|
24720
|
+
function parseRunHash(hash) {
|
|
24721
|
+
var m = String(hash).match(/^#\\/runs\\/([^?]+)(?:\\?case=(.*))?$/);
|
|
24722
|
+
if (!m) return null;
|
|
24723
|
+
// A truncated paste leaves a half-written %XX, which decodeURIComponent
|
|
24724
|
+
// throws on. Thrown out of route() that reads as "you are disconnected"
|
|
24725
|
+
// and shows the login gate, for a link the reader can see is a run.
|
|
24726
|
+
try {
|
|
24727
|
+
return { runId: decodeURIComponent(m[1]), caseKey: m[2] ? decodeURIComponent(m[2]) : null };
|
|
24728
|
+
} catch (e) {
|
|
24729
|
+
return { runId: m[1], caseKey: m[2] || null };
|
|
24730
|
+
}
|
|
24731
|
+
}
|
|
24732
|
+
|
|
24733
|
+
// --- end pure: run hash --------------------------------------------------
|
|
24734
|
+
|
|
24694
24735
|
function route() {
|
|
24695
24736
|
// Disconnected: the full-screen login gate is the only thing to show.
|
|
24696
24737
|
if (!state.token) { showAuthGate(false); return; }
|
|
@@ -24699,8 +24740,8 @@ const CLIENT_JS = `
|
|
|
24699
24740
|
// land there (e.g. right after login) instead of an empty Runs list, and
|
|
24700
24741
|
// gate any deep-linked #/runs or #/secrets to it too.
|
|
24701
24742
|
if (location.hash === "#/projects" || !state.project) { openProjects(); return; }
|
|
24702
|
-
var m = location.hash
|
|
24703
|
-
if (m) { openRunDetail(
|
|
24743
|
+
var m = parseRunHash(location.hash);
|
|
24744
|
+
if (m) { openRunDetail(m.runId, m.caseKey); return; }
|
|
24704
24745
|
if (location.hash === "#/perspectives") { openPerspectives(); return; }
|
|
24705
24746
|
if (location.hash === "#/coverage") { openCoverage(); return; }
|
|
24706
24747
|
if (location.hash === "#/secrets") { openSecrets(); return; }
|
|
@@ -26038,8 +26079,34 @@ const CLIENT_JS = `
|
|
|
26038
26079
|
function renderSpecCards(runId, results, triageState, isDrift) {
|
|
26039
26080
|
var container = document.getElementById("spec-cards");
|
|
26040
26081
|
clear(container);
|
|
26041
|
-
|
|
26082
|
+
// A slower earlier fetch can land after the reader has moved to another
|
|
26083
|
+
// run. Its cards were always wrong; what is new is that it would also
|
|
26084
|
+
// claim their case is missing from a run that has it.
|
|
26085
|
+
var caseKey = runId === state.detailRunId ? state.detailCaseKey : null;
|
|
26086
|
+
var wanted = null;
|
|
26087
|
+
results.forEach(function (r) {
|
|
26088
|
+
var card = renderSpecCard(runId, r, triageState, isDrift);
|
|
26089
|
+
if (caseKey && r.feature + "/" + r.spec === caseKey) wanted = card;
|
|
26090
|
+
container.appendChild(card);
|
|
26091
|
+
});
|
|
26042
26092
|
document.getElementById("detail-spec-count").textContent = "· " + results.length;
|
|
26093
|
+
if (!caseKey) return;
|
|
26094
|
+
// Marked as well as scrolled: a page that jumped has to say where it
|
|
26095
|
+
// jumped to, or the reader re-finds the row by hand. Cards are rebuilt
|
|
26096
|
+
// again when triage lands, so the mark is reapplied but the scroll is
|
|
26097
|
+
// not — the reader has been reading since the first one.
|
|
26098
|
+
if (wanted) {
|
|
26099
|
+
wanted.classList.add("linked");
|
|
26100
|
+
if (!state.detailScrolled) {
|
|
26101
|
+
state.detailScrolled = true;
|
|
26102
|
+
wanted.scrollIntoView({ block: "center" });
|
|
26103
|
+
}
|
|
26104
|
+
} else if (!state.detailScrolled) {
|
|
26105
|
+
// The case is not in this run (renamed, or the run predates it). Silent
|
|
26106
|
+
// would read as "it is here somewhere".
|
|
26107
|
+
state.detailScrolled = true;
|
|
26108
|
+
detailError(t("detail.caseNotInRun").replace("{case}", caseKey));
|
|
26109
|
+
}
|
|
26043
26110
|
}
|
|
26044
26111
|
|
|
26045
26112
|
// ── run detail: triage (confusion matrix) ───────────────────────────
|
|
@@ -26237,9 +26304,11 @@ const CLIENT_JS = `
|
|
|
26237
26304
|
e.textContent = msg;
|
|
26238
26305
|
}
|
|
26239
26306
|
|
|
26240
|
-
function openRunDetail(runId) {
|
|
26307
|
+
function openRunDetail(runId, caseKey) {
|
|
26241
26308
|
showView("detail");
|
|
26242
26309
|
state.detailRunId = runId;
|
|
26310
|
+
state.detailCaseKey = caseKey || null;
|
|
26311
|
+
state.detailScrolled = false;
|
|
26243
26312
|
document.getElementById("detail-title").textContent = runId.slice(0, 8);
|
|
26244
26313
|
document.getElementById("detail-error").hidden = true;
|
|
26245
26314
|
document.getElementById("learn-cta").hidden = true;
|
|
@@ -26697,7 +26766,8 @@ const CLIENT_JS = `
|
|
|
26697
26766
|
var perspState = {
|
|
26698
26767
|
doc: null, q: "", f: "all",
|
|
26699
26768
|
rerun: null, rerunSupported: null, runUrls: {}, rerunProfiles: [],
|
|
26700
|
-
drift: null
|
|
26769
|
+
drift: null,
|
|
26770
|
+
showDisabled: false
|
|
26701
26771
|
};
|
|
26702
26772
|
|
|
26703
26773
|
function perspectivesPath() {
|
|
@@ -26807,6 +26877,20 @@ const CLIENT_JS = `
|
|
|
26807
26877
|
return feature.featureName + "/" + spec.specName;
|
|
26808
26878
|
}
|
|
26809
26879
|
|
|
26880
|
+
// --- pure: perspectives visibility ---------------------------------------
|
|
26881
|
+
// Lifted out and run by perspectives-visibility.test.ts.
|
|
26882
|
+
|
|
26883
|
+
/**
|
|
26884
|
+
* A disabled case is not part of what this project verifies, so it is not in
|
|
26885
|
+
* the answer the page opens on. What the summary counts and what the table
|
|
26886
|
+
* lists both ask here, or the two disagree on how many cases exist.
|
|
26887
|
+
*/
|
|
26888
|
+
function perspHidden(spec, showDisabled) {
|
|
26889
|
+
return spec.disabled === true && !showDisabled;
|
|
26890
|
+
}
|
|
26891
|
+
|
|
26892
|
+
// --- end pure: perspectives visibility -----------------------------------
|
|
26893
|
+
|
|
26810
26894
|
// Shared by rerun and drift, which differ only in which report they read.
|
|
26811
26895
|
function ledgerEntryFor(report, feature, spec) {
|
|
26812
26896
|
return report && report.specs ? (report.specs[perspSpecKey(feature, spec)] || null) : null;
|
|
@@ -27022,10 +27106,10 @@ const CLIENT_JS = `
|
|
|
27022
27106
|
// One ledger entry as "<short sha> · <when>", linking to the hub's run detail
|
|
27023
27107
|
// and, when that run recorded one, to the CI run. Clicks must not bubble: the
|
|
27024
27108
|
// table row itself toggles the detail panel.
|
|
27025
|
-
function ledgerLine(entry) {
|
|
27109
|
+
function ledgerLine(entry, caseKey) {
|
|
27026
27110
|
var wrap = el("span");
|
|
27027
27111
|
var link = el("a", null, shortSha(entry.gitHead) || String(entry.runId).slice(0, 8));
|
|
27028
|
-
link.href =
|
|
27112
|
+
link.href = runCaseHref(entry.runId, caseKey);
|
|
27029
27113
|
link.title = t("perspectives.result.openRun");
|
|
27030
27114
|
link.addEventListener("click", function (e) { e.stopPropagation(); });
|
|
27031
27115
|
wrap.appendChild(link);
|
|
@@ -27153,7 +27237,7 @@ const CLIENT_JS = `
|
|
|
27153
27237
|
due: "rr-none", clean: "passed", drifted: "failed", undecided: "rr-unknown"
|
|
27154
27238
|
};
|
|
27155
27239
|
|
|
27156
|
-
function perspAuditCell(rr, driftEntry) {
|
|
27240
|
+
function perspAuditCell(rr, driftEntry, caseKey) {
|
|
27157
27241
|
var td = el("td");
|
|
27158
27242
|
if (!rr || !rr.audit) {
|
|
27159
27243
|
td.appendChild(el("span", "muted", "\u2014"));
|
|
@@ -27176,7 +27260,7 @@ const CLIENT_JS = `
|
|
|
27176
27260
|
// audit's own coordinate, so it comes from the drift ledger.
|
|
27177
27261
|
if (driftEntry) {
|
|
27178
27262
|
var sub = el("span", "cellsub");
|
|
27179
|
-
sub.appendChild(ledgerLine(driftEntry));
|
|
27263
|
+
sub.appendChild(ledgerLine(driftEntry, caseKey));
|
|
27180
27264
|
if (driftEntry.graded) {
|
|
27181
27265
|
sub.appendChild(document.createTextNode(" · "));
|
|
27182
27266
|
sub.appendChild(el("span", "graded-mark", t("perspectives.drift.graded")));
|
|
@@ -27186,7 +27270,7 @@ const CLIENT_JS = `
|
|
|
27186
27270
|
return td;
|
|
27187
27271
|
}
|
|
27188
27272
|
|
|
27189
|
-
function perspRunCell(rr) {
|
|
27273
|
+
function perspRunCell(rr, caseKey) {
|
|
27190
27274
|
var td = el("td");
|
|
27191
27275
|
var runState = perspRunState(rr);
|
|
27192
27276
|
// No verdict at all: this case is in the document but not in the report
|
|
@@ -27220,7 +27304,7 @@ const CLIENT_JS = `
|
|
|
27220
27304
|
var last = lastResult(rr);
|
|
27221
27305
|
if (last) {
|
|
27222
27306
|
var sub = el("span", "cellsub");
|
|
27223
|
-
sub.appendChild(ledgerLine(last.entry));
|
|
27307
|
+
sub.appendChild(ledgerLine(last.entry, caseKey));
|
|
27224
27308
|
td.appendChild(sub);
|
|
27225
27309
|
}
|
|
27226
27310
|
}
|
|
@@ -27292,16 +27376,29 @@ const CLIENT_JS = `
|
|
|
27292
27376
|
var host = document.getElementById("persp-ov");
|
|
27293
27377
|
clear(host);
|
|
27294
27378
|
var records = [];
|
|
27379
|
+
var features = 0;
|
|
27380
|
+
var listed = 0;
|
|
27295
27381
|
doc.features.forEach(function (feature) {
|
|
27382
|
+
var before = listed;
|
|
27296
27383
|
feature.specs.forEach(function (spec) {
|
|
27297
|
-
|
|
27384
|
+
if (perspHidden(spec, perspState.showDisabled)) return;
|
|
27385
|
+
listed += 1;
|
|
27386
|
+
// A disabled case has no ledger entry — the hub does not ask for one
|
|
27387
|
+
// — and an absent entry reads as "needs re-run". Counting it would
|
|
27388
|
+
// put work in the bar that nobody intends to do.
|
|
27389
|
+
if (spec.disabled !== true) records.push(ledgerEntryFor(perspState.rerun, feature, spec));
|
|
27298
27390
|
});
|
|
27391
|
+
// A feature whose cases are all hidden renders no group below, so
|
|
27392
|
+
// counting it here would put the summary above a table that disagrees.
|
|
27393
|
+
if (listed > before) features += 1;
|
|
27299
27394
|
});
|
|
27300
27395
|
|
|
27301
27396
|
var inv = el("div", "ov-inv");
|
|
27302
|
-
|
|
27397
|
+
// The inventory counts what the table lists; the bars below judge only the
|
|
27398
|
+
// cases the hub keeps a ledger for, so the two numbers can differ.
|
|
27399
|
+
inv.appendChild(el("b", null, String(listed)));
|
|
27303
27400
|
inv.appendChild(document.createTextNode(" " + t("perspectives.ov.cases") + " / "));
|
|
27304
|
-
inv.appendChild(el("b", null, String(
|
|
27401
|
+
inv.appendChild(el("b", null, String(features)));
|
|
27305
27402
|
inv.appendChild(document.createTextNode(" " + t("perspectives.ov.features")));
|
|
27306
27403
|
host.appendChild(inv);
|
|
27307
27404
|
if (!records.length) return;
|
|
@@ -27328,6 +27425,12 @@ const CLIENT_JS = `
|
|
|
27328
27425
|
// search text always applies: a chip's count has to be the number of rows
|
|
27329
27426
|
// clicking it actually leaves.
|
|
27330
27427
|
function perspMatches(feature, spec, f) {
|
|
27428
|
+
if (perspHidden(spec, perspState.showDisabled)) return false;
|
|
27429
|
+
return perspMatchesFilters(feature, spec, f);
|
|
27430
|
+
}
|
|
27431
|
+
|
|
27432
|
+
/** Everything the toolbar filters on except whether the case is disabled. */
|
|
27433
|
+
function perspMatchesFilters(feature, spec, f) {
|
|
27331
27434
|
if (f === "deterministic" && perspMode(spec) !== "deterministic") return false;
|
|
27332
27435
|
if (f === "live" && perspMode(spec) !== "live") return false;
|
|
27333
27436
|
// The verdict chips (same words as RERUN_ORDER/the 判定 column) share
|
|
@@ -27347,12 +27450,12 @@ const CLIENT_JS = `
|
|
|
27347
27450
|
return true;
|
|
27348
27451
|
}
|
|
27349
27452
|
|
|
27350
|
-
function
|
|
27453
|
+
function perspCount(pred) {
|
|
27351
27454
|
var doc = perspState.doc;
|
|
27352
27455
|
if (!doc) return 0;
|
|
27353
27456
|
var n = 0;
|
|
27354
27457
|
doc.features.forEach(function (feature) {
|
|
27355
|
-
feature.specs.forEach(function (spec) { if (
|
|
27458
|
+
feature.specs.forEach(function (spec) { if (pred(feature, spec)) n += 1; });
|
|
27356
27459
|
});
|
|
27357
27460
|
return n;
|
|
27358
27461
|
}
|
|
@@ -27844,9 +27947,13 @@ const CLIENT_JS = `
|
|
|
27844
27947
|
row.tabIndex = 0;
|
|
27845
27948
|
row.setAttribute("aria-expanded", "false");
|
|
27846
27949
|
|
|
27950
|
+
var caseKey = perspSpecKey(feature, spec);
|
|
27847
27951
|
var titleTd = el("td", "c-title");
|
|
27848
27952
|
titleTd.appendChild(document.createTextNode(spec.title));
|
|
27849
|
-
|
|
27953
|
+
// Only reachable with the toggle on, where saying which rows are the
|
|
27954
|
+
// disabled ones is the whole point of having turned it on.
|
|
27955
|
+
if (spec.disabled === true) titleTd.appendChild(el("span", "chip off", t("perspectives.filter.disabled")));
|
|
27956
|
+
titleTd.appendChild(el("span", "c-id", caseKey));
|
|
27850
27957
|
if (spec.summary) titleTd.appendChild(el("span", "csum", spec.summary));
|
|
27851
27958
|
row.appendChild(titleTd);
|
|
27852
27959
|
|
|
@@ -27857,8 +27964,8 @@ const CLIENT_JS = `
|
|
|
27857
27964
|
if (showRerun) {
|
|
27858
27965
|
var rr = ledgerEntryFor(perspState.rerun, feature, spec);
|
|
27859
27966
|
row.appendChild(perspVerdictCell(rr));
|
|
27860
|
-
row.appendChild(perspRunCell(rr));
|
|
27861
|
-
row.appendChild(perspAuditCell(rr, ledgerEntryFor(perspState.drift, feature, spec)));
|
|
27967
|
+
row.appendChild(perspRunCell(rr, caseKey));
|
|
27968
|
+
row.appendChild(perspAuditCell(rr, ledgerEntryFor(perspState.drift, feature, spec), caseKey));
|
|
27862
27969
|
}
|
|
27863
27970
|
|
|
27864
27971
|
var chevTd = el("td", "c-chev");
|
|
@@ -27910,13 +28017,23 @@ const CLIENT_JS = `
|
|
|
27910
28017
|
// Each chip also carries what it would yield — the mode breakdown the
|
|
27911
28018
|
// summary row used to spend four tiles on.
|
|
27912
28019
|
function syncPerspChips() {
|
|
28020
|
+
// What ticking the box would actually add: disabled cases that the filters
|
|
28021
|
+
// in effect would then let through. A flat count of every disabled case
|
|
28022
|
+
// promises rows the active chip or search would drop.
|
|
28023
|
+
var off = perspState.showDisabled ? 0 : perspCount(function (feature, spec) {
|
|
28024
|
+
return spec.disabled === true && perspMatchesFilters(feature, spec, perspState.f);
|
|
28025
|
+
});
|
|
28026
|
+
document.getElementById("persp-disabled-count").textContent = off ? "+" + off : "";
|
|
28027
|
+
document.getElementById("persp-show-disabled").checked = perspState.showDisabled;
|
|
27913
28028
|
var group = document.getElementById("persp-verdict-chips");
|
|
27914
28029
|
group.hidden = perspState.rerunSupported !== true;
|
|
27915
28030
|
if (perspState.rerunSupported !== true && RERUN_ORDER.indexOf(perspState.f) !== -1) perspState.f = "all";
|
|
27916
28031
|
document.querySelectorAll("#view-perspectives .fchip").forEach(function (b) {
|
|
27917
28032
|
var f = b.getAttribute("data-f");
|
|
27918
28033
|
b.setAttribute("aria-pressed", String(f === perspState.f));
|
|
27919
|
-
b.querySelector(".fcount").textContent = perspState.doc
|
|
28034
|
+
b.querySelector(".fcount").textContent = perspState.doc
|
|
28035
|
+
? String(perspCount(function (feature, spec) { return perspMatches(feature, spec, f); }))
|
|
28036
|
+
: "";
|
|
27920
28037
|
});
|
|
27921
28038
|
}
|
|
27922
28039
|
|
|
@@ -28622,6 +28739,10 @@ const CLIENT_JS = `
|
|
|
28622
28739
|
perspState.q = e.target.value.trim().toLowerCase();
|
|
28623
28740
|
renderPerspectives();
|
|
28624
28741
|
});
|
|
28742
|
+
document.getElementById("persp-show-disabled").addEventListener("change", function (e) {
|
|
28743
|
+
perspState.showDisabled = e.target.checked;
|
|
28744
|
+
renderPerspectives();
|
|
28745
|
+
});
|
|
28625
28746
|
|
|
28626
28747
|
// Debounced, unlike persp-q: each keystroke rebuilds the whole tree, and
|
|
28627
28748
|
// the universe can hold thousands of files.
|
package/dist/package.json
CHANGED