WebDocs SDK

A TypeScript API for the browser-native WebDocs engine. Load a Word document, read and mutate its model, drive the WebGPU renderer and the PDF/DOCX/ODT export — all from your own app, over one small, versioned C ABI.

WebDocs is a from-scratch word processor compiled to WebAssembly and drawn entirely on the GPU (see Technology). The SDK (@squareys/webdecks-sdk) is the supported way to build on that engine: a thin, zero-garbage binding that speaks to the wasm through a curated wdoc_sdk_* C ABI — not the raw exports. It is the same surface our own UI is being rebuilt on, so what you get is dogfooded by construction.

Status

The SDK is WebDocs-only today, covering load, model read/update, caret and selection, clipboard, styling, undo/redo, and rendering/export (ABI v2). WebSheets and WebDecks bindings share the same ABI shape and land in a later phase. The API is versioned and allowed to change before GA.

Import it

The SDK is authored in erasable-syntax-only TypeScript, resolved through its package exports:

# The SDK is a workspace package in the WebDecks monorepo.
# In your own project, add it as a dependency and import by name:
import { Webdocs, DFMT, createDocInfo } from "@squareys/webdecks-sdk";

The SDK is pure TypeScript — it drives a loaded wasm module, it does not ship one. Loading the engine (webdocs.js / webdocs.wasm plus the UI font) is the host's job; the reference demo's engine.ts is a copy-paste starting point, and Live examples shows it in a running editor.

A tiny editor in ~30 lines

Boot the engine, open a document, read it, make an edit, paint a page, and export — the whole loop, using nothing but the SDK:

import { Webdocs, DFMT, createDocInfo } from "@squareys/webdecks-sdk";
import { loadEngine } from "./engine"; // boots webdocs.wasm — see the reference demo
import { unzipDocx } from "./docx";    // inflates a .docx ZIP with DecompressionStream

// 1. Boot the engine and open a document.
const { mod } = await loadEngine("/engine/");
const doc = Webdocs.create(mod);       // asserts the ABI version, opens a doc handle

// 2. Load a .docx the user picked (any File from an <input type=file>).
const parts = await unzipDocx(await file.arrayBuffer());
doc.loadDocxParts(parts);

// 3. Read the model into a reusable record (allocates nothing per call).
const info = createDocInfo();
doc.docInfo(info);
console.log(info.paraCount + " paragraphs, " + info.wordCount + " words");

// 4. Edit: bold the first three characters of paragraph 0.
doc.setCaret(0, 0);
doc.select(0, 0, 3);
doc.toggleFmt(DFMT.BOLD);

// 5. Render page 0 into a <canvas> via WebGPU (raw RGBA -> ImageData).
const [w, h] = [816, 1056];            // 8.5x11 inch @ 96dpi
const rgba = await doc.exportPng(0, w, h, 1);
canvas.getContext("2d").putImageData(
  new ImageData(new Uint8ClampedArray(rgba), w, h), 0, 0);

// 6. Export — the same paths the WebDocs File menu uses.
const pdf = doc.exportPdf();           // Uint8Array: hand to a Blob download
const docx = doc.exportDocx();

Reading the model

The hot-path readers fill a record you own instead of allocating one per call — create the record once and reuse it across every read (this is the SDK's zero-garbage discipline, so a live editor can poll the model every frame without churning the GC):

const para = createParaInfo();
const run = createRunInfo();
for (let p = 0; p < info.paraCount; p++) {
  doc.paraInfo(p, para);               // textLen, align, style, indent, spacing
  const text = doc.paraText(p);        // the paragraph's UTF-8 text as a JS string
  doc.runInfo(p, 0, run);              // the formatting run starting at byte 0
  const bold = (run.fmt & DFMT.BOLD) !== 0;
}

Concepts worth knowing

  • Numeric handles. A document is a u32 handle into the engine; 0xFFFFFFFF is the null. Lifetime is explicit — Webdocs.create() opens one, doc.close() releases it. Nothing relies on the garbage collector to free wasm memory.
  • ABI-version gate. Webdocs.create() reads the wasm's ABI version and refuses a module it wasn't generated against, so a mismatched engine fails loudly at startup instead of corrupting reads.
  • Positions are (paragraph, byte). The caret and selection are paragraph index + UTF-8 byte offset pairs — no NUL scanning, and stable across reads.
  • Export mirrors the UI. exportPdf(), exportDocx() and exportPng() reuse the exact paths the WebDocs File menu uses, so a consumer gets pixel and print parity for free. PDF/PNG need a WebGPU device; DOCX/ODT do not.

Next

Read the API reference for every operation, or open the live examples — a full React editor built on the SDK, plus copy-paste snippets.