From fae990164e7bf69f19af4c283ab00d9c17768aec Mon Sep 17 00:00:00 2001 From: YelehaUwU Date: Tue, 25 Feb 2025 21:11:43 +0100 Subject: New design and current site indicator. --- popup/popup.css | 390 ++++++++++++++++++++++++++++++++++++++++++++++++------- popup/popup.html | 58 ++++++--- popup/popup.js | 242 +++++++++++++++++++++++++++++----- 3 files changed, 594 insertions(+), 96 deletions(-) diff --git a/popup/popup.css b/popup/popup.css index 174470e..80d3ec1 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -1,53 +1,349 @@ @import url("../shared/variables.css"); +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +:root { + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 12px; + --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 */ + --success-color: #40c057; + --warning-color: #fab005; + --danger-color: #fa5252; + --bg-color: #1e1e2e; + --secondary-bg: #2a2a3c; + --border-color: #4c4c63; + --text-primary: #e2e8f0; + --text-secondary: #a0a0b8; +} body { - width: 350px; + width: 360px; + margin: 0; + padding: 0; + font-family: 'Inter', Arial, sans-serif; + color: var(--text-primary); + background-color: var(--bg-color); + font-size: 14px; + line-height: 1.5; +} + +.container { + display: flex; + flex-direction: column; + height: 100%; +} + +/* Header Styles */ +.app-header { + background: linear-gradient(135deg, #2c3e50, #1a2530); + color: white; + padding: 16px 20px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: var(--shadow); +} + +.logo-container { + display: flex; + align-items: center; + gap: 8px; +} + +.logo-img { + width: 32px; + height: 32px; + object-fit: contain; +} + +.app-title { + margin: 0; + font-size: 20px; + font-weight: 600; + letter-spacing: -0.5px; +} + +.author { + font-size: 12px; + margin-top: 4px; + opacity: 0.9; +} + +.author a { + color: white; + text-decoration: none; + font-weight: 500; +} + +.author a:hover { + text-decoration: underline; +} + +/* Main Content Styles */ +.app-content { + padding: 20px; + display: flex; + flex-direction: column; + gap: 20px; +} + +/* Toggle Switch */ +.toggle-container { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 8px; +} + +/* Current site toggle styling */ +.current-site-container { + background-color: var(--secondary-bg); + padding: 12px; + border-radius: var(--radius-md); + margin-top: -12px; + position: relative; + animation: fadeIn 0.3s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } +} + +.toggle-switch { + position: relative; + display: inline-block; + width: 46px; + height: 24px; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #4c4c63; + transition: var(--transition); + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 3px; + bottom: 3px; + background-color: white; + transition: var(--transition); + border-radius: 50%; +} + +input:checked + .slider { + background-color: var(--accent-color); +} + +input:focus + .slider { + box-shadow: 0 0 1px var(--accent-color); +} + +input:checked + .slider:before { + transform: translateX(22px); +} + +.toggle-label { + font-weight: 500; +} + +/* Websites List */ +.section-title { + font-size: 16px; + font-weight: 600; + margin: 0 0 12px 0; + color: var(--text-primary); +} + +.websites-container { + background-color: var(--secondary-bg); + border-radius: var(--radius-md); + padding: 16px; +} + +.websites-list { + list-style: none; padding: 0; margin: 0; - background-color: var(--transparent-background-darker); - font-family: Arial, Helvetica, sans-serif; - color: var(--color-text); - - .container { - display: flex; - flex-direction: column; - gap: 12px; - - #extension-header { - background-color: var(--color-bg); - padding: 16px; - - .headline { - margin: 0; - } - } - - #extension-body { - padding: 16px; - - display: flex; - flex-direction: column; - gap: 16px; - - #extension-settings { - .setting { - display: flex; - align-items: center; - gap: 8px; - } - } - - #extension-description { - a { - color: var(--color-primary); - font-weight: bold; - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } - } - } - } + max-height: 150px; + overflow-y: auto; + 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; +} + +.websites-list li { + padding: 10px 12px; + border-bottom: 1px solid var(--border-color); + display: flex; + align-items: center; + gap: 8px; + color: var(--text-primary); +} + +.websites-list li:last-child { + border-bottom: none; +} + +.websites-list li:before { + content: "•"; + color: var(--accent-color); + font-weight: bold; + opacity: 0.9; +} + +/* Action Buttons */ +.actions { + display: flex; + flex-direction: column; + gap: 10px; +} + +.action-button { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 16px; + border: none; + border-radius: var(--radius-md); + font-weight: 500; + cursor: pointer; + transition: var(--transition); + outline: none; +} + +.action-button i { + font-size: 14px; +} + +.action-button.primary { + background-color: var(--accent-color); + color: white; + border: none; +} + +.action-button.primary:hover { + background-color: var(--hover-color); + box-shadow: 0 0 8px rgba(243, 156, 18, 0.5); +} + +.action-button.secondary { + background-color: transparent; + border: 1px solid var(--border-color); + color: var(--text-secondary); +} + +.action-button.secondary:hover { + background-color: rgba(255, 255, 255, 0.1); } + +/* Footer */ +.app-footer { + margin-top: auto; + padding: 14px 20px; + background-color: var(--secondary-bg); + border-top: 1px solid var(--border-color); + display: flex; + align-items: center; + justify-content: space-between; +} + +.footer-link { + display: flex; + align-items: center; + gap: 6px; + color: var(--text-secondary); + text-decoration: none; + font-size: 12px; + font-weight: 500; + transition: var(--transition); +} + +.footer-link:hover { + color: var(--accent-color); + opacity: 0.9; +} + +.footer-divider { + height: 16px; + width: 1px; + background-color: var(--border-color); +} + +/* Current site highlighting */ +.current-site { + background-color: rgba(229, 152, 102, 0.1); /* Subtle orange background */ + border-left: 3px solid var(--accent-color); /* Orange accent bar */ + padding-left: 9px !important; /* Compensate for the border */ +} + +.current-badge { + display: inline-block; + background-color: var(--accent-color); + color: white; + font-size: 10px; + font-weight: 600; + padding: 2px 6px; + border-radius: 10px; + margin-left: 6px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Ensure checkbox and label spacing */ +.websites-list li label { + display: flex; + align-items: center; + width: 100%; +} + +.websites-list li label input[type="checkbox"] { + margin-right: 8px; + flex-shrink: 0; +} + +/* Add a slight animation to the current site */ +.current-site { + animation: fadeIn 0.3s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0.7; } + to { opacity: 1; } +} + +/* Make sure the text doesn't overflow */ +.websites-list li label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Push the badge to the right */ +.current-badge { + margin-left: auto; + margin-right: 4px; +} \ No newline at end of file diff --git a/popup/popup.html b/popup/popup.html index 7050511..8ccdbd4 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -1,29 +1,53 @@ - + + +
-
-

ZenInternet by @sameerasw

-
-
- -
    - - - Styles repo -

    About: - sameerasw.com -

    -
    +
    +
    + ZenInternet Logo +

    ZenInternet

    +
    + +
    + +
    +
    + + Enable Styling +
    + +
    +

    Available Websites

    +
      +
      + +
      + +
      +
      + +
      diff --git a/popup/popup.js b/popup/popup.js index f743179..dc4b203 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -1,33 +1,54 @@ -new (class ExtensionPopup { +new(class ExtensionPopup { BROWSER_STORAGE_KEY = "transparentZenSettings"; browserStorageSettings = {}; enableStylingSwitch = document.getElementById("enable-styling"); refetchCSSButton = document.getElementById("refetch-css"); websitesList = document.getElementById("websites-list"); - + currentSiteHostname = ""; + constructor() { this.loadSettings().then((settings) => { if (settings) { this.browserStorageSettings = settings; - this.restoreSettings(); - this.bindEvents(); + this.getCurrentTabInfo().then(() => { + this.restoreSettings(); + this.bindEvents(); + }); } }); this.refetchCSSButton.addEventListener("click", this.refetchCSS.bind(this)); - document - .getElementById("restart-background") - .addEventListener("click", this.restartBackground); - } + this.setupContentScriptInjection(); + } + + async getCurrentTabInfo() { + try { + const tabs = await browser.tabs.query({ active: true, currentWindow: true }); + if (tabs.length > 0) { + const url = new URL(tabs[0].url); + this.currentSiteHostname = url.hostname; + console.info("Current site hostname:", this.currentSiteHostname); + } + } catch (error) { + console.error("Error getting current tab info:", error); + } + } + bindEvents() { this.enableStylingSwitch.addEventListener("change", () => { this.saveSettings(); + this.updateActiveTabStyling(); }); - this.websitesList.addEventListener("change", () => { + + this.websitesList.addEventListener("change", (event) => { this.saveSettings(); + // Update styling immediately when a checkbox changes + if (event.target.type === 'checkbox') { + this.updateActiveTabStyling(); + } }); } - + restoreSettings() { if (this.browserStorageSettings.enableStyling !== undefined) { this.enableStylingSwitch.checked = @@ -35,58 +56,112 @@ new (class ExtensionPopup { } this.loadWebsitesList(); } - + async loadSettings() { const settings = await browser.storage.local.get(this.BROWSER_STORAGE_KEY); console.info("Settings loaded", settings?.[this.BROWSER_STORAGE_KEY]); return settings?.[this.BROWSER_STORAGE_KEY] || {}; } - + saveSettings() { this.browserStorageSettings.enableStyling = this.enableStylingSwitch.checked; - + const websiteSettings = {}; this.websitesList .querySelectorAll("input[type=checkbox]") .forEach((checkbox) => { websiteSettings[checkbox.name] = checkbox.checked; }); + this.browserStorageSettings.websiteSettings = websiteSettings; - + browser.storage.local.set({ [this.BROWSER_STORAGE_KEY]: this.browserStorageSettings, }); + console.info("Settings saved", this.browserStorageSettings); } - + async loadWebsitesList() { try { - const response = await fetch("/mapper.json", { - headers: { - "Cache-Control": "no-cache", - }, - }); - if (!response.ok) throw new Error("Failed to fetch mapper.json"); - const mapping = await response.json(); + // Get the styles from storage that were fetched using refetchCSS + const stylesData = await browser.storage.local.get("styles"); + const styles = stylesData.styles || {}; + this.websitesList.innerHTML = ""; - for (const site of Object.keys(mapping)) { + + // Use the keys from styles object + const websites = Object.keys(styles); + + if (websites.length === 0) { + const listItem = document.createElement("li"); + listItem.textContent = "No styles available. Click 'Refetch latest styles' to update."; + this.websitesList.appendChild(listItem); + return; + } + + // Create array to hold all website items + const websiteItems = []; + let currentSiteItem = null; + + for (const site of websites) { + // Remove the .css extension if present + const displayName = site.replace(/\.css$/, ""); + const isChecked = - this.browserStorageSettings.websiteSettings?.[site] ?? true; + this.browserStorageSettings.websiteSettings?.[displayName] ?? true; + const listItem = document.createElement("li"); + + // Check if this site matches the current site + const isCurrent = this.isCurrentSite(displayName); + if (isCurrent) { + listItem.classList.add("current-site"); + currentSiteItem = listItem; // Store the current site item separately + } + listItem.innerHTML = ` `; - this.websitesList.appendChild(listItem); + + // Add to array if not current site + if (!isCurrent) { + websiteItems.push(listItem); + } } + + // Add current site at the top if it exists + if (currentSiteItem) { + this.websitesList.appendChild(currentSiteItem); + } + + // Add all other sites + websiteItems.forEach(item => { + this.websitesList.appendChild(item); + }); } catch (error) { console.error("Error loading websites list:", error); + this.websitesList.innerHTML = "
    • Error loading websites list. Please try refetching styles.
    • "; } } + + isCurrentSite(siteName) { + if (!this.currentSiteHostname) return false; + + // Direct match + if (this.currentSiteHostname === siteName) return true; + if (this.currentSiteHostname.startsWith("www.")) { + const nonWww = this.currentSiteHostname.replace("www.", ""); + if (nonWww === siteName) return true; + } + } + async refetchCSS() { this.refetchCSSButton.textContent = "Fetching..."; try { @@ -101,6 +176,13 @@ new (class ExtensionPopup { if (!response.ok) throw new Error("Failed to fetch styles.json"); const styles = await response.json(); await browser.storage.local.set({ styles }); + + // Reload the websites list after fetching new styles + this.loadWebsitesList(); + + // Update styling on the active tab + this.updateActiveTabStyling(); + this.refetchCSSButton.textContent = "Done!"; setTimeout(() => { this.refetchCSSButton.textContent = "Refetch latest styles"; @@ -114,9 +196,105 @@ new (class ExtensionPopup { console.error("Error refetching styles:", error); } } - - async restartBackground() { - browser.runtime.reload(); - console.info("Background script restart requested."); + + setupContentScriptInjection() { + // Listen for tab updates to apply CSS when needed + browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (changeInfo.status === 'complete') { + this.applyCSSToTab(tab); + } + }); + + // Also handle tabs that are already open when the extension starts + this.updateAllTabs(); + } + + async updateAllTabs() { + const tabs = await browser.tabs.query({}); + for (const tab of tabs) { + this.applyCSSToTab(tab); + } + } + + async updateActiveTabStyling() { + const tabs = await browser.tabs.query({ active: true, currentWindow: true }); + if (tabs.length > 0) { + this.applyCSSToTab(tabs[0]); + } + } + + async applyCSSToTab(tab) { + const url = new URL(tab.url); + const hostname = url.hostname; + + // First remove any existing CSS + try { + await browser.tabs.removeCSS(tab.id, { + code: "/* Placeholder for removing CSS */" + }); + } catch (error) { + // Ignore errors as the tab might not have any CSS injected + } + + // Check if we should apply CSS to this site + if (!this.shouldApplyCSS(hostname)) { + return; + } + + try { + // Get the styles from storage + const stylesData = await browser.storage.local.get("styles"); + const styles = stylesData.styles || {}; + + // Find matching CSS for this hostname + let cssToApply = null; + + // Check for direct match (with .css extension) + if (styles[hostname + '.css']) { + cssToApply = styles[hostname + '.css']; + } + // Check for domain matches (e.g. youtube.com matches m.youtube.com) + else { + for (const site of Object.keys(styles)) { + const siteName = site.replace(/\.css$/, ""); + if (hostname.includes(siteName)) { + cssToApply = styles[site]; + break; + } + } + } + + if (cssToApply) { + await browser.tabs.insertCSS(tab.id, { code: cssToApply }); + console.info(`Applied CSS to ${hostname}`); + } + } catch (error) { + console.error(`Error applying CSS to ${hostname}:`, error); + } + } + + shouldApplyCSS(hostname) { + // Global enable/disable switch + if (!this.browserStorageSettings.enableStyling) { + return false; + } + + // Check website-specific settings + const websiteSettings = this.browserStorageSettings.websiteSettings || {}; + + // First check for exact hostname match + if (websiteSettings[hostname] !== undefined) { + return websiteSettings[hostname]; + } + + // Then check for domain matches (e.g. youtube.com matches m.youtube.com) + for (const site in websiteSettings) { + if (hostname.includes(site)) { + return websiteSettings[site]; + } + } + + // Default to enabled if no specific setting found + return true; } -})(); +})(); \ No newline at end of file -- cgit v1.2.3