Siteβ―Builder
Editing:
Promptinator.js
writable 0666
// Promptinator.js // USAGE: Drop <div class="Promptinator">Your Prompt...</div> into any page, then load this JS. // Handles multiple .Promptinator divs on one page. Attach with: new Promptinator(div, {prompt: ...}); (function(){ // --- REGEX: [A/B/C/D/I]-label-|opts|~default~] const RX = /\[(?:([ABCDI]?)-)?([^~|\-\]]+)(?:-([^~\]]*))?(?:~([^~\]]*)~)?\]/g; // --- Utility: escapeHTML (for prompt edit preview) function esc(s){return String(s).replace(/[<>&"']/g, c=>({'<':'<','>':'>','&':'&','"':'"',"'":'''}[c]));} // --- Main Promptinator Class window.Promptinator = class Promptinator { constructor(container, opts = {}) { // Accepts: container DOM node, opts: {prompt: "string", editable: true/false} this.div = container; this.promptRaw = opts.prompt || this.div.textContent.trim(); this.state = {}; // {key: value} this.meta = []; // [{cmd,lab,opts,def}] this.editable = opts.editable !== false; // default true this.buildUI(); } buildUI() { // Clear container this.div.innerHTML = ''; // --- Editor (toggle) this.editorLink = document.createElement('span'); this.editorLink.textContent = 'βοΈ Edit prompt'; this.editorLink.className = 'pi-edit-link'; this.editorLink.style.cssText = 'cursor:pointer;color:#5c3bff;text-decoration:underline;display:block;margin-bottom:.4em;'; this.div.append(this.editorLink); this.editorForm = document.createElement('form'); this.editorForm.style.display = 'none'; this.editorForm.innerHTML = ` <textarea class="pi-edit-ta" style="width:100%;min-height:90px;font-family:monospace"></textarea> <div style="text-align:right;margin-top:.6em;"> <button type="submit">Save & Update</button> </div> `; this.div.append(this.editorForm); this.editorLink.onclick = () => { const open = this.editorForm.style.display !== ''; this.editorForm.style.display = open ? '' : 'none'; this.editorForm.querySelector('textarea').value = this.promptRaw; this.editorLink.textContent = open ? 'β² Hide editor' : 'βοΈ Edit prompt'; }; this.editorForm.onsubmit = (e) => { e.preventDefault(); this.promptRaw = this.editorForm.querySelector('textarea').value.trim(); this.editorForm.style.display = 'none'; this.editorLink.textContent = 'βοΈ Edit prompt'; this.renderPrompt(); }; // --- Prompt display this.promptDisplay = document.createElement('div'); this.promptDisplay.className = 'pi-prompt-display'; this.div.append(this.promptDisplay); // --- Toolbar (inline, only one active at a time) this.toolbar = document.createElement('div'); this.toolbar.className = 'pi-toolbar'; this.toolbar.style.display = 'none'; this.div.append(this.toolbar); // --- Render initial this.renderPrompt(); } renderPrompt() { // Parse meta/tokens this.meta = []; this.state = {}; let idx = 0; // Replace tokens, collect meta const html = this.promptRaw.replace(RX, (m, cmd='', lab, ops='', def='') => { this.meta.push({cmd, lab: lab.trim(), ops: (ops||'').split('|').filter(Boolean), def}); if (cmd === 'C') { // Multi-select: Show all default, comma separated this.state[lab] = def ? def.split(',').map(v=>v.trim()) : []; return (this.state[lab]||[]).map(tag => `<span class="pi-token" data-i="${idx}">${esc(tag)}</span>`).join(' '); } else { this.state[lab] = def || ''; return `<span class="pi-token" data-i="${idx}">${esc(this.state[lab])}</span>`; } }); // Insert prompt HTML with interactive tokens this.promptDisplay.innerHTML = `<div class="pi-prompt-text">${html.replace(/\n/g,'<br>')}</div> <div class="pi-ai-btns" style="margin-top:1em;"> <a class="pi-chatgpt" style="margin-right:.6em;" target="_blank">ChatGPT</a> <a class="pi-perplexity" style="margin-right:.6em;" target="_blank">Perplexity</a> <a class="pi-copilot" target="_blank">Copilot</a> </div>`; // Token click handlers this.promptDisplay.querySelectorAll('.pi-token').forEach(tok => { tok.onclick = (e) => this.showToolbar(e.currentTarget, +tok.dataset.i); }); // AI Buttons: always reflect current state const output = this.promptRaw.replace(RX, (m, cmd='', lab) => { if (cmd === 'C') return (this.state[lab]||[]).join(' β’ '); return this.state[lab] || ''; }); this.promptDisplay.querySelector('.pi-chatgpt').href = 'https://chatgpt.com/?prompt=' + encodeURIComponent(output); this.promptDisplay.querySelector('.pi-perplexity').href = 'https://www.perplexity.ai/search?q=' + encodeURIComponent(output); this.promptDisplay.querySelector('.pi-copilot').href = 'https://copilot.microsoft.com/?q=' + encodeURIComponent(output); // Hide toolbar on re-render this.toolbar.style.display = 'none'; } showToolbar(tokenElem, idx) { const {cmd, lab, ops, def} = this.meta[idx]; this.toolbar.innerHTML = ''; this.toolbar.style.display = 'block'; // Position toolbar: inline below token // (Absolutely position within .Promptinator) const tokenRect = tokenElem.getBoundingClientRect(); const parentRect = this.div.getBoundingClientRect(); this.toolbar.style.position = 'absolute'; this.toolbar.style.left = (tokenRect.left - parentRect.left) + 'px'; this.toolbar.style.top = (tokenRect.bottom - parentRect.top + 6) + 'px'; this.toolbar.style.minWidth = '210px'; // Label const label = document.createElement('label'); label.style = 'font-weight:600;font-size:.99em;display:block;margin-bottom:.5em;'; label.textContent = lab.replace(/_/g,' ').replace(/\b\w/g,c=>c.toUpperCase()); this.toolbar.append(label); if (cmd === 'C') { // Checkbox list const list = document.createElement('div'); list.style = 'display:flex;flex-direction:column;gap:.35em;max-height:180px;overflow:auto;'; ops.forEach(o => { const cb = document.createElement('input'); cb.type = 'checkbox'; cb.value = o; cb.checked = (this.state[lab]||[]).includes(o); cb.onchange = () => { this.state[lab] = [...list.querySelectorAll('input:checked')].map(x=>x.value); this.renderPrompt(); }; const lb = document.createElement('label'); lb.style = 'display:flex;align-items:center;gap:.5em;'; lb.append(cb, ' ', o); list.append(lb); }); this.toolbar.append(list); } else if (cmd === 'B') { // Radio ops.forEach(o => { const rb = document.createElement('input'); rb.type = 'radio'; rb.name = lab; rb.value = o; rb.checked = this.state[lab] === o; rb.onchange = () => { this.state[lab] = o; this.renderPrompt(); }; const lb = document.createElement('label'); lb.style = 'display:flex;align-items:center;gap:.5em;'; lb.append(rb, ' ', o); this.toolbar.append(lb); }); } else if (cmd === 'D') { // Dropdown (or numeric) const sel = document.createElement('select'); if (ops.length) { ops.forEach(o=>{ const opt = document.createElement('option'); opt.value = o; opt.textContent = o; if (this.state[lab] === o) opt.selected = true; sel.append(opt); }); } else { // Numeric range const max = parseInt(def||'0',10) || 5; for(let i=1;i<=max;i++){ const opt = document.createElement('option'); opt.value = String(i); opt.textContent = String(i); if (this.state[lab] === String(i)) opt.selected = true; sel.append(opt); } } sel.onchange = ()=>{this.state[lab] = sel.value; this.renderPrompt();}; this.toolbar.append(sel); } else if (cmd === 'I') { // Iframe (site picker) if (ops.length) { const select = document.createElement('select'); ops.forEach(u => { const op = document.createElement('option'); op.value = u; op.textContent = u; if (this.state[lab] === u) op.selected = true; select.append(op); }); select.onchange = ()=>{iframe.src = select.value; iframe.hidden=false;}; this.toolbar.append(select); const iframe = document.createElement('iframe'); iframe.hidden = true; iframe.style.width='100%'; iframe.style.height='150px'; this.toolbar.append(iframe); const btn = document.createElement('button'); btn.textContent = 'Use site'; btn.onclick = e=>{e.preventDefault(); this.state[lab]=select.value; this.renderPrompt();}; this.toolbar.append(btn); } } else { // Free text const field = cmd === 'A' ? (()=>{const t=document.createElement('textarea'); t.rows=4; return t;})() : (()=>{const i=document.createElement('input');i.type='text';return i;})(); field.value = this.state[lab] || ''; field.oninput = ()=>{this.state[lab] = field.value; this.renderPrompt();}; this.toolbar.append(field); } // Hide toolbar when clicking outside const hide = (ev)=>{ if (!this.toolbar.contains(ev.target) && ev.target !== tokenElem) { this.toolbar.style.display = 'none'; document.removeEventListener('mousedown', hide); } }; setTimeout(()=>document.addEventListener('mousedown', hide), 0); } }; // --- Auto-init for all .Promptinator divs on page window.addEventListener('DOMContentLoaded', ()=>{ document.querySelectorAll('.Promptinator').forEach(div=>{ new window.Promptinator(div, {editable:true}); }); }); })();
Save changes
Create folder
writable 0777
Create
Cancel