Download frontend/src/theme.tsx from SevdanurGenc/LLM-Automated-Test-Generation-Agent: direct link, hf CLI and curl.
- Browser
- Download file 2.73 kB
-
https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/frontend/src/theme.tsx
- Command line
-
hf download hf://SevdanurGenc/LLM-Automated-Test-Generation-Agent/frontend/src/theme.tsx
-
curl -L -o theme.tsx https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/frontend/src/theme.tsx
2.73 kB
| import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'; | |
| import { useI18n } from './i18n'; | |
| /** Aydınlık / karanlık tema. İlk açılışta sistem ayarı izlenir; seçim yapılınca tarayıcıda hatırlanır. */ | |
| type Theme = 'light' | 'dark'; | |
| const KEY = 'testajani.theme'; | |
| const mq = () => window.matchMedia('(prefers-color-scheme: dark)'); | |
| function stored(): Theme | null { | |
| try { | |
| const v = window.localStorage.getItem(KEY); | |
| return v === 'light' || v === 'dark' ? v : null; | |
| } catch { return null; } | |
| } | |
| const Ctx = createContext<{ dark: boolean; toggle: () => void; choice: Theme | null; setChoice: (t: Theme) => void }>( | |
| { dark: false, toggle: () => {}, choice: null, setChoice: () => {} }); | |
| export function ThemeProvider({ children }: { children: ReactNode }) { | |
| const [choice, setChoice] = useState<Theme | null>(stored); | |
| const [sysDark, setSysDark] = useState(() => mq().matches); | |
| useEffect(() => { | |
| const m = mq(); | |
| const h = () => setSysDark(m.matches); | |
| m.addEventListener('change', h); | |
| return () => m.removeEventListener('change', h); | |
| }, []); | |
| const dark = choice ? choice === 'dark' : sysDark; | |
| useEffect(() => { | |
| // CSS, :root[data-theme] ile açık seçimi; yoksa sistem ayarını (prefers-color-scheme) kullanır | |
| if (choice) document.documentElement.dataset.theme = choice; | |
| else delete document.documentElement.dataset.theme; | |
| }, [choice]); | |
| const choose = useCallback((next: Theme) => { | |
| setChoice(next); | |
| try { window.localStorage.setItem(KEY, next); } catch { /* yoksay */ } | |
| }, []); | |
| const toggle = useCallback(() => choose(dark ? 'light' : 'dark'), [dark, choose]); | |
| return <Ctx.Provider value={{ dark, toggle, choice, setChoice: choose }}>{children}</Ctx.Provider>; | |
| } | |
| export const useTheme = () => useContext(Ctx); | |
| export function ThemeSwitch() { | |
| const { dark, toggle } = useTheme(); | |
| const { t } = useI18n(); | |
| const label = dark ? t('theme.toLight') : t('theme.toDark'); | |
| return ( | |
| <button className="icon-btn theme-btn" onClick={toggle} title={label} aria-label={label}> | |
| {dark ? ( | |
| <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"> | |
| <circle cx="12" cy="12" r="4.5" /> | |
| <path d="M12 2v2.5M12 19.5V22M2 12h2.5M19.5 12H22M4.9 4.9l1.8 1.8M17.3 17.3l1.8 1.8M4.9 19.1l1.8-1.8M17.3 6.7l1.8-1.8" /> | |
| </svg> | |
| ) : ( | |
| <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"> | |
| <path d="M20.5 14.5A8.5 8.5 0 0 1 9.5 3.5a8.5 8.5 0 1 0 11 11z" /> | |
| </svg> | |
| )} | |
| </button> | |
| ); | |
| } | |