diff --git a/src/content/about.html b/src/content/about.html index d3894d5..f6459f5 100644 --- a/src/content/about.html +++ b/src/content/about.html @@ -18,8 +18,21 @@
There is a discussion to increase the storage sync quota to 1MB.
See also:
Toggle changes the browser preferences and does not require SAVE.
-Show per-tab proxies on the toolbar badge when in Proxy by Patterns mode
+Set a proxy for Incognito (Private Mode) and/or Containers
@@ -778,7 +783,7 @@FoxyProxy supports enterprise policy and managed storage, and they can be configured to set FoxyProxy options.
storage.managed is not supported on Firefox for Android.
@@ -1091,14 +1106,14 @@ http://pac-url.com/etc?type=pac&title=Work PAC&color=663300{ "mode": "disable", // mandatory: current option, necessary to enable "sync": false, // optional: not necessary as it will be disabled on managed storage "autoBackup": false, // optional: not necessary as save is disabled on managed storage - "showPatternProxy": false, // optional: Show proxies on the toolbar badge when in Proxy by Patterns mode (Firefox only) "passthrough": "", // optional: Global Exclude + "theme": "", // optional: set the theme "container": {}, // optional: Incognito/Container settings "commands": {}, // optional: keyboard shortcut settings "data": [] // mandatory: array of proxies and their patterns diff --git a/src/content/i18n.js b/src/content/i18n.js index 6e3fbcd..8b193bb 100644 --- a/src/content/i18n.js +++ b/src/content/i18n.js @@ -5,7 +5,7 @@ class I18n { static { document.querySelectorAll('template').forEach(i => this.set(i.content)); this.set(); - document.body.style.opacity = 1; // show after i18n + // document.body.style.opacity = 1; // show after i18n } static set(target = document) { diff --git a/src/content/iframe.css b/src/content/iframe.css index f3fbf27..9ae8ec5 100644 --- a/src/content/iframe.css +++ b/src/content/iframe.css @@ -1,4 +1,5 @@ @import 'default.css'; +@import 'theme.css'; /* ----- General ----- */ :root { @@ -27,7 +28,7 @@ article { /* ----- h1-h5 ----- */ h2 { color: var(--header); - font-size: 2.2em; + font-size: 2.5em; border-bottom: 1px solid var(--border); font-weight: normal; } @@ -37,7 +38,6 @@ h2:first-of-type { } h3 { - color: var(--nav-hover); font-size: 1.5em; font-weight: normal; } @@ -169,7 +169,7 @@ mark { /* ----- About ----- */ .about dt { background-color: unset; - color: var(--nav-hover); + color: var(--h3); font-weight: bold; margin-bottom: 0.2em; font-size: 1.1em; @@ -201,11 +201,11 @@ nav { nav a { color: var(--color); padding: 0.5em 1em; - transition: 0.5s; + /* transition: 0.5s; */ } nav a:hover { - color: var(--btn-bg); + background-color: var(--hover); } /* ----- /Navigation ----- */ diff --git a/src/content/log.js b/src/content/log.js index e3b566c..0afdf2e 100644 --- a/src/content/log.js +++ b/src/content/log.js @@ -7,28 +7,22 @@ export class Log { static { this.trTemplate = document.querySelector('.log template').content.firstElementChild; this.tbody = document.querySelector('.log tbody'); - - // no proxy info on chrome - if (App.firefox) { - this.tbody.textContent = ''; // remove "not available" notice - browser.webRequest.onBeforeRequest.addListener(e => this.process(e), {urls: ['*://*/*']}); - } - this.proxyCache = {}; // used to find proxy this.mode = 'disable'; + browser.webRequest.onBeforeRequest.addListener(e => this.process(e), {urls: ['*://*/*']}); } static process(e) { const tr = this.tbody.children[199] || this.trTemplate.cloneNode(true); - const [, time, container, method, doc, url, title, type, host, port, pattern] = tr.children; + const [, time, container, method, reqType, doc, url, title, type, host, port, pattern] = tr.children; time.textContent = this.formatInt(e.timeStamp); method.textContent = e.method; - doc.textContent = e.documentUrl || ''; // For a top-level document, documentUrl is undefined - doc.title = e.documentUrl || ''; - url.textContent = decodeURIComponent(e.url); - url.title = e.url; - container.classList.toggle('incognito', e.incognito); + reqType.textContent = e.type; + // For a top-level document, documentUrl is undefined, chrome uses e.initiator + this.prepareOverflow(doc, e.documentUrl || e.initiator || ''); + this.prepareOverflow(url, decodeURIComponent(e.url)); + container.classList.toggle('incognito', !!e.incognito); container.textContent = e.cookieStoreId?.startsWith('firefox-container-') ? 'C' + e.cookieStoreId.substring(18) : ''; const info = e.proxyInfo || {host: '', port: '', type: ''}; @@ -43,8 +37,7 @@ export class Log { // show matching pattern in pattern mode const pat = this.mode === 'pattern' && item?.include.find(i => new RegExp(Pattern.get(i.pattern, i.type), 'i').test(e.url)); const text = pat?.title || pat?.pattern || ''; - pattern.textContent = text; - pattern.title = text; + this.prepareOverflow(pattern, text); this.tbody.prepend(tr); // in reverse order, new on top } @@ -53,4 +46,10 @@ export class Log { return new Intl.DateTimeFormat(navigator.language, {hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false}).format(new Date(d)); } + + // set title, in case text overflows + static prepareOverflow(elem, value) { + elem.textContent = value; + elem.title = value; + } } \ No newline at end of file diff --git a/src/content/migrate.js b/src/content/migrate.js index d6bef82..3586849 100644 --- a/src/content/migrate.js +++ b/src/content/migrate.js @@ -40,30 +40,19 @@ import {CryptoJS} from '../lib/aes.3.1.2.js'; export class Migrate { static async init(pref) { - // --- 8.8 - // tidy up left-over obj from 8.0 Sync typo mistake - if (Object.hasOwn(pref, 'obj')) { - delete pref.obj; - await browser.storage.local.remove('obj'); - await browser.storage.sync.remove('obj'); - } - - // --- 8.7 - // change global proxyDNS to per-proxy + // --- 8.9 + // 8.9 remove showPatternProxy (from 8.7) + // 8.8 tidy up left-over obj Sync typo mistake (from 8.0) + // 8.7 change global proxyDNS to per-proxy (from 8.0) if (Object.hasOwn(pref, 'proxyDNS') && pref.data) { pref.data.forEach(i => i.proxyDNS = !!pref.proxyDNS); - delete pref.proxyDNS; - await browser.storage.local.remove('proxyDNS'); await browser.storage.local.set(pref); } - - // --- 8.1 - if (Object.hasOwn(pref, 'globalExcludeWildcard')) { - delete pref.globalExcludeWildcard; // from 8.0, removed in 8.1 - delete pref.globalExcludeRegex; // from 8.0, removed in 8.1 - delete pref.obj; // 8.0 Sync typo mistake - await browser.storage.local.remove(['globalExcludeWildcard', 'globalExcludeRegex', 'obj']); - } + // 8.1 remove globalExcludeWildcard, globalExcludeRegex (from 8.0) + const keys = ['showPatternProxy', 'obj', 'proxyDNS', 'globalExcludeWildcard', 'globalExcludeRegex']; + keys.forEach(i => delete pref[i]); + await browser.storage.local.remove(keys); + await browser.storage.sync.remove(keys); // --- 8.0 if (pref.data) { return; } @@ -185,7 +174,9 @@ export class Migrate { const db = App.getDefaultPref(); db.sync = !!pref.sync; - const data = Object.values(pref).filter(i => i && Object.hasOwn(i, 'address')); // null value causes an error + // null value causes an error in hasOwn, direct proxies don't have 'address' + const data = Object.values(pref).filter(i => i && ['address', 'type'].some(p => Object.hasOwn(i, p))); + data.sort((a, b) => a.index - b.index); // sort by index data.forEach(item => { @@ -193,8 +184,8 @@ export class Migrate { active: item.active === 'true' || item.active === true, // convert to boolean, some old databases have mixed types title: item.title || '', type: typeSet[item.type], // convert to actual type: http | https | socks4 | socks5 | direct | + add PAC - hostname: item.address, // rename to hostname - port: item.port, + hostname: item.address || '', // rename to hostname + port: item.port || '', username: item.username || '', password: item.password || '', cc: item.cc || '', // remove country, use CC in country-code.js @@ -208,9 +199,7 @@ export class Migrate { }; pxy.cc === 'UK' && (pxy.cc = 'GB'); // convert UK to ISO 3166-1 GB - - // --- type 'direct' - pxy.type === 'direct' && (pxy.hostname = 'DIRECT'); + pxy.type === 'direct' && (pxy.hostname = 'DIRECT'); // type 'direct' /* { diff --git a/src/content/on-request.js b/src/content/on-request.js index 0c6c24a..2c5922f 100644 --- a/src/content/on-request.js +++ b/src/content/on-request.js @@ -7,7 +7,6 @@ // Fixed in Firefox 119 import {Pattern} from './pattern.js'; -import {PageAction} from './page-action.js'; import {Location} from './location.js'; // ---------- Firefox Proxy Process ------------------------ @@ -16,11 +15,10 @@ export class OnRequest { static { // --- default values this.mode = 'disable'; - this.proxy = null; // used for Single Proxy + this.proxy = {}; // used for Single Proxy this.data = []; // used for Proxy by Pattern this.passthrough = []; // RegExp string this.net = []; // [start, end] strings - this.showPatternProxy = false; this.tabProxy = {}; // tab proxy, may be lost in MV3 if bg is unloaded this.container = {}; // incognito/container proxy @@ -40,12 +38,12 @@ export class OnRequest { const [passthrough, , net] = Pattern.getPassthrough(pref.passthrough); this.passthrough = passthrough; this.net = net; - this.showPatternProxy = pref.showPatternProxy; - const data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname); // filter data + // filter data + const data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname); - // --- single proxy - /:\d+$/.test(pref.mode) && (this.proxy = data.find(i => pref.mode === `${i.hostname}:${i.port}`)); + // --- single proxy (false|undefined|proxy object) + this.proxy = /:\d+[^/]*$/.test(pref.mode) && data.find(i => pref.mode === `${i.hostname}:${i.port}`); // --- proxy by pattern this.data = data.filter(i => i.include[0] || i.exclude[0]).map(item => { @@ -75,34 +73,37 @@ export class OnRequest { } static process(e) { + const tabId = e.tabId; switch (true) { // --- check local & global passthrough case this.bypass(e.url): + this.setAction(null, tabId); return {type: 'direct'}; // --- tab proxy - case e.tabId !== -1 && !!this.tabProxy[e.tabId]: - return this.processProxy(this.tabProxy[e.tabId]); + case tabId !== -1 && !!this.tabProxy[tabId]: + return this.processProxy(this.tabProxy[tabId], tabId); // --- incognito proxy - case e.tabId !== -1 && e.incognito && !!this.container.incognito: - return this.processProxy(this.container.incognito); + case tabId !== -1 && e.incognito && !!this.container.incognito: + return this.processProxy(this.container.incognito, tabId); // --- container proxy - case e.tabId !== -1 && e.cookieStoreId && !!this.container[e.cookieStoreId]: - return this.processProxy(this.container[e.cookieStoreId]); + case tabId !== -1 && e.cookieStoreId && !!this.container[e.cookieStoreId]: + return this.processProxy(this.container[e.cookieStoreId], tabId); // --- standard operation case this.mode === 'disable': // pass direct case this.mode === 'direct': // pass direct case this.mode.includes('://') && !/:\d+$/.test(this.mode): // PAC URL is set + this.setAction(null, tabId); return {type: 'direct'}; case this.mode === 'pattern': // check if url matches patterns - return this.processPattern(e.url, e.tabId); + return this.processPattern(e.url, tabId); default: // get the proxy for all - return this.processProxy(this.proxy); + return this.processProxy(this.proxy, tabId); } } @@ -111,31 +112,19 @@ export class OnRequest { for (const proxy of this.data) { if (!match(proxy.exclude) && match(proxy.include)) { - this.processShowPatternProxy(proxy, tabId); - return this.processProxy(proxy); + // this.processShowPatternProxy(proxy, tabId); + return this.processProxy(proxy, tabId); } } + this.setAction(null, tabId); return {type: 'direct'}; // no match } - static processShowPatternProxy(item, tabId) { - // Set to -1 if the request isn't related to a tab - if (tabId === -1 || !this.showPatternProxy) { return; } - - const host = [item.hostname, item.port].filter(Boolean).join(':'); - const title = [item.title, host, item.city, ...Location.get(item.cc)].filter(Boolean).join('\n'); - const text = item.title || item.hostname; - const color = item.color; - - browser.action.setBadgeBackgroundColor({color, tabId}); - browser.action.setTitle({title, tabId}); - browser.action.setBadgeText({text, tabId}); - } - - static processProxy(proxy) { - const {type, hostname: host, port, username, password, proxyDNS} = proxy; - if (type === 'direct') { return {type: 'direct'}; } + static processProxy(proxy, tabId) { + this.setAction(proxy, tabId); + const {type, hostname: host, port, username, password, proxyDNS} = proxy || {}; + if (!type || type === 'direct') { return {type: 'direct'}; } // https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#102 // Although API converts to number -> let port = Number.parseInt(proxyData.port, 10); @@ -163,6 +152,28 @@ export class OnRequest { return response; } + static setAction(item, tabId) { + // Set to -1 if the request isn't related to a tab + if (tabId === -1) { return; } + + // --- reset values + let title = null; + let text = null; + let color = null; + + // --- set proxy details + if (item) { + const host = [item.hostname, item.port].filter(Boolean).join(':'); + title = [item.title, host, item.city, ...Location.get(item.cc)].filter(Boolean).join('\n'); + text = item.title || item.hostname; + color = item.color; + } + + browser.action.setBadgeBackgroundColor({color, tabId}); + browser.action.setTitle({title, tabId}); + browser.action.setBadgeText({text, tabId}); + } + // ---------- passthrough -------------------------------- static bypass(url) { switch (true) { @@ -223,13 +234,21 @@ export class OnRequest { } this.tabProxy[tab.id] = pxy; - PageAction.set(tab.id, pxy); + // PageAction.set(tab.id, pxy); } static async unsetTabProxy() { const [tab] = await browser.tabs.query({currentWindow: true, active: true}); delete this.tabProxy[tab.id]; - PageAction.unset(tab.id); + // PageAction.unset(tab.id); + } + + // ---------- Update Page Action ------------------------- + static onUpdated(tabId, changeInfo, tab) { + if (changeInfo.status !== 'complete') { return; } + + const pxy = this.tabProxy[tabId]; + pxy ? this.setAction(pxy, tabId) : this.checkPageAction(tab); } // ---------- Incognito/Container ------------------------ @@ -237,14 +256,6 @@ export class OnRequest { if (tab.id === -1 || this.tabProxy[tab.id]) { return; } // not if tab proxy is set const pxy = tab.incognito ? this.container.incognito : this.container[tab.cookieStoreId]; - pxy && PageAction.set(tab.id, pxy); - } - - // ---------- Update Page Action ------------------------- - static onUpdated(tabId, changeInfo, tab) { - if (changeInfo.status !== 'complete') { return; } - - const pxy = this.tabProxy[tab.id]; - pxy ? PageAction.set(tab.id, pxy) : this.checkPageAction(tab); + pxy && this.setAction(pxy, tab.id); } } \ No newline at end of file diff --git a/src/content/options.css b/src/content/options.css index 17d1d94..94a5da3 100644 --- a/src/content/options.css +++ b/src/content/options.css @@ -1,6 +1,7 @@ @import 'default.css'; @import 'progress-bar.css'; @import 'spinner.css'; +@import 'theme.css'; /* ----- General ----- */ :root { @@ -58,13 +59,6 @@ iframe { vertical-align: text-bottom; } */ -label[for], -input[type="checkbox"], -summary, -.pointer { - cursor: pointer; -} - label > input[type="checkbox"] { margin-right: 0.5em; } @@ -80,29 +74,17 @@ textarea { resize: vertical; } -.flat { - background: var(--btn-bg); +/* .flat { color: #fff; - cursor: pointer; - text-align: center; - transition: 0.5s; border-radius: 5px; padding: 0.4em 1em; - border: 0; - font-weight: bold; font-size: 0.9em; min-width: 8em; display: inline-block; - white-space: nowrap; -} - -.flat:hover { - background: var(--btn-hover); - /* box-shadow: 0px 1px 5px var(--shadow); */ -} +} */ fieldset { - background: var(--bg); + background-color: var(--bg); border-radius: 0.5em; border: 0; padding: 1.5em; @@ -154,7 +136,7 @@ input[type="checkbox"].control { } div.nav { - background: var(--nav-bg); + background-color: var(--nav-bg); } nav { @@ -175,12 +157,12 @@ nav img { nav > label { padding: 0.5em 1em; - transition: 0.5s; + /* transition: 0.5s; */ border-radius: 0.5em 0.5em 0 0; } nav > label:hover { - background: var(--nav-hover); + background-color: var(--nav-hover); } /* nav > label img { @@ -231,29 +213,19 @@ input[type="file"] { /* ----- /Import/Export ----- */ /* ----- Submit Button ----- */ -button[type="submit"] { +/* button[type="submit"] { display: table; color: #fff; - background: var(--btn-bg); - font-size: 0.9em; - font-weight: bold; + background-color: var(--btn-bg); + font-size:0.9em; margin: 1em auto 0; - padding: 0.5em 3em; - text-shadow: 0 -1px 1px #333; - border: 0; + padding: 0.5em 5em; border-radius: 5px; - box-shadow: 0 5px var(--body-bg); } button[type="submit"]:hover { background-color: var(--btn-hover); -} - -button[type="submit"]:active { - /* background-color: #3e8e41; */ - box-shadow: 0 1px var(--body-bg); - transform: translateY(4px); -} +} */ /* ----- /Submit Button ----- */ /* ----- Toggle Switch ----- */ @@ -274,7 +246,7 @@ button[type="submit"]:active { z-index: 2; width: 14px; height: 14px; - background: #fff; + background-color: #fff; left: 1px; top: 1px; border-radius: 50%; @@ -292,14 +264,6 @@ button[type="submit"]:active { /* ----- /Toggle Switch ----- */ /* ----- Button ----- */ -button.plain { - background-color: transparent; - padding: 0; - margin: 0; - min-width: 1em; - border: none; -} - button.bin, button.test, button.close { @@ -344,10 +308,15 @@ textarea { font-size: 1em; } */ +section.options fieldset * { + transition: opacity 0.5s; +} + input[type="color"] { border: 0; } +.options div.theme, .options div.container, .options div.commands { display: grid; @@ -357,6 +326,10 @@ input[type="color"] { margin-bottom: 1em; } +.options div.theme { + margin-left: unset; +} + .options div.buttons { display: grid; grid-auto-flow: column; @@ -369,7 +342,7 @@ input[type="color"] { /* .options div.buttons { padding: 0 1.5em; margin: 0 -1.5em 1em; - background: var(--btn-bg); + background-color: var(--btn-bg); } .options div.buttons > * { @@ -520,7 +493,7 @@ details.proxy > summary span:nth-of-type(2):empty::before { .pattern-box button.bin { padding: 0; background-color: transparent; - border: none; + /* border: none; */ font-size: 1em; transition: 0.5s; color: #ccc; @@ -529,74 +502,6 @@ details.proxy > summary span:nth-of-type(2):empty::before { } /* ----- /Pattern ----- */ -/* ----- show/hide elements ----- */ -details.proxy[data-type="direct"] :is( - [data-i18n="port"], [data-id="port"], - [data-i18n="username"], [data-id="username"], - [data-i18n="password"], .password, - [data-i18n="country"], [data-id="cc"], - [data-i18n="city"], [data-id="city"], - [data-type="pac"], .pac) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -details.proxy[data-type="pac"] :is( - [data-i18n="port"], [data-id="port"], - [data-i18n="username"], [data-id="username"], - [data-i18n="password"], .password) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -details.proxy[data-type="pac"] :is(.pattern-head, .pattern-box) { - display: none; -} - -details.proxy:not([data-type="pac"]) :is([data-type="pac"], .pac) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -details.proxy[data-type="socks4"] :is( - [data-i18n="username"], [data-id="username"], - [data-i18n="password"], .password) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -details.proxy:not([data-type="socks5"]) :is([data-i18n="proxyDNS"], [data-id="proxyDNS"]) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -/* --- Chrome --- */ -.chrome .firefox { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -.chrome details.proxy[data-type="socks5"] :is( - [data-i18n="username"], [data-id="username"], - [data-i18n="password"], .password) { - opacity: 0.3; - pointer-events: none; - user-select: none; -} - -/* --- Basic --- */ -.basic :is(.pattern-head, .pattern-box) { - display: none; -} - -/* ----- /show/hide elements ----- */ - /* ----- Popup ----- */ div.popup { display: none; @@ -718,7 +623,7 @@ section.log { .log thead th { color: #fff; - background: #999; + background-color: #999; padding: 0.5em 0.2em; font-size: 0.9em; font-weight: normal; @@ -730,13 +635,13 @@ section.log { vertical-align: unset; } -.log tbody tr { - /* border-bottom: 1px solid var(--border); */ +/* .log tbody tr { + /* border-bottom: 1px solid var(--border); * / animation: sect 0.5s ease-in-out; -} +} */ .log tr:hover { - background: var(--hover) !important; + background-color: var(--hover) !important; } .log tbody tr:nth-of-type(even) { @@ -748,28 +653,27 @@ section.log { padding: 0.2em; } -.log td:nth-of-type(5), .log td:nth-of-type(6), -.log td:nth-of-type(11) { +.log td:nth-of-type(7), +.log td:nth-of-type(12) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 20em; } -.log td:nth-of-type(6) { +.log td:nth-of-type(7) { max-width: 40em; } -.log td:nth-of-type(7) { +.log td:nth-of-type(8) { border-left: 2px solid var(--border); } -.log td:nth-of-type(11) { +.log td:nth-of-type(12) { max-width: 5em; } - .log td.incognito::before { content: ''; width: 1em; @@ -795,17 +699,87 @@ section.log { content: counter(n); font-size: 0.8em; } +/* ----- /Log ----- */ -/* Not Available On Chrome (Firefox Only)" */ -.log td.unavailable { - height: 50vh; - text-align: center; - vertical-align: middle; - color: var(--btn-bg); - font-size: 3em; +/* ----- show/hide elements ----- */ +details.proxy[data-type="direct"] :is( + [data-i18n="port"], [data-id="port"], + [data-i18n="username"], [data-id="username"], + [data-i18n="password"], .password, + [data-i18n="country"], [data-id="cc"], + [data-i18n="city"], [data-id="city"], + [data-type="pac"], .pac) { + opacity: 0.3; + pointer-events: none; + user-select: none; } -.log td.unavailable::before { - content: none !important; +details.proxy[data-type="pac"] :is( + [data-i18n="port"], [data-id="port"], + [data-i18n="username"], [data-id="username"], + [data-i18n="password"], .password) { + opacity: 0.3; + pointer-events: none; + user-select: none; } -/* ----- /Log ----- */ \ No newline at end of file + +details.proxy[data-type="pac"] :is(.pattern-head, .pattern-box) { + display: none; +} + +details.proxy:not([data-type="pac"]) :is([data-type="pac"], .pac) { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +details.proxy[data-type="socks4"] :is( + [data-i18n="username"], [data-id="username"], + [data-i18n="password"], .password) { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +details.proxy:not([data-type="socks5"]) :is([data-i18n="proxyDNS"], [data-id="proxyDNS"]) { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +/* --- Chrome --- */ +body.chrome .firefox { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +body.chrome details.proxy[data-type="socks5"] :is( + [data-i18n="username"], [data-id="username"], + [data-i18n="password"], .password) { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +body:not(.chrome) [data-i18n="storeLocally"] { + opacity: 0.3; + pointer-events: none; + user-select: none; +} + +caption.firefox { + color: var(--nav-color); + visibility: hidden; +} + +body.chrome caption.firefox { + visibility: visible; + opacity: 1; +} + +/* --- Basic --- */ +.basic :is(.pattern-head, .pattern-box) { + display: none; +} +/* ----- /show/hide elements ----- */ \ No newline at end of file diff --git a/src/content/options.html b/src/content/options.html index 4f6ba62..cd0ec6d 100644 --- a/src/content/options.html +++ b/src/content/options.html @@ -70,8 +70,17 @@ - - + + ++ + +@@ -602,12 +611,14 @@diff --git a/src/content/options.js b/src/content/options.js index 55cbab5..c5cbeb3 100644 --- a/src/content/options.js +++ b/src/content/options.js @@ -61,6 +61,22 @@ class Toggle { } // ---------- /Toggle -------------------------------------- +// ---------- Theme ---------------------------------------- +// eslint-disable-next-line no-unused-vars +class Theme { + static { + this.elem = [document, ...[...document.querySelectorAll('iframe')].map(i => i.contentDocument)]; + pref.theme && this.set(pref.theme); + document.body.style.opacity = 1; // show after + document.getElementById('theme').addEventListener('change', e => this.set(e.target.value)); + } + + static set(value) { + this.elem.forEach(i => i.documentElement.className = value); + } +} +// ---------- /Theme --------------------------------------- + // ---------- Options -------------------------------------- class Options { @@ -77,7 +93,7 @@ class Options { // --- buttons document.querySelector('.options button[data-i18n="restoreDefaults"]').addEventListener('click', () => this.restoreDefaults()); - this.init(['sync', 'autoBackup', 'showPatternProxy', 'passthrough']); + this.init(['sync', 'autoBackup', 'theme', 'showPatternProxy', 'passthrough']); } static init(keys = Object.keys(pref)) { @@ -581,7 +597,11 @@ class Proxies { }); // patterns - pxy.querySelector('button[data-i18n="add|title"]').addEventListener('click', () => this.addPattern(patternBox)); + pxy.querySelector('button[data-i18n="add|title"]').addEventListener('click', () => { + this.addPattern(patternBox); + patternBox.lastElementChild.scrollIntoView(false); + patternBox.lastElementChild.children[4].focus(); + }); pxy.querySelector('input[type="file"]').addEventListener('change', e => this.importPattern(e, patternBox)); pxy.querySelector('button[data-i18n^="export"]').addEventListener('click', () => this.exportPattern(patternBox, title.value.trim() || hostname.value.trim())); diff --git a/src/content/popup.css b/src/content/popup.css index 3142dac..40bde39 100644 --- a/src/content/popup.css +++ b/src/content/popup.css @@ -1,10 +1,16 @@ @import 'default.css'; +@import 'theme.css'; /* ----- Light Theme ----- */ :root { --filter: opacity(0.4) grayscale(1); } +/* for the default theme */ +:root:not([class]) { + --nav-bg: #630; +} + /* ----- Dark Theme ----- */ @media screen and (prefers-color-scheme: dark) { :root { @@ -23,7 +29,7 @@ body { h1 { color: var(--nav-color); - background: var(--body-bg); + background-color: var(--nav-bg); margin: 0; padding: 0.5em; } @@ -34,7 +40,7 @@ h1 img { } /* ----- Buttons ----- */ -div.buttons { +div.popup-buttons { display: grid; grid-auto-flow: column; column-gap: 0.1em; @@ -42,13 +48,13 @@ div.buttons { button { color: #fff; - border: 0; + border: none; padding: 0.8em; - font-weight: bold; + /* font-weight: bold; */ } button:hover { - background: var(--btn-hover); + background-color: var(--btn-hover); } /* ----- /Buttons ----- */ @@ -125,13 +131,19 @@ input#filter { background: url('../image/filter.svg') no-repeat left 0.5em center / 1em; padding-left: 2em; margin-bottom: 0.2em; - grid-column: span 2; + /* grid-column: span 2; */ } div.list label.off { display: none; } +summary { + background-color: var(--alt-bg); + padding: 0.2em 0.5em; + margin-bottom: 0.1em; +} + div.host { display: grid; grid-template-columns: 1fr 1fr; @@ -141,24 +153,23 @@ div.host { } div.host button { - padding: 0.2em; - background: unset; + background-color: unset; + border-radius: 5px; + border: 1px solid var(--border); color: var(--color); font-weight: normal; - border: 1px solid var(--border); - border-radius: 5px; + padding: 0.2em; } div.host button:hover { - background: var(--hover); + background-color: var(--hover); } -div.host select { +/* div.host select { grid-column: span 2; -} +} */ /* ----- show/hide elements ----- */ - /* --- Chrome --- */ .chrome .firefox { opacity: 0.3; @@ -177,5 +188,4 @@ div.host select { .basic .pattern { display: none; } - /* ----- /show/hide elements ----- */ \ No newline at end of file diff --git a/src/content/popup.html b/src/content/popup.html index 17b0067..12123f4 100644 --- a/src/content/popup.html +++ b/src/content/popup.html @@ -26,26 +26,30 @@ - +
@@ -638,6 +646,7 @@- + @@ -618,9 +629,6 @@ - - + - +- -+- - -+
+ - + - - + + + + ++