Created
April 16, 2026 08:40
-
-
Save iggyughgi-glitch/7cf601cf7932eb7097899ce96625cbbd to your computer and use it in GitHub Desktop.
Nnn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Agent-Flow n8n Edition</title> | |
| <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script> | |
| <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Space+Mono&family=Syne:wght@800&display=swap'); | |
| body { margin: 0; background: #0d0f14; color: #e2e8f0; font-family: 'Space Mono', monospace; font-size: 14px; } | |
| .canvas { padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 20px; } | |
| .node { | |
| background: #181c27; border: 1px solid #252a3a; border-radius: 12px; | |
| padding: 16px; width: 100%; max-width: 400px; position: relative; | |
| transition: all 0.3s ease; box-shadow: 0 4px 20px rgba(0,0,0,0.5); | |
| } | |
| .node-running { border-color: #a78bfa; box-shadow: 0 0 15px rgba(167, 139, 250, 0.3); } | |
| .node-done { border-color: #22d3a0; } | |
| .connector { width: 2px; height: 20px; background: #252a3a; margin: -20px 0; z-index: -1; } | |
| .btn-glow:active { transform: scale(0.98); } | |
| input, textarea, button { background: #13161e; border: 1px solid #252a3a; color: #fff; padding: 12px; border-radius: 8px; margin-bottom: 10px; font-family: inherit; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="root"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect } = React; | |
| function App() { | |
| const [task, setTask] = useState(""); | |
| const [apiKey, setApiKey] = useState(localStorage.getItem("ant_key") || ""); | |
| const [nodes, setNodes] = useState([]); | |
| const [status, setStatus] = useState("idle"); | |
| const callClaude = async (messages, system) => { | |
| const url = `https://corsproxy.io/?${encodeURIComponent("https://api.anthropic.com/v1/messages")}`; | |
| const res = await fetch(url, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json", "x-api-key": apiKey, "anthropic-version": "2023-06-01" }, | |
| body: JSON.stringify({ model: "claude-3-haiku-20240307", max_tokens: 1024, system, messages }) | |
| }); | |
| const data = await res.json(); | |
| if (data.error) throw new Error(data.error.message); | |
| return data.content[0].text; | |
| }; | |
| const runWorkflow = async () => { | |
| if (!apiKey || !task) return alert("Missing Key/Task"); | |
| setStatus("running"); | |
| setNodes([{ id: 't1', type: 'TRIGGER', title: task, status: 'done' }]); | |
| try { | |
| // Planning Step | |
| const pNode = { id: 'p1', type: 'PLANNER', title: "Breaking down task...", status: 'running' }; | |
| setNodes(prev => [...prev, pNode]); | |
| const planRes = await callClaude([{ role: "user", content: task }], "Respond ONLY with a JSON object: { \"steps\": [ { \"title\": \"...\", \"desc\": \"...\" } ] }"); | |
| const plan = JSON.parse(planRes.replace(/```json|```/g, "")).steps; | |
| setNodes(prev => prev.map(n => n.id === 'p1' ? {...n, status: 'done', output: plan.map(s => s.title).join('\n')} : n)); | |
| // Execution Steps | |
| for (let i = 0; i < plan.length; i++) { | |
| const step = plan[i]; | |
| const eNode = { id: `e${i}`, type: 'EXECUTE', title: step.title, status: 'running' }; | |
| setNodes(prev => [...prev, eNode]); | |
| const output = await callClaude([{ role: "user", content: `Step: ${step.title}. Task: ${step.desc}` }], "Execute step briefly."); | |
| setNodes(prev => prev.map(n => n.id === eNode.id ? {...n, status: 'done', output} : n)); | |
| } | |
| setNodes(prev => [...prev, { id: 'done', type: 'RESULT', title: "Workflow Complete", status: 'done' }]); | |
| } catch (e) { | |
| setNodes(prev => [...prev, { id: 'err', type: 'ERROR', title: "Error", output: e.message }]); | |
| } finally { setStatus("idle"); } | |
| }; | |
| return ( | |
| <div className="canvas"> | |
| <div style={{ width: '100%', maxWidth: 400 }}> | |
| <h2 style={{ color: '#3d7fff', fontFamily: 'Syne', textAlign: 'center' }}>AGENT·FLOW</h2> | |
| <input type="password" value={apiKey} placeholder="Paste sk-ant-..." onChange={e => {setApiKey(e.target.value); localStorage.setItem("ant_key", e.target.value)}} /> | |
| <textarea value={task} placeholder="Enter workflow task..." onChange={e => setTask(e.target.value)} /> | |
| <button onClick={runWorkflow} disabled={status === 'running'} style={{ background: '#3d7fff', fontWeight: 'bold' }}> | |
| {status === 'running' ? "EXECUTING..." : "DEPLOY WORKFLOW"} | |
| </button> | |
| </div> | |
| {nodes.map((n, i) => ( | |
| <React.Fragment key={n.id}> | |
| {i > 0 && <div className="connector" />} | |
| <div className={`node ${n.status === 'running' ? 'node-running' : n.status === 'done' ? 'node-done' : ''}`}> | |
| <div style={{ fontSize: '9px', color: '#64748b', fontWeight: 'bold' }}>{n.type}</div> | |
| <div style={{ fontWeight: 'bold', margin: '4px 0' }}>{n.title}</div> | |
| {n.output && <div style={{ fontSize: '11px', color: '#94a3b8', borderTop: '1px solid #252a3a', paddingTop: '8px', marginTop: '8px', whiteSpace: 'pre-wrap' }}>{n.output}</div>} | |
| </div> | |
| </React.Fragment> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| const root = ReactDOM.createRoot(document.getElementById('root')); | |
| root.render(<App />); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment