/ src / components / ui / ModeToggle.tsx
ModeToggle.tsx
  1  // Copyright (c) 2026 VPL Solutions. All rights reserved.
  2  // Licensed under the MIT License. See LICENSE for details.
  3  //
  4  // ModeToggle — RAG Query | Agent Query mode selector (ADR-011)
  5  
  6  import { Zap, GitBranch, AlertCircle } from 'lucide-react';
  7  
  8  export type QueryMode = 'rag' | 'agent';
  9  
 10  interface ModeToggleProps {
 11    mode: QueryMode;
 12    onChange: (mode: QueryMode) => void;
 13    complexityHint?: boolean;   // show "looks multi-step" suggestion banner
 14    disabled?: boolean;
 15  }
 16  
 17  export function ModeToggle({ mode, onChange, complexityHint = false, disabled = false }: ModeToggleProps) {
 18    return (
 19      <div className="flex flex-col gap-2">
 20        <div className="inline-flex rounded-xl border border-gray-200 dark:border-white/10 bg-gray-50 dark:bg-white/[0.03] p-1 gap-1">
 21          {/* RAG Query */}
 22          <button
 23            onClick={() => !disabled && onChange('rag')}
 24            disabled={disabled}
 25            className={[
 26              'relative flex flex-col items-start gap-0.5 px-4 py-2.5 rounded-lg text-left transition-all duration-200',
 27              'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500',
 28              mode === 'rag'
 29                ? 'bg-white dark:bg-white/10 shadow-sm text-gray-900 dark:text-white'
 30                : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300',
 31              disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer',
 32            ].join(' ')}
 33            aria-pressed={mode === 'rag'}
 34          >
 35            <div className="flex items-center gap-1.5">
 36              <Zap className={[
 37                'w-3.5 h-3.5 transition-colors',
 38                mode === 'rag' ? 'text-blue-500' : 'text-gray-400 dark:text-gray-500',
 39              ].join(' ')} />
 40              <span className="text-sm font-semibold tracking-tight">RAG Query</span>
 41              {mode === 'rag' && (
 42                <span className="ml-1 inline-flex items-center px-1.5 py-0.5 rounded-full text-[10px] font-medium bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-300">
 43                  active
 44                </span>
 45              )}
 46            </div>
 47            <span className="text-[11px] text-gray-400 dark:text-gray-500 leading-tight pl-5">
 48              Direct retrieval · Fast · Stateless
 49            </span>
 50          </button>
 51  
 52          {/* Agent Query */}
 53          <button
 54            onClick={() => !disabled && onChange('agent')}
 55            disabled={disabled}
 56            className={[
 57              'relative flex flex-col items-start gap-0.5 px-4 py-2.5 rounded-lg text-left transition-all duration-200',
 58              'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-500',
 59              mode === 'agent'
 60                ? 'bg-white dark:bg-white/10 shadow-sm text-gray-900 dark:text-white'
 61                : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300',
 62              disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer',
 63            ].join(' ')}
 64            aria-pressed={mode === 'agent'}
 65          >
 66            <div className="flex items-center gap-1.5">
 67              <GitBranch className={[
 68                'w-3.5 h-3.5 transition-colors',
 69                mode === 'agent' ? 'text-purple-500' : 'text-gray-400 dark:text-gray-500',
 70              ].join(' ')} />
 71              <span className="text-sm font-semibold tracking-tight">Agent Query</span>
 72              {mode === 'agent' && (
 73                <span className="ml-1 inline-flex items-center px-1.5 py-0.5 rounded-full text-[10px] font-medium bg-purple-100 dark:bg-purple-500/20 text-purple-700 dark:text-purple-300">
 74                  active
 75                </span>
 76              )}
 77            </div>
 78            <span className="text-[11px] text-gray-400 dark:text-gray-500 leading-tight pl-5">
 79              Planner → Retriever → Synthesizer · Citations · Session
 80            </span>
 81          </button>
 82        </div>
 83  
 84        {/* Complexity hint banner — shown when query looks multi-step and user is on RAG */}
 85        {complexityHint && mode === 'rag' && (
 86          <div className="flex items-start gap-2 px-3 py-2 rounded-lg bg-purple-50 dark:bg-purple-500/10 border border-purple-200 dark:border-purple-500/20 text-xs text-purple-700 dark:text-purple-300">
 87            <AlertCircle className="w-3.5 h-3.5 mt-0.5 shrink-0" />
 88            <span>
 89              This looks like a multi-step question.{' '}
 90              <button
 91                onClick={() => onChange('agent')}
 92                className="font-semibold underline underline-offset-2 hover:text-purple-900 dark:hover:text-purple-100 transition-colors"
 93              >
 94                Switch to Agent Query
 95              </button>
 96              {' '}for a more thorough answer.
 97            </span>
 98          </div>
 99        )}
100      </div>
101    );
102  }