79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
import { useTheme, 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
|
|
* `resolvedTheme` -- the single source of truth for what's actually
|
|
* applied, including a project's scoped theme (see theme-provider) -- so
|
|
* the moment `setTheme` fires (or a scope lifts), 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 { resolvedTheme } = useTheme();
|
|
if (!mounted) return false;
|
|
return DARK_SURFACES.includes(resolvedTheme);
|
|
}
|
|
|
|
/** 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 { resolvedTheme } = useTheme();
|
|
if (!mounted) return false;
|
|
return resolvedTheme === "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 { resolvedTheme } = useTheme();
|
|
if (!mounted) return getGroupColor(key);
|
|
return getThemedGroupColor(resolvedTheme, 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";
|
|
}
|