Site Builder
Editing:
tools-writing-blogs.php
writable 0666
<?php /********************************************************************** * Promptinator – Blog‑Wizard v2.1 * ------------------------------------------------------------------- * • Login toolbar (ai_render_key_bar) * • Key‑points (rows with mic + ❌) * • FAQ selector * • NEW: Enhancement chips (CTA, bullets, tables, …) **********************************************************************/ require_once $_SERVER['DOCUMENT_ROOT'].'/openai/init.php'; ai_handle_key_post(); /* ---------- 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'=>1800]); echo json_encode(['html'=>$html]); exit; } /* ---------- look‑up lists ------------------------------------- */ $langs = ['English','Spanish','German','French','Italian','Portuguese','Dutch']; $styles = ['Creative','Informative','Narrative','Persuasive','Analytical','Journalistic']; $tones = ['Neutral','Cheerful','Humorous','Assertive','Inspirational','Professional','Emotional']; $personae=['Tech journalist','Seasoned sailor','Health coach','Finance analyst', 'Travel blogger','Food critic','Educator','Marketing strategist']; $levels = ['Grade 6','Grade 8','Grade 10','Grade 12','Expert']; /* ---------- enhancement presets (single source of truth) ------ */ $enhanceOpts = [ 'CTA' => 'End with a persuasive Call‑to‑Action.', 'Bullets' => 'Use <ul><li> bullet lists where appropriate.', 'Use tables' => 'Include a clear <table> to organise data.', 'No tables' => 'Do NOT use tables anywhere.', 'Text icons' => 'Sprinkle Unicode icons (✔︎ ✖︎ ►) to draw attention.', 'Pros/Cons list' => 'Add a Pros & Cons section.', 'Summary checklist'=> 'Finish with a checklist summary.', 'Emoji headings' => 'Add fitting emojis to <h2> headings.' ]; ?> <!doctype html> <html lang="en"><head> <meta charset="utf-8"> <title>Blog Wizard • Promptinator</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> :root{--bg:#f1f4fb;--card:#fff;--brand:#004cff;--brand-d:#0d5bfd;--shadow:0 6px 30px rgba(0,0,0,.08); --radius:26px;--red:#e24d4b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif} 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.3rem auto;padding:0 1rem;flex:1} .tool{background:var(--card);box-shadow:var(--shadow);border-radius:var(--radius);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:90px;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} .row{display:grid;gap:1.2rem} @media(min-width:760px){.row{grid-template-columns:1fr 1fr}} @media(min-width:1020px){.row.wide{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:.4rem} .token{background:#e6edff;color:#0d349d;padding:.4rem 1rem;border-radius:18px;font-weight:600;position:relative;cursor:default} .token .x{position:absolute;right:4px;top:0;color:var(--red);font-weight:700;cursor:pointer} /* tiny drop‑down look‑alike */ #enhSel{padding:.6rem 1rem} </style> </head><body> <nav class="breadcrumb"> <a href="/members/dashboard.php">Dashboard</a> » <a href="/ai-tools/tools.php">AI Toolbox</a> » Blog Wizard <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 wizard.</p> </div> <?php else: ?> <div class="tool"> <h2>Create a high‑impact blog post</h2> <form id="gen" autocomplete="off"> <input type="hidden" name="model" value=""> <input type="hidden" name="prompt" id="promptField"> <!-- topic --> <label>Topic <div class="seed-wrap" style="position:relative"> <textarea id="topic" name="topic" rows="3" placeholder="e.g. Sustainable boating" required></textarea> <button type="button" class="micBtn" id="topicMic">🎤</button> </div> </label> <!-- persona / audience --> <div class="row"> <label>Persona <select id="persona" name="persona"><?php foreach($personae as $p) echo "<option>$p</option>"; ?></select> </label> <label>Target audience <small>(optional)</small> <input id="aud" name="aud" placeholder="e.g. first‑time boat buyers"> </label> </div> <!-- reading & structure --> <div class="row wide"> <label>Reading level <select id="level" name="level"><?php foreach($levels as $l) echo "<option>$l</option>"; ?></select> </label> <label># Sections <select id="sections" name="sections"><?php for($i=2;$i<=8;$i++) echo "<option>$i</option>"; ?></select> </label> <label># Paragraphs / section <select id="paras" name="paras"><?php for($i=1;$i<=6;$i++) echo "<option>$i</option>"; ?></select> </label> </div> <!-- language / style / tone --> <div class="row wide"> <label>Language <select id="lang" name="lang"><?php foreach($langs as $l) echo "<option>$l</option>"; ?></select></label> <label>Style <select id="style" name="style"><?php foreach($styles as $s) echo "<option>$s</option>"; ?></select></label> <label>Tone <select id="tone" name="tone"><?php foreach($tones as $t) echo "<option>$t</option>"; ?></select></label> </div> <!-- keywords / URL --> <div class="row"> <label>Keywords <input id="keywords" name="keywords" placeholder="solar boating, eco engines"> </label> <label>Link URL <input id="linkurl" name="linkurl" type="url" placeholder="https://example.com"> </label> </div> <!-- key points --> <label>Important points (sections / arguments)</label> <div id="pointsWrap" style="display:flex;flex-direction:column;gap:.8rem"></div> <button type="button" id="addPoint" class="btn" style="width:150px">Add Point</button> <!-- FAQ & Enhancements --> <div class="row"> <label># FAQ questions <select id="faq" name="faq"><?php for($i=0;$i<=10;$i++) echo "<option>$i</option>"; ?></select> </label> <label>Enhancements <select id="enhSel"><option disabled selected value>— select —</option><?php foreach(array_keys($enhanceOpts) as $e) echo "<option>$e</option>"; ?></select> <div id="enhBox" class="tokenBox"></div> </label> </div> <!-- prompt preview --> <label>Prompt preview <textarea id="promptBox" readonly></textarea> </label> <button type="button" id="copyPrompt" class="btn copyBtn" hidden>Copy Prompt</button> <!-- action --> <button id="generate" class="btn">Generate Blog Post</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.3rem">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); /* ---------- microphone -------------------------------------- */ if ($('topicMic') && (window.SpeechRecognition||window.webkitSpeechRecognition)){ const SR=new (window.SpeechRecognition||window.webkitSpeechRecognition)(); SR.lang='en-US'; $('topicMic').onclick=()=>{try{SR.start();}catch{}}; SR.onresult=e=>{$('topic').value=e.results[0][0].transcript;buildPrompt();}; }else $('topicMic').style.display='none'; /* ---------- key points rows --------------------------------- */ function addPoint(val=''){ const row=document.createElement('div'); row.className='points-row'; const inp=document.createElement('input'); inp.type='text';inp.name='points[]';inp.placeholder='Main point…';inp.value=val; const mic=document.createElement('button'); mic.className='micBtn'; mic.type='button'; mic.innerHTML='🎤'; const del=document.createElement('button'); del.className='delBtn'; del.type='button'; del.innerHTML='❌'; mic.onclick=()=>startRec(inp); del.onclick=()=>{row.remove();buildPrompt();}; row.append(inp,mic,del); $('pointsWrap').appendChild(row); } $('addPoint').onclick=()=>addPoint(''); addPoint(); function startRec(target){ const R=window.SpeechRecognition||window.webkitSpeechRecognition; if(!R){alert('Speech API unsupported');return;} const rec=new R(); rec.lang='en-US'; rec.onresult=e=>{target.value=e.results[0][0].transcript;buildPrompt();}; try{rec.start();}catch{} } /* ---------- enhancement chips ------------------------------- */ $('enhSel').onchange=()=>{ const val=$('enhSel').value; if(!val) return; if([...$('enhBox').children].some(c=>c.dataset.v===val)){ $('enhSel').selectedIndex=0; return;} const chip=document.createElement('span'); chip.className='token'; chip.dataset.v=val; chip.textContent=val; chip.insertAdjacentHTML('beforeend','<span class="x">×</span>'); chip.querySelector('.x').onclick=()=>{chip.remove(); buildPrompt();}; $('enhBox').appendChild(chip); $('enhSel').selectedIndex=0; buildPrompt(); }; /* ---------- build prompt preview ---------------------------- */ const enhanceMap = <?=json_encode($enhanceOpts,JSON_UNESCAPED_SLASHES)?>; // PHP → JS object const watch=['topic','persona','aud','level','sections','paras','lang','style','tone', 'keywords','linkurl','faq']; watch.forEach(id=>$(id)?.addEventListener('input',buildPrompt)); ['faq'].forEach(id=>$(id).addEventListener('change',buildPrompt)); document.addEventListener('input',e=>{ if(e.target.name==='points[]') buildPrompt();}); buildPrompt(); function buildPrompt(){ const topic=$('topic').value.trim(); if(!topic){ $('promptBox').value=''; $('promptField').value=''; $('copyPrompt').hidden=true; return; } const persona=$('persona').value, aud =$('aud').value.trim(), lvl =$('level').value, sec =$('sections').value, par =$('paras').value, kw =$('keywords').value.trim(), url =$('linkurl').value.trim(), lang =$('lang').value, style =$('style').value, tone =$('tone').value, faq =parseInt($('faq').value,10), pts =[...document.querySelectorAll('input[name="points[]"]')].map(i=>i.value.trim()).filter(Boolean), enh =[...$('enhBox').children].map(c=>c.dataset.v); let p = `As ${persona}, write a ${lang} blog post in a ${tone} tone and ${style} style about "${topic}"`; if(aud) p += ` for an audience of ${aud}`; p += `. Structure it into ${sec} section${sec>1?'s':''} (<h2>) with ${par} paragraph${par>1?'s':''} each. `; p += `Keep reading level around ${lvl}.`; if(kw){ p += ` Seamlessly weave in these keywords: ${kw}.`; if(url) p += ` Hyperlink each keyword once to ${url}.`; } if(pts.length) p += ` Cover the following key points: ${pts.join('; ')}.`; if(faq) p += ` Add an FAQ section with exactly ${faq} Q&A pairs.`; /* enhancements */ enh.forEach(e=>{ if(enhanceMap[e]) p += ' '+enhanceMap[e]; }); p += ' Finish with <p class="excerpt">Excerpt: …</p>. Return ONLY HTML.'; $('promptBox').value=p; $('promptField').value=p; $('copyPrompt').hidden=false; } $('copyPrompt').onclick=()=>navigator.clipboard.writeText($('promptBox').value).then(()=>flash($('copyPrompt'))); /* ---------- sync model from toolbar ------------------------- */ const ms=document.querySelector('#modelSel'); if(ms) $('gen').model.value=ms.value; /* ---------- AJAX submit ------------------------------------- */ $('gen').onsubmit=e=>{ e.preventDefault(); if(!$('promptField').value) return; const btn=$('generate'), spin=$('spin'), secs=$('secs'); btn.disabled=true; spin.classList.add('show'); let s=0; secs.textContent='0'; const tick=setInterval(()=>secs.textContent=++s,1000); fetch('?ajax=1',{method:'POST',body:new FormData($('gen'))}) .then(r=>r.json()) .then(j=>{ clearInterval(tick); 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(tick); spin.classList.remove('show'); btn.disabled=false; alert(err); }); }; /* ---------- copy helpers ------------------------------------ */ 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