diff --git a/dist/index.html b/dist/index.html index 73a22d2..af3cfd9 100644 --- a/dist/index.html +++ b/dist/index.html @@ -7,8 +7,8 @@ AI Platform - - + +
diff --git a/src/index.css b/src/index.css index 3ec8d5b..174dfa6 100644 --- a/src/index.css +++ b/src/index.css @@ -5,6 +5,16 @@ @layer base { :root { --brand: 99 102 241; + /* Light theme text colors (overridden by .theme-light) */ + --page-text: 255 255 255; + --page-text-secondary: 163 163 163; + --page-text-muted: 115 115 115; + --page-label: 212 212 212; + --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; } body { @@ -16,7 +26,6 @@ @apply bg-brand-500/30; } - /* Scrollbar */ ::-webkit-scrollbar { @apply w-1.5; } @@ -26,37 +35,74 @@ ::-webkit-scrollbar-thumb { @apply bg-neutral-700 rounded-full; } + + .theme-light ::-webkit-scrollbar-thumb { + @apply bg-neutral-300; + } + + /* Light theme variable overrides */ + .theme-light { + --page-text: 17 24 39; + --page-text-secondary: 107 114 128; + --page-text-muted: 156 163 175; + --page-label: 55 65 81; + --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; + } + + .theme-claude { + --page-link: 217 119 87; + } } @layer components { .glass { - @apply bg-white/[0.03] backdrop-blur-xl border border-white/[0.06] rounded-2xl; + background: var(--page-card-bg); + backdrop-filter: blur(16px); + border: 1px solid var(--page-card-border); + @apply rounded-2xl; } .glass-sm { - @apply bg-white/[0.04] backdrop-blur-md border border-white/[0.08] rounded-xl; + background: var(--page-card-bg); + backdrop-filter: blur(12px); + border: 1px solid var(--page-card-border); + @apply rounded-xl; } .btn-primary { - @apply px-6 py-2.5 rounded-xl bg-brand-600 text-white font-medium text-sm + @apply px-6 py-2.5 rounded-xl bg-brand-600 font-medium text-sm hover:bg-brand-500 active:bg-brand-700 transition-all duration-200 ease-out shadow-lg shadow-brand-600/20 disabled:opacity-50 disabled:cursor-not-allowed; + color: #fff; } .btn-secondary { - @apply px-6 py-2.5 rounded-xl border border-white/10 text-white font-medium text-sm - hover:bg-white/5 active:bg-white/10 + @apply px-6 py-2.5 rounded-xl font-medium text-sm transition-all duration-200 ease-out disabled:opacity-50 disabled:cursor-not-allowed; + border: 1px solid var(--page-input-border); + color: rgb(var(--page-label)); + } + .btn-secondary:hover { + background: rgba(var(--page-text), 0.05); } .input { - @apply w-full px-4 py-2.5 rounded-xl border border-white/10 bg-white/[0.04] - text-white placeholder-neutral-500 text-sm + @apply w-full px-4 py-2.5 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500/60 transition-all duration-200; + background: var(--page-input-bg); + border: 1px solid var(--page-input-border); + color: rgb(var(--page-text)); + } + .input::placeholder { + color: rgb(var(--page-text-muted)); } .card-glow { @@ -72,3 +118,67 @@ @apply opacity-100; } } + +/* ═══════════════════════════════════════════════════════════ + Light theme shared-page color overrides + Outside @layer to win specificity over Tailwind utilities. + ═══════════════════════════════════════════════════════════ */ + +/* Map dark-theme text utilities to light-appropriate colors. + Uses a data attribute to avoid Tailwind circular-dependency issues. */ +.theme-light { + /* Override body defaults scoped to this container */ + color: rgb(17 24 39); +} + +/* Glass cards get subtle shadow in light mode */ +.theme-light .glass, +.theme-light .glass-sm { + box-shadow: 0 1px 3px rgba(0,0,0,0.06); +} + +/* Light theme input focus */ +.theme-light .input:focus { + border-color: rgb(99 102 241); + box-shadow: 0 0 0 3px rgba(99,102,241,0.15); +} + +/* Card glow toned down */ +.theme-light .card-glow::before { + background: linear-gradient(to right, rgba(99,102,241,0.06), rgba(168,85,247,0.06), rgba(236,72,153,0.06)); +} + +/* ═══════════════════════════════════════════════ + Claude theme accent overrides + ═══════════════════════════════════════════════ */ +.theme-claude .btn-primary { + background-color: #D97757; + box-shadow: 0 4px 14px rgba(217,119,87,0.15); +} +.theme-claude .btn-primary:hover { + background-color: #C4613F; +} +.theme-claude .btn-primary:active { + background-color: #A85232; +} + +.theme-claude .glass, +.theme-claude .glass-sm { + border-color: #E8DDD0; +} + +.theme-claude .input { + border-color: #D9C5B2; +} +.theme-claude .input:focus { + border-color: #D97757; + box-shadow: 0 0 0 3px rgba(217,119,87,0.15); +} + +.theme-claude .btn-secondary { + border-color: #D9C5B2; + color: #6B5D4F; +} +.theme-claude .btn-secondary:hover { + background-color: rgba(217,119,87,0.05); +} diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index 99d59de..8d5c099 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -50,33 +50,33 @@ export default function Dashboard() {
{/* Welcome */}
-

+

{t('dashboard.welcome')}

-

{t('dashboard.manageDesc')}

+

{t('dashboard.manageDesc')}

{/* Stats Cards */}
-

{t('dashboard.balance')}

-
+

{t('dashboard.balance')}

+
$
-

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

+

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

-

{t('dashboard.used')}

-
+

{t('dashboard.used')}

+
$
-

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

+

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

-

{t('dashboard.totalRequests')}

-
+

{t('dashboard.totalRequests')}

+
@@ -86,7 +86,7 @@ export default function Dashboard() {
{/* Redeem Code */}
-

{t('dashboard.redeemCode')}

+

{t('dashboard.redeemCode')}

-

{t('dashboard.quickLinks')}

+

{t('dashboard.quickLinks')}

-

{t('dashboard.apiKeys')}

-

{t('dashboard.manageKeys')}

+

{t('dashboard.apiKeys')}

+

{t('dashboard.manageKeys')}

-

{t('dashboard.packages')}

-

{t('dashboard.viewPlans')}

+

{t('dashboard.packages')}

+

{t('dashboard.viewPlans')}

-

{t('dashboard.pricing')}

-

{t('dashboard.modelPrices')}

+

{t('dashboard.pricing')}

+

{t('dashboard.modelPrices')}

-

{t('dashboard.apiDocs')}

-

{t('dashboard.comingSoon')}

+

{t('dashboard.apiDocs')}

+

{t('dashboard.comingSoon')}

diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index f60e77e..42990ac 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -47,15 +47,15 @@ export default function Login() {
-

{t('login.welcomeBack')}

-

+

{t('login.welcomeBack')}

+

{site?.name ? t('login.signInTo', { name: site.name }) : t('login.signInToDefault')}

- +
- +
-

+

{t('login.noAccount')}{' '} - + {t('login.createOne')}

diff --git a/src/pages/Packages.jsx b/src/pages/Packages.jsx index 0f91004..a739769 100644 --- a/src/pages/Packages.jsx +++ b/src/pages/Packages.jsx @@ -98,8 +98,8 @@ export default function Packages() { return (
-

{t('packages.title')}

-

+

{t('packages.title')}

+

{t('packages.subtitle')}

@@ -107,7 +107,7 @@ export default function Packages() { {/* Active Subscriptions */} {activeSubs.length > 0 && (
-

+

{t('packages.mySubscriptions')}

@@ -119,14 +119,14 @@ export default function Packages() { return (
- + {t('packages.subscriptionId', { id: sub.id })} {t('packages.active')}
-
+
{t('packages.expires')}: {formatDate(sub.end_time)} {sub.next_reset_time > 0 && ( {t('packages.nextReset')}: {formatDate(sub.next_reset_time)} @@ -137,7 +137,7 @@ export default function Packages() {
- + ${(remain / Q).toFixed(2)} / ${(total / Q).toFixed(2)}
@@ -149,9 +149,9 @@ export default function Packages() { )} {enabled.length === 0 ? ( -
+

{t('packages.noPackages')}

- + {t('packages.checkPricing')} →
@@ -170,7 +170,7 @@ export default function Packages() { {/* Header */}
-

{pkg.name}

+

{pkg.name}

{isSubscription && ( {getResetLabel(resetPeriod)} @@ -178,27 +178,27 @@ export default function Packages() { )}
{pkg.description && ( -

{pkg.description}

+

{pkg.description}

)}
{/* Price */}
- ${Number(pkg.price).toFixed(2)} + ${Number(pkg.price).toFixed(2)} {pkg.original_price > 0 && pkg.original_price > pkg.price && ( - ${Number(pkg.original_price).toFixed(2)} + ${Number(pkg.original_price).toFixed(2)} )}
{pkg.duration > 0 && ( -

{t('packages.daysAccess', { count: pkg.duration })}

+

{t('packages.daysAccess', { count: pkg.duration })}

)}
{/* Features */}
    {pkg.quota_amount > 0 && ( -
  • +
  • @@ -209,20 +209,20 @@ export default function Packages() {
  • )} {isSubscription && ( -
  • +
  • {t('packages.unusedQuotaExpires')}
  • )} -
  • +
  • {t('packages.allModels')}
  • -
  • +
  • @@ -254,8 +254,8 @@ export default function Packages() { return (
    !subscribing && setConfirmPkg(null)}>
    e.stopPropagation()}> -

    {t('packages.confirmTitle')}

    -

    +

    {t('packages.confirmTitle')}

    +

    {t('packages.confirmDesc', { name: confirmPkg.name, price: pkgPrice.toFixed(2) })}

    {isSubscription && ( @@ -269,7 +269,7 @@ export default function Packages() {

    )} -

    +

    {t('packages.yourBalance')} ${userBalance.toFixed(2)}

    {insufficient && ( diff --git a/src/pages/Pricing.jsx b/src/pages/Pricing.jsx index b9d9e4a..c983cf6 100644 --- a/src/pages/Pricing.jsx +++ b/src/pages/Pricing.jsx @@ -35,8 +35,8 @@ export default function Pricing() { return (
    -

    {t('pricing.title')}

    -

    +

    {t('pricing.title')}

    +

    {t('pricing.subtitle')}

    @@ -44,7 +44,7 @@ export default function Pricing() { {/* Search */}
    - +
    +
    {search ? t('pricing.noMatch') : t('pricing.noModels')}
    ) : ( @@ -66,29 +66,29 @@ export default function Pricing() { - - - - + + + + {filtered.map((m, i) => ( - -
    {t('pricing.model')}{t('pricing.inputPrice')}{t('pricing.outputPrice')}{t('pricing.status')}{t('pricing.model')}{t('pricing.inputPrice')}{t('pricing.outputPrice')}{t('pricing.status')}
    - {m.display_name || m.model_name} + {m.display_name || m.model_name} + {m.input_price != null ? `$${(Number(m.input_price) * 1000).toFixed(4)}` : '-'} + {m.output_price != null ? `$${(Number(m.output_price) * 1000).toFixed(4)}` : '-'} {m.status === 'healthy' ? t('pricing.online') : t('pricing.unknown')} diff --git a/src/pages/Register.jsx b/src/pages/Register.jsx index bfcf177..63ef58b 100644 --- a/src/pages/Register.jsx +++ b/src/pages/Register.jsx @@ -60,15 +60,15 @@ export default function Register() {
    -

    {t('register.createAccount')}

    -

    +

    {t('register.createAccount')}

    +

    {site?.name ? t('register.getStartedWith', { name: site.name }) : t('register.getStartedDefault')}

    - +
    - +
    - +
    - +
    -

    +

    {t('register.hasAccount')}{' '} - + {t('register.signIn')}

    diff --git a/src/pages/Tokens.jsx b/src/pages/Tokens.jsx index 05f9e7f..a12342f 100644 --- a/src/pages/Tokens.jsx +++ b/src/pages/Tokens.jsx @@ -103,8 +103,8 @@ export default function Tokens() {
    -

    {t('tokens.title')}

    -

    {t('tokens.subtitle')}

    +

    {t('tokens.title')}

    +

    {t('tokens.subtitle')}

    @@ -212,14 +212,14 @@ export default function Tokens() { {/* Info */}
    -

    {token.name}

    -

    +

    {token.name}

    +

    sk-***

    {/* Created time */} - + {token.created_time ? new Date(token.created_time * 1000).toLocaleDateString() : ''} @@ -230,7 +230,7 @@ export default function Tokens() { className={`px-3 py-1 text-xs rounded-lg border transition-colors ${ token.status === 1 ? 'border-green-500/30 text-green-400 hover:bg-green-500/10' - : 'border-neutral-600 text-neutral-400 hover:bg-neutral-800' + : 'border-neutral-600 text-page-secondary hover:bg-neutral-800' }`} > {token.status === 1 ? t('tokens.enabled') : t('tokens.disabled')} @@ -249,19 +249,19 @@ export default function Tokens() { {/* API Usage Note */}
    -

    {t('tokens.quickStart')}

    +

    {t('tokens.quickStart')}

    -

    {t('tokens.quickStartComment')}

    +

    {t('tokens.quickStartComment')}

    curl {window.location.origin}/v1/chat/completions \

    -

    +

    -H "Authorization: Bearer sk-your-key" \

    -

    +

    -H "Content-Type: application/json" \

    -

    +

    -d '{`{"model":"gpt-4o","messages":[{"role":"user","content":"Hello!"}]}`}'

    diff --git a/src/pages/Topup.jsx b/src/pages/Topup.jsx index e58f8d1..0bbd364 100644 --- a/src/pages/Topup.jsx +++ b/src/pages/Topup.jsx @@ -316,30 +316,30 @@ export default function Topup() { return (
    -

    {t('topup.title')}

    -

    {t('topup.subtitle')}

    +

    {t('topup.title')}

    +

    {t('topup.subtitle')}

    {/* Balance Stats */}
    -

    {t('dashboard.balance')}

    -
    +

    {t('dashboard.balance')}

    +
    $
    -

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

    +

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

    -

    {t('dashboard.used')}

    -
    +

    {t('dashboard.used')}

    +
    $
    -

    {t('dashboard.totalRequests')}

    -
    +

    {t('dashboard.totalRequests')}

    +
    @@ -349,10 +349,10 @@ export default function Topup() { {site?.enable_topup && (enableOnline || enableStripe) && epayAndStripeMethods.length > 0 && (
    -

    {t('topup.onlineTopup')}

    +

    {t('topup.onlineTopup')}

    @@ -360,7 +360,7 @@ export default function Topup() { {/* Preset Amounts */}
    - +
    {presetAmounts.map((val) => ( @@ -429,7 +429,7 @@ export default function Topup() { {/* Creem Products Section */} {site?.enable_topup && enableCreem && creemProducts.length > 0 && (
    -

    {t('topup.creemPayment') || 'Creem Payment'}

    +

    {t('topup.creemPayment') || 'Creem Payment'}

    {creemProducts.map((product) => (
    setSelectedCreemProduct(product)} > -

    {product.name}

    +

    {product.name}

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

    {product.quota && ( -

    {t('topup.quotaIncluded') || `${product.quota} quota`}

    +

    {t('topup.quotaIncluded') || `${product.quota} quota`}

    )}
    ))} @@ -470,11 +470,11 @@ export default function Topup() { {/* Crypto Payment Section */} {site?.enable_topup && enableCrypto && availableChains.length > 0 && (
    -

    {t('topup.cryptoPayment')}

    +

    {t('topup.cryptoPayment')}

    {/* Amount input for crypto */}
    - + - +
    {availableChains.map((chain) => ( @@ -580,20 +580,20 @@ export default function Topup() { {/* History */} {showHistory && (
    -

    {t('topup.history')}

    +

    {t('topup.history')}

    {historyLoading ? (
    ) : history.length === 0 ? ( -

    {t('topup.noHistory')}

    +

    {t('topup.noHistory')}

    ) : (
    {history.map((item, i) => (
    -

    ${item.amount}

    -

    +

    ${item.amount}

    +

    {new Date(item.create_time * 1000).toLocaleString()} · {item.payment_method || t('topup.redeemCode')}

    @@ -615,7 +615,7 @@ export default function Topup() { {/* Redeem Code */}
    -

    {t('topup.redeemTitle')}

    +

    {t('topup.redeemTitle')}

    !n.auth || user); return ( -
    +
    {/* Announcement */} {site?.announcement && (
    diff --git a/src/themes/clean/Layout.jsx b/src/themes/clean/Layout.jsx index a5496eb..860b290 100644 --- a/src/themes/clean/Layout.jsx +++ b/src/themes/clean/Layout.jsx @@ -27,7 +27,7 @@ export default function CleanLayout() { const visibleNavItems = navItems.filter((n) => !n.auth || user); return ( -
    +
    {/* Announcement */} {site?.announcement && (
    diff --git a/src/themes/corporate/Layout.jsx b/src/themes/corporate/Layout.jsx index 524f498..8fa90d2 100644 --- a/src/themes/corporate/Layout.jsx +++ b/src/themes/corporate/Layout.jsx @@ -27,7 +27,7 @@ export default function CorporateLayout() { const visibleNavItems = navItems.filter((n) => !n.auth || user); return ( -
    +
    {/* Announcement */} {site?.announcement && (
    diff --git a/tailwind.config.js b/tailwind.config.js index e7d1988..3ebe6d0 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -30,6 +30,14 @@ export default { 800: '#3730a3', 900: '#312e81', }, + /* Theme-aware page colors via CSS variables */ + page: { + DEFAULT: 'rgb(var(--page-text) / )', + secondary: 'rgb(var(--page-text-secondary) / )', + muted: 'rgb(var(--page-text-muted) / )', + label: 'rgb(var(--page-label) / )', + link: 'rgb(var(--page-link) / )', + }, }, animation: { 'star-movement-bottom': 'star-movement-bottom linear infinite alternate',