### 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
}
```