diff --git a/index.html b/index.html index 7e0d3fa..c71ed8b 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,17 @@ Decompile.AI +
diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx new file mode 100644 index 0000000..c4a1554 --- /dev/null +++ b/src/components/ThemeToggle.tsx @@ -0,0 +1,25 @@ +import { Moon, Sun } from "lucide-react"; +import { Button } from "@/components/ui/button.tsx"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip.tsx"; +import { useTheme } from "@/providers/ThemeProvider.tsx"; + +export const ThemeToggle = () => { + const { theme, toggleTheme } = useTheme(); + const isDark = theme === "dark"; + + return ( + + + + + {isDark ? "Light mode" : "Dark mode"} + + ); +}; diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index f59c7ef..2fa2cb6 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,6 +1,7 @@ import { Activity, Binary, FolderOpen, BookOpen } from "lucide-react"; import { Link, NavLink } from "react-router"; import { cn } from "@/lib/utils.ts"; +import { ThemeToggle } from "@/components/ThemeToggle.tsx"; const navItems = [ { href: "/", label: "Workspaces", icon: FolderOpen }, @@ -45,6 +46,11 @@ export const Header = () => { ); })} + + {/* Right actions */} +
+ +
); }; diff --git a/src/features/home/WorkspaceDialog.tsx b/src/features/home/WorkspaceDialog.tsx index 09beb31..ad5f41c 100644 --- a/src/features/home/WorkspaceDialog.tsx +++ b/src/features/home/WorkspaceDialog.tsx @@ -43,11 +43,11 @@ export const WorkspaceDialog = ({ - {isEditing ? "Edit Home" : "Create Home"} + {isEditing ? "Edit Workspace" : "Create Workspace"} {isEditing - ? "Update your home details." - : "Create a new home to organize your reverse engineering projects."} + ? "Update your workspace details." + : "Create a new workspace to organize your reverse engineering projects."}
@@ -76,7 +76,7 @@ export const WorkspaceDialog = ({ Cancel diff --git a/src/index.css b/src/index.css index 7fb8492..733b649 100644 --- a/src/index.css +++ b/src/index.css @@ -53,7 +53,54 @@ } :root { - /* Dark theme as default - IDE style for RE platform */ + /* Light theme - clean IDE style for RE platform */ + --background: oklch(0.98 0.005 260); + --foreground: oklch(0.2 0.02 260); + --card: oklch(1 0 0); + --card-foreground: oklch(0.2 0.02 260); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.2 0.02 260); + --primary: oklch(0.58 0.14 180); + --primary-foreground: oklch(0.98 0.005 260); + --secondary: oklch(0.95 0.01 260); + --secondary-foreground: oklch(0.2 0.02 260); + --muted: oklch(0.96 0.01 260); + --muted-foreground: oklch(0.5 0.02 260); + --accent: oklch(0.58 0.14 180); + --accent-foreground: oklch(0.98 0.005 260); + --destructive: oklch(0.58 0.22 25); + --border: oklch(0.9 0.01 260); + --input: oklch(0.9 0.01 260); + --ring: oklch(0.58 0.14 180); + --chart-1: oklch(0.58 0.14 180); + --chart-2: oklch(0.6 0.15 140); + --chart-3: oklch(0.55 0.15 280); + --chart-4: oklch(0.65 0.13 60); + --chart-5: oklch(0.5 0.18 330); + --radius: 0.5rem; + --sidebar: oklch(0.97 0.005 260); + --sidebar-foreground: oklch(0.2 0.02 260); + --sidebar-primary: oklch(0.58 0.14 180); + --sidebar-primary-foreground: oklch(0.98 0.005 260); + --sidebar-accent: oklch(0.95 0.01 260); + --sidebar-accent-foreground: oklch(0.2 0.02 260); + --sidebar-border: oklch(0.9 0.01 260); + --sidebar-ring: oklch(0.58 0.14 180); + + /* Custom tokens for RE platform */ + --code-bg: oklch(0.97 0.005 260); + --code-line-number: oklch(0.6 0.01 260); + --code-keyword: oklch(0.5 0.16 280); + --code-string: oklch(0.5 0.13 140); + --code-comment: oklch(0.6 0.05 140); + --code-function: oklch(0.55 0.15 60); + --code-address: oklch(0.5 0.12 180); + --success: oklch(0.55 0.16 145); + --warning: oklch(0.62 0.15 75); +} + +.dark { + /* Dark theme - IDE style for RE platform */ --background: oklch(0.12 0.01 260); --foreground: oklch(0.93 0.01 260); --card: oklch(0.16 0.01 260); @@ -77,7 +124,6 @@ --chart-3: oklch(0.6 0.15 280); --chart-4: oklch(0.75 0.12 60); --chart-5: oklch(0.55 0.18 330); - --radius: 0.5rem; --sidebar: oklch(0.14 0.01 260); --sidebar-foreground: oklch(0.93 0.01 260); --sidebar-primary: oklch(0.65 0.18 180); diff --git a/src/providers/Providers.tsx b/src/providers/Providers.tsx index 36aa6d5..06cd81e 100644 --- a/src/providers/Providers.tsx +++ b/src/providers/Providers.tsx @@ -2,6 +2,7 @@ import type { ReactNode } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Toaster } from "sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; +import { ThemeProvider } from "@/providers/ThemeProvider.tsx"; interface ProvidersProps { children: ReactNode; @@ -9,11 +10,13 @@ interface ProvidersProps { export const Providers = ({ children }: ProvidersProps) => { return ( - - - {children} - - - + + + + {children} + + + + ); }; diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx new file mode 100644 index 0000000..8a33b84 --- /dev/null +++ b/src/providers/ThemeProvider.tsx @@ -0,0 +1,54 @@ +import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; + +export type Theme = "light" | "dark"; + +interface ThemeContextValue { + theme: Theme; + setTheme: (theme: Theme) => void; + toggleTheme: () => void; +} + +const STORAGE_KEY = "decompile-ai-theme"; +const DEFAULT_THEME: Theme = "dark"; + +const ThemeContext = createContext(undefined); + +const getInitialTheme = (): Theme => { + if (typeof window === "undefined") return DEFAULT_THEME; + const stored = window.localStorage.getItem(STORAGE_KEY); + if (stored === "light" || stored === "dark") return stored; + return DEFAULT_THEME; +}; + +interface ThemeProviderProps { + children: ReactNode; +} + +export const ThemeProvider = ({ children }: ThemeProviderProps) => { + const [theme, setThemeState] = useState(getInitialTheme); + + useEffect(() => { + const root = document.documentElement; + root.classList.remove("light", "dark"); + root.classList.add(theme); + window.localStorage.setItem(STORAGE_KEY, theme); + }, [theme]); + + const setTheme = (next: Theme) => setThemeState(next); + const toggleTheme = () => setThemeState((prev) => (prev === "dark" ? "light" : "dark")); + + return ( + + {children} + + ); +}; + +// eslint-disable-next-line react-refresh/only-export-components +export const useTheme = () => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + return context; +};