Skip to content

Commit 0148514

Browse files
jahoomaclaude
andcommitted
Show session drain as status bar fill, with final-5m countdown
Ambient bar fills proportionally to time remaining so users have a passive sense of session progress; a bold warning-colored "X:XX" readout appears only for the final five minutes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ede8639 commit 0148514

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

cli/src/components/status-bar.tsx

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { TextAttributes } from '@opentui/core'
12
import React, { useEffect, useState } from 'react'
23

3-
import { FreebuffSessionCountdown } from './freebuff-session-countdown'
44
import { ScrollToBottomButton } from './scroll-to-bottom-button'
55
import { ShimmerText } from './shimmer-text'
66
import { StopButton } from './stop-button'
7+
import { useFreebuffSessionProgress } from '../hooks/use-freebuff-session-progress'
78
import { useTheme } from '../hooks/use-theme'
89
import { formatElapsedTime } from '../utils/format-elapsed-time'
910

@@ -13,6 +14,17 @@ import type { StatusIndicatorState } from '../utils/status-indicator-state'
1314

1415
const SHIMMER_INTERVAL_MS = 160
1516

17+
/** Show the "X:XX left" urgency readout under this many ms remaining. */
18+
const COUNTDOWN_VISIBLE_MS = 5 * 60_000
19+
20+
const formatCountdown = (ms: number): string => {
21+
if (ms <= 0) return 'expiring…'
22+
const totalSeconds = Math.ceil(ms / 1000)
23+
const m = Math.floor(totalSeconds / 60)
24+
const s = totalSeconds % 60
25+
return `${m}:${s.toString().padStart(2, '0')}`
26+
}
27+
1628
interface StatusBarProps {
1729
timerStartTime: number | null
1830
isAtBottom: boolean
@@ -132,8 +144,13 @@ export const StatusBar = ({
132144
const statusIndicatorContent = renderStatusIndicator()
133145
const elapsedTimeContent = renderElapsedTime()
134146

135-
// Only show gray background when there's status indicator or timer
136-
const hasContent = statusIndicatorContent || elapsedTimeContent
147+
const sessionProgress = useFreebuffSessionProgress(freebuffSession)
148+
149+
// Show gray background when there's status indicator, timer, or when the
150+
// freebuff session fill is visible (otherwise the fill would float over
151+
// transparent space).
152+
const hasContent =
153+
statusIndicatorContent || elapsedTimeContent || sessionProgress !== null
137154

138155
return (
139156
<box
@@ -147,6 +164,20 @@ export const StatusBar = ({
147164
backgroundColor: hasContent ? theme.surface : 'transparent',
148165
}}
149166
>
167+
{sessionProgress !== null && (
168+
<box
169+
style={{
170+
position: 'absolute',
171+
left: 0,
172+
top: 0,
173+
bottom: 0,
174+
// Fill anchors left and shrinks as time passes — the draining
175+
// bar is the countdown; no separate numeric readout needed.
176+
width: `${sessionProgress.fraction * 100}%`,
177+
backgroundColor: theme.surfaceHover,
178+
}}
179+
/>
180+
)}
150181
<box
151182
style={{
152183
flexGrow: 1,
@@ -176,9 +207,14 @@ export const StatusBar = ({
176207
{onStop && (statusIndicatorState.kind === 'waiting' || statusIndicatorState.kind === 'streaming') && (
177208
<StopButton onClick={onStop} />
178209
)}
179-
<text style={{ wrapMode: 'none' }}>
180-
<FreebuffSessionCountdown session={freebuffSession} />
181-
</text>
210+
{sessionProgress !== null &&
211+
sessionProgress.remainingMs < COUNTDOWN_VISIBLE_MS && (
212+
<text style={{ wrapMode: 'none' }}>
213+
<span fg={theme.warning} attributes={TextAttributes.BOLD}>
214+
{formatCountdown(sessionProgress.remainingMs)}
215+
</span>
216+
</text>
217+
)}
182218
</box>
183219
</box>
184220
)

0 commit comments

Comments
 (0)