feat(desktop): workflow builder UI with form editor, CRUD, and channel picker#231
feat(desktop): workflow builder UI with form editor, CRUD, and channel picker#231wesbillman merged 22 commits intomainfrom
Conversation
Allow users to create workflows directly from a channel view via a Zap button in the channel header bar. The dialog pre-selects the current channel and hides the channel selector since context is already known. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove dead branches in CreateWorkflowDialog (channels.length === 0 checks inside a channels.length > 1 guard can never be true). Biome auto-sorted imports in ChannelMembersBar. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DM channels don't support workflows, so disable the Zap button when channelType is "dm" — matching the existing guard on the Add Agent button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…description Reuse canAddAgents guard for the Zap button (no DMs, no archived, respects canManageMembers for private channels). Adapt dialog description text when opened from single-channel context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 — adds a proper form UI for creating workflows: - WorkflowFormBuilder with name field, trigger type selector, dynamic trigger config fields, and step builder with add/remove - Supports all trigger types (message_posted, reaction_added, webhook, schedule) and all action types (delay, send_message, send_dm, call_webhook, request_approval, add_reaction, set_channel_topic) - "Edit as YAML" toggle for power users with bidirectional form↔YAML sync - Split into three files to stay under the 500-line limit: workflowFormTypes.ts, WorkflowStepCard.tsx, WorkflowFormBuilder.tsx - Added yaml npm package for serialization/parsing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nents Extract duplicated FormSelect and FieldLabel components from WorkflowFormBuilder and WorkflowStepCard into a shared workflowFormPrimitives module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CreateWorkflowDialog now uses shared FormSelect/FieldLabel from workflowFormPrimitives instead of an inline select with duplicated styles - Fix step ID collision: use timestamp+counter instead of steps.length which produces duplicates after deletions - Extract stable resetMutation ref to avoid unnecessary reset callback re-creation on every render Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use crypto.randomUUID() for step IDs to prevent React key collisions - Add https:// URL validation warning on call_webhook steps - Set explicit method: "POST" default when creating call_webhook steps and in serialization, so display always matches YAML output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Array.isArray guard in yamlToFormState to handle malformed steps field gracefully instead of crashing on .map() - Wire up id/htmlFor on all step card inputs for proper label association Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add id attributes to all step card form inputs and connect them to
FieldLabel via htmlFor, using a wf-step-{index} prefix pattern for
unique DOM IDs. Clicking a label now focuses the associated input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create WorkflowDialog component supporting create/edit/duplicate modes - Refactor CreateWorkflowDialog as thin wrapper for backwards compatibility - Add Edit and Duplicate actions to WorkflowCard dropdown menu - Add Edit button to WorkflowDetailPanel header - Wire up dialog state management in WorkflowsView with discriminated union - Edit mode pre-populates form from existing definition, calls updateWorkflow - Duplicate mode pre-fills form with "(copy)" suffix, creates new workflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract stable .mutate references from triggerMutation and deleteMutation so useCallback deps don't change on every render. Same pattern used in WorkflowDialog for resetCreate/resetUpdate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- yamlToFormState now rejects unknown trigger/action types with a clear error message instead of silently defaulting to "delay"/"message_posted" - Add WorkflowDeleteDialog (AlertDialog pattern from PersonaDeleteDialog) showing workflow name in confirmation message - Wire delete confirmation into WorkflowsView — delete now opens dialog first instead of immediately mutating Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add mock workflow handlers to e2eBridge.ts (in-memory CRUD for create, update, delete, trigger, get workflows/runs/approvals) - Create workflows.spec.ts with 6 tests covering: - Empty state navigation - Create workflow via form builder - Edit existing workflow (name change) - Duplicate workflow - Delete with confirmation dialog - Trigger from detail panel - Add workflows.spec.ts to smoke project in playwright.config.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When WorkflowFormBuilder mounts with a YAML string that fails to parse (e.g. editing a workflow with unknown action types), it now starts in YAML mode with the parse error visible instead of silently showing an empty form. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…channel picker - Fixed header/footer in workflow dialog with scrollable content area - Added autoCapitalize="off" to all ID, snake_case, pubkey, UUID, URL, cron, interval, and emoji input fields - Generate human-readable step IDs (step_1, step_2) instead of UUIDs - Replaced native <select> with searchable channel combobox (popover + filtered list) for better UX with many channels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add min-h-0 to scrollable content area (required for flex overflow) - Add autoCorrect="off" to workflow name field per spec - Fix step ID generation to find highest existing step_N and increment from there, rather than starting from steps.length Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Parse YAML once on mount via useRef instead of three separate calls - Extract nextStepId() helper from inline addStep callback - Extract formatChannelLabel() to deduplicate channel display string - Consolidate two identical channel info branches in WorkflowDialog - Replace autoFocus with ref callback to satisfy biome a11y rule - Inline highlight reset into onChange to avoid unnecessary useEffect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-locates the step ID generator with other workflow form helpers (formStateToYaml, yamlToFormState) where it logically belongs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@codex please review |
The "Create" button selector was matching both the stream creation submit button and the new "Create workflow" button. Scope the selector to the create-stream-form to avoid the collision. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scopes all `getByRole("button", { name: "Create" })` selectors in
integration.spec.ts to the `create-stream-form` test ID to avoid
collision with the "Create workflow" button's aria-label.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scope ambiguous getByRole("button", { name: "Create" }) selectors in
channels.spec.ts, stream.spec.ts, smoke.spec.ts to create-stream-form,
and workflows.spec.ts to the dialog element.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89a751ff88
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ...actionFieldsForStep(step), | ||
| })); | ||
|
|
||
| return yamlStringify({ name: state.name, trigger, steps }); |
There was a problem hiding this comment.
Preserve top-level workflow flags in form serialization
When a workflow is edited in form mode, formStateToYaml rewrites the definition with only name, trigger, and steps, so top-level keys like enabled/description are dropped. In particular, workflows saved with enabled: false will be re-saved without that flag, and backend parsing defaults enabled to true, which can unintentionally reactivate workflows after a simple metadata edit. Please either round-trip unknown top-level fields or force YAML mode when fields outside the supported form schema are present.
Useful? React with 👍 / 👎.
| (step: Record<string, unknown>, index: number) => ({ | ||
| id: (step.id as string) ?? `step_${index + 1}`, | ||
| action: (step.action as ActionType) ?? ACTION_TYPES[0], | ||
| duration: step.duration as string | undefined, |
There was a problem hiding this comment.
Avoid stripping step guards when converting YAML to form
The YAML parser maps each step into StepFormState using only action-specific fields, so supported step metadata such as if, timeout_secs, and name is silently discarded. If a user edits any parsed workflow in form mode (for example, changing just the workflow name), those dropped fields are not re-emitted and execution semantics change (e.g., guarded steps can become unconditional). This should preserve unsupported step keys or reject form mode for definitions that include them.
Useful? React with 👍 / 👎.
Summary
step_1,step_2) instead of UUIDsNew files
ChannelCombobox.tsx— searchable channel picker with Radix Popover + keyboard navWorkflowFormBuilder.tsx— form-based workflow editor with YAML toggleWorkflowStepCard.tsx— per-step editor with action-specific config fieldsWorkflowDialog.tsx— shared create/edit/duplicate dialogWorkflowDeleteDialog.tsx— delete confirmationworkflowFormTypes.ts— types, YAML serialization, step ID generationworkflowFormPrimitives.tsx— shared form components (FieldLabel, FormSelect)workflows.spec.ts— 6 Playwright E2E testse2eBridge.tsadditions — mock workflow CRUD handlersTest plan
pnpm test:e2efor Playwright tests🤖 Generated with Claude Code