Repository navigation
Expand file tree
/
Copy pathbackground.js
More file actions
71 lines (63 loc) · 2.66 KB
/
Copy pathbackground.js
File metadata and controls
71 lines (63 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// background.js
const LLM_RULE_PROMPTS = {
"glassmorphism": "Is there excessive glassmorphism (frosted-glass blur on cards/panels)?",
"extreme-border-radius": "Do cards/buttons use extremely rounded, pill-shaped corners that look generic/AI-made?",
"amateurish-hand-drawn-svg": "Is there a cheap, amateurish hand-drawn-style SVG illustration or mascot?",
"hero-metric-layout": "Does the hero show 3 big stat numbers side by side in a generic 'metric row' layout?",
"identical-card-grids": "Are there 3+ cards in a grid that look visually identical with no differentiation?",
"aphoristic-cadence-copy": "Does the visible copy read with a repetitive 'short punchy sentence. Then another.' AI cadence?",
};
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type !== "RUN_LLM_CHECKS") return false;
runAllChecks(message.screenshot, message.apiKey)
.then((findings) => sendResponse({ ok: true, findings }))
.catch((err) => sendResponse({ ok: false, error: err.message }));
return true;
});
async function askClaude(base64Image, question, apiKey) {
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey,
"anthropic-version": "2023-06-01",
"anthropic-dangerous-direct-browser-access": "true",
},
body: JSON.stringify({
model: "claude-sonnet-4-6",
max_tokens: 200,
messages: [
{
role: "user",
content: [
{ type: "image", source: { type: "base64", media_type: "image/png", data: base64Image } },
{ type: "text", text: `${question}\n\nRespond ONLY with JSON, no markdown: {"flagged": true|false, "reason": "one short sentence"}` },
],
},
],
}),
});
if (!response.ok) {
const errText = await response.text();
throw new Error(`API error ${response.status}: ${errText.slice(0, 150)}`);
}
const data = await response.json();
const textBlock = data.content?.find((c) => c.type === "text");
if (!textBlock) return { flagged: false, reason: "no response" };
try {
return JSON.parse(textBlock.text.replace(/```json|```/g, "").trim());
} catch {
return { flagged: false, reason: "unparseable response" };
}
}
async function runAllChecks(screenshotDataUrl, apiKey) {
const base64 = screenshotDataUrl.split(",")[1];
const findings = [];
for (const [ruleId, question] of Object.entries(LLM_RULE_PROMPTS)) {
const result = await askClaude(base64, question, apiKey);
if (result.flagged) {
findings.push({ ruleId, message: result.reason, type: "llm" });
}
}
return findings;
}