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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. 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.0",
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",
@@ -333,25 +333,24 @@ const STYLE = `
333
333
  :root {
334
334
  color-scheme: dark;
335
335
 
336
- /* Borrowed simplicity, not a borrowed skin.
337
- The app this sits beside is a flat neutral grey with hairline borders and
338
- small radii, and copying that wholesale made this look like a piece of it
339
- rather than a thing of its own. So: the same restraint, a different surface.
340
- A degree or two cooler and a shade deeper, so the panel reads as the quiet
341
- instrument next to the window rather than as more of the window. */
342
- --ground: #14161a;
343
- --lift: #1b1e23;
344
- --card: rgba(200, 220, 255, 0.035);
345
- --card-hover: rgba(200, 220, 255, 0.06);
346
- --well: #0f1114;
347
- --glass: rgba(20, 22, 26, 0.9);
348
-
349
- --ink: #e8eaee;
350
- --soft: #aeb3bb;
351
- --faint: #8b9098;
352
- --faintest: #6b7079;
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
- --accent: #9a9a9a;
363
-
364
- /* Held is a grey. It is the whole idea: nothing to say, so nothing is said. */
365
- --held: #6b7079;
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
- /* Not the blue next door — a teal, so "this one wants you" is unmistakably
370
- this panel talking and not the app it is watching. */
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 from above, one shadow below, and a thin sheen along the very top
472
- edge of the window. Three flat washes, no pattern and no edge you could point
473
- at it is what stops a page of greys reading as paint. The light takes the
474
- colour of the worst thing that has happened, which on a run where everything
475
- held is no colour at all: only a little more light. */
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(132% 44% at 50% -12%, color-mix(in srgb, var(--tint) 14%, transparent), transparent 68%),
480
- radial-gradient(74% 26% at 50% -4%, var(--sheen), transparent 76%),
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(--moved); }
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 tick is grey. Twenty grey ticks and one red cross is a page you can read
975
- in a glance; twenty green ticks and one red cross is a Christmas tree. */
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 13px; align-self: flex-start;
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
- .step.ok .stepmark { color: var(--held); }
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, .step.warn .stepmark { color: var(--moved); }
1024
- .step.bad .stepno, .step.bad .stepnum, .step.bad .stepmark { color: var(--broke); }
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 'matches';
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
- ui.follow.hidden = false;
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;