mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-09-23 13:25:09 +02:00
light mode fixes
This commit is contained in:
parent
4184b717e8
commit
9ab7d16bd9
@ -316,5 +316,8 @@
|
|||||||
},
|
},
|
||||||
"grade_analytics_desc": {
|
"grade_analytics_desc": {
|
||||||
"message": "Adds an Analytics toggle to the left side of course grade pages with a score-distribution chart and your overall grade over time."
|
"message": "Adds an Analytics toggle to the left side of course grade pages with a score-distribution chart and your overall grade over time."
|
||||||
|
},
|
||||||
|
"grade_analytics_zones": {
|
||||||
|
"message": "Colored grade zones"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,3 +366,26 @@
|
|||||||
padding: 0 4px; color: var(--bctext-1, #5b6770);
|
padding: 0 4px; color: var(--bctext-1, #5b6770);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Grades table: scale down with narrow windows =====
|
||||||
|
Canvas lays out #grades_summary with content-driven auto sizing plus hard
|
||||||
|
min-widths (title 150px, details 80px) and nowrap cells (due, score,
|
||||||
|
details), giving it a ~780px floor below which the whole table overflows
|
||||||
|
the page instead of shrinking. Switch it to fixed layout with percentage
|
||||||
|
column widths (set on the header row, which drives fixed layout) and let
|
||||||
|
tight cells wrap, so the header, body and rows all scale with the
|
||||||
|
viewport. #assignments scrolls horizontally as a last resort (e.g. while a
|
||||||
|
what-if score input is open). */
|
||||||
|
#grades_summary { table-layout: fixed; width: 100%; }
|
||||||
|
#grades_summary thead th:nth-child(1) { width: 30%; } /* Name */
|
||||||
|
#grades_summary thead th:nth-child(2) { width: 12%; } /* Due */
|
||||||
|
#grades_summary thead th:nth-child(3) { width: 10%; } /* Submitted */
|
||||||
|
#grades_summary thead th:nth-child(4) { width: 7%; } /* Status */
|
||||||
|
#grades_summary thead th:nth-child(5) { width: 11%; } /* Score */
|
||||||
|
#grades_summary thead th:nth-child(6) { width: 3%; } /* asset processors */
|
||||||
|
#grades_summary thead th:nth-child(7) { width: 15%; } /* Details */
|
||||||
|
#grades_summary thead th:nth-child(8) { width: 3%; } /* progress */
|
||||||
|
#grades_summary td.due,
|
||||||
|
#grades_summary td.assignment_score,
|
||||||
|
#grades_summary td.details { white-space: normal; }
|
||||||
|
#assignments { overflow-x: auto; }
|
||||||
|
|
||||||
|
|||||||
@ -231,6 +231,12 @@
|
|||||||
<span class="cr-info-bubble-text" data-i18n="grade_analytics_desc">Adds an Analytics toggle to the left side of course grade pages with a score-distribution chart and your overall grade over time.</span>
|
<span class="cr-info-bubble-text" data-i18n="grade_analytics_desc">Adds an Analytics toggle to the left side of course grade pages with a score-distribution chart and your overall grade over time.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sub-options">
|
||||||
|
<div class="sub-option" style="margin-top:5px">
|
||||||
|
<input type="checkbox" id="grade_analytics_zones" name="grade_analytics_zones">
|
||||||
|
<label for="grade_analytics_zones" class="sub-text" data-i18n="grade_analytics_zones">Colored grade zones</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="social-buttons">
|
<div class="social-buttons">
|
||||||
<a class="discord-button" href="https://discord.gg/GjFh4JA3wh" target="_blank" rel="noopener noreferrer" title="Join our Discord">
|
<a class="discord-button" href="https://discord.gg/GjFh4JA3wh" target="_blank" rel="noopener noreferrer" title="Join our Discord">
|
||||||
|
|||||||
@ -118,6 +118,7 @@ chrome.runtime.onInstalled.addListener(function () {
|
|||||||
"sidebar_blur": 0,
|
"sidebar_blur": 0,
|
||||||
"global_search": false,
|
"global_search": false,
|
||||||
"grade_analytics": true,
|
"grade_analytics": true,
|
||||||
|
"grade_analytics_zones": false,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
116
js/content.js
116
js/content.js
@ -956,6 +956,10 @@ function applyOptionsChanges(changes) {
|
|||||||
case "grade_analytics":
|
case "grade_analytics":
|
||||||
watchGradeAnalytics();
|
watchGradeAnalytics();
|
||||||
break;
|
break;
|
||||||
|
case "grade_analytics_zones":
|
||||||
|
// Colored 10% zones on the line chart — just redraw the charts.
|
||||||
|
if (gradeAnalyticsActive() && gaOpen && gaData) renderGradeAnalytics();
|
||||||
|
break;
|
||||||
case "quiz_safe_mode":
|
case "quiz_safe_mode":
|
||||||
// Toggling safe mode changes which features run on quiz pages; reload
|
// Toggling safe mode changes which features run on quiz pages; reload
|
||||||
// so the gating is applied cleanly.
|
// so the gating is applied cleanly.
|
||||||
@ -1089,6 +1093,18 @@ async function applyCustomBackground() {
|
|||||||
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%);
|
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
/* Native left nav column: #left-side > #sticky-container.ic-sticky-frame
|
||||||
|
(the course/account/group menu links). Tint the whole #left-side column
|
||||||
|
rather than the inner .ic-sticky-frame, whose height only wraps its
|
||||||
|
links — the column spans the full viewport height like the other
|
||||||
|
sidebars, at the same bg_opacity/bg_blur as the Better Todo List
|
||||||
|
panel. Without this, a custom background (most visible in light mode)
|
||||||
|
shows through untinted behind the nav links. */
|
||||||
|
#left-side {
|
||||||
|
backdrop-filter: blur(${bgBlur}px) !important;
|
||||||
|
-webkit-backdrop-filter: blur(${bgBlur}px) !important;
|
||||||
|
background-color: color-mix(in srgb, var(--bcbackground-0), transparent ${bgTransparent}%) !important;
|
||||||
|
}
|
||||||
/* Recent feedback lives in #right-side. The dark-mode CSS
|
/* Recent feedback lives in #right-side. The dark-mode CSS
|
||||||
(darkmodecss.js) recolors its text, but those rules are dark-mode-
|
(darkmodecss.js) recolors its text, but those rules are dark-mode-
|
||||||
only — so in light mode + custom background the sub-text (context,
|
only — so in light mode + custom background the sub-text (context,
|
||||||
@ -1305,6 +1321,20 @@ async function applyCustomBackground() {
|
|||||||
-webkit-backdrop-filter: blur(${cardBlur}px) saturate(120%) !important;`
|
-webkit-backdrop-filter: blur(${cardBlur}px) saturate(120%) !important;`
|
||||||
: `background: var(--bcbackground-0) !important;`}
|
: `background: var(--bcbackground-0) !important;`}
|
||||||
}
|
}
|
||||||
|
/* Card header strip (the course-nickname bar under the hero). Canvas
|
||||||
|
paints it a solid light color ($ic-color-light) and nothing overrides
|
||||||
|
that in light mode, so with card transparency on it reads as a solid
|
||||||
|
band across an otherwise translucent card. Mirror the card surface:
|
||||||
|
transparent cards drop the strip's background so the card's glass
|
||||||
|
(tint + blur already applied to .ic-DashboardCard) shows through;
|
||||||
|
opaque cards paint it the same solid theme color as the card body.
|
||||||
|
Dark mode already flattens this strip via darkmodecss.js, so this
|
||||||
|
is inert there (same value). */
|
||||||
|
.ic-DashboardCard__header_content {
|
||||||
|
${cardTransparency
|
||||||
|
? `background: none !important;`
|
||||||
|
: `background: var(--bcbackground-0) !important;`}
|
||||||
|
}
|
||||||
tr.student_assignment.assignment_graded.editable > * {
|
tr.student_assignment.assignment_graded.editable > * {
|
||||||
border:none!important
|
border:none!important
|
||||||
}`;
|
}`;
|
||||||
@ -3740,8 +3770,15 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
|
|||||||
contentMain?.style.setProperty("backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
|
contentMain?.style.setProperty("backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
|
||||||
contentMain?.style.setProperty("-webkit-backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
|
contentMain?.style.setProperty("-webkit-backdrop-filter", `blur(${Math.max(0, Math.min(30, Number(options.bg_blur ?? 8)))}px)`, "important");
|
||||||
}
|
}
|
||||||
const sidebarParent = layoutMode === "course" && leftSide ? leftSide : mainWrapper;
|
// The rail must always render leftmost. Course-layout pages already
|
||||||
if (layoutMode === "course" && leftSide) {
|
// prepend it into #left-side; dash-layout pages that still have a
|
||||||
|
// native left nav (accounts, groups, etc.) must too — otherwise the
|
||||||
|
// native #left-side column (made position:static above) flows before
|
||||||
|
// #not_right_side and shows up to the LEFT of the Better Sidebar,
|
||||||
|
// looking like a competing sidebar once the custom background tints
|
||||||
|
// it. Prepending keeps the order: [Better Sidebar rail][native nav].
|
||||||
|
const sidebarParent = leftSide ? leftSide : mainWrapper;
|
||||||
|
if (leftSide) {
|
||||||
leftSide.style.display = "flex";
|
leftSide.style.display = "flex";
|
||||||
leftSide.style.flexDirection = "row";
|
leftSide.style.flexDirection = "row";
|
||||||
leftSide.style.alignItems = "stretch";
|
leftSide.style.alignItems = "stretch";
|
||||||
@ -3749,9 +3786,6 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
|
|||||||
leftSide.style.gap = "0";
|
leftSide.style.gap = "0";
|
||||||
}
|
}
|
||||||
document.querySelector(".ic-app-nav-toggle-and-crumbs")?.style.setProperty("display", "none");
|
document.querySelector(".ic-app-nav-toggle-and-crumbs")?.style.setProperty("display", "none");
|
||||||
if (layoutMode !== "course") {
|
|
||||||
document.getElementById("left-side")?.style.removeProperty("display");
|
|
||||||
}
|
|
||||||
if (layoutMode == "dash") {
|
if (layoutMode == "dash") {
|
||||||
document.getElementById("header")?.style.setProperty("display", "none");
|
document.getElementById("header")?.style.setProperty("display", "none");
|
||||||
}
|
}
|
||||||
@ -6333,6 +6367,22 @@ const GA_BUCKETS = [
|
|||||||
{ label: "0-9", min: 0, max: 10, color: "#7f1d1d" },
|
{ label: "0-9", min: 0, max: 10, color: "#7f1d1d" },
|
||||||
];
|
];
|
||||||
const GA_UNGRADED_COLOR = "#6b7280";
|
const GA_UNGRADED_COLOR = "#6b7280";
|
||||||
|
// 5%-wide zone colors for the line chart background: the doughnut's bucket
|
||||||
|
// colors interpolated at 5% steps (dark red at 0 → green at 100), so every
|
||||||
|
// 5% band gets its own shade.
|
||||||
|
const GA_ZONE_COLORS = (() => {
|
||||||
|
const stops = GA_BUCKETS.slice().reverse(); // 0-9 (dark red) → 90+ (green)
|
||||||
|
const rgb = (h) => [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];
|
||||||
|
const lerp = (a, b, t) => Math.round(a + (b - a) * t);
|
||||||
|
return Array.from({ length: 20 }, (_, i) => {
|
||||||
|
const m = i * 5 + 2.5; // band midpoint
|
||||||
|
const k = Math.min(stops.length - 1, Math.floor(m / 10));
|
||||||
|
if (k >= stops.length - 1) return stops[stops.length - 1].color;
|
||||||
|
const t = (m - k * 10) / 10;
|
||||||
|
const c1 = rgb(stops[k].color), c2 = rgb(stops[k + 1].color);
|
||||||
|
return `rgb(${lerp(c1[0], c2[0], t)},${lerp(c1[1], c2[1], t)},${lerp(c1[2], c2[2], t)})`;
|
||||||
|
});
|
||||||
|
})();
|
||||||
const GA_OPEN_KEY = "grade_analytics_open";
|
const GA_OPEN_KEY = "grade_analytics_open";
|
||||||
|
|
||||||
async function getGradeAnalyticsOpenState() {
|
async function getGradeAnalyticsOpenState() {
|
||||||
@ -6877,6 +6927,22 @@ function gaDrawLine(canvas, tooltip) {
|
|||||||
// can cheaply redraw with a highlight on the active dot.
|
// can cheaply redraw with a highlight on the active dot.
|
||||||
const draw = (hover) => {
|
const draw = (hover) => {
|
||||||
ctx.clearRect(0, 0, w, h);
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
// Optional colored 5% zones behind the plot ("Colored grade zones"
|
||||||
|
// popup option), tinted red→green. With Fit Y axis on, bands are
|
||||||
|
// clipped to the visible range.
|
||||||
|
if (options.grade_analytics_zones) {
|
||||||
|
GA_ZONE_COLORS.forEach((color, i) => {
|
||||||
|
const top = Math.min(yMax, (i + 1) * 5);
|
||||||
|
const bottom = Math.max(yMin, i * 5);
|
||||||
|
if (top <= bottom) return;
|
||||||
|
const y1 = pad.t + (1 - (top - yMin) / (yMax - yMin)) * (h - pad.t - pad.b);
|
||||||
|
const y2 = pad.t + (1 - (bottom - yMin) / (yMax - yMin)) * (h - pad.t - pad.b);
|
||||||
|
ctx.globalAlpha = 0.3;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(pad.l, y1, w - pad.l - pad.r, y2 - y1);
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
// Y grid: 5 evenly spaced lines across the current range.
|
// Y grid: 5 evenly spaced lines across the current range.
|
||||||
ctx.font = "11px Lato, sans-serif";
|
ctx.font = "11px Lato, sans-serif";
|
||||||
ctx.textAlign = "right"; ctx.textBaseline = "middle";
|
ctx.textAlign = "right"; ctx.textBaseline = "middle";
|
||||||
@ -6913,8 +6979,11 @@ function gaDrawLine(canvas, tooltip) {
|
|||||||
};
|
};
|
||||||
// Subtle tick under every data point on sparse charts.
|
// Subtle tick under every data point on sparse charts.
|
||||||
if (pts.length <= 25) pts.forEach((_, i) => tick(X(i)));
|
if (pts.length <= 25) pts.forEach((_, i) => tick(X(i)));
|
||||||
|
// Month boundaries: the first point of each calendar month. Points are
|
||||||
|
// already in chronological order, so a walking month counter (wrapping
|
||||||
|
// across Dec → Jan) maps each point to an absolute month.
|
||||||
|
const boundaries = [];
|
||||||
let prevM = null, absM = null;
|
let prevM = null, absM = null;
|
||||||
let lastLabeled = null;
|
|
||||||
pts.forEach((p, i) => {
|
pts.forEach((p, i) => {
|
||||||
const m = months.indexOf((p.due || "").trim().slice(0, 3));
|
const m = months.indexOf((p.due || "").trim().slice(0, 3));
|
||||||
if (m < 0) return; // no due date on this point
|
if (m < 0) return; // no due date on this point
|
||||||
@ -6922,13 +6991,38 @@ function gaDrawLine(canvas, tooltip) {
|
|||||||
else if (m >= prevM) absM += m - prevM;
|
else if (m >= prevM) absM += m - prevM;
|
||||||
else absM += 12 - prevM + m; // wrapped to a new year
|
else absM += 12 - prevM + m; // wrapped to a new year
|
||||||
prevM = m;
|
prevM = m;
|
||||||
if (absM === lastLabeled) return;
|
const last = boundaries[boundaries.length - 1];
|
||||||
lastLabeled = absM;
|
if (!last || last.absM !== absM) {
|
||||||
tick(X(i));
|
boundaries.push({ i, absM, label: (p.due || "").trim().slice(0, 3) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
boundaries.forEach(b => tick(X(b.i)));
|
||||||
|
// Collision-aware labels: greedily keep a month label only if it fits
|
||||||
|
// after the previous kept one (labels are centered, so compare against
|
||||||
|
// half-widths plus a 6px gap). The first boundary always gets a label;
|
||||||
|
// so does the last — if it doesn't fit, earlier labels are dropped to
|
||||||
|
// make room, so the axis ends on a real month instead of mid-run.
|
||||||
|
const GAP = 6;
|
||||||
|
const kept = [];
|
||||||
|
boundaries.forEach((b, idx) => {
|
||||||
|
const x = X(b.i);
|
||||||
|
const w = ctx.measureText(b.label).width;
|
||||||
|
if (idx === 0 || x - w / 2 > kept[kept.length - 1].right + GAP) {
|
||||||
|
kept.push({ ...b, x, right: x + w / 2 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const lastB = boundaries[boundaries.length - 1];
|
||||||
|
if (lastB && kept[kept.length - 1].i !== lastB.i) {
|
||||||
|
const x = X(lastB.i);
|
||||||
|
const w = ctx.measureText(lastB.label).width;
|
||||||
|
while (kept.length && kept[kept.length - 1].right + GAP > x - w / 2) kept.pop();
|
||||||
|
kept.push({ ...lastB, x, right: x + w / 2 });
|
||||||
|
}
|
||||||
|
kept.forEach((b, k) => {
|
||||||
// Anchor the edge labels inward so they don't clip.
|
// Anchor the edge labels inward so they don't clip.
|
||||||
ctx.textAlign = i === 0 ? "left" : (i === pts.length - 1 ? "right" : "center");
|
ctx.textAlign = k === 0 ? "left" : (b.i === pts.length - 1 ? "right" : "center");
|
||||||
ctx.fillStyle = text;
|
ctx.fillStyle = text;
|
||||||
ctx.fillText((p.due || "").trim().slice(0, 3), X(i), h - pad.b + 6);
|
ctx.fillText(b.label, b.x, h - pad.b + 6);
|
||||||
});
|
});
|
||||||
// Hover indicator: dashed vertical guide plus a halo ring around the
|
// Hover indicator: dashed vertical guide plus a halo ring around the
|
||||||
// hovered dot so it's obvious which point the tooltip describes.
|
// hovered dot so it's obvious which point the tooltip describes.
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const syncedSwitches = ['remind', 'tab_icons', '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', 'hide_new_canvas', 'hide_sequence_footer', 'center_cards', 'quiz_safe_mode', 'global_search', 'grade_analytics'];
|
const syncedSwitches = ['remind', 'tab_icons', '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', 'hide_new_canvas', 'hide_sequence_footer', 'center_cards', 'quiz_safe_mode', 'global_search', 'grade_analytics'];
|
||||||
const syncedSubOptions = [
|
const syncedSubOptions = [
|
||||||
|
"grade_analytics_zones",
|
||||||
"todo_hide_feedback",
|
"todo_hide_feedback",
|
||||||
"todo_full_height",
|
"todo_full_height",
|
||||||
"todo_confetti",
|
"todo_confetti",
|
||||||
@ -151,6 +152,7 @@ const defaultOptions = {
|
|||||||
"equal_height_cards": false,
|
"equal_height_cards": false,
|
||||||
"hide_new_canvas": true,
|
"hide_new_canvas": true,
|
||||||
"hide_sequence_footer": false,
|
"hide_sequence_footer": false,
|
||||||
|
"grade_analytics_zones": false,
|
||||||
"quiz_safe_mode": false,
|
"quiz_safe_mode": false,
|
||||||
"dark_mode_fix": [],
|
"dark_mode_fix": [],
|
||||||
"assignment_states": {},
|
"assignment_states": {},
|
||||||
@ -570,7 +572,7 @@ function toggleAlternateColorsVisibility(darkModeOn) {
|
|||||||
|
|
||||||
// Hide a toggle's sub-options when it's off; auto_dark only hides its time clocks.
|
// Hide a toggle's sub-options when it's off; auto_dark only hides its time clocks.
|
||||||
function toggleSubOptionsVisibility(option, isOn) {
|
function toggleSubOptionsVisibility(option, isOn) {
|
||||||
const togglesWithSubOptions = ["gpa_calc", "assignments_due", "better_todo", "auto_dark"];
|
const togglesWithSubOptions = ["gpa_calc", "assignments_due", "better_todo", "auto_dark", "grade_analytics"];
|
||||||
if (!togglesWithSubOptions.includes(option)) return;
|
if (!togglesWithSubOptions.includes(option)) return;
|
||||||
const optionEl = document.getElementById(option);
|
const optionEl = document.getElementById(option);
|
||||||
if (!optionEl) return;
|
if (!optionEl) return;
|
||||||
@ -953,6 +955,7 @@ function setup() {
|
|||||||
"fitImageToScreen",
|
"fitImageToScreen",
|
||||||
"card_transparency",
|
"card_transparency",
|
||||||
"customCardStyles",
|
"customCardStyles",
|
||||||
|
"grade_analytics_zones",
|
||||||
],
|
],
|
||||||
tabs: {
|
tabs: {
|
||||||
"advanced-settings": {
|
"advanced-settings": {
|
||||||
@ -1110,7 +1113,7 @@ function setup() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
toggleBetterSidebarSubOptions(sync["better_sidebar"] === true);
|
toggleBetterSidebarSubOptions(sync["better_sidebar"] === true);
|
||||||
["gpa_calc", "assignments_due", "better_todo", "auto_dark"].forEach(opt => {
|
["gpa_calc", "assignments_due", "better_todo", "auto_dark", "grade_analytics"].forEach(opt => {
|
||||||
toggleSubOptionsVisibility(opt, sync[opt] === true);
|
toggleSubOptionsVisibility(opt, sync[opt] === true);
|
||||||
});
|
});
|
||||||
toggleAlternateColorsVisibility(sync["dark_mode"] === true);
|
toggleAlternateColorsVisibility(sync["dark_mode"] === true);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user