I’m developing a Chrome plugin called TextCondenser that lets users select text and get condensed versions. I’m using GPT API for the text processing but running into problems with API calls and pricing concerns. I need assistance debugging my implementation to get the condensing feature working properly.
How the plugin functions:
- User selects text on any webpage
- A “Condense Text” button shows up next to selection
- Clicking triggers API call to process the selected content
- Processed result appears in popup and gets stored in extension history
Most functionality is complete but I’m struggling with the text processing logic. Here are my source files:
service-worker.js
chrome.runtime.onInstalled.addListener(() => {
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
.catch((err) => console.error("Panel Setup Error:", err));
});
page-script.js
document.addEventListener("mouseup", function (evt) {
let highlightedText = window.getSelection().toString().trim();
let currentButton = document.getElementById("condense-btn");
if (!highlightedText) {
if (currentButton) currentButton.remove();
return;
}
if (!currentButton) {
currentButton = document.createElement("button");
currentButton.id = "condense-btn";
currentButton.innerText = "Condense Text";
currentButton.style.position = "absolute";
currentButton.style.zIndex = "9999";
currentButton.style.background = "#2196f3";
currentButton.style.color = "white";
currentButton.style.border = "none";
currentButton.style.padding = "10px";
currentButton.style.cursor = "pointer";
currentButton.style.borderRadius = "4px";
currentButton.style.fontSize = "12px";
document.body.appendChild(currentButton);
}
let selection = window.getSelection().getRangeAt(0).getBoundingClientRect();
currentButton.style.top = `${selection.bottom + window.scrollY + 10}px`;
currentButton.style.left = `${selection.left + window.scrollX}px`;
currentButton.onclick = async function () {
currentButton.innerText = "Processing...";
let result = await processText(highlightedText);
alert("Condensed Version:\n" + result);
currentButton.remove();
};
document.addEventListener("click", function hideBtn(evt) {
if (!currentButton.contains(evt.target)) {
currentButton.remove();
document.removeEventListener("click", hideBtn);
}
});
});
async function processText(content) {
const token = "Your_API_Token_Here";
const endpoint = "https://api.openai.com/v1/chat/completions";
try {
const apiResponse = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: `Please condense this text: ${content}` }],
max_tokens: 150
})
});
if (!apiResponse.ok) {
let errorDetails = await apiResponse.text();
console.error("Request Failed:", errorDetails);
throw new Error("Processing failed: " + errorDetails);
}
const responseData = await apiResponse.json();
return responseData.choices?.[0]?.message?.content || "Processing failed.";
} catch (err) {
console.error("Text processing error:", err);
return "Unable to process text.";
}
}
extension.json
{
"manifest_version": 3,
"name": "TextCondenser",
"version": "1.0.0",
"description": "Condense and store text snippets quickly.",
"permissions": ["storage", "sidePanel"],
"action": {
"default_icon": {
"48": "assets/icon48.png",
"128": "assets/icon128.png"
}
},
"side_panel": {
"default_path": "panel.html"
},
"background": {
"service_worker": "service-worker.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["page-script.js"],
"run_at": "document_end"
}
]
}
panel.html
<!DOCTYPE html>
<html>
<head>
<title>Text History</title>
<style>
body {
font-family: Verdana, sans-serif;
margin: 15px;
width: 280px;
}
.history-entry {
border-bottom: 2px solid #eee;
padding: 15px;
margin-bottom: 15px;
}
.remove-btn {
background-color: #e91e63;
color: white;
border: none;
padding: 8px;
cursor: pointer;
border-radius: 3px;
}
</style>
</head>
<body>
<h3>Condensed Text History</h3>
<div id="history-container"></div>
<script src="panel-script.js"></script>
</body>
</html>
panel-script.js
document.addEventListener("DOMContentLoaded", function () {
const historyContainer = document.getElementById("history-container");
function refreshHistory() {
chrome.storage.local.get(["textHistory"], function (data) {
historyContainer.innerHTML = "";
const history = data.textHistory || [];
history.forEach((entry, idx) => {
showHistoryEntry(entry, idx);
});
});
}
function showHistoryEntry(entry, idx) {
const entryDiv = document.createElement("div");
entryDiv.classList.add("history-entry");
entryDiv.innerHTML = `
<p>${entry}</p>
<button class="remove-btn" data-index="${idx}">Remove</button>
`;
entryDiv.querySelector(".remove-btn").addEventListener("click", function () {
removeHistoryEntry(idx);
});
historyContainer.appendChild(entryDiv);
}
function removeHistoryEntry(idx) {
chrome.storage.local.get(["textHistory"], function (data) {
let history = data.textHistory || [];
history.splice(idx, 1);
chrome.storage.local.set({ textHistory: history }, function () {
refreshHistory();
});
});
}
refreshHistory();
});