Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
keepcode.zip
bugs.md
bugs.md
TODO.md
23 changes: 15 additions & 8 deletions content.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,24 @@ async function getProblemData() {
function waitForContentAndStore() {
const observer = new MutationObserver(async () => {
const titleEl = document.querySelector("div");
if (titleEl && titleEl.textContent.trim()) {
if (titleEl?.textContent.trim()) {
observer.disconnect();
const data = await getProblemData();
if (!data) return; // Don't store invalid/undefined problems
browser.storage.local.set({ [data.slug]: data }).then(() => {
console.log("Saved to storage:", data);
}).catch((err) => {
console.error("Storage error:", err);
// Only update non-status fields, always preserve status/solvedAt
browser.storage.local.get(data.slug).then((existing) => {
const prev = existing[data.slug] || {};
// Always preserve status and solvedAt if they exist
if (prev.status) data.status = prev.status;
if (prev.solvedAt) data.solvedAt = prev.solvedAt;
browser.storage.local.set({ [data.slug]: data }).then(() => {
console.log("Saved to storage (non-status fields updated):", data);
}).catch((err) => {
console.error("Storage error:", err);
});
// Start watching for submission result after we have the slug
waitForSubmissionResult(data.slug);
});
// Start watching for submission result after we have the slug
waitForSubmissionResult(data.slug);
}
});

Expand All @@ -65,7 +72,7 @@ function waitForContentAndStore() {
function waitForSubmissionResult(slug) {
const observer = new MutationObserver(() => {
const resultEl = document.querySelector('span[data-e2e-locator="submission-result"]');
if (resultEl && resultEl.textContent.includes("Accepted")) {
if (resultEl?.textContent.includes("Accepted")) {
console.log("✅ Accepted detected via submission result!");

browser.storage.local.get(slug).then((existing) => {
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"manifest_version": 2,
"name": "KeepCode",
"version": "0.2.0",
"description": "Focus on answering problems, let KeepCode handle the rest",
"version": "0.2.1",
"description": "Prep smarter for coding interviews: KeepCode tracks your LeetCode progress so you can focus on solving.",
"permissions": ["storage", "tabs", "activeTab", "https://leetcode.com/*"],
"background": {
"scripts": ["background.js"],
Expand Down
90 changes: 49 additions & 41 deletions popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,53 @@ document.addEventListener("DOMContentLoaded", () => {
return;
}

browser.runtime
.sendMessage({ type: "GET_PROBLEM_DATA", slug })
.then((data) => {
const container = document.getElementById("popupContent");
if (!data) {
container.innerHTML = "<p>To track a problem please visit a leetcode problem page</p>";
return;
}
const container = document.getElementById("popupContent");

container.innerHTML = "";
const titleEl = document.createElement("h3");
titleEl.textContent = `Problem: ${data.title}`;
const diffEl = document.createElement("p");
diffEl.textContent = `Difficulty: ${data.difficulty} `;
const statusEl = document.createElement("p");
statusEl.id = "status";
const strongEl = document.createElement("strong");
strongEl.textContent = data.status || "Unsolved";
statusEl.appendChild(strongEl);
container.appendChild(titleEl);
container.appendChild(diffEl);
container.appendChild(statusEl);

// Listen for problem solved message
browser.runtime.onMessage.addListener((msg) => {
if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) {
const statusEl = document.getElementById("status");
if (statusEl) statusEl.textContent = "Solved ✅";
}
});
})
.catch((err) => {
document.getElementById("popupContent").innerHTML = "<p>Unable to read this page</p>";
console.error(err);
browser.storage.local.get(slug).then((result) => {
let data = result[slug];
if (data) {
renderCurrentProblem(data);
} else {

browser.runtime
.sendMessage({ type: "GET_PROBLEM_DATA", slug })
.then((data) => {
if (data) {
renderCurrentProblem(data);
} else {
container.innerHTML = "<p>To track a problem please visit a leetcode problem page</p>";
}
})
.catch((err) => {
container.innerHTML = "<p>Unable to read this page.</p>";
console.error(err);
});
}
});

function renderCurrentProblem(data) {
container.innerHTML = "";
const titleEl = document.createElement("h3");
titleEl.textContent = `Problem: ${data.title}`;
const diffEl = document.createElement("p");
diffEl.textContent = `Difficulty: ${data.difficulty} `;
const statusEl = document.createElement("p");
statusEl.id = "status";
const strongEl = document.createElement("strong");
strongEl.textContent = data.status || "Unsolved";
statusEl.appendChild(strongEl);
container.appendChild(titleEl);
container.appendChild(diffEl);
container.appendChild(statusEl);

// Listen for problem solved message
browser.runtime.onMessage.addListener((msg) => {
if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) {
const statusEl = document.getElementById("status");
if (statusEl) statusEl.textContent = "Solved ✅";
}
});
}
});
});

Expand All @@ -62,10 +74,9 @@ document.addEventListener("DOMContentLoaded", () => {
p.difficulty !== "Unknown Difficulty" &&
p.status === "Solved"
);
// Sort by solvedAt descending (most recent first)

problems.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0));

// Collect all unique tags (flattened)
const tagSet = new Set();
problems.forEach((p) => {
if (Array.isArray(p.tags)) {
Expand Down Expand Up @@ -94,7 +105,6 @@ document.addEventListener("DOMContentLoaded", () => {
}
}

// Render problems (filtered and limited)
function renderProblems() {
const list = document.getElementById("solvedList");
list.innerHTML = "";
Expand All @@ -115,7 +125,7 @@ document.addEventListener("DOMContentLoaded", () => {
const difficultyClass = problem.difficulty
? problem.difficulty.toLowerCase()
: "";
// Use DOM methods instead of innerHTML for safety

const link = document.createElement("a");
link.href = problem.url;
link.target = "_blank";
Expand Down Expand Up @@ -148,12 +158,11 @@ document.addEventListener("DOMContentLoaded", () => {
renderProblems();
});

// View All link opens options page
const viewAllLink = document.getElementById("viewAllLink");
if (viewAllLink) {
viewAllLink.addEventListener("click", (e) => {
e.preventDefault();
if (browser.runtime && browser.runtime.openOptionsPage) {
if (browser.runtime?.openOptionsPage) {
browser.runtime.openOptionsPage();
} else {
window.open("../options/options.html", "_blank");
Expand All @@ -166,10 +175,9 @@ document.addEventListener("DOMContentLoaded", () => {
const optionsBtn = document.getElementById("optionsBtn");
if (optionsBtn) {
optionsBtn.addEventListener("click", () => {
if (browser.runtime && browser.runtime.openOptionsPage) {
if (browser.runtime?.openOptionsPage) {
browser.runtime.openOptionsPage();
} else {
// fallback for browsers that don't support openOptionsPage
window.open("../options/options.html", "_blank");
}
});
Expand Down