// Shared admin state: the boot data, the plugin state, and a refresh. import { createContext, useCallback, useContext, useEffect, useMemo, useState } from '@wordpress/element'; import type { ReactNode } from '@wordpress/element'; import { get, post } from '../shared/api.ts'; import type { PlanInfo, PluginState, Settings } from '../shared/types.ts'; import { boot, type Boot } from './boot.ts'; interface AppValue { boot: Boot; state: PluginState; setState: (state: PluginState) => void; refresh: () => Promise; saveSettings: (changes: Partial) => Promise; // Saves from a switch: a failure shows at the top of the screen, and null comes back. trySettings: (changes: Partial) => Promise; settingsError: unknown; connected: boolean; usable: boolean; isOwner: boolean; } const AppContext = createContext(null); export function AppProvider({ children }: { children: ReactNode }) { const data = boot(); const [state, setState] = useState(data.state); const refresh = useCallback(async () => { const next = await get('/state'); setState(next); return next; }, []); const saveSettings = useCallback(async (changes: Partial) => { const next = await post('/settings', changes); setState(next); return next; }, []); const [settingsError, setSettingsError] = useState(null); const trySettings = useCallback( async (changes: Partial) => { setSettingsError(null); try { return await saveSettings(changes); } catch (caught) { setSettingsError(caught); return null; } }, [saveSettings], ); const value = useMemo( () => ({ boot: data, state, setState, refresh, saveSettings, trySettings, settingsError, connected: state.connection.connected, usable: state.connection.connected && !state.connection.reconnect, isOwner: Boolean(state.connection.isOwner), }), [data, state, refresh, saveSettings, trySettings, settingsError], ); return {children}; } export function useApp(): AppValue { const value = useContext(AppContext); if (!value) throw new Error('useApp() outside AppProvider'); return value; } // The plan's prompt length limit: 4,000 characters, 8,000 on Business. export function usePromptLimit(): number { const [limit, setLimit] = useState(4000); useEffect(() => { get('/plan') .then((info) => setLimit(info.plan.promptCharacterLimit)) .catch(() => undefined); }, []); return limit; }