diff --git a/src/app/(app)/docs/page.tsx b/src/app/(app)/docs/page.tsx index b10f234..292f478 100644 --- a/src/app/(app)/docs/page.tsx +++ b/src/app/(app)/docs/page.tsx @@ -11,11 +11,13 @@ export const metadata: Metadata = { title: "Docs" }; export default function DocsIndexPage() { return (
-

Docs

+

+ Docs +

{DOCS.map((doc) => ( - +
diff --git a/src/app/(app)/page.tsx b/src/app/(app)/page.tsx index 7cb55f3..2114bd8 100644 --- a/src/app/(app)/page.tsx +++ b/src/app/(app)/page.tsx @@ -13,21 +13,31 @@ export default function DashboardPage() { if (config.scripts.length === 0) { return ( -
- No scripts configured yet. Add entries under{" "} - scripts: in your - config file. +
+ +

+ No scripts yet +

+

+ Add entries under{" "} + + scripts: + {" "} + in your config file to see them here. +

); } return (
-

Scripts

+

+ Scripts +

{config.scripts.map((script) => ( - +
diff --git a/src/app/(app)/runs/[runId]/page.tsx b/src/app/(app)/runs/[runId]/page.tsx index e3b624f..1c93718 100644 --- a/src/app/(app)/runs/[runId]/page.tsx +++ b/src/app/(app)/runs/[runId]/page.tsx @@ -81,36 +81,42 @@ export default async function RunDetailPage({ params }: RunDetailPageProps) { )} -
+
-
Triggered by
-
{run.triggeredBy}
+
+ Triggered by +
+
{run.triggeredBy}
-
Started
-
+
+ Started +
+
{run.startedAt ? new Date(run.startedAt).toLocaleString() : "-"}
-
Run ID
+
+ Run ID +
{run.id}
- + Command -
+            
               {run.resolvedCommand}
             
{variableEntries.length > 0 && (
- + Variables
diff --git a/src/app/(app)/runs/page.tsx b/src/app/(app)/runs/page.tsx index 01d1e55..d313b67 100644 --- a/src/app/(app)/runs/page.tsx +++ b/src/app/(app)/runs/page.tsx @@ -161,7 +161,9 @@ export default async function RunsPage({ searchParams }: RunsPageProps) { return (
-

Run History

+

+ Run History +

{rows.length === 0 ? (

@@ -196,15 +198,15 @@ export default async function RunsPage({ searchParams }: RunsPageProps) { - + {run.triggeredBy} - + {run.startedAt ? new Date(run.startedAt).toLocaleString() : "-"} - + {formatDuration(run.startedAt, run.endedAt)} diff --git a/src/app/globals.css b/src/app/globals.css index 5de9a9e..4f200d7 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -9,8 +9,14 @@ --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-sans); - --font-mono: var(--font-geist-mono); - --font-heading: var(--font-sans); + --font-mono: var(--font-mono); + --font-heading: var(--font-display); + --color-status-queued: var(--status-queued); + --color-status-running: var(--status-running); + --color-status-succeeded: var(--status-succeeded); + --color-status-failed: var(--status-failed); + --color-status-cancelled: var(--status-cancelled); + --color-status-warn: var(--status-warn); --color-sidebar-ring: var(--sidebar-ring); --color-sidebar-border: var(--sidebar-border); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); @@ -49,73 +55,90 @@ --radius-4xl: calc(var(--radius) * 2.6); } +/* + * Palette - "instrument panel": cool graphite ink/paper with a warm brass signal accent. + * The brass tone doubles as the "running" status color, so a live run literally lights the UI + * up with the brand color - see run-status-badge.tsx and run-terminal.tsx. + */ :root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --background: #f5f6f7; + --foreground: #15171a; + --card: #ffffff; + --card-foreground: #15171a; + --popover: #ffffff; + --popover-foreground: #15171a; + --primary: #c8842e; + --primary-foreground: #17130a; + --secondary: #ececee; + --secondary-foreground: #15171a; + --muted: #ececee; + --muted-foreground: #6b7280; + --accent: #ececee; + --accent-foreground: #15171a; + --destructive: #b3402e; + --border: #dfe1e3; + --input: #dfe1e3; + --ring: #c8842e; + --status-queued: #6b7280; + --status-running: #c8842e; + --status-succeeded: #3f8f5f; + --status-failed: #b3402e; + --status-cancelled: #6b7280; + --status-warn: #8a7530; + --chart-1: #c8842e; + --chart-2: #3f8f5f; + --chart-3: #6b7280; + --chart-4: #8a7530; + --chart-5: #b3402e; + --radius: 0.5rem; + --sidebar: #f5f6f7; + --sidebar-foreground: #15171a; + --sidebar-primary: #c8842e; + --sidebar-primary-foreground: #17130a; + --sidebar-accent: #ececee; + --sidebar-accent-foreground: #15171a; + --sidebar-border: #dfe1e3; + --sidebar-ring: #c8842e; } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); + --background: #14161a; + --foreground: #edeef0; + --card: #1b1e23; + --card-foreground: #edeef0; + --popover: #1b1e23; + --popover-foreground: #edeef0; + --primary: #e9a857; + --primary-foreground: #1a1305; + --secondary: #23262c; + --secondary-foreground: #edeef0; + --muted: #23262c; + --muted-foreground: #9aa0a8; + --accent: #23262c; + --accent-foreground: #edeef0; + --destructive: #e2685a; --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); + --input: oklch(1 0 0 / 14%); + --ring: #e9a857; + --status-queued: #9aa0a8; + --status-running: #e9a857; + --status-succeeded: #5fb983; + --status-failed: #e2685a; + --status-cancelled: #9aa0a8; + --status-warn: #c9a344; + --chart-1: #e9a857; + --chart-2: #5fb983; + --chart-3: #9aa0a8; + --chart-4: #c9a344; + --chart-5: #e2685a; + --sidebar: #1b1e23; + --sidebar-foreground: #edeef0; + --sidebar-primary: #e9a857; + --sidebar-primary-foreground: #1a1305; + --sidebar-accent: #23262c; + --sidebar-accent-foreground: #edeef0; --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); + --sidebar-ring: #e9a857; } @layer base { diff --git a/src/app/icon.svg b/src/app/icon.svg index c59af93..f379ed5 100644 --- a/src/app/icon.svg +++ b/src/app/icon.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e2c2539..4bf2910 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,18 +1,28 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Bricolage_Grotesque, IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google"; import { ThemeProvider } from "next-themes"; import { TooltipProvider } from "@/components/ui/tooltip"; import { Toaster } from "@/components/ui/sonner"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +// Display face for page titles and card headings (wired via --font-heading in globals.css) - used +// with restraint, never for body copy. Body/UI text and technical data (run IDs, commands, +// timestamps) share the IBM Plex family so the two registers still read as one system. +const displayFont = Bricolage_Grotesque({ + variable: "--font-display", subsets: ["latin"], }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const sansFont = IBM_Plex_Sans({ + variable: "--font-sans", subsets: ["latin"], + weight: ["400", "500", "600", "700"], +}); + +const monoFont = IBM_Plex_Mono({ + variable: "--font-mono", + subsets: ["latin"], + weight: ["400", "500", "600"], }); export const metadata: Metadata = { @@ -31,7 +41,7 @@ export default function RootLayout({ return (

- + TriggerShell
diff --git a/src/components/layout/nav.tsx b/src/components/layout/nav.tsx index 273609d..5c1bc3d 100644 --- a/src/components/layout/nav.tsx +++ b/src/components/layout/nav.tsx @@ -34,9 +34,9 @@ export function Nav({
- + TriggerShell