mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-09-22 04:54:53 +02:00
background work + bugfixes
daily backgrounds + some smaller fixes and some UI changes
This commit is contained in:
parent
25ff281bf9
commit
2c65b5ab17
@ -254,7 +254,16 @@
|
||||
<span class="option-name" data-i18n="better_sidebar">Better Sidebar</span>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<div style="margin-top: 5px">
|
||||
<div class="option" id="remlogo" style="margin-top:2px">
|
||||
<input type="radio" id="off" name="remlogo">
|
||||
<input type="radio" id="on" name="remlogo">
|
||||
<div class="slider">
|
||||
<div class="sliderknob"></div>
|
||||
<div class="sliderbg"></div>
|
||||
</div>
|
||||
<span class="option-name" data-i18n="remlogo">Remove sidebar logo</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px" id="sidebarScaleSub">
|
||||
<span class="sub-text">Sidebar scale: </span><span id="sidebarScaleValue"></span><span class="sub-text">%</span>
|
||||
<input type="range" min="70" max="150" id="sidebarScaleSlider">
|
||||
</div>
|
||||
@ -303,14 +312,6 @@
|
||||
</div>
|
||||
<span class="option-name" data-i18n="full_width">Full Width Fix</span>
|
||||
</div>
|
||||
<div class="option" id="remlogo">
|
||||
<input type="radio" id="off" name="remlogo">
|
||||
<input type="radio" id="on" name="remlogo">
|
||||
<div class="slider">
|
||||
<div class="sliderknob"></div>
|
||||
<div class="sliderbg"></div>
|
||||
</div><span class="option-name" data-i18n="remlogo">Remove sidebar logo</span>
|
||||
</div>
|
||||
<div class="option" id="hide_feedback">
|
||||
<input type="radio" id="off" name="hide_feedback">
|
||||
<input type="radio" id="on" name="hide_feedback">
|
||||
@ -941,16 +942,30 @@
|
||||
<div class="custom-font option-container">
|
||||
<h3 class="header-small">Custom Background</h3>
|
||||
<p>Pick a preset or use your own image url.</p>
|
||||
<div class="background-preset-grid" id="background-presets"></div>
|
||||
<div class="background-scale-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;">Image scale</span>
|
||||
<span class="sub-text" id="customBackgroundScaleValue">100%</span>
|
||||
<div id="customBackgroundManualControls">
|
||||
<div class="background-preset-grid" id="background-presets"></div>
|
||||
<div class="background-scale-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;">Image scale</span>
|
||||
<span class="sub-text" id="customBackgroundScaleValue">100%</span>
|
||||
</div>
|
||||
<input type="range" min="50" max="200" step="1" id="customBackgroundScale">
|
||||
</div>
|
||||
<input type="range" min="50" max="200" step="1" id="customBackgroundScale">
|
||||
<input style="width:85%;" class="card-input" id="customBackgroundLink" placeholder="https://...">
|
||||
<button id="clearCustomBackground" class="big-button" style="margin-top: 8px;">Clear Background</button>
|
||||
</div>
|
||||
<div class="sub-option" style="margin-top:10px;">
|
||||
<input type="checkbox" id="customBackgroundDaily" name="customBackgroundDaily">
|
||||
<label for="customBackgroundDaily" class="sub-text">Daily Random Image</label>
|
||||
</div>
|
||||
<div class="sub-option" style="margin-top:6px;">
|
||||
<input type="checkbox" id="customBackgroundNasaDaily" name="customBackgroundNasaDaily">
|
||||
<label for="customBackgroundNasaDaily" class="sub-text">NASA Astronomy Picture of the Day</label>
|
||||
</div>
|
||||
<div class="sub-option" style="margin-top:8px;">
|
||||
<input type="checkbox" id="fitImageToScreen" name="fitImageToScreen">
|
||||
<label for="fitImageToScreen" class="sub-text">Fit image to screen</label>
|
||||
</div>
|
||||
<input style="width:85%;" class="card-input" id="customBackgroundLink" placeholder="https://...">
|
||||
<button id="clearCustomBackground" class="big-button" style="margin-top: 8px;">Clear Background</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -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");
|
||||
|
||||
386
js/content.js
386
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 = `
|
||||
<div id="nasa-info-icon" style="display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;background:rgba(30,30,30,0.85);border:1px solid rgba(255,255,255,0.15);cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.4);">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#e2e2e2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" y1="16" x2="12" y2="12"></line>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="nasa-info-panel" style="display:none;position:absolute;bottom:calc(100% + 10px);right:0;background:#1e1e1e;border:1px solid #3c3c3c;border-radius:8px;padding:14px 18px;width:340px;max-width:calc(100vw - 40px);box-shadow:0 4px 16px rgba(0,0,0,0.4);">
|
||||
<div id="nasa-info-title" style="font-weight:600;font-size:14px;margin-bottom:4px;color:#f5f5f5;"></div>
|
||||
<div id="nasa-info-date" style="font-size:12px;color:#ababab;margin-bottom:4px;"></div>
|
||||
<div id="nasa-info-credit" style="font-size:12px;color:#dfa581;margin-bottom:8px;"></div>
|
||||
<div id="nasa-info-explanation" style="font-size:12px;color:#e2e2e2;line-height:1.5;max-height:200px;overflow-y:auto;white-space:pre-wrap;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 <html> 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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
91
js/popup.js
91
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;
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user