Download frontend/src/components/tools/ToolsPanel.jsx from Rixhot/ZARAI: direct link, hf CLI and curl.
- Browser
- Download file 7.52 kB
-
https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/components/tools/ToolsPanel.jsx
- Command line
-
hf download hf://spaces/Rixhot/ZARAI/frontend/src/components/tools/ToolsPanel.jsx
-
curl -L -o ToolsPanel.jsx https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/components/tools/ToolsPanel.jsx
7.52 kB
| /** | |
| * JARVIS — Tools Panel | |
| * Direct tool execution: calculator, web search, code runner | |
| */ | |
| import React, { useState, useEffect } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { | |
| Wrench, Search, Calculator, Code2, | |
| Play, Loader2, ChevronDown, ChevronUp, CheckCircle, XCircle, | |
| } from 'lucide-react' | |
| import clsx from 'clsx' | |
| import toast from 'react-hot-toast' | |
| import { toolsAPI } from '../../services/api' | |
| import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' | |
| import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism' | |
| const TOOLS = [ | |
| { | |
| id: 'web_search', | |
| label: 'Web Search', | |
| icon: Search, | |
| placeholder: 'Enter search query…', | |
| example: 'latest AI research 2025', | |
| color: 'text-blue-400', | |
| }, | |
| { | |
| id: 'calculator', | |
| label: 'Calculator', | |
| icon: Calculator, | |
| placeholder: 'Math expression…', | |
| example: 'sqrt(144) + 2**10', | |
| color: 'text-yellow-400', | |
| }, | |
| { | |
| id: 'code_executor', | |
| label: 'Code Runner', | |
| icon: Code2, | |
| placeholder: 'Python code…', | |
| example: 'print([x**2 for x in range(10)])', | |
| color: 'text-green-400', | |
| multiline: true, | |
| }, | |
| ] | |
| export default function ToolsPanel() { | |
| const [expanded, setExpanded] = useState(true) | |
| const [activeTool, setActiveTool] = useState('calculator') | |
| const [argument, setArgument] = useState('') | |
| const [result, setResult] = useState(null) | |
| const [loading, setLoading] = useState(false) | |
| const tool = TOOLS.find((t) => t.id === activeTool) | |
| const run = async () => { | |
| if (!argument.trim()) return | |
| setLoading(true) | |
| setResult(null) | |
| try { | |
| const data = await toolsAPI.executeTool(activeTool, argument.trim()) | |
| setResult(data) | |
| if (!data.success) toast.error('Tool returned an error') | |
| } catch (err) { | |
| toast.error(err.message) | |
| } finally { | |
| setLoading(false) | |
| } | |
| } | |
| const loadExample = () => { | |
| setArgument(tool.example) | |
| } | |
| return ( | |
| <div className="bg-jarvis-surface border-t border-jarvis-border"> | |
| {/* Header */} | |
| <div | |
| className="flex items-center justify-between px-4 py-2.5 cursor-pointer | |
| hover:bg-jarvis-panel/30 transition-colors" | |
| onClick={() => setExpanded((v) => !v)} | |
| > | |
| <div className="flex items-center gap-2"> | |
| <Wrench size={13} className="text-jarvis-accent" /> | |
| <span className="text-xs font-medium text-jarvis-text">Tool Executor</span> | |
| <span className="text-[10px] font-mono text-jarvis-dim">Direct access</span> | |
| </div> | |
| {expanded ? <ChevronDown size={13} className="text-jarvis-dim" /> : | |
| <ChevronUp size={13} className="text-jarvis-dim" />} | |
| </div> | |
| <AnimatePresence> | |
| {expanded && ( | |
| <motion.div | |
| initial={{ height: 0 }} | |
| animate={{ height: 'auto' }} | |
| exit={{ height: 0 }} | |
| className="overflow-hidden" | |
| > | |
| <div className="px-4 pb-4 space-y-3"> | |
| {/* Tool selector */} | |
| <div className="flex gap-2"> | |
| {TOOLS.map((t) => { | |
| const Icon = t.icon | |
| return ( | |
| <button | |
| key={t.id} | |
| onClick={() => { setActiveTool(t.id); setArgument(''); setResult(null) }} | |
| className={clsx( | |
| 'flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs', | |
| 'border transition-all duration-150', | |
| activeTool === t.id | |
| ? 'bg-jarvis-accent/10 border-jarvis-accent/30 text-jarvis-accent' | |
| : 'border-jarvis-border text-jarvis-dim hover:border-jarvis-muted hover:text-jarvis-muted', | |
| )} | |
| > | |
| <Icon size={11} /> | |
| {t.label} | |
| </button> | |
| ) | |
| })} | |
| </div> | |
| {/* Input */} | |
| <div className="flex gap-2"> | |
| {tool.multiline ? ( | |
| <textarea | |
| value={argument} | |
| onChange={(e) => setArgument(e.target.value)} | |
| placeholder={tool.placeholder} | |
| rows={3} | |
| className="flex-1 input-field text-xs font-mono resize-none py-2" | |
| /> | |
| ) : ( | |
| <input | |
| type="text" | |
| value={argument} | |
| onChange={(e) => setArgument(e.target.value)} | |
| onKeyDown={(e) => e.key === 'Enter' && run()} | |
| placeholder={tool.placeholder} | |
| className="flex-1 input-field text-xs font-mono" | |
| /> | |
| )} | |
| <div className="flex flex-col gap-1"> | |
| <button | |
| onClick={run} | |
| disabled={!argument.trim() || loading} | |
| className="btn-accent text-xs py-2 px-3 flex items-center gap-1" | |
| > | |
| {loading ? <Loader2 size={12} className="animate-spin" /> : <Play size={12} />} | |
| Run | |
| </button> | |
| <button | |
| onClick={loadExample} | |
| className="btn-ghost text-[10px] py-1 px-2" | |
| > | |
| Example | |
| </button> | |
| </div> | |
| </div> | |
| {/* Result */} | |
| <AnimatePresence> | |
| {result && ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 4 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className={clsx( | |
| 'panel p-3 rounded-lg', | |
| result.success ? 'border-jarvis-success/20' : 'border-jarvis-error/20', | |
| )} | |
| > | |
| <div className="flex items-center gap-1.5 mb-2"> | |
| {result.success | |
| ? <CheckCircle size={12} className="text-jarvis-success" /> | |
| : <XCircle size={12} className="text-jarvis-error" />} | |
| <span className={clsx( | |
| 'text-[10px] font-mono uppercase tracking-widest', | |
| result.success ? 'text-jarvis-success' : 'text-jarvis-error', | |
| )}> | |
| {result.success ? 'Success' : 'Error'} | |
| </span> | |
| </div> | |
| {activeTool === 'code_executor' ? ( | |
| <SyntaxHighlighter | |
| language="text" | |
| style={oneDark} | |
| customStyle={{ margin: 0, padding: '8px', background: '#070c18', fontSize: '11px', borderRadius: '6px' }} | |
| > | |
| {result.result} | |
| </SyntaxHighlighter> | |
| ) : ( | |
| <p className="text-xs text-jarvis-text font-mono whitespace-pre-wrap"> | |
| {result.result} | |
| </p> | |
| )} | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| ) | |
| } | |