diff --git a/.env.example b/.env.example index 920b0b8..1b4fb8e 100644 --- a/.env.example +++ b/.env.example @@ -6,20 +6,18 @@ THEGRAPH_API_KEY= # Aliases: DISCOVERY_URL, LIVEPEER_DISCOVERY_SERVICE_URL DISCOVERY_SERVICE_URL=https://discovery-service-production-8955.up.railway.app/v1/discovery/raw -# PymtHouse (usage + API keys + signer sessions) +# PymtHouse (usage + API keys + signer sessions + plans/subscribe) # Production: PYMTHOUSE_ISSUER_URL=https://pymthouse.com/api/v1/oidc -# Local pymthouse: http://localhost:3001/api/v1/oidc -# Public app client id (OAuth client_id) — used for Builder API path {clientId} +# Local pymthouse (`npm run dev` HTTPS): https://localhost:3001/api/v1/oidc +# (mkcert is trusted in the browser; Node fetch to localhost HTTPS usually works +# without PYMTHOUSE_ALLOW_INSECURE_HTTP when using the system/mkcert CA.) PYMTHOUSE_PUBLIC_CLIENT_ID= PYMTHOUSE_M2M_CLIENT_ID= PYMTHOUSE_M2M_CLIENT_SECRET= -# Remote signer DMZ base included as signer_url in API-key exchange responses -# (fallback when the issuer does not return signer_url). -# Production: https://pymthouse-production.up.railway.app -# Local compose: http://127.0.0.1:8080 -PYMTHOUSE_SIGNER_URL=https://pymthouse-production.up.railway.app -# Set to 1 for local dev when issuer uses http://127.0.0.1 +# Signer DMZ URL is returned by issuer exchange / GET …/apps/{id}/signer/routing +# — do not set PYMTHOUSE_SIGNER_URL on the dashboard. +# Set to 1 for local http issuer only (not needed for https://localhost with mkcert) PYMTHOUSE_ALLOW_INSECURE_HTTP= # Live-runner discovery (orchestrator /discovery endpoint). diff --git a/CLAUDE.md b/CLAUDE.md index 7e9d457..3d8597a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,29 +15,31 @@ ``` app/ ├── layout.tsx # Root layout — html/body, Geist fonts, theme bootstrap (FOUT-safe) -├── globals.css # Tailwind v4 @theme block + dashboard token layer -├── (app)/ # Dashboard chrome (sidebar, providers, keyboard shortcuts) +├── globals.css # Tailwind v4 @theme block + console token layer +├── (app)/ # Console chrome (sidebar, providers, keyboard shortcuts) │ ├── layout.tsx -│ ├── page.tsx # / → Explore (public) -│ ├── home/page.tsx # /home → Dashboard home (auth-gated via SignInWall) -│ ├── jobs, usage, keys, settings # auth-gated -│ ├── models/[id] # public +│ ├── page.tsx # / → Explore (public; → /home when signed in) +│ ├── explore/page.tsx # /explore → Explore (public) +│ ├── home/page.tsx # /home → Console home (auth-gated via SignInWall) +│ ├── calls, usage, keys, settings # auth-gated +│ ├── apps/[id] # public +│ ├── orgs/[slug] # public │ ├── network # public │ └── error.tsx ├── (auth)/ # /login, /signup — no sidebar └── not-found.tsx components/ -├── dashboard/ # Dashboard surfaces, providers (Auth, Theme), nav, charts, tables +├── console/ # Console surfaces, providers (Auth, Theme), nav, charts, tables └── design-system/ # Vendored primitives — Badge, Button, Dialog, Drawer, # ErrorState, Select, Skeleton, Tooltip, LivepeerLogo. # TEMPORARY — replace with @livepeer/design-system when published. lib/ -├── dashboard/ # mock-data, types, utils, useStarredModels, model-stats, generate-card-visual +├── console/ # mock-data, types, utils, useStarredModels, model-stats, generate-card-visual └── constants.ts # PORTAL_NAV_ITEMS, EXTERNAL_LINKS -public/images/dashboard/ # Model card visuals + daydream logo +public/images/console/ # Model card visuals + daydream logo ``` ## Conventions @@ -48,11 +50,11 @@ The Home (`/home`) shows `` for any signed-in user where `loc ### Section headings -Use `` from `components/dashboard/SectionHeader.tsx`. Dashboard convention is `text-base font-semibold` headings; do not roll ad-hoc `

` markup. +Use `` from `components/console/SectionHeader.tsx`. Console convention is `text-base font-semibold` headings; do not roll ad-hoc `

` markup. ### KPI rows -Wrap any row of `` / `` in `` (`components/dashboard/KpiStrip.tsx`). Don't roll ad-hoc `grid grid-cols-2 sm:grid-cols-4` containers — they drift over time. +Wrap any row of `` / `` in `` (`components/console/KpiStrip.tsx`). Don't roll ad-hoc `grid grid-cols-2 sm:grid-cols-4` containers — they drift over time. ### Page max-widths @@ -62,15 +64,15 @@ Each page picks one based on content type and uses it for every inner max-width - `max-w-6xl` — data tables / charts (Network, Usage) - `max-w-7xl` — catalogs / dense grids (Explore, Models list) -There's no shared `` wrapper because every page layers a sticky `` between header and content, requiring the max-width to repeat 2-3 times intentionally per page. +There's no shared `` wrapper because every page layers a sticky `` between header and content, requiring the max-width to repeat 2-3 times intentionally per page. ### Cards -Dashboard surfaces use inline classes — typically `rounded-xl border border-hairline bg-{dark-surface|dark-card|transparent}` with `p-4` (data-dense rows) or `p-6` (feature blocks). +Console surfaces use inline classes — typically `rounded-xl border border-hairline bg-{dark-surface|dark-card|transparent}` with `p-4` (data-dense rows) or `p-6` (feature blocks). ### Tables -Tables on the dashboard are intentionally bespoke — Home "Your runs", `UsageTab` activity log, and `PaymentTab` connected-providers all roll their own markup because their interaction patterns differ (sticky mobile/desktop headers, live pulse on most-recent row, highlighted target row, etc.). There is no shared `` primitive. If a future surface needs a generic sortable table, build it then — don't try to back-fit a primitive that compromises the existing surfaces. +Tables in the console are intentionally bespoke — Home "Your runs", `UsageTab` activity log, and `PaymentTab` connected-providers all roll their own markup because their interaction patterns differ (sticky mobile/desktop headers, live pulse on most-recent row, highlighted target row, etc.). There is no shared `` primitive. If a future surface needs a generic sortable table, build it then — don't try to back-fit a primitive that compromises the existing surfaces. ### Form-control focus ring @@ -78,7 +80,7 @@ All form controls (`SearchInput`, `Select`, etc.) show `focus-visible:ring-1 foc ### Loading + error states -Suspense boundaries on every dashboard route use `` as the fallback (`components/dashboard/DashboardPageSkeleton.tsx`). The `(app)/error.tsx` segment-level boundary renders `` (`components/design-system/ErrorState.tsx`) for any thrown render error, with a request ID + retry + Discord help link. +Suspense boundaries on every console route use `` as the fallback (`components/console/ConsolePageSkeleton.tsx`). The `(app)/error.tsx` segment-level boundary renders `` (`components/design-system/ErrorState.tsx`) for any thrown render error, with a request ID + retry + Discord help link. ### Color / token rules @@ -102,11 +104,11 @@ Existing tokens in `globals.css` `@theme`: `--motion-duration-fast` (150ms, hove ## Design system — vendored, not final -`components/design-system/` is **vendored**. When `@livepeer/design-system` ships as a real package, swap the imports — keep `components/design-system/` as a barrel that re-exports from the package, or delete it entirely. The 38+ dashboard components import from `@/components/design-system/X`, so the swap-out is a single barrel-file change rather than a 38-file rewrite. +`components/design-system/` is **vendored**. When `@livepeer/design-system` ships as a real package, swap the imports — keep `components/design-system/` as a barrel that re-exports from the package, or delete it entirely. The 38+ console components import from `@/components/design-system/X`, so the swap-out is a single barrel-file change rather than a 38-file rewrite. ## Don't - **No `next/image`** — use raw `` tags. Some downstream primitives need direct CSS filter/absolute stacking that `next/image`'s wrapper breaks. - **No global state** — local `useState` only. No state libraries. -- **No backend dependencies** — all data is mock (`lib/dashboard/mock-data.ts`). When the real API lands, replace the mock imports surgically; don't restructure the components around the data layer. +- **No backend dependencies** — all data is mock (`lib/console/mock-data.ts`). When the real API lands, replace the mock imports surgically; don't restructure the components around the data layer. - **No new dependencies without discussion.** The dependency list is intentionally small. diff --git a/README.md b/README.md index c824a57..d4f14a0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Livepeer Developer Dashboard +# Livepeer Console -The signed-in surface for developers using the Livepeer network — browse AI capabilities, manage API keys, monitor usage. +The signed-in surface for developers using the Livepeer network — manage API keys, monitor usage. ## Status -Early development. All data is mock-driven (`lib/dashboard/mock-data.ts`) — there is no backend wired in yet. Auth is stubbed in `components/dashboard/AuthContext.tsx`. +Early development. All data is mock-driven (`lib/console/mock-data.ts`) — there is no backend wired in yet. Auth is stubbed in `components/console/AuthContext.tsx`. This repo was extracted from [`livepeer/website`](https://github.com/livepeer/website) (branch `claude/dashboard-updates`) and ships independently from the marketing site. @@ -31,41 +31,45 @@ pnpm typecheck # tsc --noEmit ``` app/ ├── layout.tsx # Root: html/body, Geist fonts, theme bootstrap -├── globals.css # Token layer + dashboard utilities -├── (app)/ # Dashboard chrome (sidebar, providers) +├── globals.css # Token layer + console utilities +├── (app)/ # Console chrome (sidebar, providers) │ ├── layout.tsx -│ ├── page.tsx # / → Explore (public) +│ ├── page.tsx # / → Explore (public; → /home when signed in) +│ ├── explore/ # /explore (public) │ ├── home/ # /home (auth-gated) -│ ├── jobs, usage, keys, settings # auth-gated -│ ├── models/[id] # public model detail +│ ├── calls, usage, keys, settings # auth-gated +│ ├── apps/[id] # public app detail + playground +│ ├── orgs/[slug] # public org profile │ └── network # public network stats └── (auth)/ # Login + signup (no sidebar) components/ -├── dashboard/ # All dashboard surfaces +├── console/ # All console surfaces └── design-system/ # Vendored primitives — Badge, Button, Dialog, # Drawer, ErrorState, Select, Skeleton, Tooltip, # LivepeerLogo. Replace with @livepeer/design-system # when that package ships. lib/ -├── dashboard/ # Mock data, types, utils +├── console/ # Mock data, types, utils └── constants.ts # PORTAL_NAV_ITEMS + EXTERNAL_LINKS ``` ## Routes -| URL | Auth | Surface | -| ------------------ | -------- | -------------------------------- | -| `/` | public | Explore — model catalog | -| `/home` | required | Dashboard home (your runs / KPIs) | -| `/jobs` | required | Run history | -| `/usage` | required | Account usage | -| `/keys` | required | API keys | -| `/settings` | required | Account settings | -| `/models/[id]` | public | Model detail + playground | -| `/network` | public | Network stats | -| `/login` | public | Sign in | -| `/signup` | public | Sign up | +| URL | Auth | Surface | +| -------------- | -------- | --------------------------------------------- | +| `/` | public | Explore — redirects to `/home` when signed in | +| `/explore` | public | Explore — app catalog | +| `/home` | required | Console home (your runs / KPIs) | +| `/calls` | required | Call history | +| `/usage` | required | Account usage | +| `/keys` | required | API keys | +| `/settings` | required | Account settings | +| `/apps/[id]` | public | App detail + playground | +| `/orgs/[slug]` | public | Organization's published apps | +| `/network` | public | Network stats (sidebar: "Stats") | +| `/login` | public | Sign in | +| `/signup` | public | Sign up | -See `CLAUDE.md` for dashboard conventions (KPI rows, tables, motion tokens, color rules). +See `CLAUDE.md` for console conventions (KPI rows, tables, motion tokens, color rules). diff --git a/app/(app)/apps/[...id]/page.tsx b/app/(app)/apps/[...id]/page.tsx index deac82e..13effa6 100644 --- a/app/(app)/apps/[...id]/page.tsx +++ b/app/(app)/apps/[...id]/page.tsx @@ -15,12 +15,12 @@ import { ArrowUpRight, Settings as SettingsIcon, } from "lucide-react"; -import { useAuth } from "@/components/dashboard/AuthContext"; -import DashboardSubNav from "@/components/dashboard/DashboardSubNav"; -import CostTag from "@/components/dashboard/CostTag"; -import KeyBadge from "@/components/dashboard/KeyBadge"; -import CallsTable from "@/components/dashboard/CallsTable"; -import StatusDot from "@/components/dashboard/StatusDot"; +import { useAuth } from "@/components/console/AuthContext"; +import ConsoleSubNav from "@/components/console/ConsoleSubNav"; +import CostTag from "@/components/console/CostTag"; +import KeyBadge from "@/components/console/KeyBadge"; +import CallsTable from "@/components/console/CallsTable"; +import StatusDot from "@/components/console/StatusDot"; import { effectiveVisibility, setPipelineVisibility, @@ -28,31 +28,31 @@ import { PIPELINE_APP_IDS, SETTINGS_API_KEYS, MOCK_RECENT_REQUESTS, -} from "@/lib/dashboard/mock-data"; -import { useDiscoveryModel } from "@/lib/dashboard/useDiscoveryModel"; -import DashboardPageSkeleton from "@/components/dashboard/DashboardPageSkeleton"; -import { getAppIcon } from "@/lib/dashboard/utils"; -import PlaygroundForm from "@/components/dashboard/playground/PlaygroundForm"; -import JsonInput from "@/components/dashboard/playground/JsonInput"; -import PlaygroundOutput from "@/components/dashboard/playground/PlaygroundOutput"; -import TranscodingOutput from "@/components/dashboard/playground/TranscodingOutput"; -import CodeSnippets from "@/components/dashboard/playground/CodeSnippets"; -import WebcamPlayground from "@/components/dashboard/playground/WebcamPlayground"; +} from "@/lib/console/mock-data"; +import { useDiscoveryModel } from "@/lib/console/useDiscoveryModel"; +import ConsolePageSkeleton from "@/components/console/ConsolePageSkeleton"; +import { getAppIcon } from "@/lib/console/utils"; +import PlaygroundForm from "@/components/console/playground/PlaygroundForm"; +import JsonInput from "@/components/console/playground/JsonInput"; +import PlaygroundOutput from "@/components/console/playground/PlaygroundOutput"; +import TranscodingOutput from "@/components/console/playground/TranscodingOutput"; +import CodeSnippets from "@/components/console/playground/CodeSnippets"; +import WebcamPlayground from "@/components/console/playground/WebcamPlayground"; import { RunnerGatewayProvider, useRunnerGatewayContext, -} from "@/components/dashboard/playground/RunnerGatewayContext"; +} from "@/components/console/playground/RunnerGatewayContext"; import { buildLiveRunnerPayload, extractRunnerResultText, runnerGatewayPostUrl, -} from "@/lib/dashboard/runner-gateway-client"; -import AppAnalytics from "@/components/dashboard/stats/AppAnalytics"; +} from "@/lib/console/runner-gateway-client"; +import AppAnalytics from "@/components/console/stats/AppAnalytics"; import { OverviewTab, SettingsTab, -} from "@/components/dashboard/AppDetailView"; -import type { App, Pipeline, PipelineVisibility } from "@/lib/dashboard/types"; +} from "@/components/console/AppDetailView"; +import type { App, Pipeline, PipelineVisibility } from "@/lib/console/types"; // ─── Tabs ─── // @@ -695,7 +695,7 @@ function JobsTab({ runs, }: { model: App; - runs: import("@/lib/dashboard/types").AccountActivityRow[]; + runs: import("@/lib/console/types").AccountActivityRow[]; }) { if (runs.length === 0) { return ( @@ -794,7 +794,7 @@ export default function AppDetailPage() { if (discovery.status === "loading") { return (
- +
); } @@ -1037,7 +1037,7 @@ export default function AppDetailPage() { {/* Tabs — mobile scroll strip */} - { // Surface to console for now. When real telemetry lands, route to it here. - console.error("[dashboard] render error:", error); + console.error("[console] render error:", error); }, [error]); return ( @@ -24,7 +24,7 @@ export default function DashboardError({
diff --git a/app/(app)/explore/page.tsx b/app/(app)/explore/page.tsx index bdef98a..1f91453 100644 --- a/app/(app)/explore/page.tsx +++ b/app/(app)/explore/page.tsx @@ -1,4 +1,4 @@ -import ExploreView from "@/components/dashboard/ExploreView"; +import ExploreView from "@/components/console/ExploreView"; // /explore — the app catalog as a first-class destination (the sidebar's // "Explore" item points here). Reachable signed-in or out; unlike `/`, it never diff --git a/app/(app)/home/page.tsx b/app/(app)/home/page.tsx index aa476cc..9b5b510 100644 --- a/app/(app)/home/page.tsx +++ b/app/(app)/home/page.tsx @@ -3,16 +3,16 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { House } from "lucide-react"; -import { useAuth } from "@/components/dashboard/AuthContext"; -import DashboardPageHeader from "@/components/dashboard/DashboardPageHeader"; +import { useAuth } from "@/components/console/AuthContext"; +import ConsolePageHeader from "@/components/console/ConsolePageHeader"; import FirstRunChecklist, { FIRST_RUN_CHANGED_EVENT, FIRST_RUN_DISMISSED_KEY, -} from "@/components/dashboard/FirstRunChecklist"; -import HomeCommandBar from "@/components/dashboard/HomeCommandBar"; -import ConsumedAppsPanel from "@/components/dashboard/ConsumedAppsPanel"; -import ActivityPanel from "@/components/dashboard/ActivityPanel"; -import SectionHeader from "@/components/dashboard/SectionHeader"; +} from "@/components/console/FirstRunChecklist"; +import HomeCommandBar from "@/components/console/HomeCommandBar"; +import ConsumedAppsPanel from "@/components/console/ConsumedAppsPanel"; +import ActivityPanel from "@/components/console/ActivityPanel"; +import SectionHeader from "@/components/console/SectionHeader"; // ─── Home page header — just the title chrome ─── // @@ -21,7 +21,7 @@ import SectionHeader from "@/components/dashboard/SectionHeader"; // usage" is already reachable from the sidebar and the Usage panel below. function HomePageHeader() { - return ; + return ; } // ─── Home Page ─── @@ -39,15 +39,13 @@ export default function HomePage() { const { isConnected, isLoading, user } = useAuth(); const router = useRouter(); - // Signed-out users redirect to / — the public landing. + // Signed-out users redirect to /login — the console's default entry point. // The Home view is organization-only (KPIs, recent runs, capability - // leaderboard), and a SignInWall on the root /home URL was the - // wrong default: visitors arrived at a sign-in gate before they'd had a - // chance to see what's on the platform. Explore is the discovery surface - // and the right entry point for unauthenticated visitors. + // leaderboard), so there's nothing to show an unauthenticated visitor here; + // sending them straight to sign-in beats bouncing through `/`. useEffect(() => { if (!isLoading && (!isConnected || !user)) { - router.replace("/"); + router.replace("/login"); } }, [isLoading, isConnected, user, router]); @@ -58,13 +56,13 @@ export default function HomePage() { // server-side run history, but in mock mode the flag alone is the source // of truth (MOCK_RECENT_REQUESTS is always non-empty for the organization demo). const [firstRunDismissed, setFirstRunDismissed] = useState( - null, + null ); useEffect(() => { if (typeof window === "undefined") return; const read = () => setFirstRunDismissed( - window.localStorage.getItem(FIRST_RUN_DISMISSED_KEY) === "1", + window.localStorage.getItem(FIRST_RUN_DISMISSED_KEY) === "1" ); read(); // Cross-tab updates via storage; same-tab updates (e.g. Quickstart click in @@ -97,7 +95,7 @@ export default function HomePage() { const organization = "Flipbook"; const firstName = user?.name?.split(" ")[0] ?? "there"; - // Signed-out users are redirected to / via the useEffect + // Signed-out users are redirected to /login via the useEffect // above; render nothing in the meantime (one frame max) so they don't see // a flash of organization-mock data before the redirect lands. if (!isConnected || !user) { diff --git a/app/(app)/keys/page.tsx b/app/(app)/keys/page.tsx index fc62989..cd3b146 100644 --- a/app/(app)/keys/page.tsx +++ b/app/(app)/keys/page.tsx @@ -1,8 +1,8 @@ "use client"; -import KeysView from "@/components/dashboard/KeysView"; -import SignInWall from "@/components/dashboard/SignInWall"; -import { useAuth } from "@/components/dashboard/AuthContext"; +import KeysView from "@/components/console/KeysView"; +import SignInWall from "@/components/console/SignInWall"; +import { useAuth } from "@/components/console/AuthContext"; // Note: the previous server-component metadata moves out with the auth gate. // Title/description for /keys now come from the layout's defaults. diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index 4431821..caa6d88 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -2,13 +2,13 @@ import type { Metadata } from "next"; import type { CSSProperties } from "react"; import { GeistSans } from "geist/font/sans"; import { GeistMono } from "geist/font/mono"; -import { AuthProvider } from "@/components/dashboard/AuthContext"; -import { EnvironmentProvider } from "@/components/dashboard/EnvironmentContext"; -import { ThemeProvider } from "@/components/dashboard/ThemeContext"; -import DashboardSidebar from "@/components/dashboard/DashboardSidebar"; -import KeyboardShortcuts from "@/components/dashboard/KeyboardShortcuts"; +import { AuthProvider } from "@/components/console/AuthContext"; +import { EnvironmentProvider } from "@/components/console/EnvironmentContext"; +import { ThemeProvider } from "@/components/console/ThemeContext"; +import ConsoleSidebar from "@/components/console/ConsoleSidebar"; +import KeyboardShortcuts from "@/components/console/KeyboardShortcuts"; -// FOUT prevention — runs synchronously in the document, before the dashboard +// FOUT prevention — runs synchronously in the document, before the console // subtree paints. Reads the stored theme preference from localStorage and // applies `` so dual-source CSS variables resolve to // the right theme on first paint. The ThemeProvider takes over after @@ -17,26 +17,26 @@ import KeyboardShortcuts from "@/components/dashboard/KeyboardShortcuts"; // Reads the same `localStorage["theme"]` key the marketing site uses (see // `app/layout.tsx`'s inline script and `components/layout/ThemeToggle.tsx`), // so flipping the theme on either surface propagates to the other. Default -// is "dark" for first-time visitors — the dashboard's native presentation; -// light is an opt-in via Settings → Appearance. -const THEME_INIT_SCRIPT = `(function(){try{var s=localStorage.getItem('theme')||'dark';var d=s==='dark'||(s==='system'&&matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.dataset.theme=d?'dark':'light';}catch(e){document.documentElement.dataset.theme='dark';}})();`; +// is "system" for first-time visitors — we follow the OS `prefers-color-scheme` +// until the user pins light or dark via Settings → Appearance. +const THEME_INIT_SCRIPT = `(function(){try{var s=localStorage.getItem('theme')||'system';var d=s==='dark'||(s!=='light'&&matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.dataset.theme=d?'dark':'light';}catch(e){document.documentElement.dataset.theme='dark';}})();`; export const metadata: Metadata = { - title: "Developer Dashboard — Livepeer", + title: "Console — Livepeer", description: "Browse AI apps, manage API keys, and monitor usage on the Livepeer network.", }; -// Dashboard runs on Geist (Vercel's open-source font) instead of Favorit Pro — -// the dashboard is a *tool*, the marketing site is the brand. We attach the Geist +// The console runs on Geist (Vercel's open-source font) instead of Favorit Pro — +// the console is a *tool*, the marketing site is the brand. We attach the Geist // CSS variables to this subtree and override `--font-sans` / `--font-mono` so // every Tailwind `font-sans` / `font-mono` consumer below this point picks Geist. // // Density: per the Livepeer Console design (Claude Design handoff, Apr 2026), -// the dashboard subtree uses a 13.5px body with a slightly tighter letter-spacing +// the console subtree uses a 13.5px body with a slightly tighter letter-spacing // to land in the same density bracket as Linear. Sidebar width and chrome head // height are exposed as custom properties so components can reference them. -const dashboardOverrides = { +const consoleOverrides = { "--font-sans": "var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif", "--font-mono": "var(--font-geist-mono), ui-monospace, monospace", "--side-w": "232px", @@ -45,7 +45,7 @@ const dashboardOverrides = { letterSpacing: "-0.005em", } as CSSProperties; -export default function DashboardLayout({ +export default function ConsoleLayout({ children, }: { children: React.ReactNode; @@ -55,7 +55,7 @@ export default function DashboardLayout({ // fills the remaining width. return ( <> - {/* Inline theme bootstrap — must run before the dashboard subtree + {/* Inline theme bootstrap — must run before the console subtree paints. ThemeProvider below takes over post-hydration. */}