mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-09-21 12:34:53 +02:00
more minor fixes and additions
This commit is contained in:
parent
eaa2a85295
commit
3e6a5927d7
@ -860,6 +860,13 @@
|
||||
<span class="sub-text">px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-option">
|
||||
<div style="margin-top: 5px;display: flex;gap: 8px;align-items: center">
|
||||
<span class="sub-text" style="width:75px;">Image Roundness</span>
|
||||
<input type="number" id="imageRoundness" class="card-input" min="0" max="50" value="0" style="width:80px;">
|
||||
<span class="sub-text">px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-option">
|
||||
<div style="margin-top: 5px;display: flex;gap: 8px;align-items: center">
|
||||
<span class="sub-text" style="width:75px;">Extra Card Spacing</span>
|
||||
|
||||
@ -57,7 +57,7 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
"full_width": null,
|
||||
"remlogo": null,
|
||||
"gpa_calc_bounds": {
|
||||
"A+": { "cutoff": 97, "gpa": 4.3 },
|
||||
"A+": { "cutoff": 97, "gpa": 4.0 },
|
||||
"A": { "cutoff": 93, "gpa": 4 },
|
||||
"A-": { "cutoff": 90, "gpa": 3.7 },
|
||||
"B+": { "cutoff": 87, "gpa": 3.3 },
|
||||
@ -103,9 +103,10 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
"custom_styles": "",
|
||||
"imageSize": 100,
|
||||
"cardRoundness": 5,
|
||||
"imageRoundness": 0,
|
||||
'cardSpacing': 0,
|
||||
"cardWidth": 262,
|
||||
"cardHeight": 250,
|
||||
"cardHeight": 146,
|
||||
"customCardStyles": false,
|
||||
"customBackgroundLink": "",
|
||||
"customBackgroundScale": 100,
|
||||
|
||||
@ -995,6 +995,7 @@ function applyOptionsChanges(changes) {
|
||||
break;
|
||||
case "imageSize":
|
||||
case "cardRoundness":
|
||||
case "imageRoundness":
|
||||
case "cardSpacing":
|
||||
case "cardWidth":
|
||||
case "cardHeight":
|
||||
@ -3572,7 +3573,7 @@ function populateAssignments(iscompleted = false) {
|
||||
<div style="width:calc(100% - 40px);height:80%;display:flex;flex-direction:column;gap:5px;padding-left:2px;box-sizing:border-box;overflow:hidden;position:relative;">
|
||||
<div style="display:flex;flex-direction:column;gap:3px;">
|
||||
<span style="color:${classNameColor};font-size:12px;margin-top:-2px;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;box-sizing:border-box;padding-right:22px;">${item.context_name}</span>
|
||||
<a href="${taskHref}" style="color:inherit;text-decoration:none;font-weight:bold;text-overflow:ellipsis;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:-5px;">${item.plannable.title}</a>
|
||||
<a href="${taskHref}" style="color:inherit;text-decoration:none;font-weight:bold;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;padding-right:28px;margin-top:-5px;">${item.plannable.title}</a>
|
||||
<span style="color:var(--bctext-0);font-size:12px;margin-top:-5px;">${convertToDueDate(item.plannable_date)}</span>
|
||||
</div>
|
||||
${editButtonSvg}
|
||||
@ -4854,14 +4855,24 @@ Dashboard grades
|
||||
|
||||
// Map a percentage to a letter grade using the user's configurable GPA
|
||||
// calculator cutoffs (A+ down to F). Returns null when no grade is present.
|
||||
// Picks the letter with the HIGHEST cutoff the percent meets so the result
|
||||
// doesn't depend on the key order of the stored bounds object — theme imports
|
||||
// can reorder keys (e.g. alphabetically, where "A" precedes "A+"), which made
|
||||
// "+" grades unreachable and displayed e.g. 100% as "A". Cutoffs are coerced
|
||||
// with Number() so string values carried in by imported themes still match.
|
||||
function percentToLetterGrade(percent) {
|
||||
const bounds = options.gpa_calc_bounds;
|
||||
if (!bounds || typeof percent !== "number") return null;
|
||||
let best = null;
|
||||
let bestCutoff = -Infinity;
|
||||
for (const letter of Object.keys(bounds)) {
|
||||
const cutoff = bounds[letter]?.cutoff;
|
||||
if (typeof cutoff === "number" && percent >= cutoff) return letter;
|
||||
const cutoff = Number(bounds[letter]?.cutoff);
|
||||
if (Number.isFinite(cutoff) && percent >= cutoff && cutoff > bestCutoff) {
|
||||
best = letter;
|
||||
bestCutoff = cutoff;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return best;
|
||||
}
|
||||
|
||||
function insertGrades() {
|
||||
@ -5820,31 +5831,37 @@ function applyAestheticChanges() {
|
||||
if (options.customCardStyles === true) {
|
||||
if (options.imageSize !== undefined && options.imageSize !== 100) style.textContent += `.ic-DashboardCard__header_image {transform: scale(${options.imageSize / 100})!important; }`;
|
||||
if (options.cardRoundness !== undefined && options.cardRoundness !== 5) style.textContent += `.ic-DashboardCard {border-radius: ${options.cardRoundness}px!important;}`;
|
||||
// Rounds the header image band. The photo is the background of
|
||||
// .ic-DashboardCard__header_image, and .ic-DashboardCard__header_hero (a
|
||||
// child that covers the full photo with its colored overlay + 1px
|
||||
// border) sits on top of it. border-radius only clips the element it's
|
||||
// on — rounding the parent alone leaves the hero's square overlay
|
||||
// covering the corners, and rounding the hero alone leaves the photo's
|
||||
// square corners behind it — so both elements need the same radius.
|
||||
// Default 0; guard skips the default so stock cards keep square corners.
|
||||
if (options.imageRoundness !== undefined && options.imageRoundness !== 0) style.textContent += `.ic-DashboardCard__header_image, .ic-DashboardCard__header_hero {border-radius: ${options.imageRoundness}px!important;}`;
|
||||
if (options.cardSpacing !== undefined && options.cardSpacing !== 0) style.textContent += `.ic-DashboardCard {margin-right: ${options.cardSpacing / 2}px!important; margin-bottom: ${options.cardSpacing / 2}px!important;}`;
|
||||
if (options.cardWidth !== undefined && options.cardWidth !== 262) style.textContent += `.ic-DashboardCard {width: ${options.cardWidth}px!important;}`;
|
||||
// Always emit a fixed card height when custom card styles are on. The old
|
||||
// `!== 250` guard silently dropped the height rule when cardHeight matched
|
||||
// the default, which is exactly what happens after importing a theme that
|
||||
// carries the default cardHeight (250) — leaving cards content-sized.
|
||||
// Content-sized cards plus the dashboard reflow loop trigger Firefox scroll
|
||||
// anchoring to yank the viewport back up while scrolling. A fixed height
|
||||
// (even the default 250px) keeps layout stable.
|
||||
if (options.cardHeight !== undefined && options.cardHeight !== null && options.cardHeight !== "") {
|
||||
style.textContent += `.ic-DashboardCard {height: ${options.cardHeight}px!important;}`;
|
||||
// Canvas sets overflow:hidden on .ic-DashboardCard. With a fixed
|
||||
// height that clips the appended .canvasrefined-card-assignment area
|
||||
// (the assignment rows live at the bottom of the card), making the
|
||||
// .canvasrefined-assignment-link anchors unclickable for users with
|
||||
// custom card styles enabled. Allow overflow so those rows stay
|
||||
// visible and interactive when card assignments are shown.
|
||||
if (options.assignments_due === true) style.textContent += `.ic-DashboardCard {overflow: visible!important;}`;
|
||||
// Card height sizes the image band via .ic-DashboardCard__header_hero —
|
||||
// the element Canvas pins at 146px that actually drives the header image
|
||||
// height (both .ic-DashboardCard and .ic-DashboardCard__header are
|
||||
// content-sized, so a height on either just clips or adds dead space).
|
||||
// Everything else — title, actions, and the appended
|
||||
// .canvasrefined-card-assignment rows — flows below the hero, so the card
|
||||
// grows with the assignment list and nothing gets clipped. Skipped when
|
||||
// condensed cards is on, since that mode pins the hero at 60px.
|
||||
if (options.condensed_cards !== true && options.cardHeight !== undefined && options.cardHeight !== null && options.cardHeight !== "") {
|
||||
style.textContent += `.ic-DashboardCard__header_hero {height: ${options.cardHeight}px!important;}`;
|
||||
}
|
||||
// Inner card padding. Applied to the whole .ic-DashboardCard box (the
|
||||
// element that holds the hero header, title, and action buttons) so the
|
||||
// colored hero header and its content all get consistent breathing room
|
||||
// from the card's edges. Guarded by !== 0 (default).
|
||||
// Inner card padding. Applied to the whole .ic-DashboardCard box so the
|
||||
// hero header, title, and action buttons all get breathing room from the
|
||||
// card's edges. Canvas sizes the card with border-box + a fixed width,
|
||||
// so padding alone squishes the content area (narrower image/rows)
|
||||
// instead of expanding the card — switch to content-box so the padding
|
||||
// grows the card outward and the content keeps its full width.
|
||||
// Guarded by > 0 (default).
|
||||
if (options.cardPadding !== undefined && Number(options.cardPadding) > 0) {
|
||||
style.textContent += `.ic-DashboardCard {padding: ${options.cardPadding}px!important;}`;
|
||||
style.textContent += `.ic-DashboardCard {padding: ${options.cardPadding}px!important; box-sizing: content-box!important;}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
js/popup.js
51
js/popup.js
@ -32,6 +32,7 @@ const syncedSubOptions = [
|
||||
"customCardStyles",
|
||||
"imageSize",
|
||||
"cardRoundness",
|
||||
"imageRoundness",
|
||||
"cardSpacing",
|
||||
"cardWidth",
|
||||
"cardHeight",
|
||||
@ -54,7 +55,7 @@ const localSwitches = [];
|
||||
// Theme export only carries visual settings, never personal productivity data.
|
||||
const exportDarkSchedule = ["auto_dark", "auto_dark_start", "auto_dark_end", "device_dark"];
|
||||
const exportCardColorToggles = ["gradient_cards", "disable_color_overlay"];
|
||||
const exportCardStyles = ["customCardStyles", "imageSize", "cardRoundness", "cardSpacing", "cardWidth", "cardHeight", "cardPadding"];
|
||||
const exportCardStyles = ["customCardStyles", "imageSize", "cardRoundness", "imageRoundness", "cardSpacing", "cardWidth", "cardHeight", "cardPadding"];
|
||||
const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_height_cards", "remlogo", "hide_new_canvas", "tab_icons"];
|
||||
const exportSidebar = ["better_sidebar", "sidebar_scale"];
|
||||
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_timeframe", "todo_hr24", "todo_separate_scrollbar", "todo_alternate_colors", "todo_ignore_card_colors", "todo_remove_icons", "hover_preview"];
|
||||
@ -135,7 +136,7 @@ const defaultOptions = {
|
||||
"full_width": null,
|
||||
"remlogo": null,
|
||||
"gpa_calc_bounds": {
|
||||
"A+": { "cutoff": 97, "gpa": 4.3 },
|
||||
"A+": { "cutoff": 97, "gpa": 4.0 },
|
||||
"A": { "cutoff": 93, "gpa": 4 },
|
||||
"A-": { "cutoff": 90, "gpa": 3.7 },
|
||||
"B+": { "cutoff": 87, "gpa": 3.3 },
|
||||
@ -173,9 +174,10 @@ const defaultOptions = {
|
||||
"card_limit": 25,
|
||||
"imageSize": 100,
|
||||
"cardRoundness": 5,
|
||||
"imageRoundness": 0,
|
||||
"cardSpacing": 0,
|
||||
"cardWidth": 262,
|
||||
"cardHeight": 250,
|
||||
"cardHeight": 146,
|
||||
"cardPadding": 0,
|
||||
"customCardStyles": false,
|
||||
"customBackgroundLink": "",
|
||||
@ -401,6 +403,14 @@ function setupCardRoundnessInput(initial) {
|
||||
});
|
||||
}
|
||||
|
||||
function setupImageRoundnessInput(initial) {
|
||||
let el = document.querySelector("#imageRoundness");
|
||||
el.value = initial;
|
||||
el.addEventListener("input", (e) => {
|
||||
debouncedCardStyleSet("imageRoundness", e.target.value);
|
||||
});
|
||||
}
|
||||
|
||||
function setupCardSpacingInput(initial) {
|
||||
let el = document.querySelector("#cardSpacing");
|
||||
el.value = initial;
|
||||
@ -1056,6 +1066,10 @@ function setup() {
|
||||
identifier: "cardRoundness",
|
||||
setup: (initial) => setupCardRoundnessInput(initial),
|
||||
},
|
||||
{
|
||||
identifier: "imageRoundness",
|
||||
setup: (initial) => setupImageRoundnessInput(initial),
|
||||
},
|
||||
{
|
||||
identifier: "cardSpacing",
|
||||
setup: (initial) => setupCardSpacingInput(initial),
|
||||
@ -1534,7 +1548,7 @@ function setup() {
|
||||
"A-": { "cutoff": 90, "gpa": 4.0 },
|
||||
"B+": { "cutoff": 87, "gpa": 3.0 },
|
||||
"B": { "cutoff": 83, "gpa": 3.0 },
|
||||
"B-": { "cutoff": 89, "gpa": 3.0 },
|
||||
"B-": { "cutoff": 80, "gpa": 3.0 },
|
||||
"C+": { "cutoff": 77, "gpa": 2.0 },
|
||||
"C": { "cutoff": 73, "gpa": 2.0 },
|
||||
"C-": { "cutoff": 70, "gpa": 2.0 },
|
||||
@ -1545,8 +1559,9 @@ function setup() {
|
||||
});
|
||||
});
|
||||
|
||||
// Note: the card-style number inputs (imageSize, cardRoundness, cardSpacing,
|
||||
// cardWidth, cardHeight, cardPadding) are wired via their setup*Input
|
||||
// Note: the card-style number inputs (imageSize, cardRoundness,
|
||||
// imageRoundness, cardSpacing, cardWidth, cardHeight, cardPadding) are wired
|
||||
// via their setup*Input
|
||||
// handlers in menu.special above, which debounce the storage writes. The
|
||||
// duplicate listeners that used to live here referenced nonexistent
|
||||
// #*Value spans (threw on every input) and double-wrote to storage, which
|
||||
@ -1619,11 +1634,9 @@ async function getExport(storage, options) {
|
||||
case "custom_cards":
|
||||
let arr = [];
|
||||
Object.keys(storage["custom_cards"]).forEach(key => {
|
||||
if (storage["custom_cards"][key].img !== "") arr.push(storage["custom_cards"][key].img);
|
||||
const img = storage["custom_cards"][key].img;
|
||||
if (img && img !== "none" && img.trim() !== "") arr.push(img);
|
||||
});
|
||||
if (arr.length === 0) {
|
||||
arr = ["none"];
|
||||
}
|
||||
final["custom_cards"] = arr;
|
||||
break;
|
||||
case "card_colors":
|
||||
@ -1767,6 +1780,7 @@ function saveCurrentTheme() {
|
||||
"sidebar_scale": current["sidebar_scale"],
|
||||
"imageSize": current["imageSize"],
|
||||
"cardRoundness": current["cardRoundness"],
|
||||
"imageRoundness": current["imageRoundness"],
|
||||
"cardSpacing": current["cardSpacing"],
|
||||
"cardWidth": current["cardWidth"],
|
||||
"cardHeight": current["cardHeight"],
|
||||
@ -1867,7 +1881,7 @@ function displaySavedThemes() {
|
||||
let title = makeElement("p", btn, { "className": "theme-button-title", "textContent": `Theme ${index + 1}`});
|
||||
let date = makeElement("p", btn, { "className": "theme-button-creator", "textContent": `${getRelativeDate(created).time} ago` });
|
||||
let remove = makeElement("div", btn, { "className": "theme-button-remove", "textContent": "x" });
|
||||
btn.style.backgroundImage = `linear-gradient(rgba(0, 0, 0, 0.44), rgba(0, 0, 0, 0.44)), url(${local["saved_themes"][key]["custom_cards"][0]})`;
|
||||
btn.style.backgroundImage = `linear-gradient(rgba(0, 0, 0, 0.44), rgba(0, 0, 0, 0.44)), url(${local["saved_themes"][key]["custom_cards"]?.[0] || ""})`;
|
||||
btn.addEventListener("click", () => {
|
||||
importTheme(local["saved_themes"][key]);
|
||||
});
|
||||
@ -1906,16 +1920,23 @@ function importTheme(theme) {
|
||||
case "card_colors":
|
||||
sendFromPopup("setcolors", theme["card_colors"]);
|
||||
break;
|
||||
case "custom_cards":
|
||||
case "custom_cards": {
|
||||
// "none" is a legacy placeholder meaning "this theme has
|
||||
// no custom card images" — treat it as such instead of
|
||||
// writing the literal string into every card (which hides
|
||||
// the card image on the dashboard). Fall back to "" so
|
||||
// cards keep their default Canvas images.
|
||||
const themeImgs = (theme["custom_cards"] || []).filter(u => u && u !== "none" && u.trim() !== "");
|
||||
if (theme["custom_cards"].length > 0) {
|
||||
let pos = 0;
|
||||
Object.keys(sync["custom_cards"]).forEach(key => {
|
||||
sync["custom_cards"][key].img = theme["custom_cards"][pos];
|
||||
pos = (pos === theme["custom_cards"].length - 1) ? 0 : pos + 1;
|
||||
sync["custom_cards"][key].img = themeImgs.length ? themeImgs[pos] : "";
|
||||
pos = (pos === themeImgs.length - 1) ? 0 : pos + 1;
|
||||
});
|
||||
final["custom_cards"] = sync["custom_cards"];
|
||||
}
|
||||
final["custom_cards"] = sync["custom_cards"];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
final[key] = theme[key];
|
||||
break;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user