@cplieger/web-terminal-ui 4.7.1 → 4.8.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/css/00-tokens.css CHANGED
@@ -45,10 +45,24 @@
45
45
  ); /* subtle edge: the fill lightened + slightly desaturated, matching the inactive chip's faint border rather than a vivid accent outline */
46
46
 
47
47
  /* Status-dot palette (the activity vocabulary painted in 30-tabs.css; the
48
- switcher notification dot reuses the same rules). The working and input
49
- rings each derive from their own token in 30-tabs.css, so one override
50
- recolors dot + ring together. */
51
- --status-working: #d9f99d; /* agent working (pulses): pale lime */
48
+ switcher notification dot reuses the same rules). The working wave and the
49
+ input ring each derive from their own token in 30-tabs.css, so one override
50
+ recolors dot + surround together.
51
+
52
+ The trio is the conventional progress scheme — blue in flight, green
53
+ finished, orange waiting on you — which is why working is a LITERAL copy of
54
+ the default --accent blue rather than var(--accent): a consumer that
55
+ rethemes its brand accent to (say) a red should not have its "working" dot
56
+ follow, and a status vocabulary that shifts hue per consumer stops being a
57
+ vocabulary. The value being equal to the default accent is a deliberate
58
+ coincidence of taste, not a dependency.
59
+
60
+ Working shares the accent's hue family, so on the ACTIVE tab it sits on an
61
+ accent-derived fill (--tab-active-bg): measured 5.2:1 against it, and 8.0:1
62
+ against the bar, both clear of the 3:1 non-text minimum. Hue still never
63
+ carries state on its own (the wave's motion, and the donut it becomes under
64
+ prefers-reduced-motion, are the non-hue cues per WCAG 1.4.1). */
65
+ --status-working: #52a9fe; /* agent working (waves): the same blue as the default --accent */
52
66
  --status-done: #22c55e; /* turn finished: solid green */
53
67
  --status-input: #fb923c; /* action required: solid orange + ring */
54
68
  --radius: 0.75rem;
package/css/30-tabs.css CHANGED
@@ -555,17 +555,18 @@
555
555
  /* Agent working: a solid disc with two animated cues (the status-badge
556
556
  "processing" idiom — Ant Design's processing badge, the classic CSS
557
557
  live-indicator ripple): the disc keeps full opacity and gently GLOWS (its
558
- colors brighten mid-beat), while a ::after ring RIPPLES outward off the
558
+ colors brighten mid-beat), while a ::after WAVE travels outward off the
559
559
  disc's edge and dissolves, re-emitted each cycle. No static ring at rest —
560
- the disc + live ripple ARE the working read; input freezes that exact ping
561
- at its birth frame (see input below), so motion separates working from
562
- input, and the ring separates both from done's bare disc. The old
563
- single-layer pulse faded disc + ring together (opacity on the element),
564
- which read as a blink and coupled the layers — an element's opacity always
565
- multiplies onto its pseudos, so true independence requires animating disc
566
- COLORS, not element opacity. Server-side this is driven by the OSC 9;4
567
- progress signal, so it tracks agent activity, not the user typing at the
568
- prompt. */
560
+ the disc + live wave ARE the working read. The wave's soft feathered edge is
561
+ the second axis that separates working from input: input is a HARD-edged 2px
562
+ ring, held still (see input below), so the pair differs by both motion and
563
+ edge quality, and the presence of any surround separates both from done's
564
+ bare disc. The old single-layer pulse faded disc + ring together (opacity on
565
+ the element), which read as a blink and coupled the layers — an element's
566
+ opacity always multiplies onto its pseudos, so true independence requires
567
+ animating disc COLORS, not element opacity. Server-side this is driven by the
568
+ OSC 9;4 progress signal, so it tracks agent activity, not the user typing at
569
+ the prompt. */
569
570
  :where(.wt-root) .wt-status-dot[data-status="working"] {
570
571
  position: relative;
571
572
  background: var(--status-working);
@@ -573,40 +574,58 @@
573
574
  animation: wt-working-glow 1.2s ease-in-out infinite;
574
575
  }
575
576
 
576
- /* The ripple layer: at scale(1) its 2px border hugs the disc's edge (inset
577
- -2px, the 9->13px band), then the wave detaches, expanding and fading.
578
- transform + opacity only, so it composites without repaints. It exists only
579
- in the working state — no stale layer to pin when a dot changes status.
580
- (The switcher notification dot never shows working — input/done only — so
581
- its position: absolute placement never meets this rule's
582
- position: relative.) */
577
+ /* The ripple layer: a feathered radial BAND (not a hairline border) that starts
578
+ hugging the disc's edge (inset -2px, the 9->13px box) and then travels
579
+ outward, dissolving. The band is painted as a radial-gradient — soft on both
580
+ edges, so it reads as a wave of light leaving the disc rather than a ring
581
+ outline shrinking in opacity; scaling the layer scales the feather with it,
582
+ which is what makes the wave visibly dissipate as it grows. transform +
583
+ opacity only, so it composites without repaints (a gradient background is
584
+ rasterized once, unlike an animated box-shadow spread, which repaints every
585
+ frame). It exists only in the working state — no stale layer to pin when a
586
+ dot changes status. (The switcher notification dot never shows working —
587
+ input/done only — so its position: absolute placement never meets this
588
+ rule's position: relative.)
589
+
590
+ The final scale is deliberately capped at today's 27px footprint: the mobile
591
+ overview row (.wt-switcher-row-select) clips at overflow: hidden 12.5px from
592
+ the dot's center, and a wave wider than ~25px picks up a visible straight
593
+ edge there. Visibility comes from the band's weight and timing, not size. */
583
594
  :where(.wt-root) .wt-status-dot[data-status="working"]::after {
584
595
  content: "";
585
596
  position: absolute;
586
597
  inset: -2px;
587
- border: 2px solid var(--status-working);
588
598
  border-radius: 50%;
589
- animation: wt-working-ripple 1.2s ease-in-out infinite;
599
+ background: radial-gradient(
600
+ closest-side,
601
+ transparent 44%,
602
+ var(--status-working) 76%,
603
+ transparent 100%
604
+ );
605
+ animation: wt-working-ripple 1.2s linear infinite;
590
606
  }
591
607
 
592
608
  /* Agent done / ready: solid disc (--status-done), no motion. The default green
593
- matches Kiro IDE's "Ready" (#22c55e), darker and more saturated than
594
- working's default pale lime so the two states differ by lightness as well as
595
- motion. */
609
+ matches Kiro IDE's "Ready" (#22c55e). It sits at almost exactly working's
610
+ default lightness now that working is the accent blue (both ~72% OKLab L), so
611
+ the pair separates by hue plus the wave's motion rather than by lightness —
612
+ and under prefers-reduced-motion by shape, since working becomes a donut. */
596
613
  :where(.wt-root) .wt-status-dot[data-status="done"] {
597
614
  background: var(--status-done);
598
615
  border-color: var(--status-done);
599
616
  }
600
617
 
601
- /* User action required (permission / approval): the working ping frozen at
602
- its birth frame — solid dot + the same 2px ring at the ripple's 30% birth
603
- opacity, motionless (--status-input; the default orange is shifted away
604
- from the old amber so it separates from both default-lime working and green
605
- done). The stillness is the message: the agent's turn is mid-flight but
606
- blocked on you. The ring keeps it distinct from done's bare disc without
607
- leaning on hue; it derives from the same token, so a theme override
608
- recolors dot + ring together. It persists until you act on or switch into
609
- the tab. */
618
+ /* User action required (permission / approval): a solid dot inside a static,
619
+ HARD-edged 2px ring, motionless (--status-input; the default orange is
620
+ shifted away from the old amber so it separates from both blue working and
621
+ green done). The stillness plus the hard edge is the message: the
622
+ agent's turn is mid-flight but blocked on you, and the surround is a fixed
623
+ outline rather than working's soft travelling wave. (Before the wave landed,
624
+ working's ripple was a 2px ring too and this state was literally that ping
625
+ frozen at its birth frame; the two now differ by edge quality as well as
626
+ motion, which reads more distinctly at 9px.) The ring derives from the same
627
+ token as the dot, so a theme override recolors both together. It persists
628
+ until you act on or switch into the tab. */
610
629
  :where(.wt-root) .wt-status-dot[data-status="input"] {
611
630
  background: var(--status-input);
612
631
  border-color: var(--status-input);
@@ -641,13 +660,33 @@
641
660
  }
642
661
 
643
662
  /* The ripple wave: one emission per cycle, launching off the disc's edge and
644
- dissolving by ~2.1x. The 0.3 birth opacity matches the input ring's frozen
645
- weight, so a live ping and a stuck one read as the same object — subtle
646
- against the bar now that no static ring doubles it at birth. */
663
+ dissolving by ~2.1x. Two things are load-bearing and were both wrong in the
664
+ first version (the wave was there but nobody could see it):
665
+
666
+ - The travel is LINEAR while the opacity is shaped separately. An ease-in-out
667
+ shared by both properties spends the slow opening on top of the disc, where
668
+ the wave is indistinguishable from it, and then accelerates away exactly as
669
+ the opacity collapses — so the wave was only ever visible while it was
670
+ still merged with the disc. Constant-speed travel plus its own opacity
671
+ envelope keeps the wave legible across most of the cycle.
672
+ - The wave is BRIGHT at birth (0.95 after a 12% ramp-in, not 0.3) and holds
673
+ usable weight through the travel. The ramp-in is short enough to read as
674
+ instant while stopping the emission from popping in.
675
+
676
+ The disc's own glow shares this 1.2s period so the two cues stay phase-locked;
677
+ change both or neither. */
647
678
  @keyframes wt-working-ripple {
648
679
  0% {
649
680
  transform: scale(1);
650
- opacity: 0.3;
681
+ opacity: 0.15;
682
+ }
683
+
684
+ 12% {
685
+ opacity: 0.95;
686
+ }
687
+
688
+ 70% {
689
+ opacity: 0.45;
651
690
  }
652
691
 
653
692
  100% {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/web-terminal-ui",
3
- "version": "4.7.1",
3
+ "version": "4.8.0",
4
4
  "type": "module",
5
5
  "description": "Reference touch-first browser UI for @cplieger/web-terminal-engine: a composable kernel + feature modules (tabs, activity monitor, mobile toolbar, context menu, IME, predictive echo, viewport handling) and CSS. One createTerminal() call.",
6
6
  "license": "GPL-3.0-or-later",