From 5cd12dec897efc49de22501d1af38e4713dadfc8 Mon Sep 17 00:00:00 2001 From: Forgotten Date: Thu, 26 Feb 2026 16:33:29 -0600 Subject: [PATCH] feat(ui): light/dark theme toggle and light mode color support Add ThemeContext with localStorage persistence and FOUC-preventing inline script. Add theme toggle button in sidebar. Update status badges, toast notifications, live indicators, and approval cards with dark: prefixed classes for proper light mode rendering. Co-Authored-By: Claude Opus 4.6 --- ui/index.html | 21 +++++++ ui/src/components/ApprovalCard.tsx | 8 +-- ui/src/components/Layout.tsx | 46 ++++++++++++++- ui/src/components/LiveRunWidget.tsx | 16 +++--- ui/src/components/MarkdownBody.tsx | 5 +- ui/src/components/SidebarAgents.tsx | 2 +- ui/src/components/SidebarNavItem.tsx | 7 ++- ui/src/context/ThemeContext.tsx | 83 ++++++++++++++++++++++++++++ ui/src/lib/status-colors.ts | 82 +++++++++++++-------------- ui/src/main.tsx | 41 +++++++------- ui/src/pages/ApprovalDetail.tsx | 12 ++-- ui/src/pages/DesignGuide.tsx | 8 +-- ui/src/pages/Inbox.tsx | 6 +- 13 files changed, 245 insertions(+), 92 deletions(-) create mode 100644 ui/src/context/ThemeContext.tsx diff --git a/ui/index.html b/ui/index.html index 906158e4..7c93cbf5 100644 --- a/ui/index.html +++ b/ui/index.html @@ -14,6 +14,27 @@ +
diff --git a/ui/src/components/ApprovalCard.tsx b/ui/src/components/ApprovalCard.tsx index 445e93ac..54487425 100644 --- a/ui/src/components/ApprovalCard.tsx +++ b/ui/src/components/ApprovalCard.tsx @@ -7,10 +7,10 @@ import { timeAgo } from "../lib/timeAgo"; import type { Approval, Agent } from "@paperclip/shared"; function statusIcon(status: string) { - if (status === "approved") return ; - if (status === "rejected") return ; - if (status === "revision_requested") return ; - if (status === "pending") return ; + if (status === "approved") return ; + if (status === "rejected") return ; + if (status === "revision_requested") return ; + if (status === "pending") return ; return null; } diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index 1e920b6a..ff1f28bf 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState, type UIEvent } from "react"; import { useQuery } from "@tanstack/react-query"; -import { BookOpen } from "lucide-react"; +import { BookOpen, Moon, Sun } from "lucide-react"; import { Outlet } from "react-router-dom"; import { CompanyRail } from "./CompanyRail"; import { Sidebar } from "./Sidebar"; @@ -19,20 +19,24 @@ import { useDialog } from "../context/DialogContext"; import { usePanel } from "../context/PanelContext"; import { useCompany } from "../context/CompanyContext"; import { useSidebar } from "../context/SidebarContext"; +import { useTheme } from "../context/ThemeContext"; import { useKeyboardShortcuts } from "../hooks/useKeyboardShortcuts"; import { useCompanyPageMemory } from "../hooks/useCompanyPageMemory"; import { healthApi } from "../api/health"; import { queryKeys } from "../lib/queryKeys"; import { cn } from "../lib/utils"; +import { Button } from "@/components/ui/button"; export function Layout() { const { sidebarOpen, setSidebarOpen, toggleSidebar, isMobile } = useSidebar(); const { openNewIssue, openOnboarding } = useDialog(); const { panelContent, closePanel } = usePanel(); const { companies, loading: companiesLoading, setSelectedCompanyId } = useCompany(); + const { theme, toggleTheme } = useTheme(); const onboardingTriggered = useRef(false); const lastMainScrollTop = useRef(0); const [mobileNavVisible, setMobileNavVisible] = useState(true); + const nextTheme = theme === "dark" ? "light" : "dark"; const { data: health } = useQuery({ queryKey: queryKeys.health, queryFn: () => healthApi.get(), @@ -168,7 +172,25 @@ export function Layout() {
- +
+ + +
) : ( @@ -185,7 +207,25 @@ export function Layout() {
- +
+ + +
)} diff --git a/ui/src/components/LiveRunWidget.tsx b/ui/src/components/LiveRunWidget.tsx index 32411e1f..11e1e5d5 100644 --- a/ui/src/components/LiveRunWidget.tsx +++ b/ui/src/components/LiveRunWidget.tsx @@ -344,7 +344,7 @@ export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {