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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ tools/
!tools/deployment/nginx
.gitignore

# examples
examples/

# env files hold secrets — never in a build context
**/.env
**/.env.*
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,26 @@ jobs:

- name: Build
run: pnpm --filter @workflowbuilder/sdk build:lib

starter:
name: Starter build
runs-on: ubuntu-latest
defaults:
run:
working-directory: examples/workflow-builder-starter
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: examples/workflow-builder-starter/package-lock.json

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Drag-and-drop workflow builder UI with a reference back-end and an execution eng

[Try it live ->](https://app.workflowbuilder.io/)

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/synergycodes/workflowbuilder/tree/main/examples/workflow-builder-starter?title=Workflow%20Builder%20Starter%20Example&file=src%2Fapp.tsx)

Used in production by teams including [Vercom](https://www.workflowbuilder.io/case-study/vercom), [Athena Intelligence](https://www.workflowbuilder.io/case-study/athena-intelligence), [Plura AI](https://www.workflowbuilder.io/case-study/plura-ai), and others.

</div>
Expand All @@ -42,7 +44,9 @@ Three onboarding paths. Pick one based on what you want to evaluate.
| See the editor running in your browser | [B. Try the demo](#path-b-try-the-demo) | ~2 min | no |
| Run the full reference stack (editor + execution + AI) | [C. Run the full stack demo](#path-c-run-the-full-stack-demo) | ~10 min | yes |

Don't want to install or clone anything yet? [Try the live demo](https://app.workflowbuilder.io) in your browser first.
Don't want to install or clone anything yet? [Try the live demo](https://app.workflowbuilder.io) in your browser, or open the runnable starter below:

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/synergycodes/workflowbuilder/tree/main/examples/workflow-builder-starter?title=Workflow%20Builder%20Starter%20Example&file=src%2Fapp.tsx)

### Path A. Embed the SDK

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "pnpm clean:typedoc && astro dev",
"build": "pnpm clean:typedoc && node scripts/check-sidebar-categories.mjs && astro build && node scripts/touch-distribution-index.mjs",
"build": "pnpm clean:typedoc && node scripts/check-sidebar-categories.mjs && astro build && node scripts/touch-distribution-index.mjs && node scripts/copy-swa-config.mjs",
"clean:typedoc": "node -e \"import('node:fs').then(fs => fs.rmSync('src/content/docs/api', { recursive: true, force: true }))\"",
"preview": "astro preview",
"typecheck": "astro check",
Expand Down
10 changes: 10 additions & 0 deletions apps/docs/scripts/copy-swa-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Azure Static Web Apps reads staticwebapp.config.json from the artifact root,
// which is `dist` — the site sits one level down in `dist/docs`. Astro's
// `public/` copies into `dist/docs`, one level too deep, so copy it here.
import { copyFileSync, mkdirSync } from 'node:fs';

const source = './staticwebapp.config.json';
const target = './dist/staticwebapp.config.json';

mkdirSync('./dist', { recursive: true });
copyFileSync(source, target);
60 changes: 60 additions & 0 deletions apps/docs/src/components/stackblitz-embed.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
import { STARTER_FILE, STARTER_FORK_URL, STARTER_PROJECT, STARTER_TITLE } from '../config';

// `corp=1` makes StackBlitz send its own isolation headers. That works only on a
// page which is itself cross-origin isolated. `staticwebapp.config.json` grants
// isolation to one route, so render this component only there. Anywhere else the
// embed shows "Unable to run Embedded Project".
const embedParams = new URLSearchParams({
ctl: '1',
embed: '1',
corp: '1',
view: 'preview',
terminalHeight: '20',
startScript: 'dev',
title: STARTER_TITLE,
file: STARTER_FILE,
});

// The frame uses `/github/`, not `/fork/github/`, so it does not make a project
// per visitor. The link below the frame forks instead.
const embedUrl = `https://stackblitz.com/github/${STARTER_PROJECT}?${embedParams}`;
---

<div class="stackblitz-embed not-content">
<iframe
src={embedUrl}
title={`${STARTER_TITLE} on StackBlitz`}
loading="lazy"
allow="cross-origin-isolated"
referrerpolicy="strict-origin-when-cross-origin"></iframe>
{
/* Full-size link, and the way out when the frame cannot start. StackBlitz
supports embeds in Chromium browsers only, and the failure is silent, so
there is nothing to detect and nothing to fall back to automatically. */
}
<p class="escape-hatch">
<a href={STARTER_FORK_URL} target="_blank" rel="noopener noreferrer">Open on StackBlitz</a>
</p>
</div>

<style>
.stackblitz-embed {
margin-block: 1.5rem;
}

iframe {
display: block;
/* Capped against the viewport, so it fits a short laptop screen. */
block-size: min(70vh, 40rem);
inline-size: 100%;
border: 0;
border-radius: 0.5rem;
}

.escape-hatch {
margin-block: 0.5rem 0;
font-size: var(--sl-text-sm);
text-align: end;
}
</style>
24 changes: 24 additions & 0 deletions apps/docs/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
export const GITHUB_REPO_BASE =
import.meta.env['GITHUB_REPO_BASE'] ??
'https://github.com/synergycodes/workflowbuilder/blob/main/apps/demo/src/app/data/nodes';

/**
* The runnable starter on GitHub, as `owner/repo/tree/<ref>/<path>`.
*
* Change the ref here and both the embed and every docs link follow. The root
* README (twice) and the starter's own README repeat it, because Markdown
* cannot import a constant.
*/
export const STARTER_PROJECT = 'synergycodes/workflowbuilder/tree/main/examples/workflow-builder-starter';

/** File the StackBlitz editor opens. */
export const STARTER_FILE = 'src/app.tsx';

/** Project name. Always send one, or StackBlitz glues owner onto repo name. */
export const STARTER_TITLE = 'Workflow Builder Starter Example';

/**
* Opens an editable copy. Plain `/github/` gives no write access, so the first
* save reloads the page.
*/
export const STARTER_FORK_URL = `https://stackblitz.com/fork/github/${STARTER_PROJECT}?${new URLSearchParams({
title: STARTER_TITLE,
file: STARTER_FILE,
})}`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ sidebar:

import { TabItem, Tabs } from '@astrojs/starlight/components';

import StackblitzEmbed from '@/components/stackblitz-embed.astro';

## Requirements

- React 18 or 19
Expand All @@ -19,6 +21,12 @@ import { TabItem, Tabs } from '@astrojs/starlight/components';
Mount only one `<WorkflowBuilder.Root>` per page. Multi-instance is not supported. See [Side effects & limitations](/get-started/side-effects/) for the details and how to swap workflows on one page.
:::

## Try it in your browser

A small React + Vite app that uses the SDK. Click the frame to start it, then change the code.

<StackblitzEmbed />

## Installation

Install the SDK along with its peer dependencies:
Expand Down
4 changes: 3 additions & 1 deletion apps/docs/src/content/docs/overview/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { Image } from 'astro:assets';

import wbOverviewImg from '@/assets/wb-overview.webp';

import { STARTER_FORK_URL } from '@/config';

<Image src={wbOverviewImg} alt="Workflow Builder - canvas, node palette, and properties panel" />

Workflow Builder is a React SDK for building and embedding visual workflow editors into your application. It is available in open-source and enterprise editions.
Expand Down Expand Up @@ -55,7 +57,7 @@ Workflow Builder is commonly used to:

## Live demo

[Open the live demo](https://app.workflowbuilder.io) to try Workflow Builder in your browser, or [contact us](https://www.workflowbuilder.io/contact) for a guided walkthrough.
[Open the live demo](https://app.workflowbuilder.io) to try Workflow Builder in your browser. To read and edit the code, open the <a href={STARTER_FORK_URL} target="_blank" rel="noopener noreferrer">runnable starter in StackBlitz</a> — a minimal React app that embeds the SDK. For a guided walkthrough, [contact us](https://www.workflowbuilder.io/contact).

## See also

Expand Down
35 changes: 35 additions & 0 deletions apps/docs/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { defineMiddleware } from 'astro:middleware';

import swaConfig from '../staticwebapp.config.json';

/**
* Applies the production response headers in `astro dev`.
*
* The site builds to static files, so it cannot set its own headers. Azure
* Static Web Apps sends them in production, and this reads the same config, so
* one file owns the routes and the values.
*
* What needs them today: cross-origin isolation on the page that embeds
* StackBlitz. Without it the embed shows "Unable to run Embedded Project".
* **Do not** widen the route to the whole site - COEP blocks the YouTube embeds on
* other pages.
*/
const routes = swaConfig.routes.map(({ route, headers }) => ({
prefix: route.replace(/\*$/, ''),
headers: Object.entries(headers),
}));

export const onRequest = defineMiddleware(async (context, next) => {
const response = await next();

for (const { prefix, headers } of routes) {
if (!context.url.pathname.startsWith(prefix)) {
continue;
}
for (const [name, value] of headers) {
response.headers.set(name, value);
}
}

return response;
});
11 changes: 11 additions & 0 deletions apps/docs/staticwebapp.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"routes": [
{
"route": "/docs/get-started/quick-start/wb-as-react-component*",
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "credentialless"
}
}
]
}
3 changes: 3 additions & 0 deletions examples/workflow-builder-starter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
*.local
42 changes: 42 additions & 0 deletions examples/workflow-builder-starter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Workflow Builder — Starter

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/synergycodes/workflowbuilder/tree/main/examples/workflow-builder-starter?title=Workflow%20Builder%20Starter%20Example&file=src%2Fapp.tsx)

A minimal, one-click example of [Workflow Builder](https://www.workflowbuilder.io/): a
React + TypeScript + Vite app that embeds the `@workflowbuilder/sdk` editor.

Workflow Builder is a workflow editor **built on [React Flow](https://reactflow.dev/)**.
`<WorkflowBuilder.Root>` owns the React Flow canvas and adds a node palette, a
schema-driven properties panel, persistence, theming, and validation. You get a complete
editor from one component, instead of wiring React Flow by hand.

## Run it

```bash
npm install
npm run dev
```

Open the printed URL (Vite defaults to http://localhost:5173).

## What's here

- **`src/app.tsx`** — mounts `<WorkflowBuilder.Root>` with the default layout (top bar,
palette, canvas, properties panel) and `localStorage` persistence.
- **`src/nodes/`** — three example node types (`trigger`, `action`, `condition`). Each is
the canonical 4-file pattern: `schema.ts` (data), `uischema.ts` (form),
`default-properties-data.ts` (defaults), and the `PaletteItem` definition.
- **`src/diagram/initial-diagram.ts`** — a small `Trigger → Action → Condition` flow shown
on first open.

## Try it

- Drag a node from the palette onto the canvas.
- Click a node to edit its properties on the right.
- Connect nodes by dragging from one handle to another.

## Next steps

- Add your own node type by copying a folder under `src/nodes/` and registering it in
`src/nodes/index.ts`.
- Full guide and API reference: <https://www.workflowbuilder.io/docs/overview/>
12 changes: 12 additions & 0 deletions examples/workflow-builder-starter/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Workflow Builder Starter</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
7 changes: 7 additions & 0 deletions examples/workflow-builder-starter/lint-staged.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @type {import('lint-staged').Configuration}
*/
export default {
'*.{ts,tsx,js,json,css,astro,md,mdx}': (files) => `prettier --write --log-level=silent ${files.join(' ')}`,
'*.{ts,tsx}': [(files) => `eslint --max-warnings=0 --fix ${files.join(' ')}`, () => `tsc --noEmit`],
};
Loading
Loading