Files
gitlab-instance-0a899031_cn…/app/context/DevDebugContext.tsx
2026-06-08 06:05:31 -05:00

123 lines
3.0 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import {
DEBUG_FEATURES,
getDebugFeatureDefaultVisible,
type DebugFeature,
} from "@/config/debug-features";
const STORAGE_KEY = "nomadcna:dev-debug-overrides";
const IS_DEV = process.env.NODE_ENV === "development";
type Overrides = Record<string, boolean>;
interface DevDebugContextValue {
isDev: boolean;
panelOpen: boolean;
setPanelOpen: (open: boolean) => void;
features: DebugFeature[];
overrides: Overrides;
isFeatureVisible: (id: string) => boolean;
setFeatureVisible: (id: string, visible: boolean) => void;
resetAll: () => void;
}
const DevDebugContext = createContext<DevDebugContextValue | null>(null);
function readOverrides(): Overrides {
if (!IS_DEV || typeof window === "undefined") return {};
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return {};
const parsed = JSON.parse(raw) as Overrides;
return parsed && typeof parsed === "object" ? parsed : {};
} catch {
return {};
}
}
function writeOverrides(overrides: Overrides) {
if (!IS_DEV || typeof window === "undefined") return;
try {
if (Object.keys(overrides).length === 0) {
localStorage.removeItem(STORAGE_KEY);
} else {
localStorage.setItem(STORAGE_KEY, JSON.stringify(overrides));
}
} catch {
// ignore quota / private mode
}
}
export function DevDebugProvider({ children }: { children: ReactNode }) {
const [panelOpen, setPanelOpen] = useState(false);
const [overrides, setOverrides] = useState<Overrides>({});
useEffect(() => {
setOverrides(readOverrides());
}, []);
const isFeatureVisible = useCallback(
(id: string) => {
if (!IS_DEV) return getDebugFeatureDefaultVisible(id);
if (id in overrides) return overrides[id];
return getDebugFeatureDefaultVisible(id);
},
[overrides]
);
const setFeatureVisible = useCallback((id: string, visible: boolean) => {
if (!IS_DEV) return;
setOverrides((prev) => {
const defaultVisible = getDebugFeatureDefaultVisible(id);
const next = { ...prev };
if (visible === defaultVisible) {
delete next[id];
} else {
next[id] = visible;
}
writeOverrides(next);
return next;
});
}, []);
const resetAll = useCallback(() => {
if (!IS_DEV) return;
setOverrides({});
writeOverrides({});
}, []);
const value = useMemo<DevDebugContextValue>(
() => ({
isDev: IS_DEV,
panelOpen,
setPanelOpen,
features: DEBUG_FEATURES,
overrides,
isFeatureVisible,
setFeatureVisible,
resetAll,
}),
[panelOpen, overrides, isFeatureVisible, setFeatureVisible, resetAll]
);
return <DevDebugContext.Provider value={value}>{children}</DevDebugContext.Provider>;
}
export function useDevDebug() {
const context = useContext(DevDebugContext);
if (!context) {
throw new Error("useDevDebug must be used within DevDebugProvider");
}
return context;
}