Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> | |
| <title>Slop Index Lite — VibeAxis</title> | |
| <meta name="description" content="Quick, client-side 'slop' detector for text. Checks clichés, hedges, filler, repetition, and sentence bloat."> | |
| <style> | |
| :root{--bg:#0b0b0c;--fg:#eee;--mut:#9aa0a6;--card:#141518;--acc:#ffd400} | |
| *{box-sizing:border-box} html,body{height:100%} body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,Inter,Arial} | |
| .wrap{max-width:920px;margin:32px auto;padding:0 16px} | |
| h1{font-size:28px;margin:0 0 6px} .tag{color:var(--mut);font-size:14px;margin-bottom:18px} | |
| .card{background:var(--card);border:1px solid #1f2024;border-radius:16px;padding:16px;margin:16px 0} | |
| textarea{width:100%;min-height:220px;background:#0f1013;color:var(--fg);border:1px solid #25262b;border-radius:10px;padding:12px;resize:vertical;font:14px/1.45 ui-monospace,SFMono-Regular,Consolas,Monaco,monospace} | |
| .row{display:flex;gap:12px;flex-wrap:wrap;margin:12px 0} | |
| .btn{background:var(--acc);color:#000;border:0;border-radius:999px;padding:10px 14px;font-weight:700;cursor:pointer} | |
| .btn.subtle{background:#1b1c20;color:var(--fg);border:1px solid #2a2b30} | |
| .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px} | |
| .metric{background:#0f1013;border:1px solid #25262b;border-radius:12px;padding:12px} | |
| .metric b{display:block;font-size:22px;margin-bottom:6px} | |
| .bar{height:8px;border-radius:999px;background:#1e2025;overflow:hidden} | |
| .fill{height:100%;width:0;background:linear-gradient(90deg,#ff7a7a,#ffb37a)} | |
| a{color:var(--acc);text-decoration:none} a:hover{text-decoration:underline} | |
| .note{color:var(--mut);font-size:13px;margin-top:8px} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>Slop Index Lite</h1> | |
| <div class="tag">Fast, client-side gut check for filler, hedging, clichés, and repetition. No upload. No storage.</div> | |
| <div class="card"> | |
| <textarea id="txt" placeholder="Paste text here…"></textarea> | |
| <div class="row"> | |
| <button class="btn" id="analyze">Analyze</button> | |
| <button class="btn subtle" id="sample">Try Sample</button> | |
| </div> | |
| <div class="note">Heads-up: this is a heuristic demo. For the deep dive and full signal set, see the article on VibeAxis.</div> | |
| </div> | |
| <div class="grid"> | |
| <div class="metric"><b id="score">–</b>Slop Score (0–100)<div class="bar"><div class="fill" id="fill"></div></div></div> | |
| <div class="metric"><b id="uniq">–%</b>Uniqueness (type/token)</div> | |
| <div class="metric"><b id="bloat">–</b>Avg sentence length</div> | |
| <div class="metric"><b id="hedge">–</b>Hedge words</div> | |
| <div class="metric"><b id="cliche">–</b>Clichés</div> | |
| <div class="metric"><b id="ly">–</b>“-ly” adverbs</div> | |
| <div class="metric"><b id="repeat">–</b>Top word repeats</div> | |
| </div> | |
| <div class="card" id="findings" style="display:none"></div> | |
| <div class="card"> | |
| <b>Receipts / Learn More</b><br/> | |
| <a href="https://vibeaxis.com" target="_blank" rel="noopener">VibeAxis</a> — article & methodology (link to your tool post)<br/> | |
| </div> | |
| </div> | |
| <script> | |
| (function(){ | |
| const hedges = ['perhaps','maybe','seems','appears','arguably','reportedly','somewhat','kind of','sort of','likely','unlikely','possibly','generally','in my opinion','i think','we believe']; | |
| const cliches = ['at the end of the day','paradigm shift','game changer','leverage','cutting edge','world class','next generation','best in class','innovative solution','seamless experience','move the needle','win-win','low-hanging fruit']; | |
| const stop = new Set(['the','a','an','and','or','but','of','to','in','on','for','with','as','by','is','are','was','were','it','that','this','be','from','at','we','you','they','i']); | |
| const $ = s=>document.querySelector(s); | |
| const txt = $('#txt'), out = { | |
| score: $('#score'), uniq: $('#uniq'), bloat: $('#bloat'), | |
| hedge: $('#hedge'), cliche: $('#cliche'), ly: $('#ly'), repeat: $('#repeat'), | |
| fill: $('#fill'), findings: $('#findings') | |
| }; | |
| function sentences(s){ | |
| return s.split(/(?<=[\.\?\!])\s+/).map(x=>x.trim()).filter(Boolean); | |
| } | |
| function words(s){ | |
| return s.toLowerCase().replace(/[^a-z0-9\s'-]+/g,' ').split(/\s+/).filter(Boolean); | |
| } | |
| function topRepeats(tokens, n=3){ | |
| const freq = Object.create(null); | |
| tokens.forEach(t=>{ if(!stop.has(t)) freq[t]=(freq[t]||0)+1; }); | |
| return Object.entries(freq).sort((a,b)=>b[1]-a[1]).slice(0,n); | |
| } | |
| function analyze(){ | |
| const raw = txt.value || ''; | |
| const sents = sentences(raw); | |
| const toks = words(raw); | |
| const uniq = new Set(toks).size; | |
| const ttr = toks.length ? (uniq / toks.length) : 0; | |
| const avgLen = sents.length ? (toks.length / sents.length) : 0; | |
| const hedgeCount = hedges.reduce((n,h)=> n + (raw.toLowerCase().includes(h) ? 1 : 0), 0); | |
| const clicheCount = cliches.reduce((n,h)=> n + (raw.toLowerCase().includes(h) ? 1 : 0), 0); | |
| const lyCount = (raw.match(/\b\w+ly\b/gi) || []).length; | |
| const repeats = topRepeats(toks, 3); | |
| const repScore = repeats.reduce((n,[_w,c])=> n + Math.max(0,c-2), 0); | |
| // Slop score (higher = worse): bloat + hedges + clichés + adverbs + repeats – uniqueness bonus | |
| let score = 0; | |
| score += Math.max(0, avgLen - 22) * 1.2; // sentence bloat | |
| score += hedgeCount * 5; // hedging | |
| score += clicheCount * 6; // clichés | |
| score += Math.max(0, lyCount - 4) * 1.2; // adverb pileup | |
| score += repScore * 2.5; // repetition | |
| score += (1 - Math.min(1, ttr*1.25)) * 20; // low uniqueness | |
| score = Math.max(0, Math.min(100, Math.round(score))); | |
| // UI | |
| out.score.textContent = String(score); | |
| out.fill.style.width = score + '%'; | |
| out.uniq.textContent = Math.round(ttr*100)+'%'; | |
| out.bloat.textContent = sents.length? avgLen.toFixed(1) : '–'; | |
| out.hedge.textContent = hedgeCount; | |
| out.cliche.textContent = clicheCount; | |
| out.ly.textContent = lyCount; | |
| out.repeat.textContent = repeats.map(([w,c])=> w+'×'+c).join(', ') || '–'; | |
| // Findings | |
| const bullets = []; | |
| if (avgLen > 24) bullets.push('Long sentences — consider splitting.'); | |
| if (hedgeCount) bullets.push('Hedging language detected (confidence fog).'); | |
| if (clicheCount) bullets.push('Cliché phrases detected (corporate slop).'); | |
| if (lyCount > 6) bullets.push('Adverb pile-up (try stronger verbs).'); | |
| if (ttr < 0.4) bullets.push('Low uniqueness — repetition or boilerplate.'); | |
| if (!bullets.length) bullets.push('Looks fairly tight. Try a tougher sample.'); | |
| out.findings.style.display = 'block'; | |
| out.findings.innerHTML = '<b>Highlights</b><ul style="margin:8px 0 0 16px">'+bullets.map(b=>'<li>'+b+'</li>').join('')+'</ul>'; | |
| } | |
| $('#analyze').addEventListener('click', analyze); | |
| $('#sample').addEventListener('click', function(){ | |
| txt.value = `At the end of the day, our next-generation solution delivers a seamless experience that will move the needle. We generally believe this innovative approach will arguably revolutionize workflows, kind of. It’s probably the best in class.`; | |
| analyze(); | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |