Siteβ―Builder
Editing:
index7.php
writable 0666
<?php /** -------------------------------------------------------------- * Promptinator v6 (Full) β SELFβCONTAINED TEMPLATE (v6.4) * ------------------------------------------------------------- * β’ Rootβanchored business path /ph/<10βdigitβphone>/business.json * β’ Meta tags always render (biz description fallback; prompt β’ desc) * β’ ?debug=1 prints path(s) tried + flags in a red box * β’ FULL JavaScript restored (no more βomittedβ) so tokens convert * and AI buttons populate. * ------------------------------------------------------------- */ // ---------- 1. BOOTSTRAP INPUTS ------------------------------------ $ph = $_GET['ph'] ?? null; // phoneβslug directory $idx = (int) ($_GET['idx'] ?? 0); // prompt index (0βbased) $debug = isset($_GET['debug']); // ?debug=1 toggle // ---------- 2. LOAD BUSINESS JSON ---------------------------------- $biz = []; $pathsTried = []; if ($ph) { $root = rtrim($_SERVER['DOCUMENT_ROOT'] ?? __DIR__, '/'); $pathsTried[] = $primary = "$root/ph/$ph/business.json"; $pathsTried[] = $secondary = __DIR__ . "/ph/$ph/business.json"; foreach ($pathsTried as $p) if (is_readable($p)) { $biz = json_decode(file_get_contents($p), true) ?? []; break; } } if (isset($biz['lat']) && !isset($biz['latitude'])) $biz['latitude'] = $biz['lat']; if (isset($biz['lon']) && !isset($biz['longitude'])) $biz['longitude'] = $biz['lon']; // ---------- 3. LOAD PROMPTS ---------------------------------------- $prompts = []; $promptsPath = __DIR__ . '/prompts.json'; if (is_readable($promptsPath)) { $ary = json_decode(file_get_contents($promptsPath), true); if (is_array($ary)) $prompts = array_column($ary, 'prompt'); } $currentPrompt = $prompts[$idx] ?? ''; $currentIndex = $idx; // ---------- 4. BUILD CANONICAL URL --------------------------------- $chronicleUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']==='on' ? 'https://' : 'http://') . ($_SERVER['HTTP_HOST'] ?? 'localhost') . strtok($_SERVER['REQUEST_URI'], '?'); $safe = fn($k)=>htmlspecialchars((string)($biz[$k] ?? ''), ENT_QUOTES); ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title><?= $safe('name') ? $safe('name').' | Prompt Playground' : 'Ultimate Prompt Playground v6' ?></title> <?php if ($biz): $descSeed = $currentPrompt ?: ($biz['description'] ?? ''); $baseLine = trim(($biz['name'] ?? '').($biz['city']?", {$biz['city']}, {$biz['state']}":'')); $desc = htmlspecialchars(trim($descSeed.($descSeed&&$baseLine?' β ':'').$baseLine)); ?> <meta name="description" content="<?= $desc ?>"> <meta property="og:title" content="<?= $safe('name').($currentPrompt?' | Prompt '.($currentIndex+1):'') ?>"> <meta property="og:description" content="<?= $desc ?>"> <meta property="og:url" content="<?= htmlspecialchars($chronicleUrl) ?>"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="<?= $safe('name').($currentPrompt?' Prompt':'') ?>"> <meta name="twitter:description" content="<?= $desc ?>"> <meta name="geo.region" content="US-<?= $safe('state') ?>"> <meta name="geo.placename" content="<?= $safe('city') ?>"> <?php if (!empty($biz['latitude']) && !empty($biz['longitude'])): ?> <meta name="geo.position" content="<?= $biz['latitude'] . ',' . $biz['longitude'] ?>"> <meta name="ICBM" content="<?= $biz['latitude'] . ',' . $biz['longitude'] ?>"> <?php endif; ?> <?php endif; ?> <!-- ---------- 5. DESIGN RESOURCES -------------------------------- --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> <script src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script> <!-- ---------- 6. INLINE CSS (unchanged) -------------------------- --> <style> body { max-width:900px; margin:2rem auto; font-family:system-ui,sans-serif; } h1 { text-align:center; margin-bottom:1.5rem; } .biz-card{ background:#f9fafb; border:1px solid #ccc; border-radius:6px; padding:1rem; margin-bottom:1rem; display:flex; align-items:center; gap:1rem; } .biz-card h2{ margin:0; font-size:1.5rem; } .biz-details{ display:flex; flex-direction:column; gap:.25rem; } .biz-details p{ margin:0; font-size:1rem; } .biz-icon{ font-size:1.5rem; color:#6c48ff; } .nav-buttons{ display:flex; justify-content:space-between; margin:1rem 0; } .nav-buttons button{ padding:.75rem 1.5rem; font-size:1rem; border:none; border-radius:6px; background:#6c48ff; color:#fff; cursor:pointer; } .nav-buttons button:disabled{ background:#ccc; cursor:default; } .prompt-card{ position:relative; background:#fff; border:1px solid #ddd; border-radius:8px; padding:1rem; margin-bottom:1.5rem; } .prompt-text{ white-space:pre-wrap; line-height:1.6; } .prompt-text h2{ font-size:1.25rem; margin:1rem 0 .5rem; } .prompt-text ul,.prompt-text ol{ margin:.5rem 0 .5rem 1.25rem; } .prompt-text li{ margin:.25rem 0; } .prompt-text strong{ font-weight:700; } .token{ display:inline-flex; background:#e8e0ff; color:#5c3bff; padding:.3rem .6rem; border-radius:4px; cursor:pointer; user-select:none; flex:0 1 auto; min-width:0; white-space:normal; overflow-wrap:break-word; word-break:break-word; margin-bottom:.3rem; } .token.placeholder{ opacity:.6; } .toolbar{ position:absolute; background:#fff; border:1px solid #ccc; border-radius:6px; box-shadow:0 2px 12px rgba(0,0,0,.1); padding:.8rem; display:flex; flex-direction:column; gap:.6rem; z-index:10; width:300px; } .toolbar label{ font-size:.9rem; font-weight:600; margin-bottom:.3rem; } .toolbar input[type="text"], .toolbar select, .toolbar textarea{ width:100%; padding:.4rem; border:1px solid #999; border-radius:4px; font-size:1rem; } .toolbar select{ max-height:200px; overflow:auto; } .toolbar textarea{ resize:vertical; } .checkbox-list{ display:flex; flex-direction:column; gap:.4rem; max-height:200px; overflow:auto; padding-right:.2rem; } .checkbox-list label{ display:flex; align-items:center; gap:.6rem; padding:.4rem .6rem; background:#f0f4ff; border-radius:4px; cursor:pointer; transition:background .2s; } .checkbox-list label:hover{ background:#e0e8ff; } .checkbox-list input[type="checkbox"]{ accent-color:#3b82f6; width:1.2em; height:1.2em; flex-shrink:0; } .ai-btns{ display:flex; gap:.6rem; margin-top:1rem; } .ai-btns a{ flex:1; text-align:center; padding:.7rem; border-radius:6px; color:#fff; text-decoration:none; font-weight:600; } .ai-btns a.chatgpt{ background:#6c48ff; } .ai-btns a.perplexity{ background:#1ca7ec; } .ai-btns a.copilot{ background:#00c853; color:#173E22; } .ai-btns a:hover{ filter:brightness(1.1); } </style> </head> <body> <h1>Promptinator By Melanie AI</h1> <!-- --------- 7. BUSINESS PROFILE CARD ----------------------------- --> <div id="biz-profile" class="biz-card" <?= $biz ? '' : 'hidden' ?>> <div class="biz-icon">π’</div> <div class="biz-details"> <h2><?= $safe('name') ?></h2> <p>π <span class="biz-address"><?= $safe('address') ?></span></p> <p>π <span class="biz-phone"><?= $safe('phone') ?></span></p> </div> </div> <!-- Navigation --> <div class="nav-buttons"> <button id="prev-btn" disabled>Previous</button> <button id="next-btn" disabled>Next</button> </div> <!-- Prompt Container --> <div id="app"></div> <!-- ---------- 8. PASS PHP DATA TO JS ------------------------------ --> <script>window.bizFromPHP = <?= json_encode($biz, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT) ?>;</script> <script> (async () => { const SEP = ' β’ '; const RX = /\[(?:([ABCDI]?)-)?([^~|\-\]]+)(?:-([^~]*?))?(?:~([^~]*?)~)?\]/g; /* --- Use serverβside business first; fallback to fetch if none --- */ let biz = window.bizFromPHP || {}; if (!biz.name) { const ph = new URLSearchParams(location.search).get('ph') || ''; if (ph) { try { biz = await fetch(`/ph/${ph}/business.json`).then(r=>r.json()); } catch {} } } /* --- Load prompts (clientβside) ---------------------------------- */ let prompts = []; try { prompts = await fetch('prompts.json').then(r=>r.json()).then(a=>a.map(o=>o.prompt)); } catch {} if (!prompts.length) return; const app = document.getElementById('app'); const cards = []; const formatPhone = n => { const d = (n || '').replace(/\D/g,''); return d.length===10 ? `(${d.slice(0,3)}) ${d.slice(3,6)}-${d.slice(6)}` : n; }; if (biz.name) { document.querySelector('#biz-profile .biz-address').textContent = biz.address || ''; document.querySelector('#biz-profile .biz-phone' ).textContent = formatPhone(biz.phone); } /* --- Helper: place token toolbar --------------------------------- */ const placeToolbar = (tb, tok) => { const card = tok.closest('.prompt-card'); const tr = tok.getBoundingClientRect(); const cr = card.getBoundingClientRect(); tb.style.top = (tr.bottom - cr.top) + 'px'; tb.style.left = (tr.left - cr.left) + 'px'; }; /* --- Build prompt cards ------------------------------------------ */ for (const tpl of prompts) { const meta = []; const state = {}; ['name','slogan','description','address','city','state','zip','phone','website'].forEach(k=>state[k]=biz[k]||''); state.service = Array.isArray(biz.tags) ? biz.tags.slice(0,1) : []; state.location = Array.isArray(biz.location_tags) ? biz.location_tags.slice(0,1) : []; tpl.replace(RX, (m, cmd='', lab, ops='', def='') => { lab = lab.trim(); let opts = []; if (cmd==='C') { opts = lab==='service' ? biz.tags : lab==='location' ? biz.location_tags : ops.split('|').filter(Boolean); state[lab] = def ? def.split(',').map(v=>v.trim()) : (opts.length ? [opts[0]] : []); } else if (cmd==='B') { opts = ops.split('|').filter(Boolean); state[lab] = def || opts[0] || ''; } else if (cmd==='D') { state[lab] = def || state[lab] || ''; } else if (cmd==='I') { state[lab] = def || state[lab] || ''; } else { state[lab] = def || state[lab] || ''; } meta.push({cmd: cmd || 'A', lab, opts, rawOps: ops, defaultVal: def}); return ''; }); /* --- Card skeleton --- */ const card = document.createElement('div'); card.className='prompt-card'; card.innerHTML=`<div class="prompt-text"></div><div class="ai-btns"><a class="chatgpt" data-base="https://chatgpt.com/?prompt=" target="_blank">ChatGPT</a><a class="perplexity" data-base="https://www.perplexity.ai/search?q=" target="_blank">Perplexity</a><a class="copilot" data-base="https://copilot.microsoft.com/?q=" target="_blank">Copilot</a></div>`; app.append(card); cards.push(card); /* --- Hidden toolbar --- */ const tb = document.createElement('div'); tb.className='toolbar'; tb.style.display='none'; card.append(tb); /* --- Render helpers --- */ const renderText = () => { const cont = card.querySelector('.prompt-text'); const substituted = tpl.replace(RX, (m, cmd='', lab) => { const idx = meta.findIndex(x=>x.lab===lab.trim()); return cmd==='C' ? (state[lab]||[]).map(tag=>`<span class="token" data-i="${idx}">${tag}</span>`).join(' ') : `<span class="token" data-i="${idx}">${state[lab]||''}</span>`; }); cont.innerHTML = marked.parse(substituted); cont.querySelectorAll('.token').forEach(t=>t.onclick=onTokenClick); }; const rebuild = () => { renderText(); const out = tpl.replace(RX, (m, cmd='', lab)=> cmd==='C' ? (state[lab.trim()]||[]).join(SEP) : (state[lab.trim()]||'')); card.querySelectorAll('.ai-btns a').forEach(a=>a.href = a.dataset.base + encodeURIComponent(out)); }; renderText(); rebuild(); /* --- Token click logic --- */ function onTokenClick(e) { const tok = e.currentTarget; const idx = +tok.dataset.i; const {cmd, lab, opts, rawOps, defaultVal} = meta[idx]; tb.innerHTML=''; placeToolbar(tb,tok); tb.style.display='flex'; document.addEventListener('mousedown', function off(ev){ if(!tb.contains(ev.target)&&ev.target!==tok){ tb.style.display='none'; document.removeEventListener('mousedown', off); } }); const label=document.createElement('label'); label.textContent=lab.replace(/_/g,' '); tb.append(label); if(cmd==='C'){ // multiβselect const list=document.createElement('div'); list.className='checkbox-list'; opts.forEach(o=>{ const lb=document.createElement('label'); const cb=document.createElement('input'); cb.type='checkbox'; cb.value=o; cb.checked=(state[lab]||[]).includes(o); cb.onchange=()=>{ state[lab]=[...list.querySelectorAll('input:checked')].map(i=>i.value); rebuild(); }; lb.append(cb,' ',o); list.append(lb); }); tb.append(list); } else if(cmd==='B'){ // radio opts.forEach(o=>{ const lb=document.createElement('label'); const rd=document.createElement('input'); rd.type='radio'; rd.name=lab; rd.value=o; rd.checked=(state[lab]===o); rd.onchange=()=>{ state[lab]=o; rebuild(); tb.style.display='none'; }; lb.append(rd,' ',o); tb.append(lb); }); } else if(cmd==='D'){ // dropdown const sel=document.createElement('select'); if(opts.length){ opts.forEach(o=>{ const op=document.createElement('option'); op.value=o; op.textContent=o; if(o===state[lab]) op.selected=true; sel.append(op); }); } else { const max=parseInt(rawOps||defaultVal,10)||0; for(let i=1;i<=max;i++){ const op=document.createElement('option'); op.value=String(i); op.textContent=i; if(String(i)===state[lab]) op.selected=true; sel.append(op);} } sel.onchange=()=>{ state[lab]=sel.value; rebuild(); tb.style.display='none'; }; tb.append(sel); } else if(cmd==='I'){ // iframe picker or numeric if(opts.length){ const select=document.createElement('select'); const ph=document.createElement('option'); ph.disabled=true; ph.selected=true; ph.textContent=state[lab]||'Pick a site'; select.append(ph); opts.forEach(url=>{ const op=document.createElement('option'); op.value=url; op.textContent=url; select.append(op); }); const iframe=document.createElement('iframe'); iframe.hidden=true; iframe.style='width:100%;height:200px'; const btn=document.createElement('button'); btn.textContent='Use site'; btn.style.marginTop='.6rem'; select.onchange=()=>{ iframe.hidden=false; iframe.src=select.value; }; btn.onclick=()=>{ state[lab]=select.value; rebuild(); tb.style.display='none'; }; tb.append(select, iframe, btn); } else { const inp=document.createElement('input'); inp.type='number'; inp.value=state[lab]||''; inp.oninput=()=>{ state[lab]=inp.value; rebuild(); }; tb.append(inp); } } 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=state[lab]||''; field.oninput=()=>{ state[lab]=field.value; rebuild(); }; tb.append(field); } } } /* --- Navigation -------------------------------------------------- */ const prevBtn=document.getElementById('prev-btn'); const nextBtn=document.getElementById('next-btn'); let current=<?= $currentIndex ?>; const show=idx=>{ cards.forEach((c,i)=>c.style.display=(i===idx?'block':'none')); prevBtn.disabled = idx===0; nextBtn.disabled = idx===cards.length-1; prevBtn.textContent = idx>0 ? 'β '+prompts[idx-1].split(/\s+/).slice(0,4).join(' ')+'β¦' : 'Previous'; nextBtn.textContent = idx<prompts.length-1 ? prompts[idx+1].split(/\s+/).slice(0,4).join(' ')+'β¦ β' : 'Next'; }; prevBtn.onclick=()=>{ if(current>0) show(--current); }; nextBtn.onclick=()=>{ if(current<cards.length-1) show(++current); }; show(current); /* --- Shortcode tutorial ------------------------------------------ */ const tutorial=document.createElement('section'); tutorial.style='margin-top:2rem;padding:1rem;border-top:1px solid #ddd;'; tutorial.innerHTML=`<h2>Shortcode Tutorial</h2><ul><li><strong>A-tokens</strong> β <code>[A-key-~default~]</code> (text)</li><li><strong>B-tokens</strong> β <code>[B-key-|opt1|opt2|]</code> (single)</li><li><strong>C-tokens</strong> β <code>[C-key-|opt1|opt2|]</code> (multi)</li><li><strong>D-tokens</strong> β <code>[D-key-5~3~]</code> (dropdown)</li><li><strong>I-tokens</strong> β <code>[I-key-|url1|url2|]</code> (iframe)</li></ul>`; app.append(tutorial); })(); </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel