synor/apps/desktop-wallet/src/pages/CLI/CliDashboard.tsx
Gulshan Yadav f08eb965c2 a
2026-02-02 15:00:13 +05:30

183 lines
6.3 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
import { Terminal, Info, Send, Loader2 } from 'lucide-react';
import { useCliStore } from '../../store/cli';
export default function CliDashboard() {
const {
history,
isExecuting,
execute,
loadHistory,
clearOutput,
navigateHistory,
} = useCliStore();
const [input, setInput] = useState('');
const outputRef = useRef<HTMLDivElement>(null);
useEffect(() => {
loadHistory();
}, [loadHistory]);
// Auto-scroll to bottom when history changes
useEffect(() => {
if (outputRef.current) {
outputRef.current.scrollTop = outputRef.current.scrollHeight;
}
}, [history]);
const handleCommand = async () => {
if (!input.trim() || isExecuting) return;
const cmd = input.trim();
setInput('');
await execute(cmd);
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleCommand();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
const prev = navigateHistory('up');
if (prev !== null) setInput(prev);
} else if (e.key === 'ArrowDown') {
e.preventDefault();
const next = navigateHistory('down');
if (next !== null) setInput(next);
}
};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold flex items-center gap-3">
<Terminal className="text-synor-400" />
CLI Mode
</h1>
<p className="text-gray-400 mt-1">Terminal interface for power users</p>
</div>
<button
onClick={clearOutput}
className="px-3 py-1 bg-gray-800 rounded text-sm hover:bg-gray-700"
>
Clear
</button>
</div>
<div className="bg-gray-900 rounded-xl border border-gray-800 overflow-hidden">
<div
ref={outputRef}
className="p-4 font-mono text-sm bg-black/50 h-96 overflow-y-auto"
>
{/* Welcome message if no history */}
{history.length === 0 && (
<div className="text-gray-500">
<p className="text-synor-400">{'>'} Welcome to Synor CLI Mode</p>
<p className="text-synor-400">{'>'} Type "help" for available commands</p>
<p className="text-synor-400">{'>'}</p>
</div>
)}
{/* Command history */}
{history.map((result, i) => (
<div key={i} className="mb-2">
<div className="text-synor-400">
{'>'} {result.command}
</div>
<div className={`whitespace-pre-wrap ${result.isError ? 'text-red-400' : 'text-gray-300'}`}>
{result.output}
</div>
</div>
))}
{/* Loading indicator */}
{isExecuting && (
<div className="flex items-center gap-2 text-gray-500">
<Loader2 size={14} className="animate-spin" />
<span>Executing...</span>
</div>
)}
</div>
<div className="border-t border-gray-800 p-3 flex gap-2">
<span className="text-synor-400 font-mono">{'>'}</span>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Enter command..."
className="flex-1 bg-transparent text-white font-mono outline-none"
autoFocus
disabled={isExecuting}
/>
<button
onClick={handleCommand}
disabled={isExecuting || !input.trim()}
className="p-2 bg-synor-600 rounded-lg hover:bg-synor-700 disabled:opacity-50"
>
{isExecuting ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
</button>
</div>
</div>
{/* Quick Commands */}
<div className="bg-gray-900 rounded-xl p-4 border border-gray-800">
<h3 className="font-medium mb-3">Quick Commands</h3>
<div className="flex flex-wrap gap-2">
{['help', 'balance', 'address', 'status', 'utxos', 'peers'].map((cmd) => (
<button
key={cmd}
onClick={() => { setInput(cmd); }}
className="px-3 py-1 bg-gray-800 rounded font-mono text-sm hover:bg-gray-700"
>
{cmd}
</button>
))}
</div>
</div>
{/* Command Reference */}
<div className="bg-gray-900 rounded-xl p-4 border border-gray-800">
<h3 className="font-medium mb-3">Command Reference</h3>
<div className="grid grid-cols-2 gap-2 text-sm">
<div className="font-mono text-synor-400">help</div>
<div className="text-gray-400">Show all available commands</div>
<div className="font-mono text-synor-400">balance</div>
<div className="text-gray-400">Show wallet balance</div>
<div className="font-mono text-synor-400">address</div>
<div className="text-gray-400">Show wallet addresses</div>
<div className="font-mono text-synor-400">send &lt;addr&gt; &lt;amount&gt;</div>
<div className="text-gray-400">Send SYN to address</div>
<div className="font-mono text-synor-400">utxos</div>
<div className="text-gray-400">List unspent outputs</div>
<div className="font-mono text-synor-400">history</div>
<div className="text-gray-400">Transaction history</div>
<div className="font-mono text-synor-400">status</div>
<div className="text-gray-400">Network status</div>
<div className="font-mono text-synor-400">peers</div>
<div className="text-gray-400">Connected peers</div>
<div className="font-mono text-synor-400">clear</div>
<div className="text-gray-400">Clear output</div>
</div>
</div>
<div className="bg-gray-900/50 rounded-lg p-4 border border-gray-800 flex items-start gap-3">
<Info className="text-gray-500 mt-0.5" size={18} />
<p className="text-sm text-gray-400">
CLI mode provides a terminal interface for advanced users who prefer keyboard-driven
interaction. Use / arrows to navigate command history.
</p>
</div>
</div>
);
}