mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 06:02:14 +03:00
fix(dashboard): data tables paint an opaque surface so the grid doesn't bleed through (#4233)
Integrated into release/v3.8.29
This commit is contained in:
committed by
GitHub
parent
48773face6
commit
291103513e
@@ -1,6 +1,6 @@
|
||||
# OmniRoute — Design System & Visual Identity
|
||||
|
||||
> **Status:** standardization plan. **Phases 1–3 are implemented in this PR** (grid, primitives, status-color centralization, mono token, and the DataTable token migration). The DataTable migration is **faithful** — dark stays byte-identical (the new `--table-*` dark values equal the old hardcoded rgba); light is fixed (it was buggy always-dark via dead `var()` fallbacks). **⚠️ Wants a visual pass before merge** (light-theme tables + the secondary-text shift `#888`→`--color-text-muted`). **Phase 4 is now largely done too** (C6 focus-ring → accent, C7 Checkbox/Textarea primitives, C9 `cn()`→tailwind-merge); only the selective C8 hex-sweep remains. Note several remaining "hardcoded" hex are _intentional_ (always-dark console terminal, ReactFlow SVG strokes) and must NOT be swept. **Phase 5 (D4 + D8): the grid now reaches every standalone screen** (login/auth/error/legal/status/onboarding — their opaque `bg-bg` full-screen wrappers were hiding it) **and the dashboard content shell is fluid up to 4K** (`max-w-7xl` → `max-w-[3840px]`) so it follows the viewport on large monitors instead of centering with wide side gutters.
|
||||
> **Status:** standardization plan. **Phases 1–3 are implemented in this PR** (grid, primitives, status-color centralization, mono token, and the DataTable token migration). The DataTable migration is **faithful** — dark stays byte-identical (the new `--table-*` dark values equal the old hardcoded rgba); light is fixed (it was buggy always-dark via dead `var()` fallbacks). **⚠️ Wants a visual pass before merge** (light-theme tables + the secondary-text shift `#888`→`--color-text-muted`). **Phase 4 is now largely done too** (C6 focus-ring → accent, C7 Checkbox/Textarea primitives, C9 `cn()`→tailwind-merge); only the selective C8 hex-sweep remains. Note several remaining "hardcoded" hex are _intentional_ (always-dark console terminal, ReactFlow SVG strokes) and must NOT be swept. **Phase 5 (D4 + D8): the grid now reaches every standalone screen** (login/auth/error/legal/status/onboarding — their opaque `bg-bg` full-screen wrappers were hiding it) **and the dashboard content shell is fluid up to 4K** (`max-w-7xl` → `max-w-[3840px]`) so it follows the viewport on large monitors instead of centering with wide side gutters. **Phase 6 (D9): data tables are now opaque surfaces** so the grid no longer bleeds through their rows — card-less tables paint `bg-surface`, and the two log tables' semi-transparent `bg-black/5` tint (which tailwind-merge let win over the Card's `bg-surface`) is removed. The grid size itself is already correct (32px, identical to the site); a "bigger" grid on a running instance is a stale build, not code.
|
||||
> **Date:** 2026-06-16 · **Scope:** unify the OmniRoute dashboard (`src/`) with the marketing site (`_mono_repo/omnirouteSite/`) into **one visual identity** — same graph-paper grid background, same color tokens, standardized components.
|
||||
|
||||
---
|
||||
@@ -192,6 +192,7 @@ Each phase: `npm run lint` + `npm run typecheck:core` + a visual pass.
|
||||
- **D6 — Radius 14/9 product-wide:** Rec: yes (Phase 2).
|
||||
- **D7 — Phase 1 ships first:** Chosen.
|
||||
- **D8 — Layout width (Phase 5):** the dashboard content shell was capped at `max-w-7xl` (1280px), centering with wide empty side gutters on large monitors. ✅ **DONE** — raised to a fluid `max-w-[3840px]` (true 4K): content now follows the viewport up to ~4K and only centers beyond it (`DashboardLayout.tsx`). Deliberately-narrow pages stay narrow by design (`ProviderOnboardingWizard` max-w-5xl, `Rtk`/`CavemanContextPageClient` max-w-6xl).
|
||||
- **D9 — Opaque data tables (Phase 6):** with the dashboard content area now transparent (so the grid wallpaper shows through, Phase 5), data tables whose container was _not_ an opaque surface let the grid bleed through their transparent even-rows / low-alpha zebra. ✅ **DONE** — every card-less table now paints `bg-surface` (or, for the `<DataTable>` primitive, `background: var(--color-surface)` on its scroll container). Fixed: `DataTable` (primitive), `ProxyLogger`/`RequestLoggerV2` (their `<Card>` `bg-black/5 dark:bg-black/20` tint was winning over the Card's `bg-surface` via tailwind-merge → ~95% transparent), `BatchListTab`/`FilesListTab`/`CacheEntriesTab`/`ReasoningCacheTab`/`cache page`/`FreePoolTab`/`ModelMappingTable`/`HeaderTable`, plus the two CSS-grid "tables" in the cache views (`bg-surface/35` → `bg-surface`). Tables already inside a `<Card>`/Modal were verified opaque and deliberately left untouched (bg-surface there is a redundant no-op). The grid itself needed **no change** — dashboard `body::before` is byte-identical to the site (`--grid-size: 32px`); any "bigger grid" seen on a running instance is a stale pre-`#4143` build, not code. Guarded by `tests/unit/design-grid-background.test.ts` (Phase 6 block).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ export default function BatchListTab({
|
||||
</div>
|
||||
|
||||
{/* Table — 9 columns: Status | ID | Endpoint | Model | Progress | Cost | Created | Expires | Actions */}
|
||||
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)]">
|
||||
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]">
|
||||
<table className="w-full text-sm" role="table" aria-label={t("batchListBatchesTable")}>
|
||||
<thead>
|
||||
<tr className="bg-[var(--color-bg-alt)] border-b border-[var(--color-border)]">
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function FilesListTab({
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)]">
|
||||
<div className="overflow-x-auto overflow-y-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]">
|
||||
<table className="w-full text-sm" role="table" aria-label={t("batchFilesListFilesTable")}>
|
||||
<thead>
|
||||
<tr className="bg-[var(--color-bg-alt)] border-b border-[var(--color-border)]">
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function CacheEntriesTab() {
|
||||
<div className="text-sm text-text-muted text-center py-8">{t("noEntries")}</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="overflow-x-auto">
|
||||
<div className="overflow-x-auto bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="text-left text-xs text-text-muted border-b border-border/30">
|
||||
|
||||
@@ -277,7 +277,7 @@ export default function ReasoningCacheTab() {
|
||||
{providerEntries.length > 0 && (
|
||||
<div className="rounded-2xl border border-border/30 bg-surface/20 p-5">
|
||||
<h3 className="text-sm font-medium text-text-main">{t("reasoningByProvider")}</h3>
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">
|
||||
@@ -330,7 +330,7 @@ export default function ReasoningCacheTab() {
|
||||
{modelEntries.length > 0 && (
|
||||
<div className="rounded-2xl border border-border/30 bg-surface/20 p-5">
|
||||
<h3 className="text-sm font-medium text-text-main">{t("reasoningByModel")}</h3>
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">
|
||||
@@ -376,7 +376,7 @@ export default function ReasoningCacheTab() {
|
||||
{t("reasoningNoData")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-hidden rounded-2xl border border-border/20 bg-surface/35">
|
||||
<div className="overflow-hidden rounded-2xl border border-border/20 bg-surface">
|
||||
<div className="grid grid-cols-[minmax(120px,1fr)_100px_minmax(100px,1fr)_80px_80px_60px] gap-3 border-b border-border/20 px-4 py-3 text-[11px] font-medium uppercase tracking-[0.12em] text-text-muted">
|
||||
<span>{t("reasoningToolCallId")}</span>
|
||||
<span>{t("tableProvider")}</span>
|
||||
|
||||
4
src/app/(dashboard)/dashboard/cache/page.tsx
vendored
4
src/app/(dashboard)/dashboard/cache/page.tsx
vendored
@@ -293,7 +293,7 @@ function PromptTrendPanel({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 overflow-hidden rounded-2xl border border-border/25 bg-surface/35">
|
||||
<div className="mt-5 overflow-hidden rounded-2xl border border-border/25 bg-surface">
|
||||
<div className="grid grid-cols-[84px_minmax(0,1fr)_92px_120px] gap-3 border-b border-border/20 px-4 py-3 text-[11px] font-medium uppercase tracking-[0.12em] text-text-muted">
|
||||
<span>{hourLabel}</span>
|
||||
<span>{activityLabel}</span>
|
||||
@@ -570,7 +570,7 @@ export default function CachePage() {
|
||||
</div>
|
||||
|
||||
{providerEntries.length > 0 ? (
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface/35">
|
||||
<div className="mt-3 overflow-x-auto rounded-2xl border border-border/20 bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border/20 text-left text-[11px] uppercase tracking-[0.12em] text-text-muted">
|
||||
|
||||
@@ -216,7 +216,7 @@ export default function FreePoolTab() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="overflow-x-auto rounded border border-border">
|
||||
<div className="overflow-x-auto rounded border border-border bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-surface-alt text-text-muted text-xs">
|
||||
<tr>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function ModelMappingTable({ agentId, mappings, onSave }: ModelMappingTab
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="rounded-lg border border-border/40 overflow-hidden">
|
||||
<div className="rounded-lg border border-border/40 overflow-hidden bg-surface">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border/40 bg-surface/60">
|
||||
|
||||
@@ -22,7 +22,7 @@ export function HeaderTable({ headers }: HeaderTableProps) {
|
||||
{masked ? "Show" : "Hide"}
|
||||
</button>
|
||||
</div>
|
||||
<table className="w-full text-xs font-mono border-collapse">
|
||||
<table className="w-full text-xs font-mono border-collapse bg-surface">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left px-2 py-1 text-text-muted font-medium">Name</th>
|
||||
|
||||
@@ -100,7 +100,16 @@ export default function DataTable({
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ overflow: "auto", maxHeight, borderRadius: "8px" }}>
|
||||
<div
|
||||
style={{
|
||||
overflow: "auto",
|
||||
maxHeight,
|
||||
borderRadius: "8px",
|
||||
// Opaque surface so the body grid wallpaper never bleeds through the
|
||||
// transparent even-rows / low-alpha zebra when the table renders card-less.
|
||||
background: "var(--color-surface)",
|
||||
}}
|
||||
>
|
||||
<table
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -390,7 +390,7 @@ export default function ProxyLogger() {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<Card className="overflow-hidden bg-black/5 dark:bg-black/20">
|
||||
<Card className="overflow-hidden bg-surface">
|
||||
<div className="p-0 overflow-x-auto max-h-[calc(100vh-320px)] overflow-y-auto">
|
||||
{loading && logs.length === 0 ? (
|
||||
<div className="p-8 text-center text-text-muted">{t("loadingProxyLogs")}</div>
|
||||
|
||||
@@ -947,7 +947,7 @@ const RequestLoggerV2 = forwardRef<RequestLoggerV2Handle, { initialSelectedId?:
|
||||
{/* Table */}
|
||||
<Card
|
||||
padding="none"
|
||||
className="min-h-[460px] resize-y overflow-auto bg-black/5 dark:bg-black/20"
|
||||
className="min-h-[460px] resize-y overflow-auto bg-surface"
|
||||
>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
|
||||
@@ -222,3 +222,82 @@ test("DashboardLayout content shell is fluid up to ~4K before centering", () =>
|
||||
);
|
||||
assert.ok(!dashboardLayout.includes("max-w-7xl"), "the old 1280px max-w-7xl cap is gone");
|
||||
});
|
||||
|
||||
// ── Phase 6: data tables are opaque content surfaces so the grid never bleeds through ──
|
||||
//
|
||||
// The dashboard content area is intentionally transparent (the body::before grid shows
|
||||
// through as a wallpaper). A data table whose nearest ancestor is NOT an opaque surface
|
||||
// would let the grid bleed through its transparent even-rows / low-alpha zebra rows.
|
||||
// Cards already carry bg-surface; these guards cover the shared table primitives and the
|
||||
// tables that render *without* a Card. Tables verified to live inside a <Card>/Modal are
|
||||
// intentionally left untouched (bg-surface there would be a redundant no-op).
|
||||
|
||||
test("DataTable primitive paints its own opaque surface", () => {
|
||||
const dt = read("../../src/shared/components/DataTable.tsx");
|
||||
assert.ok(
|
||||
dt.includes("var(--color-surface)"),
|
||||
"DataTable scroll container is opaque (its even rows are transparent by design)"
|
||||
);
|
||||
});
|
||||
|
||||
test("log table cards are opaque (no semi-transparent bg-black tint)", () => {
|
||||
// bg-black/5|20 on a <Card> wins over the Card's own bg-surface via tailwind-merge,
|
||||
// turning the big log tables ~95% transparent — the grid bled straight through them.
|
||||
for (const p of [
|
||||
"../../src/shared/components/ProxyLogger.tsx",
|
||||
"../../src/shared/components/RequestLoggerV2.tsx",
|
||||
]) {
|
||||
const src = read(p);
|
||||
assert.ok(
|
||||
!src.includes("bg-black/5") && !src.includes("bg-black/20"),
|
||||
`${p} must not tint the table Card with bg-black/5|20 (it drops the Card's opaque surface)`
|
||||
);
|
||||
assert.ok(src.includes("bg-surface"), `${p} table card uses the opaque bg-surface`);
|
||||
}
|
||||
});
|
||||
|
||||
test("card-less data tables wrap their table in an opaque surface", () => {
|
||||
const expect = [
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/batch/BatchListTab.tsx",
|
||||
"rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]",
|
||||
],
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/batch/FilesListTab.tsx",
|
||||
"rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)]",
|
||||
],
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx",
|
||||
"overflow-x-auto bg-surface",
|
||||
],
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/settings/components/proxy/FreePoolTab.tsx",
|
||||
"rounded border border-border bg-surface",
|
||||
],
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/tools/agent-bridge/components/ModelMappingTable.tsx",
|
||||
"overflow-hidden bg-surface",
|
||||
],
|
||||
[
|
||||
"../../src/app/(dashboard)/dashboard/tools/traffic-inspector/components/shared/HeaderTable.tsx",
|
||||
"bg-surface",
|
||||
],
|
||||
];
|
||||
for (const [p, needle] of expect) {
|
||||
const src = read(p);
|
||||
assert.ok(src.includes(needle), `${p} must include "${needle}" so the table is opaque over the grid`);
|
||||
}
|
||||
});
|
||||
|
||||
test("semi-transparent cache table boxes are now opaque", () => {
|
||||
for (const p of [
|
||||
"../../src/app/(dashboard)/dashboard/cache/components/ReasoningCacheTab.tsx",
|
||||
"../../src/app/(dashboard)/dashboard/cache/page.tsx",
|
||||
]) {
|
||||
const src = read(p);
|
||||
assert.ok(
|
||||
!src.includes("bg-surface/35"),
|
||||
`${p} table box no longer uses the ~35%-opaque bg-surface/35 (the grid bled through it)`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user