Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4220dbf
feat(ai-studio): auto-load flagship template and add first-visit disc…
librowski-synergy Jun 24, 2026
3e6613b
chore(execution-worker): default to gemini-2.5-flash-lite for the demo
librowski-synergy Jun 24, 2026
065be47
feat(backend): gate workflow execution with rate limit and Turnstile
librowski-synergy Jun 24, 2026
56e37cd
feat(ai-studio): attach Turnstile token to workflow execution
librowski-synergy Jun 24, 2026
18d9fa5
feat(ai-studio): add AI Debate, Content Repurposer and Meeting Notes …
librowski-synergy Jun 24, 2026
0ab7796
refactor(ai-studio): rename markdown-preview node to visualize
librowski-synergy Jun 24, 2026
0e6755f
feat(ai-studio): detect output format for visualize
librowski-synergy Jun 24, 2026
878eee0
feat(ai-studio): add mode param to visualize node
librowski-synergy Jun 24, 2026
4844ba9
feat(ai-studio): json/table/text/stat-card renderers for visualize
librowski-synergy Jun 24, 2026
b887031
feat(ai-studio): always-on visualize card with empty state
librowski-synergy Jun 24, 2026
309bdae
feat(ai-studio): chart renderer (recharts) for visualize
librowski-synergy Jun 24, 2026
0af904e
feat(ai-studio): mermaid diagram renderer for visualize
librowski-synergy Jun 24, 2026
a392405
feat(ai-studio): export visualize output (png/svg/copy)
librowski-synergy Jun 24, 2026
2531a86
feat(ai-studio): expand visualize to fullscreen modal
librowski-synergy Jun 24, 2026
590422e
docs(ai-studio): finalize visualize backlog and refresh getRenderer c…
librowski-synergy Jun 24, 2026
3d76546
feat(ai-studio): render visualize output inside the node body
librowski-synergy Jun 24, 2026
9a4031b
fix(ai-studio): stop mermaid error graphics on non-diagram input
librowski-synergy Jun 25, 2026
18e5ff1
feat(ai-studio): render embedded mermaid/json blocks in visualize
librowski-synergy Jun 25, 2026
5fae667
feat(ai-studio): AI adapt - LLM-convert output to the chosen format
librowski-synergy Jun 25, 2026
16bb030
feat(ai-studio): add aiAdapt toggle to visualize node config
librowski-synergy Jun 25, 2026
88c52c8
feat(backend): stronger visualize adapt prompts for better conversion
librowski-synergy Jun 25, 2026
c554ce9
feat(ai-studio): render markdown in table cells and stat-card values
librowski-synergy Jun 25, 2026
04de11b
fix(ai-studio): execution-panel dark-mode contrast, modal heading, vi…
librowski-synergy Jun 25, 2026
73c2e97
feat(ai-studio): linked CDN logo with theme-aware variants
librowski-synergy Jun 25, 2026
8908ede
fix(ai-studio): keep app-bar Save button clear of the logo overlay
librowski-synergy Jun 25, 2026
ff3660a
feat(ai-studio): add Visualize node to AI Debate template
librowski-synergy Jun 25, 2026
4aded6a
feat(ai-studio): add Visualize node to Meeting Notes template
librowski-synergy Jun 25, 2026
6ac096d
feat(ai-studio): converge Content Repurposer into a Visualize node
librowski-synergy Jun 25, 2026
dc524d6
refactor(ai-studio): drop dead code and unused exports
librowski-synergy Jun 25, 2026
3a9dc01
chore(ai-studio): drop internal planning docs from the PR
librowski-synergy Jun 25, 2026
42475e3
fix(ai-studio): declare ai-agent output so node mentions resolve
librowski-synergy Jun 25, 2026
655f8c5
feat(ai-studio): web search tool for AI Agent nodes (Tavily)
librowski-synergy Jun 25, 2026
c87ebd1
feat(ai-studio): add Market Research template using web search
librowski-synergy Jun 25, 2026
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
6 changes: 6 additions & 0 deletions apps/ai-studio/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Base URL of the reference backend the demo talks to.
VITE_BACKEND_URL=http://127.0.0.1:3001
# Cloudflare Turnstile site key (public). Leave empty to run the demo without
# bot protection (local dev). When set, the Run button attaches a Turnstile
# token that the backend verifies before executing a workflow.
VITE_TURNSTILE_SITE_KEY=
5 changes: 5 additions & 0 deletions apps/ai-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@
"@workflow-builder/types": "workspace:*",
"@xyflow/react": "catalog:",
"clsx": "^2.1.1",
"html-to-image": "1.11.11",
"immer": "^10.1.1",
"mermaid": "^11.15.0",
"react": "^19.1.0",
"react-dom": "catalog:",
"react-markdown": "^10.1.0",
"recharts": "^3.9.0",
"remark-gfm": "^4.0.1",
"zustand": "^5.0.1"
},
"devDependencies": {
Expand Down
16 changes: 15 additions & 1 deletion apps/ai-studio/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { WorkflowBuilder } from '@workflowbuilder/sdk';

import './brand-override.css';
import '@workflowbuilder/sdk/style.css';

import { BrandLogo } from '../components/brand/brand-logo';
import { AiStudioControls } from '../components/controls/ai-studio-controls';
import { DisclaimerModal } from '../components/disclaimer/disclaimer-modal';
import { ExecutionHighlighting } from '../components/execution/highlighting';
import { ExecutionLogPanel } from '../components/execution/log-panel';
import { ExecutionNodeDetail } from '../components/execution/node-detail';
import { aiStudioTemplates } from '../data/ai-studio-templates';
import { aiStudioNodeTypes } from '../data/node-types';
import { supportTriageFlow } from '../data/support-triage-flow';
import { plugin as aiStudioFeaturesPlugin } from '../plugin';

// Auto-load a relatable, runnable workflow on first visit instead of greeting
// the visitor with a blank canvas. Passing non-empty initial nodes/edges makes
// the SDK skip the welcome picker; a returning visitor's saved diagram still wins.
const flagship = supportTriageFlow.value;

export function App() {
return (
<WorkflowBuilder.Root
name="ai-studio"
name={flagship.name}
layoutDirection={flagship.layoutDirection}
initialNodes={flagship.diagram.nodes}
initialEdges={flagship.diagram.edges}
nodeTypes={aiStudioNodeTypes}
diagramTemplates={aiStudioTemplates}
plugins={[aiStudioFeaturesPlugin]}
>
<WorkflowBuilder.DefaultLayout />
<BrandLogo />
<AiStudioControls />
<ExecutionLogPanel />
<ExecutionNodeDetail />
<ExecutionHighlighting />
<DisclaimerModal />
</WorkflowBuilder.Root>
);
}
14 changes: 14 additions & 0 deletions apps/ai-studio/src/app/brand-override.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* TEMPORARY override. The SDK app bar (`Toolbar`) renders the Workflow Builder
logo as an SVG with CSS-module class `logo`, followed by a `nav-segment`
holding the Save button, in a flex row (gap 2rem). The SDK exposes no prop to
replace the logo or make it a link, so we hide its pixels here and render our
own linked CDN logo (see BrandLogo). We keep the logo's box in the flex flow
(`visibility`, NOT `display: none`) and reserve a width matching the overlay,
so the Save button keeps its place instead of sliding left underneath the
overlay. The prefix selectors survive the per-build module-hash suffix (e.g.
`_logo_2tbzr_18`). Remove this and the BrandLogo overlay once the SDK exposes
a logo / logoHref prop on <WorkflowBuilder.Root>. */
[class^='_toolbar_'] [class^='_logo_'] {
visibility: hidden;
width: 6rem; /* >= the BrandLogo overlay width (~5.8rem) so the Save button clears it */
}
35 changes: 35 additions & 0 deletions apps/ai-studio/src/components/brand/brand-logo.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Overlays the app bar's logo slot (the SDK logo sits at ~33px / 37px, 55x22).
Fixed so it stays put over the fixed app bar. */
.logo {
position: fixed;
top: 2.45rem;
left: 2.05rem;
z-index: 100;
display: inline-flex;
align-items: center;
height: 1.15rem;
}

.image {
height: 100%;
width: auto;
}

/* Theme-swapped logo: the SDK sets `data-theme` on <html>. Show the dark-text
logo by default (light theme) and the white-text logo when the dark theme is
active, so neither variant carries a baked-in background. */
.light {
display: block;
}

.dark {
display: none;
}

:global([data-theme='dark']) .light {
display: none;
}

:global([data-theme='dark']) .dark {
display: block;
}
27 changes: 27 additions & 0 deletions apps/ai-studio/src/components/brand/brand-logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import styles from './brand-logo.module.css';

// The CDN's `-solid` asset bakes in a white background (a white box in dark
// mode), so we use the transparent dark-text / white-text variants of the same
// logo and swap them by the active SDK theme (see brand-logo.module.css).
const LOGO_LIGHT = 'https://cdn.synergycodes.com/workflow-builder-logo.svg';
const LOGO_DARK = 'https://cdn.synergycodes.com/workflow-builder-logo-white.svg';
const LOGO_HREF = 'https://workflowbuilder.io';

// Temporary consumer-side replacement for the SDK app-bar logo (the SDK one is
// hidden via brand-override.css). Renders the CDN logo over the app bar's logo
// slot and links it to workflowbuilder.io. Replace with an SDK logo/logoHref
// prop on <WorkflowBuilder.Root> when one is available.
export function BrandLogo() {
return (
<a
className={styles['logo']}
href={LOGO_HREF}
target="_blank"
rel="noreferrer noopener"
aria-label="Workflow Builder - workflowbuilder.io"
>
<img className={`${styles['image']} ${styles['light']}`} src={LOGO_LIGHT} alt="Workflow Builder" />
<img className={`${styles['image']} ${styles['dark']}`} src={LOGO_DARK} alt="" aria-hidden="true" />
</a>
);
}
111 changes: 111 additions & 0 deletions apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
.overlay {
position: fixed;
inset: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
background: rgba(8, 10, 14, 0.55);
backdrop-filter: blur(2px);
}

.card {
position: relative;
width: 100%;
max-width: 460px;
padding: 2rem;
font-family: var(--wb-font-family);
background: var(--ax-ui-bg-primary-default, #ffffff);
border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3);
border-radius: var(--wb-app-bar-border-radius, 0.75rem);
box-shadow: 0 1.25rem 3.75rem rgba(0, 0, 0, 0.25);
color: var(--ax-txt-primary-default, #151516);
}

.close {
position: absolute;
top: 0.9rem;
right: 0.9rem;
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border: none;
border-radius: 0.5rem;
background: transparent;
color: var(--ax-txt-tertiary-default, #6f7480);
cursor: pointer;
}

.close:hover {
background: var(--ax-ui-bg-secondary-default, #f5f5f7);
color: var(--ax-txt-primary-default, #151516);
}

.heading {
display: flex;
align-items: center;
gap: 0.7rem;
margin-bottom: 0.9rem;
}

.icon {
display: flex;
align-items: center;
justify-content: center;
width: 2.25rem;
height: 2.25rem;
flex-shrink: 0;
border-radius: 0.6rem;
background: var(--ax-ui-bg-secondary-default, #f5f5f7);
color: var(--ax-colors-acc1-500, #1096e7);
}

.title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: var(--ax-txt-primary-default, #151516);
}

.body {
margin: 0 0 1.5rem;
font-size: 0.92rem;
line-height: 1.6;
color: var(--ax-txt-secondary-default, #4d5059);
}

.body p {
margin: 0 0 0.7rem;
}

.body p:last-child {
margin-bottom: 0;
}

.body strong {
color: var(--ax-txt-primary-default, #151516);
font-weight: 600;
}

.cta {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 0.7rem 1rem;
border: none;
border-radius: 0.625rem;
background: var(--ax-colors-acc1-500, #1096e7);
color: #ffffff;
font-size: 0.95rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
}

.cta:hover {
background: var(--ax-colors-acc1-600, #0477c5);
}
75 changes: 75 additions & 0 deletions apps/ai-studio/src/components/disclaimer/disclaimer-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Info, X } from '@phosphor-icons/react';
import { useState } from 'react';

import styles from './disclaimer-modal.module.css';

const STORAGE_KEY = 'ai-studio:disclaimer-acknowledged';

function hasAcknowledged(): boolean {
try {
return localStorage.getItem(STORAGE_KEY) === 'true';
} catch {
return false;
}
}

export function DisclaimerModal() {
const [open, setOpen] = useState(() => !hasAcknowledged());

if (!open) {
return null;
}

function dismiss() {
try {
localStorage.setItem(STORAGE_KEY, 'true');
} catch {
// A locked-down or private session is fine — just close for this visit.
}
setOpen(false);
}

return (
<div className={styles['overlay']} role="presentation" onClick={dismiss}>
<div
className={styles['card']}
role="dialog"
aria-modal="true"
aria-labelledby="disclaimer-title"
onClick={(event) => event.stopPropagation()}
>
<button className={styles['close']} onClick={dismiss} aria-label="Close">
<X size={18} weight="bold" />
</button>

<div className={styles['heading']}>
<div className={styles['icon']}>
<Info size={22} weight="fill" />
</div>
<h2 className={styles['title']} id="disclaimer-title">
Welcome to AI Studio
</h2>
</div>

<div className={styles['body']}>
<p>
This is a live demo of <strong>Workflow Builder</strong> — a toolkit for building visual, AI-powered
workflow editors.
</p>
<p>
The workflows here run for real: every AI step calls a live model through <strong>OpenRouter</strong>.
</p>
<p>
It is <strong>not</strong> a place to test or benchmark AI models. The model is just the engine — the point
is to show what you can build with Workflow Builder. To keep the demo open to everyone, runs are
rate-limited.
</p>
</div>

<button className={styles['cta']} onClick={dismiss}>
Start exploring
</button>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
background: var(--wb-app-bar-background);
border: 0.0625rem solid var(--wb-app-bar-border-color);
border-radius: var(--wb-app-bar-border-radius);
color: var(--ax-txt-primary-default, #151516);
display: flex;
flex-direction: column;
z-index: 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
background: var(--wb-app-bar-background);
border: 0.0625rem solid var(--wb-app-bar-border-color);
border-radius: var(--wb-app-bar-border-radius);
color: var(--ax-txt-primary-default, #151516);
display: flex;
flex-direction: column;
z-index: 11;
Expand Down
Loading
Loading