staysfixed 0.7.2 → 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/CHANGELOG.md +429 -0
- package/README.md +193 -57
- package/docs/design-v2.md +24 -4
- package/docs/getting-started.md +19 -6
- package/docs/guards.md +2 -2
- package/docs/how-v2-works.md +12 -11
- package/docs/mcp.md +17 -8
- package/docs/settings.md +564 -0
- package/docs/watching.md +10 -4
- package/examples/staysfixed.config.electron.js +17 -6
- package/examples/staysfixed.config.web.js +22 -5
- package/package.json +2 -1
- package/src/cli/index.js +55 -46
- package/src/cli/status.js +45 -1
- package/src/cli/watch-flags.js +54 -0
- package/src/core/config.js +54 -3
- package/src/core/paths.js +15 -0
- package/src/guard/run.js +70 -3
- package/src/report/console.js +50 -6
- package/src/run.js +11 -0
- package/src/types.js +3 -0
- package/src/v2/adapters/android-driver.js +6 -1
- package/src/v2/adapters/android.js +97 -2
- package/src/v2/adapters/child.js +101 -0
- package/src/v2/adapters/contract.js +42 -5
- package/src/v2/adapters/electron.js +72 -6
- package/src/v2/adapters/http.js +18 -11
- package/src/v2/adapters/ios-driver.js +64 -14
- package/src/v2/adapters/ios.js +247 -25
- package/src/v2/adapters/process.js +783 -71
- package/src/v2/adapters/python.js +495 -0
- package/src/v2/adapters/source.js +373 -18
- package/src/v2/adapters/web-driver.js +134 -24
- package/src/v2/adapters/web.js +149 -18
- package/src/v2/adapters/windows.js +18 -1
- package/src/v2/browsers.js +66 -3
- package/src/v2/cause.js +61 -17
- package/src/v2/check.js +653 -69
- package/src/v2/ci.js +130 -35
- package/src/v2/cli.js +65 -42
- package/src/v2/cluster.js +220 -14
- package/src/v2/coverage.js +43 -176
- package/src/v2/detect.js +308 -60
- package/src/v2/doctor.js +353 -54
- package/src/v2/escalate.js +5 -1
- package/src/v2/init.js +183 -66
- package/src/v2/intent.js +9 -23
- package/src/v2/journeys/from-suite.js +336 -30
- package/src/v2/journeys/index.js +99 -6
- package/src/v2/mcp/tools.js +90 -16
- package/src/v2/normalise.js +169 -23
- package/src/v2/observation.js +19 -33
- package/src/v2/rank.js +216 -23
- package/src/v2/reference.js +160 -24
- package/src/v2/remote.js +113 -18
- package/src/v2/run.js +103 -14
- package/src/v2/sealed.js +0 -20
- package/src/v2/selfcheck.js +190 -13
- package/src/v2/ship.js +55 -5
- package/src/v2/store.js +67 -1
- package/src/v2/types.js +12 -2
- package/src/v2/waiver.js +64 -54
- package/src/v2/watch/events.js +60 -215
- package/src/v2/watch/focus.js +14 -4
- package/src/v2/watch/panel.js +167 -17
package/src/v2/watch/panel.js
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
* the last one.
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
|
-
import { SURFACE_WORDS, SOURCE_WORDS, surfaceWord } from './events.js';
|
|
52
|
+
import { SURFACE_WORDS, SURFACE_NOTES, SOURCE_WORDS, CLASS_WORDS, surfaceWord } from './events.js';
|
|
53
53
|
|
|
54
54
|
/** @typedef {import('./events.js').PanelPlanShape} PanelPlan */
|
|
55
55
|
/** @typedef {import('./events.js').PanelJourney} PanelJourney */
|
|
@@ -201,6 +201,12 @@ export function panelHtml(plan = {}) {
|
|
|
201
201
|
reference,
|
|
202
202
|
words: SURFACE_WORDS,
|
|
203
203
|
sources: SOURCE_WORDS,
|
|
204
|
+
// Embedded, never re-typed. This page used to keep its own hand-written copy of the
|
|
205
|
+
// class words and the two had already drifted apart — it said "a bug already reported
|
|
206
|
+
// once" where events.js says "a bug you already reported", and it had no word at all for
|
|
207
|
+
// an ordinary finding. One list, one place, the same rule SURFACE_WORDS has always had.
|
|
208
|
+
classes: CLASS_WORDS,
|
|
209
|
+
surfaceNotes: SURFACE_NOTES,
|
|
204
210
|
});
|
|
205
211
|
|
|
206
212
|
const subtitle = surfaces.length ? surfaces.join(' · ') : '';
|
|
@@ -231,7 +237,8 @@ export function panelHtml(plan = {}) {
|
|
|
231
237
|
`<span class="app" id="surfaces"${subtitle ? '' : ' hidden'}>${escapeHtml(subtitle)}</span></p>`,
|
|
232
238
|
// The one sentence a person reads from four feet away.
|
|
233
239
|
'<p class="state" id="state">getting ready</p>',
|
|
234
|
-
'<
|
|
240
|
+
'<div class="what" id="what"></div>',
|
|
241
|
+
'<button class="whatmore" id="whatmore" type="button" hidden></button>',
|
|
235
242
|
'<div class="meter">',
|
|
236
243
|
'<div class="track" id="track"><div class="fill" id="fill"></div></div>',
|
|
237
244
|
'<span class="counts mono" id="counts"></span>',
|
|
@@ -531,6 +538,12 @@ button { font: inherit; color: inherit; }
|
|
|
531
538
|
to { opacity: 1; transform: none; filter: blur(0); }
|
|
532
539
|
}
|
|
533
540
|
.what { margin: 7px 0 0; font-size: var(--t-small); color: var(--faint); line-height: 1.5; overflow-wrap: anywhere; }
|
|
541
|
+
.what p { margin: 0 0 5px; }
|
|
542
|
+
.what p:last-child { margin-bottom: 0; }
|
|
543
|
+
.whatmore { margin: 6px 0 0; padding: 0; border: 0; background: none; font: inherit; font-size: var(--t-small); color: var(--dim); cursor: pointer; letter-spacing: .02em; }
|
|
544
|
+
.whatmore:hover { color: var(--text); }
|
|
545
|
+
.whatmore::before { content: '\\25B8'; display: inline-block; margin-right: 5px; transition: transform .18s ease; }
|
|
546
|
+
.whatmore[data-open="1"]::before { transform: rotate(90deg); }
|
|
534
547
|
|
|
535
548
|
/* One hairline, not a row of blocks: each journey adds its own slice, so
|
|
536
549
|
progress and outcome are the same object. A walk where everything held ends
|
|
@@ -615,6 +628,9 @@ button { font: inherit; color: inherit; }
|
|
|
615
628
|
font-size: var(--t-meta); color: var(--faintest);
|
|
616
629
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
617
630
|
}
|
|
631
|
+
/* What "walked" actually MEANT on this surface. "Website" alone does not tell anybody what
|
|
632
|
+
was looked at, and the whole worth of a green run is the reader knowing what it covers. */
|
|
633
|
+
.surfacewhat { padding: 0 3px 7px; font-size: var(--t-meta); color: var(--faintest); }
|
|
618
634
|
.items { border-radius: var(--radius); background: var(--card); overflow: hidden; box-shadow: inset 0 0 0 1px var(--line); }
|
|
619
635
|
.item + .item { box-shadow: inset 0 1px 0 var(--line); }
|
|
620
636
|
.item { transition: background var(--calm) var(--ease); }
|
|
@@ -825,7 +841,7 @@ const SCRIPT = `
|
|
|
825
841
|
// This page only ever runs in the window Stays Fixed just opened, so there is no
|
|
826
842
|
// compatibility question to answer and nothing to load.
|
|
827
843
|
|
|
828
|
-
var plan = { product: '', project: '', journeys: [], surfaces: [], reference: null, words: {}, sources: {} };
|
|
844
|
+
var plan = { product: '', project: '', journeys: [], surfaces: [], reference: null, words: {}, sources: {}, classes: {}, surfaceNotes: {} };
|
|
829
845
|
try {
|
|
830
846
|
var blob = document.getElementById('staysfixed-plan');
|
|
831
847
|
if (blob && blob.textContent) plan = JSON.parse(blob.textContent) || plan;
|
|
@@ -838,7 +854,7 @@ const SCRIPT = `
|
|
|
838
854
|
|
|
839
855
|
var ui = {
|
|
840
856
|
clock: el('clock'), product: el('product'), surfaces: el('surfaces'), targetsep: el('targetsep'),
|
|
841
|
-
state: el('state'), what: el('what'), track: el('track'), fill: el('fill'), counts: el('counts'),
|
|
857
|
+
state: el('state'), what: el('what'), whatMore: el('whatmore'), track: el('track'), fill: el('fill'), counts: el('counts'),
|
|
842
858
|
refstrip: el('refstrip'), refname: el('refname'), refhow: el('refhow'), refwarn: el('refwarn'),
|
|
843
859
|
groupWalk: el('groupWalk'), surfaceList: el('surfaceList'), countWalk: el('countWalk'),
|
|
844
860
|
groupWobble: el('groupWobble'), wSteady: el('wSteady'), wUnstable: el('wUnstable'),
|
|
@@ -968,9 +984,60 @@ const SCRIPT = `
|
|
|
968
984
|
ui.aura.style.setProperty('--tint', map[tone] || 'var(--accent)');
|
|
969
985
|
}
|
|
970
986
|
|
|
987
|
+
// The summary, one sentence to a line, with the tail folded away.
|
|
988
|
+
//
|
|
989
|
+
// The engine's summary is a single paragraph that carries every fact a run produced, and
|
|
990
|
+
// on a real check it runs to fifteen lines. In a terminal that is a wall of text; in a
|
|
991
|
+
// window three hundred pixels wide it is a grey block nobody reads, sitting directly under
|
|
992
|
+
// the one sentence that matters. The facts are not the problem — dropping any of them
|
|
993
|
+
// would be worse — so nothing is thrown away here. It is split at sentence ends, the
|
|
994
|
+
// first two are shown, and the rest are one click away with the count on the button, so
|
|
995
|
+
// the window opens on something a person can actually take in.
|
|
996
|
+
var whatRest = [];
|
|
997
|
+
var whatOpen = false;
|
|
998
|
+
|
|
999
|
+
// Note the doubled backslashes, and never write a backtick in this file.
|
|
1000
|
+
//
|
|
1001
|
+
// Everything in this function lives inside a template literal in panel.js, and a template
|
|
1002
|
+
// literal eats an unknown escape exactly the way a string literal does: a single
|
|
1003
|
+
// backslash-s arrives in the browser as a plain letter s. Written once, this split ran as
|
|
1004
|
+
// (?<=[.!?])s+ — it matched letters instead of spaces, never split anything, and the
|
|
1005
|
+
// window went on showing the same fifteen-line paragraph while every test passed.
|
|
1006
|
+
function splitSentences(paragraph) {
|
|
1007
|
+
var raw = String(paragraph || '').split(/(?<=[.!?])\\s+(?=[A-Z0-9"'\u201c])/);
|
|
1008
|
+
var out = [];
|
|
1009
|
+
for (var i = 0; i < raw.length; i++) {
|
|
1010
|
+
var one = raw[i].replace(/^\\s+|\\s+$/g, '');
|
|
1011
|
+
if (one) out.push(one);
|
|
1012
|
+
}
|
|
1013
|
+
return out;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function paintWhat() {
|
|
1017
|
+
while (ui.what.firstChild) ui.what.removeChild(ui.what.firstChild);
|
|
1018
|
+
var lines = whatShown.concat(whatOpen ? whatRest : []);
|
|
1019
|
+
for (var i = 0; i < lines.length; i++) {
|
|
1020
|
+
var p = document.createElement('p');
|
|
1021
|
+
p.appendChild(document.createTextNode(lines[i]));
|
|
1022
|
+
ui.what.appendChild(p);
|
|
1023
|
+
}
|
|
1024
|
+
show(ui.what, lines.length > 0);
|
|
1025
|
+
if (whatRest.length === 0) {
|
|
1026
|
+
show(ui.whatMore, false);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
show(ui.whatMore, true);
|
|
1030
|
+
ui.whatMore.setAttribute('data-open', whatOpen ? '1' : '0');
|
|
1031
|
+
text(ui.whatMore, whatOpen ? 'less' : whatRest.length + ' more ' + plural(whatRest.length, 'thing', 'things') + ' this run says');
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
var whatShown = [];
|
|
1035
|
+
|
|
971
1036
|
function setWhat(sentence) {
|
|
972
|
-
|
|
973
|
-
|
|
1037
|
+
var all = splitSentences(sentence);
|
|
1038
|
+
whatShown = all.slice(0, 2);
|
|
1039
|
+
whatRest = all.slice(2);
|
|
1040
|
+
paintWhat();
|
|
974
1041
|
}
|
|
975
1042
|
|
|
976
1043
|
function updateCounts() {
|
|
@@ -1012,7 +1079,7 @@ const SCRIPT = `
|
|
|
1012
1079
|
// The walk
|
|
1013
1080
|
// -------------------------------------------------------------------------
|
|
1014
1081
|
|
|
1015
|
-
function surfaceBox(word,
|
|
1082
|
+
function surfaceBox(word, surface) {
|
|
1016
1083
|
if (surfaces[word]) return surfaces[word];
|
|
1017
1084
|
var box = document.createElement('section');
|
|
1018
1085
|
box.className = 'surface';
|
|
@@ -1023,12 +1090,22 @@ const SCRIPT = `
|
|
|
1023
1090
|
name.textContent = word;
|
|
1024
1091
|
var hint = document.createElement('span');
|
|
1025
1092
|
hint.className = 'surfacenote';
|
|
1026
|
-
hint.textContent =
|
|
1093
|
+
hint.textContent = '';
|
|
1027
1094
|
head.appendChild(name);
|
|
1028
1095
|
head.appendChild(hint);
|
|
1029
1096
|
var items = document.createElement('div');
|
|
1030
1097
|
items.className = 'items';
|
|
1031
1098
|
box.appendChild(head);
|
|
1099
|
+
// How this surface is watched, in one line. A person reading "iPhone · 4 journeys" has
|
|
1100
|
+
// been told nothing about what the tool actually looked at, and on a green run that is
|
|
1101
|
+
// the only thing that says how much the green is worth.
|
|
1102
|
+
var what = surface && plan.surfaceNotes ? plan.surfaceNotes[surface] : '';
|
|
1103
|
+
if (what) {
|
|
1104
|
+
var line = document.createElement('p');
|
|
1105
|
+
line.className = 'surfacewhat';
|
|
1106
|
+
line.textContent = what;
|
|
1107
|
+
box.appendChild(line);
|
|
1108
|
+
}
|
|
1032
1109
|
box.appendChild(items);
|
|
1033
1110
|
if (ui.surfaceList) ui.surfaceList.appendChild(box);
|
|
1034
1111
|
show(ui.groupWalk, true);
|
|
@@ -1041,7 +1118,7 @@ const SCRIPT = `
|
|
|
1041
1118
|
if (journeys[name]) return journeys[name];
|
|
1042
1119
|
var info = meta || {};
|
|
1043
1120
|
var word = surfaceWord(info.surface, info.surfaceWord);
|
|
1044
|
-
var group = surfaceBox(word);
|
|
1121
|
+
var group = surfaceBox(word, info.surface);
|
|
1045
1122
|
group.count++;
|
|
1046
1123
|
group.hint.textContent = commas(group.count) + ' ' + plural(group.count, 'journey', 'journeys');
|
|
1047
1124
|
|
|
@@ -1309,7 +1386,10 @@ const SCRIPT = `
|
|
|
1309
1386
|
if (f.sealed) sealedFindings.push(f);
|
|
1310
1387
|
show(ui.groupFindings, true);
|
|
1311
1388
|
show(ui.nothing, false);
|
|
1312
|
-
|
|
1389
|
+
// The row is kept ON the finding, because a finding can be taken back — see
|
|
1390
|
+
// dropFindingsNotIn below. Without a handle on its own row there is no way to unsay one.
|
|
1391
|
+
f.node = findingRow(f);
|
|
1392
|
+
ui.findingList.appendChild(f.node);
|
|
1313
1393
|
text(ui.countFindings, commas(findings.length));
|
|
1314
1394
|
renderNeeds();
|
|
1315
1395
|
|
|
@@ -1445,12 +1525,10 @@ const SCRIPT = `
|
|
|
1445
1525
|
return item;
|
|
1446
1526
|
}
|
|
1447
1527
|
|
|
1528
|
+
// The one list, embedded from events.js. There used to be a second copy written out here
|
|
1529
|
+
// by hand, and it had already drifted from the first.
|
|
1448
1530
|
function classWord(name) {
|
|
1449
|
-
|
|
1450
|
-
money: 'money', 'sign-in': 'signing in', 'data-loss': 'losing data',
|
|
1451
|
-
crash: 'a crash', guard: 'a bug already reported once'
|
|
1452
|
-
};
|
|
1453
|
-
return words[name] || name;
|
|
1531
|
+
return (plan.classes && plan.classes[name]) || name;
|
|
1454
1532
|
}
|
|
1455
1533
|
|
|
1456
1534
|
function onCoverage(ev) {
|
|
@@ -1458,6 +1536,13 @@ const SCRIPT = `
|
|
|
1458
1536
|
if (!c) return;
|
|
1459
1537
|
show(ui.groupCoverage, true);
|
|
1460
1538
|
show(ui.nothing, false);
|
|
1539
|
+
// ONE count of addresses on this page, not two. The header counts up as each journey
|
|
1540
|
+
// reports in; the coverage ledger is the finished tally, worked out over every address
|
|
1541
|
+
// the run touched. Where a journey and its neighbour watch the same address the running
|
|
1542
|
+
// total counts it twice and the ledger counts it once, and the page ended up carrying
|
|
1543
|
+
// two figures with the same words on them and different numbers underneath. The ledger
|
|
1544
|
+
// is the one that was counted properly, so when it lands it is the one that stands.
|
|
1545
|
+
if (typeof c.paths === 'number') { watched = c.paths; updateCounts(); }
|
|
1461
1546
|
ui.covFigures.textContent = '';
|
|
1462
1547
|
addFigure(commas(c.paths), plural(c.paths, 'address watched', 'addresses watched'));
|
|
1463
1548
|
addFigure(commas(c.journeys), plural(c.journeys, 'journey walked', 'journeys walked'));
|
|
@@ -1567,12 +1652,37 @@ const SCRIPT = `
|
|
|
1567
1652
|
return;
|
|
1568
1653
|
}
|
|
1569
1654
|
|
|
1570
|
-
|
|
1655
|
+
// The waived ones go. A finding only ever arrived here; there was no way to take one
|
|
1656
|
+
// away, and findings ARE taken away — the engine's verdict carries every difference it
|
|
1657
|
+
// found, then the gates remove the ones an agent has recorded as intended and the
|
|
1658
|
+
// settled verdict arrives second. Both reach this window, so a waived finding stayed on
|
|
1659
|
+
// screen, in the "What survived" list, beside a terminal that had already stopped
|
|
1660
|
+
// reporting it. The window was the only place still calling it a problem.
|
|
1661
|
+
// Only ever done when the check actually named the survivors: a verdict that arrives
|
|
1662
|
+
// already trimmed carries a count and no list, and clearing the list on that would wipe
|
|
1663
|
+
// the findings rather than settle them.
|
|
1664
|
+
if (Array.isArray(ev.findingIds)) dropFindingsNotIn(ev.findingIds);
|
|
1665
|
+
|
|
1666
|
+
// WHAT THE RUN SAYS ABOUT ITSELF, not just how many findings came out of it. The
|
|
1667
|
+
// headline was picked from the finding counts alone and never read ok at all, so the run
|
|
1668
|
+
// that compared NOTHING with anything — no findings, because nothing was compared — got
|
|
1669
|
+
// this tool's all-clear sentence. That is the single most dangerous sentence it can produce,
|
|
1670
|
+
// said about the one run that proves least. The engine already exits ok:false with
|
|
1671
|
+
// "NO ANSWER FROM THIS RUN" on it; the window says the same thing.
|
|
1672
|
+
var tone = v.sealed > 0 ? 'wait' : (v.findings > 0 ? 'moved' : (v.ok === false ? 'wait' : 'held'));
|
|
1571
1673
|
var sentence;
|
|
1572
1674
|
if (v.sealed > 0) {
|
|
1573
1675
|
sentence = commas(v.sealed) + ' ' + plural(v.sealed, 'thing needs', 'things need') + ' you.';
|
|
1574
1676
|
} else if (v.findings > 0) {
|
|
1575
1677
|
sentence = commas(v.findings) + ' ' + plural(v.findings, 'finding', 'findings') + ' the agent has to deal with.';
|
|
1678
|
+
} else if (v.newlyUnstable > 0) {
|
|
1679
|
+
// No finding, and still not a pass: something that used to give the same answer every
|
|
1680
|
+
// time does not any more. A change that made a product unpredictable has broken
|
|
1681
|
+
// something, even when no single value can be pointed at.
|
|
1682
|
+
sentence = commas(v.newlyUnstable) + ' ' + plural(v.newlyUnstable, 'address that was', 'addresses that were') +
|
|
1683
|
+
' steady before this change ' + plural(v.newlyUnstable, 'wobbles', 'wobble') + ' now.';
|
|
1684
|
+
} else if (v.ok === false) {
|
|
1685
|
+
sentence = 'No answer from this run — that is not a pass.';
|
|
1576
1686
|
} else {
|
|
1577
1687
|
sentence = 'Everything that worked still works.';
|
|
1578
1688
|
}
|
|
@@ -1582,7 +1692,11 @@ const SCRIPT = `
|
|
|
1582
1692
|
// hundreds of unopened doors, or measured against a stored record, is a smaller claim
|
|
1583
1693
|
// than it looks, and the smaller claim is the true one.
|
|
1584
1694
|
var caveats = [];
|
|
1585
|
-
|
|
1695
|
+
// Said ONCE. The engine already ends the summary with this exact sentence, and pushing
|
|
1696
|
+
// it again here printed the whole "compared against the stored record, that is genuinely
|
|
1697
|
+
// weaker..." paragraph twice in a row, one straight after the other, on every single
|
|
1698
|
+
// stored-record run. A window that repeats itself reads like a window that is broken.
|
|
1699
|
+
if (v.modeWarning && String(v.summary || '').indexOf(v.modeWarning) === -1) caveats.push(v.modeWarning);
|
|
1586
1700
|
if (v.differencesNoise) {
|
|
1587
1701
|
caveats.push(commas(v.differencesNoise) + ' ' + plural(v.differencesNoise, 'difference was', 'differences were') + ' this build arguing with itself, and were subtracted.');
|
|
1588
1702
|
}
|
|
@@ -1591,6 +1705,42 @@ const SCRIPT = `
|
|
|
1591
1705
|
renderNeeds();
|
|
1592
1706
|
}
|
|
1593
1707
|
|
|
1708
|
+
// Take back every finding the check no longer stands behind.
|
|
1709
|
+
function dropFindingsNotIn(ids) {
|
|
1710
|
+
var keep = {};
|
|
1711
|
+
for (var i = 0; i < ids.length; i++) keep[ids[i]] = true;
|
|
1712
|
+
var left = [];
|
|
1713
|
+
var repaint = {};
|
|
1714
|
+
for (var j = 0; j < findings.length; j++) {
|
|
1715
|
+
var f = findings[j];
|
|
1716
|
+
if (keep[f.id]) { left.push(f); continue; }
|
|
1717
|
+
if (f.node && f.node.parentNode) f.node.parentNode.removeChild(f.node);
|
|
1718
|
+
if (f.journey) repaint[f.journey] = true;
|
|
1719
|
+
}
|
|
1720
|
+
findings = left;
|
|
1721
|
+
// The journey a waived finding was found on goes back to whatever its REMAINING findings
|
|
1722
|
+
// say, not straight to clean. A journey with two findings, one of them waived, is still a
|
|
1723
|
+
// journey with a finding on it, and blanking its mark would hide the other one.
|
|
1724
|
+
for (var name in repaint) {
|
|
1725
|
+
if (!Object.prototype.hasOwnProperty.call(repaint, name) || !journeys[name]) continue;
|
|
1726
|
+
var worst = 'held';
|
|
1727
|
+
for (var m = 0; m < findings.length; m++) {
|
|
1728
|
+
if (findings[m].journey !== name) continue;
|
|
1729
|
+
var t = findings[m].sealed ? 'wait' : (findings[m]['class'] === 'crash' ? 'broke' : 'moved');
|
|
1730
|
+
if (rank(t) > rank(worst)) worst = t;
|
|
1731
|
+
}
|
|
1732
|
+
markJourney(journeys[name], worst, worst !== 'held');
|
|
1733
|
+
}
|
|
1734
|
+
var stillSealed = [];
|
|
1735
|
+
for (var k = 0; k < sealedFindings.length; k++) if (keep[sealedFindings[k].id]) stillSealed.push(sealedFindings[k]);
|
|
1736
|
+
sealedFindings = stillSealed;
|
|
1737
|
+
text(ui.countFindings, commas(findings.length));
|
|
1738
|
+
show(ui.groupFindings, findings.length > 0);
|
|
1739
|
+
// The footer is the one part a person is meant to act on, so it is redrawn from what is
|
|
1740
|
+
// left rather than kept from what there was.
|
|
1741
|
+
renderNeeds();
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1594
1744
|
// -------------------------------------------------------------------------
|
|
1595
1745
|
// Evidence, at full size
|
|
1596
1746
|
// -------------------------------------------------------------------------
|