diff --git a/src/components/ConfigExporter.jsx b/src/components/ConfigExporter.jsx index 25d6a2b..a349a8e 100644 --- a/src/components/ConfigExporter.jsx +++ b/src/components/ConfigExporter.jsx @@ -258,7 +258,7 @@ print(message.content[0].text)`; return (
{/* Header */} -
+

@@ -316,14 +316,14 @@ print(message.content[0].text)`; {modelDropdownOpen && ( -
+
{availableModels .filter(m => m.toLowerCase().includes(modelSearch.toLowerCase())) .map((model) => (
{/* Config Preview */} -
-
+
+
@@ -377,25 +377,25 @@ print(message.content[0].text)`;
-
+        
           {config}
         
diff --git a/src/index.css b/src/index.css index 174dfa6..0a6eb25 100644 --- a/src/index.css +++ b/src/index.css @@ -5,16 +5,30 @@ @layer base { :root { --brand: 99 102 241; - /* Light theme text colors (overridden by .theme-light) */ + /* Text colors */ --page-text: 255 255 255; --page-text-secondary: 163 163 163; --page-text-muted: 115 115 115; --page-label: 212 212 212; + --page-link: 129 140 248; + /* Surface colors */ --page-card-bg: rgba(255,255,255,0.03); --page-card-border: rgba(255,255,255,0.06); --page-input-bg: rgba(255,255,255,0.04); --page-input-border: rgba(255,255,255,0.1); - --page-link: 129 140 248; + --page-surface: rgba(255,255,255,0.04); + --page-surface-hover: rgba(255,255,255,0.08); + --page-divider: rgba(255,255,255,0.06); + --page-inset: rgba(0,0,0,0.4); + /* Semantic colors — dark mode defaults */ + --page-success: 74 222 128; + --page-success-text: 74 222 128; + --page-warning: 250 204 21; + --page-warning-text: 253 224 71; + --page-danger: 248 113 113; + --page-danger-text: 248 113 113; + --page-info: 192 132 252; + --page-info-text: 192 132 252; } body { @@ -46,15 +60,33 @@ --page-text-secondary: 107 114 128; --page-text-muted: 156 163 175; --page-label: 55 65 81; + --page-link: 79 70 229; --page-card-bg: rgba(255,255,255,0.85); --page-card-border: rgb(229,231,235); --page-input-bg: #fff; --page-input-border: rgb(209,213,219); - --page-link: 79 70 229; + --page-surface: rgba(0,0,0,0.03); + --page-surface-hover: rgba(0,0,0,0.06); + --page-divider: rgb(229,231,235); + --page-inset: rgba(0,0,0,0.04); + --page-success: 22 163 74; + --page-success-text: 22 163 74; + --page-warning: 202 138 4; + --page-warning-text: 161 98 7; + --page-danger: 220 38 38; + --page-danger-text: 220 38 38; + --page-info: 124 58 237; + --page-info-text: 124 58 237; } .theme-claude { --page-link: 217 119 87; + --page-card-bg: rgba(255,255,255,0.7); + --page-card-border: #E8DDD0; + --page-surface: rgba(61,48,36,0.04); + --page-surface-hover: rgba(61,48,36,0.08); + --page-divider: #E8DDD0; + --page-inset: rgba(61,48,36,0.06); } } @@ -182,3 +214,34 @@ .theme-claude .btn-secondary:hover { background-color: rgba(217,119,87,0.05); } + +/* ═══════════════════════════════════════════════ + Theme-aware utility classes for surfaces/dividers + ═══════════════════════════════════════════════ */ +.surface { + background: var(--page-surface); +} +.surface-hover:hover { + background: var(--page-surface-hover); +} +.bg-page-inset { + background: var(--page-inset); +} +.divide-page > * + * { + border-color: var(--page-divider); +} +.border-page-divider { + border-color: var(--page-divider); +} +.bg-page-surface { + background: var(--page-surface); +} +.bg-page-surface-hover { + background: var(--page-surface-hover); +} +.hover\:bg-page-surface:hover { + background: var(--page-surface); +} +.hover\:bg-page-surface-hover:hover { + background: var(--page-surface-hover); +} diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index 8d5c099..ccbdf53 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -3,9 +3,7 @@ import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; import { getUserUsage, redeemCode, Q, quotaToDollar } from '../api'; -import SpotlightCard from '../components/bits/SpotlightCard'; import CountUp from '../components/bits/CountUp'; -import ShinyText from '../components/bits/ShinyText'; import toast from 'react-hot-toast'; export default function Dashboard() { @@ -33,14 +31,12 @@ export default function Dashboard() { if (res.data.success) { toast.success(t('dashboard.redeemSuccess')); setRedeemInput(''); - // Refresh both usage AND user data to update balance await Promise.all([loadUsage(), refreshUser()]); } } catch (err) { /* interceptor */ } setRedeeming(false); }; - // Use usage data (fresh from API) for balance/used — NOT stale user object const quota = usage?.quota ?? user?.quota ?? 0; const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0; const requestCount = usage?.request_count ?? user?.request_count ?? 0; @@ -51,35 +47,35 @@ export default function Dashboard() { {/* Welcome */}

- {t('dashboard.welcome')} + {t('dashboard.welcome')} {user?.display_name || user?.username || 'User'}

{t('dashboard.manageDesc')}

{/* Stats Cards */}
- +

{t('dashboard.balance')}

$

{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}

- +
- +

{t('dashboard.used')}

$

{t('dashboard.quotaUnits', { count: usedQuota.toLocaleString() })}

- +
- +

{t('dashboard.totalRequests')}

- +
{/* Quick Actions */} @@ -105,15 +101,15 @@ export default function Dashboard() {

{t('dashboard.quickLinks')}

- +

{t('dashboard.apiKeys')}

{t('dashboard.manageKeys')}

- +

{t('dashboard.packages')}

{t('dashboard.viewPlans')}

- +

{t('dashboard.pricing')}

{t('dashboard.modelPrices')}

diff --git a/src/pages/Pricing.jsx b/src/pages/Pricing.jsx index 10436e2..44a778f 100644 --- a/src/pages/Pricing.jsx +++ b/src/pages/Pricing.jsx @@ -73,7 +73,7 @@ export default function Pricing() { className={`inline-flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-sm font-medium transition-all ${ !vendor ? 'bg-brand-500 text-white shadow-lg shadow-brand-500/25' - : 'glass-sm text-page-secondary hover:text-page hover:bg-white/[0.08]' + : 'glass-sm text-page-secondary hover:text-page hover:bg-page-surface-hover' }`} > {t('pricing.allVendors')} @@ -85,7 +85,7 @@ export default function Pricing() { className={`inline-flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-sm font-medium transition-all ${ vendor === v.name ? 'bg-brand-500 text-white shadow-lg shadow-brand-500/25' - : 'glass-sm text-page-secondary hover:text-page hover:bg-white/[0.08]' + : 'glass-sm text-page-secondary hover:text-page hover:bg-page-surface-hover' }`} > {v.name} @@ -118,7 +118,7 @@ export default function Pricing() {
- + @@ -127,7 +127,7 @@ export default function Pricing() { {filtered.map((m, i) => ( - + @@ -140,8 +140,8 @@ export default function Pricing() {
{t('pricing.model')} {t('pricing.inputPrice')} {t('pricing.outputPrice')}
{m.display_name || m.model_name} {m.status === 'healthy' ? t('pricing.online') : t('pricing.unknown')} diff --git a/src/pages/Tokens.jsx b/src/pages/Tokens.jsx index dd0c031..283cbc2 100644 --- a/src/pages/Tokens.jsx +++ b/src/pages/Tokens.jsx @@ -149,12 +149,12 @@ export default function Tokens() {

{t('tokens.newApiKey')}

-

+

{t('tokens.keyWarning')}

-
- +
+ {newKey} @@ -245,13 +245,13 @@ export default function Tokens() { {/* Key row — always visible with copy button */} {token.key && ( -
+
sk-{token.key} diff --git a/src/pages/Topup.jsx b/src/pages/Topup.jsx index ba6ceae..dc17e72 100644 --- a/src/pages/Topup.jsx +++ b/src/pages/Topup.jsx @@ -8,7 +8,6 @@ import { createCryptoOrder, getCryptoOrderStatus, getTopupHistory, Q, quotaToDollar, } from '../api'; -import SpotlightCard from '../components/bits/SpotlightCard'; import CountUp from '../components/bits/CountUp'; import toast from 'react-hot-toast'; @@ -322,27 +321,27 @@ export default function Topup() { {/* Balance Stats */}
- +

{t('dashboard.balance')}

$

{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}

- +
- +

{t('dashboard.used')}

$
- +
- +

{t('dashboard.totalRequests')}

- +
{/* Online Topup - EPay & Stripe */} @@ -369,7 +368,7 @@ export default function Topup() { className={`py-2.5 px-3 rounded-xl text-sm font-medium transition-all ${ selectedPreset === val ? 'bg-brand-500 text-white shadow-lg shadow-brand-500/25' - : 'glass-sm text-page-label hover:text-page hover:bg-white/[0.08]' + : 'glass-sm text-page-label hover:text-page hover:bg-page-surface-hover' }`} > ${val} @@ -399,7 +398,7 @@ export default function Topup() {

{t('topup.calculating')}

) : payAmount ? (

- {t('topup.payAmountLabel')}: ¥{payAmount} + {t('topup.payAmountLabel')}: ¥{payAmount}

) : null}
@@ -415,7 +414,7 @@ export default function Topup() { key={method.type} onClick={() => handlePay(method.type)} disabled={paymentLoading || !amount} - className="px-4 py-2.5 rounded-xl text-sm font-medium glass-sm text-page-label hover:text-page hover:bg-white/[0.08] disabled:opacity-40 disabled:cursor-not-allowed transition-all" + className="px-4 py-2.5 rounded-xl text-sm font-medium glass-sm text-page-label hover:text-page hover:bg-page-surface-hover disabled:opacity-40 disabled:cursor-not-allowed transition-all" > {isCurrentLoading ? t('topup.processing') : method.name} @@ -437,12 +436,12 @@ export default function Topup() { className={`glass-sm rounded-xl p-4 cursor-pointer transition-all ${ selectedCreemProduct?.productId === product.productId ? 'ring-2 ring-brand-500 bg-brand-500/10' - : 'hover:bg-white/[0.06]' + : 'hover:bg-page-surface' }`} onClick={() => setSelectedCreemProduct(product)} >

{product.name}

-

+

${product.price} {product.currency || 'USD'}

{product.quota && ( @@ -499,7 +498,7 @@ export default function Topup() { className={`px-4 py-2 rounded-xl text-sm font-medium transition-all ${ selectedChain === chain.key ? 'bg-brand-500 text-white shadow-lg shadow-brand-500/25' - : 'glass-sm text-page-label hover:text-page hover:bg-white/[0.08]' + : 'glass-sm text-page-label hover:text-page hover:bg-page-surface-hover' }`} > {chain.label} @@ -519,7 +518,7 @@ export default function Topup() { className={`px-4 py-2 rounded-xl text-sm font-medium transition-all ${ selectedToken === token ? 'bg-brand-500 text-white shadow-lg shadow-brand-500/25' - : 'glass-sm text-page-label hover:text-page hover:bg-white/[0.08]' + : 'glass-sm text-page-label hover:text-page hover:bg-page-surface-hover' }`} > {token.toUpperCase()} @@ -599,10 +598,10 @@ export default function Topup() {
{item.status === 'success' ? t('topup.statusSuccess') : item.status === 'pending' ? t('topup.statusPending') : t('topup.statusFailed')} diff --git a/tailwind.config.js b/tailwind.config.js index 3ebe6d0..4448bbf 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -37,6 +37,10 @@ export default { muted: 'rgb(var(--page-text-muted) / )', label: 'rgb(var(--page-label) / )', link: 'rgb(var(--page-link) / )', + success: 'rgb(var(--page-success-text) / )', + warning: 'rgb(var(--page-warning-text) / )', + danger: 'rgb(var(--page-danger-text) / )', + info: 'rgb(var(--page-info-text) / )', }, }, animation: {