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
66 changes: 66 additions & 0 deletions apps/web/src/components/settings/ProviderInstanceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useEffect, useRef, useState, type ReactNode } from "react";
import {
isProviderDriverKind,
resolveProviderInstanceEnabled,
type EnvironmentId,
type ProviderInstanceConfig,
type ProviderInstanceEnvironmentVariable,
type ProviderInstanceId,
Expand All @@ -31,6 +32,7 @@ import {
readCustomModelEntries,
toCustomModelSetting,
} from "@t3tools/shared/model";
import { limitsNotice } from "@t3tools/shared/usageLimits";
import { cn } from "../../lib/utils";
import { useCopyToClipboard } from "../../hooks/useCopyToClipboard";
import { normalizeProviderAccentColor } from "../../providerInstances";
Expand All @@ -48,6 +50,7 @@ import { ProviderInstanceIcon, providerInstanceInitials } from "../chat/Provider
import { ProviderAccentColorPicker } from "./ProviderAccentColorPicker";
import { RedactedSensitiveText } from "./RedactedSensitiveText";
import { SettingsRow, SettingsSection } from "./settingsLayout";
import { LimitWindows, ResetCredits } from "../usage/UsageLimits";
import {
getProviderVersionAdvisoryPresentation,
PROVIDER_STATUS_STYLES,
Expand Down Expand Up @@ -170,6 +173,60 @@ function ProviderAuthEmail(props: { readonly email: string | undefined }) {
);
}

function ProviderUsageLimits({
environmentId,
provider,
}: {
readonly environmentId?: EnvironmentId;
readonly provider: ServerProvider | undefined;
}) {
const [now] = useState(() => Date.now());
const usageLimits = provider?.usageLimits;
const notice = usageLimits ? limitsNotice(usageLimits) : null;
const providerName = provider?.displayName?.trim() || provider?.driver || "provider";

return (
<SettingsSection title="Usage limits">
{!provider ? (
<SettingsRow title="Usage data is not available yet." />
) : !usageLimits ? (
<SettingsRow
title="No usage data reported"
description={`Refresh provider status after signing in to ${providerName} to check your limits.`}
/>
) : notice ? (
<SettingsRow title="Usage limits unavailable" description={notice} />
) : (
<div className="overflow-hidden p-3 sm:p-4">
<div className="relative overflow-hidden">
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<p className="text-sm font-medium text-foreground">Current subscription</p>
<p className="mt-1 truncate text-xs text-muted-foreground">
{provider.auth.label ?? provider.auth.type ?? "Not reported"}
</p>
</div>
</div>
<div className="mt-4 border-t border-border/60 pt-3">
<LimitWindows driver={provider.driver} windows={usageLimits.windows} now={now} />
</div>
{usageLimits.resetCredits && environmentId ? (
<div className="mt-3 border-t border-border/60 pt-3">
<ResetCredits
environmentId={environmentId}
instanceId={provider.instanceId}
credits={usageLimits.resetCredits}
now={now}
/>
</div>
) : null}
</div>
</div>
)}
</SettingsSection>
);
}

function ProviderEnvironmentSection(props: {
readonly environment: ReadonlyArray<ProviderInstanceEnvironmentVariable>;
readonly onChange: (environment: ReadonlyArray<ProviderInstanceEnvironmentVariable>) => void;
Expand Down Expand Up @@ -344,6 +401,7 @@ function ProviderEnvironmentSection(props: {

interface ProviderInstanceCardProps {
readonly instanceId: ProviderInstanceId;
readonly environmentId?: EnvironmentId;
readonly instance: ProviderInstanceConfig;
readonly driverOption: DriverOption | undefined;
readonly liveProvider: ServerProvider | undefined;
Expand Down Expand Up @@ -399,6 +457,7 @@ interface ProviderInstanceCardProps {
*/
export function ProviderInstanceCard({
instanceId,
environmentId,
instance,
driverOption,
liveProvider,
Expand Down Expand Up @@ -914,6 +973,13 @@ export function ProviderInstanceCard({
</div>
</SettingsSection>
) : null}

{mode === "editor" ? (
<ProviderUsageLimits
{...(environmentId ? { environmentId } : {})}
provider={liveProvider}
/>
) : null}
</>
);
}
4 changes: 2 additions & 2 deletions apps/web/src/components/settings/ProviderSettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ import { stackedThreadToast, toastManager } from "../ui/toast";
import { AddProviderInstanceDialog } from "./AddProviderInstanceDialog";
import { ExpandableText } from "./ExpandableText";
import { ProviderInstanceCard } from "./ProviderInstanceCard";
import { UsageProviderSettings } from "./UsageProviderSettings";
import { ProviderSetupSection, readAntigravityAuthMethod } from "./ProviderSetupSection";
import { DRIVER_OPTIONS, getDriverOption } from "./providerDriverMeta";
import { searchableSetting } from "./settingsSearch";
import { UsageProviderSettings } from "./UsageProviderSettings";
import {
backgroundActivityOverrideSettings,
buildProviderInstanceUpdatePatch,
Expand Down Expand Up @@ -777,7 +777,6 @@ export function EnvironmentProviderSettings({
const selectedRow =
rows.find((row) => row.instanceId === selectedInstanceId) ??
(targetInstanceMissing ? null : (rows[0] ?? null));

const updateProviderInstance = (
row: InstanceRow,
next: ProviderInstanceConfig,
Expand Down Expand Up @@ -892,6 +891,7 @@ export function EnvironmentProviderSettings({
<ProviderInstanceCard
key={row.instanceId}
instanceId={row.instanceId}
environmentId={environmentId}
instance={row.instance}
driverOption={driverOption}
liveProvider={liveProvider}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/usage/UsageLimits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,14 @@ export function LimitWindows({
const resetsIn = formatResetsIn(window, now);
return (
<Fragment key={window.id}>
<span className="flex min-w-0 items-center gap-2 text-xs">
<span className="flex min-w-0 items-center gap-1 text-xs">
<span className="truncate text-muted-foreground">{window.label}</span>
<span className="ms-auto shrink-0 font-medium text-foreground tabular-nums">
{remainingPercent(window)}% left
</span>
</span>
<WindowBar color={color} window={window} now={now} />
<span className="flex items-center gap-2 text-xs whitespace-nowrap text-muted-foreground tabular-nums">
<span className="flex items-center gap-2 text-xs whitespace-nowrap text-muted-foreground tabular-nums w-full">
{pace ? <PaceIcon pace={pace} /> : null}
<span className="ms-auto shrink-0">{resetsIn ?? ""}</span>
</span>
Expand Down
Loading