Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 172ca7a

Browse files
authored
Merge pull request #64 from zubyj/auto-theme
Auto Theme Support: Match LeetCode’s Light/Dark Mode Automatically
2 parents 4e06b07 + 1f983d1 commit 172ca7a

File tree

8 files changed

+311
-21
lines changed

8 files changed

+311
-21
lines changed

‎src/background/background.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ chrome.runtime.onInstalled.addListener(() => {
4040
});
4141

4242
// Load default settings
43-
chrome.storage.local.set({ fontSize: 14 });
43+
chrome.storage.local.set({ fontSize: 12 });// Default to small display size
4444
chrome.storage.local.set({ showExamples: true });
4545
chrome.storage.local.set({ showDifficulty: true });
4646
chrome.storage.local.set({ showRating: true });
4747
chrome.storage.local.set({ showCompanyTags: true });
48-
chrome.storage.local.set({ isDarkTheme: true });
48+
// Set default theme to auto mode and default to dark
49+
chrome.storage.local.set({ isDarkTheme: true }); // Default to dark theme
50+
chrome.storage.local.set({ themeMode: 'auto' });
4951
});
5052

5153
chrome.runtime.onMessage.addListener((request) => {

‎src/content-script/themeDetector.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Listen for messages from the background script or popup
2+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3+
if (request.action === 'detectTheme') {
4+
const theme = detectPageTheme();
5+
sendResponse({ theme });
6+
}
7+
return true; // Keep the message channel open for asynchronous response
8+
});
9+
10+
// Function to detect the theme of the current LeetCode page
11+
function detectPageTheme() {
12+
// Method 1: Check HTML tag class for 'dark' or 'light'
13+
const htmlElement = document.documentElement;
14+
if (htmlElement.classList.contains('dark')) {
15+
return 'dark';
16+
} else if (htmlElement.classList.contains('light')) {
17+
return 'light';
18+
}
19+
20+
// Method 2: Check data-theme attribute
21+
const dataTheme = htmlElement.getAttribute('data-theme');
22+
if (dataTheme === 'dark') {
23+
return 'dark';
24+
} else if (dataTheme === 'light') {
25+
return 'light';
26+
}
27+
28+
// Method 3: Check background color to determine if dark or light
29+
const backgroundColor = window.getComputedStyle(document.body).backgroundColor;
30+
if (isColorDark(backgroundColor)) {
31+
return 'dark';
32+
}
33+
34+
// Default to dark if we can't determine
35+
return 'dark';
36+
}
37+
38+
// Helper function to determine if a color is dark based on luminance
39+
function isColorDark(color) {
40+
// Extract RGB values
41+
const rgb = color.match(/\d+/g);
42+
if (!rgb || rgb.length < 3) return true; // Default to dark if can't extract
43+
44+
// Calculate relative luminance
45+
const r = parseInt(rgb[0]) / 255;
46+
const g = parseInt(rgb[1]) / 255;
47+
const b = parseInt(rgb[2]) / 255;
48+
49+
// Weighted luminance formula (human eye is more sensitive to green)
50+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
51+
52+
// Return true for dark colors (lower luminance)
53+
return luminance < 0.5;
54+
}

‎src/content-script/update-description-tab.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,61 @@ interface Problem {
2020
// Add other properties as needed
2121
}
2222

23+
// Detect LeetCode's theme and set extension theme accordingly
24+
function detectAndSyncTheme() {
25+
chrome.storage.local.get(['themeMode'], (result) => {
26+
// Only sync theme if in auto mode
27+
if (result.themeMode !== 'auto') {
28+
return;
29+
}
30+
31+
// Get the current LeetCode theme from HTML tag
32+
const htmlElement = document.documentElement;
33+
const leetcodeTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
34+
35+
// Set the extension theme based on LeetCode's theme
36+
chrome.storage.local.set({
37+
isDarkTheme: leetcodeTheme === 'dark'
38+
});
39+
40+
console.log(`Theme auto-detected: ${leetcodeTheme}`);
41+
42+
// Set up observer for future theme changes
43+
observeThemeChanges();
44+
});
45+
}
46+
47+
// Observe theme changes in LeetCode and update extension theme
48+
function observeThemeChanges() {
49+
chrome.storage.local.get(['themeMode'], (result) => {
50+
// Only observe changes if theme mode is set to 'auto'
51+
if (result.themeMode !== 'auto') {
52+
return;
53+
}
54+
55+
const htmlElement = document.documentElement;
56+
57+
// Create a new observer
58+
const observer = new MutationObserver((mutations) => {
59+
mutations.forEach((mutation) => {
60+
if (mutation.attributeName === 'class') {
61+
const leetcodeTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
62+
chrome.storage.local.set({
63+
isDarkTheme: leetcodeTheme === 'dark'
64+
});
65+
console.log(`Theme changed to: ${leetcodeTheme}`);
66+
}
67+
});
68+
});
69+
70+
// Start observing
71+
observer.observe(htmlElement, {
72+
attributes: true,
73+
attributeFilter: ['class']
74+
});
75+
});
76+
}
77+
2378
// show the leetcode difficulty if the user has enabled it in the settings
2479
function showDifficulty() {
2580
chrome.storage.local.get(['showDifficulty'], (result) => {
@@ -191,11 +246,21 @@ function loadCompanyTags(problemTitle: string, companyTagContainer: HTMLElement)
191246
return companyTagContainer;
192247
}
193248

194-
chrome.runtime.onMessage.addListener((request) => {
249+
chrome.runtime.onMessage.addListener((request,sender,sendResponse) => {
195250
if (request.action === 'updateDescription') {
251+
// Detect theme on first load of a problem page
252+
detectAndSyncTheme();
196253
showExamples();
197254
showCompanyTags(request.title.split('-')[0].trim());
198255
showDifficulty();
199256
showRating(request.title.split('-')[0].trim());
257+
} else if (request.action === 'getTheme') {
258+
// Return the current LeetCode theme
259+
const htmlElement = document.documentElement;
260+
const leetcodeTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
261+
sendResponse({ theme: leetcodeTheme });
200262
}
263+
264+
// Return true to indicate we will send a response asynchronously (needed for sendResponse)
265+
return true;
201266
});

‎src/popup/popup.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ pre[class*="language-"] {
330330
padding: calc(15px * var(--scale-factor)) 0;
331331
}
332332

333+
#theme-text {
334+
font-weight: 600;
335+
}
336+
333337
.video-container {
334338
display: none;
335339
border-radius: var(--border-radius);

‎src/popup/popup.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,14 +250,34 @@ async function main(): Promise<void> {
250250
initializeScaleFactor();
251251
await loadStoredData();
252252

253-
// get name of current tab and set info message
253+
// get name of current tab and set info message, also check theme if in auto mode
254254
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
255255
const tab = tabs[0];
256256
if (tab.url && tab.url.includes('leetcode.com/problems')) {
257257
chrome.storage.local.set({ 'currentLeetCodeProblemTitle': tab.title });
258258
if (tab.title && infoMessage) {
259259
infoMessage.textContent = tab.title.split('-')[0];
260260
}
261+
262+
// Check if we're in auto mode and need to sync theme
263+
chrome.storage.local.get(['themeMode'], (result) => {
264+
if (result.themeMode === 'auto') {
265+
// Send a message to detect theme
266+
chrome.tabs.sendMessage(
267+
tab.id as number,
268+
{ action: 'getTheme' },
269+
(response) => {
270+
if (!chrome.runtime.lastError && response && response.theme) {
271+
// Apply detected theme
272+
document.documentElement.setAttribute('data-theme', response.theme);
273+
chrome.storage.local.set({
274+
isDarkTheme: response.theme === 'dark'
275+
});
276+
}
277+
}
278+
);
279+
}
280+
});
261281
}
262282
});
263283

@@ -283,8 +303,9 @@ function initializeScaleFactor(): void {
283303
chrome.storage.local.get('fontSize', function (data) {
284304
if (data.fontSize) {
285305
let scaleFactor: number;
306+
const fontSize = data.fontSize.toString();
286307

287-
switch (data.fontSize) {
308+
switch (fontSize) {
288309
case '12':
289310
scaleFactor = 0.9;
290311
break;
@@ -297,6 +318,10 @@ function initializeScaleFactor(): void {
297318
}
298319

299320
document.documentElement.style.setProperty('--scale-factor', scaleFactor.toString());
321+
} else {
322+
// Default to small if not set
323+
document.documentElement.style.setProperty('--scale-factor', '0.9');
324+
chrome.storage.local.set({ fontSize: 12 });
300325
}
301326
});
302327
}

‎src/popup/settings.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</select>
2121
<button id="enable-dark-theme-btn" class="material-button settings-btn">
2222
<span id="theme-icon"></span>
23-
<span id="theme-text">Dark mode</span>
23+
<span>Display</span><span id="theme-text">Dark</span>
2424
</button>
2525
<button id="show-company-tags-btn" class="material-button settings-btn">
2626
<span id="show-company-tags-icon"> </span> Show Company Tags

‎src/popup/settings.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,45 @@ homeButton.onclick = () => {
1717
document.addEventListener('DOMContentLoaded', () => {
1818

1919
initializeTheme();
20+
21+
// Check active tab for theme if in auto mode
22+
chrome.storage.local.get(['themeMode'], (result) => {
23+
if (result.themeMode === 'auto') {
24+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
25+
if (tabs[0] && tabs[0].id && tabs[0].url && tabs[0].url.includes('leetcode.com/problems')) {
26+
chrome.tabs.sendMessage(
27+
tabs[0].id,
28+
{ action: 'getTheme' },
29+
(response) => {
30+
if (!chrome.runtime.lastError && response && response.theme) {
31+
// Apply detected theme
32+
document.documentElement.setAttribute('data-theme', response.theme);
33+
chrome.storage.local.set({
34+
isDarkTheme: response.theme === 'dark'
35+
});
36+
// Update UI
37+
const themeIcon = document.getElementById('theme-icon');
38+
const themeText = document.getElementById('theme-text');
39+
if (themeIcon && themeText) {
40+
themeIcon.textContent = '🔄';
41+
themeText.textContent = 'Auto';
42+
}
43+
}
44+
}
45+
);
46+
}
47+
});
48+
}
49+
});
50+
2051
document.getElementById('enable-dark-theme-btn')?.addEventListener('click', () => {
2152
toggleTheme();
2253
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
2354
chrome.tabs.sendMessage(tabs[0].id || 0, { action: 'updateDescription', title: tabs[0].title || 'title' });
2455
chrome.tabs.sendMessage(tabs[0].id || 0, { action: 'updateSolutions', title: tabs[0].title || 'title' });
2556
});
2657
});
58+
2759
chrome.storage.local.get(['showCompanyTags'], (result) => {
2860
const showCompanyTagsIcon = document.getElementById('show-company-tags-icon');
2961
if (showCompanyTagsIcon) showCompanyTagsIcon.textContent = result.showCompanyTags ? '✅' : '❌';
@@ -45,14 +77,19 @@ document.addEventListener('DOMContentLoaded', () => {
4577
const fontSizeSelect = document.getElementById('font-size-select') as HTMLSelectElement;
4678
chrome.storage.local.get('fontSize', function (data) {
4779
if (data.fontSize) {
48-
fontSizeSelect.value = data.fontSize;
49-
updateScaleFactor(data.fontSize);
80+
fontSizeSelect.value = data.fontSize.toString();
81+
updateScaleFactor(data.fontSize.toString());
82+
} else {
83+
// Default to small if not set
84+
fontSizeSelect.value = '12';
85+
updateScaleFactor('12');
86+
chrome.storage.local.set({ fontSize: 12 });
5087
}
5188
});
5289

5390
fontSizeSelect.onchange = function (event: Event) {
5491
const selectedFontSize = (event.target as HTMLInputElement).value;
55-
chrome.storage.local.set({ fontSize: selectedFontSize });
92+
chrome.storage.local.set({ fontSize: parseInt(selectedFontSize) });
5693
updateScaleFactor(selectedFontSize);
5794
};
5895

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /