Miraiclip SDK
Fundamentals

Events & patches

Know exactly what changed, and sync it to other clients.

const off = project.events.on("patches", ({ patches, inverse, source }) => {
  console.log(source, patches);
  // "dispatch" [{ op: "replace", path: "/clips/intro/startUs", value: 1000000 }]
});

off(); // stop listening

Events

EventPayloadFires
patches{ patches, inverse, source }Every document change
history{ kind, label? }A commit, undo or redo; see History
playhead{ positionUs }setPlayhead, including every frame during playback
selection{ ids }setSelection

project.events.on returns an unsubscribe function. project.events.off(event, listener) works too.

For UI state, prefer project.subscribe(selector, listener): it fires only when the value you render changes. Use events when you need what changed.

Patches

patches are RFC 6902 JSON Patch operations (add, replace, remove) with paths relative to the document root. inverse undoes them.

sourceMeaning
dispatchA command ran
undo / redoHistory moved; one event per step, even for a transaction
transaction-rollbackA failed transaction reverted its earlier changes

A transaction emits one patches event per command as it runs, then a single history commit at the end.

Syncing other clients

Patches are what make collaboration possible. Send them to peers and apply them to their copy of the document:

import { applyJsonPatches, type JsonPatchOp, type ProjectDocument } from "@miraiclip/core";

// Sender: forward every event, rollbacks included, in order
project.events.on("patches", ({ patches }) => send(patches));

// Receiver: a read-only mirror of the document
let mirror: ProjectDocument = project.toJSON();
function onRemotePatches(patches: JsonPatchOp[]) {
  mirror = applyJsonPatches(mirror, patches); // returns a new document; the input isn't mutated
}

declare function send(patches: JsonPatchOp[]): void;

applyJsonPatches handles the add, replace and remove operations the engine emits.

Patches or commands?

Patches mirror a document exactly, which suits viewers, previews and server-side copies. For two people editing at once, send commands instead and dispatch them on every client: each client validates a command against its own current state, so an edit that no longer applies is rejected instead of being written blindly. See Collaboration.

Other uses

UseHow
AutosaveDebounce on patches, then save project.toJSON()
Dirty flagSet on patches, clear after saving
Audit logStore patches with a timestamp and user
Targeted UI updatesCheck patch.path, e.g. /clips/<id>/…, and refresh only that clip's row

On this page