Skip to content
Open
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
4 changes: 4 additions & 0 deletions LeetCode-Token-Viewer/icons/dropdown-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion LeetCode-Token-Viewer/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "Merge your LeetCode session and CSRF token",
"version": "1.0",
"manifest_version": 3,
"permissions": ["cookies", "activeTab", "scripting"],
"permissions": ["cookies", "activeTab", "scripting","storage"],
"host_permissions": ["https://leetcode.com/*"],
"action": {
"default_popup": "popup.html",
Expand Down
180 changes: 159 additions & 21 deletions LeetCode-Token-Viewer/popup.html
Original file line number Diff line number Diff line change
@@ -1,73 +1,211 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>LeetCode Token Viewer</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');

body {
font-family: "Segoe UI", sans-serif;
background: #f5f7fa;
font-family: "Share Tech Mono", monospace;
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 280px;
width: 320px;
transition: background 0.3s ease, color 0.3s ease;
}

/* === DARK THEME === */
body.dark {
background: radial-gradient(circle at top, #0a0a0a 0%, #000 100%);
color: #00ff9d;
}

body.dark h1, body.dark .token-box, body.dark {
color: #00ff9d;
}

/* === LIGHT THEME === */
body.light {
background: #f7f7f7;
color: #004422;
}

body.light h1, body.light .token-box, body.light {
color: #004422;
}

h1 {
font-size: 16px;
margin-bottom: 10px;
color: #333;
margin-bottom: 12px;
letter-spacing: 1px;
}

.token-box {
background: #fff;
border: 1px solid #ccc;
border-radius: 8px;
padding: 15px;
width: 100%;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
word-break: break-all;
font-size: 13px;
color: #444;
min-height: 60px;
text-align: center;
transition: 0.3s ease;
}

.label {
font-weight: bold;
color: #000;
body.dark .token-box {
background: rgba(0, 20, 0, 0.85);
border: 1px solid #00ff9d33;
box-shadow: 0 0 10px #00ff9d33, inset 0 0 6px #00ff9d22;
}

body.light .token-box {
background: #fff;
border: 1px solid #ccc;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

button {
margin-top: 15px;
background: #007bff;
color: white;
margin-top: 12px;
border: none;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
letter-spacing: 0.5px;
transition: 0.2s;
color:#004422
}

body.dark button {
background: linear-gradient(90deg, #00ff9d, #00cc66);
box-shadow: 0 0 8px #00ff9d55;
}

body.light button {
background: linear-gradient(90deg, #008b45, #00b96a);
box-shadow: 0 0 6px #00b96a55;
}

button:hover {
background: #0056b3;
transform: translateY(-1px);
}

#container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}

/* === Toggle Switch === */
.switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
}

.switch input {
opacity: 0;
width: 0;
height: 0;
}

.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 20px;
transition: 0.3s;
}

.slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: white;
border-radius: 50%;
transition: 0.3s;
}

input:checked + .slider {
background-color: #00cc66;
}

input:checked + .slider:before {
transform: translateX(16px);
}

/* === Button Row === */
.button-row {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 12px;
width: 100%;
}

.button-row button img.arrow {
width: 14px;
height: 14px;
margin-left: 6px;
vertical-align: middle;
transition: transform 0.3s ease;
}

.button-row button.active img.arrow {
transform: rotate(180deg);
}

#status {
margin-top: 10px;
margin-top: 8px;
font-size: 12px;
color: green;
text-align: center;
opacity: 0.8;
}

#themeLabel {
font-size: 12px;
margin-left: 6px;
}
</style>
</head>
<body>
<h1>🔐 LeetCode Token Viewer</h1>
<div id="container">
<h1>LeetCode Token Viewer</h1>
<div class="theme-toggle">
<label class="switch">
<input type="checkbox" id="themeToggle" />
<span class="slider"></span>
</label>
<span id="themeLabel">🌙 Dark</span>
</div>
</div>

<div id="token" class="token-box">Fetching tokens...</div>
<button id="copy">Copy Merge Token</button>

<!-- ✅ Button row for Show More + Copy -->
<div class="button-row">
<button id="copy">Copy Merge Token</button>
</div>

<div id="status"></div>

<!-- External JS -->
<script src="theme.js"></script>
<script src="popup.js"></script>
</body>
</html>
55 changes: 40 additions & 15 deletions LeetCode-Token-Viewer/popup.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
async function getLeetCodeTokens() {
try {
const cookies = await chrome.cookies.getAll({ domain: "leetcode.com" });
const session = cookies.find(c => c.name === "LEETCODE_SESSION");
const csrftoken = cookies.find(c => c.name === "csrftoken");

const tokenBox = document.getElementById("token");
const status = document.getElementById("status");
const copyBtn = document.getElementById("copy");
const buttonRow = document.querySelector(".button-row"); // flex container for buttons

if (session && csrftoken) {
const encode = (str) => {
const lenStr = str.length.toString().padStart(4, '0');
return lenStr + str;
};
const cookies = await chrome.cookies.getAll({ domain: "leetcode.com" });
const session = cookies.find(c => c.name === "LEETCODE_SESSION");
const csrftoken = cookies.find(c => c.name === "csrftoken");

if (session && csrftoken) {
const encode = (str) => str.length.toString().padStart(4, "0") + str;
const combinedToken = encode(session.value) + encode(csrftoken.value);

const formatted = `
<div><span class="label">Merge Token:</span> ${combinedToken}</div>
`;
tokenBox.innerHTML = formatted;
// Remove loading effect smoothly
tokenBox.classList.remove("loading");
tokenBox.style.opacity = "0";
tokenBox.textContent = `Merge Token: ${combinedToken}`;
setTimeout(() => (tokenBox.style.opacity = "1"), 50);

// Apply truncation
tokenBox.style.display = "-webkit-box";
tokenBox.style.webkitLineClamp = "5";
tokenBox.style.webkitBoxOrient = "vertical";
tokenBox.style.overflow = "hidden";

// Add Show More button only if token is long
if (combinedToken.length > 200) {
const toggleBtn = document.createElement("button");
toggleBtn.id = "toggleBtn";
toggleBtn.innerHTML = 'Show More &#9662;'; // ▼ symbol

// Append it to the button row (flex row)
if (buttonRow) buttonRow.insertBefore(toggleBtn, copyBtn);

let expanded = false;
toggleBtn.addEventListener("click", () => {
expanded = !expanded;
tokenBox.style.webkitLineClamp = expanded ? "unset" : "5";
toggleBtn.innerHTML = expanded ? "Show Less &#9652;" : "Show More &#9662;"; // ▲ symbol
});
}

// Copy handler
copyBtn.onclick = () => {
navigator.clipboard.writeText(combinedToken);
status.textContent = "✅ Merge token copied!";
setTimeout(() => status.textContent = "", 2000);
setTimeout(() => (status.textContent = ""), 2000);
};
} else {
tokenBox.classList.remove("loading");
tokenBox.textContent = "⚠️ Please log in to leetcode.com first.";
copyBtn.disabled = true;
}
} catch (err) {
console.error("Error fetching cookies:", err);
document.getElementById("token").textContent = "Error fetching tokens.";
const tokenBox = document.getElementById("token");
tokenBox.classList.remove("loading");
tokenBox.textContent = "Error fetching tokens.";
}
}

Expand Down
21 changes: 21 additions & 0 deletions LeetCode-Token-Viewer/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const themeToggle = document.getElementById("themeToggle");
const themeLabel = document.getElementById("themeLabel");

// Load saved theme or default to dark
document.body.classList.add(localStorage.getItem("theme") || "dark");
themeToggle.checked = document.body.classList.contains("light");
themeLabel.textContent = themeToggle.checked ? "Light" : "Dark";

themeToggle.addEventListener("change", () => {
if (themeToggle.checked) {
document.body.classList.remove("dark");
document.body.classList.add("light");
themeLabel.textContent = "Light";
localStorage.setItem("theme", "light");
} else {
document.body.classList.remove("light");
document.body.classList.add("dark");
themeLabel.textContent = "Dark";
localStorage.setItem("theme", "dark");
}
});