From 1558a7fa075b53379f2874c2352c03d117d585ac Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Wed, 23 Sep 2026 20:03:32 -0700 Subject: [PATCH] fix(desk): the heid code-review and bug-hunt panels on r2b merge 2, folded The bug hunt (4/4) and code review (4/4) were both clean on mechanism. Their shared catch was the one-sided minute check. Dates: - The date filters never raise. One clock outside the calendar's range 500'd the Desk for every booth, because every row renders in one response. An unrenderable date now renders nothing. - "Updated" shows whenever it differs from "created" by a minute or more, either way. Copied content is often older than its folder. - A clock ahead of now shows its date, never "just now". - A day is 24h ("1d ago" never appeared). The row: - The controls are last in the markup, so the booth's name comes first in tab order and wipe last. The cluster is placed over the strip from the row's box. The theme: - A choice made in one tab moves the Booth's other open tabs. - The theme mark goes only on ask fragments the embed mounted. Tests, strengthened after the code review: - the pill is visible at rest; - keyboard focus reveals the controls; - the controls act with scripts off; - Reveal all reaches the doc page; - the high-contrast check reads tokens that actually differ; - the art-light extras are written from SVOS, not derived from the copies; - two overstated mutation rows are replaced (one was a runtime no-op, one went red through a syntax error). Contract amended. r2b.toml 55/55 proved. 799 passed. --- booth/app.py | 33 ++++- booth/static/embed.js | 13 +- booth/templates/_dates.html | 22 ++- booth/templates/base.html | 37 +++-- booth/templates/index.html | 48 +++--- .../r2b_desk_reveal_theme.contract.md | 56 +++++-- tests/mutations/r2b.toml | 140 +++++++++++++++--- tests/test_flow.py | 57 ++++++- tests/test_flow_browser.py | 124 +++++++++++++++- 9 files changed, 446 insertions(+), 84 deletions(-) diff --git a/booth/app.py b/booth/app.py index 8937cfb..a0311e7 100644 --- a/booth/app.py +++ b/booth/app.py @@ -261,29 +261,50 @@ def human_dur(seconds: float) -> str: _MONTHS = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") +# NEVER RAISE: the Desk renders every row in ONE response, and a clock a fleet +# session can set (content mtimes) or a filesystem can report (birth time) may +# lie outside the calendar's range — which raised through the template and +# 500'd the index for every booth (heid bug-hunt, 4/4). A date that cannot be +# rendered is "" and the macro renders nothing, as for an unknown birth time. +_BAD_DATE = (OverflowError, OSError, ValueError) + + def date_iso(epoch: float) -> str: - return datetime.fromtimestamp(epoch, tz=timezone.utc).isoformat(timespec="seconds") + try: + return datetime.fromtimestamp(epoch, tz=timezone.utc).isoformat(timespec="seconds") + except _BAD_DATE: + return "" def date_stamp(epoch: float) -> str: - return time.strftime("%Y-%m-%d %H:%M", time.localtime(epoch)) + try: + return time.strftime("%Y-%m-%d %H:%M", time.localtime(epoch)) + except _BAD_DATE: + return "" def date_day(epoch: float, now: float | None = None) -> str: - """'12 Sep', with the year only when it is not this year's.""" - t, n = time.localtime(epoch), time.localtime(time.time() if now is None else now) + """'12 Sep', with the year only when it is not this year's; '' when the + calendar cannot hold it.""" + try: + t, n = time.localtime(epoch), time.localtime(time.time() if now is None else now) + except _BAD_DATE: + return "" day = f"{t.tm_mday} {_MONTHS[t.tm_mon - 1]}" return day if t.tm_year == n.tm_year else f"{day} {t.tm_year}" def date_ago(seconds: float) -> str: - """An age in its largest whole unit: 'just now', '12m ago', '5h ago', '3d ago'.""" + """An age in its largest whole unit: 'just now', '12m ago', '5h ago', + '1d ago'. A day is 24h (hours used to run on to 47h, so '1d ago' never + appeared). The caller never passes a clock AHEAD of now: that is said as a + date, not an age.""" s = int(seconds) if s < 60: return "just now" if s < 3600: return f"{s // 60}m ago" - if s < 2 * 86400: + if s < 86400: return f"{s // 3600}h ago" return f"{s // 86400}d ago" diff --git a/booth/static/embed.js b/booth/static/embed.js index 24da2c7..15d2f36 100644 --- a/booth/static/embed.js +++ b/booth/static/embed.js @@ -348,12 +348,19 @@ choice (the same localStorage key the Booth's toggle writes — same origin). Absent or unreadable = follow the OS, as before. The host page's own is the author's and is never touched. */ + var ours = []; // every element this script mounted function bkTheme() { var t = null; try { t = localStorage.getItem("booth.theme"); } catch (e) {} var forced = t === "light" || t === "dark"; - Array.prototype.forEach.call(document.querySelectorAll(".bk-ask"), function (el) { - if (forced) el.setAttribute("data-bk-theme", t); else el.removeAttribute("data-bk-theme"); + /* OUR fragments only (heid bug-hunt): an author's own `.bk-ask` in the + host page is theirs, and is never marked. */ + ours.forEach(function (root) { + var els = [root].concat(Array.prototype.slice.call(root.querySelectorAll(".bk-ask"))); + els.forEach(function (el) { + if (!el.classList.contains("bk-ask")) return; + if (forced) el.setAttribute("data-bk-theme", t); else el.removeAttribute("data-bk-theme"); + }); }); } /* A choice made in another tab moves an open report live. */ @@ -376,6 +383,8 @@ if (!data) return; favicon(data.favicon); var mounted = place(data.marks || []); + ours = []; + Object.keys(mounted).forEach(function (id) { ours = ours.concat(mounted[id]); }); bkTheme(); reassociate(); asksChip(data.open || [], mounted); diff --git a/booth/templates/_dates.html b/booth/templates/_dates.html index efbcb79..0dd92bf 100644 --- a/booth/templates/_dates.html +++ b/booth/templates/_dates.html @@ -1,11 +1,21 @@ {# r2b D1b — a booth's two dates, defined ONCE for the Desk row and the booth header. Created is the day it began (the filesystem's birth time); updated is how recently its CONTENT moved (`landed_at`, the clock "new since you - looked" reads). Each is a