Overflow Tap: the tile says what Tap is on; the corner dot says how it went

The "TAP IS ON · since …" line (and its SWITCHING / NOT SWITCHED forms, and
the red error line under the destination) are gone from the tile. A tile
shows the keyword Tap is on, its destination, the groups moved, the slide
chip and the trail; status is the corner dot's job, as everywhere else in
ProdDash.

The dot now follows the retries honestly: a switch Overflow refuses is
retried three times, ten seconds apart, with the dot amber ("retrying (n of
4 attempts failed)") while the module is still fixing it itself; only when
the attempts are spent does the dot go red with the reason on hover, and the
alert fire if it is switched on. The budget counts refusals in a row across
keywords, not per keyword — a service keeps bringing new keywords, and each
one restarting a fresh budget meant a dead Overflow never turned the dot
red. The alert is raised once per outage, and a success clears the streak.
Verified against the mock with --fail-switch: amber through three refusals,
red on the fourth with one alert, later refusals stay red without
re-alerting, and green again on the first success once the mock is back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-10-25 13:57:45 -04:00
co-authored by Claude Fable 5.1
parent 227e3bb846
commit 6fffefc6c0
4 changed files with 61 additions and 67 deletions
+15 -26
View File
@@ -1,10 +1,12 @@
/* Overflow Tap — the tile.
*
* One fact matters across a booth: which keyword Tap is on right now, and
* whether the switch actually landed. That is the big line. Under it, the
* destination it maps to and the groups it moved; a small line says whether
* the slide on screen carries a keyword at all; and the last four switches
* sit at the bottom with their times, so a tech can see the service's trail.
* One fact matters across a booth: which keyword Tap is on right now. That
* is the big line. Under it, the destination it maps to and the groups it
* moved; a small line says whether the slide on screen carries a keyword at
* all; and the last four switches sit at the bottom with their times, so a
* tech can see the service's trail. Whether the last switch landed is the
* corner dot's to say — red when Overflow refused it and the module's own
* retries did not fix that — never a line of text in the tile.
*
* Everything comes from the server's `state` stream. The tile never talks to
* ProPresenter or Overflow itself.
@@ -74,34 +76,21 @@ export default function create({ root, moduleApi }) {
box.append(el('div', 'ot-offline', why));
}
// The keyword Tap is on, its destination and the groups moved. No status
// words here: how the switch went is the corner dot's job (its hover
// carries the reason), and the trail's ✓ / ✕ keeps the record. The
// keyword's colour follows along — dimmed while a switch is in flight,
// red once Overflow has refused it for good.
const main = el('div', 'ot-main');
if (cur) {
// The label carries the state and, when there is one, the moment it
// became true; the small lines under the destination are the groups
// moved — or, when the switch failed, what went wrong, in their place
// (a tile has room for one or the other).
let label = `TAP IS ON · since ${clock(cur.at)}`;
let tone = 'ok';
let note = '';
if (cur.pending) {
label = cur.attempts > 1 ? `SWITCHING · attempt ${cur.attempts}` : 'SWITCHING';
tone = 'muted';
} else if (!cur.ok) {
label = `NOT SWITCHED · ${clock(cur.at)}`;
tone = 'alert';
note = cur.error || '';
}
main.dataset.tone = tone;
main.append(el('div', 'ot-label', label));
main.dataset.tone = cur.pending ? 'muted' : (cur.ok ? 'ok' : 'alert');
main.append(el('div', 'ot-keyword', cur.keyword));
if (on('showDestination')) main.append(el('div', 'ot-dest', cur.destination || ''));
if (note) main.append(el('div', 'ot-error', note));
else if (on('showGroups') && cur.groups && cur.groups.length) main.append(el('div', 'ot-groups', cur.groups.join(' · ')));
if (on('showGroups') && cur.groups && cur.groups.length) main.append(el('div', 'ot-groups', cur.groups.join(' · ')));
} else {
main.dataset.tone = 'muted';
main.append(el('div', 'ot-label', 'NO KEYWORD YET'));
main.append(el('div', 'ot-keyword is-none', '—'));
main.append(el('div', 'ot-dest', 'Tap stays where Overflow left it'));
main.append(el('div', 'ot-dest', 'No keyword sent yet'));
}
box.append(main);