Files
site-builder/craft/src/state/ProjectExtrasContext.tsx
T
shadowdaoandClaude Sonnet 5.5 21c2776c88 feat(site-builder): block registry, notices, local prefs, project extras, overlay slot
Add an exported block registry (keywords, search, insertBlock) consumed by
BlocksPanel, a NoticeProvider/NoticeHost toast stack, a guarded localStorage
prefs helper, a ProjectExtras store persisted as project.json 'extras', and a
CanvasOverlays mount point.

Refs #38
Refs #41
Refs #49
Refs #48
Refs #57
Refs #58

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 11:14:51 -07:00

54 lines
2.0 KiB
TypeScript

import React, { createContext, useCallback, useContext, useMemo, useRef, ReactNode } from 'react';
import { useSaveGuard } from './SaveGuardContext';
/**
* Feature data that is not Craft state (style presets, custom sections, ...),
* persisted as the top-level `extras` key of project.json. Held in a ref, not
* React state: nothing renders from it directly, and a save queued behind
* another must read the latest value at send time.
*/
interface ProjectExtrasValue {
get<T>(key: string, fallback: T): T;
set(key: string, value: unknown): void;
update<T>(key: string, fn: (prev: T) => T, fallback: T): void;
all(): Record<string, unknown>;
/** Load hydration. Does NOT mark the editor dirty. */
replaceAll(extras: Record<string, unknown>): void;
}
const noop: ProjectExtrasValue = {
get: (_k, fallback) => fallback,
set: () => {},
update: () => {},
all: () => ({}),
replaceAll: () => {},
};
const ProjectExtrasContext = createContext<ProjectExtrasValue>(noop);
export const useProjectExtras = () => useContext(ProjectExtrasContext);
export const ProjectExtrasProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const { markDirty } = useSaveGuard();
const store = useRef<Record<string, unknown>>({});
const markRef = useRef(markDirty);
markRef.current = markDirty;
const value = useMemo<ProjectExtrasValue>(() => ({
get: <T,>(key: string, fallback: T): T => (key in store.current ? (store.current[key] as T) : fallback),
set: (key, v) => {
store.current = { ...store.current, [key]: v };
markRef.current();
},
update: <T,>(key: string, fn: (prev: T) => T, fallback: T) => {
const prev = (key in store.current ? store.current[key] : fallback) as T;
store.current = { ...store.current, [key]: fn(prev) };
markRef.current();
},
all: () => store.current,
replaceAll: (extras) => { store.current = { ...extras }; },
}), []);
return <ProjectExtrasContext.Provider value={value}>{children}</ProjectExtrasContext.Provider>;
};