Siteβ―Builder
Editing:
melanie-prompts.js
writable 0666
/* melanie-prompts.js drop in /js and include on any page */ (() => { /* ---------- 0. grammar helpers ---------------- */ const RX = /\[(?:([A-DI]?)-)?([^~|\-\]]+)(?:-([^~\]]+))?(?:~([^~]+)~)?\]/ig; /* ---------- 1. boot on DOM ready -------------- */ document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('[data-melanie-prompt]').forEach(card => { const tpl = card.dataset.melaniePrompt; // raw template const out = card.querySelector('[data-m-output]'); const state = {}, meta = []; /* 1-A replace tokens with clickable <span> */ const html = tpl.replace(RX, (m, cmd = '', lab, ops = '', def = '') => { meta.push({cmd, lab, ops: ops.split('|').filter(Boolean), def}); state[lab] = def || lab; return `<span class="token" data-i="${meta.length - 1}">[${state[lab]}]</span>`; }); out.innerHTML = html.replace(/\n/g, '<br>'); /* 1-B token click β popup */ out.addEventListener('click', e => { if (!e.target.matches('.token')) return; const idx = +e.target.dataset.i; const info = meta[idx]; const lab = info.lab; popup(card, info, state[lab], v => { state[lab] = v; e.target.textContent = '[' + v + ']'; }); }); /* 1-C AI buttons in this card */ card.querySelectorAll('[data-m-ai]').forEach(btn => btn.onclick = () => { const base = btn.dataset.mAi; const prompt = tpl.replace(RX, (_, __, lab) => state[lab]); window.open(base + encodeURIComponent(prompt), '_blank'); }); }); }); /* ---------- 2. popup factory ------------------- */ const dlg = document.createElement('dialog'); dlg.style.padding = '0'; dlg.style.border = 'none'; document.body.append(dlg); dlg.addEventListener('click', e => { if (e.target === dlg) dlg.close(); }); dlg.addEventListener('cancel', e => { e.preventDefault(); dlg.close(); }); function popup(card, meta, cur, done) { dlg.innerHTML = '<div class="popup-body"></div>'; const box = dlg.firstChild; box.className = 'popup-body'; const nice = meta.lab.replace(/[-_]/g, ' ') .replace(/\b\w/g, l => l.toUpperCase()); // two flavors of close: update-only vs update+close const update = v => done(v); const save = v => { done(v); dlg.close(); }; switch (meta.cmd) { case 'A': { // single-line input with live update and final save box.innerHTML = `<label>${nice}<input type="text" value="${cur}"></label>`; const inp = box.querySelector('input'); inp.addEventListener('input', e => update(e.target.value)); inp.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); save(e.target.value); } }); inp.addEventListener('blur', () => save(inp.value)); inp.focus(); break; } case 'B': { box.innerHTML = `<fieldset><legend>${nice}</legend>` + meta.ops.map(o => `<label><input type="radio" name="r" value="${o}" ${o === cur ? 'checked' : ''}>${o}</label>` ).join('<br>') + `</fieldset>`; box.onclick = e => { if (e.target.name === 'r') save(e.target.value); }; break; } case 'C': { box.innerHTML = `<fieldset><legend>${nice}</legend>` + meta.ops.map(o => `<label><input type="checkbox" value="${o}" ${cur.split(', ').includes(o) ? 'checked' : ''}>${o}</label>` ).join('<br>') + `</fieldset><button>Done</button>`; box.querySelector('button').onclick = () => save([...box.querySelectorAll('input:checked')] .map(c => c.value).join(', ') || cur); break; } case 'D': { let ops = meta.ops.map(s => s.trim()).filter(Boolean); if (ops.length === 1 && /^\d+$/.test(ops[0])) ops = Array.from({length: +ops[0]}, (_, i) => '' + (i + 1)); box.innerHTML = `<label>${nice}<select>` + ops.map(o => `<option${o == cur ? ' selected' : ''}>${o}</option>` ).join('') + `</select></label>`; box.querySelector('select').onchange = e => save(e.target.value); break; } case 'I': { box.innerHTML = ` <label>${nice}<select> <option disabled selected>${cur}</option> ${meta.ops.map(u => `<option>${u}</option>`).join('')} </select></label> <iframe hidden style="border:1px solid #ccd;width:100%;min-height:340px;margin-top:.6rem"></iframe> <button style="margin-top:.6rem">Use site</button>`; const sel = box.querySelector('select'), fr = box.querySelector('iframe'); sel.onchange = () => { fr.hidden = false; fr.src = sel.value; }; box.querySelector('button').onclick = () => save(sel.value || cur); break; } default: { box.innerHTML = `<label>${nice}<input value="${cur}"></label>`; const inp = box.querySelector('input'); inp.addEventListener('keydown', e => { if (e.key === 'Enter') save(inp.value); }); inp.addEventListener('blur', () => save(inp.value)); inp.focus(); } } dlg.showModal(); } })();
Save changes
Create folder
writable 0777
Create
Cancel