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 96e419a

Browse files
authored
Merge pull request #66 from zubyj/fix-dynamic-routing
Fix Dynamic Routing & Visual Update for Leetcode tabs
2 parents b0aa327 + 3e8c970 commit 96e419a

File tree

9 files changed

+1088
-394
lines changed

9 files changed

+1088
-394
lines changed

‎manifest.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
},
1515
"permissions": [
1616
"storage",
17-
"tabs"
17+
"tabs",
18+
"webNavigation"
1819
],
1920
"host_permissions": [
2021
"https://api.leetcodeapp.com/*"
@@ -33,7 +34,8 @@
3334
],
3435
"matches": [
3536
"https://leetcode.com/problems/*"
36-
]
37+
],
38+
"run_at": "document_end"
3739
}
3840
],
3941
"web_accessible_resources": [

‎src/background/background.ts

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,39 +70,104 @@ chrome.runtime.onMessage.addListener((request) => {
7070
}
7171
});
7272

73+
// Keep track of the last state to avoid duplicate updates
74+
let lastState = {
75+
problemPath: '',
76+
view: '', // 'problem' or 'solutions'
77+
lastPathname: '', // Track full pathname to detect real navigation
78+
lastUrl: '', // Track full URL to detect refreshes
79+
lastUpdateTime: 0 // Track time of last update to prevent rapid re-triggers
80+
};
81+
7382
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
74-
if (changeInfo.status==='complete'&&tab.url) {
83+
if (tab.url) {
7584
const url = tab.url;
7685
let problemUrl = /^https:\/\/leetcode\.com\/problems\/.*\/?/;
86+
87+
// Check if this is a leetcode problem page
7788
if (url.match(problemUrl)) {
78-
chrome.storage.local.get(['currentLeetCodeProblemTitle', 'descriptionTabUpdated', 'solutionsTabUpdated'], (result) => {
79-
let lastTitle = result.currentLeetCodeProblemTitle || '';
80-
let descriptionTabUpdated = result.descriptionTabUpdated || false;
81-
let solutionsTabUpdated = result.solutionsTabUpdated || false;
82-
if (tab.title !== lastTitle) {
89+
// Extract the problem path from the URL
90+
const problemPath = url.match(/\/problems\/([^/]+)/)?.[1];
91+
const pathname = new URL(url).pathname;
92+
93+
// Determine the current view - now only distinguishing between problem view and solutions
94+
let currentView = url.includes('/solutions') ? 'solutions' : 'problem';
95+
96+
// Only trigger updates on actual page loads or problem changes
97+
const isPageLoad = changeInfo.status === 'complete';
98+
const isProblemChange = problemPath !== lastState.problemPath;
99+
const isViewChange = currentView !== lastState.view;
100+
101+
// Check if this is a video navigation within solutions
102+
const isInternalSolutionsNavigation =
103+
currentView === 'solutions' &&
104+
lastState.view === 'solutions' &&
105+
problemPath === lastState.problemPath;
106+
107+
// Detect actual page refresh vs internal navigation
108+
const isActualRefresh =
109+
url === lastState.lastUrl &&
110+
isPageLoad &&
111+
changeInfo.url === undefined &&
112+
!isInternalSolutionsNavigation &&
113+
Date.now() - lastState.lastUpdateTime > 1000;
114+
115+
const isRealNavigation =
116+
!isInternalSolutionsNavigation &&
117+
((pathname !== lastState.lastPathname || isViewChange) &&
118+
!pathname.includes('playground') &&
119+
!pathname.includes('editor') &&
120+
!pathname.includes('interpret-solution') &&
121+
!pathname.includes('submissions'));
122+
123+
// Update last URL and time
124+
if (!isInternalSolutionsNavigation) {
125+
lastState.lastUrl = url;
126+
}
127+
128+
// Only update if there's a real navigation, problem change, or actual refresh
129+
if ((isProblemChange || (isViewChange && isRealNavigation) || isActualRefresh) && problemPath) {
130+
console.log(`State change detected - ${
131+
isProblemChange ? 'New Problem' :
132+
isViewChange ? 'View Changed' :
133+
isActualRefresh ? 'Page Refresh' :
134+
'Page Load'
135+
}`);
136+
137+
// Update last state
138+
lastState.problemPath = problemPath;
139+
lastState.view = currentView;
140+
lastState.lastPathname = pathname;
141+
lastState.lastUpdateTime = Date.now();
142+
143+
// Reset flags only on problem change or actual refresh
144+
if (isProblemChange || isActualRefresh) {
83145
chrome.storage.local.set({
146+
'currentLeetCodeProblem': problemPath,
84147
'currentLeetCodeProblemTitle': tab.title,
85148
'descriptionTabUpdated': false,
86149
'solutionsTabUpdated': false
87150
});
88-
// If the title has changed, we reset both flags
89-
descriptionTabUpdated = false;
90-
solutionsTabUpdated = false;
91151
}
92152

93-
let descriptionUrl = /^https:\/\/leetcode\.com\/problems\/.*\/(description\/)?/;
94-
if (!descriptionTabUpdated && url.match(descriptionUrl)) {
95-
chrome.storage.local.set({ 'descriptionTabUpdated': true });
96-
chrome.tabs.sendMessage(tabId, { action: 'updateDescription', title: tab.title || 'title' });
97-
}
153+
// Get current state
154+
chrome.storage.local.get(['descriptionTabUpdated', 'solutionsTabUpdated'], (result) => {
155+
let descriptionTabUpdated = result.descriptionTabUpdated || false;
156+
let solutionsTabUpdated = result.solutionsTabUpdated || false;
98157

99-
let solutionsUrl = /^https:\/\/leetcode\.com\/problems\/.*\/solutions\/?/;
100-
if (url.match(solutionsUrl)) {
101-
chrome.storage.local.set({ 'solutionsTabUpdated': true });
102-
chrome.tabs.sendMessage(tabId, { action: 'updateSolutions', title: tab.title || 'title' });
103-
}
104-
});
158+
// Always update description tab when in problem view
159+
if (currentView === 'problem') {
160+
chrome.storage.local.set({ 'descriptionTabUpdated': true });
161+
chrome.tabs.sendMessage(tabId, { action: 'updateDescription', title: tab.title || 'title' });
162+
}
163+
164+
// Always update solutions tab when in solutions view
165+
if (currentView === 'solutions') {
166+
chrome.storage.local.set({ 'solutionsTabUpdated': true });
167+
chrome.tabs.sendMessage(tabId, { action: 'updateSolutions', title: tab.title || 'title' });
168+
}
169+
});
170+
}
105171
}
106172
}
107-
});
108-
173+
});

‎src/content-script/themeDetector.js

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
// Listen for messages from the background script or popup
22
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3-
if (request.action === 'detectTheme') {
4-
const theme = detectPageTheme();
5-
console.log(`Detecting theme: ${theme}`);
6-
sendResponse({ theme });
3+
if (request.action === 'detectTheme' || request.action === 'getTheme') {
4+
debouncedThemeDetection(sendResponse);
5+
return true; // Keep the message channel open for asynchronous response
76
}
8-
if (request.action === 'getTheme') {
9-
const theme = detectPageTheme();
10-
console.log(`Getting theme: ${theme}`);
11-
sendResponse({ theme });
12-
}
13-
return true; // Keep the message channel open for asynchronous response
147
});
158

169
// Function to detect the theme of the current LeetCode page
1710
function detectPageTheme() {
18-
console.log('Starting theme detection on leetcode page...');
19-
2011
// Force a quick check to see if this is a LeetCode page
2112
const url = window.location.href;
2213
const isLeetCodePage = url.includes('leetcode.com');
23-
console.log('Is LeetCode page:', isLeetCodePage, url);
2414

2515
// Method 1: Check for LeetCode's light theme indicator (most reliable)
2616
// In light mode LeetCode specifically has a white background for these elements
@@ -30,11 +20,9 @@ function detectPageTheme() {
3020

3121
if (mainContent) {
3222
const bgColor = window.getComputedStyle(mainContent).backgroundColor;
33-
console.log('Main content background color:', bgColor);
3423

3524
// LeetCode light mode has white or very light background
3625
if (bgColor.includes('255, 255, 255') || bgColor.includes('rgb(255, 255, 255)')) {
37-
console.log('Theme detected from content: LIGHT (white background)');
3826
return 'light';
3927
}
4028
}
@@ -45,79 +33,48 @@ function detectPageTheme() {
4533
// If the dark mode switcher has a sun icon, it means we're in light mode
4634
const sunIcon = darkModeSwitcher.querySelector('svg[data-icon="sun"]');
4735
if (sunIcon) {
48-
console.log('Theme detected from dark mode switcher: LIGHT (sun icon visible)');
4936
return 'light';
5037
}
5138
// If the dark mode switcher has a moon icon, it means we're in dark mode
5239
const moonIcon = darkModeSwitcher.querySelector('svg[data-icon="moon"]');
5340
if (moonIcon) {
54-
console.log('Theme detected from dark mode switcher: dark (moon icon visible)');
5541
return 'dark';
5642
}
5743
}
5844

5945
// Method 3: Check HTML tag class for 'dark' or 'light'
6046
const htmlElement = document.documentElement;
6147
if (htmlElement.classList.contains('dark')) {
62-
console.log('Theme detected from HTML class: dark');
6348
return 'dark';
6449
} else if (htmlElement.classList.contains('light')) {
65-
console.log('Theme detected from HTML class: LIGHT');
6650
return 'light';
6751
}
6852

6953
// Method 4: Check data-theme attribute
7054
const dataTheme = htmlElement.getAttribute('data-theme');
7155
if (dataTheme === 'dark') {
72-
console.log('Theme detected from data-theme: dark');
7356
return 'dark';
7457
} else if (dataTheme === 'light') {
75-
console.log('Theme detected from data-theme: LIGHT');
7658
return 'light';
7759
}
7860

7961
// Method 5: Check header/navbar background color (very reliable for LeetCode)
8062
const header = document.querySelector('header') || document.querySelector('nav');
8163
if (header) {
8264
const headerBgColor = window.getComputedStyle(header).backgroundColor;
83-
console.log('Header background color:', headerBgColor);
8465

8566
// LeetCode light mode header is usually white or very light
8667
if (headerBgColor.includes('255, 255, 255') ||
8768
headerBgColor.includes('rgb(255, 255, 255)') ||
8869
!isColorDark(headerBgColor)) {
89-
console.log('Theme detected from header: LIGHT');
9070
return 'light';
9171
} else {
92-
console.log('Theme detected from header: dark');
9372
return 'dark';
9473
}
9574
}
9675

97-
// Method 6: Check the code editor background (LeetCode specific)
98-
const codeEditor = document.querySelector('.monaco-editor');
99-
if (codeEditor) {
100-
const editorBgColor = window.getComputedStyle(codeEditor).backgroundColor;
101-
console.log('Code editor background color:', editorBgColor);
102-
if (isColorDark(editorBgColor)) {
103-
console.log('Theme detected from code editor: dark');
104-
return 'dark';
105-
} else {
106-
console.log('Theme detected from code editor: LIGHT');
107-
return 'light';
108-
}
109-
}
110-
111-
// Method 7: Check background color to determine if dark or light
112-
const backgroundColor = window.getComputedStyle(document.body).backgroundColor;
113-
console.log('Body background color:', backgroundColor);
114-
if (isColorDark(backgroundColor)) {
115-
console.log('Theme detected from body bg: dark');
116-
return 'dark';
117-
} else {
118-
console.log('Theme detected from body bg: LIGHT');
119-
return 'light';
120-
}
76+
// Default to dark if can't detect
77+
return 'dark';
12178
}
12279

12380
// Helper function to determine if a color is dark based on luminance
@@ -140,4 +97,31 @@ function isColorDark(color) {
14097

14198
// Return true for dark colors (lower luminance)
14299
return luminance < 0.5;
143-
}
100+
}
101+
102+
// Debounce function to limit how often a function can be called
103+
function debounce(func, wait) {
104+
let timeout;
105+
return function executedFunction(...args) {
106+
const later = () => {
107+
clearTimeout(timeout);
108+
func(...args);
109+
};
110+
clearTimeout(timeout);
111+
timeout = setTimeout(later, wait);
112+
};
113+
}
114+
115+
// Store last detected theme to prevent unnecessary updates
116+
let lastDetectedTheme = null;
117+
118+
// Debounced theme detection function
119+
const debouncedThemeDetection = debounce((sendResponse) => {
120+
const theme = detectPageTheme();
121+
if (theme !== lastDetectedTheme) {
122+
lastDetectedTheme = theme;
123+
if (sendResponse) {
124+
sendResponse({ theme });
125+
}
126+
}
127+
}, 500);

0 commit comments

Comments
(0)

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