'use strict'; (function () { var UI_ID = 'dshmem-panel'; var LOG_CAP = 60; var state = { phase: 'off', connected: false, loopsLeft: 0, loopsMax: 0, dailyCount: 0, dailyMax: 0, resetCbs: [], stopCbs: [], saveCbs: [], startCbs: [] }; var el = {}; function byId(id) { return document.getElementById(id); } function mk(tag, cls, txt) { var n = document.createElement(tag); if (cls) n.className = cls; if (txt !== undefined) n.textContent = txt; return n; } function build() { if (byId(UI_ID)) return; var root = mk('div', 'dshmem-panel'); root.id = UI_ID; var head = mk('div', 'dshmem-head'); el.dot = mk('span', 'dshmem-dot'); el.dot.id = 'dshmem-dot'; var title = mk('span', 'dshmem-title', 'DSH Memory'); el.phase = mk('span', 'dshmem-phase', 'Off'); el.phase.id = 'dshmem-phase'; el.toggle = mk('span', 'dshmem-toggle', '-'); head.appendChild(el.dot); head.appendChild(title); head.appendChild(el.phase); head.appendChild(el.toggle); el.body = mk('div', 'dshmem-body'); root.appendChild(head); root.appendChild(el.body); var row1 = mk('div', 'dshmem-row'); el.start = mk('button', 'dshmem-btn primary', 'Start'); el.reset = mk('button', 'dshmem-btn', 'Reset queue'); el.stop = mk('button', 'dshmem-btn danger', 'Stop'); row1.appendChild(el.start); row1.appendChild(el.reset); row1.appendChild(el.stop); el.body.appendChild(row1); el.stats = mk('div','dshmem-stats'); el.stats.id='dshmem-stats'; el.body.appendChild(el.stats); el.metrics = mk('div','dshmem-stats'); el.metrics.id='dshmem-metrics'; el.metrics.textContent='metrics: —'; el.body.appendChild(el.metrics); var set = mk('div', 'dshmem-settings'); var lbl = mk('div', 'dshmem-label', 'Token (save, then reload page)'); el.token = mk('input', 'dshmem-input'); el.token.type = 'password'; el.token.placeholder = 'dshmem-bridge-token'; var row2 = mk('div', 'dshmem-row'); el.save = mk('button', 'dshmem-btn primary', 'Save'); el.health = mk('button', 'dshmem-btn', 'Health'); row2.appendChild(el.save); row2.appendChild(el.health); set.appendChild(lbl); set.appendChild(el.token); set.appendChild(row2); el.body.appendChild(set); el.log = mk('div', 'dshmem-log'); el.log.id = 'dshmem-log'; el.body.appendChild(el.log); el.preview = mk('div', 'dshmem-preview'); el.preview.style.display = 'none'; el.body.appendChild(el.preview); el.confirm = mk('div', 'dshmem-confirm-box'); el.confirm.style.display = 'none'; el.body.appendChild(el.confirm); document.body.appendChild(root); } function wire() { el.toggle.addEventListener('click', function () { el.body.style.display = el.body.style.display === 'none' ? '' : 'none'; }); el.reset.addEventListener('click', function () { el.start.disabled = false; state.resetCbs.forEach(function (cb) { try { cb(); } catch (e) {} }); }); el.stop.addEventListener('click', function () { el.start.disabled = false; state.stopCbs.forEach(function (cb) { try { cb(); } catch (e) {} }); }); el.start.addEventListener('click', function () { el.start.disabled = true; state.startCbs.forEach(function (cb) { try { cb(); } catch (e) {} }); }); el.save.addEventListener('click', function () { var v = el.token.value || ''; state.saveCbs.forEach(function (cb) { try { cb(v); } catch (e) {} }); }); el.health.addEventListener('click', function () { try { globalThis.DSH_BRIDGE.health().then(function (r) { if (r && r.ok) { API.setConnected(true); API.log('health ok, bridge up'); } else { API.setConnected(false); API.log('health fail: ' + ((r && r.error && r.error.message) || '?')); } }); } catch (e) { API.log('health throw: ' + String(e)); } }); } var API = { setPhase: function (p) { state.phase = p; if (el.phase) el.phase.textContent = p; }, setConnected: function (b) { state.connected = !!b; if (el.dot) el.dot.className = 'dshmem-dot ' + (b ? 'ok' : 'err'); }, setStartEnabled: function (b) { if (el.start) el.start.disabled = !b; }, setStats: function (ws, memN, skillN) { if (!el.stats) return; el.stats.textContent = 'WS: ' + (ws || '?') + ' mem: ' + (memN || 0) + ' skills: ' + (skillN || 0); }, setMetrics: function (m) { if (!el.metrics) return; if (!m) { el.metrics.textContent = 'metrics: —'; return; } el.metrics.textContent = 'acc: ' + m.accum + ' · turn: ' + m.delta + ' · msg: ' + m.chars + 'ch (~' + m.est_tok + 't) ctx ' + (m.ctx_tok || 0) + 't ' + (m.ctx_pct || 0) + 'pct'; }, log: function (m) { if (!el.log) return; var t = new Date().toLocaleTimeString(); var line = '[' + t + '] ' + m; var txt = (line + String.fromCharCode(10) + (el.log.textContent || '')).split(String.fromCharCode(10)).slice(0, LOG_CAP).join(String.fromCharCode(10)); el.log.textContent = txt; }, onResetLoops: function (cb) { state.resetCbs.push(cb); }, onStop: function (cb) { state.stopCbs.push(cb); }, onStart: function (cb) { state.startCbs.push(cb); }, onSaveToken: function (cb) { state.saveCbs.push(cb); }, getToken: function () { return el.token ? el.token.value : ''; }, setToken: function (v) { if (el.token) el.token.value = v || ''; }, showConfirm: function (opt) { if (!el.confirm) return; opt = opt || {}; el.confirm.textContent = ''; el.confirm.appendChild(mk('div', 'dshmem-confirm-title', opt.title || 'Confirm')); el.confirm.appendChild(mk('div', 'dshmem-label', opt.detail || '')); var r = mk('div', 'dshmem-row'); var ok = mk('button', 'dshmem-btn primary', 'Allow'); var no = mk('button', 'dshmem-btn danger', 'Deny'); ok.addEventListener('click', function () { API.hideConfirm(); if (opt.onAllow) opt.onAllow(); }); no.addEventListener('click', function () { API.hideConfirm(); if (opt.onDeny) opt.onDeny(); }); r.appendChild(ok); r.appendChild(no); el.confirm.appendChild(r); el.confirm.style.display = 'block'; }, hideConfirm: function () { if (el.confirm) el.confirm.style.display = 'none'; }, showPreview: function (opt) { if (!el.preview) return; opt = opt || {}; var sec = opt.seconds || 2; var text = opt.text || ''; var cancelled = false; el.preview.style.display = 'block'; var render = function () { el.preview.textContent = 'Sending in ' + sec + 's (click to cancel):' + String.fromCharCode(10) + text.slice(0, 500); }; render(); var iv = setInterval(function () { sec--; if (sec <= 0) { clearInterval(iv); el.preview.style.display = 'none'; el.preview.onclick = null; if (!cancelled && opt.onSend) opt.onSend(); } else render(); }, 1000); el.preview.onclick = function () { cancelled = true; clearInterval(iv); el.preview.style.display = 'none'; el.preview.onclick = null; if (opt.onCancel) opt.onCancel(); }; }, hidePreview: function () { if (el.preview) el.preview.style.display = 'none'; }, init: function () { build(); wire(); } }; globalThis.DSHMEM = API; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { API.init(); }); } else { API.init(); } })();