Core Functionalities of @openmaic/editor Package: Three-Layer Architecture Explained
The @openmaic/editor package provides a modular editing engine split into core (document/transaction logic), React (framework bindings), and UI (ready-to-use components) layers that share a unified transaction system.
The @openmaic/editor package serves as the central editing engine of OpenMAIC, an open-source academic content creation platform developed by Tsinghua University. Understanding the @openmaic/editor core functionalities helps developers integrate rich text and slide editing capabilities at the appropriate abstraction level for their use case.
Three-Layer Architecture Overview
The @openmaic/editor package implements a deliberate layered onion pattern with three interoperable sub-packages. Each layer exposes a focused API while sharing a common data model and transaction system.
| Layer | Package Path | Purpose |
|---|---|---|
| Core | @openmaic/editor/core |
Document representation, transaction creation, undo/redo history, selection management |
| React | @openmaic/editor/react |
React-specific shortcuts, selection primitives, hooks for state management |
| UI | @openmaic/editor/ui |
High-level components including toolbars, insertion menus, and editable canvas |
According to the package README, this separation ensures that low-level editing operations remain framework-agnostic while higher layers provide convenient abstractions for React applications.
Core Layer: Transaction Engine and Document Model
The @openmaic/editor/core sub-package contains pure TypeScript data structures with no UI or framework dependencies. This enables usage in Node.js scripts, test environments, or alternative frontend frameworks.
Primary Core APIs
createEditorTransaction– Constructs a transaction object describing document changesapplyEditorTransaction– Applies a transaction to a document, returning a new immutable statecreateEditorHistory– Initializes undo/redo history management- Document tree queries – Methods for navigating and inspecting the hierarchical document structure
The transaction system powers history replay, enabling robust undo/redo functionality and laying groundwork for collaborative editing scenarios. All state changes in @openmaic/editor follow this immutable transaction pattern.
Framework-Agnostic Design
The core layer's independence from React means developers can:
- Run document transformations in server-side Node.js processes
- Integrate with Vue, Svelte, or other frameworks
- Write deterministic tests without DOM dependencies
This separation of concerns is verified in [tests/packages/editor-manifest.test.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/tests/packages/editor-manifest.test.ts), which confirms the three layers build and export correctly.
React Layer: Framework Bindings and Selection Primitives
The @openmaic/editor/react sub-package provides thin adapters that bridge the core transaction model with React's rendering lifecycle and state management patterns.
Key React Exports
EMPTY_SELECTION– Sentinel value representing no active selectionSelectiontype – TypeScript interface for cursor and range selectionsuseEditorHistoryhook – React hook integratingcreateEditorHistorywith component state- Transaction dispatch utilities for React state updates
These primitives allow React components to participate in the transaction system without directly managing the underlying document immutable updates.
UI Layer: Ready-to-Use Editing Components
The @openmaic/editor/ui sub-package delivers full React components that render complete editing surfaces. These components consume core APIs internally while exposing declarative props for common use cases.
Primary UI Components
EditableSlideCanvasWithUI– Full editing canvas with integrated toolbar and insertion menusEditorInsertItem– Configuration object for items that can be inserted into documents- UI-related hooks – Pre-built hooks for common interaction patterns
The UI layer implements declarative insertion defaults: toolbar and menu components automatically generate appropriate EditorInsertItem objects that the core transaction engine processes. This abstraction hides transaction complexity from typical UI development workflows.
Shared Transaction Format: The Unifying Abstraction
All three layers share a single EditorTransaction format. This design decision enables seamless movement between abstraction levels:
- A UI component's toolbar button generates the same transaction structure as a programmatic server-side script
- History state captured at the UI layer can be replayed through core APIs
- Custom components can inject transactions directly into the dispatch pipeline
This consistency is demonstrated in scripts/smoke-test-package-tarballs.mjs, which verifies typical imports work correctly across all layers.
Practical Usage Examples
Low-Level Core Transaction
// Programmatic document editing without React
import { createEditorTransaction, applyEditorTransaction } from '@openmaic/editor/core';
const tx = createEditorTransaction({
steps: [{ type: 'insertParagraph', position: 0 }]
});
const newDoc = applyEditorTransaction(currentDoc, tx);
React Integration with History Management
import { EMPTY_SELECTION } from '@openmaic/editor/react';
import { useEditorHistory } from '@openmaic/editor/react';
import { EditableSlideCanvasWithUI } from '@openmaic/editor/ui';
export default function SlideEditor({ initialDoc }) {
const { history, dispatch } = useEditorHistory(initialDoc);
return (
<EditableSlideCanvasWithUI
doc={history.currentDoc}
onTransaction={dispatch}
selection={EMPTY_SELECTION}
/>
);
}
Custom Insertion Control
import { EditorInsertItem } from '@openmaic/editor/ui';
function InsertMathBlock({ onInsert }) {
const mathItem: EditorInsertItem = {
type: 'math',
content: '\\sum_{i=0}^{n} x_i'
};
return (
<button onClick={() => onInsert(mathItem)}>
Insert Equation
</button>
);
}
Extensibility Features
The @openmaic/editor architecture supports custom element adapters through its extension points:
- Built-in adapters handle standard OpenMAIC elements: text blocks, mathematical expressions, images, and structured containers
- Custom adapter registration allows third-party developers to add new element types that integrate with the transaction system and UI components
- Adapter interface specifies how to serialize, render, and transform custom elements within the document model
This extensibility ensures the editing engine accommodates domain-specific content without core modifications.
Summary
- Three-layer architecture: Core (framework-agnostic), React (bindings), UI (components) – each importable separately via
@openmaic/editor/* - Unified transaction system:
createEditorTransactionandapplyEditorTransactionpower all state changes across layers - Immutable document model: All edits produce new document states, enabling undo/redo via
createEditorHistory - React integration:
EMPTY_SELECTION,Selectiontype, anduseEditorHistoryhook bridge core and component layers - Production-ready components:
EditableSlideCanvasWithUIprovides complete editing surfaces with minimal configuration - Extensible element system: Custom adapters integrate new content types into the transaction and rendering pipeline
Frequently Asked Questions
What is the difference between @openmaic/editor/core and @openmaic/editor/react?
The core sub-package contains pure TypeScript logic for documents and transactions with no framework dependencies, usable in Node.js or any frontend framework. The React sub-package provides React-specific bindings including hooks and selection primitives that connect core functionality to React's component lifecycle. Choose core for headless editing or alternative frameworks; choose React for standard React applications.
How does the transaction system enable undo/redo functionality?
Every state change in @openmaic/editor creates an EditorTransaction object describing the operation. The createEditorHistory function maintains a stack of these transactions alongside document snapshots. Undo pops and inverts the most recent transaction; redo reapplies previously undone transactions. This approach also supports future collaborative editing through operational transformation of transaction sequences.
Can I use @openmaic/editor without React?
Yes. The @openmaic/editor/core sub-package has zero React dependencies. You can create transactions with createEditorTransaction, apply them with applyEditorTransaction, and manage history through createEditorHistory in any JavaScript or TypeScript environment including Node.js servers, Vue applications, or vanilla JavaScript projects.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →