UNPKG

qnce-engine

Version:

Core QNCE (Quantum Narrative Convergence Engine) - Framework agnostic narrative engine with performance optimization

323 lines (322 loc) 10 kB
"use strict"; /** * QNCE Engine React Integration * * React hooks and utilities for integrating QNCE Engine with React applications. * Provides convenient hooks for managing narrative state, undo/redo operations, * and autosave functionality. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.useQNCE = useQNCE; exports.useUndoRedo = useUndoRedo; exports.useAutosave = useAutosave; const react_1 = require("react"); /** * React hook for QNCE Engine integration * * Provides a complete interface for managing QNCE Engine state in React applications, * including undo/redo functionality, autosave, and automatic re-renders. * * @param engine - The QNCE Engine instance * @param config - Configuration options for the hook * @returns Hook return object with state and actions * * @example * ```tsx * function NarrativeComponent() { * const engine = useMemo(() => createQNCEEngine(DEMO_STORY), []); * const { * currentNode, * availableChoices, * selectChoice, * undo, * redo, * canUndo, * canRedo * } = useQNCE(engine, { * enableUndoRedo: true, * enableAutosave: true, * maxUndoEntries: 50 * }); * * return ( * <div> * <p>{currentNode?.text}</p> * * <div> * {availableChoices.map(choice => ( * <button key={choice.text} onClick={() => selectChoice(choice)}> * {choice.text} * </button> * ))} * </div> * * <div> * <button onClick={undo} disabled={!canUndo}> * Undo * </button> * <button onClick={redo} disabled={!canRedo}> * Redo * </button> * </div> * </div> * ); * } * ``` */ function useQNCE(engine, config = {}) { const { autoUpdate = true, enableUndoRedo = true, maxUndoEntries = 50, maxRedoEntries = 25, enableAutosave = true, autosaveThrottleMs = 100 } = config; // Force re-render counter const [, setUpdateCounter] = (0, react_1.useState)(0); // Initialize engine configuration (0, react_1.useEffect)(() => { if (enableUndoRedo) { engine.configureUndoRedo({ enabled: true, maxUndoEntries, maxRedoEntries }); } if (enableAutosave) { engine.configureAutosave({ enabled: true, throttleMs: autosaveThrottleMs, triggers: ['choice', 'flag-change', 'state-load'] }); } }, [engine, enableUndoRedo, maxUndoEntries, maxRedoEntries, enableAutosave, autosaveThrottleMs]); // Force re-render when autoUpdate is enabled const refresh = (0, react_1.useCallback)(() => { if (autoUpdate) { setUpdateCounter(prev => prev + 1); } }, [autoUpdate]); // Memoized state getters const currentNode = (0, react_1.useMemo)(() => { try { return engine.getCurrentNode(); } catch { return null; } }, [engine, autoUpdate]); const availableChoices = (0, react_1.useMemo)(() => { try { return engine.getAvailableChoices(); } catch { return []; } }, [engine, autoUpdate]); const flags = (0, react_1.useMemo)(() => { return engine.getState().flags; }, [engine, autoUpdate]); // Undo/Redo state const canUndo = (0, react_1.useMemo)(() => engine.canUndo(), [engine, autoUpdate]); const canRedo = (0, react_1.useMemo)(() => engine.canRedo(), [engine, autoUpdate]); const undoCount = (0, react_1.useMemo)(() => engine.getUndoCount(), [engine, autoUpdate]); const redoCount = (0, react_1.useMemo)(() => engine.getRedoCount(), [engine, autoUpdate]); // Actions with automatic refresh const selectChoice = (0, react_1.useCallback)(async (choice) => { let choiceToSelect; if (typeof choice === 'string') { // Find choice by text const foundChoice = availableChoices.find(c => c.text === choice); if (!foundChoice) { throw new Error(`Choice not found: ${choice}`); } choiceToSelect = foundChoice; } else { choiceToSelect = choice; } engine.selectChoice(choiceToSelect); refresh(); }, [engine, refresh, availableChoices]); const setFlag = (0, react_1.useCallback)((key, value) => { engine.setFlag(key, value); refresh(); }, [engine, refresh]); const resetNarrative = (0, react_1.useCallback)(() => { engine.resetNarrative(); refresh(); }, [engine, refresh]); const undo = (0, react_1.useCallback)(() => { const result = engine.undo(); refresh(); return result; }, [engine, refresh]); const redo = (0, react_1.useCallback)(() => { const result = engine.redo(); refresh(); return result; }, [engine, refresh]); const clearHistory = (0, react_1.useCallback)(() => { engine.clearHistory(); refresh(); }, [engine, refresh]); const autosave = (0, react_1.useCallback)(async () => { await engine.manualAutosave(); }, [engine]); const configureAutosave = (0, react_1.useCallback)((config) => { engine.configureAutosave(config); }, [engine]); const saveState = (0, react_1.useCallback)(async () => { return await engine.saveState(); }, [engine]); const loadState = (0, react_1.useCallback)(async (serializedState) => { await engine.loadState(serializedState); refresh(); }, [engine, refresh]); return { // Core state engine, currentNode, availableChoices, flags, // Actions selectChoice, setFlag, resetNarrative, // Undo/Redo undo, redo, canUndo, canRedo, undoCount, redoCount, clearHistory, // Autosave autosave, configureAutosave, // State management saveState, loadState, // Utility refresh }; } /** * Hook for managing just undo/redo functionality * * A lightweight hook focused specifically on undo/redo operations. * Useful when you want to add undo/redo to an existing QNCE integration. * * @param engine - The QNCE Engine instance * @param config - Undo/redo configuration * @returns Undo/redo state and actions * * @example * ```tsx * function UndoRedoControls({ engine }: { engine: QNCEEngine }) { * const { undo, redo, canUndo, canRedo, undoCount, redoCount } = useUndoRedo(engine); * * return ( * <div className="undo-redo-controls"> * <button onClick={undo} disabled={!canUndo}> * ← Undo ({undoCount}) * </button> * <button onClick={redo} disabled={!canRedo}> * Redo ({redoCount}) → * </button> * </div> * ); * } * ``` */ function useUndoRedo(engine, config = {}) { const { maxUndoEntries = 50, maxRedoEntries = 25 } = config; const [, setUpdateCounter] = (0, react_1.useState)(0); // Configure undo/redo on mount (0, react_1.useEffect)(() => { engine.configureUndoRedo({ enabled: true, maxUndoEntries, maxRedoEntries }); }, [engine, maxUndoEntries, maxRedoEntries]); const refresh = (0, react_1.useCallback)(() => { setUpdateCounter(prev => prev + 1); }, []); const undo = (0, react_1.useCallback)(() => { const result = engine.undo(); refresh(); return result; }, [engine, refresh]); const redo = (0, react_1.useCallback)(() => { const result = engine.redo(); refresh(); return result; }, [engine, refresh]); const clearHistory = (0, react_1.useCallback)(() => { engine.clearHistory(); refresh(); }, [engine, refresh]); const canUndo = (0, react_1.useMemo)(() => engine.canUndo(), [engine]); const canRedo = (0, react_1.useMemo)(() => engine.canRedo(), [engine]); const undoCount = (0, react_1.useMemo)(() => engine.getUndoCount(), [engine]); const redoCount = (0, react_1.useMemo)(() => engine.getRedoCount(), [engine]); const historySummary = (0, react_1.useMemo)(() => ({ undoCount, redoCount, canUndo, canRedo }), [undoCount, redoCount, canUndo, canRedo]); return { undo, redo, canUndo, canRedo, undoCount, redoCount, clearHistory, historySummary }; } /** * Hook for managing autosave functionality * * Provides control over the autosave system for fine-grained management. * * @param engine - The QNCE Engine instance * @param config - Autosave configuration * @returns Autosave state and actions * * @example * ```tsx * function AutosaveIndicator({ engine }: { engine: QNCEEngine }) { * const { autosave, configure, isEnabled } = useAutosave(engine, { * throttleMs: 200, * triggers: ['choice', 'flag-change'] * }); * * return ( * <div> * <span>Autosave: {isEnabled ? 'ON' : 'OFF'}</span> * <button onClick={autosave}>Save Now</button> * </div> * ); * } * ``` */ function useAutosave(engine, config = {}) { const { throttleMs = 100, triggers = ['choice', 'flag-change', 'state-load'], enabled = true } = config; (0, react_1.useEffect)(() => { engine.configureAutosave({ enabled, throttleMs, triggers: triggers }); }, [engine, enabled, throttleMs, triggers]); const autosave = (0, react_1.useCallback)(async () => { await engine.manualAutosave(); }, [engine]); const configure = (0, react_1.useCallback)((config) => { engine.configureAutosave(config); }, [engine]); return { autosave, configure, isEnabled: enabled }; }