widgettdc-api / apps /backend /public /neural_link_v5.html
Kraft102's picture
Update backend source
34367da verified
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WidgeTDC | Omni-Link v5.1</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-void: #020203;
--panel-glass: rgba(10, 15, 30, 0.85);
--border-color: rgba(255, 255, 255, 0.08);
--neon-cyan: #22d3ee;
--neon-purple: #c084fc;
--neon-green: #4ade80;
--neon-amber: #fbbf24;
--font-display: 'Orbitron', sans-serif;
--font-code: 'JetBrains Mono', monospace;
}
body { background-color: var(--bg-void); color: #e2e8f0; font-family: var(--font-code); overflow: hidden; }
.font-display { font-family: var(--font-display); }
#neural-canvas { position: absolute; inset: 0; z-index: 0; }
.ui-layer { position: absolute; inset: 0; pointer-events: none; z-index: 10; }
.pointer-auto { pointer-events: auto; }
/* Professional Glassmorphism */
.glass-panel {
background: var(--panel-glass);
border: 1px solid var(--border-color);
backdrop-filter: blur(16px);
box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}
.status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; margin-right: 8px; position: relative; }
.status-dot::after { content: ''; position: absolute; inset: -2px; border-radius: 50%; opacity: 0.4; background: inherit; animation: ping 2s cubic-bezier(0, 0, 0.2, 1) infinite; }
.status-dot.online { background-color: var(--neon-green); box-shadow: 0 0 8px var(--neon-green); }
.status-dot.offline { background-color: var(--neon-amber); }
.inspector-panel { transform: translateX(110%); transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
.inspector-panel.active { transform: translateX(0); }
.chat-msg { margin-bottom: 6px; padding: 6px 10px; border-left: 2px solid transparent; font-size: 11px; line-height: 1.4; background: rgba(255,255,255,0.02); }
.chat-msg.sys { border-color: var(--neon-cyan); }
.chat-msg.api { border-color: var(--neon-purple); }
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
@keyframes ping { 75%, 100% { transform: scale(2); opacity: 0; } }
.omni-input { background: transparent; border: none; outline: none; width: 100%; color: white; font-family: var(--font-code); font-size: 13px; }
/* Blueprint Grid */
.grid-bg {
position: fixed; inset: 0; pointer-events: none; z-index: -1; opacity: 0.08;
background-image:
linear-gradient(rgba(34, 211, 238, 0.3) 1px, transparent 1px),
linear-gradient(90deg, rgba(34, 211, 238, 0.3) 1px, transparent 1px);
background-size: 40px 40px;
}
/* Scrollbar */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
</style>
</head>
<body>
<div class="grid-bg"></div>
<canvas id="neural-canvas"></canvas>
<div class="ui-layer p-6 flex flex-col h-screen">
<!-- HEADER -->
<header class="flex justify-between items-start pointer-auto mb-6 shrink-0">
<div>
<h1 class="font-display text-2xl text-white tracking-[0.2em] uppercase bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">
Omni-Link <span class="text-cyan-500 text-xs align-top font-bold tracking-normal ml-1">PRO</span>
</h1>
<div class="flex items-center gap-2 mt-2 text-[10px] text-gray-400 font-medium uppercase tracking-widest">
<span id="conn-dot" class="status-dot offline"></span>
<span id="conn-status">System Offline</span>
</div>
</div>
<div class="glass-panel px-4 py-3 rounded-md text-[11px] font-mono text-gray-400 flex gap-6 border-t border-white/10">
<div class="flex flex-col">
<span class="text-[9px] text-gray-600 tracking-wider">NODES</span>
<span id="stat-nodes" class="text-white font-bold text-sm">0</span>
</div>
<div class="w-px bg-white/10"></div>
<div class="flex flex-col">
<span class="text-[9px] text-gray-600 tracking-wider">LATENCY</span>
<span id="stat-latency" class="text-cyan-400 font-bold text-sm">--</span>
</div>
</div>
</header>
<div class="flex-1 flex gap-6 overflow-hidden relative">
<!-- STREAM -->
<div class="w-72 glass-panel rounded-md flex flex-col pointer-auto z-20 border-l-2 border-l-cyan-500/30">
<div class="p-3 border-b border-white/5 bg-black/20 flex justify-between items-center">
<span class="font-display text-[10px] text-cyan-500 tracking-widest">ACTIVITY LOG</span>
<div class="w-1.5 h-1.5 rounded-full bg-cyan-500 animate-pulse"></div>
</div>
<div id="chat-stream" class="flex-1 overflow-y-auto p-3"></div>
</div>
<!-- INSPECTOR -->
<div id="inspector" class="inspector-panel absolute right-0 top-0 bottom-0 w-96 glass-panel rounded-l-xl flex flex-col pointer-auto z-30 border-r-0 border-y border-l border-white/10 shadow-2xl">
<div class="p-5 border-b border-white/5 bg-gradient-to-r from-black/40 to-transparent flex justify-between items-center">
<h2 class="font-display text-xs text-gray-400 tracking-[0.2em]">NODE TELEMETRY</h2>
<button onclick="closeInspector()" class="text-gray-500 hover:text-white transition-colors text-lg leading-none">&times;</button>
</div>
<div class="p-6 text-center relative group">
<div class="absolute inset-0 bg-cyan-500/5 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
<span id="inspect-label" class="text-xl font-display text-white relative z-10 block tracking-wide">Select Node</span>
<span id="inspect-type" class="text-[10px] text-cyan-400 font-mono border border-cyan-900/50 bg-cyan-950/20 px-2 py-1 rounded-sm mt-3 inline-block relative z-10 tracking-wider uppercase">SYSTEM</span>
<button id="btn-nudge" class="w-full mt-6 py-3 rounded-sm bg-white/5 hover:bg-white/10 text-cyan-200 text-xs font-bold border border-white/10 transition-all relative z-10 flex items-center justify-center gap-2 group-hover:border-cyan-500/30">
<span class="w-2 h-2 bg-cyan-400 rounded-full shadow-[0_0_8px_#22d3ee]"></span>
TRIGGER REFLEX
</button>
</div>
<div class="flex-1 overflow-y-auto p-0 font-mono bg-black/20">
<div id="inspect-json" class="p-4"></div>
</div>
</div>
</div>
<!-- OMNI BAR -->
<footer class="mt-6 mx-auto w-full max-w-2xl pointer-auto z-40 shrink-0">
<div class="glass-panel rounded-full pl-5 pr-2 py-2 flex items-center gap-4 shadow-[0_0_30px_rgba(0,0,0,0.5)] border border-white/10 ring-1 ring-white/5">
<span class="text-cyan-500 font-bold animate-pulse text-lg"></span>
<input id="omni-input" type="text" class="omni-input" placeholder="Enter command to inject thought... (e.g., /inject Memory Protocol_7)">
<div class="text-[9px] text-gray-600 font-display tracking-wider px-3 py-1 border border-white/5 rounded-full">READY</div>
</div>
</footer>
</div>
<script>
const API_URL = '/api/cortex';
// Professional Palette matching Project Theme
const config = {
colors: {
bg: '#020203',
node: '#3b82f6', // Blue
agent: '#c084fc', // Purple
ingestion: '#f59e0b', // Amber
memory: '#10b981', // Emerald
system: '#64748b', // Slate
link: 'rgba(59, 130, 246, 0.15)'
},
physics: { friction: 0.94, spring: 0.03, repulsion: 350 }
};
let state = {
nodes: [],
links: [],
camera: { x: 0, y: 0, zoom: 1 },
selection: null,
drag: { active: false, node: null },
particles: [] // For data flow animation
};
// --- CORE LOGIC ---
async function fetchGraph() {
try {
const start = Date.now();
const res = await fetch(`${API_URL}/graph`);
const data = await res.json();
if(data.success) {
const latency = Date.now() - start;
document.getElementById('stat-latency').innerText = latency + 'ms';
document.getElementById('conn-status').innerText = 'SYSTEM ONLINE';
document.getElementById('conn-dot').className = 'status-dot online';
// Merge existing nodes to preserve position/velocity if possible
const newNodes = data.graph.nodes.map(n => {
const existing = state.nodes.find(en => en.id === n.id);
return existing ? { ...n, x: existing.x, y: existing.y, vx: existing.vx, vy: existing.vy } : { ...n, vx:0, vy:0 };
});
state.nodes = newNodes;
state.links = data.graph.links.map(l => ({
source: state.nodes.find(n => n.id === l.source),
target: state.nodes.find(n => n.id === l.target)
})).filter(l => l.source && l.target);
updateStats();
// Spawn particles for new data
if (state.particles.length < 20) spawnParticles();
}
} catch (err) {
document.getElementById('conn-status').innerText = 'DISCONNECTED';
document.getElementById('conn-dot').className = 'status-dot offline';
}
}
async function sendNudge(node) {
log('OPERATOR', `Manual override: [${node.label}]`, 'sys');
try {
const res = await fetch(`${API_URL}/nudge`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nodeId: node.id })
});
const data = await res.json();
if(data.success) {
log('CORTEX', `Response: ${data.reaction}`, 'api');
// Visual pulse
node.pulse = 1.5;
}
} catch(err) { log('ERROR', 'Command rejected.', 'sys'); }
}
async function injectNode(label) {
try {
await fetch(`${API_URL}/inject`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ label, type: 'Memory', data: { origin: 'Omni-Link Console' } })
});
fetchGraph();
} catch(err) { log('ERROR', 'Injection rejected.', 'sys'); }
}
function spawnParticles() {
state.links.forEach(l => {
if (Math.random() > 0.7) {
state.particles.push({
link: l,
pos: 0,
speed: 0.02 + Math.random() * 0.03,
color: l.target.type === 'Agent' ? config.colors.agent : config.colors.node
});
}
});
}
// --- VISUALS ---
const canvas = document.getElementById('neural-canvas');
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
function render() {
try {
// Physics
state.nodes.forEach(n1 => {
state.nodes.forEach(n2 => {
if (n1 === n2) return;
const dx = n1.x - n2.x, dy = n1.y - n2.y;
const dist = Math.sqrt(dx*dx + dy*dy) || 1;
if (dist < 400) {
const f = config.physics.repulsion / (dist*dist);
n1.vx += (dx/dist)*f; n1.vy += (dy/dist)*f;
}
});
n1.vx *= config.physics.friction; n1.vy *= config.physics.friction;
n1.x += n1.vx; n1.y += n1.vy;
// Dampen pulse
if (n1.pulse > 1) n1.pulse -= 0.02;
});
state.links.forEach(l => {
const dx = l.target.x - l.source.x, dy = l.target.y - l.source.y;
const dist = Math.sqrt(dx*dx + dy*dy);
const f = (dist - 180) * config.physics.spring;
const fx = (dx/dist)*f, fy = (dy/dist)*f;
l.source.vx += fx; l.source.vy += fy;
l.target.vx -= fx; l.target.vy -= fy;
});
// Clear & Center
ctx.clearRect(0, 0, canvas.width, canvas.height);
const cx = canvas.width/2 + state.camera.x;
const cy = canvas.height/2 + state.camera.y;
// Draw Links
ctx.lineWidth = 1;
state.links.forEach(l => {
ctx.beginPath();
ctx.moveTo(cx + l.source.x, cy + l.source.y);
ctx.lineTo(cx + l.target.x, cy + l.target.y);
const grad = ctx.createLinearGradient(cx + l.source.x, cy + l.source.y, cx + l.target.x, cy + l.target.y);
grad.addColorStop(0, 'rgba(59, 130, 246, 0.05)');
grad.addColorStop(0.5, 'rgba(59, 130, 246, 0.2)');
grad.addColorStop(1, 'rgba(59, 130, 246, 0.05)');
ctx.strokeStyle = grad;
ctx.stroke();
});
// Draw Particles
for (let i = state.particles.length - 1; i >= 0; i--) {
let p = state.particles[i];
p.pos += p.speed;
if (p.pos >= 1) { state.particles.splice(i, 1); continue; }
const x = cx + p.link.source.x + (p.link.target.x - p.link.source.x) * p.pos;
const y = cy + p.link.source.y + (p.link.target.y - p.link.source.y) * p.pos;
ctx.fillStyle = p.color;
ctx.shadowBlur = 6;
ctx.shadowColor = p.color;
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI*2);
ctx.fill();
ctx.shadowBlur = 0;
}
if (Math.random() > 0.9) spawnParticles();
// Draw Nodes
state.nodes.forEach(n => {
const x = cx + n.x, y = cy + n.y;
let r = (n.radius || 25) * (n.pulse || 1);
if (r <= 0) r = 10; // Safety
// Types & Colors
let baseColor = config.colors.node;
let icon = "⚫";
if (n.type === 'Ingestion') { baseColor = config.colors.ingestion; icon = n.label.includes('Outlook') ? "📧" : "📂"; }
else if (n.type === 'Memory') { baseColor = config.colors.memory; icon = "🧠"; }
else if (n.type === 'Agent') { baseColor = config.colors.agent; icon = "🤖"; }
else if (n.type === 'System') { baseColor = config.colors.system; icon = "⚙️"; }
// Selection Glow
if (state.selection === n) {
ctx.beginPath();
ctx.arc(x, y, r + 4, 0, Math.PI*2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fill();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
ctx.lineWidth = 1;
ctx.stroke();
}
// Sphere Body
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2);
try {
const grad = ctx.createRadialGradient(x - r/3, y - r/3, r/10, x, y, r);
grad.addColorStop(0, adjustColor(baseColor, 140));
grad.addColorStop(0.4, baseColor);
grad.addColorStop(1, adjustColor(baseColor, -60));
ctx.fillStyle = grad;
} catch (e) {
// Fallback if gradient fails (e.g. invalid color)
ctx.fillStyle = baseColor;
}
ctx.fill();
// Inner Rim Light
ctx.beginPath();
ctx.arc(x, y, r * 0.85, 0, Math.PI*2);
ctx.strokeStyle = 'rgba(255,255,255,0.1)';
ctx.lineWidth = 1;
ctx.stroke();
// Icon
ctx.font = `${r*0.5}px sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(icon, x, y);
// Label
ctx.font = "10px 'JetBrains Mono'";
ctx.fillStyle = "rgba(255,255,255,0.6)";
ctx.fillText(n.label, x, y + r + 14);
});
} catch (err) {
console.error("Render Error:", err);
}
requestAnimationFrame(render);
}
// Utility: Adjust Hex Color Brightness (Robust)
function adjustColor(color, amount) {
if (!color || typeof color !== 'string' || !color.startsWith('#')) return color || '#888888';
const num = parseInt(color.replace('#',''), 16);
if (isNaN(num)) return color;
let r = (num >> 16) + amount;
let g = ((num >> 8) & 0x00FF) + amount;
let b = (num & 0x0000FF) + amount;
r = Math.max(Math.min(255, r), 0);
g = Math.max(Math.min(255, g), 0);
b = Math.max(Math.min(255, b), 0);
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// --- INPUTS ---
canvas.addEventListener('mousedown', e => {
const mx = e.clientX - canvas.width/2 - state.camera.x;
const my = e.clientY - canvas.height/2 - state.camera.y;
const hit = state.nodes.find(n => Math.hypot(n.x - mx, n.y - my) < 30);
if (hit) { state.selection = hit; state.drag = { active: true, node: hit }; openInspector(hit); }
else { closeInspector(); }
});
canvas.addEventListener('mousemove', e => {
if(state.drag.active) {
state.drag.node.x = e.clientX - canvas.width/2 - state.camera.x;
state.drag.node.y = e.clientY - canvas.height/2 - state.camera.y;
state.drag.node.vx = 0; state.drag.node.vy = 0;
}
});
canvas.addEventListener('mouseup', () => state.drag.active = false);
document.getElementById('omni-input').addEventListener('keydown', e => {
if(e.key === 'Enter') {
const cmd = e.target.value.trim();
e.target.value = "";
if(cmd.startsWith('/inject')) injectNode(cmd.split(' ')[1] || 'Node');
}
});
function openInspector(node) {
document.getElementById('inspector').classList.add('active');
document.getElementById('inspect-label').innerText = node.label;
document.getElementById('inspect-type').innerText = node.type;
const container = document.getElementById('inspect-json');
container.innerHTML = '';
if (node.data) {
const table = document.createElement('table');
table.className = "w-full text-left text-[11px] border-collapse";
for (const [key, value] of Object.entries(node.data)) {
const row = table.insertRow();
row.className = "border-b border-white/5";
const cellKey = row.insertCell();
cellKey.className = "py-2 pr-2 text-gray-500 font-medium w-1/2 align-top";
cellKey.textContent = key.toUpperCase();
const cellVal = row.insertCell();
cellVal.className = "py-2 text-gray-200 font-mono text-right break-all";
if (Array.isArray(value)) {
cellVal.innerHTML = value.map(v => `<span class="inline-block bg-white/10 px-1.5 py-0.5 rounded text-[9px] mr-1 mb-1">${v}</span>`).join('');
} else {
cellVal.textContent = value;
}
}
container.appendChild(table);
} else {
container.innerHTML = '<div class="py-8 text-gray-600 text-xs italic text-center">Awaiting telemetry stream...</div>';
}
const btn = document.getElementById('btn-nudge');
const newBtn = btn.cloneNode(true);
btn.parentNode.replaceChild(newBtn, btn); // Clean event listeners
newBtn.addEventListener('click', () => sendNudge(node));
}
function closeInspector() {
document.getElementById('inspector').classList.remove('active');
state.selection = null;
}
function log(src, msg, type) {
const div = document.createElement('div');
div.className = `chat-msg ${type}`;
div.innerHTML = `<span class="opacity-50 font-bold mr-2 tracking-wider text-[9px]">${src}</span> ${msg}`;
div.style.animation = 'slideIn 0.3s cubic-bezier(0,0,0.2,1)';
const container = document.getElementById('chat-stream');
container.appendChild(div);
container.scrollTop = container.scrollHeight;
}
function updateStats() {
document.getElementById('stat-nodes').innerText = state.nodes.length;
}
// Init
fetchGraph();
render();
log('SYSTEM', 'Omni-Link v5.1 Initialized', 'sys');
</script>
</body>
</html>