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 changes
  • applyEditorTransaction – Applies a transaction to a document, returning a new immutable state
  • createEditorHistory – 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 selection
  • Selection type – TypeScript interface for cursor and range selections
  • useEditorHistory hook – React hook integrating createEditorHistory with 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 menus
  • EditorInsertItem – 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: createEditorTransaction and applyEditorTransaction power all state changes across layers
  • Immutable document model: All edits produce new document states, enabling undo/redo via createEditorHistory
  • React integration: EMPTY_SELECTION, Selection type, and useEditorHistory hook bridge core and component layers
  • Production-ready components: EditableSlideCanvasWithUI provides 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →