You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two defects in the chat message header — and the first does not fix the second
Reported together, but they are independent, and it matters: removing the year saves 30px; the buttons cover 42px. Shortening the stamp alone leaves the overlap.
Measured — Playwright, WebKit, production, signed in
Instance bd43f4de-… (/console/instances/:id/chat), 12 bubbles, deviceScaleFactor 2, isMobile, hasTouch. Bounding-box intersection of each action button against the timestamp <span>:
=== WebKit @ 320px === === WebKit @ 390px ===
sample stamp: "8 Aug 2026 at 2:12 pm" (110px wide)
Copy message overlaps by 18×16px
Delete this turn overlaps by 24×16px
Identical at both widths (the buttons are absolutely positioned, so the overlap does not scale). 42 of 110px — 38% of the timestamp — sits underneath the two buttons, including the minutes on the right-hand end.
Stamp width by format, same font (700 10px system-ui), WebKit:
current (with year) "8 Aug 2026 at 2:12 pm" 116px —
no year "8 Aug at 2:12 pm" 86px saves 30px
time only "2:12 pm" 41px saves 75px
Where it is
The overlap — three individually-correct decisions:
InstanceDetail.tsx:1123-1124 — the header row is flex items-center justify-between gap-3, so {formatDateTime(m.createdAt)} is pinned to the same right edge, at the same vertical band.
The reason it is mobile-only is the visibility rule both buttons share:
Above sm they are invisible until hover, so the collision is never seen. Below sm they are permanently visible — correct (there is no hover on touch, and CopyButton's own comment says so) — and permanently on top of the timestamp. Nothing is wrong with any of the three decisions; the bug is that no one reserved the space.
Fix 1 — reserve the corner (this is the overlap fix, and it is one class)
The buttons occupy 42px from the right edge. Give the header row that much padding below sm, where they are always visible:
// InstanceDetail.tsx:1123 and :1124 — the user and assistant header rows
className="text-2xs … flex items-center justify-between gap-3 pr-12 sm:pr-0"
pr-12 = 48px ≥ 42px measured, and sm:pr-0 keeps the desktop layout byte-for-byte (the buttons are hover-only there, so no space needs reserving). Verify with the same script rather than by eye — the acceptance criterion below is the measurement.
Fix 2 — drop the year, but conditionally
Do not remove year outright. formatDateTime is shared — IndexingTab.tsx:322 ("Indexed …"), SystemMessage.tsx:40, and both chat headers — and an unqualified "8 Aug" on a row from last year is worse than the width it saves. Show the year only when it is not the current year:
Every current-year message gets the 30px back; an older one stays unambiguous. This also makes the change safe for the two non-chat call sites, which was the reason not to just delete the option.
The full timestamp remains available on hover — stampTitle (messageStamp.ts) already puts the complete local time with the named zone on title. That is what makes shortening the visible stamp safe rather than lossy.
Alternatives considered and rejected
Time-only stamps ("2:12 pm", saves 75px). Tempting — it would also cover the 42px on its own. Rejected: there is no day divider. I grepped for one (isSameDay, DayDivider, dateSeparator) — nothing. The per-message date is currently the only date context in the thread, so time-only would make a scrollback undatable. It becomes the right answer only as part of adding a day divider, which is a separate, larger change and should be its own ticket.
Move the timestamp to the bubble's bottom edge. A real option, but it is a visual redesign of every message and needs a design pass; the padding fix is one class and reversible.
Acceptance criteria
Re-running the measurement in WebKit at 320px and 390px reports zero bounding-box intersection between Copy message / Delete this turn and the timestamp span.
A current-year message renders no year; a previous-year message still does.
Desktop (≥sm) layout is unchanged — the buttons stay hover-revealed and no padding is reserved.
Hovering a stamp still shows the full local time with the zone name.
Regression risk
pr-12 narrows the space for the left-hand group ("You"/"Assistant" + the playback button). At 320px that group is ~90px against a bubble of ~270px, so there is room — but the check belongs in the same measurement run, not in review.
Changing shared formatDateTime touches IndexingTab and SystemMessage. The conditional keeps both correct; a test pinning "current year ⇒ no year, other year ⇒ year" is the cheap guard.
The e2e mobile guard runs Chromium; this was found and measured in WebKit. Whatever test is added should run WebKit, or it will not see this class of defect.
Related: #389 (the tap-target work that made these buttons always-visible on mobile — this is its unforeseen interaction), #336 (why the stamps exist), #333 (the prior mobile-width defect, and the note that per-row width is scarce), #345 (stampTitle and the stored timezone).
Two defects in the chat message header — and the first does not fix the second
Reported together, but they are independent, and it matters: removing the year saves 30px; the buttons cover 42px. Shortening the stamp alone leaves the overlap.
Measured — Playwright, WebKit, production, signed in
Instance
bd43f4de-…(/console/instances/:id/chat), 12 bubbles,deviceScaleFactor 2,isMobile,hasTouch. Bounding-box intersection of each action button against the timestamp<span>:Identical at both widths (the buttons are absolutely positioned, so the overlap does not scale). 42 of 110px — 38% of the timestamp — sits underneath the two buttons, including the minutes on the right-hand end.
Stamp width by format, same font (
700 10px system-ui), WebKit:Where it is
The overlap — three individually-correct decisions:
InstanceDetail.tsx:58—CopyButton:className="tap-target absolute top-1 right-1.5 …"DeleteTurnButton.tsx:75—className="tap-target absolute top-1 right-8 …"InstanceDetail.tsx:1123-1124— the header row isflex items-center justify-between gap-3, so{formatDateTime(m.createdAt)}is pinned to the same right edge, at the same vertical band.The reason it is mobile-only is the visibility rule both buttons share:
Above
smthey are invisible until hover, so the collision is never seen. Belowsmthey are permanently visible — correct (there is no hover on touch, andCopyButton's own comment says so) — and permanently on top of the timestamp. Nothing is wrong with any of the three decisions; the bug is that no one reserved the space.The year —
packages/sdk/src/ui.ts:243:What to do
Fix 1 — reserve the corner (this is the overlap fix, and it is one class)
The buttons occupy 42px from the right edge. Give the header row that much padding below
sm, where they are always visible:pr-12= 48px ≥ 42px measured, andsm:pr-0keeps the desktop layout byte-for-byte (the buttons are hover-only there, so no space needs reserving). Verify with the same script rather than by eye — the acceptance criterion below is the measurement.Fix 2 — drop the year, but conditionally
Do not remove
yearoutright.formatDateTimeis shared —IndexingTab.tsx:322("Indexed …"),SystemMessage.tsx:40, and both chat headers — and an unqualified "8 Aug" on a row from last year is worse than the width it saves. Show the year only when it is not the current year:Every current-year message gets the 30px back; an older one stays unambiguous. This also makes the change safe for the two non-chat call sites, which was the reason not to just delete the option.
The full timestamp remains available on hover —
stampTitle(messageStamp.ts) already puts the complete local time with the named zone ontitle. That is what makes shortening the visible stamp safe rather than lossy.Alternatives considered and rejected
isSameDay,DayDivider,dateSeparator) — nothing. The per-message date is currently the only date context in the thread, so time-only would make a scrollback undatable. It becomes the right answer only as part of adding a day divider, which is a separate, larger change and should be its own ticket.tap-target(24×24 visual, 44px vertical reach) because they are absolutely positioned and can expand outside the row without making every header taller. Making them flex children puts that height back into the row. Not worth it for a padding bug.Acceptance criteria
Copy message/Delete this turnand the timestamp span.sm) layout is unchanged — the buttons stay hover-revealed and no padding is reserved.Regression risk
pr-12narrows the space for the left-hand group ("You"/"Assistant" + the playback button). At 320px that group is ~90px against a bubble of ~270px, so there is room — but the check belongs in the same measurement run, not in review.formatDateTimetouchesIndexingTabandSystemMessage. The conditional keeps both correct; a test pinning "current year ⇒ no year, other year ⇒ year" is the cheap guard.Related: #389 (the tap-target work that made these buttons always-visible on mobile — this is its unforeseen interaction), #336 (why the stamps exist), #333 (the prior mobile-width defect, and the note that per-row width is scarce), #345 (
stampTitleand the stored timezone).