diff --git a/.changeset/batch-set-field-value.md b/.changeset/batch-set-field-value.md new file mode 100644 index 00000000..187d397c --- /dev/null +++ b/.changeset/batch-set-field-value.md @@ -0,0 +1,11 @@ +--- +"@simplepdf/embed": minor +"@simplepdf/react-embed-pdf": minor +"@simplepdf/web-embed-pdf": minor +--- + +**Fill several fields in one call: `batchSetFieldValues` sets every value at once, or none of them.** +- `batchSetFieldValues({ updates: [{ fieldId, value }] })` applies every update in one step, reverted by a single undo in the editor, and resolves with no data. +- If any update is rejected (an unknown field, an invalid option, a read-only field, an unreadable signature URL, the same field twice), nothing changes: the result fails with `bad_request:batch_validation_failed` and `error.details.failures` lists every rejected update with its `fieldId`, `code` and `message` (typed as `BatchValidationFailedDetails`). +- Values follow the `setFieldValue` rules and are never typed out. +- Available everywhere the editor operations are: `embed.actions.batchSetFieldValues`, `useEmbed().actions.batchSetFieldValues`, the `batchSetFieldValues` agentic tool on every tool subpath, and the `simplepdf_embed_batch_set_field_values` WebMCP tool, including on pages using `@simplepdf/web-embed-pdf`. diff --git a/.changeset/leaner-agent-requests.md b/.changeset/leaner-agent-requests.md new file mode 100644 index 00000000..f2fa4b38 --- /dev/null +++ b/.changeset/leaner-agent-requests.md @@ -0,0 +1,12 @@ +--- +"@simplepdf/embed": minor +"@simplepdf/react-embed-pdf": patch +"@simplepdf/web-embed-pdf": patch +--- + +**Smaller installs, leaner agent requests, readable stack traces.** +- The agentic tools no longer repeat each tool's description inside its input schema: with the shorter descriptions below, a model request carrying all the AI SDK or TanStack AI tools sends about 3 KB less (24%) even with the new `batchSetFieldValues` tool, and the agentic entry points are 28 to 29% smaller. +- Tool descriptions are about 24% shorter (900 fewer characters across the 16 tools of the previous release): they state behavior and leave the details to each parameter, permission notes are gone (a refused call answers with its error code), and they name no tool or key whose spelling differs between surfaces, so they read true whether your agent sees camelCase (AI SDK, TanStack AI) or snake_case (WebMCP). The signature `value` documents that plain text is drawn as a typed signature. `getAnnotatedPage` states that the WebMCP tool returns the render as an image block and the rest of the result as a text block. +- Smaller installs: no source maps, so `@simplepdf/embed` unpacks to 248 KB instead of 596 KB and `@simplepdf/react-embed-pdf` to 74 KB instead of 110 KB. `@simplepdf/react-embed-pdf` is no longer minified, so an error inside it shows readable code in your devtools and error tracker; your bundler still minifies your app. +- `@simplepdf/web-embed-pdf` drops its `browser` field, so bundlers resolve the readable `dist/index.js`; the script-tag build on unpkg stays minified. +- The `/schemas` input schemas no longer carry the operation description at their root; read it from `SIMPLEPDF_TOOLS[name].description`, or from `OPERATIONS` in `@simplepdf/embed/protocol` for every operation including `loadDocument`. diff --git a/embed/README.md b/embed/README.md index 4787cbf9..462a3664 100644 --- a/embed/README.md +++ b/embed/README.md @@ -184,7 +184,7 @@ await embed.actions.rotatePage({ page: 1 }) await embed.actions.download() ``` -Full set: `createField`, `deleteFields`, `deletePages`, `detectFields`, `download`, `focusField`, `getAnnotatedPage`, `getDocumentContent`, `getFields`, `goTo`, `loadDocument`, `movePage`, `rotatePage`, `selectTool`, `setFieldValue`, `submit`. +Full set: `batchSetFieldValues`, `createField`, `deleteFields`, `deletePages`, `detectFields`, `download`, `focusField`, `getAnnotatedPage`, `getDocumentContent`, `getFields`, `goTo`, `loadDocument`, `movePage`, `rotatePage`, `selectTool`, `setFieldValue`, `submit`. **"Fill and read this document for me"** is just these operations in sequence, exactly what the agentic tools expose to a model: diff --git a/embed/embed-api.json b/embed/embed-api.json index 15ea6a49..41bc6286 100644 --- a/embed/embed-api.json +++ b/embed/embed-api.json @@ -1,5 +1,5 @@ { - "editor_version": "65235d76-20261009T151312Z", + "editor_version": "7845f7c3-20261014T100758Z", "description": "This is the SimplePDF editor interface contract. Drive the editor programmatically over window.postMessage with the editor iframe: post a JSON string { \"type\": {{operation request_type}}, \"request_id\": {{a request correlation id you generate}}, \"data\": {{input matching input_schema}} } to the iframe; the editor replies with { \"type\": \"REQUEST_RESULT\", \"data\": { \"request_id\": {{the same correlation id}}, \"result\": {{result}} } } (match each reply to its request by this id), where result is { \"success\": true, \"data\": {{a value matching the operation's output_schema; null for ops that return nothing}} } or { \"success\": false, \"error\": { code, message } }. Each operation lists the op-specific `error_codes` it can return; on top of those, any op may also fail with a gateway/permission code (origin/plan/signup gating, editor-not-ready, or an internal error). Every code is within `editor_error_schema` (the complete closed union to narrow against), where each code const carries a `description` of its meaning. Outbound events (see `events`) are pushed the same way. `operations` lists every operation the editor supports. Most require the embedding origin to be allowlisted (\"whitelisted\") for the tenant in the SimplePDF admin dashboard, and the tenant plan to permit them (LOAD_DOCUMENT is always available). A call the current setup does not permit returns the matching gateway code: forbidden:origin_not_whitelisted when the origin is not allowlisted, or bad_request:plan_upgrade_required when the plan excludes it. All JSON schemas use the json_schema_dialect declared at the root. We recommend the @simplepdf/embed package for a typed, ergonomic wrapper over this contract (https://github.com/SimplePDF/simplepdf-embed).", "json_schema_dialect": "https://json-schema.org/draft/2020-12/schema", "protocol": { @@ -57,7 +57,7 @@ "const": false }, "error": { - "description": "The error object when success is false: a `code` plus a human-readable `message` (with typed `details` for bad_request:missing_required_fields). Its full shape and code set are editor_error_schema, where each code const carries a description of its meaning." + "description": "The error object when success is false: a `code` plus a human-readable `message` (with typed `details` for bad_request:batch_validation_failed and bad_request:missing_required_fields). Its full shape and code set are editor_error_schema, where each code const carries a description of its meaning." } }, "required": ["success", "error"] @@ -76,9 +76,92 @@ } }, "operations": [ + { + "request_type": "BATCH_SET_FIELD_VALUES", + "description": "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data.", + "input_schema": { + "type": "object", + "properties": { + "updates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field_id": { + "type": "string", + "description": "ID of the field to update." + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature." + } + }, + "required": ["field_id", "value"] + }, + "description": "The fields to set, at least one, each field at most once. Fields not listed keep their value." + } + }, + "required": ["updates"], + "description": "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data." + }, + "output_schema": { + "type": "null" + }, + "error_codes": [ + "bad_request:batch_validation_failed", + "bad_request:invalid_value", + "bad_request:no_document_loaded" + ], + "tool": { + "name": "simplepdf_embed_batch_set_field_values", + "description": "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data.", + "input_schema": { + "type": "object", + "properties": { + "updates": { + "items": { + "type": "object", + "properties": { + "field_id": { + "type": "string", + "description": "ID of the field to update." + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature." + } + }, + "required": ["field_id", "value"] + }, + "description": "The fields to set, at least one, each field at most once. Fields not listed keep their value.", + "type": "array" + } + }, + "required": ["updates"] + }, + "annotations": { + "destructiveHint": false, + "openWorldHint": true + } + } + }, { "request_type": "CREATE_FIELD", - "description": "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.", + "description": "Create a new overlay field on a page. Returns the id of the created field.", "input_schema": { "type": "object", "properties": { @@ -108,12 +191,12 @@ "description": "1-based page to place the field on." }, "value": { - "description": "Optional initial value. A string for text/checkbox fields, or a data URL or http(s) URL (fetched by the editor) for signature/picture fields.", + "description": "Optional initial value. A string for text/checkbox fields; a data URL or http(s) URL (fetched by the editor) for signature/picture fields, or plain text for a signature, drawn as a typed signature.", "type": "string" } }, "required": ["type", "x", "y", "width", "height", "page"], - "description": "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled." + "description": "Create a new overlay field on a page. Returns the id of the created field." }, "output_schema": { "type": "object", @@ -137,7 +220,7 @@ ], "tool": { "name": "simplepdf_embed_create_field", - "description": "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.", + "description": "Create a new overlay field on a page. Returns the id of the created field.", "input_schema": { "type": "object", "properties": { @@ -167,7 +250,7 @@ "type": "integer" }, "value": { - "description": "Optional initial value. A string for text/checkbox fields, or a data URL or http(s) URL (fetched by the editor) for signature/picture fields.", + "description": "Optional initial value. A string for text/checkbox fields; a data URL or http(s) URL (fetched by the editor) for signature/picture fields, or plain text for a signature, drawn as a typed signature.", "type": "string" } }, @@ -181,7 +264,7 @@ }, { "request_type": "DELETE_FIELDS", - "description": "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.", + "description": "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive.", "input_schema": { "type": "object", "properties": { @@ -197,7 +280,7 @@ "type": "integer" } }, - "description": "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled." + "description": "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive." }, "output_schema": { "type": "object", @@ -218,7 +301,7 @@ ], "tool": { "name": "simplepdf_embed_delete_fields", - "description": "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.", + "description": "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive.", "input_schema": { "type": "object", "properties": { @@ -242,7 +325,7 @@ }, { "request_type": "DELETE_PAGES", - "description": "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.", + "description": "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { @@ -255,7 +338,7 @@ } }, "required": ["pages"], - "description": "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled." + "description": "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive." }, "output_schema": { "type": "null" @@ -269,7 +352,7 @@ ], "tool": { "name": "simplepdf_embed_delete_pages", - "description": "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.", + "description": "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { @@ -290,11 +373,11 @@ }, { "request_type": "DETECT_FIELDS", - "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.", + "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected.", "input_schema": { "type": "object", "properties": {}, - "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled." + "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected." }, "output_schema": { "type": "object", @@ -305,10 +388,13 @@ }, "required": ["detected_count"] }, - "error_codes": ["forbidden:editing_not_allowed", "bad_request:no_document_loaded"], + "error_codes": [ + "forbidden:editing_not_allowed", + "bad_request:no_document_loaded" + ], "tool": { "name": "simplepdf_embed_detect_fields", - "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.", + "description": "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected.", "input_schema": { "type": "object", "properties": {} @@ -348,7 +434,7 @@ }, { "request_type": "FOCUS_FIELD", - "description": "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.", + "description": "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next.", "input_schema": { "type": "object", "properties": { @@ -358,7 +444,7 @@ } }, "required": ["field_id"], - "description": "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next." + "description": "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next." }, "output_schema": { "type": "object", @@ -379,10 +465,14 @@ }, "required": ["hint"] }, - "error_codes": ["bad_request:invalid_value", "bad_request:no_document_loaded", "bad_request:field_not_found"], + "error_codes": [ + "bad_request:invalid_value", + "bad_request:no_document_loaded", + "bad_request:field_not_found" + ], "tool": { "name": "simplepdf_embed_focus_field", - "description": "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.", + "description": "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next.", "input_schema": { "type": "object", "properties": { @@ -400,7 +490,7 @@ }, { "request_type": "GET_ANNOTATED_PAGE", - "description": "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.", + "description": "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block.", "input_schema": { "type": "object", "properties": { @@ -410,7 +500,7 @@ } }, "required": ["page"], - "description": "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant." + "description": "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block." }, "output_schema": { "type": "object", @@ -439,12 +529,22 @@ "description": "Badge number (as drawn on the render) to field_id, for every field on the page." } }, - "required": ["page", "image_data_url", "image_width", "image_height", "badges"] + "required": [ + "page", + "image_data_url", + "image_width", + "image_height", + "badges" + ] }, - "error_codes": ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"], + "error_codes": [ + "bad_request:no_document_loaded", + "bad_request:invalid_page", + "bad_request:page_out_of_range" + ], "tool": { "name": "simplepdf_embed_get_annotated_page", - "description": "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.", + "description": "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block.", "input_schema": { "type": "object", "properties": { @@ -463,7 +563,7 @@ }, { "request_type": "GET_DOCUMENT_CONTENT", - "description": "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.", + "description": "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content.", "input_schema": { "type": "object", "properties": { @@ -473,7 +573,7 @@ "enum": ["auto", "ocr"] } }, - "description": "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }." + "description": "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content." }, "output_schema": { "type": "object", @@ -499,10 +599,13 @@ }, "required": ["name", "pages"] }, - "error_codes": ["bad_request:invalid_value", "bad_request:no_document_loaded"], + "error_codes": [ + "bad_request:invalid_value", + "bad_request:no_document_loaded" + ], "tool": { "name": "simplepdf_embed_get_document_content", - "description": "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.", + "description": "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content.", "input_schema": { "type": "object", "properties": { @@ -521,11 +624,11 @@ }, { "request_type": "GET_FIELDS", - "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.", + "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list.", "input_schema": { "type": "object", "properties": {}, - "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }." + "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list." }, "output_schema": { "type": "object", @@ -550,7 +653,15 @@ }, "type": { "type": "string", - "enum": ["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT", "DROPDOWN", "RADIO"] + "enum": [ + "TEXT", + "SIGNATURE", + "PICTURE", + "CHECKBOX", + "COMB_TEXT", + "DROPDOWN", + "RADIO" + ] }, "page": { "type": "integer", @@ -582,7 +693,14 @@ "description": "Valid values for this field (a DROPDOWN/RADIO's choices), or null. When set, a value set on or created for the field must be one of these." } }, - "required": ["field_id", "name", "type", "page", "value", "options"] + "required": [ + "field_id", + "name", + "type", + "page", + "value", + "options" + ] } } }, @@ -591,7 +709,7 @@ "error_codes": ["bad_request:no_document_loaded"], "tool": { "name": "simplepdf_embed_get_fields", - "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.", + "description": "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list.", "input_schema": { "type": "object", "properties": {} @@ -604,7 +722,7 @@ }, { "request_type": "GO_TO", - "description": "Scroll the editor to a specific 1-based page. Returns no data.", + "description": "Scroll the editor to a page. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -614,15 +732,19 @@ } }, "required": ["page"], - "description": "Scroll the editor to a specific 1-based page. Returns no data." + "description": "Scroll the editor to a page. Returns no data." }, "output_schema": { "type": "null" }, - "error_codes": ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"], + "error_codes": [ + "bad_request:no_document_loaded", + "bad_request:invalid_page", + "bad_request:page_out_of_range" + ], "tool": { "name": "simplepdf_embed_go_to", - "description": "Scroll the editor to a specific 1-based page. Returns no data.", + "description": "Scroll the editor to a page. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -663,7 +785,11 @@ "output_schema": { "type": "null" }, - "error_codes": ["bad_request:failed_to_load_document", "bad_request:invalid_value", "bad_request:invalid_page"], + "error_codes": [ + "bad_request:failed_to_load_document", + "bad_request:invalid_value", + "bad_request:invalid_page" + ], "tool": { "name": "simplepdf_embed_load_document", "description": "Replace the document in the editor with one supplied as a base64 data URL or an http(s) URL the editor fetches. Destructive: the current document and every edit in it are discarded. Replies once the document and its fields are ready, so the next operation can act on it, and DOCUMENT_LOADED follows the reply; the URL already open is left as is and answers once it settles; a document that cannot be loaded or prepared answers bad_request:failed_to_load_document. Returns no data.", @@ -693,7 +819,7 @@ }, { "request_type": "MOVE_PAGE", - "description": "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.", + "description": "Move a page to another position, reordering the document. Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { @@ -707,7 +833,7 @@ } }, "required": ["from_page", "to_page"], - "description": "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled." + "description": "Move a page to another position, reordering the document. Returns no data. Destructive." }, "output_schema": { "type": "null" @@ -721,7 +847,7 @@ ], "tool": { "name": "simplepdf_embed_move_page", - "description": "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.", + "description": "Move a page to another position, reordering the document. Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { @@ -743,17 +869,17 @@ }, { "request_type": "ROTATE_PAGE", - "description": "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.", + "description": "Rotate a page 90 degrees clockwise. Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { "page": { "type": "integer", - "description": "1-based page to rotate 90 degrees clockwise." + "description": "1-based page to rotate." } }, "required": ["page"], - "description": "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled." + "description": "Rotate a page 90 degrees clockwise. Returns no data. Destructive." }, "output_schema": { "type": "null" @@ -767,12 +893,12 @@ ], "tool": { "name": "simplepdf_embed_rotate_page", - "description": "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.", + "description": "Rotate a page 90 degrees clockwise. Returns no data. Destructive.", "input_schema": { "type": "object", "properties": { "page": { - "description": "1-based page to rotate 90 degrees clockwise.", + "description": "1-based page to rotate.", "type": "integer" } }, @@ -793,7 +919,13 @@ "anyOf": [ { "type": "string", - "enum": ["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT"] + "enum": [ + "TEXT", + "SIGNATURE", + "PICTURE", + "CHECKBOX", + "COMB_TEXT" + ] }, { "type": "null" @@ -808,7 +940,10 @@ "output_schema": { "type": "null" }, - "error_codes": ["bad_request:no_document_loaded", "bad_request:invalid_tool"], + "error_codes": [ + "bad_request:no_document_loaded", + "bad_request:invalid_tool" + ], "tool": { "name": "simplepdf_embed_select_tool", "description": "Activate a field-placement tool in the editor toolbar so the user can draw that field type, or pass null to clear the active tool. Returns no data.", @@ -819,7 +954,13 @@ "anyOf": [ { "type": "string", - "enum": ["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT"] + "enum": [ + "TEXT", + "SIGNATURE", + "PICTURE", + "CHECKBOX", + "COMB_TEXT" + ] }, { "type": "null" @@ -837,7 +978,7 @@ }, { "request_type": "SET_FIELD_VALUE", - "description": "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.", + "description": "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -858,25 +999,25 @@ "type": "null" } ], - "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox) or a data URL or http(s) URL, fetched by the editor (signature/picture)." + "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature." } }, "required": ["field_id", "value"], - "description": "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data." + "description": "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data." }, "output_schema": { "type": "null" }, "error_codes": [ - "bad_request:invalid_value", + "bad_request:field_not_found", "bad_request:invalid_signature_url", - "bad_request:no_document_loaded", + "bad_request:invalid_value", "bad_request:read_only", - "bad_request:field_not_found" + "bad_request:no_document_loaded" ], "tool": { "name": "simplepdf_embed_set_field_value", - "description": "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.", + "description": "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -897,7 +1038,7 @@ "type": "null" } ], - "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox) or a data URL or http(s) URL, fetched by the editor (signature/picture)." + "description": "New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature." } }, "required": ["field_id", "value"] @@ -910,7 +1051,7 @@ }, { "request_type": "SUBMIT", - "description": "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.", + "description": "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -920,7 +1061,7 @@ } }, "required": ["download_copy"], - "description": "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data." + "description": "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data." }, "output_schema": { "type": "null" @@ -932,7 +1073,7 @@ ], "tool": { "name": "simplepdf_embed_submit", - "description": "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.", + "description": "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data.", "input_schema": { "type": "object", "properties": { @@ -1017,6 +1158,65 @@ "locales": ["fr", "en", "it", "de", "pt", "es", "ja", "nl"], "editor_error_schema": { "anyOf": [ + { + "type": "object", + "properties": { + "code": { + "type": "string", + "const": "bad_request:batch_validation_failed", + "description": "At least one update of batch_set_field_values was rejected, so no field changed. details.failures lists every rejected update with its field_id and the set_field_value code and message explaining why." + }, + "message": { + "type": "string" + }, + "details": { + "type": "object", + "properties": { + "failures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field_id": { + "type": "string" + }, + "code": { + "anyOf": [ + { + "type": "string", + "const": "bad_request:field_not_found", + "description": "No field on the loaded document matches the provided field_id." + }, + { + "type": "string", + "const": "bad_request:invalid_signature_url", + "description": "The signature value is not a valid, reachable image URL." + }, + { + "type": "string", + "const": "bad_request:invalid_value", + "description": "The provided value is missing or not valid for this operation or field type." + }, + { + "type": "string", + "const": "bad_request:read_only", + "description": "The document or field is read-only and cannot be modified." + } + ] + }, + "message": { + "type": "string" + } + }, + "required": ["field_id", "code", "message"] + } + } + }, + "required": ["failures"] + } + }, + "required": ["code", "message", "details"] + }, { "type": "object", "properties": { diff --git a/embed/etc/index.api.md b/embed/etc/index.api.md index 0d7c1df9..3b7195b0 100644 --- a/embed/etc/index.api.md +++ b/embed/etc/index.api.md @@ -5,12 +5,28 @@ ```ts // @public (undocumented) -export type BridgeError = { - code: 'bad_request:missing_required_fields'; - message: string; - details: MissingRequiredFieldsDetails; -} | { - code: Exclude; +export type BatchSetFieldValuesInput = { + updates: Array<{ + fieldId: string; + value: string | null; + }>; +}; + +// @public (undocumented) +export type BatchValidationFailedDetails = { + failures: Array<{ + fieldId: string; + code: "bad_request:field_not_found" | "bad_request:invalid_signature_url" | "bad_request:invalid_value" | "bad_request:read_only"; + message: string; + }>; +}; + +// Warning: (ae-forgotten-export) The symbol "DetailedBridgeError" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "EditorErrorDetailsByCode" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type BridgeError = DetailedBridgeError | { + code: Exclude; message: string; }; @@ -264,6 +280,7 @@ export type GoToInput = { // @public (undocumented) export type IframeActions = { + batchSetFieldValues: (input: BatchSetFieldValuesInput) => Promise; createField: (input: CreateFieldInput) => Promise>; deleteFields: (input?: DeleteFieldsInput) => Promise>; deletePages: (input: DeletePagesInput) => Promise; diff --git a/embed/etc/protocol.api.md b/embed/etc/protocol.api.md index b4e9d971..46a405dd 100644 --- a/embed/etc/protocol.api.md +++ b/embed/etc/protocol.api.md @@ -5,7 +5,7 @@ ```ts // @public (undocumented) -export const EDITOR_ERROR_CODES: readonly ["bad_request:download_blocked", "bad_request:editor_not_ready", "bad_request:event_not_allowed", "bad_request:failed_to_load_document", "bad_request:field_not_found", "bad_request:invalid_dimensions", "bad_request:invalid_event_type", "bad_request:invalid_field_ids", "bad_request:invalid_field_type", "bad_request:invalid_page", "bad_request:invalid_signature_url", "bad_request:invalid_tool", "bad_request:invalid_value", "bad_request:missing_required_fields", "bad_request:no_document_loaded", "bad_request:page_not_found", "bad_request:page_out_of_range", "bad_request:plan_upgrade_required", "bad_request:read_only", "bad_request:signup_required", "forbidden:editing_not_allowed", "forbidden:origin_not_whitelisted", "forbidden:whitelist_required", "unexpected:internal_error"]; +export const EDITOR_ERROR_CODES: readonly ["bad_request:batch_validation_failed", "bad_request:download_blocked", "bad_request:editor_not_ready", "bad_request:event_not_allowed", "bad_request:failed_to_load_document", "bad_request:field_not_found", "bad_request:invalid_dimensions", "bad_request:invalid_event_type", "bad_request:invalid_field_ids", "bad_request:invalid_field_type", "bad_request:invalid_page", "bad_request:invalid_signature_url", "bad_request:invalid_tool", "bad_request:invalid_value", "bad_request:missing_required_fields", "bad_request:no_document_loaded", "bad_request:page_not_found", "bad_request:page_out_of_range", "bad_request:plan_upgrade_required", "bad_request:read_only", "bad_request:signup_required", "forbidden:editing_not_allowed", "forbidden:origin_not_whitelisted", "forbidden:whitelist_required", "unexpected:internal_error"]; // @public (undocumented) export const EXTRACTION_MODES: readonly ["auto", "ocr"]; @@ -27,10 +27,18 @@ export const LOCALES: readonly ["fr", "en", "it", "de", "pt", "es", "ja", "nl"]; // @public (undocumented) export const OPERATIONS: readonly [{ + readonly request_type: "BATCH_SET_FIELD_VALUES"; + readonly wire_type: "BATCH_SET_FIELD_VALUES"; + readonly method: "batchSetFieldValues"; + readonly description: "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data."; + readonly error_codes: readonly ["bad_request:batch_validation_failed", "bad_request:invalid_value", "bad_request:no_document_loaded"]; + readonly is_agentic_tool: true; + readonly has_output: false; +}, { readonly request_type: "CREATE_FIELD"; readonly wire_type: "CREATE_FIELD"; readonly method: "createField"; - readonly description: "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled."; + readonly description: "Create a new overlay field on a page. Returns the id of the created field."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:invalid_dimensions", "bad_request:invalid_value", "bad_request:page_out_of_range", "bad_request:page_not_found", "bad_request:invalid_field_type", "bad_request:invalid_signature_url"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -38,7 +46,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "DELETE_FIELDS"; readonly wire_type: "DELETE_FIELDS"; readonly method: "deleteFields"; - readonly description: "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled."; + readonly description: "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:no_document_loaded", "bad_request:invalid_field_ids", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:page_not_found"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -46,7 +54,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "DELETE_PAGES"; readonly wire_type: "DELETE_PAGES"; readonly method: "deletePages"; - readonly description: "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"]; readonly is_agentic_tool: true; readonly has_output: false; @@ -54,7 +62,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "DETECT_FIELDS"; readonly wire_type: "DETECT_FIELDS"; readonly method: "detectFields"; - readonly description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled."; + readonly description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:no_document_loaded"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -70,7 +78,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "FOCUS_FIELD"; readonly wire_type: "FOCUS_FIELD"; readonly method: "focusField"; - readonly description: "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next."; + readonly description: "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next."; readonly error_codes: readonly ["bad_request:invalid_value", "bad_request:no_document_loaded", "bad_request:field_not_found"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -78,7 +86,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "GET_ANNOTATED_PAGE"; readonly wire_type: "GET_ANNOTATED_PAGE"; readonly method: "getAnnotatedPage"; - readonly description: "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant."; + readonly description: "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block."; readonly error_codes: readonly ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -86,7 +94,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "GET_DOCUMENT_CONTENT"; readonly wire_type: "GET_DOCUMENT_CONTENT"; readonly method: "getDocumentContent"; - readonly description: "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }."; + readonly description: "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content."; readonly error_codes: readonly ["bad_request:invalid_value", "bad_request:no_document_loaded"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -94,7 +102,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "GET_FIELDS"; readonly wire_type: "GET_FIELDS"; readonly method: "getFields"; - readonly description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }."; + readonly description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list."; readonly error_codes: readonly ["bad_request:no_document_loaded"]; readonly is_agentic_tool: true; readonly has_output: true; @@ -102,7 +110,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "GO_TO"; readonly wire_type: "GO_TO"; readonly method: "goTo"; - readonly description: "Scroll the editor to a specific 1-based page. Returns no data."; + readonly description: "Scroll the editor to a page. Returns no data."; readonly error_codes: readonly ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"]; readonly is_agentic_tool: true; readonly has_output: false; @@ -118,7 +126,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "MOVE_PAGE"; readonly wire_type: "MOVE_PAGE"; readonly method: "movePage"; - readonly description: "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Move a page to another position, reordering the document. Returns no data. Destructive."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"]; readonly is_agentic_tool: true; readonly has_output: false; @@ -126,7 +134,7 @@ export const OPERATIONS: readonly [{ readonly request_type: "ROTATE_PAGE"; readonly wire_type: "ROTATE_PAGE"; readonly method: "rotatePage"; - readonly description: "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Rotate a page 90 degrees clockwise. Returns no data. Destructive."; readonly error_codes: readonly ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"]; readonly is_agentic_tool: true; readonly has_output: false; @@ -142,15 +150,15 @@ export const OPERATIONS: readonly [{ readonly request_type: "SET_FIELD_VALUE"; readonly wire_type: "SET_FIELD_VALUE"; readonly method: "setFieldValue"; - readonly description: "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data."; - readonly error_codes: readonly ["bad_request:invalid_value", "bad_request:invalid_signature_url", "bad_request:no_document_loaded", "bad_request:read_only", "bad_request:field_not_found"]; + readonly description: "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data."; + readonly error_codes: readonly ["bad_request:field_not_found", "bad_request:invalid_signature_url", "bad_request:invalid_value", "bad_request:read_only", "bad_request:no_document_loaded"]; readonly is_agentic_tool: true; readonly has_output: false; }, { readonly request_type: "SUBMIT"; readonly wire_type: "SUBMIT"; readonly method: "submit"; - readonly description: "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data."; + readonly description: "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data."; readonly error_codes: readonly ["bad_request:no_document_loaded", "bad_request:invalid_value", "bad_request:missing_required_fields"]; readonly is_agentic_tool: true; readonly has_output: false; @@ -184,13 +192,13 @@ export const OVERLAY_TOOL_TYPES: readonly ["TEXT", "SIGNATURE", "PICTURE", "CHEC export type OverlayToolType = (typeof OVERLAY_TOOL_TYPES)[number]; // @public (undocumented) -export const REQUEST_TYPES: ("CREATE_FIELD" | "DELETE_FIELDS" | "DELETE_PAGES" | "DETECT_FIELDS" | "DOWNLOAD" | "FOCUS_FIELD" | "GET_ANNOTATED_PAGE" | "GET_DOCUMENT_CONTENT" | "GET_FIELDS" | "GO_TO" | "LOAD_DOCUMENT" | "MOVE_PAGE" | "ROTATE_PAGE" | "SELECT_TOOL" | "SET_FIELD_VALUE" | "SUBMIT")[]; +export const REQUEST_TYPES: ("BATCH_SET_FIELD_VALUES" | "CREATE_FIELD" | "DELETE_FIELDS" | "DELETE_PAGES" | "DETECT_FIELDS" | "DOWNLOAD" | "FOCUS_FIELD" | "GET_ANNOTATED_PAGE" | "GET_DOCUMENT_CONTENT" | "GET_FIELDS" | "GO_TO" | "LOAD_DOCUMENT" | "MOVE_PAGE" | "ROTATE_PAGE" | "SELECT_TOOL" | "SET_FIELD_VALUE" | "SUBMIT")[]; // @public (undocumented) export type RequestType = (typeof OPERATIONS)[number]["request_type"]; // @public (undocumented) -export const WIRE_TYPES: ("CREATE_FIELD" | "DELETE_FIELDS" | "DELETE_PAGES" | "DETECT_FIELDS" | "DOWNLOAD" | "FOCUS_FIELD" | "GET_ANNOTATED_PAGE" | "GET_DOCUMENT_CONTENT" | "GET_FIELDS" | "GO_TO" | "LOAD_DOCUMENT" | "MOVE_PAGE" | "ROTATE_PAGE" | "SELECT_TOOL" | "SET_FIELD_VALUE" | "SUBMIT")[]; +export const WIRE_TYPES: ("BATCH_SET_FIELD_VALUES" | "CREATE_FIELD" | "DELETE_FIELDS" | "DELETE_PAGES" | "DETECT_FIELDS" | "DOWNLOAD" | "FOCUS_FIELD" | "GET_ANNOTATED_PAGE" | "GET_DOCUMENT_CONTENT" | "GET_FIELDS" | "GO_TO" | "LOAD_DOCUMENT" | "MOVE_PAGE" | "ROTATE_PAGE" | "SELECT_TOOL" | "SET_FIELD_VALUE" | "SUBMIT")[]; // @public (undocumented) export type WireType = (typeof OPERATIONS)[number]["wire_type"]; diff --git a/embed/etc/schemas.api.md b/embed/etc/schemas.api.md index 822dadcb..6becd80f 100644 --- a/embed/etc/schemas.api.md +++ b/embed/etc/schemas.api.md @@ -6,6 +6,17 @@ import { z } from 'zod'; +// @public (undocumented) +export const BatchSetFieldValuesInput: z.ZodObject<{ + updates: z.ZodArray; + }, z.core.$strip>>; +}, z.core.$strip>; + +// @public (undocumented) +export type BatchSetFieldValuesInput = z.infer; + // @public (undocumented) export const CreateFieldInput: z.ZodObject<{ type: z.ZodEnum<{ diff --git a/embed/etc/tanstack-ai.api.md b/embed/etc/tanstack-ai.api.md index 8753caba..cdd4656f 100644 --- a/embed/etc/tanstack-ai.api.md +++ b/embed/etc/tanstack-ai.api.md @@ -26,7 +26,7 @@ export type SimplePDFToolName = keyof typeof TOOL_DEFINITIONS; // Warnings were encountered during analysis: // -// dist/tanstack-ai.d.ts:61:5 - (ae-forgotten-export) The symbol "Embed" needs to be exported by the entry point tanstack-ai.d.ts +// dist/tanstack-ai.d.ts:66:5 - (ae-forgotten-export) The symbol "Embed" needs to be exported by the entry point tanstack-ai.d.ts // (No @packageDocumentation comment for this package) diff --git a/embed/etc/tools.api.md b/embed/etc/tools.api.md index 97239f8d..e52b690c 100644 --- a/embed/etc/tools.api.md +++ b/embed/etc/tools.api.md @@ -18,8 +18,17 @@ export const routeToolCall: (actions: IframeActions, toolName: SimplePDFToolName // @public (undocumented) export const SIMPLEPDF_TOOLS: { + readonly batchSetFieldValues: { + readonly description: "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data."; + readonly inputSchema: zod.ZodObject<{ + updates: zod.ZodArray; + }, zod_v4_core.$strip>>; + }, zod_v4_core.$strip>; + }; readonly createField: { - readonly description: "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled."; + readonly description: "Create a new overlay field on a page. Returns the id of the created field."; readonly inputSchema: zod.ZodObject<{ type: zod.ZodEnum<{ TEXT: "TEXT"; @@ -37,20 +46,20 @@ export const SIMPLEPDF_TOOLS: { }, zod_v4_core.$strip>; }; readonly deleteFields: { - readonly description: "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled."; + readonly description: "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive."; readonly inputSchema: zod.ZodObject<{ fieldIds: zod.ZodOptional>; page: zod.ZodOptional; }, zod_v4_core.$strip>; }; readonly deletePages: { - readonly description: "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive."; readonly inputSchema: zod.ZodObject<{ pages: zod.ZodArray; }, zod_v4_core.$strip>; }; readonly detectFields: { - readonly description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled."; + readonly description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected."; readonly inputSchema: zod.ZodObject<{}, zod_v4_core.$strip>; }; readonly download: { @@ -58,19 +67,19 @@ export const SIMPLEPDF_TOOLS: { readonly inputSchema: zod.ZodObject<{}, zod_v4_core.$strip>; }; readonly focusField: { - readonly description: "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next."; + readonly description: "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next."; readonly inputSchema: zod.ZodObject<{ fieldId: zod.ZodString; }, zod_v4_core.$strip>; }; readonly getAnnotatedPage: { - readonly description: "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant."; + readonly description: "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block."; readonly inputSchema: zod.ZodObject<{ page: zod.ZodNumber; }, zod_v4_core.$strip>; }; readonly getDocumentContent: { - readonly description: "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }."; + readonly description: "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content."; readonly inputSchema: zod.ZodObject<{ extractionMode: zod.ZodOptional; }; readonly getFields: { - readonly description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }."; + readonly description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list."; readonly inputSchema: zod.ZodObject<{}, zod_v4_core.$strip>; }; readonly goTo: { - readonly description: "Scroll the editor to a specific 1-based page. Returns no data."; + readonly description: "Scroll the editor to a page. Returns no data."; readonly inputSchema: zod.ZodObject<{ page: zod.ZodNumber; }, zod_v4_core.$strip>; }; readonly movePage: { - readonly description: "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Move a page to another position, reordering the document. Returns no data. Destructive."; readonly inputSchema: zod.ZodObject<{ fromPage: zod.ZodNumber; toPage: zod.ZodNumber; }, zod_v4_core.$strip>; }; readonly rotatePage: { - readonly description: "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled."; + readonly description: "Rotate a page 90 degrees clockwise. Returns no data. Destructive."; readonly inputSchema: zod.ZodObject<{ page: zod.ZodNumber; }, zod_v4_core.$strip>; @@ -114,7 +123,7 @@ export const SIMPLEPDF_TOOLS: { }, zod_v4_core.$strip>; }; readonly setFieldValue: { - readonly description: "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data."; + readonly description: "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data."; readonly inputSchema: zod.ZodObject<{ animate: zod.ZodOptional; fieldId: zod.ZodString; @@ -122,7 +131,7 @@ export const SIMPLEPDF_TOOLS: { }, zod_v4_core.$strip>; }; readonly submit: { - readonly description: "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data."; + readonly description: "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data."; readonly inputSchema: zod.ZodObject<{ downloadCopy: zod.ZodBoolean; }, zod_v4_core.$strip>; diff --git a/embed/scripts/check-bundle-size.mjs b/embed/scripts/check-bundle-size.mjs index 2d436271..893a068f 100644 --- a/embed/scripts/check-bundle-size.mjs +++ b/embed/scripts/check-bundle-size.mjs @@ -20,10 +20,10 @@ const DIST = join(dirname(fileURLToPath(import.meta.url)), '..', 'dist') const BUDGETS = { 'index.js': 10 * 1024, 'protocol.js': 3.5 * 1024, - 'schemas.js': 3 * 1024, - 'tools.js': 5 * 1024, - 'ai-sdk.js': 5.5 * 1024, - 'tanstack-ai.js': 5.5 * 1024, + 'schemas.js': 2 * 1024, + 'tools.js': 4.5 * 1024, + 'ai-sdk.js': 5 * 1024, + 'tanstack-ai.js': 5 * 1024, } const importsOf = (file, pattern) => { diff --git a/embed/scripts/generate.mjs b/embed/scripts/generate.mjs index 632d57cd..d10c9508 100644 --- a/embed/scripts/generate.mjs +++ b/embed/scripts/generate.mjs @@ -355,17 +355,27 @@ contractLines.push('export type DocumentContentResult = GetDocumentContentOutput contractLines.push("export type DocumentContentPage = GetDocumentContentOutput['pages'][number]") contractLines.push('') -// The typed `details` payload carried by the one error code that has them, derived -// from editor_error_schema so the package's BridgeError never restates it. -const missingFieldsVariant = (contract.editor_error_schema.anyOf ?? []).find( - (variant) => variant.properties?.code?.const === 'bad_request:missing_required_fields', +// The typed `details` payload of every error code that carries one, derived from +// editor_error_schema so the package's BridgeError never restates it: one +// `Details` type per code ('bad_request:missing_required_fields' -> +// MissingRequiredFieldsDetails) and the code -> details map BridgeError derives from. +const detailedErrorVariants = (contract.editor_error_schema.anyOf ?? []).filter( + (variant) => typeof variant.properties?.code?.const === 'string' && variant.properties?.details !== undefined, ) -if (missingFieldsVariant?.properties?.details === undefined) { +if (!detailedErrorVariants.some((variant) => variant.properties.code.const === 'bad_request:missing_required_fields')) { throw new Error('embed-api.json: the bad_request:missing_required_fields variant lacks a details schema') } -contractLines.push( - `export type MissingRequiredFieldsDetails = ${tsForNode(missingFieldsVariant.properties.details, true)}`, -) +const toDetailsTypeName = (code) => `${toPascal(code.split(':')[1])}Details` +for (const variant of detailedErrorVariants) { + contractLines.push( + `export type ${toDetailsTypeName(variant.properties.code.const)} = ${tsForNode(variant.properties.details, true)}`, + ) +} +contractLines.push('export type EditorErrorDetailsByCode = {') +for (const variant of detailedErrorVariants) { + contractLines.push(` '${variant.properties.code.const}': ${toDetailsTypeName(variant.properties.code.const)}`) +} +contractLines.push('}') contractLines.push('') // Outbound event payload types. @@ -426,7 +436,8 @@ schemaLines.push('') for (const op of contract.operations) { const stem = toPascal(op.request_type) - schemaLines.push(`export const ${stem}Input = ${zodForNode(op.input_schema, { withDescription: true })}`) + // The description lives on the tool (src/generated/tools.ts); repeating it here sends it to the model twice + schemaLines.push(`export const ${stem}Input = ${zodForNode(op.input_schema, { withDescription: false })}`) // Value + type share the PascalCase name (the canonical zod idiom). schemaLines.push(`export type ${stem}Input = z.infer`) } diff --git a/embed/src/bridge.ts b/embed/src/bridge.ts index 8679f814..80a99669 100644 --- a/embed/src/bridge.ts +++ b/embed/src/bridge.ts @@ -515,6 +515,7 @@ export const attachEmbed = ({ // ordering, input validation, and typed errors, and always replies. Each method // posts the request and correlates the reply; no client-side validation. const methods = { + batchSetFieldValues: (input) => sendRequest('BATCH_SET_FIELD_VALUES', input), createField: (input) => sendRequest('CREATE_FIELD', input), // All-optional input: deleteFields() with no args clears every field. deleteFields: (input) => sendRequest('DELETE_FIELDS', input ?? {}), diff --git a/embed/src/generated/contract.ts b/embed/src/generated/contract.ts index c136e428..6addc71b 100644 --- a/embed/src/generated/contract.ts +++ b/embed/src/generated/contract.ts @@ -5,7 +5,7 @@ import type { METHOD_NAMES } from './method-names' export const LOCALES = ["fr", "en", "it", "de", "pt", "es", "ja", "nl"] as const export type Locale = (typeof LOCALES)[number] -export const EDITOR_ERROR_CODES = ["bad_request:download_blocked", "bad_request:editor_not_ready", "bad_request:event_not_allowed", "bad_request:failed_to_load_document", "bad_request:field_not_found", "bad_request:invalid_dimensions", "bad_request:invalid_event_type", "bad_request:invalid_field_ids", "bad_request:invalid_field_type", "bad_request:invalid_page", "bad_request:invalid_signature_url", "bad_request:invalid_tool", "bad_request:invalid_value", "bad_request:missing_required_fields", "bad_request:no_document_loaded", "bad_request:page_not_found", "bad_request:page_out_of_range", "bad_request:plan_upgrade_required", "bad_request:read_only", "bad_request:signup_required", "forbidden:editing_not_allowed", "forbidden:origin_not_whitelisted", "forbidden:whitelist_required", "unexpected:internal_error"] as const +export const EDITOR_ERROR_CODES = ["bad_request:batch_validation_failed", "bad_request:download_blocked", "bad_request:editor_not_ready", "bad_request:event_not_allowed", "bad_request:failed_to_load_document", "bad_request:field_not_found", "bad_request:invalid_dimensions", "bad_request:invalid_event_type", "bad_request:invalid_field_ids", "bad_request:invalid_field_type", "bad_request:invalid_page", "bad_request:invalid_signature_url", "bad_request:invalid_tool", "bad_request:invalid_value", "bad_request:missing_required_fields", "bad_request:no_document_loaded", "bad_request:page_not_found", "bad_request:page_out_of_range", "bad_request:plan_upgrade_required", "bad_request:read_only", "bad_request:signup_required", "forbidden:editing_not_allowed", "forbidden:origin_not_whitelisted", "forbidden:whitelist_required", "unexpected:internal_error"] as const export type EditorErrorCode = (typeof EDITOR_ERROR_CODES)[number] export const FIELD_TYPES = ["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT", "DROPDOWN", "RADIO"] as const @@ -17,6 +17,8 @@ export type OverlayToolType = (typeof OVERLAY_TOOL_TYPES)[number] export const EXTRACTION_MODES = ["auto", "ocr"] as const export type ExtractionMode = (typeof EXTRACTION_MODES)[number] +export type BatchSetFieldValuesInput = { updates: Array<{ fieldId: string; value: string | null }> } +export type BatchSetFieldValuesOutput = null export type CreateFieldInput = { type: OverlayToolType; x: number; y: number; width: number; height: number; page: number; value?: string } export type CreateFieldOutput = { fieldId: string } export type DeleteFieldsInput = { fieldIds?: string[]; page?: number } @@ -53,7 +55,12 @@ export type SubmitOutput = null export type DocumentContentResult = GetDocumentContentOutput export type DocumentContentPage = GetDocumentContentOutput['pages'][number] +export type BatchValidationFailedDetails = { failures: Array<{ fieldId: string; code: "bad_request:field_not_found" | "bad_request:invalid_signature_url" | "bad_request:invalid_value" | "bad_request:read_only"; message: string }> } export type MissingRequiredFieldsDetails = { unfilledRequiredFieldsCount: number } +export type EditorErrorDetailsByCode = { + 'bad_request:batch_validation_failed': BatchValidationFailedDetails + 'bad_request:missing_required_fields': MissingRequiredFieldsDetails +} export type EditorReadyPayload = Record export type DocumentLoadedPayload = { document_id: string } @@ -61,11 +68,20 @@ export type PageFocusedPayload = { previous_page: number | null; current_page: n export type SubmissionSentPayload = { document_id: string; submission_id: string } export const OPERATIONS = [ + { + request_type: "BATCH_SET_FIELD_VALUES", + wire_type: "BATCH_SET_FIELD_VALUES", + method: "batchSetFieldValues", + description: "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data.", + error_codes: ["bad_request:batch_validation_failed", "bad_request:invalid_value", "bad_request:no_document_loaded"] as const, + is_agentic_tool: true, + has_output: false, + } /* BatchSetFieldValues */, { request_type: "CREATE_FIELD", wire_type: "CREATE_FIELD", method: "createField", - description: "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.", + description: "Create a new overlay field on a page. Returns the id of the created field.", error_codes: ["forbidden:editing_not_allowed", "bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:invalid_dimensions", "bad_request:invalid_value", "bad_request:page_out_of_range", "bad_request:page_not_found", "bad_request:invalid_field_type", "bad_request:invalid_signature_url"] as const, is_agentic_tool: true, has_output: true, @@ -74,7 +90,7 @@ export const OPERATIONS = [ request_type: "DELETE_FIELDS", wire_type: "DELETE_FIELDS", method: "deleteFields", - description: "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.", + description: "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive.", error_codes: ["forbidden:editing_not_allowed", "bad_request:no_document_loaded", "bad_request:invalid_field_ids", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:page_not_found"] as const, is_agentic_tool: true, has_output: true, @@ -83,7 +99,7 @@ export const OPERATIONS = [ request_type: "DELETE_PAGES", wire_type: "DELETE_PAGES", method: "deletePages", - description: "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.", + description: "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive.", error_codes: ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"] as const, is_agentic_tool: true, has_output: false, @@ -92,7 +108,7 @@ export const OPERATIONS = [ request_type: "DETECT_FIELDS", wire_type: "DETECT_FIELDS", method: "detectFields", - description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.", + description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected.", error_codes: ["forbidden:editing_not_allowed", "bad_request:no_document_loaded"] as const, is_agentic_tool: true, has_output: true, @@ -110,7 +126,7 @@ export const OPERATIONS = [ request_type: "FOCUS_FIELD", wire_type: "FOCUS_FIELD", method: "focusField", - description: "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.", + description: "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next.", error_codes: ["bad_request:invalid_value", "bad_request:no_document_loaded", "bad_request:field_not_found"] as const, is_agentic_tool: true, has_output: true, @@ -119,7 +135,7 @@ export const OPERATIONS = [ request_type: "GET_ANNOTATED_PAGE", wire_type: "GET_ANNOTATED_PAGE", method: "getAnnotatedPage", - description: "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.", + description: "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block.", error_codes: ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"] as const, is_agentic_tool: true, has_output: true, @@ -128,7 +144,7 @@ export const OPERATIONS = [ request_type: "GET_DOCUMENT_CONTENT", wire_type: "GET_DOCUMENT_CONTENT", method: "getDocumentContent", - description: "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.", + description: "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content.", error_codes: ["bad_request:invalid_value", "bad_request:no_document_loaded"] as const, is_agentic_tool: true, has_output: true, @@ -137,7 +153,7 @@ export const OPERATIONS = [ request_type: "GET_FIELDS", wire_type: "GET_FIELDS", method: "getFields", - description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.", + description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list.", error_codes: ["bad_request:no_document_loaded"] as const, is_agentic_tool: true, has_output: true, @@ -146,7 +162,7 @@ export const OPERATIONS = [ request_type: "GO_TO", wire_type: "GO_TO", method: "goTo", - description: "Scroll the editor to a specific 1-based page. Returns no data.", + description: "Scroll the editor to a page. Returns no data.", error_codes: ["bad_request:no_document_loaded", "bad_request:invalid_page", "bad_request:page_out_of_range"] as const, is_agentic_tool: true, has_output: false, @@ -164,7 +180,7 @@ export const OPERATIONS = [ request_type: "MOVE_PAGE", wire_type: "MOVE_PAGE", method: "movePage", - description: "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.", + description: "Move a page to another position, reordering the document. Returns no data. Destructive.", error_codes: ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"] as const, is_agentic_tool: true, has_output: false, @@ -173,7 +189,7 @@ export const OPERATIONS = [ request_type: "ROTATE_PAGE", wire_type: "ROTATE_PAGE", method: "rotatePage", - description: "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.", + description: "Rotate a page 90 degrees clockwise. Returns no data. Destructive.", error_codes: ["forbidden:editing_not_allowed", "bad_request:invalid_page", "bad_request:page_out_of_range", "bad_request:no_document_loaded", "bad_request:page_not_found"] as const, is_agentic_tool: true, has_output: false, @@ -191,8 +207,8 @@ export const OPERATIONS = [ request_type: "SET_FIELD_VALUE", wire_type: "SET_FIELD_VALUE", method: "setFieldValue", - description: "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.", - error_codes: ["bad_request:invalid_value", "bad_request:invalid_signature_url", "bad_request:no_document_loaded", "bad_request:read_only", "bad_request:field_not_found"] as const, + description: "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data.", + error_codes: ["bad_request:field_not_found", "bad_request:invalid_signature_url", "bad_request:invalid_value", "bad_request:read_only", "bad_request:no_document_loaded"] as const, is_agentic_tool: true, has_output: false, } /* SetFieldValue */, @@ -200,7 +216,7 @@ export const OPERATIONS = [ request_type: "SUBMIT", wire_type: "SUBMIT", method: "submit", - description: "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.", + description: "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data.", error_codes: ["bad_request:no_document_loaded", "bad_request:invalid_value", "bad_request:missing_required_fields"] as const, is_agentic_tool: true, has_output: false, diff --git a/embed/src/generated/drift.ts b/embed/src/generated/drift.ts index 75cefc55..674c0034 100644 --- a/embed/src/generated/drift.ts +++ b/embed/src/generated/drift.ts @@ -14,6 +14,7 @@ export type DriftGuards = [ AssertTrue>, AssertTrue>, AssertTrue>, + AssertTrue>, AssertTrue>, AssertTrue>, AssertTrue>, diff --git a/embed/src/generated/method-names.ts b/embed/src/generated/method-names.ts index cd19a9a9..596c7cef 100644 --- a/embed/src/generated/method-names.ts +++ b/embed/src/generated/method-names.ts @@ -2,4 +2,4 @@ // The SDK method names alone, so createEmbed can validate a `webMCP.exclude` list // without pulling the operations table into the zero-dep root; contract.ts derives // MethodName from this list. -export const METHOD_NAMES = ["createField", "deleteFields", "deletePages", "detectFields", "download", "focusField", "getAnnotatedPage", "getDocumentContent", "getFields", "goTo", "loadDocument", "movePage", "rotatePage", "selectTool", "setFieldValue", "submit"] as const +export const METHOD_NAMES = ["batchSetFieldValues", "createField", "deleteFields", "deletePages", "detectFields", "download", "focusField", "getAnnotatedPage", "getDocumentContent", "getFields", "goTo", "loadDocument", "movePage", "rotatePage", "selectTool", "setFieldValue", "submit"] as const diff --git a/embed/src/generated/schemas.ts b/embed/src/generated/schemas.ts index 194c51ec..eaa4055d 100644 --- a/embed/src/generated/schemas.ts +++ b/embed/src/generated/schemas.ts @@ -1,6 +1,13 @@ // AUTO-GENERATED from embed-api.json by scripts/generate.mjs. Do not edit by hand. import { z } from 'zod' +export const BatchSetFieldValuesInput = z.object({ + updates: z.array(z.object({ + fieldId: z.string().describe("ID of the field to update."), + value: z.string().nullable().describe("New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature."), +})).describe("The fields to set, at least one, each field at most once. Fields not listed keep their value."), +}) +export type BatchSetFieldValuesInput = z.infer export const CreateFieldInput = z.object({ type: z.enum(["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT"]).describe("Field type to create."), x: z.number().describe("Field x position, in PDF points."), @@ -8,66 +15,66 @@ export const CreateFieldInput = z.object({ width: z.number().describe("Field width, in PDF points."), height: z.number().describe("Field height, in PDF points."), page: z.number().int().describe("1-based page to place the field on."), - value: z.string().describe("Optional initial value. A string for text/checkbox fields, or a data URL or http(s) URL (fetched by the editor) for signature/picture fields.").optional(), -}).describe("Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.") + value: z.string().describe("Optional initial value. A string for text/checkbox fields; a data URL or http(s) URL (fetched by the editor) for signature/picture fields, or plain text for a signature, drawn as a typed signature.").optional(), +}) export type CreateFieldInput = z.infer export const DeleteFieldsInput = z.object({ fieldIds: z.array(z.string()).describe("IDs of the fields to delete. Omit to delete every field on the target page.").optional(), page: z.number().int().describe("1-based page to scope the deletion to. Omit to target all pages.").optional(), -}).describe("Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.") +}) export type DeleteFieldsInput = z.infer export const DeletePagesInput = z.object({ pages: z.array(z.number().int()).describe("1-based page numbers to delete."), -}).describe("Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.") +}) export type DeletePagesInput = z.infer -export const DetectFieldsInput = z.object({}).describe("Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.") +export const DetectFieldsInput = z.object({}) export type DetectFieldsInput = z.infer -export const DownloadInput = z.object({}).describe("Generate and download the current document as a PDF. Returns no data.") +export const DownloadInput = z.object({}) export type DownloadInput = z.infer export const FocusFieldInput = z.object({ fieldId: z.string().describe("ID of the field to focus and scroll into view."), -}).describe("Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.") +}) export type FocusFieldInput = z.infer export const GetAnnotatedPageInput = z.object({ page: z.number().int().describe("1-based page to render, at its current position."), -}).describe("Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.") +}) export type GetAnnotatedPageInput = z.infer export const GetDocumentContentInput = z.object({ extractionMode: z.enum(["auto", "ocr"]).describe("Extraction strategy: 'auto' (default) or 'ocr' to force optical recognition.").optional(), -}).describe("Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.") +}) export type GetDocumentContentInput = z.infer -export const GetFieldsInput = z.object({}).describe("List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.") +export const GetFieldsInput = z.object({}) export type GetFieldsInput = z.infer export const GoToInput = z.object({ page: z.number().int().describe("1-based page to navigate to."), -}).describe("Scroll the editor to a specific 1-based page. Returns no data.") +}) export type GoToInput = z.infer export const LoadDocumentInput = z.object({ dataUrl: z.string().describe("The document to load: a data URL, or an http(s) URL the editor fetches."), name: z.string().describe("Optional display name for the document.").optional(), page: z.number().int().describe("Optional 1-based page to open the document on.").optional(), -}).describe("Replace the document in the editor with one supplied as a base64 data URL or an http(s) URL the editor fetches. Destructive: the current document and every edit in it are discarded. Replies once the document and its fields are ready, so the next operation can act on it, and DOCUMENT_LOADED follows the reply; the URL already open is left as is and answers once it settles; a document that cannot be loaded or prepared answers bad_request:failed_to_load_document. Returns no data.") +}) export type LoadDocumentInput = z.infer export const MovePageInput = z.object({ fromPage: z.number().int().describe("1-based current position of the page to move."), toPage: z.number().int().describe("1-based destination position for the page."), -}).describe("Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.") +}) export type MovePageInput = z.infer export const RotatePageInput = z.object({ - page: z.number().int().describe("1-based page to rotate 90 degrees clockwise."), -}).describe("Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.") + page: z.number().int().describe("1-based page to rotate."), +}) export type RotatePageInput = z.infer export const SelectToolInput = z.object({ tool: z.enum(["TEXT", "SIGNATURE", "PICTURE", "CHECKBOX", "COMB_TEXT"]).nullable().describe("Tool to activate, or null to deselect."), -}).describe("Activate a field-placement tool in the editor toolbar so the user can draw that field type, or pass null to clear the active tool. Returns no data.") +}) export type SelectToolInput = z.infer export const SetFieldValueInput = z.object({ animate: z.boolean().describe("Type a text value out character by character (true, the default) so a person watching the editor can follow the field being filled, or set it at once (false) for headless or batch filling where speed matters. Only text values are typed: checkbox, option, signature, picture and null values are always set at once.").optional(), fieldId: z.string().describe("ID of the field to update."), - value: z.string().nullable().describe("New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox) or a data URL or http(s) URL, fetched by the editor (signature/picture)."), -}).describe("Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.") + value: z.string().nullable().describe("New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature."), +}) export type SetFieldValueInput = z.infer export const SubmitInput = z.object({ downloadCopy: z.boolean().describe("When true, the signer also receives a downloaded copy on submit."), -}).describe("Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.") +}) export type SubmitInput = z.infer diff --git a/embed/src/generated/tools.ts b/embed/src/generated/tools.ts index 644e10e1..d18bcc6a 100644 --- a/embed/src/generated/tools.ts +++ b/embed/src/generated/tools.ts @@ -4,21 +4,22 @@ import * as Schemas from './schemas' // The agentic tool registry. Each tool name is the camelCase operation name; // load_document is excluded here (a host/setup action; the WebMCP surface registers it). export const TOOL_DEFINITIONS = { - createField: { description: "Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.", inputSchema: Schemas.CreateFieldInput }, - deleteFields: { description: "Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.", inputSchema: Schemas.DeleteFieldsInput }, - deletePages: { description: "Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.", inputSchema: Schemas.DeletePagesInput }, - detectFields: { description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.", inputSchema: Schemas.DetectFieldsInput }, + batchSetFieldValues: { description: "Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data.", inputSchema: Schemas.BatchSetFieldValuesInput }, + createField: { description: "Create a new overlay field on a page. Returns the id of the created field.", inputSchema: Schemas.CreateFieldInput }, + deleteFields: { description: "Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive.", inputSchema: Schemas.DeleteFieldsInput }, + deletePages: { description: "Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive.", inputSchema: Schemas.DeletePagesInput }, + detectFields: { description: "Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected.", inputSchema: Schemas.DetectFieldsInput }, download: { description: "Generate and download the current document as a PDF. Returns no data.", inputSchema: Schemas.DownloadInput }, - focusField: { description: "Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.", inputSchema: Schemas.FocusFieldInput }, - getAnnotatedPage: { description: "Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.", inputSchema: Schemas.GetAnnotatedPageInput }, - getDocumentContent: { description: "Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.", inputSchema: Schemas.GetDocumentContentInput }, - getFields: { description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.", inputSchema: Schemas.GetFieldsInput }, - goTo: { description: "Scroll the editor to a specific 1-based page. Returns no data.", inputSchema: Schemas.GoToInput }, - movePage: { description: "Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.", inputSchema: Schemas.MovePageInput }, - rotatePage: { description: "Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.", inputSchema: Schemas.RotatePageInput }, + focusField: { description: "Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next.", inputSchema: Schemas.FocusFieldInput }, + getAnnotatedPage: { description: "Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block.", inputSchema: Schemas.GetAnnotatedPageInput }, + getDocumentContent: { description: "Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content.", inputSchema: Schemas.GetDocumentContentInput }, + getFields: { description: "List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list.", inputSchema: Schemas.GetFieldsInput }, + goTo: { description: "Scroll the editor to a page. Returns no data.", inputSchema: Schemas.GoToInput }, + movePage: { description: "Move a page to another position, reordering the document. Returns no data. Destructive.", inputSchema: Schemas.MovePageInput }, + rotatePage: { description: "Rotate a page 90 degrees clockwise. Returns no data. Destructive.", inputSchema: Schemas.RotatePageInput }, selectTool: { description: "Activate a field-placement tool in the editor toolbar so the user can draw that field type, or pass null to clear the active tool. Returns no data.", inputSchema: Schemas.SelectToolInput }, - setFieldValue: { description: "Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.", inputSchema: Schemas.SetFieldValueInput }, - submit: { description: "Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.", inputSchema: Schemas.SubmitInput }, + setFieldValue: { description: "Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data.", inputSchema: Schemas.SetFieldValueInput }, + submit: { description: "Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data.", inputSchema: Schemas.SubmitInput }, } as const export type SimplePDFToolName = keyof typeof TOOL_DEFINITIONS diff --git a/embed/src/generated/webmcp-tools.ts b/embed/src/generated/webmcp-tools.ts index 3d4ff04e..36854d9c 100644 --- a/embed/src/generated/webmcp-tools.ts +++ b/embed/src/generated/webmcp-tools.ts @@ -25,20 +25,21 @@ export type WebMCPToolRecord = { } export const WEBMCP_TOOLS = { - createField: {"name":"simplepdf_embed_create_field","description":"Create a new overlay field of the given type at an (x, y) position and size (in PDF points) on a 1-based page. Returns { field_id } for the created field. Requires editing to be enabled.","inputSchema":{"type":"object","properties":{"type":{"enum":["TEXT","SIGNATURE","PICTURE","CHECKBOX","COMB_TEXT"],"description":"Field type to create.","type":"string"},"x":{"description":"Field x position, in PDF points.","type":"number"},"y":{"description":"Field y position, in PDF points.","type":"number"},"width":{"description":"Field width, in PDF points.","type":"number"},"height":{"description":"Field height, in PDF points.","type":"number"},"page":{"description":"1-based page to place the field on.","type":"integer"},"value":{"description":"Optional initial value. A string for text/checkbox fields, or a data URL or http(s) URL (fetched by the editor) for signature/picture fields.","type":"string"}},"required":["type","x","y","width","height","page"]},"annotations":{"destructiveHint":false,"openWorldHint":true},"wireType":"CREATE_FIELD"}, - deleteFields: {"name":"simplepdf_embed_delete_fields","description":"Delete overlay fields by id; omit field_ids to delete every field on the given 1-based page, or omit both field_ids and page to delete every overlay field in the document. Returns { deleted_count }. Destructive; requires editing to be enabled.","inputSchema":{"type":"object","properties":{"field_ids":{"description":"IDs of the fields to delete. Omit to delete every field on the target page.","items":{"type":"string"},"type":"array"},"page":{"description":"1-based page to scope the deletion to. Omit to target all pages.","type":"integer"}}},"annotations":{"destructiveHint":true},"wireType":"DELETE_FIELDS"}, - deletePages: {"name":"simplepdf_embed_delete_pages","description":"Delete one or more 1-based pages from the document (it cannot delete every visible page). Returns no data. Destructive; requires editing to be enabled.","inputSchema":{"type":"object","properties":{"pages":{"items":{"type":"integer"},"description":"1-based page numbers to delete.","type":"array"}},"required":["pages"]},"annotations":{"destructiveHint":true},"wireType":"DELETE_PAGES"}, - detectFields: {"name":"simplepdf_embed_detect_fields","description":"Automatically detect fillable fields in the loaded document and add them as editable fields. Returns { detected_count }. Requires editing to be enabled.","inputSchema":{"type":"object","properties":{}},"annotations":{"destructiveHint":false},"wireType":"DETECT_FIELDS"}, + batchSetFieldValues: {"name":"simplepdf_embed_batch_set_field_values","description":"Set existing fields, addressed by ids from the field list, in one atomic undo step. If any update is rejected, nothing changes and the call fails with bad_request:batch_validation_failed, whose details list each rejection with its field id, code and message. Text is set at once, never typed. Returns no data.","inputSchema":{"type":"object","properties":{"updates":{"items":{"type":"object","properties":{"field_id":{"type":"string","description":"ID of the field to update."},"value":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature."}},"required":["field_id","value"]},"description":"The fields to set, at least one, each field at most once. Fields not listed keep their value.","type":"array"}},"required":["updates"]},"annotations":{"destructiveHint":false,"openWorldHint":true},"wireType":"BATCH_SET_FIELD_VALUES"}, + createField: {"name":"simplepdf_embed_create_field","description":"Create a new overlay field on a page. Returns the id of the created field.","inputSchema":{"type":"object","properties":{"type":{"enum":["TEXT","SIGNATURE","PICTURE","CHECKBOX","COMB_TEXT"],"description":"Field type to create.","type":"string"},"x":{"description":"Field x position, in PDF points.","type":"number"},"y":{"description":"Field y position, in PDF points.","type":"number"},"width":{"description":"Field width, in PDF points.","type":"number"},"height":{"description":"Field height, in PDF points.","type":"number"},"page":{"description":"1-based page to place the field on.","type":"integer"},"value":{"description":"Optional initial value. A string for text/checkbox fields; a data URL or http(s) URL (fetched by the editor) for signature/picture fields, or plain text for a signature, drawn as a typed signature.","type":"string"}},"required":["type","x","y","width","height","page"]},"annotations":{"destructiveHint":false,"openWorldHint":true},"wireType":"CREATE_FIELD"}, + deleteFields: {"name":"simplepdf_embed_delete_fields","description":"Delete overlay fields: the ones listed, every one on a page, or every one in the document. Returns the number of fields deleted. Destructive.","inputSchema":{"type":"object","properties":{"field_ids":{"description":"IDs of the fields to delete. Omit to delete every field on the target page.","items":{"type":"string"},"type":"array"},"page":{"description":"1-based page to scope the deletion to. Omit to target all pages.","type":"integer"}}},"annotations":{"destructiveHint":true},"wireType":"DELETE_FIELDS"}, + deletePages: {"name":"simplepdf_embed_delete_pages","description":"Delete pages from the document (it cannot delete every visible page). Returns no data. Destructive.","inputSchema":{"type":"object","properties":{"pages":{"items":{"type":"integer"},"description":"1-based page numbers to delete.","type":"array"}},"required":["pages"]},"annotations":{"destructiveHint":true},"wireType":"DELETE_PAGES"}, + detectFields: {"name":"simplepdf_embed_detect_fields","description":"Automatically detect fillable fields in the loaded document and add them as editable fields. Returns the number of fields detected.","inputSchema":{"type":"object","properties":{}},"annotations":{"destructiveHint":false},"wireType":"DETECT_FIELDS"}, download: {"name":"simplepdf_embed_download","description":"Generate and download the current document as a PDF. Returns no data.","inputSchema":{"type":"object","properties":{}},"annotations":{"destructiveHint":false},"wireType":"DOWNLOAD"}, - focusField: {"name":"simplepdf_embed_focus_field","description":"Scroll an existing field into view and focus it, addressed by its id (from the field list). Returns a hint describing the user action expected next.","inputSchema":{"type":"object","properties":{"field_id":{"description":"ID of the field to focus and scroll into view.","type":"string"}},"required":["field_id"]},"annotations":{"destructiveHint":false},"wireType":"FOCUS_FIELD"}, - getAnnotatedPage: {"name":"simplepdf_embed_get_annotated_page","description":"Render a page as a PNG with every field on it outlined and numbered, so a vision model can SEE which field sits where on the printed form. Feed the image and the badges map to a multimodal model to label fields; get_fields returns the matching ids. The render shows the printed form and field placement, not filled-in values (read those with get_fields). Returns { page, image_data_url, image_width, image_height, badges } where badges maps each number drawn on the image to its field_id. It renders document content, so it is gated exactly like get_document_content: the embedding origin must be whitelisted for the tenant.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to render, at its current position.","type":"integer"}},"required":["page"]},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_ANNOTATED_PAGE"}, - getDocumentContent: {"name":"simplepdf_embed_get_document_content","description":"Extract the document's content page by page as Markdown (pass extraction_mode 'ocr' to force optical recognition, which returns plain text). Use it to read what the document says. Returns { name, pages: [{ page, content }] }.","inputSchema":{"type":"object","properties":{"extraction_mode":{"description":"Extraction strategy: 'auto' (default) or 'ocr' to force optical recognition.","enum":["auto","ocr"],"type":"string"}}},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_DOCUMENT_CONTENT"}, - getFields: {"name":"simplepdf_embed_get_fields","description":"List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, and current value. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, call get_annotated_page. Returns { fields }.","inputSchema":{"type":"object","properties":{}},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_FIELDS"}, - goTo: {"name":"simplepdf_embed_go_to","description":"Scroll the editor to a specific 1-based page. Returns no data.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to navigate to.","type":"integer"}},"required":["page"]},"annotations":{"destructiveHint":false},"wireType":"GO_TO"}, + focusField: {"name":"simplepdf_embed_focus_field","description":"Scroll an existing field into view and focus it, addressed by its id from the field list. Returns a hint describing the user action expected next.","inputSchema":{"type":"object","properties":{"field_id":{"description":"ID of the field to focus and scroll into view.","type":"string"}},"required":["field_id"]},"annotations":{"destructiveHint":false},"wireType":"FOCUS_FIELD"}, + getAnnotatedPage: {"name":"simplepdf_embed_get_annotated_page","description":"Render a page as a PNG with every field outlined and numbered, so a vision model can see which field sits where on the printed form (placement only: read values from the field list). Returns the page, the PNG as a data URL, its width and height in pixels, and badges mapping each number drawn on the image to a field id; as a WebMCP tool the PNG arrives as an image block and the rest as a text block.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to render, at its current position.","type":"integer"}},"required":["page"]},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_ANNOTATED_PAGE"}, + getDocumentContent: {"name":"simplepdf_embed_get_document_content","description":"Extract the document's content page by page as Markdown, or as plain text when optical recognition is forced. Use it to read what the document says. Returns the document name and each page's content.","inputSchema":{"type":"object","properties":{"extraction_mode":{"description":"Extraction strategy: 'auto' (default) or 'ocr' to force optical recognition.","enum":["auto","ocr"],"type":"string"}}},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_DOCUMENT_CONTENT"}, + getFields: {"name":"simplepdf_embed_get_fields","description":"List every fillable field in the loaded document, including native dropdown and radio AcroFields. Each field reports its id, name, type, page, current value, and the options its value must be one of, if any. Call this first to discover field ids before reading or setting values. To SEE where each field sits on the printed page, render the annotated page. Returns the field list.","inputSchema":{"type":"object","properties":{}},"annotations":{"readOnlyHint":true,"untrustedContentHint":true},"wireType":"GET_FIELDS"}, + goTo: {"name":"simplepdf_embed_go_to","description":"Scroll the editor to a page. Returns no data.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to navigate to.","type":"integer"}},"required":["page"]},"annotations":{"destructiveHint":false},"wireType":"GO_TO"}, loadDocument: {"name":"simplepdf_embed_load_document","description":"Replace the document in the editor with one supplied as a base64 data URL or an http(s) URL the editor fetches. Destructive: the current document and every edit in it are discarded. Replies once the document and its fields are ready, so the next operation can act on it, and DOCUMENT_LOADED follows the reply; the URL already open is left as is and answers once it settles; a document that cannot be loaded or prepared answers bad_request:failed_to_load_document. Returns no data.","inputSchema":{"type":"object","properties":{"data_url":{"description":"The document to load: a data URL, or an http(s) URL the editor fetches.","type":"string"},"name":{"description":"Optional display name for the document.","type":"string"},"page":{"description":"Optional 1-based page to open the document on.","type":"integer"}},"required":["data_url"]},"annotations":{"destructiveHint":true,"openWorldHint":true},"wireType":"LOAD_DOCUMENT"}, - movePage: {"name":"simplepdf_embed_move_page","description":"Move a page from one 1-based position to another, reordering the document. Returns no data. Destructive; requires editing to be enabled.","inputSchema":{"type":"object","properties":{"from_page":{"description":"1-based current position of the page to move.","type":"integer"},"to_page":{"description":"1-based destination position for the page.","type":"integer"}},"required":["from_page","to_page"]},"annotations":{"destructiveHint":true},"wireType":"MOVE_PAGE"}, - rotatePage: {"name":"simplepdf_embed_rotate_page","description":"Rotate a 1-based page 90 degrees clockwise. Returns no data. Destructive; requires editing to be enabled.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to rotate 90 degrees clockwise.","type":"integer"}},"required":["page"]},"annotations":{"destructiveHint":true},"wireType":"ROTATE_PAGE"}, + movePage: {"name":"simplepdf_embed_move_page","description":"Move a page to another position, reordering the document. Returns no data. Destructive.","inputSchema":{"type":"object","properties":{"from_page":{"description":"1-based current position of the page to move.","type":"integer"},"to_page":{"description":"1-based destination position for the page.","type":"integer"}},"required":["from_page","to_page"]},"annotations":{"destructiveHint":true},"wireType":"MOVE_PAGE"}, + rotatePage: {"name":"simplepdf_embed_rotate_page","description":"Rotate a page 90 degrees clockwise. Returns no data. Destructive.","inputSchema":{"type":"object","properties":{"page":{"description":"1-based page to rotate.","type":"integer"}},"required":["page"]},"annotations":{"destructiveHint":true},"wireType":"ROTATE_PAGE"}, selectTool: {"name":"simplepdf_embed_select_tool","description":"Activate a field-placement tool in the editor toolbar so the user can draw that field type, or pass null to clear the active tool. Returns no data.","inputSchema":{"type":"object","properties":{"tool":{"anyOf":[{"type":"string","enum":["TEXT","SIGNATURE","PICTURE","CHECKBOX","COMB_TEXT"]},{"type":"null"}],"description":"Tool to activate, or null to deselect."}},"required":["tool"]},"annotations":{"destructiveHint":false},"wireType":"SELECT_TOOL"}, - setFieldValue: {"name":"simplepdf_embed_set_field_value","description":"Set the value of an existing field addressed by its id (from the field list), or clear it with null. If the field has options (see the field list), value must be one of them; otherwise value is a string (text or checkbox value) or a data URL or http(s) URL the editor fetches (signature, picture). Text values are typed out by default; pass animate: false to set them at once. Returns no data.","inputSchema":{"type":"object","properties":{"animate":{"description":"Type a text value out character by character (true, the default) so a person watching the editor can follow the field being filled, or set it at once (false) for headless or batch filling where speed matters. Only text values are typed: checkbox, option, signature, picture and null values are always set at once.","type":"boolean"},"field_id":{"description":"ID of the field to update.","type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox) or a data URL or http(s) URL, fetched by the editor (signature/picture)."}},"required":["field_id","value"]},"annotations":{"destructiveHint":false,"openWorldHint":true},"wireType":"SET_FIELD_VALUE"}, - submit: {"name":"simplepdf_embed_submit","description":"Submit the completed document through the editor's finalization flow. This is irreversible. When download_copy is true the signer also gets a downloaded copy. Fails with missing_required_fields when required fields are unfilled. Returns no data.","inputSchema":{"type":"object","properties":{"download_copy":{"description":"When true, the signer also receives a downloaded copy on submit.","type":"boolean"}},"required":["download_copy"]},"annotations":{"destructiveHint":true},"wireType":"SUBMIT"}, + setFieldValue: {"name":"simplepdf_embed_set_field_value","description":"Set or clear (null) one existing field, addressed by its id from the field list. Text is typed out unless animate is false. Returns no data.","inputSchema":{"type":"object","properties":{"animate":{"description":"Type a text value out character by character (true, the default) so a person watching the editor can follow the field being filled, or set it at once (false) for headless or batch filling where speed matters. Only text values are typed: checkbox, option, signature, picture and null values are always set at once.","type":"boolean"},"field_id":{"description":"ID of the field to update.","type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"New value for the field, or null to clear it. If the field has options (see the field list), it must be one of them; otherwise a string (text/checkbox), or a data URL or http(s) URL, fetched by the editor (signature/picture), or plain text for a signature, drawn as a typed signature."}},"required":["field_id","value"]},"annotations":{"destructiveHint":false,"openWorldHint":true},"wireType":"SET_FIELD_VALUE"}, + submit: {"name":"simplepdf_embed_submit","description":"Submit the completed document through the editor's finalization flow. This is irreversible. Fails with bad_request:missing_required_fields when required fields are unfilled. Returns no data.","inputSchema":{"type":"object","properties":{"download_copy":{"description":"When true, the signer also receives a downloaded copy on submit.","type":"boolean"}},"required":["download_copy"]},"annotations":{"destructiveHint":true},"wireType":"SUBMIT"}, } as const satisfies Record diff --git a/embed/src/tools.ts b/embed/src/tools.ts index 9904a511..fc2eb598 100644 --- a/embed/src/tools.ts +++ b/embed/src/tools.ts @@ -7,6 +7,7 @@ import type { z } from 'zod' import { TOOL_DEFINITIONS, type SimplePDFToolName } from './generated/tools' import { + BatchSetFieldValuesInput, CreateFieldInput, DeleteFieldsInput, DeletePagesInput, @@ -58,6 +59,8 @@ export const routeToolCall = ( input: unknown, ): Promise> => { switch (toolName) { + case 'batchSetFieldValues': + return dispatch(BatchSetFieldValuesInput, input, (value) => actions.batchSetFieldValues(value)) case 'createField': return dispatch(CreateFieldInput, input, (value) => actions.createField(value)) case 'deleteFields': diff --git a/embed/src/types.ts b/embed/src/types.ts index f9994f7e..afc4e310 100644 --- a/embed/src/types.ts +++ b/embed/src/types.ts @@ -4,6 +4,7 @@ // the single source of truth; nothing in this file restates a wire shape. import type { + BatchSetFieldValuesInput, CreateFieldInput, CreateFieldOutput, DeleteFieldsInput, @@ -12,6 +13,7 @@ import type { DetectFieldsOutput, DocumentLoadedPayload, EditorErrorCode, + EditorErrorDetailsByCode, EditorReadyPayload, FocusFieldInput, FocusFieldOutput, @@ -22,7 +24,6 @@ import type { GetFieldsOutput, GoToInput, LoadDocumentInput, - MissingRequiredFieldsDetails, MovePageInput, PageFocusedPayload, RotatePageInput, @@ -33,6 +34,8 @@ import type { } from './generated/contract' export type { + BatchSetFieldValuesInput, + BatchValidationFailedDetails, CreateFieldInput, CreateFieldOutput, DeleteFieldsInput, @@ -91,15 +94,15 @@ export type BridgeOwnedErrorCode = // boundary, where internal failures collapse to unexpected:internal_error). export type BridgeErrorCode = BridgeOwnedErrorCode | EditorErrorCode -// bad_request:missing_required_fields carries typed details; every other code -// is a plain { code, message }. Discriminated on `code`. +// The codes of EditorErrorDetailsByCode carry typed details; every other code is a +// plain { code, message }. Discriminated on `code` (distributed per detailed code). +type DetailedBridgeError = TCode extends keyof EditorErrorDetailsByCode + ? { code: TCode; message: string; details: EditorErrorDetailsByCode[TCode] } + : never + export type BridgeError = - | { - code: 'bad_request:missing_required_fields' - message: string - details: MissingRequiredFieldsDetails - } - | { code: Exclude; message: string } + | DetailedBridgeError + | { code: Exclude; message: string } export type BridgeResult = | { success: true; data: TData } @@ -136,6 +139,7 @@ export type EditorEventMap = { [TEvent in EditorEvent as TEvent['type']]: TEvent // throws. Drift-guarded against the generated operation set in ./generated/drift.ts // (a new operation fails the build until added here). export type IframeActions = { + batchSetFieldValues: (input: BatchSetFieldValuesInput) => Promise createField: (input: CreateFieldInput) => Promise> deleteFields: (input?: DeleteFieldsInput) => Promise> deletePages: (input: DeletePagesInput) => Promise diff --git a/embed/src/webmcp.ts b/embed/src/webmcp.ts index 7c355e50..156b1fd6 100644 --- a/embed/src/webmcp.ts +++ b/embed/src/webmcp.ts @@ -72,6 +72,7 @@ const toCallToolResult = (wireType: WireType, result: BridgeResult): Ca switch (wireType) { case 'GET_ANNOTATED_PAGE': return toAnnotatedPageToolResult(result) + case 'BATCH_SET_FIELD_VALUES': case 'CREATE_FIELD': case 'DELETE_FIELDS': case 'DELETE_PAGES': diff --git a/embed/test/mount.test.ts b/embed/test/mount.test.ts index 8770d2ea..7c3cc9a5 100644 --- a/embed/test/mount.test.ts +++ b/embed/test/mount.test.ts @@ -414,6 +414,6 @@ describe(createEmbed.name, () => { document.body.innerHTML = '
' const misspelled: unknown = { target: '#root', companyIdentifier: 'acme', webMCP: { enabled: true, exclude: ['sumbit'] } } // @ts-expect-error exercising the runtime guard for untyped JS callers - expect(() => createEmbed(misspelled)).toThrow(/webMCP\.exclude names no tool: sumbit \(known: createField/) + expect(() => createEmbed(misspelled)).toThrow(/webMCP\.exclude names no tool: sumbit \(known: batchSetFieldValues, createField/) }) }) diff --git a/embed/test/tanstack-ai.test.ts b/embed/test/tanstack-ai.test.ts index 1b93ada9..4f2658cc 100644 --- a/embed/test/tanstack-ai.test.ts +++ b/embed/test/tanstack-ai.test.ts @@ -4,9 +4,9 @@ import type { BridgeResult } from '../src/types' import { makeEmbedStub } from './helpers' describe('simplePDFToolDefinitions', () => { - it('returns the 15 agentic operations as execute-less definitions (loadDocument excluded)', () => { + it('returns the 16 agentic operations as execute-less definitions (loadDocument excluded)', () => { const definitions = simplePDFToolDefinitions() - expect(definitions).toHaveLength(15) + expect(definitions).toHaveLength(16) expect(definitions.map((definition) => definition.name)).not.toContain('loadDocument') for (const definition of definitions) { expect(typeof definition.description).toBe('string') @@ -16,9 +16,9 @@ describe('simplePDFToolDefinitions', () => { }) describe('createSimplePDFTools', () => { - it('produces a client tool for each of the 15 agentic operations', () => { + it('produces a client tool for each of the 16 agentic operations', () => { const tools = createSimplePDFTools({ embed: makeEmbedStub() }) - expect(tools).toHaveLength(15) + expect(tools).toHaveLength(16) expect(tools.every((tool) => typeof tool.execute === 'function')).toBe(true) }) diff --git a/embed/test/tools.test.ts b/embed/test/tools.test.ts index dfb4639e..955742bb 100644 --- a/embed/test/tools.test.ts +++ b/embed/test/tools.test.ts @@ -17,15 +17,21 @@ describe(isSimplePDFToolName.name, () => { }) describe('SIMPLEPDF_TOOLS', () => { - it('exposes the 15 agentic operations with descriptions + input schemas (loadDocument excluded)', () => { + it('exposes the 16 agentic operations with descriptions + input schemas (loadDocument excluded)', () => { const names = Object.keys(SIMPLEPDF_TOOLS) - expect(names).toHaveLength(15) + expect(names).toHaveLength(16) expect(names).not.toContain('loadDocument') for (const definition of Object.values(SIMPLEPDF_TOOLS)) { expect(typeof definition.description).toBe('string') expect(definition.inputSchema).toBeDefined() } }) + + it('carries each description on the tool only, so the model never receives it twice', () => { + for (const definition of Object.values(SIMPLEPDF_TOOLS)) { + expect(definition.inputSchema.description).toBeUndefined() + } + }) }) describe(routeToolCall.name, () => { diff --git a/embed/test/webmcp.test.ts b/embed/test/webmcp.test.ts index a6b10f24..50c97fef 100644 --- a/embed/test/webmcp.test.ts +++ b/embed/test/webmcp.test.ts @@ -166,7 +166,7 @@ describe('attachEmbed({ webMCP })', () => { // loadDocument is a host-page tool like it is in the editor's own registration. expect(modelContext.liveToolNames()).toContain('simplepdf_embed_load_document') const setFieldValue = findTool(modelContext, 'simplepdf_embed_set_field_value') - expect(setFieldValue.description).toMatch(/^Set the value of an existing field/) + expect(setFieldValue.description).toBe(WEBMCP_TOOLS.setFieldValue.description) expect(setFieldValue.inputSchema.type).toBe('object') expect(Object.keys(setFieldValue.inputSchema.properties ?? {})).toEqual(['animate', 'field_id', 'value']) expect(setFieldValue.inputSchema.required).toEqual(['field_id', 'value']) diff --git a/embed/tsup.config.ts b/embed/tsup.config.ts index 656dedd5..d86ffe95 100644 --- a/embed/tsup.config.ts +++ b/embed/tsup.config.ts @@ -19,7 +19,6 @@ export default defineConfig({ dts: true, treeshake: true, splitting: true, - sourcemap: true, clean: true, external: ['zod', '@tanstack/ai'], }) diff --git a/package-lock.json b/package-lock.json index 8a1c04a2..e989ba6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ }, "embed": { "name": "@simplepdf/embed", - "version": "0.7.0", + "version": "0.8.0", "license": "MIT", "devDependencies": { "@tanstack/ai": "^0.38.0", @@ -5783,13 +5783,12 @@ }, "react": { "name": "@simplepdf/react-embed-pdf", - "version": "1.13.0", + "version": "1.13.1", "license": "MIT", "dependencies": { - "@simplepdf/embed": "0.7.0" + "@simplepdf/embed": "0.8.0" }, "devDependencies": { - "@rollup/plugin-terser": "^0.4.4", "@tanstack/ai": "^0.38.0", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -6741,10 +6740,10 @@ }, "web": { "name": "@simplepdf/web-embed-pdf", - "version": "1.8.4", + "version": "1.9.1", "license": "MIT", "dependencies": { - "@simplepdf/embed": "0.7.0" + "@simplepdf/embed": "0.8.0" }, "devDependencies": { "@rollup/plugin-node-resolve": "^16.0.3", diff --git a/react/README.md b/react/README.md index d4b87292..cccdbf70 100644 --- a/react/README.md +++ b/react/README.md @@ -155,6 +155,7 @@ Actions are camelCase (the editor's snake_case wire is transformed for you). `us | `actions.deleteFields({ fieldIds?, page? })` | Delete fields by id or page, or all fields if both are omitted | | `actions.getDocumentContent({ extractionMode })` | Extract document content (`'auto'` or `'ocr'`) | | `actions.setFieldValue({ fieldId, value })` | Set a field's value | +| `actions.batchSetFieldValues({ updates })` | Set several fields at once (`updates: [{ fieldId, value }]`): all apply, or none do | | `actions.submit({ downloadCopy })` | Submit the document | …plus `createField`, `getFields`, `getAnnotatedPage`, `focusField`, `movePage`, `rotatePage`, `deletePages`, `download`, and `loadDocument`. All actions return a `Promise` with a result object: `{ success: true, data: ... }` or `{ success: false, error: { code, message } }`. diff --git a/react/package.json b/react/package.json index c05513b9..19a66ee9 100644 --- a/react/package.json +++ b/react/package.json @@ -66,7 +66,6 @@ } }, "devDependencies": { - "@rollup/plugin-terser": "^0.4.4", "@tanstack/ai": "^0.38.0", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "^6.9.1", diff --git a/react/rollup.config.js b/react/rollup.config.js index c2b37d14..066c7647 100644 --- a/react/rollup.config.js +++ b/react/rollup.config.js @@ -2,7 +2,6 @@ import scss from 'rollup-plugin-scss'; import postcss from 'postcss'; import autoprefixer from 'autoprefixer'; import typescript from 'rollup-plugin-typescript2'; -import terser from '@rollup/plugin-terser'; // Dual CJS + ESM (so existing require() consumers keep working — this is a non-breaking minor). // react / react-dom / zod / the core are external (peer or dependency), resolved at the @@ -24,7 +23,6 @@ export default { dir: 'dist', format: 'cjs', exports: 'named', - sourcemap: true, entryFileNames: '[name].cjs', chunkFileNames: 'chunks/[name]-[hash].cjs', }, @@ -32,7 +30,6 @@ export default { dir: 'dist', format: 'es', exports: 'named', - sourcemap: true, entryFileNames: '[name].esm.js', chunkFileNames: 'chunks/[name]-[hash].esm.js', }, @@ -50,11 +47,6 @@ export default { exclude: ['node_modules', 'src/**/*.test.ts', 'src/**/*.test.tsx', 'src/**/*.test-d.ts', 'vitest.setup.ts'], }, }), - terser({ - format: { - comments: false, - }, - }), ], external: isExternal, }; diff --git a/react/src/embed-pdf.tsx b/react/src/embed-pdf.tsx index 5a78eec0..f4ee7bac 100644 --- a/react/src/embed-pdf.tsx +++ b/react/src/embed-pdf.tsx @@ -408,6 +408,7 @@ export const useEmbed = (): { const actions = React.useMemo( () => ({ + batchSetFieldValues: (input) => embedRef.current?.batchSetFieldValues(input) ?? notMounted(), createField: (input) => embedRef.current?.createField(input) ?? notMounted(), deleteFields: (input) => embedRef.current?.deleteFields(input) ?? notMounted(), deletePages: (input) => embedRef.current?.deletePages(input) ?? notMounted(), diff --git a/react/tsconfig.json b/react/tsconfig.json index 2ff760f6..1181cfa3 100644 --- a/react/tsconfig.json +++ b/react/tsconfig.json @@ -4,7 +4,6 @@ "module": "esnext", "target": "es5", "lib": ["es6", "dom"], - "sourceMap": true, "allowJs": false, "jsx": "react", "declaration": true, diff --git a/web/package.json b/web/package.json index 9dbc7c6a..7787b356 100644 --- a/web/package.json +++ b/web/package.json @@ -13,7 +13,6 @@ }, "type": "module", "main": "dist/index.js", - "browser": "dist/index.min.js", "unpkg": "dist/index.min.js", "types": "dist/index.d.ts", "author": "bendersej", diff --git a/web/tsconfig.json b/web/tsconfig.json index 3a8e67f6..098d6f4c 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -4,7 +4,6 @@ "module": "esnext", "target": "es5", "lib": ["es2020", "dom"], - "sourceMap": true, "allowJs": false, "jsx": "react", "declaration": true,