API reference
The full @squareys/webdecks-sdk surface (ABI v2), grouped by area. Types
are mirrored from the SDK's TypeScript definitions; the source is the single source of
truth for tsdoc.
Every method below hangs off a Webdocs instance
(const doc = Webdocs.create(mod)). New to it? Start with
Getting started.
Lifecycle
A document is a numeric handle into the engine. Open one over a loaded wasm module, and release it explicitly — the SDK never leans on the garbage collector to free wasm memory.
Webdocs.create(mod): Webdocs Assert the wasm's ABI version (throwing on a mismatch) and open a document handle. `mod` is the loaded WebDocs module (the object with HEAPU8, _malloc/_free and the _wdoc_sdk_* exports).
doc.close(): void Release the handle. In this phase it resets the model back to a single empty paragraph.
Loading
doc.loadDocxParts(parts: DocxPart[]): void Parse an already-inflated .docx into the model and reset the caret. Each DocxPart is { name, data } — a ZIP entry path and its INFLATED bytes. Inflating the ZIP is the browser's job (DecompressionStream); the SDK never ships a decompressor.
Reading the model
Every reader fills a record you allocate once and reuse, returning true on success or false for a bad handle / out-of-range index. No allocation per call — poll them every frame safely.
doc.docInfo(out: DocInfo): boolean Whole-document counters: paraCount, wordCount, charCount, pageCount. pageCount is 0 until a layout exists (a WebGPU frame has run).
doc.paraCount(): number Shorthand for just the paragraph count.
doc.paraInfo(para: number, out: ParaInfo): boolean One paragraph's properties: textLen (UTF-8 bytes), align (ALIGN.*), style (PSTYLE.*), indentLevel, and spaceBefore/spaceAfter in px @96dpi.
doc.runInfo(para: number, off: number, out: RunInfo): boolean The maximal equal-format run starting at byte `off`: start, end (exclusive), fmt (DFMT bit flags), color (packed 0xRRGGBBAA, 0 = default), family (font id) and sizePx. Walk a paragraph's formatting by stepping off to each run's end.
doc.paraText(para: number): string The paragraph's text as a JS string. Allocates that one string — a convenience off the zero-garbage path.
doc.paraTextInto(para: number, dst: Uint8Array): number Copy the paragraph text (UTF-8, no NUL) into a caller buffer and return the FULL byte length, which may exceed dst.length (size dst from paraInfo().textLen). Allocates nothing.
doc.selInfo(out: SelInfo): boolean The current caret + selection: caretPara/caretByte (the insertion point), anchorPara/anchorByte (the selection start), and hasSelection (1 when they differ).
Editing
Mutations behave exactly like the corresponding keystroke or menu action — pending style and undo included — because they are the engine's own operations under curated names.
doc.setCaret(para: number, byte: number): void Collapse the caret to (para, byte), clearing any selection.
doc.select(para: number, a: number, b: number): void Select the range [a, b) within one paragraph (anchor = a, caret = b).
doc.insertText(s: string): void Insert a JS string at the caret, replacing any selection. Encodes the string once (input side).
doc.insertBytes(bytes: Uint8Array, len: number): void Zero-garbage insert: the caller owns the UTF-8 bytes; the SDK copies `len` of them into scratch and inserts at the caret.
doc.toggleFmt(bit: number): void Toggle a DFMT bit (BOLD, ITALIC, …) over the selection.
doc.setParaStyle(style: number): void Apply a PSTYLE ordinal (Body, H1…H3, Bullet, Numbered) to every paragraph the caret / selection touches.
UI-level operations
The same ops the editor's own keyboard and menus call — one code path, so a TS app never forks the behaviour.
doc.copy(): void · doc.cut(): void · doc.paste(): void The editor's internal rich clipboard. copy grabs the selection; cut copies then deletes; paste inserts the clip at the caret, splitting paragraphs on newlines and keeping formatting.
doc.moveCaret(unit: number, dir: number, extend: boolean): void Move the caret one MOVE.* unit in `dir` (< 0 backward/up/start, > 0 forward/down/end). `extend` grows the selection (Shift+move) instead of collapsing it. LINE and LINE_EDGE need a laid-out document (drive a renderFrame first); otherwise they no-op.
doc.undo(): boolean · doc.redo(): boolean Undo / redo one step of history. Returns true if something changed. The caret is clamped into the result.
doc.selection(): Selection A thin Selection facade over this document (see below). Holds only the doc reference and one reusable record — allocates nothing per operation.
Selection facade
Ergonomics over the numeric caret/select/move ABI — build selection UI without touching offsets.
sel.set(para, byte) · sel.range(para, a, b) Collapse the caret, or select a range within one paragraph.
sel.left / right / wordLeft / wordRight / up / down / home / end / docStart / docEnd(extend = false) Named caret motions with the direction folded in — the common Word/Docs movements. Pass true to extend the selection.
sel.read(out?: SelInfo): boolean Refresh the caret/selection state. Fills a caller record, or the facade's own reusable one when called with no argument.
sel.caretPara() · caretByte() · anchorPara() · anchorByte() · hasSelection() Scalar getters that read back through the shared record.
Rendering & export
Low-level frame painting plus the same high-level export paths the WebDocs File menu uses, for pixel and print parity. PDF/PNG need a WebGPU device; DOCX/ODT do not.
doc.renderFrame(w, h, dpr, zoom): boolean Paint the document to the on-screen WebGPU canvas (the swapchain configured at init) with no editing caret — a static page paint. Returns false if the GPU device / renderer is not up yet. A renderer-only product drives frames through this one call.
doc.exportPng(page, w, h, scale, waitFrame?): Promise<Uint8Array> Render a page offscreen and resolve to its raw w*h*4 RGBA bytes (the browser owns PNG encoding — createImageBitmap / canvas.toBlob). `scale` is device px per logical px @96dpi. A headless host resolves an empty buffer.
doc.pngBegin / pngRender / pngReady / pngPtr / pngEnd The low-level offscreen capture that exportPng wraps: size an RGBA8 target, render a page into it, poll ready on a frame, read the tight buffer at ptr, then free it.
doc.exportDocx(): Uint8Array · doc.exportOdt(): Uint8Array · doc.exportPdf(): Uint8Array Build the document into the named format and return the bytes. DOCX and ODT are exact and WebGPU-free; PDF measurement needs the glyph cache (GPU path).
Records
The scalar shapes the readers fill, and the factories that create a reusable instance. Reuse one record across many reads to keep steady-state operations allocation-free.
interface DocInfo { paraCount; wordCount; charCount; pageCount; }
interface ParaInfo { textLen; align; style; indentLevel; spaceBefore; spaceAfter; }
interface RunInfo { start; end; fmt; color; family; sizePx; }
interface SelInfo { caretPara; caretByte; anchorPara; anchorByte; hasSelection; }
// Allocate a reusable record once, then refill it on every read:
createDocInfo(); createParaInfo(); createRunInfo(); createSelInfo(); Constants
Exported alongside Webdocs — the flag and ordinal tables the ops take:
DFMT BOLD ITALIC UNDERLINE STRIKE SUPER SUB SMALLCAPS ALLCAPS DSTRIKE // bit flags
PSTYLE BODY H1 H2 H3 BULLET NUMBERED IMAGE TABLE // ordinals
ALIGN LEFT CENTER RIGHT JUSTIFY // ordinals
MOVE CHAR WORD LINE LINE_EDGE DOC // moveCaret units Licensing & anti-theft locking Deferred
The shipping plan calls for the engine to validate a signed licence token (domain + expiry) inside the wasm at init — unlicensed origins get a watermark or a refusal, and the public live-example build is a sandboxed demo build. That work is intentionally deferred: it needs the token scheme chosen first, and is tracked as a TODO rather than shipped here. Until then the SDK loads an unlocked build with no origin gate.