summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--background.js97
-rw-r--r--inject-css.js107
-rw-r--r--manifest.json2
-rw-r--r--popup/popup.css51
-rw-r--r--popup/popup.html9
-rw-r--r--popup/popup.js141
6 files changed, 311 insertions, 96 deletions
diff --git a/background.js b/background.js
index 94baffa..04adfd0 100644
--- a/background.js
+++ b/background.js
@@ -16,44 +16,85 @@ async function applyCSSToTab(tab) {
const skipListData = await browser.storage.local.get(
SKIP_FORCE_THEMING_KEY
);
- const skipList = skipListData[SKIP_FORCE_THEMING_KEY] || [];
-
- const cssFileName =
- Object.keys(data.styles?.website || {}).find((key) => {
- const siteName = key.replace(".css", "");
- if (siteName.startsWith("+")) {
- const baseSiteName = siteName.slice(1);
- return hostname.endsWith(baseSiteName);
+ const siteList = skipListData[SKIP_FORCE_THEMING_KEY] || [];
+ const isWhitelistMode = globalSettings.whitelistMode || false;
+
+ // Find the best matching CSS file
+ let bestMatch = null;
+ let bestMatchLength = 0;
+
+ for (const key of Object.keys(data.styles?.website || {})) {
+ const siteName = key.replace(".css", "");
+ if (siteName.startsWith("+")) {
+ const baseSiteName = siteName.slice(1);
+ if (
+ hostname.endsWith(baseSiteName) &&
+ baseSiteName.length > bestMatchLength
+ ) {
+ bestMatch = key;
+ bestMatchLength = baseSiteName.length;
}
- return hostname === siteName || hostname === `www.${siteName}`;
- }) ||
- (globalSettings.forceStyling && !skipList.includes(hostname)
- ? "example.com.css"
- : null);
-
- if (!cssFileName) return;
-
- const features = data.styles.website[cssFileName];
- const siteKey = `transparentZenSettings.${hostname}`;
- const siteData = await browser.storage.local.get(siteKey);
- const featureSettings = siteData[siteKey] || {};
-
- let combinedCSS = "";
- for (const [feature, css] of Object.entries(features)) {
- if (featureSettings[feature] !== false) {
- combinedCSS += css + "\n";
+ } else if (hostname === siteName || hostname === `www.${siteName}`) {
+ // Exact match has priority
+ bestMatch = key;
+ break;
+ } else if (
+ hostname.endsWith(siteName) &&
+ siteName.length > bestMatchLength
+ ) {
+ bestMatch = key;
+ bestMatchLength = siteName.length;
}
}
- if (combinedCSS) {
- await browser.tabs.insertCSS(tab.id, { code: combinedCSS });
- console.log(`Injected custom CSS for ${hostname}`);
+ // If we found a direct match, use it
+ if (bestMatch) {
+ await applyCSS(tab.id, hostname, data.styles.website[bestMatch]);
+ return;
+ }
+
+ // Otherwise, check if we should apply forced styling
+ if (globalSettings.forceStyling) {
+ const siteInList = siteList.includes(hostname);
+
+ // In whitelist mode: apply only if site is in the list
+ // In blacklist mode: apply only if site is NOT in the list
+ if (
+ (isWhitelistMode && siteInList) ||
+ (!isWhitelistMode && !siteInList)
+ ) {
+ await applyCSS(
+ tab.id,
+ hostname,
+ data.styles.website["example.com.css"]
+ );
+ }
}
} catch (error) {
console.error(`Error applying CSS to ${hostname}:`, error);
}
}
+async function applyCSS(tabId, hostname, features) {
+ if (!features) return;
+
+ const siteKey = `transparentZenSettings.${hostname}`;
+ const siteData = await browser.storage.local.get(siteKey);
+ const featureSettings = siteData[siteKey] || {};
+
+ let combinedCSS = "";
+ for (const [feature, css] of Object.entries(features)) {
+ if (featureSettings[feature] !== false) {
+ combinedCSS += css + "\n";
+ }
+ }
+
+ if (combinedCSS) {
+ await browser.tabs.insertCSS(tabId, { code: combinedCSS });
+ console.log(`Injected custom CSS for ${hostname}`);
+ }
+}
+
let autoUpdateInterval;
function startAutoUpdate() {
diff --git a/inject-css.js b/inject-css.js
index ea1525a..d53a915 100644
--- a/inject-css.js
+++ b/inject-css.js
@@ -21,54 +21,91 @@ if (logging) console.log("inject-css.js script loaded");
const currentUrl = window.location.hostname;
if (logging) console.log("Current URL hostname", currentUrl);
- let cssFileName = Object.keys(data.styles?.website || {}).find((key) => {
+ // Find the best matching CSS file
+ let bestMatch = null;
+ let bestMatchLength = 0;
+
+ for (const key of Object.keys(data.styles?.website || {})) {
const siteName = key.replace(".css", "");
if (siteName.startsWith("+")) {
const baseSiteName = siteName.slice(1);
- return currentUrl.endsWith(baseSiteName);
- }
- return currentUrl === siteName || currentUrl === `www.${siteName}`;
- });
-
- const skipListData = await browser.storage.local.get(
- SKIP_FORCE_THEMING_KEY
- );
- const skipList = skipListData[SKIP_FORCE_THEMING_KEY] || [];
-
- if (!cssFileName && settings.transparentZenSettings?.forceStyling) {
- if (skipList.includes(currentUrl)) {
- if (logging) console.log("Skipping forced theming for this site");
- return;
+ if (
+ currentUrl.endsWith(baseSiteName) &&
+ baseSiteName.length > bestMatchLength
+ ) {
+ bestMatch = key;
+ bestMatchLength = baseSiteName.length;
+ }
+ } else if (currentUrl === siteName || currentUrl === `www.${siteName}`) {
+ // Exact match has priority
+ bestMatch = key;
+ break;
+ } else if (
+ currentUrl.endsWith(siteName) &&
+ siteName.length > bestMatchLength
+ ) {
+ bestMatch = key;
+ bestMatchLength = siteName.length;
}
- cssFileName = "example.com.css";
}
- if (!cssFileName) {
- if (logging) console.log("No CSS file found for current site");
+ // If a direct match was found, use it
+ if (bestMatch) {
+ await injectCSS(currentUrl, data.styles.website[bestMatch]);
return;
}
- if (logging) console.log("CSS file found for current site", cssFileName);
-
- const features = data.styles.website[cssFileName];
- const siteKey = `transparentZenSettings.${currentUrl}`;
- const siteData = await browser.storage.local.get(siteKey);
- const featureSettings = siteData[siteKey] || {};
+ // If no direct match was found and force styling is enabled, check whitelist/blacklist mode
+ if (settings.transparentZenSettings?.forceStyling) {
+ const skipListData = await browser.storage.local.get(
+ SKIP_FORCE_THEMING_KEY
+ );
+ const siteList = skipListData[SKIP_FORCE_THEMING_KEY] || [];
+ const isWhitelistMode =
+ settings.transparentZenSettings?.whitelistMode || false;
+ const siteInList = siteList.includes(currentUrl);
- let combinedCSS = "";
- for (const [feature, css] of Object.entries(features)) {
- if (featureSettings[feature] !== false) {
- combinedCSS += css + "\n";
+ // In whitelist mode: apply only if site is in the list
+ // In blacklist mode: apply only if site is NOT in the list
+ if (
+ (isWhitelistMode && siteInList) ||
+ (!isWhitelistMode && !siteInList)
+ ) {
+ await injectCSS(currentUrl, data.styles.website["example.com.css"]);
+ } else {
+ if (logging)
+ console.log(
+ `Styling skipped due to ${
+ isWhitelistMode ? "whitelist" : "blacklist"
+ } mode settings`
+ );
}
- }
-
- if (combinedCSS) {
- const style = document.createElement("style");
- style.textContent = combinedCSS;
- document.head.appendChild(style);
- if (logging) console.log(`Injected custom CSS for ${currentUrl}`);
+ } else {
+ if (logging) console.log("No CSS file found for current site");
}
} catch (error) {
console.error("Error injecting CSS:", error);
}
})();
+
+async function injectCSS(hostname, features) {
+ if (!features) return;
+
+ const siteKey = `transparentZenSettings.${hostname}`;
+ const siteData = await browser.storage.local.get(siteKey);
+ const featureSettings = siteData[siteKey] || {};
+
+ let combinedCSS = "";
+ for (const [feature, css] of Object.entries(features)) {
+ if (featureSettings[feature] !== false) {
+ combinedCSS += css + "\n";
+ }
+ }
+
+ if (combinedCSS) {
+ const style = document.createElement("style");
+ style.textContent = combinedCSS;
+ document.head.appendChild(style);
+ if (logging) console.log(`Injected custom CSS for ${hostname}`);
+ }
+}
diff --git a/manifest.json b/manifest.json
index 3ad194a..356ff24 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Zen Internet",
- "version": "1.4.1",
+ "version": "1.5.0",
"description": "Inject custom css from my repository in real time",
"browser_specific_settings": {
"gecko": {
diff --git a/popup/popup.css b/popup/popup.css
index 0058174..1a47b2a 100644
--- a/popup/popup.css
+++ b/popup/popup.css
@@ -1,4 +1,4 @@
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
:root {
--radius-sm: 6px;
@@ -7,7 +7,7 @@
--transition: all 0.2s ease;
--shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
--accent-color: #f98764; /* Subtle Zen-themed orange */
- --hover-color: #fa9475; /* Lighter hover state */
+ --hover-color: #fa9475; /* Lighter hover state */
--success-color: #40c057;
--warning-color: #fab005;
--danger-color: #fa5252;
@@ -22,7 +22,7 @@ body {
width: 360px;
margin: 0;
padding: 0;
- font-family: 'Inter', Arial, sans-serif;
+ font-family: "Inter", Arial, sans-serif;
color: var(--text-primary);
background-color: var(--bg-color);
font-size: 14px;
@@ -106,8 +106,14 @@ body {
}
@keyframes fadeIn {
- from { opacity: 0; transform: translateY(-5px); }
- to { opacity: 1; transform: translateY(0); }
+ from {
+ opacity: 0;
+ transform: translateY(-5px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
.toggle-switch {
@@ -243,7 +249,7 @@ input:checked + .slider:before {
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
background-color: var(--bg-color);
-
+
/* Firefox scrollbar styling */
scrollbar-width: thin;
scrollbar-color: var(--accent-color) transparent;
@@ -355,6 +361,14 @@ input:checked + .slider:before {
padding-left: 9px !important; /* Compensate for the border */
}
+/* Mode Indicator */
+.mode-indicator {
+ font-size: 0.85em;
+ color: var(--text-secondary);
+ margin: 8px 0;
+ font-style: italic;
+}
+
.current-badge {
display: inline-block;
background-color: var(--accent-color);
@@ -387,8 +401,12 @@ input:checked + .slider:before {
}
@keyframes fadeIn {
- from { opacity: 0.7; }
- to { opacity: 1; }
+ from {
+ opacity: 0.7;
+ }
+ to {
+ opacity: 1;
+ }
}
/* Make sure the text doesn't overflow */
@@ -404,22 +422,29 @@ input:checked + .slider:before {
margin-right: 4px;
}
-#addon-version, #last-fetched-time{
+#addon-version,
+#last-fetched-time {
font-size: 0.75em;
}
-.forcing-container{
+.forcing-container {
background-color: var(--secondary-bg);
border-radius: var(--radius-md);
padding: 16px;
margin-bottom: 16px;
}
-.warning{
+.warning {
color: var(--warning-color);
margin-top: 8px;
}
-#reload{
+#reload {
margin-top: 16px;
-} \ No newline at end of file
+}
+
+.mode-hint {
+ font-size: 0.75em;
+ color: var(--text-secondary);
+ margin-left: 8px;
+}
diff --git a/popup/popup.html b/popup/popup.html
index c07e5fa..8ffa03d 100644
--- a/popup/popup.html
+++ b/popup/popup.html
@@ -73,10 +73,17 @@
</div>
<div class="toggle-container">
<label class="toggle-switch">
+ <input type="checkbox" id="whitelist-mode">
+ <span class="slider round"></span>
+ </label>
+ <span id="whitelist-mode-label" class="toggle-label">Blacklist Mode</span>
+ </div>
+ <div class="toggle-container">
+ <label class="toggle-switch">
<input type="checkbox" id="skip-force-theming">
<span class="slider round"></span>
</label>
- <span class="toggle-label">Skip Forcing for this Site</span>
+ <span id="site-toggle-label" class="toggle-label">Skip Forcing for this Site</span>
</div>
</div>
diff --git a/popup/popup.js b/popup/popup.js
index a71eb4f..d81ed7d 100644
--- a/popup/popup.js
+++ b/popup/popup.js
@@ -13,9 +13,13 @@ new (class ExtensionPopup {
autoUpdateSwitch = document.getElementById("auto-update");
lastFetchedTime = document.getElementById("last-fetched-time");
forceStylingSwitch = document.getElementById("force-styling");
+ whitelistModeSwitch = document.getElementById("whitelist-mode");
+ whitelistModeLabel = document.getElementById("whitelist-mode-label");
skipForceThemingSwitch = document.getElementById("skip-force-theming");
+ siteToggleLabel = document.getElementById("site-toggle-label");
skipForceThemingList = [];
reloadButton = document.getElementById("reload");
+ modeIndicator = document.getElementById("mode-indicator");
constructor() {
if (logging) console.log("Initializing ExtensionPopup");
@@ -45,6 +49,11 @@ new (class ExtensionPopup {
);
this.reloadButton.addEventListener("click", this.reloadPage.bind(this));
+ this.whitelistModeSwitch.addEventListener(
+ "change",
+ this.handleWhitelistModeChange.bind(this)
+ );
+
// Setup auto-update and display last fetched time
this.setupAutoUpdate();
this.displayLastFetchedTime();
@@ -97,9 +106,18 @@ new (class ExtensionPopup {
this.globalSettings.enableStyling ?? true;
this.autoUpdateSwitch.checked = this.globalSettings.autoUpdate ?? false;
this.forceStylingSwitch.checked = this.globalSettings.forceStyling ?? false;
- this.skipForceThemingSwitch.checked = this.skipForceThemingList.includes(
+ this.whitelistModeSwitch.checked =
+ this.globalSettings.whitelistMode ?? false;
+
+ this.updateModeLabels();
+
+ // In whitelist mode, checked means "include this site"
+ // In blacklist mode, checked means "skip this site"
+ const isInList = this.skipForceThemingList.includes(
this.currentSiteHostname
);
+ this.skipForceThemingSwitch.checked = isInList;
+
this.loadCurrentSiteFeatures();
}
@@ -131,6 +149,7 @@ new (class ExtensionPopup {
this.globalSettings.enableStyling = this.enableStylingSwitch.checked;
this.globalSettings.autoUpdate = this.autoUpdateSwitch.checked;
this.globalSettings.forceStyling = this.forceStylingSwitch.checked;
+ this.globalSettings.whitelistMode = this.whitelistModeSwitch.checked;
browser.storage.local
.set({
@@ -180,14 +199,20 @@ new (class ExtensionPopup {
const index = this.skipForceThemingList.indexOf(this.currentSiteHostname);
if (isChecked && index === -1) {
+ // Add to the list (whitelist: include, blacklist: skip)
this.skipForceThemingList.push(this.currentSiteHostname);
} else if (!isChecked && index !== -1) {
+ // Remove from the list (whitelist: exclude, blacklist: include)
this.skipForceThemingList.splice(index, 1);
}
- browser.storage.local.set({
- [SKIP_FORCE_THEMING_KEY]: this.skipForceThemingList,
- });
+ browser.storage.local
+ .set({
+ [SKIP_FORCE_THEMING_KEY]: this.skipForceThemingList,
+ })
+ .then(() => {
+ this.updateActiveTabStyling();
+ });
}
async loadCurrentSiteFeatures() {
@@ -335,34 +360,51 @@ new (class ExtensionPopup {
const hostname = url.hostname;
try {
- await browser.tabs.removeCSS(tab.id, {
- code: "/* Placeholder for removing CSS */",
- });
- } catch (error) {}
+ // Try to remove any existing CSS first
+ try {
+ await browser.tabs.removeCSS(tab.id, {
+ code: "/* Placeholder for removing CSS */",
+ });
+ } catch (error) {
+ // Ignore errors as they may occur if no CSS was previously applied
+ }
- if (!this.shouldApplyCSS(hostname)) return;
+ if (!this.shouldApplyCSS(hostname)) return;
- try {
const stylesData = await browser.storage.local.get("styles");
const styles = stylesData.styles?.website || {};
- let siteKey = null;
+ // First try to find a direct match for a CSS file
+ let bestMatch = null;
+ let bestMatchLength = 0;
+
for (const site of Object.keys(styles)) {
const siteName = site.replace(/\.css$/, "");
if (siteName.startsWith("+")) {
const baseSiteName = siteName.slice(1);
- if (hostname.endsWith(baseSiteName)) {
- siteKey = site;
- break;
+ if (
+ hostname.endsWith(baseSiteName) &&
+ baseSiteName.length > bestMatchLength
+ ) {
+ bestMatch = site;
+ bestMatchLength = baseSiteName.length;
}
} else if (hostname === siteName || hostname === `www.${siteName}`) {
- siteKey = site;
+ // Exact match has priority
+ bestMatch = site;
break;
+ } else if (
+ hostname.endsWith(siteName) &&
+ siteName.length > bestMatchLength
+ ) {
+ bestMatch = site;
+ bestMatchLength = siteName.length;
}
}
- if (siteKey && styles[siteKey]) {
- const features = styles[siteKey];
+ // If we found a direct match, use it
+ if (bestMatch) {
+ const features = styles[bestMatch];
const siteStorageKey = `${this.BROWSER_STORAGE_KEY}.${hostname}`;
const siteData = await browser.storage.local.get(siteStorageKey);
const featureSettings = siteData[siteStorageKey] || {};
@@ -376,7 +418,36 @@ new (class ExtensionPopup {
if (combinedCSS) {
await browser.tabs.insertCSS(tab.id, { code: combinedCSS });
- console.info(`Applied CSS to ${hostname}`);
+ console.info(`Applied CSS to ${hostname} (direct match)`);
+ }
+ } else if (this.globalSettings.forceStyling) {
+ // Otherwise check for forced styling
+ const isInList = this.skipForceThemingList.includes(hostname);
+ const isWhitelistMode = this.globalSettings.whitelistMode;
+
+ // Determine if we should apply forced styling
+ const shouldApplyForcedStyling =
+ (isWhitelistMode && isInList) || (!isWhitelistMode && !isInList);
+
+ if (shouldApplyForcedStyling && styles["example.com.css"]) {
+ const features = styles["example.com.css"];
+ const siteStorageKey = `${this.BROWSER_STORAGE_KEY}.${hostname}`;
+ const siteData = await browser.storage.local.get(siteStorageKey);
+ const featureSettings = siteData[siteStorageKey] || {};
+
+ let combinedCSS = "";
+ for (const [feature, css] of Object.entries(features)) {
+ if (featureSettings[feature] !== false) {
+ combinedCSS += css + "\n";
+ }
+ }
+
+ if (combinedCSS) {
+ await browser.tabs.insertCSS(tab.id, { code: combinedCSS });
+ console.info(`Applied forced CSS to ${hostname}`);
+ }
+ } else {
+ console.info(`Skipping forced styling for ${hostname}`);
}
}
} catch (error) {
@@ -434,4 +505,38 @@ new (class ExtensionPopup {
}
}
}
+
+ handleWhitelistModeChange() {
+ this.updateModeLabels();
+ this.saveSettings();
+ }
+
+ updateModeIndicator() {
+ if (this.whitelistModeSwitch.checked) {
+ this.modeIndicator.textContent =
+ "In Whitelist Mode (apply only to listed sites)";
+ } else {
+ this.modeIndicator.textContent =
+ "In Blacklist Mode (apply to all except listed sites)";
+ }
+ }
+
+ updateSiteToggleLabel() {
+ // Update the label based on the current mode
+ if (this.whitelistModeSwitch.checked) {
+ this.siteToggleLabel.textContent = "Enable for this Site";
+ } else {
+ this.siteToggleLabel.textContent = "Skip Forcing for this Site";
+ }
+ }
+
+ updateModeLabels() {
+ if (this.whitelistModeSwitch.checked) {
+ this.whitelistModeLabel.textContent = "Whitelist Mode";
+ this.siteToggleLabel.textContent = "Enable for this Site";
+ } else {
+ this.whitelistModeLabel.textContent = "Blacklist Mode";
+ this.siteToggleLabel.textContent = "Skip Forcing for this Site";
+ }
+ }
})();