yujisium's picture
Fintech Dark Patterns NLP Detector - full project upload
5752a28 verified
Raw
History Blame Contribute Delete
5.52 kB
const elements = {
scanButton: document.getElementById("scan-button"),
clearButton: document.getElementById("clear-button"),
status: document.getElementById("status"),
summary: document.getElementById("summary"),
scannedCount: document.getElementById("scanned-count"),
flaggedCount: document.getElementById("flagged-count"),
minimumScore: document.getElementById("minimum-score"),
pageLabel: document.getElementById("page-label"),
results: document.getElementById("results"),
apiUrl: document.getElementById("api-url"),
minimumScoreInput: document.getElementById("minimum-score-input"),
maxItems: document.getElementById("max-items"),
saveSettings: document.getElementById("save-settings"),
testConnection: document.getElementById("test-connection"),
};
function setStatus(message, tone = "neutral") {
elements.status.textContent = message;
elements.status.dataset.tone = tone;
}
function setBusy(busy) {
elements.scanButton.disabled = busy;
elements.clearButton.disabled = busy;
elements.scanButton.textContent = busy ? "Scanning page..." : "Scan current page";
}
function sendMessage(message) {
return chrome.runtime.sendMessage(message);
}
function createFinding(result) {
const card = document.createElement("article");
card.className = "finding";
card.dataset.severity = result.severity || "medium";
const header = document.createElement("div");
header.className = "finding-header";
const type = document.createElement("span");
type.className = "finding-type";
type.textContent = result.prediction;
const score = document.createElement("span");
score.className = "score";
score.textContent = `${result.confidence}% ${result.confidenceBand}`;
header.append(type, score);
const evidence = document.createElement("p");
evidence.className = "evidence";
evidence.textContent = result.text;
const phrases = document.createElement("div");
phrases.className = "phrases";
const phraseText = (result.explanation || [])
.map((item) => item.phrase)
.join(", ");
phrases.textContent = phraseText
? `Trigger phrases: ${phraseText}`
: "No stable word-level explanation available.";
const locate = document.createElement("button");
locate.className = "locate";
locate.textContent = "Locate on page";
locate.addEventListener("click", async () => {
const response = await sendMessage({
type: "FOCUS_RESULT",
elementId: result.id,
});
if (!response?.ok) {
setStatus(response?.error || "Could not locate the element.", "error");
}
});
card.append(header, evidence, phrases, locate);
return card;
}
function renderScan(response) {
elements.summary.hidden = false;
elements.scannedCount.textContent = response.scanned;
elements.flaggedCount.textContent = response.flagged;
elements.minimumScore.textContent = `${response.minimumScore || 0}%`;
elements.pageLabel.textContent = response.page?.title || "";
elements.results.replaceChildren();
if (!response.results?.length) {
const empty = document.createElement("p");
empty.className = "empty";
empty.textContent = "No visible text exceeded the current review threshold.";
elements.results.appendChild(empty);
return;
}
for (const result of response.results) {
elements.results.appendChild(createFinding(result));
}
}
async function runScan() {
setBusy(true);
setStatus("Collecting visible interface text and running the NLP model...");
try {
const response = await sendMessage({ type: "RUN_SCAN" });
if (!response?.ok) {
throw new Error(response?.error || "Scan failed.");
}
renderScan(response);
setStatus(
`Audit complete. ${response.flagged} of ${response.scanned} texts require review.`,
"success",
);
} catch (error) {
setStatus(error.message, "error");
} finally {
setBusy(false);
}
}
async function clearHighlights() {
const response = await sendMessage({ type: "CLEAR_HIGHLIGHTS" });
if (!response?.ok) {
setStatus(response?.error || "Could not clear highlights.", "error");
return;
}
setStatus("Page highlights cleared.", "success");
}
async function loadSettings() {
const response = await sendMessage({ type: "GET_SETTINGS" });
if (!response?.ok) {
setStatus(response?.error || "Could not load settings.", "error");
return;
}
elements.apiUrl.value = response.apiUrl;
elements.minimumScoreInput.value = response.minimumScore;
elements.maxItems.value = response.maxItems;
}
async function saveSettings() {
const response = await sendMessage({
type: "SAVE_SETTINGS",
settings: {
apiUrl: elements.apiUrl.value,
minimumScore: elements.minimumScoreInput.value,
maxItems: elements.maxItems.value,
},
});
if (!response?.ok) {
setStatus(response?.error || "Could not save settings.", "error");
return;
}
setStatus("Settings saved.", "success");
}
async function testConnection() {
setStatus("Testing local API connection...");
const response = await sendMessage({ type: "TEST_CONNECTION" });
if (!response?.ok) {
setStatus(response?.error || "API connection failed.", "error");
return;
}
setStatus("Local NLP API is ready and calibrated.", "success");
}
elements.scanButton.addEventListener("click", runScan);
elements.clearButton.addEventListener("click", clearHighlights);
elements.saveSettings.addEventListener("click", saveSettings);
elements.testConnection.addEventListener("click", testConnection);
loadSettings();