fix: auto-advance branch detail and pin navigation bar

- Auto-advance to next undetailed branch after generation completes,
  using a useEffect that watches the count of detailed branches
- Cap tree preview at max-h-48 with internal scroll so the nav bar
  is never pushed off screen
- Make nav bar sticky bottom-0 with bg-card so it stays visible
  regardless of content height

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-21 04:21:55 -05:00
parent 55bfeb8ec2
commit f269d96d51

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState, useEffect, useRef } from 'react'
import { Check, RefreshCw, SkipForward, ChevronRight, ChevronLeft } from 'lucide-react'
import { useAIFlowBuilderStore } from '@/store/aiFlowBuilderStore'
import { GeneratingAnimation } from './GeneratingAnimation'
@@ -21,6 +21,18 @@ export function BranchDetailView() {
const allBranchesHaveDetail = selectedBranches.every((b) => b.steps)
const branchesWithDetail = selectedBranches.filter((b) => b.steps).length
// Auto-advance to next branch without detail after generation completes
const prevDetailCount = useRef(branchesWithDetail)
useEffect(() => {
if (branchesWithDetail > prevDetailCount.current) {
prevDetailCount.current = branchesWithDetail
const nextIndex = selectedBranches.findIndex((b) => !b.steps)
if (nextIndex !== -1) {
setViewingIndex(nextIndex)
}
}
}, [branchesWithDetail, selectedBranches])
const handleGenerate = async (branchName: string) => {
setError(null)
await generateBranchDetail(branchName)
@@ -35,103 +47,106 @@ export function BranchDetailView() {
}
return (
<div className="space-y-4">
{/* Branch tabs */}
<div className="flex items-center gap-2 overflow-x-auto pb-1">
{selectedBranches.map((branch, i) => (
<button
key={branch.name}
type="button"
onClick={() => setViewingIndex(i)}
className={cn(
'flex shrink-0 items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors',
viewingIndex === i
? 'border-primary/30 bg-primary/10 text-foreground'
: 'border-border text-muted-foreground hover:bg-accent',
branch.steps && 'pr-2'
<div className="flex flex-col gap-4">
{/* Content area */}
<div className="space-y-4">
{/* Branch tabs */}
<div className="flex items-center gap-2 overflow-x-auto pb-1">
{selectedBranches.map((branch, i) => (
<button
key={branch.name}
type="button"
onClick={() => setViewingIndex(i)}
className={cn(
'flex shrink-0 items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors',
viewingIndex === i
? 'border-primary/30 bg-primary/10 text-foreground'
: 'border-border text-muted-foreground hover:bg-accent',
branch.steps && 'pr-2'
)}
>
{branch.name}
{branch.steps && (
<Check className="h-3 w-3 text-green-400" />
)}
</button>
))}
</div>
{/* Current branch detail */}
{currentBranch && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">{currentBranch.name}</h3>
<p className="text-xs text-muted-foreground">{currentBranch.description}</p>
</div>
</div>
{currentBranch.steps ? (
<div className="space-y-3">
{/* Mini tree preview */}
<div className="max-h-48 overflow-y-auto rounded-lg border border-border bg-accent/30 p-3">
<NodePreview node={currentBranch.steps} depth={0} />
</div>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => handleGenerate(currentBranch.name)}
disabled={isLoading}
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50"
>
<RefreshCw className="h-3 w-3" />
Regenerate
</button>
</div>
</div>
) : (
<div className="flex flex-col items-center gap-3 rounded-lg border border-dashed border-border bg-accent/20 py-8">
<p className="text-sm text-muted-foreground">
Generate AI detail for this branch
</p>
<div className="flex gap-2">
<button
type="button"
onClick={() => handleGenerate(currentBranch.name)}
disabled={isLoading}
className={cn(
'rounded-lg bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20',
isLoading ? 'cursor-not-allowed opacity-50' : 'hover:opacity-90'
)}
>
Generate Detail
</button>
<button
type="button"
onClick={() => {
if (viewingIndex < selectedBranches.length - 1) {
setViewingIndex(viewingIndex + 1)
}
}}
className="flex items-center gap-1 rounded-lg border border-border px-3 py-2 text-sm text-muted-foreground hover:bg-accent"
>
<SkipForward className="h-3.5 w-3.5" />
Skip
</button>
</div>
</div>
)}
>
{branch.name}
{branch.steps && (
<Check className="h-3 w-3 text-green-400" />
)}
</button>
))}
</div>
)}
{/* Error */}
{error && (
<div className="rounded-lg border border-red-400/20 bg-red-400/5 px-3 py-2 text-sm text-red-400">
{error}
</div>
)}
</div>
{/* Current branch detail */}
{currentBranch && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">{currentBranch.name}</h3>
<p className="text-xs text-muted-foreground">{currentBranch.description}</p>
</div>
</div>
{currentBranch.steps ? (
<div className="space-y-3">
{/* Mini tree preview */}
<div className="rounded-lg border border-border bg-accent/30 p-3">
<NodePreview node={currentBranch.steps} depth={0} />
</div>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => handleGenerate(currentBranch.name)}
disabled={isLoading}
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50"
>
<RefreshCw className="h-3 w-3" />
Regenerate
</button>
</div>
</div>
) : (
<div className="flex flex-col items-center gap-3 rounded-lg border border-dashed border-border bg-accent/20 py-8">
<p className="text-sm text-muted-foreground">
Generate AI detail for this branch
</p>
<div className="flex gap-2">
<button
type="button"
onClick={() => handleGenerate(currentBranch.name)}
disabled={isLoading}
className={cn(
'rounded-lg bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20',
isLoading ? 'cursor-not-allowed opacity-50' : 'hover:opacity-90'
)}
>
Generate Detail
</button>
<button
type="button"
onClick={() => {
if (viewingIndex < selectedBranches.length - 1) {
setViewingIndex(viewingIndex + 1)
}
}}
className="flex items-center gap-1 rounded-lg border border-border px-3 py-2 text-sm text-muted-foreground hover:bg-accent"
>
<SkipForward className="h-3.5 w-3.5" />
Skip
</button>
</div>
</div>
)}
</div>
)}
{/* Error */}
{error && (
<div className="rounded-lg border border-red-400/20 bg-red-400/5 px-3 py-2 text-sm text-red-400">
{error}
</div>
)}
{/* Navigation */}
<div className="flex items-center justify-between border-t border-border pt-3">
{/* Navigation — sticky so it's always visible */}
<div className="sticky bottom-0 flex items-center justify-between border-t border-border bg-card pt-3 pb-1">
<div className="flex items-center gap-2">
<button
type="button"