You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
7.0 KiB
129 lines
7.0 KiB
'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: [], dotMode: null |
|
}; |
|
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; API._applyDot(); }, setDot: function (m) { state.dotMode = (m === 'ok') ? null : m; API._applyDot(); }, _applyDot: function () { if (!el.dot) return; var md = state.dotMode || (state.connected ? 'ok' : 'err'); var cls = 'dshmem-dot'; if (md === 'ok' || md === 'err' || md === 'busy' || md === 'ok-busy') cls += ' ' + md; el.dot.className = cls; }, |
|
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(); } |
|
})(); |