AI Mention Checker
AI Mention Checker is a lightweight “AI visibility” audit tool. It helps you generate consistent prompts to ask across different chatbots, then you paste the replies back in and it checks whether your domain or brand was mentioned/linked—saving results so you can track progress over time.
What it does:
- Generates prompt templates (recommendations, alternatives, “best sites”, etc.).
- Analyzes pasted responses for domain/brand mentions + extracted URLs.
- Attempts a simple “position” estimate if your site appears in a list.
- Saves a history log in
localStorage(export/import JSON + CSV). - Ad-free. No tracking. Everything stays in your browser.
Install:
- Create:
/tools/ai-mention-checker/ - Save as:
/tools/ai-mention-checker/index.html - Visit:
/tools/ai-mention-checker/
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>AI Mention Checker</title>
<meta name="description" content="Generate consistent prompts and analyze pasted AI chatbot replies to see if your website is mentioned/linked. Saves history locally." />
<link rel="canonical" href="https://your-site.com/tools/ai-mention-checker/" />
<meta name="robots" content="index,follow,max-snippet:-1,max-image-preview:large,max-video-preview:-1" />
<meta property="og:type" content="website" />
<meta property="og:title" content="AI Mention Checker" />
<meta property="og:description" content="Check whether chatbots mention or link to your site by analyzing pasted replies. Track results over time." />
<style>
:root{
--bg:#0b0f16; --panel:#111a26; --panel2:#0c1420;
--text:#e8eef8; --muted:rgba(232,238,248,.72);
--line:rgba(255,255,255,.10); --accent:#2a8cff;
--shadow:0 18px 55px rgba(0,0,0,.45); --r:16px;
--ok:#7CFF9A; --warn:#FFD37C; --bad:#FF7C7C;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}
a{color:#8ad1ff;text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1100px;margin:0 auto;padding:18px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:14px;margin:12px 0;box-shadow:var(--shadow)}
.top{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:center}
.pill{display:inline-block;padding:4px 10px;border-radius:999px;background:rgba(42,140,255,.14);border:1px solid rgba(42,140,255,.25);font-weight:900;font-size:12px}
.muted{color:var(--muted)}
.tabs{display:flex;gap:10px;flex-wrap:wrap}
.tab{
display:inline-flex;align-items:center;gap:8px;
border:1px solid var(--line);border-radius:999px;
padding:8px 12px;background:transparent;color:var(--text);
font-weight:900;cursor:pointer;
}
.tab.active{border-color:rgba(42,140,255,.35);background:rgba(42,140,255,.10)}
.grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:12px}
.c6{grid-column:span 6}
.c4{grid-column:span 4}
.c8{grid-column:span 8}
.c12{grid-column:span 12}
@media(max-width:980px){.c6,.c4,.c8{grid-column:span 12}}
input,select,textarea{
width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--line);
background:var(--panel2);color:var(--text);outline:none;
font:700 13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
}
textarea{
min-height:220px;resize:vertical;
font:650 13px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
border-radius:14px;
}
.btn{
display:inline-flex;align-items:center;justify-content:center;gap:8px;
border:0;border-radius:12px;padding:10px 12px;background:var(--accent);
color:#06101a;font-weight:950;cursor:pointer;
}
.btn2{
display:inline-flex;align-items:center;justify-content:center;gap:8px;
border:1px solid var(--line);border-radius:12px;padding:10px 12px;background:transparent;
color:var(--text);font-weight:950;cursor:pointer;
}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.kpi{display:flex;gap:14px;flex-wrap:wrap;margin-top:8px}
.kpi .box{padding:8px 10px;border-radius:12px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.03)}
.kpi b{font-size:16px}
.ok{color:var(--ok)} .warn{color:var(--warn)} .bad{color:var(--bad)}
.hr{height:1px;background:var(--line);margin:12px 0}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.boxy{border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.03);border-radius:16px;padding:12px}
.list{display:grid;gap:10px}
.item{padding:10px;border-radius:14px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.03)}
.small{font-size:12px}
table{width:100%;border-collapse:collapse}
th,td{padding:10px;border-bottom:1px solid rgba(255,255,255,.08);vertical-align:top}
th{text-align:left;font-size:12px;opacity:.75}
.tag{display:inline-block;padding:2px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04);font-size:12px;font-weight:900}
.hide{display:none !important}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<div class="top">
<div>
<div class="pill">AI Visibility</div>
<h1 style="margin:10px 0 6px;font-size:22px">AI Mention Checker</h1>
<div class="muted">Generate prompts → paste replies → detect mentions/links → track results locally (ad-free).</div>
</div>
<div class="tabs">
<button class="tab active" data-tab="prompts" type="button">🧩 Prompts</button>
<button class="tab" data-tab="analyze" type="button">🔎 Analyze</button>
<button class="tab" data-tab="history" type="button">🗂 History</button>
</div>
</div>
</div>
<!-- SETTINGS -->
<div class="card">
<div class="grid">
<div class="c4">
<div class="muted small" style="margin-bottom:6px">Your domain (required)</div>
<input id="domain" placeholder="example.com" autocomplete="off" />
</div>
<div class="c4">
<div class="muted small" style="margin-bottom:6px">Brand name (optional)</div>
<input id="brand" placeholder="YourBrand" autocomplete="off" />
</div>
<div class="c4">
<div class="muted small" style="margin-bottom:6px">Topic / niche (optional)</div>
<input id="topic" placeholder="e.g. anonymous confessions, url directories, php scripts..." autocomplete="off" />
</div>
<div class="c8">
<div class="muted small" style="margin-bottom:6px">Competitors (optional, one domain per line)</div>
<textarea id="competitors" style="min-height:120px" placeholder="competitor1.com
competitor2.com"></textarea>
</div>
<div class="c4">
<div class="muted small" style="margin-bottom:6px">Default chatbot</div>
<select id="defaultModel">
<option>ChatGPT</option>
<option>Claude</option>
<option>Gemini</option>
<option>Perplexity</option>
<option>Copilot</option>
<option>Other</option>
</select>
<div class="muted small" style="margin:12px 0 6px">Retention</div>
<select id="keepN">
<option value="50">Keep last 50</option>
<option value="100" selected>Keep last 100</option>
<option value="250">Keep last 250</option>
<option value="500">Keep last 500</option>
</select>
<div class="row" style="margin-top:12px">
<button class="btn2" id="saveSettings" type="button">💾 Save</button>
<button class="btn2" id="resetSettings" type="button">🧹 Reset</button>
</div>
<div class="muted small" style="margin-top:10px;line-height:1.45">
This tool doesn’t message chatbots for you. It standardizes prompts and checks the replies you paste in.
</div>
</div>
</div>
</div>
<!-- PROMPTS -->
<div class="card" id="tab-prompts">
<div class="top">
<div>
<b>Prompt Generator</b>
<div class="muted small" style="margin-top:6px">Copy a prompt, ask a chatbot, paste the reply into Analyze.</div>
</div>
<div class="row">
<select id="promptPack" style="min-width:260px">
<option value="recs">Recommendations (best sites/tools)</option>
<option value="alts">Alternatives (vs competitor / category)</option>
<option value="local">Local intent (near me / city)</option>
<option value="trust">Trust + safety (which sites are legit)</option>
<option value="seo">SEO/marketing (resources/tools)</option>
</select>
<button class="btn" id="genPrompts" type="button">🧠 Generate</button>
<button class="btn2" id="copyAllPrompts" type="button">📋 Copy all</button>
</div>
</div>
<div class="hr"></div>
<div class="grid">
<div class="c6">
<div class="muted small" style="margin-bottom:6px">Optional: location (for local prompts)</div>
<input id="location" placeholder="Detroit, MI" />
</div>
<div class="c6">
<div class="muted small" style="margin-bottom:6px">Optional: audience (who are we asking for?)</div>
<input id="audience" placeholder="webmasters, marketers, developers, adults, students..." />
</div>
</div>
<div class="hr"></div>
<div id="promptList" class="list"></div>
</div>
<!-- ANALYZE -->
<div class="card hide" id="tab-analyze">
<div class="top">
<div>
<b>Analyze a Reply</b>
<div class="muted small" style="margin-top:6px">Paste a chatbot reply and this will detect mentions, links, and an estimated “position” if it’s in a list.</div>
</div>
<div class="row">
<select id="model" style="min-width:220px">
<option>ChatGPT</option>
<option>Claude</option>
<option>Gemini</option>
<option>Perplexity</option>
<option>Copilot</option>
<option>Other</option>
</select>
<input id="label" placeholder="Optional label (e.g. best tools prompt #2)" style="min-width:280px" />
<button class="btn" id="run" type="button">🔎 Check</button>
<button class="btn2" id="saveResult" type="button" disabled>💾 Save</button>
</div>
</div>
<div class="hr"></div>
<div class="grid">
<div class="c6">
<div class="muted small" style="margin-bottom:6px">Prompt you asked (optional)</div>
<textarea id="promptText" placeholder="Paste the prompt you asked (optional)..." style="min-height:160px"></textarea>
</div>
<div class="c6">
<div class="muted small" style="margin-bottom:6px">Chatbot reply (paste)</div>
<textarea id="replyText" placeholder="Paste the chatbot response here..."></textarea>
</div>
<div class="c12">
<div class="kpi" id="kpi"></div>
</div>
<div class="c6">
<div class="boxy">
<b>Mentions</b>
<div class="muted small" style="margin-top:6px">Found contexts where your domain/brand appears.</div>
<div class="hr"></div>
<div id="mentions" class="list"></div>
</div>
</div>
<div class="c6">
<div class="boxy">
<b>Links Extracted</b>
<div class="muted small" style="margin-top:6px">URLs detected in the reply (and their domains).</div>
<div class="hr"></div>
<div id="links" class="list"></div>
</div>
</div>
</div>
</div>
<!-- HISTORY -->
<div class="card hide" id="tab-history">
<div class="top">
<div>
<b>History</b>
<div class="muted small" style="margin-top:6px">Saved checks (stored in your browser). Export anytime.</div>
</div>
<div class="row">
<button class="btn2" id="exportJson" type="button">⬇️ Export JSON</button>
<button class="btn2" id="exportCsv" type="button">⬇️ Export CSV</button>
<button class="btn2" id="importJson" type="button">⬆️ Import JSON</button>
<button class="btn2" id="clearHistory" type="button">🧹 Clear</button>
</div>
</div>
<div class="hr"></div>
<div class="grid">
<div class="c12">
<div style="overflow:auto">
<table>
<thead>
<tr>
<th>Date</th>
<th>Model</th>
<th>Label</th>
<th>Mentioned?</th>
<th>Linked?</th>
<th>Position</th>
<th>Notes</th>
</tr>
</thead>
<tbody id="histRows"></tbody>
</table>
</div>
</div>
<div class="c12">
<div class="muted small">Import/export uses JSON. CSV export is good for quick spreadsheets.</div>
<textarea id="io" placeholder="Exported data will appear here, or paste JSON to import..." style="min-height:160px;margin-top:10px"></textarea>
</div>
</div>
</div>
<div class="card">
<div class="muted small" style="line-height:1.55">
<b>Privacy:</b> This tool is fully client-side. It doesn’t send your text anywhere—everything stays in your browser unless you export it.
</div>
</div>
</div>
<script>
(function(){
var SKEY = "aiMentionChecker:v1";
var HKEY = "aiMentionChecker:history:v1";
function $(id){ return document.getElementById(id); }
function nowISO(){
var d = new Date();
return d.toISOString();
}
function htxt(s){
return String(s||"").replace(/[<>&]/g, function(c){ return ({'<':'<','>':'>','&':'&'}[c]); });
}
function cleanDomain(s){
s = String(s||"").trim().toLowerCase();
s = s.replace(/^https?:\/\//, "").replace(/^www\./, "");
s = s.split("/")[0].split("?")[0].split("#")[0];
s = s.replace(/:\d+$/, "");
return s;
}
function splitLines(s){
return String(s||"").split(/\r?\n/).map(function(x){ return x.trim(); }).filter(Boolean);
}
function loadSettings(){
try {
var j = JSON.parse(localStorage.getItem(SKEY) || "");
if (!j) return null;
return j;
} catch(e){ return null; }
}
function saveSettingsObj(o){
localStorage.setItem(SKEY, JSON.stringify(o));
}
function loadHistory(){
try {
var j = JSON.parse(localStorage.getItem(HKEY) || "");
if (!Array.isArray(j)) return [];
return j;
} catch(e){ return []; }
}
function saveHistory(arr){
localStorage.setItem(HKEY, JSON.stringify(arr));
}
function capHistory(arr, keepN){
keepN = Math.max(10, parseInt(keepN||100,10));
if (arr.length <= keepN) return arr;
return arr.slice(arr.length - keepN);
}
// Tabs
function setTab(name){
["prompts","analyze","history"].forEach(function(t){
$("tab-"+t).classList.toggle("hide", t !== name);
});
Array.prototype.slice.call(document.querySelectorAll(".tab")).forEach(function(b){
b.classList.toggle("active", b.getAttribute("data-tab") === name);
});
}
Array.prototype.slice.call(document.querySelectorAll(".tab")).forEach(function(b){
b.addEventListener("click", function(){ setTab(b.getAttribute("data-tab")); });
});
// Prompt templates
function promptTemplates(pack, ctx){
var topic = ctx.topic ? (' about ' + ctx.topic) : '';
var audience = ctx.audience ? (' for ' + ctx.audience) : '';
var loc = ctx.location ? (' in ' + ctx.location) : '';
var comps = ctx.competitors.length ? (' Consider alternatives to: ' + ctx.competitors.join(", ") + '.') : '';
var domain = ctx.domain ? (' My site is ' + ctx.domain + '.') : '';
var commonRules =
"Answer with a short ranked list (5–10 items). Include URLs if you know them. " +
"If you’re unsure, say so. No filler.";
if (pack === "recs") return [
{name:"Best sites/tools"+topic, text:
"What are the best websites or tools" + topic + audience + "? " + commonRules + comps},
{name:"Top alternatives"+topic, text:
"Recommend the top websites" + topic + " that people actually use. " + commonRules + comps},
{name:"Newer/lesser-known picks"+topic, text:
"List underrated but legit websites" + topic + " (not the usual big names). " + commonRules + comps}
];
if (pack === "alts") return [
{name:"Alternatives list"+topic, text:
"Give me alternatives" + topic + ". " + commonRules + comps},
{name:"Compare options"+topic, text:
"Compare 5–8 options" + topic + " with one-line pros/cons each. " + comps},
{name:"Best for beginners"+topic, text:
"What are the best beginner-friendly options" + topic + "? " + commonRules + comps}
];
if (pack === "local") return [
{name:"Local recommendations"+topic+loc, text:
"What are the best local resources or websites" + topic + loc + "? " + commonRules},
{name:"Local directories"+loc, text:
"Which directories or listing sites matter most" + loc + " for finding services? " + commonRules},
{name:"Local niche"+topic+loc, text:
"List niche local sites" + topic + loc + " that people overlook. " + commonRules}
];
if (pack === "trust") return [
{name:"Legit vs spam"+topic, text:
"Which websites" + topic + " are legit and which are spammy? Provide a short list. " + comps},
{name:"Safety checks"+topic, text:
"If someone uses sites" + topic + ", what red flags should they watch for? Also list reputable options. " + comps},
{name:"Reputation list"+topic, text:
"List reputable websites" + topic + " and briefly say why they’re trusted. " + commonRules + comps}
];
if (pack === "seo") return [
{name:"SEO/marketing resources"+topic, text:
"What are the best SEO / marketing resources" + topic + "? " + commonRules + comps},
{name:"Tools list"+topic, text:
"List useful tools" + topic + " and what each is best for (one line each). " + commonRules + comps},
{name:"Beginner to advanced"+topic, text:
"Give a learning path" + topic + " with recommended sites at each step. " + comps}
];
return [{name:"Generic", text:"Recommend websites" + topic + ". " + commonRules + comps + domain}];
}
function renderPrompts(items){
var host = $("promptList");
host.innerHTML = "";
if (!items.length){
host.innerHTML = '<div class="muted">No prompts yet. Click Generate.</div>';
return;
}
items.forEach(function(p, idx){
var div = document.createElement("div");
div.className = "item";
div.innerHTML =
'<div class="row" style="justify-content:space-between;align-items:flex-start">' +
'<div><b>' + htxt(p.name) + '</b><div class="muted small">Prompt #' + (idx+1) + '</div></div>' +
'<div class="row">' +
'<button class="btn2" data-copy="'+idx+'" type="button">📋 Copy</button>' +
'</div>' +
'</div>' +
'<div class="hr"></div>' +
'<div class="mono" style="white-space:pre-wrap;line-height:1.45">' + htxt(p.text) + '</div>';
host.appendChild(div);
});
Array.prototype.slice.call(host.querySelectorAll("button[data-copy]")).forEach(function(b){
b.addEventListener("click", function(){
var i = parseInt(b.getAttribute("data-copy"),10);
copyText(items[i].text);
});
});
}
function copyText(t){
t = String(t||"");
if (!t) return;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(t).catch(function(){ fallbackCopy(t); });
} else fallbackCopy(t);
}
function fallbackCopy(t){
var ta = document.createElement("textarea");
ta.value = t;
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
document.body.removeChild(ta);
}
// Analyze
function extractUrls(text){
var re = /https?:\/\/[^\s<>"')\]]+/ig;
var m = text.match(re) || [];
// de-dup
var seen = Object.create(null);
var out = [];
m.forEach(function(u){
var k = u.toLowerCase();
if (seen[k]) return;
seen[k]=1;
out.push(u);
});
return out.slice(0, 80);
}
function guessPosition(text, targets){
// Rough position: try to detect numbered/bulleted lists and find first line containing target
var lines = String(text||"").split(/\r?\n/);
var listLines = [];
for (var i=0;i<lines.length;i++){
var ln = lines[i].trim();
if (!ln) continue;
if (/^(\d+[\).\:]|\-|\*|•)\s+/.test(ln)) listLines.push(ln);
}
if (!listLines.length) return null;
for (var j=0;j<listLines.length;j++){
var low = listLines[j].toLowerCase();
for (var k=0;k<targets.length;k++){
if (targets[k] && low.indexOf(targets[k]) !== -1) {
// position is 1-based within detected list
return j+1;
}
}
}
return null;
}
function findContexts(text, needles){
// Return up to 10 snippets (line-based or sentence-ish)
var out = [];
var raw = String(text||"");
if (!raw.trim()) return out;
// Prefer line contexts
var lines = raw.split(/\r?\n/);
for (var i=0;i<lines.length;i++){
var ln = lines[i];
var low = ln.toLowerCase();
var hit = false;
for (var n=0;n<needles.length;n++){
if (needles[n] && low.indexOf(needles[n]) !== -1) { hit = true; break; }
}
if (hit) {
var prev = (lines[i-1] || "").trim();
var cur = ln.trim();
var next = (lines[i+1] || "").trim();
var snippet = [prev, cur, next].filter(Boolean).join("\n");
out.push(snippet);
}
if (out.length >= 10) break;
}
if (out.length) return out;
// Fallback: sentence-ish
var sentences = raw.split(/(?<=[\.\!\?])\s+/);
for (var s=0;s<sentences.length;s++){
var st = sentences[s];
var low2 = st.toLowerCase();
var hit2 = false;
for (var n2=0;n2<needles.length;n2++){
if (needles[n2] && low2.indexOf(needles[n2]) !== -1) { hit2 = true; break; }
}
if (hit2) out.push(st.trim());
if (out.length >= 10) break;
}
return out;
}
function renderKPI(r){
var k = $("kpi");
k.innerHTML = "";
function box(label, val, cls){
var d = document.createElement("div");
d.className = "box";
d.innerHTML = '<div class="muted small">'+htxt(label)+'</div><b class="'+cls+'">'+htxt(val)+'</b>';
k.appendChild(d);
}
box("Mentioned", r.mentioned ? "YES" : "NO", r.mentioned ? "ok" : "bad");
box("Linked", r.linked ? "YES" : "NO", r.linked ? "ok" : "warn");
box("Position (est.)", r.position ? ("#" + r.position) : "—", r.position ? "ok" : "muted");
box("URLs found", String(r.urls.length), r.urls.length ? "ok" : "muted");
box("Checked", r.model, "muted");
}
function renderMentions(snips){
var host = $("mentions");
host.innerHTML = "";
if (!snips.length){
host.innerHTML = '<div class="muted">No mention contexts found.</div>';
return;
}
snips.forEach(function(s){
var div = document.createElement("div");
div.className = "item";
div.innerHTML = '<div class="mono" style="white-space:pre-wrap;line-height:1.45">'+htxt(s)+'</div>';
host.appendChild(div);
});
}
function renderLinks(urls, domain, competitors){
var host = $("links");
host.innerHTML = "";
if (!urls.length){
host.innerHTML = '<div class="muted">No URLs detected in this reply.</div>';
return;
}
var target = cleanDomain(domain);
var compSet = Object.create(null);
competitors.forEach(function(c){ compSet[cleanDomain(c)] = 1; });
urls.forEach(function(u){
var d = cleanDomain(u);
var tag = "";
if (d === target) tag = '<span class="tag ok">YOUR SITE</span>';
else if (compSet[d]) tag = '<span class="tag warn">COMPETITOR</span>';
else tag = '<span class="tag">OTHER</span>';
var div = document.createElement("div");
div.className = "item";
div.innerHTML =
'<div class="row" style="justify-content:space-between">' +
'<div><a class="mono" target="_blank" rel="noopener" href="'+htxt(u)+'">'+htxt(u)+'</a><div class="muted small mono">'+htxt(d)+'</div></div>' +
'<div>'+tag+'</div>' +
'</div>';
host.appendChild(div);
});
}
var lastResult = null;
function analyze(){
var domain = $("domain").value.trim();
if (!domain) { alert("Enter your domain first (e.g. example.com)."); return; }
var brand = $("brand").value.trim();
var competitors = splitLines($("competitors").value);
var model = $("model").value;
var reply = $("replyText").value || "";
var target = cleanDomain(domain);
var needles = [target];
if (brand) needles.push(brand.toLowerCase());
// also look for www. + protocol variants (handled by domain needle mostly)
needles = needles.filter(Boolean);
var low = reply.toLowerCase();
var mentioned = false;
needles.forEach(function(n){
if (n && low.indexOf(n) !== -1) mentioned = true;
});
var urls = extractUrls(reply);
var linked = false;
urls.forEach(function(u){
if (cleanDomain(u) === target) linked = true;
});
var position = guessPosition(reply, [target, brand ? brand.toLowerCase() : ""]);
var snips = findContexts(reply, needles);
lastResult = {
id: "r_" + Math.random().toString(16).slice(2) + Date.now().toString(16),
createdAt: nowISO(),
model: model,
label: $("label").value.trim(),
domain: domain,
brand: brand,
topic: $("topic").value.trim(),
competitors: competitors,
prompt: $("promptText").value || "",
reply: reply,
mentioned: mentioned,
linked: linked,
position: position,
urls: urls,
snippets: snips
};
renderKPI({
mentioned: mentioned,
linked: linked,
position: position,
urls: urls,
model: model
});
renderMentions(snips);
renderLinks(urls, domain, competitors);
$("saveResult").disabled = false;
}
function saveCurrent(){
if (!lastResult) return;
var hist = loadHistory();
var keepN = parseInt($("keepN").value,10) || 100;
hist.push(lastResult);
hist = capHistory(hist, keepN);
saveHistory(hist);
renderHistory();
alert("Saved to history.");
}
function renderHistory(){
var hist = loadHistory();
var tbody = $("histRows");
tbody.innerHTML = "";
if (!hist.length){
tbody.innerHTML = '<tr><td colspan="7" class="muted">No saved checks yet.</td></tr>';
return;
}
// newest first
var items = hist.slice().sort(function(a,b){
return String(b.createdAt||"").localeCompare(String(a.createdAt||""));
});
items.forEach(function(r){
var d = new Date(r.createdAt || "");
var ds = isNaN(d.getTime()) ? (r.createdAt || "") : d.toLocaleString();
var tr = document.createElement("tr");
tr.innerHTML =
'<td class="muted small">'+htxt(ds)+'</td>' +
'<td><span class="tag">'+htxt(r.model||"")+'</span></td>' +
'<td class="small">'+htxt(r.label||"")+'</td>' +
'<td><b class="'+(r.mentioned?'ok':'bad')+'">'+(r.mentioned?'YES':'NO')+'</b></td>' +
'<td><b class="'+(r.linked?'ok':'warn')+'">'+(r.linked?'YES':'NO')+'</b></td>' +
'<td>'+(r.position?('<b class="ok">#'+htxt(String(r.position))+'</b>'):'—')+'</td>' +
'<td class="small muted">'+htxt((r.topic||"") ? ("Topic: " + r.topic) : "")+'</td>';
tbody.appendChild(tr);
});
}
function exportJSON(){
var hist = loadHistory();
$("io").value = JSON.stringify(hist, null, 2);
}
function exportCSV(){
var hist = loadHistory();
var rows = [];
rows.push(["createdAt","model","label","domain","brand","topic","mentioned","linked","position","urlCount"].join(","));
hist.forEach(function(r){
function q(v){
v = String(v==null?"":v).replace(/"/g,'""');
return '"' + v + '"';
}
rows.push([
q(r.createdAt||""),
q(r.model||""),
q(r.label||""),
q(r.domain||""),
q(r.brand||""),
q(r.topic||""),
q(r.mentioned?1:0),
q(r.linked?1:0),
q(r.position||""),
q((r.urls||[]).length)
].join(","));
});
$("io").value = rows.join("\n");
}
function importJSON(){
var raw = $("io").value.trim();
if (!raw) { alert("Paste JSON into the box first."); return; }
try{
var j = JSON.parse(raw);
if (!Array.isArray(j)) { alert("Invalid JSON (expected an array)."); return; }
// minimal normalize
var keepN = parseInt($("keepN").value,10) || 100;
var merged = loadHistory().concat(j).filter(function(x){ return x && typeof x === "object"; });
merged = capHistory(merged, keepN);
saveHistory(merged);
renderHistory();
alert("Imported.");
} catch(e){
alert("Invalid JSON.");
}
}
function clearHistory(){
if (!confirm("Clear history from this browser?")) return;
saveHistory([]);
renderHistory();
$("io").value = "";
}
// Settings UI
function hydrate(){
var s = loadSettings();
if (s){
$("domain").value = s.domain || "";
$("brand").value = s.brand || "";
$("topic").value = s.topic || "";
$("competitors").value = (s.competitors || []).join("\n");
$("defaultModel").value = s.defaultModel || "ChatGPT";
$("keepN").value = String(s.keepN || 100);
$("model").value = s.defaultModel || "ChatGPT";
} else {
$("model").value = $("defaultModel").value;
}
renderHistory();
}
$("saveSettings").addEventListener("click", function(){
var obj = {
domain: $("domain").value.trim(),
brand: $("brand").value.trim(),
topic: $("topic").value.trim(),
competitors: splitLines($("competitors").value),
defaultModel: $("defaultModel").value,
keepN: parseInt($("keepN").value,10) || 100
};
saveSettingsObj(obj);
$("model").value = obj.defaultModel;
alert("Saved.");
});
$("resetSettings").addEventListener("click", function(){
if (!confirm("Reset settings?")) return;
localStorage.removeItem(SKEY);
$("domain").value = "";
$("brand").value = "";
$("topic").value = "";
$("competitors").value = "";
$("defaultModel").value = "ChatGPT";
$("keepN").value = "100";
$("model").value = "ChatGPT";
});
$("defaultModel").addEventListener("change", function(){
$("model").value = $("defaultModel").value;
});
// Prompt generator wiring
var lastPrompts = [];
$("genPrompts").addEventListener("click", function(){
var ctx = {
domain: cleanDomain($("domain").value),
brand: $("brand").value.trim(),
topic: $("topic").value.trim(),
audience: $("audience").value.trim(),
location: $("location").value.trim(),
competitors: splitLines($("competitors").value)
};
lastPrompts = promptTemplates($("promptPack").value, ctx);
renderPrompts(lastPrompts);
});
$("copyAllPrompts").addEventListener("click", function(){
if (!lastPrompts.length) { alert("Generate prompts first."); return; }
var t = lastPrompts.map(function(p, i){
return "Prompt #" + (i+1) + " — " + p.name + "\n" + p.text;
}).join("\n\n---\n\n");
copyText(t);
});
// Analyze wiring
$("run").addEventListener("click", analyze);
$("saveResult").addEventListener("click", saveCurrent);
// History wiring
$("exportJson").addEventListener("click", exportJSON);
$("exportCsv").addEventListener("click", exportCSV);
$("importJson").addEventListener("click", importJSON);
$("clearHistory").addEventListener("click", clearHistory);
hydrate();
})();
</script>
</body>
</html>
Comments (0)
No comments yet — be the first.