UI bugfix round: copy payload, activity time, spanning project line, modal scroll #24

Merged
ClaudiuSchuster merged 1 commit from oss-oo/observatory-bugfix-round-gfix20260924 into main 2026-09-24 16:37:15 +00:00
Owner

Four narrow-window UI defects reported from the cockpit, fixed and verified per-bug with a CDP-driven fixture harness (headless Chrome, trusted input events, granted clipboard) at 1280/900/400 px.

1. Copy title button copied wrong content (or nothing)

Root cause: the title payload was embedded with JSON.stringify(...) into a double-quoted onclick attribute. The HTML parser ends the attribute at the first JSON double-quote, so the rendered attribute was literally onclick="copyText(" — the handler never compiled (el.onclick === null) and every click was a no-op. The sessionId copy button escaped differently and happened to work, which produced the inconsistent "sometimes nothing, sometimes something else" behavior.

Fix: payloads ride in data-copy / data-copy-msg attributes (HTML-escaped, decoded by the DOM); one delegated document-level click listener reads the clicked row's dataset — quoting/entities in titles can never break it, and the listener survives the 3 s table re-renders. copyText gains an execCommand fallback for unfocused/non-secure contexts and reports failures via toast.

Verified: clicking 📋 on five probe rows (quoted title, 152-char title, two similar-prefix titles, running row) puts EXACTLY that row's displayed title on the clipboard; sessionId copy still exact.

2. "Last activity" truncated to "HH:…"

Root cause: the cell rendered the full locale datetime (9/24/2026, 6:01:23 PM) into a percentage-width column (14% → 43 px at 400 px) with nowrap+ellipsis — clipped to 18:….

Fix: compact fmtActivity — now / 8m / 2h 05m relative when fresh, Wed 16:10 within the week, Aug 10 18:10 beyond; the full timestamp moves to the cell tooltip. Column widths now ride on header cells in pixels (the name column takes the leftover) so the time column keeps its width at any window size. The models column slims down: chips abbreviate to the bare model name (GLM-5.3, claude-opus-4-5) with the full id on hover, tighter chip padding, per-chip ellipsis discipline.

Verified: no clipped activity cell at 1280/900/400; every entry shows minutes at minimum (relative or HH:MM).

3. Project/worktree line squeezed multi-line

Root cause: the path line lived in a div inside the first (name) column — at 400 px that column is ~100 px and a 48-char path wrapped to 7 lines.

Fix: each session now renders as a two-row group in its own tbody: the main row plus a second row whose single td colspan="7" spans the full table width — one clean line down to ~400 px (ellipsis + full path in the tooltip for extreme paths). Group-level hover/cursor, run tint and hidden dimming apply to both rows; every row control keeps working (sort, pager, unhide, show, copy, title link, row click). Below 560 px the models column and the show-button label drop out so the remaining columns stay readable.

Verified: spanning cell colspan=7, single line, width ≈ table width at all three sizes; sort/pager/unhide regression-tested.

4. Modal/drawer scroll focus

Root cause: neither dialog took focus on open and the page scroll stayed live — wheel events over non-scrollable dialog parts (header, footer, backdrop) or at the scroll boundary scrolled the dashboard behind.

Fix: dialogs get tabindex="-1" and focus({preventScroll:true}) on open; scroll bodies get overscroll-behavior: contain; the page scroll locks (overflow:hidden + scrollbar-width compensation) while either dialog is open and unlocks when the last one closes.

Verified: after opening, activeElement is inside the dialog; CDP wheel events over the header and at the scroll bottom leave window.scrollY at 0 while the dialog body scrolls; lock releases on close (✕/ESC/backdrop).

Notes

  • A subtle layout bug surfaced during verification and is fixed here: .models { display:flex } also hit the th.models header cell; a flex box is not a table-cell, so Blink dropped it as the fixed-layout first-row width definer and injected a phantom auto column. The flex layout is now scoped to td.models.
  • docs/screenshot.png regenerated from the fixture with the new row layout.
  • Gates: py_compile + --help, make check-social-preview, Super-Linter slim v8.7.0 (workflow env, exit 0) — plus the 26-check functional harness (evidence in the PR conversation).
Four narrow-window UI defects reported from the cockpit, fixed and verified per-bug with a CDP-driven fixture harness (headless Chrome, trusted input events, granted clipboard) at 1280/900/400 px. ## 1. Copy title button copied wrong content (or nothing) **Root cause:** the title payload was embedded with `JSON.stringify(...)` into a double-quoted `onclick` attribute. The HTML parser ends the attribute at the first JSON double-quote, so the rendered attribute was literally `onclick="copyText("` — the handler never compiled (`el.onclick === null`) and every click was a no-op. The sessionId copy button escaped differently and happened to work, which produced the inconsistent "sometimes nothing, sometimes something else" behavior. **Fix:** payloads ride in `data-copy` / `data-copy-msg` attributes (HTML-escaped, decoded by the DOM); one delegated document-level click listener reads the clicked row's dataset — quoting/entities in titles can never break it, and the listener survives the 3 s table re-renders. `copyText` gains an `execCommand` fallback for unfocused/non-secure contexts and reports failures via toast. **Verified:** clicking 📋 on five probe rows (quoted title, 152-char title, two similar-prefix titles, running row) puts EXACTLY that row's displayed title on the clipboard; sessionId copy still exact. ## 2. "Last activity" truncated to "HH:…" **Root cause:** the cell rendered the full locale datetime (`9/24/2026, 6:01:23 PM`) into a percentage-width column (14% → 43 px at 400 px) with nowrap+ellipsis — clipped to `18:…`. **Fix:** compact `fmtActivity` — `now` / `8m` / `2h 05m` relative when fresh, `Wed 16:10` within the week, `Aug 10 18:10` beyond; the full timestamp moves to the cell tooltip. Column widths now ride on header cells in pixels (the name column takes the leftover) so the time column keeps its width at any window size. The models column slims down: chips abbreviate to the bare model name (`GLM-5.3`, `claude-opus-4-5`) with the full id on hover, tighter chip padding, per-chip ellipsis discipline. **Verified:** no clipped activity cell at 1280/900/400; every entry shows minutes at minimum (relative or HH:MM). ## 3. Project/worktree line squeezed multi-line **Root cause:** the path line lived in a `div` inside the first (name) column — at 400 px that column is ~100 px and a 48-char path wrapped to 7 lines. **Fix:** each session now renders as a two-row group in its own `tbody`: the main row plus a second row whose single `td colspan="7"` spans the full table width — one clean line down to ~400 px (ellipsis + full path in the tooltip for extreme paths). Group-level hover/cursor, run tint and hidden dimming apply to both rows; every row control keeps working (sort, pager, unhide, show, copy, title link, row click). Below 560 px the models column and the show-button label drop out so the remaining columns stay readable. **Verified:** spanning cell `colspan=7`, single line, width ≈ table width at all three sizes; sort/pager/unhide regression-tested. ## 4. Modal/drawer scroll focus **Root cause:** neither dialog took focus on open and the page scroll stayed live — wheel events over non-scrollable dialog parts (header, footer, backdrop) or at the scroll boundary scrolled the dashboard behind. **Fix:** dialogs get `tabindex="-1"` and `focus({preventScroll:true})` on open; scroll bodies get `overscroll-behavior: contain`; the page scroll locks (`overflow:hidden` + scrollbar-width compensation) while either dialog is open and unlocks when the last one closes. **Verified:** after opening, `activeElement` is inside the dialog; CDP wheel events over the header and at the scroll bottom leave `window.scrollY` at 0 while the dialog body scrolls; lock releases on close (✕/ESC/backdrop). ## Notes - A subtle layout bug surfaced during verification and is fixed here: `.models { display:flex }` also hit the `th.models` header cell; a flex box is not a table-cell, so Blink dropped it as the fixed-layout first-row width definer and injected a phantom auto column. The flex layout is now scoped to `td.models`. - `docs/screenshot.png` regenerated from the fixture with the new row layout. - Gates: `py_compile` + `--help`, `make check-social-preview`, Super-Linter slim v8.7.0 (workflow env, exit 0) — plus the 26-check functional harness (evidence in the PR conversation).
UI bugfix round: copy payload, activity time, spanning project line, modal scroll
All checks were successful
Checks / Functional checks (pull_request) Successful in 15s
1c8f99afcd
Four narrow-window defects fixed in dashboard.html:

- Copy title: the payload was JSON.stringify'd into a double-quoted
  onclick attribute, which the HTML parser truncated at the first quote
  (onclick="copyText(") — the handler never compiled and clicks copied
  nothing. Payloads now ride in data-copy attributes handled by one
  delegated listener that survives the 3 s re-renders; copyText gains an
  execCommand fallback for unfocused/non-secure contexts.
- Last activity: full locale datetimes ellipsized to "HH:…" on narrow
  windows. Now compact and never truncated: relative (now / 8m / 2h 05m)
  for fresh entries, weekday+HH:MM within the week, date+HH:MM beyond;
  full timestamp on hover. Column widths moved from percentages to
  header-cell pixels so the time column keeps its width at any window
  size; model chips abbreviate to the bare model name (full id on hover)
  to reclaim width.
- Project/worktree line: was squeezed into the name column and wrapped
  to many lines. Each session now renders as a two-row group whose
  second row spans the full table width via colspan, one clean line down
  to ~400 px (ellipsis + tooltip for extreme paths). Below 560 px the
  models column drops out and the show button becomes an icon so the
  remaining columns stay readable.
- Modal/drawer scroll: focus stayed outside the dialog, so wheel events
  over non-scrollable dialog parts scrolled the page behind. Dialogs now
  take focus on open (tabindex="-1" + focus()), their scroll bodies
  contain overscroll, and the page scroll locks while a dialog is open.

README documents the scroll isolation; screenshot regenerated from the
fixture with the new row layout.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
OSS-Singularity/zcode-dispatch-observatory!24
No description provided.