repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
A Autonomous Ai Agent called Leone Ai
stars
latest
clone command
git clone gitlawb://did:key:z6MkhS7a...kPdV/a-autonomous-ai...git clone gitlawb://did:key:z6MkhS7a.../a-autonomous-ai...ff8aa704sync from playground20h ago| #1 | import { useState, useRef, useEffect, useCallback } from "react"; |
| #2 | |
| #3 | /* ── Types ── */ |
| #4 | interface Message { |
| #5 | id: string; |
| #6 | role: "user" | "agent"; |
| #7 | content: string; |
| #8 | timestamp: Date; |
| #9 | } |
| #10 | |
| #11 | interface Conversation { |
| #12 | id: string; |
| #13 | title: string; |
| #14 | messages: Message[]; |
| #15 | createdAt: Date; |
| #16 | } |
| #17 | |
| #18 | interface TaskLog { |
| #19 | id: string; |
| #20 | text: string; |
| #21 | status: "running" | "done"; |
| #22 | timestamp: Date; |
| #23 | } |
| #24 | |
| #25 | interface DeployedAgent { |
| #26 | id: string; |
| #27 | name: string; |
| #28 | chain: string; |
| #29 | status: "active" | "deploying" | "paused"; |
| #30 | txHash: string; |
| #31 | balance: string; |
| #32 | calls: number; |
| #33 | deployedAt: Date; |
| #34 | } |
| #35 | |
| #36 | interface NetworkInfo { |
| #37 | chain: string; |
| #38 | chainId: number; |
| #39 | blockNumber: number; |
| #40 | gasPrice: string; |
| #41 | rpcStatus: "connected" | "degraded" | "disconnected"; |
| #42 | latency: number; |
| #43 | } |
| #44 | |
| #45 | /* ── Constants ── */ |
| #46 | const CAPABILITIES = [ |
| #47 | "Research", |
| #48 | "Code Generation", |
| #49 | "Data Analysis", |
| #50 | "Onchain Deploy", |
| #51 | "Smart Contracts", |
| #52 | "Agent Scaling", |
| #53 | "Cross-Chain", |
| #54 | "Staking", |
| #55 | "Task Planning", |
| #56 | "Problem Solving", |
| #57 | ]; |
| #58 | |
| #59 | const SUGGESTIONS = [ |
| #60 | { title: "Deploy an agent onchain", desc: "Launch a decentralized AI agent to Ethereum" }, |
| #61 | { title: "Scale my agents", desc: "Auto-scale agents across multiple chains" }, |
| #62 | { title: "Analyze onchain data", desc: "Parse transactions and find patterns" }, |
| #63 | { title: "Stake & earn rewards", desc: "Stake LEO tokens to power your agents" }, |
| #64 | ]; |
| #65 | |
| #66 | const MOCK_CHAINS = ["Ethereum", "Base", "Arbitrum", "Polygon", "Optimism"]; |
| #67 | |
| #68 | const MOCK_DEPLOYED: DeployedAgent[] = [ |
| #69 | { |
| #70 | id: "ag-1", |
| #71 | name: "Research Bot Alpha", |
| #72 | chain: "Ethereum", |
| #73 | status: "active", |
| #74 | txHash: "0x7a3f...e91c", |
| #75 | balance: "2.45 ETH", |
| #76 | calls: 12847, |
| #77 | deployedAt: new Date(Date.now() - 86400000 * 12), |
| #78 | }, |
| #79 | { |
| #80 | id: "ag-2", |
| #81 | name: "Data Scraper v2", |
| #82 | chain: "Base", |
| #83 | status: "active", |
| #84 | txHash: "0x3b8d...4f2a", |
| #85 | balance: "0.82 ETH", |
| #86 | calls: 5391, |
| #87 | deployedAt: new Date(Date.now() - 86400000 * 5), |
| #88 | }, |
| #89 | { |
| #90 | id: "ag-3", |
| #91 | name: "DeFi Monitor", |
| #92 | chain: "Arbitrum", |
| #93 | status: "paused", |
| #94 | txHash: "0x9e1c...7b3d", |
| #95 | balance: "1.10 ETH", |
| #96 | calls: 28103, |
| #97 | deployedAt: new Date(Date.now() - 86400000 * 30), |
| #98 | }, |
| #99 | ]; |
| #100 | |
| #101 | const AGENT_RESPONSES: Record<string, string> = { |
| #102 | default: `I've analyzed your request and here's my approach: |
| #103 | |
| #104 | **Step 1:** Understand the requirements and scope |
| #105 | **Step 2:** Break down the task into manageable subtasks |
| #106 | **Step 3:** Execute each subtask with appropriate tools |
| #107 | **Step 4:** Validate results and iterate if needed |
| #108 | |
| #109 | I'm ready to proceed. Would you like me to start with any specific aspect?`, |
| #110 | |
| #111 | code: `Here's a solution I've prepared: |
| #112 | |
| #113 | \`\`\`typescript |
| #114 | // Core implementation |
| #115 | interface AgentTask { |
| #116 | id: string; |
| #117 | type: string; |
| #118 | status: "pending" | "active" | "complete"; |
| #119 | execute(): Promise<Result>; |
| #120 | } |
| #121 | |
| #122 | class LeoneAgent implements AgentTask { |
| #123 | constructor( |
| #124 | public id: string, |
| #125 | public type: string, |
| #126 | public status: "pending" = "pending" |
| #127 | ) {} |
| #128 | |
| #129 | async execute(): Promise<Result> { |
| #130 | this.status = "active"; |
| #131 | const result = await this.process(); |
| #132 | this.status = "complete"; |
| #133 | return result; |
| #134 | } |
| #135 | } |
| #136 | \`\`\` |
| #137 | |
| #138 | This gives us a solid foundation. Shall I expand on any part?`, |
| #139 | |
| #140 | research: `Based on my research, here are the key findings: |
| #141 | |
| #142 | 1. **Architecture Pattern:** Microservices with event-driven communication provides the best scalability |
| #143 | 2. **Technology Stack:** TypeScript + Node.js offers strong typing and ecosystem support |
| #144 | 3. **Best Practices:** Implement circuit breakers and retry logic for resilience |
| #145 | |
| #146 | **Recommendation:** Start with a monolith and extract services as needed. This avoids premature complexity while maintaining a clear upgrade path. |
| #147 | |
| #148 | Want me to dive deeper into any of these areas?`, |
| #149 | |
| #150 | analysis: `Here's my analysis summary: |
| #151 | |
| #152 | | Metric | Value | Trend | |
| #153 | |--------|-------|-------| |
| #154 | | Performance | 94.2% | +2.1% | |
| #155 | | Error Rate | 0.3% | -0.1% | |
| #156 | | Throughput | 1.2k/s | +150/s | |
| #157 | |
| #158 | **Key Insights:** |
| #159 | - Response times improved by 12% after the last optimization |
| #160 | - Memory usage is stable at ~512MB under load |
| #161 | - No critical bottlenecks identified |
| #162 | |
| #163 | I can generate a detailed report if needed.`, |
| #164 | |
| #165 | deploy: `**Deployment initiated for onchain agent:** |
| #166 | |
| #167 | \`\`\` |
| #168 | ┌─────────────────────────────────────┐ |
| #169 | │ Agent Deployment Pipeline │ |
| #170 | ├─────────────────────────────────────┤ |
| #171 | │ 1. Compile agent bytecode ✓ │ |
| #172 | │ 2. Deploy proxy contract ✓ │ |
| #173 | │ 3. Register on Agent Registry ✓ │ |
| #174 | │ 4. Fund gas reservoir ✓ │ |
| #175 | │ 5. Activate inference loop ✓ │ |
| #176 | └─────────────────────────────────────┘ |
| #177 | \`\`\` |
| #178 | |
| #179 | **Details:** |
| #180 | - **Chain:** Ethereum Mainnet |
| #181 | - **Contract:** \`0x7a3f...e91c\` |
| #182 | - **Gas Used:** 0.012 ETH |
| #183 | - **Status:** Active & processing tasks |
| #184 | |
| #185 | Your agent is now live onchain and ready to accept autonomous tasks. It will self-fund gas from its reservoir and can be scaled to additional chains.`, |
| #186 | |
| #187 | scale: `**Multi-chain scaling analysis complete:** |
| #188 | |
| #189 | Your agents are currently deployed across **3 chains**. Here's the scaling recommendation: |
| #190 | |
| #191 | | Chain | Current Load | Capacity | Action | |
| #192 | |-------|-------------|----------|--------| |
| #193 | | Ethereum | 78% | High | Optimize gas | |
| #194 | | Base | 45% | Very High | No action | |
| #195 | | Arbitrum | 92% | Medium | Scale up | |
| #196 | |
| #197 | **Recommended Actions:** |
| #198 | 1. Deploy 2 additional agent replicas on Arbitrum |
| #199 | 2. Enable cross-chain task routing for load balancing |
| #200 | 3. Set up automated failover between Ethereum and Base |
| #201 | |
| #202 | Shall I execute the scaling plan?`, |
| #203 | |
| #204 | stake: `**Staking Dashboard:** |
| #205 | |
| #206 | \`\`\` |
| #207 | ┌─────────────────────────────────────┐ |
| #208 | │ LEO Token Staking Summary │ |
| #209 | ├─────────────────────────────────────┤ |
| #210 | │ Staked: 5,000 LEO │ |
| #211 | │ Rewards: 127.5 LEO (2.55%) │ |
| #212 | │ Unbonding: 0 LEO │ |
| #213 | │ Agent Power: ████████░░ 80% │ |
| #214 | └─────────────────────────────────────┘ |
| #215 | \`\`\` |
| #216 | |
| #217 | **Staking Benefits:** |
| #218 | - Higher agent compute priority |
| #219 | - Reduced gas fees on Leone infrastructure |
| #220 | - Governance voting rights |
| #221 | - Revenue share from network fees |
| #222 | |
| #223 | Would you like to stake more LEO or adjust your delegation?`, |
| #224 | }; |
| #225 | |
| #226 | /* ── Helpers ── */ |
| #227 | function generateId(): string { |
| #228 | return Math.random().toString(36).substring(2, 10); |
| #229 | } |
| #230 | |
| #231 | function formatTime(date: Date): string { |
| #232 | return date.toLocaleTimeString("en-US", { |
| #233 | hour: "2-digit", |
| #234 | minute: "2-digit", |
| #235 | }); |
| #236 | } |
| #237 | |
| #238 | function formatDate(date: Date): string { |
| #239 | return date.toLocaleDateString("en-US", { |
| #240 | month: "short", |
| #241 | day: "numeric", |
| #242 | }); |
| #243 | } |
| #244 | |
| #245 | function getAgentResponse(input: string): string { |
| #246 | const lower = input.toLowerCase(); |
| #247 | if ( |
| #248 | lower.includes("deploy") || |
| #249 | lower.includes("launch") || |
| #250 | lower.includes("onchain") || |
| #251 | lower.includes("on-chain") || |
| #252 | lower.includes("contract") |
| #253 | ) { |
| #254 | return AGENT_RESPONSES.deploy; |
| #255 | } |
| #256 | if ( |
| #257 | lower.includes("scale") || |
| #258 | lower.includes("replica") || |
| #259 | lower.includes("multi-chain") || |
| #260 | lower.includes("cross-chain") |
| #261 | ) { |
| #262 | return AGENT_RESPONSES.scale; |
| #263 | } |
| #264 | if ( |
| #265 | lower.includes("stake") || |
| #266 | lower.includes("leo") || |
| #267 | lower.includes("reward") || |
| #268 | lower.includes("earn") |
| #269 | ) { |
| #270 | return AGENT_RESPONSES.stake; |
| #271 | } |
| #272 | if ( |
| #273 | lower.includes("code") || |
| #274 | lower.includes("build") || |
| #275 | lower.includes("implement") || |
| #276 | lower.includes("function") |
| #277 | ) { |
| #278 | return AGENT_RESPONSES.code; |
| #279 | } |
| #280 | if ( |
| #281 | lower.includes("research") || |
| #282 | lower.includes("find") || |
| #283 | lower.includes("compare") || |
| #284 | lower.includes("analyze") |
| #285 | ) { |
| #286 | return AGENT_RESPONSES.research; |
| #287 | } |
| #288 | if ( |
| #289 | lower.includes("data") || |
| #290 | lower.includes("metrics") || |
| #291 | lower.includes("performance") || |
| #292 | lower.includes("report") |
| #293 | ) { |
| #294 | return AGENT_RESPONSES.analysis; |
| #295 | } |
| #296 | return AGENT_RESPONSES.default; |
| #297 | } |
| #298 | |
| #299 | /* ── Network Status Hook ── */ |
| #300 | function useNetworkInfo(): NetworkInfo { |
| #301 | const [info, setInfo] = useState<NetworkInfo>({ |
| #302 | chain: "Ethereum", |
| #303 | chainId: 1, |
| #304 | blockNumber: 19_847_321, |
| #305 | gasPrice: "12.4", |
| #306 | rpcStatus: "connected", |
| #307 | latency: 42, |
| #308 | }); |
| #309 | |
| #310 | useEffect(() => { |
| #311 | const interval = setInterval(() => { |
| #312 | setInfo((prev) => ({ |
| #313 | ...prev, |
| #314 | blockNumber: prev.blockNumber + Math.floor(Math.random() * 3), |
| #315 | gasPrice: (10 + Math.random() * 8).toFixed(1), |
| #316 | latency: 30 + Math.floor(Math.random() * 40), |
| #317 | })); |
| #318 | }, 4000); |
| #319 | return () => clearInterval(interval); |
| #320 | }, []); |
| #321 | |
| #322 | return info; |
| #323 | } |
| #324 | |
| #325 | /* ── Components ── */ |
| #326 | |
| #327 | function NetworkStatusBar({ network }: { network: NetworkInfo }) { |
| #328 | return ( |
| #329 | <div className="network-bar"> |
| #330 | <div className="network-item"> |
| #331 | <span className={`net-dot ${network.rpcStatus}`} /> |
| #332 | <span className="net-label">{network.chain}</span> |
| #333 | </div> |
| #334 | <div className="network-item"> |
| #335 | <span className="net-label dim">Block</span> |
| #336 | <span className="net-value">#{network.blockNumber.toLocaleString()}</span> |
| #337 | </div> |
| #338 | <div className="network-item"> |
| #339 | <span className="net-label dim">Gas</span> |
| #340 | <span className="net-value">{network.gasPrice} gwei</span> |
| #341 | </div> |
| #342 | <div className="network-item"> |
| #343 | <span className="net-label dim">Latency</span> |
| #344 | <span className="net-value">{network.latency}ms</span> |
| #345 | </div> |
| #346 | </div> |
| #347 | ); |
| #348 | } |
| #349 | |
| #350 | function DeployedAgentsPanel({ |
| #351 | agents, |
| #352 | onSelect, |
| #353 | }: { |
| #354 | agents: DeployedAgent[]; |
| #355 | onSelect: (name: string) => void; |
| #356 | }) { |
| #357 | return ( |
| #358 | <div className="deployed-panel"> |
| #359 | <div className="deployed-header"> |
| #360 | <span className="deployed-title">Deployed Agents</span> |
| #361 | <span className="deployed-count">{agents.length}</span> |
| #362 | </div> |
| #363 | <div className="deployed-list"> |
| #364 | {agents.map((agent) => ( |
| #365 | <div |
| #366 | key={agent.id} |
| #367 | className="deployed-item" |
| #368 | onClick={() => onSelect(agent.name)} |
| #369 | > |
| #370 | <div className="deployed-row-top"> |
| #371 | <span className="deployed-name">{agent.name}</span> |
| #372 | <span className={`deployed-status ${agent.status}`}> |
| #373 | {agent.status} |
| #374 | </span> |
| #375 | </div> |
| #376 | <div className="deployed-row-bottom"> |
| #377 | <span className="deployed-chain">{agent.chain}</span> |
| #378 | <span className="deployed-balance">{agent.balance}</span> |
| #379 | <span className="deployed-calls">{(agent.calls / 1000).toFixed(1)}k calls</span> |
| #380 | </div> |
| #381 | </div> |
| #382 | ))} |
| #383 | </div> |
| #384 | </div> |
| #385 | ); |
| #386 | } |
| #387 | |
| #388 | function ChainSelector({ |
| #389 | chains, |
| #390 | active, |
| #391 | onSelect, |
| #392 | }: { |
| #393 | chains: string[]; |
| #394 | active: string; |
| #395 | onSelect: (c: string) => void; |
| #396 | }) { |
| #397 | return ( |
| #398 | <div className="chain-selector"> |
| #399 | {chains.map((chain) => ( |
| #400 | <button |
| #401 | key={chain} |
| #402 | className={`chain-btn ${chain === active ? "active" : ""}`} |
| #403 | onClick={() => onSelect(chain)} |
| #404 | > |
| #405 | {chain} |
| #406 | </button> |
| #407 | ))} |
| #408 | </div> |
| #409 | ); |
| #410 | } |
| #411 | |
| #412 | /* ── Main App ── */ |
| #413 | export default function App() { |
| #414 | const [conversations, setConversations] = useState<Conversation[]>([ |
| #415 | { |
| #416 | id: "1", |
| #417 | title: "New conversation", |
| #418 | messages: [], |
| #419 | createdAt: new Date(), |
| #420 | }, |
| #421 | ]); |
| #422 | const [activeConvId, setActiveConvId] = useState("1"); |
| #423 | const [input, setInput] = useState(""); |
| #424 | const [isThinking, setIsThinking] = useState(false); |
| #425 | const [agentStatus, setAgentStatus] = useState<"idle" | "thinking" | "error">("idle"); |
| #426 | const [taskLogs, setTaskLogs] = useState<TaskLog[]>([]); |
| #427 | const [view, setView] = useState<"chat" | "agents" | "network">("chat"); |
| #428 | const [activeChain, setActiveChain] = useState("Ethereum"); |
| #429 | const [deployedAgents] = useState<DeployedAgent[]>(MOCK_DEPLOYED); |
| #430 | const chatEndRef = useRef<HTMLDivElement>(null); |
| #431 | const textareaRef = useRef<HTMLTextAreaElement>(null); |
| #432 | const network = useNetworkInfo(); |
| #433 | |
| #434 | const activeConv = conversations.find((c) => c.id === activeConvId); |
| #435 | |
| #436 | const scrollToBottom = useCallback(() => { |
| #437 | chatEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| #438 | }, []); |
| #439 | |
| #440 | useEffect(() => { |
| #441 | scrollToBottom(); |
| #442 | }, [activeConv?.messages, isThinking, scrollToBottom]); |
| #443 | |
| #444 | useEffect(() => { |
| #445 | if (textareaRef.current) { |
| #446 | textareaRef.current.style.height = "auto"; |
| #447 | textareaRef.current.style.height = |
| #448 | Math.min(textareaRef.current.scrollHeight, 150) + "px"; |
| #449 | } |
| #450 | }, [input]); |
| #451 | |
| #452 | const sendMessage = useCallback( |
| #453 | (text?: string) => { |
| #454 | const content = (text || input).trim(); |
| #455 | if (!content || isThinking) return; |
| #456 | |
| #457 | const userMsg: Message = { |
| #458 | id: generateId(), |
| #459 | role: "user", |
| #460 | content, |
| #461 | timestamp: new Date(), |
| #462 | }; |
| #463 | |
| #464 | setConversations((prev) => |
| #465 | prev.map((c) => |
| #466 | c.id === activeConvId |
| #467 | ? { |
| #468 | ...c, |
| #469 | messages: [...c.messages, userMsg], |
| #470 | title: |
| #471 | c.messages.length === 0 |
| #472 | ? content.slice(0, 40) + (content.length > 40 ? "..." : "") |
| #473 | : c.title, |
| #474 | } |
| #475 | : c |
| #476 | ) |
| #477 | ); |
| #478 | setInput(""); |
| #479 | setIsThinking(true); |
| #480 | setAgentStatus("thinking"); |
| #481 | setView("chat"); |
| #482 | |
| #483 | const taskId = generateId(); |
| #484 | setTaskLogs((prev) => [ |
| #485 | ...prev, |
| #486 | { |
| #487 | id: taskId, |
| #488 | text: `Processing: "${content.slice(0, 30)}..."`, |
| #489 | status: "running", |
| #490 | timestamp: new Date(), |
| #491 | }, |
| #492 | ]); |
| #493 | |
| #494 | const delay = 800 + Math.random() * 1200; |
| #495 | setTimeout(() => { |
| #496 | const agentMsg: Message = { |
| #497 | id: generateId(), |
| #498 | role: "agent", |
| #499 | content: getAgentResponse(content), |
| #500 | timestamp: new Date(), |
| #501 | }; |
| #502 | |
| #503 | setConversations((prev) => |
| #504 | prev.map((c) => |
| #505 | c.id === activeConvId |
| #506 | ? { ...c, messages: [...c.messages, agentMsg] } |
| #507 | : c |
| #508 | ) |
| #509 | ); |
| #510 | setIsThinking(false); |
| #511 | setAgentStatus("idle"); |
| #512 | setTaskLogs((prev) => |
| #513 | prev.map((t) => (t.id === taskId ? { ...t, status: "done" } : t)) |
| #514 | ); |
| #515 | }, delay); |
| #516 | }, |
| #517 | [input, isThinking, activeConvId] |
| #518 | ); |
| #519 | |
| #520 | const newConversation = () => { |
| #521 | const conv: Conversation = { |
| #522 | id: generateId(), |
| #523 | title: "New conversation", |
| #524 | messages: [], |
| #525 | createdAt: new Date(), |
| #526 | }; |
| #527 | setConversations((prev) => [conv, ...prev]); |
| #528 | setActiveConvId(conv.id); |
| #529 | setTaskLogs([]); |
| #530 | setView("chat"); |
| #531 | }; |
| #532 | |
| #533 | const handleKeyDown = (e: React.KeyboardEvent) => { |
| #534 | if (e.key === "Enter" && !e.shiftKey) { |
| #535 | e.preventDefault(); |
| #536 | sendMessage(); |
| #537 | } |
| #538 | }; |
| #539 | |
| #540 | return ( |
| #541 | <div className="app"> |
| #542 | {/* ── Sidebar ── */} |
| #543 | <aside className="sidebar"> |
| #544 | <div className="sidebar-header"> |
| #545 | <div className="logo-icon">L</div> |
| #546 | <div className="logo-text"> |
| #547 | <h1>Leone AI</h1> |
| #548 | <span>Decentralized Agent Platform</span> |
| #549 | </div> |
| #550 | </div> |
| #551 | |
| #552 | <button className="new-chat-btn" onClick={newConversation}> |
| #553 | <span>+</span> New Chat |
| #554 | </button> |
| #555 | |
| #556 | <ChainSelector |
| #557 | chains={MOCK_CHAINS} |
| #558 | active={activeChain} |
| #559 | onSelect={setActiveChain} |
| #560 | /> |
| #561 | |
| #562 | <div className="sidebar-nav"> |
| #563 | <button |
| #564 | className={`nav-btn ${view === "chat" ? "active" : ""}`} |
| #565 | onClick={() => setView("chat")} |
| #566 | > |
| #567 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #568 | <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /> |
| #569 | </svg> |
| #570 | Chat |
| #571 | </button> |
| #572 | <button |
| #573 | className={`nav-btn ${view === "agents" ? "active" : ""}`} |
| #574 | onClick={() => setView("agents")} |
| #575 | > |
| #576 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #577 | <rect x="2" y="7" width="20" height="14" rx="2" ry="2" /> |
| #578 | <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" /> |
| #579 | </svg> |
| #580 | Agents |
| #581 | </button> |
| #582 | <button |
| #583 | className={`nav-btn ${view === "network" ? "active" : ""}`} |
| #584 | onClick={() => setView("network")} |
| #585 | > |
| #586 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #587 | <circle cx="12" cy="12" r="10" /> |
| #588 | <line x1="2" y1="12" x2="22" y2="12" /> |
| #589 | <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /> |
| #590 | </svg> |
| #591 | Network |
| #592 | </button> |
| #593 | </div> |
| #594 | |
| #595 | <div className="conversations-label">Conversations</div> |
| #596 | <div className="conversations-list"> |
| #597 | {conversations.map((conv) => ( |
| #598 | <div |
| #599 | key={conv.id} |
| #600 | className={`conv-item ${conv.id === activeConvId && view === "chat" ? "active" : ""}`} |
| #601 | onClick={() => { |
| #602 | setActiveConvId(conv.id); |
| #603 | setView("chat"); |
| #604 | }} |
| #605 | > |
| #606 | <span className="conv-icon"> |
| #607 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #608 | <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /> |
| #609 | </svg> |
| #610 | </span> |
| #611 | <span className="conv-title">{conv.title}</span> |
| #612 | </div> |
| #613 | ))} |
| #614 | </div> |
| #615 | |
| #616 | <DeployedAgentsPanel |
| #617 | agents={deployedAgents} |
| #618 | onSelect={(name) => sendMessage(`Tell me about agent: ${name}`)} |
| #619 | /> |
| #620 | |
| #621 | <div className="agent-status"> |
| #622 | <span className={`status-dot ${agentStatus}`} /> |
| #623 | <span> |
| #624 | {agentStatus === "idle" |
| #625 | ? "Agent ready" |
| #626 | : agentStatus === "thinking" |
| #627 | ? "Thinking..." |
| #628 | : "Error occurred"} |
| #629 | </span> |
| #630 | </div> |
| #631 | </aside> |
| #632 | |
| #633 | {/* ── Main Content ── */} |
| #634 | <main className="main-content"> |
| #635 | <header className="header"> |
| #636 | <span className="header-title"> |
| #637 | {view === "chat" |
| #638 | ? activeConv?.title || "Leone AI" |
| #639 | : view === "agents" |
| #640 | ? "Deployed Agents" |
| #641 | : "Network Status"} |
| #642 | </span> |
| #643 | <div className="header-actions"> |
| #644 | <button className="header-btn" onClick={() => sendMessage("Deploy an agent onchain")}> |
| #645 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #646 | <path d="M12 5v14M5 12h14" /> |
| #647 | </svg> |
| #648 | Deploy Agent |
| #649 | </button> |
| #650 | <button className="header-btn"> |
| #651 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #652 | <circle cx="12" cy="12" r="3" /> |
| #653 | <path d="M12 1v6m0 6v6M4.22 4.22l4.24 4.24m7.08 7.08l4.24 4.24M1 12h6m6 0h6M4.22 19.78l4.24-4.24m7.08-7.08l4.24-4.24" /> |
| #654 | </svg> |
| #655 | Settings |
| #656 | </button> |
| #657 | </div> |
| #658 | </header> |
| #659 | |
| #660 | <NetworkStatusBar network={network} /> |
| #661 | |
| #662 | <div className="capabilities-bar"> |
| #663 | {CAPABILITIES.map((cap) => ( |
| #664 | <span |
| #665 | key={cap} |
| #666 | className="cap-tag" |
| #667 | onClick={() => sendMessage(cap)} |
| #668 | > |
| #669 | {cap} |
| #670 | </span> |
| #671 | ))} |
| #672 | </div> |
| #673 | |
| #674 | {/* ── Agents View ── */} |
| #675 | {view === "agents" && ( |
| #676 | <div className="agents-view"> |
| #677 | <div className="agents-grid"> |
| #678 | {deployedAgents.map((agent) => ( |
| #679 | <div key={agent.id} className="agent-card"> |
| #680 | <div className="agent-card-header"> |
| #681 | <div className="agent-card-icon"> |
| #682 | {agent.name.charAt(0)} |
| #683 | </div> |
| #684 | <div className="agent-card-info"> |
| #685 | <h3>{agent.name}</h3> |
| #686 | <span className="agent-card-chain">{agent.chain}</span> |
| #687 | </div> |
| #688 | <span className={`agent-card-status ${agent.status}`}> |
| #689 | {agent.status} |
| #690 | </span> |
| #691 | </div> |
| #692 | <div className="agent-card-stats"> |
| #693 | <div className="agent-stat"> |
| #694 | <span className="agent-stat-label">Balance</span> |
| #695 | <span className="agent-stat-value">{agent.balance}</span> |
| #696 | </div> |
| #697 | <div className="agent-stat"> |
| #698 | <span className="agent-stat-label">Total Calls</span> |
| #699 | <span className="agent-stat-value">{agent.calls.toLocaleString()}</span> |
| #700 | </div> |
| #701 | <div className="agent-stat"> |
| #702 | <span className="agent-stat-label">Contract</span> |
| #703 | <span className="agent-stat-value mono">{agent.txHash}</span> |
| #704 | </div> |
| #705 | <div className="agent-stat"> |
| #706 | <span className="agent-stat-label">Deployed</span> |
| #707 | <span className="agent-stat-value">{formatDate(agent.deployedAt)}</span> |
| #708 | </div> |
| #709 | </div> |
| #710 | <div className="agent-card-actions"> |
| #711 | <button |
| #712 | className="agent-action-btn" |
| #713 | onClick={() => sendMessage(`Scale agent ${agent.name}`)} |
| #714 | > |
| #715 | Scale |
| #716 | </button> |
| #717 | <button |
| #718 | className="agent-action-btn secondary" |
| #719 | onClick={() => sendMessage(`Pause agent ${agent.name}`)} |
| #720 | > |
| #721 | {agent.status === "paused" ? "Resume" : "Pause"} |
| #722 | </button> |
| #723 | </div> |
| #724 | </div> |
| #725 | ))} |
| #726 | <div className="agent-card add-card" onClick={() => sendMessage("Deploy a new agent")}> |
| #727 | <div className="add-card-content"> |
| #728 | <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"> |
| #729 | <path d="M12 5v14M5 12h14" /> |
| #730 | </svg> |
| #731 | <span>Deploy New Agent</span> |
| #732 | </div> |
| #733 | </div> |
| #734 | </div> |
| #735 | </div> |
| #736 | )} |
| #737 | |
| #738 | {/* ── Network View ── */} |
| #739 | {view === "network" && ( |
| #740 | <div className="network-view"> |
| #741 | <div className="network-overview"> |
| #742 | <div className="network-stat-card"> |
| #743 | <span className="net-stat-label">Active Agents</span> |
| #744 | <span className="net-stat-value">3</span> |
| #745 | <span className="net-stat-change positive">+1 this week</span> |
| #746 | </div> |
| #747 | <div className="network-stat-card"> |
| #748 | <span className="net-stat-label">Total Calls (24h)</span> |
| #749 | <span className="net-stat-value">46,341</span> |
| #750 | <span className="net-stat-change positive">+12.3%</span> |
| #751 | </div> |
| #752 | <div className="network-stat-card"> |
| #753 | <span className="net-stat-label">Gas Spent (24h)</span> |
| #754 | <span className="net-stat-value">0.084 ETH</span> |
| #755 | <span className="net-stat-change negative">+5.1%</span> |
| #756 | </div> |
| #757 | <div className="network-stat-card"> |
| #758 | <span className="net-stat-label">Staked LEO</span> |
| #759 | <span className="net-stat-value">5,000</span> |
| #760 | <span className="net-stat-change positive">80% power</span> |
| #761 | </div> |
| #762 | </div> |
| #763 | |
| #764 | <div className="network-chains"> |
| #765 | <h3>Chain Status</h3> |
| #766 | <div className="chain-table"> |
| #767 | <div className="chain-table-header"> |
| #768 | <span>Chain</span> |
| #769 | <span>Status</span> |
| #770 | <span>Agents</span> |
| #771 | <span>Avg Latency</span> |
| #772 | <span>Gas Price</span> |
| #773 | </div> |
| #774 | {MOCK_CHAINS.map((chain) => ( |
| #775 | <div key={chain} className="chain-table-row"> |
| #776 | <span className="chain-name">{chain}</span> |
| #777 | <span className="chain-status"> |
| #778 | <span className="net-dot connected" /> Online |
| #779 | </span> |
| #780 | <span>{chain === "Ethereum" ? 1 : chain === "Base" ? 1 : chain === "Arbitrum" ? 1 : 0}</span> |
| #781 | <span>{30 + Math.floor(Math.random() * 50)}ms</span> |
| #782 | <span>{(10 + Math.random() * 15).toFixed(1)} gwei</span> |
| #783 | </div> |
| #784 | ))} |
| #785 | </div> |
| #786 | </div> |
| #787 | |
| #788 | <div className="network-actions"> |
| #789 | <button |
| #790 | className="net-action-btn" |
| #791 | onClick={() => sendMessage("Deploy an agent onchain")} |
| #792 | > |
| #793 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #794 | <path d="M12 5v14M5 12h14" /> |
| #795 | </svg> |
| #796 | Deploy Agent |
| #797 | </button> |
| #798 | <button |
| #799 | className="net-action-btn" |
| #800 | onClick={() => sendMessage("Stake LEO tokens")} |
| #801 | > |
| #802 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #803 | <path d="M12 2L2 7l10 5 10-5-10-5z" /> |
| #804 | <path d="M2 17l10 5 10-5" /> |
| #805 | <path d="M2 12l10 5 10-5" /> |
| #806 | </svg> |
| #807 | Stake LEO |
| #808 | </button> |
| #809 | <button |
| #810 | className="net-action-btn" |
| #811 | onClick={() => sendMessage("Scale my agents across chains")} |
| #812 | > |
| #813 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #814 | <polyline points="23 6 13.5 15.5 8.5 10.5 1 18" /> |
| #815 | <polyline points="17 6 23 6 23 12" /> |
| #816 | </svg> |
| #817 | Scale Infrastructure |
| #818 | </button> |
| #819 | </div> |
| #820 | </div> |
| #821 | )} |
| #822 | |
| #823 | {/* ── Chat View ── */} |
| #824 | {view === "chat" && ( |
| #825 | <> |
| #826 | {taskLogs.length > 0 && ( |
| #827 | <div className="task-panel"> |
| #828 | {taskLogs.slice(-3).map((task) => ( |
| #829 | <div key={task.id} className="task-item"> |
| #830 | <div className={`task-icon ${task.status}`}> |
| #831 | {task.status === "done" ? ( |
| #832 | <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3"> |
| #833 | <polyline points="20 6 9 17 4 12" /> |
| #834 | </svg> |
| #835 | ) : ( |
| #836 | <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #837 | <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" /> |
| #838 | </svg> |
| #839 | )} |
| #840 | </div> |
| #841 | <span className="task-text">{task.text}</span> |
| #842 | <span className="task-time">{formatTime(task.timestamp)}</span> |
| #843 | </div> |
| #844 | ))} |
| #845 | </div> |
| #846 | )} |
| #847 | |
| #848 | <div className="chat-area"> |
| #849 | {activeConv?.messages.length === 0 ? ( |
| #850 | <div className="welcome"> |
| #851 | <div className="welcome-logo">L</div> |
| #852 | <h2>Leone AI</h2> |
| #853 | <p> |
| #854 | Build, deploy, and scale decentralized AI agents with powerful |
| #855 | onchain infrastructure. Research, code, analyze data, and |
| #856 | execute autonomous tasks across multiple chains. |
| #857 | </p> |
| #858 | <div className="suggestions"> |
| #859 | {SUGGESTIONS.map((s) => ( |
| #860 | <button |
| #861 | key={s.title} |
| #862 | className="suggestion" |
| #863 | onClick={() => sendMessage(s.title)} |
| #864 | > |
| #865 | <strong>{s.title}</strong> |
| #866 | {s.desc} |
| #867 | </button> |
| #868 | ))} |
| #869 | </div> |
| #870 | </div> |
| #871 | ) : ( |
| #872 | <> |
| #873 | {activeConv?.messages.map((msg) => ( |
| #874 | <div key={msg.id} className="message"> |
| #875 | <div className={`message-avatar ${msg.role}`}> |
| #876 | {msg.role === "user" ? "U" : "L"} |
| #877 | </div> |
| #878 | <div className="message-body"> |
| #879 | <div className="message-header"> |
| #880 | {msg.role === "user" ? "You" : "Leone AI"} |
| #881 | <span className="timestamp"> |
| #882 | {formatTime(msg.timestamp)} |
| #883 | </span> |
| #884 | </div> |
| #885 | <div className="message-content"> |
| #886 | {msg.content.split("\n").map((line, i) => ( |
| #887 | <p key={i}>{line || "\u00A0"}</p> |
| #888 | ))} |
| #889 | </div> |
| #890 | </div> |
| #891 | </div> |
| #892 | ))} |
| #893 | {isThinking && ( |
| #894 | <div className="thinking-indicator"> |
| #895 | <div className="message-avatar agent">L</div> |
| #896 | <div> |
| #897 | <div className="thinking-dots"> |
| #898 | <span /> |
| #899 | <span /> |
| #900 | <span /> |
| #901 | </div> |
| #902 | <div className="thinking-label"> |
| #903 | Leone is analyzing onchain data... |
| #904 | </div> |
| #905 | </div> |
| #906 | </div> |
| #907 | )} |
| #908 | </> |
| #909 | )} |
| #910 | <div ref={chatEndRef} /> |
| #911 | </div> |
| #912 | </> |
| #913 | )} |
| #914 | |
| #915 | {/* ── Input Area ── */} |
| #916 | <div className="input-area"> |
| #917 | <div className="input-wrapper"> |
| #918 | <div className="input-container"> |
| #919 | <textarea |
| #920 | ref={textareaRef} |
| #921 | value={input} |
| #922 | onChange={(e) => setInput(e.target.value)} |
| #923 | onKeyDown={handleKeyDown} |
| #924 | placeholder="Ask Leone AI anything — deploy, scale, analyze..." |
| #925 | rows={1} |
| #926 | /> |
| #927 | <button |
| #928 | className="send-btn" |
| #929 | onClick={() => sendMessage()} |
| #930 | disabled={!input.trim() || isThinking} |
| #931 | > |
| #932 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| #933 | <line x1="22" y1="2" x2="11" y2="13" /> |
| #934 | <polygon points="22 2 15 22 11 13 2 9 22 2" /> |
| #935 | </svg> |
| #936 | </button> |
| #937 | </div> |
| #938 | </div> |
| #939 | <div className="input-footer"> |
| #940 | <span className="input-hint"> |
| #941 | <kbd className="kbd">Enter</kbd> to send |
| #942 | <kbd className="kbd">Shift+Enter</kbd> for new line |
| #943 | </span> |
| #944 | <span>Leone AI v2.0 — Decentralized Agent Platform</span> |
| #945 | </div> |
| #946 | </div> |
| #947 | </main> |
| #948 | </div> |
| #949 | ); |
| #950 | } |
| #951 |