@alpha2-sh/editor
An editing SDK for alpha2: editing sessions over the engine's project document, with typed commands, validation and undo, and a browser bridge that previews, plays and exports the document with the alpha2 engine.
Install
npm install @alpha2-sh/editor alpha2
alpha2 is a peer dependency at the same version as this package. React is an optional peer, needed only for @alpha2-sh/editor/react (React 19.3 or a later 19). The browser requirements are alpha2's.
Documents and commands
The root import is the editing core. It loads no DOM, React, WebAssembly or audio, so it also runs in Node.js.
import { createEditor, createEmptyDocument, serializeDocument, parseDocument } from "@alpha2-sh/editor";
const editor = createEditor({ document: createEmptyDocument({ width: 1280, height: 720 }) });
const result = editor.dispatch({
type: "track.add",
track: { id: "video", name: "Video", kind: "video", clips: [] },
});
if (!result.ok) throw result.error; // an EditorError with a `code`
editor.undo();
editor.redo();
const json = serializeDocument(editor.document); // portable JSON; media bytes stay apart
const reopened = parseDocument(json);
editor.dispose();
- An editor document holds an engine project (
document.project), the editor's metadata, and a descriptor of each asset.serializeDocumentandparseDocumentwrite and read it as JSON;parseDocumentalso reads a plain engine project. dispatchapplies oneCommand(the typed union covers the project, tracks, clips, effects, transitions, caption cues, composition graphs, assets and markers); atransactionapplies several and is checked once, by the document it ends with. A command the document's rules refuse returns{ ok: false, error }and changes nothing.canExecuteasks without applying.- Every change that commits is one undo step. The history keeps up to 120 steps and 64 MiB by default (
createEditor({ historyLimit, historyByteLimit })). beginGesture()previews a drag: eachupdate(command)shows a candidate ineditor.draftwithout touching the document or the history, andcommit()adds one undo step (cancel()adds none).subscribe,getSnapshotandsubscribeSelectornotify views of changes. A refusal is anEditorError, whosecodesays which check failed.
Browser bridge
@alpha2-sh/editor/browser connects a session to the engine in the browser: import, preview, playback with audio, and export to an MP4, MOV, MKV or WebM file through WebCodecs.
import { createEditor, createEmptyDocument } from "@alpha2-sh/editor";
import { createBrowserBridge } from "@alpha2-sh/editor/browser";
const editor = createEditor({ document: createEmptyDocument({ width: 1280, height: 720 }) });
const bridge = createBrowserBridge({
editor,
// URLs of alpha2/wasm/alpha2_wasm_bg.wasm, alpha2/wasm/alpha2_audio_worklet.wasm and
// alpha2/audio-worklet-processor, as the application's bundler serves them.
runtimeAssets: { wasmUrl, audioWasmUrl, workletUrl },
});
await bridge.bindPreview(canvas);
const { assetIds } = await bridge.importFiles(files); // from an <input type="file">
const trackId = crypto.randomUUID();
const placed = editor.dispatch({
type: "transaction",
commands: [
{ type: "track.add", track: { id: trackId, kind: "video", name: "Video", clips: [] } },
{
type: "clip.insert",
trackId,
clip: {
id: crypto.randomUUID(),
source_id: assetIds[0],
timeline_range: { start: 0, end: 120000 },
source_range: { start: 0, end: 120000 },
time_map: { type: "forward" },
},
},
],
});
if (!placed.ok) throw placed.error;
await bridge.seek(0);
const artifact = await bridge.startExport({ container: "webm" }).result;
// artifact.kind "file": a temporary file in the origin's private storage; release it once downloaded.
if (artifact.kind === "file") await artifact.release();
await bridge.dispose();
await editor.dispose();
- Import checks each file with the engine (media, fonts,
.cubeLUTs, SRT and WebVTT captions throughimportCaptions) and adds it to the document in one undoable step.importSequenceimports numbered PNG or OpenEXR files as one image sequence. A source whose provider reads no file, such as a LaTeX formula ({ format: "latex", latex, font_size }), is kept in the project itself:putProviderSourceadds or edits one in one undoable step, and it draws in preview and both exports with no file to relink. bindPreviewdraws on a canvas. WithruntimeAssets.workerFactory, a worker running the@alpha2-sh/editor/preview-workerentry renders the preview off the main thread; without one, or when the worker fails, the main thread does.hashWorkerFactory(@alpha2-sh/editor/hash-worker) andextensionsWorkerFactory(@alpha2-sh/editor/extensions-worker, withextensionsWasmUrlandextensionModuleUrlssuch as{ pdf }) move file hashing and SVG, animated image, Lottie, OpenEXR and PDF reading into workers too.seek,playandpausemove the playhead; with audio, playback follows the audio clock. Audio preview needs a cross-origin isolated page; editing and video preview do not.startExportreturns a job withresult, progress (subscribe) andcancel.container(mp4by default,mov,mkvorwebm) names the file and decides the codecs: an absentvideoCodecoraudioCodecis the container's default (H.264 and AAC, or VP9 and Opus for WebM, with Opus where a browser cannot encode AAC in MP4 or MKV), and one the container does not offer is refused. The output goes to a temporary file in the origin's private storage by default (release()deletes it), to a file the user picked (output: { kind: "file", handle }), or to memory (output: { kind: "memory", maxBytes }). Whether the browser encodes a codec is the browser's to say:bridge.videoEncoders()asks ahead, and an export with a codec the browser does not encode is refused before it renders.
React
@alpha2-sh/editor/react has EditorProvider, useEditor, useEditorSelector(selector, equal?) and EditorPreview, which binds a bridge's preview to a canvas it makes:
<EditorProvider editor={editor}>
<EditorPreview bridge={bridge} onError={reportError} />
</EditorProvider>
The application owns the session: unmounting the provider does not dispose it.
Native export
@alpha2-sh/editor/native-client (createNativeClient({ baseUrl })) exports a document on an HTTP export service that the application runs, for example on top of @alpha2-sh/node. It uploads the document's media (bridge.nativeAssets) and follows the job to a downloadable file.
License
MIT, copyright Alpha2 contributors.