Arun Dass

AI Planning

AI chats 453

About

AI Planning Workflow

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds AI Planning to your project and fits it to your theme.

Code

How AI Planning is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import React, { useState, useRef } from 'react';
import { 
  ChevronDown, 
  ChevronRight, 
  Loader2, 
  Check, 
  Search, 
  FileText, 
  BrainCircuit,
  AlertTriangle,
  Code,
  TerminalSquare
} from 'lucide-react';

export type PlanStepStatus = 'pending' | 'active' | 'success' | 'error';

export interface PlanStep {
  id: string;
  title: string;
  content?: React.ReactNode;
  status: PlanStepStatus;
  icon?: React.ReactNode;
  duration?: string;
  defaultExpanded?: boolean;
}

export interface AgentPlanningProps {
  title?: string;
  steps?: PlanStep[];
}

const DEFAULT_STEPS: PlanStep[] = [
  {
    id: '1',
    title: 'Analyze request and extract constraints',
    status: 'success',
    duration: '0.4s',
    icon: <Search className="w-3.5 h-3.5" />,
    content: (
      <div className="space-y-2 font-mono text-[11px] text-muted-foreground mt-2">
        <div className="flex items-start gap-2 text-emerald-600 dark:text-emerald-400 font-medium">
          <Check className="w-3.5 h-3.5 mt-0.5 shrink-0" />
          <span>Parsed user intent: Build minimalist UI Component</span>
        </div>
        <div className="grid grid-cols-[80px_1fr] gap-1.5 mt-3 bg-secondary/30 p-2.5 rounded-md border border-border/50">
          <span className="text-foreground/50 font-medium">Language:</span>
          <span className="text-foreground">TypeScript, React</span>
          
          <span className="text-foreground/50 font-medium">Styling:</span>
          <span className="text-foreground">Tailwind CSS v4 (OKLCH variables)</span>
          
          <span className="text-foreground/50 font-medium">Constraints:</span>
          <span className="text-amber-600 dark:text-amber-400">Single-file, Interactive, No Overlaps</span>
        </div>
      </div>
    )
  },
  {
    id: '2',
    title: 'Search UI knowledge base',
    status: 'success',
    duration: '1.2s',
    icon: <FileText className="w-3.5 h-3.5" />,
    content: (
      <div className="space-y-3 font-mono text-[11px] mt-2">
        <div className="flex items-center gap-2">
          <span className="text-muted-foreground">Executing tool:</span>
          <span className="px-1.5 py-0.5 rounded-md bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 border border-indigo-500/20 font-semibold flex items-center gap-1">
            <TerminalSquare className="w-3 h-3" />
            vector_search
          </span>
        </div>
        <div className="p-3 rounded-md bg-card border border-border shadow-sm text-muted-foreground">
          <div className="text-emerald-600 dark:text-emerald-400 mb-2 font-semibold">Success: Retrieved 3 semantic patterns</div>
          <ul className="space-y-1.5 list-disc list-inside">
            <li>Claude 3.5 Sonnet thinking block layout</li>
            <li>Tailwind v4 flex-timeline micro-interactions</li>
            <li>React state-driven accordions with smooth max-height</li>
          </ul>
        </div>
      </div>
    )
  },
  {
    id: '3',
    title: 'Synthesize component logic',
    status: 'active',
    duration: '...',
    icon: <BrainCircuit className="w-3.5 h-3.5" />,
    defaultExpanded: true,
    content: (
      <div className="space-y-3 font-mono text-[11px] mt-2">
        <div className="flex items-center gap-2 text-blue-600 dark:text-blue-400 font-medium">
          <Loader2 className="w-3.5 h-3.5 animate-spin" />
          <span>Generating structured timeline layout...</span>
        </div>
        
        <div className="relative rounded-md overflow-hidden bg-zinc-950 dark:bg-black/60 border border-border/50 p-3.5 shadow-inner">
          <div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-blue-500 to-indigo-500 opacity-50" />
          <div className="text-zinc-400 space-y-1.5 leading-relaxed">
            <div><span className="text-purple-400">const</span> <span className="text-blue-300">timelineLayout</span> = <span className="text-yellow-300">useMemo</span>(...)</div>
            <div className="pl-4">- Fixing absolute positioning overlaps</div>
            <div className="pl-4">- Applying distinct icon columns</div>
            <div className="pl-4 text-zinc-300 font-medium animate-pulse flex items-center gap-1">
              <ChevronRight className="w-3 h-3 text-blue-400" />
              Injecting rich content panels |
            </div>
          </div>
        </div>
      </div>
    )
  },
  {
    id: '4',
    title: 'Review dependency conflicts',
    status: 'error',
    duration: '0.8s',
    icon: <AlertTriangle className="w-3.5 h-3.5" />,
    content: (
      <div className="space-y-2 font-mono text-[11px] mt-2 animate-in fade-in zoom-in-95 duration-300">
        <div className="p-3 rounded-md bg-rose-500/10 border border-rose-500/20">
          <div className="text-rose-600 dark:text-rose-400 font-bold mb-1 flex items-center gap-1.5">
            <AlertTriangle className="w-3.5 h-3.5" />
            Warning: Component styling deviation
          </div>
          <div className="text-rose-600/80 dark:text-rose-400/80 leading-relaxed">
            Previous absolute positioning caused icon overlaps. Sub-agent is resolving layout tree to a strict flex-row grid before continuing execution.
          </div>
        </div>
      </div>
    )
  },
  {
    id: '5',
    title: 'Execute final rendering',
    status: 'pending',
    icon: <Code className="w-3.5 h-3.5" />,
  }
];

export const AgentPlanning: React.FC<AgentPlanningProps> = ({
  title = "Agent is planning",
  steps = DEFAULT_STEPS,
}) => {
  const [isMainExpanded, setIsMainExpanded] = useState(true);
  
  // Track expanded state of individual step details
  const [expandedSteps, setExpandedSteps] = useState<Record<string, boolean>>(
    steps.reduce((acc, step) => {
      acc[step.id] = step.defaultExpanded || false;
      return acc;
    }, {} as Record<string, boolean>)
  );

  const mainContentRef = useRef<HTMLDivElement>(null);

  const toggleStep = (id: string, e: React.MouseEvent) => {
    e.stopPropagation();
    setExpandedSteps(prev => ({ ...prev, [id]: !prev[id] }));
  };

  const hasActive = steps.some(s => s.status === 'active');
  const allSuccess = steps.every(s => s.status === 'success');

  const getStatusColor = (status: PlanStepStatus) => {
    switch (status) {
      case 'success': return 'bg-emerald-100 text-emerald-600 ring-emerald-500/20 dark:bg-emerald-500/20 dark:text-emerald-400';
      case 'active': return 'bg-blue-100 text-blue-600 ring-blue-500/30 dark:bg-blue-500/20 dark:text-blue-400';
      case 'error': return 'bg-rose-100 text-rose-600 ring-rose-500/20 dark:bg-rose-500/20 dark:text-rose-400';
      case 'pending': return 'bg-secondary text-muted-foreground ring-border/50 dark:bg-secondary/50';
    }
  };

  return (
    <div className="w-full max-w-2xl mx-auto my-4 font-sans text-foreground">
      {/* Outer Card Container */}
      <div className="bg-card border border-border shadow-sm rounded-xl overflow-hidden transition-all duration-300">
        
        {/* Top Header / Trigger Badge */}
        <div 
          onClick={() => setIsMainExpanded(!isMainExpanded)}
          className={`flex items-center justify-between px-4 py-3.5 cursor-pointer transition-colors select-none
            ${isMainExpanded ? 'bg-secondary/30 border-b border-border/50' : 'hover:bg-secondary/30'}
          `}
        >
          <div className="flex items-center gap-3">
            <div className="flex items-center justify-center w-5 h-5">
              {hasActive ? (
                <Loader2 className="w-4 h-4 text-blue-600 dark:text-blue-400 animate-spin" />
              ) : allSuccess ? (
                <Check className="w-4 h-4 text-emerald-600 dark:text-emerald-400" />
              ) : (
                <BrainCircuit className="w-4 h-4 text-muted-foreground" />
              )}
            </div>
            
            <span className="text-[15px] font-semibold text-foreground/90 tracking-tight">
              {title}
            </span>
          </div>

          <div className="flex items-center justify-center w-6 h-6 rounded-md hover:bg-secondary text-muted-foreground transition-colors">
            {isMainExpanded ? (
              <ChevronDown className="w-4 h-4" />
            ) : (
              <ChevronRight className="w-4 h-4" />
            )}
          </div>
        </div>

        {/* Expandable Main Timeline Area */}
        <div 
          className={`grid transition-all duration-500 ease-in-out bg-card ${
            isMainExpanded ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
          }`}
        >
          <div className="overflow-hidden">
          <div ref={mainContentRef} className="p-5 flex flex-col">
            
            {steps.map((step, index) => {
              const isStepExpanded = expandedSteps[step.id];
              const isLast = index === steps.length - 1;
              
              return (
                <div 
                  key={step.id} 
                  className={`relative flex gap-4 animate-in fade-in slide-in-from-top-4 duration-500 fill-mode-both
                    ${step.status === 'pending' ? 'opacity-60 grayscale' : 'opacity-100'}
                  `}
                  style={{ animationDelay: `${index * 80}ms` }}
                >
                  {/* Timeline connecting line */}
                  {!isLast && (
                    <div className="absolute left-[11px] top-7 bottom-[-10px] w-[2px] bg-border/60 z-0" />
                  )}

                  {/* Icon Column */}
                  <div className="relative z-10 flex-none w-6 h-6 mt-0.5">
                    <div className={`flex items-center justify-center w-full h-full rounded-full ring-4 ring-card transition-colors duration-300
                      ${getStatusColor(step.status)}
                    `}>
                      {step.status === 'success' ? (
                        <Check className="w-3.5 h-3.5" />
                      ) : step.status === 'active' ? (
                        <Loader2 className="w-3.5 h-3.5 animate-spin" />
                      ) : (
                        step.icon || <div className="w-1.5 h-1.5 rounded-full bg-current" />
                      )}
                    </div>
                  </div>

                  {/* Content Column */}
                  <div className="flex-1 pb-6">
                    {/* Step Header */}
                    <div 
                      className={`flex items-center justify-between group rounded-md -mx-2 px-2 py-1 transition-colors
                        ${step.content ? 'cursor-pointer hover:bg-secondary/50' : ''}
                      `}
                      onClick={(e) => step.content && toggleStep(step.id, e)}
                    >
                      <span className={`text-[14px] tracking-tight transition-colors duration-200
                        ${step.status === 'active' ? 'text-foreground font-semibold' : 
                          step.status === 'error' ? 'text-rose-600 dark:text-rose-400 font-semibold' : 
                          'text-foreground/80 group-hover:text-foreground font-medium'}
                      `}>
                        {step.title}
                      </span>

                      <div className="flex items-center gap-3">
                        {step.duration && (
                          <span className="text-[11px] font-mono text-muted-foreground tabular-nums">
                            {step.duration}
                          </span>
                        )}
                        {step.content && (
                          <div className="text-muted-foreground/40 group-hover:text-muted-foreground transition-colors">
                            {isStepExpanded ? <ChevronDown className="w-4 h-4" /> : <ChevronRight className="w-4 h-4" />}
                          </div>
                        )}
                      </div>
                    </div>

                    {/* Step Expanded Content */}
                    {step.content && (
                      <div 
                        className={`grid transition-all duration-400 ease-in-out ${
                          isStepExpanded ? 'grid-rows-[1fr] mt-2 opacity-100' : 'grid-rows-[0fr] mt-0 opacity-0'
                        }`}
                      >
                        <div className="overflow-hidden">
                          <div className="pt-1 pb-2">
                            {step.content}
                          </div>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
            
          </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default AgentPlanning;

More by Arun Dass

View all
  • Dashboard Sidebar

    Dashboard Sidebar

    1.7K
  • AI Agent Processing States

    AI Agent Processing States

    182
  • Kanban Board

    Kanban Board

    160
  • FileUpload

    FileUpload

    155
  • Neomorphism Metric Card

    Neomorphism Metric Card

    88
  • Focus Hero

    Focus Hero

    38
  • AI Prompt Input Box

    AI Prompt Input Box

    2
  • Fuji

    Fuji

    1

More in AI chats

View all
  • AI Actions

    AI Actions

    319
  • Agent Chat

    Agent Chat

    17
  • System Message

    System Message

    54
  • Message Thread

    Message Thread

    35
  • Color Picker

    Color Picker

    81
  • AI Task List

    AI Task List

    160
  • Modal Card

    Modal Card

    101
  • Quota Banner

    Quota Banner

    21