├── .dockerignore ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── package-lock.json ├── package.json ├── packages ├── angular │ ├── angular.json │ ├── bin │ │ └── build.sh │ ├── ng-package.json │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── create-document.ts │ │ ├── create-template.ts │ │ ├── css-vars.ts │ │ ├── direct-template.ts │ │ ├── index.ts │ │ ├── multisign-document.ts │ │ ├── sign-document.ts │ │ ├── trusted-resource-url-pipe.ts │ │ ├── update-document.ts │ │ └── update-template.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── util │ │ └── trusted-resource-url-pipe.ts ├── mitosis │ ├── mitosis.config.cjs │ ├── package.json │ ├── src │ │ ├── create-document.lite.tsx │ │ ├── create-template.lite.tsx │ │ ├── css-vars.ts │ │ ├── direct-template.lite.tsx │ │ ├── index.ts │ │ ├── multisign-document.lite.tsx │ │ ├── sign-document.lite.tsx │ │ ├── update-document.lite.tsx │ │ └── update-template.lite.tsx │ └── tsconfig.json ├── preact │ ├── package.json │ ├── src │ │ ├── create-document.tsx │ │ ├── create-template.tsx │ │ ├── css-vars.ts │ │ ├── direct-template.tsx │ │ ├── index.ts │ │ ├── multisign-document.tsx │ │ ├── sign-document.tsx │ │ ├── update-document.tsx │ │ └── update-template.tsx │ ├── tsconfig.json │ └── vite.config.mts ├── react │ ├── package.json │ ├── src │ │ ├── create-document.tsx │ │ ├── create-template.tsx │ │ ├── css-vars.ts │ │ ├── direct-template.tsx │ │ ├── index.ts │ │ ├── multisign-document.tsx │ │ ├── sign-document.tsx │ │ ├── update-document.tsx │ │ └── update-template.tsx │ ├── tsconfig.json │ └── vite.config.mts ├── solid │ ├── package.json │ ├── src │ │ ├── create-document.tsx │ │ ├── create-template.tsx │ │ ├── css-vars.ts │ │ ├── direct-template.tsx │ │ ├── index.ts │ │ ├── multisign-document.tsx │ │ ├── sign-document.tsx │ │ ├── update-document.tsx │ │ └── update-template.tsx │ ├── tsconfig.json │ └── vite.config.mts ├── svelte │ ├── package.json │ ├── src │ │ ├── create-document.svelte │ │ ├── create-template.svelte │ │ ├── css-vars.ts │ │ ├── direct-template.svelte │ │ ├── index.ts │ │ ├── multisign-document.svelte │ │ ├── sign-document.svelte │ │ ├── update-document.svelte │ │ └── update-template.svelte │ ├── svelte.config.mjs │ ├── tsconfig.json │ └── vite.config.mts ├── vue │ ├── auto-imports.d.ts │ ├── package.json │ ├── src │ │ ├── create-document.vue │ │ ├── create-template.vue │ │ ├── css-vars.ts │ │ ├── direct-template.vue │ │ ├── index.ts │ │ ├── multisign-document.vue │ │ ├── sign-document.vue │ │ ├── update-document.vue │ │ └── update-template.vue │ ├── tsconfig.json │ └── vite.config.mts └── webcomponent │ ├── package.json │ ├── src │ └── index.ts │ ├── tsconfig.json │ └── vite.config.mts ├── playground ├── .env.example ├── .gitignore ├── .npmrc ├── README.md ├── components.json ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── public │ └── vite.svg ├── src │ ├── App.css │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── embeddings │ │ │ └── multisign.tsx │ │ └── ui │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── carousel.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── pagination.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ └── tooltip.tsx │ ├── hooks │ │ └── use-mobile.ts │ ├── index.css │ ├── lib │ │ └── utils.ts │ ├── main.tsx │ └── vite-env.d.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── prettier.config.cjs ├── scripts ├── build.sh └── version.sh └── tooling └── typescript ├── base.json └── react-library.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | #angular 12 | .angular 13 | 14 | # next.js 15 | .next/ 16 | out/ 17 | build 18 | dist/ 19 | 20 | # sveltekit 21 | .svelte-kit 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env 34 | .env.local 35 | .env.development.local 36 | .env.test.local 37 | .env.production.local 38 | 39 | # turbo 40 | .turbo 41 | .turbo-cookie 42 | 43 | # vercel 44 | .vercel 45 | 46 | # contentlayer 47 | .contentlayer 48 | 49 | # intellij 50 | .idea 51 | 52 | # vscode 53 | .vscode/* 54 | !.vscode/settings.json 55 | !.vscode/tasks.json 56 | !.vscode/launch.json 57 | !.vscode/extensions.json 58 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Config files 2 | *.config.js 3 | *.config.cjs 4 | 5 | # Statically hosted javascript files 6 | apps/*/public/*.js 7 | apps/*/public/*.cjs 8 | scripts/ 9 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | env: { 4 | es2022: true, 5 | node: true, 6 | browser: true, 7 | }, 8 | parser: '@typescript-eslint/parser', 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:@typescript-eslint/recommended', 12 | 'plugin:prettier/recommended', 13 | ], 14 | plugins: ['@typescript-eslint'], 15 | parserOptions: { 16 | sourceType: 'module', 17 | ecmaVersion: 2020, 18 | }, 19 | rules: { 20 | 'no-unused-vars': 'off', 21 | '@typescript-eslint/no-unused-vars': [ 22 | 'error', 23 | { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, 24 | ], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Package 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node: [18, 20] 9 | name: Build on Node ${{ matrix.node }} 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Setup node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: ${{ matrix.node }} 16 | registry-url: 'https://registry.npmjs.org' 17 | cache: 'npm' 18 | - run: npm install 19 | - run: ./scripts/build.sh 20 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v3 11 | with: 12 | node-version: '20.x' 13 | registry-url: 'https://registry.npmjs.org' 14 | cache: 'npm' 15 | - run: npm install 16 | - run: ./scripts/build.sh 17 | - run: npm publish --workspaces --no-git-checks 18 | env: 19 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test Package 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node: [18, 20] 9 | name: Test on Node ${{ matrix.node }} 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Setup node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: ${{ matrix.node }} 16 | registry-url: 'https://registry.npmjs.org' 17 | cache: 'npm' 18 | - run: npm install 19 | - run: ./scripts/build.sh 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | #angular 12 | .angular 13 | 14 | # next.js 15 | .next/ 16 | out/ 17 | build 18 | dist/ 19 | 20 | # sveltekit 21 | .svelte-kit 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env 34 | .env.local 35 | .env.development.local 36 | .env.test.local 37 | .env.production.local 38 | 39 | # turbo 40 | .turbo 41 | .turbo-cookie 42 | 43 | # vercel 44 | .vercel 45 | 46 | # contentlayer 47 | .contentlayer 48 | 49 | # intellij 50 | .idea 51 | 52 | # vscode 53 | .vscode/* 54 | !.vscode/settings.json 55 | !.vscode/tasks.json 56 | !.vscode/launch.json 57 | !.vscode/extensions.json 58 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | public 4 | **/**/node_modules 5 | **/**/.next 6 | **/**/public 7 | packages/lib/translations/**/*.js 8 | 9 | packages/angular/src 10 | packages/react/src 11 | packages/preact/src 12 | packages/solid/src 13 | packages/svelte/src 14 | packages/vue/src 15 | 16 | *.lock 17 | *.log 18 | *.test.ts 19 | 20 | .gitignore 21 | .npmignore 22 | .prettierignore 23 | .DS_Store 24 | .eslintignore 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Documenso Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Documenso Embeds 2 | 3 | Documenso Embeds provides native embedding packages for the Documenso application. These packages allow developers to easily integrate the document signing capabilities of Documenso into their applications. 4 | Check out the docs here: https://docs.documenso.com/developers/embedding 5 | 6 | ## Purpose 7 | 8 | This project aims to provide easy-to-use components for embedding Documenso's signing capabilities into your own applications and websites. It allows developers to seamlessly integrate document signing features into their applications, regardless of the framework they're using. 9 | 10 | ## Supported Libraries 11 | 12 | Documenso Embed supports multiple frontend frameworks and technologies: 13 | 14 | - React 15 | - Vue 16 | - Svelte 17 | - Preact 18 | - Solid 19 | - Web Components 20 | 21 | All of the above is powered by Mitosis which is a framework for building libraries and components that can run anywhere. 22 | 23 | ## Support 24 | 25 | If you have any questions or need help with the project, please raise an issue on GitHub. If you are an enterprise customer, you may also reach out to us on our dedicated slack channel or via email. 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documenso-embed", 3 | "version": "0.0.0", 4 | "private": true, 5 | "workspaces": [ 6 | "packages/*" 7 | ], 8 | "scripts": { 9 | "build": "./scripts/build.sh", 10 | "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,scss,md,html,svelte,vue,yaml,yml}\"", 11 | "make:version": "npm version --workspaces --no-git-tag-version -m \"v%s\"" 12 | }, 13 | "license": "MIT", 14 | "description": "", 15 | "devDependencies": { 16 | "@arethetypeswrong/cli": "^0.15.4", 17 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 18 | "@types/node": "^20.14.2", 19 | "@typescript-eslint/eslint-plugin": "^7.12.0", 20 | "@typescript-eslint/parser": "^7.12.0", 21 | "eslint": "^8.57.0", 22 | "eslint-config-prettier": "^9.1.0", 23 | "eslint-plugin-prettier": "^5.1.3", 24 | "eslint-plugin-react": "^7.34.2", 25 | "eslint-plugin-react-hooks": "^4.6.2", 26 | "prettier": "^3.3.1", 27 | "prettier-plugin-sql": "^0.18.0", 28 | "prettier-plugin-svelte": "^3.3.2", 29 | "prettier-plugin-tailwindcss": "^0.6.1", 30 | "publint": "^0.2.10", 31 | "rimraf": "^6.0.1", 32 | "tsconfig-paths": "^4.2.0", 33 | "typescript": "~5.5.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/angular/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular": { 7 | "projectType": "library", 8 | "root": "./", 9 | "sourceRoot": "./src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "./ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "./tsconfig.lib.json" 20 | } 21 | }, 22 | "defaultConfiguration": "production" 23 | } 24 | } 25 | } 26 | }, 27 | "cli": { 28 | "analytics": false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/angular/bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")/.." 6 | 7 | # Replace or copy ./util/trusted-resource-url-pipe.ts to ./src/trusted-resource-url-pipe.ts 8 | cp ./util/trusted-resource-url-pipe.ts ./src/trusted-resource-url-pipe.ts 9 | 10 | # Array of files to process 11 | files=('./src/direct-template.ts' './src/sign-document.ts' './src/create-document.ts' './src/create-template.ts') 12 | 13 | # Detect OS type 14 | OS_TYPE=$(uname) 15 | 16 | # Loop over the array of files 17 | for file in "${files[@]}" 18 | do 19 | if [ "$OS_TYPE" = "Darwin" ]; then 20 | # macOS (BSD) sed commands 21 | sed -i '' 's/; 85 | } 86 | -------------------------------------------------------------------------------- /packages/mitosis/src/create-template.lite.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, onUnMount, useRef, useStore } from '@builder.io/mitosis'; 2 | 3 | import { CssVars } from './css-vars'; 4 | 5 | export type EmbedCreateTemplateProps = { 6 | className?: string; 7 | host?: string; 8 | presignToken: string; 9 | externalId?: string; 10 | 11 | // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 12 | css?: string | undefined; 13 | cssVars?: (CssVars & Record) | undefined; 14 | 15 | darkModeDisabled?: boolean | undefined; 16 | 17 | features?: { 18 | allowConfigureSignatureTypes?: boolean; 19 | allowConfigureLanguage?: boolean; 20 | allowConfigureDateFormat?: boolean; 21 | allowConfigureTimezone?: boolean; 22 | allowConfigureRedirectUrl?: boolean; 23 | allowConfigureCommunication?: boolean; 24 | }; 25 | 26 | // Additional props to be passed to the iframe, used for testing out features 27 | // prior to being added to the main props 28 | additionalProps?: Record | undefined; 29 | 30 | onTemplateCreated?: (data: { externalId: string; templateId: number }) => void; 31 | }; 32 | 33 | export default function EmbedCreateTemplate(props: EmbedCreateTemplateProps) { 34 | const __iframe = useRef(null); 35 | 36 | const state = useStore({ 37 | get src() { 38 | const appHost = props.host || 'https://app.documenso.com'; 39 | 40 | const encodedOptions = btoa( 41 | encodeURIComponent( 42 | JSON.stringify({ 43 | externalId: props.externalId, 44 | features: props.features, 45 | 46 | css: props.css, 47 | cssVars: props.cssVars, 48 | darkModeDisabled: props.darkModeDisabled, 49 | ...props.additionalProps, 50 | }), 51 | ), 52 | ); 53 | 54 | const srcUrl = new URL(`/embed/v1/authoring/template/create`, appHost); 55 | 56 | srcUrl.searchParams.set('token', props.presignToken); 57 | srcUrl.hash = encodedOptions; 58 | 59 | return srcUrl.toString(); 60 | }, 61 | 62 | handleMessage(event: MessageEvent) { 63 | if (__iframe?.contentWindow === event.source) { 64 | switch (event.data.type) { 65 | case 'template-created': 66 | props.onTemplateCreated?.({ 67 | templateId: event.data.templateId, 68 | externalId: event.data.externalId, 69 | }); 70 | break; 71 | } 72 | } 73 | }, 74 | }); 75 | 76 | onMount(() => { 77 | window.addEventListener('message', state.handleMessage); 78 | }); 79 | 80 | onUnMount(() => { 81 | window.removeEventListener('message', state.handleMessage); 82 | }); 83 | 84 | return ; 85 | } 86 | -------------------------------------------------------------------------------- /packages/mitosis/src/css-vars.ts: -------------------------------------------------------------------------------- 1 | export type CssVars = { 2 | /** Base background color */ 3 | background?: string | null; 4 | /** Base text color */ 5 | foreground?: string | null; 6 | /** Muted/subtle background color */ 7 | muted?: string | null; 8 | /** Muted/subtle text color */ 9 | mutedForeground?: string | null; 10 | /** Popover/dropdown background color */ 11 | popover?: string | null; 12 | /** Popover/dropdown text color */ 13 | popoverForeground?: string | null; 14 | /** Card background color */ 15 | card?: string | null; 16 | /** Card border color */ 17 | cardBorder?: string | null; 18 | /** Card border tint/highlight color */ 19 | cardBorderTint?: string | null; 20 | /** Card text color */ 21 | cardForeground?: string | null; 22 | /** Field card background color */ 23 | fieldCard?: string | null; 24 | /** Field card border color */ 25 | fieldCardBorder?: string | null; 26 | /** Field card text color */ 27 | fieldCardForeground?: string | null; 28 | /** Widget background color */ 29 | widget?: string | null; 30 | /** Widget text color */ 31 | widgetForeground?: string | null; 32 | /** Default border color */ 33 | border?: string | null; 34 | /** Input field border color */ 35 | input?: string | null; 36 | /** Primary action/button color */ 37 | primary?: string | null; 38 | /** Primary action/button text color */ 39 | primaryForeground?: string | null; 40 | /** Secondary action/button color */ 41 | secondary?: string | null; 42 | /** Secondary action/button text color */ 43 | secondaryForeground?: string | null; 44 | /** Accent/highlight color */ 45 | accent?: string | null; 46 | /** Accent/highlight text color */ 47 | accentForeground?: string | null; 48 | /** Destructive/danger action color */ 49 | destructive?: string | null; 50 | /** Destructive/danger text color */ 51 | destructiveForeground?: string | null; 52 | /** Focus ring color */ 53 | ring?: string | null; 54 | /** Border radius size in REM units */ 55 | radius?: string | null; 56 | /** Warning/alert color */ 57 | warning?: string | null; 58 | }; 59 | -------------------------------------------------------------------------------- /packages/mitosis/src/direct-template.lite.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, onUnMount, useRef, useStore } from '@builder.io/mitosis'; 2 | 3 | import { CssVars } from './css-vars'; 4 | 5 | export type EmbedDirectTemplateProps = { 6 | className?: string; 7 | host?: string; 8 | token: string; 9 | externalId?: string; 10 | 11 | // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 12 | css?: string | undefined; 13 | cssVars?: (CssVars & Record) | undefined; 14 | 15 | darkModeDisabled?: boolean | undefined; 16 | 17 | email?: string | undefined; 18 | lockEmail?: boolean | undefined; 19 | name?: string | undefined; 20 | lockName?: boolean | undefined; 21 | 22 | // Additional props to be passed to the iframe, used for testing out features 23 | // prior to being added to the main props 24 | additionalProps?: Record | undefined; 25 | 26 | onDocumentReady?: () => void; 27 | onDocumentCompleted?: (data: { token: string; documentId: number; recipientId: number }) => void; 28 | onDocumentError?: (error: string) => void; 29 | onFieldSigned?: () => void; 30 | onFieldUnsigned?: () => void; 31 | }; 32 | 33 | export default function EmbedDirectTemplate(props: EmbedDirectTemplateProps) { 34 | const __iframe = useRef(null); 35 | 36 | const state = useStore({ 37 | get src() { 38 | const appHost = props.host || 'https://app.documenso.com'; 39 | 40 | const encodedOptions = btoa( 41 | encodeURIComponent( 42 | JSON.stringify({ 43 | name: props.name, 44 | lockName: props.lockName, 45 | email: props.email, 46 | lockEmail: props.lockEmail, 47 | 48 | css: props.css, 49 | cssVars: props.cssVars, 50 | darkModeDisabled: props.darkModeDisabled, 51 | ...props.additionalProps, 52 | }), 53 | ), 54 | ); 55 | 56 | const srcUrl = new URL(`/embed/direct/${props.token}`, appHost); 57 | 58 | if (props.externalId) { 59 | srcUrl.searchParams.set('externalId', props.externalId); 60 | } 61 | 62 | return `${srcUrl}#${encodedOptions}`; 63 | }, 64 | 65 | handleMessage(event: MessageEvent) { 66 | if (__iframe?.contentWindow === event.source) { 67 | switch (event.data.action) { 68 | case 'document-ready': 69 | props.onDocumentReady?.(); 70 | break; 71 | 72 | case 'document-completed': 73 | props.onDocumentCompleted?.(event.data.data); 74 | break; 75 | 76 | case 'document-error': 77 | props.onDocumentError?.(event.data.data); 78 | break; 79 | 80 | case 'field-signed': 81 | props.onFieldSigned?.(); 82 | break; 83 | 84 | case 'field-unsigned': 85 | props.onFieldUnsigned?.(); 86 | break; 87 | } 88 | } 89 | }, 90 | }); 91 | 92 | onMount(() => { 93 | window.addEventListener('message', state.handleMessage); 94 | }); 95 | 96 | onUnMount(() => { 97 | window.removeEventListener('message', state.handleMessage); 98 | }); 99 | 100 | return ; 101 | } 102 | -------------------------------------------------------------------------------- /packages/mitosis/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EmbedDirectTemplate } from './direct-template.lite'; 2 | export { default as EmbedSignDocument } from './sign-document.lite'; 3 | export { default as unstable_EmbedCreateDocument } from './create-document.lite'; 4 | export { default as unstable_EmbedCreateTemplate } from './create-template.lite'; 5 | export { default as unstable_EmbedUpdateDocument } from './update-document.lite'; 6 | export { default as unstable_EmbedUpdateTemplate } from './update-template.lite'; 7 | export { default as unstable_EmbedMultiSignDocument } from './multisign-document.lite'; 8 | -------------------------------------------------------------------------------- /packages/mitosis/src/sign-document.lite.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, onUnMount, useRef, useStore } from '@builder.io/mitosis'; 2 | 3 | import { CssVars } from './css-vars'; 4 | 5 | export type EmbedSignDocumentProps = { 6 | className?: string; 7 | host?: string; 8 | token: string; 9 | 10 | // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 11 | css?: string | undefined; 12 | cssVars?: (CssVars & Record) | undefined; 13 | 14 | darkModeDisabled?: boolean | undefined; 15 | 16 | name?: string | undefined; 17 | lockName?: boolean | undefined; 18 | allowDocumentRejection?: boolean | undefined; 19 | 20 | // Additional props to be passed to the iframe, used for testing out features 21 | // prior to being added to the main props 22 | additionalProps?: Record | undefined; 23 | 24 | onDocumentReady?: () => void; 25 | onDocumentCompleted?: (data: { token: string; documentId: number; recipientId: number }) => void; 26 | onDocumentError?: (error: string) => void; 27 | onDocumentRejected?: (data: { 28 | token: string; 29 | documentId: number; 30 | recipientId: number; 31 | reason: string; 32 | }) => void; 33 | }; 34 | 35 | export default function EmbedSignDocument(props: EmbedSignDocumentProps) { 36 | const __iframe = useRef(null); 37 | 38 | const state = useStore({ 39 | get src() { 40 | const appHost = props.host || 'https://app.documenso.com'; 41 | 42 | const encodedOptions = btoa( 43 | encodeURIComponent( 44 | JSON.stringify({ 45 | name: props.name, 46 | lockName: props.lockName, 47 | 48 | css: props.css, 49 | cssVars: props.cssVars, 50 | darkModeDisabled: props.darkModeDisabled, 51 | allowDocumentRejection: props.allowDocumentRejection, 52 | ...props.additionalProps, 53 | }), 54 | ), 55 | ); 56 | 57 | const srcUrl = new URL(`/embed/sign/${props.token}`, appHost); 58 | 59 | return `${srcUrl}#${encodedOptions}`; 60 | }, 61 | 62 | handleMessage(event: MessageEvent) { 63 | if (__iframe?.contentWindow === event.source) { 64 | switch (event.data.action) { 65 | case 'document-ready': 66 | props.onDocumentReady?.(); 67 | break; 68 | 69 | case 'document-completed': 70 | props.onDocumentCompleted?.(event.data.data); 71 | break; 72 | 73 | case 'document-error': 74 | props.onDocumentError?.(event.data.data); 75 | break; 76 | 77 | case 'document-rejected': 78 | props.onDocumentRejected?.(event.data.data); 79 | break; 80 | } 81 | } 82 | }, 83 | }); 84 | 85 | onMount(() => { 86 | window.addEventListener('message', state.handleMessage); 87 | }); 88 | 89 | onUnMount(() => { 90 | window.removeEventListener('message', state.handleMessage); 91 | }); 92 | 93 | return ; 94 | } 95 | -------------------------------------------------------------------------------- /packages/mitosis/src/update-document.lite.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, onUnMount, useRef, useStore } from '@builder.io/mitosis'; 2 | 3 | import { CssVars } from './css-vars'; 4 | 5 | export type EmbedUpdateDocumentProps = { 6 | className?: string; 7 | host?: string; 8 | presignToken: string; 9 | documentId: number; 10 | externalId?: string; 11 | 12 | // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 13 | css?: string | undefined; 14 | cssVars?: (CssVars & Record) | undefined; 15 | 16 | darkModeDisabled?: boolean | undefined; 17 | 18 | features?: { 19 | allowConfigureSignatureTypes?: boolean; 20 | allowConfigureLanguage?: boolean; 21 | allowConfigureDateFormat?: boolean; 22 | allowConfigureTimezone?: boolean; 23 | allowConfigureRedirectUrl?: boolean; 24 | allowConfigureCommunication?: boolean; 25 | }; 26 | 27 | // Additional props to be passed to the iframe, used for testing out features 28 | // prior to being added to the main props 29 | additionalProps?: Record | undefined; 30 | 31 | onDocumentUpdated?: (data: { externalId: string; documentId: number }) => void; 32 | }; 33 | 34 | export default function EmbedUpdateDocument(props: EmbedUpdateDocumentProps) { 35 | const __iframe = useRef(null); 36 | 37 | const state = useStore({ 38 | get src() { 39 | const appHost = props.host || 'https://app.documenso.com'; 40 | 41 | const encodedOptions = btoa( 42 | encodeURIComponent( 43 | JSON.stringify({ 44 | token: props.presignToken, 45 | 46 | externalId: props.externalId, 47 | features: props.features, 48 | 49 | css: props.css, 50 | cssVars: props.cssVars, 51 | darkModeDisabled: props.darkModeDisabled, 52 | ...props.additionalProps, 53 | }), 54 | ), 55 | ); 56 | 57 | const srcUrl = new URL(`/embed/v1/authoring/document/edit/${props.documentId}`, appHost); 58 | 59 | srcUrl.searchParams.set('token', props.presignToken); 60 | srcUrl.hash = encodedOptions; 61 | 62 | return srcUrl.toString(); 63 | }, 64 | 65 | handleMessage(event: MessageEvent) { 66 | if (__iframe?.contentWindow === event.source) { 67 | switch (event.data.type) { 68 | case 'document-updated': 69 | props.onDocumentUpdated?.({ 70 | documentId: event.data.documentId, 71 | externalId: event.data.externalId, 72 | }); 73 | break; 74 | } 75 | } 76 | }, 77 | }); 78 | 79 | onMount(() => { 80 | window.addEventListener('message', state.handleMessage); 81 | }); 82 | 83 | onUnMount(() => { 84 | window.removeEventListener('message', state.handleMessage); 85 | }); 86 | 87 | return ; 88 | } 89 | -------------------------------------------------------------------------------- /packages/mitosis/src/update-template.lite.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, onUnMount, useRef, useStore } from '@builder.io/mitosis'; 2 | 3 | import { CssVars } from './css-vars'; 4 | 5 | export type EmbedUpdateTemplateProps = { 6 | className?: string; 7 | host?: string; 8 | presignToken: string; 9 | templateId: number; 10 | externalId?: string; 11 | 12 | // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 13 | css?: string | undefined; 14 | cssVars?: (CssVars & Record) | undefined; 15 | 16 | darkModeDisabled?: boolean | undefined; 17 | 18 | features?: { 19 | allowConfigureSignatureTypes?: boolean; 20 | allowConfigureLanguage?: boolean; 21 | allowConfigureDateFormat?: boolean; 22 | allowConfigureTimezone?: boolean; 23 | allowConfigureRedirectUrl?: boolean; 24 | allowConfigureCommunication?: boolean; 25 | }; 26 | 27 | // Additional props to be passed to the iframe, used for testing out features 28 | // prior to being added to the main props 29 | additionalProps?: Record | undefined; 30 | 31 | onTemplateUpdated?: (data: { externalId: string; templateId: number }) => void; 32 | }; 33 | 34 | export default function EmbedUpdateTemplate(props: EmbedUpdateTemplateProps) { 35 | const __iframe = useRef(null); 36 | 37 | const state = useStore({ 38 | get src() { 39 | const appHost = props.host || 'https://app.documenso.com'; 40 | 41 | const encodedOptions = btoa( 42 | encodeURIComponent( 43 | JSON.stringify({ 44 | externalId: props.externalId, 45 | features: props.features, 46 | 47 | css: props.css, 48 | cssVars: props.cssVars, 49 | darkModeDisabled: props.darkModeDisabled, 50 | ...props.additionalProps, 51 | }), 52 | ), 53 | ); 54 | 55 | const srcUrl = new URL(`/embed/v1/authoring/template/edit/${props.templateId}`, appHost); 56 | 57 | srcUrl.searchParams.set('token', props.presignToken); 58 | srcUrl.hash = encodedOptions; 59 | 60 | return srcUrl.toString(); 61 | }, 62 | 63 | handleMessage(event: MessageEvent) { 64 | if (__iframe?.contentWindow === event.source) { 65 | switch (event.data.type) { 66 | case 'template-updated': 67 | props.onTemplateUpdated?.({ 68 | templateId: event.data.templateId, 69 | externalId: event.data.externalId, 70 | }); 71 | break; 72 | } 73 | } 74 | }, 75 | }); 76 | 77 | onMount(() => { 78 | window.addEventListener('message', state.handleMessage); 79 | }); 80 | 81 | onUnMount(() => { 82 | window.removeEventListener('message', state.handleMessage); 83 | }); 84 | 85 | return ; 86 | } 87 | -------------------------------------------------------------------------------- /packages/mitosis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tooling/typescript/react-library.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "jsxImportSource": "@builder.io/mitosis" 6 | }, 7 | "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts"], 8 | "exclude": ["dist", "build", "node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@documenso/embed-preact", 3 | "version": "0.3.0", 4 | "license": "MIT", 5 | "files": [ 6 | "dist" 7 | ], 8 | "types": "dist/index.d.ts", 9 | "main": "dist/index.js", 10 | "module": "dist/index.mjs", 11 | "exports": { 12 | "./package.json": "./package.json", 13 | ".": { 14 | "import": { 15 | "types": "./dist/index.d.mts", 16 | "default": "./dist/index.mjs" 17 | }, 18 | "require": { 19 | "types": "./dist/index.d.ts", 20 | "default": "./dist/index.js" 21 | } 22 | } 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "scripts": { 28 | "build": "vite build", 29 | "clean": "rimraf node_modules && rimraf src", 30 | "check-exports": "attw --pack .", 31 | "publint": "publint" 32 | }, 33 | "dependencies": {}, 34 | "peerDependencies": { 35 | "preact": "^10.0.0" 36 | }, 37 | "devDependencies": { 38 | "@documenso/embed": "*", 39 | "@preact/preset-vite": "^2.9.0", 40 | "vite": "^7.0.0", 41 | "vite-plugin-dts": "^4.1.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/preact/src/create-document.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { h, Fragment } from "preact"; 3 | import { useRef, useEffect } from "preact/hooks"; 4 | 5 | export type EmbedCreateDocumentProps = { 6 | className?: string; 7 | host?: string; 8 | presignToken: string; 9 | externalId?: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 10 | 11 | css?: string | undefined; 12 | cssVars?: (CssVars & Record) | undefined; 13 | darkModeDisabled?: boolean | undefined; 14 | features?: { 15 | allowConfigureSignatureTypes?: boolean; 16 | allowConfigureLanguage?: boolean; 17 | allowConfigureDateFormat?: boolean; 18 | allowConfigureTimezone?: boolean; 19 | allowConfigureRedirectUrl?: boolean; 20 | allowConfigureCommunication?: boolean; 21 | }; // Additional props to be passed to the iframe, used for testing out features 22 | // prior to being added to the main props 23 | 24 | additionalProps?: Record | undefined; 25 | onDocumentCreated?: (data: { 26 | externalId: string; 27 | documentId: number; 28 | }) => void; 29 | }; 30 | import { CssVars } from "./css-vars"; 31 | 32 | function EmbedCreateDocument(props: EmbedCreateDocumentProps) { 33 | const __iframe = useRef(null); 34 | function src() { 35 | const appHost = props.host || "https://app.documenso.com"; 36 | const encodedOptions = btoa( 37 | encodeURIComponent( 38 | JSON.stringify({ 39 | externalId: props.externalId, 40 | features: props.features, 41 | css: props.css, 42 | cssVars: props.cssVars, 43 | darkModeDisabled: props.darkModeDisabled, 44 | ...props.additionalProps, 45 | }) 46 | ) 47 | ); 48 | const srcUrl = new URL(`/embed/v1/authoring/document/create`, appHost); 49 | srcUrl.searchParams.set("token", props.presignToken); 50 | srcUrl.hash = encodedOptions; 51 | return srcUrl.toString(); 52 | } 53 | 54 | function handleMessage(event: MessageEvent) { 55 | if (__iframe.current?.contentWindow === event.source) { 56 | switch (event.data.type) { 57 | case "document-created": 58 | props.onDocumentCreated?.({ 59 | documentId: event.data.documentId, 60 | externalId: event.data.externalId, 61 | }); 62 | break; 63 | } 64 | } 65 | } 66 | 67 | useEffect(() => { 68 | window.addEventListener("message", handleMessage); 69 | }, []); 70 | 71 | useEffect(() => { 72 | return () => { 73 | window.removeEventListener("message", handleMessage); 74 | }; 75 | }, []); 76 | 77 | return 74 | 75 | ); 76 | } 77 | 78 | export default EmbedCreateDocument; 79 | -------------------------------------------------------------------------------- /packages/solid/src/create-template.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, createSignal, createMemo } from "solid-js"; 2 | 3 | export type EmbedCreateTemplateProps = { 4 | className?: string; 5 | host?: string; 6 | presignToken: string; 7 | externalId?: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 8 | 9 | css?: string | undefined; 10 | cssVars?: (CssVars & Record) | undefined; 11 | darkModeDisabled?: boolean | undefined; 12 | features?: { 13 | allowConfigureSignatureTypes?: boolean; 14 | allowConfigureLanguage?: boolean; 15 | allowConfigureDateFormat?: boolean; 16 | allowConfigureTimezone?: boolean; 17 | allowConfigureRedirectUrl?: boolean; 18 | allowConfigureCommunication?: boolean; 19 | }; // Additional props to be passed to the iframe, used for testing out features 20 | // prior to being added to the main props 21 | 22 | additionalProps?: Record | undefined; 23 | onTemplateCreated?: (data: { 24 | externalId: string; 25 | templateId: number; 26 | }) => void; 27 | }; 28 | 29 | import { CssVars } from "./css-vars"; 30 | 31 | function EmbedCreateTemplate(props: EmbedCreateTemplateProps) { 32 | const src = createMemo(() => { 33 | const appHost = props.host || "https://app.documenso.com"; 34 | const encodedOptions = btoa( 35 | encodeURIComponent( 36 | JSON.stringify({ 37 | externalId: props.externalId, 38 | features: props.features, 39 | css: props.css, 40 | cssVars: props.cssVars, 41 | darkModeDisabled: props.darkModeDisabled, 42 | ...props.additionalProps, 43 | }) 44 | ) 45 | ); 46 | const srcUrl = new URL(`/embed/v1/authoring/template/create`, appHost); 47 | srcUrl.searchParams.set("token", props.presignToken); 48 | srcUrl.hash = encodedOptions; 49 | return srcUrl.toString(); 50 | }); 51 | 52 | function handleMessage(event: MessageEvent) { 53 | if (__iframe?.contentWindow === event.source) { 54 | switch (event.data.type) { 55 | case "template-created": 56 | props.onTemplateCreated?.({ 57 | templateId: event.data.templateId, 58 | externalId: event.data.externalId, 59 | }); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | let __iframe: HTMLIFrameElement; 66 | 67 | onMount(() => { 68 | window.addEventListener("message", handleMessage); 69 | }); 70 | 71 | return ( 72 | <> 73 | 74 | 75 | ); 76 | } 77 | 78 | export default EmbedCreateTemplate; 79 | -------------------------------------------------------------------------------- /packages/solid/src/css-vars.ts: -------------------------------------------------------------------------------- 1 | export type CssVars = { 2 | /** Base background color */ 3 | background?: string | null; 4 | /** Base text color */ 5 | 6 | foreground?: string | null; 7 | /** Muted/subtle background color */ 8 | 9 | muted?: string | null; 10 | /** Muted/subtle text color */ 11 | 12 | mutedForeground?: string | null; 13 | /** Popover/dropdown background color */ 14 | 15 | popover?: string | null; 16 | /** Popover/dropdown text color */ 17 | 18 | popoverForeground?: string | null; 19 | /** Card background color */ 20 | 21 | card?: string | null; 22 | /** Card border color */ 23 | 24 | cardBorder?: string | null; 25 | /** Card border tint/highlight color */ 26 | 27 | cardBorderTint?: string | null; 28 | /** Card text color */ 29 | 30 | cardForeground?: string | null; 31 | /** Field card background color */ 32 | 33 | fieldCard?: string | null; 34 | /** Field card border color */ 35 | 36 | fieldCardBorder?: string | null; 37 | /** Field card text color */ 38 | 39 | fieldCardForeground?: string | null; 40 | /** Widget background color */ 41 | 42 | widget?: string | null; 43 | /** Widget text color */ 44 | 45 | widgetForeground?: string | null; 46 | /** Default border color */ 47 | 48 | border?: string | null; 49 | /** Input field border color */ 50 | 51 | input?: string | null; 52 | /** Primary action/button color */ 53 | 54 | primary?: string | null; 55 | /** Primary action/button text color */ 56 | 57 | primaryForeground?: string | null; 58 | /** Secondary action/button color */ 59 | 60 | secondary?: string | null; 61 | /** Secondary action/button text color */ 62 | 63 | secondaryForeground?: string | null; 64 | /** Accent/highlight color */ 65 | 66 | accent?: string | null; 67 | /** Accent/highlight text color */ 68 | 69 | accentForeground?: string | null; 70 | /** Destructive/danger action color */ 71 | 72 | destructive?: string | null; 73 | /** Destructive/danger text color */ 74 | 75 | destructiveForeground?: string | null; 76 | /** Focus ring color */ 77 | 78 | ring?: string | null; 79 | /** Border radius size in REM units */ 80 | 81 | radius?: string | null; 82 | /** Warning/alert color */ 83 | 84 | warning?: string | null; 85 | } -------------------------------------------------------------------------------- /packages/solid/src/direct-template.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, createSignal, createMemo } from "solid-js"; 2 | 3 | export type EmbedDirectTemplateProps = { 4 | className?: string; 5 | host?: string; 6 | token: string; 7 | externalId?: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 8 | 9 | css?: string | undefined; 10 | cssVars?: (CssVars & Record) | undefined; 11 | darkModeDisabled?: boolean | undefined; 12 | email?: string | undefined; 13 | lockEmail?: boolean | undefined; 14 | name?: string | undefined; 15 | lockName?: boolean | undefined; // Additional props to be passed to the iframe, used for testing out features 16 | // prior to being added to the main props 17 | 18 | additionalProps?: Record | undefined; 19 | onDocumentReady?: () => void; 20 | onDocumentCompleted?: (data: { 21 | token: string; 22 | documentId: number; 23 | recipientId: number; 24 | }) => void; 25 | onDocumentError?: (error: string) => void; 26 | onFieldSigned?: () => void; 27 | onFieldUnsigned?: () => void; 28 | }; 29 | 30 | import { CssVars } from "./css-vars"; 31 | 32 | function EmbedDirectTemplate(props: EmbedDirectTemplateProps) { 33 | const src = createMemo(() => { 34 | const appHost = props.host || "https://app.documenso.com"; 35 | const encodedOptions = btoa( 36 | encodeURIComponent( 37 | JSON.stringify({ 38 | name: props.name, 39 | lockName: props.lockName, 40 | email: props.email, 41 | lockEmail: props.lockEmail, 42 | css: props.css, 43 | cssVars: props.cssVars, 44 | darkModeDisabled: props.darkModeDisabled, 45 | ...props.additionalProps, 46 | }) 47 | ) 48 | ); 49 | const srcUrl = new URL(`/embed/direct/${props.token}`, appHost); 50 | 51 | if (props.externalId) { 52 | srcUrl.searchParams.set("externalId", props.externalId); 53 | } 54 | 55 | return `${srcUrl}#${encodedOptions}`; 56 | }); 57 | 58 | function handleMessage(event: MessageEvent) { 59 | if (__iframe?.contentWindow === event.source) { 60 | switch (event.data.action) { 61 | case "document-ready": 62 | props.onDocumentReady?.(); 63 | break; 64 | 65 | case "document-completed": 66 | props.onDocumentCompleted?.(event.data.data); 67 | break; 68 | 69 | case "document-error": 70 | props.onDocumentError?.(event.data.data); 71 | break; 72 | 73 | case "field-signed": 74 | props.onFieldSigned?.(); 75 | break; 76 | 77 | case "field-unsigned": 78 | props.onFieldUnsigned?.(); 79 | break; 80 | } 81 | } 82 | } 83 | 84 | let __iframe: HTMLIFrameElement; 85 | 86 | onMount(() => { 87 | window.addEventListener("message", handleMessage); 88 | }); 89 | 90 | return ( 91 | <> 92 | 93 | 94 | ); 95 | } 96 | 97 | export default EmbedDirectTemplate; 98 | -------------------------------------------------------------------------------- /packages/solid/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EmbedDirectTemplate } from './direct-template'; 2 | export { default as EmbedSignDocument } from './sign-document'; 3 | export { default as unstable_EmbedCreateDocument } from './create-document'; 4 | export { default as unstable_EmbedCreateTemplate } from './create-template'; 5 | export { default as unstable_EmbedUpdateDocument } from './update-document'; 6 | export { default as unstable_EmbedUpdateTemplate } from './update-template'; 7 | export { default as unstable_EmbedMultiSignDocument } from './multisign-document' -------------------------------------------------------------------------------- /packages/solid/src/sign-document.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, createSignal, createMemo } from "solid-js"; 2 | 3 | export type EmbedSignDocumentProps = { 4 | className?: string; 5 | host?: string; 6 | token: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 7 | 8 | css?: string | undefined; 9 | cssVars?: (CssVars & Record) | undefined; 10 | darkModeDisabled?: boolean | undefined; 11 | name?: string | undefined; 12 | lockName?: boolean | undefined; 13 | allowDocumentRejection?: boolean | undefined; // Additional props to be passed to the iframe, used for testing out features 14 | // prior to being added to the main props 15 | 16 | additionalProps?: Record | undefined; 17 | onDocumentReady?: () => void; 18 | onDocumentCompleted?: (data: { 19 | token: string; 20 | documentId: number; 21 | recipientId: number; 22 | }) => void; 23 | onDocumentError?: (error: string) => void; 24 | onDocumentRejected?: (data: { 25 | token: string; 26 | documentId: number; 27 | recipientId: number; 28 | reason: string; 29 | }) => void; 30 | }; 31 | 32 | import { CssVars } from "./css-vars"; 33 | 34 | function EmbedSignDocument(props: EmbedSignDocumentProps) { 35 | const src = createMemo(() => { 36 | const appHost = props.host || "https://app.documenso.com"; 37 | const encodedOptions = btoa( 38 | encodeURIComponent( 39 | JSON.stringify({ 40 | name: props.name, 41 | lockName: props.lockName, 42 | css: props.css, 43 | cssVars: props.cssVars, 44 | darkModeDisabled: props.darkModeDisabled, 45 | allowDocumentRejection: props.allowDocumentRejection, 46 | ...props.additionalProps, 47 | }) 48 | ) 49 | ); 50 | const srcUrl = new URL(`/embed/sign/${props.token}`, appHost); 51 | return `${srcUrl}#${encodedOptions}`; 52 | }); 53 | 54 | function handleMessage(event: MessageEvent) { 55 | if (__iframe?.contentWindow === event.source) { 56 | switch (event.data.action) { 57 | case "document-ready": 58 | props.onDocumentReady?.(); 59 | break; 60 | 61 | case "document-completed": 62 | props.onDocumentCompleted?.(event.data.data); 63 | break; 64 | 65 | case "document-error": 66 | props.onDocumentError?.(event.data.data); 67 | break; 68 | 69 | case "document-rejected": 70 | props.onDocumentRejected?.(event.data.data); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | let __iframe: HTMLIFrameElement; 77 | 78 | onMount(() => { 79 | window.addEventListener("message", handleMessage); 80 | }); 81 | 82 | return ( 83 | <> 84 | 85 | 86 | ); 87 | } 88 | 89 | export default EmbedSignDocument; 90 | -------------------------------------------------------------------------------- /packages/solid/src/update-document.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, createSignal, createMemo } from "solid-js"; 2 | 3 | export type EmbedUpdateDocumentProps = { 4 | className?: string; 5 | host?: string; 6 | presignToken: string; 7 | documentId: number; 8 | externalId?: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 9 | 10 | css?: string | undefined; 11 | cssVars?: (CssVars & Record) | undefined; 12 | darkModeDisabled?: boolean | undefined; 13 | features?: { 14 | allowConfigureSignatureTypes?: boolean; 15 | allowConfigureLanguage?: boolean; 16 | allowConfigureDateFormat?: boolean; 17 | allowConfigureTimezone?: boolean; 18 | allowConfigureRedirectUrl?: boolean; 19 | allowConfigureCommunication?: boolean; 20 | }; // Additional props to be passed to the iframe, used for testing out features 21 | // prior to being added to the main props 22 | 23 | additionalProps?: Record | undefined; 24 | onDocumentUpdated?: (data: { 25 | externalId: string; 26 | documentId: number; 27 | }) => void; 28 | }; 29 | 30 | import { CssVars } from "./css-vars"; 31 | 32 | function EmbedUpdateDocument(props: EmbedUpdateDocumentProps) { 33 | const src = createMemo(() => { 34 | const appHost = props.host || "https://app.documenso.com"; 35 | const encodedOptions = btoa( 36 | encodeURIComponent( 37 | JSON.stringify({ 38 | token: props.presignToken, 39 | externalId: props.externalId, 40 | features: props.features, 41 | css: props.css, 42 | cssVars: props.cssVars, 43 | darkModeDisabled: props.darkModeDisabled, 44 | ...props.additionalProps, 45 | }) 46 | ) 47 | ); 48 | const srcUrl = new URL( 49 | `/embed/v1/authoring/document/edit/${props.documentId}`, 50 | appHost 51 | ); 52 | srcUrl.searchParams.set("token", props.presignToken); 53 | srcUrl.hash = encodedOptions; 54 | return srcUrl.toString(); 55 | }); 56 | 57 | function handleMessage(event: MessageEvent) { 58 | if (__iframe?.contentWindow === event.source) { 59 | switch (event.data.type) { 60 | case "document-updated": 61 | props.onDocumentUpdated?.({ 62 | documentId: event.data.documentId, 63 | externalId: event.data.externalId, 64 | }); 65 | break; 66 | } 67 | } 68 | } 69 | 70 | let __iframe: HTMLIFrameElement; 71 | 72 | onMount(() => { 73 | window.addEventListener("message", handleMessage); 74 | }); 75 | 76 | return ( 77 | <> 78 | 79 | 80 | ); 81 | } 82 | 83 | export default EmbedUpdateDocument; 84 | -------------------------------------------------------------------------------- /packages/solid/src/update-template.tsx: -------------------------------------------------------------------------------- 1 | import { onMount, createSignal, createMemo } from "solid-js"; 2 | 3 | export type EmbedUpdateTemplateProps = { 4 | className?: string; 5 | host?: string; 6 | presignToken: string; 7 | templateId: number; 8 | externalId?: string; // @src: /apps/web/src/app/embed/direct/[[...url]]/schema 9 | 10 | css?: string | undefined; 11 | cssVars?: (CssVars & Record) | undefined; 12 | darkModeDisabled?: boolean | undefined; 13 | features?: { 14 | allowConfigureSignatureTypes?: boolean; 15 | allowConfigureLanguage?: boolean; 16 | allowConfigureDateFormat?: boolean; 17 | allowConfigureTimezone?: boolean; 18 | allowConfigureRedirectUrl?: boolean; 19 | allowConfigureCommunication?: boolean; 20 | }; // Additional props to be passed to the iframe, used for testing out features 21 | // prior to being added to the main props 22 | 23 | additionalProps?: Record | undefined; 24 | onTemplateUpdated?: (data: { 25 | externalId: string; 26 | templateId: number; 27 | }) => void; 28 | }; 29 | 30 | import { CssVars } from "./css-vars"; 31 | 32 | function EmbedUpdateTemplate(props: EmbedUpdateTemplateProps) { 33 | const src = createMemo(() => { 34 | const appHost = props.host || "https://app.documenso.com"; 35 | const encodedOptions = btoa( 36 | encodeURIComponent( 37 | JSON.stringify({ 38 | externalId: props.externalId, 39 | features: props.features, 40 | css: props.css, 41 | cssVars: props.cssVars, 42 | darkModeDisabled: props.darkModeDisabled, 43 | ...props.additionalProps, 44 | }) 45 | ) 46 | ); 47 | const srcUrl = new URL( 48 | `/embed/v1/authoring/template/edit/${props.templateId}`, 49 | appHost 50 | ); 51 | srcUrl.searchParams.set("token", props.presignToken); 52 | srcUrl.hash = encodedOptions; 53 | return srcUrl.toString(); 54 | }); 55 | 56 | function handleMessage(event: MessageEvent) { 57 | if (__iframe?.contentWindow === event.source) { 58 | switch (event.data.type) { 59 | case "template-updated": 60 | props.onTemplateUpdated?.({ 61 | templateId: event.data.templateId, 62 | externalId: event.data.externalId, 63 | }); 64 | break; 65 | } 66 | } 67 | } 68 | 69 | let __iframe: HTMLIFrameElement; 70 | 71 | onMount(() => { 72 | window.addEventListener("message", handleMessage); 73 | }); 74 | 75 | return ( 76 | <> 77 | 78 | 79 | ); 80 | } 81 | 82 | export default EmbedUpdateTemplate; 83 | -------------------------------------------------------------------------------- /packages/solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tooling/typescript/react-library.json", 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "jsxImportSource": "solid-js" 6 | }, 7 | "include": ["."], 8 | "exclude": ["dist", "build", "node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/solid/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { copyFileSync } from 'node:fs'; 2 | import { defineConfig } from 'vite'; 3 | import dts from 'vite-plugin-dts'; 4 | import solid from 'vite-plugin-solid'; 5 | 6 | export default defineConfig({ 7 | build: { 8 | lib: { 9 | entry: './src/index.ts', 10 | name: '@documenso/embed-solid', 11 | formats: ['es', 'cjs'], 12 | fileName: 'index', 13 | }, 14 | rollupOptions: { 15 | external: ['solid-js'], 16 | }, 17 | }, 18 | plugins: [ 19 | solid(), 20 | dts({ 21 | include: ['src'], 22 | afterBuild() { 23 | copyFileSync('dist/index.d.ts', 'dist/index.d.mts'); 24 | }, 25 | }), 26 | ], 27 | }); 28 | -------------------------------------------------------------------------------- /packages/svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@documenso/embed-svelte", 3 | "version": "0.3.0", 4 | "license": "MIT", 5 | "files": [ 6 | "dist" 7 | ], 8 | "types": "dist/index.d.ts", 9 | "main": "dist/index.js", 10 | "module": "dist/index.js", 11 | "exports": { 12 | "./package.json": "./package.json", 13 | ".": { 14 | "import": { 15 | "types": "./dist/index.d.ts", 16 | "default": "./dist/index.js" 17 | }, 18 | "require": { 19 | "types": "./dist/index.d.ts", 20 | "default": "./dist/index.js" 21 | } 22 | } 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "scripts": { 28 | "build": "svelte-package -i src/", 29 | "clean": "rimraf node_modules && rimraf src", 30 | "check-exports": "attw --pack .", 31 | "publint": "publint" 32 | }, 33 | "dependencies": {}, 34 | "peerDependencies": { 35 | "svelte": "^4.0.0" 36 | }, 37 | "devDependencies": { 38 | "@documenso/embed": "*", 39 | "@sveltejs/adapter-auto": "^3.2.4", 40 | "@sveltejs/kit": "^2.5.25", 41 | "@sveltejs/package": "^2.3.4", 42 | "@sveltejs/vite-plugin-svelte": "^3.1.2", 43 | "vite": "^7.0.0", 44 | "vite-plugin-dts": "^4.1.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/svelte/src/create-document.svelte: -------------------------------------------------------------------------------- 1 | 28 | 29 | 90 | 91 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/src/create-template.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/src/css-vars.ts: -------------------------------------------------------------------------------- 1 | export type CssVars = { 2 | /** Base background color */ 3 | background?: string | null; 4 | /** Base text color */ 5 | 6 | foreground?: string | null; 7 | /** Muted/subtle background color */ 8 | 9 | muted?: string | null; 10 | /** Muted/subtle text color */ 11 | 12 | mutedForeground?: string | null; 13 | /** Popover/dropdown background color */ 14 | 15 | popover?: string | null; 16 | /** Popover/dropdown text color */ 17 | 18 | popoverForeground?: string | null; 19 | /** Card background color */ 20 | 21 | card?: string | null; 22 | /** Card border color */ 23 | 24 | cardBorder?: string | null; 25 | /** Card border tint/highlight color */ 26 | 27 | cardBorderTint?: string | null; 28 | /** Card text color */ 29 | 30 | cardForeground?: string | null; 31 | /** Field card background color */ 32 | 33 | fieldCard?: string | null; 34 | /** Field card border color */ 35 | 36 | fieldCardBorder?: string | null; 37 | /** Field card text color */ 38 | 39 | fieldCardForeground?: string | null; 40 | /** Widget background color */ 41 | 42 | widget?: string | null; 43 | /** Widget text color */ 44 | 45 | widgetForeground?: string | null; 46 | /** Default border color */ 47 | 48 | border?: string | null; 49 | /** Input field border color */ 50 | 51 | input?: string | null; 52 | /** Primary action/button color */ 53 | 54 | primary?: string | null; 55 | /** Primary action/button text color */ 56 | 57 | primaryForeground?: string | null; 58 | /** Secondary action/button color */ 59 | 60 | secondary?: string | null; 61 | /** Secondary action/button text color */ 62 | 63 | secondaryForeground?: string | null; 64 | /** Accent/highlight color */ 65 | 66 | accent?: string | null; 67 | /** Accent/highlight text color */ 68 | 69 | accentForeground?: string | null; 70 | /** Destructive/danger action color */ 71 | 72 | destructive?: string | null; 73 | /** Destructive/danger text color */ 74 | 75 | destructiveForeground?: string | null; 76 | /** Focus ring color */ 77 | 78 | ring?: string | null; 79 | /** Border radius size in REM units */ 80 | 81 | radius?: string | null; 82 | /** Warning/alert color */ 83 | 84 | warning?: string | null; 85 | } -------------------------------------------------------------------------------- /packages/vue/src/direct-template.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EmbedDirectTemplate } from './direct-template.vue'; 2 | export { default as EmbedSignDocument } from './sign-document.vue'; 3 | export { default as unstable_EmbedCreateDocument } from './create-document.vue'; 4 | export { default as unstable_EmbedCreateTemplate } from './create-template.vue'; 5 | export { default as unstable_EmbedUpdateDocument } from './update-document.vue'; 6 | export { default as unstable_EmbedUpdateTemplate } from './update-template.vue'; 7 | export { default as unstable_EmbedMultiSignDocument } from './multisign-document.vue' -------------------------------------------------------------------------------- /packages/vue/src/sign-document.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/src/update-document.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/src/update-template.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tooling/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/vue/vite.config.mts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue'; 2 | import { copyFileSync } from 'node:fs'; 3 | import AutoImport from 'unplugin-auto-import/vite'; 4 | import { defineConfig } from 'vite'; 5 | import dts from 'vite-plugin-dts'; 6 | 7 | export default defineConfig({ 8 | build: { 9 | lib: { 10 | entry: './src/index.ts', 11 | name: '@documenso/embed-vue', 12 | formats: ['es', 'cjs'], 13 | fileName: 'index', 14 | }, 15 | rollupOptions: { 16 | external: ['vue'], 17 | }, 18 | }, 19 | plugins: [ 20 | AutoImport({ 21 | imports: ['vue'], 22 | vueTemplate: true, 23 | }), 24 | vue(), 25 | dts({ 26 | include: ['src', 'auto-imports.d.ts'], 27 | afterBuild() { 28 | copyFileSync('dist/index.d.ts', 'dist/index.d.mts'); 29 | }, 30 | }), 31 | ], 32 | }); 33 | -------------------------------------------------------------------------------- /packages/webcomponent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@documenso/embed-webcomponent", 3 | "version": "0.3.0", 4 | "license": "MIT", 5 | "files": [ 6 | "dist" 7 | ], 8 | "types": "dist/index.d.ts", 9 | "main": "dist/index.js", 10 | "module": "dist/index.mjs", 11 | "exports": { 12 | "./package.json": "./package.json", 13 | ".": { 14 | "import": { 15 | "types": "./dist/index.d.mts", 16 | "default": "./dist/index.mjs" 17 | }, 18 | "require": { 19 | "types": "./dist/index.d.ts", 20 | "default": "./dist/index.js" 21 | } 22 | } 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "scripts": { 28 | "build": "vite build", 29 | "clean": "rimraf node_modules", 30 | "check-exports": "attw --pack .", 31 | "publint": "publint" 32 | }, 33 | "dependencies": { 34 | "@documenso/embed-preact": "*", 35 | "preact": "^10.0.0", 36 | "preact-custom-element": "^4.3.0" 37 | }, 38 | "peerDependencies": { 39 | "preact": "^10.0.0" 40 | }, 41 | "devDependencies": { 42 | "@documenso/embed": "*", 43 | "@types/preact-custom-element": "^4.0.4", 44 | "vite": "^7.0.0", 45 | "vite-plugin-dts": "^4.1.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/webcomponent/src/index.ts: -------------------------------------------------------------------------------- 1 | import register from 'preact-custom-element'; 2 | 3 | import { EmbedDirectTemplate, EmbedSignDocument } from '@documenso/embed-preact'; 4 | 5 | register(EmbedDirectTemplate, 'documenso-embed-direct-template', [ 6 | 'token', 7 | 'host', 8 | 'externalId', 9 | 'css', 10 | 'email', 11 | 'lockEmail', 12 | 'name', 13 | 'lockName', 14 | 'onDocumentReady', 15 | 'onDocumentCompleted', 16 | 'onDocumentError', 17 | ]); 18 | 19 | register(EmbedSignDocument, 'documenso-embed-sign-document', [ 20 | 'token', 21 | 'host', 22 | 'css', 23 | 'name', 24 | 'lockName', 25 | 'onDocumentReady', 26 | 'onDocumentCompleted', 27 | 'onDocumentError', 28 | ]); 29 | -------------------------------------------------------------------------------- /packages/webcomponent/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tooling/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/webcomponent/vite.config.mts: -------------------------------------------------------------------------------- 1 | import preact from '@preact/preset-vite'; 2 | import { copyFileSync } from 'node:fs'; 3 | import { defineConfig } from 'vite'; 4 | import dts from 'vite-plugin-dts'; 5 | 6 | export default defineConfig({ 7 | build: { 8 | lib: { 9 | entry: './src/index.ts', 10 | name: '@documenso/embed-webcomponent', 11 | formats: ['es', 'cjs'], 12 | fileName: 'index', 13 | }, 14 | }, 15 | plugins: [ 16 | preact(), 17 | dts({ 18 | include: ['src'], 19 | afterBuild() { 20 | copyFileSync('dist/index.d.ts', 'dist/index.d.mts'); 21 | }, 22 | }), 23 | ], 24 | }); 25 | -------------------------------------------------------------------------------- /playground/.env.example: -------------------------------------------------------------------------------- 1 | VITE_EMBED_HOST="https://app.documenso.com" -------------------------------------------------------------------------------- /playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /playground/.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | -------------------------------------------------------------------------------- /playground/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: 13 | 14 | ```js 15 | export default tseslint.config({ 16 | extends: [ 17 | // Remove ...tseslint.configs.recommended and replace with this 18 | ...tseslint.configs.recommendedTypeChecked, 19 | // Alternatively, use this for stricter rules 20 | ...tseslint.configs.strictTypeChecked, 21 | // Optionally, add this for stylistic rules 22 | ...tseslint.configs.stylisticTypeChecked, 23 | ], 24 | languageOptions: { 25 | // other options... 26 | parserOptions: { 27 | project: ['./tsconfig.node.json', './tsconfig.app.json'], 28 | tsconfigRootDir: import.meta.dirname, 29 | }, 30 | }, 31 | }); 32 | ``` 33 | 34 | You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: 35 | 36 | ```js 37 | // eslint.config.js 38 | import reactDom from 'eslint-plugin-react-dom'; 39 | import reactX from 'eslint-plugin-react-x'; 40 | 41 | export default tseslint.config({ 42 | plugins: { 43 | // Add the react-x and react-dom plugins 44 | 'react-x': reactX, 45 | 'react-dom': reactDom, 46 | }, 47 | rules: { 48 | // other rules... 49 | // Enable its recommended typescript rules 50 | ...reactX.configs['recommended-typescript'].rules, 51 | ...reactDom.configs.recommended.rules, 52 | }, 53 | }); 54 | ``` 55 | -------------------------------------------------------------------------------- /playground/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/index.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /playground/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import reactHooks from 'eslint-plugin-react-hooks'; 3 | import reactRefresh from 'eslint-plugin-react-refresh'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], 23 | }, 24 | }, 25 | ); 26 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "../scripts/build.sh && vite", 8 | "build": "../scripts/build.sh && tsc -b && vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@hookform/resolvers": "^5.0.1", 14 | "@radix-ui/react-accordion": "^1.2.11", 15 | "@radix-ui/react-alert-dialog": "^1.1.14", 16 | "@radix-ui/react-aspect-ratio": "^1.1.7", 17 | "@radix-ui/react-avatar": "^1.1.10", 18 | "@radix-ui/react-checkbox": "^1.3.2", 19 | "@radix-ui/react-collapsible": "^1.1.11", 20 | "@radix-ui/react-context-menu": "^2.2.15", 21 | "@radix-ui/react-dialog": "^1.1.14", 22 | "@radix-ui/react-dropdown-menu": "^2.1.15", 23 | "@radix-ui/react-hover-card": "^1.1.14", 24 | "@radix-ui/react-label": "^2.1.7", 25 | "@radix-ui/react-menubar": "^1.1.15", 26 | "@radix-ui/react-navigation-menu": "^1.2.13", 27 | "@radix-ui/react-popover": "^1.1.14", 28 | "@radix-ui/react-progress": "^1.1.7", 29 | "@radix-ui/react-radio-group": "^1.3.7", 30 | "@radix-ui/react-scroll-area": "^1.2.9", 31 | "@radix-ui/react-select": "^2.2.5", 32 | "@radix-ui/react-separator": "^1.1.7", 33 | "@radix-ui/react-slider": "^1.3.5", 34 | "@radix-ui/react-slot": "^1.2.3", 35 | "@radix-ui/react-switch": "^1.2.5", 36 | "@radix-ui/react-tabs": "^1.1.12", 37 | "@radix-ui/react-toggle": "^1.1.9", 38 | "@radix-ui/react-toggle-group": "^1.1.10", 39 | "@radix-ui/react-tooltip": "^1.2.7", 40 | "@tailwindcss/vite": "^4.1.8", 41 | "class-variance-authority": "^0.7.1", 42 | "clsx": "^2.1.1", 43 | "cmdk": "^1.1.1", 44 | "date-fns": "^4.1.0", 45 | "embla-carousel-react": "^8.6.0", 46 | "input-otp": "^1.4.2", 47 | "lucide-react": "^0.513.0", 48 | "next-themes": "^0.4.6", 49 | "react": "^19.1.0", 50 | "react-day-picker": "^8.10.1", 51 | "react-dom": "^19.1.0", 52 | "react-hook-form": "^7.57.0", 53 | "react-resizable-panels": "^3.0.2", 54 | "recharts": "^2.15.3", 55 | "sonner": "^2.0.5", 56 | "tailwind-merge": "^3.3.0", 57 | "tailwindcss": "^4.1.8", 58 | "vaul": "^1.1.2", 59 | "zod": "^3.25.51" 60 | }, 61 | "devDependencies": { 62 | "@documenso/embed-react": "file:../packages/react", 63 | "@eslint/js": "^9.25.0", 64 | "@types/node": "^20.0.0", 65 | "@types/react": "^19.1.2", 66 | "@types/react-dom": "^19.1.2", 67 | "@vitejs/plugin-react": "^5.0.0", 68 | "eslint": "^9.25.0", 69 | "eslint-plugin-react-hooks": "^5.2.0", 70 | "eslint-plugin-react-refresh": "^0.4.19", 71 | "globals": "^16.0.0", 72 | "tw-animate-css": "^1.3.4", 73 | "typescript": "~5.5.4", 74 | "typescript-eslint": "^8.30.1", 75 | "vite": "^7.0.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /playground/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playground/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documenso/embeds/c3fc75f22aec317d01da70d7be35ef2e5774c3eb/playground/src/App.css -------------------------------------------------------------------------------- /playground/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; 2 | 3 | import './App.css'; 4 | import MultiSignEmbedding from './components/embeddings/multisign'; 5 | 6 | export default function App() { 7 | return ( 8 |
9 |
10 |

Embedding Test Bed

11 |

12 | Configure and test different types of embeddings 13 |

14 |
15 | 16 | 17 | 18 | Multi Sign 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /playground/src/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '@/lib/utils'; 4 | import * as AccordionPrimitive from '@radix-ui/react-accordion'; 5 | import { ChevronDownIcon } from 'lucide-react'; 6 | 7 | function Accordion({ ...props }: React.ComponentProps) { 8 | return ; 9 | } 10 | 11 | function AccordionItem({ 12 | className, 13 | ...props 14 | }: React.ComponentProps) { 15 | return ( 16 | 21 | ); 22 | } 23 | 24 | function AccordionTrigger({ 25 | className, 26 | children, 27 | ...props 28 | }: React.ComponentProps) { 29 | return ( 30 | 31 | svg]:rotate-180', 35 | className, 36 | )} 37 | {...props} 38 | > 39 | {children} 40 | 41 | 42 | 43 | ); 44 | } 45 | 46 | function AccordionContent({ 47 | className, 48 | children, 49 | ...props 50 | }: React.ComponentProps) { 51 | return ( 52 | 57 |
{children}
58 |
59 | ); 60 | } 61 | 62 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; 63 | -------------------------------------------------------------------------------- /playground/src/components/ui/alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '@/lib/utils'; 4 | import { type VariantProps, cva } from 'class-variance-authority'; 5 | 6 | const alertVariants = cva( 7 | 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', 8 | { 9 | variants: { 10 | variant: { 11 | default: 'bg-card text-card-foreground', 12 | destructive: 13 | 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: 'default', 18 | }, 19 | }, 20 | ); 21 | 22 | function Alert({ 23 | className, 24 | variant, 25 | ...props 26 | }: React.ComponentProps<'div'> & VariantProps) { 27 | return ( 28 |
34 | ); 35 | } 36 | 37 | function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { 38 | return ( 39 |
44 | ); 45 | } 46 | 47 | function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) { 48 | return ( 49 |
57 | ); 58 | } 59 | 60 | export { Alert, AlertTitle, AlertDescription }; 61 | -------------------------------------------------------------------------------- /playground/src/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'; 2 | 3 | function AspectRatio({ ...props }: React.ComponentProps) { 4 | return ; 5 | } 6 | 7 | export { AspectRatio }; 8 | -------------------------------------------------------------------------------- /playground/src/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | 5 | import { cn } from '@/lib/utils'; 6 | import * as AvatarPrimitive from '@radix-ui/react-avatar'; 7 | 8 | function Avatar({ className, ...props }: React.ComponentProps) { 9 | return ( 10 | 15 | ); 16 | } 17 | 18 | function AvatarImage({ className, ...props }: React.ComponentProps) { 19 | return ( 20 | 25 | ); 26 | } 27 | 28 | function AvatarFallback({ 29 | className, 30 | ...props 31 | }: React.ComponentProps) { 32 | return ( 33 | 38 | ); 39 | } 40 | 41 | export { Avatar, AvatarImage, AvatarFallback }; 42 | -------------------------------------------------------------------------------- /playground/src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '@/lib/utils'; 4 | import { Slot } from '@radix-ui/react-slot'; 5 | import { type VariantProps, cva } from 'class-variance-authority'; 6 | 7 | const badgeVariants = cva( 8 | 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', 9 | { 10 | variants: { 11 | variant: { 12 | default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', 13 | secondary: 14 | 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', 15 | destructive: 16 | 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', 17 | outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: 'default', 22 | }, 23 | }, 24 | ); 25 | 26 | function Badge({ 27 | className, 28 | variant, 29 | asChild = false, 30 | ...props 31 | }: React.ComponentProps<'span'> & VariantProps & { asChild?: boolean }) { 32 | const Comp = asChild ? Slot : 'span'; 33 | 34 | return ( 35 | 36 | ); 37 | } 38 | 39 | export { Badge, badgeVariants }; 40 | -------------------------------------------------------------------------------- /playground/src/components/ui/breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '@/lib/utils'; 4 | import { Slot } from '@radix-ui/react-slot'; 5 | import { ChevronRight, MoreHorizontal } from 'lucide-react'; 6 | 7 | function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { 8 | return