diff --git a/packages/dashboard/app/components/InlineCreateCard.tsx b/packages/dashboard/app/components/InlineCreateCard.tsx index 85f0ae4269..7299b4ffff 100644 --- a/packages/dashboard/app/components/InlineCreateCard.tsx +++ b/packages/dashboard/app/components/InlineCreateCard.tsx @@ -1083,6 +1083,10 @@ export function InlineCreateCard({ className="inline-create-optional-steps" aria-label={t("inline.optionalWorkflowSteps", "Optional workflow steps")} > + {/* FNXC:TaskCreation 2026-06-21-00:00: + Inline quick-add uses the shared optional-steps dropdown so it matches + the modal/quick-add keyboard + a11y behavior and toggles the same + enabledWorkflowSteps set submitted on create. */} 0 || pendingImages.length > 0 || selectedWorkflowId !== undefined || + // Optional workflow steps the user toggled count as unsaved work. (Workflows + // whose steps are defaultOn:false — today's only shipped step — seed an empty + // set, so this stays false until the user actually opts a step in.) + enabledWorkflowSteps.length > 0 || executorModel !== "" || validatorModel !== "" || planningModel !== "" || @@ -179,7 +183,7 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask, githubTrackingEnabled || githubRepoOverrideTrimmed !== ""; setHasDirtyState(isDirty); - }, [description, dependencies, pendingImages, selectedWorkflowId, executorModel, validatorModel, planningModel, thinkingLevel, selectedAgentId, reviewLevel, autoMerge, priority, nodeId, branchMode, branch, baseBranch, githubTrackingEnabled, githubRepoOverrideTrimmed]); + }, [description, dependencies, pendingImages, selectedWorkflowId, enabledWorkflowSteps, executorModel, validatorModel, planningModel, thinkingLevel, selectedAgentId, reviewLevel, autoMerge, priority, nodeId, branchMode, branch, baseBranch, githubTrackingEnabled, githubRepoOverrideTrimmed]); const handleClose = useCallback(async () => { if (hasDirtyState) { diff --git a/packages/dashboard/app/components/TaskForm.tsx b/packages/dashboard/app/components/TaskForm.tsx index de69a52f60..1c73dd8ec8 100644 --- a/packages/dashboard/app/components/TaskForm.tsx +++ b/packages/dashboard/app/components/TaskForm.tsx @@ -306,6 +306,9 @@ export function TaskForm({ let cancelled = false; setOptionalSteps([]); if (!effectiveOptionalWorkflowId) { + // Clear any in-flight loading state (a prior fetch may have been cancelled + // mid-flight when switching to "No workflow"), so the loading row never sticks. + setOptionalStepsLoading(false); onEnabledWorkflowStepsChange?.([]); return; } diff --git a/packages/dashboard/app/components/WorkflowNodeEditor.tsx b/packages/dashboard/app/components/WorkflowNodeEditor.tsx index 7e51096525..4b3e2d39cb 100644 --- a/packages/dashboard/app/components/WorkflowNodeEditor.tsx +++ b/packages/dashboard/app/components/WorkflowNodeEditor.tsx @@ -2502,6 +2502,7 @@ function InnerEditor({ optionalSteps={optionalSteps} onChange={setOptionalSteps} readOnly={isBuiltin} + pluginTemplates={pluginTemplates.map((p) => p.template)} /> )} @@ -2812,6 +2813,7 @@ function InnerEditor({ optionalSteps={optionalSteps} onChange={setOptionalSteps} readOnly={isBuiltin} + pluginTemplates={pluginTemplates.map((p) => p.template)} /> )} diff --git a/packages/dashboard/app/components/WorkflowOptionalStepsDropdown.tsx b/packages/dashboard/app/components/WorkflowOptionalStepsDropdown.tsx index 362bd19a19..80fd71c0ad 100644 --- a/packages/dashboard/app/components/WorkflowOptionalStepsDropdown.tsx +++ b/packages/dashboard/app/components/WorkflowOptionalStepsDropdown.tsx @@ -1,4 +1,9 @@ /** + * FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + * Users selecting optional workflow steps at task creation need one consistent + * multi-select control across every creation surface, with full keyboard + screen- + * reader support, so the quick-add card and the full New Task modal behave identically. + * * WorkflowOptionalStepsDropdown — a controlled multi-select for a workflow's * optional steps, shared by the quick-add card (U5) and the full New Task modal * (U4) so both creation surfaces present the same interaction. @@ -109,10 +114,11 @@ export function WorkflowOptionalStepsDropdown({ : t("workflowOptionalSteps.triggerCount", "Steps: {{count}} selected", { count: selectedCount }); const onTriggerKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") { + // ArrowUp also opens (ARIA listbox authoring guidance), landing on the last option. + if (e.key === "ArrowDown" || e.key === "ArrowUp" || e.key === "Enter" || e.key === " ") { e.preventDefault(); setIsOpen(true); - setActiveIndex(0); + setActiveIndex(e.key === "ArrowUp" ? steps.length - 1 : 0); } }; @@ -161,8 +167,8 @@ export function WorkflowOptionalStepsDropdown({ ref={panelRef} className="wf-optional-steps-dropdown-panel" role="listbox" + aria-multiselectable="true" aria-label={t("workflowOptionalSteps.title", "Optional steps")} - aria-labelledby={labelId} data-testid="wf-optional-steps-dropdown-panel" style={{ top: position.top, left: position.left, minWidth: position.width }} onKeyDown={onPanelKeyDown} diff --git a/packages/dashboard/app/components/WorkflowOptionalStepsPanel.tsx b/packages/dashboard/app/components/WorkflowOptionalStepsPanel.tsx index baa6fc47fd..40bdc94a0f 100644 --- a/packages/dashboard/app/components/WorkflowOptionalStepsPanel.tsx +++ b/packages/dashboard/app/components/WorkflowOptionalStepsPanel.tsx @@ -1,4 +1,9 @@ /** + * FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + * Workflow authors need to declare which step templates are optional and set each + * one's defaultOn from the visual editor (persisted on the IR's `optionalSteps` + * array) so optional steps are authorable without hand-editing IR. + * * WorkflowOptionalStepsPanel — the workflow editor's optional-step authoring * surface. Sibling to {@link WorkflowFieldsPanel} / WorkflowSettingsPanel: lives * alongside the canvas in {@link WorkflowNodeEditor} and mutates the IR's diff --git a/packages/dashboard/app/components/workflow-phase-badge.tsx b/packages/dashboard/app/components/workflow-phase-badge.tsx index 2993d011f4..fe5a171a97 100644 --- a/packages/dashboard/app/components/workflow-phase-badge.tsx +++ b/packages/dashboard/app/components/workflow-phase-badge.tsx @@ -1,4 +1,8 @@ /** + * FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + * One phase chip (pre-merge / post-merge) shared by every workflow-step surface so + * the badge looks identical across the results tab, authoring panel, and dropdown. + * * Shared phase chip for workflow steps (pre-merge / post-merge). Extracted from * WorkflowResultsTab so the node-editor optional-steps panel and the optional-step * dropdown render an identical badge without duplicating markup.