mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: use Popover for context indicator on mobile viewports (#23747)
## Problem The context-usage indicator ring in the agents chat uses a Radix UI `Tooltip`, which only opens on hover. On mobile/touch devices there is no hover event, so tapping the indicator does nothing. ## Fix On mobile viewports (`< 640 px`, matching the existing `isMobileViewport()` helper), render a `Popover` instead of a `Tooltip` so that tapping the ring toggles the context-usage info. Desktop behavior (hover tooltip) is unchanged. - Extract the trigger button and content into shared variables to avoid duplication - Conditionally render `Popover` (mobile) or `Tooltip` (desktop) based on viewport width - Both `Popover` and `PopoverContent` were already imported in the file
This commit is contained in:
@@ -223,57 +223,75 @@ const ContextUsageIndicator: FC<{ usage: AgentContextUsage | null }> = ({
|
||||
? `Context usage ${percentLabel}. ${formatTokenCount(usedTokens)} of ${formatTokenCount(contextLimitTokens)} tokens used.`
|
||||
: "Context usage";
|
||||
|
||||
const triggerButton = (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={ariaLabel}
|
||||
className="relative inline-flex size-7 shrink-0 items-center justify-center rounded-full border-none bg-transparent p-0 outline-none transition-colors hover:bg-surface-secondary/60 focus-visible:ring-2 focus-visible:ring-content-link/40"
|
||||
>
|
||||
<svg
|
||||
className={cn("size-icon-sm -rotate-90", toneClassName)}
|
||||
viewBox={`0 0 ${RING_SIZE} ${RING_SIZE}`}
|
||||
aria-hidden
|
||||
>
|
||||
<circle
|
||||
cx={RING_SIZE / 2}
|
||||
cy={RING_SIZE / 2}
|
||||
r={RING_RADIUS}
|
||||
fill="none"
|
||||
strokeWidth={RING_STROKE}
|
||||
className="stroke-content-secondary/25"
|
||||
/>
|
||||
<circle
|
||||
cx={RING_SIZE / 2}
|
||||
cy={RING_SIZE / 2}
|
||||
r={RING_RADIUS}
|
||||
fill="none"
|
||||
strokeWidth={RING_STROKE}
|
||||
strokeLinecap="round"
|
||||
className="stroke-current transition-all duration-300 ease-out"
|
||||
style={{
|
||||
strokeDasharray: `${RING_CIRCUMFERENCE} ${RING_CIRCUMFERENCE}`,
|
||||
strokeDashoffset: dashOffset,
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
|
||||
const tooltipContent = (
|
||||
<div className="text-xs text-content-primary">
|
||||
{hasPercent
|
||||
? `${percentLabel} – ${formatTokenCountCompact(usedTokens)} / ${formatTokenCountCompact(contextLimitTokens)} context used`
|
||||
: "Context usage unavailable"}
|
||||
{hasPercent &&
|
||||
usage?.compressionThreshold !== undefined &&
|
||||
usage.compressionThreshold > 0 && (
|
||||
<div className="mt-1 text-content-secondary">
|
||||
Compacts at {usage.compressionThreshold}%
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
// On mobile viewports, Radix Tooltip only opens on hover which
|
||||
// doesn't exist on touch devices. Use a Popover instead so a tap
|
||||
// toggles the context-usage info.
|
||||
if (isMobileViewport()) {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>{triggerButton}</PopoverTrigger>
|
||||
<PopoverContent side="top" className="w-auto px-3 py-2">
|
||||
{tooltipContent}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={ariaLabel}
|
||||
className="relative inline-flex size-7 shrink-0 items-center justify-center rounded-full border-none bg-transparent p-0 outline-none transition-colors hover:bg-surface-secondary/60 focus-visible:ring-2 focus-visible:ring-content-link/40"
|
||||
>
|
||||
<svg
|
||||
className={cn("size-icon-sm -rotate-90", toneClassName)}
|
||||
viewBox={`0 0 ${RING_SIZE} ${RING_SIZE}`}
|
||||
aria-hidden
|
||||
>
|
||||
<circle
|
||||
cx={RING_SIZE / 2}
|
||||
cy={RING_SIZE / 2}
|
||||
r={RING_RADIUS}
|
||||
fill="none"
|
||||
strokeWidth={RING_STROKE}
|
||||
className="stroke-content-secondary/25"
|
||||
/>
|
||||
<circle
|
||||
cx={RING_SIZE / 2}
|
||||
cy={RING_SIZE / 2}
|
||||
r={RING_RADIUS}
|
||||
fill="none"
|
||||
strokeWidth={RING_STROKE}
|
||||
strokeLinecap="round"
|
||||
className="stroke-current transition-all duration-300 ease-out"
|
||||
style={{
|
||||
strokeDasharray: `${RING_CIRCUMFERENCE} ${RING_CIRCUMFERENCE}`,
|
||||
strokeDashoffset: dashOffset,
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
<div className="text-xs text-content-primary">
|
||||
{hasPercent
|
||||
? `${percentLabel} – ${formatTokenCountCompact(usedTokens)} / ${formatTokenCountCompact(contextLimitTokens)} context used`
|
||||
: "Context usage unavailable"}
|
||||
{hasPercent &&
|
||||
usage?.compressionThreshold !== undefined &&
|
||||
usage.compressionThreshold > 0 && (
|
||||
<div className="mt-1 text-content-secondary">
|
||||
Compacts at {usage.compressionThreshold}%
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
<TooltipTrigger asChild>{triggerButton}</TooltipTrigger>
|
||||
<TooltipContent side="top">{tooltipContent}</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user