staysfixed 0.3.0 → 0.3.1
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/package.json +1 -1
- package/src/watch/panel.js +73 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "staysfixed",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Prove that what already worked still works after an agent changed the code. Picture checks, guards for fixed bugs, a pre-release walkthrough, and known-good markers — as a CLI and as an MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/watch/panel.js
CHANGED
|
@@ -333,25 +333,24 @@ const STYLE = `
|
|
|
333
333
|
:root {
|
|
334
334
|
color-scheme: dark;
|
|
335
335
|
|
|
336
|
-
/*
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
--
|
|
343
|
-
--
|
|
344
|
-
--
|
|
345
|
-
|
|
346
|
-
--
|
|
347
|
-
--
|
|
348
|
-
|
|
349
|
-
--
|
|
350
|
-
|
|
351
|
-
--
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
--line: rgba(255, 255, 255, 0.05);
|
|
336
|
+
/* Neutral black. No warm cast, no cool cast — every tint I tried made the
|
|
337
|
+
panel look like it was lit by something that was not in the room. The
|
|
338
|
+
character comes from the three state colours and nothing else. */
|
|
339
|
+
--ground: #101010;
|
|
340
|
+
--lift: #191919;
|
|
341
|
+
--card: rgba(255, 255, 255, 0.035);
|
|
342
|
+
--card-hover: rgba(255, 255, 255, 0.06);
|
|
343
|
+
--well: #0a0a0a;
|
|
344
|
+
--glass: rgba(16, 16, 16, 0.9);
|
|
345
|
+
|
|
346
|
+
--ink: #ededed;
|
|
347
|
+
--soft: #b2b2b2;
|
|
348
|
+
--faint: #8d8d8d;
|
|
349
|
+
--faintest: #6d6d6d;
|
|
350
|
+
/* Not sure yet: it wobbled, it needed a second go, or the answer is soft. */
|
|
351
|
+
--doubt: #e8b85c;
|
|
352
|
+
|
|
353
|
+
--line: rgba(255, 255, 255, 0.055);
|
|
355
354
|
--line-firm: rgba(255, 255, 255, 0.17);
|
|
356
355
|
--sheen: rgba(255, 255, 255, 0.03);
|
|
357
356
|
--shadow: rgba(0, 0, 0, 0.5);
|
|
@@ -359,16 +358,25 @@ const STYLE = `
|
|
|
359
358
|
/* Activity, not state. Something is happening — the shutter, a running dot,
|
|
360
359
|
a live segment of the meter. Deliberately colourless: "happening" is not
|
|
361
360
|
news, and the moment it has news it has a colour instead. */
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
361
|
+
/* The brand mark, the thing that is running, and the check that wants a person
|
|
362
|
+
are all this one colour — so on a page that is otherwise grey, this means
|
|
363
|
+
"you". A clear sky blue: bright enough to find at a glance, cool enough not
|
|
364
|
+
to be mistaken for the amber that means something moved. */
|
|
365
|
+
--accent: #4fb3f0;
|
|
366
|
+
|
|
367
|
+
/* Held is green, and visibly so.
|
|
368
|
+
It was a grey at first, on the argument that a check which passed has
|
|
369
|
+
nothing to say. True of a whole run — the verdict line and the meter are
|
|
370
|
+
quiet when all is well — but wrong of the individual check: a person
|
|
371
|
+
scanning a list of thirty things wants to SEE that twenty-nine of them
|
|
372
|
+
held, not infer it from an absence. Green passed, red broke, amber not
|
|
373
|
+
sure. Grey is only for what never ran. */
|
|
374
|
+
--held: #25d366;
|
|
366
375
|
|
|
367
376
|
/* The three that have something to say. Pure, because they are alone. */
|
|
368
377
|
--broke: #ff4438;
|
|
369
|
-
/*
|
|
370
|
-
|
|
371
|
-
--wait: #3fb9a6;
|
|
378
|
+
/* Waiting for a person is the brand colour itself. */
|
|
379
|
+
--wait: #4fb3f0;
|
|
372
380
|
--moved: #ffc24d;
|
|
373
381
|
|
|
374
382
|
/* Nobody ran it. Barely there on purpose. */
|
|
@@ -468,17 +476,17 @@ img { display: block; }
|
|
|
468
476
|
button { font: inherit; color: inherit; }
|
|
469
477
|
.glyph { width: 16px; height: 16px; flex: 0 0 auto; }
|
|
470
478
|
|
|
471
|
-
/* One light
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
479
|
+
/* One light above, one shadow below. Nothing else.
|
|
480
|
+
There was a five-bloom mesh here with warm and coloured washes in it; on a
|
|
481
|
+
neutral black ground it read as a stain rather than as depth. What is left is
|
|
482
|
+
the least that stops a page of greys looking like flat paint, plus the tint of
|
|
483
|
+
the worst thing that has happened — which on a run where everything held is no
|
|
484
|
+
colour at all. */
|
|
476
485
|
.aura {
|
|
477
486
|
position: fixed; inset: 0; pointer-events: none; z-index: 0;
|
|
478
487
|
background:
|
|
479
|
-
radial-gradient(
|
|
480
|
-
radial-gradient(
|
|
481
|
-
radial-gradient(120% 62% at 50% 114%, var(--shadow), transparent 64%);
|
|
488
|
+
radial-gradient(126% 42% at 50% -12%, color-mix(in srgb, var(--tint) 12%, transparent), transparent 70%),
|
|
489
|
+
radial-gradient(128% 56% at 50% 116%, var(--shadow), transparent 62%);
|
|
482
490
|
transition: background 620ms var(--ease);
|
|
483
491
|
}
|
|
484
492
|
|
|
@@ -845,6 +853,7 @@ button { font: inherit; color: inherit; }
|
|
|
845
853
|
transition: background var(--calm) var(--ease), box-shadow var(--calm) var(--ease);
|
|
846
854
|
}
|
|
847
855
|
.item.held .dot { width: 7px; height: 7px; background: var(--held); box-shadow: none; }
|
|
856
|
+
.item.held .rverdict { color: color-mix(in srgb, var(--held) 78%, var(--faint)); }
|
|
848
857
|
.item.wait .dot { background: var(--wait); box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--wait) 42%, transparent), 0 0 0 4px color-mix(in srgb, var(--wait) 12%, transparent); }
|
|
849
858
|
.item.moved .dot { background: var(--moved); box-shadow: 0 0 0 4px color-mix(in srgb, var(--moved) 17%, transparent); }
|
|
850
859
|
.item.broke .dot {
|
|
@@ -958,7 +967,7 @@ button { font: inherit; color: inherit; }
|
|
|
958
967
|
padding: 4px 0 4px 11px;
|
|
959
968
|
box-shadow: inset 1px 0 0 var(--line);
|
|
960
969
|
}
|
|
961
|
-
.step.warn { box-shadow: inset 1.5px 0 0 var(--
|
|
970
|
+
.step.warn { box-shadow: inset 1.5px 0 0 var(--doubt); }
|
|
962
971
|
.step.bad { box-shadow: inset 1.5px 0 0 var(--broke); }
|
|
963
972
|
.step.skipped, .step.ended { box-shadow: inset 1px 0 0 var(--resting); }
|
|
964
973
|
.step.running { box-shadow: inset 1.5px 0 0 color-mix(in srgb, var(--accent) 66%, transparent); }
|
|
@@ -971,18 +980,34 @@ button { font: inherit; color: inherit; }
|
|
|
971
980
|
ticking off rather than a table that appears when it is all over. A step
|
|
972
981
|
still running carries a soft dot that breathes — an honest "this one is
|
|
973
982
|
happening", with nothing that spins on after the step it belongs to.
|
|
974
|
-
A
|
|
975
|
-
|
|
983
|
+
A check that passed wears a filled green disc with a white tick in it. The
|
|
984
|
+
first version drew a bare grey tick on the argument that twenty green ticks
|
|
985
|
+
is a Christmas tree — true of a colour used for decoration, wrong here: a
|
|
986
|
+
person scanning the list wants to SEE that these held, and a disc reads as
|
|
987
|
+
done from further away than a hairline tick ever will. */
|
|
976
988
|
.stepmark {
|
|
977
989
|
/* Level with the first line of the label, never floating between the label
|
|
978
990
|
and the number under it. */
|
|
979
|
-
flex: 0 0
|
|
991
|
+
flex: 0 0 16px; align-self: flex-start;
|
|
980
992
|
display: flex; align-items: center; justify-content: center;
|
|
981
|
-
height: 18px; color: var(--faint);
|
|
993
|
+
width: 16px; height: 18px; color: var(--faint);
|
|
982
994
|
transition: color var(--calm) var(--ease);
|
|
983
995
|
}
|
|
984
996
|
.stepmark .glyph { width: 13px; height: 13px; }
|
|
985
|
-
|
|
997
|
+
|
|
998
|
+
/* The disc. Only for a settled verdict — a step still running keeps its bare
|
|
999
|
+
mark, because a filled circle would say it had finished. */
|
|
1000
|
+
.step.ok .stepmark, .step.warn .stepmark, .step.bad .stepmark {
|
|
1001
|
+
width: 16px; height: 16px; margin-top: 1px;
|
|
1002
|
+
border-radius: 50%;
|
|
1003
|
+
color: #0b1a10;
|
|
1004
|
+
}
|
|
1005
|
+
.step.ok .stepmark .glyph, .step.warn .stepmark .glyph, .step.bad .stepmark .glyph {
|
|
1006
|
+
width: 11px; height: 11px;
|
|
1007
|
+
}
|
|
1008
|
+
.step.ok .stepmark { background: var(--held); color: #06230f; }
|
|
1009
|
+
.step.warn .stepmark { background: var(--doubt); color: #2a1c00; }
|
|
1010
|
+
.step.bad .stepmark { background: var(--broke); color: #2a0703; }
|
|
986
1011
|
.step.skipped .stepmark, .step.ended .stepmark { color: var(--faintest); }
|
|
987
1012
|
.step.running .stepmark { color: var(--accent); }
|
|
988
1013
|
.step.running .stepmark .glyph { animation: markpulse 1.6s var(--ease) infinite; }
|
|
@@ -1020,8 +1045,8 @@ button { font: inherit; color: inherit; }
|
|
|
1020
1045
|
be read from across the row: its words go to full strength, its number takes
|
|
1021
1046
|
the colour of what happened. */
|
|
1022
1047
|
.step.warn .stepwhat, .step.bad .stepwhat { color: var(--ink); font-weight: 560; }
|
|
1023
|
-
.step.warn .stepno, .step.warn .stepnum
|
|
1024
|
-
.step.bad .stepno, .step.bad .stepnum
|
|
1048
|
+
.step.warn .stepno, .step.warn .stepnum { color: var(--doubt); }
|
|
1049
|
+
.step.bad .stepno, .step.bad .stepnum { color: var(--broke); }
|
|
1025
1050
|
.step.skipped .stepwhat, .step.skipped .stepno, .step.skipped .stepnum,
|
|
1026
1051
|
.step.ended .stepwhat, .step.ended .stepno, .step.ended .stepnum { color: var(--faintest); }
|
|
1027
1052
|
|
|
@@ -1462,7 +1487,7 @@ const SCRIPT = `
|
|
|
1462
1487
|
return 'broken again';
|
|
1463
1488
|
}
|
|
1464
1489
|
switch (status) {
|
|
1465
|
-
case 'passed': return '
|
|
1490
|
+
case 'passed': return 'unchanged';
|
|
1466
1491
|
case 'changed':
|
|
1467
1492
|
var n = ev.diffPixels || 0;
|
|
1468
1493
|
return commas(n) + ' ' + plural(n, 'pixel', 'pixels') + ' moved';
|
|
@@ -2223,10 +2248,13 @@ const SCRIPT = `
|
|
|
2223
2248
|
// scrolls for themselves. Nothing is more annoying than a list that yanks
|
|
2224
2249
|
// itself back while you are reading it.
|
|
2225
2250
|
var following = true;
|
|
2251
|
+
var runOver = false;
|
|
2226
2252
|
function stopFollowing() {
|
|
2227
2253
|
if (!following) return;
|
|
2228
2254
|
following = false;
|
|
2229
|
-
|
|
2255
|
+
// Nothing to follow once the run has ended, so the pill would be an offer
|
|
2256
|
+
// to do nothing — and it sits over the list while it makes it.
|
|
2257
|
+
ui.follow.hidden = runOver;
|
|
2230
2258
|
}
|
|
2231
2259
|
['wheel', 'touchmove', 'keydown'].forEach(function (name) {
|
|
2232
2260
|
ui.scroll.addEventListener(name, stopFollowing, { passive: true });
|
|
@@ -3021,6 +3049,7 @@ const SCRIPT = `
|
|
|
3021
3049
|
syncWork(order[q]);
|
|
3022
3050
|
}
|
|
3023
3051
|
runningItem = null;
|
|
3052
|
+
runOver = true;
|
|
3024
3053
|
ui.follow.hidden = true;
|
|
3025
3054
|
scanning(false);
|
|
3026
3055
|
var summary = ev.summary || null;
|