Node Graph Editor
GPT 5.6 Luna Pro · typescript

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 21,473 bytes · SHA-256 d7f5fe959602
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Logic Loom — Node Graph Studio</title>
<style>
/* ── css: base ── */
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#080d14;color:#dce8f4;font-family:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif}button{font:inherit;color:inherit}#shell{height:100%;display:flex;flex-direction:column;background:radial-gradient(circle at 70% -10%,#18283b 0,#0b121d 42%,#080d14 100%)}#topbar{height:68px;flex:none;display:flex;align-items:center;justify-content:space-between;padding:0 24px;border-bottom:1px solid #233246;background:rgba(8,13,20,.9);box-shadow:0 8px 30px rgba(0,0,0,.2);z-index:5}.brand{display:flex;align-items:center;gap:11px}.brand-mark{font-size:32px;line-height:1;color:#44d7c6;text-shadow:0 0 18px #44d7c6}.brand h1{margin:0;font-size:16px;letter-spacing:3px;font-weight:800;color:#eef7ff}.brand p{margin:3px 0 0;font-size:9px;letter-spacing:2.5px;color:#71869b}.top-status{display:flex;align-items:center;gap:10px;font-size:10px;letter-spacing:1.5px;color:#86a0b6}.status-dot{width:7px;height:7px;background:#53e0a2;border-radius:50%;box-shadow:0 0 10px #53e0a2}.divider{width:1px;height:22px;background:#26384c;margin:0 10px}.ghost-btn{background:transparent;border:1px solid #2c4258;border-radius:5px;padding:7px 12px;font-size:10px;letter-spacing:1px;cursor:pointer}.ghost-btn:hover{border-color:#44d7c6;color:#44d7c6}#workspace{flex:1;display:flex;min-height:0}#palette{width:236px;flex:none;background:#0d1622;border-right:1px solid #233246;padding:22px 14px 16px;z-index:4}.panel-kicker{font-size:10px;letter-spacing:2.3px;color:#668097;font-weight:700;margin:0 8px 18px}.palette-group{margin-bottom:19px}.group-label{font-size:9px;letter-spacing:1.7px;color:#4f687d;margin:0 8px 8px}.palette-item{width:100%;height:45px;display:flex;align-items:center;gap:10px;background:transparent;border:1px solid transparent;border-radius:7px;padding:6px 8px;text-align:left;cursor:pointer}.palette-item:hover{background:#172637;border-color:#2c455b;transform:translateX(2px)}.palette-item b{display:block;font-size:11px;color:#d7e7f2}.palette-item small{display:block;font-size:9px;color:#6f879a;margin-top:2px}.item-icon,.gate-badge{width:27px;height:27px;display:grid;place-items:center;flex:none;border-radius:6px}.toggle-icon{color:#55e0ad;background:#153a3b;font-size:14px}.clock-icon{color:#e7b75b;background:#3a2e1d;font-size:20px}.lamp-icon{color:#ffbd60;background:#3a2b1b;font-size:17px}.display-icon{color:#fa7383;background:#3b202a;font-size:10px;font-weight:800}.gate-badge{color:#7eb5e7;background:#172e43;font-size:7px;font-weight:800;border:1px solid #285174}.add{margin-left:auto;color:#47657d;font-size:18px}.palette-foot{position:absolute;bottom:19px;padding:0 8px;color:#60798e;font-size:9px;letter-spacing:1px}.legend{margin:7px 0}.legend-key{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:8px}.input-key{background:#69b7ff;box-shadow:0 0 6px #69b7ff}.output-key{background:#ffab58;box-shadow:0 0 6px #ffab58}.palette-foot p{font-size:9px;line-height:1.6;letter-spacing:.4px;color:#536a7f;margin-top:15px}#stage{position:relative;flex:1;min-width:0;overflow:hidden;cursor:grab}#stage:active{cursor:grabbing}#graphCanvas{position:absolute;inset:0;width:100%;height:100%}#help{position:absolute;right:17px;bottom:13px;color:#516a80;font-size:9px;letter-spacing:.5px;pointer-events:none}#help span{color:#91abc0;border:1px solid #2b4154;padding:3px 5px;margin-left:8px;font-size:8px}.toast{position:absolute;top:18px;left:50%;transform:translateX(-50%) translateY(-12px);opacity:0;background:#3b1820;border:1px solid #dd586c;border-radius:5px;color:#ff9ba6;padding:9px 14px;font-size:11px;letter-spacing:.5px;pointer-events:none;transition:.2s}.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* ── css: responsive ── */
@media(max-width:700px){#palette{width:190px;padding-left:8px;padding-right:8px}.palette-foot{display:none}#topbar{padding:0 12px}.top-status{gap:5px}.top-status #runStatus{display:none}.divider{margin:0 4px}}
/* ── css: motion ── */
@keyframes pulseGlow{0%,100%{opacity:.75}50%{opacity:1}}
</style>
</head>
<body>
<main id="shell"><header id="topbar"><div class="brand"><span class="brand-mark">⌬</span><div><h1>LOGIC LOOM</h1><p>LIVE CIRCUIT STUDIO</p></div></div><div class="top-status"><span class="status-dot"></span><span id="runStatus">SIMULATION LIVE</span><span class="divider"></span><button id="clearBtn" class="ghost-btn">CLEAR</button><button id="saveBtn" class="ghost-btn">SAVE</button></div></header><section id="workspace"><aside id="palette"><div class="panel-kicker">COMPONENTS</div><div class="palette-group"><div class="group-label">INPUTS</div><button class="palette-item" data-type="toggle"><span class="item-icon toggle-icon">●</span><span><b>Toggle</b><small>binary source</small></span><span class="add">+</span></button><button class="palette-item" data-type="clock"><span class="item-icon clock-icon">◷</span><span><b>Clock</b><small>pulse generator</small></span><span class="add">+</span></button></div><div class="palette-group"><div class="group-label">LOGIC GATES</div><button class="palette-item" data-type="and"><span class="gate-badge">AND</span><span><b>AND</b><small>all inputs true</small></span><span class="add">+</span></button><button class="palette-item" data-type="or"><span class="gate-badge">OR</span><span><b>OR</b><small>any input true</small></span><span class="add">+</span></button><button class="palette-item" data-type="not"><span class="gate-badge">NOT</span><span><b>NOT</b><small>invert signal</small></span><span class="add">+</span></button><button class="palette-item" data-type="xor"><span class="gate-badge">XOR</span><span><b>XOR</b><small>exclusive true</small></span><span class="add">+</span></button><button class="palette-item" data-type="nand"><span class="gate-badge">NAND</span><span><b>NAND</b><small>not all true</small></span><span class="add">+</span></button><button class="palette-item" data-type="nor"><span class="gate-badge">NOR<small>none true</small></span><span class="add">+</span></button></div><div class="palette-group"><div class="group-label">OUTPUTS</div><button class="palette-item" data-type="lamp"><span class="item-icon lamp-icon">◉</span><span><b>Lamp</b><small>visual output</small></span><span class="add">+</span></button><button class="palette-item" data-type="display"><span class="item-icon display-icon">88</span><span><b>Hex Display</b><small>4-bit output</small></span><span class="add">+</span></button></div><div class="palette-foot"><div class="legend"><span class="legend-key input-key"></span> INPUT PORTS</div><div class="legend"><span class="legend-key output-key"></span> OUTPUT PORTS</div><p>Drag from an orange port<br>to a blue port to connect.</p></div></aside><div id="stage"><canvas id="graphCanvas"></canvas><div id="toast" class="toast"></div><div id="help"><span>WHEEL</span> zoom <span>DRAG EMPTY</span> pan <span>DEL</span> remove</div></div></section></main>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// LOGIC LOOM — single-file node graph editor
// Tunable constants
const CFG = { minZoom: .5, maxZoom: 2, nodeW: 154, headerH: 32, rowH: 24, portR: 6, hitR: 13, grid: 28, saveKey: 'logic-loom-scene-v1' };
const canvas = document.querySelector('#graphCanvas'), stage = document.querySelector('#stage'), ctx = canvas.getContext('2d'), toast = document.querySelector('#toast');
const palette = { toggle: { label: 'TOGGLE', inputs: 0, color: '#54e0ad' }, clock: { label: 'CLOCK', inputs: 0, color: '#e4b35d' }, and: { label: 'AND', inputs: 2, color: '#71b4ea' }, or: { label: 'OR', inputs: 2, color: '#71b4ea' }, not: { label: 'NOT', inputs: 1, color: '#71b4ea' }, xor: { label: 'XOR', inputs: 2, color: '#71b4ea' }, nand: { label: 'NAND', inputs: 2, color: '#71b4ea' }, nor: { label: 'NOR', inputs: 2, color: '#71b4ea' }, lamp: { label: 'LAMP', inputs: 1, color: '#ffad5a' }, display: { label: 'HEX DISPLAY', inputs: 4, color: '#ed7184' } };
let scene = loadScene();
let zoom = 1, pan = { x: stage.clientWidth / 2 - 270, y: stage.clientHeight / 2 - 150 };
let selected = null;
let hoverPort = null;
let dragNode = null;
let wireDrag = null;
let panning = false, last = { x: 0, y: 0 };
let flashUntil = 0;
function uid() { return 'n' + Math.random().toString(36).slice(2, 8); }
function makeNode(type, x, y) { return { id: uid(), type, x, y, state: false, bits: Array(palette[type].inputs).fill(false), rate: 1, phase: 0 }; }
function loadScene() { try {
const x = JSON.parse(localStorage.getItem(CFG.saveKey) || '');
if (x?.nodes && x?.wires)
return x;
}
catch { } return { nodes: [makeNode('toggle', 70, 100), makeNode('toggle', 70, 245), makeNode('and', 320, 165), makeNode('not', 570, 165), makeNode('lamp', 805, 165)], wires: [{ from: '', fromPort: 0, to: '', toPort: 0 }] }; }
// repair demo ids, then wire demo by position
if (scene.nodes.length && scene.wires.some(w => !w.from)) {
const [a, b, g, inv, l] = scene.nodes;
scene.wires = [{ from: a.id, fromPort: 0, to: g.id, toPort: 0 }, { from: b.id, fromPort: 0, to: g.id, toPort: 1 }, { from: g.id, fromPort: 0, to: inv.id, toPort: 0 }, { from: inv.id, fromPort: 0, to: l.id, toPort: 0 }];
}
function node(id) { return scene.nodes.find(n => n.id === id); }
function world(sx, sy) { return { x: (sx - pan.x) / zoom, y: (sy - pan.y) / zoom }; }
function screen(x, y) { return { x: x * zoom + pan.x, y: y * zoom + pan.y }; }
function nodeH(n) { return CFG.headerH + Math.max(palette[n.type].inputs, 1) * CFG.rowH + 28; }
function portPos(n, side, i) { const count = side === 'in' ? palette[n.type].inputs : 1; return { x: n.x + (side === 'in' ? 0 : CFG.nodeW), y: n.y + CFG.headerH + 12 + i * CFG.rowH }; }
function portAt(w) { for (const n of scene.nodes) {
for (const side of ['in', 'out']) {
const count = side === 'in' ? palette[n.type].inputs : 1;
for (let i = 0; i < count; i++) {
const p = portPos(n, side, i);
if (Math.hypot(p.x - w.x, p.y - w.y) < CFG.hitR / zoom)
return { node: n.id, index: i, side };
}
}
} return null; }
function nodeAt(w) { for (let i = scene.nodes.length - 1; i >= 0; i--) {
const n = scene.nodes[i];
if (w.x >= n.x && w.x <= n.x + CFG.nodeW && w.y >= n.y && w.y <= n.y + nodeH(n))
return n;
} return null; }
function wirePath(w) { const a = portPos(node(w.from), 'out', w.fromPort), b = portPos(node(w.to), 'in', w.toPort), dx = Math.max(55, Math.abs(b.x - a.x) * .45); return { a, b, c1: { x: a.x + dx, y: a.y }, c2: { x: b.x - dx, y: b.y } }; }
function clear() { ctx.clearRect(0, 0, canvas.width, canvas.height); const d = window.devicePixelRatio || 1; ctx.save(); ctx.scale(d, d); const W = canvas.clientWidth, H = canvas.clientHeight; ctx.fillStyle = '#09111a'; ctx.fillRect(0, 0, W, H); ctx.translate(pan.x, pan.y); ctx.scale(zoom, zoom); drawGrid(W, H); for (const w of scene.wires)
drawWire(w); if (wireDrag)
drawPreview(); for (const n of scene.nodes)
drawNode(n); ctx.restore(); requestAnimationFrame(clear); }
function drawGrid(W, H) { const g = CFG.grid; const left = (-pan.x / zoom) - g, top = (-pan.y / zoom) - g, right = left + W / zoom + g * 2, bottom = top + H / zoom + g * 2; ctx.fillStyle = '#172534'; for (let x = Math.floor(left / g) * g; x < right; x += g)
for (let y = Math.floor(top / g) * g; y < bottom; y += g) {
ctx.fillRect(x - 0.7, y - 0.7, 1.4, 1.4);
} }
function drawWire(w) { const p = wirePath(w), on = node(w.from).state; ctx.beginPath(); ctx.moveTo(p.a.x, p.a.y); ctx.bezierCurveTo(p.c1.x, p.c1.y, p.c2.x, p.c2.y, p.b.x, p.b.y); ctx.lineWidth = on ? 4 : 2; ctx.strokeStyle = on ? '#52ddbd' : '#314b63'; ctx.shadowBlur = on ? 14 : 0; ctx.shadowColor = '#4be1c0'; ctx.stroke(); ctx.shadowBlur = 0; }
function drawPreview() { const p = portPos(node(wireDrag.from.node), 'out', wireDrag.from.index), q = wireDrag.to; ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.bezierCurveTo(p.x + 70, p.y, q.x - 70, q.y, q.x, q.y); ctx.lineWidth = 3; ctx.strokeStyle = hoverPort ? '#8debd7' : '#607a91'; ctx.setLineDash([7, 5]); ctx.stroke(); ctx.setLineDash([]); }
function drawNode(n) { const h = nodeH(n), col = palette[n.type].color, s = selected === n.id; ctx.save(); ctx.shadowColor = s ? col : 'rgba(0,0,0,.45)'; ctx.shadowBlur = s ? 18 : 12; ctx.fillStyle = '#172534'; round(n.x, n.y, CFG.nodeW, h, 8); ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = s ? col : '#2b4358'; ctx.lineWidth = s ? 1.5 : 1; round(n.x, n.y, CFG.nodeW, h, 8); ctx.stroke(); ctx.fillStyle = col; round(n.x, n.y, CFG.nodeW, CFG.headerH, 8); ctx.fill(); ctx.fillStyle = '#0c1721'; ctx.fillRect(n.x, n.y + CFG.headerH - 8, CFG.nodeW, 8); ctx.font = '800 10px system-ui'; ctx.fillStyle = '#dbeaf4'; ctx.fillText(palette[n.type].label, n.x + 13, n.y + 20); ctx.font = '9px system-ui'; ctx.fillStyle = '#71899b'; ctx.fillText(n.type === 'clock' ? `${n.rate.toFixed(1)} Hz` : 'SIGNAL', n.x + 82, n.y + 20); const count = palette[n.type].inputs; for (let i = 0; i < count; i++) {
const p = portPos(n, 'in', i);
portCircle(p.x, p.y, '#69b7ff', hoverPort?.node === n.id && hoverPort.index === i && hoverPort.side === 'in');
ctx.fillStyle = '#7891a4';
ctx.font = '9px system-ui';
ctx.fillText(count > 1 ? 'IN ' + (i + 1) : 'IN', n.x + 12, p.y + 3);
} const op = portPos(n, 'out', 0); portCircle(op.x, op.y, n.state ? '#ffbf6c' : '#d77d45', hoverPort?.node === n.id && hoverPort.side === 'out'); ctx.fillStyle = n.state ? '#ffbf6c' : '#7590a3'; ctx.fillText(n.state ? 'ON' : 'OFF', n.x + 104, op.y + 3); if (n.type === 'toggle') {
ctx.fillStyle = n.state ? '#55e0ad' : '#334b5b';
ctx.fillRect(n.x + 12, n.y + CFG.headerH + 14, 35, 14);
ctx.fillStyle = n.state ? '#dffff2' : '#7891a4';
ctx.beginPath();
ctx.arc(n.x + (n.state ? 39 : 20), n.y + CFG.headerH + 21, 6, 0, Math.PI * 2);
ctx.fill();
} if (n.type === 'display') {
const val = n.bits.reduce((a, b, i) => a + (b ? (1 << (3 - i)) : 0), 0);
ctx.font = 'bold 27px monospace';
ctx.fillStyle = n.state ? '#ff7187' : '#4b3b48';
ctx.fillText(val.toString(16).toUpperCase(), n.x + 62, n.y + 74);
} if (n.type === 'lamp') {
ctx.beginPath();
ctx.arc(n.x + 77, n.y + 70, 19, 0, Math.PI * 2);
ctx.fillStyle = n.state ? '#ffd174' : '#304254';
ctx.shadowColor = '#ffc45f';
ctx.shadowBlur = n.state ? 22 : 0;
ctx.fill();
} if (n.type === 'clock') {
ctx.fillStyle = '#e4b35d';
ctx.font = '16px monospace';
ctx.fillText('◷', n.x + 14, n.y + 59);
} ctx.restore(); }
function round(x, y, w, h, r) { ctx.beginPath(); ctx.roundRect(x, y, w, h, r); }
function portCircle(x, y, c, hot) { ctx.beginPath(); ctx.arc(x, y, hot ? CFG.portR + 3 : CFG.portR, 0, Math.PI * 2); ctx.fillStyle = '#09131d'; ctx.fill(); ctx.beginPath(); ctx.arc(x, y, hot ? CFG.portR + 1 : CFG.portR - 1, 0, Math.PI * 2); ctx.fillStyle = c; ctx.shadowColor = c; ctx.shadowBlur = hot ? 14 : 5; ctx.fill(); ctx.shadowBlur = 0; }
function evalAll() { const indeg = new Map(); const outs = new Map(); scene.nodes.forEach(n => { indeg.set(n.id, 0); outs.set(n.id, []); n.bits.fill(false); }); scene.wires.forEach(w => { indeg.set(w.to, (indeg.get(w.to) || 0) + 1); outs.get(w.from).push(w); }); const q = scene.nodes.filter(n => indeg.get(n.id) === 0); for (const n of q)
compute(n); let done = 0; while (q.length) {
const n = q.shift();
done++;
for (const w of outs.get(n.id)) {
const t = node(w.to);
t.bits[w.toPort] = n.state;
indeg.set(t.id, indeg.get(t.id) - 1);
if (indeg.get(t.id) === 0) {
compute(t);
q.push(t);
}
}
} save(); return done === scene.nodes.length; }
function compute(n) { const b = n.bits; switch (n.type) {
case 'toggle': break;
case 'clock':
n.state = n.phase % 2 === 1;
break;
case 'and':
n.state = b.every(Boolean);
break;
case 'or':
n.state = b.some(Boolean);
break;
case 'not':
n.state = !b[0];
break;
case 'xor':
n.state = b.filter(Boolean).length === 1;
break;
case 'nand':
n.state = !b.every(Boolean);
break;
case 'nor':
n.state = !b.some(Boolean);
break;
case 'lamp':
n.state = !!b[0];
break;
case 'display':
n.state = b.some(Boolean);
break;
} }
function save() { localStorage.setItem(CFG.saveKey, JSON.stringify(scene)); }
function notify(t) { toast.textContent = t; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 1100); }
function cycle(from, to) { const seen = new Set(); const visit = (id) => { if (id === from)
return true; if (seen.has(id))
return false; seen.add(id); return scene.wires.filter(w => w.from === id).some(w => visit(w.to)); }; return visit(to); }
function connect(from, to) { if (from.side !== 'out' || to.side !== 'in' || from.node === to.node && from.index === to.index)
return false; if (cycle(from.node, to.node)) {
flashUntil = performance.now() + 500;
notify('FEEDBACK LOOP REJECTED');
return false;
} scene.wires = scene.wires.filter(w => !(w.to === to.node && w.toPort === to.index)); scene.wires.push({ from: from.node, fromPort: from.index, to: to.node, toPort: to.index }); evalAll(); save(); return true; }
function resize() { const d = devicePixelRatio || 1; canvas.width = canvas.clientWidth * d; canvas.height = canvas.clientHeight * d; }
function pos(e) { const r = canvas.getBoundingClientRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; }
canvas.addEventListener('pointerdown', e => { const s = pos(e), w = world(s.x, s.y), p = portAt(w), n = nodeAt(w); e.target.setPointerCapture?.(e.pointerId); if (p) {
if (p.side === 'out') {
wireDrag = { from: p, to: w };
}
else {
const old = scene.wires.find(x => x.to === p.node && x.toPort === p.index);
if (old) {
wireDrag = { from: { node: old.from, index: old.fromPort, side: 'out' }, to: w };
}
}
}
else if (n) {
selected = n.id;
dragNode = { id: n.id, dx: w.x - n.x, dy: w.y };
if (n.type === 'toggle') {
n.state = !n.state;
evalAll();
}
}
else {
selected = null;
panning = true;
last = s;
} });
canvas.addEventListener('pointermove', e => { const s = pos(e), w = world(s.x, s.y); hoverPort = portAt(w); if (wireDrag) {
wireDrag.to = w;
}
else if (dragNode) {
const n = node(dragNode.id);
n.x = w.x - dragNode.dx;
n.y = w.y - dragNode.dy;
save();
}
else if (panning) {
pan.x += s.x - last.x;
pan.y += s.y - last.y;
last = s;
} });
canvas.addEventListener('pointerup', e => { if (wireDrag) {
const target = hoverPort;
if (target)
connect(wireDrag.from, target);
wireDrag = null;
} dragNode = null; panning = false; });
canvas.addEventListener('wheel', e => { e.preventDefault(); const s = pos(e), before = world(s.x, s.y), factor = e.deltaY < 0 ? 1.1 : .9; zoom = Math.max(CFG.minZoom, Math.min(CFG.maxZoom, zoom * factor)); pan.x = s.x - before.x * zoom; pan.y = s.y - before.y * zoom; }, { passive: false });
window.addEventListener('keydown', e => { if ((e.key === 'Delete' || e.key === 'Backspace') && selected) {
scene.wires = scene.wires.filter(w => w.from !== selected && w.to !== selected);
scene.nodes = scene.nodes.filter(n => n.id !== selected);
selected = null;
evalAll();
save();
} });
document.querySelectorAll('.palette-item').forEach(el => el.addEventListener('click', () => { const type = el.getAttribute('data-type'); const c = world(stage.clientWidth / 2, stage.clientHeight / 2); const n = makeNode(type, c.x - CFG.nodeW / 2, c.y - 50); scene.nodes.push(n); selected = n.id; evalAll(); save(); }));
document.querySelector('#clearBtn').addEventListener('click', () => { scene = { nodes: [], wires: [] }; selected = null; save(); });
document.querySelector('#saveBtn').addEventListener('click', () => { save(); notify('SCENE SAVED'); });
window.addEventListener('resize', resize);
resize();
evalAll();
setInterval(() => { for (const n of scene.nodes)
if (n.type === 'clock') {
n.phase++;
compute(n);
evalAll();
} }, 500);
clear();
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"typescript","sessionId":"579e181f-88f1-4335-b0f9-3d81bbb8bc77","tokensIn":1012373,"tokensOut":31844,"tokensTotal":1044217,"turns":23,"toolCalls":30,"failedToolCalls":1,"timestamp":"2026-07-31T03:11:27.234Z"} -->