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 }