diff --git a/periscope/src/frontend/src/components/theme/theme-provider.tsx b/periscope/src/frontend/src/components/theme/theme-provider.tsx new file mode 100644 index 0000000..9ec648b --- /dev/null +++ b/periscope/src/frontend/src/components/theme/theme-provider.tsx @@ -0,0 +1,18 @@ +"use client"; + +import type { ReactNode } from "react"; +import { ThemeProvider as NextThemes } from "next-themes"; + +/** Class-based color scheme. Default is dark; OS preference is ignored. */ +export function ThemeProvider({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/periscope/src/frontend/src/components/theme/theme-toggle.tsx b/periscope/src/frontend/src/components/theme/theme-toggle.tsx new file mode 100644 index 0000000..8982266 --- /dev/null +++ b/periscope/src/frontend/src/components/theme/theme-toggle.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; +import { cn } from "@/lib/utils"; + +export function ThemeToggle({ className }: { className?: string }) { + const { resolvedTheme, setTheme } = useTheme(); + + const flip = () => { + setTheme(resolvedTheme === "dark" ? "light" : "dark"); + }; + + return ( + + ); +} diff --git a/tests/test_periscope_frontend_theme_rewrite.py b/tests/test_periscope_frontend_theme_rewrite.py new file mode 100644 index 0000000..224ecb7 --- /dev/null +++ b/tests/test_periscope_frontend_theme_rewrite.py @@ -0,0 +1,37 @@ +"""next-themes wrapper and toggle live under periscope/src.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SRC = ROOT / "periscope" / "src" / "frontend" / "src" + + +def test_theme_modules_are_src(): + for rel in ( + "components/theme/theme-provider.tsx", + "components/theme/theme-toggle.tsx", + ): + text = (SRC / rel).read_text(encoding="utf-8") + assert "Native Periscope overlay" not in text[:400] + + +def test_theme_provider_contract(): + text = (SRC / "components/theme/theme-provider.tsx").read_text(encoding="utf-8") + assert "export function ThemeProvider" in text + assert 'attribute="class"' in text + assert 'defaultTheme="dark"' in text + assert "enableSystem={false}" in text + assert "disableTransitionOnChange" in text + clerk = SRC / "components/theme/clerk-theme-provider.tsx" + assert not clerk.exists() + + +def test_theme_toggle_contract(): + text = (SRC / "components/theme/theme-toggle.tsx").read_text(encoding="utf-8") + assert "export function ThemeToggle" in text + assert 'aria-label="Toggle theme"' in text + assert "resolvedTheme" in text + assert 'className="hidden h-4 w-4 dark:block"' in text + assert 'className="h-4 w-4 dark:hidden"' in text