# word-editor API

`word-editor` exposes the DOCX editor as a native Web Component. The component can be embedded in any browser application, independent of React, Vue, Angular, Svelte, or a server-rendered host.

中文说明：本文档以当前代码实现为准，覆盖生产集成所需的元素属性、方法、命令、事件、字体网关、授权和部署注意事项。

## Installation

Build the runtime and import the generated browser module:

```bash
npm run build
```

The build command validates the committed runtime against a deterministic fingerprint of `packages/word-editor/src`, the rebuild script, and the locked compiler inputs. It regenerates `packages/word-editor/dist` only when files are missing or that fingerprint is stale; use `npm run serve:demo` only when deliberately serving an already-validated build.

```html
<word-editor id="editor" mode="edit" zoom="1"></word-editor>

<script type="module">
  import "/packages/word-editor/dist/index.js";

  const editor = document.querySelector("#editor");
  await editor.importDocument(file, { name: file.name });
</script>
```

The default package entry registers `<word-editor>` automatically. Advanced hosts can import and call `defineWordEditorElement(tagName)` if they need a custom tag name.

```ts
import { WordEditorElement, defineWordEditorElement } from "./packages/word-editor/dist/index.js";

defineWordEditorElement("acme-word-editor");
```

## Lifecycle

The element must be connected to the DOM before `importDocument()`, `loadBlank()`, `saveDocument()`, or command methods are called. A typical production lifecycle is:

1. Render `<word-editor>`.
2. Wait for `ready` or call `importDocument()` after the next microtask once the element is connected.
3. Subscribe to `loaded`, `changed`, `saved`, and `error`.
4. Keep host-side dirty state in sync with `changed`.
5. Upload or persist the Blob returned by `saveDocument()`.
6. Call `destroy()` only when the host intentionally tears down the editor instance.

```ts
const editor = document.querySelector("word-editor");

editor.addEventListener("ready", () => {
  console.log("editor mounted");
});

editor.addEventListener("changed", (event) => {
  console.log("dirty:", event.detail.dirty);
});

editor.addEventListener("error", (event) => {
  console.error(event.detail.code, event.detail.message);
});

await editor.importDocument(file, { name: file.name });
const docx = await editor.saveDocument();
```

## Element Attributes

| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `mode` | `edit`, `view` | `edit` | Editing or view mode. Mirrors `setMode()`. |
| `engine` | `ts` | `ts` | Current production engine. The public property is `engineKind`. |
| `zoom` | positive number | `1` | Page zoom ratio. Mirrors `setZoom()`. |
| `auto-font-gateway` | boolean attribute | disabled | Enables background font loading from a manifest. |
| `font-gateway-url` | URL | default gateway when auto loading is enabled | Font gateway manifest URL. |
| `font-gateway-limit` | positive integer | default auto limit | Maximum number of gateway fonts to load automatically. |
| `license-required` | boolean attribute | disabled | Requires runtime license validation before mounting. |
| `license-feature` | string | runtime default | Feature name used by the license gate. |

Example:

```html
<word-editor
  mode="edit"
  zoom="1"
  auto-font-gateway
  font-gateway-url="https://font.flyfish.group/manifest.json"
  font-gateway-limit="32"
  license-required
  license-feature="word-editor-production">
</word-editor>
```

## Constructor Options

`new WordEditorElement(options)` accepts the same core configuration for programmatic mounting.

```ts
const editor = new WordEditorElement({
  mode: "edit",
  engine: "ts",
  zoom: 1,
  fonts: [
    {
      family: "Aptos",
      src: 'local("Aptos")',
      display: "swap",
    },
  ],
  fontGateway: {
    autoLoad: true,
    manifestUrl: "https://font.flyfish.group/manifest.json",
    limit: 32,
    background: true,
    awaitFontFaces: false,
  },
});

document.body.append(editor);
```

## Methods

### `load(input, name?)`

Loads a DOCX file into the editor.

```ts
await editor.load(file, file.name);
await editor.load(blob, "contract.docx");
await editor.load(arrayBuffer, "template.docx");
```

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| `input` | `File \| Blob \| ArrayBuffer` | DOCX payload. |
| `name` | `string \| undefined` | Display and save name. |

Resolves when the document is available in the editor. On success, the editor emits `loaded` and resets dirty state with `changed`.

### `loadBlank(name?)`

Creates a blank document.

```ts
await editor.loadBlank("blank.docx");
```

If the current engine does not support blank documents, the method rejects with an error.

### `importDocument(input, options?)`

Stable host-integration alias for loading DOCX data. It accepts `File`, `Blob`, `ArrayBuffer`, and `Uint8Array`.

```ts
await editor.importDocument(file, { name: file.name });
await editor.importDocument(bytes, { name: "contract.docx" });
```

### `save()`

Serializes the current document to a DOCX Blob.

```ts
const blob = await editor.save();
await uploadDocument(blob);
```

The method emits `saved` and then updates dirty state with `changed`.

### `saveDocument()` and `exportDocument(options?)`

`saveDocument()` is the explicit host-integration alias for `save()` and returns a DOCX `Blob`. `exportDocument()` returns either a `Blob` or an `ArrayBuffer` for upload and transport APIs.

```ts
const blob = await editor.saveDocument();
await uploadDocument(blob);

const bytes = await editor.exportDocument({ output: "array-buffer" });
```

### `exec(command, value?)`

Executes a ribbon/API command.

```ts
await editor.exec("bold");
await editor.exec("fontName", "Aptos");
await editor.exec("fontSizePt", "12");
await editor.exec("foreColor", "#185abd");
await editor.exec("toggleRuler");
```

Formatting commands act on the current selection where supported. View commands update the editor UI state.

### `insertText(text)`

Inserts plain text at the current selection. Newline characters become line breaks and HTML is not interpreted.

```ts
editor.insertText("由第三方系统插入的审批意见\nApproved.");
```

Returns `true` when the operation is accepted. Protected or view-only documents return `false`.

### `insertContent(content, options?)`

Provides one insertion API for the current cursor/selection, document start, document end, and bookmark boundaries. Content is plain text; HTML is never interpreted. Newline characters become line breaks.

```ts
editor.insertContent("current selection", { at: "cursor" });
editor.insertContent({ type: "text", text: "document prefix" }, { at: "document-start" });
editor.insertContent("document suffix", { at: "document-end" });
editor.insertContent("signed", {
  at: "bookmark",
  bookmark: "CustomerSignature",
  placement: "start",
});
```

Bookmark `placement` accepts `before`, `start`, `end`, and `after`. `start` is the default and preserves the behavior of `insertTextAtBookmark()`.

### `insertTable(rows?, columns?)`

Inserts a table at the current selection.

```ts
await editor.insertTable(3, 4);
```

Defaults to `2 x 2`.

### `insertImage(input, options?)`

Inserts an image at the current selection.

```ts
await editor.insertImage(file, {
  name: file.name,
  mimeType: file.type,
  alt: "Approved contract stamp",
  maxWidthPx: 640,
});
```

Options:

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | Image name for metadata. |
| `mimeType` | `string` | Image MIME type. |
| `alt` | `string` | Alternative text. |
| `maxWidthPx` | `number` | Optional display width cap. |

### Bookmark Methods

Bookmarks are maintained as first-class document anchors. The API supports existence checks, listing, creation, deletion, lookup, and text insertion at a bookmark without replacing the whole document DOM.

```ts
const name = editor.addBookmark("CustomerSignature");
const exists = editor.hasBookmark("CustomerSignature");
const bookmark = editor.findBookmark("CustomerSignature");
const allBookmarks = editor.listBookmarks();
const position = editor.getBookmarkPosition("CustomerSignature");
editor.gotoBookmark("CustomerSignature");

editor.insertTextAtBookmark("CustomerSignature", "Signed by Wang Yu\n2026-06-30");
editor.deleteBookmark("CustomerSignature");
```

| Method | Return | Description |
| --- | --- | --- |
| `listBookmarks()` | `WordEditorBookmarkInfo[]` | Returns all bookmarks with identity, text, page, and start/end logical and visual positions. |
| `hasBookmark(name)` | `boolean` | Checks whether a bookmark exists by name or id. |
| `findBookmark(name)` | `WordEditorBookmarkInfo \| undefined` | Returns one bookmark descriptor by name or id. |
| `getBookmarkPosition(name)` | `WordEditorBookmarkPosition \| undefined` | Returns fresh start/end positions, including page, block and document offsets plus page/viewport coordinates. |
| `gotoBookmark(name)` | `boolean` | Scrolls to and selects the bookmark location. |
| `addBookmark(name?)` | `string \| undefined` | Creates a point bookmark or range bookmark from the current selection and returns the final unique name. |
| `deleteBookmark(name)` | `boolean` | Removes bookmark markers while preserving bookmarked content. |
| `insertTextAtBookmark(name, text)` | `boolean` | Inserts plain text at the bookmark using DOM Range; newline characters become line breaks. |

Position conventions are explicit: `page` is one-based; `blockIndex`, `offsetInBlock`, and `documentOffset` are zero-based UTF-16 offsets. `pageOffset` is relative to the rendered page and `viewportRect` is a snapshot relative to the browser viewport, so visual coordinates should be queried again after zoom, resize, or pagination.

### Font Methods

```ts
await editor.registerFont({
  family: "Source Han Sans SC",
  src: "url(/fonts/SourceHanSansSC.woff2)",
  display: "swap",
  license: "licensed",
});

await editor.registerFonts(fonts);
const imported = await editor.importFont(file, { family: "Custom Sans", license: "tenant" });
const loaded = await editor.loadFontGatewayFonts({ manifestUrl, limit: 32, background: true });
const repositoryFonts = await editor.loadRepositoryFonts(manifestUrl, true, { families: ["Aptos"] });
const persisted = await editor.listPersistedFonts();
await editor.clearPersistedFonts();
```

Available font APIs:

| Method | Description |
| --- | --- |
| `registerFont(font)` | Registers one font source with the active engine. |
| `registerFonts(fonts)` | Registers multiple font sources. |
| `importFont(input, options?)` | Imports a font file and returns a `WordEditorFontSource`. |
| `loadPersistedFonts()` | Loads browser-persisted fonts. |
| `listPersistedFonts()` | Lists browser-persisted font records. |
| `clearPersistedFonts()` | Clears browser-persisted font records. |
| `loadRepositoryFonts(manifestUrl?, persist?, options?)` | Loads fonts from a custom manifest. |
| `loadFontGatewayFonts(options?)` | Loads fonts through the configured gateway. |

### View and Runtime Methods

```ts
editor.setMode("view");
editor.setMode("edit");
editor.setZoom(1.1);
editor.reflowPages();

const status = await editor.getRuntimeStatus();
console.log(status?.pageCount, status?.dirty, status?.fonts);
```

| Method / Property | Description |
| --- | --- |
| `setMode(mode)` | Sets `edit` or `view` mode. |
| `setEngine(engine)` | Sets the engine attribute. Current valid value is `ts`. |
| `setZoom(value)` | Sets the page zoom ratio. |
| `reflowPages()` | Requests pagination/layout refresh. |
| `getRuntimeStatus()` | Reads runtime status including engine, document name, dirty flag, page count, fonts, persisted fonts, layout and feature metadata. |
| `dirty` | Boolean dirty state. |
| `documentName` | Current document name if a document is loaded. |
| `mode` | Current mode. |
| `engineKind` | Current engine kind. |
| `zoom` | Current zoom ratio. |
| `destroy()` | Destroys the engine and clears the component host. |

## Commands

Commands are accepted by `exec(command, value?)`. The `WordEditorCommand` type currently includes:

### Formatting

`bold`, `italic`, `underline`, `strikeThrough`, `fontName`, `fontSize`, `fontSizePt`, `foreColor`, `hiliteColor`, `removeFormat`, `increaseFont`, `decreaseFont`, `subscript`, `superscript`, `textEffects`.

### Paragraph and Lists

`justifyLeft`, `justifyCenter`, `justifyRight`, `justifyFull`, `insertUnorderedList`, `insertOrderedList`, `multilevelList`, `increaseIndent`, `decreaseIndent`, `sortParagraphs`, `lineSpacing`, `toggleBorders`, `toggleShading`, `formatBlock`.

### Page Layout and View

`pageBreak`, `setPageSize`, `setMargins`, `setOrientation`, `toggleNavigation`, `toggleRuler`, `togglePageEnds`, `toggleGridlines`, `toggleSnapToGrid`, `toggleColumns`, `toggleLineNumbers`, `togglePageBorder`, `togglePageColor`, `toggleDarkMode`.

### Tables

`insertTable`, `insertTableRow`, `insertTableColumn`, `deleteTableRow`, `deleteTableColumn`, `mergeCellRight`, `splitCell`, `sortTableAscending`, `sortTableDescending`, `autoFitTable`, `distributeTableRows`, `distributeTableColumns`.

### Insert Objects

`insertEquation`, `insertFraction`, `insertRadical`, `insertScript`, `insertIntegral`, `insertMatrix`, `insertShape`, `insertTextBox`, `insertWordArt`, `insertInkStroke`, `insertSmartArt`, `insertChart`, `insertHyperlink`, `insertOnlineVideo`, `insertPageNumber`, `insertSymbol`, `insertEmoji`.

### References, Review, and Collaboration

`insertComment`, `insertFootnote`, `insertEndnote`, `insertToc`, `deleteToc`, `insertCitation`, `insertBibliography`, `insertTableOfFigures`, `insertCaption`, `insertCrossReference`, `insertBookmark`, `toggleTrackChanges`, `toggleHeaderFooterEditing`, `acceptRevision`, `rejectRevision`, `acceptAllRevisions`, `rejectAllRevisions`, `previousComment`, `nextComment`, `previousRevision`, `nextRevision`, `deleteComment`, `toggleMarkupView`, `toggleRevisionFilter`, `updateFields`, `showNotes`.

### Search and History

`undo`, `redo`, `find`, `replace`, `replaceAll`.

## Events

Subscribe with `addEventListener`.

| Event | Detail | Description |
| --- | --- | --- |
| `ready` | none | Engine mounted and editor surface is available. |
| `loaded` | `WordEditorLoadResult` | Document loaded. Includes name, engine, optional page count, fonts, persisted fonts, font fallback and coverage data. |
| `changed` | `{ dirty: boolean }` | Dirty state changed. |
| `saved` | `WordEditorSaveResult` | Save completed. Includes document name and DOCX Blob. |
| `error` | `WordEditorErrorDetail` | License, loading, saving, command, or runtime error. |
| `word-editor-operation` | operation commit | Bubbled composed event from the internal editing surface for audit/collaboration integrations. |
| `word-editor-action` | `{ action, ...detail, respondWith?(result) }` | Cancelable bubbled/composed host action event for integrations such as saving, sharing, plugins, or external workflows. A host that takes over Save should call `preventDefault()` and pass its persistence promise to `respondWith`; dirty state is cleared only after that promise fulfills. |
| `word-editor-semantic-paste` | semantic paste payload | Bubbled composed event with structured paste summary and operations. |

Example:

```ts
editor.addEventListener("loaded", (event) => {
  const { name, pageCount, fonts } = event.detail;
  updateTitle(name);
  updateStatus(`${pageCount ?? 0} pages, ${fonts?.length ?? 0} fonts`);
});

editor.addEventListener("saved", async (event) => {
  await uploadDocument(event.detail.blob);
});

editor.addEventListener("word-editor-action", (event) => {
  if (event.detail.action !== "save") return;
  event.preventDefault();
  event.detail.respondWith(uploadDocument(event.detail.blob));
});

editor.addEventListener("error", (event) => {
  showToast(event.detail.message);
});
```

## TypeScript Types

The package exports the public types from `core/types.ts`:

```ts
import type {
  WordEditorCommand,
  WordEditorBookmarkInfo,
  WordEditorBookmarkPosition,
  WordEditorContentInput,
  WordEditorDocumentExportOptions,
  WordEditorDocumentImportOptions,
  WordEditorDocumentInput,
  WordEditorDocumentPosition,
  WordEditorEngineKind,
  WordEditorErrorDetail,
  WordEditorFontGatewayAutoOptions,
  WordEditorFontGatewayOptions,
  WordEditorFontRepositoryLoadOptions,
  WordEditorFontSource,
  WordEditorInsertImageOptions,
  WordEditorInsertContentOptions,
  WordEditorLoadResult,
  WordEditorMode,
  WordEditorOptions,
  WordEditorRuntimeStatus,
  WordEditorSaveResult,
} from "./packages/word-editor/dist/index.js";
```

Key shapes:

```ts
type WordEditorMode = "view" | "edit";
type WordEditorEngineKind = "ts";

interface WordEditorFontSource {
  family: string;
  src?: string;
  source?: string;
  blob?: Blob | ArrayBuffer;
  descriptors?: FontFaceDescriptors;
  weight?: string;
  style?: string;
  display?: "auto" | "block" | "swap" | "fallback" | "optional";
  license?: string;
  fileName?: string;
  id?: string;
  persisted?: boolean;
  persist?: boolean;
}

interface WordEditorErrorDetail {
  code: string;
  message: string;
  detail?: unknown;
}
```

## Font Gateway

The font gateway should be asynchronous for best first-screen performance. The editor can render the document first, load fonts in the background, then reflow pages when fonts become available.

```html
<word-editor
  auto-font-gateway
  font-gateway-url="https://font.flyfish.group/manifest.json"
  font-gateway-limit="32">
</word-editor>
```

Programmatic loading:

```ts
await editor.loadFontGatewayFonts({
  manifestUrl: "https://font.flyfish.group/manifest.json",
  families: ["Aptos", "SimSun", "Microsoft YaHei"],
  limit: 32,
  persist: true,
  background: true,
  awaitFontFaces: false,
});

editor.reflowPages();
```

Production guidance:

- Keep `awaitFontFaces: false` for automatic entry-time loading unless the host intentionally wants to block.
- Set a conservative `limit` to avoid excessive memory and network use.
- Persist only fonts that your license and tenant policy allow.
- Surface progress in host UI if the host triggers manual font loading.

## Licensing

The runtime supports an explicit license gate. When `license-required` is present, the component validates the configured license before creating the engine. If validation fails, it:

- Sets `data-license-error`.
- Renders a license error panel.
- Dispatches an `error` event with a `WordEditorErrorDetail`.

```html
<word-editor
  license-required
  license-feature="word-editor-production">
</word-editor>
```

Production deployments should include:

- `office-preview-license.json`
- `auth/word-editor-license-runtime.mjs`
- `auth/word-editor-license-signature.mjs`
- `auth/word-editor-license-policy.mjs`
- `auth/word-editor-license-public-keys.json`
- A release metadata file such as `word-editor-release.json`

## Cloudflare Pages Deployment

Current production origin:

```txt
https://docx-editor.pages.dev
```

Build and deploy:

```bash
npm run build:cloudflare
npm run deploy:cloudflare
```

`deploy:cloudflare` uploads the existing verified artifact and intentionally does not rebuild it.

The Cloudflare Pages artifact includes:

- `/` product homepage
- `/docs/` documentation site
- `/docs/*.md` source documentation
- `/examples/demo/` production demo/editor
- `/word-editor-runtime-loader.mjs` same-origin authorized runtime loader
- `/auth/*` license runtime
- `/wasm/word-editor-runtime-core.wasm` sealed runtime payload
- `/word-editor-release.json`

## Error Handling

Use the `error` event as the single host-facing error channel.

```ts
editor.addEventListener("error", (event) => {
  const detail = event.detail;
  logEditorError(detail.code, detail.message, detail.detail);
  showDocumentError(detail.message);
});
```

Recommended host behavior:

- Show user-safe messages for load/save failures.
- Keep technical detail in logs.
- Disable destructive actions while `save()` is pending.
- Preserve the original file until the new Blob is successfully stored.

## Production Checklist

- Root page loads from `/`.
- Documentation loads from `/docs/`.
- Demo loads from `/examples/demo/`.
- `office-preview-license.json` returns `Cache-Control: no-store`.
- WASM returns `Content-Type: application/wasm`.
- `word-editor-release.json` identifies `product: "word-editor"` and the deploy origin.
- Web Component emits `ready`, `loaded`, `changed`, `saved`, and `error` as expected.
- Font gateway loading does not block the first visible document render.
- Host app handles dirty state before navigation.
- Only affected tests are run during iteration; broader browser gates are run before release.
