Skip to content

Commit 48442cd

Browse files
committed
fix: avoid live session overview layout shift
1 parent 57e74fd commit 48442cd

3 files changed

Lines changed: 38 additions & 11 deletions

File tree

frontend/src/features/accounts/tests/accountCardLayout.test.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ test('account cards skip unsupported quota placeholder modules when telemetry is
9494
assert.doesNotMatch(source, /UnsupportedQuotaPlaceholder/);
9595
assert.match(source, /<QuotaBars quotaDisplay=\{resolvedQuotaDisplay\} t=\{t\} \/>/);
9696
assert.match(source, /<BillingBalance billing=\{billing\} \/>/);
97-
assert.match(source, /<RateLimitGuard rateLimitStatus=\{rateLimitStatus\} usageSummary=\{usageSummary\} t=\{t\} \/>/);
97+
assert.match(source, /<RateLimitGuard rateLimitStatus=\{rateLimitStatus\} usageSummary=\{usageSummary\} refreshing=\{rateLimitRefreshing \|\| usageRefreshing\} t=\{t\} \/>/);
9898
});
9999

100100
test('quota bars surface stale runtime error reason on cards and details', async () => {
@@ -136,7 +136,7 @@ test('full attribution cards do not render the retired traffic metrics module',
136136
assert.doesNotMatch(sectionsSource, /buildTrafficCurveState|AccountTrafficFlowState|TrafficChart|TrafficSummary/);
137137
assert.doesNotMatch(cardSource, /TrafficMetricsModule|TrafficSection|UsageMetrics/);
138138
assert.match(cardSource, /<QuotaBars quotaDisplay=\{resolvedQuotaDisplay\} t=\{t\} \/>/);
139-
assert.match(cardSource, /<RateLimitGuard rateLimitStatus=\{rateLimitStatus\} usageSummary=\{usageSummary\} t=\{t\} \/>/);
139+
assert.match(cardSource, /<RateLimitGuard rateLimitStatus=\{rateLimitStatus\} usageSummary=\{usageSummary\} refreshing=\{rateLimitRefreshing \|\| usageRefreshing\} t=\{t\} \/>/);
140140
});
141141

142142
test('account card footer actions use a solid top divider with two equal columns', async () => {

frontend/src/features/codex-live-sessions/components/CodexLiveSessionDetail.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ function SessionOverview({
152152
return (
153153
<section className="grid min-w-0 gap-5" data-codex-live-overview="true">
154154
<div
155-
className="grid min-w-0 gap-3 lg:grid-cols-[minmax(0,1.25fr)_minmax(9rem,0.65fr)_minmax(9rem,0.65fr)_minmax(9rem,0.65fr)]"
155+
className="relative grid min-w-0 gap-3 lg:grid-cols-[minmax(0,1.25fr)_minmax(9rem,0.65fr)_minmax(9rem,0.65fr)_minmax(9rem,0.65fr)]"
156156
data-codex-overview-summary-cards="true"
157157
>
158158
<section className="grid min-h-[5.75rem] min-w-0 content-between border border-[color:color-mix(in_srgb,var(--border-color)_28%,transparent)] bg-[var(--bg-main)] p-2.5" data-codex-overview-card="identity">
@@ -186,14 +186,7 @@ function SessionOverview({
186186
secondaryValue={`${summary.errorSessions} ${t('codex_live_sessions.summary_failed')}`}
187187
accent={summary.errorSessions > 0 ? 'danger' : summary.degradedSessions > 0 ? 'warning' : 'neutral'}
188188
/>
189-
{loading || errorMessage ? (
190-
<div className="flex min-h-8 items-center justify-between gap-3 border border-dashed border-[color:color-mix(in_srgb,var(--border-color)_35%,transparent)] px-3 py-1.5 font-mono text-[length:var(--font-size-ui-2xs)] font-black uppercase lg:col-span-4">
191-
<span className="text-[var(--text-muted)]">
192-
{loading ? t('codex_live_sessions.detail_loading') : t('codex_live_sessions.detail_stale')}
193-
</span>
194-
{errorMessage ? <span className="min-w-0 truncate text-right text-[var(--color-status-warning)]">{errorMessage}</span> : null}
195-
</div>
196-
) : null}
189+
<OverviewStatusNotice loading={loading} errorMessage={errorMessage} t={t} />
197190
</div>
198191

199192
<OverviewTimingTrend
@@ -214,6 +207,26 @@ function SessionOverview({
214207
);
215208
}
216209

210+
function OverviewStatusNotice({ loading, errorMessage, t }: { loading?: boolean; errorMessage?: string; t: Translate }) {
211+
if (!loading && !errorMessage) {
212+
return null;
213+
}
214+
215+
return (
216+
<div
217+
className="pointer-events-none absolute right-0 top-[calc(100%+0.5rem)] z-10 flex max-w-[min(32rem,100%)] items-center gap-3 bg-[var(--bg-main)] px-2.5 py-1 font-mono text-[length:var(--font-size-ui-2xs)] font-black uppercase shadow-[0_0_0_1px_color-mix(in_srgb,var(--border-color)_28%,transparent),3px_3px_0_color-mix(in_srgb,var(--shadow-color)_42%,transparent)]"
218+
data-codex-overview-status-overlay="true"
219+
role="status"
220+
aria-live="polite"
221+
>
222+
<span className="shrink-0 text-[var(--text-muted)]">
223+
{loading ? t('codex_live_sessions.detail_loading') : t('codex_live_sessions.detail_stale')}
224+
</span>
225+
{errorMessage ? <span className="min-w-0 truncate text-right text-[var(--color-status-warning)]">{errorMessage}</span> : null}
226+
</div>
227+
);
228+
}
229+
217230
function OverviewSummaryCard({
218231
label,
219232
primaryValue,

frontend/src/features/codex-live-sessions/model.test.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,20 @@ test('codex live session detail header uses request timing trend chart', async (
11001100
assert.doesNotMatch(detailSource, /font-size-ui-5xl/);
11011101
});
11021102

1103+
test('codex live overview loading notice does not push the timing trend down', async () => {
1104+
const detailSource = await readFile(new URL('./components/CodexLiveSessionDetail.tsx', import.meta.url), 'utf8');
1105+
const overviewSource = detailSource.slice(
1106+
detailSource.indexOf('function SessionOverview('),
1107+
detailSource.indexOf('function OverviewTimingTrend('),
1108+
);
1109+
1110+
assert.match(overviewSource, /<OverviewStatusNotice loading=\{loading\} errorMessage=\{errorMessage\} t=\{t\} \/>/);
1111+
assert.match(overviewSource, /data-codex-overview-status-overlay="true"/);
1112+
assert.match(overviewSource, /absolute right-0 top-\[calc\(100%\+0\.5rem\)\]/);
1113+
assert.doesNotMatch(overviewSource, /loading \|\| errorMessage \? \(\s*<div className="flex min-h-8 items-center justify-between gap-3 border border-dashed/);
1114+
assert.doesNotMatch(overviewSource, /lg:col-span-4/);
1115+
});
1116+
11031117
test('codex live sessions feature splits row snapshot polling from detail history loading', async () => {
11041118
const featureSource = await readFile(new URL('./CodexLiveSessionsFeature.tsx', import.meta.url), 'utf8');
11051119

0 commit comments

Comments
 (0)