Organize/components/theme/use-dark-theme.ts

77 lines
2.8 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useTheme, resolveTheme, DARK_SURFACES } from "./theme-provider";
import { getGroupColor, getThemedGroupColor, type GroupColor } from "@/lib/colors";
// The themes whose *surfaces* are dark (see app/globals.css) come from the
// provider's single source of truth -- currently Dark, Ocean, Pine, Plum,
// Midnight, Ember, Rosewood. Anything that used to compare
// `resolvedTheme === "dark"` to pick a color variant (group card
// fills/strokes, markdown widget color modes, ...) uses this instead.
function useMounted(): boolean {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
}
/**
* True when the active theme has dark surfaces (Dark, Ocean, Pine, Plum,
* Midnight, Ember, Rosewood, Cyberpunk).
*
* Reactive to UI theme switches: the value is derived from the provider's
* theme state (the single source of truth), so the moment `setTheme` fires,
* every consumer re-renders with the new variant in the same commit -- no
* stale card colors, no manual refresh needed.
*
* Hydration-safe: until mounted we always report "light", matching the
* server render (the no-FOUC script in app/layout.tsx covers the CSS side
* of that first frame).
*/
export function isDarkTheme(): boolean {
const mounted = useMounted();
const { theme } = useTheme();
if (!mounted) return false;
return DARK_SURFACES.includes(resolveTheme(theme));
}
/** True when the "cyberpunk" theme is active. Used to swap the neon
* color palette onto group cards only for that theme.
*/
export function isCyberpunkTheme(): boolean {
const mounted = useMounted();
const { theme } = useTheme();
if (!mounted) return false;
return resolveTheme(theme) === "cyberpunk";
}
/**
* The group color for the active theme. Themes with their own palette
* (cyberpunk, blueprint, vaporwave, notebook, starfield -- see
* lib/colors.ts) get theirs; everything else gets the base color. Same
* contract as isDarkTheme: reactive to theme switches (derived from the
* provider's state, so a setTheme re-renders every consumer in the same
* commit) and hydration-safe (until mounted we report the base color,
* matching the server render).
*/
export function useGroupColor(key: string): GroupColor {
const mounted = useMounted();
const { theme } = useTheme();
if (!mounted) return getGroupColor(key);
return getThemedGroupColor(resolveTheme(theme), key);
}
/**
* The `data-color-mode` value the markdown editor/preview widgets expect.
* They only understand light/dark, so the light looks (default, sunset,
* meadow, honey, rose, lavender, slate) report as light and the dark looks
* report as dark.
*/
export function colorModeFromTheme(): "light" | "dark" {
return isDarkTheme() ? "dark" : "light";
}