✨ style(web): unify design system across default frontend
🔤 Typography - Fix --font-sans stack: 'Public Sans Variable' was never applied (wrong family name), add CJK fallbacks (PingFang SC / Microsoft YaHei UI / Noto Sans SC) - Sync <html lang> with i18n language for correct CJK glyph selection - Remove dead fake font system (font-provider.tsx, config/fonts.ts, --font-inter/--font-manrope) - Typography contract: 3-tier headings, arbitrary text-[10px]/[11px] → text-xs (46 files), no uppercase table headers, font-bold → font-semibold - Numbers use tabular-nums only; font-mono reserved for keys/IDs/code (29 files) 🎨 Color discipline - Retire StatusBadge string-hash rainbow (autoColor → neutral); all variants resolve to 5 semantic voices - Add tintedBadgeClassMap as the single tinted-pill recipe - Replace hardcoded emerald/amber/rose/sky classes with semantic tokens across ~30 files (timing badges, progress bars, system panels, pricing, rankings, log row tints) - Drop all !important text overrides and [font-family:var(--font-body)] hacks 🧘 Motion restraint - Remove admin entrance animations: table row stagger, card hover lift/shadow, button press scale, dashboard shimmer line - Neuter page-transition stagger components into plain wrappers; page transitions reduced to pure opacity fade - Decorative motion stays landing-only 📐 Shape - Single radius source: --radius 1rem → 0.625rem - Sparklines use solid chart tokens instead of decorative gradients - PanelWrapper: border-only (no shadow), align radius with Card 📏 Guardrails - Add "Design System Discipline" section (3.10.1) to web/default/AGENTS.md ✅ Verified: tsgo typecheck clean, rsbuild production build passes
This commit is contained in:
@@ -22,7 +22,7 @@ import { Zap } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { DataTableColumnHeader } from '@/components/data-table'
|
||||
import { StatusBadge } from '@/components/status-badge'
|
||||
import { StatusBadge, tintedBadgeClassMap } from '@/components/status-badge'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -90,7 +90,7 @@ export function createTimestampColumn<T>(config: {
|
||||
return <span className='text-muted-foreground/60 text-xs'>-</span>
|
||||
}
|
||||
return (
|
||||
<span className='font-mono text-xs tabular-nums'>
|
||||
<span className='text-xs tabular-nums'>
|
||||
{formatTimestampToDate(timestamp, unit)}
|
||||
</span>
|
||||
)
|
||||
@@ -137,22 +137,16 @@ export function createDurationColumn<T>(config: {
|
||||
const variant =
|
||||
duration.durationSec > warningThresholdSec ? 'danger' : 'success'
|
||||
|
||||
const durationBgMap: Record<string, string> = {
|
||||
success:
|
||||
'border border-emerald-200/40 bg-emerald-50/35 !text-emerald-600 dark:border-emerald-900/40 dark:bg-emerald-950/15 dark:!text-emerald-400',
|
||||
warning:
|
||||
'border border-amber-200/45 bg-amber-50/35 !text-amber-600 dark:border-amber-900/40 dark:bg-amber-950/15 dark:!text-amber-400',
|
||||
danger:
|
||||
'border border-rose-200/50 bg-rose-50/35 !text-red-600 dark:border-rose-900/40 dark:bg-rose-950/15 dark:!text-red-400',
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusBadge
|
||||
label={`${duration.durationSec.toFixed(1)}s`}
|
||||
variant={variant}
|
||||
size='sm'
|
||||
copyable={false}
|
||||
className={cn('rounded-md font-mono', durationBgMap[variant])}
|
||||
className={cn(
|
||||
'rounded-md tabular-nums',
|
||||
tintedBadgeClassMap[variant]
|
||||
)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -225,7 +219,7 @@ export function createFailReasonColumn<T>(config: {
|
||||
onClick={() => setDialogOpen(true)}
|
||||
title={cellTitle}
|
||||
>
|
||||
<span className='truncate leading-snug text-red-600 group-hover:underline dark:text-red-400'>
|
||||
<span className='text-destructive truncate leading-snug group-hover:underline'>
|
||||
{failReason}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
+38
-30
@@ -21,7 +21,11 @@ import { CircleAlert, GitBranch, Sparkles, KeyRound } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { StatusBadge, type StatusBadgeProps } from '@/components/status-badge'
|
||||
import {
|
||||
StatusBadge,
|
||||
tintedBadgeClassMap,
|
||||
type StatusBadgeProps,
|
||||
} from '@/components/status-badge'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import {
|
||||
Popover,
|
||||
@@ -304,7 +308,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
|
||||
return (
|
||||
<div className='flex min-w-0 flex-col gap-0.5'>
|
||||
<span className='truncate font-mono text-xs tabular-nums'>
|
||||
<span className='truncate text-xs tabular-nums'>
|
||||
{formatTimestampToDate(timestamp)}
|
||||
</span>
|
||||
<StatusBadge
|
||||
@@ -312,7 +316,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
variant={config.color as StatusBadgeProps['variant']}
|
||||
size='sm'
|
||||
copyable={false}
|
||||
className='!text-xs [&_span]:!text-xs'
|
||||
className='text-xs [&_span]:text-xs'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -402,7 +406,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
}
|
||||
>
|
||||
<GitBranch
|
||||
className='size-3.5 text-amber-500'
|
||||
className='text-warning size-3.5'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
@@ -423,7 +427,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
{affinity && (
|
||||
<button
|
||||
type='button'
|
||||
className='absolute -top-1 -right-1 leading-none text-amber-500'
|
||||
className='text-warning absolute -top-1 -right-1 leading-none'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setAffinityTarget({
|
||||
@@ -443,7 +447,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
)}
|
||||
</div>
|
||||
{log.channel_name && (
|
||||
<span className='text-muted-foreground/70 truncate [font-family:var(--font-body)] !text-xs'>
|
||||
<span className='text-muted-foreground/70 truncate text-xs'>
|
||||
{channelName}
|
||||
</span>
|
||||
)}
|
||||
@@ -510,7 +514,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
<Avatar className='ring-border/60 size-6 ring-1 max-sm:hidden'>
|
||||
<AvatarFallback
|
||||
className={cn(
|
||||
'text-[11px] font-semibold',
|
||||
'text-xs font-semibold',
|
||||
!sensitiveVisible && 'bg-muted text-muted-foreground'
|
||||
)}
|
||||
style={
|
||||
@@ -577,7 +581,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
copyText={sensitiveVisible ? tokenName : undefined}
|
||||
size='sm'
|
||||
showDot={false}
|
||||
className='border-border/60 bg-muted/30 text-foreground h-6 max-w-full gap-1.5 overflow-hidden rounded-md border px-2 py-0.5 [font-family:var(--font-body)]'
|
||||
className='border-border/60 bg-muted/30 text-foreground h-6 max-w-full gap-1.5 overflow-hidden rounded-md border px-2 py-0.5'
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
{sensitiveVisible && tokenName.length > 16 && (
|
||||
@@ -588,7 +592,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{metaParts.length > 0 && (
|
||||
<span className='text-muted-foreground/60 truncate [font-family:var(--font-body)] !text-xs'>
|
||||
<span className='text-muted-foreground/60 truncate text-xs'>
|
||||
{metaParts.join(' · ')}
|
||||
</span>
|
||||
)}
|
||||
@@ -637,16 +641,12 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
? getFirstResponseTimeColor(frt / 1000)
|
||||
: 'neutral'
|
||||
|
||||
const timingBgMap: Record<string, string> = {
|
||||
success:
|
||||
'border border-emerald-200/40 bg-emerald-50/35 !text-emerald-600 dark:border-emerald-900/40 dark:bg-emerald-950/15 dark:!text-emerald-400',
|
||||
warning:
|
||||
'border border-amber-200/45 bg-amber-50/35 !text-amber-600 dark:border-amber-900/40 dark:bg-amber-950/15 dark:!text-amber-400',
|
||||
danger:
|
||||
'border border-rose-200/50 bg-rose-50/35 !text-red-600 dark:border-rose-900/40 dark:bg-rose-950/15 dark:!text-red-400',
|
||||
neutral:
|
||||
'border border-border/60 bg-muted/30 dark:border-border/40 dark:bg-muted/20',
|
||||
}
|
||||
const timingClass = (variant: string) =>
|
||||
tintedBadgeClassMap[
|
||||
(variant in tintedBadgeClassMap
|
||||
? variant
|
||||
: 'neutral') as keyof typeof tintedBadgeClassMap
|
||||
]
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-1'>
|
||||
@@ -656,7 +656,10 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
variant={timeVariant as StatusBadgeProps['variant']}
|
||||
size='sm'
|
||||
copyable={false}
|
||||
className={cn('rounded-md font-mono', timingBgMap[timeVariant])}
|
||||
className={cn(
|
||||
'rounded-md tabular-nums',
|
||||
timingClass(timeVariant)
|
||||
)}
|
||||
/>
|
||||
{log.is_stream &&
|
||||
(frt != null && frt > 0 ? (
|
||||
@@ -667,8 +670,8 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
showDot={false}
|
||||
copyable={false}
|
||||
className={cn(
|
||||
'rounded-md font-mono',
|
||||
timingBgMap[frtVariant]
|
||||
'rounded-md tabular-nums',
|
||||
timingClass(frtVariant)
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
@@ -678,12 +681,15 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
size='sm'
|
||||
showDot={false}
|
||||
copyable={false}
|
||||
className={cn('rounded-md font-mono', timingBgMap.neutral)}
|
||||
className={cn(
|
||||
'rounded-md tabular-nums',
|
||||
tintedBadgeClassMap.neutral
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className='flex items-center gap-1 [font-family:var(--font-body)] !text-xs leading-none'>
|
||||
<span className='text-muted-foreground/60 [font-family:var(--font-body)] !text-xs leading-none'>
|
||||
<div className='flex items-center gap-1 text-xs leading-none'>
|
||||
<span className='text-muted-foreground/60 text-xs leading-none'>
|
||||
{log.is_stream ? t('Stream') : t('Non-stream')}
|
||||
{tokensPerSecond != null && (
|
||||
<>
|
||||
@@ -701,7 +707,9 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={<CircleAlert className='size-3 text-red-500' />}
|
||||
render={
|
||||
<CircleAlert className='text-destructive size-3' />
|
||||
}
|
||||
></TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className='space-y-0.5 text-xs'>
|
||||
@@ -751,12 +759,12 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<span className='font-mono text-xs font-medium tabular-nums'>
|
||||
<span className='text-xs font-medium tabular-nums'>
|
||||
{promptTokens.toLocaleString()} /{' '}
|
||||
{completionTokens.toLocaleString()}
|
||||
</span>
|
||||
{(cacheReadTokens > 0 || cacheWriteTokens > 0) && (
|
||||
<div className='flex items-center gap-1 text-[11px]'>
|
||||
<div className='flex items-center gap-1 text-xs'>
|
||||
{cacheReadTokens > 0 && (
|
||||
<span className='text-muted-foreground/60'>
|
||||
{t('Cache')}↓ {cacheReadTokens.toLocaleString()}
|
||||
@@ -815,7 +823,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<span className='border-border/80 bg-muted/60 inline-flex h-6 w-fit items-center rounded-md border px-2 [font-family:var(--font-body)] text-sm leading-none font-semibold tabular-nums'>
|
||||
<span className='border-border/80 bg-muted/60 inline-flex h-6 w-fit items-center rounded-md border px-2 text-sm leading-none font-semibold tabular-nums'>
|
||||
{quotaDisplay.prefix && (
|
||||
<span className='mr-1'>{quotaDisplay.prefix}</span>
|
||||
)}
|
||||
@@ -853,7 +861,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
|
||||
primary.muted
|
||||
? 'text-muted-foreground/60'
|
||||
: primary.danger
|
||||
? 'text-red-600 dark:text-red-400'
|
||||
? 'text-destructive'
|
||||
: 'text-foreground'
|
||||
)}
|
||||
>
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ export function useDrawingLogsColumns(
|
||||
|
||||
return (
|
||||
<div className='flex min-w-0 flex-col gap-0.5'>
|
||||
<span className='truncate font-mono text-xs tabular-nums'>
|
||||
<span className='truncate text-xs tabular-nums'>
|
||||
{formatTimestampToDate(submitTime, 'milliseconds')}
|
||||
</span>
|
||||
<StatusBadge
|
||||
@@ -151,7 +151,7 @@ export function useDrawingLogsColumns(
|
||||
copyText={mjId}
|
||||
variant='neutral'
|
||||
size='sm'
|
||||
className='border-border/60 bg-muted/30 !text-foreground max-w-full truncate rounded-md border px-1.5 py-0.5 font-mono'
|
||||
className='border-border/60 bg-muted/30 text-foreground max-w-full truncate rounded-md border px-1.5 py-0.5 font-mono'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
+6
-6
@@ -102,15 +102,15 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
|
||||
|
||||
return (
|
||||
<div className='flex min-w-0 flex-col gap-0.5'>
|
||||
<span className='truncate font-mono text-xs tabular-nums'>
|
||||
<span className='truncate text-xs tabular-nums'>
|
||||
{formatTimestampToDate(submitTime, 'seconds')}
|
||||
</span>
|
||||
{log.finish_time ? (
|
||||
<span className='text-muted-foreground/60 truncate font-mono text-[11px] tabular-nums'>
|
||||
<span className='text-muted-foreground/60 truncate text-xs tabular-nums'>
|
||||
{formatTimestampToDate(log.finish_time, 'seconds')}
|
||||
</span>
|
||||
) : (
|
||||
<span className='text-muted-foreground/50 text-[11px]'>-</span>
|
||||
<span className='text-muted-foreground/50 text-xs'>-</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@@ -143,7 +143,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
|
||||
<Avatar className='ring-border/60 size-6 ring-1 max-sm:hidden'>
|
||||
<AvatarFallback
|
||||
className={cn(
|
||||
'text-[11px] font-semibold',
|
||||
'text-xs font-semibold',
|
||||
!sensitiveVisible && 'bg-muted text-muted-foreground'
|
||||
)}
|
||||
style={
|
||||
@@ -179,9 +179,9 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
|
||||
copyText={taskId}
|
||||
variant='neutral'
|
||||
size='sm'
|
||||
className='border-border/60 bg-muted/30 !text-foreground max-w-full truncate rounded-md border px-1.5 py-0.5 font-mono'
|
||||
className='border-border/60 bg-muted/30 text-foreground max-w-full truncate rounded-md border px-1.5 py-0.5 font-mono'
|
||||
/>
|
||||
<span className='text-muted-foreground/60 truncate text-[11px]'>
|
||||
<span className='text-muted-foreground/60 truncate text-xs'>
|
||||
{t(log.platform)} · {t(taskActionMapper.getLabel(log.action))}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ function StatBadge(props: {
|
||||
<span className='border-border/60 bg-muted/25 inline-flex h-7 items-center gap-2 rounded-md border px-2.5 text-xs shadow-xs'>
|
||||
<span className={cn('h-3.5 w-0.5 rounded-full', props.accent)} />
|
||||
<span className='text-muted-foreground'>{props.label}</span>
|
||||
<span className='text-foreground/85 font-mono font-semibold tabular-nums'>
|
||||
<span className='text-foreground/85 font-semibold tabular-nums'>
|
||||
{props.value}
|
||||
</span>
|
||||
</span>
|
||||
@@ -91,17 +91,17 @@ export function CommonLogsStats() {
|
||||
<StatBadge
|
||||
label={t('Usage')}
|
||||
value={sensitiveVisible ? formatLogQuota(stats?.quota || 0) : '••••'}
|
||||
accent='bg-sky-500/70'
|
||||
accent='bg-chart-1/70'
|
||||
/>
|
||||
<StatBadge
|
||||
label={t('RPM')}
|
||||
value={stats?.rpm || 0}
|
||||
accent='bg-rose-500/65'
|
||||
accent='bg-chart-5/70'
|
||||
/>
|
||||
<StatBadge
|
||||
label={t('TPM')}
|
||||
value={stats?.tpm || 0}
|
||||
accent='bg-slate-400/70'
|
||||
accent='bg-muted-foreground/50'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -77,9 +77,9 @@ const CHANNEL_FIELD_LABELS: Record<string, string> = {
|
||||
function timingTextColorClass(
|
||||
variant: 'success' | 'warning' | 'danger'
|
||||
): string {
|
||||
if (variant === 'success') return 'text-emerald-600'
|
||||
if (variant === 'warning') return 'text-amber-600'
|
||||
return 'text-rose-600'
|
||||
if (variant === 'success') return 'text-success'
|
||||
if (variant === 'warning') return 'text-warning'
|
||||
return 'text-destructive'
|
||||
}
|
||||
|
||||
function DetailRow(props: {
|
||||
@@ -628,7 +628,10 @@ export function DetailsDialog(props: DetailsDialogProps) {
|
||||
label={t('IP Address')}
|
||||
value={
|
||||
<span className='flex items-center gap-1'>
|
||||
<Globe className='size-3 text-amber-500' aria-hidden='true' />
|
||||
<Globe
|
||||
className='text-muted-foreground size-3'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
{props.log.ip}
|
||||
</span>
|
||||
}
|
||||
@@ -814,7 +817,7 @@ export function DetailsDialog(props: DetailsDialogProps) {
|
||||
/>
|
||||
))}
|
||||
{showLegacyTopupWarning && (
|
||||
<div className='flex items-start gap-1.5 text-xs text-amber-600 dark:text-amber-400'>
|
||||
<div className='text-warning flex items-start gap-1.5 text-xs'>
|
||||
<Info className='mt-0.5 size-3.5 shrink-0' aria-hidden='true' />
|
||||
<span>
|
||||
{t(
|
||||
@@ -1030,9 +1033,9 @@ export function DetailsDialog(props: DetailsDialogProps) {
|
||||
value={
|
||||
<span className='flex items-center gap-1'>
|
||||
{other.admin_info.local_count_tokens ? (
|
||||
<Monitor className='size-3 text-blue-500' />
|
||||
<Monitor className='text-muted-foreground size-3' />
|
||||
) : (
|
||||
<Cloud className='size-3 text-emerald-500' />
|
||||
<Cloud className='text-muted-foreground size-3' />
|
||||
)}
|
||||
<span className='text-xs'>
|
||||
{other.admin_info.local_count_tokens
|
||||
@@ -1080,7 +1083,7 @@ export function DetailsDialog(props: DetailsDialogProps) {
|
||||
)}
|
||||
{Array.isArray(other.stream_status.errors) &&
|
||||
other.stream_status.errors.length > 0 && (
|
||||
<pre className='bg-background/60 mt-1 max-h-32 overflow-y-auto rounded border p-2 font-mono text-[11px] leading-relaxed wrap-break-word whitespace-pre-wrap'>
|
||||
<pre className='bg-background/60 mt-1 max-h-32 overflow-y-auto rounded border p-2 font-mono text-xs leading-relaxed wrap-break-word whitespace-pre-wrap'>
|
||||
{other.stream_status.errors.join('\n')}
|
||||
</pre>
|
||||
)}
|
||||
@@ -1155,7 +1158,7 @@ export function DetailsDialog(props: DetailsDialogProps) {
|
||||
className='shrink-0 font-medium'
|
||||
copyable={false}
|
||||
/>
|
||||
<span className='min-w-0 font-mono text-[11px] leading-relaxed break-all sm:wrap-break-word'>
|
||||
<span className='min-w-0 font-mono text-xs leading-relaxed break-all sm:wrap-break-word'>
|
||||
{parsed.content}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -120,7 +120,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) {
|
||||
>
|
||||
{advancedOpen ? t('Collapse') : t('Expand')}
|
||||
{activeAdvancedCount > 0 && (
|
||||
<Badge className='ml-0.5 size-5 justify-center p-0 text-[10px]'>
|
||||
<Badge className='ml-0.5 size-5 justify-center p-0 text-xs'>
|
||||
{activeAdvancedCount}
|
||||
</Badge>
|
||||
)}
|
||||
@@ -157,7 +157,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) {
|
||||
>
|
||||
{t('Filter')}
|
||||
{activeMobileFilterCount > 0 && (
|
||||
<Badge className='ml-0.5 size-5 justify-center p-0 text-[10px]'>
|
||||
<Badge className='ml-0.5 size-5 justify-center p-0 text-xs'>
|
||||
{activeMobileFilterCount}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
@@ -132,7 +132,7 @@ function ModelBadgeContent(props: ModelBadgeProps) {
|
||||
showDot={!provider}
|
||||
autoColor={provider ? undefined : props.modelName}
|
||||
className={cn(
|
||||
'border-border/60 bg-muted/30 h-6 max-w-none gap-1.5 rounded-md border px-2 [font-family:var(--font-body)]',
|
||||
'border-border/60 bg-muted/30 h-6 max-w-none gap-1.5 rounded-md border px-2',
|
||||
provider && 'text-foreground',
|
||||
props.className
|
||||
)}
|
||||
|
||||
@@ -41,10 +41,8 @@ import { getLogTypeConfig } from '../lib/utils'
|
||||
import type { LogCategory } from '../types'
|
||||
|
||||
const logTypeRowTint: Record<number, string> = {
|
||||
[LOG_TYPE_ENUM.ERROR]:
|
||||
'bg-rose-50/40 dark:bg-rose-950/20 border-rose-200/50 dark:border-rose-900/30',
|
||||
[LOG_TYPE_ENUM.REFUND]:
|
||||
'bg-blue-50/30 dark:bg-blue-950/15 border-blue-200/50 dark:border-blue-900/30',
|
||||
[LOG_TYPE_ENUM.ERROR]: 'bg-destructive/5 border-destructive/20',
|
||||
[LOG_TYPE_ENUM.REFUND]: 'bg-info/5 border-info/20',
|
||||
}
|
||||
|
||||
interface UsageLogsMobileListProps<TData> {
|
||||
@@ -129,7 +127,7 @@ function SummaryField<TData>({
|
||||
<div
|
||||
className={cn('bg-muted/20 min-w-0 rounded-md px-2 py-1.5', className)}
|
||||
>
|
||||
<div className='text-muted-foreground mb-1 text-[11px] leading-none font-medium select-none'>
|
||||
<div className='text-muted-foreground mb-1 text-xs leading-none font-medium select-none'>
|
||||
{label}
|
||||
</div>
|
||||
<CompactCell
|
||||
@@ -156,7 +154,7 @@ function MobileLogTimeStatus({
|
||||
|
||||
return (
|
||||
<div className='space-y-1'>
|
||||
<div className='font-mono text-xs leading-tight tabular-nums'>
|
||||
<div className='text-xs leading-tight tabular-nums'>
|
||||
{formatTimestampToDate(timestamp)}
|
||||
</div>
|
||||
<div
|
||||
@@ -200,7 +198,7 @@ function CommonLogsCard<TData>({
|
||||
|
||||
<div className='grid grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)] gap-1.5'>
|
||||
<div className='bg-muted/20 min-w-0 rounded-md px-2 py-1.5'>
|
||||
<div className='text-muted-foreground mb-1 text-[11px] leading-none font-medium select-none'>
|
||||
<div className='text-muted-foreground mb-1 text-xs leading-none font-medium select-none'>
|
||||
{t('Time')}
|
||||
</div>
|
||||
<MobileLogTimeStatus
|
||||
@@ -217,7 +215,7 @@ function CommonLogsCard<TData>({
|
||||
<SummaryField
|
||||
label={t('Token')}
|
||||
cell={cells.get('token_name')}
|
||||
valueClassName='[&_.flex-col]:max-w-none [&_.flex-col>*:not(:first-child)]:text-[11px] [&_.flex-col>*:not(:first-child)]:leading-none'
|
||||
valueClassName='[&_.flex-col]:max-w-none [&_.flex-col>*:not(:first-child)]:text-xs [&_.flex-col>*:not(:first-child)]:leading-none'
|
||||
/>
|
||||
<SummaryField
|
||||
label={t('Timing')}
|
||||
|
||||
@@ -48,13 +48,13 @@ import { UsageLogsMobileList } from './usage-logs-mobile-card'
|
||||
const route = getRouteApi('/_authenticated/usage-logs/$section')
|
||||
|
||||
const logTypeRowTint: Record<number, string> = {
|
||||
[LOG_TYPE_ENUM.ERROR]: 'bg-rose-50/40 dark:bg-rose-950/20',
|
||||
[LOG_TYPE_ENUM.REFUND]: 'bg-blue-50/30 dark:bg-blue-950/15',
|
||||
[LOG_TYPE_ENUM.ERROR]: 'bg-destructive/5',
|
||||
[LOG_TYPE_ENUM.REFUND]: 'bg-info/5',
|
||||
}
|
||||
|
||||
// Warning tint for logs where a quota conversion saturated (admin-only marker).
|
||||
// Takes precedence over the per-type tint since it flags a billing anomaly.
|
||||
const quotaSaturationRowTint = 'bg-amber-50/60 dark:bg-amber-950/25'
|
||||
const quotaSaturationRowTint = 'bg-warning/10'
|
||||
|
||||
function getColumnVisibilityStorageKey(
|
||||
logCategory: LogCategory,
|
||||
|
||||
Reference in New Issue
Block a user