diff --git a/_locales/de/messages.json b/_locales/de/messages.json
index 9d0625c..25a7283 100644
--- a/_locales/de/messages.json
+++ b/_locales/de/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Relative Fälligkeitsdaten"
},
+ "equal_height_cards": {
+ "message": "Gleiche Höhe"
+ },
"enter_multiple": {
"message": "(Mehrere mit Komma eingeben)"
},
diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 763e5c7..6aa66f7 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -122,6 +122,9 @@
"relative_due_dates": {
"message": "Relative Due Dates"
},
+ "equal_height_cards": {
+ "message": "Equal Height"
+ },
"enter_multiple": {
"message": "(Enter Multiple With A Comma)"
},
diff --git a/_locales/es/messages.json b/_locales/es/messages.json
index 98f3a62..791af9e 100644
--- a/_locales/es/messages.json
+++ b/_locales/es/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Fechas pendientes relativas"
},
+ "equal_height_cards": {
+ "message": "Altura igual"
+ },
"enter_multiple": {
"message": "(Ingrese varios con una coma)"
},
diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json
index 9c7670b..3b9f03c 100644
--- a/_locales/fr/messages.json
+++ b/_locales/fr/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Dates d'échéance relatives"
},
+ "equal_height_cards": {
+ "message": "Hauteur égale"
+ },
"enter_multiple": {
"message": "(Entrez plusieurs avec une virgule)"
},
diff --git a/_locales/it/messages.json b/_locales/it/messages.json
index e0ee3b2..3b84286 100644
--- a/_locales/it/messages.json
+++ b/_locales/it/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Date di scadenza relative"
},
+ "equal_height_cards": {
+ "message": "Altezza uguale"
+ },
"enter_multiple": {
"message": "(Inserisci più numeri con una virgola)"
},
diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json
index 2c2558b..7844d83 100644
--- a/_locales/ja/messages.json
+++ b/_locales/ja/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "相対的な期日"
},
+ "equal_height_cards": {
+ "message": "高さを揃える"
+ },
"enter_multiple": {
"message": "(カンマで複数入力)"
},
diff --git a/_locales/pt_PT/messages.json b/_locales/pt_PT/messages.json
index c34d903..615f8f5 100644
--- a/_locales/pt_PT/messages.json
+++ b/_locales/pt_PT/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Datas de vencimento relativas"
},
+ "equal_height_cards": {
+ "message": "Altura igual"
+ },
"enter_multiple": {
"message": "(Digite vários com uma vírgula)"
},
diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json
index 330510d..a16213a 100644
--- a/_locales/ru/messages.json
+++ b/_locales/ru/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Относительные сроки выполнения"
},
+ "equal_height_cards": {
+ "message": "Одинаковая высота"
+ },
"enter_multiple": {
"message": "(Введите несколько через запятую)"
},
diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json
index 391df5b..0e59384 100644
--- a/_locales/sv/messages.json
+++ b/_locales/sv/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "Relativa förfallodatum"
},
+ "equal_height_cards": {
+ "message": "Jämn höjd"
+ },
"enter_multiple": {
"message": "(Ange flera med komma)"
},
diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json
index f3506ed..72a9006 100644
--- a/_locales/zh_CN/messages.json
+++ b/_locales/zh_CN/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "相对到期日"
},
+ "equal_height_cards": {
+ "message": "等高"
+ },
"enter_multiple": {
"message": "(用逗号输入多个)"
},
diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json
index f3506ed..72a9006 100644
--- a/_locales/zh_TW/messages.json
+++ b/_locales/zh_TW/messages.json
@@ -113,6 +113,9 @@
"relative_due_dates": {
"message": "相对到期日"
},
+ "equal_height_cards": {
+ "message": "等高"
+ },
"enter_multiple": {
"message": "(用逗号输入多个)"
},
diff --git a/html/popup.html b/html/popup.html
index 942208c..59530e5 100644
--- a/html/popup.html
+++ b/html/popup.html
@@ -120,6 +120,10 @@
+
+
+
+
Max items to show:
diff --git a/js/background.js b/js/background.js
index 85aec37..a88bc2c 100644
--- a/js/background.js
+++ b/js/background.js
@@ -72,6 +72,7 @@ chrome.runtime.onInstalled.addListener(function () {
// "todo_overdues": false,
"card_overdues": false,
"relative_dues": false,
+ "equal_height_cards": false,
"hide_feedback": false,
"dark_mode_fix": [],
"assignment_states": {},
diff --git a/js/content.js b/js/content.js
index 6af0c8a..0db3283 100644
--- a/js/content.js
+++ b/js/content.js
@@ -663,6 +663,11 @@ function applyOptionsChanges(changes) {
cardAssignments = preloadAssignmentEls();
loadCardAssignments();
break;
+ case "equal_height_cards":
+ // No need to rebuild the assignment rows — just stretch (or reset)
+ // the card heights in place for a snappy toggle.
+ equalizeCardHeights();
+ break;
case "custom_cards":
case "custom_cards_2":
case "custom_cards_3":
@@ -3537,6 +3542,51 @@ function createCardAssignment(assignment) {
let cardAssignments;
+/*
+Equal Height Cards — when enabled, every dashboard card's assignment area is
+stretched to match the tallest one so cards with fewer assignments don't end
+up shorter than the rest. Uses min-height (never a fixed height) so cards can
+still grow if their content exceeds the tallest card.
+*/
+let equalHeightResizeTimer = null;
+
+function equalizeCardHeights() {
+ const cards = document.querySelectorAll(".ic-DashboardCard");
+ if (cards.length === 0) return;
+
+ const enabled = options.equal_height_cards === true && options.assignments_due === true;
+
+ // Always clear any previously applied min-height first so we can either
+ // measure natural heights (when enabling) or fully reset (when disabling).
+ cards.forEach(card => {
+ const area = card.querySelector(".canvasrefined-card-assignment");
+ if (area) area.style.removeProperty("min-height");
+ });
+
+ if (!enabled) return;
+
+ // Measure the natural height of each card's assignment area, then stretch
+ // every area to the tallest one. Card headers are uniform, so equalizing
+ // the assignment area makes all cards the same total height.
+ let maxHeight = 0;
+ cards.forEach(card => {
+ const area = card.querySelector(".canvasrefined-card-assignment");
+ if (area) maxHeight = Math.max(maxHeight, area.offsetHeight);
+ });
+
+ if (maxHeight > 0) {
+ cards.forEach(card => {
+ const area = card.querySelector(".canvasrefined-card-assignment");
+ if (area) area.style.minHeight = maxHeight + "px";
+ });
+ }
+}
+
+window.addEventListener("resize", () => {
+ if (equalHeightResizeTimer) clearTimeout(equalHeightResizeTimer);
+ equalHeightResizeTimer = setTimeout(equalizeCardHeights, 150);
+});
+
function preloadAssignmentEls() {
return new Promise((resolve, reject) => {
let assignmentEls = {};
@@ -3569,6 +3619,7 @@ function loadCardAssignments() {
document.querySelectorAll(".canvasrefined-card-assignment").forEach(card => {
card.style.display = "none";
});
+ equalizeCardHeights();
return;
}
setupCardAssignments();
@@ -3608,6 +3659,9 @@ function loadCardAssignments() {
let assignmentDivLink = makeElement("a", assignmentContainer, { "className": "canvasrefined-assignment-link", "textContent": "None" });
}
});
+ // Wait one frame so the browser lays out the freshly appended
+ // assignment rows before measuring/equalizing card heights.
+ requestAnimationFrame(equalizeCardHeights);
} catch (e) {
logError(e);
}
diff --git a/js/popup.js b/js/popup.js
index bed498c..dd7d206 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -6,6 +6,7 @@ const syncedSubOptions = [
"device_dark",
"relative_dues",
"card_overdues",
+ "equal_height_cards",
// "todo_overdues",
"gpa_calc_prepend",
"auto_dark",
@@ -112,6 +113,7 @@ const defaultOptions = {
// "todo_overdues": false,
"card_overdues": false,
"relative_dues": false,
+ "equal_height_cards": false,
"hide_feedback": false,
"dark_mode_fix": [],
"assignment_states": {},
@@ -412,6 +414,25 @@ function toggleBetterTodoSubOptions(betterTodoOn) {
}
}
+// Hide the sub-options of a big toggle when it is turned off.
+// For gpa_calc / assignments_due / better_todo the whole .sub-options block is
+// hidden. For auto_dark ("schedule dark mode") only the start/end time clocks
+// (.timesets) are hidden, leaving the device-dark checkbox visible.
+function toggleSubOptionsVisibility(option, isOn) {
+ const togglesWithSubOptions = ["gpa_calc", "assignments_due", "better_todo", "auto_dark"];
+ if (!togglesWithSubOptions.includes(option)) return;
+ const optionEl = document.getElementById(option);
+ if (!optionEl) return;
+ const subOptions = optionEl.parentElement.querySelector(":scope > .sub-options");
+ if (!subOptions) return;
+ if (option === "auto_dark") {
+ const timesets = subOptions.querySelector(".timesets");
+ if (timesets) timesets.style.display = isOn ? "" : "none";
+ } else {
+ subOptions.style.display = isOn ? "" : "none";
+ }
+}
+
function setup() {
const menu = {
@@ -428,6 +449,7 @@ function setup() {
"device_dark",
"relative_dues",
"card_overdues",
+ "equal_height_cards",
// "todo_overdues",
"gpa_calc_prepend",
"auto_dark",
@@ -563,10 +585,14 @@ function setup() {
if (option === "better_todo") {
toggleBetterTodoSubOptions(status);
}
+ toggleSubOptionsVisibility(option, status);
});
});
toggleBetterSidebarSubOptions(sync["better_sidebar"] === true);
toggleBetterTodoSubOptions(sync["better_todo"] === true);
+ ["gpa_calc", "assignments_due", "better_todo", "auto_dark"].forEach(opt => {
+ toggleSubOptionsVisibility(opt, sync[opt] === true);
+ });
});
chrome.storage.sync.get(menu.checkboxes, sync => {