some bug fixes and attempted widget

This commit is contained in:
Guy Sandler 2026-07-13 08:23:20 -07:00
parent b366ec6e23
commit fb5651eda9
5 changed files with 180 additions and 6 deletions

View File

@ -65,6 +65,12 @@
"24hrformat": { "24hrformat": {
"message": "Use 24 hr Format" "message": "Use 24 hr Format"
}, },
"widgets": {
"message": "Widgets"
},
"widget_clock": {
"message": "Clock"
},
"dashboard_notes": { "dashboard_notes": {
"message": "Dashboard Notes" "message": "Dashboard Notes"
}, },

View File

@ -50,6 +50,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M22 9l-10 -4l-10 4l10 4l10 -4v6"></path><path d="M6 10.6v5.4a6 3 0 0 0 12 0v-5.4"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M22 9l-10 -4l-10 4l10 4l10 -4v6"></path><path d="M6 10.6v5.4a6 3 0 0 0 12 0v-5.4"></path></svg>
<span data-i18n="gpa_settings">GPA Settings</span> <span data-i18n="gpa_settings">GPA Settings</span>
</button> </button>
<button id="widgets-btn" class="big-button tab-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"/><path d="M9 9h6v6h-6z"/><path d="M3 10h2m-2 4h2m10 -10v2m-4 -2v2m-4 -2v2m10 8h2m-2 4h2"/></svg>
<span data-i18n="widgets">Widgets</span>
</button>
<button id="report-issue-btn" class="big-button tab-btn"> <button id="report-issue-btn" class="big-button tab-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 9v4"></path><path d="M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0z"></path><path d="M12 16h.01"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 9v4"></path><path d="M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0z"></path><path d="M12 16h.01"></path></svg>
<span data-i18n="report_issue">Report issue</span> <span data-i18n="report_issue">Report issue</span>
@ -955,6 +959,28 @@
</div> </div>
<div class="widgets-container tab" style="display:none">
<div class="tab-header">
<div>
<h2 style="margin-top: 0" data-i18n="widgets">Widgets</h2>
</div>
<button class="big-button back-btn" data-i18n="back">Back</button>
</div>
<div class="options">
<div class="option-container">
<div class="option" id="widget_clock">
<input type="radio" id="off" name="widget_clock">
<input type="radio" id="on" name="widget_clock">
<div class="slider">
<div class="sliderknob"></div>
<div class="sliderbg"></div>
</div>
<span class="option-name">Clock</span>
</div>
</div>
</div>
</div>
<div class="report-issue-container tab"> <div class="report-issue-container tab">
<div class="tab-header"> <div class="tab-header">
<div> <div>

View File

@ -35,6 +35,7 @@ chrome.runtime.onInstalled.addListener(function () {
"assignments_done": [], "assignments_done": [],
"dashboard_grades": true, "dashboard_grades": true,
"assignment_date_format": false, "assignment_date_format": false,
"widget_clock": false,
"dashboard_notes": false, "dashboard_notes": false,
"dashboard_notes_text": "", "dashboard_notes_text": "",
"better_todo": false, "better_todo": false,

View File

@ -501,6 +501,9 @@ function applyOptionsChanges(changes) {
case "gradient_cards": case "gradient_cards":
changeGradientCards(); changeGradientCards();
break; break;
case "widget_clock":
loadWidgets();
break;
case "dashboard_notes": case "dashboard_notes":
loadDashboardNotes(); loadDashboardNotes();
break; break;
@ -884,12 +887,19 @@ function checkDashboardReady() {
if (mutation.type !== "childList") continue; if (mutation.type !== "childList") continue;
if (current_page == "/" || current_page == "" || current_page.match(/^\/courses\/(\d+)(?:\/|$)/)) { if (current_page == "/" || current_page == "" || current_page.match(/^\/courses\/(\d+)(?:\/|$)/)) {
if (dashboardReadyTimer) continue; if (dashboardReadyTimer) continue;
const dashboardCards = document.querySelector("#DashboardCard_Container");
if (dashboardCards) {
loadWidgets();
}
dashboardReadyTimer = setTimeout(() => { dashboardReadyTimer = setTimeout(() => {
dashboardReadyTimer = null; dashboardReadyTimer = null;
const dashboardCards = document.querySelector("#DashboardCard_Container"); const c = document.querySelector("#DashboardCard_Container");
if (dashboardCards) { if (c) {
let cards = document.querySelectorAll(".ic-DashboardCard"); let cards = document.querySelectorAll(".ic-DashboardCard");
loadWidgets();
changeGradientCards(); changeGradientCards();
setupCardAssignments(); setupCardAssignments();
loadCardAssignments(); loadCardAssignments();
@ -3059,6 +3069,7 @@ async function changeColorPreset(colors) {
let cards = document.querySelectorAll(".ic-DashboardCard__header"); let cards = document.querySelectorAll(".ic-DashboardCard__header");
let sortedCards = []; let sortedCards = [];
cards.forEach(card => { cards.forEach(card => {
if (card.closest('.canvasrefined-widget-clock')) return;
sortedCards.push({ "href": card.querySelector(".ic-DashboardCard__link").href, "el": card }); sortedCards.push({ "href": card.querySelector(".ic-DashboardCard__link").href, "el": card });
}); });
sortedCards.sort((a, b) => a.href > b.href ? 1 : -1); sortedCards.sort((a, b) => a.href > b.href ? 1 : -1);
@ -3283,6 +3294,7 @@ function insertGrades() {
let cards = document.querySelectorAll('.ic-DashboardCard'); let cards = document.querySelectorAll('.ic-DashboardCard');
if (cards.length === 0 || cards[0].querySelectorAll(".ic-DashboardCard__link").length === 0) return; if (cards.length === 0 || cards[0].querySelectorAll(".ic-DashboardCard__link").length === 0) return;
for (let i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
if (cards[i].classList.contains('canvasrefined-widget-clock')) continue;
let course_id = parseInt(cards[i].querySelector(".ic-DashboardCard__link").href.split("courses/")[1]); let course_id = parseInt(cards[i].querySelector(".ic-DashboardCard__link").href.split("courses/")[1]);
data.forEach(grade => { data.forEach(grade => {
if (course_id === grade.id) { if (course_id === grade.id) {
@ -3394,6 +3406,7 @@ function loadCardAssignments() {
const now = new Date(); const now = new Date();
cards.forEach(card => { cards.forEach(card => {
if (card.classList.contains('canvasrefined-widget-clock')) return;
let count = 0; let count = 0;
let link = card.querySelector(".ic-DashboardCard__link"); let link = card.querySelector(".ic-DashboardCard__link");
if (!link) return; if (!link) return;
@ -3486,9 +3499,12 @@ function loadCardAssignments2(c = null) {
function setupCardAssignments() { function setupCardAssignments() {
if (options.assignments_due !== true) return; if (options.assignments_due !== true) return;
try { try {
if (document.querySelectorAll('.ic-DashboardCard').length > 0 && document.querySelectorAll('.canvasrefined-card-container').length > 0) return; let widgetsCount = document.querySelectorAll('.canvasrefined-widget-clock .canvasrefined-card-container').length;
let containersCount = document.querySelectorAll('.canvasrefined-card-container').length - widgetsCount;
if (document.querySelectorAll('.ic-DashboardCard').length > 0 && containersCount > 0) return;
let cards = document.querySelectorAll('.ic-DashboardCard'); let cards = document.querySelectorAll('.ic-DashboardCard');
cards.forEach(card => { cards.forEach(card => {
if (card.classList.contains('canvasrefined-widget-clock')) return;
let assignmentContainer = card.querySelector(".canvasrefined-card-assignment") || makeElement("div", card, { "className": "canvasrefined-card-assignment" }); let assignmentContainer = card.querySelector(".canvasrefined-card-assignment") || makeElement("div", card, { "className": "canvasrefined-card-assignment" });
let assignmentsDueHeader = card.querySelector(".canvasrefined-card-header-container") || makeElement("div", assignmentContainer, { "className": "canvasrefined-card-header-container" }); let assignmentsDueHeader = card.querySelector(".canvasrefined-card-header-container") || makeElement("div", assignmentContainer, { "className": "canvasrefined-card-header-container" });
let assignmentsDueLabel = card.querySelector(".canvasrefined-card-header") || makeElement("h3", assignmentsDueHeader, { "className": "canvasrefined-card-header", "textContent": chrome.i18n.getMessage("due") }); let assignmentsDueLabel = card.querySelector(".canvasrefined-card-header") || makeElement("h3", assignmentsDueHeader, { "className": "canvasrefined-card-header", "textContent": chrome.i18n.getMessage("due") });
@ -3505,7 +3521,12 @@ Card customization
*/ */
function getCardId(card) { function getCardId(card) {
let id = card.querySelector(".ic-DashboardCard__link").href.split("courses/")[1]; let link = card.querySelector(".ic-DashboardCard__link");
if (!link) return -1;
let href = link.href;
if (!href || !href.includes("courses/")) return -1;
let id = href.split("courses/")[1];
if (!id) return -1;
// no ~ // no ~
if (!id.includes("~")) return id; if (!id.includes("~")) return id;
@ -3778,7 +3799,8 @@ function setupGPACalc() {
try { try {
grades?.then(result => { grades?.then(result => {
const dashboardContainer = document.querySelector(".ic-DashboardCard__box__container"); const sortableContainer = document.querySelector(".ic-DashboardCard__box__container");
const dashboardContainer = document.querySelector("#DashboardCard_Container");
if (!dashboardContainer) return; if (!dashboardContainer) return;
let container2 = document.querySelector(".canvasrefined-gpa-card"); let container2 = document.querySelector(".canvasrefined-gpa-card");
@ -3853,6 +3875,12 @@ function setupGPACalc() {
} }
} }
try {
if (sortableContainer && window.jQuery && window.jQuery.fn && window.jQuery.fn.sortable) {
window.jQuery(sortableContainer).sortable('refresh');
}
} catch (e) {}
calculateGPA2(); calculateGPA2();
}); });
} catch (e) { } catch (e) {
@ -3899,6 +3927,114 @@ function loadDashboardNotes() {
} }
} }
/*
Widgets
*/
let clockInterval;
function loadWidgets() {
if (options.widget_clock === true) {
let card = document.querySelector('.canvasrefined-widget-clock');
if (!card) {
let container = document.querySelector(".ic-DashboardCard__box__container");
if (!container) return;
let realCard = container.querySelector(".ic-DashboardCard");
if (realCard) {
card = realCard.cloneNode(false);
card.className = "ic-DashboardCard canvasrefined-widget-clock";
} else {
card = document.createElement("div");
card.className = "ic-DashboardCard canvasrefined-widget-clock";
}
card.setAttribute("aria-label", "Clock widget");
card.setAttribute("data-testid", "draggable-card");
card.setAttribute("draggable", "true");
card.style.cssText = "opacity: 1; display: inline-block; vertical-align: top;";
card.innerHTML = `
<div class="ic-DashboardCard__header">
<span class="screenreader-only">Clock</span>
<div class="ic-DashboardCard__header_image">
<div class="ic-DashboardCard__header_hero" aria-hidden="true" style="background-color:#2d3b45;opacity:0.6"></div>
</div>
<a href="#" class="ic-DashboardCard__link">
<div class="ic-DashboardCard__header_content">
<h2 class="ic-DashboardCard__header-title ellipsis" title="Clock" data-testid="dashboard-card-title"><span style="color:#2d3b45">Clock</span></h2>
<div class="canvasrefined-clock-display ic-DashboardCard__header-subtitle ellipsis" style="font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;color:#2d3b45"></div>
<div class="canvasrefined-clock-date ic-DashboardCard__header-term ellipsis" style="font-size:12px;color:#666"></div>
</div>
</a>
<div>
<div class="ic-DashboardCard__header-button-bg" style="background-color:#2d3b45;opacity:0"></div>
<span data-position="Popover___1" data-cid="Position Popover" class="css-1ihz85b-position">
<button type="button" class="Button Button--icon-action-rev ic-DashboardCard__header-button" aria-expanded="false" data-popover-trigger="true" data-position-target="Popover___1">
<i class="icon-more" aria-hidden="true"></i>
<span class="screenreader-only">Clock widget</span>
</button>
</span>
</div>
</div>
<nav class="ic-DashboardCard__action-container" aria-label="Actions for Clock">
<a class="ic-DashboardCard__action" style="display:inherit"><img class="canvasrefined-link-image" style="display:none"></a>
<a class="ic-DashboardCard__action" style="display:inherit"><img class="canvasrefined-link-image" style="display:none"></a>
<a class="ic-DashboardCard__action" style="display:inherit"><img class="canvasrefined-link-image" style="display:none"></a>
<a class="ic-DashboardCard__action" style="display:inherit"><img class="canvasrefined-link-image" style="display:none"></a>
</nav>
<div class="canvasrefined-card-assignment" style="display:none">
<div class="canvasrefined-card-header-container"><h3 class="canvasrefined-card-header">Due</h3></div>
<div class="canvasrefined-card-container"><div class="canvasrefined-assignment-container"><a class="canvasrefined-assignment-link">None</a></div></div>
</div>
`;
container.appendChild(card);
try {
if (window.jQuery && window.jQuery.fn && window.jQuery.fn.sortable) {
let $c = window.jQuery(container);
if ($c.sortable('instance')) {
let opts = $c.sortable('option');
$c.sortable('destroy');
$c.sortable(window.jQuery.extend({}, opts));
}
}
} catch (e) {}
card.addEventListener('dragstart', function (e) {
e.dataTransfer.setData('text', 'canvasrefined-widget-clock');
e.dataTransfer.effectAllowed = 'move';
});
setTimeout(() => {
try {
let $c = window.jQuery && window.jQuery(container);
if ($c && $c.sortable && $c.sortable('instance')) {
$c.sortable('refresh');
}
} catch (e) {}
}, 500);
updateClock();
clockInterval = setInterval(updateClock, 1000);
}
} else {
let card = document.querySelector('.canvasrefined-widget-clock');
if (card) card.remove();
if (clockInterval) {
clearInterval(clockInterval);
clockInterval = null;
}
}
}
function updateClock() {
let display = document.querySelector('.canvasrefined-clock-display');
let dateEl = document.querySelector('.canvasrefined-clock-date');
if (!display) return;
let now = new Date();
let hours = String(now.getHours()).padStart(2, '0');
let minutes = String(now.getMinutes()).padStart(2, '0');
let seconds = String(now.getSeconds()).padStart(2, '0');
display.textContent = `${hours}:${minutes}:${seconds}`;
if (dateEl) {
dateEl.textContent = now.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' });
}
}
/* /*
Custom font Custom font
*/ */

View File

@ -1,4 +1,4 @@
const syncedSwitches = ['remind', 'tab_icons', 'hide_feedback', 'dark_mode', 'remlogo', 'full_width', 'auto_dark', 'assignments_due', 'gpa_calc', 'gradient_cards', 'disable_color_overlay', 'dashboard_grades', 'dashboard_notes', 'better_todo', 'better_sidebar', 'condensed_cards']; const syncedSwitches = ['remind', 'tab_icons', 'hide_feedback', 'dark_mode', 'remlogo', 'full_width', 'auto_dark', 'assignments_due', 'gpa_calc', 'gradient_cards', 'disable_color_overlay', 'dashboard_grades', 'dashboard_notes', 'better_todo', 'better_sidebar', 'condensed_cards', 'widget_clock'];
const syncedSubOptions = [ const syncedSubOptions = [
"todo_hide_feedback", "todo_hide_feedback",
"todo_full_height", "todo_full_height",
@ -133,6 +133,7 @@ const defaultOptions = {
"customCardStyles": false, "customCardStyles": false,
"customBackgroundLink": "", "customBackgroundLink": "",
"customBackgroundScale": 100, "customBackgroundScale": 100,
"widget_clock": false,
} }
}; };
@ -401,6 +402,10 @@ function setup() {
setup: displayThemeList, setup: displayThemeList,
tab: ".import-export", tab: ".import-export",
}, },
"widgets-btn": {
setup: null,
tab: ".widgets-container",
},
"report-issue-btn": { "report-issue-btn": {
setup: displayErrors, setup: displayErrors,
tab: ".report-issue-container", tab: ".report-issue-container",