// devMode.js // Minimal, single-file dev mode with scriptable console API /** * Initialize dev mode: exposes a scriptable API for stepping through state history. * @param {object} opts * @param {function} opts.getState - returns current app state * @param {function} opts.setState - sets app state * @param {function} opts.render - triggers app re-render */ export function initDevMode({ getState, setState, render }) { let history = []; let pointer = -1; let firstLoad = true; function pushState(state) { if (pointer < history.length - 1) history = history.slice(0, pointer + 1); history.push(clone(state)); pointer = history.length - 1; logInstructions(); } function goTo(idx) { if (idx < 0 || idx >= history.length) return; pointer = idx; setState(clone(history[pointer])); render(); logInstructions(); } function next() { if (pointer < history.length - 1) goTo(pointer + 1); } function prev() { if (pointer > 0) goTo(pointer - 1); } function get() { return history[pointer]; } function clone(obj) { return JSON.parse(JSON.stringify(obj)); } function table(obj) { console.table(dev.history); } function logInstructions() { if (firstLoad) { console.log('[DevMode] State history debugger'); console.log('Usage:'); console.log('- dev.next() // step forward'); console.log('- dev.prev() // step backward'); console.log('- dev.goTo(n) // jump to state n (1-based)'); console.log('- dev.get() // get current state'); console.log('- dev.table() // display history as a table'); console.log('- dev.history // array of all states'); console.log('- dev.pointer // current pointer (0-based)'); firstLoad = false; } } // Expose API globally for console use window.dev = { next, prev, goTo, get, table, get pointer() { return pointer; }, get history() { return history.slice(); }, }; // Initial state pushState(getState()); return { pushState }; }