mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-31 16:26:30 +00:00
feat: add model plaza with group-scoped pricing showcase
- public /model-plaza page (standalone + admin-embedded) listing groups with discounted effective prices alongside LiteLLM official reference - faceted platform/group/rate filters: cross-dimension options gray out instead of disappearing, platform-tinted chips via accent color-mix - paid-price columns highlighted with per-platform tint band - OptionalJWT middleware so anonymous and signed-in users share one route - admin settings: enable switch, require-auth switch, markdown description
This commit is contained in:
@@ -104,6 +104,11 @@ export const FeatureFlags = {
|
||||
mode: 'opt-in',
|
||||
label: 'Available Channels',
|
||||
}),
|
||||
modelPlaza: defineFlag({
|
||||
key: 'model_plaza_enabled',
|
||||
mode: 'opt-in',
|
||||
label: 'Model Plaza',
|
||||
}),
|
||||
payment: defineFlag({
|
||||
key: 'payment_enabled',
|
||||
mode: 'opt-out',
|
||||
|
||||
@@ -39,6 +39,29 @@ const BORDER: Record<Platform, string> = {
|
||||
}
|
||||
const BORDER_DEFAULT = 'border-gray-200 dark:border-dark-700'
|
||||
|
||||
// ── Border strong (higher-contrast platform tint, e.g. plaza group cards) ──
|
||||
const BORDER_STRONG: Record<Platform, string> = {
|
||||
anthropic: 'border-orange-500/35 dark:border-orange-500/30',
|
||||
openai: 'border-green-500/35 dark:border-green-500/30',
|
||||
antigravity: 'border-purple-500/35 dark:border-purple-500/30',
|
||||
gemini: 'border-blue-500/35 dark:border-blue-500/30',
|
||||
grok: 'border-zinc-800/35 dark:border-zinc-500/35',
|
||||
composite: 'border-cyan-500/35 dark:border-cyan-500/30',
|
||||
}
|
||||
const BORDER_STRONG_DEFAULT = 'border-gray-300 dark:border-dark-600'
|
||||
|
||||
// ── Accent (single raw color per platform; consumers derive washes/tints
|
||||
// from it via CSS color-mix, e.g. plaza paid-price zone) ──
|
||||
const ACCENT: Record<Platform, string> = {
|
||||
anthropic: '#f97316', // orange-500
|
||||
openai: '#22c55e', // green-500
|
||||
antigravity: '#a855f7', // purple-500
|
||||
gemini: '#3b82f6', // blue-500
|
||||
grok: '#71717a', // zinc-500
|
||||
composite: '#06b6d4', // cyan-500
|
||||
}
|
||||
const ACCENT_DEFAULT = '#14b8a6' // primary-500 (teal)
|
||||
|
||||
// ── Accent bar (gradient) ───────────────────────────────────────────
|
||||
const ACCENT_BAR: Record<Platform, string> = {
|
||||
anthropic: 'bg-gradient-to-r from-orange-400 to-orange-500',
|
||||
@@ -144,6 +167,14 @@ export function platformBorderClass(p: string): string {
|
||||
return isPlatform(p) ? BORDER[p] : BORDER_DEFAULT
|
||||
}
|
||||
|
||||
export function platformBorderStrongClass(p: string): string {
|
||||
return isPlatform(p) ? BORDER_STRONG[p] : BORDER_STRONG_DEFAULT
|
||||
}
|
||||
|
||||
export function platformAccentColor(p: string): string {
|
||||
return isPlatform(p) ? ACCENT[p] : ACCENT_DEFAULT
|
||||
}
|
||||
|
||||
export function platformAccentBarClass(p: string): string {
|
||||
return isPlatform(p) ? ACCENT_BAR[p] : ACCENT_BAR_DEFAULT
|
||||
}
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
/**
|
||||
* formatScaled formats a per-token (or per-request) USD price scaled by `scale`.
|
||||
*
|
||||
* formatScaled(0.000003, 1_000_000) → "$3" // per 1M tokens
|
||||
* formatScaled(0.5, 1) → "$0.5" // per request
|
||||
* formatScaled(null, 1_000_000) → "-"
|
||||
* formatScaled(0.000003, 1_000_000) → "$3" // per 1M tokens
|
||||
* formatScaled(0.5, 1) → "$0.5" // per request
|
||||
* formatScaled(null, 1_000_000) → "-"
|
||||
* formatScaled(0.000003, 1_000_000, 2) → "$3.00" // pad to ≥2 decimals
|
||||
* formatScaled(1.25e-8, 1_000_000, 2) → "$0.0125" // longer decimals kept as-is
|
||||
*
|
||||
* Uses toPrecision(10) then strips trailing zeros to avoid IEEE 754 display noise.
|
||||
* `minFractionDigits` pads the result back up to a minimum number of decimals.
|
||||
*/
|
||||
export function formatScaled(value: number | null, scale: number): string {
|
||||
export function formatScaled(value: number | null, scale: number, minFractionDigits = 0): string {
|
||||
if (value == null) return '-'
|
||||
return `$${(value * scale).toPrecision(10).replace(/\.?0+$/, '')}`
|
||||
let s = (value * scale).toPrecision(10).replace(/\.?0+$/, '')
|
||||
if (minFractionDigits > 0 && !s.includes('e')) {
|
||||
const dot = s.indexOf('.')
|
||||
const digits = dot === -1 ? 0 : s.length - dot - 1
|
||||
if (digits < minFractionDigits) {
|
||||
s = (dot === -1 ? `${s}.` : s) + '0'.repeat(minFractionDigits - digits)
|
||||
}
|
||||
}
|
||||
return `$${s}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user