Siteβ―Builder
Editing:
dynamicPromptinator.js
writable 0666
/* DynamicΒ Promptinator β localβAI edition βΒ v2025β08β24Β r4 * ------------------------------------------------------------ * + Original SOCIAL Promptinator features * + Purple .aiβbtn styling * + β‘Β LocalΒ AI button (only if OpenAI key present) * + Posts: prompt, model, temperature, max_tokens * + Result panel below card, copyβHTML button * + NEW: Markdown rendering + chat bubbles * ------------------------------------------------------------ */ export default class Promptinator { /* ============ helpers / flags ================================ */ static _lastMeta = []; // for Eβbuttons static _lastState = []; static _hasE = false; static _btnCSS = false; // inject CSS once static _escAttr = s => String(s) .replace(/&/g,'&') .replace(/"/g,'"') .replace(/</g,'<') .replace(/>/g,'>'); /* --- veryβlight Markdown β HTML (headings, bold, italic, lists) */ static _md2html(md){ return md .replace(/^###### (.*)$/gim,'<h6>$1</h6>') .replace(/^##### (.*)$/gim,'<h5>$1</h5>') .replace(/^#### (.*)$/gim ,'<h4>$1</h4>') .replace(/^### (.*)$/gim ,'<h3>$1</h3>') .replace(/^## (.*)$/gim ,'<h2>$1</h2>') .replace(/^# (.*)$/gim ,'<h1>$1</h1>') .replace(/\*\*(.+?)\*\*/gim,'<strong>$1</strong>') .replace(/\*(.+?)\*/gim ,'<em>$1</em>') .replace(/^\> (.*)$/gim,'<blockquote>$1</blockquote>') .replace(/^\s*[-*] (.*)$/gim,'<li>$1</li>') .replace(/(<li>.*<\/li>)/gim,s=>'<ul>'+s+'</ul>') .replace(/^\s*\d+\.\s(.*)$/gim,'<li>$1</li>') .replace(/(<ul>.*<\/ul>)/gim,s=>s) // leave ULs intact .replace(/\n{2,}/g,'<br><br>'); } /* ============ public initialiser ============================ */ static init({ mount, defaultPrompt }) { /* ---------- 0Β Β· parse template ----------------------------- */ const prompt = mount.dataset.prompt || defaultPrompt || mount.textContent.trim(); let template = prompt; const RX=/\[(?:([A-DEIH]?)-)?([^~|\-\]]*)(?:-([^~\]]*))?(?:~([^~]+)~)?\]/ig; const state={}, meta=[]; const allowEdit =(mount.dataset.edit ?? mount.getAttribute('edit') ?? '1')!=='0'; const allowThink =(mount.dataset.think ?? (mount.hasAttribute('think')?mount.getAttribute('think'):'1'))!=='0'; /* ---------- 1Β Β· profile helpers ---------------------------- */ let prof = window.profileData || null, hasProf= !!prof; const nonEmpty = v => v!==null && v!==undefined && String(v).trim()!==''; const pick = (path,f='')=>path.split('.').reduce((o,k)=>o&&o[k],prof) ?? f; function applyProfile(st,mt){ if(!hasProf) return; mt.forEach((m,i)=>{ const lab=(m.lab||'').toLowerCase().trim(); const set=v=>{ if(nonEmpty(v)) st[i]=v; }; /* core fields -------------------------------------------- */ switch(lab){ case 'handle':case '@handle': set(prof.handle); return; case 'slug': set(prof.slug); return; case 'name':case 'full_name': set(prof.name||prof.display_name); return; case 'display_name': set(prof.display_name); return; case 'slogan': set(prof.slogan); return; case 'description': set(prof.description); return; case 'address':case 'street_address': set(prof.address); return; case 'city': set(prof.city); return; case 'state': set(prof.state); return; case 'zip':case 'zip_code': set(prof.zip); return; case 'lat': set(prof.lat); return; case 'lon': set(prof.lon); return; case 'phone': set(prof.phone); return; case 'website': set(prof.website); return; case 'email': set(prof.email); return; case 'premium': set(prof.premium?'Yes':'No'); return; } /* channel shortcuts -------------------------------------- */ const ch={youtube:'channels.video.youtube',rumble:'channels.video.rumble', odysee:'channels.video.odysee',spotify:'channels.podcast.spotify', apple:'channels.podcast.apple',rss:'channels.podcast.rss', twitter:'channels.social.twitter',tiktok:'channels.social.tiktok', instagram:'channels.social.instagram',facebook:'channels.social.facebook', linkedin:'channels.social.linkedin',patreon:'channels.social.patreon'}; if(lab in ch){ set(pick(ch[lab])); return; } if(lab in prof){ set(prof[lab]); } // direct match /* Cβtoken defaults --------------------------------------- */ if(m.cmd==='C'){ if(/service/.test(lab)&&Array.isArray(prof.tags)){ m.opts=prof.tags; if(!st[i]?.length&&prof.tags.length) st[i]=[prof.tags[0]]; } if(/location/.test(lab)&&Array.isArray(prof.location_tags)){ m.opts=prof.location_tags; if(!st[i]?.length&&prof.location_tags.length) st[i]=[prof.location_tags[0]]; } } }); } /* wait for late profile injection --------------------------- */ let tries=0; const watch=setInterval(()=>{ if(!hasProf&&window.profileData){ prof=window.profileData; hasProf=true; applyProfile(state,meta); render(); clearInterval(watch); } if(++tries>20) clearInterval(watch); },150); /* ---------- 2Β Β· renderer ----------------------------------- */ function render(){ Promptinator._injectBtnCSS(); // ensure aiβbtn & bubble CSS meta.length=0; let idx=0; template.replace(RX,(_,cmd='',lab='',ops='',def='')=>{ lab=lab.trim(); const opts=ops.split('|').filter(Boolean); if(!(idx in state)){ state[idx]=cmd==='C' ? (def?def.split(',').map(v=>v.trim()):(opts.length?opts.slice(0,1):[])) : (def||opts[0]||lab); } meta.push({cmd:cmd||'A',lab,opts,def}); idx++; return''; }); if(!render._haveProfile){ applyProfile(state,meta); render._haveProfile=true; } idx=0; const html=template.replace(RX,(_,cmd='',lab='')=>{ lab=lab.trim(); const val=state[idx]; let out; if(cmd==='C'){ const list=(Array.isArray(val)?val:[val]).filter(nonEmpty); if(!list.length&&meta[idx].opts.length) list.push(meta[idx].opts[0]); out=list.map(v=>`<span class="token" data-i="${idx}" data-label="${lab}">[${v}]</span>`).join(', '); }else if(cmd==='E'){ out=`<button class="token e-btn" data-i="${idx}" data-label="${lab}">${/^\S+@\S+\.\S+$/.test(val)?'Send':'Submit'}</button>`; }else if(cmd==='H'){ out=`<span class="token h-dot" data-i="${idx}" data-label="${lab}" title="Hidden instruction">β’</span>`; }else{ out=`<span class="token" data-i="${idx}" data-label="${lab}">[${nonEmpty(val)?val:'+'}]</span>`; } idx++; return out; }); const hasE = meta.some(m=>m.cmd==='E'); Promptinator._hasE=hasE; const noPrompt = template.trim()===''; const showAI = allowThink && !hasE && !noPrompt; const helper = noPrompt && allowEdit ? '<div class="promptinator-helper">π‘ Click the pencilΒ (βοΈ)Β toΒ addΒ aΒ prompt.</div>' : ''; /* external buttons ---------------------------------------- */ const ext = ` <a class="ai-btn chatgpt" target="_blank">π§ ChatGPT</a> <a class="ai-btn perplexity"target="_blank">π Perplexity</a> <a class="ai-btn copilot" target="_blank">π€ Copilot</a>`; const hasKey = !!document.getElementById('modelSel'); const localBtn = (showAI && hasKey) ? '<button type="button" class="ai-btn localai">β‘Β LocalΒ AI</button>' : ''; const aiBtns = showAI ? `<div class="ai-buttons" style="margin-top:1em;display:flex;flex-wrap:wrap;gap:.6em;">${ext}${localBtn}</div>` : ''; promptText.innerHTML = (noPrompt?'':html.replace(/\n/g,'<br>')) + helper + aiBtns; editor.classList.toggle('promptinator--empty', noPrompt); if(showAI){ let i=0, full = template.replace(RX,()=>{const v=state[i++]; return Array.isArray(v)?v.join(', '):v;}); promptText.querySelector('.chatgpt').href = 'https://chat.openai.com/?prompt='+encodeURIComponent(full); promptText.querySelector('.perplexity').href= 'https://www.perplexity.ai/search?q='+encodeURIComponent(full); promptText.querySelector('.copilot').href = 'https://copilot.microsoft.com/?q='+encodeURIComponent(full); } /* ------------- LocalΒ AI handler -------------------------- */ const lb = promptText.querySelector('.localai'); if(lb){ lb.onclick = ()=>{ /* build flattened prompt */ let i=0; const full = template.replace(RX,()=>{const v=state[i++]; return Array.isArray(v)?v.join(', '):v;}); /* gather user defaults */ const mdl = localStorage.getItem('aiModel') || (document.getElementById('modelSel')?.value || 'gpt-3.5-turbo'); const defs = JSON.parse(localStorage.getItem('aiDefaults')||'{}'); const temp = defs.temp ?? 0.7; const max = defs.max ?? 800; /* make / reuse panel */ const shell = mount.closest('.pi-shell') || mount; let panel = document.getElementById('localResultBox'); if(!panel){ panel=document.createElement('div'); panel.id='localResultBox'; panel.style.cssText='max-width:clamp(280px,90vw,720px);margin:1.8rem auto;'+ 'padding:1.4rem;background:#fff;border:3.5px solid #ffb63b;'+ 'border-radius:27px;box-shadow:0 8px 26px rgba(0,0,0,.06);'; panel.innerHTML=` <h3 style="margin-top:0">LocalΒ AIΒ Answer</h3> <div class="outZone"></div> <button class="copyHtml" style="margin-top:1rem;background:#004cff;color:#fff;border:none;border-radius:8px; padding:.55rem 1.2rem;font-weight:600;cursor:pointer"> CopyΒ HTML </button> <span class="spinner" style="display:none;margin-left:.7rem;gap:.4em; align-items:center;font-size:.9rem;"> <svg viewBox="0 0 24 24" style="width:18px;height:18px;animation:rot 1s linear infinite"> <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle></svg> <span>0</span>s </span>`; shell.parentNode.insertBefore(panel,shell.nextSibling); } const out = panel.querySelector('.outZone'), copy = panel.querySelector('.copyHtml'), spin = panel.querySelector('.spinner'); /* helper to append a bubble ---------------------------- */ const appendBubble=(parent,text,who)=>{ const div=document.createElement('div'); div.className='chat-bubble '+(who==='human'?'chat-human':'chat-ai'); div.innerHTML = who==='human' ? Promptinator._escAttr(text).replace(/\n/g,'<br>') : Promptinator._md2html(text); parent.append(div); }; /* spinner ---------------------------------------------- */ lb.disabled=true; spin.style.display='inline-flex'; let secs=0; spin.lastElementChild.textContent='0'; const tick=setInterval(()=>spin.lastElementChild.textContent=++secs,1000); /* AJAX -------------------------------------------------- */ fetch(location.pathname+'?ajax=1',{ method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body: 'prompt='+encodeURIComponent(full)+ '&model='+encodeURIComponent(mdl)+ '&temperature='+encodeURIComponent(temp)+ '&max_tokens='+encodeURIComponent(max) }) .then(r=>r.json()) .then(j=>{ clearInterval(tick); spin.style.display='none'; lb.disabled=false; if(j.error){alert(j.error);return;} const res=(j.html||'').trim(); out.innerHTML=''; // start fresh bubbles appendBubble(out,full,'human'); const aiText = res.startsWith('<') ? res : Promptinator._escAttr(res); appendBubble(out,aiText,'ai'); /* ========== create chat box + history =============== */ buildChat(full, aiText, mdl, temp, max, panel, out, spin, lb, appendBubble); panel.scrollIntoView({behavior:'smooth',block:'center'}); }) .catch(err=>{ clearInterval(tick); spin.style.display='none'; lb.disabled=false; alert(err); }); copy.onclick=()=>navigator.clipboard.writeText(out.innerHTML) .then(()=>{ copy.textContent='β Copied'; setTimeout(()=>copy.textContent='CopyΒ HTML',1200); }); }; } }/* render */ /* ========== build chat box ================================== */ function buildChat(firstUser, firstAssistant, mdl, temp, max, panel, out, spin, lb, appendBubble){ if(panel.querySelector('.chatBox')) return; // already built let chatHistory = [ {role:'user', content:firstUser}, {role:'assistant', content:firstAssistant.replace(/<[^>]+>/g,'')} ]; const chat = document.createElement('div'); chat.className='chatBox'; chat.innerHTML=` <textarea placeholder="Reply or ask followβup β¦" style="width:100%;min-height:70px;font-size:1rem;border:1px solid #ccd4e8; border-radius:8px;padding:.5rem;margin-top:1rem;"></textarea> <button class="sendFollow" style="margin-top:.6rem;background:#5c3bff;color:#fff;border:none;border-radius:7px; padding:.45rem 1.1rem;font-weight:600;cursor:pointer">Send</button>`; out.after(chat); const ta = chat.querySelector('textarea'); const send = chat.querySelector('.sendFollow'); ta.addEventListener('keydown',e=>{ if(e.key==='Enter' && e.ctrlKey){ e.preventDefault(); send.click(); } }); send.onclick = ()=>{ const follow = ta.value.trim(); if(!follow) return; ta.value=''; send.disabled=true; lb.disabled=true; spin.style.display='inline-flex'; let secs=0; spin.lastElementChild.textContent='0'; const tick=setInterval(()=>spin.lastElementChild.textContent=++secs,1000); appendBubble(out,follow,'human'); const msgs=[...chatHistory,{role:'user',content:follow}]; fetch(location.pathname+'?ajax=1',{ method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body: 'prompt='+encodeURIComponent(JSON.stringify(msgs))+ '&model='+encodeURIComponent(mdl)+ '&temperature='+encodeURIComponent(temp)+ '&max_tokens='+encodeURIComponent(max) }) .then(r=>r.json()) .then(j=>{ clearInterval(tick); spin.style.display='none'; send.disabled=false; lb.disabled=false; if(j.error){alert(j.error);return;} const raw=(j.html||'').trim(); const display = raw.startsWith('<') ? raw : Promptinator._escAttr(raw); appendBubble(out,display,'ai'); chatHistory.push({role:'assistant', content:display.replace(/<[^>]+>/g,'')}); out.lastChild.scrollIntoView({behavior:'smooth',block:'nearest'}); }) .catch(err=>{ clearInterval(tick); spin.style.display='none'; send.disabled=false; lb.disabled=false; alert(err); }); }; } /* ---- scaffold / editor toggle ------------------------------ */ const editor=document.createElement('div'); editor.className='Promptinator'; editor.innerHTML=` <div style="display:flex;align-items:baseline;gap:.5em;"> ${allowEdit?'<span id="edit-link" style="cursor:pointer">βοΈ</span>':''} <div class="prompt-text" style="flex:1 1 auto;"></div> </div> ${allowEdit?`<form id="edit-form" style="display:none;margin-top:.6em;"> <textarea style="width:100%;min-height:90px;font-family:monospace;"></textarea> <div style="text-align:right;margin-top:.7em;"><button type="submit">SaveΒ &Β Update</button></div> </form>`:''}`; mount.innerHTML=''; mount.append(editor); const promptText = editor.querySelector('.prompt-text'); /* edit pencil ----------------------------------------------- */ if(allowEdit){ const link=editor.querySelector('#edit-link'), form=editor.querySelector('#edit-form'), ta =form?.querySelector('textarea'); link&&link.addEventListener('click',()=>{ const open=getComputedStyle(form).display==='none'; if(open){ form.style.display=''; ta.value=template; ta.focus(); Promptinator.enhanceTextarea?.(ta); }else{ form.style.display='none'; template=ta.value.trim(); Object.keys(state).forEach(k=>delete state[k]); render._haveProfile=false; render(); } }); form&&form.addEventListener('submit',e=>{ e.preventDefault(); template=ta.value.trim(); Object.keys(state).forEach(k=>delete state[k]); render._haveProfile=false; render(); form.style.display='none'; }); } render(); // first paint /* token click β popup ---------------------------------------- */ mount.addEventListener('click',e=>{ if(!e.target.matches('.token'))return; const i=+e.target.dataset.i,info=meta[i],val=state[i]; if(info.cmd==='E'){ Promptinator._lastMeta = meta.slice(); Promptinator._lastState = Object.assign([],state); } Promptinator._popup(info, Array.isArray(val)?val.join(', '):val, v=>{ state[i]=info.cmd==='C' ? v.split(',').map(s=>s.trim()).filter(Boolean) : v; render();}); }); }/* init */ /* ===== inject aiβbtn & bubble CSS once ======================= */ static _injectBtnCSS(){ if(Promptinator._btnCSS) return; const st=document.createElement('style'); st.textContent=` .ai-btn{ display:inline-flex;align-items:center;gap:.3em; background:#5c3bff;color:#fff !important;font-weight:600; border:none;border-radius:7px;padding:.4em 1.2em; text-decoration:none;cursor:pointer;font-size:.92rem} .ai-btn:hover{background:#482ce0} /* chat bubbles */ .chat-bubble{max-width:95%;margin:.4rem 0;padding:.6rem .9rem;border-radius:8px; line-height:1.45;font-size:1.03rem} .chat-human{background:#e5f1ff;color:#10335d} .chat-ai{background:transparent} @keyframes rot{to{transform:rotate(360deg)}} `; document.head.appendChild(st); Promptinator._btnCSS=true; } /* ---------- full popup implementation (unchanged) ------------- */ static _popup(meta,cur,done){ let dlg=document.getElementById('promptinator-dlg'); if(!dlg){ dlg=document.createElement('dialog'); dlg.id='promptinator-dlg'; document.body.append(dlg); dlg.addEventListener('click',e=>{ if(e.target===dlg) dlg.close(); }); dlg.addEventListener('cancel',e=>{ e.preventDefault(); dlg.close(); }); } const pretty=s=>s.replace(/[-_]/g,' ').replace(/\b\w/g,c=>c.toUpperCase()); const nice=meta.lab&&meta.lab.trim() ? pretty(meta.lab) : (cur || {A:'Text Input',B:'Choice',C:'Options',D:'Dropdown',E:'Send',I:'URL Picker',H:'Hidden note'}[meta.cmd] || 'Input'); const showPH = Promptinator._hasE && cur === meta.def; const phAttr = showPH ? ` placeholder="${Promptinator._escAttr(cur)}"` : ''; const valAttr= showPH ? '' : Promptinator._escAttr(cur); let inner=''; switch(meta.cmd){ case 'C':{ let opts=meta.opts; const labLow=(meta.lab||'').toLowerCase(); if(window.profileData){ if(labLow.startsWith('service')&&Array.isArray(window.profileData.tags)) opts=window.profileData.tags; if(labLow.startsWith('location')&&Array.isArray(window.profileData.location_tags)) opts=window.profileData.location_tags; } inner=`<fieldset><legend>${nice}</legend>`+ opts.map(o=>`<label><input type="checkbox" value="${o}" ${cur.split(', ').includes(o)?'checked':''}> ${o}</label>`).join('')+ `</fieldset> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; break; } case 'A': case 'H': inner=`<label>${nice}<textarea style="width:100%;font-size:1rem;" rows="4"${phAttr}>${valAttr}</textarea></label> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; break; case 'B': inner=`<fieldset><legend>${nice}</legend>`+ meta.opts.map(o=>`<label><input type="radio" name="r" value="${o}" ${o===cur?'checked':''}> ${o}</label>`).join('')+ `</fieldset>`; break; case 'D':{ let opts=meta.opts; if(opts.length===1&&/^\d+$/.test(opts[0])) opts=Array.from({length:+opts[0]},(_,i)=>String(i+1)); inner=`<label>${nice}<select>`+ opts.map(o=>`<option${o===cur?' selected':''}>${o}</option>`).join('')+ `</select></label>`; break; } case 'I': inner=`<label>${nice}<select>`+ (showPH?`<option disabled selected>${Promptinator._escAttr(cur)}</option>`:'')+ meta.opts.map(u=>`<option${u===cur?' selected':''}>${u}</option>`).join('')+ `</select></label> <iframe hidden style="width:100%;height:300px;border:1px solid #ccc;margin-top:.6em;"></iframe> <div style="text-align:right;margin-top:.7em;"><button type="button">Use site</button></div>`; break; case 'E':{ const isEmail=/^\S+@\S+\.\S+$/.test(cur); inner=`<div style="padding:1em 0;text-align:center;font-size:1.1rem;font-weight:600;">${nice}</div> <div style="text-align:right;margin-top:.7em;"><button type="button">${isEmail?'Send':'Submit'}</button></div>`; break; } default: inner=`<label>${nice}<input type="text" style="width:100%;font-size:1rem;"${phAttr} value="${valAttr}"></label> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; } dlg.innerHTML=`<div class="popup-body">${inner}</div>`; dlg.showModal(); const pb=dlg.querySelector('.popup-body'); if(meta.cmd==='C'){ pb.querySelector('button').onclick=()=>{ const vals=[...pb.querySelectorAll('input:checked')].map(i=>i.value); done(vals.join(', ')||cur); dlg.close(); }; return; } if(meta.cmd==='E'){ const btn=pb.querySelector('button'); const isEmail=/^\S+@\S+\.\S+$/.test(cur); if(isEmail){ const body=Promptinator._lastMeta.map((m,i)=>`${pretty(m.lab)}: ${Promptinator._lastState[i]}`).join('\n'); btn.onclick=()=>{ window.location.href=`mailto:${cur}?subject=${encodeURIComponent(nice)}&body=${encodeURIComponent(body)}`; dlg.close(); }; }else{ const form=document.createElement('form'); form.method='post'; form.action=cur; const t=document.createElement('input'); t.type='hidden'; t.name='_formTitle'; t.value=nice; form.append(t); Promptinator._lastMeta.forEach((m,i)=>{ const inp=document.createElement('input'); inp.type='hidden'; inp.name=pretty(m.lab).replace(/\s+/g,'-').toLowerCase(); inp.value=Promptinator._lastState[i]; form.append(inp); }); pb.append(form); btn.onclick=()=>{ form.submit(); dlg.close(); }; } return; } if(meta.cmd==='A'||meta.cmd==='H'){ const ta=pb.querySelector('textarea'); ta.focus(); pb.querySelector('button').onclick=()=>{ done(ta.value); dlg.close(); }; return; } if(meta.cmd==='B'){ pb.onclick=e=>{ if(e.target.name==='r'){ done(e.target.value); dlg.close(); } }; return; } if(meta.cmd==='D'){ pb.querySelector('select').onchange=e=>{ done(e.target.value); dlg.close(); }; return; } if(meta.cmd==='I'){ const sel=pb.querySelector('select'), fr=pb.querySelector('iframe'); sel.onchange=()=>{ fr.hidden=false; fr.src=sel.value; }; pb.querySelector('button').onclick=()=>{ done(sel.value||cur); dlg.close(); }; return; } const inp=pb.querySelector('input'); inp.focus(); pb.querySelector('button').onclick=()=>{ done(inp.value); dlg.close(); }; inp.onkeydown=e=>{ if(e.key==='Enter'){ done(inp.value); dlg.close(); } }; } /* popup */ } /* === end of class === */ /* =================================================================== EMBEDDED PENCIL TOOLBAR =================================================================== */ const HELP_URL='https://promptinator.ai/shortcode-help.php'; const SNIPPETS={name:'[name]',slogan:'[slogan]',description:'[description]',address:'[address]',city:'[city]', state:'[state]',zip:'[zip]',phone:'[phone]',website:'[website]',email:'[email]', c_service:'[C-service-]',c_location:'[C-location-]', h_hidden:'[H-hidden-~Internal GPT instructions here~]', plain_input:'[-labelfield-~Input~]',textarea_a:'[A-labelfield-~Textarea~]',radio_b:'[B-labelfield-|Choice 1|Choice 2|~Choice 2~]', checkbox_c:'[C-labelfield-|Opt A|Opt B|~Opt A~]',dropdown_num_d:'[D-labelfield-10~3~]', dropdown_txt_d:'[D-labelfield-|Opt A|Opt B|Opt C|~Opt B~]', url_picker_i:'[I-labelfield-|https://site1.com|site2.com|~Pick a site~]', email_submit_e:'[E-labelfield-~you@site.com~]',form_post_e:'[E-labelfield-~https://example.com/endpoint~]'}; const MENU=[['name','Name'],['slogan','Slogan'],['description','Description'],['address','Street address'], ['city','City'],['state','State'],['zip','ZIP code'],['phone','Phone number'],['website','Website URL'], ['email','Email address'],['c_service','Services (C)'],['c_location','Locations (C)'], ['h_hidden','Hidden instructions (H)'], ['plain_input','Plain input'],['textarea_a','Textarea (A)'],['radio_b','Radio buttons (B)'], ['checkbox_c','Checkbox group (C)'],['dropdown_num_d','Dropdown numeric (D)'],['dropdown_txt_d','Dropdown text (D)'], ['url_picker_i','URL pickerΒ +Β preview (I)'],['email_submit_e','Email submit (E)'],['form_post_e','FormβPOST submit (E)']]; function enhanceTextarea(ta){ if(!ta||ta.dataset.scMenu)return; ta.dataset.scMenu='yes'; const m=ta.value.match(/-label(\d+)(?=[-~|])/g); ta.dataset.labelCount = m ? String(Math.max(...m.map(s=>+s.replace(/\D/g,'')))+1) : '0'; const sel=document.createElement('select'); sel.className='promptinator-select'; sel.innerHTML='<option disabled selected>Insert shortcodeβ¦</option>'+ MENU.map(([k,t])=>`<option value="${k}">${t}</option>`).join(''); sel.onchange=()=>{ const raw=SNIPPETS[sel.value]; if(raw){ const n=+ta.dataset.labelCount; const snip=raw.replace(/-labelfield(?=[-~|])/i,`-label${n}`); ta.dataset.labelCount=String(n+1); insertAtCaret(ta,snip); } sel.selectedIndex=0; }; const help=document.createElement('a'); help.className='promptinator-help'; help.textContent='Help'; help.href=HELP_URL; help.title='Open shortcode helper'; help.onclick=e=>{ e.preventDefault(); window.open(HELP_URL,'promptinatorHelp','popup=yes,width=520,height=720,scrollbars=yes,resizable=yes'); }; const bar=document.createElement('div'); bar.className='promptinator-bar'; bar.append(sel,help); ta.parentNode.insertBefore(bar,ta); } function insertAtCaret(el,snippet){ el.focus(); const{selectionStart:s,selectionEnd:e,value:t}=el; el.value=t.slice(0,s)+snippet+t.slice(e); const p=s+snippet.length; el.selectionStart=el.selectionEnd=p; el.dispatchEvent(new Event('input',{bubbles:true})); } /* oneβtime CSS injection */ (function(){ const css=` .promptinator-bar{display:flex;align-items:center;gap:.55em;margin-bottom:.35em;} .promptinator-select{flex:1 1 auto;padding:.3em .55em;} .promptinator-help{flex:0 0 auto;font-size:.9rem;color:#176edc;text-decoration:underline;cursor:pointer;} .promptinator-bar+textarea{width:100%;box-sizing:border-box;} .Promptinator.promptinator--empty .ai-buttons{display:none!important;} .promptinator-helper{margin-top:1em;font-size:.9em;color:#555;background:#fff9c4;text-align:center;padding:.5em .8em;border-radius:5px;} .token.h-dot{cursor:pointer;font-weight:bold;font-size:1.2em;} @keyframes rot{to{transform:rotate(360deg);}} `; const st=document.createElement('style'); st.appendChild(document.createTextNode(css)); document.head.appendChild(st); })(); document.addEventListener('click',e=>{ const pencil=e.target.closest('#edit-link'); if(!pencil)return; const form=(pencil.closest('.Promptinator')||document.body).querySelector('#edit-form'); if(!form)return; requestAnimationFrame(()=>{ if(getComputedStyle(form).display!=='none'){ const ta=form.querySelector('textarea'); if(ta)enhanceTextarea(ta); } }); }); Promptinator.enhanceTextarea = enhanceTextarea; export { enhanceTextarea };
Save changes
Create folder
writable 0777
Create
Cancel