docs: add AI builder UX improvements implementation plan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-24 00:37:08 -05:00
parent 770cad88a7
commit 7fb39924d0

View File

@@ -0,0 +1,354 @@
# AI Builder UX Improvements Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Three frontend-only UX improvements: always-available Publish button, "Generate All" branches in the AI wizard, and rotating activity messages during generation.
**Architecture:** All changes are frontend-only. Feature 1 is a one-line fix in `TreeEditorPage.tsx`. Features 2 and 3 involve adding state to `aiFlowBuilderStore.ts` and updating `BranchDetailView.tsx` and `GeneratingAnimation.tsx`. No backend changes.
**Tech Stack:** React 19, TypeScript, Zustand, Tailwind CSS v3, Lucide React icons.
**Design doc:** `docs/plans/2026-02-24-ai-builder-ux-improvements.md`
---
## Task 1: Fix Publish button — remove `!isDirty` gate
**Files:**
- Modify: `frontend/src/pages/TreeEditorPage.tsx` (line ~654)
**Step 1: Make the change**
Find this line in `TreeEditorPage.tsx`:
```tsx
disabled={isSaving || !isDirty || hasBlockingErrors}
```
Change to:
```tsx
disabled={isSaving || hasBlockingErrors}
```
That's the only change needed. The button's `title` text references "Ctrl+S when no errors" which is still accurate — leave it.
**Step 2: Verify build passes**
```bash
cd frontend && npm run build 2>&1 | tail -20
```
Expected: no errors.
**Step 3: Commit**
```bash
git add frontend/src/pages/TreeEditorPage.tsx
git commit -m "fix: allow publishing clean drafts without requiring local edits"
```
---
## Task 2: Add rotating activity messages to `GeneratingAnimation`
**Files:**
- Modify: `frontend/src/components/ai-builder/GeneratingAnimation.tsx`
**Step 1: Read the current file**
Read `frontend/src/components/ai-builder/GeneratingAnimation.tsx` to understand current structure before changing it.
**Step 2: Rewrite with rotating messages**
Replace the entire file content with:
```tsx
import { useEffect, useState } from 'react'
import { cn } from '@/lib/utils'
const MESSAGES = [
'Setting up your flow...',
'Building diagnostic paths...',
'Putting the pieces in place...',
'Almost there...',
] as const
const MESSAGE_DURATIONS = [4000, 8000, 8000, Infinity] // ms each message shows
interface GeneratingAnimationProps {
branchContext?: { current: number; total: number }
}
export function GeneratingAnimation({ branchContext }: GeneratingAnimationProps) {
const [messageIndex, setMessageIndex] = useState(0)
// Reset and advance message on mount/remount
useEffect(() => {
setMessageIndex(0)
let current = 0
const advance = () => {
current += 1
if (current < MESSAGES.length - 1) {
setMessageIndex(current)
timer = setTimeout(advance, MESSAGE_DURATIONS[current])
} else {
setMessageIndex(MESSAGES.length - 1)
}
}
let timer = setTimeout(advance, MESSAGE_DURATIONS[0])
return () => clearTimeout(timer)
}, [])
return (
<div className="flex flex-col items-center justify-center gap-4 py-10">
{/* Spinner */}
<div className="h-8 w-8 animate-spin rounded-full border-2 border-border border-t-primary" />
{/* Branch context (Generate All mode) */}
{branchContext && (
<p className="text-xs font-label uppercase tracking-wide text-muted-foreground">
Branch {branchContext.current} of {branchContext.total}
</p>
)}
{/* Rotating message */}
<p
key={messageIndex}
className={cn(
'text-sm text-muted-foreground transition-opacity duration-500',
)}
>
{MESSAGES[messageIndex]}
</p>
</div>
)
}
```
**Step 3: Verify build passes**
```bash
cd frontend && npm run build 2>&1 | tail -20
```
Expected: no errors.
**Step 4: Commit**
```bash
git add frontend/src/components/ai-builder/GeneratingAnimation.tsx
git commit -m "feat: add rotating activity messages to generation loading state"
```
---
## Task 3: Add `generateAllBranchDetails` and cancel to the store
**Files:**
- Modify: `frontend/src/store/aiFlowBuilderStore.ts`
**Step 1: Read the current store**
Read `frontend/src/store/aiFlowBuilderStore.ts` fully to understand current state shape before modifying.
**Step 2: Add new state fields and actions**
Add to the `AIFlowBuilderState` interface (after `isLoading: boolean`):
```tsx
isGeneratingAll: boolean
stopGeneratingAll: boolean
generateAllBranchDetails: () => Promise<void>
cancelGenerateAll: () => void
```
Add to the initial state in `create()(...)` (after `isLoading: false`):
```tsx
isGeneratingAll: false,
stopGeneratingAll: false,
```
Add the two new actions after `assemble`:
```tsx
generateAllBranchDetails: async () => {
const { selectedBranches, generateBranchDetail } = get()
const undetailed = selectedBranches.filter((b) => !b.steps)
if (undetailed.length === 0) return
set({ isGeneratingAll: true, stopGeneratingAll: false, error: null })
for (const branch of undetailed) {
if (get().stopGeneratingAll) break
// Set currentBranchIndex so tabs show the active branch
const idx = get().selectedBranches.findIndex((b) => b.name === branch.name)
if (idx !== -1) set({ currentBranchIndex: idx })
await generateBranchDetail(branch.name)
// If generateBranchDetail set phase to 'error', stop
if (get().phase === 'error') break
}
set({ isGeneratingAll: false })
},
cancelGenerateAll: () => {
set({ stopGeneratingAll: true })
},
```
Also add `isGeneratingAll: false, stopGeneratingAll: false` to the `reset()` action's `set({...})` call.
**Step 3: Verify TypeScript compiles**
```bash
cd frontend && npm run build 2>&1 | tail -20
```
Expected: no errors.
**Step 4: Commit**
```bash
git add frontend/src/store/aiFlowBuilderStore.ts
git commit -m "feat: add generateAllBranchDetails and cancelGenerateAll to AI builder store"
```
---
## Task 4: Update `BranchDetailView` with Generate All UI
**Files:**
- Modify: `frontend/src/components/ai-builder/BranchDetailView.tsx`
**Step 1: Read the current file**
Read `frontend/src/components/ai-builder/BranchDetailView.tsx` fully.
**Step 2: Add imports and pull new store state**
Add `Zap, Square` to the lucide-react import (Zap = lightning bolt for "Generate All", Square = stop).
Pull new state from store in the component:
```tsx
const {
// existing...
isGeneratingAll,
generateAllBranchDetails,
cancelGenerateAll,
} = useAIFlowBuilderStore()
```
**Step 3: Add Generate All / Stop button above branch tabs**
After the opening `<div className="flex flex-col gap-4">` and before the branch tabs div, add:
```tsx
{/* Generate All / Stop control */}
{(() => {
const undetailedCount = selectedBranches.filter((b) => !b.steps).length
if (undetailedCount === 0) return null
return (
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">
{undetailedCount} branch{undetailedCount !== 1 ? 'es' : ''} need detail
</span>
{isGeneratingAll ? (
<button
type="button"
onClick={cancelGenerateAll}
className="flex items-center gap-1.5 rounded-lg border border-red-400/30 bg-red-400/10 px-3 py-1.5 text-xs font-medium text-red-400 hover:bg-red-400/20"
>
<Square className="h-3 w-3" />
Stop
</button>
) : (
<button
type="button"
onClick={generateAllBranchDetails}
disabled={isLoading}
className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90 disabled:opacity-50"
>
<Zap className="h-3 w-3" />
Generate All
</button>
)}
</div>
)
})()}
```
**Step 4: Disable individual controls during `isGeneratingAll`**
On the "Generate Detail" button (the primary one in the empty-state section):
```tsx
disabled={isLoading || isGeneratingAll}
```
On the "Skip" button:
```tsx
disabled={isGeneratingAll}
// also add: className includes opacity-50 when disabled
```
On the "Regenerate" button:
```tsx
disabled={isLoading || isGeneratingAll}
```
**Step 5: Pass `branchContext` to `GeneratingAnimation`**
The `GeneratingAnimation` is rendered when `phase === 'generating' && isLoading`. Update that render:
```tsx
if (phase === 'generating' && isLoading) {
return (
<GeneratingAnimation
branchContext={
isGeneratingAll
? {
current: selectedBranches.filter((b) => b.steps).length + 1,
total: selectedBranches.length,
}
: undefined
}
/>
)
}
```
**Step 6: Verify build passes**
```bash
cd frontend && npm run build 2>&1 | tail -20
```
Expected: no errors.
**Step 7: Commit**
```bash
git add frontend/src/components/ai-builder/BranchDetailView.tsx
git commit -m "feat: add Generate All button and per-branch progress to AI builder detail stage"
```
---
## Task 5: Push and verify
**Step 1: Push branch**
```bash
git push
```
**Step 2: Verify CI passes**
```bash
gh pr checks 88 2>&1 | head -20
```
Expected: all checks passing (or wait for them to run).
**Step 3: Manual smoke test checklist**
- [ ] Open a fresh AI-generated draft in the tree editor → Publish button is enabled
- [ ] Open AI Flow Builder, complete foundation → scaffold → select branches
- [ ] On detail stage: "Generate All" button is visible
- [ ] Click "Generate All" → branches generate one at a time, tabs show progress, "Branch X of Y" appears in animation
- [ ] "Stop" button appears during run, clicking it halts after current branch
- [ ] Activity messages cycle: "Setting up your flow..." → "Building diagnostic paths..." → "Putting the pieces in place..." → "Almost there..."
- [ ] Single-branch generate still works as before