Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c33cdef
feat(docs): add live @workflowbuilder/ui component gallery
librowski-synergy Jun 23, 2026
76fe513
docs: point UI-library references at @workflowbuilder/ui
librowski-synergy Jun 23, 2026
253a967
feat(docs): expand UI Library into page-per-component reference
librowski-synergy Jun 24, 2026
07f8916
refactor(ui): export component prop types and add @default tags
librowski-synergy Jun 24, 2026
9cc49a5
feat(docs): generate UI Library props and CSS tables from source via …
librowski-synergy Jun 24, 2026
dda4c36
docs(ui): render examples in isolated fixed-size previews with card-s…
librowski-synergy Jun 24, 2026
43e9c3d
docs(ui): add live previews to diagram-component pages
librowski-synergy Jun 24, 2026
f394d20
docs(ui): anchor the Status example to a positioned container
librowski-synergy Jun 26, 2026
7e895f4
docs(ui): @base-ui/react is no longer a peer dependency
librowski-synergy Jun 26, 2026
7512477
ci(docs): build UI before docs in the deploy workflow
Jul 30, 2026
0403a31
fix(docs): harden the UI API generator and guard component coverage
Jul 30, 2026
5bbe804
docs(ui): document Collapsible and Icon switch, align Styles guidance
Jul 30, 2026
3a8dfa8
fix(ui): correct segment-picker shape prop's @default doc tag
Jul 30, 2026
be9378c
chore: sync pnpm-lock.yaml with workspace typescript resolution
Jul 30, 2026
6612625
refactor(ui): drop redundant Partial around WithIcon in ModalProps
Jul 31, 2026
2868575
fix(docs): include shared prop types in TypeDoc entry points
Jul 31, 2026
21bf559
fix(docs): load the ui stylesheet at document level for portalled pre…
Aug 10, 2026
748e49e
docs: drop the stale import-order requirement from the ui setup page
Aug 10, 2026
e6dc898
refactor(ui): export the full DatePicker props surface
Aug 10, 2026
63ddfb6
fix(docs): document union and overload components through the variant…
Aug 10, 2026
9b3b044
fix(docs): fail loudly on every silent-empty path in the ui-api pipeline
Aug 10, 2026
e9ee791
ci: build the docs site on PRs that can break it
Aug 10, 2026
a0910f7
docs(changeset): record the exported ui prop-type surface
Aug 10, 2026
14cb16c
docs: pin section index pages to the top of their sidebar groups
Aug 10, 2026
062c391
fix(ui): export the NavButton variant prop types from the barrel
Aug 11, 2026
28433f8
docs(ui): add default tags for runtime defaults missing from the tables
Aug 11, 2026
a4dd0b5
fix(docs): stop marking variant-only props as globally required
Aug 11, 2026
b657196
ci(docs): require an MDX page for every generated component entry
Aug 11, 2026
f74e170
ci(docs): widen the docs gate path filter
Aug 11, 2026
a637d9f
fix(docs): let oversized examples shrink or grow the preview stage
Aug 11, 2026
d0f504c
docs: complete the stateful examples and describe Snackbar's scope
Aug 11, 2026
1983921
docs: align pages with what the code actually does
Aug 11, 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
5 changes: 5 additions & 0 deletions .changeset/ui-export-prop-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/ui': minor
---

Component prop types are now exported: `AvatarProps`, `CheckboxProps`, `RadioProps`, `StatusProps`, `TooltipProps`, `MenuProps`, `ModalProps`, `EdgeLabelProps`, `NodeIconProps`, `NodeDescriptionProps`, `NodeAsPortWrapperProps`, `SegmentPickerProps` (with its controlled/uncontrolled variants), the NavButton variant prop types, and `DatePickerProps` now covers the component's full runtime surface (`value`, `defaultValue`, `placeholder`, `valueFormat`, `type`, `error`). Supporting types used in those signatures (`Shape`, `IconNode`) are exported as well.
5 changes: 5 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# UI must build first: docs imports @workflowbuilder/ui/{styles,tokens}.css
# and its components, both resolved from packages/ui/dist.
- name: Build UI
run: pnpm build:ui

# SDK must build first so TypeDoc can read its compiled types.
- name: Build SDK
run: pnpm --filter @workflowbuilder/sdk build:lib
Expand Down
57 changes: 57 additions & 0 deletions .github/workflows/pr-check-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: PR Check (docs)

# The docs site is generated from packages/ui source (ui-api tables, live
# examples) and consumes its built dist, so a PR touching either can break
# the docs build - which otherwise first runs at release-time deploy.
# Full `astro build` is the gate: it runs the ui-api generator, the
# component-coverage guard, and page rendering (loud failures included).
# Typecheck is deliberately absent: apps/docs tolerates known starlight
# virtual-module type errors.

on:
pull_request:
branches:
- main
paths:
- 'apps/docs/**'
- 'packages/ui/**'
- 'packages/tokens/**'
# starlight-typedoc generates the /api reference from SDK sources.
- 'packages/sdk/**'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'package.json'
- '.github/workflows/pr-check-docs.yml'

permissions:
contents: read

jobs:
docs:
name: Docs build (ui-api generator + coverage + astro)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: true

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

- name: Enable Corepack
run: npm i -g corepack@latest

- name: Install pnpm
run: corepack prepare

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build UI library
run: pnpm build:ui

- name: Build docs
run: pnpm build:docs
6 changes: 3 additions & 3 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: PR Check
# Runs on every PR to give reviewers fast feedback on whether the change
# is safe to merge. Focus on the published packages @workflowbuilder/sdk and
# @workflowbuilder/ui (plus its private @workflowbuilder/ui-tokens build) +
# global format consistency. Other workspaces (apps/docs, demo, ai-studio, …)
# are not checked here — they're internal and have their own broken-state
# tolerances (e.g. starlight virtual-module type errors).
# global format consistency. apps/docs has its own path-filtered workflow
# (pr-check-docs.yml); demo and ai-studio are not checked here — they're
# internal and have their own broken-state tolerances.

on:
pull_request:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,8 @@ CLAUDE.local.md
# and is copied into this dir by a tiny Astro integration after typedoc
# generation (see astro.config.mjs).
apps/docs/src/content/docs/api/

# UI Library props + CSS-variable data, generated from @workflowbuilder/ui by
# apps/docs/scripts/generate-ui-api.mjs (TypeDoc + CSS extraction) on every
# docs build / dev. Source of truth is the library, so keep it out of git.
apps/docs/src/generated/
25 changes: 24 additions & 1 deletion apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { copyFileSync, mkdirSync } from 'node:fs';
import path from 'node:path';

import react from '@astrojs/react';
import starlight from '@astrojs/starlight';
import umami from '@yeskunall/astro-umami';
import { defineConfig, passthroughImageService } from 'astro/config';
Expand Down Expand Up @@ -70,6 +71,7 @@ export default defineConfig({
},
integrations: [
icon(),
react(),
umami({ id: UMAMI_WEBSITE_ID }),
starlight({
plugins: [
Expand Down Expand Up @@ -113,7 +115,19 @@ export default defineConfig({
},
}),
],
customCss: ['./src/styles/custom.css'],
// `@workflowbuilder/ui` styles are safe to load globally: everything is
// layered (no global reset), classes are hashed or opt-in, and tokens.css
// only defines `--ax-*` custom properties keyed on `html[data-theme]` —
// which Starlight already toggles, so the live component showcases follow
// the docs light/dark theme. index.css is required at document level:
// Modal/Menu/Select/Tooltip/DatePicker portal their popups to body,
// outside the shadow roots that carry the preview styles.
customCss: [
'./src/styles/custom.css',
'@workflowbuilder/ui/styles.css',
'@workflowbuilder/ui/tokens.css',
'@workflowbuilder/ui/index.css',
],
components: {
Head: './src/components/head.astro',
Search: './src/components/search.astro',
Expand Down Expand Up @@ -159,6 +173,15 @@ export default defineConfig({
{ label: 'Node Schemas', autogenerate: { directory: 'node-schemas' } },
{ label: 'Built-in Nodes', autogenerate: { directory: 'nodes' } },
{ label: 'Plugins', autogenerate: { directory: 'plugins' } },
{
label: 'UI Library',
items: [
{ label: 'Overview', link: '/ui-library/overview/' },
{ label: 'Design tokens', link: '/ui-library/design-tokens/' },
{ label: 'UI Components', autogenerate: { directory: 'ui-library/ui-components' } },
{ label: 'Diagram Components', autogenerate: { directory: 'ui-library/diagram-components' } },
],
},
// API Reference — pages auto-generated by `starlight-typedoc` from
// packages/sdk's barrel into `src/content/docs/api/<Category>/`.
// Folder names match the `@category` tag in source TSDoc verbatim.
Expand Down
15 changes: 12 additions & 3 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,37 @@
"private": true,
"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",
"dev": "pnpm clean:typedoc && pnpm generate:ui-api && astro dev",
"build": "pnpm clean:typedoc && pnpm generate:ui-api && node scripts/check-sidebar-categories.mjs && astro build && node scripts/touch-distribution-index.mjs",
"generate:ui-api": "node scripts/generate-ui-api.mjs && node scripts/check-ui-component-coverage.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",
"typecheck": "pnpm generate:ui-api && astro check",
"format": "prettier --write --log-level silent \"**/*.astro\"",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"@astrojs/react": "^4.4.0",
"@astrojs/starlight": "^0.37.6",
"@base-ui/react": "catalog:",
"@iconify-json/ph": "^1.2.2",
"@phosphor-icons/react": "catalog:",
"@workflowbuilder/sdk": "workspace:*",
"@workflowbuilder/ui": "workspace:*",
"@yeskunall/astro-umami": "^0.0.7",
"astro": "^5.18.0",
"astro-icon": "^1.1.5",
"marked": "^15.0.0",
"react": "catalog:",
"react-dom": "catalog:",
"rehype-external-links": "^3.0.0",
"starlight-image-zoom": "^0.13.2"
},
"devDependencies": {
"@astrojs/check": "^0.9.6",
"@types/react": "catalog:",
"@types/react-dom": "^19.1.0",
"eslint-plugin-astro": "^1.3.1",
"starlight-typedoc": "^0.21.3",
"typedoc": "^0.28.9",
Expand Down
103 changes: 103 additions & 0 deletions apps/docs/scripts/check-ui-component-coverage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Asserts that every entry in packages/ui/vite.config.mts's `componentEntries`
// (the list of publishable subpath bundles) has at least one matching entry
// in generate-ui-api.mjs's `COMPONENTS` (the list of docs pages with a
// generated Props / CSS variables table).
//
// Without this guard, a new component can be added to the package's public
// entry points and shipped to npm without ever getting a docs page - nothing
// else in the build fails, the page just silently never exists. TypeDoc's
// strict mode doesn't catch this either: it only checks that exported types
// have doc comments, not that a docs page renders them.
//
// A vite entry counts as covered if COMPONENTS has a `dir` equal to the entry
// name, or nested under it (`${entry}/...`) - entries like `node` bundle
// several flat docs pages (node-icon, node-description, ...) rather than
// mapping 1:1 by name. `NARRATIVE_ONLY` is an escape hatch for a vite entry
// that is deliberately docs-only-by-prose with no generated props table at
// all (none today - kept for the next one, e.g. a future compound component
// documented like NodePanel).
//
// Wired into apps/docs/package.json's `generate:ui-api` script, right after
// the generator runs, so `dev` / `build` / `typecheck` all catch the drift.

import { globSync, readFileSync } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';

const here = path.dirname(fileURLToPath(import.meta.url));
const documentsRoot = path.resolve(here, '..');
const repoRoot = path.resolve(documentsRoot, '../..');
const viteConfigPath = path.resolve(repoRoot, 'packages/ui/vite.config.mts');
const generatorPath = path.resolve(here, 'generate-ui-api.mjs');

const NARRATIVE_ONLY = new Set([]);

function extractComponentEntries(source) {
const match = /const componentEntries = \[([\s\S]*?)] as const;/.exec(source);
if (!match) throw new Error('Could not find `componentEntries` in packages/ui/vite.config.mts');
return [...match[1].matchAll(/'([^']+)'/g)].map((m) => m[1]);
}

function extractComponentDirectories(source) {
const match = /const COMPONENTS = \[([\s\S]*?)];/.exec(source);
if (!match) throw new Error('Could not find `COMPONENTS` in generate-ui-api.mjs');
return [...match[1].matchAll(/dir:\s*'([^']+)'/g)].map((m) => m[1]);
}

function extractComponentSlugs(source) {
const match = /const COMPONENTS = \[([\s\S]*?)];/.exec(source);
if (!match) throw new Error('Could not find `COMPONENTS` in generate-ui-api.mjs');
return [...match[1].matchAll(/slug:\s*'([^']+)'/g)].map((m) => m[1]);
}

const componentEntries = extractComponentEntries(readFileSync(viteConfigPath, 'utf8'));
const generatorSource = readFileSync(generatorPath, 'utf8');
const componentDirectories = extractComponentDirectories(generatorSource);
const componentSlugs = extractComponentSlugs(generatorSource);

const missing = componentEntries.filter((entry) => {
if (NARRATIVE_ONLY.has(entry)) return false;
return !componentDirectories.some((directory) => directory === entry || directory.startsWith(`${entry}/`));
});

// A COMPONENTS entry only produces data - nothing renders it unless an MDX
// page passes the slug to PropsTable / CssVariablesTable. Without this leg,
// adding a generator entry with no page passes the whole build silently.
const contentRoot = path.resolve(documentsRoot, 'src/content/docs');
const pageSources = globSync('**/*.mdx', { cwd: contentRoot }).map((file) =>
readFileSync(path.resolve(contentRoot, file), 'utf8'),
);
const unrendered = componentSlugs.filter(
(slug) => !pageSources.some((source) => source.includes(`slug="${slug}"`)),
);

if (missing.length > 0) {
console.error('error: componentEntries in packages/ui/vite.config.mts have no matching COMPONENTS entry.\n');
for (const entry of missing) {
console.error(` - ${entry}`);
}
console.error(
'\nAdd a COMPONENTS entry in apps/docs/scripts/generate-ui-api.mjs with a matching `dir`, ' +
'or add the entry to NARRATIVE_ONLY in this script if it is deliberately prose-only.',
);
process.exitCode = 1;
}

if (unrendered.length > 0) {
console.error('error: COMPONENTS entries in generate-ui-api.mjs are rendered by no MDX page.\n');
for (const slug of unrendered) {
console.error(` - ${slug}`);
}
console.error(
'\nAdd a docs page that uses <PropsTable slug="..."> / <CssVariablesTable slug="...">, ' +
'or remove the generator entry.',
);
process.exitCode = 1;
}

if (missing.length === 0 && unrendered.length === 0) {
console.log(
`✓ component coverage ok — ${componentEntries.length} vite entries and ${componentSlugs.length} docs slugs cross-checked.`,
);
}
Loading