diff --git a/.changeset/canvas-node-geometry.md b/.changeset/canvas-node-geometry.md new file mode 100644 index 000000000..f26ede7a7 --- /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 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 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..451976080 --- /dev/null +++ b/.changeset/connectable-item-width.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Decision branch rows and AI tool rows derive their width cap from the node shell and their container insets, so long labels keep the full available width after the node shell spacing change instead of truncating early. 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..c361456b1 --- /dev/null +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md @@ -0,0 +1,68 @@ +### 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 items (register: node geometry gaps). Any cap +is therefore a provisional implementation decision, to be revisited when the design provides one. + +## 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 item width; recorded as a decision made without a + design in the DS 2.0 divergence register. The design's node body matrix (row padding 8px, + radius 4px) is a separate follow-up and does not change 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);