### beginTransaction() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Demonstrates how to start a transaction, execute commands, and commit them as a single unit. ```javascript undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.execute(command3); undoManager.commitTransaction(); ``` -------------------------------- ### abortTransaction() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Illustrates how to start a transaction, queue commands, and then abort the transaction to undo those commands. ```javascript undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.abortTransaction(); ``` -------------------------------- ### createSnapshot() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example usage of createSnapshot(). ```javascript undoManager.execute(command1); undoManager.execute(command2); undoManager.createSnapshot(); ``` -------------------------------- ### importState() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example of importing state from localStorage. ```javascript const savedState = JSON.parse(localStorage.getItem('appState')); undoManager.importState(savedState); ``` -------------------------------- ### Install Oops.js using npm Source: https://github.com/heyputer/oops.js/blob/master/README.md Installs the Oops.js package via npm and provides an example of how to import it into a JavaScript file. ```bash npm install @heyputer/oops.js ``` ```javascript import Oops from '@heyputer/oops.js'; ``` -------------------------------- ### deserializeCommand() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example of deserializing a single command. ```javascript const serialized = { type: 'AddCommand', data: { value: 10 } }; const command = undoManager.deserializeCommand(serialized); ``` -------------------------------- ### maxStackSize Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Examples demonstrating how to set the maxStackSize option for memory-constrained or rich editor environments. ```javascript // Memory-constrained environment: keep only 30 commands const undoManager = new Oops({ maxStackSize: 30 }); // Rich editor: allow up to 200 commands const undoManager = new Oops({ maxStackSize: 200 }); ``` -------------------------------- ### snapshotInterval Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Examples showing how to configure the snapshotInterval for different application needs, balancing safety and memory usage. ```javascript // Safety-critical: create snapshots every 5 commands const undoManager = new Oops({ snapshotInterval: 5 }); // Memory-constrained: create snapshots every 25 commands const undoManager = new Oops({ snapshotInterval: 25 }); ``` -------------------------------- ### serializeState() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example of serializing the state and saving it to localStorage. ```javascript const jsonState = undoManager.serializeState(); localStorage.setItem('undo-state', jsonState); ``` -------------------------------- ### deserializeState() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example of deserializing state from localStorage. ```javascript const jsonState = localStorage.getItem('undo-state'); undoManager.deserializeState(jsonState); ``` -------------------------------- ### npm Installation Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Command to install the oops.js package using npm. ```bash npm install @heyputer/oops.js ``` -------------------------------- ### exportState() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example of exporting the state and saving it to localStorage. ```javascript const state = undoManager.exportState(); localStorage.setItem('appState', JSON.stringify(state)); ``` -------------------------------- ### Use Case Example - Text Editing Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Examples demonstrating how to configure mergeWindow for different text editing scenarios, from aggressive merging to disabling auto-merging. ```javascript // Aggressive merging: merge all character insertions within 500ms of each other // User types "hello" quickly - all merged into single undo const undoManager = new Oops({ mergeWindow: 500 }); // Conservative merging: only merge within 1500ms // User types slowly - more undo steps but finer control const undoManager = new Oops({ mergeWindow: 1500 }); // Disable auto-merging (merging still possible via command merge()) const undoManager = new Oops({ mergeWindow: 0 }); ``` -------------------------------- ### yarn Installation Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Command to install the oops.js package using yarn. ```bash yarn add @heyputer/oops.js ``` -------------------------------- ### registerCommand() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Demonstrates registering a custom command factory and then executing a command by its registered name. ```javascript class DeleteUserCommand { constructor(data) { this.userId = data?.userId; } execute() { /* ... */ } undo() { /* ... */ } serialize() { return { type: 'DeleteUserCommand', data: { userId: this.userId } }; } } undoManager.registerCommand('deleteUser', (data) => new DeleteUserCommand(data)); undoManager.execute('deleteUser'); ``` -------------------------------- ### recoverFromSnapshot() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Example usage of recoverFromSnapshot() within a try-catch block. ```javascript try { undoManager.undo(); } catch (error) { undoManager.recoverFromSnapshot(); } ``` -------------------------------- ### Type Implementation Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Complete example showing all type interactions, including Command interface implementation, manager instantiation, command registration, change listener, execution, and state export/import. ```javascript // Implement Command interface class UserCreateCommand { constructor(userData) { this.userData = userData; this.userId = null; } execute() { // Execute command this.userId = database.createUser(this.userData); return this.userId; } undo() { // Reverse command database.deleteUser(this.userId); } serialize() { // Return SerializedCommand return { type: 'UserCreateCommand', data: { userData: this.userData, userId: this.userId } }; } canMerge(other) { return false; // User creates don't merge } } // Create manager with ConstructorOptions const undoManager = new Oops({ maxStackSize: 50, mergeWindow: 1000 }); // Register command with CommandFactory undoManager.registerCommand('userCreate', (data) => { const cmd = new UserCreateCommand(data?.userData); cmd.userId = data?.userId; return cmd; }); // Add ChangeListener undoManager.addChangeListener((state) => { console.log('State changed:', state); // State object }); // Execute with ExecuteOptions undoManager.execute(new UserCreateCommand({name: 'John'}), { silent: false, undoable: true }); // Export and restore const exportedState = undoManager.exportState(); // ExportedState const json = JSON.stringify(exportedState); undoManager.importState(JSON.parse(json)); ``` -------------------------------- ### pnpm Installation Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Command to install the oops.js package using pnpm. ```bash pnpm add @heyputer/oops.js ``` -------------------------------- ### Dynamic Configuration Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Demonstrates reading and modifying configuration settings dynamically after instantiation, with a note that creating a new instance is generally recommended. ```javascript const undoManager = new Oops({ maxStackSize: 50, snapshotInterval: 10 }); // Read current settings (for inspection) const maxStack = undoManager.maxStackSize; const interval = undoManager.snapshotInterval; // Modify settings (this is supported internally) // Note: Not recommended - create new instance instead undoManager.maxStackSize = 100; undoManager.mergeWindow = 2000; ``` -------------------------------- ### Oops Constructor Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Demonstrates how to create a new instance of the Oops undo/redo manager with custom configuration options. ```javascript const undoManager = new Oops({ maxStackSize: 50, snapshotInterval: 15, compressThreshold: 200, mergeWindow: 500 }); ``` -------------------------------- ### DebugCommand Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md An example of a command class demonstrating execute, undo, and serialize methods with error logging. ```javascript class DebugCommand { execute() { console.log('Executing:', this.constructor.name); try { // Operation } catch (error) { console.error('Execute error:', error); throw error; } } undo() { console.log('Undoing:', this.constructor.name); try { // Undo operation } catch (error) { console.error('Undo error:', error); throw error; } } serialize() { console.log('Serializing:', this.constructor.name); return { type: this.constructor.name, data: {} }; } } ``` -------------------------------- ### Execute Command Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Shows how to execute a command, including a custom command class and options for silent execution or non-undoable commands. ```javascript class AddCommand { constructor(value) { this.value = value; this.previousValue = 0; } execute() { this.previousValue = appState.total; appState.total += this.value; } undo() { appState.total = this.previousValue; } serialize() { return { type: 'AddCommand', data: { value: this.value } }; } } undoManager.execute(new AddCommand(10)); undoManager.execute(new AddCommand(5), { silent: true }); undoManager.execute('registeredCommand'); ``` -------------------------------- ### compressThreshold Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Examples illustrating the use of compressThreshold to control automatic history compression, including disabling it. ```javascript // Text editor with many character-insert commands: aggressive compression const undoManager = new Oops({ compressThreshold: 50 }); // Data management app with few mergeable commands: minimal compression const undoManager = new Oops({ compressThreshold: 300 }); // Disable compression (for debugging or memory-rich environments) const undoManager = new Oops({ compressThreshold: Infinity }); ``` -------------------------------- ### commitTransaction() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Shows how to begin a transaction, add commands, and then commit them to be executed as a single unit. ```javascript undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.commitTransaction(); ``` -------------------------------- ### Redo Command Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Demonstrates how to redo the last undone command or a specified number of commands. ```javascript undoManager.redo(); // Redo last undone command undoManager.redo(2); // Redo last 2 undone commands ``` -------------------------------- ### TypeScript Definitions Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Example of JSDoc comments for TypeScript projects lacking .d.ts files. ```javascript /** * @typedef {Object} Command * @property {Function} execute * @property {Function} undo * @property {Function} serialize * @property {Function} [canMerge] * @property {Function} [merge] */ /** * @param {Command} command * @param {Object} [options] * @param {boolean} [options.silent] * @param {boolean} [options.undoable] */ function execute(command, options) { // ... } ``` -------------------------------- ### SerializedCommand Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example of serializing and restoring a command. ```javascript const command = new DeleteItemCommand(itemId); const serialized = command.serialize(); // Result: { type: 'DeleteItemCommand', data: { itemId: '123' } } // Store in JSON const json = JSON.stringify(serialized); // Restore from JSON const restored = undoManager.deserializeCommand(JSON.parse(json)); ``` -------------------------------- ### Undo Command Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Illustrates how to undo the last executed command or a specified number of commands. ```javascript undoManager.undo(); // Undo last command undoManager.undo(3); // Undo last 3 commands ``` -------------------------------- ### clear() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Illustrates clearing all undo and redo history and checking the resulting state. ```javascript undoManager.clear(); console.log(undoManager.canUndo); // false console.log(undoManager.canRedo); // false ``` -------------------------------- ### Oops Constructor Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example of instantiating the Oops manager with custom constructor options. ```javascript const undoManager = new Oops({ maxStackSize: 100, snapshotInterval: 10, compressThreshold: 50, mergeWindow: 500 }); ``` -------------------------------- ### Register Commands by Name Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Example of registering commands by name for execution. ```javascript undoManager.registerCommand('setColor', () => new SetColorCommand()); undoManager.execute('setColor'); ``` -------------------------------- ### Execute a Command Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Example of executing a command using the undo manager. ```javascript undoManager.execute(new MyCommand()); ``` -------------------------------- ### SetValueCommand Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example implementation of a command that sets a value. ```javascript class SetValueCommand { constructor(value) { this.newValue = value; this.previousValue = null; } execute() { this.previousValue = appState.value; appState.value = this.newValue; } undo() { appState.value = this.previousValue; } serialize() { return { type: 'SetValueCommand', data: { newValue: this.newValue, previousValue: this.previousValue } }; } canMerge(other) { return other instanceof SetValueCommand; } merge(other) { const merged = new SetValueCommand(this.newValue); merged.previousValue = other.previousValue; return merged; } } ``` -------------------------------- ### execute() Method Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Shows how to execute all sub-commands within a CompositeCommand in sequence. ```javascript const composite = new CompositeCommand([command1, command2, command3]); composite.execute(); // All three commands execute in sequence ``` -------------------------------- ### Example Usage of Oops.js Source: https://github.com/heyputer/oops.js/blob/master/README.md Demonstrates how to create an instance of the Oops undo manager, define a custom command (AddNumberCommand), and use the execute, undo, and redo methods. ```javascript // Create an instance of Oops const undoManager = new Oops(); // Define a simple command class AddNumberCommand { constructor(number) { this.number = number; this.previousTotal = 0; } execute() { this.previousTotal = total; total += this.number; } undo() { total = this.previousTotal; } } // Use the undo manager let total = 0; undoManager.execute(new AddNumberCommand(5)); console.log(total); // Output: 5 undoManager.execute(new AddNumberCommand(3)); console.log(total); // Output: 8 undoManager.undo(); console.log(total); // Output: 5 undoManager.redo(); console.log(total); // Output: 8 ``` -------------------------------- ### Manual CompositeCommand Definition Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Example of how to manually define the CompositeCommand class if needed. ```javascript // Manual definition if needed class CompositeCommand { // ... see src/Oops.js for full implementation } ``` -------------------------------- ### Undo and Redo Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Examples of undoing and redoing commands, including multiple steps. ```javascript undoManager.undo(); // Undo last command undoManager.undo(3); // Undo last 3 commands undoManager.redo(); // Redo last undone command ``` -------------------------------- ### Unknown Command Error Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Demonstrates how an unknown command error is triggered and how to prevent it by registering the command first. ```javascript const undoManager = new Oops(); // Error: Unknown command: 'deleteUser' undoManager.execute('deleteUser'); // "Unknown command: deleteUser" thrown // Fix: Register the command first undoManager.registerCommand('deleteUser', () => new DeleteUserCommand()); undoManager.execute('deleteUser'); // Now works ``` -------------------------------- ### Direct Source Import Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Example of importing the library directly from source, with a note about production usage. ```javascript // Directly from source const Oops = require('./src/Oops.js'); // Note: This bypasses the npm package path resolution // Not recommended for production; use @heyputer/oops.js instead ``` -------------------------------- ### CompositeCommand Constructor Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Demonstrates how to create a new CompositeCommand instance with an array of sub-commands. ```javascript const cmd1 = new SetNameCommand('John'); const cmd2 = new SetAgeCommand(30); const composite = new CompositeCommand([cmd1, cmd2]); ``` -------------------------------- ### addChangeListener() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Shows how to register a listener function that updates the UI based on the undo/redo state changes. ```javascript const updateUI = (state) => { document.getElementById('undo-btn').disabled = !state.canUndo; document.getElementById('redo-btn').disabled = !state.canRedo; }; undoManager.addChangeListener(updateUI); ``` -------------------------------- ### ExportedState Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example of exporting and importing the undo/redo manager state. ```javascript // Export state for persistence const state = undoManager.exportState(); // Save to localStorage localStorage.setItem('undoState', JSON.stringify(state)); // Later, restore const saved = JSON.parse(localStorage.getItem('undoState')); undoManager.importState(saved); // Or use convenience methods const json = undoManager.serializeState(); localStorage.setItem('undoState', json); const json = localStorage.getItem('undoState'); undoManager.deserializeState(json); ``` -------------------------------- ### Build Oops.js from Source Source: https://github.com/heyputer/oops.js/blob/master/README.md Provides the commands to clone the Oops.js repository, install dependencies, and build the project from its source code. ```bash git clone https://github.com/heyputer/oops.js.git cd oops.js npm install npm run build ``` -------------------------------- ### State Object Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example of listening to state changes and updating UI elements. ```javascript undoManager.addChangeListener((state) => { if (state.canUndo) { undoButton.classList.remove('disabled'); undoButton.textContent = `Undo (${state.undoStackSize})`; } else { undoButton.classList.add('disabled'); undoButton.textContent = 'Undo'; } redoButton.disabled = !state.canRedo; redoButton.textContent = state.canRedo ? `Redo (${state.redoStackSize})` : 'Redo'; }); ``` -------------------------------- ### Serialization and Persistence Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Demonstrates how CompositeCommands are serialized for storage and automatically restored. ```javascript const composite = new CompositeCommand([cmd1, cmd2]); const state = undoManager.exportState(); const jsonState = JSON.stringify(state); localStorage.setItem('undo-state', jsonState); // Later, import restores composite structure automatically undoManager.deserializeState(localStorage.getItem('undo-state')); ``` -------------------------------- ### removeChangeListener() Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Demonstrates how to remove a previously registered change listener. ```javascript undoManager.removeChangeListener(updateUI); ``` -------------------------------- ### Composite Command Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Examples of how composite commands are created automatically by commitTransaction or manually. ```javascript // Created automatically by commitTransaction with multiple commands undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.commitTransaction(); // Internally creates: // { // type: 'CompositeCommand', // data: [command1.serialize(), command2.serialize()] // } // Or create manually const composite = new CompositeCommand([command1, command2]); const serialized = composite.serialize(); ``` -------------------------------- ### serialize() Method Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Demonstrates serializing a CompositeCommand and its sub-commands into a JSON-compatible object. ```javascript const composite = new CompositeCommand([cmd1, cmd2]); const serialized = composite.serialize(); // Result: { // type: 'CompositeCommand', // data: [ // cmd1.serialize(), // cmd2.serialize() // ] // } ``` -------------------------------- ### No Named Exports Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Illustrates correct and incorrect ways to import the Oops class, highlighting the use of default export. ```javascript // ✓ Correct import Oops from '@heyputer/oops.js'; const manager = new Oops(); // ✗ Will not work (no named export) import { Oops } from '@heyputer/oops.js'; // ✗ Will not work (no named export) import { CompositeCommand } from '@heyputer/oops.js'; ``` -------------------------------- ### Invalid State Error Examples Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Demonstrates scenarios that trigger invalid state errors during state import or deserialization, and provides fixes. ```javascript // Error: Invalid state object undoManager.importState(null); undoManager.importState('not an object'); undoManager.importState(42); // Error: Failed to deserialize state: Unexpected token undoManager.deserializeState('invalid json {{{'); // Error: Unknown command type undoManager.deserializeState(JSON.stringify({ undoStack: [{ type: 'UnregisteredCommand', data: {} }], redoStack: [] })); // Fix: Ensure state is valid object const validState = undoManager.exportState(); undoManager.importState(validState); // Success // Fix: Parse JSON carefully try { const state = JSON.parse(jsonString); undoManager.importState(state); } catch (error) { console.error('Failed to restore state:', error); } ``` -------------------------------- ### Change Listener Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Example of adding and removing a state change listener to the undo manager. ```javascript const listener = (state) => { console.log(`Can undo: ${state.canUndo}`); console.log(`Can redo: ${state.canRedo}`); console.log(`Undo stack size: ${state.undoStackSize}`); }; undoManager.addChangeListener(listener); // Later remove undoManager.removeChangeListener(listener); ``` -------------------------------- ### Group Commands as Single Operation Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Example of grouping multiple commands into a single transaction. ```javascript undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.execute(command3); undoManager.commitTransaction(); // All three undone/redone together ``` -------------------------------- ### undo() Method Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Illustrates how to undo all sub-commands within a CompositeCommand in reverse order. ```javascript const composite = new CompositeCommand([command1, command2, command3]); composite.execute(); // ... later ... composite.undo(); // Undoes in reverse: command3, command2, command1 ``` -------------------------------- ### Command Execution Error Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Illustrates a command that throws an error during execution and how it's caught and handled by the undo manager. ```javascript class DangerousCommand { execute() { throw new Error('Something went wrong'); } undo() {} serialize() { return { type: 'DangerousCommand', data: {} }; } } try { undoManager.execute(new DangerousCommand()); } catch (error) { console.log(error.message); // "Something went wrong" // Stack is unchanged, undo still works } ``` -------------------------------- ### canMerge Method Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Demonstrates that CompositeCommand.canMerge always returns false. ```javascript const composite = new CompositeCommand([cmd1, cmd2]); const simpleCmd = new SimpleCommand(); console.log(composite.canMerge(simpleCmd)); // false console.log(composite.canMerge(composite)); // false ``` -------------------------------- ### Command Factory Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Examples of registering command factories for creating new commands or reconstructing from serialized data. ```javascript // Factory for creating new commands undoManager.registerCommand('setText', () => new SetTextCommand()); // Factory for reconstructing from serialized data undoManager.registerCommand('setColor', (data) => { const cmd = new SetColorCommand(data.color); cmd.previousColor = data.previousColor; return cmd; }); ``` -------------------------------- ### Redo Operation Error Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Demonstrates a scenario where a redo operation fails due to changed application state, triggering automatic snapshot recovery. ```javascript class ConditionalCommand { execute() { if (!appState.initialized) { throw new Error('Cannot execute: app not initialized'); } appState.count++; } undo() { appState.count--; } serialize() { return { type: 'ConditionalCommand', data: {} }; } } undoManager.execute(new ConditionalCommand()); undoManager.undo(); // Success appState.initialized = false; // State changed try { undoManager.redo(); // Throws; recovery is automatic } catch (error) { console.log('Recovery from snapshot complete'); } ``` -------------------------------- ### CDN Distribution Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Examples of how to include the oops.js library via CDN, including latest version, specific version, and source map. ```html ``` -------------------------------- ### Listen to State Changes Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Example of adding a change listener to the undo manager to update the UI based on undo/redo availability. ```javascript undoManager.addChangeListener((state) => { updateUI(state.canUndo, state.canRedo); }); ``` -------------------------------- ### static deserialize() Method Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Shows how to reconstruct a CompositeCommand from its serialized form using a provided deserialization function. ```javascript const deserializer = (cmd) => undoManager.deserializeCommand(cmd); const serialized = { type: 'CompositeCommand', data: [ { type: 'SetNameCommand', data: { name: 'John' } }, { type: 'SetAgeCommand', data: { age: 30 } } ] }; const composite = CompositeCommand.deserialize(serialized.data, deserializer); ``` -------------------------------- ### Execute Command Options Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Examples of executing commands with different options like silent notifications or disabling undo capability. ```javascript // Execute with notification undoManager.execute(command); // Execute silently (no listener notifications) undoManager.execute(command, { silent: true }); // Execute without undo capability undoManager.execute(command, { undoable: false }); // Execute with multiple options undoManager.execute(command, { silent: true, undoable: false }); ``` -------------------------------- ### Undo Operation Error Example Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Shows a command that throws an error during its undo operation, triggering automatic recovery from the last valid snapshot. ```javascript class FragileCommand { constructor() { this.previousValue = null; } execute() { this.previousValue = appState.value; appState.value = 'new value'; } undo() { // This throws if appState is not in expected format throw new Error('Cannot undo: appState corrupted'); } serialize() { return { type: 'FragileCommand', data: {} }; } } try { undoManager.execute(new FragileCommand()); undoManager.undo(); // Throws; recovery is automatic } catch (error) { // recoverFromSnapshot() has already been called console.log('Recovery complete'); } ``` -------------------------------- ### Configuration Options (Parameters) Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Illustrates the optional parameters for the Oops constructor. ```javascript new Oops({ maxStackSize?: number, // default: Infinity snapshotInterval?: number, // default: 10 compressThreshold?: number, // default: 100 mergeWindow?: number // default: 1000 }) ``` -------------------------------- ### Build and Distribution - Source Build Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Command to build the project and generate the minified bundle. ```bash npm run build ``` -------------------------------- ### Build and Distribution - Testing Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Command to run the Jest test suite. ```bash npm test ``` -------------------------------- ### Package.json Entry Points Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md The package.json configuration showing the main entry point and other metadata. ```json { "name": "@heyputer/oops.js", "version": "1.0.2", "main": "src/index.js", "keywords": ["Undo", "Redo", "Oops", "Command"], "license": "MIT" } ``` -------------------------------- ### Save and Restore State Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Demonstrates how to save and restore the application state using localStorage and the undoManager. ```javascript // Save const json = undoManager.serializeState(); localStorage.setItem('appState', json); // Restore const json = localStorage.getItem('appState'); undoManager.deserializeState(json); ``` -------------------------------- ### Basic Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Demonstrates the basic usage of Oops.js, including creating an instance, defining a custom command, and performing undo/redo operations. ```javascript import Oops from '@heyputer/oops.js'; // Create an instance const undoManager = new Oops(); // Define a command class AddNumberCommand { constructor(number) { this.number = number; this.previousTotal = 0; } execute() { this.previousTotal = total; total += this.number; } undo() { total = this.previousTotal; } serialize() { return { type: 'AddNumberCommand', data: { number: this.number, previousTotal: this.previousTotal } }; } } // Use the manager let total = 0; undoManager.execute(new AddNumberCommand(5)); console.log(total); // 5 undoManager.execute(new AddNumberCommand(3)); console.log(total); // 8 undoManager.undo(); console.log(total); // 5 undoManager.redo(); console.log(total); // 8 ``` -------------------------------- ### File Structure Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Illustrates the directory and file layout of the oops.js package. ```bash @heyputer/oops.js/ ├── src/ │ ├── index.js (Entry point, exports Oops) │ └── Oops.js (Main source, defines Oops and CompositeCommand) ├── dist/ │ ├── oops.min.js (Minified browser build) │ └── oops.min.js.map (Source map) ├── package.json (Version 1.0.2) └── README.md (Usage guide and features) ``` -------------------------------- ### Oops Constructor Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Shows the signature for the Oops class constructor. ```javascript new Oops(options?) ``` -------------------------------- ### Scenario 1: Simple Form Editor Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Configuration for a simple form editor with limited undo needs, featuring a default mergeWindow. ```javascript const undoManager = new Oops({ maxStackSize: 20, // Limited history snapshotInterval: 20, // Less frequent snapshots compressThreshold: 50, // Minimal compression mergeWindow: 1000 // Default merge behavior }); ``` -------------------------------- ### createSnapshot() Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Creates a serialized snapshot of current undo/redo stacks for error recovery. ```javascript createSnapshot() ``` -------------------------------- ### Scenario 4: Memory-Constrained Environment Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Configuration for memory-constrained environments, using a mergeWindow of 300ms to help reduce stack size. ```javascript const undoManager = new Oops({ maxStackSize: 10, // Very limited history snapshotInterval: 50, // Infrequent snapshots compressThreshold: 20, // Aggressive compression mergeWindow: 300 // Merge to reduce stack size }); ``` -------------------------------- ### Constructor Signature Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md The Oops undo/redo manager constructor accepts an optional options object for configuration. ```javascript new Oops(options) ``` -------------------------------- ### Scenario 5: Mission-Critical Application Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Configuration for mission-critical applications, with a conservative mergeWindow of 5000ms. ```javascript const undoManager = new Oops({ maxStackSize: 150, // Ample history snapshotInterval: 3, // Very frequent snapshots compressThreshold: 200, // Compression only at large sizes mergeWindow: 5000 // Conservative merging }); ``` -------------------------------- ### Command Factory Error Handling Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Illustrates how to create robust command factories that handle potential errors during command instantiation and registration. ```javascript class SafeCommand { constructor(data) { if (!data || typeof data !== 'object') { throw new Error('Invalid command data'); } this.data = data; } execute() { // Validate state before operation if (!isValidState()) { throw new Error('Cannot execute: invalid state'); } this.previousState = captureState(); applyOperation(this.data); } undo() { if (!this.previousState) { throw new Error('No previous state to restore'); } restoreState(this.previousState); } serialize() { return { type: 'SafeCommand', data: this.data }; } } undoManager.registerCommand('safeCmd', (data) => { try { return new SafeCommand(data); } catch (error) { console.error('Failed to create command:', error); throw new Error(`Unknown command: safeCmd`); } }); ``` -------------------------------- ### Configuration Quick Reference Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/README.md Provides a quick reference for common Oops.js configuration options. ```javascript new Oops({ maxStackSize: 100, // Max commands to store snapshotInterval: 10, // Snapshot every N commands compressThreshold: 100, // Compress at N+ commands mergeWindow: 1000 // Merge within N milliseconds }) ``` -------------------------------- ### deserializeCommand(serializedCmd) Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Deserializes a single command using registered command factories. ```javascript deserializeCommand(serializedCmd) ``` -------------------------------- ### CompositeCommand Constructor Source: https://github.com/heyputer/oops.js/blob/master/README.md Creates a new instance of the CompositeCommand class, which groups multiple commands. ```javascript new CompositeCommand(commands) ``` -------------------------------- ### Default Export: Oops Class Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Demonstrates how to import and instantiate the Oops class using CommonJS, ES6 Import, and Browser/CDN methods. ```javascript // CommonJS const Oops = require('@heyputer/oops.js'); const manager = new Oops(); // ES6 Import import Oops from '@heyputer/oops.js'; const manager = new Oops(); // Browser/CDN const manager = new Oops(); // Available as window.Oops when loaded via CDN ``` -------------------------------- ### Scenario 2: Rich Text Editor Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Configuration for a rich text editor requiring aggressive merging for frequent character input. ```javascript const undoManager = new Oops({ maxStackSize: 100, // Generous history snapshotInterval: 10, // Regular snapshots for recovery compressThreshold: 50, // Aggressive compression of text commands mergeWindow: 500 // Merge rapid character input }); ``` -------------------------------- ### Command Interface Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/exports.md Defines the interface that all commands must implement. ```javascript { execute() : any, undo() : void, serialize() : { type: string, data: any } canMerge?(other) : boolean, merge?(other) : Command } ``` -------------------------------- ### Basic Error Handling Pattern Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Illustrates fundamental try-catch blocks for handling errors during command execution and undo operations. ```javascript const undoManager = new Oops(); try { undoManager.execute(command); } catch (error) { console.error('Execution failed:', error.message); // Handle gracefully - state before execute() is maintained } try { undoManager.undo(); } catch (error) { console.error('Undo failed:', error.message); // State is recovered from snapshot automatically } ``` -------------------------------- ### mergeWindow Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Defines the time window in milliseconds for automatic command merging. Commands within this window can be merged if other conditions are met. ```javascript mergeWindow: number = 1000 ``` -------------------------------- ### deserializeState(jsonState) Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Deserializes a JSON string and imports the state. ```javascript deserializeState(jsonState) ``` -------------------------------- ### recoverFromSnapshot() Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Recovers the undo/redo state from the most recent valid snapshot. ```javascript recoverFromSnapshot() ``` -------------------------------- ### Command Interface Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Defines the interface for commands passed to the execute() method. ```javascript { execute() : any, // Performs the command action, returns optional result undo() : void, // Reverses the command action serialize() : Object, // Returns serializable representation canMerge? : (other) => boolean, // Optional: checks if can merge with another command merge? : (other) => Command // Optional: returns merged command } ``` -------------------------------- ### compressHistory() Method Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Merges compatible adjacent commands to reduce memory usage. Automatically called when undoStack.length exceeds compressThreshold. ```javascript compressHistory() ``` ```javascript undoManager.compressHistory(); console.log(undoManager.exportState().undoStack.length); // Potentially smaller ``` -------------------------------- ### Snapshot Usage Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Note on internal snapshot usage and the recommendation to use exportState for persistence. ```javascript // Internal snapshot (don't use directly) undoManager.createSnapshot(); // For persistence, use exportState const state = undoManager.exportState(); localStorage.setItem('appState', JSON.stringify(state)); ``` -------------------------------- ### Scenario 3: Graphic Design Application Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md Configuration for a graphic design application with manual operations and a larger mergeWindow to merge operations seconds apart. ```javascript const undoManager = new Oops({ maxStackSize: 200, // Large history for many operations snapshotInterval: 5, // Frequent snapshots (critical app) compressThreshold: 500, // Compression only at large stacks mergeWindow: 2000 // Merge operations seconds apart }); ``` -------------------------------- ### serializeState() Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Serializes the current state to a JSON string. ```javascript serializeState() ``` -------------------------------- ### importState(state) Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Imports a previously exported state, restoring the undo/redo manager. ```javascript importState(state) ``` -------------------------------- ### Default Configuration Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/configuration.md The default configuration options used by Oops.js when no options are provided. ```javascript const undoManager = new Oops(); // Equivalent to: const undoManager = new Oops({ maxStackSize: Infinity, snapshotInterval: 10, compressThreshold: 100, mergeWindow: 1000 }); ``` -------------------------------- ### Error Handling with Change Listeners Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Shows how change listeners are invoked even after automatic state recovery following an undo error. ```javascript undoManager.addChangeListener((state) => { // This is called even after error recovery console.log('State after recovery:', state); updateUI(state); }); try { undoManager.undo(); } catch (error) { // Listener already called with recovered state showErrorNotification(error.message); } ``` -------------------------------- ### Registering Sub-command Types for Serialization Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Shows how to register sub-command types with the Oops manager for proper serialization and deserialization of composites. ```javascript undoManager.registerCommand('MoveCommand', (data) => new MoveCommand(data)); undoManager.registerCommand('ResizeCommand', (data) => new ResizeCommand(data)); // CompositeCommand is handled automatically const composite = new CompositeCommand([ new MoveCommand({ x: 10 }), new ResizeCommand({ width: 50 }) ]); undoManager.execute(composite); ``` -------------------------------- ### Serialization Error Handling Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Provides patterns for safely saving and restoring application state using JSON serialization and localStorage. ```javascript function saveState() { try { const state = undoManager.exportState(); const json = JSON.stringify(state); localStorage.setItem('appState', json); return true; } catch (error) { console.error('Failed to save state:', error); return false; } } function restoreState() { try { const json = localStorage.getItem('appState'); if (json) { undoManager.deserializeState(json); } } catch (error) { console.error('Failed to restore state:', error); // Optionally clear corrupted state localStorage.removeItem('appState'); undoManager.clear(); } } ``` -------------------------------- ### Transaction Error Handling Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/errors.md Demonstrates how to manage errors within a transaction, including rollback using abortTransaction. ```javascript undoManager.beginTransaction(); try { undoManager.execute(command1); undoManager.execute(command2); // Throws undoManager.execute(command3); // Not executed undoManager.commitTransaction(); } catch (error) { // Transaction still on stack undoManager.abortTransaction(); // Undoes command1 and command2 console.error('Transaction failed and rolled back'); } ``` -------------------------------- ### Use Oops.js via CDN Source: https://github.com/heyputer/oops.js/blob/master/README.md Includes the Oops.js library directly in an HTML file using a CDN link, making the Oops class globally available. ```html ``` -------------------------------- ### Command Interface Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md The base interface that all commands must implement. ```javascript interface Command { execute() : any undo() : void serialize() : SerializedCommand canMerge?(other: Command) : boolean merge?(other: Command) : Command } ``` -------------------------------- ### Manual Creation for Grouped Operations Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Illustrates manual creation of CompositeCommand to group related operations. ```javascript class LayoutCommand { constructor(data) { this.data = data; } execute() { /* apply layout */ } undo() { /* revert layout */ } serialize() { return { type: 'LayoutCommand', data: this.data }; } } const moveCmd = new LayoutCommand({ x: 10, y: 20 }); const resizeCmd = new LayoutCommand({ width: 100, height: 50 }); const layout = new CompositeCommand([moveCmd, resizeCmd]); undoManager.execute(layout); ``` -------------------------------- ### exportState() Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/Oops.md Exports the current undo/redo state as a serializable object. ```javascript exportState() ``` -------------------------------- ### Error Behavior During execute() with Transaction API Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Illustrates how to use the Oops transaction API with abortTransaction() for atomic transactions with rollback when an error occurs during execute(). ```javascript undoManager.beginTransaction(); try { undoManager.execute(cmd1); undoManager.execute(cmd2); // Throws undoManager.execute(cmd3); } catch (error) { undoManager.abortTransaction(); // Undoes cmd1 and cmd2 } ``` -------------------------------- ### Automatic Creation via Transactions Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Shows how CompositeCommand is automatically created when committing transactions. ```javascript const undoManager = new Oops(); undoManager.beginTransaction(); undoManager.execute(command1); undoManager.execute(command2); undoManager.execute(command3); undoManager.commitTransaction(); // Internally creates CompositeCommand([command1, command2, command3]) ``` -------------------------------- ### CompositeCommand deserialize static method Source: https://github.com/heyputer/oops.js/blob/master/README.md Static method to deserialize a CompositeCommand from serialized data. ```javascript static deserialize(data, deserializeCommand) ``` -------------------------------- ### SerializedCommand Interface Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md Represents a command in serialized form, suitable for JSON storage. ```javascript interface SerializedCommand { type: string data: any } ``` -------------------------------- ### Command Interface Requirements Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/api-reference/CompositeCommand.md Defines the required methods for sub-commands to be used with CompositeCommand. ```javascript { execute() : void, // Performs the action undo() : void, // Reverses the action serialize() : Object // Returns { type: string, data: any } } ``` -------------------------------- ### Constructor Options Interface Source: https://github.com/heyputer/oops.js/blob/master/_autodocs/types.md TypeScript interface defining the options for the Oops constructor. ```typescript interface ConstructorOptions { maxStackSize?: number snapshotInterval?: number compressThreshold?: number mergeWindow?: number } ```