From b1f47943b723c628b6dd9f6cc67f160df58a55b8 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 22 Sep 2026 10:16:56 +0200 Subject: [PATCH 01/50] chore(docs): remove outdated demo notes (#169) --- docs/wb-backend-demo-strategy.html | 318 ----------------------------- 1 file changed, 318 deletions(-) delete mode 100644 docs/wb-backend-demo-strategy.html diff --git a/docs/wb-backend-demo-strategy.html b/docs/wb-backend-demo-strategy.html deleted file mode 100644 index 0aa7376da..000000000 --- a/docs/wb-backend-demo-strategy.html +++ /dev/null @@ -1,318 +0,0 @@ - - - - - - Workflow Builder — Backend Execution & Interactive Demo Strategy - - - - -

Workflow Builder — Interactive Demo Strategy

-

Wykorzystanie backend execution layer do prezentacji wartości produktu dla klientów mid-market i enterprise

- -
- Data: Kwiecień 2026 - Status: POC zrealizowany, planowanie kolejnych etapów - Segment docelowy: Mid-market / Enterprise B2B SaaS -
- - - -

Kontekst biznesowy

- -

Workflow Builder to frontend SDK do budowania edytorów workflow — embeddowalny komponent, nie platforma SaaS. Klient dostaje kod źródłowy, osadza go w swoim produkcie, a backend execution pozostaje po jego stronie. Licencja Enterprise to jednorazowy koszt €6 990.

- -

Przy tym modelu cenowym naturalnymi klientami są firmy mid-market i enterprise budujące produkty B2B SaaS, które potrzebują edytora workflow jako feature swojego produktu — nie startupy szukające taniego MVP.

- -
-

Zrealizowano POC referencyjnej implementacji backendu: Temporal (orkiestracja), Hono (API), PostgreSQL (persistence), OpenRouter (LLM). Frontend plugin AI Studio integruje się przez SSE streaming — użytkownik klika Play i obserwuje real-time execution z podświetleniem krawędzi, statusami nodów i panelem wyników.

-
- -

Celem jest wykorzystanie tego backendu do materiałów, które przekonają decydentów (CTO, VP Engineering, Product Leads) że WB przyspieszy ich time-to-market o miesiące w porównaniu do budowania edytora in-house.

- - - -

Kto jest naszym klientem?

- -

Firmy, które budują produkty z konfigurowalnymi workflow dla swoich użytkowników końcowych. Nie firmy które używają workflow (to klienci Zapier/Make), ale firmy które osadzają edytor workflow w swoim produkcie.

- -

5 najsilniejszych segmentów rynku

- -
1.AI / LLM Orchestration Platforms — najszybciej rosnący segment. Firmy budujące platformy AI agent (à la Flowise, Dify, LangGraph Studio) potrzebują wizualnego edytora do komponowania łańcuchów promptów, RAG pipeline'ów, narzędzi. Dziś większość hakuje React Flow od zera.
- -
2.FinTech / InsurTech — systemy loan origination, underwriting, KYC/AML. Każdy klient (bank, ubezpieczyciel) ma inne reguły decyzyjne. Edytor workflow to feature produktu, nie dodatek. Budżety duże, potrzeba pilna.
- -
3.Customer Experience / Contact Center SaaS — platformy CCaaS budujące edytory call flow, chatbot routing, journey orchestration. WB ma już template "Call flow" który trafia w ten rynek.
- -
4.Healthcare / Clinical Operations — systemy EHR, care coordination, clinical trial management. Workflow kliniczne są silnie zróżnicowane per instytucja i regulowane — nie da się ich hardcodeować.
- -
5.DevOps / Platform Engineering — CI/CD, incident response, infrastructure provisioning. Organizacje budujące wewnętrzne platformy potrzebują wizualnych runbook builderów zamiast YAML-owych konfiguracji.
- - - -

3 scenariusze demo dopasowane do rynku

- -

Każdy template celuje w inny segment i demonstruje inne możliwości SDK. Razem pokrywają: AI orchestration, decision branching, parallel execution, context passing, real-time monitoring.

- -
-
-
1
- AI Agent Orchestration Pipeline -
-

Zapytanie użytkownika → klasyfikacja intencji → warunkowy routing do RAG / cache → human review gate → odpowiedź

-

Workflow modeluje typowy pipeline AI agent platformy: zapytanie trafia do klasyfikatora, na podstawie złożoności jest routowane do ścieżki z retrieval-augmented generation (RAG) lub bezpośredniej odpowiedzi z cache. Złożone zapytania przechodzą przez bramkę human-in-the-loop.

-
Trigger (query) → AI Classify Intent → Decision (complexity) → ∥ RAG Path | Direct Response ∥ → Human Review Gate → Deliver Response
-

- AI orchestration - conditional routing - najgorętszy segment -

-

Segment: AI / LLM Orchestration Platforms

-

Dlaczego to działa: Każda firma budująca produkt AI w 2026 rozpozna ten pattern. Pokazuje że WB to nie toy — obsługuje klasyfikację, branching, parallel paths, human-in-the-loop. Demo mówi: „Twoi użytkownicy mogą konfigurować takie pipeline'y wizualnie w Twoim produkcie."

-
- -
-
-
2
- Loan Application Decisioning -
-

Wniosek kredytowy → weryfikacja równoległa → AI risk scoring → decyzja → compliance review

-

Symulacja pipeline'u loan origination: wniosek triggeruje równoległe sprawdzenia (credit score, KYC, fraud check), AI agent ocenia ryzyko, Decision node routuje do auto-approval, manual review lub rejection. Ścieżka manual review ma delay (SLA timer) i eskalację.

-
Trigger (application) → ∥ Credit Check | KYC | Fraud Scan ∥ → AI Risk Score → Decision (tier) → Auto-Approve / Manual Review / Reject
-

- parallel verification - decision branching - FinTech / InsurTech -

-

Segment: FinTech / InsurTech — systemy loan origination, underwriting, compliance

-

Dlaczego to działa: Firmy budujące platformy kredytowe wydają miesiące na własne edytory reguł decyzyjnych. Ten template mówi: „Zamiast budować od zera — weź WB, dodaj swoje typy nodów, i masz production-ready feature za ułamek czasu." Fan-out weryfikacji + AI scoring + multi-way decision to pattern który FinTech natychmiast rozpoznaje.

-
- -
-
-
3
- Customer Support Escalation -
-

Ticket → AI klasyfikacja + sentiment → routing priorytetowy → SLA timer → eskalacja

-

Incoming ticket jest klasyfikowany przez AI agenta (priorytet + sentiment), routowany do odpowiedniego zespołu przez Decision node. Ścieżka krytyczna ma timer SLA i automatyczną eskalację gdy czas mija. Łączy AI z deterministycznym rule-based routingiem.

-
Trigger (ticket) → AI Classify (priority + sentiment) → Decision (tier) → ∥ L1 Auto-Response | L2 Agent Queue | L3 Escalation ∥ → SLA Check → Close / Escalate
-

- AI + rules hybrid - SLA timers - CX / Contact Center -

-

Segment: Customer Experience / Contact Center SaaS

-

Dlaczego to działa: Platformy CX (Zendesk, Intercom, Freshdesk competitors) potrzebują konfigurowalnego routingu jako core feature. Ten template trafia w ogromny, ugruntowany rynek z realnymi budżetami. Połączenie AI klasyfikacji z rule-based decision nodes pokazuje że WB nie jest „albo AI albo reguły" — obsługuje oba podejścia w jednym workflow.

-
- - - -

Dwa poziomy realizacji

- -
-
-

Poziom 1 — Przyszły tydzień

-

Cel: Gotowy materiał demo do pokazania klientom i na spotkania sales.

-
    -
  • 3 nowe template'y z dobrymi system promptami
  • -
  • Dopracowanie UI: markery na nodach, detail panel, execution log
  • -
  • Nagranie screencastu z każdego template'u (2-3 min per template)
  • -
  • Możliwość uruchomienia demo lokalnie przez sales/BD (pnpm dev:full)
  • -
  • Zaktualizowany Sales Inquiry template z wiedzą produktową WB
  • -
-

Output: 3 nagrane demo + gotowe środowisko lokalne do live demo na callach.

-
- -
-

Poziom 2 — Custom branded app

-

Cel: Udowodnić klientom że WB da się głęboko dostosować — od kolorów po custom node types.

-
    -
  • Osobna apka (np. demo.workflowbuilder.io) z custom brandingiem — inny kolor, logo, dark mode, nazewnictwo
  • -
  • Custom node types specyficzne per vertical (np. „Credit Check", „AI Agent", „SLA Timer")
  • -
  • Backend na Azure z Cloudflare Turnstile (zero friction, zero login)
  • -
  • Template picker: odwiedzający wybiera branżę (AI Platform / FinTech / CX) → widzi dedykowany template
  • -
  • Opcjonalny email capture po zakończeniu execution („Chcesz takie workflow w swoim produkcie?")
  • -
-

Output: Publiczna apka, która wygląda jak inny produkt zbudowany na WB — proof że SDK jest naprawdę white-label.

-
-
- - - -

Architektura (Poziom 2)

- -
-  demo.workflowbuilder.io         demo-api.workflowbuilder.io
-  ┌─────────────────────┐         ┌──────────────────────────┐
-  │  Custom branded SPA  │───────▶│  Hono API (port 3001)    │
-  │  + WB SDK (themed)   │  HTTPS │  + Temporal Server       │
-  │  + Template picker   │◀───────│  + Execution Worker      │
-  │  + SSE execution     │   SSE  │  + PostgreSQL             │
-  └─────────────────────┘         └──────────────────────────┘
-         │                               │
-   Cloudflare Turnstile           OpenRouter (LLM)
-   (invisible CAPTCHA)            Claude Haiku / Sonnet
-   + rate limit 5/IP/h
-
- -

Ochrona przed nadużyciami — bez logowania

-

Zamiast OAuth (które odpycha potencjalnych klientów), zastosowane zostanie podejście dwuwarstwowe:

- - - - -

Plan realizacji

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EtapZakresEffortZależności
1aNowe template'y
3 template'y (AI Pipeline, Loan Decisioning, Support Escalation) z system promptami, testy e2e
2-3 dniBrak — POC gotowy
1bScreencasts
Nagranie 2-3 min video per template, opis use case'u, upload na Vimeo/YouTube
1 dzieńEtap 1a
2aCustom branded app
Osobna apka z custom theme (kolory, logo, node types), template picker
5-7 dniEtap 1a
2bHosting + ochrona
Azure Container App, Turnstile, rate limiting, CORS, custom domain
3-5 dniAzure subscription, domena
2cIntegracja ze stroną
Sekcja „Try It Live" na workflowbuilder.io, link do branded demo, lead capture
2-3 dniEtap 2a + 2b
- - - -

Dlaczego custom app, a nie embed na stronie?

- -

Kluczowa obiekcja klienta to: „Czy to naprawdę da się dostosować do naszego produktu?"

- -

Jeśli demo wygląda jak domyślny Workflow Builder — klient widzi SDK. Jeśli demo wygląda jak kompletnie inny produkt zbudowany na WB — klient widzi potencjał.

- -

Custom branded app odpowiada na tę obiekcję zanim klient ją sformułuje. Pokazuje:

- - - - -

Oczekiwany wpływ

- - - - - -

Obecny stan POC

- -

Backend execution layer jest w pełni funkcjonalny na branchu feature/WB-37-workflow-execution:

- - - - - - From 052c396cc90aeebe5b8a1055b7625ec43a88a38e Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 24 Sep 2026 10:42:04 +0200 Subject: [PATCH 02/50] Design System 2.0 (#170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [DS 2.0] Fix dangling --wb-* custom properties + token-usage guard (#83) * fix(sdk): repoint dangling --wb-* custom properties at existing tokens 23 CSS variable names were used via var() but defined nowhere — --ax- prefix typos, misspelled token names, and two edge-label variables with no definition. Form message (neutral/warning) and the variables-settings modal were visibly rendering without their intended colors and spacing. Redundant fallbacks that masked the typos are dropped in the same lines; where a used name has no token equivalent, the value-equivalent token is substituted (tab-header 16px gap -> spacing-16). * fix(ui): resolve undefined custom properties in icon-switch and text-area icon-switch: the base thumb background referenced a variable that never existed and is always overridden by the primary/secondary variant rule — dropped the dead declaration. text-area: the disabled background variable was consumed but never defined; define it as transparent (today's effective render) so the --ax-public-* override contract actually works. * fix(ai-studio): correct --ax-txt-secondary token name in log panel The real token is --ax-txt-secondary-default. The shorter name never existed, so the hardcoded gray fallback was what actually rendered. Remaining dead fallbacks in this file are dropped alongside. * style: drop dead fallbacks on system design tokens Every one of these var(--ax-…, fallback) fallbacks is unreachable — the token is defined in the shipped tokens.css, so the fallback never wins. They also mask exactly the typo class the new token-usage lint catches, which now rejects unannotated fallbacks on system tokens. * chore(tokens): add token-usage lint guard lint-token-usage.mjs validates every var(--…) in packages/*/src and apps/*/src against the names that actually exist (tokens dist, source definitions incl. inline styles and setProperty, the provisional registry, and a runtime allowlist). Unknown names and unannotated fallbacks on system tokens are errors. Full-scan and per-file modes, self-builds dist when missing, and reports the var(--ax-*) usage count as the DS 2.0 migration meter. * ci: run the token-usage lint in pr-check and lint-staged CI runs the full scan in the ui job right after pnpm build:ui, so the dist it validates against is the one just built. lint-staged runs the per-file mode on staged css/ts files; the script path is anchored to the repo root because workspace lint-staged configs re-export the root config and run commands from their own directory. * docs(sdk): document the --wb-* prefix ownership convention * refactor(tokens): replace the custom token-usage lint with stylelint The hand-rolled regex scanner (~160 lines, growing with every review finding: comments, multi-line var(), @property, template literals) becomes configuration: csstools/value-no-unknown-custom-properties validates every var(--…) in workspace CSS against definitions collected by a small importFrom module (token dist + all source CSS + a short runtime-defined list), and a ~40-line local stylelint rule keeps the fallback ban on system tokens, with exceptions via the standard stylelint-disable-next-line comment and a mandatory reason. A real CSS parser fixes two scanner bugs for free (var() inside comments no longer flags, multi-line var() no longer escapes) and apps/docs is now excluded consistently in CI, lint-staged, and manual runs via ignoreFiles. The DS 2.0 migration meter stays as a one-liner in the CI step. Lockfile note: adding the stylelint devDependencies also re-resolves stale transitive typescript peer entries (5.9.3 -> 5.6.3) that no declared range permits — a one-time correction; declared ranges are all ~5.6.3. * fix(sdk): use the primary text token for the neutral message variant --ax-colors-gray-800 is a mode-less primitive; on the dark theme it lands dark-on-dark (1.61:1) against the theme-aware secondary background. --ax-txt-primary-default resolves to the same gray-800 in light and to gray-100 in dark. The sdk changeset is reworded around the changes a consumer can actually see (the message variants are not reachable from the public API yet), and the ui changes from this PR get their missed changeset (a new public TextArea variable is an API addition). * refactor(sdk): drop unconsumed edge color variables --wb-edge-color, -temporary, -hover, and -select have zero consumers — the live edge colors are the --ax-public-edge-color-* pair in packages/ui. The label-edge variables that are consumed stay; the stale missing-token comments on them pointed at tokens that exist. * fix(repo): resolve stylelint importFrom from the config file, name the meter The csstools rule resolves importFrom against process.cwd(), and lint-staged runs commands from workspace directories — caught by the pre-commit hook itself. The config moves to .stylelintrc.mjs and anchors the path via import.meta.url. The DS 2.0 migration meter moves out of an inline CI shell string into tools/migration-meter.mjs behind pnpm migration:meter. * docs: drop a brittle cross-rule remark from the stylelint plugin * ci: drop the migration meter from pr-check Informational, not a gate — pnpm migration:meter stays as an on-demand command for migration PRs. * docs: explain the stylelint definition source in plain terms * refactor(repo): replace the runtime-variable allowlist with in-place disables A central RUNTIME_DEFINED list in the definition collector rots and hides the reason away from the code. Each of the five usage sites of a JS-set variable now carries a stylelint-disable comment with the reason — self-cleaning: remove the usage and the exception goes with it. * chore: drop the migration meter from the repo Informational-only; lives on as a private local script for the DS 2.0 migration work instead of shipped tooling. * chore: shield the DS 2.0 migration changelog from prettier The designer changelog lands verbatim in packages/tokens/migration/ on the integration branch; prettier reformatting it corrupted token paths inside emphasis markers. Prettier reads .prettierignore only from the cwd and lint-staged runs from workspace directories, so the lint-staged command passes the root ignore file explicitly. * fix: restore the full .prettierignore and anchor it for lint-staged The previous commit accidentally replaced the whole ignore file with the single migration entry — the original entries (dist, coverage, .astro, generated icons) are back, migration/ added on top. lint-staged runs prettier from workspace directories where the root .prettierignore is invisible, so the command now passes it explicitly. * chore: drop tracker ids from changeset filenames * ci: run PR checks for the DS 2.0 stack branches Stacked PRs target the branch below them, and the pull_request branches filter matches the base — without these entries every layer above the bottom runs with no CI until the cascade retargets it. Temporary; removed when the stack lands. * ci: run PR checks for every base branch The base allowlist needed a new entry for each integration or stack branch and left dead entries behind; the default pull_request trigger covers them all. pr-check-docs keeps its paths filter. * fix(ai-studio): drop the raw fallback on the skipped marker token * [DS 2.0] Typography layer (wb-text-* roles) (#84) * feat(ui): add DS 2.0 type role classes 39 wb-type-{family}-{size}[-emphasized] utility classes straight from the Typography guidelines table (Display, Headline, Title, Body, Label, Node, UI/Code). Font sizes reference the wb/font-size primitives; until the DS 2.0 variables export ships them in tokens.css, the same block defines them as provisional values — the unlayered export will override the layered block, and the block gets removed with the export task. Existing ax-public-* classes are untouched; adoption starts with the Chips redesign. * feat(sdk): bundle Inter for the UI/Code type role Same rationale as Poppins — CSP-safe, GDPR-safe, air-gap-safe. Regular only, matching the single UI/Code style. * docs: add the typography page to the UI Library section * chore(ui): drop internal ticket reference from the provisional-block comment * fix(ui): typography review fixes - wb-type-code sets the family on descendants too — the SDK's universal font-family rule would otherwise repaint nested elements in Poppins, defeating the one role that ships its own typeface. - Font weights are order-independent: 400 and 600 live on disjoint selector lists instead of a base rule overridden later by source order (the repo already treats that hazard as a shipped bug class). - The NNN = px x 12.5 formula is approximate for 137 and 162 (truncation) — say so; drop the false monospace claim from the UI/Code comment. - sdk README documents the wb-type-* carve-out from --wb-font-family. * feat(ui): ship Poppins and Inter with the ui package The wb-type-* classes declare families the package did not deliver — a standalone consumer silently fell back to system-ui. The fontsource imports move from the SDK stylesheet to a ui fonts.css entry on the root barrel (layered via import layer(), matching the built-css guard), with the CSP/GDPR/air-gap bundling rationale carried along. The SDK inherits the same 12 font faces through the JS module graph — no duplication, and its own fontsource dependencies are gone. * refactor(ui): rename the type-role classes to wb-text-* In code, "type" reads as a data type before it reads as typography; text-* is the established CSS convention for typographic utilities. The classes have no consumers yet, so the rename is free. * docs(ui): prune paraphrase comments from the typography layer The per-family group headers restated the class names; the used-for prose lives in the docs page. What stays carries information the code cannot: the provisional block's lifecycle, the descendant-selector workaround, the naming scheme with the Emphasized=600 mapping, and the deliberate absence of an emphasized UI/Code variant. * refactor(ui): move the provisional font-size primitives to their own file A to-be-deleted block hiding inside typography.css relies on someone reading the comment; a dedicated provisional.css is wired into both distribution channels (the barrel import and the combine-css-bundle globals list), so deleting the file without cleaning the wire-ups fails the build in two places instead of silently shipping stale values. * refactor(ui): underscore-prefix the provisional stylesheet _provisional.css sorts first and reads as exceptional at a glance. * docs(sdk): move the wb-font-family lifecycle note to its definition The theming section stays about what the variable does today; the 3.0.0 removal-or-narrowing decision lives as a comment at the definition, where whoever touches it will actually look. * docs(ui): drop the wb-text section header The Emphasized=600 mapping is visible in the weight rule itself and the provisional file announces itself by name. * docs(ui): generalize the descendant-selector rationale The hazard is any universal font-family reset, not specifically the SDK's — the comment should survive the variable's possible 3.0.0 removal. * docs(ui): drop the Regular-only comment The docs page's scale table already records it. * chore: drop tracker ids from changeset filenames * fix(ui): declare the fontsource dependencies the fonts entry imports The original move committed fonts.css but lost the package.json/lockfile hunks in a branch-hop stash; local builds kept passing on node_modules installed outside git, and a clean install failed to resolve the imports — vite then emits no CSS assets at all and the combine step aborts. * docs: deprecate the ax-public typography classes explicitly "Remain available" invited new adoption; they only exist for the migration window and are removed in 3.0.0. * refactor(ui): drop the descendant font selector from the code role It patched one role against the universal font-family reset while every role's descendants stay exposed — symptom-level and inconsistent. The reset's fate is decided at the 3.0.0 close-out; until then the layers are allowed to be individually incomplete as long as the whole stack lands coherent. * docs(ui): tighten the fonts.css header to the two facts that matter * docs: drop the version label from evergreen prose Naming the system version in component docs and headers goes stale the moment the next version starts; the export references carry the meaning on their own. * feat(tokens): fail the build on colliding token names with different values (#85) Style-dictionary's name/kebab flattening can map two distinct token paths to one CSS custom property, and the later definition silently wins. The validator derives names by invoking SD's own name/kebab transform, so the check cannot drift from the build, and aborts on any collision whose values differ. * [DS 2.0] Chip component (#87) * feat(ui): add Chip component Compact tag in the DS 2.0 solid and outline treatments, four sizes, optional prefix icon and close affordance. Sized and colored per the Chips and Tag component set in the DS 2.0 Figma file; the chip tokens and wb/radius/50 live in _provisional.css until the token export ships them. The outline background is two stacked fills by design: the brand surface under a translucent theme overlay. * refactor(sdk): render the conditions counter tag with Chip The local .tag treatment is replaced by the UI Chip component; the class keeps only its layout margins and the tag background variable goes away with it. * docs: add the Chip component page Example, usage snippet, and generated props table. The component defines no ax-public-* variables (deprecated contract), so the CSS variables section renders the standard empty state. * fix(ui): apply Chip review findings The dark outline overlay aliased gray-900-75, whose exported value is the documented pre-2.0 bug (50% alpha instead of 75) - mixed from gray-900 directly, like the light theme already does. The close button gets the standard focus-visible ring. Variant and Size are renamed to ChipVariant/ChipSize and exported - the bare names shadowed two existing public types and rendered misleadingly in the generated props table. Chip now forwards its ref and native span attributes, and the close affordance takes an overridable closeLabel. The outline stroke is an inset ring instead of a border so outline and solid chips render the same width. Spacing metrics bind the space/size primitives from the provisional set. * docs(ui): sharpen Chip prop contracts The label doc records why it is a string (it feeds the close affordance's accessible name) and size gets a description - its TSDoc was only a default tag, which the generated props table does not surface, leaving an empty cell. * fix(ui): chip outline ring sits outside the box The Figma component strokes with strokeAlign OUTSIDE; the inset ring came from an unverified review claim of an inside stroke and ate one pixel of the chip's padding. * docs(ui): cut chip comments down to the exceptions Version labels in prose age badly; prop docs carry only defaults and the close-affordance contract, and the two CSS notes state their invariant without narrating the source. * docs(ui): drop the outline chip comments The token names carry the layering (inset under overlay) and the outer box-shadow ring is a standard idiom. * refactor(ui): rename the chip component paths to the singular form * [DS 2.0] Token pipeline 2.0: adopt the wb variable export (#88) * feat(tokens): switch the build to the 2.0 variable export The tokens.json export now comes from the WB Token Export plugin: Numerals is gone, Canvas (root-scoped) and per-theme Effects sets are in, and font-size primitives are emitted in rem while the other dimensions stay in px, as the design docs specify. The provisional stylesheet and both of its wire-ups go away - every value it carried ships in tokens.css now. * refactor: adopt the wb token names across the workspaces Mechanical rewrite of every var(--ax-...) usage to its wb equivalent: renames and prefix rules from the migration map plus value-matched dimension tokens (the old numeral scales map 1:1 onto wb/space, wb/size and wb/radius; two 100px pill radii become radius-full). Hand-mapped leftovers keep their old rendered values per theme: the accent chips in the sdk message and branch-card controls and the list-item selected/destructive backgrounds, which the retired dropdown tokens used to carry. No compatibility bridge: consumers theme through the unchanged ax-public contract, and the stylelint guard fails the build on any name this rewrite might have missed. * fix(tokens): emit dimensions in rem and bind the right token families The retired numeral primitives were authored in rem, so component boxes scaled with the reader's font size; the refreshed export is px-authored, which left text scaling while its containers stopped. Both transforms now convert px to rem and refuse any other unit instead of emitting NaN. Consumers bind the family that owns the value: radius from the radius scale, shadow geometry and edge strokes from the effects and canvas sets. List rows take the hover fill, which stays lighter than the surface they sit on in the dark theme. The docs API generator reads wb-prefixed definitions again, so component variables are classified as colors instead of falling through to sizes. Public theming docs point at names that exist, and the token README documents the unit split and the new sets. * fix(tokens): address pipeline switch review * fix(ai-studio): read execution status leftovers from the wb tokens * [DS 2.0] Rebuild Button with explicit variant, size and shape props (#89) * feat(ui)!: rebuild Button on the design system axes The component takes variant, size and shape as explicit props and composes its content from prefixIcon, children and suffixIcon, so the three structural subtypes and the children-shape inference behind them are gone. Ten variants cover the solid and ghost treatments, sizes are letter-scaled, and square and round render the icon-only box. Metrics and colours come from the design component set: heights 48/42/36/32/28 with matching padding, gap, radius, icon box and label role per size, and every fill, border and label colour bound to its component token. Call sites move with it: the outlined treatment is now ghost-secondary while secondary is the solid grey fill, error becomes critical, and word sizes become letters. * fix(ui)!: make the disabled and loading states legible, tighten Button props Disabled solid buttons paired the disabled surface with the ghost text colour, which resolves to the same value in the dark theme - the label and icons disappeared. They take the disabled text token the design specifies, and each ghost variant takes its own accent counterpart. The loading dots inherit the current colour instead of the on-accent one, so they stay visible on light ghost buttons. Props are a union: a label button with optional icons, or a square or round button that requires the icon it renders. Combinations that rendered an empty box no longer compile, and the element-child fallback is gone, so nothing inspects children's shape any more. Two SDK wrappers and one call site that erased the discrimination now name the label form explicitly. Loading also reaches assistive technology and blocks activation, not just pointers. The placeholder draws one dashed outline again, the retired variant variables are deleted, and the docs table lists every prop the component takes. * refactor(sdk)!: adopt the redesigned secondary treatment at call sites The design system's secondary is the solid grey fill. Call sites that carried the old outlined secondary move to it instead of keeping their former look through ghost-secondary - cancel and auxiliary actions now render solid, as the redesign intends. The outlined treatment stays available as ghost-secondary for deliberate use. * fix(ui): restore dashed placeholders and tighten button contracts * docs(ui): document the public button variable migration in the changeset * [DS 2.0] Rebuild the form fields against the design system (#90) * feat(ui)!: rebuild the form fields and add NumberField Fields take an explicit state - default, critical, success or read-only - in place of the error boolean, and sizes are letter-scaled. Hover, focus and filled stay visual states rather than props. Read-only keeps the field focusable and copyable through the native attribute, while disabled stays out of the tab order. Icons arrive as prefix and suffix slots with an optional clear affordance. Heights, padding, gap, radius and the label roles come from the design component set, and every surface, border and text colour binds to its field token. NumberField is new: an input with an always-visible stepper that honours min, max and step from both the buttons and the arrow keys. * fix(ui)!: compose fields with label and helper rows, harden NumberField Fields render the measured composition again: a label row, the control row and a helper row. The label is associated with the control, the helper joins its accessible description, and the critical state marks the control invalid, so validation text is announced instead of being merely visible. Focus is visible in every state, not only the default one. Interactive adornments no longer go into the decorative icon slot, which does not take pointer events - the datetime field renders its variable-picker button beside the input, the way its sibling branches already do. NumberField now has a value contract: clearing is representable and reported, a clamped value is emitted once, a controlled parent that refuses an update wins, non-finite input never reaches a callback, step=any keeps working, off-grid boundaries stay submittable, and malformed paste is rejected rather than reinterpreted. Fifteen tests pin those paths. * docs(sdk): declare the field adoption as a minor bump * fix(ui)!: simplify NumberField and harden field composition * refactor(ui)!: drop NumberField from the field layer * fix(ui,sdk): expose clearLabel on the field controls and align the critical variable names * fix(ui): stabilize field composition * fix(docs): replace inline form example styles * [DS 2.0] Repoint the remaining controls, overlays and effects (#91) * style(ui): bind focus rings, elevation and control metrics to the token set Focusable controls draw the ring the design system defines: the focus-element geometry with the focus colour, replacing the hardcoded offsets each component carried. Elevation comes from the shadow ramp with its own colour token, so dark mode no longer needs a second rule. Selector, snackbar, tooltip and date-picker surfaces bind their component tokens, and the sizes that were literal rem values now take the size scale. * style(sdk,ai-studio): repoint the remaining design-token usage The SDK global stylesheet, the diagram surface and the execution markers bind canvas and ui tokens; app-scoped variables stay app-scoped. * fix(ui): restore the contracts and metrics the repoint changed The public snackbar background variables carry the complete layered value again - a status tint over the surface, consumed as one background - because two SDK surfaces read them directly and lost their tint when the tint moved into a private rule. Selector and switch dimensions go back to the values they had. The size scale has no step for most of them, so snapping to the nearest one silently resized every checkbox, radio and switch; a token is bound only where it resolves to the same value. The date-picker popup takes the base surface again, so hovering a day is visible, and the generic radio dot rule no longer overrides the size classes that follow it. * fix(ui,sdk): restore modal hierarchy and align repointed details with the design * fix(ui): preserve list item focus in forced colors * [DS 2.0] Rebuild NavButton, add MenuTriggerButton (#92) * feat(ui)!: rebuild NavButton, add MenuTriggerButton NavButton takes size, styleVariant and explicit icon slots, so the label/icon/icon+label subtypes and the shared children-shape guards are gone - nothing imports them any more, so they are deleted too. The selected state and the mouse-down state are now separate: selection survives hover and pointer-down overrides it, each on its own token, which is what the design defines and what the token export ships. Seven sizes carry the design's boxes, padding, gap, radius and icon boxes; nav labels keep the regular weight. MenuTriggerButton composes NavButton and maps its open state onto selection. SegmentPicker keeps its public API and translates to the new slots internally. * fix(ui): restore SegmentPicker compatibility and tighten the nav slots SegmentPicker accepted an icon, a label, or a mix of both as children; the slot rewrite only handled the single-element form, so mixed content lost its icon treatment. Children are normalised into slots again, the selected segment reports its state to assistive technology, and clicking it no longer emits a change it used to swallow. NavButton's props became a union: a label button with optional icons, or an icon button that requires its icon. Neither form can now be written so that it renders nothing. The visual treatment prop is called variant, matching the other components. The language selector lets the button own its label typography instead of composing a retired global class, and the two style modules the rebuild orphaned are gone. * docs(sdk): declare the nav adoption as a minor bump * fix(ui,sdk)!: give NavButton real state axes and tighten the trigger contracts * fix(ui,sdk): complete NavButton adoption fixes * fix(ui,sdk): localize disclosure labels * [DS 2.0] Ship the fonts as assets instead of base64 (#93) * perf(ui)!: ship the fonts as assets, inline only the two used weights The library inlined twelve font faces as base64 because Vite's library mode inlines every asset unconditionally, so the built stylesheet carried 382 KB of fonts: index.css was 509 KB and the SDK stylesheet, which bundles it, 591 KB. The faces are generated after Vite finishes, from the fontsource metadata, and copied into dist/assets. Poppins 400 and 600 latin stay inlined - the only weights the typography classes declare - so the common text needs no extra request. Everything else is fetched on demand, and each face now carries the unicode-range the per-subset fontsource files omit, so a document without extended latin skips those files entirely. The legacy woff source is gone. index.css is 150 KB, the SDK stylesheet 230 KB, and dist gains ten woff2 files. A new gate fails the build when a stylesheet references an asset that is not in dist - the failure mode this arrangement invites, and the one a previous font change hit only on clean CI. Consumers keep their imports; a Content-Security-Policy naming font-src needs 'self' rather than 'data:', and the dist layout has to survive copying. * docs: scope the label recommendation, follow the font move in knip The role table recommended the emphasized large label for chips, while the chip component set specifies the small regular label at every size - the row now names buttons alone and chips get their measured role. Knip's ignore entry moves to the ui workspace, where the fontsource packages now live and are consumed outside its JS/TS walk, and the CLAUDE.md table stops claiming knip runs as part of pnpm check. * fix(ui,sdk)!: carry the font faces in the entry chunk and harden the font pipeline * fix(ui,sdk): harden font asset handling * fix(ui): ship font licenses with font assets * docs: register the tenant and font decision logs in the index * [DS 2.0] Rename public CSS variables to the wb namespace (#97) * refactor(ui,sdk)!: rename public CSS variables * fix(sdk,docs): layer public variables and true up rename docs * [DS 2.0] Bind editor text to explicit typography roles (#98) * refactor(ui,sdk)!: bind text to explicit type roles * fix(ui,sdk)!: bind typography to the public family lever and settle the type-role review * fix(sdk,ui): close explicit typography review gaps * fix(sdk): align code editor with code type role * fix(ui): read the date picker font size from the token * [DS 2.0] Migrate the ui primitives to the type roles (#101) * refactor(ui)!: migrate primitives to type roles and drop legacy classes * fix(ui): complete typography migration notes * [DS 2.0] Split the CSS variable namespaces (#102) * refactor(ui,sdk)!: split the CSS variable namespaces * fix(ui,sdk): correct namespace migration contract * [DS 2.0] Shrink the built-CSS checker and document the pitfalls (#104) * build(ui): streamline built CSS validation * fix(ui): harden built CSS checks * [DS 2.0] Use the measured source-node height for self-loop edges (#121) * fix(sdk): use the measured source-node height for self-loop geometry and label LabelEdge subscribes to the source node's height in the React Flow store (measured.height, then height, then 0) instead of reading a getNode() snapshot, so the loop path and its label follow every remeasurement - xyflow 12 writes ResizeObserver results to measured.height only. The same value is passed to SelfConnectingEdge, whose nodeHeight prop previously defaulted to 0. * fix(sdk): derive the self-loop height inside SelfConnectingEdge Review follow-up. The measured-height selector moves to a shared useSelfLoopNodeHeight hook (exported), so SelfConnectingEdge reads the source node's measured height when nodeHeight is omitted instead of drawing the loop for a zero-height node; LabelEdge keeps passing its own value so loop and label stay in lockstep. Specs cover the regular-edge early exit, the realistic measured-but-empty fixture and both paths of the new default. * [DS 2.0] Render the default canvas port at the designed 8px (#122) * fix(ui): zero inherited xyflow handle minimum for 8px default ports xyflow's 5px minimum applies to the content box and expands the bordered port to 9px. Reset both minimums so 4px content plus 2px borders remains 8px. * fix(ui): grow the target port while a connection hovers it React Flow captures the pointer on the dragged handle, so the node under the cursor never receives :hover and its ports stayed at the 8px default during a connection. The connectingto state now shares the connectingfrom rule, so the target shows the same 16px active port as the source. * fix(ui): grow only valid connection targets and drop the handle geometry check Review follow-up. The connecting-state rule now targets .connectingto.valid, so a port rejected by isValidConnection keeps its default size while the connection line reports invalid; the comment explains the real reason the target needs its own rule (xyflow assigns connectingto by proximity within connectionRadius, so :hover cannot express it). The built-CSS geometry check is removed: it matched every rule ending in .react-flow__handle and would have failed any future state rule on the bare handle, and its exact-string comparison against minified output was fragile. The pitfalls entry records the manual check instead. * [DS 2.0] Move the node shell to the DS 2.0 geometry and derive ConnectableItem width (#123) * fix(ui): move the node shell to DS 2.0 geometry (241px, head padding and gap tokens) Width follows the design master as a canvas dimension in px (it must not scale with the root font size). Padding and vertical gap bind to the node head roles canvas-node-head-h-pad and canvas-node-head-gap from the token export (8px each) instead of the generic space-100 step. * fix(sdk): derive the ConnectableItem width from the real container insets Replaces the undocumented 6 * padding factor with named insets (shell padding + border, plus the wrapping container's own inset declared via --wb-sdk-connectable-item-inset). The old content-box correction terms are dropped because the SDK applies a global border-box reset. * docs(sdk): describe the ConnectableItem cap change and its follow-up in shipped terms Review follow-up. The changeset states the actual effect (the cap narrows to the space the section offers, so labels truncate instead of overflowing). The decision log drops pointers to a register outside the repo and names the pending design work with a grep-able slug. * [DS 2.0] Strip measured node sizes from saved and exported diagrams (#124) * fix(sdk): strip measured node sizes from saved and exported diagrams Runtime dimensions were persisted alongside the diagram and observed to stay at the stale stored value after remeasurement, so exports and localStorage carried sizes that no longer matched the rendered nodes. Sizes are a runtime fact; nodes are measured again on load. * chore(fixtures): drop stored measured node sizes from demo and AI Studio flows Runtime dimensions are no longer persisted (see the sdk change in this branch), so the 82 stored measured values were dead data that still recorded the pre-DS 2.0 width. * [DS 2.0] Keep node title and subtitle on one line with ellipsis and tooltip (#126) * fix(ui): keep node title and subtitle on one line with ellipsis and tooltip Design rule for DS 2.0 nodes: fixed width, height grows, title and subtitle truncate to one line with the full text in a tooltip. The description block also stops contributing intrinsic width (contain: inline-size), so a long subtitle no longer widens bodies sized with min-width: max-content. * fix(sdk): show the full connectable item label in a tooltip Decision branch rows and AI tool rows already clip long labels with an ellipsis but exposed no way to read the full text. The label now carries a title attribute, matching the node title and subtitle behaviour. * fix(sdk): apply the design body spacing to node sections and rows (#136) Sections (Decision branches, AI Agent tools) use 8px padding and gap and the content radius role; rows use 8px padding and a 4px radius. Values bind to the space/100 and radius/50 primitives with missing-token markers until the node.body roles land in the token export. The derived row width becomes 205px. * feat(ui): drop the warning and ghost-warning button variants (#137) Design confirmed the warning button has no practical use in the product: the only occurrences were the import dialog's ignore-and-import action and the warning snackbar action, both of which now render as secondary. The variants, their public custom properties and the docs example entries are removed; the semantic warning role (snackbar surface, node focus ring, execution statuses) is untouched. Migration steps live in the changeset. * chore(tokens): refresh the Figma export and bind button sizes to the size scale (#138) Export from 14.09.2026 adds ui/bg/selected, ui/bg/selected-hover and the size steps 225, 250, 350, 450 and 525 (18, 20, 28, 36, 42px). No token was removed or changed. Button heights L/M/XS and icon sizes L/M/S now resolve from the size steps, which replaces six provisional rem literals with the values design published for them. * feat(ui): mark the current choice in Menu with selected (#139) A menu whose items define selected renders them as a Base UI radio group, so the current entry carries menuitemradio and aria-checked. Selected list entries (menu radio items and Select options) move from the solid accent fill to the ui/bg/selected role with default text and a selected-hover state. The language menu marks the current language. * feat(sdk): render palette entries with the canvas Node states (#140) Design answer to the palette questions: the palette has no component of its own, an entry is the Node without ports and badge, and its states are the Node states. Hover already came from the shell; the drag preview now renders the Active state (outline and ring), and read-only entries render the Disabled variant instead of a faded copy. NodePanel.Root, NodeDescription and NodeIcon gain a disabled prop with their own public defaults; the four node templates pass it through. The palette's unused variables file and outline styling are removed. * chore(tokens): adopt the 15.09 and 16.09 exports (design system 1.1.9 to 1.1.12) (#149) Node body and row roles, focus ring rename (shadow and the four colour roles), ghost label roles, rebuilt orange scale. The Status invalid badge and the indicator dot move from the orange-400 primitive to the warning roles so they stay visible on the rebuilt scale. * fix: address review follow-ups on the canvas stack (#150) * fix: address review follow-ups on the canvas stack - persist: strip the runtime dragging flag alongside measured and selected, drop dragging from the bundled templates, cover getStoreDataForIntegration - node description: document the contain: inline-size constraint - connectable item: rewrite the width decision log with the current numbers and a status section; reword the tooltip changeset - menu: lay out the RadioGroup wrapper as a column so the list-box gap applies - ui specs: unmount the React root after each test - palette: forward disabled in the custom template guide and the demo multi-port template, document it on NodePanel.Root, reword the changeset * fix: apply the 16.09 design answers to rows, loops and the demo template - rows: in the horizontal layout Decision branches and AI tools fill the section width like the design's Node / Row; the derived cap and the Decision max-content rule now apply to the DOWN layout only; branch and tool rows are spaced with the node body gap - self-loop: the apex sits 48px above the node's top edge regardless of node height and port position; getSelfLoopHeight is exported - node text: follow-up markers for the DS Tooltip decided for 3.1 - demo: the multi-port template forwards disabled to icon and description * test: set IS_REACT_ACT_ENVIRONMENT once per package in a vitest setup file (#152) * test(ui): set IS_REACT_ACT_ENVIRONMENT once in a vitest setup file The flag was set ad hoc at the top of six specs, with two competing typings (a globalThis cast and a declare global block). vitest.setup.ts sets it for every test, types.d.ts declares it, and the setup file is type-checked but excluded from the emitted declarations. * test(ai-studio): set IS_REACT_ACT_ENVIRONMENT in a vitest setup file Same layout as packages/ui: vitest.setup.ts sets the flag, types.d.ts declares it, the spec drops its declare global block. * fix: follow-ups from the final Design System 2.0 review (#161) * fix(ui): follow the design library on node rows, fields and controls Final review before 3.0 read every value back from the Figma masters: - node rows drop the border the master does not have, take canvas/node/bg-content-default and Body/S Emphasized; sections and the AI tools wrapper outline with canvas/node/stroke-default - the node subtitle renders at Label/S, the size the master binds - disabled and read-only fields, plus hovered list items, use ui/bg/inset like the checkbox, radio and switch already did - field labels, helpers and the required marker each take their own text role, and a success helper finally has one - Select and DatePicker join the field redesign: they render inside Field, share the one size scale, and paint a disabled background - edge labels subtract their border from the padding, so the outer size matches and selection no longer resizes the label - NavButton label padding, menu item inline padding, snackbar width and buttons, segment picker padding and avatar sizes match the library The self-loop apex is derived from the node's top edge instead of half its height, which only agreed on a 64px node. * fix(sdk): ship the font licences and document every NavButton prop The SDK copies the UI font assets into its own dist so consumers resolve `./assets/*.woff2` without installing the UI package, but the copy filter kept only `.woff2` files, leaving the SIL Open Font License texts behind. The copy step moves into `font-assets.mts` and now takes the licences too, with a spec that no longer needs a build to run. NavButton reported 4 props in the docs because its base type sat behind `Omit`, which the UI API generator could only warn about. The generator unwraps `Omit`, `Pick` and `Partial` over first-party types, and warns when a referenced prop type is missing from the TypeDoc output at all. NavButton documents 8 props. * fix(sdk): repoint the editor at the renamed input size variables The letter-based field size scale replaced the word-based one and removed `--wb-public-input-padding-medium` and `--wb-public-input-border-radius-medium`, but the syntax highlighter, the dynamic typed input and the variable text still referenced the old names. An undefined custom property makes the declaration invalid at computed-value time, so those surfaces lost their padding and radius with nothing failing. The changesets are rewritten against 2.3.0, the last published version. A reader of the CHANGELOG never sees the states this branch passed through, so superseded entries are merged into the change that replaced them: the self-loop height step into the apex change, the row width cap into the fill rule, the body roles into the body spacing, and the three token exports plus the token prefix rename into one design-token entry. Every major changeset now carries a `Breaking changes:` list, and RELEASE.md states both rules. * docs: add the 3.0 upgrade guide Covers the move from the bundled @synergycodes/overflow-ui to @workflowbuilder/ui, the three CSS custom property families that replace the single --ax- namespace, the component API migrations, and the behaviour changes a 2.3.0 editor will notice. The design-token section states the real shape of the change: of the 668 tokens published in 2.3.0, 150 keep their name under the new prefix and 518 have no direct counterpart, because the per-component spacing and radius layer became a generic scale plus semantic role sets. The 88 primitives that keep their name but change value are listed with both values, since they repaint a theme that migrates by prefix alone. * docs: scope the button shape migration to Button `SegmentPicker` still takes the `Shape` type it always had, `default` or `circle`, and maps `circle` onto the NavButton `round` variant internally. Only `Button` moved to `ButtonShape` with `default`, `square` and `round`. The changeset and the upgrade guide told every consumer to rename `shape="circle"`, which is wrong advice for `SegmentPicker`. * fix(ui): paint a disabled list entry the way the design library does The `Menu item` master binds `ui/bg/inset` for Hover, Focus, Active and Disabled alike, and `ui/text/disabled` for a disabled label, across all three sizes. The disabled rule went transparent instead and took its text colour from `--wb-public-input-color-disabled`, which resolves to `ui/text/ghost-default` - the role the master gives the suffix text, not the label. Both values are exposed as `--wb-public-list-item-background-color-disabled` and `--wb-public-list-item-color-disabled`, so the entry stops reaching across to the input's variable for a colour that was wrong anyway. * feat(ui): mark the chosen list row with a check, not colour alone The `Menu item` master carries a `Selected indicator` in its Selected and Selected + Hover variants: Phosphor Check, Outline/Bold, 16px, bound to `ui/icon/action-default` and drawn at the trailing edge. Its usage guidelines state that a selection is never communicated by colour alone. Neither `Menu` nor `Select` rendered it, and the two states they had to distinguish sit 1.011:1 apart in the light theme. Row icons also take their own roles now: `ui/icon/subtle-default` by default and `ui/icon/disabled` when the row is disabled, where before they inherited the label colour. A destructive row keeps its own colour, since the master has no destructive variant to follow. * fix(sdk): render the palette Templates action as ghost-secondary The `Left_Sidebar` master binds Ghost-Secondary, Size S for it. The button kept `variant="secondary"` through the redesign, which only renamed its size; `secondary` meanwhile went from the outlined treatment to the solid grey one, so the action turned into a filled grey slab. * fix(ui,sdk): align the snackbar row and stop the folder label wrapping The snackbar row had no vertical alignment. Its message column stretched to the row height, which the 32px close button sets, so the text rendered 6px above the icon and the button. The `Snackbar` master aligns the row to centre and, in the two-line variant, keeps the icon level with the first line - both are now expressed in CSS. In the app bar, `.title` carried `white-space: nowrap` and `.folder-name` did not, so entering title edit mode widened the field and broke the label over two lines. * feat(ui): follow the design answers on placeholder, avatar, disabled and critical rows Token export of 18.09 (library 1.1.13): new `gray-550` primitive, new values for `ui/text/muted-default` in both themes, `ui/icon/subtle-default` in dark and `ui/text/critical-default` in light. Nothing renamed or removed. - the placeholder takes `ui/text/subtle-default`; `ui/text/ghost-default` is the disabled-state role and left it at 1.8:1 - `Avatar` medium and small bind `size/300` and `size/225` instead of literals - a disabled list row has no background; the fill it carried was inherited from the neighbouring hover variant, not a decision - a destructive row becomes `tone="critical"`: the label and icon carry the critical colour, the row keeps the ordinary background, and the red tints are gone * fix(ui): keep a selected edge label the size it has in every other state The padding subtracts the regular border width in every state, but the selected and temporary rules swapped `border-width` to the bold size without touching the padding, so the label still grew 2px per axis on selection - the same delta as before the padding compensation landed. Only the overall box had got smaller. The `Edge_labels` master draws every state of a size at one box (XS 85x28, S 98x32, M 102x36) and thickens the stroke inward, so the extra weight is now an inset shadow and the border box stays put. * chore(ui): start the published version line at 1.0.0 `@workflowbuilder/ui` has never been on npm, so its first release starts its own version line rather than continuing the internal 2.0.0 that no consumer can install. The package version drops to 0.1.0 so the pending major changesets resolve it to 1.0.0; the SDK still resolves to 3.0.0. The upgrade guide named 3.0.0 as fact and `move-ui-library-in-repo.md` cited `@workflowbuilder/ui@2.0.0`, a number that would have reached the CHANGELOG without ever reaching npm. * docs(changeset): state what the segment picker change actually did The entry claimed the padding now follows the size of the picker. There is no such mechanism and none was intended: all 70 variants of the master sit at zero container padding, so the padding and its public property were removed outright. That removal is breaking for anyone overriding `--ax-public-segment-picker-padding`, which shipped in the UI library bundled by 2.3.0, so the entry moves to a major with a migration step. * docs: count distinct custom properties, not token paths The 2.3.0 export carries `acc7- 100` through `acc7- 950` alongside the same names without the space, and each pair collapses to one CSS custom property. Counting paths made the totals ten high: 658 published and 508 without a counterpart, not 668 and 518. The new-role count moves to 419 with the `gray-550` primitive the 18.09 export added; 150, 88 and 62 reproduce unchanged. * docs(changeset): put the self-loop breaking list on a major `SelfConnectingEdge` was exported from 2.3.0 with a `nodeHeight` prop that no longer exists, so the entry is breaking and its `Breaking changes:` list belongs on a major, as RELEASE.md now states. The resolved version is unchanged; the CHANGELOG grouping is not. * fix: close four gaps the external review found - the props-table generator recurses into the source of `Omit` / `Pick` / `Partial` instead of resolving it by id, so a nested utility type no longer drops every inherited prop, and it warns when the keys are not string literals rather than silently keeping what it should drop - the SDK font copy throws when no `OFL-*` file was found; the prefix is a second copy of a convention `packages/ui` owns, and a rename there used to republish the fonts unlicensed with both specs still green - `useSelfLoopApexY` resolves its own fallback and returns a number, so the loop and its label read one expression instead of two that agreed by luck - `Select` and `DatePicker` set `aria-required` on their button trigger Docs: the upgrade guide lists the three public properties that go away with no counterpart, which two major changesets promise, and RELEASE.md points at a changeset that still exists. * docs(release): drop the org-level trusted publisher path RELEASE.md sent the maintainer of a not-yet-published package to the npm organization settings for an "Add trusted publisher" button. No npm documentation describes that path, and the `npm trust` reference states the opposite prerequisite: the package must already exist on the registry. The step now names the documented page only and says that a first version is published by hand, with the trusted publisher registered afterwards. This applies to `@workflowbuilder/ui@1.0.0`. * fix(docs): lay out UI examples inside the preview shadow root Examples render in a shadow root that receives only the library CSS, so the per-example CSS modules for Button, NavButton, Input and TextArea never reached their wrappers and every child stacked as a block. ComponentPreview now owns the layout as `Stack` and `Row`, and the three module files go away. The stage also clipped tall examples: `overflow: hidden` makes it a scroll container, which has no content-based automatic minimum, so `aspect-ratio` acted as a hard clamp. `overflow: clip` hides the same overflow without that side effect and the stage grows with its content. * refactor(ui): make the menu trigger a Menu.TriggerButton compound part `MenuTriggerButton` only ever renders as the `Menu` child, so it moves next to `Menu` and is reached as `Menu.TriggerButton`, like `SegmentPicker.Item` and `Tooltip.Trigger`. `Menu` now tracks its open state for the uncontrolled case as well and hands it to the trigger through context, so the `isOpen` prop and the duplicated state in every consumer go away. Docs: the standalone page folds into a section on the Menu page with its own props table; the registry entry stays so the coverage guard keeps the props table in check. The package has not been published yet, so nothing here is a migration. * chore(tokens): adopt the 21.09 export (design system 1.1.14) Three values change and one token is added; nothing is renamed or removed. `ui/bg/canvas` and `ui/bg/app` become gray-300 in the light theme, so the canvas parts from the white panels, and the grid dots step down to gray-450. In the dark theme `ui/bg/app` moves from gray-900 to gray-800. The new `ui/bg/inset-subtle` (gray-200 / gray-650) has no consumer yet. The export's code-syntax fixes on the Figma side (`--wb-bg-app`, `--wb-surface-sunken-subtle`) do not reach this repository: the generated property names come from the token path, not from Figma's code syntax. * fix(sdk): give VerticalLayout the class its stylesheet declares The renderer asked the CSS module for `horizontal-layout`, which `vertical-layout.module.css` never defines, so the wrapper rendered as a plain block and stacked controls touched. Present since the 2.0.0 code drop; visible wherever a `VerticalLayout` holds more than one control, such as the two selects under the AI Agent node's Operational Settings. * docs(changeset): drop a reverted claim and tighten two release notes `menu-item-selected.md` still described a disabled list entry keeping the inset surface and exposing `--wb-public-list-item-background-color-disabled`. That commit was reverted when the design answers came back: on this branch a disabled row is transparent and no stylesheet declares the variable, which the critical-tone changeset already states. Only the `--wb-public-list-item-color- disabled` migration note stays. `vertical-layout-class.md` names the nested layouts as the affected surface; the root layout takes its 16px gap from `.json-form-container > div` and never depended on the broken class. `canvas-background-role.md` no longer orders `ui/bg/inset-subtle` between `bg/base` and `bg/inset`, which holds only in the light theme. * docs: align the UI changelog with the 1.0.0 first release and defer the nodeId removal `packages/ui/CHANGELOG.md` still introduced the package as `2.0.0` and kept a note between the `# Changelog` heading and the first version, which `release:version` would have folded into the generated release notes. The first section is now `## [1.0.0]`, ready to receive the generated bullets, with the overflow-ui provenance inside it. `getHandleId`'s deprecated `nodeId` stays a no-op through 3.x; its JSDoc now names 4.0 as the removal, since the 3.0 upgrade already carries the token migration. --- .changeset/accessible-dismissible-labels.md | 6 + .changeset/app-bar-folder-name-nowrap.md | 5 + .changeset/avatar-size-primitives.md | 5 + .changeset/built-css-invariants.md | 5 + .changeset/button-redesign.md | 16 + .changeset/canvas-background-role.md | 6 + .changeset/canvas-node-geometry.md | 5 + .changeset/canvas-port-size.md | 5 + .changeset/chips-component.md | 5 + .changeset/conditions-tag-chip.md | 5 + .changeset/connectable-item-tooltip.md | 5 + .changeset/css-variable-namespaces-sdk.md | 10 + .changeset/dangling-token-names.md | 5 + .changeset/design-library-parity.md | 9 + .changeset/explicit-type-roles-ui.md | 5 + .changeset/explicit-type-roles.md | 10 + .changeset/field-aria-required.md | 5 + .changeset/font-assets.md | 16 + .changeset/form-fields-redesign.md | 15 + .changeset/import-modal-secondary.md | 5 + .changeset/language-selector-selected.md | 5 + .changeset/menu-item-critical-tone.md | 10 + .changeset/menu-item-selected.md | 7 + .changeset/move-ui-library-in-repo.md | 2 +- .changeset/nav-button-redesign.md | 12 + .changeset/node-body-spacing.md | 5 + .changeset/node-panel-disabled.md | 5 + .changeset/node-rows-fill.md | 5 + .changeset/node-text-ellipsis.md | 5 + .changeset/palette-node-states.md | 7 + .changeset/palette-templates-ghost.md | 5 + .changeset/persist-without-measured.md | 5 + .changeset/placeholder-subtle-role.md | 5 + .changeset/port-connecting-target.md | 5 + .changeset/public-variable-namespace.md | 11 + .changeset/remove-legacy-type-classes.md | 12 + .changeset/self-loop-offset.md | 10 + .changeset/snackbar-row-alignment.md | 5 + .changeset/textarea-disabled-background.md | 5 + .changeset/token-set-2.md | 14 + .changeset/type-role-classes.md | 6 + .changeset/ui-export-prop-types.md | 2 +- .changeset/ui-layer-root-defaults.md | 2 +- .changeset/vertical-layout-class.md | 5 + .github/workflows/pr-check-docs.yml | 4 +- .github/workflows/pr-check.yml | 10 +- .prettierignore | 2 + .stylelintrc.mjs | 15 + CLAUDE.md | 2 +- DECISION-LOGS.md | 8 + .../controls/ai-studio-controls.module.css | 6 +- .../controls/ai-studio-controls.tsx | 28 +- .../disclaimer/disclaimer-modal.module.css | 38 +- .../src/components/execution/highlighting.css | 33 +- .../components/execution/log-panel.module.css | 24 +- .../execution/node-markers.module.css | 18 +- .../visualize/copy-result-button.spec.tsx | 6 - .../components/visualize/renderers.module.css | 42 +- .../visualize/visualize-card.module.css | 22 +- .../visualize/visualize-modal.module.css | 22 +- apps/ai-studio/src/data/ai-debate-flow.ts | 10 - .../src/data/content-repurposer-flow.ts | 12 - apps/ai-studio/src/data/meeting-notes-flow.ts | 10 - apps/ai-studio/src/data/research-flow.ts | 6 - .../ai-studio/src/data/support-triage-flow.ts | 16 - .../buttons-undo-redo/buttons-undo-redo.tsx | 20 +- apps/ai-studio/types.d.ts | 7 + apps/ai-studio/vite.config.mts | 1 + apps/ai-studio/vitest.setup.ts | 1 + .../tenant-context-port.decision-log.md | 2 + .../components/dashed-edge/dashed-edge.tsx | 2 +- .../multi-port-node-template.tsx | 7 +- .../src/app/data/templates/black-friday.ts | 15 - apps/demo/src/app/data/templates/call-flow.ts | 26 - .../src/app/data/templates/simple-flow.ts | 22 - .../app/data/templates/user-registration.ts | 18 - .../buttons-undo-redo/buttons-undo-redo.tsx | 20 +- apps/docs/astro.config.mjs | 10 +- apps/docs/scripts/generate-ui-api.mjs | 74 +- apps/docs/scripts/ui-components.mjs | 33 +- .../src/components/ui-examples/button.tsx | 26 +- apps/docs/src/components/ui-examples/chip.tsx | 17 + .../ui-examples/component-preview.module.css | 9 +- .../ui-examples/component-preview.tsx | 23 +- .../docs/src/components/ui-examples/input.tsx | 38 +- apps/docs/src/components/ui-examples/menu.tsx | 29 +- .../src/components/ui-examples/nav-button.tsx | 21 +- .../src/components/ui-examples/status.tsx | 2 +- .../src/components/ui-examples/text-area.tsx | 26 +- .../src/content/docs/get-started/theming.md | 17 +- .../content/docs/get-started/upgrade-to-3.md | 323 + .../content/docs/guides/add-a-custom-node.mdx | 18 +- .../design-system-and-customization.mdx | 28 +- .../content/docs/plugins/reshapable-edges.mdx | 2 +- .../content/docs/ui-library/design-tokens.mdx | 38 +- .../diagram-components/node-description.mdx | 15 + .../diagram-components/node-icon.mdx | 4 + .../diagram-components/node-panel.mdx | 18 +- .../src/content/docs/ui-library/overview.mdx | 69 +- .../content/docs/ui-library/typography.mdx | 58 + .../docs/ui-library/ui-components/button.mdx | 7 +- .../docs/ui-library/ui-components/chip.mdx | 33 + .../docs/ui-library/ui-components/index.mdx | 2 +- .../docs/ui-library/ui-components/input.mdx | 23 +- .../docs/ui-library/ui-components/menu.mdx | 54 +- .../ui-library/ui-components/nav-button.mdx | 20 +- .../ui-library/ui-components/text-area.mdx | 16 +- apps/docs/src/sdk-shim.ts | 5 +- docs/how-to-change-css-tokens.md | 24 +- knip.config.js | 8 +- lint-staged.config.mjs | 7 +- package.json | 7 +- packages/RELEASE.md | 4 +- packages/sdk/README.md | 23 +- packages/sdk/font-assets.mts | 29 + packages/sdk/font-license.spec.mts | 58 + packages/sdk/package.json | 3 +- .../sdk/sdk-restructuring.decision-log.md | 2 + .../button-submit/button-submit.module.css | 10 +- .../button-submit/button-submit.tsx | 5 +- .../components/form/label/label.module.css | 16 +- .../form/message/message.module.css | 38 +- .../src/components/loader/loader.module.css | 11 +- packages/sdk/src/components/loader/loader.tsx | 6 +- .../src/components/sidebar/sidebar.module.css | 22 +- .../sdk/src/components/sidebar/variables.css | 16 +- .../src/features/app-bar/app-bar.module.css | 17 +- .../app-bar/components/controls/controls.tsx | 11 +- .../project-selection.spec.tsx | 24 +- .../project-selection/project-selection.tsx | 9 +- .../sdk/src/features/app-bar/variables.css | 12 +- packages/sdk/src/features/diagram/diagram.tsx | 2 +- .../src/features/diagram/edges/edge.consts.ts | 9 +- .../enhanced-base-edge.module.css | 4 +- .../edges/label-edge/label-edge.module.css | 30 - .../edges/label-edge/label-edge.spec.tsx | 88 + .../diagram/edges/label-edge/label-edge.tsx | 16 +- .../diagram/edges/label-edge/variables.css | 9 - .../self-connecting-edge.spec.tsx | 80 + .../self-connecting-edge.tsx | 30 +- .../features/diagram/handles/get-handle-id.ts | 4 +- .../ai-agent-node-template.module.css | 10 +- .../ai-agent-node-template.tsx | 13 +- .../icon-placeholder.module.css | 4 +- .../node-wrapper-info.module.css | 14 +- .../components/tool-info/tool-info.module.css | 2 +- .../connectable-item-width.decision-log.md | 81 + .../connectable-item.module.css | 33 +- .../connectable-item.spec.tsx | 25 + .../connectable-item/connectable-item.tsx | 5 +- .../node-section/node-section.module.css | 25 +- .../placeholder-button.module.css | 3 - .../placeholder-button/placeholder-button.tsx | 11 +- .../components/branches-container.module.css | 4 +- .../decision-node-template.module.css | 4 +- .../decision-node-template.tsx | 14 +- .../start-node-template.module.css | 2 +- .../start-node-template.tsx | 13 +- .../workflow-node-template.module.css | 2 +- .../workflow-node-template.tsx | 13 +- .../language-selector.module.css | 6 - .../language-selector.spec.tsx | 32 +- .../language-selector/language-selector.tsx | 17 +- packages/sdk/src/features/i18n/locales/en.ts | 3 +- packages/sdk/src/features/i18n/locales/pl.ts | 3 +- .../export-modal/export-modal.tsx | 3 +- .../import-export-modal.module.css | 20 +- .../import-modal/import-modal.tsx | 18 +- .../components/save-button/save-button.tsx | 17 +- .../saving-status/saving-status.module.css | 14 +- .../indicator-dot/indicator-dot.module.css | 22 +- .../ai-tools-control/ai-tools-control.tsx | 25 +- .../branch-card/branch-card.module.css | 54 +- .../branch-card/branch-card.tsx | 16 +- .../decision-branches-control.module.css | 4 +- .../dynamic-conditions-control.module.css | 21 +- .../dynamic-conditions-control.tsx | 17 +- .../conditions-form-field.module.css | 12 +- .../conditions-form-field.tsx | 18 +- .../conditions-form.module.css | 1 - .../conditions-form.tsx | 14 +- .../rich-text-control.module.css | 10 +- .../text-area-control/text-area-control.tsx | 2 +- .../controls/text-control/text-control.tsx | 2 +- .../layouts/group-layout/group-layout.tsx | 2 +- .../vertical-layout/vertical-layout.tsx | 2 +- .../json-form/utils/unknown-renderer.tsx | 2 +- .../delete-confirmation.module.css | 4 +- .../delete-confirmation.tsx | 2 +- .../modals/providers/modal-provider.tsx | 3 + .../components/tile.module.css | 36 +- .../template-selector/components/tile.tsx | 4 +- .../template-selector.module.css | 10 +- .../template-selector/template-selector.tsx | 2 +- .../components/dragged-item/dragged-item.tsx | 2 +- .../components/footer/palette-footer.tsx | 2 +- .../components/header/palette-header.tsx | 10 +- .../components/items/palette-item.module.css | 4 - .../palette/components/items/palette-item.tsx | 2 +- .../palette/node-preview-container.spec.tsx | 14 +- .../palette/node-preview-container.tsx | 29 +- .../features/palette/palette-container.tsx | 3 +- .../sdk/src/features/palette/variables.css | 4 - .../header/properties-bar-header.tsx | 21 +- .../properties-bar/properties-bar.tsx | 2 +- .../components/syntax-highlighter.module.css | 59 +- .../components/syntax-highlighter.tsx | 4 +- .../dynamic-typed-input.module.css | 8 +- .../dynamic-typed-input.tsx | 14 +- ...dynamic-typed-variable-or-input.module.css | 4 - .../dynamic-typed-variable-or-input.tsx | 22 +- .../variable-select.module.css | 2 +- .../variable-text/variable-text.module.css | 94 +- .../variable-text/variable-text.tsx | 7 +- .../modals/modal-settings.module.css | 10 +- .../settings/settings-navigation.module.css | 29 +- .../pane-edit-variable/pane-edit-variable.tsx | 2 +- .../pane-list/pane-list.module.css | 10 +- .../pane-list/pane-list.tsx | 12 +- .../pane-remove-variable.module.css | 19 +- .../pane-remove-variable.tsx | 12 +- .../variable-form/variable-form.module.css | 2 +- .../variable-form/variable-form.tsx | 8 +- .../variable-preview/variable-meta.module.css | 23 +- .../variable-preview/variable-meta.tsx | 7 +- .../variable-preview.module.css | 15 +- .../variable-preview/variable-preview.tsx | 22 +- .../modals/tab/tab-header.module.css | 8 +- .../variables/modals/tab/tab-header.tsx | 14 +- packages/sdk/src/index.css | 47 +- packages/sdk/src/index.ts | 5 +- .../slices/diagram-slice/actions.spec.ts | 46 +- .../src/store/slices/diagram-slice/actions.ts | 6 +- .../utils/dynamic-values.spec.ts | 43 + .../diagram-slice/utils/dynamic-values.ts | 2 +- .../src/workflow-builder-root/root-shell.tsx | 4 +- .../workflow-builder-root.types.ts | 2 +- packages/sdk/vite.config.mts | 50 +- packages/tokens/README.md | 49 +- packages/tokens/config.ts | 16 +- packages/tokens/src/css-variable-name.spec.ts | 18 + packages/tokens/src/css-variable-name.ts | 11 + packages/tokens/src/index.ts | 16 + packages/tokens/src/tokens-to-css.ts | 53 +- .../tokens/src/validate-collisions.spec.ts | 101 + packages/tokens/src/validate-collisions.ts | 133 + packages/tokens/tokens.json | 5717 +++++++++-------- packages/ui/CHANGELOG.md | 21 +- packages/ui/README.md | 10 +- packages/ui/built-css-pitfalls.md | 170 + packages/ui/combine-css-bundle.mts | 196 +- packages/ui/combine-css-bundle.spec.mts | 45 + packages/ui/css-layers.md | 6 +- packages/ui/font-assets.decision-log.md | 57 + packages/ui/font-license.spec.mts | 24 + packages/ui/package.json | 9 +- packages/ui/postcss-layer-root-defaults.mts | 2 +- packages/ui/scripts/check-built-css.ts | 368 +- .../ui/src/components/accordion/accordion.tsx | 2 +- .../src/components/avatar/avatar.module.css | 26 +- .../button/base-button/base-button.module.css | 18 +- .../ui/src/components/button/guards.spec.ts | 85 - packages/ui/src/components/button/guards.ts | 18 - .../button/nav-button/nav-button.module.css | 206 + .../button/nav-button/nav-button.spec.tsx | 44 + .../button/nav-button/nav-button.tsx | 151 +- .../nav-button-icon-padding.module.css | 14 - .../nav-icon-button.module.css | 11 - .../nav-icon-button/nav-icon-button.tsx | 40 - .../nav-button-gap.module.css | 15 - .../nav-icon-label-button-padding.module.css | 16 - .../nav-icon-label-button.tsx | 40 - .../nav-label-button-padding.module.css | 16 - .../nav-label-button/nav-label-button.tsx | 26 - .../nav-button-border-radius.module.css | 16 - .../styles/nav-button-font-size.module.css | 6 - .../styles/nav-button-icon-size.module.css | 20 - .../nav-button/styles/nav-button.module.css | 37 - .../src/components/button/nav-button/types.ts | 41 +- .../button/regular-button/button.tsx | 164 +- .../icon-button/icon-button.tsx | 27 - .../icon-label-button/icon-label-button.tsx | 27 - .../label-button/label-button.tsx | 29 - .../label-button/loader.module.css | 58 - .../button/regular-button/loader.module.css | 59 + .../components/button/regular-button/types.ts | 62 +- .../button/styles/border-radius.module.css | 47 +- .../button/styles/font-size.module.css | 16 +- .../components/button/styles/gap.module.css | 30 +- .../button/styles/height.module.css | 29 + .../icon-label-button-padding.module.css | 34 - .../button/styles/icon-padding.module.css | 30 +- .../button/styles/icon-size.module.css | 62 +- .../styles/label-button-padding.module.css | 31 - .../button/styles/padding.module.css | 29 + .../button/styles/variant.module.css | 412 +- .../components/checkbox/checkbox.module.css | 94 +- .../ui/src/components/chip/chip.module.css | 75 + packages/ui/src/components/chip/chip.tsx | 51 + packages/ui/src/components/chip/index.ts | 1 + .../collapsible/collapsible.module.css | 6 +- .../components/collapsible/collapsible.tsx | 31 +- .../date-picker/date-picker.module.css | 62 +- .../components/date-picker/date-picker.tsx | 72 +- .../ui/src/components/date-picker/types.ts | 20 + .../src/components/date-picker/variables.css | 24 +- .../edge-label/edge-label-size.module.css | 110 +- .../edge/edge-label/edge-label.module.css | 49 +- .../edge/use-edge-styles/use-edge-styles.css | 12 +- .../edge/use-edge-styles/use-edge-styles.tsx | 12 +- packages/ui/src/components/input/index.ts | 1 + .../components/input/input-root.module.css | 72 +- .../ui/src/components/input/input.module.css | 13 +- packages/ui/src/components/input/input.tsx | 111 +- packages/ui/src/components/input/types.ts | 29 +- .../ui/src/components/input/variables.css | 28 +- packages/ui/src/components/menu/index.ts | 1 + packages/ui/src/components/menu/menu-item.tsx | 43 +- .../src/components/menu/menu-open-context.ts | 4 + .../components/menu/menu-trigger-button.tsx | 26 + packages/ui/src/components/menu/menu.spec.tsx | 48 + packages/ui/src/components/menu/menu.tsx | 41 +- packages/ui/src/components/menu/types.ts | 17 +- .../ui/src/components/modal/modal.module.css | 154 +- packages/ui/src/components/modal/modal.tsx | 16 +- .../node-as-port-wrapper.tsx | 6 +- .../node-as-port-wrapper/node-as-port.css | 8 +- .../node-description.module.css | 28 +- .../node-description.spec.tsx | 43 + .../node-description/node-description.tsx | 15 +- .../node/node-icon/node-icon.module.css | 29 +- .../components/node/node-icon/node-icon.tsx | 6 +- .../node/node-panel/handle.module.css | 64 +- .../node/node-panel/node-panel.module.css | 62 +- .../node/node-panel/node-panel.spec.tsx | 44 + .../components/node/node-panel/node-panel.tsx | 9 +- .../radio-button/radio-size.module.css | 36 +- .../components/radio-button/radio.module.css | 48 +- .../border-radius-size.module.css | 28 +- .../item/segment-picker-item.tsx | 89 +- .../segment-picker/segment-picker.module.css | 14 +- .../segment-picker/segment-picker.spec.tsx | 65 + .../segment-picker/segment-picker.tsx | 53 +- .../segment-picker/utils/context.ts | 11 +- .../utils/get-valid-shape.spec.ts | 20 +- .../segment-picker/utils/get-valid-shape.ts | 18 +- .../select-button/select-button.module.css | 42 +- .../select/select-option/select-option.tsx | 4 + .../src/components/select/select.module.css | 1 + packages/ui/src/components/select/select.tsx | 123 +- .../components/separator/separator.module.css | 8 +- .../components/action-buttons.module.css | 2 +- .../snackbar/components/action-buttons.tsx | 12 +- .../snackbar/components/icon.module.css | 13 +- .../snackbar/components/message.module.css | 4 +- .../snackbar/components/message.tsx | 4 +- .../components/snackbar/snackbar.module.css | 95 +- .../src/components/status/status.module.css | 20 +- .../switch/icon-switch/icon-switch.module.css | 65 +- .../components/switch/switch-size.module.css | 88 +- .../src/components/switch/switch.module.css | 48 +- packages/ui/src/components/text-area/index.ts | 1 + .../components/text-area/text-area.module.css | 126 +- .../ui/src/components/text-area/text-area.tsx | 197 +- .../components/tooltip/tooltip-content.tsx | 2 +- .../src/components/tooltip/tooltip.module.css | 28 +- packages/ui/src/index.ts | 2 + .../shared/components/field/field.module.css | 61 + .../shared/components/field/field.spec.tsx | 76 + .../ui/src/shared/components/field/field.tsx | 53 + .../styles/field-control-height.module.css | 24 + .../styles/field-control-size.module.css | 42 + .../src/shared/styles/field-control-size.ts | 7 + .../shared/styles/input-font-size.module.css | 14 +- .../src/shared/styles/input-size.module.css | 33 - .../ui/src/shared/styles/list-box.module.css | 33 +- .../shared/styles/list-item-size.module.css | 33 +- .../ui/src/shared/styles/list-item.module.css | 101 +- packages/ui/src/shared/types/field.ts | 28 + packages/ui/src/styles/globals.css | 2 +- packages/ui/src/styles/typography.css | 294 +- packages/ui/tsconfig.json | 2 +- packages/ui/types.d.ts | 8 + packages/ui/typography-composes.spec.mts | 44 + packages/ui/vite.config.mts | 5 +- packages/ui/vitest.config.mts | 1 + packages/ui/vitest.setup.ts | 1 + pnpm-lock.yaml | 674 +- tools/stylelint/custom-properties.mjs | 36 + tools/stylelint/no-system-token-fallbacks.mjs | 68 + 390 files changed, 10691 insertions(+), 6640 deletions(-) create mode 100644 .changeset/accessible-dismissible-labels.md create mode 100644 .changeset/app-bar-folder-name-nowrap.md create mode 100644 .changeset/avatar-size-primitives.md create mode 100644 .changeset/built-css-invariants.md create mode 100644 .changeset/button-redesign.md create mode 100644 .changeset/canvas-background-role.md create mode 100644 .changeset/canvas-node-geometry.md create mode 100644 .changeset/canvas-port-size.md create mode 100644 .changeset/chips-component.md create mode 100644 .changeset/conditions-tag-chip.md create mode 100644 .changeset/connectable-item-tooltip.md create mode 100644 .changeset/css-variable-namespaces-sdk.md create mode 100644 .changeset/dangling-token-names.md create mode 100644 .changeset/design-library-parity.md create mode 100644 .changeset/explicit-type-roles-ui.md create mode 100644 .changeset/explicit-type-roles.md create mode 100644 .changeset/field-aria-required.md create mode 100644 .changeset/font-assets.md create mode 100644 .changeset/form-fields-redesign.md create mode 100644 .changeset/import-modal-secondary.md create mode 100644 .changeset/language-selector-selected.md create mode 100644 .changeset/menu-item-critical-tone.md create mode 100644 .changeset/menu-item-selected.md create mode 100644 .changeset/nav-button-redesign.md create mode 100644 .changeset/node-body-spacing.md create mode 100644 .changeset/node-panel-disabled.md create mode 100644 .changeset/node-rows-fill.md create mode 100644 .changeset/node-text-ellipsis.md create mode 100644 .changeset/palette-node-states.md create mode 100644 .changeset/palette-templates-ghost.md create mode 100644 .changeset/persist-without-measured.md create mode 100644 .changeset/placeholder-subtle-role.md create mode 100644 .changeset/port-connecting-target.md create mode 100644 .changeset/public-variable-namespace.md create mode 100644 .changeset/remove-legacy-type-classes.md create mode 100644 .changeset/self-loop-offset.md create mode 100644 .changeset/snackbar-row-alignment.md create mode 100644 .changeset/textarea-disabled-background.md create mode 100644 .changeset/token-set-2.md create mode 100644 .changeset/type-role-classes.md create mode 100644 .changeset/vertical-layout-class.md create mode 100644 .stylelintrc.mjs create mode 100644 apps/ai-studio/types.d.ts create mode 100644 apps/ai-studio/vitest.setup.ts create mode 100644 apps/docs/src/components/ui-examples/chip.tsx create mode 100644 apps/docs/src/content/docs/get-started/upgrade-to-3.md create mode 100644 apps/docs/src/content/docs/ui-library/typography.mdx create mode 100644 apps/docs/src/content/docs/ui-library/ui-components/chip.mdx create mode 100644 packages/sdk/font-assets.mts create mode 100644 packages/sdk/font-license.spec.mts delete mode 100644 packages/sdk/src/features/diagram/edges/label-edge/label-edge.module.css create mode 100644 packages/sdk/src/features/diagram/edges/label-edge/label-edge.spec.tsx delete mode 100644 packages/sdk/src/features/diagram/edges/label-edge/variables.css create mode 100644 packages/sdk/src/features/diagram/edges/self-connecting-edge/self-connecting-edge.spec.tsx create mode 100644 packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md create mode 100644 packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.spec.tsx delete mode 100644 packages/sdk/src/features/diagram/nodes/components/placeholder-button/placeholder-button.module.css delete mode 100644 packages/sdk/src/features/i18n/components/language-selector/language-selector.module.css delete mode 100644 packages/sdk/src/features/palette/variables.css delete mode 100644 packages/sdk/src/features/variables/components/dynamic-typed-variable-or-input/dynamic-typed-variable-or-input.module.css create mode 100644 packages/sdk/src/store/slices/diagram-slice/utils/dynamic-values.spec.ts create mode 100644 packages/tokens/src/css-variable-name.spec.ts create mode 100644 packages/tokens/src/css-variable-name.ts create mode 100644 packages/tokens/src/validate-collisions.spec.ts create mode 100644 packages/tokens/src/validate-collisions.ts create mode 100644 packages/ui/built-css-pitfalls.md create mode 100644 packages/ui/combine-css-bundle.spec.mts create mode 100644 packages/ui/font-assets.decision-log.md create mode 100644 packages/ui/font-license.spec.mts delete mode 100644 packages/ui/src/components/button/guards.spec.ts delete mode 100644 packages/ui/src/components/button/guards.ts create mode 100644 packages/ui/src/components/button/nav-button/nav-button.module.css create mode 100644 packages/ui/src/components/button/nav-button/nav-button.spec.tsx delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-button/nav-button-icon-padding.module.css delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-button/nav-icon-button.module.css delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-button/nav-icon-button.tsx delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-label-button/nav-button-gap.module.css delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-label-button/nav-icon-label-button-padding.module.css delete mode 100644 packages/ui/src/components/button/nav-button/nav-icon-label-button/nav-icon-label-button.tsx delete mode 100644 packages/ui/src/components/button/nav-button/nav-label-button/nav-label-button-padding.module.css delete mode 100644 packages/ui/src/components/button/nav-button/nav-label-button/nav-label-button.tsx delete mode 100644 packages/ui/src/components/button/nav-button/styles/nav-button-border-radius.module.css delete mode 100644 packages/ui/src/components/button/nav-button/styles/nav-button-font-size.module.css delete mode 100644 packages/ui/src/components/button/nav-button/styles/nav-button-icon-size.module.css delete mode 100644 packages/ui/src/components/button/nav-button/styles/nav-button.module.css delete mode 100644 packages/ui/src/components/button/regular-button/icon-button/icon-button.tsx delete mode 100644 packages/ui/src/components/button/regular-button/icon-label-button/icon-label-button.tsx delete mode 100644 packages/ui/src/components/button/regular-button/label-button/label-button.tsx delete mode 100644 packages/ui/src/components/button/regular-button/label-button/loader.module.css create mode 100644 packages/ui/src/components/button/regular-button/loader.module.css create mode 100644 packages/ui/src/components/button/styles/height.module.css delete mode 100644 packages/ui/src/components/button/styles/icon-label-button-padding.module.css delete mode 100644 packages/ui/src/components/button/styles/label-button-padding.module.css create mode 100644 packages/ui/src/components/button/styles/padding.module.css create mode 100644 packages/ui/src/components/chip/chip.module.css create mode 100644 packages/ui/src/components/chip/chip.tsx create mode 100644 packages/ui/src/components/chip/index.ts create mode 100644 packages/ui/src/components/menu/menu-open-context.ts create mode 100644 packages/ui/src/components/menu/menu-trigger-button.tsx create mode 100644 packages/ui/src/components/menu/menu.spec.tsx create mode 100644 packages/ui/src/components/node/node-description/node-description.spec.tsx create mode 100644 packages/ui/src/components/node/node-panel/node-panel.spec.tsx create mode 100644 packages/ui/src/components/segment-picker/segment-picker.spec.tsx create mode 100644 packages/ui/src/shared/components/field/field.module.css create mode 100644 packages/ui/src/shared/components/field/field.spec.tsx create mode 100644 packages/ui/src/shared/components/field/field.tsx create mode 100644 packages/ui/src/shared/styles/field-control-height.module.css create mode 100644 packages/ui/src/shared/styles/field-control-size.module.css create mode 100644 packages/ui/src/shared/styles/field-control-size.ts delete mode 100644 packages/ui/src/shared/styles/input-size.module.css create mode 100644 packages/ui/src/shared/types/field.ts create mode 100644 packages/ui/typography-composes.spec.mts create mode 100644 packages/ui/vitest.setup.ts create mode 100644 tools/stylelint/custom-properties.mjs create mode 100644 tools/stylelint/no-system-token-fallbacks.mjs diff --git a/.changeset/accessible-dismissible-labels.md b/.changeset/accessible-dismissible-labels.md new file mode 100644 index 000000000..295da7446 --- /dev/null +++ b/.changeset/accessible-dismissible-labels.md @@ -0,0 +1,6 @@ +--- +'@workflowbuilder/ui': minor +'@workflowbuilder/sdk': patch +--- + +`Modal` now accepts `closeLabel`, while `Collapsible` accepts `expandLabel` and `collapseLabel`, all with English defaults. The SDK supplies its localized labels to these controls. diff --git a/.changeset/app-bar-folder-name-nowrap.md b/.changeset/app-bar-folder-name-nowrap.md new file mode 100644 index 000000000..d44844d18 --- /dev/null +++ b/.changeset/app-bar-folder-name-nowrap.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +The app bar's folder label no longer wraps onto a second line while the diagram title is being edited. The title already carried `white-space: nowrap`; the label next to it did not, so the wider edit field pushed it over two lines. diff --git a/.changeset/avatar-size-primitives.md b/.changeset/avatar-size-primitives.md new file mode 100644 index 000000000..af9ffde87 --- /dev/null +++ b/.changeset/avatar-size-primitives.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +`Avatar` sizes `medium` and `small` bind the `--wb-ds-size-300` and `--wb-ds-size-225` primitives instead of literal values. The design library defines all four avatar sizes; the two smaller ones were written as literals because their primitives were read as missing. diff --git a/.changeset/built-css-invariants.md b/.changeset/built-css-invariants.md new file mode 100644 index 000000000..5931d6964 --- /dev/null +++ b/.changeset/built-css-invariants.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +Harden published CSS validation with deterministic layer, namespace, public-default, import, and asset checks, and validate the package surface with publint. diff --git a/.changeset/button-redesign.md b/.changeset/button-redesign.md new file mode 100644 index 000000000..cd4c9ef6c --- /dev/null +++ b/.changeset/button-redesign.md @@ -0,0 +1,16 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': minor +--- + +`Button` is rebuilt on the Design System 2.0 specification. It takes `variant`, `size` and `shape`, and composes its content from `prefixIcon`, `children` and `suffixIcon` instead of inferring a subtype from the children structure. The variant list is `primary`, `secondary`, `critical`, `success` and a ghost treatment of each; sizes are letter-based; `shape` is `default`, `square` or `round`. + +Breaking changes: + +- Migrate variants: `gray` and the old outlined `secondary` both become the solid grey `secondary` (use `ghost-secondary` where the outlined treatment should stay), `error` becomes `critical`, `ghost-destructive` becomes `ghost-critical`, and `warning` becomes `critical` for destructive actions or `secondary` for cautionary ones. +- Migrate sizes: `extra-large`, `large`, `medium`, `small`, `extra-small` become `xl`, `l`, `m`, `s`, `xs`. The `xx-small` and `xxx-small` steps are gone. +- Replace `shape="circle"` with `shape="round"` on `Button`, which also gains `shape="square"`. `SegmentPicker` keeps the `Shape` type it always had (`default` or `circle`), so its own `shape` prop needs no change. +- `Variant` is renamed to `ButtonVariant`. `BaseRegularButtonProps` and the old label/icon/icon-and-label component subtypes are removed; `LabelButtonProps` and `IconButtonProps` are redefined for the new API. +- Retarget public button overrides: the `gray`, `error`, `warning` and `ghost-destructive` variable families no longer exist, and the size suffixes in variable names follow the new scale. The `secondary` family now describes the solid grey variant, so overrides written for the old outlined `secondary` belong on `ghost-secondary`. + +A warning `Snackbar` renders its action button as `secondary`. diff --git a/.changeset/canvas-background-role.md b/.changeset/canvas-background-role.md new file mode 100644 index 000000000..c611a68ce --- /dev/null +++ b/.changeset/canvas-background-role.md @@ -0,0 +1,6 @@ +--- +'@workflowbuilder/sdk': patch +'@workflowbuilder/ui': patch +--- + +Token export of 21.09 (library 1.1.14). `ui/bg/canvas` and `ui/bg/app` are gray-300 in the light theme, so the canvas no longer shares the white of the panels, and the grid dots step down to gray-450. In the dark theme `ui/bg/app` moves from gray-900 to gray-800; no surface is `#070708` any more. The export adds `ui/bg/inset-subtle` (gray-200 / gray-650), which nothing binds yet. Nothing is renamed or removed. diff --git a/.changeset/canvas-node-geometry.md b/.changeset/canvas-node-geometry.md new file mode 100644 index 000000000..96bd983b0 --- /dev/null +++ b/.changeset/canvas-node-geometry.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +Canvas nodes follow the Design System 2.0 geometry: the default node shell width is the designed 241px (`--wb-public-node-width: 241px`, a canvas dimension that no longer scales with the root font size), and the node shell padding and vertical gap bind to the node head roles `--wb-ds-canvas-node-head-h-pad` and `--wb-ds-canvas-node-head-gap` (8px each) instead of the generic spacing step. The node subtitle renders at the Label/S role the design master binds. Overrides of the public node variables keep working unchanged. diff --git a/.changeset/canvas-port-size.md b/.changeset/canvas-port-size.md new file mode 100644 index 000000000..a18364b96 --- /dev/null +++ b/.changeset/canvas-port-size.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +Default canvas ports now render at the designed 8px outer size. diff --git a/.changeset/chips-component.md b/.changeset/chips-component.md new file mode 100644 index 000000000..c3f808c1d --- /dev/null +++ b/.changeset/chips-component.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': minor +--- + +Add the `Chip` component — a compact tag in the DS 2.0 solid and outline treatments, four sizes (`s`–`xl`), optional prefix icon and close affordance. diff --git a/.changeset/conditions-tag-chip.md b/.changeset/conditions-tag-chip.md new file mode 100644 index 000000000..6b9f17cde --- /dev/null +++ b/.changeset/conditions-tag-chip.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +The dynamic-conditions counter tag is rendered with the UI `Chip` component. Same neutral surface colors; the tag gets the DS 2.0 chip metrics — 10px label (was 12px) in a 20px-tall pill with slightly tighter padding. diff --git a/.changeset/connectable-item-tooltip.md b/.changeset/connectable-item-tooltip.md new file mode 100644 index 000000000..03aa0e433 --- /dev/null +++ b/.changeset/connectable-item-tooltip.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Decision branch rows and AI tool rows expose the full label as a native tooltip (`title`), so a clipped row stays readable. diff --git a/.changeset/css-variable-namespaces-sdk.md b/.changeset/css-variable-namespaces-sdk.md new file mode 100644 index 000000000..3e2be8432 --- /dev/null +++ b/.changeset/css-variable-namespaces-sdk.md @@ -0,0 +1,10 @@ +--- +'@workflowbuilder/sdk': major +--- + +SDK custom properties are split into a public override surface and a private one. The public controls take the `--wb-public-` prefix; everything else moves to `--wb-sdk-` and is no longer part of the supported surface. + +Breaking changes: + +- Rename `--wb-background-color` to `--wb-public-background-color`, `--wb-transition` to `--wb-public-transition`, and every `--wb-scroll-*` property to `--wb-public-scroll-*`. +- Drop overrides of any other `--wb-` property. They now live under `--wb-sdk-`, which is private and may change without a major release. diff --git a/.changeset/dangling-token-names.md b/.changeset/dangling-token-names.md new file mode 100644 index 000000000..2f5551eee --- /dev/null +++ b/.changeset/dangling-token-names.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Fix CSS variable names that pointed at non-existent tokens: the variables-settings modal picks up its token-defined colors, spacing, and radii, and diagram edge labels use their intended hover/selection colors. diff --git a/.changeset/design-library-parity.md b/.changeset/design-library-parity.md new file mode 100644 index 000000000..235ba88a6 --- /dev/null +++ b/.changeset/design-library-parity.md @@ -0,0 +1,9 @@ +--- +'@workflowbuilder/ui': major +--- + +Several components pick up metrics that drifted from the design library: `NavButton` label padding, menu item inline padding, `Snackbar` width and action buttons, `SegmentPicker` padding and `Avatar` sizes. Edge labels subtract their border width from their padding, so the outer size matches the design. The heavier stroke a selected label carries is drawn inward, the way the master's inside stroke works, so selection no longer resizes it either. + +Breaking changes: + +- `SegmentPicker` no longer pads its container, and `--wb-public-segment-picker-padding` is gone with it. Every variant of the design master sits at zero padding; the 4px that used to be there came from a token of the old system with no counterpart in this one. The track still shows through the 4px gap between segments, which `--wb-public-segment-picker-gap` controls. Drop any override of the removed property. diff --git a/.changeset/explicit-type-roles-ui.md b/.changeset/explicit-type-roles-ui.md new file mode 100644 index 000000000..1800b345e --- /dev/null +++ b/.changeset/explicit-type-roles-ui.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': minor +--- + +Make typography roles carry their font families and add the public `--wb-public-font-family` and `--wb-public-font-family-mono` overrides. diff --git a/.changeset/explicit-type-roles.md b/.changeset/explicit-type-roles.md new file mode 100644 index 000000000..57892916c --- /dev/null +++ b/.changeset/explicit-type-roles.md @@ -0,0 +1,10 @@ +--- +'@workflowbuilder/sdk': major +--- + +The SDK no longer resets the font of the whole document. Typography is bound to the built-in type roles and to the public `--wb-public-font-family` lever, so setting that one variable rethemes both the roles and the builder root. Small text on SDK surfaces takes an explicit 10px, 11px or 12px role per surface, including the 12px code editor text that now matches the code role. + +Breaking changes: + +- Host pages that relied on the SDK stylesheet setting their font must set it themselves; the SDK only styles its own subtree now. +- Set `--wb-public-font-family` instead of overriding font declarations on SDK elements. The previous per-element overrides no longer reach every surface. diff --git a/.changeset/field-aria-required.md b/.changeset/field-aria-required.md new file mode 100644 index 000000000..3e55b0289 --- /dev/null +++ b/.changeset/field-aria-required.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +`Select` and `DatePicker` set `aria-required` on their trigger when the field is required, so the requirement reaches a screen reader and not only the asterisk beside the label. `Input` and `TextArea` already carried the native attribute. diff --git a/.changeset/font-assets.md b/.changeset/font-assets.md new file mode 100644 index 000000000..d2e308341 --- /dev/null +++ b/.changeset/font-assets.md @@ -0,0 +1,16 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': major +--- + +Fonts now ship as `.woff2` assets next to the stylesheets, with only the two dominant faces inlined. + +Breaking changes: + +- Preserve the published `dist/assets` directory next to copied stylesheets so their relative font URLs keep resolving. +- If a Content Security Policy exists, allow `data:` and `'self'` or the serving origin in `font-src`, or in `default-src` when `font-src` is absent. + +Both packages ship the SIL Open Font License texts (`OFL-poppins.txt`, `OFL-inter.txt`) in `dist/assets` next to the +font files, so copying that directory keeps the redistribution licensed. + +Only Poppins latin 400 and 600 are inline. Other weights, Inter, and non-ASCII glyphs use `font-display: swap` assets and may briefly render in the fallback font; preload the relevant files when that flash of unstyled text (FOUT) is unacceptable. diff --git a/.changeset/form-fields-redesign.md b/.changeset/form-fields-redesign.md new file mode 100644 index 000000000..118539747 --- /dev/null +++ b/.changeset/form-fields-redesign.md @@ -0,0 +1,15 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': minor +--- + +Form controls are rebuilt on one field composition. `Input`, `TextArea`, `Select` and `DatePicker` render inside a shared `Field` that provides an associated label, helper text, a required marker and the visual state, so a label no longer has to be wired up by hand. `Input` and `TextArea` take letter sizes, icon slots and an optional clear action; `Select` and `DatePicker` gain `label`, `helperText`, `state`, `isRequired` and `id`, and paint a disabled background. + +Breaking changes: + +- Replace the boolean `error` prop on `Input` and `TextArea` with `state="critical"`. `state` also carries `success` and `read-only`. +- Replace `startAdornment` and `endAdornment` with `prefixIcon` and `suffixIcon`. +- Migrate `Input` and `TextArea` sizes from `large`, `medium`, `small` to `l`, `m`, `s`; `xs` is new. `Select` and `DatePicker` keep the word-based `size` prop and map it internally. +- Retarget the public control variables: the `-error` families become `-critical`, and the size suffixes in `--wb-public-input-padding-*`, `--wb-public-input-gap-*` and `--wb-public-input-border-radius-*` follow the letter scale (`-medium` becomes `-m`, and so on). + +New public properties cover the states the old surface had no lever for: success and hover borders, disabled backgrounds, icon and placeholder colours, and the label, helper and required-marker colours of the field composition. diff --git a/.changeset/import-modal-secondary.md b/.changeset/import-modal-secondary.md new file mode 100644 index 000000000..a2c9e0474 --- /dev/null +++ b/.changeset/import-modal-secondary.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +The import dialog's "Ignore and import" action uses the `secondary` button variant instead of the removed `warning` variant. diff --git a/.changeset/language-selector-selected.md b/.changeset/language-selector-selected.md new file mode 100644 index 000000000..0a9633ac4 --- /dev/null +++ b/.changeset/language-selector-selected.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +The language menu marks the current language as selected. diff --git a/.changeset/menu-item-critical-tone.md b/.changeset/menu-item-critical-tone.md new file mode 100644 index 000000000..081d15c37 --- /dev/null +++ b/.changeset/menu-item-critical-tone.md @@ -0,0 +1,10 @@ +--- +'@workflowbuilder/ui': major +--- + +A destructive menu entry is expressed as a tone, not a variant: `MenuItem` takes `tone="critical"`, which colours the label and the icon and leaves the row the background every other row has. A disabled entry has no background either. + +Breaking changes: + +- Replace `destructive: true` with `tone: "critical"` on menu items. +- Drop overrides of `--wb-public-list-item-background-color-destructive`, `--wb-public-list-item-background-color-hover-destructive` and `--wb-public-list-item-color-destructive`. The tone exposes `--wb-public-list-item-color-critical`, `--wb-public-list-item-color-critical-disabled` and `--wb-public-list-item-icon-color-critical`; the row's red tints are gone. diff --git a/.changeset/menu-item-selected.md b/.changeset/menu-item-selected.md new file mode 100644 index 000000000..f8c14ee24 --- /dev/null +++ b/.changeset/menu-item-selected.md @@ -0,0 +1,7 @@ +--- +'@workflowbuilder/ui': minor +--- + +A selected `Menu` item or `Select` option renders a check mark at the trailing edge of the row, so the choice is not carried by background colour alone. Icons inside a list row take their own role instead of the label colour, through the new `--wb-public-list-item-icon-color`, `--wb-public-list-item-icon-color-disabled` and `--wb-public-list-item-indicator-color` properties. + +`Menu` items accept `selected`; a menu with a selection renders its entries as a radio group (`menuitemradio`, `aria-checked`) and highlights the current one. Selected list entries (menu items and `Select` options) use the design roles `ui/bg/selected` and `ui/bg/selected-hover` with default text instead of a solid accent fill; the new `--wb-public-list-item-background-color-selected-hover` property covers the hovered selected state. A hovered list entry takes the `ui/bg/inset` role, the same surface the checkbox, radio and switch already used. A disabled entry's label takes `ui/text/disabled` instead of the input's disabled colour, exposed as `--wb-public-list-item-color-disabled`, so an override that used to reach disabled menu entries through `--wb-public-input-color-disabled` moves there. diff --git a/.changeset/move-ui-library-in-repo.md b/.changeset/move-ui-library-in-repo.md index b8dd6b002..e7ed02216 100644 --- a/.changeset/move-ui-library-in-repo.md +++ b/.changeset/move-ui-library-in-repo.md @@ -4,4 +4,4 @@ Consume the UI component library from the in-repo `@workflowbuilder/ui` (Base UI) instead of the published `@synergycodes/overflow-ui`. -The SDK previously bundled `@synergycodes/overflow-ui@1.0.0-beta.27` (built on MUI / Mantine / Emotion / Floating UI). It now bundles the in-repo `@workflowbuilder/ui@2.0.0`, rebuilt on [Base UI](https://base-ui.com/). `@base-ui/react` is now a regular dependency of the SDK (installed automatically, not bundled) rather than an inlined implementation detail. Bundled component visuals and interaction details change accordingly; the SDK's exported symbols are unchanged, but public types deriving from the UI library (`InputControlProps`, `TextAreaControlProps`) now build on `@workflowbuilder/ui` type shapes (picked keys unchanged), and the internal DOM structure and class names of all bundled UI changed (MUI Base + Mantine → Base UI) — styles or tests written against those internal class names may need updating. Modal open/close now runs its enter and exit fade transitions (previously the dialog appeared and disappeared instantly). +The SDK previously bundled `@synergycodes/overflow-ui@1.0.0-beta.27` (built on MUI / Mantine / Emotion / Floating UI). It now bundles the in-repo `@workflowbuilder/ui`, rebuilt on [Base UI](https://base-ui.com/). `@base-ui/react` is now a regular dependency of the SDK (installed automatically, not bundled) rather than an inlined implementation detail. Bundled component visuals and interaction details change accordingly; the SDK's exported symbols are unchanged, but public types deriving from the UI library (`InputControlProps`, `TextAreaControlProps`) now build on `@workflowbuilder/ui` type shapes (picked keys unchanged), and the internal DOM structure and class names of all bundled UI changed (MUI Base + Mantine → Base UI) — styles or tests written against those internal class names may need updating. Modal open/close now runs its enter and exit fade transitions (previously the dialog appeared and disappeared instantly). diff --git a/.changeset/nav-button-redesign.md b/.changeset/nav-button-redesign.md new file mode 100644 index 000000000..92cf2abe3 --- /dev/null +++ b/.changeset/nav-button-redesign.md @@ -0,0 +1,12 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': minor +--- + +`NavButton` is rebuilt on the Design System 2.0 specification: it takes `size`, `variant` (`square`, `round`, `plain`), `prefixIcon`, `suffixIcon` and `children` instead of inferring a subtype from the children structure, and the selected state no longer shares a treatment with the pointer-down state. `Menu.TriggerButton` is new: an icon-only trigger that shows the pressed state while its `Menu` is open. + +Breaking changes: + +- Pass icons through `prefixIcon` or `suffixIcon`. An icon passed as `children` is rendered as label content, not as an icon. +- Migrate sizes to the letter scale. +- `SegmentPicker` keeps its API but adopts the new slots, so an icon passed as `SegmentPicker.Item` children must move to an explicit icon slot. diff --git a/.changeset/node-body-spacing.md b/.changeset/node-body-spacing.md new file mode 100644 index 000000000..af697a43c --- /dev/null +++ b/.changeset/node-body-spacing.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Node sections and the rows inside them (Decision branches, AI tool rows) follow the design library: 8px padding and gap everywhere, 4px row radius and 8px section radius, where before the rows used 12/10px padding and a 6px radius. Padding, gap and radius bind to the `canvas/node/body-*` and `canvas/node/row-*` roles instead of raw spacing primitives. A row carries the content background (`canvas/node/bg-content-default`) and its label renders at Body/S Emphasized; the border a row used to draw is gone, and only the section and the AI tools wrapper outline, with `canvas/node/stroke-default`. diff --git a/.changeset/node-panel-disabled.md b/.changeset/node-panel-disabled.md new file mode 100644 index 000000000..e6b74943a --- /dev/null +++ b/.changeset/node-panel-disabled.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': minor +--- + +`NodePanel.Root`, `NodeDescription` and `NodeIcon` accept `disabled` and render the Node Disabled variant: muted surface, title, subtitle, icon and no hover reaction. New public properties: `--wb-public-node-background-color-disabled`, `--wb-public-node-title-color-disabled`, `--wb-public-node-subtitle-color-disabled`, `--wb-public-node-icon-color-disabled`, `--wb-public-node-icon-container-background-color-disabled`. diff --git a/.changeset/node-rows-fill.md b/.changeset/node-rows-fill.md new file mode 100644 index 000000000..2f2f14a82 --- /dev/null +++ b/.changeset/node-rows-fill.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Decision branch rows and AI tool rows fill the width their section offers, as the design library's `Node / Row` does, instead of capping it with a width derived from the node shell. At the default node width a row measures 205px. Long labels truncate at the section edge instead of overflowing it, and branch rows are spaced with the node body gap (8px) like tool rows. The derived cap survives only in the vertical (DOWN) layout, where a branch row has no design master and an uncapped label would widen the node. diff --git a/.changeset/node-text-ellipsis.md b/.changeset/node-text-ellipsis.md new file mode 100644 index 000000000..55ddf0373 --- /dev/null +++ b/.changeset/node-text-ellipsis.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +`NodeDescription` keeps both the title and the subtitle on one line: long text is truncated with an ellipsis and exposed in full through the element's native tooltip, and the text no longer widens nodes whose body sizes to its content (for example the Decision node). Nodes keep the fixed design width and grow in height only. The block relies on `contain: inline-size`, so it needs an ancestor with a definite inline size; inside an `inline-flex` or `fit-content` wrapper it renders at zero width. diff --git a/.changeset/palette-node-states.md b/.changeset/palette-node-states.md new file mode 100644 index 000000000..e170cb4ff --- /dev/null +++ b/.changeset/palette-node-states.md @@ -0,0 +1,7 @@ +--- +'@workflowbuilder/sdk': minor +--- + +Palette entries render the canvas Node states instead of their own styling: hover is the Node Hover state, the drag preview is the Node Active state (outline and ring), and entries that cannot be added (read-only mode) render the Node Disabled state instead of a faded copy. `WorkflowNodeTemplateProps` gains `disabled?: boolean`; the built-in templates forward it. + +Migration for custom node templates: forward `disabled` to `NodePanel.Root`, `NodeIcon` and `NodeDescription`, otherwise the palette entry looks draggable while the palette is locked (the wrapper no longer fades it). diff --git a/.changeset/palette-templates-ghost.md b/.changeset/palette-templates-ghost.md new file mode 100644 index 000000000..319eb1e33 --- /dev/null +++ b/.changeset/palette-templates-ghost.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +The palette's "Templates" action renders as `ghost-secondary`, the variant the design library binds for it. It kept `secondary` through the button redesign, and that name changed meaning: it used to be the outlined treatment and is now the solid grey one, so the button had turned into a filled slab. diff --git a/.changeset/persist-without-measured.md b/.changeset/persist-without-measured.md new file mode 100644 index 000000000..bf099f0b9 --- /dev/null +++ b/.changeset/persist-without-measured.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Saved and exported diagrams (`getStoreDataForIntegration`, the localStorage/API/props integrations) no longer include the runtime `measured` node sizes or the `dragging` flag; nodes are measured again on load, so stored data cannot carry stale dimensions. diff --git a/.changeset/placeholder-subtle-role.md b/.changeset/placeholder-subtle-role.md new file mode 100644 index 000000000..f5b0f3cbc --- /dev/null +++ b/.changeset/placeholder-subtle-role.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +`Input` and `TextArea` draw their placeholder in `ui/text/subtle-default`, the role the design library binds for it. It was on `ui/text/ghost-default`, the weakest step of the text hierarchy, which the library reserves for disabled-state fine print; the placeholder sat at 1.8:1 instead of 8:1. diff --git a/.changeset/port-connecting-target.md b/.changeset/port-connecting-target.md new file mode 100644 index 000000000..8ace56ca0 --- /dev/null +++ b/.changeset/port-connecting-target.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +A valid target port grows to its hover size while a connection is dragged near it, matching the dragged source port; a target rejected by `isValidConnection` keeps its default size. diff --git a/.changeset/public-variable-namespace.md b/.changeset/public-variable-namespace.md new file mode 100644 index 000000000..f4a45b503 --- /dev/null +++ b/.changeset/public-variable-namespace.md @@ -0,0 +1,11 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': major +--- + +The public component override surface moves from the `--ax-public-*` prefix to `--wb-public-*`, with no compatibility aliases. + +Breaking changes: + +- Replace the `--ax-public-` prefix with `--wb-public-` in every consumer override. +- The prefix alone is not always enough: the button, input, text area and select families also rename variant names and size suffixes in the same release, and some properties are removed outright. Check each overridden name against the 3.0 upgrade guide rather than renaming the prefix mechanically. diff --git a/.changeset/remove-legacy-type-classes.md b/.changeset/remove-legacy-type-classes.md new file mode 100644 index 000000000..edd97f098 --- /dev/null +++ b/.changeset/remove-legacy-type-classes.md @@ -0,0 +1,12 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': major +--- + +The legacy typography utility classes are removed in favour of the Design System 2.0 type roles. + +Breaking changes: + +- `ax-public-h1` through `ax-public-h12` and `ax-public-p1` through `ax-public-p12` have no one-to-one replacement. Migrate each to the `wb-text-{family}-{size}[-emphasized]` role that matches its semantic use. +- Migrate `ax-public-button-large`, `ax-public-button-medium`, `ax-public-button-small` and `ax-public-button-extra-small` to `wb-text-label-xl-emphasized`, `wb-text-label-l-emphasized`, `wb-text-label-m-emphasized` and `wb-text-label-s-emphasized`. +- Migrate `ax-public-edge-label-medium`, `ax-public-edge-label-small` and `ax-public-edge-label-extra-small` to `wb-text-label-m`, `wb-text-label-m` and `wb-text-label-s`. diff --git a/.changeset/self-loop-offset.md b/.changeset/self-loop-offset.md new file mode 100644 index 000000000..6578aee69 --- /dev/null +++ b/.changeset/self-loop-offset.md @@ -0,0 +1,10 @@ +--- +'@workflowbuilder/sdk': major +--- + +The apex of a self-connecting edge sits 48px above the source node's top edge, for any node height. `SELF_CONNECTING_EDGE_LABEL_OFFSET` is now `48` and is measured from that edge; `useSelfLoopApexY` is exported for custom edges that draw their own loop, and `SelfConnectingEdge` no longer takes `nodeHeight` (the node height alone cannot place the apex, since ports sit at a fixed offset inside the header rather than at mid-height). + +Breaking changes: + +- Drop the `nodeHeight` prop from any custom use of `SelfConnectingEdge`; the component reads the node position from the React Flow store. +- Previously the loop was drawn a flat 100px above the source port, which on a 64px node put the apex 68px above its top edge; loops on every node now peak 48px above it. diff --git a/.changeset/snackbar-row-alignment.md b/.changeset/snackbar-row-alignment.md new file mode 100644 index 000000000..6cae73e31 --- /dev/null +++ b/.changeset/snackbar-row-alignment.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +Snackbar centres its row, so the message sits level with the icon and the close button. The row had no vertical alignment, which left the message at the top while the taller close button set the row height; the text rendered 6px above everything else. A snackbar with a subtitle keeps its icon aligned to the first line, as the design library draws it. diff --git a/.changeset/textarea-disabled-background.md b/.changeset/textarea-disabled-background.md new file mode 100644 index 000000000..2009c6f50 --- /dev/null +++ b/.changeset/textarea-disabled-background.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': minor +--- + +TextArea exposes `--wb-public-textarea-root-background-color-disabled`; a never-applied icon-switch thumb background declaration was removed. diff --git a/.changeset/token-set-2.md b/.changeset/token-set-2.md new file mode 100644 index 000000000..71d76aa7e --- /dev/null +++ b/.changeset/token-set-2.md @@ -0,0 +1,14 @@ +--- +'@workflowbuilder/ui': major +'@workflowbuilder/sdk': major +--- + +The design-token surface published as `--ax-*` custom properties is replaced by `--wb-ds-*` properties generated from the Design System 2.0 Figma variables. This is not a prefix swap: primitives are renamed and re-scaled, semantic colours are defined per theme, and the export adds canvas, shadow (effects) and component role sets that had no counterpart before. The 3.0 upgrade guide in the documentation carries the full old-to-new mapping. + +Breaking changes: + +- Move every override of an `--ax-*` design token to the `--wb-ds-*` property listed for it in the upgrade guide. There are no compatibility aliases, and a token with no listed counterpart has none. +- Expect a repainted palette wherever tokens are used unchanged: the brand accent is `#3969ff`, and the red, green and orange scales carry new values. Warning surfaces shift the most, because the orange scale was rebuilt around `--wb-ds-colors-orange-500` (`#fdac1b`). +- Public component variables move from `--ax-public-*` to `--wb-public-*`, and several families are renamed at the same time; see the separate public-variable and component changes. + +New in the export and used by the components: canvas node body and row metrics (`--wb-ds-canvas-node-body-*`, `--wb-ds-canvas-node-row-*`), node focus ring colours and shadow (`--wb-ds-canvas-node-focus-ring-*`, `--wb-ds-shadow-canvas-focus-ring-node-*`), selected list backgrounds (`--wb-ds-ui-bg-selected`, `--wb-ds-ui-bg-selected-hover`), ghost button label roles that hold WCAG AA on every tint (`--wb-ds-components-button-ghost-*-text-default`), and the size steps that button heights and icon sizes now resolve from. diff --git a/.changeset/type-role-classes.md b/.changeset/type-role-classes.md new file mode 100644 index 000000000..7e827bfd3 --- /dev/null +++ b/.changeset/type-role-classes.md @@ -0,0 +1,6 @@ +--- +'@workflowbuilder/ui': minor +'@workflowbuilder/sdk': minor +--- + +Add the Design System 2.0 type roles as `wb-text-{family}-{size}[-emphasized]` utility classes (40 styles: Display, Headline, Title, Body, Label, Node, UI/Code) in `@workflowbuilder/ui`, Poppins and Inter (for the `wb-text-code` role) now ship with `@workflowbuilder/ui` itself, so the classes work standalone; the SDK inherits the fonts through the UI package instead of bundling its own. The fortieth style is `wb-text-code-mono`, an additional monospace role not named in the design system. diff --git a/.changeset/ui-export-prop-types.md b/.changeset/ui-export-prop-types.md index 383d256fe..382ad638c 100644 --- a/.changeset/ui-export-prop-types.md +++ b/.changeset/ui-export-prop-types.md @@ -2,4 +2,4 @@ '@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. +Component prop types are now exported: `AvatarProps`, `CheckboxProps`, `RadioProps`, `StatusProps`, `TooltipProps`, `MenuProps`, `ModalProps`, `EdgeLabelProps`, `NodeIconProps`, `NodeDescriptionProps`, `NodeAsPortWrapperProps`, `SegmentPickerProps` (with its controlled/uncontrolled variants), `NavButtonProps`, 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. diff --git a/.changeset/ui-layer-root-defaults.md b/.changeset/ui-layer-root-defaults.md index e51b1f16b..502c958c5 100644 --- a/.changeset/ui-layer-root-defaults.md +++ b/.changeset/ui-layer-root-defaults.md @@ -2,4 +2,4 @@ '@workflowbuilder/ui': minor --- -Variable defaults (`--ax-public-*` component defaults and the `--ax-*` design tokens in `tokens.css`) now ship inside the `ui.base` cascade layer. A plain `:root { --ax-…: … }` override in your app now wins regardless of stylesheet load order; previously a lazily loaded component stylesheet could silently restore the default. +Variable defaults (`--wb-public-*` component defaults and the `--wb-ds-*` design tokens in `tokens.css`) now ship inside the `ui.base` cascade layer. A plain `:root` override of a `--wb-public-*` variable or an `@workflowbuilder/ui` design token now wins regardless of stylesheet load order; previously a lazily loaded component stylesheet could silently restore the default. diff --git a/.changeset/vertical-layout-class.md b/.changeset/vertical-layout-class.md new file mode 100644 index 000000000..c1ccfdf1b --- /dev/null +++ b/.changeset/vertical-layout-class.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +A nested `VerticalLayout` in the properties form referenced a class its stylesheet never declared, so its children rendered as plain blocks with no gap. The root layout was never affected: `.json-form-container > div` gives it a 16px gap on its own. Controls stacked in a nested vertical layout, such as the two selects under the AI Agent node's Operational Settings, now keep the 8px gap the layout defines. diff --git a/.github/workflows/pr-check-docs.yml b/.github/workflows/pr-check-docs.yml index 84d61443d..3bb1fa73a 100644 --- a/.github/workflows/pr-check-docs.yml +++ b/.github/workflows/pr-check-docs.yml @@ -8,11 +8,13 @@ name: PR Check (docs) # Typecheck is deliberately absent: apps/docs tolerates known starlight # virtual-module type errors. +# PRs into main and release get checks. Add an integration branch here for +# the time it is the base of stacked PRs. on: pull_request: branches: - main - - release # merging into release is what deploys the docs site + - release # the docs site is deployed by hand from release, so its PRs get the same checks paths: - 'apps/docs/**' - 'packages/ui/**' diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 9ff75f7d5..27a8d3e06 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -8,9 +8,12 @@ name: PR Check # determinism tests guard Temporal replay safety and so must not be able to # regress silently. Plus the deploy compose files, which ship to the demo VM on # every deploy, and 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. +# (pr-check-docs.yml); demo and ai-studio are not built or type-checked here — they're +# internal and have their own broken-state tolerances — but their CSS goes +# through the style lint below like every other workspace's. +# PRs into main and release get checks. Add an integration branch here for +# the time it is the base of stacked PRs. on: pull_request: branches: @@ -262,6 +265,9 @@ jobs: # the check:built-css guard. run: pnpm build:ui + - name: Style lint (token usage + fallbacks) + run: pnpm lint:styles + execution: name: Execution pipeline lint + typecheck + test runs-on: ubuntu-latest diff --git a/.prettierignore b/.prettierignore index ebef0901d..ab6d57a23 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,6 +5,8 @@ apps/icons/src/utils/icons.gen.ts # Astro auto-generated content collections + types (gitignored, regenerated on build/dev) **/.astro/ +# Designer changelog checked in verbatim — reformatting corrupted token paths in emphasis markers +packages/tokens/migration/ # Recorded Temporal Event Histories. Machine-written, and deliberately kept byte-identical # to what `temporal workflow show --output json` emits so the two stay interchangeable. packages/temporal/test/replay/histories/ diff --git a/.stylelintrc.mjs b/.stylelintrc.mjs new file mode 100644 index 000000000..82d696860 --- /dev/null +++ b/.stylelintrc.mjs @@ -0,0 +1,15 @@ +import { fileURLToPath } from 'node:url'; + +// The csstools rule resolves importFrom paths against process.cwd(), and +// lint-staged runs commands from workspace directories — anchor to this file. +const customProperties = fileURLToPath(new URL('./tools/stylelint/custom-properties.mjs', import.meta.url)); + +/** @type {import('stylelint').Config} */ +export default { + plugins: ['stylelint-value-no-unknown-custom-properties', './tools/stylelint/no-system-token-fallbacks.mjs'], + ignoreFiles: ['**/node_modules/**', '**/dist/**', 'apps/docs/**'], + rules: { + 'csstools/value-no-unknown-custom-properties': [true, { importFrom: [customProperties] }], + 'wb/no-system-token-fallbacks': true, + }, +}; diff --git a/CLAUDE.md b/CLAUDE.md index ae2f7ded3..5c719ab54 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,7 @@ Each workspace has its own context. Read the relevant file before extending a wo | Workspace | Authoritative docs | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | `packages/sdk` | `packages/sdk/README.md` | -| `packages/ui` | `packages/ui/README.md` (+ `packages/ui/css-layers.md`) | +| `packages/ui` | `packages/ui/README.md` (+ `packages/ui/css-layers.md`, `packages/ui/built-css-pitfalls.md`) | | `packages/tokens` | `packages/tokens/README.md` | | `packages/ai-config` | `packages/ai-config/README.md` | | `packages/execution-core` | `packages/execution-core/README.md` | diff --git a/DECISION-LOGS.md b/DECISION-LOGS.md index c2df150c7..67666aae7 100644 --- a/DECISION-LOGS.md +++ b/DECISION-LOGS.md @@ -6,14 +6,22 @@ - _08.04.2025_: [Lazy-loaded Icons](./apps/icons/lazy-loaded-icons-08-04-2025.decision-log.md) - _15.04.2025_: [Internationalization implementation with i18next](./packages/sdk/src/features/i18n/i18next.decision-log.md) - _26.05.2025_: [JSON Form Validation Strategy](./packages/sdk/src/features/json-form/form-validation.decision-log.md) +- _05.03.2026_: [Independent docs deployment strategy](./apps/docs/docs-deployment.decision-log.md) +- _13.03.2026_: [Remark plugin for automatic base path link rewriting](./apps/docs/remark-base-path-links.decision-log.md) - _16.04.2026_: [CSP-safe Ajv replacement with @cfworker/json-schema](./packages/sdk/src/utils/validation/workflow-builder-validator-16-04-2026.decision-log.md) - _22.04.2026_: [SDK restructuring — inversion, relocation, plugin API, config naming](./packages/sdk/sdk-restructuring.decision-log.md) - _27.04.2026_: [Default to 127.0.0.1 binding for the reference backend](./apps/backend/local-dev-binding.decision-log.md) - _27.04.2026_: [Workflow cancellation handling in Temporal engine](./packages/temporal/src/workflow/cancellation-handling.decision-log.md) - _28.04.2026_: [Topological scheduling for the graph runner](./packages/execution-core/topological-scheduling.decision-log.md) - _29.04.2026_: [Decision executor fails fast on no matching branch](./packages/execution-core/decision-no-match.decision-log.md) +- _30.04.2026 (revised 04.05.2026 after team review)_: [Audience-based docs IA + schema authoring reference](./apps/docs/docs-restructure.decision-log.md) +- _30.04.2026_: [TypeDoc-driven API Reference for `@workflowbuilder/sdk`](./apps/docs/typedoc-api-reference.decision-log.md) - _05.05.2026_: [Extract AI Studio from `apps/demo` into its own `apps/ai-studio` app](./apps/ai-studio/ai-studio-extraction.decision-log.md) - _05.05.2026_: [Workspace layout — relocate libraries to `packages/`](./packages/sdk/workspace-layout.decision-log.md) - _06.05.2026_: [Make execution-core generic over the consumer's node union](./packages/execution-core/generic-execution-core.decision-log.md) - _15.05.2026_: [AuthPort seam for backend authn/authz](./apps/backend/auth-port.decision-log.md) +- _03.06.2026_: [TenantContextPort — multi-tenant identity seam for the reference backend](./apps/backend/tenant-context-port.decision-log.md) +- _07.08.2026_: [Keep the postcss box-sizing plugin over lint-based or selector-based alternatives](./packages/ui/postcss-box-sizing.decision-log.md) - _24.08.2026_: [`incomplete` as a third terminal state, distinct from `failed` and from a stall](./packages/execution-core/terminal-states.decision-log.md) +- _31.08.2026_: [Ship common font faces inline and the rest as assets](./packages/ui/font-assets.decision-log.md) +- _07.09.2026 (revised 14.09.2026, 15.09.2026 and 16.09.2026)_: [Derive the ConnectableItem width from the real container insets](./packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md) diff --git a/apps/ai-studio/src/components/controls/ai-studio-controls.module.css b/apps/ai-studio/src/components/controls/ai-studio-controls.module.css index 80833ed6a..772cdbb6d 100644 --- a/apps/ai-studio/src/components/controls/ai-studio-controls.module.css +++ b/apps/ai-studio/src/components/controls/ai-studio-controls.module.css @@ -17,9 +17,9 @@ } .panel { - background: var(--wb-app-bar-background); - border-radius: var(--wb-app-bar-border-radius); - border: 0.0625rem solid var(--wb-app-bar-border-color); + background: var(--wb-sdk-app-bar-background); + border-radius: var(--wb-sdk-app-bar-border-radius); + border: 0.0625rem solid var(--wb-sdk-app-bar-border-color); display: flex; gap: 0.5rem; padding: 0.75rem 1rem; diff --git a/apps/ai-studio/src/components/controls/ai-studio-controls.tsx b/apps/ai-studio/src/components/controls/ai-studio-controls.tsx index 45133ba32..62d362fe5 100644 --- a/apps/ai-studio/src/components/controls/ai-studio-controls.tsx +++ b/apps/ai-studio/src/components/controls/ai-studio-controls.tsx @@ -38,18 +38,28 @@ export function AiStudioControls() { >
{isRunning ? ( - - - + } + /> ) : ( - - - + } + /> )} {isDone && ( - - - + } + /> )}
diff --git a/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css b/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css index 24fe2fe2b..2ec798ed1 100644 --- a/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css +++ b/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css @@ -15,12 +15,12 @@ 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); + font-family: var(--wb-public-font-family, 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); + background: var(--wb-ds-ui-bg-base); + border: 0.0625rem solid var(--wb-ds-ui-stroke-default); + border-radius: var(--wb-sdk-app-bar-border-radius); box-shadow: 0 1.25rem 3.75rem rgba(0, 0, 0, 0.25); - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); } .close { @@ -35,13 +35,13 @@ border: none; border-radius: 0.5rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); cursor: pointer; } .close:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-ui-text-default); } .heading { @@ -59,22 +59,22 @@ 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); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-colors-acc1-500); } .title { margin: 0; font-size: 1.25rem; font-weight: 600; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); } .body { margin: 0 0 1.5rem; font-size: 0.92rem; line-height: 1.6; - color: var(--ax-txt-secondary-default, #4d5059); + color: var(--wb-ds-ui-text-subtle-default); } .body p { @@ -86,7 +86,7 @@ } .body strong { - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); font-weight: 600; } @@ -98,7 +98,7 @@ padding: 0.7rem 1rem; border: none; border-radius: 0.625rem; - background: var(--ax-colors-acc1-500, #1096e7); + background: var(--wb-ds-colors-acc1-500); color: #ffffff; font-size: 0.95rem; font-weight: 600; @@ -107,7 +107,7 @@ } .cta:hover { - background: var(--ax-colors-acc1-600, #0477c5); + background: var(--wb-ds-colors-acc1-600); } .reopen { @@ -120,14 +120,14 @@ justify-content: center; width: 2.5rem; height: 2.5rem; - border: 0.0625rem solid var(--wb-app-bar-border-color); + border: 0.0625rem solid var(--wb-sdk-app-bar-border-color); border-radius: 50%; - background: var(--wb-app-bar-background); - color: var(--ax-txt-secondary-default, #4d5059); + background: var(--wb-sdk-app-bar-background); + color: var(--wb-ds-ui-text-subtle-default); cursor: pointer; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); } .reopen:hover { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--wb-ds-colors-acc1-500); } diff --git a/apps/ai-studio/src/components/execution/highlighting.css b/apps/ai-studio/src/components/execution/highlighting.css index f485e2779..f70b08095 100644 --- a/apps/ai-studio/src/components/execution/highlighting.css +++ b/apps/ai-studio/src/components/execution/highlighting.css @@ -1,34 +1,33 @@ html[data-theme='light'] { - --ai-studio-edge-color--active: var(--ax-colors-green-400); - --ai-studio-status-color--incomplete: var(--ax-txt-warning-default); + --ai-studio-edge-color--active: var(--wb-ds-colors-green-400); + --ai-studio-status-color--completed: var(--wb-ds-colors-green-400); } html[data-theme='dark'] { - --ai-studio-edge-color--active: var(--ax-colors-green-200); - /* No dark warning-accent token exists (--ax-txt-warning-default is orange-100 body-text cream). */ - --ai-studio-status-color--incomplete: var(--ax-colors-orange-300); + --ai-studio-edge-color--active: var(--wb-ds-colors-green-200); + --ai-studio-status-color--completed: var(--wb-ds-colors-green-200); } :root { - --ai-studio-status-color--completed: var(--ax-txt-success-default); - --ai-studio-status-color--failed: var(--ax-txt-error-default); + --ai-studio-status-color--incomplete: var(--wb-ds-ui-text-warning-default); + --ai-studio-status-color--failed: var(--wb-ds-ui-text-critical-default); --ai-studio-status-bg--completed: color-mix(in srgb, var(--ai-studio-status-color--completed), transparent 85%); --ai-studio-status-bg--incomplete: color-mix(in srgb, var(--ai-studio-status-color--incomplete), transparent 85%); --ai-studio-status-bg--failed: color-mix(in srgb, var(--ai-studio-status-color--failed), transparent 85%); --ai-studio-node-shadow-color--active: var(--ai-studio-edge-color--active); --ai-studio-node-shadow-color--completed: var(--ai-studio-edge-color--active); - --ai-studio-node-shadow-color--failed: var(--ax-colors-red-400); + --ai-studio-node-shadow-color--failed: var(--wb-ds-colors-red-400); - --ai-studio-node-shadow--active: var(--ax-token-shadow-focus-node-active-x) var(--ax-token-shadow-focus-node-active-y) - var(--ax-token-shadow-focus-node-active-blur) var(--ax-token-shadow-focus-node-active-spread) - var(--ai-studio-node-shadow-color--active); + --ai-studio-node-shadow--active: var(--wb-ds-shadow-canvas-focus-ring-node-x) + var(--wb-ds-shadow-canvas-focus-ring-node-y) var(--wb-ds-shadow-canvas-focus-ring-node-blur) + var(--wb-ds-shadow-canvas-focus-ring-node-spread) var(--ai-studio-node-shadow-color--active); - --ai-studio-node-shadow--completed: var(--ax-token-shadow-focus-node-active-x) - var(--ax-token-shadow-focus-node-active-y) var(--ax-token-shadow-focus-node-active-blur) - var(--ax-token-shadow-focus-node-active-spread) var(--ai-studio-node-shadow-color--completed); + --ai-studio-node-shadow--completed: var(--wb-ds-shadow-canvas-focus-ring-node-x) + var(--wb-ds-shadow-canvas-focus-ring-node-y) var(--wb-ds-shadow-canvas-focus-ring-node-blur) + var(--wb-ds-shadow-canvas-focus-ring-node-spread) var(--ai-studio-node-shadow-color--completed); - --ai-studio-node-shadow--failed: var(--ax-token-shadow-focus-node-active-x) var(--ax-token-shadow-focus-node-active-y) - var(--ax-token-shadow-focus-node-active-blur) var(--ax-token-shadow-focus-node-active-spread) - var(--ai-studio-node-shadow-color--failed); + --ai-studio-node-shadow--failed: var(--wb-ds-shadow-canvas-focus-ring-node-x) + var(--wb-ds-shadow-canvas-focus-ring-node-y) var(--wb-ds-shadow-canvas-focus-ring-node-blur) + var(--wb-ds-shadow-canvas-focus-ring-node-spread) var(--ai-studio-node-shadow-color--failed); } diff --git a/apps/ai-studio/src/components/execution/log-panel.module.css b/apps/ai-studio/src/components/execution/log-panel.module.css index 22b063107..43a5b0d27 100644 --- a/apps/ai-studio/src/components/execution/log-panel.module.css +++ b/apps/ai-studio/src/components/execution/log-panel.module.css @@ -1,18 +1,20 @@ .panel { position: fixed; bottom: 1rem; + /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties -- set from log-panel.tsx inline style */ right: var(--log-panel-right, 1rem); width: 30rem; max-width: calc(100vw - 3rem); max-height: 26.875rem; - 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); + background: var(--wb-sdk-app-bar-background); + border: 0.0625rem solid var(--wb-sdk-app-bar-border-color); + border-radius: var(--wb-sdk-app-bar-border-radius); + color: var(--wb-ds-ui-text-default); display: flex; flex-direction: column; z-index: 10; font-size: 0.75rem; + font-family: var(--wb-public-font-family, 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); overflow: hidden; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); } @@ -28,7 +30,7 @@ padding: 0.5rem 0.75rem; cursor: pointer; user-select: none; - border-bottom: 0.0625rem solid var(--wb-app-bar-border-color); + border-bottom: 0.0625rem solid var(--wb-sdk-app-bar-border-color); flex-shrink: 0; } @@ -55,7 +57,7 @@ .event { padding: 0.375rem 0.75rem; - border-bottom: 0.0625rem solid color-mix(in srgb, var(--wb-app-bar-border-color), transparent 50%); + border-bottom: 0.0625rem solid color-mix(in srgb, var(--wb-sdk-app-bar-border-color), transparent 50%); &:last-child { border-bottom: none; @@ -91,7 +93,7 @@ .badge--node_started, .badge--node_skipped { - color: var(--ax-txt-secondary-default, #888); + color: var(--wb-ds-ui-text-subtle-default); } .badge--execution_incomplete { @@ -104,7 +106,7 @@ } .badge--execution_cancelled { - color: var(--ax-txt-secondary-default, #888); + color: var(--wb-ds-ui-text-subtle-default); } .node-id { @@ -132,7 +134,7 @@ .detail { margin-top: 0.25rem; padding: 0.375rem 0.5rem; - background: color-mix(in srgb, var(--wb-app-bar-border-color), transparent 70%); + background: color-mix(in srgb, var(--wb-sdk-app-bar-border-color), transparent 70%); border-radius: 0.25rem; white-space: pre-wrap; word-break: break-word; @@ -178,6 +180,6 @@ } .event--highlighted { - background: color-mix(in srgb, var(--ax-colors-acc1-500, #1096e7), transparent 88%); - box-shadow: inset 0.1875rem 0 0 var(--ax-colors-acc1-500, #1096e7); + background: color-mix(in srgb, var(--wb-ds-colors-acc1-500), transparent 88%); + box-shadow: inset 0.1875rem 0 0 var(--wb-ds-colors-acc1-500); } diff --git a/apps/ai-studio/src/components/execution/node-markers.module.css b/apps/ai-studio/src/components/execution/node-markers.module.css index 2d2711449..ccc94344b 100644 --- a/apps/ai-studio/src/components/execution/node-markers.module.css +++ b/apps/ai-studio/src/components/execution/node-markers.module.css @@ -1,17 +1,17 @@ .container { position: absolute; - top: calc(100% + 0.5rem); + top: calc(100% + var(--wb-ds-space-100)); left: 0; font-weight: 500; - padding: var(--ax-public-node-padding); - border-radius: var(--ax-public-node-border-radius); - border: var(--ax-public-node-border-size) solid var(--ax-public-node-border-color); - background: var(--ax-public-node-background-color); - color: var(--ax-public-node-title-subtitle); + padding: var(--wb-public-node-padding); + border-radius: var(--wb-public-node-border-radius); + border: var(--wb-public-node-border-size) solid var(--wb-public-node-border-color); + background: var(--wb-public-node-background-color); + color: var(--wb-public-node-title-subtitle); display: flex; align-items: center; justify-content: center; - gap: 0.5rem; + gap: var(--wb-ds-space-100); &, * { @@ -34,7 +34,7 @@ } .icon--completed { - color: var(--ax-public-node-title-color); + color: var(--wb-public-node-title-color); } .icon--failed { @@ -42,7 +42,7 @@ } .icon--skipped { - color: var(--ax-txt-secondary-default, #888); + color: var(--wb-ds-ui-text-subtle-default); opacity: 0.7; } diff --git a/apps/ai-studio/src/components/visualize/copy-result-button.spec.tsx b/apps/ai-studio/src/components/visualize/copy-result-button.spec.tsx index e3837defe..2d60950a7 100644 --- a/apps/ai-studio/src/components/visualize/copy-result-button.spec.tsx +++ b/apps/ai-studio/src/components/visualize/copy-result-button.spec.tsx @@ -9,12 +9,6 @@ vi.mock('../../utils/export-visualization', () => ({ copyResult: vi.fn(), })); -declare global { - // eslint-disable-next-line no-var - var IS_REACT_ACT_ENVIRONMENT: boolean; -} -globalThis.IS_REACT_ACT_ENVIRONMENT = true; - async function click(button: HTMLButtonElement) { await act(async () => { button.dispatchEvent(new MouseEvent('click', { bubbles: true })); diff --git a/apps/ai-studio/src/components/visualize/renderers.module.css b/apps/ai-studio/src/components/visualize/renderers.module.css index 6d64314e0..cd133c035 100644 --- a/apps/ai-studio/src/components/visualize/renderers.module.css +++ b/apps/ai-studio/src/components/visualize/renderers.module.css @@ -1,7 +1,7 @@ .markdown { font-size: 0.85rem; line-height: 1.6; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); overflow-wrap: anywhere; } @@ -40,7 +40,7 @@ } .markdown a { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--wb-ds-ui-text-action-default); text-decoration: underline; } @@ -51,7 +51,7 @@ .markdown code { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.82em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--wb-ds-ui-bg-elevated); padding: 0.1em 0.35em; border-radius: 0.25rem; } @@ -59,7 +59,7 @@ .markdown pre { margin: 0.6em 0; padding: 0.7em 0.85em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--wb-ds-ui-bg-elevated); border-radius: 0.5rem; overflow-x: auto; } @@ -72,8 +72,8 @@ .markdown blockquote { margin: 0.6em 0; padding-left: 0.85em; - border-left: 0.1875rem solid var(--ax-ui-stroke-primary-default, #edeff3); - color: var(--ax-txt-secondary-default, #4d5059); + border-left: 0.1875rem solid var(--wb-ds-ui-stroke-default); + color: var(--wb-ds-ui-text-subtle-default); } .rich p { @@ -89,13 +89,13 @@ .rich code { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.85em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--wb-ds-ui-bg-elevated); padding: 0.05em 0.3em; border-radius: 0.2rem; } .rich a { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--wb-ds-ui-text-action-default); text-decoration: underline; } @@ -108,7 +108,7 @@ font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.8rem; line-height: 1.55; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); white-space: pre-wrap; overflow-wrap: anywhere; } @@ -117,7 +117,7 @@ font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.78rem; line-height: 1.5; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); } .json-row { @@ -132,18 +132,18 @@ .json-children { padding-left: 0.9rem; - border-left: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border-left: 0.0625rem solid var(--wb-ds-ui-stroke-default); margin-left: 0.25rem; } .json-toggle { width: 0.7rem; flex-shrink: 0; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); } .json-key { - color: var(--ax-colors-acc1-600, #0477c5); + color: var(--wb-ds-colors-acc1-600); } .json-string { @@ -160,7 +160,7 @@ } .json-bracket { - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); } .table { @@ -171,14 +171,14 @@ .table th, .table td { - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border: 0.0625rem solid var(--wb-ds-ui-stroke-default); padding: 0.3em 0.5em; text-align: left; vertical-align: top; } .table th { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--wb-ds-ui-bg-elevated); font-weight: 600; position: sticky; top: 0; @@ -192,15 +192,15 @@ .stat-card { padding: 0.6rem 0.7rem; - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border: 0.0625rem solid var(--wb-ds-ui-stroke-default); border-radius: 0.5rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--wb-ds-ui-bg-elevated); } .stat-value { font-size: 1.05rem; font-weight: 600; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); overflow-wrap: anywhere; } @@ -209,13 +209,13 @@ font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.03em; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); } .fallback-note { margin-bottom: 0.4rem; font-size: 0.72rem; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); } .chart { diff --git a/apps/ai-studio/src/components/visualize/visualize-card.module.css b/apps/ai-studio/src/components/visualize/visualize-card.module.css index 90a45315a..2465c79d6 100644 --- a/apps/ai-studio/src/components/visualize/visualize-card.module.css +++ b/apps/ai-studio/src/components/visualize/visualize-card.module.css @@ -2,10 +2,10 @@ width: 100%; margin-top: 0.4rem; padding-top: 0.5rem; - border-top: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); - font-family: var(--wb-font-family); + border-top: 0.0625rem solid var(--wb-ds-ui-stroke-default); + font-family: var(--wb-public-font-family, 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); text-align: left; - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); &, * { @@ -30,8 +30,8 @@ font-size: 0.64rem; padding: 0.15rem 0.4rem; border-radius: 0.3rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-tertiary-default, #6f7480); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-ui-text-muted-default); } .actions { @@ -50,7 +50,7 @@ border: none; border-radius: 0.3rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); cursor: pointer; } @@ -60,8 +60,8 @@ } .action:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-ui-text-default); } .action:disabled { @@ -93,7 +93,7 @@ .empty-icon { width: 1.6rem; height: 1.6rem; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); opacity: 0.55; } @@ -101,7 +101,7 @@ margin: 0; font-size: 0.74rem; line-height: 1.45; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); } .dots { @@ -114,7 +114,7 @@ width: 0.45rem; height: 0.45rem; border-radius: 50%; - background: var(--ax-colors-acc1-500, #1096e7); + background: var(--wb-ds-colors-acc1-500); animation: md-pulse 1s ease-in-out infinite; } diff --git a/apps/ai-studio/src/components/visualize/visualize-modal.module.css b/apps/ai-studio/src/components/visualize/visualize-modal.module.css index 7850c1acc..e22b238e2 100644 --- a/apps/ai-studio/src/components/visualize/visualize-modal.module.css +++ b/apps/ai-studio/src/components/visualize/visualize-modal.module.css @@ -8,7 +8,7 @@ padding: 2rem; background: rgba(8, 10, 14, 0.55); backdrop-filter: blur(2px); - font-family: var(--wb-font-family); + font-family: var(--wb-public-font-family, 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); } .modal { @@ -16,11 +16,11 @@ flex-direction: column; width: min(56rem, 92vw); max-height: 86vh; - background: var(--ax-ui-bg-primary-default, #ffffff); - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + background: var(--wb-ds-ui-bg-base); + border: 0.0625rem solid var(--wb-ds-ui-stroke-default); border-radius: 0.9rem; box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.4); - color: var(--ax-txt-primary-default, #151516); + color: var(--wb-ds-ui-text-default); overflow: hidden; &, @@ -34,7 +34,7 @@ align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; - border-bottom: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border-bottom: 0.0625rem solid var(--wb-ds-ui-stroke-default); } .title { @@ -47,8 +47,8 @@ font-size: 0.7rem; padding: 0.15rem 0.5rem; border-radius: 0.3rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-tertiary-default, #6f7480); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-ui-text-muted-default); } .actions { @@ -66,7 +66,7 @@ border: none; border-radius: 0.4rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); cursor: pointer; } @@ -76,8 +76,8 @@ } .action:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--wb-ds-ui-bg-elevated); + color: var(--wb-ds-ui-text-default); } .body { @@ -87,6 +87,6 @@ } .loading { - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--wb-ds-ui-text-muted-default); font-size: 0.85rem; } diff --git a/apps/ai-studio/src/data/ai-debate-flow.ts b/apps/ai-studio/src/data/ai-debate-flow.ts index a98de28cd..d36623e88 100644 --- a/apps/ai-studio/src/data/ai-debate-flow.ts +++ b/apps/ai-studio/src/data/ai-debate-flow.ts @@ -21,8 +21,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 63 }, - dragging: false, }, { id: 'optimist-1', @@ -41,8 +39,6 @@ why now. Be persuasive but honest, no hype.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'skeptic-1', @@ -61,8 +57,6 @@ hidden costs, what could go wrong. Surface the objections others gloss over.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'verdict-1', @@ -92,8 +86,6 @@ Be decisive.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'visualize-1', @@ -110,8 +102,6 @@ Be decisive.`, icon: 'Eye', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, ], edges: [ diff --git a/apps/ai-studio/src/data/content-repurposer-flow.ts b/apps/ai-studio/src/data/content-repurposer-flow.ts index 8eefa272c..53e91f552 100644 --- a/apps/ai-studio/src/data/content-repurposer-flow.ts +++ b/apps/ai-studio/src/data/content-repurposer-flow.ts @@ -26,8 +26,6 @@ of the most tedious parts of their week.`, icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 63 }, - dragging: false, }, { id: 'twitter-1', @@ -48,8 +46,6 @@ Number each post (1/, 2/, ...).`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'linkedin-1', @@ -71,8 +67,6 @@ Keep it under 200 words. Professional but human.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'instagram-1', @@ -93,8 +87,6 @@ Keep it under 200 words. Professional but human.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'pack-1', @@ -125,8 +117,6 @@ Keep each draft's wording as-is - do not rewrite it. Just organize and label.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'visualize-1', @@ -143,8 +133,6 @@ Keep each draft's wording as-is - do not rewrite it. Just organize and label.`, icon: 'Eye', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, ], edges: [ diff --git a/apps/ai-studio/src/data/meeting-notes-flow.ts b/apps/ai-studio/src/data/meeting-notes-flow.ts index dcf46a382..7937c929a 100644 --- a/apps/ai-studio/src/data/meeting-notes-flow.ts +++ b/apps/ai-studio/src/data/meeting-notes-flow.ts @@ -25,8 +25,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 63 }, - dragging: false, }, { id: 'summary-1', @@ -44,8 +42,6 @@ was decided. Neutral, factual tone.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'actions-1', @@ -64,8 +60,6 @@ mark it "unassigned". Do not invent items that were not discussed.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'recap-1', @@ -94,8 +88,6 @@ line "_Meeting Bot_".`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'visualize-1', @@ -112,8 +104,6 @@ line "_Meeting Bot_".`, icon: 'Eye', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, ], edges: [ diff --git a/apps/ai-studio/src/data/research-flow.ts b/apps/ai-studio/src/data/research-flow.ts index 065408ee6..31639c192 100644 --- a/apps/ai-studio/src/data/research-flow.ts +++ b/apps/ai-studio/src/data/research-flow.ts @@ -20,8 +20,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 63 }, - dragging: false, }, { id: 'research-1', @@ -52,8 +50,6 @@ Only state things you found via search. If a claim isn't supported by a result, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'visualize-1', @@ -70,8 +66,6 @@ Only state things you found via search. If a claim isn't supported by a result, icon: 'Eye', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, ], edges: [ diff --git a/apps/ai-studio/src/data/support-triage-flow.ts b/apps/ai-studio/src/data/support-triage-flow.ts index 9569a46e1..71dfc4b92 100644 --- a/apps/ai-studio/src/data/support-triage-flow.ts +++ b/apps/ai-studio/src/data/support-triage-flow.ts @@ -34,8 +34,6 @@ Head of Ops, Brightwave`, icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 63 }, - dragging: false, }, { id: 'classify-1', @@ -65,8 +63,6 @@ Use the exact lowercase keyword on the Type line - it drives downstream routing. icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'decision-1', @@ -117,8 +113,6 @@ Use the exact lowercase keyword on the Type line - it drives downstream routing. icon: 'ArrowsSplit', }, selected: false, - measured: { width: 258, height: 236 }, - dragging: false, }, { id: 'billing-1', @@ -142,8 +136,6 @@ You handle billing issues. Draft a reply to the customer: icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'bug-1', @@ -167,8 +159,6 @@ You triage product bugs. Draft a reply to the customer: icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'general-1', @@ -191,8 +181,6 @@ You answer how-to and general questions. Draft a friendly reply: icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'qa-1', @@ -218,8 +206,6 @@ If not, output "⚠️ NEEDS REVISION" followed by specific, actionable fixes.`, icon: 'AiAgent', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, { id: 'visualize-1', @@ -236,8 +222,6 @@ If not, output "⚠️ NEEDS REVISION" followed by specific, actionable fixes.`, icon: 'Eye', }, selected: false, - measured: { width: 258, height: 123 }, - dragging: false, }, ], edges: [ diff --git a/apps/ai-studio/src/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx b/apps/ai-studio/src/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx index 7472ffbca..b65347b69 100644 --- a/apps/ai-studio/src/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx +++ b/apps/ai-studio/src/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx @@ -12,12 +12,20 @@ export function ButtonsUndoRedo() { return ( <> - - - - - - + } + /> + } + /> ); } diff --git a/apps/ai-studio/types.d.ts b/apps/ai-studio/types.d.ts new file mode 100644 index 000000000..7d5b04108 --- /dev/null +++ b/apps/ai-studio/types.d.ts @@ -0,0 +1,7 @@ +declare global { + // React reads this flag to know the test environment wraps updates in act(); vitest.setup.ts sets it. + // eslint-disable-next-line no-var + var IS_REACT_ACT_ENVIRONMENT: boolean; +} + +export {}; diff --git a/apps/ai-studio/vite.config.mts b/apps/ai-studio/vite.config.mts index d2b9039ac..0741fa4ce 100644 --- a/apps/ai-studio/vite.config.mts +++ b/apps/ai-studio/vite.config.mts @@ -37,6 +37,7 @@ export default defineConfig(() => { test: { globals: true, environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], }, }; }); diff --git a/apps/ai-studio/vitest.setup.ts b/apps/ai-studio/vitest.setup.ts new file mode 100644 index 000000000..ef46f7b26 --- /dev/null +++ b/apps/ai-studio/vitest.setup.ts @@ -0,0 +1 @@ +globalThis.IS_REACT_ACT_ENVIRONMENT = true; diff --git a/apps/backend/tenant-context-port.decision-log.md b/apps/backend/tenant-context-port.decision-log.md index ef937c2d8..391dc3adf 100644 --- a/apps/backend/tenant-context-port.decision-log.md +++ b/apps/backend/tenant-context-port.decision-log.md @@ -2,6 +2,8 @@ ### Proposed by: Kacper Cierzniewski +### Date: 03.06.2026 + ### Proposed: 21.05.2026 — Landed: 03.06.2026 (`fa5999dd`) > This is the **decision** (why this shape, what was rejected, what it does and does not protect). The **how-to-wire-it** lives in [`multi-tenancy.md`](./multi-tenancy.md) — that document tracks the code and is the source of truth for current signatures and per-seam status. If a snippet here ever disagrees with the code, the code wins. diff --git a/apps/demo/src/app/components/dashed-edge/dashed-edge.tsx b/apps/demo/src/app/components/dashed-edge/dashed-edge.tsx index c39c0c21f..a1750c008 100644 --- a/apps/demo/src/app/components/dashed-edge/dashed-edge.tsx +++ b/apps/demo/src/app/components/dashed-edge/dashed-edge.tsx @@ -21,7 +21,7 @@ import { type EdgeProps, getSmoothStepPath } from '@xyflow/react'; * "Custom edges and selection". * * To diverge from the built-in selection look, add a `selected` branch to the - * `style` below, or restyle every edge globally via the `--ax-public-edge-color-select` + * `style` below, or restyle every edge globally via the `--wb-public-edge-color-select` * CSS variable. See the SDK README, "Custom edges and selection". */ export function DashedEdge({ diff --git a/apps/demo/src/app/components/multi-port-node/multi-port-node-template.tsx b/apps/demo/src/app/components/multi-port-node/multi-port-node-template.tsx index 05fb08080..595d0911c 100644 --- a/apps/demo/src/app/components/multi-port-node/multi-port-node-template.tsx +++ b/apps/demo/src/app/components/multi-port-node/multi-port-node-template.tsx @@ -24,6 +24,7 @@ export const MultiPortNodeTemplate = defineNodeTemplate( label, description, selected = false, + disabled = false, data, showHandles = true, }: WorkflowNodeTemplateProps) => { @@ -40,10 +41,10 @@ export const MultiPortNodeTemplate = defineNodeTemplate( return (
- + - - + + diff --git a/apps/demo/src/app/data/templates/black-friday.ts b/apps/demo/src/app/data/templates/black-friday.ts index 289414c3b..7e5d74fbc 100644 --- a/apps/demo/src/app/data/templates/black-friday.ts +++ b/apps/demo/src/app/data/templates/black-friday.ts @@ -21,7 +21,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '8ca95405-7bb6-496d-90ae-850b3ee88dc1', @@ -39,7 +38,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '3817f8c6-eba8-485d-9476-cf944f64a373', @@ -57,7 +55,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'aebc5083-5773-492d-bb49-1e928f56e331', @@ -75,8 +72,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'd6adcc9c-3146-46fd-9b0f-af78e94ad04d', @@ -98,7 +93,6 @@ const diagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '60ed7855-3c3a-42ca-a770-8240a3a8ee12', @@ -120,7 +114,6 @@ const diagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '77a46d81-8b23-43a0-8c37-1e29335b67a0', @@ -138,8 +131,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'da28f8fe-c3a0-44c0-8213-c7c88d62164e', @@ -157,7 +148,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '455a0738-c1cc-4856-992a-07654eb29182', @@ -175,7 +165,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'd27658ca-925d-450d-9ddd-737a5f0aeeb4', @@ -193,8 +182,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'f1a5e5cb-58fa-4379-a45e-437553153b5f', @@ -216,8 +203,6 @@ const diagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, ], edges: [ diff --git a/apps/demo/src/app/data/templates/call-flow.ts b/apps/demo/src/app/data/templates/call-flow.ts index 1d46283bd..c7a2dc737 100644 --- a/apps/demo/src/app/data/templates/call-flow.ts +++ b/apps/demo/src/app/data/templates/call-flow.ts @@ -17,7 +17,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '6e4c57e4-4136-4aa4-a183-43679b1f7170', @@ -35,7 +34,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'a845d12e-0852-4809-96ce-334159e100fe', @@ -53,7 +51,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '65706043-b1d2-4600-b918-3b82ff5dfc13', @@ -71,7 +68,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'c187ef68-0fb8-47da-9a62-310afe91bfaa', @@ -89,8 +85,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'b2a4b7a7-1900-4218-aea4-902de2e4eb51', @@ -108,7 +102,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '09c67367-8d0d-44ae-bf4e-66b36f5c389f', @@ -126,7 +119,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'b01ced25-ca06-47c7-a14f-21e5a0200a61', @@ -144,8 +136,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'f1d785cc-1715-4b27-97e4-59347f08e660', @@ -163,7 +153,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '6c3fca17-a1d4-4008-8a72-605d91eb7fd6', @@ -181,7 +170,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '6c0ecb78-cd09-4728-8dc7-414ab1c42ea2', @@ -199,8 +187,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '18562830-da0b-443e-a859-79cb95473f59', @@ -218,7 +204,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'bc34e3ae-6dd2-4543-8efe-190863df49e6', @@ -236,8 +221,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '6ab2c9e2-7e1e-4061-83ea-83a88aa246eb', @@ -255,8 +238,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'feb863db-de51-4aee-8746-f9e02d993cb7', @@ -274,8 +255,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '456f755a-a1fe-4e18-a850-802cf28b4cf5', @@ -293,7 +272,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'cd9ae822-5ae4-4fe0-b876-760d460faf0e', @@ -311,8 +289,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '53f8322b-96ef-4775-824a-020f02cccd20', @@ -330,8 +306,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, ], edges: [ diff --git a/apps/demo/src/app/data/templates/simple-flow.ts b/apps/demo/src/app/data/templates/simple-flow.ts index 4746a71cd..55f39df58 100644 --- a/apps/demo/src/app/data/templates/simple-flow.ts +++ b/apps/demo/src/app/data/templates/simple-flow.ts @@ -23,8 +23,6 @@ const defaultDiagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'da47caa9-c695-47bb-be52-b30bb8a6be6d', @@ -42,8 +40,6 @@ const defaultDiagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '47375954-4e4a-4567-b7d3-c70c3921e1dd', @@ -61,8 +57,6 @@ const defaultDiagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'e7ecd597-55ca-4bba-9d32-d0c51173046d', @@ -80,8 +74,6 @@ const defaultDiagram: DiagramModel = { icon: 'ArrowsSplit', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '798dbba1-d356-4fcd-8ba2-90e75f4912f9', @@ -99,7 +91,6 @@ const defaultDiagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '801b6f56-05d9-4639-b426-ef171741a408', @@ -121,8 +112,6 @@ const defaultDiagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'fd7745bf-4562-447f-a65c-2581b6a77eac', @@ -140,7 +129,6 @@ const defaultDiagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'c87efe62-6394-43c2-8714-96c0dc19a407', @@ -158,7 +146,6 @@ const defaultDiagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '3bfaad20-c0b8-4a90-bf8b-04c3eec2ce31', @@ -176,7 +163,6 @@ const defaultDiagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '8b356b4a-5959-48ff-9374-cd07dc9522f6', @@ -194,7 +180,6 @@ const defaultDiagram: DiagramModel = { icon: 'ArrowsSplit', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'abac90c2-af48-458e-8e0c-48a505d0826e', @@ -216,8 +201,6 @@ const defaultDiagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '33bfbf0d-f0eb-452e-aeba-f330bb9badec', @@ -235,7 +218,6 @@ const defaultDiagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '263e749c-dfaa-4aed-af11-c7ad161aee54', @@ -253,7 +235,6 @@ const defaultDiagram: DiagramModel = { icon: 'ArrowsSplit', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'caae31f8-9bf6-4488-addf-d1b80842e1f7', @@ -271,7 +252,6 @@ const defaultDiagram: DiagramModel = { icon: 'PaperPlaneRight', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: 'c125ce95-d5f4-4a93-a9de-407e45016d8d', @@ -289,8 +269,6 @@ const defaultDiagram: DiagramModel = { icon: 'PaperPlaneRight', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, ], edges: [ diff --git a/apps/demo/src/app/data/templates/user-registration.ts b/apps/demo/src/app/data/templates/user-registration.ts index 5c310196d..e24f6bf6d 100644 --- a/apps/demo/src/app/data/templates/user-registration.ts +++ b/apps/demo/src/app/data/templates/user-registration.ts @@ -17,7 +17,6 @@ const diagram: DiagramModel = { icon: 'Lightning', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '5e11be1b-8db6-4a73-9fef-4e0bdf3f4aad', @@ -35,7 +34,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '91c179cb-71f5-4c14-abdc-24d17480af18', @@ -53,7 +51,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, { id: '2b2942c5-56a2-41c6-bb3d-d653b4c314da', @@ -71,8 +68,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'eea762a9-de40-4783-affe-aee7a0f02be6', @@ -94,8 +89,6 @@ const diagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'c9f39f2a-5d60-409e-bf6a-c01602583c96', @@ -113,8 +106,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '8fd0bd71-81ab-4499-8cf5-7b39967aa7f8', @@ -132,8 +123,6 @@ const diagram: DiagramModel = { icon: 'ListChecks', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '0ce0778e-8592-4763-81be-070797bf50c2', @@ -151,8 +140,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '862de19e-af00-4e22-aea3-f1558b9140e7', @@ -174,8 +161,6 @@ const diagram: DiagramModel = { icon: 'Timer', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: 'cd262d84-8735-47be-8ccd-174296f2e21a', @@ -193,8 +178,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, - dragging: false, }, { id: '7d55e5c9-6761-4940-8559-90a0d2bfc805', @@ -212,7 +195,6 @@ const diagram: DiagramModel = { icon: 'PlayCircle', }, selected: false, - measured: { width: 258, height: 64 }, }, ], edges: [ diff --git a/apps/demo/src/app/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx b/apps/demo/src/app/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx index 7472ffbca..b65347b69 100644 --- a/apps/demo/src/app/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx +++ b/apps/demo/src/app/plugins/undo-redo/components/buttons-undo-redo/buttons-undo-redo.tsx @@ -12,12 +12,20 @@ export function ButtonsUndoRedo() { return ( <> - - - - - - + } + /> + } + /> ); } diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 0453e265c..865c75517 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -50,7 +50,7 @@ export default defineConfig({ // Route `@workflowbuilder/sdk` through a docs-only shim that re-exports // just the symbols demo's schema/uischema files import — without the // SDK barrel's CSS side-effect, which would leak a full-viewport reset - // (body overflow:hidden, global Poppins) into the docs layout. + // (body overflow:hidden) into the docs layout. { find: /^@workflowbuilder\/sdk$/, replacement: path.resolve(import.meta.dirname, 'src/sdk-shim.ts'), @@ -117,9 +117,9 @@ export default defineConfig({ ], // `@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: + // defines `--wb-*` custom properties on `:root` and `html[data-theme]`. + // Starlight toggles the latter, 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: [ @@ -167,6 +167,7 @@ export default defineConfig({ }, { label: 'Theming', link: '/get-started/theming/' }, { label: 'Side effects & limitations', link: '/get-started/side-effects/' }, + { label: 'Upgrade to 3.0', link: '/get-started/upgrade-to-3/' }, ], }, { label: 'Guides', autogenerate: { directory: 'guides' } }, @@ -178,6 +179,7 @@ export default defineConfig({ items: [ { label: 'Overview', link: '/ui-library/overview/' }, { label: 'Design tokens', link: '/ui-library/design-tokens/' }, + { label: 'Typography', link: '/ui-library/typography/' }, { label: 'UI Components', autogenerate: { directory: 'ui-library/ui-components' } }, { label: 'Diagram Components', autogenerate: { directory: 'ui-library/diagram-components' } }, ], diff --git a/apps/docs/scripts/generate-ui-api.mjs b/apps/docs/scripts/generate-ui-api.mjs index f641b21bc..99a3bd861 100644 --- a/apps/docs/scripts/generate-ui-api.mjs +++ b/apps/docs/scripts/generate-ui-api.mjs @@ -72,7 +72,9 @@ function findTypeByName(root, name, warnings) { for (const child of node.children ?? []) walk(child); })(root); if (matches.length > 1 && warnings) { - warnings.push(`type name "${name}" is ambiguous (${matches.length} declarations) - the table would document whichever TypeDoc emitted first`); + warnings.push( + `type name "${name}" is ambiguous (${matches.length} declarations) - the table would document whichever TypeDoc emitted first`, + ); } return matches[0] ?? null; } @@ -213,23 +215,52 @@ function collectProps(typeNode, byId, accumulator = new Map(), context = null) { // Follow first-party prop types only; both declaration forms count. if (target && (target.kind === 2_097_152 || target.kind === 256)) { collectProps(target, byId, accumulator, context); + } else if (!target && context) { + context.warnings.push( + `"${context.slug}": props of ${typeNode.name} are missing from the table - export the type so TypeDoc emits it`, + ); } return accumulator; } - // Partial / Omit would silently drop every prop of X. - if (typeNode.type === 'reference' && typeNode.typeArguments?.length && context) { - const firstParty = typeNode.typeArguments.find( - (argument) => argument.type === 'reference' && typeof argument.target === 'number' && byId.get(argument.target), - ); - if (firstParty) { + if (typeNode.type === 'reference' && typeNode.typeArguments?.length) { + const [source, keys] = typeNode.typeArguments; + const sourceIsFirstParty = + source && source.type === 'reference' && typeof source.target === 'number' && byId.get(source.target); + if (typeNode.name === 'Omit' || typeNode.name === 'Pick' || typeNode.name === 'Partial') { + const named = collectProps(source, byId, new Map(), context); + const listed = new Set(literalNames(keys)); + if (keys && listed.size === 0 && context) { + context.warnings.push( + `"${context.slug}": the keys of ${typeNode.name}<...> are not string literals, so the table ${ + typeNode.name === 'Pick' ? 'keeps nothing' : 'drops nothing' + } - inline the keys or extend the generator`, + ); + } + for (const [name, property] of named) { + const keep = typeNode.name === 'Pick' ? listed.has(name) : !listed.has(name); + if (!keep) continue; + const optional = typeNode.name === 'Partial' ? { ...property, required: false } : property; + if (!accumulator.has(name)) accumulator.set(name, optional); + } + return accumulator; + } + if (sourceIsFirstParty && context) { context.warnings.push( - `"${context.slug}": props of ${firstParty.name} are hidden behind ${typeNode.name}<...> - unwrap the utility type or extend the generator`, + `"${context.slug}": props of ${source.name} are hidden behind ${typeNode.name}<...> - unwrap the utility type or extend the generator`, ); } } return accumulator; } +// String literals a utility type was given, e.g. the 'children' in Omit. +function literalNames(typeNode) { + if (!typeNode) return []; + if (typeNode.type === 'literal' && typeof typeNode.value === 'string') return [typeNode.value]; + if (typeNode.type === 'union') return typeNode.types.flatMap((member) => literalNames(member)); + return []; +} + function addProperty(child, byId, accumulator) { if (child.kind !== 1024 || accumulator.has(child.name)) return; // 1024 = Property accumulator.set(child.name, { @@ -270,8 +301,7 @@ function collectVariantProps(propsTypeNames, project, byId, warnings, slug, cont const sharedByAll = occurrences.length === perVariant.length && distinctTypes.size === 1; // Required in every variant, else the table documents an impossible call. - const requiredEverywhere = - occurrences.length === perVariant.length && occurrences.every((o) => o.prop.required); + const requiredEverywhere = occurrences.length === perVariant.length && occurrences.every((o) => o.prop.required); const requiredInItsVariants = !requiredEverywhere && occurrences.every((o) => o.prop.required); const base = occurrences[0].prop; @@ -299,7 +329,7 @@ function collectVariantProps(propsTypeNames, project, byId, warnings, slug, cont return merged; } -function extractCssVariables(directory, warnings, slug) { +function extractCssVariables(directory, cssSources, warnings, slug) { // No directory - the entry documents an API, not a styled component. if (!directory) return []; @@ -316,12 +346,18 @@ function extractCssVariables(directory, warnings, slug) { const files = globSync('**/*.css', { cwd: abs }) .filter((file) => !nestedPrefixes.some((prefix) => file.startsWith(prefix))) - .sort(); + .sort() + .map((file) => path.resolve(abs, file)); + for (const source of cssSources ?? []) { + const sourcePath = path.resolve(uiSource, source); + if (existsSync(sourcePath)) files.push(sourcePath); + else warnings.push(`"${slug}": CSS source ${source} does not exist`); + } const seen = new Set(); const variables = []; for (const file of files) { - const css = readFileSync(path.resolve(abs, file), 'utf8'); - const re = /(--ax-public-[\w-]+)\s*:\s*([^;]*?)(?:\/\*\s*(.*?)\s*\*\/)?\s*;/g; + const css = readFileSync(file, 'utf8'); + const re = /(--wb-public-[\w-]+)\s*:\s*([^;]*?)(?:\/\*\s*(.*?)\s*\*\/)?\s*;/g; let m; while ((m = re.exec(css))) { if (seen.has(m[1])) continue; @@ -349,7 +385,7 @@ function readTokenValues() { if (!existsSync(tokenDistribution)) return values; for (const file of globSync('*.css', { cwd: tokenDistribution })) { const css = readFileSync(path.resolve(tokenDistribution, file), 'utf8'); - for (const [, name, value] of css.matchAll(/(--ax-[\w-]+)\s*:\s*([^;]+);/g)) { + for (const [, name, value] of css.matchAll(/(--wb-ds-[\w-]+)\s*:\s*([^;]+);/g)) { if (!values.has(name)) values.set(name, value.trim()); } } @@ -374,9 +410,9 @@ async function main() { let props = []; const context = { warnings, slug: component.slug }; if (Array.isArray(component.propsType)) { - props = [...collectVariantProps(component.propsType, project, byId, warnings, component.slug, context).values()].sort( - (a, b) => a.name.localeCompare(b.name), - ); + props = [ + ...collectVariantProps(component.propsType, project, byId, warnings, component.slug, context).values(), + ].sort((a, b) => a.name.localeCompare(b.name)); } else if (component.propsType) { const typeNode = findTypeByName(project, component.propsType, warnings); if (typeNode) { @@ -392,7 +428,7 @@ async function main() { name: component.name, props, nativeElement: context.nativeElement ?? null, - cssVariables: extractCssVariables(component.dir, warnings, component.slug), + cssVariables: extractCssVariables(component.dir, component.cssSources, warnings, component.slug), }; } diff --git a/apps/docs/scripts/ui-components.mjs b/apps/docs/scripts/ui-components.mjs index 03f4d47a1..7e9c65c5a 100644 --- a/apps/docs/scripts/ui-components.mjs +++ b/apps/docs/scripts/ui-components.mjs @@ -12,24 +12,31 @@ export const COMPONENTS = [ { slug: 'accordion', name: 'Accordion', propsType: 'AccordionProps', dir: 'accordion' }, { slug: 'avatar', name: 'Avatar', propsType: 'AvatarProps', dir: 'avatar' }, - // No single props type - one of three variants depending on `children`. - { - slug: 'button', - name: 'Button', - propsType: ['LabelButtonProps', 'IconButtonProps', 'IconLabelButtonProps'], - dir: 'button', - }, + { slug: 'button', name: 'Button', propsType: ['LabelButtonProps', 'IconButtonProps'], dir: 'button' }, { slug: 'checkbox', name: 'Checkbox', propsType: 'CheckboxProps', dir: 'checkbox' }, + { slug: 'chip', name: 'Chip', propsType: 'ChipProps', dir: 'chip' }, { slug: 'collapsible', name: 'Collapsible', propsType: 'CollapsibleProps', dir: 'collapsible' }, { slug: 'date-picker', name: 'DatePicker', propsType: 'DatePickerProps', dir: 'date-picker' }, { slug: 'icon-switch', name: 'IconSwitch', propsType: 'IconSwitchProps', dir: 'switch/icon-switch' }, - { slug: 'input', name: 'Input', propsType: 'InputProps', dir: 'input' }, + { + slug: 'input', + name: 'Input', + propsType: 'InputProps', + dir: 'input', + cssSources: [ + 'shared/components/field/field.module.css', + 'shared/styles/field-control-height.module.css', + 'shared/styles/field-control-size.module.css', + ], + }, { slug: 'menu', name: 'Menu', propsType: 'MenuProps', dir: 'menu' }, + // Documented on the Menu page; styled through the NavButton variables. + { slug: 'menu-trigger-button', name: 'Menu.TriggerButton', propsType: 'MenuTriggerButtonProps', dir: null }, { slug: 'modal', name: 'Modal', propsType: 'ModalProps', dir: 'modal' }, { slug: 'nav-button', name: 'NavButton', - propsType: ['NavLabelButtonProps', 'NavIconButtonProps', 'NavIconLabelButtonProps'], + propsType: ['NavLabelButtonProps', 'NavIconButtonProps'], dir: 'button/nav-button', }, { slug: 'radio', name: 'Radio', propsType: 'RadioProps', dir: 'radio-button' }, @@ -44,7 +51,13 @@ export const COMPONENTS = [ { slug: 'snackbar', name: 'Snackbar', propsType: 'SnackbarProps', dir: 'snackbar' }, { slug: 'status', name: 'Status', propsType: 'StatusProps', dir: 'status' }, { slug: 'switch', name: 'Switch', propsType: 'BaseSwitchProps', dir: 'switch' }, - { slug: 'text-area', name: 'TextArea', propsType: 'TextAreaProps', dir: 'text-area' }, + { + slug: 'text-area', + name: 'TextArea', + propsType: 'TextAreaProps', + dir: 'text-area', + cssSources: ['shared/components/field/field.module.css', 'shared/styles/field-control-size.module.css'], + }, { slug: 'tooltip', name: 'Tooltip', propsType: 'TooltipProps', dir: 'tooltip' }, // Diagram components. { slug: 'node-icon', name: 'NodeIcon', propsType: 'NodeIconProps', dir: 'node/node-icon' }, diff --git a/apps/docs/src/components/ui-examples/button.tsx b/apps/docs/src/components/ui-examples/button.tsx index c2e5a32e9..8b2ec24cb 100644 --- a/apps/docs/src/components/ui-examples/button.tsx +++ b/apps/docs/src/components/ui-examples/button.tsx @@ -1,11 +1,35 @@ +import { Plus, X } from '@phosphor-icons/react'; import { Button } from '@workflowbuilder/ui'; import { ComponentPreview } from './component-preview'; +const SOLID_VARIANTS = ['primary', 'secondary', 'critical', 'success'] as const; +const GHOST_VARIANTS = ['ghost-primary', 'ghost-secondary', 'ghost-critical', 'ghost-success'] as const; + export function ButtonExample() { return ( - + + + {SOLID_VARIANTS.map((variant) => ( + + ))} + + + {GHOST_VARIANTS.map((variant) => ( + + ))} + + + + + ); } diff --git a/apps/docs/src/components/ui-examples/chip.tsx b/apps/docs/src/components/ui-examples/chip.tsx new file mode 100644 index 000000000..3c0f0cc2b --- /dev/null +++ b/apps/docs/src/components/ui-examples/chip.tsx @@ -0,0 +1,17 @@ +import { Tag } from '@phosphor-icons/react'; +import { Chip } from '@workflowbuilder/ui'; + +import { ComponentPreview } from './component-preview'; + +export function ChipExample() { + return ( + +
+ + + } /> + {}} /> +
+
+ ); +} diff --git a/apps/docs/src/components/ui-examples/component-preview.module.css b/apps/docs/src/components/ui-examples/component-preview.module.css index be646fae6..65917fb34 100644 --- a/apps/docs/src/components/ui-examples/component-preview.module.css +++ b/apps/docs/src/components/ui-examples/component-preview.module.css @@ -3,16 +3,15 @@ width: 100%; max-width: 50rem; aspect-ratio: 2 / 1; - /* The ratio is a preferred size, not a clamp: overflow:hidden zeroes the - content-based automatic minimum, so restore it - oversized examples grow - the stage instead of being clipped. */ - min-height: fit-content; margin-top: 1rem; padding: 1rem; display: flex; align-items: center; justify-content: center; - overflow: hidden; + /* `clip`, not `hidden`: a scroll container has no content-based automatic + minimum, which would turn the ratio into a hard clamp and cut tall + examples. `clip` still hides the spotlight's negative margins. */ + overflow: clip; border: 1px solid var(--sl-color-gray-5); border-radius: 0.5rem; background-color: var(--sl-color-bg); diff --git a/apps/docs/src/components/ui-examples/component-preview.tsx b/apps/docs/src/components/ui-examples/component-preview.tsx index 5ca9ad289..45ccd5a35 100644 --- a/apps/docs/src/components/ui-examples/component-preview.tsx +++ b/apps/docs/src/components/ui-examples/component-preview.tsx @@ -1,16 +1,28 @@ -import componentCss from '@workflowbuilder/ui/index.css?raw'; -import globalCss from '@workflowbuilder/ui/styles.css?raw'; import { type ReactNode, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import styles from './component-preview.module.css'; +import previewCss from '../../../../../packages/ui/tmp/docs-preview.css?raw'; + // Examples render in a shadow root so Starlight's rules cannot reach them and // the library's cannot leak out. Inherited and custom properties still cross // the boundary - that is how the docs theme reaches the examples. Inside a // shadow root `:root` matches nothing, hence the retarget to `:host`. -const shadowCss = `${`${globalCss}\n${componentCss}`.replaceAll(':root', ':host')} -:host > :not(style) { max-width: 100%; }`; +// The boundary also stops the docs' own stylesheets, so example layout lives +// here as `Stack` and `Row` rather than in per-example CSS modules. +const shadowCss = `${previewCss.replaceAll(':root', ':host')} +:host > :not(style) { max-width: 100%; } +.stack { display: flex; flex-direction: column; gap: var(--wb-ds-space-150); } +.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--wb-ds-space-100); }`; + +function Stack({ children }: { children: ReactNode }) { + return
{children}
; +} + +function Row({ children }: { children: ReactNode }) { + return
{children}
; +} export function ComponentPreview({ children }: { children: ReactNode }) { const hostRef = useRef(null); @@ -36,3 +48,6 @@ export function ComponentPreview({ children }: { children: ReactNode }) {
); } + +ComponentPreview.Stack = Stack; +ComponentPreview.Row = Row; diff --git a/apps/docs/src/components/ui-examples/input.tsx b/apps/docs/src/components/ui-examples/input.tsx index f1d4cc4b2..321ba3ff1 100644 --- a/apps/docs/src/components/ui-examples/input.tsx +++ b/apps/docs/src/components/ui-examples/input.tsx @@ -1,14 +1,48 @@ +import { MagnifyingGlass } from '@phosphor-icons/react'; import { Input } from '@workflowbuilder/ui'; import { useState } from 'react'; import { ComponentPreview } from './component-preview'; export function InputExample() { - const [value, setValue] = useState(''); + const [search, setSearch] = useState(''); + const [projectName, setProjectName] = useState(''); + const [displayName, setDisplayName] = useState(''); return ( - setValue(event.target.value)} /> + + } + placeholder="Large input" + value={search} + onChange={(event) => setSearch(event.target.value)} + onClear={() => setSearch('')} + clearLabel="Clear search" + /> + setProjectName(event.target.value)} + /> + setDisplayName(event.target.value)} + /> + + ); } diff --git a/apps/docs/src/components/ui-examples/menu.tsx b/apps/docs/src/components/ui-examples/menu.tsx index 8583d4787..4922322f7 100644 --- a/apps/docs/src/components/ui-examples/menu.tsx +++ b/apps/docs/src/components/ui-examples/menu.tsx @@ -1,20 +1,33 @@ +import { DotsThreeVertical } from '@phosphor-icons/react'; import { Button, Menu } from '@workflowbuilder/ui'; import { ComponentPreview } from './component-preview'; +const ITEMS = [ + { label: 'Edit', onClick: () => {} }, + { label: 'Duplicate', onClick: () => {} }, + { type: 'separator' as const }, + { label: 'Delete', tone: 'critical' as const, onClick: () => {} }, +]; + export function MenuExample() { return ( - {} }, - { label: 'Duplicate', onClick: () => {} }, - { type: 'separator' }, - { label: 'Delete', destructive: true, onClick: () => {} }, - ]} - > + ); } + +export function MenuTriggerButtonExample() { + return ( + + + + + + + + ); +} diff --git a/apps/docs/src/components/ui-examples/nav-button.tsx b/apps/docs/src/components/ui-examples/nav-button.tsx index edba42241..24fa6e958 100644 --- a/apps/docs/src/components/ui-examples/nav-button.tsx +++ b/apps/docs/src/components/ui-examples/nav-button.tsx @@ -1,11 +1,28 @@ -import { NavButton } from '@workflowbuilder/ui'; +import { ArrowRight, House, Plus } from '@phosphor-icons/react'; +import { NAV_BUTTON_SIZES, NavButton } from '@workflowbuilder/ui'; import { ComponentPreview } from './component-preview'; export function NavButtonExample() { return ( - Nav button + + + }>Square + }> + Round + + } /> + } suffixIcon={}> + Selected + + + + {NAV_BUTTON_SIZES.map((size) => ( + } /> + ))} + + ); } diff --git a/apps/docs/src/components/ui-examples/status.tsx b/apps/docs/src/components/ui-examples/status.tsx index 91f76bd50..4c3f25a11 100644 --- a/apps/docs/src/components/ui-examples/status.tsx +++ b/apps/docs/src/components/ui-examples/status.tsx @@ -14,7 +14,7 @@ export function StatusExample() { display: 'inline-flex', alignItems: 'center', padding: '0.875rem 1.25rem', - border: '1px solid var(--ax-ui-stroke-primary-default, #3a3a3a)', + border: '1px solid var(--wb-ds-ui-stroke-default)', borderRadius: '0.5rem', }} > diff --git a/apps/docs/src/components/ui-examples/text-area.tsx b/apps/docs/src/components/ui-examples/text-area.tsx index d0d45749d..605ee0f4c 100644 --- a/apps/docs/src/components/ui-examples/text-area.tsx +++ b/apps/docs/src/components/ui-examples/text-area.tsx @@ -4,11 +4,33 @@ import { useState } from 'react'; import { ComponentPreview } from './component-preview'; export function TextAreaExample() { - const [value, setValue] = useState(''); + const [description, setDescription] = useState(''); + const [summary, setSummary] = useState(''); return ( -