Skip to content

Commit f9add1d

Browse files
authored
Merge pull request #3 from dzienisz/feat/new-design
refactor: modernize extension with improved UI, analysis engine and history tracking
2 parents ffb1b1f + d952a18 commit f9add1d

File tree

8 files changed

+458
-485
lines changed

8 files changed

+458
-485
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ The **CSR vs SSR Detector** is a Chrome extension that allows users to identify
1414

1515
The extension analyzes the content of the page to detect if it was rendered on the server or by the client. It then provides this information in a clear, easily accessible format within your Chrome browser.
1616

17+
## Sequence Diagram
18+
19+
![Sequence Diagram](sequence_diagram.png)
20+
1721
## Installation
1822

1923
1. Download the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/csr-vs-ssr-detector/fhiopdjeekafnhmfbcfoolhejdgjpkgg).

popupV2.js renamed to analyzer.js

Lines changed: 103 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,3 @@
1-
document.getElementById("analyze").addEventListener("click", () => {
2-
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
3-
// Execute the analysis function directly (embedded)
4-
chrome.scripting.executeScript(
5-
{
6-
target: { tabId: tabs[0].id },
7-
function: pageAnalyzer,
8-
},
9-
(results) => {
10-
if (results && results[0] && results[0].result) {
11-
const { renderType, confidence, indicators, detailedInfo } = results[0].result;
12-
13-
// Create enhanced result display
14-
let resultHTML = `
15-
<div style="border: 1px solid #ddd; border-radius: 8px; padding: 12px; margin-bottom: 10px;">
16-
<div style="margin-bottom: 8px;">
17-
<strong style="color: #2563eb;">Render Type:</strong>
18-
<br>
19-
<span style="font-weight: 600; color: ${getTypeColor(renderType)}">${renderType}</span>
20-
</div>
21-
<div style="margin-bottom: 8px;">
22-
<strong style="color: #2563eb;">Confidence:</strong>
23-
<span style="font-weight: 600;">${confidence}%</span>
24-
${getConfidenceBar(confidence)}
25-
</div>
26-
<div style="margin-bottom: 8px;">
27-
<strong style="color: #2563eb;">Analysis Score:</strong>
28-
<br>
29-
<span style="font-size: 12px; color: #666;">
30-
SSR: ${detailedInfo.ssrScore} | CSR: ${detailedInfo.csrScore}
31-
(${detailedInfo.ssrPercentage}% SSR)
32-
</span>
33-
</div>
34-
</div>
35-
36-
<div style="margin-bottom: 10px;">
37-
<strong style="color: #2563eb;">Key Indicators (${detailedInfo.totalIndicators}):</strong>
38-
<div style="margin-top: 4px; font-size: 13px; line-height: 1.4;">
39-
${indicators.map(indicator => `<span style="display: inline-block; background: #f1f5f9; padding: 2px 6px; margin: 2px 2px; border-radius: 5px; font-size: 11px;">${indicator}</span>`).join('')}
40-
</div>
41-
</div>
42-
`;
43-
44-
// Add framework detection if available
45-
if (detailedInfo.frameworks && detailedInfo.frameworks.length > 0) {
46-
resultHTML += `
47-
<div style="margin-bottom: 8px;">
48-
<strong style="color: #2563eb;">Detected Frameworks:</strong>
49-
<span style="font-weight:700;">${detailedInfo.frameworks.join(', ').toUpperCase()}</span>
50-
</div>
51-
`;
52-
}
53-
54-
// Add timing information if available
55-
if (detailedInfo.timing) {
56-
resultHTML += `
57-
<div style="margin-bottom: 8px;">
58-
<strong style="color: #2563eb;">Performance:</strong>
59-
<div style="font-size: 12px; color: #666; margin-top: 2px;">
60-
DOM Ready: ${detailedInfo.timing.domContentLoaded}ms
61-
${detailedInfo.timing.firstContentfulPaint ?
62-
` | FCP: ${detailedInfo.timing.firstContentfulPaint}ms` : ''}
63-
</div>
64-
</div>
65-
`;
66-
}
67-
68-
document.getElementById("result").innerHTML = resultHTML;
69-
} else {
70-
document.getElementById("result").innerHTML = `
71-
<div style="color: #dc2626; font-weight: 500;">
72-
❌ Analysis failed. Please try again.
73-
</div>
74-
`;
75-
}
76-
}
77-
);
78-
});
79-
});
80-
811
// Enhanced Page Analysis Module - Improved SSR vs CSR Detection
822
function pageAnalyzer() {
833
const indicators = [];
@@ -110,7 +30,10 @@ function pageAnalyzer() {
11030
nuxt: document.querySelector('#__nuxt, #__NUXT__') !== null,
11131
gatsby: document.querySelector('#___gatsby') !== null,
11232
sveltekit: document.querySelector('#svelte') !== null,
113-
astro: document.querySelector('[data-astro-island]') !== null
33+
astro: document.querySelector('[data-astro-island]') !== null,
34+
remix: document.querySelector('[data-remix-run]') !== null,
35+
qwik: document.querySelector('[q\\:container]') !== null,
36+
solidjs: document.querySelector('[data-solid]') !== null
11437
};
11538

11639
const foundFrameworks = Object.entries(frameworkMarkers)
@@ -202,6 +125,24 @@ function pageAnalyzer() {
202125
}
203126
}
204127

128+
// === STATIC SITE GENERATOR DETECTION ===
129+
const staticGeneratorMarkers = {
130+
jekyll: document.querySelector('meta[name="generator"][content*="Jekyll"]') !== null,
131+
hugo: document.querySelector('meta[name="generator"][content*="Hugo"]') !== null,
132+
eleventy: document.querySelector('meta[name="generator"][content*="Eleventy"]') !== null,
133+
hexo: document.querySelector('meta[name="generator"][content*="Hexo"]') !== null
134+
};
135+
136+
const foundGenerators = Object.entries(staticGeneratorMarkers)
137+
.filter(([_, found]) => found)
138+
.map(([generator, _]) => generator);
139+
140+
if (foundGenerators.length > 0) {
141+
ssrScore += 40;
142+
indicators.push(`${foundGenerators.join(', ')} static site generator detected (SSR)`);
143+
detailedInfo.generators = foundGenerators;
144+
}
145+
205146
// === PERFORMANCE TIMING ANALYSIS ===
206147
const performanceEntries = performance.getEntriesByType('navigation');
207148
if (performanceEntries.length > 0) {
@@ -212,16 +153,16 @@ function pageAnalyzer() {
212153
// Fast initial render suggests SSR
213154
if (domContentLoadedTime < 30) {
214155
ssrScore += 25;
215-
indicators.push("very fast DOM ready (DOM Ready)");
156+
indicators.push("very fast DOM ready (SSR)");
216157
} else if (domContentLoadedTime > 500) {
217158
csrScore += 20;
218-
indicators.push("slow DOM ready (DOM Ready)");
159+
indicators.push("slow DOM ready (CSR)");
219160
}
220161

221162
// FCP timing analysis
222163
if (firstContentfulPaint && firstContentfulPaint.startTime < 800) {
223164
ssrScore += 15;
224-
indicators.push("fast first contentful paint (FCP)");
165+
indicators.push("fast first contentful paint (SSR)");
225166
}
226167

227168
detailedInfo.timing = {
@@ -242,7 +183,7 @@ function pageAnalyzer() {
242183

243184
if (hasClientRouting) {
244185
csrScore += 20;
245-
indicators.push("client-side routing detected");
186+
indicators.push("client-side routing detected (CSR)");
246187
}
247188

248189
// === LOADING STATES AND PLACEHOLDERS ===
@@ -338,3 +279,80 @@ function getConfidenceBar(confidence) {
338279
</div>
339280
`;
340281
}
282+
283+
// Helper function to create results HTML
284+
function createResultsHTML(results) {
285+
const { renderType, confidence, indicators, detailedInfo } = results;
286+
287+
let resultHTML = `
288+
<div style="border: 1px solid #ddd; border-radius: 8px; padding: 12px; margin-bottom: 10px;">
289+
<div style="margin-bottom: 8px;">
290+
<strong style="color: #2563eb;">Render Type:</strong>
291+
<br>
292+
<span style="font-weight: 600; color: ${getTypeColor(renderType)}">${renderType}</span>
293+
</div>
294+
<div style="margin-bottom: 8px;">
295+
<strong style="color: #2563eb;">Confidence:</strong>
296+
<span style="font-weight: 600;">${confidence}%</span>
297+
${getConfidenceBar(confidence)}
298+
</div>
299+
<div style="margin-bottom: 8px;">
300+
<strong style="color: #2563eb;">Analysis Score:</strong>
301+
<br>
302+
<span style="font-size: 12px; color: #666;">
303+
SSR: ${detailedInfo.ssrScore} | CSR: ${detailedInfo.csrScore}
304+
(${detailedInfo.ssrPercentage}% SSR)
305+
</span>
306+
</div>
307+
</div>
308+
309+
<div style="margin-bottom: 10px;">
310+
<strong style="color: #2563eb;">Key Indicators (${detailedInfo.totalIndicators}):</strong>
311+
<div style="margin-top: 4px; font-size: 13px; line-height: 1.4;">
312+
${indicators.map(indicator => `<span style="display: inline-block; background: #f1f5f9; padding: 2px 6px; margin: 2px 2px; border-radius: 5px; font-size: 11px;">${indicator}</span>`).join('')}
313+
</div>
314+
</div>
315+
`;
316+
317+
// Add framework detection if available
318+
if (detailedInfo.frameworks && detailedInfo.frameworks.length > 0) {
319+
resultHTML += `
320+
<div style="margin-bottom: 8px;">
321+
<strong style="color: #2563eb;">Detected Frameworks:</strong>
322+
<span style="font-weight:700;">${detailedInfo.frameworks.join(', ').toUpperCase()}</span>
323+
</div>
324+
`;
325+
}
326+
327+
// Add static generator information if available
328+
if (detailedInfo.generators && detailedInfo.generators.length > 0) {
329+
resultHTML += `
330+
<div style="margin-bottom: 8px;">
331+
<strong style="color: #2563eb;">Static Site Generator:</strong>
332+
<span style="font-weight:700;">${detailedInfo.generators.join(', ').toUpperCase()}</span>
333+
</div>
334+
`;
335+
}
336+
337+
// Add timing information if available
338+
if (detailedInfo.timing) {
339+
resultHTML += `
340+
<div style="margin-bottom: 8px;">
341+
<strong style="color: #2563eb;">Performance:</strong>
342+
<div style="font-size: 12px; color: #666; margin-top: 2px;">
343+
DOM Ready: ${detailedInfo.timing.domContentLoaded}ms
344+
${detailedInfo.timing.firstContentfulPaint ?
345+
` | FCP: ${detailedInfo.timing.firstContentfulPaint}ms` : ''}
346+
</div>
347+
</div>
348+
`;
349+
}
350+
351+
return resultHTML;
352+
}
353+
354+
// Export functions for use in other files
355+
window.pageAnalyzer = pageAnalyzer;
356+
window.getTypeColor = getTypeColor;
357+
window.getConfidenceBar = getConfidenceBar;
358+
window.createResultsHTML = createResultsHTML;

background.js

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,75 @@
1+
// Listen for extension icon click
12
chrome.action.onClicked.addListener((tab) => {
3+
// First inject the analyzer.js script
24
chrome.scripting.executeScript(
35
{
46
target: { tabId: tab.id },
5-
function: analyzePage,
7+
files: ['analyzer.js']
68
},
7-
(results) => {
8-
if (chrome.runtime.lastError) {
9-
console.error(chrome.runtime.lastError.message);
10-
} else {
11-
console.log("Page analyzed");
12-
}
9+
() => {
10+
// After analyzer.js is injected, run the analysis
11+
chrome.scripting.executeScript(
12+
{
13+
target: { tabId: tab.id },
14+
function: () => window.pageAnalyzer()
15+
},
16+
(results) => {
17+
if (results && results[0] && results[0].result) {
18+
const analysisResults = results[0].result;
19+
20+
// Show notification with results
21+
showNotification(analysisResults, tab.url);
22+
23+
// Save to history
24+
saveToHistory(tab.url, tab.title || tab.url, analysisResults);
25+
} else {
26+
// Show error notification
27+
chrome.notifications.create({
28+
type: 'basic',
29+
iconUrl: 'icon.webp',
30+
title: 'Analysis Failed',
31+
message: 'Unable to analyze the current page. Please try again.',
32+
priority: 1
33+
});
34+
}
35+
}
36+
);
1337
}
1438
);
1539
});
1640

17-
function analyzePage() {
18-
const isCSR =
19-
!document.body.innerHTML.includes('<div id="root">') &&
20-
!document.body.innerHTML.includes('<div id="app">');
21-
const result = isCSR ? "Client-Side Rendered" : "Server-Side Rendered";
41+
// Show notification with analysis results
42+
function showNotification(results, url) {
43+
const { renderType, confidence, indicators } = results;
44+
45+
// Create notification
46+
chrome.notifications.create({
47+
type: 'basic',
48+
iconUrl: 'icon.webp',
49+
title: `${renderType} (${confidence}% confidence)`,
50+
message: `Key indicators: ${indicators.slice(0, 2).join(', ')}${indicators.length > 2 ? '...' : ''}`,
51+
priority: 1
52+
});
53+
}
2254

23-
// Display the result to the user
24-
alert(`This page is: ${result}`);
55+
// Save analysis to history
56+
function saveToHistory(url, title, results) {
57+
chrome.storage.local.get(['analysisHistory'], (data) => {
58+
const history = data.analysisHistory || [];
59+
60+
// Add new entry (limit to 10 entries)
61+
const newEntry = {
62+
url: url,
63+
title: title,
64+
timestamp: Date.now(),
65+
results: results
66+
};
67+
68+
// Add to beginning of array and limit to 10 entries
69+
history.unshift(newEntry);
70+
if (history.length > 10) history.pop();
71+
72+
// Save back to storage
73+
chrome.storage.local.set({ analysisHistory: history });
74+
});
2575
}

0 commit comments

Comments
 (0)