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.