Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/embed-company-identifier-snake-operations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@simplepdf/embed": minor
---

camelCase SDK surface grouped into `actions` / `events` / `lifecycle`, `companyIdentifier`, and direct loading of SimplePDF documents URLs.

- **Grouped handle**: `createEmbed` returns `{ actions, events, lifecycle }` — `embed.actions.*` (operations), `embed.events.on(type, handler)` (subscriptions), `embed.lifecycle.dispose()` (teardown).
- **camelCase everywhere on the SDK**, with the snake_case wire kept behind a transform owned by the bridge: method names + their arguments + results + the agentic tool names/args are camelCase (`embed.actions.getFields()`, `embed.actions.setFieldValue({ fieldId, value })`, `embed.actions.submit({ downloadCopy })`, `tools.getDocumentContent`). The editor's snake_case wire is generated from `embed-api.json` and transformed at the postMessage boundary — consumers never see it.
- **Events are the deliberate exception**: `embed.events.on(type, handler)` delivers the editor's outbound payloads VERBATIM (snake_case fields, e.g. `document_id`) for `EDITOR_READY` / `DOCUMENT_LOADED` / `PAGE_FOCUSED` / `SUBMISSION_SENT`, so the React layer's `onEmbedEvent` is unchanged.
- **`companyIdentifier`** replaces `tenant` in `createEmbed` (it is the consumer's own SimplePDF subdomain — `tenant` read as if SimplePDF were multi-tenant per consumer).
- **Documents URLs load directly**: when `document.url` is a `<tenant>.<baseDomain>/documents/<id>` URL (https, single tenant label), `createEmbed` navigates the iframe straight to it (carrying `?context=`) instead of host-fetching — so prefilled/stored documents open as themselves.
- The React layer moved OUT of this package into `@simplepdf/react-embed-pdf` (the `/react` subpath is removed); the editor iframe is granted `clipboard-read; clipboard-write` by default.
23 changes: 23 additions & 0 deletions .changeset/react-embed-pdf-2-core-rebuild.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@simplepdf/react-embed-pdf": minor
---

Rebuilt on the `@simplepdf/embed` core, adding an AI-SDK-native agentic surface — a non-breaking superset of the existing component API.

`@simplepdf/react-embed-pdf` no longer hand-rolls its own iframe bridge; it is a thin React layer over the shared `@simplepdf/embed` core (the same core `web-embed-pdf` and future framework adapters sit on).

**The existing `<EmbedPDF>` contract is preserved (drop-in):** the props (`companyIdentifier`, `documentURL`, `mode` — still defaulting to `"modal"`, `onEmbedEvent`, `locale`, `baseDomain`, `context`, `className`, `style`) and, crucially, `onEmbedEvent` still emits the editor's events VERBATIM: `{ type: 'EDITOR_READY' | 'DOCUMENT_LOADED' | 'PAGE_FOCUSED' | 'SUBMISSION_SENT', data }` with snake_case payloads. `useEmbed()` still returns `{ embedRef, actions }`.

**New (additive):**

- A new opt-in `@simplepdf/react-embed-pdf/ai-sdk` subpath exposes the agentic surface: `useEmbedTools(embedRef)` binds the tool registry to the live editor for the Vercel AI SDK (`useChat({ tools })`), plus `simplePDFToolDefinitions` (server) and `createSimplePDFExecutor`. It mirrors `@simplepdf/embed`'s `/ai-sdk`, so the package root stays zod-free.
- `useEmbed().actions` now exposes the FULL editor surface (camelCase): `createField`, `getFields`, `setFieldValue`, `focusField`, `movePage`, `rotatePage`, `deletePages`, `download`, … — not just the original six.
- A typed `document` prop (`{ url } | { dataUrl } | { file }`), the same shape as `createEmbed`. It also accepts data URLs and File/Blob, and a SimplePDF documents URL loads directly (prefill etc.). `documentURL` is now `@deprecated` (still works) in favor of it.
- An optional `logger` prop surfaces the bridge's structured lifecycle/error logging.
- The forwarded `ref` (`embedRef.current`) stays the flat actions handle — `embedRef.current.selectTool(...)`, etc. — now exposing the full camelCase action set. (The framework-free `@simplepdf/embed` core groups its handle as `embed.actions` / `embed.events` / `embed.lifecycle`; the React layer flattens it to keep the existing ref contract.)

**Imperative actions stay backward-compatible.** `selectTool` and `submit` gained camelCase argument shapes to match the rest of the SDK (`selectTool({ tool })`, `submit({ downloadCopy })`), but the previous forms — `selectTool(toolType)` and `submit({ downloadCopyOnDevice })` — still work as deprecated overloads that normalize to the new shape, so existing `useEmbed().actions` callers don't change. A relative `documentURL` / trigger `href` (e.g. `/form.pdf`) is still accepted — it is resolved against the page URL, as before.

One behavioral note: calling an action before `<EmbedPDF>` has mounted now resolves to `{ success: false, error: { code: 'unexpected:iframe_not_mounted' } }` (the previous form returned `bad_request:embed_ref_not_available`). Code that checks `result.success` is unaffected; only code branching on the exact pre-mount error string needs updating.

Packaging is preserved: still dual CJS + ESM, so `require()` consumers keep working. `zod` remains a peer dependency, now required **only** by the agentic `/ai-sdk` subpath (it validates tool input) — the package root (`<EmbedPDF>`, `useEmbed`) is zod-free, so a non-agentic app never loads it. Install `zod` only if you import `/ai-sdk`; npm 7+ adds it automatically, pnpm / Yarn PnP users add it explicitly.
15 changes: 0 additions & 15 deletions .changeset/rename-boxed-text-to-comb-text.md

This file was deleted.

21 changes: 0 additions & 21 deletions .changeset/rename-remove-fields-to-delete-fields.md

This file was deleted.

15 changes: 0 additions & 15 deletions .changeset/replace-create-field-with-detect-fields.md

This file was deleted.

41 changes: 41 additions & 0 deletions .github/workflows/embed.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Embed

on:
push:
branches:
- main
paths:
- embed/**
- .github/workflows/embed.yaml
pull_request:
branches:
- main
paths:
- embed/**
- .github/workflows/embed.yaml

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

# @simplepdf/embed is a workspace package, so install from the root.
- name: Install dependencies
run: npm ci

- name: Types
run: npm run --workspace @simplepdf/embed test:types

- name: Tests
run: npm run --workspace @simplepdf/embed test

- name: Bundle size budgets
run: npm run --workspace @simplepdf/embed check:size
17 changes: 11 additions & 6 deletions .github/workflows/react.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ on:
- main
paths:
- react/**
- embed/**
- .github/workflows/react.yaml
pull_request:
branches:
- main
paths:
- react/**
- embed/**
- .github/workflows/react.yaml

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: react

steps:
- name: Checkout code
Expand All @@ -30,14 +29,20 @@ jobs:
with:
node-version: "22"

# react-embed-pdf depends on the @simplepdf/embed workspace package, so install
# from the workspace ROOT (the internal dep resolves via the workspace, not npm)
# rather than a per-package `npm ci`, and build the core before type-checking.
- name: Install dependencies
run: npm ci

- name: Build the core
run: npm run --workspace @simplepdf/embed build

- name: Formatting
run: npm run test:format
run: npm run --workspace @simplepdf/react-embed-pdf test:format

- name: Types
run: npm run test:types
run: npm run --workspace @simplepdf/react-embed-pdf test:types

- name: Tests
run: npm test
run: npm run --workspace @simplepdf/react-embed-pdf test
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ https://github.com/SimplePDF/simplepdf-embed/assets/10613140/8924f018-6076-4e44-

# Get started

- 🧩 [Typed bridge](./embed/README.md) - `@simplepdf/embed` (typed client + React hook + AI SDK adapter, generated from the editor manifest)
- 🧩 [Iframe bridge](./embed/README.md) - `@simplepdf/embed` (framework-free client to embed + programmatically drive the editor, with an AI SDK adapter, generated from the editor manifest; the React layer is `@simplepdf/react-embed-pdf`)
- ⚛️ [React component](./react/README.md) - `@simplepdf/react-embed-pdf`
- 🚀 [Script tag](./web/README.md) - `@simplepdf/web-embed-pdf`
- 🛠 [Iframe API](./documentation/IFRAME.md) - `postMessage` events
Expand Down Expand Up @@ -146,7 +146,7 @@ With a [Pro plan](https://simplepdf.com/pricing), you can:

```jsx
// React - branding configured in your dashboard settings
<EmbedPDF companyIdentifier="yourcompany">
<EmbedPDF mode="modal" companyIdentifier="yourcompany">
<button>Edit PDF</button>
</EmbedPDF>
```
Expand Down Expand Up @@ -252,16 +252,16 @@ Use `getDocumentContent()` to extract text from the PDF. See the [React](./react

### Downloading the modified PDF

Use `submit({ downloadCopyOnDevice: true })` to trigger a browser download of the modified PDF.
Use `submit({ downloadCopy: true })` to trigger a browser download of the modified PDF.

### Server-side PDF generation & storage

SimplePDF handles PDF generation and storage so you don't have to. When users submit, the filled PDF is automatically generated and stored - either on SimplePDF's servers or your own storage.

| Method | How it works | Use case |
| ------------------------------------------- | ----------------------------- | ------------------------------------ |
| `submit` with `downloadCopyOnDevice: true` | Browser downloads the PDF | End-user saves their work |
| `submit` with `downloadCopyOnDevice: false` | PDF sent to SimplePDF servers | Server-side collection via webhooks |
| `submit` with `downloadCopy: true` | Browser downloads the PDF | End-user saves their work |
| `submit` with `downloadCopy: false` | PDF sent to SimplePDF servers | Server-side collection via webhooks |
| S3/Azure/SharePoint integration | PDF stored in your storage | Programmatic access via your storage |

**Available integrations:**
Expand Down Expand Up @@ -349,7 +349,7 @@ Yes. Use viewer mode to display PDFs without any editing capabilities.
<EmbedPDF
companyIdentifier="react-viewer"
mode="inline"
documentURL="https://example.com/document.pdf"
document={{ url: 'https://example.com/document.pdf' }}
style={{ width: 900, height: 800 }}
/>
```
Expand Down
22 changes: 11 additions & 11 deletions copilot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ See the privacy notes above for the per-route audio-egress disclosure.

### Load a specific document via `?url=`

To open a specific document instead of the bundled demo forms, append `?url=<document-url>`. The value is used verbatim as the editor iframe `src`, so pass a valid SimplePDF document URL (e.g. a `/documents/<id>` link, optionally with a `?prefill=<id>`):
To open a specific document instead of the bundled demo forms, append `?url=<document-url>`. The value is passed as the `document` to `<EmbedPDF>`; a SimplePDF documents URL (a `/documents/<id>` link, optionally with a `?prefill=<id>`) is navigated to directly by the embed core, so pass a valid one:

```
http://localhost:3001/?url=https%3A%2F%2Fdemo.simplepdf.com%2Fdocuments%2Fc28f061b-1974-4251-ba7a-d08bedc3ef28%3Fprefill%3D35fdf39e-2e06-4712-bb9d-f62d2f88ce50
Expand Down Expand Up @@ -212,16 +212,16 @@ The chat sidebar advertises these tools to the model. Each runs inside the ifram

| Tool | Purpose |
|------|---------|
| `get_fields` | List form fields currently on the document |
| `get_document_content` | Extract text content per page |
| `detect_fields` | Auto-detect missing fields on scanned PDFs |
| `focus_field` | Highlight + scroll to a field |
| `set_field_value` | Write a value into a field |
| `select_tool` | Switch the editor toolbar (`TEXT`, `COMB_TEXT`, `CHECKBOX`, `SIGNATURE`, `PICTURE`) |
| `go_to` | Navigate to a specific page (1-indexed) |
| `move_page` | Reorder a visible page (`from_page` → `to_page`, both 1-indexed). Destructive — only fired on explicit user request |
| `delete_page` | Remove a visible page and its fields (last remaining page can't be deleted). Destructive — only fired on explicit user request |
| `rotate_page` | Rotate a visible page 90° clockwise per call. Destructive — only fired on explicit user request |
| `getFields` | List form fields currently on the document |
| `getDocumentContent` | Extract text content per page |
| `detectFields` | Auto-detect missing fields on scanned PDFs |
| `focusField` | Highlight + scroll to a field |
| `setFieldValue` | Write a value into a field |
| `selectTool` | Switch the editor toolbar (`TEXT`, `COMB_TEXT`, `CHECKBOX`, `SIGNATURE`, `PICTURE`) |
| `goTo` | Navigate to a specific page (1-indexed) |
| `movePage` | Reorder a visible page (`fromPage` → `toPage`, both 1-indexed). Destructive — only fired on explicit user request |
| `deletePages` | Remove visible pages and their fields (last remaining page can't be deleted). Destructive — only fired on explicit user request |
| `rotatePage` | Rotate a visible page 90° clockwise per call. Destructive — only fired on explicit user request |
| `submit` (Pro mode) / `download` (demo mode) | Finalize: real iframe `SUBMIT` on a Pro fork (lands in BYOS + webhooks) vs. an in-browser `DOWNLOAD` on the hosted demo |

Tool input + output schemas + the bridge that posts these events into the iframe live in the [`@simplepdf/embed`](../embed) package (generated from the editor contract); copilot's tool catalogue + middleware live in `src/lib/tools/` (`definitions.ts`, `middleware.ts`). System prompt: `src/server/tools.ts`. Public iframe contract these tools exercise: [`documentation/IFRAME.md`](../documentation/IFRAME.md).
Expand Down
3 changes: 2 additions & 1 deletion copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"@ai-sdk/openai": "^3.0.53",
"@ai-sdk/react": "^3.0.170",
"@aws-sdk/client-s3": "^3.1034.0",
"@simplepdf/embed": "^0.4.0",
"@simplepdf/embed": "0.5.0",
"@simplepdf/react-embed-pdf": "1.11.0",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "latest",
"@tanstack/react-router": "latest",
Expand Down
Loading
Loading