Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 54 additions & 20 deletions apps/mobile/src/features/usage/UsageLimitsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ function WindowRow(props: {
const timeLeft = paceRemainingPercent(window, now);
const pace = paceOf(window, now);
const resetsIn = formatResetsIn(window, now);
const [barWidth, setBarWidth] = useState(0);
const tickX =
timeLeft === null || barWidth < 6
? null
: Math.min(Math.max((barWidth * timeLeft) / 100, 3), barWidth - 3);
const bar = (
<View
className="h-1.5 flex-row overflow-hidden rounded-full bg-subtle"
style={barWidth > 0 ? { width: barWidth } : undefined}
>
<View
className={
remaining <= 10
? "h-full rounded-full bg-red-500"
: remaining <= 30
? "h-full rounded-full bg-amber-500"
: "h-full rounded-full bg-foreground"
}
style={[
{ flex: remaining },
remaining > 30 && props.color ? { backgroundColor: props.color } : null,
]}
/>
<View style={{ flex: 100 - remaining }} />
</View>
);
return (
<View className="gap-1">
<View className="flex-row items-baseline justify-between gap-3">
Expand All @@ -57,27 +83,35 @@ function WindowRow(props: {
{remaining}% left
</Text>
</View>
<View className="h-3 justify-center">
<View className="h-1.5 flex-row overflow-hidden rounded-full bg-subtle">
<View
className={
remaining <= 10
? "h-full rounded-full bg-red-500"
: remaining <= 30
? "h-full rounded-full bg-amber-500"
: "h-full rounded-full bg-foreground"
}
style={[
{ flex: remaining },
remaining > 30 && props.color ? { backgroundColor: props.color } : null,
]}
/>
<View style={{ flex: 100 - remaining }} />
</View>
{timeLeft !== null ? (
<View
className="h-3 justify-center"
onLayout={(event) => setBarWidth(event.nativeEvent.layout.width)}
>
{tickX === null ? (
bar
) : (
<View className="h-1.5">
{/* Clip the same full-width bar so neither fill proportions nor corner radii change. */}
{(
[
[0, tickX - 3],
[tickX + 3, barWidth],
] as const
).map(([start, end]) => (
<View
key={start}
className="absolute top-0 bottom-0 overflow-hidden"
style={{ left: start, width: end - start }}
>
<View style={{ marginLeft: -start }}>{bar}</View>
</View>
))}
</View>
)}
{tickX !== null ? (
<View
className="absolute top-0 bottom-0 w-px bg-foreground"
style={{ left: `${timeLeft}%`, opacity: 0.6 }}
className="absolute top-px bottom-px w-0.5 rounded-full bg-foreground"
style={{ left: tickX - 1 }}
/>
) : null}
</View>
Expand Down
35 changes: 24 additions & 11 deletions apps/web/src/components/usage/UsageLimits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function WindowBar({
const timestampFormat = usePrimarySettings((settings) => settings.timestampFormat);
const remaining = remainingPercent(window);
const timeLeft = paceRemainingPercent(window, now);
const tickPosition = timeLeft === null ? null : `clamp(3px, ${timeLeft}%, calc(100% - 3px))`;
const resetsIn = formatResetsIn(window, now);
const resetsAt = window.resetsAt
? formatUpcomingTimestamp(window.resetsAt, timestampFormat, now)
Expand All @@ -105,18 +106,30 @@ function WindowBar({
/>
}
>
<div className="absolute inset-x-0 inset-y-1.5 rounded-full bg-muted" />
{remaining > 0 ? (
<div
className="absolute inset-y-1.5 left-0 rounded-full"
style={{ width: `${remaining}%`, backgroundColor: color }}
/>
) : null}
{timeLeft !== null ? (
{/* Mask both layers so the gap exposes even a translucent composer surface. */}
<div
className="absolute inset-0"
style={
tickPosition === null
? undefined
: {
maskImage: `linear-gradient(to right, black calc(${tickPosition} - 3px), transparent calc(${tickPosition} - 3px), transparent calc(${tickPosition} + 3px), black calc(${tickPosition} + 3px))`,
}
}
>
<div className="absolute inset-x-0 inset-y-1.5 rounded-full bg-muted" />
{remaining > 0 ? (
<div
className="absolute inset-y-1.5 left-0 rounded-full"
style={{ width: `${remaining}%`, backgroundColor: color }}
/>
) : null}
</div>
{tickPosition !== null ? (
<span
aria-hidden
className="absolute inset-y-0.5 w-px -translate-x-1/2 bg-foreground/60"
style={{ left: `${timeLeft}%` }}
className="absolute inset-y-1 w-0.5 -translate-x-1/2 rounded-full bg-foreground"
style={{ left: tickPosition }}
/>
) : null}
</TooltipTrigger>
Expand All @@ -126,7 +139,7 @@ function WindowBar({
{remaining}% left{timeLeft !== null ? ` · ${timeLeft}% of the window left` : ""}
</span>
{timeLeft !== null ? (
<span className="text-muted-foreground">The line is where even spending would be.</span>
<span className="text-muted-foreground">The tick is where even spending would be.</span>
) : null}
{resetsAt ? (
<span className="text-muted-foreground">
Expand Down
Loading