From b24c8fed3ed05e4e2b555cad02cd146d515abb33 Mon Sep 17 00:00:00 2001 From: Victoria Williams Date: Sat, 15 Nov 2025 16:35:11 -0500 Subject: [PATCH] fix: front end legibility with styling --- client/src/App.tsx | 53 ++++-- client/src/components/ui/Button.tsx | 10 + client/src/components/ui/GlassButton.tsx | 13 ++ client/src/index.css | 222 +++++------------------ client/src/lib/ai.ts | 0 client/src/lib/api.ts | 31 ++-- client/src/pages/ConfigurePage.tsx | 39 ++-- client/src/pages/ConnectPage.tsx | 12 +- client/tailwind.config.css | 6 - 9 files changed, 143 insertions(+), 243 deletions(-) create mode 100644 client/src/components/ui/GlassButton.tsx create mode 100644 client/src/lib/ai.ts delete mode 100644 client/tailwind.config.css diff --git a/client/src/App.tsx b/client/src/App.tsx index 70c9b18..f20834e 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,5 @@ // src/App.tsx -import { BrowserRouter, Routes, Route, Navigate, Link } from "react-router-dom"; +import { BrowserRouter, Routes, Route, Navigate, Link, useLocation } from "react-router-dom"; import ConnectPage from "./pages/ConnectPage"; import ConfigurePage from "./pages/ConfigurePage"; import SecretsPage from "./pages/SecretsPage"; @@ -8,42 +8,59 @@ import Jenkins from "./routes/Jenkins"; import { useRepoStore } from "./store/useRepoStore"; import { usePipelineStore } from "./store/usePipelineStore"; -// (optional) ShadCN test -// import { Button } from "@/components/ui/button"; - function NeedRepo({ children }: { children: JSX.Element }) { const { repo, branch } = useRepoStore(); return !repo || !branch ? : children; } function NeedPipeline({ children }: { children: JSX.Element }) { const { result } = usePipelineStore(); - const hasYaml = - result?.generated_yaml || result?.yaml || result?.data?.generated_yaml; + const hasYaml = result?.generated_yaml || result?.yaml || result?.data?.generated_yaml; return !hasYaml ? : children; } +// optional: simple active-link helper +function NavLink({ to, children }: { to: string; children: React.ReactNode }) { + const { pathname } = useLocation(); + const active = pathname.startsWith(to); + return ( + + {children} + + ); +} + export default function App() { return ( -
- {/* Base gradient (covers entire viewport) */} -
- {/* Frosted glass overlay */} -
+
+ {/* Base gradient */} +
+ {/* Subtle dark veil for contrast */} +
+ {/* Frosted glass shimmer – IMPORTANT: pointer-events-none so it never blocks clicks */} +
{/* App content above blur */}
+ 1 Connect + 2 Configure + 3 Secrets + 4 Dashboard + 5 Jenkins +
-
+
} /> } /> diff --git a/client/src/components/ui/Button.tsx b/client/src/components/ui/Button.tsx index 65d4fcd..49a8a6b 100644 --- a/client/src/components/ui/Button.tsx +++ b/client/src/components/ui/Button.tsx @@ -19,6 +19,16 @@ const buttonVariants = cva( "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", + + // ✅ Glass (matches Connect page look) + glass: + "border border-white/30 bg-white/10 text-slate-100 " + + "hover:bg-white/20 backdrop-blur-md shadow-glass", + + // optional: a slightly stronger white version for primary actions + glassSolid: + "border border-white/40 bg-white/20 text-slate-900 " + + "hover:bg-white/30 backdrop-blur-md shadow-glass", }, size: { default: "h-9 px-4 py-2", diff --git a/client/src/components/ui/GlassButton.tsx b/client/src/components/ui/GlassButton.tsx new file mode 100644 index 0000000..8676705 --- /dev/null +++ b/client/src/components/ui/GlassButton.tsx @@ -0,0 +1,13 @@ +export function GlassButton({ children, onClick }: any) { + return ( + + ); +} \ No newline at end of file diff --git a/client/src/index.css b/client/src/index.css index 91f20d2..e51aa75 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,195 +1,57 @@ +/* 1️⃣ Import Tailwind */ @import "tailwindcss"; -@plugin "tailwindcss-animate"; -@custom-variant dark (&:is(.dark *)); +/* 2️⃣ Import Satoshi font from Fontshare */ +@import url("https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&display=swap"); -@tailwind base; -@tailwind components; -@tailwind utilities; +/* 3️⃣ Define theme variables (font + glass shadow) */ +@theme { + --font-sans: "Satoshi", system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, Helvetica, Arial, sans-serif; -/* src/index.css */ -html, body, #root { height: 100%; } -body { background: transparent; } - - -/* :root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.13 0.028 261.692); - --card: oklch(1 0 0); - --card-foreground: oklch(0.13 0.028 261.692); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.13 0.028 261.692); - --primary: oklch(0.21 0.034 264.665); - --primary-foreground: oklch(0.985 0.002 247.839); - --secondary: oklch(0.967 0.003 264.542); - --secondary-foreground: oklch(0.21 0.034 264.665); - --muted: oklch(0.967 0.003 264.542); - --muted-foreground: oklch(0.551 0.027 264.364); - --accent: oklch(0.967 0.003 264.542); - --accent-foreground: oklch(0.21 0.034 264.665); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.928 0.006 264.531); - --input: oklch(0.928 0.006 264.531); - --ring: oklch(0.707 0.022 261.325); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0.002 247.839); - --sidebar-foreground: oklch(0.13 0.028 261.692); - --sidebar-primary: oklch(0.21 0.034 264.665); - --sidebar-primary-foreground: oklch(0.985 0.002 247.839); - --sidebar-accent: oklch(0.967 0.003 264.542); - --sidebar-accent-foreground: oklch(0.21 0.034 264.665); - --sidebar-border: oklch(0.928 0.006 264.531); - --sidebar-ring: oklch(0.707 0.022 261.325); + --shadow-glass: 0 12px 40px rgba(0, 0, 0, 0.35); } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} +/* 4️⃣ Base styles that apply globally */ +@layer base { + html { + font-size: 19px; /* slightly larger base */ + } -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} + body { + @apply font-sans antialiased text-slate-100 bg-transparent selection:bg-white/20; + text-shadow: 0 1px 2px rgba(0,0,0,0.4); + } -h1 { - font-size: 3.2em; - line-height: 1.1; + /* ✨ Ensure all inputs, selects, and buttons use white text */ + input, select, textarea, button { + @apply text-slate-100 placeholder:text-slate-400; + } } -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} -@theme inline { - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); -} -.dark { - --background: oklch(0.13 0.028 261.692); - --foreground: oklch(0.985 0.002 247.839); - --card: oklch(0.21 0.034 264.665); - --card-foreground: oklch(0.985 0.002 247.839); - --popover: oklch(0.21 0.034 264.665); - --popover-foreground: oklch(0.985 0.002 247.839); - --primary: oklch(0.928 0.006 264.531); - --primary-foreground: oklch(0.21 0.034 264.665); - --secondary: oklch(0.278 0.033 256.848); - --secondary-foreground: oklch(0.985 0.002 247.839); - --muted: oklch(0.278 0.033 256.848); - --muted-foreground: oklch(0.707 0.022 261.325); - --accent: oklch(0.278 0.033 256.848); - --accent-foreground: oklch(0.985 0.002 247.839); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.551 0.027 264.364); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.034 264.665); - --sidebar-foreground: oklch(0.985 0.002 247.839); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0.002 247.839); - --sidebar-accent: oklch(0.278 0.033 256.848); - --sidebar-accent-foreground: oklch(0.985 0.002 247.839); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); +/* @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"); + +@import "tailwindcss"; +@plugin "tailwindcss-animate"; + + + +/* Global font + smoothing */ +/* html, +body { + @apply font-sans antialiased text-slate-100; + font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, Helvetica, Arial, sans-serif; } -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} */ +@custom-variant dark (&:is(.dark *)); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, body, #root { height: 100%; } +body { background: transparent; } */ \ No newline at end of file diff --git a/client/src/lib/ai.ts b/client/src/lib/ai.ts new file mode 100644 index 0000000..e69de29 diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 4b52cca..b9a0727 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -34,25 +34,25 @@ async function mcp(tool: string, input: Record = {}): Promise } export const api = { - // Pull repos via MCP repo_reader tool (mocked backend). Maps to simple string[] of full_name. + // ✅ method syntax (preferred) async listRepos(): Promise<{ repos: string[] }> { - const data = await mcp<{ - repositories: { name: string; full_name: string; branches?: string[] }[]; - }>("repo_reader", {}); - const repos = (data?.data?.repositories ?? []).map((r) => r.full_name); + const data = await mcp<{ repositories: { full_name: string; branches?: string[] }[] }>( + "repo_reader", + {} + ); + const repos = (data.repositories ?? []).map(r => r.full_name); return { repos }; }, - // Derive branches by calling repo_reader again and selecting the repo. async listBranches(repo: string): Promise<{ branches: string[] }> { - const data = await mcp<{ - repositories: { name: string; full_name: string; branches?: string[] }[]; - }>("repo_reader", {}); - const item = (data?.data?.repositories ?? []).find((r) => r.full_name === repo); + const data = await mcp<{ repositories: { full_name: string; branches?: string[] }[] }>( + "repo_reader", + {} + ); + const item = (data.repositories ?? []).find(r => r.full_name === repo); return { branches: item?.branches ?? [] }; }, - // Generate pipeline via MCP pipeline_generator (mock). Assume provider 'aws' for now. async createPipeline(payload: any) { const { repo, branch, template = "node_app", options } = payload || {}; const data = await mcp("pipeline_generator", { @@ -65,21 +65,22 @@ export const api = { return data; }, - // List AWS roles via MCP oidc_adapter and map to list of ARNs. async listAwsRoles(): Promise<{ roles: string[] }> { const data = await mcp<{ roles?: { name: string; arn: string }[] }>( "oidc_adapter", { provider: "aws" } ); - const roles = (data.roles ?? []).map((r) => r.arn); - return { roles }; + return { roles: (data.roles ?? []).map(r => r.arn) }; }, - // Not implemented on server yet; keep API shape but throw a helpful error. async openPr(_payload: any) { throw new Error("openPr is not implemented on the server (no MCP tool)"); }, + // ... keep the rest of your existing methods like getConnections, getSecretPresence, etc. + + + // --- Mocked config/secrets endpoints for Secrets/Preflight flow --- async getConnections(_repo: string): Promise<{ githubAppInstalled: boolean; diff --git a/client/src/pages/ConfigurePage.tsx b/client/src/pages/ConfigurePage.tsx index 9f5c070..d833d9e 100644 --- a/client/src/pages/ConfigurePage.tsx +++ b/client/src/pages/ConfigurePage.tsx @@ -5,6 +5,7 @@ import { useRepoStore } from "../store/useRepoStore"; import { usePipelineStore } from "../store/usePipelineStore"; import { useChatStore } from "../store/useChatStore"; import { parseChatForPipeline, summarizeChanges } from "@/lib/aiAssist"; +import { GlassButton } from "@/components/ui/GlassButton"; const STAGES = ["build", "test", "deploy"] as const; @@ -110,16 +111,18 @@ export default function ConfigurePage() {
{/* history */} -
+
{messages.map((m, i) => (
- {m.text} - + className={`inline-block rounded px-3 py-2 text-sm leading-relaxed whitespace-pre-wrap ${ + m.role === "user" + ? "bg-white/20 text-slate-100" + : "bg-white/10 border border-white/20 text-slate-200" + }`} +> + {m.text} +
))}
@@ -131,14 +134,14 @@ export default function ConfigurePage() { "Node 18; npm ci; npm test; npm run build", "role arn:aws:iam::123456789012:role/app-ci", ].map((ex) => ( - + ))}
@@ -156,7 +159,7 @@ export default function ConfigurePage() {
Press ⌘/Ctrl + Enter to send
- - +
@@ -222,7 +225,7 @@ export default function ConfigurePage() { disabled={busy} value={pipeline.options?.awsRoleArn ?? ""} onChange={(e) => pipeline.setOption?.("awsRoleArn", e.target.value)} - className="rounded-md border px-3 py-2" + className="rounded-md border px-3 py-2 text-white bg-black" > {pipeline.roles?.map((r: any) => ( @@ -235,14 +238,14 @@ export default function ConfigurePage() { {/* ====== Actions ====== */}
- - +
{/* ====== YAML Preview ====== */} diff --git a/client/src/pages/ConnectPage.tsx b/client/src/pages/ConnectPage.tsx index 534613d..de2a04d 100644 --- a/client/src/pages/ConnectPage.tsx +++ b/client/src/pages/ConnectPage.tsx @@ -1,4 +1,4 @@ -// src/pages/ConnectPage.tsx +import { GlassButton } from "../components/ui/GlassButton"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { @@ -11,16 +11,16 @@ export default function ConnectPage() { return (
-
+

Connect your repository

- +
@@ -61,13 +61,13 @@ export default function ConnectPage() {
- +
diff --git a/client/tailwind.config.css b/client/tailwind.config.css deleted file mode 100644 index f587683..0000000 --- a/client/tailwind.config.css +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - theme: { extend: {} }, - plugins: [], -};