# How Slide Rendering and Editing Works in OpenMAIC: Architecture and Implementation

> Explore OpenMAIC's dual-layer architecture for slide rendering and editing. Learn how SlideCanvas and Canvas manage static previews and interactive states for efficient manipulation.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: architecture
- Published: 2026-09-01

---

**OpenMAIC separates read-only slide rendering from interactive editing through a dual-layer architecture where `SlideCanvas` handles static previews and `Canvas` manages mutable element states via React hooks and Zustand stores.**

The THU-MAIC/OpenMAIC codebase implements a sophisticated slide rendering and editing system that cleanly divides presentation logic from interaction logic. This architecture allows the same rendering primitives to power both thumbnail previews and full-featured editing canvases while maintaining high performance and clean state management.

## The Dual-Layer Architecture

OpenMAIC organizes slide functionality into two distinct layers: a read-only renderer for static display and an interactive editor for manipulation.

### Read-Only Rendering with SlideCanvas

The **read-only rendering layer** displays slides without editing UI, powering thumbnails, playback scenes, and static previews. The core entry point is `SlideCanvas` from `@openmaic/renderer`, wrapped by `SlideRendererProvider` located at [`packages/@openmaic/renderer/src/context.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/renderer/src/context.tsx).

The provider supplies the `Slide` object, optional custom renderers for images and video, and scale/background props. `SlideCanvas` (source: [`packages/@openmaic/renderer/src/SlideCanvas.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/renderer/src/SlideCanvas.tsx)) receives this data, auto-fits the slide to its container via `viewportStyles`, and renders each element through the internal `SlideElement` hierarchy.

Custom renderers (`renderImage`, `renderVideo`) allow callers to inject thumbnail-specific UI such as play badges or retry buttons instead of default `<img>` or `<video>` tags.

```tsx
// components/slide-renderer/SlideThumbnail.tsx
<SlideCanvas
  slide={resolved.slide}
  chrome={false}
  renderImage={(el, src, defaultContent) => /* custom image UI */}
  renderVideo={(el) => /* custom video UI */}
  videoInteractive={false}
/>

```

### Interactive Editing with SlideEditor

The **interactive editing layer** enables users to create, move, resize, rotate, and style slide elements. The entry point is `SlideEditor` in [[`components/slide-renderer/Editor/index.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/index.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/index.tsx), which renders either the editable `Canvas` or `PlaybackScreenCanvas` based on the current `StageMode`.

```tsx
// components/slide-renderer/Editor/index.tsx
export function SlideEditor({ mode }: { readonly mode: StageMode }) {
  return (
    <div className="flex flex-col h-full">
      <div className="flex-1 overflow-hidden">
        {mode === 'autonomous' ? <Canvas /> : <PlaybackScreenCanvas />}
      </div>
    </div>
  );
}

```

`Canvas` (source: [[`components/slide-renderer/Editor/Canvas/index.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/index.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/index.tsx)) serves as the heart of the editor, coordinating data flow, selection states, and gesture handling.

## Data Flow and State Management

The slide rendering and editing system relies on a multi-tiered state architecture that separates persistent slide data from transient UI states.

### SceneContext and Mutable Slide State

`Canvas` pulls the current slide from the global **SceneContext** (`useSceneSelector`) as implemented in [[`lib/contexts/scene-context.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/contexts/scene-context.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/contexts/scene-context.tsx). It mirrors this data in a local mutable `elementList` to allow drag, rotate, and scale operations to mutate positions without immediately triggering global store updates.

When a user completes a gesture, the modified `elementList` synchronizes back to the SceneContext. This pattern prevents excessive re-renders during continuous interactions like dragging.

### Specialized Stores for UI and Input

Two Zustand stores manage editor-specific state:

- **Canvas Store** ([[`lib/store/canvas.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/canvas.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/canvas.ts)) tracks UI-only state including `activeElementIdList` (selection), ruler visibility, grid size, and context menu position.

- **Keyboard Store** ([[`lib/store/keyboard.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/keyboard.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/keyboard.ts)) captures key presses such as space-bar for panning and modifier keys for shortcuts.

## Element Interaction Implementation

OpenMAIC implements slide editing through a collection of specialized React hooks that handle specific gesture types, keeping the `Canvas` component clean and maintainable.

### Gesture Hooks for Drag, Rotate, and Scale

The editor encapsulates complex interaction logic in custom hooks located under `components/slide-renderer/Editor/Canvas/hooks/`:

- **`useDragElement`** watches mouse movements and updates element positions, emitting alignment lines for snapping.
- **`useRotateElement`** rotates elements around their center points using the current `canvasScale` for mathematically correct transforms.
- **`useScaleElement`** handles corner-drag resizing, including support for multi-selection scaling via `scaleMultiElement`.

These hooks receive `elementListRef`, `viewportRef`, and `canvasScale` as parameters, operating on the mutable ref to ensure 60FPS performance during gestures.

### Selection and Context Menus

Clicking an element triggers `selectElement` (implemented in `useSelectElement`), which updates `activeElementIdList` in the canvas store. The `ContextMenu` component (built via the `contextmenus` function inside `Canvas`) renders right-click menus offering copy, paste, select-all, ruler toggle, and reset page actions.

## Rendering Elements in the Editor

Within the editing canvas, elements pass through a wrapper system that adds interactive handles while delegating actual rendering to type-specific components.

### The EditableElement Wrapper

Inside `Canvas`, the component maps over `elementList` to render each slide element through `EditableElement` (source: [[`components/slide-renderer/Editor/Canvas/EditableElement.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/EditableElement.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/EditableElement.tsx)):

```tsx
// components/slide-renderer/Editor/Canvas/index.tsx (excerpt)
{elementList.map((element: PPTElement, index: number) =>
  !hiddenElementIdList.includes(element.id) ? (
    <EditableElement
      key={element.id}
      elementInfo={element}
      elementIndex={index + 1}
      isMultiSelect={activeElementIdList.length > 1}
      selectElement={selectElement}
      openLinkDialog={openLinkDialog}
    />
  ) : null,
)}

```

`EditableElement` determines the concrete renderer based on `element.type` (text, image, video, shape) and attaches resize handles, rotation controls, and the `Operate` toolbar for actions like delete and duplicate.

### Concrete Element Renderers

Each element type has a dedicated component under `components/slide-renderer/components/element/`. For example:

- **Text**: [[`BaseTextElement.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/BaseTextElement.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/components/element/TextElement/BaseTextElement.tsx) handles text rendering and inline editing activation.
- **Images**: [[`BaseImageElement.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/BaseImageElement.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/components/element/ImageElement/BaseImageElement.tsx) manages image display and cropping overlays.

These components receive props for attaching editor handles while implementing their own display logic and event handlers.

## Summary

- **OpenMAIC uses a dual-layer architecture** separating read-only `SlideCanvas` from interactive `Canvas` components.
- **State management is tiered** with SceneContext for slide data, canvas store for UI state, and keyboard store for input handling.
- **Interaction logic is encapsulated** in specialized hooks (`useDragElement`, `useRotateElement`, `useScaleElement`) that operate on mutable refs for performance.
- **Element rendering is modular** with `EditableElement` wrapping type-specific renderers and attaching editing UI.
- **File locations** include `packages/@openmaic/renderer/src/SlideCanvas.tsx` for rendering and [`components/slide-renderer/Editor/Canvas/index.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/index.tsx) for editing logic.

## Frequently Asked Questions

### How does OpenMAIC handle element selection in the slide editor?

OpenMAIC tracks element selection through the canvas store ([`lib/store/canvas.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/canvas.ts)) which maintains the `activeElementIdList` array. When a user clicks an element, the `selectElement` function from `useSelectElement` updates this store, causing `EditableElement` to render selection borders and the `Operate` toolbar. Multi-selection is supported by checking `activeElementIdList.length > 1` and rendering `MultiSelectOperate` handles.

### What is the difference between SlideCanvas and Canvas in OpenMAIC?

`SlideCanvas` ([`packages/@openmaic/renderer/src/SlideCanvas.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/renderer/src/SlideCanvas.tsx)) is a read-only renderer used for thumbnails and playback that receives a `Slide` object and renders it to fit a container. `Canvas` ([[`components/slide-renderer/Editor/Canvas/index.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/index.tsx)](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/slide-renderer/Editor/Canvas/index.tsx)) is the interactive editing surface that manages mutable element states, selection, and gestures through React hooks and Zustand stores.

### How does OpenMAIC support custom media rendering in slides?

The `SlideRendererProvider` accepts optional `renderImage` and `renderVideo` props that override default media tags. This allows components like `SlideThumbnail` to inject custom UI such as play badges for videos or loading states for images while reusing the core `SlideCanvas` rendering logic. The custom renderers receive the element data, source URL, and default content as parameters.

### Which hooks handle element transformations in the OpenMAIC editor?

OpenMAIC uses three primary hooks for transformations located in `components/slide-renderer/Editor/Canvas/hooks/`: `useDragElement` for position changes and alignment snapping, `useRotateElement` for calculating rotation around element centers, and `useScaleElement` for resizing including multi-selection scenarios. These hooks manipulate the `elementListRef` directly during gestures to maintain smooth 60FPS performance before committing changes to the global store.