Files
fusion/packages/dashboard/app/components/WorkflowNodeEditor.css
gsxdsm 6ff930e2f2 FN-6701: theme workflow editor controls
Theme workflow editor controls and checkbox affordances with Fusion tokens.

- Add scoped React Flow zoom control and mini-map theme overrides for the workflow editor canvas.
- Apply the workflow accent token to workflow settings, field, and column-trait checkboxes.
- Cover the CSS contract with assertions against hardcoded white backgrounds and missing token usage.

Files changed:
 .../app/components/WorkflowFieldsPanel.css         |  8 +++
 .../app/components/WorkflowNodeEditor.css          | 55 +++++++++++++++++++
 .../app/components/WorkflowSettingsPanel.css       |  8 +++
 .../__tests__/WorkflowNodeEditor.css.test.ts       | 64 ++++++++++++++++++++++
 4 files changed, 135 insertions(+)

Fusion-Task-Id: FN-6701

Fusion-Task-Lineage: e2e510ca-e1f7-408b-b113-ec30c9a3763a
2026-06-19 05:31:47 -07:00

2019 lines
43 KiB
CSS

.wf-editor-modal {
--wf-editor-touch-target: calc(var(--space-xl) + var(--space-lg) + var(--space-xs));
display: flex;
flex-direction: column;
width: min(1200px, 95vw);
height: min(820px, 92vh);
min-width: 640px;
min-height: 480px;
resize: both;
overflow: hidden;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-md);
}
.wf-create-modal {
--wf-editor-touch-target: calc(var(--space-xl) + var(--space-lg) + var(--space-xs));
}
.wf-editor-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-md);
border-bottom: 1px solid var(--border);
}
.wf-editor-header h2 {
margin: 0;
font-size: 1rem;
color: var(--text);
}
.wf-editor-close {
display: inline-flex;
align-items: center;
background: transparent;
border: none;
color: var(--text-muted);
cursor: pointer;
}
/* U2/R5: one-time legacy-step migration notice banner. */
.wf-migration-notice {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
padding: var(--space-sm) var(--space-md);
background: color-mix(in srgb, var(--accent) 12%, transparent);
border-bottom: 1px solid var(--border);
color: var(--text);
font-size: 0.85rem;
}
.wf-migration-notice-text {
flex: 1;
}
.wf-migration-notice-dismiss {
display: inline-flex;
align-items: center;
background: transparent;
border: none;
color: var(--text-muted);
cursor: pointer;
flex-shrink: 0;
}
.wf-migration-notice-dismiss:hover {
color: var(--text);
}
.wf-editor-close:hover {
color: var(--text);
}
.wf-editor-body {
display: flex;
flex: 1;
min-width: 0;
min-height: 0;
}
.wf-editor-sidebar {
display: flex;
flex-direction: column;
gap: var(--space-xs);
width: 300px;
min-width: 0;
padding: var(--space-sm);
border-right: 1px solid var(--border);
overflow-x: hidden;
overflow-y: auto;
}
/* U12: columns + fields authoring sections moved into the left sidebar, below
the workflow list. Each is a collapsible disclosure whose toggle button is the
section header; the panels' own internal <h3> is suppressed to avoid a double
header. */
.wf-sidebar-panels {
display: flex;
flex-direction: column;
gap: var(--space-xs);
min-width: 0;
margin-top: var(--space-sm);
padding-top: var(--space-sm);
border-top: 1px solid var(--border);
}
.wf-sidebar-section {
display: flex;
flex-direction: column;
gap: var(--space-xs);
min-width: 0;
}
.wf-sidebar-section-toggle {
display: flex;
align-items: center;
gap: var(--space-xs);
width: 100%;
min-width: 0;
padding: var(--space-xs) var(--space-sm);
background: transparent;
border: none;
border-radius: var(--radius-sm);
color: var(--text);
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
cursor: pointer;
}
.wf-sidebar-section-toggle:hover {
background: var(--bg-secondary);
}
/* When nested in the sidebar disclosure, the panels are stacked full-width
blocks rather than side columns: drop the dedicated width/border and let them
flow inside the sidebar's own scroll. */
.wf-sidebar-section .wf-column-panel,
.wf-sidebar-section .wf-fields-panel {
width: auto;
min-width: 0;
padding: 0 var(--space-xs) var(--space-xs);
border-left: none;
overflow-y: visible;
}
/* The disclosure toggle is the visible section header; hide the panels' own
title heading to avoid a duplicate. The Add button (also in the header) stays. */
.wf-sidebar-section .wf-column-panel-header h3,
.wf-sidebar-section .wf-fields-panel-header h3 {
display: none;
}
.wf-sidebar-section .wf-column-panel-header,
.wf-sidebar-section .wf-fields-panel-header {
justify-content: flex-end;
}
.wf-editor-new {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
cursor: pointer;
}
.wf-editor-new:hover {
background: var(--bg-tertiary);
}
/* U5/R10: sidebar import affordance + persistent inline error/warning regions. */
.wf-editor-import {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
cursor: pointer;
}
.wf-editor-import:hover {
background: var(--bg-tertiary);
}
.wf-editor-import:disabled {
opacity: 0.6;
cursor: default;
}
.wf-editor-import-error {
padding: var(--space-xs) var(--space-sm);
background: color-mix(in srgb, var(--ws-error) 8%, var(--bg-secondary));
border: 1px solid var(--ws-error);
border-radius: var(--radius-sm);
color: var(--ws-error);
font-size: 0.8rem;
}
.wf-editor-import-warnings {
padding: var(--space-xs) var(--space-sm);
background: color-mix(in srgb, var(--ws-warning) 8%, var(--bg-secondary));
border: 1px solid var(--ws-warning);
border-radius: var(--radius-sm);
color: var(--ws-warning);
font-size: 0.8rem;
}
.wf-editor-import-warning {
margin: 0;
}
.wf-editor-import-warning + .wf-editor-import-warning {
margin-top: var(--space-xs);
}
.wf-editor-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.wf-editor-list li {
min-width: 0;
}
.wf-editor-list-item {
width: 100%;
min-width: 0;
overflow: hidden;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
padding: var(--space-xs) var(--space-sm);
background: transparent;
border: 1px solid transparent;
border-radius: var(--radius-sm);
color: var(--text);
cursor: pointer;
}
.wf-editor-list-item:hover {
background: var(--bg-secondary);
}
.wf-editor-list-item.active {
background: var(--bg-tertiary);
border-color: var(--border);
}
.wf-editor-empty {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-md);
color: var(--text-muted);
font-size: 0.85rem;
}
.wf-editor-select-note {
padding: var(--space-sm) var(--space-md);
background: color-mix(in srgb, var(--ws-info) 8%, var(--bg-secondary));
border: 1px solid var(--ws-info);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 0.85rem;
}
.wf-editor-canvas-wrap {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
.wf-editor-mobile-back {
display: none;
}
.wf-editor-canvas-empty {
flex: 1;
justify-content: center;
}
/* No-workflow onboarding panel (R9): icon + heading + explanation + create CTA. */
.wf-editor-onboard {
flex-direction: column;
text-align: center;
gap: var(--space-sm);
padding: var(--space-lg);
}
.wf-editor-onboard-icon {
color: var(--text-muted);
}
.wf-editor-onboard-title {
margin: 0;
font-size: 1rem;
color: var(--text);
}
.wf-editor-onboard-text {
margin: 0;
max-width: 36ch;
color: var(--text-muted);
}
.wf-editor-onboard-cta {
margin-top: var(--space-xs);
}
/* Trivial-graph palette hint (R9): non-blocking banner over the canvas. */
.wf-trivial-hint {
position: absolute;
top: var(--space-sm);
left: 50%;
transform: translateX(-50%);
z-index: 5;
pointer-events: none;
max-width: min(90%, 42ch);
padding: var(--space-xs) var(--space-sm);
background: color-mix(in srgb, var(--ws-info) 8%, var(--bg-secondary));
border: 1px solid var(--ws-info);
border-radius: var(--radius-sm);
color: var(--ws-info);
font-size: 0.8rem;
text-align: center;
}
.wf-editor-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
padding: var(--space-sm);
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
}
.wf-editor-palette {
display: flex;
gap: var(--space-xs);
flex-wrap: wrap;
min-width: 0;
}
.wf-palette-btn,
.wf-editor-action,
.wf-editor-delete,
.wf-editor-save {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
min-width: 0;
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
cursor: pointer;
overflow-wrap: anywhere;
transition: background var(--transition-fast);
}
.wf-palette-btn:hover,
.wf-editor-action:hover,
.wf-editor-delete:hover {
background: var(--bg-tertiary);
}
.wf-editor-action:disabled {
opacity: 0.6;
cursor: default;
}
.wf-editor-actions {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.wf-editor-readonly-note {
font-size: 0.75rem;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* U9/R8: palette Templates section — collapsible, grouped, filterable. */
.wf-templates {
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding: var(--space-sm);
border-bottom: 1px solid var(--border);
}
.wf-templates-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
}
.wf-templates-toggle {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
background: transparent;
border: none;
border-radius: var(--radius-sm);
color: var(--text);
font-weight: 600;
cursor: pointer;
}
.wf-templates-toggle:hover {
background: var(--bg-tertiary);
}
.wf-templates-filter {
flex: 0 1 220px;
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 0.8rem;
}
.wf-templates-body {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.wf-templates-conflict {
padding: var(--space-xs) var(--space-sm);
background: color-mix(in srgb, var(--ws-error) 8%, var(--bg-secondary));
border: 1px solid var(--ws-error);
border-radius: var(--radius-sm);
color: var(--ws-error);
font-size: 0.8rem;
}
.wf-templates-group {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.wf-templates-group-title {
margin: 0;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-dim);
}
.wf-templates-entries {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.wf-templates-entry {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 0.8rem;
cursor: pointer;
transition: background var(--transition-fast);
}
.wf-templates-entry:hover {
background: var(--bg-tertiary);
}
.wf-templates-entry:disabled {
opacity: 0.6;
cursor: default;
}
.wf-templates-badge {
padding: 0 var(--space-xs);
background: color-mix(in srgb, var(--accent) 12%, transparent);
border-radius: var(--radius-sm);
color: var(--text-muted);
font-size: 0.7rem;
}
/* Neutralize the fieldset wrapper so it only gates interactivity, not layout. */
.wf-inspector-fields {
display: contents;
border: none;
margin: 0;
padding: 0;
min-inline-size: 0;
}
.wf-editor-save {
background: var(--todo);
border-color: var(--todo);
color: var(--bg);
}
.wf-editor-save:disabled {
opacity: 0.6;
cursor: default;
}
.wf-editor-delete {
color: var(--ws-error);
}
/* Inspector delete buttons (U3): sit below the field group, sized to the panel. */
.wf-inspector-delete {
margin-top: var(--space-sm);
justify-content: center;
}
.wf-editor-banner {
padding: var(--space-sm) var(--space-md);
background: var(--bg-secondary);
border-bottom: 1px solid var(--ws-warning);
color: var(--ws-warning);
font-size: 0.85rem;
}
/* Info-tone banner (KTD-4): branching graph runs on the interpreter only — not a
* failure, so it uses the info token rather than the warning treatment. */
.wf-editor-banner--info {
border-bottom-color: var(--ws-info);
color: var(--ws-info);
background: color-mix(in srgb, var(--ws-info) 6%, var(--bg-secondary));
}
.wf-editor-canvas {
flex: 1;
width: 100%;
min-width: 0;
min-height: 0;
overflow: hidden;
position: relative;
}
/*
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
React Flow ships white default controls and mini-map chrome, but the workflow editor must inherit Fusion theme tokens in both dark and light modes. Scope overrides to the canvas so other React Flow consumers keep their local contracts.
*/
.wf-editor-canvas .react-flow__controls {
background: var(--surface);
border: var(--btn-border-width) solid var(--border);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-sm);
color: var(--text);
overflow: hidden;
}
.wf-editor-canvas .react-flow__controls-button {
background: var(--surface);
border: 0;
border-bottom: var(--btn-border-width) solid var(--border);
color: var(--text);
fill: currentColor;
}
.wf-editor-canvas .react-flow__controls-button:hover {
background: var(--surface-hover);
color: var(--text);
}
.wf-editor-canvas .react-flow__controls-button svg {
fill: currentColor;
stroke: currentColor;
}
.wf-editor-canvas .react-flow__minimap {
background: var(--surface);
border: var(--btn-border-width) solid var(--border);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-sm);
}
.wf-editor-canvas .react-flow__minimap-node {
fill: var(--bg-secondary);
stroke: var(--border);
}
.wf-editor-canvas .react-flow__minimap-mask {
fill: color-mix(in srgb, var(--surface) 70%, transparent);
}
.wf-mobile-shell {
display: none;
}
.wf-layout-toggle {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
width: fit-content;
min-height: 30px;
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-muted);
font: inherit;
font-size: 0.8rem;
cursor: pointer;
}
.wf-layout-toggle:hover {
background: var(--bg-tertiary);
color: var(--text);
}
.wf-layout-toggle:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.wf-editor-body--simple-layout :where(.wf-editor-readonly-banner),
.wf-editor-body--simple-layout :where(.wf-editor-toolbar),
.wf-editor-body--simple-layout :where(.wf-templates),
.wf-editor-body--simple-layout :where(.wf-editor-canvas) {
display: none;
}
.wf-editor-body--simple-layout .wf-mobile-shell {
display: flex;
flex: 1 1 auto;
min-height: 0;
flex-direction: column;
border-top: 1px solid var(--border);
overflow: hidden;
}
.wf-mobile-tabs {
display: flex;
flex: 0 0 auto;
gap: var(--space-xs);
padding: var(--space-sm);
overflow-x: auto;
overflow-y: visible;
border-bottom: 1px solid var(--border);
}
.wf-mobile-tab {
flex: 0 0 auto;
min-height: var(--wf-editor-touch-target);
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text);
cursor: pointer;
transition:
background var(--transition-fast),
transform var(--transition-fast),
box-shadow var(--transition-fast);
}
.wf-mobile-tab--active {
border-color: var(--accent, var(--ws-info));
background: var(--bg-tertiary);
}
.wf-mobile-tab:hover {
background: var(--bg-tertiary);
}
.wf-mobile-tab:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.wf-mobile-tab:active {
transform: scale(0.97);
}
.wf-mobile-panel {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
.wf-mobile-add,
.wf-mobile-actions,
.wf-mobile-destination {
display: flex;
flex-direction: column;
gap: var(--space-sm);
padding: var(--space-sm);
}
.wf-mobile-add-section {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.wf-mobile-add-section h3,
.wf-mobile-template-group h4 {
margin: 0;
color: var(--text);
font-size: 0.85rem;
}
.wf-mobile-add-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-xs);
}
.wf-mobile-add-option,
.wf-mobile-template-option {
display: inline-flex;
align-items: center;
justify-content: flex-start;
gap: var(--space-xs);
min-width: 0;
min-height: var(--wf-editor-touch-target);
padding: var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text);
cursor: pointer;
text-align: left;
overflow-wrap: anywhere;
transition:
background var(--transition-fast),
transform var(--transition-fast),
box-shadow var(--transition-fast);
}
.wf-mobile-add-option:hover,
.wf-mobile-template-option:hover {
background: var(--bg-tertiary);
}
.wf-mobile-add-option:focus-visible,
.wf-mobile-template-option:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.wf-mobile-add-option:active,
.wf-mobile-template-option:active {
transform: scale(0.97);
}
.wf-mobile-template-filter {
width: 100%;
}
.wf-mobile-template-group {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.wf-mobile-actions .wf-editor-action,
.wf-mobile-actions .wf-editor-delete,
.wf-mobile-actions .wf-editor-save {
justify-content: center;
min-height: var(--wf-editor-touch-target);
}
.wf-mobile-ai-panel {
position: static;
inset: auto;
width: 100%;
box-shadow: none;
}
.wf-editor-inspector {
display: flex;
flex-direction: column;
gap: var(--space-sm);
width: 280px;
padding: var(--space-md);
border-left: 1px solid var(--border);
overflow-y: auto;
}
.wf-editor-inspector h3 {
margin: 0;
font-size: 0.9rem;
color: var(--text);
}
.wf-inspector-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
}
.wf-inspector-toggle {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text);
font: inherit;
cursor: pointer;
}
.wf-inspector-toggle:hover {
background: var(--bg-tertiary);
}
.wf-inspector-toggle--collapsed {
position: absolute;
right: var(--space-sm);
bottom: var(--space-sm);
z-index: 5;
box-shadow: var(--shadow-sm);
}
.wf-field {
display: flex;
flex-direction: column;
gap: var(--space-xs);
font-size: 0.8rem;
color: var(--text-muted);
}
.wf-field input,
.wf-field textarea,
.wf-field select {
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font: inherit;
}
.wf-field input:focus,
.wf-field textarea:focus,
.wf-field select:focus {
outline: none;
box-shadow: var(--focus-ring);
}
/* ── Prompt fullscreen editor ─────────────────────────────────── */
.wf-prompt-editor {
position: relative;
}
.wf-prompt-editor--fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10000;
background: var(--surface);
padding: max(var(--space-lg), env(safe-area-inset-top, 0))
max(var(--space-lg), env(safe-area-inset-right, 0))
max(var(--space-lg), env(safe-area-inset-bottom, 0))
max(var(--space-lg), env(safe-area-inset-left, 0));
display: flex;
flex-direction: column;
}
.wf-prompt-editor--fullscreen .wf-field {
flex: 1;
display: flex;
flex-direction: column;
}
.wf-prompt-editor--fullscreen textarea {
flex: 1;
min-height: unset;
resize: none;
border-radius: var(--radius-md);
font-size: 0.9375rem;
line-height: 1.6;
}
.wf-prompt-fullscreen-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--space-md);
font-size: 0.8125rem;
font-weight: 600;
color: var(--text-muted);
}
.wf-prompt-expand-btn {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
}
.wf-prompt-expand-btn--inline {
position: absolute;
top: calc(var(--space-xs) + 1.2em + var(--space-xs));
right: var(--space-xs);
z-index: 2;
color: var(--text-muted);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: calc(var(--space-xs) / 2) var(--space-xs);
}
.wf-prompt-expand-btn--inline:hover {
background: var(--bg-tertiary);
color: var(--text);
}
.wf-prompt-editor:not(.wf-prompt-editor--fullscreen) textarea {
padding-right: calc(var(--space-xl) + var(--space-md));
}
.wf-inspector-note {
margin: 0;
font-size: 0.78rem;
color: var(--text-dim);
}
.wf-inspector-note--info {
color: var(--ws-warning);
}
.wf-field--checkbox {
flex-direction: row;
align-items: center;
gap: var(--space-sm);
cursor: pointer;
}
.wf-field--checkbox input[type="checkbox"] {
width: auto;
padding: 0;
margin: 0;
background: none;
border: none;
accent-color: var(--todo);
cursor: pointer;
}
/* Canvas nodes */
/* ── U1: card-style nodes ──
* Cards have a header row (icon + label + badges + error badge) and an
* independent config-summary row. Sizing mirrors WF_CARD_WIDTH /
* WF_CARD_MAX_WIDTH in workflow-flow-mapping.ts; the max-width ceiling forces
* long labels/summaries to truncate rather than grow the canvas. Per-kind
* accent uses a left border + a faint header tint over a semantic token. */
.wf-node {
display: flex;
flex-direction: column;
gap: 2px;
box-sizing: border-box;
width: 200px;
max-width: 240px;
padding: var(--space-xs) var(--space-sm);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-left: 3px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 0.8rem;
}
.wf-node-header {
display: flex;
align-items: center;
gap: var(--space-xs);
min-width: 0;
}
/* Summary row: single line, truncates independently of the header. */
.wf-node-summary {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.68rem;
color: var(--text-muted);
}
/* Per-kind accent: left border color + a faint header tint. color-mix over
* semantic tokens keeps both themes legible without raw colors. */
.wf-node-start {
border-left-color: var(--ws-success);
}
.wf-node-end {
border-left-color: var(--text-muted);
}
.wf-node-prompt {
border-left-color: var(--accent, var(--todo));
background: color-mix(in srgb, var(--accent, var(--todo)) 5%, var(--bg-secondary));
}
.wf-node-script {
border-left-color: var(--text-muted);
font-family: var(--font-mono, monospace);
}
.wf-node-gate {
border-left-color: var(--ws-warning);
background: color-mix(in srgb, var(--ws-warning) 6%, var(--bg-secondary));
}
.wf-node-hold {
border-left-color: var(--ws-warning);
}
.wf-node-split,
.wf-node-join {
border-left-color: var(--accent, var(--ws-info));
}
.wf-node-merge {
border-left-color: var(--ws-info);
border-style: dashed;
}
/* ── Step-inversion nodes (KTD-3/4/12/15, U8) ── */
.wf-node-step-execute {
border-left-color: var(--accent, var(--ws-info));
background: color-mix(in srgb, var(--accent, var(--ws-info)) 5%, var(--bg-secondary));
}
.wf-node-step-review {
border-left-color: var(--ws-info);
background: color-mix(in srgb, var(--ws-info) 6%, var(--bg-secondary));
}
.wf-node-parse-steps {
border-left-color: var(--ws-info);
}
.wf-node-code {
border-left-color: var(--text-muted);
font-family: var(--font-mono, monospace);
}
/* A foreach renders as a React Flow group node containing its template
* subgraph. Children are positioned inside the group's box. */
.wf-foreach-group {
width: 100%;
height: 100%;
box-sizing: border-box;
border: 1px dashed var(--accent, var(--ws-info));
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--accent, var(--ws-info)) 6%, transparent);
padding: var(--space-xs);
}
.wf-foreach-group.wf-node--error {
border-color: var(--ws-error);
}
.wf-foreach-header {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
font-size: 0.8rem;
color: var(--text);
}
.wf-foreach-empty {
margin-top: var(--space-sm);
padding: var(--space-sm);
border: 1px dashed var(--border);
border-radius: var(--radius-sm);
font-size: 0.7rem;
color: var(--text-muted);
text-align: center;
}
/* Rework edges (KTD-5): dashed accent stroke with a loop affordance. */
.wf-edge-rework .react-flow__edge-path {
stroke: var(--accent, var(--ws-info));
stroke-dasharray: 5 4;
stroke-width: 2;
}
/* Failure edges (R2): a distinct dash pattern from rework plus an error-token
* stroke. Two-channel rule — the condition label is always rendered (third
* channel is color) so failure edges stay distinguishable in low-contrast
* themes. */
.react-flow__edge.wf-edge-failure .react-flow__edge-path {
stroke: var(--ws-error);
stroke-dasharray: 2 4;
stroke-width: 2;
}
.wf-code-source {
font-family: var(--font-mono, monospace);
font-size: 0.72rem;
white-space: pre;
overflow-x: auto;
}
.wf-editor-sidebar .wf-code-source {
overflow-x: hidden;
overflow-wrap: anywhere;
white-space: pre-wrap;
}
/* Header overflow priority (R1): icon fixed-width; label flex-shrinks first
* with ellipsis; badges + error badge hold their width flush right. */
.wf-node-icon {
display: inline-flex;
flex-shrink: 0;
color: var(--text-muted);
}
.wf-node-label {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wf-node-badge {
flex-shrink: 0;
font-size: 0.65rem;
text-transform: uppercase;
padding: 1px var(--space-xs);
border-radius: var(--radius-sm);
background: var(--ws-warning);
color: var(--bg);
}
.wf-spin {
animation: wf-spin 1s linear infinite;
}
@keyframes wf-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* ── U10: swimlane bands, column panel, error badges, read-only banner ── */
.wf-column-band {
border: 1px dashed var(--border);
background: color-mix(in srgb, var(--bg-secondary) 65%, transparent);
border-radius: var(--radius-md);
pointer-events: none;
}
.wf-node--error {
border-color: var(--ws-error);
border-left-color: var(--ws-error);
}
.wf-node-error-badge {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
flex-shrink: 0;
font-size: 0.65rem;
padding: 1px var(--space-xs);
border-radius: var(--radius-sm);
background: var(--ws-error);
color: var(--bg);
}
.wf-editor-readonly-banner {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-md);
padding: var(--space-sm) var(--space-md);
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border);
}
.wf-editor-duplicate-primary {
font-weight: 600;
}
.wf-editor-banner--warn {
background: var(--ws-warning);
color: var(--bg);
}
/* Inline name + description strip (KTD-10). */
.wf-name-strip {
display: flex;
align-items: baseline;
gap: var(--space-sm);
padding: var(--space-xs) var(--space-sm);
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
}
.wf-workflow-name,
.wf-workflow-name--readonly {
font-size: 0.95rem;
font-weight: 600;
color: var(--text);
background: none;
border: 1px solid transparent;
border-radius: var(--radius-sm);
padding: 2px var(--space-xs);
cursor: pointer;
text-align: left;
}
.wf-workflow-name:hover {
background: var(--bg-tertiary);
}
.wf-workflow-name--readonly {
cursor: default;
}
.wf-workflow-name-input {
font-size: 0.95rem;
font-weight: 600;
color: var(--text);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 2px var(--space-xs);
}
.wf-workflow-description,
.wf-workflow-description--readonly {
font-size: 0.78rem;
color: var(--text-dim);
background: none;
border: 1px solid transparent;
border-radius: var(--radius-sm);
padding: 2px var(--space-xs);
cursor: pointer;
text-align: left;
}
.wf-workflow-description:hover {
background: var(--bg-tertiary);
}
.wf-workflow-description--readonly {
cursor: default;
}
.wf-workflow-description-input {
font-size: 0.78rem;
color: var(--text);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 2px var(--space-xs);
min-width: 220px;
}
/* Create-workflow dialog (KTD-7). */
/* Template picker (U4/R7): radiogroup of Blank + built-ins + user workflows. */
.wf-template-list {
display: flex;
flex-direction: column;
gap: var(--space-xs);
max-height: 220px;
overflow-y: auto;
padding: var(--space-xs);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
}
.wf-template-section {
margin: var(--space-xs) 0 var(--space-xs);
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-dim);
}
.wf-template-option {
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
border: 1px solid transparent;
border-radius: var(--radius-sm);
cursor: pointer;
color: var(--text);
}
.wf-template-option:hover {
background: var(--surface-hover);
}
.wf-template-option.selected {
border-color: var(--accent);
background: var(--surface-hover-strong);
}
.wf-template-option:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.wf-template-option-name {
font-size: 0.85rem;
font-weight: 600;
}
.wf-template-option-desc {
font-size: 0.78rem;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wf-template-option-count {
font-size: 0.72rem;
color: var(--text-dim);
}
.wf-create-error {
margin: var(--space-xs) 0 0;
font-size: 0.8rem;
color: var(--ws-error);
}
.wf-column-panel {
display: flex;
flex-direction: column;
gap: var(--space-sm);
width: 280px;
min-width: 260px;
padding: var(--space-md);
border-left: 1px solid var(--border);
overflow-y: auto;
}
.wf-column-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
}
.wf-column-add,
.wf-column-move,
.wf-column-remove {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text);
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), opacity var(--transition-fast);
}
.wf-column-add {
flex: 0 0 auto;
padding: var(--space-xs) var(--space-sm);
font-size: 0.72rem;
font-weight: 600;
color: var(--accent);
}
.wf-column-move,
.wf-column-remove {
width: 1.75rem;
min-width: 1.75rem;
height: 1.75rem;
padding: 0;
}
.wf-column-add:hover:not(:disabled),
.wf-column-move:hover:not(:disabled) {
border-color: var(--accent);
background: var(--surface-hover);
color: var(--accent);
}
.wf-column-remove:hover:not(:disabled) {
border-color: var(--ws-error);
background: color-mix(in srgb, var(--ws-error) 12%, transparent);
color: var(--ws-error);
}
.wf-column-add:focus-visible,
.wf-column-move:focus-visible,
.wf-column-remove:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.wf-column-add:disabled,
.wf-column-move:disabled,
.wf-column-remove:disabled {
cursor: not-allowed;
border-color: var(--border);
background: var(--bg-tertiary);
color: var(--text-dim);
}
.wf-column-panel-empty {
font-size: 0.75rem;
color: var(--text-muted);
margin: 0;
}
.wf-column-panel-errors {
display: flex;
flex-direction: column;
gap: var(--space-xs);
margin: 0;
padding: var(--space-sm);
border: 1px solid var(--ws-error);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--ws-error) 10%, transparent);
}
.wf-column-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.wf-column-item {
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-sm);
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.wf-column-item--error {
border-color: var(--ws-error);
}
.wf-column-item-head {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.wf-column-name {
flex: 1;
min-width: 0;
}
.wf-column-item-actions {
display: flex;
gap: 2px;
}
.wf-column-violation {
display: flex;
align-items: center;
gap: var(--space-xs);
font-size: 0.7rem;
color: var(--ws-error);
margin: 0;
}
.wf-column-traits,
.wf-column-agent {
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding-top: var(--space-xs);
border-top: 1px dashed var(--border);
}
.wf-column-trait-options {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.wf-column-trait {
display: inline-flex;
align-items: center;
gap: 2px;
font-size: 0.7rem;
color: var(--text-muted);
}
/*
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
Column trait toggles are left-sidebar workflow controls; keep their enabled and disabled checkbox chrome on the workflow accent token instead of React/browser defaults.
*/
.wf-column-trait input[type="checkbox"] {
accent-color: var(--todo);
}
.wf-column-traits-label,
.wf-column-agent-label {
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-dim);
}
.wf-column-agent-select {
width: 100%;
min-width: 0;
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text);
font-size: 0.72rem;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}
.wf-column-agent-select:hover:not(:disabled) {
border-color: var(--accent);
background: var(--surface-hover);
}
.wf-column-agent-select:focus {
outline: none;
border-color: var(--accent);
box-shadow: var(--focus-ring);
}
.wf-column-agent-select:disabled {
background: var(--bg-tertiary);
color: var(--text-dim);
cursor: not-allowed;
}
.wf-column-agent-badge {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
flex: 0 1 auto;
min-width: 0;
max-width: 100%;
padding: 1px var(--space-xs);
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--accent) 12%, transparent);
color: var(--accent);
font-size: 0.65rem;
font-weight: 600;
}
.wf-column-agent-badge--stale {
border-color: color-mix(in srgb, var(--ws-warning) 55%, var(--border));
background: color-mix(in srgb, var(--ws-warning) 12%, transparent);
color: var(--ws-warning);
}
.wf-column-agent-error,
.wf-column-agent-stale {
display: flex;
align-items: center;
gap: var(--space-xs);
margin: 0;
font-size: 0.68rem;
}
.wf-column-agent-error {
color: var(--ws-error);
}
.wf-column-agent-stale {
color: var(--ws-warning);
}
.wf-column-agent-mode {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.wf-column-agent-mode-option {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: 1px var(--space-xs);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-muted);
font-size: 0.68rem;
}
.wf-column-agent-mode-option:has(input:checked) {
border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
background: color-mix(in srgb, var(--accent) 10%, transparent);
color: var(--text);
}
.wf-column-agent-mode-option:has(input:disabled) {
background: var(--bg-tertiary);
color: var(--text-dim);
cursor: not-allowed;
}
/* ── U10/R11: Design-with-AI affordances ─────────────────────────────────── */
/* Create-dialog disclosure (above the template picker). */
.wf-ai-create {
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding: var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-secondary);
}
.wf-ai-toggle {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
align-self: flex-start;
padding: var(--space-xs) var(--space-xs);
background: transparent;
border: none;
border-radius: var(--radius-sm);
color: var(--accent);
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
}
.wf-ai-toggle:hover {
background: var(--surface-hover);
}
.wf-ai-create-body {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.wf-ai-prompt {
width: 100%;
resize: vertical;
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg);
color: var(--text);
font-size: 0.85rem;
}
.wf-ai-prompt:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.wf-ai-actions {
display: flex;
gap: var(--space-xs);
}
/* Toolbar popover panel anchored under the "Design with AI" button. */
.wf-ai-edit-wrap {
position: relative;
}
.wf-ai-panel {
position: absolute;
top: calc(100% + var(--space-xs));
right: 0;
z-index: 20;
width: 320px;
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding: var(--space-sm);
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--bg);
box-shadow: var(--shadow-md);
}
@media (max-width: 768px) {
.wf-prompt-editor--fullscreen {
padding: var(--space-sm);
}
.modal-overlay:has(.wf-editor-modal),
.modal-overlay:has(.wf-create-modal) {
padding-top: 0;
align-items: stretch;
justify-content: stretch;
}
.wf-editor-modal,
.wf-create-modal {
width: 100vw;
min-width: 0;
max-width: 100vw;
height: 100vh;
height: 100dvh;
min-height: 0;
max-height: 100dvh;
margin: 0;
border: none;
border-radius: 0;
resize: none;
flex: 1 1 auto;
}
.wf-editor-header,
.wf-migration-notice,
.wf-editor-readonly-banner {
flex-wrap: wrap;
}
.wf-migration-notice,
.wf-editor-readonly-banner {
gap: var(--space-sm);
}
.wf-editor-body {
flex-direction: column;
width: 100%;
min-width: 0;
overflow-x: hidden;
}
.wf-editor-body--list-stage .wf-editor-sidebar {
display: flex;
width: 100%;
min-width: 0;
flex: 1 1 auto;
max-height: none;
border-right: none;
border-bottom: none;
overflow-x: hidden;
overflow-y: auto;
}
.wf-editor-body--list-stage .wf-editor-canvas-wrap,
.wf-editor-body--list-stage .wf-editor-inspector {
display: none;
}
.wf-editor-body--editor-stage .wf-editor-sidebar {
display: none;
}
.wf-editor-body--editor-stage .wf-editor-canvas-wrap {
display: flex;
flex: 0 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
overflow: hidden;
}
.wf-editor-body--editor-stage .wf-editor-canvas {
flex: 1 1 auto;
min-height: 0;
}
.wf-editor-body--editor-stage .wf-editor-inspector {
width: 100%;
min-width: 0;
max-height: none;
flex: 1 1 auto;
min-height: 0;
border-left: none;
border-top: 1px solid var(--border);
overflow-y: auto;
}
.wf-editor-mobile-back {
display: inline-flex;
align-items: center;
align-self: flex-start;
gap: var(--space-xs);
min-height: var(--wf-editor-touch-target);
margin: var(--space-sm) var(--space-sm) 0;
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text);
font-size: 0.8rem;
cursor: pointer;
}
.wf-editor-sidebar .wf-column-panel {
width: 100%;
min-width: 0;
border-left: none;
border-top: 1px solid var(--border);
}
.wf-editor-body .wf-settings-panel {
width: 100%;
min-width: 0;
max-height: 50vh;
border-left: none;
border-top: 1px solid var(--border);
overflow-y: auto;
}
.wf-editor-canvas-wrap {
flex: 1 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
}
.wf-editor-canvas {
width: 100%;
min-width: 0;
max-width: 100%;
overflow: hidden;
}
.wf-editor-body--editor-stage :where(.wf-editor-readonly-banner),
.wf-editor-body--editor-stage :where(.wf-editor-toolbar),
.wf-editor-body--editor-stage :where(.wf-templates),
.wf-editor-body--editor-stage :where(.wf-editor-canvas) {
display: none;
}
.wf-mobile-shell {
display: flex;
flex: 1 1 auto;
min-height: 0;
flex-direction: column;
border-top: 1px solid var(--border);
overflow: hidden;
}
.wf-editor-canvas .react-flow,
.wf-editor-canvas .react-flow__renderer,
.wf-editor-canvas .react-flow__pane,
.wf-editor-canvas .react-flow__viewport {
width: 100%;
min-width: 0;
max-width: 100%;
height: 100%;
}
.wf-editor-list-item,
.wf-editor-new,
.wf-editor-import,
.wf-sidebar-section-toggle,
.wf-palette-btn,
.wf-editor-action,
.wf-editor-delete,
.wf-editor-save,
.wf-templates-toggle,
.wf-templates-entry,
.wf-template-option,
.wf-ai-toggle,
.wf-workflow-name,
.wf-workflow-name-input,
.wf-column-panel button,
.wf-fields-panel button,
.wf-settings-panel button {
min-height: var(--wf-editor-touch-target);
}
.wf-editor-list-item,
.wf-editor-new,
.wf-editor-import,
.wf-palette-btn,
.wf-editor-action,
.wf-editor-delete,
.wf-editor-save,
.wf-templates-toggle,
.wf-templates-entry,
.wf-ai-toggle {
padding: var(--space-sm) var(--space-md);
}
.wf-field input,
.wf-field textarea,
.wf-field select,
.wf-column-name,
.wf-column-agent-select,
.wf-templates-filter,
.wf-ai-prompt {
min-height: var(--wf-editor-touch-target);
padding: var(--space-sm) var(--space-md);
}
.wf-template-list {
max-height: 40vh;
}
.wf-editor-toolbar {
flex-wrap: nowrap;
overflow-x: auto;
}
.wf-editor-palette,
.wf-editor-actions {
flex: 0 0 auto;
flex-wrap: nowrap;
}
.wf-templates {
flex: 0 0 auto;
}
.wf-templates-header {
flex-wrap: nowrap;
}
.wf-templates-toggle {
flex: 0 0 auto;
}
.wf-inspector-toggle {
min-height: var(--wf-editor-touch-target);
padding: var(--space-sm) var(--space-md);
}
.wf-ai-panel {
position: fixed;
inset: var(--space-sm);
top: auto;
right: auto;
z-index: 30;
width: auto;
}
.wf-editor-inspector {
width: 100%;
max-height: 40vh;
border-left: none;
border-top: 1px solid var(--border);
overflow-y: auto;
}
.wf-editor-body--mobile-node-detail .wf-editor-canvas-wrap {
display: none;
min-height: 0;
}
.wf-editor-body--mobile-node-detail .wf-editor-inspector {
display: flex;
flex: 1 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
max-height: none;
border-left: none;
border-top: none;
overflow-y: auto;
}
.wf-editor-body--mobile-edge-detail .wf-editor-canvas-wrap {
display: none;
min-height: 0;
}
.wf-editor-body--mobile-edge-detail .wf-editor-inspector {
display: flex;
flex: 1 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
max-height: none;
border-left: none;
border-top: none;
overflow-y: auto;
}
}