fix(ui): flex quota rows and thin global scrollbars

Replace the fixed-table quota layout with flex rows that shrink cleanly,
keep the hidden-quota chip row from overflowing, and use thin mac-like
scrollbars app-wide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
decolua
2026-07-29 18:10:33 +07:00
parent 8b0fcf4b16
commit 15dfd86416
3 changed files with 124 additions and 96 deletions

View File

@@ -148,9 +148,7 @@ export default function QuotaTable({
)} )}
</div> </div>
<div className="overflow-x-auto"> <div className="space-y-px">
<table className="w-full table-fixed text-left">
<tbody>
{currentPageRows.map((quota) => { {currentPageRows.map((quota) => {
const colors = getColorClasses(quota.remaining); const colors = getColorClasses(quota.remaining);
const countdown = formatResetTime(quota.resetAt); const countdown = formatResetTime(quota.resetAt);
@@ -162,21 +160,20 @@ export default function QuotaTable({
const countdownLabel = recurring ? `in ${countdown}` : `expires in ${countdown}`; const countdownLabel = recurring ? `in ${countdown}` : `expires in ${countdown}`;
return ( return (
<tr <div
key={`${quota.name}-${quota.index}`} key={`${quota.name}-${quota.index}`}
className="border-b border-black/5 dark:border-white/5 hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors" className={`flex items-center gap-2 border-b border-black/5 dark:border-white/5 hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors ${cellPad}`}
> >
<td className={`${cellPad} w-[30%]`}> {/* Name */}
<div className="flex items-center gap-1.5 min-w-0"> <div className="flex w-36 min-w-0 items-center gap-1.5">
<span className="text-[10px] shrink-0">{colors.emoji}</span> <span className="text-[10px] shrink-0">{colors.emoji}</span>
<span className={`${nameText} font-medium text-text-primary truncate`}> <span className={`${nameText} font-medium text-text-primary truncate`}>
{quota.name} {quota.name}
</span> </span>
</div> </div>
</td>
<td className={`${cellPad} w-[45%]`}> {/* Progress + used/total */}
<div className={compact ? "space-y-1" : "space-y-1.5"}> <div className={`min-w-0 flex-1 ${compact ? "space-y-1" : "space-y-1.5"}`}>
<div className={`${compact ? "h-1" : "h-1.5"} rounded-full overflow-hidden border ${colors.bgLight} ${ <div className={`${compact ? "h-1" : "h-1.5"} rounded-full overflow-hidden border ${colors.bgLight} ${
quota.remaining === 0 ? "border-black/10 dark:border-white/10" : "border-transparent" quota.remaining === 0 ? "border-black/10 dark:border-white/10" : "border-transparent"
}`}> }`}>
@@ -186,18 +183,21 @@ export default function QuotaTable({
/> />
</div> </div>
<div className={`flex items-center justify-between ${compact ? "text-[10px]" : "text-xs"}`}> <div className={`flex items-center justify-between gap-1 min-w-0 ${compact ? "text-[10px]" : "text-xs"}`}>
<span className="text-text-muted"> <span
className="text-text-muted truncate"
title={`${quota.used.toLocaleString()} / ${quota.total > 0 ? quota.total.toLocaleString() : "∞"}`}
>
{quota.used.toLocaleString()} / {quota.total > 0 ? quota.total.toLocaleString() : "∞"} {quota.used.toLocaleString()} / {quota.total > 0 ? quota.total.toLocaleString() : "∞"}
</span> </span>
<span className={`font-medium ${colors.text}`}> <span className={`font-medium ${colors.text} shrink-0`}>
{quota.remaining}% {quota.remaining}%
</span> </span>
</div> </div>
</div> </div>
</td>
<td className={`${cellPad} ${hasHideAction ? "w-[20%]" : "w-[25%]"}`}> {/* Reset time */}
<div className="min-w-0 shrink">
{countdown !== "-" || resetDisplay ? ( {countdown !== "-" || resetDisplay ? (
compact ? ( compact ? (
<div <div
@@ -207,14 +207,14 @@ export default function QuotaTable({
{countdown !== "-" ? countdownLabel : resetDisplay} {countdown !== "-" ? countdownLabel : resetDisplay}
</div> </div>
) : ( ) : (
<div className="space-y-0.5"> <div className="min-w-0 space-y-0.5">
{countdown !== "-" && ( {countdown !== "-" && (
<div className={`${resetPrimary} text-text-primary font-medium`}> <div className={`${resetPrimary} text-text-primary font-medium truncate`}>
{countdownLabel} {countdownLabel}
</div> </div>
)} )}
{resetDisplay && ( {resetDisplay && (
<div className={`${resetSecondary} text-text-muted`}> <div className={`${resetSecondary} text-text-muted truncate`}>
{resetDisplay} {resetDisplay}
</div> </div>
)} )}
@@ -223,14 +223,14 @@ export default function QuotaTable({
) : ( ) : (
<div className={`${resetPrimary} text-text-muted italic`}>N/A</div> <div className={`${resetPrimary} text-text-muted italic`}>N/A</div>
)} )}
</td> </div>
{/* Hide action */}
{hasHideAction && ( {hasHideAction && (
<td className={`${cellPad} w-[5%] text-right`}>
<button <button
type="button" type="button"
onClick={() => onHideQuota(quota)} onClick={() => onHideQuota(quota)}
className="inline-flex h-6 w-6 items-center justify-center rounded-md text-text-muted transition-colors hover:bg-black/5 hover:text-text-primary dark:hover:bg-white/5" className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-text-muted transition-colors hover:bg-black/5 hover:text-text-primary dark:hover:bg-white/5"
title="Hide this quota row" title="Hide this quota row"
aria-label={`Hide quota ${quota.name}`} aria-label={`Hide quota ${quota.name}`}
> >
@@ -238,13 +238,10 @@ export default function QuotaTable({
visibility_off visibility_off
</span> </span>
</button> </button>
</td>
)} )}
</tr> </div>
); );
})} })}
</tbody>
</table>
</div> </div>
{totalPages > 1 && ( {totalPages > 1 && (

View File

@@ -1270,7 +1270,7 @@ export default function ProviderLimits() {
visibility_off visibility_off
</span> </span>
<span className="shrink-0">Hidden:</span> <span className="shrink-0">Hidden:</span>
<div className="flex w-full items-center gap-1"> <div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto whitespace-nowrap pb-2">
{hiddenQuotaRows.map((quotaRow) => ( {hiddenQuotaRows.map((quotaRow) => (
<button <button
key={getQuotaVisibilityKey(quotaRow)} key={getQuotaVisibilityKey(quotaRow)}

View File

@@ -256,6 +256,37 @@ input, textarea {
background: rgba(229, 106, 74, 0.55); background: rgba(229, 106, 74, 0.55);
} }
/* Global thin mac-like scrollbars (overrides clunky Windows/Linux default) */
* {
scrollbar-width: thin;
scrollbar-color: rgba(120, 120, 120, 0.35) transparent;
}
.dark * {
scrollbar-color: rgba(180, 180, 180, 0.3) transparent;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: rgba(120, 120, 120, 0.35);
border-radius: 9999px;
border: 2px solid transparent;
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
background-color: rgba(120, 120, 120, 0.55);
}
.dark *::-webkit-scrollbar-thumb {
background-color: rgba(180, 180, 180, 0.3);
}
.dark *::-webkit-scrollbar-thumb:hover {
background-color: rgba(180, 180, 180, 0.5);
}
/* Reusable elevated card */ /* Reusable elevated card */
.card-soft { .card-soft {
background-color: var(--color-surface); background-color: var(--color-surface);