diff --git a/static/js/cookbook-hwfit.js b/static/js/cookbook-hwfit.js index 3d438aa..6817d15 100644 --- a/static/js/cookbook-hwfit.js +++ b/static/js/cookbook-hwfit.js @@ -1094,12 +1094,13 @@ export function _hwfitInit() { for (const sel of selectors) { if (!sel) continue; const currentVal = sel.value; - sel.innerHTML = ``; + let html = ``; _envState.servers.forEach((s, i) => { if (!s.host) return; const label = s.name || s.host || `Server ${i + 1}`; - sel.innerHTML += ``; + html += ``; }); + sel.innerHTML = html; sel.value = currentVal; } } diff --git a/static/js/group.js b/static/js/group.js index 4914d76..ed98872 100644 --- a/static/js/group.js +++ b/static/js/group.js @@ -487,10 +487,11 @@ export async function showModelPicker() { `; const sel = document.createElement('select'); sel.style.cssText = 'font-size:11px;padding:3px 6px;border-radius:4px;border:1px solid var(--border);background:var(--bg);color:var(--fg);max-width:140px;'; - sel.innerHTML = ''; + let optsHtml = ''; characters.forEach(c => { - sel.innerHTML += ``; + optsHtml += ``; }); + sel.innerHTML = optsHtml; sel.addEventListener('change', () => { if (sel.value) { const ch = characters.find(c => c.id === sel.value); diff --git a/static/js/ui.js b/static/js/ui.js index 3142790..5af1d2c 100644 --- a/static/js/ui.js +++ b/static/js/ui.js @@ -516,14 +516,15 @@ export function styledPrompt(message, { }); } +// Lookup table for esc(); hoisted out of the replace callback so it is +// allocated once rather than per matched character. +const _ESC_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; /** * HTML-escape a string to prevent XSS. * Canonical implementation — other modules should use uiModule.esc() instead of local copies. */ export function esc(s) { - return (s || '').replace(/[&<>"']/g, function(m) { - return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]; - }); + return (s || '').replace(/[&<>"']/g, (m) => _ESC_MAP[m]); } // ── Mobile: suppress synthetic click/mousedown on backdrop ──