mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-13 04:58:50 +00:00
The Usage tab shows a bar for each configured limit. It turns amber at 80% and red at 100%. The tab also shows rejected requests per day, a projected month-end cost with budget headroom, and CSV export for users and groups. Names link to Charts with the matching scope. The Charts tab ranks models over the full 24h, 30d or 12mo window. A provider filter is applied on the server, and the ranking can be exported as CSV. A new Events tab lists recent failures, filtered by kind and user. The selected tab, filters and scope live in the URL hash. Polling stops while the page is hidden. Views show which replica answered, and a banner lists configuration warnings. CSV cells that start with a formula character are neutralised. Links sit outside the accordion buttons, so both controls keep their accessible names. admin_assets_gen.go is regenerated. Claude-Session: https://claude.ai/code/session_01V5jYJNxpDFdJkyyExpADrv
188 lines
8.4 KiB
CSS
188 lines
8.4 KiB
CSS
@import "tailwindcss";
|
|
@import "tw-animate-css";
|
|
@import "shadcn-vue/tailwind.css";
|
|
|
|
/*
|
|
* Theme tokens (design note, not slop-by-default): shadcn-vue's `init`
|
|
* scaffolds an achromatic "stone" palette (chroma 0 everywhere), a Google
|
|
* Fonts Inter import, and a Geist Variable heading font. All three are
|
|
* rewritten below:
|
|
*
|
|
* - No Google Fonts import, no Geist. The admin CSP (admin.go) ships
|
|
* style-src 'self' with no external host allowed, and the panel must load
|
|
* zero external assets per the operator brief — a network font would
|
|
* silently fail under that CSP anyway. Headings and body both use the
|
|
* platform's own UI font stack; numeric/tabular data (usage tables, chart
|
|
* axes) uses the platform monospace stack for column alignment.
|
|
* - A deliberate signature color (deep teal, hue ~200) replaces the
|
|
* grayscale-everything default and the reflexive indigo/purple accent.
|
|
* - Chart series use the Okabe & Ito (2008) colorblind-safe qualitative
|
|
* palette rather than picking hues by eye — distinguishable under
|
|
* protanopia, deuteranopia, and tritanopia alike.
|
|
* - Dark mode is driven by `prefers-color-scheme`, not a `.dark` class
|
|
* toggle: this panel has no theme switcher, so the OS setting is the only
|
|
* signal there is. Tailwind v4's default `dark:` variant already means
|
|
* "@media (prefers-color-scheme: dark)" — shadcn-vue's init overrides it
|
|
* to a `.dark`-class variant for apps with a manual theme toggle; that
|
|
* override is dropped here so any `dark:` utility (ours or a generated
|
|
* ui/ primitive's) follows the OS setting, same as the CSS variables below.
|
|
*/
|
|
|
|
@theme inline {
|
|
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, monospace;
|
|
--color-ring: var(--ring);
|
|
--color-input: var(--input);
|
|
--color-border: var(--border);
|
|
--color-destructive: var(--destructive);
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
--color-accent: var(--accent);
|
|
--color-muted-foreground: var(--muted-foreground);
|
|
--color-muted: var(--muted);
|
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
--color-secondary: var(--secondary);
|
|
--color-primary-foreground: var(--primary-foreground);
|
|
--color-primary: var(--primary);
|
|
--color-popover-foreground: var(--popover-foreground);
|
|
--color-popover: var(--popover);
|
|
--color-card-foreground: var(--card-foreground);
|
|
--color-card: var(--card);
|
|
--color-foreground: var(--foreground);
|
|
--color-background: var(--background);
|
|
--color-chart-requests: var(--chart-requests);
|
|
--color-chart-tokens-in: var(--chart-tokens-in);
|
|
--color-chart-tokens-out: var(--chart-tokens-out);
|
|
--color-chart-cost: var(--chart-cost);
|
|
--color-status-warn: var(--status-warn);
|
|
--color-bar-amber: var(--bar-amber);
|
|
--radius-sm: calc(var(--radius) - 4px);
|
|
--radius-md: calc(var(--radius) - 2px);
|
|
--radius-lg: var(--radius);
|
|
--radius-xl: calc(var(--radius) + 4px);
|
|
/* Powers the `font-heading` utility Tailwind v4 auto-generates from any
|
|
--font-* @theme token — CardTitle.vue and AlertTitle.vue apply it (a
|
|
review fix: they previously wrote the unrecognized class
|
|
`cn-font-heading`, matching nothing this token or Tailwind's engine
|
|
generates, so it rendered as a plain no-op string with zero styling
|
|
effect). Same font as body today; the token exists so a future
|
|
distinct heading face is a one-line change here, not a find-replace
|
|
across every component that uses it. */
|
|
--font-heading: var(--font-sans);
|
|
}
|
|
|
|
:root {
|
|
--background: oklch(0.99 0.004 75);
|
|
--foreground: oklch(0.19 0.012 75);
|
|
--card: oklch(0.995 0.003 75);
|
|
--card-foreground: oklch(0.19 0.012 75);
|
|
--popover: oklch(0.995 0.003 75);
|
|
--popover-foreground: oklch(0.19 0.012 75);
|
|
--primary: oklch(0.46 0.09 200);
|
|
--primary-foreground: oklch(0.98 0.006 200);
|
|
--secondary: oklch(0.95 0.006 75);
|
|
--secondary-foreground: oklch(0.22 0.01 75);
|
|
--muted: oklch(0.95 0.006 75);
|
|
--muted-foreground: oklch(0.48 0.012 75);
|
|
--accent: oklch(0.93 0.02 200);
|
|
--accent-foreground: oklch(0.28 0.05 200);
|
|
--destructive: oklch(0.55 0.2 25);
|
|
--border: oklch(0.9 0.007 75);
|
|
--input: oklch(0.9 0.007 75);
|
|
--ring: oklch(0.55 0.09 200);
|
|
--radius: 0.375rem;
|
|
|
|
/* Chart series — Okabe & Ito (2008) colorblind-safe qualitative set. */
|
|
--chart-requests: #009e73;
|
|
--chart-tokens-in: #0072b2;
|
|
--chart-tokens-out: #e69f00;
|
|
--chart-cost: #d55e00;
|
|
|
|
/*
|
|
* --status-warn: a semantic "degraded, not broken" indicator (the
|
|
* Providers tab's amber success-rate badge, ProviderRateBadge.vue) —
|
|
* deliberately its OWN token, not a reuse of --chart-tokens-out despite
|
|
* sharing that token's hue family. --chart-tokens-out is tuned to read
|
|
* against a chart's plot background at a small mark size, not as body
|
|
* text on --card; measured here at #e69f00-on-card it comes out to
|
|
* ~2.22:1, well under WCAG 1.4.3's 4.5:1 text-contrast minimum — a
|
|
* genuine accessibility bug on exactly the badge tier meant to say
|
|
* "something's wrong" (MUST-1, v0.22 review round). #a35c00 on the
|
|
* light --card measures ~5.06:1.
|
|
*/
|
|
--status-warn: #a35c00;
|
|
|
|
/*
|
|
* --bar-amber: F1 (usage-vs-limit bars, UsageBar.vue) — the filled
|
|
* amber segment of a budget bar at BAR_AMBER_THRESHOLD (lib/usage-
|
|
* bars.ts). The track it fills against is --muted (UsageBar.vue's
|
|
* `bg-muted` on the bar's own background span) — WCAG 1.4.11 non-text
|
|
* contrast requires 3:1 against that ADJACENT color.
|
|
*
|
|
* P8 review fix: this used to match --chart-tokens-out's hue/value
|
|
* (#e69f00) on the reasoning that it was "already proven legible at
|
|
* small UI sizes on both --card and --muted" — that reasoning was
|
|
* never actually checked against --muted specifically, and #e69f00
|
|
* measures (WCAG relative-luminance formula, oklch(0.95 0.006 75)
|
|
* --muted as the track) 1.95:1 there, well under the 3:1 this comment
|
|
* itself claimed. Reusing --status-warn's own light-mode value
|
|
* (#a35c00) instead measures 4.44:1 against --muted — comfortably
|
|
* clears 3:1, and keeps every semantic amber in this panel visually
|
|
* consistent (same value already used for the "degraded" badge tier,
|
|
* ProviderRateBadge.vue) rather than adding a third amber shade.
|
|
*/
|
|
--bar-amber: #a35c00;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--background: oklch(0.17 0.007 75);
|
|
--foreground: oklch(0.95 0.005 75);
|
|
--card: oklch(0.21 0.009 75);
|
|
--card-foreground: oklch(0.95 0.005 75);
|
|
--popover: oklch(0.21 0.009 75);
|
|
--popover-foreground: oklch(0.95 0.005 75);
|
|
--primary: oklch(0.74 0.11 200);
|
|
--primary-foreground: oklch(0.16 0.012 200);
|
|
--secondary: oklch(0.27 0.011 75);
|
|
--secondary-foreground: oklch(0.93 0.005 75);
|
|
--muted: oklch(0.27 0.011 75);
|
|
--muted-foreground: oklch(0.68 0.012 75);
|
|
--accent: oklch(0.32 0.035 200);
|
|
--accent-foreground: oklch(0.91 0.03 200);
|
|
--destructive: oklch(0.68 0.19 25);
|
|
--border: oklch(1 0 0 / 12%);
|
|
--input: oklch(1 0 0 / 16%);
|
|
--ring: oklch(0.62 0.1 200);
|
|
|
|
/* Same hues, most already legible on a dark ground at this
|
|
saturation — Okabe-Ito was designed for slide/print use on light
|
|
and dark backgrounds alike. */
|
|
--chart-requests: #16b891;
|
|
--chart-tokens-in: #4098d7;
|
|
--chart-tokens-out: #f0ac1f;
|
|
--chart-cost: #e8763a;
|
|
|
|
/* #f0ac1f on the dark --card measures ~8.97:1 — no separate dark
|
|
value was needed to clear WCAG 1.4.3 here, unlike light mode
|
|
(see --status-warn's own doc comment above), but the token is
|
|
still redefined per-mode rather than left to fall through, so a
|
|
reader auditing this block sees every semantic-color token's
|
|
dark value in one place. */
|
|
--status-warn: #f0ac1f;
|
|
|
|
/* Same reasoning as --chart-tokens-out's own dark-mode comment above — legible as-is, redefined here anyway so every semantic-color token's dark value lives in one place. */
|
|
--bar-amber: #f0ac1f;
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
@apply border-border outline-ring/50;
|
|
}
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
@apply font-sans;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
}
|