diff --git a/.changeset/canvas-node-geometry.md b/.changeset/canvas-node-geometry.md new file mode 100644 index 000000000..8792e7a6e --- /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. Overrides of the public node variables keep working unchanged. diff --git a/.changeset/connectable-item-width.md b/.changeset/connectable-item-width.md new file mode 100644 index 000000000..e393c0299 --- /dev/null +++ b/.changeset/connectable-item-width.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Decision branch rows and AI tool rows cap their width to the space their section actually offers, so long labels truncate at the section edge instead of overflowing it. diff --git a/DECISION-LOGS.md b/DECISION-LOGS.md index cc8cddf5c..7944eebb7 100644 --- a/DECISION-LOGS.md +++ b/DECISION-LOGS.md @@ -22,4 +22,6 @@ - _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_: [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/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css b/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css index 1f00a8753..79cf17881 100644 --- a/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css +++ b/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css @@ -1,4 +1,6 @@ .container { + --wb-sdk-connectable-item-inset: calc(0.625rem + 1px); + display: flex; flex-direction: column; gap: 0.75rem; diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md new file mode 100644 index 000000000..cfc7b3331 --- /dev/null +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md @@ -0,0 +1,69 @@ +### Title: Derive the ConnectableItem width from the real container insets + +### Proposed by: Jan Librowski + +### Date: 07.09.2026 + +## Context + +`ConnectableItem` (a node body row that carries its own port: Decision branches, AI tools) caps its +width with an absolute `max-width` computed from the public node width. The cap is needed because the +Decision template sets `min-width: max-content` on its body, so a long branch label would otherwise +widen the whole node instead of truncating. + +The previous rule was: + +```css +max-width: calc( + var(--wb-public-node-width) - (6 * var(--wb-public-node-padding)) + 2 * + var(--wb-sdk-connectable-item-horizontal-padding) + 2 * var(--wb-sdk-connectable-item-border-width) +); +``` + +Two problems surfaced while moving the node shell to the DS 2.0 geometry (width 241px): + +- The `6 *` factor is not documented anywhere. It only approximates the real horizontal insets + between the node edge and the item: shell padding plus border on both sides (2 x 9px) and the + section padding plus border on both sides (2 x 11px), 40px in total against the 48px it subtracts. +- The `+ 2 * padding + 2 * border` terms assume content-box sizing. The SDK applies a global + `box-sizing: border-box` reset (`packages/sdk/src/index.css`), so `max-width` already refers to + the border box and the terms inflate the cap: at 241px the old rule allows 219px while only 201px + are available inside a section, so a long label could overflow its section by 18px. + +The design system does not specify a width for these rows. Any cap is therefore a provisional +implementation decision, to be revisited when the design publishes one +(follow-up: connectable-item-design-width). + +## Decision + +The cap is derived from named insets between the node's outer edge and the item: + +```css +max-width: calc( + var(--wb-public-node-width) - 2 * (var(--wb-public-node-padding) + var(--wb-public-node-border-size)) - 2 * + var(--wb-sdk-connectable-item-inset) +); +``` + +`--wb-sdk-connectable-item-inset` is the horizontal inset (one side) added by the container that +wraps the items. It defaults to `0rem` and each wrapping container declares its own value: + +- `NodeSection` sets it to its padding plus border width, so Decision branches inside a section get + `241 - 2 x (8 + 1) - 2 x (10 + 1) = 201px` (with the previous 258px shell: 218px). +- The AI template wraps its tool rows in `NodeInfoWrapper` (padding 0.625rem plus a 1px border + per side), which therefore declares the same inset, so tool rows get + `241 - 2 x (8 + 1) - 2 x (10 + 1) = 201px` as well. The default `0rem` applies only to a + container that adds no horizontal padding. + +The variable is not cumulative: a wrapper declares the inset it adds itself, and a container that +adds horizontal padding without declaring it lets its rows exceed the visible width by that padding. + +## Consequences + +- Item width follows the shell geometry exactly and can no longer exceed the space its container + actually offers. +- The variable makes the nesting explicit and reviewable per container instead of encoding it in a + single global multiplier. +- Provisional until the design specifies the row width (follow-up: connectable-item-design-width). + The design's node body matrix (row padding 8px, radius 4px) is a separate change and does not + alter this derivation. diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css index ccf0e43b1..288ceb344 100644 --- a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css @@ -5,6 +5,7 @@ --wb-sdk-connectable-item-border-width: 0.0625rem; --wb-sdk-connectable-item-border-color: var(--wb-ds-components-text-field-stroke-default); --wb-sdk-connectable-item-border-radius: 0.375rem; + --wb-sdk-connectable-item-inset: 0rem; } .connectable-item { @@ -13,8 +14,8 @@ position: relative; display: flex; max-width: calc( - var(--wb-public-node-width) - (6 * var(--wb-public-node-padding)) + 2 * - var(--wb-sdk-connectable-item-horizontal-padding) + 2 * var(--wb-sdk-connectable-item-border-width) + var(--wb-public-node-width) - 2 * (var(--wb-public-node-padding) + var(--wb-public-node-border-size)) - 2 * + var(--wb-sdk-connectable-item-inset) ); padding: var(--wb-sdk-connectable-item-vertical-padding) var(--wb-sdk-connectable-item-horizontal-padding); background-color: var(--wb-sdk-connectable-item-background); diff --git a/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css b/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css index 2bb941f45..b293a982f 100644 --- a/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css +++ b/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css @@ -7,6 +7,8 @@ } .container { + --wb-sdk-connectable-item-inset: calc(var(--wb-sdk-node-section-padding) + var(--wb-sdk-node-section-border-width)); + display: flex; flex-direction: column; gap: var(--wb-sdk-node-section-gap); diff --git a/packages/ui/src/components/node/node-panel/node-panel.module.css b/packages/ui/src/components/node/node-panel/node-panel.module.css index e08fc7909..4a8e962f0 100644 --- a/packages/ui/src/components/node/node-panel/node-panel.module.css +++ b/packages/ui/src/components/node/node-panel/node-panel.module.css @@ -1,10 +1,10 @@ :root { - --wb-public-node-width: 16.125rem; + --wb-public-node-width: 241px; --wb-public-node-height: 100%; --wb-public-node-border-size: 0.0625rem; - --wb-public-node-padding: var(--wb-ds-space-100); - --wb-public-node-gap: var(--wb-ds-space-100); + --wb-public-node-padding: var(--wb-ds-canvas-node-head-h-pad); + --wb-public-node-gap: var(--wb-ds-canvas-node-head-gap); --wb-public-node-border-radius: var(--wb-ds-canvas-node-head-radius); --wb-public-node-border-color: var(--wb-ds-canvas-node-stroke-default);