diff --git a/src/options/body.css b/src/options/body.css
index 0b5c8db..b4bab7f 100644
--- a/src/options/body.css
+++ b/src/options/body.css
@@ -509,7 +509,7 @@ html[foo=bar] {
#sidebar {
height: calc(6px + var(--body-height));
width: 160px;
- padding: 8px 6px;
+ padding: 0 6px 8px;
overflow: hidden;
box-sizing: border-box;
@@ -534,6 +534,47 @@ html[foo=bar] {
background-color: var(--box-shadow-color);
}
#sidebar .sidebar_section[selected] {
+ background-color: rgba(0, 0, 0, 0.12);
+}
+html[dark_mode='true'] #sidebar .sidebar_section[selected] {
+ background-color: rgba(255, 255, 255, 0.12);
+}
+#sidebar_top_row {
+ display: flex;
+ justify-content: center;
+ margin: 0 auto 2px;
+ width: fit-content;
+ border-bottom: 1px solid var(--box-shadow-color);
+ border-radius: 0 0 6px 6px;
+}
+#sidebar_top_row .sidebar_section {
+ font-size: 10px;
+ font-weight: 400;
+ text-transform: lowercase;
+ opacity: 0.5;
+ padding: 2px 6px;
+ border-radius: 0;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+#sidebar_top_row .sidebar_section[selected] {
+ opacity: 1;
+}
+#active_sidebar .active_count {
+ font-size: 9px;
+ min-width: 14px;
+ height: 14px;
+ line-height: 14px;
+ text-align: center;
+ border-radius: 7px;
+ background-color: rgba(0, 0, 0, 0.15);
+ color: var(--label-color);
+}
+html[dark_mode='true'] #active_sidebar .active_count {
+ background-color: rgba(255, 255, 255, 0.2);
+}
+#sidebar #active_sidebar[selected] .active_count {
background-color: var(--label-color);
color: var(--body-background-color);
}
diff --git a/src/options/main.js b/src/options/main.js
index e93d7b9..901fd9d 100644
--- a/src/options/main.js
+++ b/src/options/main.js
@@ -66,6 +66,77 @@ function logStorageChange(changes, area) {
browser.storage.onChanged.addListener(logStorageChange);
+function refreshActiveSection() {
+ const section = document.getElementById('active_section');
+ const sidebarItem = document.getElementById('active_sidebar');
+ if (!section || !sidebarItem) return;
+
+ const isSelected = sidebarItem.hasAttribute('selected');
+
+ // Remove old option nodes
+ section.querySelectorAll('.option').forEach(opt => opt.remove());
+
+ let count = 0;
+ SECTIONS.forEach(s => {
+ s.options.forEach(option => {
+ const { id, name, effects, display, premium } = option;
+ if (display === false) return;
+ if (!cache[id]) return;
+
+ count++;
+ const optionNode = TEMPLATE_OPTION.cloneNode(true);
+ optionNode.classList.remove('removed');
+ optionNode.removeAttribute('id');
+ optionNode.setAttribute('name', name);
+ optionNode.querySelector('.option_label').innerText = name;
+
+ const svg = optionNode.querySelector('svg');
+ svg.toggleAttribute('active', true);
+
+ optionNode.addEventListener('click', async e => {
+ if (premium && HTML.getAttribute('is_premium') !== 'true') {
+ const signedIn = await Auth.isSignedIn();
+ if (!signedIn) {
+ if (typeof openPremiumRequiredModal === 'function') {
+ openPremiumRequiredModal();
+ } else {
+ displayStatus('Premium feature - sign in to upgrade');
+ }
+ } else if (typeof openUpgradeModal === 'function') {
+ openUpgradeModal();
+ } else {
+ displayStatus('Premium feature - upgrade to access');
+ }
+ return;
+ }
+
+ updateSetting(id, false, { manual: true });
+ // Sync the original toggle
+ const originalSvg = document.querySelector(`div#${id} svg`);
+ originalSvg?.toggleAttribute('active', false);
+
+ if (effects && false in effects) {
+ Object.entries(effects[false]).forEach(([eid, val]) => {
+ const s = document.querySelector(`div#${eid} svg`);
+ s?.toggleAttribute('active', val);
+ updateSetting(eid, val);
+ });
+ }
+ });
+
+ section.append(optionNode);
+ });
+ });
+
+ // Update sidebar badge
+ const badge = sidebarItem.querySelector('.active_count');
+ if (badge) badge.innerText = count;
+
+ // Only show the section when its sidebar item is selected and there are options
+ section.classList.toggle('removed', !isSelected || count === 0);
+}
+
+
function populateOptions(SECTIONS, headerSettings, SETTING_VALUES) {
// Clear the options list, and the sidebar
@@ -73,6 +144,13 @@ function populateOptions(SECTIONS, headerSettings, SETTING_VALUES) {
SIDEBAR.innerHTML = '';
let allTags = [];
+ // Create the Active Options section (hidden until sidebar selected)
+ const activeSection = TEMPLATE_SECTION.cloneNode(true);
+ activeSection.id = 'active_section';
+ activeSection.querySelector('.section_label > a').innerText = 'Active Options';
+ activeSection.querySelector('.section_label > a').removeAttribute('href');
+ OPTIONS_LIST.append(activeSection);
+
// Add option nodes to the HTML.
SECTIONS.forEach(section => {
const { name, tags, options } = section;
@@ -151,6 +229,25 @@ function populateOptions(SECTIONS, headerSettings, SETTING_VALUES) {
OPTIONS_LIST.append(sectionNode);
});
+ // Add "All" and "Active" sidebar items at the top
+ const sidebarTopRow = document.createElement('div');
+ sidebarTopRow.id = 'sidebar_top_row';
+
+ const allSidebar = document.createElement('div');
+ allSidebar.id = 'all_sidebar';
+ allSidebar.className = 'sidebar_section';
+ allSidebar.innerText = 'all';
+ allSidebar.addEventListener('click', showAll);
+
+ const activeSidebar = document.createElement('div');
+ activeSidebar.id = 'active_sidebar';
+ activeSidebar.className = 'sidebar_section';
+ activeSidebar.innerHTML = 'active';
+ activeSidebar.addEventListener('click', activeSidebarListener);
+
+ sidebarTopRow.append(allSidebar, activeSidebar);
+ SIDEBAR.append(sidebarTopRow);
+
// Add sections to the sidebar
const uniqueTags = Array.from(new Set(allTags));
uniqueTags.forEach(tag => {
@@ -237,6 +334,7 @@ function populateOptions(SECTIONS, headerSettings, SETTING_VALUES) {
// Begin time loop -- checks for timedChanges, scheduling
timeLoop();
+ refreshActiveSection();
HTML.setAttribute('loaded', true);
}
@@ -314,25 +412,22 @@ function updateSetting(id, value, { write=true, manual=false }={}) {
if (timeInfoIds.includes(id)) {
updateTimeInfo();
}
+
+ refreshActiveSection();
}
function onSearchInput(e) {
- const { target } = e;
- const { value } = target;
- const sidebarSections = qsa('.sidebar_section');
- const sections = qsa('.section_container:not(#template_section)');
-
- // Reset
- sidebarSections.forEach(s => s.removeAttribute('selected'));
- sections.forEach(section => {
- section.classList.remove('removed');
- const options = Array.from(section.querySelectorAll('div.option'));
- options.forEach(option => option.classList.remove('removed'));
- });
+ const { value } = e.target;
+ showAll();
if (value === '') return;
+ // Deselect "all" during active search
+ const allSidebar = document.getElementById('all_sidebar');
+ if (allSidebar) allSidebar.removeAttribute('selected');
+
+ const sections = qsa('.section_container:not(#template_section):not(#active_section)');
const searchTerms = value.toLowerCase().split(' ');
sections.forEach(section => {
@@ -362,27 +457,53 @@ function onSearchInput(e) {
}
-function sidebarSectionListener(e) {
- const sidebarSection = e.target;
+function showAll() {
const sidebarSections = qsa('.sidebar_section');
- const selected = sidebarSection.toggleAttribute('selected');
- const tag = sidebarSection.getAttribute('tag');
- const sections = qsa('.section_container:not(#template_section)');
+ const sections = qsa('.section_container:not(#template_section):not(#active_section)');
+ const activeSection = document.getElementById('active_section');
- recordEvent('Section selected', { tag, selected });
-
- // Reset
+ sidebarSections.forEach(s => s.removeAttribute('selected'));
+ const allSidebar = document.getElementById('all_sidebar');
+ if (allSidebar) allSidebar.setAttribute('selected', '');
sections.forEach(section => {
section.classList.remove('removed');
- const options = Array.from(section.querySelectorAll('div.option'));
- options.forEach(option => option.classList.remove('removed'));
+ section.querySelectorAll('div.option').forEach(opt => opt.classList.remove('removed'));
});
- sidebarSections.forEach(sidebarSection => {
- sidebarSection.removeAttribute('selected');
- })
+ if (activeSection) activeSection.classList.add('removed');
+}
- sidebarSection.toggleAttribute('selected', selected);
- if (!selected) return;
+
+function activeSidebarListener(e) {
+ const target = e.currentTarget;
+ const selected = target.toggleAttribute('selected');
+
+ if (selected) {
+ // Reset other selections, hide regular sections, show active
+ qsa('.sidebar_section').forEach(s => {
+ if (s !== target) s.removeAttribute('selected');
+ });
+ qsa('.section_container:not(#template_section):not(#active_section)').forEach(s => s.classList.add('removed'));
+ refreshActiveSection();
+ } else {
+ showAll();
+ }
+}
+
+
+function sidebarSectionListener(e) {
+ const sidebarSection = e.target;
+ const wasSelected = sidebarSection.hasAttribute('selected');
+ const tag = sidebarSection.getAttribute('tag');
+
+ recordEvent('Section selected', { tag, selected: !wasSelected });
+
+ showAll();
+ if (wasSelected) return;
+
+ const allSidebar = document.getElementById('all_sidebar');
+ if (allSidebar) allSidebar.removeAttribute('selected');
+ sidebarSection.setAttribute('selected', '');
+ const sections = qsa('.section_container:not(#template_section):not(#active_section)');
sections.forEach(section => {
const sectionTags = section.getAttribute('tags').split(',').map(t => t.trim());
diff --git a/src/options/options.css b/src/options/options.css
index 3012f5c..52d51dc 100644
--- a/src/options/options.css
+++ b/src/options/options.css
@@ -29,6 +29,10 @@
text-decoration: none;
}
+#active_section .section_label {
+ display: none;
+}
+
div.option {
cursor: default;