Site Builder
Editing:
tools-writing-press-release.php
writable 0666
<?php /********************************************************************** * Promptinator – Press‑Release Studio v2.0 (toolbar edition, Aug‑2025) **********************************************************************/ require_once $_SERVER['DOCUMENT_ROOT'].'/openai/init.php'; ai_handle_key_post(); // save / delete key first /* ---------- AJAX end‑point ------------------------------------ */ if ($_SERVER['REQUEST_METHOD']==='POST' && ($_GET['ajax']??'')==='1') { header('Content-Type: application/json; charset=utf-8'); if (!ai_has_key()) { echo json_encode(['error'=>'No API key']); exit; } $prompt = trim($_POST['prompt']??''); if(!$prompt){ echo json_encode(['error'=>'Prompt missing']); exit; } $html = ai_chat($prompt,['max_tokens'=>1200]); // length handled by preset echo json_encode(['html'=>$html]); exit; } ?> <!doctype html> <html lang="en"><head> <meta charset="utf-8"> <title>Press‑Release Studio • Promptinator</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> :root{--bg:#f1f4fb;--card:#fff;--brand:#004cff;--brand-d:#0d5bfd;--radius:26px; --shadow:0 6px 30px rgba(0,0,0,.08);--red:#e24d4b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif} *,*::before,*::after{box-sizing:border-box} body{margin:0;min-height:100vh;display:flex;flex-direction:column;background:var(--bg)} .breadcrumb{background:#eee;padding:.6rem 1rem;font-weight:600} .breadcrumb a{color:var(--brand);text-decoration:none} main{max-width:1000px;width:100%;margin:2.4rem auto;padding:0 1rem;flex:1} .tool{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);padding:2rem;display:grid;gap:1.6rem} label{font-weight:600;font-size:.95rem;display:block;margin-bottom:.25rem} input,select,textarea{width:100%;padding:.75rem 1rem;border:1px solid #d0d6e7;border-radius:10px;font-size:1rem} textarea{min-height:100px;resize:vertical;font-family:ui-monospace,monospace} input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand)} .btn{background:var(--brand);color:#fff;border:none;border-radius:10px;padding:.9rem 1.7rem;font-size:1rem;font-weight:600;cursor:pointer} .btn:hover{background:var(--brand-d)} small{font-size:.8rem;color:#555} .grid{display:grid;gap:1.3rem} @media(min-width:760px){.grid.two{grid-template-columns:1fr 1fr}.grid.three{grid-template-columns:repeat(3,1fr)}} #spin{display:none;gap:.5ch;align-items:center;margin-top:.9rem} #spin.show{display:flex} #spin svg{width:20px;height:20px;animation:rot 1s linear infinite}@keyframes rot{to{transform:rotate(360deg)}} .preview{border:1px solid #d0d4e0;padding:1rem;border-radius:12px;background:#fafbff;max-height:60vh;overflow:auto} .micBtn,.delBtn{position:absolute;top:50%;transform:translateY(-50%);border:none;background:none;font-size:1.35rem;cursor:pointer} .seed-wrap .micBtn{right:14px} .points-row{position:relative;width:100%} .points-row input{padding-right:5.3rem} .points-row .micBtn{right:48px} .points-row .delBtn{right:12px;color:var(--red)} .tokenBox{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.3rem} .token{background:#e8efff;color:#003c9e;padding:.35rem .9rem;border-radius:18px;font-weight:600;position:relative} .token .x{position:absolute;top:-6px;right:-6px;width:16px;height:16px;line-height:16px;border-radius:50%;background:var(--red);color:#fff;font-size:12px;text-align:center;cursor:pointer} </style> </head><body> <!-- breadcrumb consistent with other tools --> <nav class="breadcrumb"> <a href="/members/dashboard.php">Dashboard</a> » <a href="/ai-tools/tools.php">AI Toolbox</a> » Press‑Release Studio <a href="/<?= htmlspecialchars($_SESSION['slug']??'') ?>/" style="float:right">View Site</a> </nav> <?php ai_render_key_bar(); ?> <?php require_once $_SERVER['DOCUMENT_ROOT'].'/ai-tools/share.php'; ?> <main> <?php if(!ai_has_key()): ?> <div class="tool" style="text-align:center"> <h2>Connect your OpenAI key</h2> <p>Save the key in the black bar above to unlock the studio.</p> </div> <?php else: ?> <div class="tool"> <h2>Create a polished press release</h2> <form id="gen" autocomplete="off"> <input type="hidden" name="prompt" id="promptField"><!-- toolbar injects model --> <!-- core news hook --> <label>News hook / announcement <div class="seed-wrap" style="position:relative"> <textarea id="news" rows="3" name="news" placeholder="e.g. We’re launching the world’s lightest e‑bike…" required></textarea> <button type="button" id="newsMic" class="micBtn">🎤</button> </div> </label> <div class="grid two"> <label>Company name<input id="company" required></label> <label>Tagline <small>(optional)</small><input id="slogan"></label> </div> <div class="grid two"> <label>Reference URL <small>(optional)</small><input id="url" type="url" placeholder="https://…"></label> <label>Dateline date<input id="date" type="date" value="<?=date('Y-m-d')?>"></label> </div> <label>Boilerplate (About us) <textarea id="boiler" rows="4" required></textarea> </label> <!-- length / tone preset --> <label>Length & tone preset <select id="preset"> <option data-len="500" data-tone="Professional, objective">Standard · 500 w</option> <option data-len="400" data-tone="Excited, engaging">Enthusiastic Launch · 400 w</option> <option data-len="300" data-tone="Concise, data‑driven">Investor Focus · 300 w</option> <option data-len="450" data-tone="Storytelling, warm">Human‑interest · 450 w</option> <option data-len="800" data-tone="In‑depth, authoritative">Extended · 800 w</option> </select> </label> <!-- key points --> <label>Key points to highlight (optional)</label> <div id="pointsWrap" style="display:flex;flex-direction:column;gap:.8rem"></div> <button type="button" id="addPoint" class="btn" style="width:140px">Add Point</button> <!-- extra elements --> <label>Optional extra elements</label> <select id="elemSelect"> <option disabled selected value>— select element —</option> <option>CEO quote</option><option>Partner quote</option><option>Statistics figure</option> <option>Call‑to‑action</option><option>Image caption mention</option> </select> <div id="elemBox" class="tokenBox"></div> <!-- media contact --> <h3 style="margin-top:1.4rem">Media contact</h3> <div class="grid three"> <label>Name<input id="cName"></label> <label>Title<input id="cTitle"></label> <label>Email<input id="cEmail" type="email"></label> <label>Phone<input id="cPhone" type="tel"></label> </div> <label><input type="checkbox" id="hero"> Suggest hero‑image ideas</label> <!-- prompt preview --> <label>Prompt preview <textarea id="promptBox" readonly></textarea> </label> <button type="button" id="copyPrompt" class="btn copyBtn" hidden>Copy Prompt</button> <button id="generate" class="btn">Generate Press Release</button> <div id="spin"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/></svg><span id="secs">0</span> s</div> </form> <section id="out" style="display:none"> <h3>HTML source</h3> <textarea id="raw" readonly style="min-height:160px"></textarea> <button class="btn copyBtn" id="copyRaw">Copy HTML</button> <h3 style="margin-top:1.4rem">Rendered preview</h3> <div id="rend" class="preview"></div> <button class="btn copyBtn" id="copyRend">Copy Rendered</button> </section> </div> <?php endif; ?> </main> <script> (()=>{ const $=id=>document.getElementById(id); /* ---------- mic for news hook -------------------------------- */ if($('newsMic') && (window.SpeechRecognition||window.webkitSpeechRecognition)){ const SR=new (window.SpeechRecognition||window.webkitSpeechRecognition)(); SR.lang='en-US'; $('newsMic').onclick=()=>{try{SR.start();}catch{}}; SR.onresult=e=>{$('news').value=e.results[0][0].transcript;buildPrompt();}; }else $('newsMic').style.display='none'; /* ---------- key‑points rows ---------------------------------- */ function addPoint(val=''){ const row=document.createElement('div');row.className='points-row'; const inp=document.createElement('input');inp.placeholder='Key point…';inp.value=val; const mic=document.createElement('button');mic.type='button';mic.className='micBtn';mic.innerHTML='🎤'; const del=document.createElement('button');del.type='button';del.className='delBtn';del.innerHTML='❌'; mic.onclick=()=>startRec(inp); del.onclick=()=>{row.remove();buildPrompt();}; row.append(inp,mic,del); $('pointsWrap').appendChild(row); } $('addPoint').onclick=()=>addPoint(); /* speech helper for points */ function startRec(target){ const SR=window.SpeechRecognition||window.webkitSpeechRecognition; if(!SR){alert('Speech API unsupported');return;} const r=new SR();r.lang='en-US';r.onresult=e=>{target.value=e.results[0][0].transcript;buildPrompt();}; try{r.start();}catch{} } /* ---------- extra elements tokens ---------------------------- */ $('elemSelect').onchange=()=>{ const val=$('elemSelect').value;if(!val)return; if([...$('elemBox').children].some(t=>t.dataset.v===val)){$('elemSelect').selectedIndex=0;return;} const s=document.createElement('span');s.className='token';s.dataset.v=val;s.textContent=val; s.insertAdjacentHTML('beforeend','<span class="x">×</span>'); s.querySelector('.x').onclick=()=>{s.remove();buildPrompt();}; $('elemBox').appendChild(s);$('elemSelect').selectedIndex=0;buildPrompt(); }; /* ---------- build prompt preview ----------------------------- */ const watchIds=['news','company','slogan','url','date','boiler','preset','hero', 'cName','cTitle','cEmail','cPhone']; watchIds.forEach(id=>$(id).addEventListener('input',buildPrompt)); document.addEventListener('input',e=>{if(e.target.placeholder==='Key point…')buildPrompt();}); buildPrompt(); function buildPrompt(){ if(!$('news').value.trim() || !$('company').value.trim() || !$('boiler').value.trim()){ $('promptBox').value='';$('promptField').value='';$('copyPrompt').hidden=true;return; } const news=$('news').value.trim(), comp=$('company').value.trim(), slog=$('slogan').value.trim(), url=$('url').value.trim(), date=$('date').value, boiler=$('boiler').value.trim(), preset=$('preset').selectedOptions[0], len=preset.dataset.len, tone=preset.dataset.tone, pts=[...document.querySelectorAll('#pointsWrap input')].map(i=>i.value.trim()).filter(Boolean), elems=[...$('elemBox').children].map(t=>t.dataset.v), hero=$('hero').checked, cN=$('cName').value.trim(), cT=$('cTitle').value.trim(), cE=$('cEmail').value.trim(),cP=$('cPhone').value.trim(); let p=`Write a press release (~${len} words, tone: ${tone}).\n` +`MUST INCLUDE: headline, dateline (${date}), body`; if(elems.length) p+=`, elements: ${elems.join(', ')}`; if(pts.length) p+=`, weave these key points: ${pts.join('; ')}`; p+=`, boilerplate, media contact.\n` +`NEWS HOOK: ${news}\nCOMPANY: ${comp}${slog?' – '+slog:''}\n`; if(url) p+=`REFERENCE URL: ${url}\n`; p+=`BOILERPLATE:\n${boiler}\n`; if(hero) p+='Finish with a list of three royalty‑free hero‑image ideas.\n'; if(cN||cE||cP) p+=`MEDIA CONTACT (use provided details where possible): ${cN} ${cT?` – ${cT}`:''} ${cE} ${cP}`; $('promptBox').value=p; $('promptField').value=p; $('copyPrompt').hidden=false; } $('copyPrompt').onclick=()=>navigator.clipboard.writeText($('promptBox').value).then(()=>flash($('copyPrompt'))); /* ---------- AJAX submit ------------------------------------- */ $('gen').onsubmit=e=>{ e.preventDefault(); buildPrompt(); const btn=$('generate'),spin=$('spin'),secs=$('secs'); if(!$('promptField').value){alert('Please complete required fields');return;} btn.disabled=true;spin.classList.add('show');let s=0;secs.textContent='0'; const t=setInterval(()=>secs.textContent=++s,1000); fetch('?ajax=1',{method:'POST',body:new FormData($('gen'))}) .then(r=>r.json()).then(j=>{ clearInterval(t);spin.classList.remove('show');btn.disabled=false; if(j.error){alert(j.error);return;} $('raw').value=j.html; $('rend').innerHTML=j.html; $('out').style.display=''; $('copyRaw').onclick=()=>navigator.clipboard.writeText($('raw').value).then(()=>flash($('copyRaw'))); $('copyRend').onclick=()=>navigator.clipboard.writeText($('rend').innerHTML).then(()=>flash($('copyRend'))); $('rend').scrollIntoView({behavior:'smooth'}); }) .catch(err=>{clearInterval(t);spin.classList.remove('show');btn.disabled=false;alert(err);}); }; /* ---------- copy helper ------------------------------------- */ function flash(btn){const t=btn.textContent;btn.textContent='✔ Copied';setTimeout(()=>btn.textContent=t,1200);} })(); </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel