diff --git a/pkg/miniapp/static/map-preview.html b/pkg/miniapp/static/map-preview.html new file mode 100644 index 000000000..4df562f39 --- /dev/null +++ b/pkg/miniapp/static/map-preview.html @@ -0,0 +1,98 @@ + + + + + + Orchestration Room — Preview + + + + + + + +
+ 👑 conductor + 👩‍💼 secretary + 🔍 scout + 📊 analyst + 💻 coder + 🔧 worker + 🎯 coordinator + 🚪 door +
+ + + + diff --git a/pkg/miniapp/static/map.js b/pkg/miniapp/static/map.js new file mode 100644 index 000000000..74b56e433 --- /dev/null +++ b/pkg/miniapp/static/map.js @@ -0,0 +1,204 @@ +// map.js — Orchestration Room +// +// External asset: drop map.png (320×320px) next to index.html to replace +// the procedural fallback. Character positions (MAP_POSITIONS) are defined +// in canvas-pixel coordinates and remain valid regardless of which rendering +// path is used — just make sure your map.png matches them. +// +// Usage: +// loadMapAsset(function() { drawMap(ctx); }); // call once on init +// drawMap(ctx); // call each frame + +// ─── Character home positions (px, canvas 320×320) ───────────────────────── +// +// ┌──────────────────────────────┐ +// │ [conductor desk] │ y ≈ 20–50 +// │ 👑(160,58) 👩‍💼(108,58) │ +// │ [carpet] │ +// │ [WS1] [WS2] [WS3] │ y ≈ 80–100 +// │ 🔍40 💻144 📊248 │ y = 106 +// │ [meeting area] │ y ≈ 130–192 +// │ [WS4] [WS5] │ y ≈ 200–220 +// │ 🔧40 🎯144 │ y = 222 +// │ 🚪(160,308) │ door +// └──────────────────────────────┘ + +var MAP_POSITIONS = { + door: { x: 160, y: 314 }, // entry / exit point + conductor: { x: 160, y: 58 }, + secretary: { x: 108, y: 58 }, + meeting: { x: 160, y: 161 }, // neutral zone for conversations + stations: [ + { x: 40, y: 106 }, // S0 scout + { x: 144, y: 106 }, // S1 analyst + { x: 248, y: 106 }, // S2 coder + { x: 40, y: 222 }, // S3 worker + { x: 144, y: 222 }, // S4 coordinator + ], +}; + +// ─── Asset loading ────────────────────────────────────────────────────────── + +var _mapImage = null; + +// Call once before first draw. cb() is invoked when ready (image or fallback). +function loadMapAsset(cb) { + var img = new Image(); + img.onload = function() { _mapImage = img; cb(); }; + img.onerror = function() { cb(); }; // no map.png → use fallback + img.src = './map.png'; +} + +// ─── Public draw entry point ──────────────────────────────────────────────── + +function drawMap(ctx) { + ctx.imageSmoothingEnabled = false; + if (_mapImage) { + ctx.drawImage(_mapImage, 0, 0, 320, 320); + } else { + _drawMapFallback(ctx); + } +} + +// ─── Procedural fallback ──────────────────────────────────────────────────── + +var _C = { + wallDark: '#0c1018', + wallHighlight: '#252d3f', + floorA: '#171b2c', + floorB: '#1b2033', + carpetBase: '#1a2050', + carpetBorder: '#2a3480', + deskBack: '#2c3e6b', + deskTop: '#3a50a0', + deskEdge: '#4a6ac0', + deskShadow: '#1a2448', + monitorFrame: '#070b14', + monitorBlue: '#1040a0', + monitorGlow: '#4488ff', + wsBase: '#162818', + wsTop: '#1e3822', + wsEdge: '#2a5030', + termGlow: '#00dd55', + rugFill: '#1c2248', + rugBorder: '#283070', + doorMid: '#8a5818', + doorLight: '#a06820', + doorGold: '#c8940a', +}; + +function _r(ctx, color, x, y, w, h, alpha) { + ctx.globalAlpha = alpha === undefined ? 1 : alpha; + ctx.fillStyle = color; + ctx.fillRect(x, y, w, h); + ctx.globalAlpha = 1; +} + +function _b(ctx, color, x, y, w, h) { + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.strokeRect(x + 0.5, y + 0.5, w - 1, h - 1); +} + +function _dot(ctx, color, x, y) { + ctx.fillStyle = color; + ctx.fillRect(x, y, 2, 2); +} + +function _workstation(ctx, x, y) { + _r(ctx, _C.wsBase, x, y, 48, 20); + _r(ctx, _C.wsTop, x, y, 48, 8); + _r(ctx, _C.wsEdge, x, y, 2, 20); + _r(ctx, _C.wsEdge, x+46, y, 2, 20); + _r(ctx, _C.wsEdge, x, y, 48, 2); + // terminal screen + _r(ctx, _C.monitorFrame, x+16, y+2, 16, 12); + _r(ctx, '#041008', x+17, y+3, 14, 10); + _r(ctx, '#003315', x+18, y+4, 12, 8); + _r(ctx, _C.termGlow, x+20, y+6, 8, 3); + _dot(ctx, '#00ff88', x+22, y+6); +} + +function _drawMapFallback(ctx) { + var T = 16; + + // floor tiles + for (var ty = 0; ty < 20; ty++) { + for (var tx = 0; tx < 20; tx++) { + ctx.fillStyle = (tx + ty) % 2 === 0 ? _C.floorA : _C.floorB; + ctx.fillRect(tx * T, ty * T, T, T); + } + } + + // conductor carpet + _r(ctx, _C.carpetBase, 16, 16, 288, 50); + _b(ctx, _C.carpetBorder, 18, 18, 284, 46); + + // conductor desk + _r(ctx, _C.deskBack, 96, 20, 128, 30); + _r(ctx, _C.deskTop, 96, 20, 128, 12); + _r(ctx, _C.deskEdge, 96, 20, 128, 2); + _r(ctx, _C.deskEdge, 96, 20, 2, 30); + _r(ctx, _C.deskEdge, 222, 20, 2, 30); + _r(ctx, _C.deskShadow,96,48, 128, 4); + // monitor + _r(ctx, _C.monitorFrame, 138, 22, 44, 14); + _r(ctx, _C.monitorBlue, 140, 23, 40, 12); + _r(ctx, _C.monitorGlow, 156, 26, 8, 6); + _r(ctx, '#6699ff', 158, 27, 4, 3); + + // workstations + _workstation(ctx, 16, 80); // S0 + _workstation(ctx, 128, 80); // S1 (x+24 = 152 ≈ 144 center) + _workstation(ctx, 224, 80); // S2 + _workstation(ctx, 16, 200); // S3 + _workstation(ctx, 128, 200); // S4 + + // meeting rug + _r(ctx, _C.rugFill, 64, 130, 192, 62, 0.55); + _b(ctx, _C.rugBorder, 66, 132, 188, 58); + _b(ctx, '#202860', 70, 136, 180, 50); + + // bulletin board (left wall) + _r(ctx, '#2c1a06', 18, 148, 36, 44); + _r(ctx, '#3a2508', 20, 150, 32, 40); + _r(ctx, '#cc9900', 22, 153, 12, 8); + _r(ctx, '#dd8800', 22, 164, 10, 6); + _r(ctx, '#bb7700', 34, 155, 13, 8); + _r(ctx, '#ccaa00', 33, 165, 11, 6); + _dot(ctx, '#ff4444', 28, 153); + _dot(ctx, '#44aaff', 41, 158); + _dot(ctx, '#44ff88', 27, 165); + + // server rack (right wall) + _r(ctx, '#111122', 285, 80, 18, 112); + _r(ctx, '#181830', 287, 82, 14, 108); + for (var i = 0; i < 10; i++) { + var ry = 85 + i * 10; + _r(ctx, '#0a0a12', 288, ry, 12, 8); + var lc = ['#00ff44','#0044ff','#ff3300','#111111'][i % 4]; + _r(ctx, lc, 296, ry + 2, 3, 4); + } + + // walls (drawn last to cover any overruns) + _r(ctx, _C.wallDark, 0, 0, 320, 16); + _r(ctx, _C.wallHighlight, 0, 14, 320, 2); + _r(ctx, _C.wallDark, 0, 0, 16, 320); + _r(ctx, _C.wallHighlight,14, 0, 2, 320); + _r(ctx, _C.wallDark, 304, 0, 16, 320); + _r(ctx, _C.wallHighlight,304, 0, 2, 320); + _r(ctx, _C.wallDark, 0, 304, 144, 16); + _r(ctx, _C.wallDark, 176, 304, 144, 16); + _r(ctx, _C.wallHighlight, 0, 304, 144, 2); + _r(ctx, _C.wallHighlight,176, 304, 144, 2); + + // door + _r(ctx, '#0a0808', 144, 292, 32, 12); // outside (dark) + _r(ctx, _C.doorMid, 144, 280, 32, 24); + _r(ctx, _C.doorLight, 144, 280, 32, 3); + _r(ctx, _C.doorLight, 144, 280, 3, 24); + _r(ctx, _C.doorLight, 173, 280, 3, 24); + _r(ctx, '#4a2408', 146, 284, 12, 16); // door panels + _r(ctx, '#4a2408', 162, 284, 12, 16); + _r(ctx, _C.doorGold, 170, 291, 5, 5); // handle +}