@onodocs/canvas 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +407 -219
- package/SDK.md +407 -219
- package/agreement-review.md +13 -0
- package/ai-report-demo.md +40 -0
- package/application-integration.md +87 -0
- package/document-collaboration.md +50 -0
- package/document-forms.md +84 -0
- package/document-modes.md +44 -0
- package/index.js +264 -39
- package/package.json +11 -3
- package/proposal.md +36 -0
- package/templates.md +71 -0
- package/types/canvas/index.d.ts +3 -0
- package/types/canvas/presentation.d.ts +7 -1
- package/types/canvas/text-selection.d.ts +10 -2
- package/types/foundation/page-interaction.d.ts +26 -0
- package/types/render-api/index.d.ts +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Agreement review
|
|
2
|
+
|
|
3
|
+
Run `npm run build`, then `npm run example:agreement-review` and open the printed local address. From an installed SDK package, install `esbuild` alongside `@onodocs/sdk` and run `node node_modules/@onodocs/sdk/examples/agreement-review/serve.mjs`.
|
|
4
|
+
|
|
5
|
+
Choose a clause and add a comment in the review panel. Switch reviewer identity to reply as the other fictional party. Select Suggest wording, edit the clause, and submit. The review panel shows the deletion and insertion separately. Accept all or Reject all decides the entire replacement; individual buttons decide one revision at a time. Compare with original shows the current text against the original agreement.
|
|
6
|
+
|
|
7
|
+
Download reviewed Word to retain comments, replies and pending text revisions. Open the file in Word, save it, and use Open returned Word file to continue reviewing. Accepted wording is ordinary text and rejected wording is removed. The demo asks before replacing changes that have not been downloaded.
|
|
8
|
+
|
|
9
|
+
The browser holds saved versions for this tab only. Compare version checks a saved snapshot. To undo a wording edit, choose Editing in the editor mode selector and use Undo. Download before closing the tab. No document is uploaded to a service. The reviewer selector is a demonstration identity, not authentication.
|
|
10
|
+
|
|
11
|
+
The fictional sample is for evaluating the workflow. Word 16.0 exchange validation covers comments, replies, pending text replacements and an ordinary Word text edit. It does not establish compatibility for every Word review feature.
|
|
12
|
+
|
|
13
|
+
Run `npm run validate:agreement-review` for the browser and installed Word exchange scenario. This requires Chrome and Microsoft Word on Windows.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Report review example
|
|
2
|
+
|
|
3
|
+
In a repository checkout, run `npm run build` and `npm run example:report`, then open `http://127.0.0.1:5194`. The supplied report uses fictional quarterly figures and contains two conflicting claims. Review the suggestions, highlight their evidence, adjust the proposed wording and apply or dismiss each change. Download Word or PDF, or open Extracted content to download JSON and Markdown. You can open another DOCX locally.
|
|
4
|
+
|
|
5
|
+
From an npm installation, install `@onodocs/sdk`, `@onodocs/canvas` and `esbuild`, then run `node node_modules/@onodocs/sdk/examples/ai-report/serve.mjs`. The archive includes the report example and its shared logo asset.
|
|
6
|
+
|
|
7
|
+
The default reviewer is a local demonstration with predetermined suggestions for the supplied report. It does not call an AI provider or analyze arbitrary documents. The display identifies it as a sample reviewer. To use a real provider, supply a reviewer when mounting the example:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { mountReport } from "./main.js";
|
|
11
|
+
import { httpReviewer } from "./report.js";
|
|
12
|
+
|
|
13
|
+
const report = await mountReport(container, {
|
|
14
|
+
input: docxBytes,
|
|
15
|
+
reviewer: httpReviewer("/api/review-report", "Your application review service"),
|
|
16
|
+
document: { licenseKey }
|
|
17
|
+
});
|
|
18
|
+
// Call report.dispose() when leaving the page.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Keep provider credentials on your backend. The endpoint receives `{ content, markdown }`; JSON nodes contain opaque `source` references. Ask the provider for the response below. `start` and `end` are UTF-16 offsets within the referenced paragraph, with `end` exclusive. `quote` must equal that range. References from other documents or earlier reviews are rejected. The application can instead provide `{ name, analyze({ content, markdown, signal }) }` directly. The callback must honor cancellation and return the same response format.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"summary": "Check this claim against the delivery section.",
|
|
26
|
+
"findings": [{
|
|
27
|
+
"source": "paragraph source from the current extraction",
|
|
28
|
+
"start": 0,
|
|
29
|
+
"end": 9,
|
|
30
|
+
"quote": "All done.",
|
|
31
|
+
"reason": "The supporting passage lists unfinished work.",
|
|
32
|
+
"replacement": "Some work remains.",
|
|
33
|
+
"evidence": [{ "source": "supporting paragraph source", "start": 0, "end": 8, "quote": "Two left" }]
|
|
34
|
+
}]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`replacement` and `evidence` are optional. Citations establish where a statement came from; they do not establish that a provider's reasoning is correct. Users review each suggestion before applying it. Applying a change disables suggestions from the previous document snapshot. Review again to work with current citations. Opening another file discards the current document, so download accepted changes first. The sample has no remote storage or autosave.
|
|
39
|
+
|
|
40
|
+
Run `npm run validate:report` for the browser journey. `-- --review` captures candidates when creating or intentionally revising the demonstration design. Inspect both candidates against the feature specification before manually accepting them as references. Never replace a reference merely to pass a comparison.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Application integration
|
|
2
|
+
|
|
3
|
+
The session accepts the editor's [document modes](document-modes.md). A mode change preserves session revision and dirty state; edits, review actions and filled answers use the same committed-change and autosave path.
|
|
4
|
+
|
|
5
|
+
`@onodocs/sdk/application` embeds the existing Word editor with application saving and recovery. Use matching SDK and Canvas packages built with these entry points. Older published packages do not include them.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import { openApplicationEditor } from "@onodocs/sdk/application";
|
|
9
|
+
import { recoveryStore } from "./recovery.js";
|
|
10
|
+
|
|
11
|
+
const application = await openApplicationEditor({
|
|
12
|
+
container,
|
|
13
|
+
input: await response.arrayBuffer(),
|
|
14
|
+
document: { licenseKey },
|
|
15
|
+
recovery: recoveryStore(`${userId}:${documentId}`),
|
|
16
|
+
restoreRecovery: userChoseRestore,
|
|
17
|
+
async persist(bytes, signal) {
|
|
18
|
+
const saved = await fetch(`/api/documents/${documentId}`, {
|
|
19
|
+
method: "PUT", credentials: "same-origin", signal,
|
|
20
|
+
headers: { "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "If-Match": etag },
|
|
21
|
+
body: bytes
|
|
22
|
+
});
|
|
23
|
+
if (!saved.ok) throw new Error(`Save failed (${saved.status})`);
|
|
24
|
+
etag = saved.headers.get("ETag");
|
|
25
|
+
},
|
|
26
|
+
onEvent(event) {
|
|
27
|
+
saveButton.disabled = event.state.saving;
|
|
28
|
+
if (event.type === "error") showError(event.error);
|
|
29
|
+
if (event.type === "saved") showSaved();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
saveButton.onclick = () => application.save().catch(showError);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The host owns authorization, ETags and conflict UI. A 409/412 should offer reload, save-as or explicit conflict resolution. `persist` must reject unsuccessful storage and honor its signal. `application.save()` resolves after storage acknowledges the bytes. The editor's Download Word control downloads locally; it does not persist remotely.
|
|
36
|
+
|
|
37
|
+
State exposes `dirty`, `saving` and `revision`. Events are `ready`, `change`, `saving`, `saved`, `error` and `disposed`; errors include `error`. Event callback exceptions are logged without rolling back changes. Edits during a save remain dirty until their own save succeeds. Saves serialize and failures remain retryable. Autosave debounces changes for 1000 ms; set `autosaveDelay: false` for manual saving or choose another nonnegative delay.
|
|
38
|
+
|
|
39
|
+
The optional recovery store implements asynchronous `read`, `write` and `remove`. `examples/application/recovery.js` uses IndexedDB. Scope its key to the authenticated user and document; clear drafts on logout where your privacy policy requires it. Ask before restoring a draft that may conflict with newer server content. `restoreRecovery: true` loads the draft and starts dirty. Recovery is written before remote saving and removed only after the current revision succeeds. A recovery write failure stops that save. Without a recovery store, remote saving still works.
|
|
40
|
+
|
|
41
|
+
Recovery checkpoints occur when saving starts. Abrupt termination can lose edits within the debounce window. Do not rely on asynchronous beforeunload saving. Warn while dirty and await save before application navigation. Disposal aborts work and retains the checkpoint; it does not flush. Create a new session when changing user or document identity. Use `application.editor.execute`, undo and redo for tracked edits. After a successful direct document mutation, call `application.markChanged()` to publish a change and schedule autosave. Failed mutations must not call it. Editor open/newDocument calls bypass session tracking. The default session toolbar omits open/new; keep that restriction when customizing it.
|
|
42
|
+
|
|
43
|
+
## Frontend frameworks
|
|
44
|
+
|
|
45
|
+
`examples/application/mount.ts` returns immediate cleanup while dynamically importing the browser component. Pass input, persistence, recovery and events as options. The existing [framework viewer projects](https://github.com/onodocs/onodocs/tree/main/examples/frameworks) supply application shells and bundler setup; the same mounting pattern supports editing. Copy the supplied Editor.jsx, Editor.vue, Editor.svelte or editor.component.ts adapter together with mount.ts. Use a framework key or remount the component when changing document identity/options. The blazor.js module returns a JS object handle exposing save and dispose; pass a DotNetObjectReference implementing OnEditorEvent. Its save endpoint must enforce your application authorization and CSRF policy. The editor owns the host element's contents; the framework owns surrounding controls.
|
|
46
|
+
|
|
47
|
+
| Framework | Lifecycle |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| React / Next.js | Use `examples/application/Editor.jsx` in a client component. Memoize options and onReady to avoid replacing sessions on ordinary renders. Effect cleanup handles Strict Mode and unmounting. |
|
|
50
|
+
| Vue / Nuxt | Call `stop = mountEditor(host.value, options)` in onMounted; call stop in onBeforeUnmount. |
|
|
51
|
+
| Svelte / SvelteKit | Return `mountEditor(host, options)` from onMount; bind the host with bind:this. |
|
|
52
|
+
| Angular | Mount in ngAfterViewInit and clean up in ngOnDestroy. Forward events through application change detection. |
|
|
53
|
+
| Razor Pages | Import from a module script after the host exists; clean up before replacing the page fragment. |
|
|
54
|
+
| Blazor | Import after first render. Retain a JavaScript handle to cleanup, invoke it from IAsyncDisposable, then dispose the module. Handle a disconnected circuit during cleanup. |
|
|
55
|
+
|
|
56
|
+
Abort source fetching when unmounting. Do not invoke mounting during SSR. The helper delays browser imports until mounting; direct callers can pass a signal to openApplicationEditor. Existing read-only viewers can keep using the SDK/Canvas mount helper.
|
|
57
|
+
|
|
58
|
+
## Self-hosted HTTP
|
|
59
|
+
|
|
60
|
+
`@onodocs/sdk/http` exports `createHttpService({ renderer, authorize, document?, onError? })`. It wraps the existing server renderer and backend-viewer manifest/page contract. Authorization returns a stable application-controlled principal string or undefined. Every request authenticates; each document belongs to its principal. Call service.handle from a Node HTTP listener. Stop requests, await service.dispose, then dispose the renderer. TypeScript backends should install @types/node and include node in their compiler types.
|
|
61
|
+
|
|
62
|
+
Run `npm run build`, set `ONODOCS_SERVICE_TOKEN`, then run `npm run example:http`. The sample in `examples/http-service/server.mjs` defaults to loopback port 5191 and Chrome. HOST, PORT, CHROMIUM_PATH and ONODOCS_LICENSE_KEY configure it. Keep its bearer credential on the backend. Browsers should use an application-owned authenticated gateway. For installed-package use, copy the sample and install matching SDK/Canvas releases containing these entry points.
|
|
63
|
+
|
|
64
|
+
| Request | Response |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| POST /documents with raw DOCX bytes | 201 JSON with id, url and pages, plus Location |
|
|
67
|
+
| GET /documents/:id | Canvas manifest JSON |
|
|
68
|
+
| GET /documents/:id/pages/:index | Page JSON, zero-based |
|
|
69
|
+
| GET /documents/:id/docx | Original DOCX |
|
|
70
|
+
| GET /documents/:id/pdf | Searchable PDF |
|
|
71
|
+
| DELETE /documents/:id | 204 after disposal |
|
|
72
|
+
|
|
73
|
+
Sessions are immutable and process-local. Upload edited bytes under a new URL and delete the old session. The application retains original and saved bytes in durable storage; restart discards HTTP sessions. Delete sessions in finally, including on export failure. Multiple instances require routing requests back to the owning process. Admission controls, deadlines and process supervision belong to deployment, without adding engine document-size or iteration rejection policies.
|
|
74
|
+
|
|
75
|
+
Forward the manifest and every page/export route through your gateway with authorization. Canvas can open the returned URL using `openDocument(url, { request: { credentials: "same-origin" }, container })`. Preserve the pages/:index path. This reuses the existing backend viewer without sending original Word files to its browser.
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
node examples/http-service/client.mjs input.docx output.pdf
|
|
79
|
+
python examples/http-service/client.py input.docx output.pdf
|
|
80
|
+
dotnet run --file examples/http-service/client.cs -- input.docx output.pdf
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
These clients upload, export and delete. They use ONODOCS_SERVICE_TOKEN and optional ONODOCS_SERVICE_URL. C# uses .NET 10 file-based execution. Other languages can use the same protocol with their standard HTTP clients. Node applications can also call createRenderer directly.
|
|
84
|
+
|
|
85
|
+
Deploy behind your application's TLS proxy on a private interface using an unprivileged account. Install Node, Chromium and the required fonts; keep browser sandbox support enabled. Use a supervisor that sends SIGTERM and permits cleanup. Inject secrets through its protected environment. Verify an authenticated upload/export/delete after startup to check browser and font availability. This work supplies deployment code and guidance without publishing or changing a production deployment.
|
|
86
|
+
|
|
87
|
+
Browser builds must deploy generated lazy PDF chunks alongside the entry bundle. Serve over HTTPS with a content security policy that permits required font/image sources and Blob/data resources. Authentication, durable storage, draft retention and backups remain application-owned.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Shared document sessions
|
|
2
|
+
|
|
3
|
+
Use `@onodocs/sdk/collaboration` for a customer-hosted shared document service and `@onodocs/sdk/collaboration/browser` for the browser editor. Collaboration is optional. Ordinary browser editing and rendering need neither entry point nor a network service.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createCollaborationService } from "@onodocs/sdk/collaboration";
|
|
7
|
+
|
|
8
|
+
const service = createCollaborationService({
|
|
9
|
+
authorize: async (documentId, requestContext) => {
|
|
10
|
+
const user = await authenticate(requestContext);
|
|
11
|
+
return await documentPermission(documentId, user);
|
|
12
|
+
},
|
|
13
|
+
storage: {
|
|
14
|
+
load: documentId => loadSharedRecord(documentId),
|
|
15
|
+
receipt: (documentId, userId, mutationId) => loadReceiptDigest(documentId, userId, mutationId),
|
|
16
|
+
compareExchange: (documentId, token, next, receipt) => commitSharedRecord(documentId, token, next, receipt),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Authorization returns `{id, name, role}` with role `edit`, `review` or `view`, or `undefined` to deny access. It runs on reads and writes, including immediately before committing. Review authors come from this identity. Reviewers can comment, reply, resolve/delete comments and accept/reject tracked revisions. They cannot replace document content or enable tracking. Customer authorization should decide which people receive each role.
|
|
22
|
+
|
|
23
|
+
The stored record contains `bytes`, an opaque `token`, a structural `checkpoint`, and no document history. Initialize both tokens with separate `crypto.randomUUID()` values. Store receipts separately with an index on document, user and mutation ID. `receipt` returns the stored digest or `undefined`. `compareExchange` must atomically commit the complete next record and receipt together only if the stored token equals the expected token, returning `false` otherwise. Do not persist document bytes separately from receipts. Retain receipts while clients may recover old drafts; removing one can cause an old retry to be mistaken for a new mutation. SQLite persistence in the sample demonstrates the contract. Multi-process stores need the same atomic condition.
|
|
24
|
+
|
|
25
|
+
Expose `service.exchange(documentId, requestBody, authenticatedContext)` through your application transport. The document ID and context come from your routing and authentication. Apply your existing origin/CSRF checks and access policy. Return the JSON reply; map `CollaborationAccessError` to HTTP 403. The provided browser transport sends same-origin authenticated POST requests and uses cancellation signals. Do not trust a browser-provided author, role or user ID as authentication.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import {
|
|
29
|
+
openCollaborativeEditor,
|
|
30
|
+
createCollaborationTransport,
|
|
31
|
+
createCollaborationRecovery,
|
|
32
|
+
} from "@onodocs/sdk/collaboration/browser";
|
|
33
|
+
|
|
34
|
+
const session = await openCollaborativeEditor({
|
|
35
|
+
container: document.querySelector("main")!,
|
|
36
|
+
exchange: createCollaborationTransport("/documents/brief/collaboration"),
|
|
37
|
+
recovery: createCollaborationRecovery(`${documentId}:${userId}:${tabId}`),
|
|
38
|
+
onError: error => showConnectionError(error),
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Keep `tabId` in session storage so reloads recover the same draft and independent tabs do not overwrite one another's recovery. The recovery API can use customer storage instead of IndexedDB. A failed recovery write is reported and leaves an in-memory draft available for download. Durable recovery requires successful storage writes. Dispose the session when closing the editor. Call `session.sync()` for an immediate reconnect attempt; regular polling runs automatically.
|
|
43
|
+
|
|
44
|
+
Edits to different unchanged paragraphs can merge. Changes to the same paragraph, stale formatting/structure and stale review actions require resolution. The editor retains the local DOCX and offers download, explicit discard for the shared copy, or an edit-only replacement of the observed shared copy. A replacement still requires an unchanged server token. Further edits made after an uncertain acknowledgment remain in the local draft and require resolution when the first request is recovered. This conservative behavior avoids guessing which text or formatting a user intended to replace.
|
|
45
|
+
|
|
46
|
+
Synchronization clears local snapshot undo history at each shared checkpoint. Otherwise undo could restore a document from before a colleague's changes. Presence lists names and roles and draws colored peer carets and text selections in the document. Names appear beside the selected line. Positions belong to a specific document token and disappear when stale, disconnected or viewing an uncommitted local draft. Fresh positions return after synchronization. Overlays follow scrolling, zoom and published clipping without changing your local selection. Presence expires after 15 seconds without contact. Permission changes update modes and preserve uncommitted drafts. Recovery can reopen a draft without a connection, but publishing always requires current server authorization.
|
|
47
|
+
|
|
48
|
+
Use the collaboration composition's editor for authoring. Direct mutations through `editor.document` bypass its journal and are trusted host operations. Opening another document through that editor is also a host lifecycle operation, not a shared replacement; close the session and open the other document's session instead. `WordEditor.synchronize` runs a host callback within the editor queue. Return `{bytes, allowedModes}` to publish a new checkpoint. Never call another queued editor operation from that callback or from an awaited `onChange`; use the underlying document's `save()` when capturing bytes.
|
|
49
|
+
|
|
50
|
+
Run `npm run example:collaboration` after building. Open `http://127.0.0.1:5195/?user=alex` and `?user=sam` in separate browser profiles, or `?user=jo` for review-only access. The sample uses fixed demo identities, loopback HTTP and a local SQLite database under the user's temporary OnoDocs directory. Replace its identity picker with customer authentication for production. Run `npm run validate:collaboration` for the independent browser feature suite.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Document forms
|
|
2
|
+
|
|
3
|
+
Use `@onodocs/sdk/forms` to collect typed answers in tagged Word content controls. The template stays a DOCX file. Store its `FormDefinition` JSON beside it and pass both to the filling application. The definition identifies editable regions and supplies validation rules.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { openDocument } from "@onodocs/sdk";
|
|
7
|
+
import { createForm } from "@onodocs/sdk/forms";
|
|
8
|
+
|
|
9
|
+
const document = await openDocument(templateBytes);
|
|
10
|
+
try {
|
|
11
|
+
const form = createForm(document, {
|
|
12
|
+
title: "Purchase request",
|
|
13
|
+
fields: [
|
|
14
|
+
{ id: "name", tag: "request.name", label: "Full name", type: "text", required: true },
|
|
15
|
+
{ id: "quantity", tag: "request.quantity", label: "Quantity", type: "number", min: 1, max: 10 }
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
await form.fill({ name: "Alex Morgan", quantity: 2 });
|
|
19
|
+
const { bytes, answers } = await form.complete();
|
|
20
|
+
await storeCompletedRequest(bytes, answers);
|
|
21
|
+
} finally { document.dispose(); }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`fill` accepts partial answers and validates the whole supplied batch before committing it. Unknown fields and changes to read-only fields fail. Required values may stay empty in a draft. `complete` requires all required answers, saves the document and returns the answers from that same snapshot. `FormValidationError.issues` contains field IDs and messages. `answers()` reads current document values; `validate()` reports completion issues without saving. Both asynchronous methods accept `{ signal }`.
|
|
25
|
+
|
|
26
|
+
| Type | Answer and rules |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| `text` | Single-line string; optional `maxLength` counts Unicode code points. |
|
|
29
|
+
| `multiline` | String with line breaks; optional `maxLength`. |
|
|
30
|
+
| `email` | Single-line string with a basic address syntax check. |
|
|
31
|
+
| `number` | Finite number; optional inclusive `min` and `max`. |
|
|
32
|
+
| `date` | Valid calendar date as `YYYY-MM-DD`. |
|
|
33
|
+
| `choice` | One exact string from `choices`. |
|
|
34
|
+
| `checkbox` | Boolean, stored in Word as Yes or No. A required checkbox must be true. |
|
|
35
|
+
|
|
36
|
+
Blank answers are `null`. Each field requires a distinct `id` and `tag`, a `label` and a `type`. Optional `help` appears below its input. `readOnly` includes an existing value in answers while preventing changes through the form. Repeated document controls with the same tag receive the same answer; inconsistent repeated values prevent completion. Missing tags prevent completion. Regions must support the SDK's existing text updates; bound controls, locked content and complex structures may be unsuitable.
|
|
37
|
+
|
|
38
|
+
## Browser filling and authoring
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import { openForm, createFormDesigner } from "@onodocs/sdk/forms/browser";
|
|
42
|
+
|
|
43
|
+
const view = await openForm({
|
|
44
|
+
container, input: templateBytes, definition,
|
|
45
|
+
persist: bytes => saveDraft(bytes),
|
|
46
|
+
onComplete: ({ bytes, answers }) => submitRequest(bytes, answers)
|
|
47
|
+
});
|
|
48
|
+
// Before leaving the form:
|
|
49
|
+
await view.save();
|
|
50
|
+
view.dispose();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`openForm` displays the document beside labelled inputs. The document preview is read-only. Focusing an input selects its region in the document. Valid changed values update the preview and use the [application persistence and recovery](application-integration.md) callbacks. `view.save()` flushes pending inputs and saves a draft without requiring completion. `view.complete()` flushes, validates, persists and invokes `onComplete`. Dispose the view on unmount. Scope draft storage to the user, template and definition revision, and ask before restoring stale drafts.
|
|
54
|
+
|
|
55
|
+
`createFormDesigner({ container })` provides an editable document and field sidebar. Call `designer.open(bytes, definition)` to load a template, or omit the definition to begin one. Select a paragraph, enter the field details, then add the field. The default inserts a new answer paragraph after the selection. Clear the insert option to wrap the selected ordinary paragraph while preserving its formatting. Existing tags can be bound by entering their Word tag. Select a field in the sidebar to edit its rules. Native property editing supports a single block text control per tag. Remove deletes its form rule and leaves its document content in place. `designer.save()` returns `{ bytes, definition }`; save both. The editor's undo history applies to document edits, so restore an undone region or remove its rule before saving.
|
|
56
|
+
|
|
57
|
+
The designer writes native Word content controls with a protected container and editable contents. These controls remain readable in Word. The form's type rules and read-only policy belong to the host definition. They are not document encryption or a tamper-proof restriction in external editors. Applications accepting submissions should use their trusted definition and enforce their own authorization and validation.
|
|
58
|
+
|
|
59
|
+
Run `npm run build` followed by `npm run example:forms` for the equipment-request example at localhost port 5192. It supports template and definition downloads, browser-local drafts, and completed DOCX plus answers JSON downloads. Installed packages include `examples/document-forms` and its shared `examples/application/recovery.js`; copy both directories, install `esbuild` and matching SDK/Canvas packages, and run `node examples/document-forms/serve.mjs`. No hosted service is required.
|
|
60
|
+
|
|
61
|
+
## Try the equipment-request workflow
|
|
62
|
+
|
|
63
|
+
Run `npm run build` and `npm run example:forms`, then open `http://127.0.0.1:5192`. The sample starts in filling mode for the fictional Northwind Operations team. It uses the same equipment-request template as the API and LibreOffice fixture checks.
|
|
64
|
+
|
|
65
|
+
1. Leave the form empty and choose **Complete form** to see the required fields. Enter an invalid email or quantity above 10 to see field-specific feedback. Invalid input stays visible for correction and does not change the Word document.
|
|
66
|
+
2. Enter Alex Morgan, `alex@example.com`, quantity 2, required date 2026-11-01 and Monitor. Add delivery notes and confirm the request. The request number and policy text remain read-only in the filling interface.
|
|
67
|
+
3. Choose **Save draft**, reload the page and choose **Restore draft**. You can also download a Word draft and reopen it with **Open Word draft**. Incomplete required fields are allowed in drafts; entered values must satisfy their type and range rules.
|
|
68
|
+
4. Choose **Complete form**, then download the completed Word document and answers JSON individually. Both files come from the same completion result. Changing any input hides those download buttons until you complete the form again. The sample never submits the request to a company service.
|
|
69
|
+
5. Use **Start new request** to remove this form's browser draft and restore the template's starting values. The confirmation lets you cancel without losing the current request.
|
|
70
|
+
|
|
71
|
+
Drafts use the shared IndexedDB recovery example. The storage key includes a digest of the template bytes and field definition, so different templates or rules cannot silently restore each other's drafts. This sample stores one request per template in the current browser. It has no accounts or cross-device storage. Storage failures retain the visible answers and report the failure; allow storage and retry saving before closing the page. Use your application's user/request identity, authorization and persistence service in a production integration.
|
|
72
|
+
|
|
73
|
+
Open **Developer tools & template authoring** to edit the template, import a Word document and matching definition, or download both source files. Authoring intentionally permits document changes; the filling preview only permits the defined answers. Field changes create a separate draft identity. This panel is an evaluation tool, not a user authorization boundary.
|
|
74
|
+
|
|
75
|
+
To run the shipped example directly from an installed SDK:
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
npm install @onodocs/sdk esbuild
|
|
79
|
+
node node_modules/@onodocs/sdk/examples/document-forms/serve.mjs
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Evaluation mode adds a watermark. For licensed use, provide the license key in the `document` options passed to both `openForm` and `createFormDesigner`, following the SDK licensing guide.
|
|
83
|
+
|
|
84
|
+
Run `npm run validate:forms` for the business-user and authoring journeys, including draft recovery, invalid/corrected input, storage failure/retry, document preservation, matching exports and reviewed desktop/mobile screenshots. Add `-- --word` to check reopening in installed Word. Core rendering validation remains a separate command.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Document modes
|
|
2
|
+
|
|
3
|
+
Configure `createEditor` or `openApplicationEditor` with `mode` and `allowedModes`. The toolbar shows a mode selector when you explicitly provide more than one allowed mode. All modes use the same open document. Switching preserves edits, selection, pending formatting and undo history.
|
|
4
|
+
|
|
5
|
+
| Mode | Available actions |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `view` | Select, copy, find and export. Read review information and compare documents when review is configured. |
|
|
8
|
+
| `edit` | Type, format, author document structure, replace text, undo and redo. Configured review actions include tracked editing and version restoration. |
|
|
9
|
+
| `review` | Add comments and replies, resolve or delete threads, accept or reject revisions, and undo review actions. Document text, formatting and structure cannot be edited in this mode. |
|
|
10
|
+
| `form` | Fill declared answer fields, validate and complete the form, and undo form changes. The surrounding document cannot be edited through the editor. |
|
|
11
|
+
|
|
12
|
+
Save and PDF export are available in every mode. Review mode requires `review` options, including the host's reviewer identity callback. Form mode requires a `FormDefinition`. [Document forms](document-forms.md) describes field rules and answer types. [Application integration](application-integration.md) describes saving, recovery and host lifecycle.
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { openApplicationEditor } from "@onodocs/sdk/application";
|
|
16
|
+
|
|
17
|
+
const application = await openApplicationEditor({
|
|
18
|
+
container, input: bytes,
|
|
19
|
+
mode: "view",
|
|
20
|
+
allowedModes: ["view", "edit", "review", "form"],
|
|
21
|
+
form: definition,
|
|
22
|
+
review: { identity: () => currentReviewer },
|
|
23
|
+
persist: (bytes, signal) => saveDraft(bytes, signal),
|
|
24
|
+
onModeChange: mode => updateModeIndicator(mode),
|
|
25
|
+
onFormComplete: async ({ bytes, answers }) => submitCompletedForm(bytes, answers)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
await application.editor.setMode("form");
|
|
29
|
+
await application.editor.fill({ name: "Alex Morgan" });
|
|
30
|
+
const { bytes: completed, answers } = await application.editor.completeForm();
|
|
31
|
+
await application.save();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`setMode` rejects modes outside `allowedModes`. The selector, keyboard input and editor methods use the same mode restrictions. Custom toolbar commands run only in editing mode unless their descriptor explicitly supplies `modes`, for example `{ id: "inspect", label: "Inspect", modes: ["view", "edit"], execute }`. The host remains responsible for the callback's actions.
|
|
35
|
+
|
|
36
|
+
When omitted, the initial mode is editing and the available modes are viewing, editing and any configured review/form modes. `readOnly: true` fixes the session to viewing. You can call `setMode` without showing a selector, or use `toolbar: false` with application-owned controls. The current `mode` and `allowedModes` are readable from the editor. A mode change emits `onModeChange`; it does not mark the document dirty.
|
|
37
|
+
|
|
38
|
+
Mode transitions wait for queued document edits and flush form inputs. Invalid form inputs keep the current mode and remain visible for correction. Required fields may remain empty in a draft; completion checks them. Finish an active IME composition before switching. Review comment and reply drafts remain in the session across mode changes. They are not part of exported DOCX until submitted. Form `save`, PDF export and `completeForm` also flush pending field inputs. `answers()` returns committed document values. The form panel's Complete form button invokes `onFormComplete`; a direct `completeForm()` call returns its result to the caller.
|
|
39
|
+
|
|
40
|
+
History is retained across modes. Viewing cannot undo or redo. Form and review modes can undo only their own most recent operations, so a form user cannot use Undo to remove an earlier general edit. Editing mode can undo all document operations. Mode changes do not reopen the document or create history entries. Opening another file is an explicit document replacement and clears editor history.
|
|
41
|
+
|
|
42
|
+
Derive the allowed modes from your application's permissions. This is an interface and editor-operation policy, not application authorization, file encryption or a restriction on external Word editors. The low-level `editor.document` API remains available to trusted application code and is not filtered by editor mode. The host must authorize storage, review identity, history and submissions. If permissions change, preserve permitted draft work and create a session with the new allowed modes; merely switching to viewing does not revoke modes already offered by that session.
|
|
43
|
+
|
|
44
|
+
Run `npm run build` and `npm run example:modes` for the local four-mode request at port 5196. Its Save draft stores bytes only in memory for the current session. The installed SDK includes `examples/document-modes` and its shared `document-forms` server/template files. Copy both directories, install matching SDK/Canvas packages and `esbuild`, then run `node examples/document-modes/serve.mjs`.
|