fix(worklog): add full-date tooltip + hide event-time under 420px (Fable UX)

Two non-blocking UX improvements from the Fable gate on #5739:
1. Full date+time tooltip (title attr) on the worklog event-time label — the bare
   clock is date-ambiguous when a settled session is reviewed later or a run crosses
   midnight. New _activityFullClockLabel (range-guarded so a bad epoch never yields
   'Invalid Date' in the tooltip) + test.
2. Hide the time label under 420px — its ~50px of fixed chrome squeezes the
   collapsed row's flexible preview toward zero on phones.

Co-authored-by: rodboev.
This commit is contained in:
nesquena-hermesandrodboev. committed 2026-07-11 00:38:23 +00:00
1 parent a86324985e
commit 97fe772fdb
3 files changed
+50

No files matched your search

+7
View File
@@ -4118,6 +4118,13 @@ body.resizing .sidebar{transition:none!important;}
white-space:nowrap;
font-size:10px;
}
/* Narrow viewports: the ~50px of fixed time-label chrome squeezes the collapsed
row's flexible preview (the most informative part) toward zero. Hide the label
under ~420px; the full timing is still available in Compact/expanded review and
via the title tooltip on wider screens. (#5739 Fable UX fix.) */
@media (max-width:420px){
.transparent-event-time{ display:none; }
}
.transparent-event-row .thinking-card-label{
display:inline;
flex:0 0 auto;
+17
View File
@@ -5568,6 +5568,18 @@ function _activityClockLabel(ts){
if(!Number.isFinite(stamp)||stamp<=0)return'';
try{return new Date(stamp*1000).toLocaleTimeString([], {hour:'numeric',minute:'2-digit'});}catch(_){return'';}
}
// Full date+time label for the worklog event-time tooltip (title attr). Guards the
// same valid-Date range as _timestampSeconds so a bad epoch never yields "Invalid
// Date" in the tooltip. (#5739)
function _activityFullClockLabel(ts){
const stamp=Number(ts);
if(!Number.isFinite(stamp)||stamp<=0||stamp>8.64e12)return'';
try{
const d=new Date(stamp*1000);
if(isNaN(d.getTime()))return'';
return d.toLocaleString([], {year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'});
}catch(_){return'';}
}
function _timestampSeconds(value){
if(value===undefined||value===null||value==='') return null;
if(value instanceof Date){
@@ -5663,6 +5675,11 @@ function _syncTransparentEventTimestamp(row, header, opts){
timeEl.className='transparent-event-time';
}
timeEl.textContent=label;
// Full date+time tooltip: the bare clock label is date-ambiguous when a settled
// session is reviewed days later (or a run crosses midnight), and timing is the
// whole point of this label. (#5739 Fable UX fix.)
const fullLabel=_activityFullClockLabel(ts);
if(fullLabel) timeEl.setAttribute('title',fullLabel); else timeEl.removeAttribute('title');
timeEl.setAttribute('data-event-at',String(ts));
timeEl.setAttribute('data-event-at-source',source);
row.setAttribute('data-event-at',String(ts));
@@ -247,6 +247,7 @@ global._toolWorklogListEl = () => null;
global._activityKeyForLiveTurn = () => 'activity-key';
global._assistantTurnBlocks = () => null;
eval(extractFunc('_activityClockLabel'));
eval(extractFunc('_activityFullClockLabel'));
eval(extractFunc('_timestampSeconds'));
eval(extractFunc('_firstValidTimestampSeconds'));
eval(extractFunc('_transparentEventTimestampSeconds'));
@@ -458,6 +459,31 @@ process.stdout.write(JSON.stringify({
assert data["normalLabel"]
def test_full_clock_label_tooltip_includes_date_and_rejects_bad_epochs():
# The worklog time label carries a full date+time tooltip (title attr) so a
# settled session reviewed days later (or a run crossing midnight) is not
# date-ambiguous. Bad epochs must yield '' (never 'Invalid Date'). (#5739 Fable.)
data = _run_node(
"""
process.stdout.write(JSON.stringify({
normal: _activityFullClockLabel(1700000901),
huge: _activityFullClockLabel(1e20),
zero: _activityFullClockLabel(0),
negative: _activityFullClockLabel(-1),
}));
"""
)
# a normal epoch yields a non-empty full label with the year present, no "Invalid Date"
assert data["normal"]
assert "Invalid Date" not in data["normal"]
assert "2023" in data["normal"] or "2024" in data["normal"] or "2025" in data["normal"] or "2026" in data["normal"]
# out-of-range / non-positive epochs yield empty string (no tooltip)
assert data["huge"] == ""
assert data["zero"] == ""
assert data["negative"] == ""
def test_settled_anchor_scene_rows_use_row_created_at_timestamps():
data = _run_node(
"""