bootstrap
Adom Hydrogen (platform base)
Public Made by Adomby adom
Layer Hydrogen's workspace onto a standard Adom workstation.
← Commit history
0.5.53: widget helper keeps stale numbers dimmed while a token refreshes
3 files changed
+22−10
bin/hydrogen-widget-lib.py+18−6@@ -23,6 +23,7 @@ html,body{margin:0;height:100%%;background:transparent;color:#e6ebef;font-family .v{font-variant-numeric:tabular-nums;text-align:right} .big{font-weight:600;font-variant-numeric:tabular-nums} .err{color:#f0a060}+#w.stale{opacity:.45} @media (max-height:33px){ .w{font-size:clamp(7px,37vh,12px);line-height:1.2} .lbl{font-size:clamp(6px,30vh,9px)}.bar{width:clamp(24px,130vh,40px);height:clamp(4px,20vh,6px)}.v{min-width:2.4em}@@ -47,7 +48,9 @@ html,body{margin:0;height:100%%;background:transparent;color:#e6ebef;font-family <script> const R=%(refresh_ms)d; function bar(l,p,sev){const c=(sev==='critical'||p>=90)?'hot':(sev==='warning'||p>=70)?'warn':'';return `<span class="row"><span class="lbl">${l}</span><span class="bar ${c}"><i style="width:${Math.max(0,Math.min(100,p))}%%"></i></span><span class="v">${Math.round(p)}%%</span></span>`;}-function render(d){document.getElementById('w').innerHTML=%(render_js)s;postWidth();}+/* d.stale = the last good data while the provider is unreachable or the token is between refreshes:+ drawn dimmed, the reason in the tooltip, never an error where numbers were. */+function render(d0){const d=d0&&d0.stale?d0.stale:d0;const el=document.getElementById('w');el.classList.toggle('stale',!!(d0&&d0.stale));el.title=(d0&&d0.stale)?String(d0.error||''):'';el.innerHTML=%(render_js)s;postWidth();} /* THE WIDGET DECIDES ITS WIDTH: ask the host for the intrinsic width of what is shown in the current layout (tall rows are narrow, the compact row is wide). Measured on a hidden clone laid out at max-content, so the slot's current width never feeds back into the answer (rows stretch to@@ -71,19 +74,28 @@ def serve(title, data_fn, render_js, refresh_ms=60000, mark=None): port = int(os.environ.get('HYDROGEN_WIDGET_PORT') or (sys.argv[sys.argv.index('--port')+1] if '--port' in sys.argv else 8930)) # One upstream call per interval at most, however often the page asks; on an upstream # error the interval doubles up to 15 minutes and returns to normal on the next success.- cache = {'t': 0, 'v': {'error': 'starting'}, 'wait': refresh_ms/1000}+ cache = {'t': 0, 'v': {'error': 'starting'}, 'wait': refresh_ms/1000, 'good': None} lock = threading.Lock() def data(): with lock: if time.time() - cache['t'] >= cache['wait'] - 1: try: cache['v'] = data_fn(); cache['wait'] = refresh_ms/1000+ if not cache['v'].get('error'): cache['good'] = cache['v'] except Exception as e:- # A 401/403 from the provider is an expired sign-in, so say that instead of- # printing the HTTP error (ConfRoomROG 2026-09-14: "HTTP Error 401: Unauthorized").+ # A 401/403 is almost always an EXPIRED access token, not a missing sign-in:+ # Claude Code and Codex refresh their own tokens the next time they run, and+ # the widget re-reads the file. Never tell a signed-in user to sign in again+ # (AdomLapper 2026-09-14: "sign in to Claude again" while John was chatting).+ # Say what is true, keep the last good numbers on screen (dimmed), and retry+ # every minute instead of backing off; other errors still back off. code = getattr(e, 'code', None)- msg = ('sign in to %s again' % title.replace(' usage', '')) if code in (401, 403) else str(e)[:120]- cache['v'] = {'error': msg}; cache['wait'] = min(900, max(refresh_ms/1000, cache['wait']) * 2)+ if code in (401, 403):+ msg = '%s token expired, waiting for %s to refresh it' % (title.replace(' usage', ''), 'Claude Code' if 'Claude' in title else 'Codex')+ cache['wait'] = 60+ else:+ msg = str(e)[:120]; cache['wait'] = min(900, max(refresh_ms/1000, cache['wait']) * 2)+ cache['v'] = {'error': msg, 'stale': cache['good']} if cache['good'] else {'error': msg} cache['t'] = time.time() return cache['v'] class H(BaseHTTPRequestHandler):
package.json+2−2@@ -4,7 +4,7 @@ "type": "bootstrap", "title": "Adom Hydrogen (platform base)", "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",- "version": "0.5.52",+ "version": "0.5.53", "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.", "org": "adom", "tags": [@@ -33,4 +33,4 @@ "hydrogen editor configuration", "where do hydrogen skills come from" ]-}+}
page.json+2−2@@ -4,7 +4,7 @@ "type": "bootstrap", "title": "Adom Hydrogen (platform base)", "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",- "version": "0.5.52",+ "version": "0.5.53", "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.", "org": "adom", "tags": [@@ -49,4 +49,4 @@ "hydrogen editor configuration", "where do hydrogen skills come from" ]-}+}