popup organizing+2digit%+hide tasks when complete on card tasks

This commit is contained in:
Guy Sandler 2026-10-03 16:02:08 -07:00
parent 09b4ae61b0
commit d16a91e7c9
16 changed files with 171 additions and 71 deletions

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Neues Canvas ausblenden"
},
"hide_completed_cards": {
"message": "Abgeschlossene ausblenden"
}
}

View File

@ -337,5 +337,8 @@
},
"grade_analytics_zones": {
"message": "Colored grade zones"
},
"hide_completed_cards": {
"message": "Hide Completed"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Ocultar nuevo Canvas"
},
"hide_completed_cards": {
"message": "Ocultar completadas"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Masquer le nouveau Canvas"
},
"hide_completed_cards": {
"message": "Masquer les devoirs terminés"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Nascondi nuovo Canvas"
},
"hide_completed_cards": {
"message": "Nascondi completati"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "新しい Canvas を非表示"
},
"hide_completed_cards": {
"message": "完了した課題を非表示"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Ocultar novo Canvas"
},
"hide_completed_cards": {
"message": "Ocultar concluídas"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Скрыть новый Canvas"
},
"hide_completed_cards": {
"message": "Скрыть выполненные"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "Dölj ny Canvas"
},
"hide_completed_cards": {
"message": "Dölj färdiga"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "隐藏新版 Canvas"
},
"hide_completed_cards": {
"message": "隐藏已完成的作业"
}
}

View File

@ -253,5 +253,8 @@
},
"hide_new_canvas": {
"message": "隱藏新版 Canvas"
},
"hide_completed_cards": {
"message": "隱藏已完成的作業"
}
}

View File

@ -84,6 +84,16 @@ h2 {margin-top: 20px;font-weight:600}
#todo_timeframe option {background:#1e1e1e;color:#e2e2e2;}
#todo_timeframe option:checked,
#todo_timeframe option:hover {background:#2e2e2e;color:#fff;}
/* Collapsible "More options" section for the Better Todo List sub-options */
.todo-more-options {margin-top: 7px;}
.todo-more-toggle {display: flex;align-items:center;gap:5px;font-size:12px;color:#a7a7a7;cursor:pointer;user-select:none;padding:3px 0;border-radius:5px;transition:color .1s;}
.todo-more-toggle:hover {color:#e2e2e2;}
.todo-more-toggle svg {flex:none;transition:transform .15s ease;}
.todo-more-options.expanded .todo-more-toggle svg {transform: rotate(90deg);}
.todo-more-toggle .todo-expanded-label {display:none;}
.todo-more-options.expanded .todo-expanded-label {display:inline;}
.todo-more-items {display:none;flex-direction:column;}
.todo-more-options.expanded .todo-more-items {display:flex;padding-left:2px;}
#advanced-settings, #gpa-bounds-btn, #custom-font-btn, #customize-dark-btn, #card-colors-btn {width: 100%;}
.back-btn {width: 120px;height:100%;margin-top:0}
.big-button {border: none;border-radius: 7px;background: var(--containerbg);color: #fff;font-family: inherit;padding: 12px 10px;font-weight:600}

View File

@ -122,6 +122,10 @@
<input type="checkbox" id="card_overdues" name="card_overdues">
<label for="card_overdues" class="sub-text" data-i18n="show_past_due">Show past due</label>
</div>
<div class="sub-option">
<input type="checkbox" id="hide_completed_cards" name="hide_completed_cards">
<label for="hide_completed_cards" class="sub-text" data-i18n="hide_completed_cards">Hide completed</label>
</div>
<div class="sub-option">
<input type="checkbox" id="relative_dues" name="relative_dues">
<label for="relative_dues" class="sub-text" data-i18n="relative_due_dates">Relative due dates</label>
@ -268,26 +272,7 @@
<span class="option-name" data-i18n="better_todo">Better Todo List</span>
</div>
<div class="sub-options">
<div class="sub-option">
<input type="checkbox" id="todo_separate_scrollbar" name="todo_separate_scrollbar">
<label for="todo_separate_scrollbar" class="sub-text" data-i18n="todo_separate_scrollbar">Separate Scrollbar</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hr24" name="todo_hr24">
<label for="todo_hr24" class="sub-text" data-i18n="24hrformat">Use 24hr format</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_full_height" name="todo_full_height">
<label for="todo_full_height" class="sub-text" data-i18n="todo_full_height">Force Full Height</label> <!-- TO REMOVE and replace with full height -->
</div>
<div class="sub-option">
<input type="checkbox" id="hover_preview" name="hover_preview">
<label for="hover_preview" class="sub-text" data-i18n="hover_preview">Previews on hover</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hide_feedback" name="todo_hide_feedback">
<label for="todo_hide_feedback" class="sub-text" data-i18n="todo_hide_feedback">Hide Recent Feedback</label>
</div>
<!-- Frequently-changed options stay visible; rarer ones live in the collapsible "More options" section below -->
<div class="sub-option" style="flex-direction:column;align-items:stretch;gap:4px;">
<label for="todo_progress_rings" class="sub-text">Progress display</label>
<select id="todo_progress_rings" name="todo_progress_rings">
@ -308,10 +293,49 @@
<option value="2month">2 months</option>
</select>
</div>
<div class="sub-option">
<input type="checkbox" id="hover_preview" name="hover_preview">
<label for="hover_preview" class="sub-text" data-i18n="hover_preview">Previews on hover</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_show_scores" name="todo_show_scores">
<label for="todo_show_scores" class="sub-text">Show assignment scores</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_confetti" name="todo_confetti">
<label for="todo_confetti" class="sub-text">Show confetti on complete</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hide_read" name="todo_hide_read">
<label for="todo_hide_read" class="sub-text">Hide read announcements</label>
</div>
<div style="margin-top: 5px">
<span class="sub-text" data-i18n="max_items">Max items to show: </span><span
id="numTodoItems"></span>
<input type="range" min="1" max="12" id="numTodoItemsSlider">
</div>
<div class="todo-more-options" id="todo-more-options">
<div class="todo-more-toggle" id="todo-more-toggle" role="button" tabindex="0" aria-expanded="false" aria-controls="todo-more-items">
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l6 6l-6 6" /></svg>
<span data-i18n="more_options">More options</span>
</div>
<div class="todo-more-items" id="todo-more-items">
<div class="sub-option">
<input type="checkbox" id="todo_separate_scrollbar" name="todo_separate_scrollbar">
<label for="todo_separate_scrollbar" class="sub-text" data-i18n="todo_separate_scrollbar">Separate Scrollbar</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hr24" name="todo_hr24">
<label for="todo_hr24" class="sub-text" data-i18n="24hrformat">Use 24hr format</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_full_height" name="todo_full_height">
<label for="todo_full_height" class="sub-text" data-i18n="todo_full_height">Force Full Height</label> <!-- TO REMOVE and replace with full height -->
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hide_feedback" name="todo_hide_feedback">
<label for="todo_hide_feedback" class="sub-text" data-i18n="todo_hide_feedback">Hide Recent Feedback</label>
</div>
<div class="sub-option" id="todo_alternate_colors_wrap" style="display:none;">
<input type="checkbox" id="todo_alternate_colors" name="todo_alternate_colors">
<label for="todo_alternate_colors" class="sub-text" data-i18n="todo_alternate_colors">Alternate colors</label>
@ -320,22 +344,11 @@
<input type="checkbox" id="todo_ignore_card_colors" name="todo_ignore_card_colors">
<label for="todo_ignore_card_colors" class="sub-text">Ignore card colors</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_hide_read" name="todo_hide_read">
<label for="todo_hide_read" class="sub-text">Hide read announcements</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_remove_icons" name="todo_remove_icons">
<label for="todo_remove_icons" class="sub-text">Remove icons</label>
</div>
<div class="sub-option">
<input type="checkbox" id="todo_show_scores" name="todo_show_scores">
<label for="todo_show_scores" class="sub-text">Show assignment scores</label>
</div>
<div style="margin-top: 5px">
<span class="sub-text" data-i18n="max_items">Max items to show: </span><span
id="numTodoItems"></span>
<input type="range" min="1" max="12" id="numTodoItemsSlider">
</div>
</div>
</div>

View File

@ -53,6 +53,7 @@ chrome.runtime.onInstalled.addListener(function () {
"grade_hover": false,
"card_letter": true,
// "hide_completed": false,
"hide_completed_cards": false,
"num_todo_items": 10,
"custom_font": { "link": "", "family": "" },
"hover_preview": true,

View File

@ -1071,6 +1071,10 @@ function applyOptionsChanges(changes) {
// Stretch or reset card heights in place instead of rebuilding rows.
equalizeCardHeights();
break;
case "hide_completed_cards":
// Assignment elements are already preloaded; just re-filter the render.
loadCardAssignments();
break;
case "custom_cards":
customizeCards();
// Hiding/unhiding a card changes which courses appear in the todo
@ -9374,9 +9378,9 @@ function renderGaStats() {
// graded assignments (green climbing, red falling, grey steady).
let trendVal = "-", trendColor = "var(--bctext-0)";
if (gaData.trend != null) {
if (gaData.trend > 0.05) { trendVal = "\u25B2 +" + gaData.trend.toFixed(1) + "%"; trendColor = "#16a34a"; }
else if (gaData.trend < -0.05) { trendVal = "\u25BC " + gaData.trend.toFixed(1) + "%"; trendColor = "#dc2626"; }
else { trendVal = "\u25BA " + gaData.trend.toFixed(1) + "%"; trendColor = "var(--bctext-1)"; }
if (gaData.trend > 0.05) { trendVal = "\u25B2 +" + gaData.trend.toFixed(2) + "%"; trendColor = "#16a34a"; }
else if (gaData.trend < -0.05) { trendVal = "\u25BC " + gaData.trend.toFixed(2) + "%"; trendColor = "#dc2626"; }
else { trendVal = "\u25BA " + gaData.trend.toFixed(2) + "%"; trendColor = "var(--bctext-1)"; }
}
// Grade goal card from the Final Calculator tab: the score needed on the
// final to hit the stored target grade.
@ -9387,11 +9391,11 @@ function renderGaStats() {
let val, color;
if (needed <= 0) { val = "\u2713 Secured"; color = "#16a34a"; }
else if (needed > 100) { val = "Out of reach"; color = "#dc2626"; }
else { val = "\u2265 " + needed.toFixed(1) + "%"; color = gaNeededColor(needed); }
else { val = "\u2265 " + needed.toFixed(2) + "%"; color = gaNeededColor(needed); }
goalStat = stat("Final Exam", val, color);
}
stats.innerHTML =
stat("Overall grade", gaData.current == null ? "-" : gaData.current.toFixed(1) + "%") +
stat("Overall grade", gaData.current == null ? "-" : gaData.current.toFixed(2) + "%") +
stat("Grade trend (last 5)", trendVal, trendColor) +
stat("Graded", gaData.graded) +
stat("Ungraded", gaData.ungraded) +
@ -9511,12 +9515,12 @@ function renderGaCalculator() {
let head, sub;
if (needed <= 0) {
head = `<span style="font-size:20px;font-weight:700;color:#16a34a;">You're already there!</span>`;
sub = `Even a 0 on the final leaves you at <b>${withZero.toFixed(1)}%</b>, which is above your <b>${target}%</b> goal.`;
sub = `Even a 0 on the final leaves you at <b>${withZero.toFixed(2)}%</b>, which is above your <b>${target}%</b> goal.`;
} else if (needed > 100) {
head = `<span style="font-size:20px;font-weight:700;color:#dc2626;">Out of reach</span>`;
sub = `Even a perfect final only gets you to <b>${withPerfect.toFixed(1)}%</b>, which is below your <b>${target}%</b> goal.`;
sub = `Even a perfect final only gets you to <b>${withPerfect.toFixed(2)}%</b>, which is below your <b>${target}%</b> goal.`;
} else {
head = `<span style="font-size:20px;font-weight:700;color:${gaNeededColor(needed)};">You need ≥ ${needed.toFixed(1)}% on the final</span>`;
head = `<span style="font-size:20px;font-weight:700;color:${gaNeededColor(needed)};">You need ≥ ${needed.toFixed(2)}% on the final</span>`;
sub = `You got this!`;
}
box.innerHTML = head + `<div style="margin-top:6px;color:var(--bctext-1);font-size:13px;">${sub}</div>`;
@ -9626,8 +9630,8 @@ function gaBuildHeatmapData() {
function gaHeatmapShowTip(tip, e, cell, avg) {
const d = cell.date;
const rows = cell.items.map(p =>
`<div style="margin-top:2px;color:var(--bctext-1);">${gaEscHtml(p.title)} — <b style="color:${gaHeatmapColor(p.pct)};">${p.pct.toFixed(1)}%</b></div>`).join("");
tip.innerHTML = `<b>${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}</b> — avg <b style="color:${gaHeatmapColor(avg)};">${avg.toFixed(1)}%</b><div style="margin-top:4px;font-size:11px;color:var(--bctext-1);">${cell.items.length} assignment${cell.items.length === 1 ? "" : "s"}:</div>${rows}`;
`<div style="margin-top:2px;color:var(--bctext-1);">${gaEscHtml(p.title)} — <b style="color:${gaHeatmapColor(p.pct)};">${p.pct.toFixed(2)}%</b></div>`).join("");
tip.innerHTML = `<b>${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}</b> — avg <b style="color:${gaHeatmapColor(avg)};">${avg.toFixed(2)}%</b><div style="margin-top:4px;font-size:11px;color:var(--bctext-1);">${cell.items.length} assignment${cell.items.length === 1 ? "" : "s"}:</div>${rows}`;
tip.style.display = "block";
const host = tip.offsetParent || tip.parentNode;
const hostRect = host.getBoundingClientRect();
@ -9850,7 +9854,7 @@ function gaDrawLine(canvas, tooltip) {
}
// Y grid: 5 evenly spaced lines across the current range.
ctx.textAlign = "right"; ctx.textBaseline = "middle";
const decimals = (yMax - yMin) <= 10 ? 1 : 0;
const decimals = (yMax - yMin) <= 1 ? 2 : (yMax - yMin) <= 10 ? 1 : 0;
for (let i = 0; i <= 5; i++) {
const v = yMin + (yMax - yMin) * (i / 5);
const y = pad.t + (1 - (v - yMin) / (yMax - yMin)) * (h - pad.t - pad.b);
@ -10001,7 +10005,7 @@ function gaDrawLine(canvas, tooltip) {
}
const p = pts[best];
gaShowTooltip(tooltip, X(best), Y(p.grade),
`<b>${p.title}</b><br>Overall: ${p.grade == null ? "-" : p.grade.toFixed(1) + "%"}<br>This: ${p.score}/${p.points} (${p.pct.toFixed(1)}%)${p.due ? `<br>Due: ${p.due}` : ""}`);
`<b>${p.title}</b><br>Overall: ${p.grade == null ? "-" : p.grade.toFixed(2) + "%"}<br>This: ${p.score}/${p.points} (${p.pct.toFixed(2)}%)${p.due ? `<br>Due: ${p.due}` : ""}`);
};
canvas._gaLeave = () => {
tooltip.style.display = "none";
@ -10149,7 +10153,7 @@ function gaComputeImagineTotal() {
// restates the real grade.
function gaImagineTotalHtml(pct) {
const letter = gaLetterFor(pct);
const pctText = pct == null || !isFinite(pct) ? "—" : pct.toFixed(1) + "%";
const pctText = pct == null || !isFinite(pct) ? "—" : pct.toFixed(2) + "%";
// The wrapper is an inline-flex row with align-items:center so the
// badge pill sits vertically centered with the grade text. No
// flex-wrap: the narrow score cell would stack the items instead.

View File

@ -11,6 +11,7 @@ const syncedSubOptions = [
"device_dark",
"relative_dues",
"card_overdues",
"hide_completed_cards",
"equal_height_cards",
// "todo_overdues",
"gpa_calc_prepend",
@ -174,6 +175,7 @@ const defaultOptions = {
},
// "todo_overdues": false,
"card_overdues": false,
"hide_completed_cards": false,
"relative_dues": false,
"equal_height_cards": false,
"hide_new_canvas": true,
@ -188,6 +190,7 @@ const defaultOptions = {
"todo_full_height": false,
"todo_progress_rings": "rings",
"todo_timeframe": "all",
"todo_more_expanded": false,
"todo_confetti": true,
"device_dark": false,
"cumulative_gpa": { "name": "Cumulative GPA", "hidden": false, "weight": "dnc", "credits": 999, "gr": 3.21 },
@ -375,6 +378,31 @@ function setupProgressRingsSelect(initial) {
});
}
// Collapsible "More options" section for the Better Todo List sub-options.
// Persists whether it's expanded so the popup reopens in the same state.
function setupTodoMoreOptions(initial) {
const wrap = document.getElementById("todo-more-options");
const toggle = document.getElementById("todo-more-toggle");
if (!wrap || !toggle) return;
const setExpanded = (expanded) => {
wrap.classList.toggle("expanded", expanded === true);
toggle.setAttribute("aria-expanded", expanded === true ? "true" : "false");
};
setExpanded(initial === true);
const flip = () => {
const expanded = !wrap.classList.contains("expanded");
setExpanded(expanded);
chrome.storage.sync.set({ "todo_more_expanded": expanded });
};
toggle.addEventListener("click", flip);
toggle.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
flip();
}
});
}
// Timeframe dropdown for the upcoming Tasks tab. Persisted so the Better Todo
// List remembers the selected range across sessions.
function setupTimeframeSelect(initial) {
@ -857,6 +885,12 @@ function setupFeatureSearch(menu) {
if (node.style && node.style.display === "none") node.style.display = "";
node = node.parentElement;
}
// Expand the Better Todo "More options" collapsible if the target lives inside it.
const moreWrap = el.closest ? el.closest("#todo-more-options") : null;
if (moreWrap && !moreWrap.classList.contains("expanded")) {
const toggle = document.getElementById("todo-more-toggle");
if (toggle) toggle.click();
}
}
function highlight(el) {
@ -1125,6 +1159,7 @@ function setup() {
"device_dark",
"relative_dues",
"card_overdues",
"hide_completed_cards",
"equal_height_cards",
// "todo_overdues",
"gpa_calc_prepend",
@ -1304,6 +1339,10 @@ function setup() {
identifier: "todo_timeframe",
setup: (initial) => setupTimeframeSelect(initial),
},
{
identifier: "todo_more_expanded",
setup: (initial) => setupTodoMoreOptions(initial),
},
],
};