accessibility 1

This commit is contained in:
Guy Sandler 2026-08-23 14:34:35 -07:00
parent 6820ef1163
commit 1a6e78a3b6
7 changed files with 282 additions and 23 deletions

View File

@ -56,6 +56,30 @@
"better_sidebar": {
"message": "Better Sidebar"
},
"opacity_options": {
"message": "Opacity & Blur"
},
"opacity_only_background": {
"message": "Only available with a custom background. Lower opacity to let the background show through; add blur for a glass effect."
},
"background_opacity": {
"message": "Background opacity"
},
"bg_blur": {
"message": "Background blur"
},
"sidebar_opacity": {
"message": "Sidebar opacity"
},
"sidebar_blur": {
"message": "Sidebar blur"
},
"sidebar_opacity_auto": {
"message": "Applies to the Better Sidebar when it's on, otherwise the normal sidebar."
},
"reset_default": {
"message": "Reset to default"
},
"max_items": {
"message": "Max Items: "
},

View File

@ -21,8 +21,8 @@ h2 {font-size:24px;}
.header {font-size: 16px;display: flex;align-items: center;gap:12px; margin-bottom:14px;}
.header h1 { font-weight: 600; font-size:24px; }
#bclogo {height: 30px;width:30px;filter: none}
#numAssignmentsSlider, #numTodoItemsSlider, #sidebarScaleSlider, #customBackgroundScale {display: block;margin-top: 14px;width: 100%;-webkit-appearance: none; appearance: none;background:var(--inputbg);outline: none;height:6px;border-radius:20px;}
#numAssignmentsSlider::-webkit-slider-thumb, #numTodoItemsSlider::-webkit-slider-thumb, #sidebarScaleSlider::-webkit-slider-thumb, #customBackgroundScale::-webkit-slider-thumb {-webkit-appearance: none; appearance: none;height: 18px; width: 18px;background: #727272;cursor: pointer;border-radius:30px;}
#numAssignmentsSlider, #numTodoItemsSlider, #sidebarScaleSlider, #customBackgroundScale, #bgOpacitySlider, #sidebarOpacitySlider, #bgBlurSlider, #sidebarBlurSlider {display: block;margin-top: 14px;width: 100%;-webkit-appearance: none; appearance: none;background:var(--inputbg);outline: none;height:6px;border-radius:20px;}
#numAssignmentsSlider::-webkit-slider-thumb, #numTodoItemsSlider::-webkit-slider-thumb, #sidebarScaleSlider::-webkit-slider-thumb, #customBackgroundScale::-webkit-slider-thumb, #bgOpacitySlider::-webkit-slider-thumb, #sidebarOpacitySlider::-webkit-slider-thumb, #bgBlurSlider::-webkit-slider-thumb, #sidebarBlurSlider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none;height: 18px; width: 18px;background: #727272;cursor: pointer;border-radius:30px;}
.option-container {font-size: 14px; margin-top: 8px;background: var(--containerbg);padding: 18px;border-radius: 14px;box-sizing:border-box}
.options .option-container {padding: 14px;}
a.option-container {display: block;color:#5ca5f6;font-size: 14px;}
@ -130,6 +130,8 @@ h2 {margin-top: 20px;font-weight:600}
.background-preset-card {min-height: 96px; display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start; border-radius: 8px;}
.background-preset-card.selected {outline: 2px solid #56Caf0; outline-offset: 2px;}
.background-preset-scale {font-size:10px;white-space:nowrap;opacity:0.9;}
.opacity-reset-btn {display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;border-radius:50%;background:var(--inputbg);color:#cfcfcf;cursor:pointer;font-size:13px;line-height:1;padding:0;transition:background .15s ease,color .15s ease;}
.opacity-reset-btn:hover {background:#3a3a3a;color:#fff;}
.theme-sort-btn {padding:10px 30px 10px 20px;border-radius:7px;background:none;border:none;color: #9d9d9d;font-size:12px;font-weight:600;text-align: left;cursor: pointer;transition:.18s color}
.theme-sort-btn:hover {color: #fff;}
.theme-header {display:flex;justify-content: space-between;margin-bottom:10px;align-items:center}

View File

@ -1025,6 +1025,50 @@
<label for="fitImageToScreen" class="sub-text">Fit image to screen</label>
</div>
</div>
<div class="option-container" id="opacity-options" style="display:none;">
<h3 class="header-small" data-i18n="opacity_options">Opacity &amp; Blur</h3>
<p class="sub-text" style="color:#a4a4a4;margin:4px 0 8px;" data-i18n="opacity_only_background">Only available with a custom background.</p>
<div class="opacity-slider-row">
<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:6px;">
<span class="sub-text" style="font-weight:600;color:#e2e2e2;" data-i18n="background_opacity">Background opacity</span>
<div style="display:flex;align-items:center;gap:6px;">
<button class="opacity-reset-btn" id="bgOpacityReset" type="button" title="Reset to default" data-i18n-title="reset_default"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg></button>
<span class="sub-text" id="bgOpacityValue">65%</span>
</div>
</div>
<input type="range" min="0" max="100" step="1" id="bgOpacitySlider">
</div>
<div class="opacity-slider-row">
<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:10px;">
<span class="sub-text" style="font-weight:600;color:#e2e2e2;" data-i18n="bg_blur">Background blur</span>
<div style="display:flex;align-items:center;gap:6px;">
<button class="opacity-reset-btn" id="bgBlurReset" type="button" title="Reset to default" data-i18n-title="reset_default"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg></button>
<span class="sub-text" id="bgBlurValue">8px</span>
</div>
</div>
<input type="range" min="0" max="30" step="1" id="bgBlurSlider">
</div>
<div class="opacity-slider-row">
<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:10px;">
<span class="sub-text" style="font-weight:600;color:#e2e2e2;" data-i18n="sidebar_opacity">Sidebar opacity</span>
<div style="display:flex;align-items:center;gap:6px;">
<button class="opacity-reset-btn" id="sidebarOpacityReset" type="button" title="Reset to default" data-i18n-title="reset_default"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg></button>
<span class="sub-text" id="sidebarOpacityValue">100%</span>
</div>
</div>
<input type="range" min="0" max="100" step="1" id="sidebarOpacitySlider">
</div>
<div class="opacity-slider-row">
<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:10px;">
<span class="sub-text" style="font-weight:600;color:#e2e2e2;" data-i18n="sidebar_blur">Sidebar blur</span>
<div style="display:flex;align-items:center;gap:6px;">
<button class="opacity-reset-btn" id="sidebarBlurReset" type="button" title="Reset to default" data-i18n-title="reset_default"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg></button>
<span class="sub-text" id="sidebarBlurValue">0px</span>
</div>
</div>
<input type="range" min="0" max="30" step="1" id="sidebarBlurSlider">
</div>
</div>
</div>

View File

@ -111,6 +111,10 @@ chrome.runtime.onInstalled.addListener(function () {
"customBackgroundNasaDaily": false,
"nasaInfoOverlay": false,
"fitImageToScreen": false,
"bg_opacity": 65,
"sidebar_opacity": 100,
"bg_blur": 8,
"sidebar_blur": 0,
}
};

View File

@ -724,6 +724,18 @@ function applyOptionsChanges(changes) {
case "customBackgroundLink":
applyCustomBackground();
break;
case "bg_opacity":
applyCustomBackground();
applyBetterSidebarContentPanel();
break;
case "bg_blur":
applyCustomBackground();
applyBetterSidebarContentPanel();
break;
case "sidebar_opacity":
case "sidebar_blur":
applyCustomBackground();
break;
case "better_todo":
if (options.better_todo) {
setupBetterTodo();
@ -825,6 +837,19 @@ async function applyCustomBackground() {
const backgroundScale = Number(activeBackground.scale) || 100;
const backgroundUrl = JSON.stringify(activeBackground.url);
const fitToScreen = options.fitImageToScreen === true;
// Opacity sliders (0-100). 100 = fully opaque surface, 0 = fully transparent
// so the background image shows through. Only emitted while a background is
// active, since transparency without an image just exposes the dark body.
const bgOpacity = Math.max(0, Math.min(100, Number(options.bg_opacity ?? 65)));
const sidebarOpacity = Math.max(0, Math.min(100, Number(options.sidebar_opacity ?? 100)));
const bgTransparent = 100 - bgOpacity;
const sidebarTransparent = 100 - sidebarOpacity;
// Blur sliders (px). Pairs with opacity: blur only has a visible effect when
// the surface is semi-transparent (opacity < 100) so the background behind
// shows through and gets blurred. Default 8px on content surfaces preserves
// the previous dashboard-header glass look; sidebar defaults to none.
const bgBlur = Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)));
const sidebarBlur = Math.max(0, Math.min(30, Number(options.sidebar_blur ?? 0)));
style.textContent = `
#wrapper {
background-image: url(${backgroundUrl}) !important;
@ -848,20 +873,77 @@ async function applyCustomBackground() {
margin-left: -35px !important;
margin-right: -35px !important;
box-sizing: border-box !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 20%) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
border: 1px solid color-mix(in srgb, var(--bcborders) 60%, transparent) !important;
border-radius: 10px !important;
position: sticky !important;
top: 0 !important;
z-index: 1000 !important;
backdrop-filter: blur(8px) saturate(120%) !important;
-webkit-backdrop-filter: blur(8px) saturate(120%) !important;
backdrop-filter: blur(${bgBlur}px) saturate(120%) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) saturate(120%) !important;
}
#right-side-wrapper {
/* backdrop-filter: blur(10px) !important; */
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 35%);
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%);
border-radius: 5px;
}
/* Recent feedback lives in #right-side. The dark-mode CSS
(darkmodecss.js) recolors its text, but those rules are dark-mode-
only so in light mode + custom background the sub-text (context,
grade, quote) keeps Canvas's default gray on the now-translucent
panel and becomes hard to read. Recolor to the theme text color so
it stays readable in both modes whenever a background is active.
Mirrors the dark-mode selectors; redundant (same value) in dark mode. */
.recent_feedback .event-details {
background: none !important;
}
#right-side .event-details .event-details__context,
#right-side .event-details .event-details__context *,
#right-side .recent_feedback .event-details p,
#right-side .recent_feedback .event-details span {
color: var(--bctext-0) !important;
}
.event-details strong {
color: var(--bctext-0) !important;
}
/* Native global nav sidebar. color-mix only accepts a solid color, so
gradient/image sidebars keep their existing look (rule is invalid and
ignored). At 100% opacity this is equivalent to var(--bcsidebar).
Sidebar blur only shows when sidebar opacity < 100.
The icon/text colors are recolored to var(--bcsidebar-text) to match
the background we just set without this, light mode (where
--bcsidebar is the light default #e3e3e3) would leave institution-
themed light icons on a now-light background = white-on-white.
Mirrors the dark-mode rules in css/darkmodecss.js. */
.ic-app-header {
background: color-mix(in srgb, var(--bcsidebar), transparent ${sidebarTransparent}%) !important;
backdrop-filter: blur(${sidebarBlur}px) !important;
-webkit-backdrop-filter: blur(${sidebarBlur}px) !important;
}
.ic-app-header__menu-list-link svg,
.ic-app-header__menu-list-item.ic-app-header__menu-list-item--active svg {
fill: var(--bcsidebar-text) !important;
}
.menu-item-icon-container,
.ic-app-header__menu-list-link .menu-item__text,
.ic-app-header__menu-list-item.ic-app-header__menu-list-item--active .menu-item__text {
color: var(--bcsidebar-text) !important;
}
.ic-app-header__menu-list-item.ic-app-header__menu-list-item--active .ic-app-header__menu-list-link,
.ic-app-header__menu-list-link:hover {
background: #0000004f !important;
}
/* Better sidebar. The inline background-color is var(--bcsidebar), so the
!important here is required to override it. The same sidebar_opacity /
sidebar_blur sliders drive both surfaces, so whichever sidebar is
active (Better Sidebar when enabled, otherwise the native nav) picks
up the value. */
#better-sidebar-container {
background-color: color-mix(in srgb, var(--bcsidebar), transparent ${sidebarTransparent}%) !important;
backdrop-filter: blur(${sidebarBlur}px) !important;
-webkit-backdrop-filter: blur(${sidebarBlur}px) !important;
}
.header-bar {
background: none !important;
padding: 0 !important;
@ -870,8 +952,8 @@ async function applyCustomBackground() {
.item-group-condensed,
.item-group-container {
background: transparent !important;
/* backdrop-filter: blur(14px) saturate(120%) !important;
-webkit-backdrop-filter: blur(14px) saturate(120%) !important; */
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
border-radius: 12px !important;
border: 1px solid color-mix(in srgb, var(--bcborders) 75%, transparent) !important;
/* box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12) !important; */
@ -897,7 +979,9 @@ async function applyCustomBackground() {
border-radius: 0 !important;
}
#assignments.ui-tabs-panel {
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 35%) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
border-radius: 5px !important;
}
#assignments {
@ -910,7 +994,9 @@ async function applyCustomBackground() {
#content {
margin: 36px 48px 48px !important;
padding: 10px !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 35%) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
border-radius: 5px !important;
box-sizing: border-box !important;
}
@ -919,14 +1005,18 @@ async function applyCustomBackground() {
#content {
margin: 36px 48px 48px !important;
padding: 10px !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 35%) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
border-radius: 5px !important;
box-sizing: border-box !important;
}
` : ""}
${isConversationsPage() ? `
.css-1nh4pc4-view-flexItem {
background-color: color-mix(in srgb, var(--bcbackground-0), transparent 35%) !important;
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
border-radius: 5px !important;
box-sizing: border-box !important;
}
@ -977,15 +1067,16 @@ async function applyCustomBackground() {
padding-top: 0 !important;
}
/* Apply backdrop blur only to module panels, not to all headers */
/* Module panels keep the slider blur on hover (previously a fixed 5px). */
.item-group-condensed.context_module,
.item-group-condensed.context_module_item,
.item-group-condensed[class~="context_module"],
.item-group-condensed.context_module:hover,
.item-group-condensed.context_module_item:hover,
.item-group-condensed.context_module.context_module_item_hover,
.item-group-condensed.context_module_item.context_module_item_hover {
backdrop-filter: blur(5px) !important;
-webkit-backdrop-filter: blur(5px) !important;
backdrop-filter: blur(${bgBlur}px) !important;
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
}
.canvasrefined-gpa-card,
.canvasrefined-gpa,
@ -3069,6 +3160,22 @@ function applySidebarScaleStyles(sidebarList) {
sidebarList.style.setProperty("--bc-sidebar-label-size", `${Math.round(14 * scale)}px`);
}
// Re-apply the tinted course-content panel when the background opacity slider
// changes. Only the better sidebar (course mode) gives .ic-Layout-contentMain a
// tinted panel in the first place (see setupBetterSidebar). Re-applies both the
// opacity and blur sliders so changing either updates the panel live.
function applyBetterSidebarContentPanel() {
if (!options.better_sidebar) return;
if (getSidebarLayoutMode() !== "course") return;
const contentMain = document.querySelector(".ic-Layout-contentMain");
if (!contentMain) return;
const bgOpacity = Math.max(0, Math.min(100, Number(options.bg_opacity ?? 65)));
const bgBlur = Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)));
contentMain.style.setProperty("background", `color-mix(in srgb, var(--bcbackground-0) ${bgOpacity}%, transparent)`, "important");
contentMain.style.setProperty("backdrop-filter", `blur(${bgBlur}px)`, "important");
contentMain.style.setProperty("-webkit-backdrop-filter", `blur(${bgBlur}px)`, "important");
}
async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
if (!options.better_sidebar) return;
if (document.querySelector('#better-sidebar-container')) return;
@ -3127,9 +3234,9 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
contentMain?.style.setProperty("margin", "26px 38px 38px", "important");
contentMain?.style.setProperty("padding", "10px", "important");
contentMain?.style.setProperty("border-radius", "10px", "important");
contentMain?.style.setProperty("background", "color-mix(in srgb, var(--bcbackground-0) 45%, transparent)", "important");
contentMain?.style.setProperty("backdrop-filter", "blur(5px)", "important");
contentMain?.style.setProperty("-webkit-backdrop-filter", "blur(5px)", "important");
contentMain?.style.setProperty("background", `color-mix(in srgb, var(--bcbackground-0) ${Math.max(0, Math.min(100, Number(options.bg_opacity ?? 65)))}%, transparent)`, "important");
contentMain?.style.setProperty("backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
contentMain?.style.setProperty("-webkit-backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
}
const sidebarParent = layoutMode === "course" && leftSide ? leftSide : mainWrapper;
if (layoutMode === "course" && leftSide) {
@ -4823,7 +4930,6 @@ function applyAestheticChanges() {
if (options.hide_feedback === true) style.textContent += ".recent_feedback {display: none}";
if (options.full_width === true) style.textContent += "#wrapper,.ic-Layout-wrapper{max-width:100%!important}";
if (options.center_cards === true) style.textContent += ".ic-DashboardCard__box__container{display:flex!important;flex-wrap:wrap!important;justify-content:center!important;align-items:flex-start!important}";
if (options.customCardStyles === true) {
if (options.imageSize !== undefined && options.imageSize !== 100) style.textContent += `.ic-DashboardCard__header_image {transform: scale(${options.imageSize / 100})!important; }`;
if (options.cardRoundness !== undefined && options.cardRoundness !== 5) style.textContent += `.ic-DashboardCard {border-radius: ${options.cardRoundness}px!important;}`;

View File

@ -35,6 +35,10 @@ const syncedSubOptions = [
"customBackgroundNasaDaily",
"fitImageToScreen",
"sidebar_scale",
"bg_opacity",
"sidebar_opacity",
"bg_blur",
"sidebar_blur",
];
const localSwitches = [];
@ -46,7 +50,7 @@ const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_he
const exportSidebar = ["better_sidebar", "sidebar_scale"];
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_hr24", "todo_separate_scrollbar"];
const exportGpa = ["gpa_calc", "gpa_calc_prepend", "gpa_calc_cumulative", "gpa_calc_weighted"];
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen"];
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen", "bg_opacity", "sidebar_opacity", "bg_blur", "sidebar_blur"];
// Master "On/off toggles" = every visual toggle (no GPA, no dark-mode schedule,
// no personal productivity features).
const exportToggles = ["dark_mode"].concat(exportCardColorToggles, exportLayout, exportSidebar, exportTodo);
@ -93,6 +97,10 @@ const defaultOptions = {
"better_todo": true,
"better_sidebar": false,
"sidebar_scale": 100,
"bg_opacity": 65,
"sidebar_opacity": 100,
"bg_blur": 8,
"sidebar_blur": 0,
"todo_hr24": false,
"todo_separate_scrollbar": false,
"condensed_cards": false,
@ -255,6 +263,32 @@ function setupSidebarScaleSlider(initial) {
});
}
// Generic range slider (0..max). `key` is the storage id, `valueId` is the
// <span> that shows the live value, `resetId` is the small reset button that
// restores `defaultValue`. `unit` is appended to the displayed value ("%" or "px").
function setupRangeSlider(key, sliderId, valueId, resetId, defaultValue, max, unit, initial) {
const el = document.querySelector("#" + sliderId);
const out = document.querySelector("#" + valueId);
if (!el || !out) return;
const value = Math.max(0, Math.min(max, parseInt(initial)));
if (isNaN(value)) return;
el.value = value;
out.textContent = `${value}${unit}`;
el.addEventListener("input", function () {
out.textContent = `${this.value}${unit}`;
chrome.storage.sync.set({ [key]: parseInt(this.value) });
});
const reset = document.querySelector("#" + resetId);
if (reset) {
reset.addEventListener("click", () => {
const v = Math.max(0, Math.min(max, parseInt(defaultValue)));
el.value = v;
out.textContent = `${v}${unit}`;
chrome.storage.sync.set({ [key]: v });
});
}
}
// Progress display dropdown. Normalizes legacy boolean values:
// true -> rings, false/undefined -> none.
function setupProgressRingsSelect(initial) {
@ -345,10 +379,12 @@ function setupCardHeightInput(initial) {
function setupCustomBackgroundLink(initial) {
let el = document.querySelector("#customBackgroundLink");
el.value = initial || "";
toggleOpacityOptions();
el.addEventListener("input", (e) => {
chrome.storage.sync.set({ "customBackgroundLink": e.target.value });
renderBackgroundPresetSelection();
})
toggleOpacityOptions();
});
}
function setupCustomBackgroundScale(initial) {
@ -429,6 +465,7 @@ function displayBackgroundPresets() {
document.querySelector("#customBackgroundScaleValue").textContent = `${backgroundScale}%`;
chrome.storage.sync.set({ "customBackgroundLink": backgroundUrl, "customBackgroundScale": backgroundScale });
renderBackgroundPresetSelection();
toggleOpacityOptions();
});
});
renderBackgroundPresetSelection();
@ -446,6 +483,21 @@ function toggleBetterSidebarSubOptions(betterSidebarOn) {
}
}
// The opacity sliders live under "Edit dark mode" but only make sense (and
// only show) when a custom background is active, since transparency without a
// background image behind these surfaces would just expose the dark body.
function customBackgroundActive() {
const link = document.querySelector("#customBackgroundLink");
const daily = document.querySelector("#customBackgroundDaily");
const nasa = document.querySelector("#customBackgroundNasaDaily");
return (link && link.value.trim() !== "") || (daily && daily.checked) || (nasa && nasa.checked);
}
function toggleOpacityOptions() {
const el = document.getElementById("opacity-options");
if (el) el.style.display = customBackgroundActive() ? "" : "none";
}
// Hide the standalone feedback toggle when Better Todo's own sub-option replaces it.
function toggleBetterTodoSubOptions(betterTodoOn) {
const hideFeedbackEl = document.getElementById("hide_feedback");
@ -839,6 +891,22 @@ function setup() {
{
identifier: "sidebar_scale",
setup: (initial) => setupSidebarScaleSlider(initial),
},
{
identifier: "bg_opacity",
setup: (initial) => setupRangeSlider("bg_opacity", "bgOpacitySlider", "bgOpacityValue", "bgOpacityReset", 65, 100, "%", initial),
},
{
identifier: "sidebar_opacity",
setup: (initial) => setupRangeSlider("sidebar_opacity", "sidebarOpacitySlider", "sidebarOpacityValue", "sidebarOpacityReset", 100, 100, "%", initial),
},
{
identifier: "bg_blur",
setup: (initial) => setupRangeSlider("bg_blur", "bgBlurSlider", "bgBlurValue", "bgBlurReset", 8, 30, "px", initial),
},
{
identifier: "sidebar_blur",
setup: (initial) => setupRangeSlider("sidebar_blur", "sidebarBlurSlider", "sidebarBlurValue", "sidebarBlurReset", 0, 30, "px", initial),
},
{
identifier: "card_limit",
@ -935,6 +1003,7 @@ function setup() {
chrome.storage.sync.set(JSON.parse(`{"${option}": ${status}}`));
}
syncCustomBackgroundDailyState(document.querySelector("#customBackgroundDaily")?.checked === true || document.querySelector("#customBackgroundNasaDaily")?.checked === true);
toggleOpacityOptions();
});
const value = sync[option] !== undefined ? sync[option] : defaultOptions.sync[option];
document.querySelector("#" + option).checked = value;
@ -942,6 +1011,7 @@ function setup() {
syncCustomBackgroundDailyState(sync.customBackgroundDaily === true || sync.customBackgroundNasaDaily === true);
toggleDarkModeDisable(sync.auto_dark);
displayBackgroundPresets();
toggleOpacityOptions();
});
const specialOptions = menu.special.map(obj => obj.identifier);
@ -986,6 +1056,10 @@ function setup() {
const msg = chrome.i18n.getMessage(el.dataset.i18nPlaceholder);
if (msg) el.placeholder = msg;
});
document.querySelectorAll('[data-i18n-title]').forEach(el => {
const msg = chrome.i18n.getMessage(el.dataset.i18nTitle);
if (msg) el.title = msg;
});
// header feature search (search bar that jumps to features)
setupFeatureSearch(menu);
@ -1371,6 +1445,7 @@ function setup() {
document.querySelector("#customBackgroundScaleValue").textContent = "100%";
renderBackgroundPresetSelection();
sendFromPopup("updateBackground");
toggleOpacityOptions();
});
const applyFontsDropdownState = (isOpen) => {
@ -1584,6 +1659,10 @@ function saveCurrentTheme() {
"customBackgroundDaily": current["customBackgroundDaily"],
"customBackgroundNasaDaily": current["customBackgroundNasaDaily"],
"fitImageToScreen": current["fitImageToScreen"],
"bg_opacity": current["bg_opacity"],
"sidebar_opacity": current["sidebar_opacity"],
"bg_blur": current["bg_blur"],
"sidebar_blur": current["sidebar_blur"],
}
const now = new Date();
local["saved_themes"][now.getTime()] = trimmed;

View File

@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Canvas Refined",
"description": "Even More Feature packed extension for Canvas.",
"version": "6.3.1",
"version": "6.4.0",
"icons": {
"16": "icon/icon-16.png",
"32": "icon/icon-32.png",