From 8fcfdb1e6e4a9273289da45d215176d4910c129a Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Mon, 10 Aug 2026 04:00:05 +0200 Subject: [PATCH] feat(tabs): orbit the working ring around a busy session tab's dot The desktop home rail and the phone overview both draw a spinning `tab-load-spin` ring around their green dot while a session works; the tab strip itself only pulsed. Same ring on the tab dot now, so "working" reads identically on every surface. Drawn as a ::after border circle rather than a halo: the skin block sets `box-shadow: none` on .tab-status.busy to keep tab dots quiet and outranks any plain class rule, and a pseudo-element sidesteps that without reintroducing the glow. It is absolutely positioned, so it never widens the tab or shifts the label, and it is disabled under prefers-reduced-motion. Phones keep their existing tell (a 9px dot with a glow) and suppress the ring: a 15px ring inside a 32px tab would sit on top of the tab name. Co-Authored-By: Claude Opus 5 (1M context) --- src/web/public/mobile.css | 7 +++++++ src/web/public/styles.css | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index 4f09065d..2c0cf791 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -680,6 +680,13 @@ html.mobile-init .file-browser-panel { box-shadow: 0 0 8px 2px color-mix(in srgb, var(--green) 55%, transparent) !important; } + /* No orbiting ring on phone tabs (styles.css draws one on desktop/tablet): + the dot is already enlarged to 9px with a glow here, and a 15px ring in a + 32px tab would sit on top of the tab name. The glow is the phone's tell. */ + .session-tab .tab-status.busy::after { + display: none; + } + /* Truncate tab names more aggressively on mobile */ .session-tab .tab-name { max-width: 50px; diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 3df50b85..b36f4f54 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -1316,11 +1316,30 @@ html[data-line-anim="packet"] .connection-line.line-enter { .session-tab .tab-status.idle { background: var(--green); } .session-tab .tab-status.busy { + position: relative; background: var(--green); animation: pulse 1.5s infinite; /* Performance: GPU acceleration for animation */ will-change: opacity; } +/* Orbiting ring on a WORKING tab: the same `tab-load-spin` circle the desktop + home rail (.home-sessions-dot--working) and the phone overview + (.mobile-overview-dot--working) draw, so "working" reads identically on every + surface. Pseudo-element on purpose — the skin block below sets + `box-shadow: none` on .tab-status.busy to keep tab dots halo-free, and it + outranks any plain class rule here; a ring drawn with a border is untouched by + that and keeps the tabs quiet while still showing motion. Absolutely + positioned, so it never widens the tab or shifts the label. */ +.session-tab .tab-status.busy::after { + content: ''; + position: absolute; + inset: -3px; + border: 1.5px solid color-mix(in srgb, var(--green) 25%, transparent); + border-top-color: var(--green); + border-radius: 50%; + animation: tab-load-spin 0.7s linear infinite; + pointer-events: none; +} .session-tab .tab-status.error { background: var(--red); } .session-tab .tab-status.ended { background: var(--text-muted); opacity: 0.5; } @@ -14298,6 +14317,7 @@ html[data-skin="daylight-blue"] .welcome-btn-tunnel.active:hover { } @media (prefers-reduced-motion: reduce) { + .session-tab .tab-status.busy::after, .home-sessions-row, .home-sessions-dot, .home-sessions-dot::after {