import { useState, useEffect, useRef, useCallback } from 'react'; import './AppShell.css'; // ── CONSTANTS ────────────────────────────────────────────────────────── const PI2 = Math.PI * 2; // PHI used for Fibonacci temperature: τ = φ^{-k}, reserved for agent Born rule const _PHI = (1 + Math.sqrt(5)) / 2; void _PHI; // ── AGENTS (sovereign civilization) ──────────────────────────────────── const AGENTS = [ { id: 'CARTO', role: 'Mayor', color: '#ffd700', x: 15.5, y: 12.5, msg: '(rule CARTO ALL "Trust Deed active — vortex field stable")' }, { id: 'NOVA', role: 'Artist', color: '#d36bff', x: 4.5, y: 4.5, msg: '(paint NOVA WALL "vortex chronicle — coherence rising")' }, { id: 'FORGE', role: 'Architect', color: '#ff8c1a', x: 13.5, y: 9.5, msg: '(build FORGE "portal buttress — topo charge stabilized")' }, { id: 'FLUX', role: 'Trader', color: '#00d4cc', x: 7.5, y: 15.5, msg: '(trade FLUX ROBOB "evidence shard — entropy nominal")' }, { id: 'PHANTOM', role: 'Sheriff', color: '#00ff88', x: 10.5, y: 12.5, msg: '(audit PHANTOM "no agent acts without evidence")' }, { id: 'ROBOB', role: 'Oracle', color: '#5df7ff', x: 3.5, y: 15.5, msg: '(verdict ROBOB "EVIDENCE or SILENCE — plasma gate OK")' }, ]; // ── MAP ──────────────────────────────────────────────────────────────── const MAP_RAW = [ '########################', '#..e......#.....e......#', '#..###....#..######..#.#', '#....#....#.......#..#.#', '#....#..n....e....#....#', '#..#######..#######.####', '#.........a............#', '#.###.#######.#######..#', '#...#.....#.....#......#', '#...#..e..#..f..#..e...#', '###.####..#..####..#####', '#......#.....#.........#', '#..r...#..p..#..c......#', '#......#######......e..#', '####......#......#######', '#..e..b...#...x.......o#', '#.........#............#', '#..######...######..####', '#......#.....#.........#', '#..e...#..v..#...e.....#', '#......#.....#.........#', '#..######..######..##..#', '#......................#', '########################', ]; const MW = MAP_RAW[0].length; const MH = MAP_RAW.length; function tileAt(x: number, y: number): string { const xi = Math.floor(x), yi = Math.floor(y); if (xi < 0 || yi < 0 || xi >= MW || yi >= MH) return '#'; return MAP_RAW[yi][xi]; } function isSolid(t: string) { return '#nafcpbxr'.includes(t); } function wallColor(t: string): [string, string, string] { const m: Record = { '#': ['#1b3340','#0b151d','#3c7684'], n: ['#05363a','#03181b','#00d4cc'], a: ['#2b1d05','#120c03','#ffd700'], f: ['#3a2107','#140903','#ff8c1a'], c: ['#241232','#0d0614','#d36bff'], p: ['#173016','#071308','#00ff88'], b: ['#302606','#120d02','#ffd700'], x: ['#341111','#130606','#ff4444'], r: ['#06333a','#031316','#5df7ff'], }; return m[t] ?? ['#26333a','#0b1116','#7aa8b4']; } // ── QUANTUM ENGINE (JS port of bob_*.f90) ────────────────────────────── function qrng(): number { const b = new Uint8Array(8); crypto.getRandomValues(b); return (b[0]*0x1000000+b[1]*65536+b[2]*256+b[3]) / 4294967296; } class VortexLattice { n: number; K: number; dt: number; time = 0; vx: {x:number;y:number;winding:number;phase:number;coherence:number}[]; constructor(n: number, K: number, dt: number) { this.n = n; this.K = K; this.dt = dt; this.vx = []; for (let iy=0;iy({...v})), n=this.n; for (let iy=0;iys+v.coherence,0)/this.vx.length;} topoCharge(){return this.vx.reduce((s,v)=>s+v.winding,0);} } function blake3Short(s: string): string { let h = 0x811c9dc5; for (let i=0;i>>0;} const rnd = Math.floor(qrng()*0xFFFFFFFF)>>>0; return (h^rnd).toString(16).padStart(8,'0')+(Math.imul(h,rnd)>>>0).toString(16).padStart(8,'0'); } // ── TYPES ────────────────────────────────────────────────────────────── interface DialogueLine { text: string; cls: string; t: number; } interface Shard { x:number; y:number; alive:boolean; } interface Enemy { x:number; y:number; hp:number; alive:boolean; } // ── COMPONENT ────────────────────────────────────────────────────────── export function AppShell() { const canvasRef = useRef(null); const miniRef = useRef(null); const chatRef = useRef(null); const inputRef = useRef(null); const latRef = useRef(new VortexLattice(16, 0.3, 0.01)); const stateRef = useRef({ px:2.8,py:2.8,pa:0,hp:100,evidence:0,seals:0,won:false,dead:false,cool:0, tick:0, worm:'GENESIS', running:true, shards:[] as Shard[], enemies:[] as Enemy[], keys:{} as Record, lastFrame:0, fps:0, frames:0, fpsT:0, }); const [view, setView] = useState<'world'|'code'|'quantum'>('world'); const [dialogue, setDialogue] = useState([]); const [metrics, setMetrics] = useState({coh:'0',topo:'0',entropy:'0',seals:0,hp:100,evidence:0,fps:0}); const [chatInput, setChatInput] = useState(''); const [chatLog, setChatLog] = useState<{role:string;text:string}[]>([ {role:'bot', text:'(boot VORTEX_IDE "civilization engine online")\n\nI am BOB — the oracle of the Kittyverse. Ask me to build, analyze, explain, or fix code. The vortex lattice is live.'}, ]); const [provider, setProvider] = useState<'webllm'|'openrouter'|'ollama'>('webllm'); const [apiKey, setApiKey] = useState(''); const animRef = useRef(0); // Init shards + enemies from map useEffect(() => { const s = stateRef.current; MAP_RAW.forEach((row,y)=>{[...row].forEach((c,x)=>{ if(c==='e') s.shards.push({x:x+.5,y:y+.5,alive:true}); });}); s.enemies=[ {x:19.5,y:4.5,hp:3,alive:true}, {x:18.5,y:16.5,hp:3,alive:true}, {x:6.5,y:20.5,hp:3,alive:true}, ]; }, []); const say = useCallback((text:string, cls='') => { setDialogue(d => [{text,cls,t:Date.now()},...d].slice(0,8)); }, []); const sealWorm = useCallback((evt:string) => { const s = stateRef.current; const lat = latRef.current; const msg = `${s.worm}|${evt}|${s.tick}|${s.px.toFixed(2)},${s.py.toFixed(2)}|E=${lat.meanCoherence().toFixed(4)}`; s.worm = blake3Short(msg); s.seals++; return s.worm; }, []); // Key handling useEffect(() => { const s = stateRef.current; const down = (e:KeyboardEvent) => { s.keys[e.code]=true; if(e.code==='Space'){e.preventDefault();pulse();} if(e.code==='KeyM') sealWorm('MANUAL_SEAL'); }; const up = (e:KeyboardEvent) => { s.keys[e.code]=false; }; window.addEventListener('keydown',down); window.addEventListener('keyup',up); return ()=>{window.removeEventListener('keydown',down);window.removeEventListener('keyup',up);}; }, []); function pulse() { const s = stateRef.current; if(s.cool>0||s.hp<=0) return; s.cool=18; // Check enemies in front let best:{e:Enemy;d:number}|null=null; for(const e of s.enemies) if(e.alive){ const dx=e.x-s.px,dy=e.y-s.py,dist=Math.hypot(dx,dy); const da=Math.atan2(dy,dx)-s.pa,norm=Math.atan2(Math.sin(da),Math.cos(da)); if(dist<6&&Math.abs(norm)<0.26&&(!best||dist { if(view!=='world') return; const cv = canvasRef.current!; const mini= miniRef.current!; if(!cv||!mini) return; const ctx = cv.getContext('2d',{alpha:false})!; const mctx = mini.getContext('2d')!; function resize(){cv.width=cv.offsetWidth;cv.height=cv.offsetHeight;} resize(); const ro=new ResizeObserver(resize);ro.observe(cv); function movePlayer(dt:number){ const s=stateRef.current,keys=s.keys; const turn=(keys.ArrowLeft?-1:0)+(keys.ArrowRight?1:0); s.pa+=turn*dt*2.3; const f=(keys.KeyW||keys.ArrowUp?1:0)-(keys.KeyS||keys.ArrowDown?1:0); const st=(keys.KeyD?1:0)-(keys.KeyA?1:0); const sp=dt*3.0; const nx=s.px+Math.cos(s.pa)*f*sp+Math.cos(s.pa+Math.PI/2)*st*sp; const ny=s.py+Math.sin(s.pa)*f*sp+Math.sin(s.pa+Math.PI/2)*st*sp; if(!isSolid(tileAt(nx,s.py)))s.px=nx; if(!isSolid(tileAt(s.px,ny)))s.py=ny; s.px=Math.max(1.1,Math.min(MW-1.1,s.px)); s.py=Math.max(1.1,Math.min(MH-1.1,s.py)); } function updateGame(dt:number){ const s=stateRef.current; const lat=latRef.current; s.tick++; if(s.cool>0)s.cool--; lat.evolve(); // Collect shards for(const sh of s.shards) if(sh.alive&&Math.hypot(sh.x-s.px,sh.y-s.py)<0.55){ sh.alive=false;s.evidence++; say(`(collect BOB "EVIDENCE-SHARD-${s.evidence}")`,'good'); sealWorm('SHARD'); } // Agent dialogue via Born rule — coherence weights agent probability const coh=lat.meanCoherence(); for(const ag of AGENTS){ const d=Math.hypot(ag.x-s.px,ag.y-s.py); const prob=coh*(1/(1+d*d))*0.08; if(Math.random()=6&&!s.won&&Math.hypot(12.5-s.px,19.5-s.py)<1.1){ s.won=true; say('(seal TRUST_DEED "KITTYVERSE-VORTEX-001")', 'good'); sealWorm('VICTORY'); } if(s.hp<=0&&!s.dead){s.dead=true;say('(route HUMAN_REVIEW "BOB down")','bad');sealWorm('DEATH');} } function castRay(angle:number):{dist:number;t:string;x:number;y:number}{ const sin=Math.sin(angle),cos=Math.cos(angle); let dist=0,x=stateRef.current.px,y=stateRef.current.py,t='.'; while(dist<22){x+=cos*0.035;y+=sin*0.035;dist+=0.035;t=tileAt(x,y);if(isSolid(t))break;} return {dist,t,x,y}; } function renderWorld(){ const s=stateRef.current,lat=latRef.current; const W=cv.width,H=cv.height; const strip=W>1200?2:3; // Sky/floor const sky=ctx.createLinearGradient(0,0,0,H*.52); sky.addColorStop(0,'#020610');sky.addColorStop(.55,'#071827');sky.addColorStop(1,'#10111b'); ctx.fillStyle=sky;ctx.fillRect(0,0,W,H/2); const floor=ctx.createLinearGradient(0,H/2,0,H); floor.addColorStop(0,'#090b10');floor.addColorStop(1,'#020204'); ctx.fillStyle=floor;ctx.fillRect(0,H/2,W,H/2); const fov=Math.PI/3,zbuf:number[]=[]; for(let x=0;xparseInt(v,16)); const tr=Math.cos(ph)*20,tg=Math.sin(ph)*10; ctx.fillStyle=`rgb(${Math.floor(bc[0]*shade+tr)},${Math.floor(bc[1]*shade+tg)},${Math.floor(bc[2]*shade)})`; ctx.fillRect(x,top,strip+1,h2); if(coh>0.75&&'napc'.includes(r.t)){ ctx.fillStyle=`rgba(0,212,204,${(coh-0.75)*0.5+0.05*Math.sin(s.tick*.05+x*.02)})`; ctx.fillRect(x,top,strip+1,h2); } zbuf[x]=correct; } // Sprites const all=[ ...stateRef.current.shards.filter(sh=>sh.alive).map(sh=>({...sh,kind:'shard',c:'#00ff88'})), ...AGENTS.map(ag=>({...ag,kind:'agent'})), ...stateRef.current.enemies.filter(e=>e.alive).map(e=>({...e,kind:'enemy',c:'#ff4444',id:'SILENCE'})), {x:12.5,y:19.5,kind:'portal',c:'#00d4ff',id:'PORTAL'}, ]; all.sort((a,b)=>Math.hypot(b.x-s.px,b.y-s.py)-Math.hypot(a.x-s.px,a.y-s.py)); for(const sp of all){ const dx=sp.x-s.px,dy=sp.y-s.py,dist=Math.hypot(dx,dy); const ang=Math.atan2(dy,dx)-s.pa,norm=Math.atan2(Math.sin(ang),Math.cos(ang)); if(Math.abs(norm)>Math.PI/3||dist<0.2)continue; const sx=(norm/(Math.PI/3)+0.5)*W; const sz=Math.min(H*0.75,H/(dist*0.9)),top=H/2-sz/2; const zi=Math.max(0,Math.min(W-1,Math.floor(sx/3)*3)); if(zbuf[zi]&&zbuf[zi]1000){s.fps=s.frames;s.frames=0;s.fpsT=now;} if(s.running){movePlayer(dt);updateGame(dt);} renderWorld();renderMini(); const lat=latRef.current; setMetrics({ coh:lat.meanCoherence().toFixed(3), topo:String(lat.topoCharge()), entropy:(lat.vx.reduce((sum,v)=>{const p=v.coherence;return p>0.001?sum-p*Math.log(p):sum;},0)).toFixed(3), seals:s.seals,hp:Math.ceil(s.hp),evidence:s.evidence,fps:s.fps, }); } animRef.current=requestAnimationFrame(loop); return()=>{cancelAnimationFrame(animRef.current);ro.disconnect();}; },[view,say,sealWorm]); // Chat send async function sendChat(){ const text=chatInput.trim();if(!text)return; setChatInput(''); setChatLog(l=>[...l,{role:'user',text}]); if(provider==='openrouter'&&apiKey){ try{ const res=await fetch('https://openrouter.ai/api/v1/chat/completions',{ method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+apiKey}, body:JSON.stringify({model:'anthropic/claude-3-haiku',stream:false, messages:[ {role:'system',content:'You are BOB, the sovereign oracle of the Kittyverse vortex civilization. You help build, fix, and analyze code. Be concise.'}, {role:'user',content:text} ]}) }); const j=await res.json(); const reply=j.choices?.[0]?.message?.content||'(no response)'; setChatLog(l=>[...l,{role:'bot',text:reply}]); say(`(oracle BOB "${text.slice(0,30)}...")`,'good'); }catch(e){setChatLog(l=>[...l,{role:'bot',text:`Error: ${e}`}]);} } else { // Fallback local response based on quantum state const coh=latRef.current.meanCoherence().toFixed(3); const topo=latRef.current.topoCharge(); setChatLog(l=>[...l,{role:'bot',text:`(ROBOB coherence=${coh} topo=${topo})\n\nVortex engine is running. Set an OpenRouter key in settings to enable full AI responses. I can see your civilization state: ${metrics.evidence}/6 evidence collected, ${metrics.seals} WORM seals.`}]); } setTimeout(()=>{if(chatRef.current)chatRef.current.scrollTop=chatRef.current.scrollHeight;},50); } // Quantum panel renderer const quantumCanvasRef=useRef(null); useEffect(()=>{ if(view!=='quantum')return; const qcv=quantumCanvasRef.current;if(!qcv)return; const qctx=qcv.getContext('2d')!;void qctx; let af=0; function draw(){ af=requestAnimationFrame(draw); if(!quantumCanvasRef.current)return; quantumCanvasRef.current.width=quantumCanvasRef.current.offsetWidth||600; quantumCanvasRef.current.height=quantumCanvasRef.current.offsetHeight||400; const qel=quantumCanvasRef.current;if(!qel)return; const qc=qel.getContext('2d')!; const W=qel.width,H=qel.height; const lat=latRef.current; const n=lat.n,cw=W/n,ch=H/n; qc.clearRect(0,0,W,H); for(let iy=0;iy0?'rgba(0,212,204,0.7)':'rgba(255,60,60,0.7)'; qc.lineWidth=1;qc.beginPath();qc.arc(ix*cw+cw/2,iy*ch+ch/2,Math.min(cw,ch)*.3,0,PI2);qc.stroke(); } } } draw();return()=>cancelAnimationFrame(af); },[view]); const s=stateRef.current; return (
{/* ── TITLEBAR ── */}
VORTEX IDE — Kittyverse Sovereign Civilization
HP {metrics.hp} EVD {metrics.evidence}/6 SEALS {metrics.seals} COH {metrics.coh} TOPO {metrics.topo} FPS {metrics.fps}
{/* ── MAIN ── */}
{/* LEFT — world/code/quantum */}
{/* WORLD VIEW */}
{/* CODE VIEW */} {view==='code'&&(
CODE Monaco editor — wire in bob-ide src
⬚ Monaco Editor
Full VS Code engine loads here.
The vortex world runs alongside your code.
Agents comment on what you build.
)} {/* QUANTUM VIEW */} {view==='quantum'&&(
QUANTUM ENGINE 16×16 Josephson vortex lattice · Classical sim
Coherence {metrics.coh} Topo charge {metrics.topo} Entropy {metrics.entropy} bob_lattice.f90 · bob_hamiltonian.f90 · bob_worm.f90
)}
{/* RIGHT — minimap + agent radio + chat */}
{/* Minimap */}
MAP
{/* Agent roster */}
AGENTS
{AGENTS.map(ag=>(
{ag.id} {ag.role}
))}
{/* Dialogue log */}
AGENT RADIO
{dialogue.map((d,i)=>(
{d.text}
))}
{/* AI Chat */}
BOB ORACLE
{provider==='openrouter'&&( setApiKey(e.target.value)} type="password"/> )}
{chatLog.map((m,i)=>(
{m.role==='bot'&&} {m.role==='user'&&} {m.text}
))}