Pick a preset or use your own image url.
-
-
-
-
Image scale
-
100%
+
+
+
+
+ Image scale
+ 100%
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
diff --git a/js/background.js b/js/background.js
index e9a9c23..85aec37 100644
--- a/js/background.js
+++ b/js/background.js
@@ -104,6 +104,10 @@ chrome.runtime.onInstalled.addListener(function () {
"customCardStyles": false,
"customBackgroundLink": "",
"customBackgroundScale": 100,
+ "customBackgroundDaily": false,
+ "customBackgroundNasaDaily": false,
+ "nasaInfoOverlay": false,
+ "fitImageToScreen": false,
}
};
@@ -122,6 +126,11 @@ chrome.runtime.onInstalled.addListener(function () {
newLocalOptions[option] = default_options["local"][option];
})
+ // migrate old setting name
+ if (sync["nasaFitToScreen"] !== undefined && sync["fitImageToScreen"] === undefined) {
+ newSyncOptions["fitImageToScreen"] = sync["nasaFitToScreen"];
+ }
+
if (Object.keys(newLocalOptions).length > 0) {
chrome.storage.local.set(newLocalOptions);
}
@@ -139,4 +148,90 @@ chrome.runtime.onInstalled.addListener(function () {
});
});
+// The NASA APOD API with the demo key is limited to 30 requests/hour and 50/day.
+// All calls are serialized through this worker and gated against those limits.
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ if (message?.type === "getNasaBackground") {
+ getNasaBackground().then(sendResponse);
+ return true;
+ }
+});
+
+let nasaRequestQueue = Promise.resolve();
+function queuedNasaTask(task) {
+ const run = nasaRequestQueue.then(task, task);
+ nasaRequestQueue = run.catch(() => {});
+ return run;
+}
+
+async function getNasaBackground() {
+ return queuedNasaTask(async () => {
+ const date = new Date();
+ for (let i = 0; i < 7; i++) {
+ const dateStr = date.toISOString().slice(0, 10);
+ const cacheKey = `nasa_apod_${dateStr}`;
+ const metadataKey = `nasa_apod_meta_${dateStr}`;
+ const cached = await chrome.storage.local.get([cacheKey, metadataKey]);
+ if (cached[cacheKey]) return cached[cacheKey];
+
+ // Don't re-probe dates the API already told us don't exist
+ const missingKey = `nasa_apod_missing_${dateStr}`;
+ const missing = await chrome.storage.local.get(missingKey);
+ if (missing[missingKey]) {
+ date.setDate(date.getDate() - 1);
+ continue;
+ }
+
+ const data = await callNasaApi(dateStr);
+ if (data === "ratelimited" || data === null) return null;
+ if (data === "missing") {
+ await chrome.storage.local.set({ [missingKey]: true });
+ date.setDate(date.getDate() - 1);
+ continue;
+ }
+
+ const url = data.thumbnail_url || data.hdurl || data.url;
+ if (!url) return null;
+ const result = { url, scale: 100, date: dateStr };
+ await chrome.storage.local.set({ [cacheKey]: result });
+ await chrome.storage.local.set({ [metadataKey]: { title: data.title || "", date: data.date || dateStr, copyright: data.copyright || "", explanation: data.explanation || "" } });
+ return result;
+ }
+ return null;
+ });
+}
+
+async function callNasaApi(dateStr) {
+ const now = Date.now();
+ const { nasa_api_calls = [] } = await chrome.storage.local.get("nasa_api_calls");
+ const lastHour = nasa_api_calls.filter(t => now - t < 3600 * 1000);
+ const lastDay = nasa_api_calls.filter(t => now - t < 24 * 3600 * 1000);
+ if (lastHour.length >= 30 || lastDay.length >= 50) {
+ console.warn("[CanvasRefined] NASA API rate limit reached, skipping request");
+ return "ratelimited";
+ }
+
+ let response;
+ try {
+ response = await fetch(`https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&thumbs=true&date=${dateStr}`);
+ } catch (error) {
+ console.error("[CanvasRefined] Failed to fetch NASA APOD:", error);
+ return null;
+ }
+
+ // The request was made, so it counts against the quota
+ await chrome.storage.local.set({ nasa_api_calls: [...lastDay, now] });
+
+ if (response.status === 429) return "ratelimited";
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ if (errorData.code === 404 && (errorData.msg || "").toLowerCase().includes("no data available")) {
+ return "missing";
+ }
+ return null;
+ }
+
+ return await response.json().catch(() => null);
+}
+
// chrome.runtime.setUninstallURL("https://diditupe.dev/canvasrefined/goodbye");
diff --git a/js/content.js b/js/content.js
index bd64c5e..6af0c8a 100644
--- a/js/content.js
+++ b/js/content.js
@@ -30,6 +30,10 @@ function isConversationsPage() {
return /^\/conversations(?:\/|$)/.test(current_page);
}
+function isAccountsPage() {
+ return /^\/accounts(?:\/|$)/.test(current_page);
+}
+
function isProfilePage() {
return /^\/profile(?:\/|$)/.test(current_page);
}
@@ -58,72 +62,6 @@ function addSubmissionPageButton() {
}, true);
}
-let sequenceFooterObserver = null;
-
-function isAssignmentPage() {
- return /^\/courses\/\d+\/assignments(?:\/\d+)?(?:\/|$)/.test(current_page);
-}
-
-function removeSequenceFooter() {
- if (!isAssignmentPage()) return false;
- const sequenceFooter = document.getElementById("sequence_footer");
- if (!sequenceFooter) return false;
- sequenceFooter.remove();
- return true;
-}
-
-function watchSequenceFooter() {
- if (!isAssignmentPage()) return;
- if (removeSequenceFooter()) return;
- if (sequenceFooterObserver) return;
-
- sequenceFooterObserver = new MutationObserver(() => {
- if (removeSequenceFooter() && sequenceFooterObserver) {
- sequenceFooterObserver.disconnect();
- sequenceFooterObserver = null;
- }
- });
-
- sequenceFooterObserver.observe(document.documentElement, { childList: true, subtree: true });
- setTimeout(() => {
- if (sequenceFooterObserver) {
- sequenceFooterObserver.disconnect();
- sequenceFooterObserver = null;
- }
- }, 10000);
-}
-
-function ensureSubmissionPageButton() {
- const assignmentLink = getSubmissionAssignmentLink();
- if (!assignmentLink) return false;
- const content = document.getElementById("content");
- if (!content) return false;
- if (content.querySelector("#canvasrefined-assignment-return")) return true;
- addSubmissionPageButton();
- return Boolean(content.querySelector("#canvasrefined-assignment-return"));
-}
-
-function watchSubmissionPageButton() {
- if (!getSubmissionAssignmentLink()) return;
- if (ensureSubmissionPageButton()) return;
- if (submissionPageButtonObserver) return;
-
- submissionPageButtonObserver = new MutationObserver(() => {
- if (ensureSubmissionPageButton() && submissionPageButtonObserver) {
- submissionPageButtonObserver.disconnect();
- submissionPageButtonObserver = null;
- }
- });
-
- submissionPageButtonObserver.observe(document.documentElement, { childList: true, subtree: true });
- setTimeout(() => {
- if (submissionPageButtonObserver) {
- submissionPageButtonObserver.disconnect();
- submissionPageButtonObserver = null;
- }
- }, 10000);
-}
-
function addProfileLogoutPageButton() {
if (!isProfilePage()) return;
const content = document.getElementById("content");
@@ -168,6 +106,194 @@ function watchProfileLogoutPageButton() {
}, 10000);
}
+function ensureSubmissionPageButton() {
+ const assignmentLink = getSubmissionAssignmentLink();
+ if (!assignmentLink) return false;
+ const content = document.getElementById("content");
+ if (!content) return false;
+ if (content.querySelector("#canvasrefined-assignment-return")) return true;
+ addSubmissionPageButton();
+ return Boolean(content.querySelector("#canvasrefined-assignment-return"));
+}
+
+function watchSequenceFooter() {
+ if (!isAssignmentPage()) return;
+ if (removeSequenceFooter()) return;
+ if (sequenceFooterObserver) return;
+
+ sequenceFooterObserver = new MutationObserver(() => {
+ if (removeSequenceFooter() && sequenceFooterObserver) {
+ sequenceFooterObserver.disconnect();
+ sequenceFooterObserver = null;
+ }
+ });
+
+ sequenceFooterObserver.observe(document.documentElement, { childList: true, subtree: true });
+ setTimeout(() => {
+ if (sequenceFooterObserver) {
+ sequenceFooterObserver.disconnect();
+ sequenceFooterObserver = null;
+ }
+ }, 10000);
+}
+
+function watchSubmissionPageButton() {
+ if (!getSubmissionAssignmentLink()) return;
+ if (ensureSubmissionPageButton()) return;
+ if (submissionPageButtonObserver) return;
+
+ submissionPageButtonObserver = new MutationObserver(() => {
+ if (ensureSubmissionPageButton() && submissionPageButtonObserver) {
+ submissionPageButtonObserver.disconnect();
+ submissionPageButtonObserver = null;
+ }
+ });
+
+ submissionPageButtonObserver.observe(document.documentElement, { childList: true, subtree: true });
+ setTimeout(() => {
+ if (submissionPageButtonObserver) {
+ submissionPageButtonObserver.disconnect();
+ submissionPageButtonObserver = null;
+ }
+ }, 10000);
+}
+
+async function getActiveCustomBackground() {
+ const syncOpts = await chrome.storage.sync.get([
+ "customBackgroundDaily",
+ "customBackgroundNasaDaily",
+ "customBackgroundLink",
+ "customBackgroundScale",
+ ]);
+
+ console.log("[CanvasRefined] getActiveCustomBackground:", syncOpts);
+
+ if (syncOpts.customBackgroundNasaDaily === true) {
+ console.log("[CanvasRefined] Using NASA APOD");
+ return await getNasaDailyBackground();
+ }
+
+ if (syncOpts.customBackgroundDaily === true) {
+ console.log("[CanvasRefined] Using Wikimedia Featured");
+ const dailyPreset = await getDailyBackgroundPreset();
+ if (dailyPreset) {
+ console.log("[CanvasRefined] Wikimedia URL:", dailyPreset.url);
+ return {
+ url: dailyPreset.url,
+ scale: dailyPreset.scale,
+ };
+ }
+ console.log("[CanvasRefined] Wikimedia returned null");
+ }
+
+ if (syncOpts.customBackgroundLink && syncOpts.customBackgroundLink !== "") {
+ console.log("[CanvasRefined] Using custom link");
+ return {
+ url: syncOpts.customBackgroundLink,
+ scale: syncOpts.customBackgroundScale || 100,
+ };
+ }
+
+ console.log("[CanvasRefined] No custom background");
+ return null;
+}
+
+async function getDailyBackgroundPreset() {
+ const today = new Date();
+ const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
+ const cacheKey = `picsum_daily_${dateStr}`;
+ const cached = await chrome.storage.local.get(cacheKey);
+ if (cached[cacheKey]) return cached[cacheKey];
+
+ const url = `https://picsum.photos/seed/${dateStr}/1920/1080`;
+ const result = { url, scale: 100 };
+ await chrome.storage.local.set({ [cacheKey]: result });
+ return result;
+}
+
+async function getNasaDailyBackground() {
+ try {
+ return await chrome.runtime.sendMessage({ type: "getNasaBackground" });
+ } catch (error) {
+ console.error("[CanvasRefined] Failed to fetch NASA APOD:", error);
+ return null;
+ }
+}
+
+let nasaInfoOverlayEl = null;
+
+function isDashboardPage() {
+ return !!document.querySelector("#DashboardCard_Container");
+}
+
+function createNasaInfoOverlay() {
+ if (options.customBackgroundNasaDaily !== true) return;
+ if (nasaInfoOverlayEl || !isDashboardPage()) return;
+
+ const contentMain = document.querySelector("#content.ic-Layout-contentMain, .ic-Layout-contentMain");
+ if (!contentMain) return;
+ if (getComputedStyle(contentMain).position === "static") {
+ contentMain.style.position = "relative";
+ }
+
+ nasaInfoOverlayEl = document.createElement("div");
+ nasaInfoOverlayEl.id = "canvasrefined-nasa-info-overlay";
+ nasaInfoOverlayEl.style.cssText = "position:absolute;right:24px;bottom:24px;z-index:9999;";
+ nasaInfoOverlayEl.innerHTML = `
+
+
+
+
+ `;
+
+ const icon = nasaInfoOverlayEl.querySelector("#nasa-info-icon");
+ const panel = nasaInfoOverlayEl.querySelector("#nasa-info-panel");
+
+ const showPanel = async () => {
+ const dateStr = new Date().toISOString().slice(0, 10);
+ const cacheKey = `nasa_apod_${dateStr}`;
+ const cached = await chrome.storage.local.get(cacheKey);
+ const metaDate = cached[cacheKey]?.date || dateStr;
+ const metadataKey = `nasa_apod_meta_${metaDate}`;
+ const metadata = await chrome.storage.local.get(metadataKey);
+ const meta = metadata[metadataKey];
+ if (meta) {
+ document.getElementById("nasa-info-title").textContent = meta.title || "";
+ document.getElementById("nasa-info-date").textContent = `Date: ${meta.date}`;
+ document.getElementById("nasa-info-credit").textContent = meta.copyright ? `Credit: ${meta.copyright}` : "";
+ document.getElementById("nasa-info-explanation").textContent = meta.explanation || "No description available.";
+ panel.style.display = "block";
+ }
+ };
+
+ const hidePanel = () => {
+ panel.style.display = "none";
+ };
+
+ icon.addEventListener("mouseenter", showPanel);
+ icon.addEventListener("mouseleave", hidePanel);
+ panel.addEventListener("mouseenter", showPanel);
+ panel.addEventListener("mouseleave", hidePanel);
+
+ contentMain.appendChild(nasaInfoOverlayEl);
+}
+
+function removeNasaInfoOverlay() {
+ if (nasaInfoOverlayEl) {
+ nasaInfoOverlayEl.remove();
+ nasaInfoOverlayEl = null;
+ }
+}
+
function getSidebarStateMode(mode = getSidebarLayoutMode()) {
return mode === "course" ? "course" : "dashboard";
}
@@ -348,6 +474,7 @@ async function reminderWatch() {
}
function updateReminders() {
+ if (!assignments || typeof assignments.then !== "function") return;
const fiveDays = 1000 * 60 * 60 * 24 * 5;
const now = (new Date()).getTime();
const list = [];
@@ -438,6 +565,17 @@ function isDomainCanvasPage() {
}
function startExtension() {
+ // Remove footer robustly - run first so a crash below can't block it
+ const removeFooter = () => {
+ const footer = document.querySelector('footer#footer.ic-app-footer, footer#footer');
+ if (footer) footer.remove();
+ };
+ removeFooter();
+ const footerObserver = new MutationObserver(() => {
+ removeFooter();
+ });
+ footerObserver.observe(document.documentElement, { childList: true, subtree: true });
+
toggleDarkMode();
chrome.storage.sync.get(["better_sidebar", "sidebar_scale"], result => {
@@ -463,7 +601,6 @@ function startExtension() {
//getClassAverages();
- setTimeout(() => document.getElementById("footer")?.remove(), 800);
setTimeout(() => runDarkModeFixer(false), 800);
setTimeout(() => runDarkModeFixer(false), 4500);
});
@@ -567,6 +704,21 @@ function applyOptionsChanges(changes) {
applyAestheticChanges();
break;
case "customBackgroundScale":
+ applyCustomBackground();
+ break;
+ case "customBackgroundDaily":
+ applyCustomBackground();
+ removeNasaInfoOverlay();
+ break;
+ case "customBackgroundNasaDaily":
+ applyCustomBackground();
+ if (options.customBackgroundNasaDaily === true) {
+ createNasaInfoOverlay();
+ } else {
+ removeNasaInfoOverlay();
+ }
+ break;
+ case "fitImageToScreen":
applyCustomBackground();
break;
// case "show_updates":
@@ -674,22 +826,31 @@ function ensureBetterSidebar() {
setupBetterSidebar(getSidebarLayoutMode());
}
-function applyCustomBackground() {
+async function applyCustomBackground() {
// let style = document.querySelector("#DashboardCard_Container")
let style = document.querySelector("#canvasrefined-background") || document.createElement('style');
style.id = "canvasrefined-background";
-
- if (options.customBackgroundLink && options.customBackgroundLink !== "") {
- const backgroundScale = Number(options.customBackgroundScale) || 100;
- style.textContent = `
+
+ const activeBackground = await getActiveCustomBackground();
+ console.log("[CanvasRefined] activeBackground:", activeBackground);
+ if (!activeBackground) {
+ if (style.isConnected) style.remove();
+ return;
+ }
+
+ const backgroundScale = Number(activeBackground.scale) || 100;
+ const backgroundUrl = JSON.stringify(activeBackground.url);
+ const fitToScreen = options.fitImageToScreen === true;
+ console.log("[CanvasRefined] Applying background:", activeBackground.url, "fitToScreen:", fitToScreen);
+ style.textContent = `
#wrapper {
- background-image: url('${options.customBackgroundLink}') !important;
+ background-image: url(${backgroundUrl}) !important;
background-repeat: no-repeat !important;
background-position: center center !important;
background-attachment: fixed !important;
}
@media (orientation: landscape) {
- #wrapper { background-size: ${backgroundScale}% auto !important; }
+ #wrapper { background-size: ${fitToScreen ? 'cover' : backgroundScale + '% auto'} !important; }
}
@media (orientation: portrait) {
#wrapper { background-size: cover !important; }
@@ -698,6 +859,20 @@ function applyCustomBackground() {
background: none !important;
/* backdrop-filter: blur(10px) !important; */
border-radius: 5px;
+ padding-left: 20px !important;
+ }
+ #dashboard_header_container {
+ 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;
+ 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;
}
#right-side-wrapper {
// backdrop-filter: blur(10px) !important;
@@ -837,8 +1012,7 @@ function applyCustomBackground() {
tr.student_assignment.assignment_graded.editable > * {
border:none!important
}`;
- // TODO: liquid glass?
- }
+ // TODO: liquid glass?
document.documentElement.appendChild(style);
}
@@ -903,6 +1077,7 @@ function checkDashboardReady() {
loadDashboardNotes();
setupGPACalc();
showUpdateMsg();
+ createNasaInfoOverlay();
}
const rightSide = document.querySelector("#right-side");
@@ -942,7 +1117,7 @@ function recieveMessage(request, sender, sendResponse) {
case ("getcolors"): sendResponse(getCardColors()); break;
case ("inspect"): sendResponse(inspectDarkMode(true)); break;
case ("fixdm"): sendResponse(runDarkModeFixer(true)); break;
- case ("updateBackground"): clearCustomBackground(); sendResponse(true); break;
+ case ("updateBackground"): applyCustomBackground(); sendResponse(true); break;
default: sendResponse(true);
}
}
@@ -2442,8 +2617,13 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
const contentMain = document.querySelector(".ic-Layout-contentMain");
contentMain?.style.setProperty("flex", "1 1 auto");
contentMain?.style.setProperty("min-width", "0");
+ const notRightSide = document.getElementById("not_right_side");
+ if (notRightSide && isAccountsPage()) {
+ notRightSide.style.setProperty("width", "100%");
+ notRightSide.style.setProperty("max-width", "100%");
+ notRightSide.style.setProperty("min-width", "0");
+ }
if (layoutMode === "course" && leftSide) {
- const notRightSide = document.getElementById("not_right_side");
const rightSideWrapper = document.getElementById("right-side-wrapper");
const sectionTabs = document.getElementById("section-tabs");
leftSide.style.setProperty("padding-top", "0", "important");
@@ -3790,7 +3970,7 @@ function setupGPACalc() {
grades?.then(result => {
const sortableContainer = document.querySelector(".ic-DashboardCard__box__container");
- const dashboardContainer = document.querySelector("#DashboardCard_Container");
+ const dashboardContainer = sortableContainer || document.querySelector("#DashboardCard_Container");
if (!dashboardContainer) return;
let container2 = document.querySelector(".canvasrefined-gpa-card");
@@ -3850,17 +4030,15 @@ function setupGPACalc() {
if (cumulative) cumulative.style.display = options.gpa_calc_cumulative ? "block" : "none";
const shouldPrepend = options.gpa_calc_prepend === true;
- const firstCard = shouldPrepend ? container : container2;
- const secondCard = shouldPrepend ? container2 : container;
-
- if (firstCard.parentElement !== dashboardContainer) {
- dashboardContainer.prepend(firstCard);
- }
- if (secondCard.parentElement !== dashboardContainer) {
- if (shouldPrepend) {
- dashboardContainer.prepend(secondCard);
- } else {
- dashboardContainer.appendChild(secondCard);
+ if (shouldPrepend) {
+ if (dashboardContainer.children[0] !== container || dashboardContainer.children[1] !== container2) {
+ dashboardContainer.insertBefore(container, dashboardContainer.firstChild);
+ dashboardContainer.insertBefore(container2, container.nextSibling);
+ }
+ } else {
+ if (dashboardContainer.lastElementChild !== container || container2.nextElementSibling !== container) {
+ dashboardContainer.appendChild(container2);
+ dashboardContainer.appendChild(container);
}
}
}
@@ -4013,23 +4191,39 @@ function changeFullWidth() {
function changeGradientCards() {
if (options.gradient_cards === true) {
let cardheads = document.querySelectorAll('.ic-DashboardCard__header_hero');
- let cardcss = document.querySelector("#gradientcss") || document.createElement('style');
- cardcss.id = "gradientcss";
- cardcss.textContent = "";
- document.documentElement.appendChild(cardcss);
+ // Only create + append the style once; never re-append an already-
+ // attached element, since appending to triggers the
+ // MutationObserver in checkDashboardReady() and re-runs this function.
+ let cardcss = document.querySelector("#gradientcss");
+ if (!cardcss) {
+ cardcss = document.createElement('style');
+ cardcss.id = "gradientcss";
+ document.documentElement.appendChild(cardcss);
+ }
+
+ // Build the full CSS into a string first, then only touch the DOM
+ // if the content actually changed. This keeps #gradientcss from being
+ // cleared/rewritten on every observer tick.
+ let css = "";
for (let i = 0; i < cardheads.length; i++) {
let colorone = cardheads[i].style.backgroundColor.split(',');
let [r, g, b] = [parseInt(colorone[0].split('(')[1]), parseInt(colorone[1]), parseInt(colorone[2])];
let [h, s, l] = [rgbToHsl(r, g, b)[0], rgbToHsl(r, g, b)[1], rgbToHsl(r, g, b)[2]];
let degree = ((h % 60) / 60) >= .66 ? 30 : ((h % 60) / 60) <= .33 ? -30 : 15;
let newh = h > 300 ? (360 - (h + 65)) + (65 + degree) : h + 65 + degree;
- cardcss.textContent += ".ic-DashboardCard:nth-of-type(" + (i + 1) + ") .ic-DashboardCard__header_hero{background: linear-gradient(115deg, hsl(" + h + "deg," + s + "%," + l + "%) 5%, hsl(" + newh + "deg," + s + "%," + l + "%) 100%)!important}";
+ css += ".ic-DashboardCard:nth-of-type(" + (i + 1) + ") .ic-DashboardCard__header_hero{background: linear-gradient(115deg, hsl(" + h + "deg," + s + "%," + l + "%) 5%, hsl(" + newh + "deg," + s + "%," + l + "%) 100%)!important}";
+ }
+
+ if (cardcss.textContent !== css) {
+ cardcss.textContent = css;
}
} else {
let cardcss = document.querySelector("#gradientcss");
- if (cardcss) cardcss.textContent = "";
+ if (cardcss && cardcss.textContent !== "") {
+ cardcss.textContent = "";
+ }
}
}
diff --git a/js/popup.js b/js/popup.js
index 6d665ca..bed498c 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -29,6 +29,9 @@ const syncedSubOptions = [
"cardHeight",
"customBackgroundLink",
"customBackgroundScale",
+ "customBackgroundDaily",
+ "customBackgroundNasaDaily",
+ "fitImageToScreen",
"sidebar_scale",
];
const localSwitches = [];
@@ -133,6 +136,9 @@ const defaultOptions = {
"customCardStyles": false,
"customBackgroundLink": "",
"customBackgroundScale": 100,
+ "customBackgroundDaily": false,
+ "customBackgroundNasaDaily": false,
+ "fitImageToScreen": false,
}
};
@@ -315,9 +321,41 @@ function setupCustomBackgroundScale(initial) {
});
}
+function getDailyBackgroundPreset() {
+ if (typeof backgroundPresets === "undefined" || !Array.isArray(backgroundPresets) || backgroundPresets.length === 0) {
+ return null;
+ }
+
+ const today = new Date();
+ const dayNumber = Math.floor(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()) / 86400000);
+ return backgroundPresets[Math.abs(dayNumber) % backgroundPresets.length];
+}
+
+function syncCustomBackgroundDailyState(isDaily) {
+ const manualControls = document.getElementById("customBackgroundManualControls");
+ if (manualControls) {
+ manualControls.style.opacity = isDaily ? "0.45" : "1";
+ manualControls.style.pointerEvents = isDaily ? "none" : "auto";
+ manualControls.style.filter = isDaily ? "grayscale(1)" : "none";
+ }
+
+ ["customBackgroundLink", "customBackgroundScale", "clearCustomBackground"].forEach(id => {
+ const el = document.getElementById(id);
+ if (el) el.disabled = isDaily;
+ });
+
+ document.querySelectorAll(".background-preset-card").forEach(button => {
+ button.disabled = isDaily;
+ });
+
+ renderBackgroundPresetSelection();
+}
+
function renderBackgroundPresetSelection() {
- const currentLink = document.querySelector("#customBackgroundLink")?.value || "";
- const currentScale = String(document.querySelector("#customBackgroundScale")?.value || "100");
+ const isDaily = document.querySelector("#customBackgroundDaily")?.checked === true || document.querySelector("#customBackgroundNasaDaily")?.checked === true;
+ const dailyPreset = isDaily ? getDailyBackgroundPreset() : null;
+ const currentLink = isDaily ? (dailyPreset?.url || "") : (document.querySelector("#customBackgroundLink")?.value || "");
+ const currentScale = isDaily ? String(dailyPreset?.scale || "100") : String(document.querySelector("#customBackgroundScale")?.value || "100");
document.querySelectorAll(".background-preset-card").forEach(button => {
const matchesLink = button.dataset.backgroundUrl === currentLink;
const matchesScale = button.dataset.backgroundScale === currentScale;
@@ -349,6 +387,29 @@ function displayBackgroundPresets() {
});
});
renderBackgroundPresetSelection();
+ syncCustomBackgroundDailyState(document.querySelector("#customBackgroundDaily")?.checked === true || document.querySelector("#customBackgroundNasaDaily")?.checked === true);
+}
+
+function toggleBetterSidebarSubOptions(betterSidebarOn) {
+ const remlogoEl = document.getElementById("remlogo");
+ if (remlogoEl) {
+ remlogoEl.style.display = betterSidebarOn ? "none" : "flex";
+ }
+ const sidebarScaleEl = document.getElementById("sidebarScaleSub");
+ if (sidebarScaleEl) {
+ sidebarScaleEl.style.display = betterSidebarOn ? "block" : "none";
+ }
+}
+
+// When the Better Todo List is on, its own "Hide Recent Feedback" sub-option
+// (todo_hide_feedback) replaces the standalone "Hide recent feedback" toggle,
+// so hide the big #hide_feedback toggle to avoid showing two controls for the
+// same thing. Mirrors toggleBetterSidebarSubOptions.
+function toggleBetterTodoSubOptions(betterTodoOn) {
+ const hideFeedbackEl = document.getElementById("hide_feedback");
+ if (hideFeedbackEl) {
+ hideFeedbackEl.style.display = betterTodoOn ? "none" : "flex";
+ }
}
function setup() {
@@ -376,6 +437,9 @@ function setup() {
"grade_hover",
// "hide_completed",
"hover_preview",
+ "customBackgroundDaily",
+ "customBackgroundNasaDaily",
+ "fitImageToScreen",
// "scheduledReminder",
"customCardStyles",
],
@@ -493,8 +557,16 @@ function setup() {
if (option === "auto_dark") {
toggleDarkModeDisable(status);
}
+ if (option === "better_sidebar") {
+ toggleBetterSidebarSubOptions(status);
+ }
+ if (option === "better_todo") {
+ toggleBetterTodoSubOptions(status);
+ }
});
});
+ toggleBetterSidebarSubOptions(sync["better_sidebar"] === true);
+ toggleBetterTodoSubOptions(sync["better_todo"] === true);
});
chrome.storage.sync.get(menu.checkboxes, sync => {
@@ -503,11 +575,21 @@ function setup() {
if (!checkbox) {console.log(option); return;}
checkbox.addEventListener("change", function (e) {
let status = this.checked;
- chrome.storage.sync.set(JSON.parse(`{"${option}": ${status}}`));
+ if (option === "customBackgroundDaily" && status) {
+ document.querySelector("#customBackgroundNasaDaily").checked = false;
+ chrome.storage.sync.set({ "customBackgroundDaily": true, "customBackgroundNasaDaily": false });
+ } else if (option === "customBackgroundNasaDaily" && status) {
+ document.querySelector("#customBackgroundDaily").checked = false;
+ chrome.storage.sync.set({ "customBackgroundNasaDaily": true, "customBackgroundDaily": false });
+ } else {
+ chrome.storage.sync.set(JSON.parse(`{"${option}": ${status}}`));
+ }
+ syncCustomBackgroundDailyState(document.querySelector("#customBackgroundDaily")?.checked === true || document.querySelector("#customBackgroundNasaDaily")?.checked === true);
});
const value = sync[option] !== undefined ? sync[option] : defaultOptions.sync[option];
document.querySelector("#" + option).checked = value;
});
+ syncCustomBackgroundDailyState(sync.customBackgroundDaily === true || sync.customBackgroundNasaDaily === true);
/*
document.querySelector('#autodark_start').value = result.auto_dark_start["hour"] + ":" + result.auto_dark_start["minute"];
document.querySelector('#autodark_end').value = result.auto_dark_end["hour"] + ":" + result.auto_dark_end["minute"];
@@ -662,7 +744,7 @@ function setup() {
final = { ...final, ...(await getExport(storage, ["custom_styles"])) };
break;
case "export-background":
- final = { ...final, ...(await getExport(storage, ["customBackgroundLink", "customBackgroundScale"])) };
+ final = { ...final, ...(await getExport(storage, ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "fitImageToScreen"])) };
break;
}
}
@@ -1270,6 +1352,7 @@ function saveCurrentTheme() {
"cardHeight": current["cardHeight"],
"customBackgroundLink": current["customBackgroundLink"],
"customBackgroundScale": current["customBackgroundScale"],
+ "customBackgroundDaily": current["customBackgroundDaily"],
}
const now = new Date();
local["saved_themes"][now.getTime()] = trimmed;
diff --git a/manifest.json b/manifest.json
index 890cb8d..4eb77c1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -25,7 +25,7 @@
"content_scripts": [
{
"matches": ["https://*/*"],
- "js": ["css/darkmodecss.js", "js/content.js"],
+ "js": ["css/darkmodecss.js", "js/backgrounds.js", "js/content.js"],
"css": ["css/content.css"],
"run_at": "document_start"
}