SiteโฏBuilder
Editing:
melanie-prompts.js
writable 0666
/* melanie-prompts.js Updated to replicate Melanie AI Promptinator behavior: - Supports A, B, C, D, I tokens - Renders prompts with dynamic tokens - Inline toolbar for editing values - AI buttons with dynamic prompt URL generation - Optional Markdown parsing via marked.js */ (() => { const SEP = ' โข '; const RX = /\[(?:([ABCDI]?)-)?([^~|\-\]]+)(?:-([^~]*?))?(?:~([^~]+)~)?\]/g; document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('[data-melanie-prompt]').forEach(card => { const tpl = card.dataset.melaniePrompt; const out = card.querySelector('[data-m-output]'); const aiBtns = [...card.querySelectorAll('[data-m-ai]')]; const state = {}; const meta = []; // Initialize meta and state tpl.replace(RX, (m, cmd = 'A', lab, ops = '', def = '') => { const opts = ops.split('|').filter(Boolean); meta.push({cmd, lab, opts}); if (cmd === 'C') { state[lab] = def ? def.split(',').map(v => v.trim()) : []; } else { state[lab] = def || ''; } }); function renderText() { const substituted = tpl.replace(RX, (m, cmd = 'A', lab) => { const idx = meta.findIndex(x => x.lab === lab); if (cmd === 'C') { return (state[lab] || []).map(val => `<span class=\"token\" data-i=\"${idx}\">${val}</span>`).join(' '); } return `<span class=\"token\" data-i=\"${idx}\">${state[lab] || ''}</span>`; }); if (window.marked) { out.innerHTML = marked.parse(substituted); } else { out.innerHTML = substituted.replace(/\n/g, '<br>'); } out.querySelectorAll('.token').forEach(el => el.addEventListener('click', onTokenClick)); } function rebuild() { const promptText = tpl.replace(RX, (m, cmd = 'A', lab) => { if (cmd === 'C') { return (state[lab] || []).join(SEP); } return state[lab] || ''; }); aiBtns.forEach(btn => { const base = btn.dataset.mAi; btn.href = base + encodeURIComponent(promptText); }); } function onTokenClick(e) { const tok = e.currentTarget; const idx = parseInt(tok.dataset.i, 10); const {cmd, lab, opts} = meta[idx]; const cur = state[lab]; popup(card, {cmd, lab, opts}, cur, newVal => { if (cmd === 'C') { state[lab] = typeof newVal === 'string' ? newVal.split(',').map(v => v.trim()).filter(v => v) : newVal; } else { state[lab] = newVal; } renderText(); rebuild(); }); } // Initial render renderText(); rebuild(); }); }); // Popup dialog singleton const dlg = document.createElement('dialog'); dlg.style.padding = '0'; dlg.style.border = 'none'; document.body.appendChild(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) { const {cmd, lab, opts} = meta; dlg.innerHTML = '<div class="popup-body"></div>'; const box = dlg.firstChild; box.className = 'popup-body'; const nice = lab.replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); const close = v => { done(v); dlg.close(); }; switch (cmd) { case 'A': box.innerHTML = `<label>${nice}<textarea rows="4">${cur}</textarea></label>`; const ta = box.querySelector('textarea'); ta.onkeydown = e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); close(ta.value); } }; ta.onblur = () => dlg.close(); ta.focus(); break; case 'B': box.innerHTML = `<fieldset><legend>${nice}</legend>${ opts.map(o => `<label><input type=\"radio\" name=\"r\" value=\"${o}\" ${o === cur ? 'checked' : ''}>${o}</label>`).join('<br>') }</fieldset>`; box.addEventListener('click', e => { if (e.target.name === 'r') close(e.target.value); }); break; case 'C': box.innerHTML = `<fieldset><legend>${nice}</legend>${ opts.map(o => `<label><input type=\"checkbox\" value=\"${o}\" ${cur.includes(o) ? 'checked' : ''}>${o}</label>`).join('<br>') }</fieldset><button style=\"margin-top:.6rem\">Done</button>`; box.querySelector('button').addEventListener('click', () => { const selected = [...box.querySelectorAll('input:checked')].map(c => c.value); close(selected.join(', ')); }); break; case 'D': { let choices = opts.length ? opts.slice() : []; if (choices.length === 1 && /^\d+$/.test(choices[0])) { choices = Array.from({length: +choices[0]}, (_, i) => `${i+1}`); } box.innerHTML = `<label>${nice}<select>${ choices.map(o => `<option${o === cur ? ' selected' : ''}>${o}</option>`).join('') }</select></label>`; box.querySelector('select').addEventListener('change', e => close(e.target.value)); break; } case 'I': box.innerHTML = `<label>${nice}<select><option disabled selected>${cur}</option>$ opts.map(u => `<option>${u}</option>`).join('') }</select></label> <iframe hidden style="border:1px solid #ccd;width:100%;min-height:200px;margin-top:.6rem"></iframe> <button style="margin-top:.6rem">Use site</button>`; { const sel = box.querySelector('select'); const fr = box.querySelector('iframe'); sel.addEventListener('change', () => { fr.hidden = false; fr.src = sel.value; }); box.querySelector('button').addEventListener('click', () => close(sel.value || cur)); } break; default: box.innerHTML = `<label>${nice}<input value="${cur}"></label>`; const inp = box.querySelector('input'); inp.onkeydown = e => { if (e.key === 'Enter') close(inp.value); }; inp.onblur = () => dlg.close(); inp.focus(); } dlg.showModal(); } })();
Save changes
Create folder
writable 0777
Create
Cancel