Compare commits

...

4 Commits

Author SHA1 Message Date
Guy Sandler
b84a374099 quick bump 2026-08-15 19:32:30 -07:00
Guy Sandler
7f3c8b5681 small changes 2026-08-15 19:17:28 -07:00
Guy Sandler
7ca9c8ff25 improved no dark mode compatibility 2026-08-15 18:26:22 -07:00
Guy Sandler
fdaa2754b8 fixing bugs, centering, links, theme comparability 2026-08-15 17:51:55 -07:00
20 changed files with 452 additions and 93 deletions

View File

@ -66,33 +66,34 @@ Canvas Refined adds more with more to come!
- Simplified UI
- Some options won't show up when not needed
- Markdown in dashboard notes
- Feature search bar
- Smaller additions:
- Equal height cards for card assignments
- Center cards
- Hide new dashbaord button
## Planned Features (by priority)
- auto rotate theme + theme history + fix theme submissions
## Planned Features
- mail assistent + ui revamp
- better calender (+ calender sync)
- better what if grade
- global search
- fix darkmode fixer
- global canvas search
- fix darkmode fixer (and automatic)
- grade history with graph
- more shapes for the better todo ring thing
## Extra features that might be added:
- card grade position, card outline
- theme copy button
- revamp cards page UI
- streaks
- caching pages for faster loading
- maybe find a way for faster loading
- liquid glass theme?
- animated backgrounds, rotating background, time/weather reactive backgrounds, maybe chache if it becomes an issue
- rotating background, time/weather background overlay
- custom side logo
- transcribe lecture (if there is demand for it)
- flashcards
- goals
- Scheduled Reminder Popups
- preview font
- button to remove all card images and undo
- auto dark mode based on background
- potential gemini nano features (local AI on your computer) if it would be able to run on more devices
## Community suggestions (maybe will be done at some point)
- when opening assignments it will show you "if you get a 0 on this your grade will be _"
@ -100,6 +101,7 @@ Canvas Refined adds more with more to come!
- module sorting (newest, oldest) (maybe grid view)
- grade leaderboard per class (opt in)
- GPA preset by school name maybe
- sticky notes
## Dev Installation
@ -200,7 +202,7 @@ To add a new feature, please follow these guidelines.
- If the function only adds css, it should be added to `applyAestheticChanges()`, and in this case should not be a separate function, instead add the css to the existing styles found in this function.
- Anything else should be put under `startExtension()` and should be placed no higher than the `checkDashboardReady` function found here.
## Star History
## Star History (I think github broke it)
<a href="https://www.star-history.com/?repos=GuySandler%2FCanvasRefined&type=date&legend=top-left">
<picture>

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Der Fixer wird automatisch auf diesen Seiten ausgeführt:"
},
"hide_links": {
"message": "Links ausblenden"
},
"show_links": {
"message": "Links einblenden"
},
"center_cards": {
"message": "Karten zentrieren"
},
"hide_new_canvas": {
"message": "Neues Canvas ausblenden"
}
}

View File

@ -250,5 +250,17 @@
},
"fix_dm_text": {
"message": "The Fixer Is Automatically Running On These Pages:"
},
"hide_links": {
"message": "Hide links"
},
"show_links": {
"message": "Show links"
},
"center_cards": {
"message": "Center Cards"
},
"hide_new_canvas": {
"message": "Hide New Dashboard"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "El reparador se ejecuta automáticamente en estas páginas:"
},
"hide_links": {
"message": "Ocultar enlaces"
},
"show_links": {
"message": "Mostrar enlaces"
},
"center_cards": {
"message": "Centrar tarjetas"
},
"hide_new_canvas": {
"message": "Ocultar nuevo Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Le fixateur s'exécute automatiquement sur ces pages :"
},
"hide_links": {
"message": "Masquer les liens"
},
"show_links": {
"message": "Afficher les liens"
},
"center_cards": {
"message": "Centrer les cartes"
},
"hide_new_canvas": {
"message": "Masquer le nouveau Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Il fissatore viene eseguito automaticamente su queste pagine:"
},
"hide_links": {
"message": "Nascondi link"
},
"show_links": {
"message": "Mostra link"
},
"center_cards": {
"message": "Centra schede"
},
"hide_new_canvas": {
"message": "Nascondi nuovo Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Fixer はこれらのページで自動的に実行されます:"
},
"hide_links": {
"message": "リンクを非表示"
},
"show_links": {
"message": "リンクを表示"
},
"center_cards": {
"message": "カードを中央配置"
},
"hide_new_canvas": {
"message": "新しい Canvas を非表示"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "O Fixer está sendo executado automaticamente nestas páginas:"
},
"hide_links": {
"message": "Ocultar ligações"
},
"show_links": {
"message": "Mostrar ligações"
},
"center_cards": {
"message": "Centrar cartões"
},
"hide_new_canvas": {
"message": "Ocultar novo Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Fixer автоматически запускается на этих страницах:"
},
"hide_links": {
"message": "Скрыть ссылки"
},
"show_links": {
"message": "Показать ссылки"
},
"center_cards": {
"message": "Центрировать карточки"
},
"hide_new_canvas": {
"message": "Скрыть новый Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "Fixern körs automatiskt på dessa sidor:"
},
"hide_links": {
"message": "Dölj länkar"
},
"show_links": {
"message": "Visa länkar"
},
"center_cards": {
"message": "Centrera kort"
},
"hide_new_canvas": {
"message": "Dölj ny Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "修复程序会自动在这些页面上运行:"
},
"hide_links": {
"message": "隐藏链接"
},
"show_links": {
"message": "显示链接"
},
"center_cards": {
"message": "居中卡片"
},
"hide_new_canvas": {
"message": "隐藏新版 Canvas"
}
}

View File

@ -238,5 +238,17 @@
},
"fix_dm_text": {
"message": "修复程序会自动在这些页面上运行:"
},
"hide_links": {
"message": "隱藏連結"
},
"show_links": {
"message": "顯示連結"
},
"center_cards": {
"message": "置中卡片"
},
"hide_new_canvas": {
"message": "隱藏新版 Canvas"
}
}

View File

@ -63,6 +63,10 @@
opacity: .95;
}
.canvasrefined-custom-btn {background: #f5f5f5; border: 1px solid #c7cdd1; border-radius:6px; padding: 2px 8px;}
/* Better sidebar buttons sit on the --bcsidebar surface and use --bcsidebar-text
for labels/icons, so drop the light button chrome and use a subtle hover tint. */
.better-sidebar-btn {background: transparent !important; border-color: transparent !important;}
.better-sidebar-btn:hover {background: color-mix(in srgb, var(--bcsidebar-text) 14%, transparent) !important;}
.canvasrefined-viewmore-btn {display: block;margin:0 auto;margin-top: 8px;}
.canvasrefined-course-percent, .canvasrefined-course-credit {border: 1px solid #ccc; color: var(--ic-brand-font-color-dark)}

View File

@ -68,6 +68,18 @@ h2 {margin-top: 20px;font-weight:600}
#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}
.social-buttons {display: flex;gap: 8px;margin-top: 8px;}
.social-buttons a {flex: 1;box-sizing: border-box;}
.discord-button {display: flex;align-items: center;justify-content: center;gap: 8px;padding: 12px 10px;border: none;border-radius: 7px;background: #5865F2;color: #fff;font-family: inherit;font-size: 14px;font-weight: 600;text-decoration: none;transition: .15s background}
.discord-button:hover {background: #4752c4;cursor: pointer}
.discord-button:active {filter: brightness(85%)}
.github-button {display: flex;align-items: center;justify-content: center;gap: 8px;padding: 12px 10px;border: none;border-radius: 7px;background: #30363d;color: #fff;font-family: inherit;font-size: 14px;font-weight: 600;text-decoration: none;transition: .15s background}
.github-button:hover {background: #21262d;cursor: pointer}
.github-button:active {filter: brightness(85%)}
.discord-button svg, .github-button svg {flex-shrink: 0}
.social-toggle {text-align: center;font-size: 12px;color: #9a9a9a;margin-top: 6px;cursor: pointer;user-select: none;transition: .15s color;text-decoration: none}
.social-toggle:hover {color: #e2e2e2}
.social-buttons.hidden {display: none}
#setSingleColor, #setGradientColor {padding: 8px 20px;margin-top:5px;}
.gpa-bounds-input {font-size: 14px;background: var(--inputbg); border: none;padding:6px;color: #e2e2e2;margin-top: 6px;border-radius: 6px; font-family: inherit; width: 55px; box-sizing: border-box;text-align: right}
.gpa-bounds span {font-size: 16px; width: 25px;display: inline-block}
@ -141,7 +153,7 @@ textarea {resize: vertical}
.sidebar-changer {display: flex;gap: 10px;}
.custom-key { margin-right: 5px; }
.options-presets .customization-button {padding:9px 14px}
#export-output {margin: 0; height: 100%;}
#export-output {margin: 0; height: calc(100% - 46px);}
input[type="checkbox"] { appearance: none; background: var(--inputbg); margin: 0; width: 16px; height: 16px; border: 1px solid #1a1a1a;;border-radius: 4px; display: grid; place-content: center;}
input[type="checkbox"]::before { content: ""; width: 10px; height: 10px; transform: scale(0); box-shadow: inset 1em 1em #323232; transform-origin: center; clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);}
input[type="checkbox"]:checked::before {transform: scale(1);}

View File

@ -190,6 +190,17 @@
<span class="option-name" data-i18n="todo_remind">Todo Reminders</span>
</div>
</div>
<div class="social-buttons">
<a class="discord-button" href="https://discord.gg/GjFh4JA3wh" target="_blank" rel="noopener noreferrer" title="Join our Discord">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.369a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.331c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
<span>Discord</span>
</a>
<a class="github-button" href="https://github.com/GuySandler/CanvasRefined" target="_blank" rel="noopener noreferrer" title="View on GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
<span>GitHub</span>
</a>
</div>
<div class="social-toggle" id="social-toggle" role="button" tabindex="0"></div>
</div>
<div class="options-right">
@ -267,6 +278,15 @@
</div>
</div>
<div class="option-container" style="display:flex;flex-direction:column;gap:6px;">
<div class="option" id="center_cards">
<input type="radio" id="off" name="center_cards">
<input type="radio" id="on" name="center_cards">
<div class="slider">
<div class="sliderknob"></div>
<div class="sliderbg"></div>
</div>
<span class="option-name" data-i18n="center_cards">Center Cards</span>
</div>
<div class="option" id="dashboard_notes">
<input type="radio" id="off" name="dashboard_notes">
<input type="radio" id="on" name="dashboard_notes">
@ -325,6 +345,14 @@
<div class="sliderbg"></div>
</div><span class="option-name" data-i18n="tab_icons">Color coded tab icons</span>
</div>
<div class="option" id="hide_new_canvas">
<input type="radio" id="off" name="hide_new_canvas">
<input type="radio" id="on" name="hide_new_canvas">
<div class="slider">
<div class="sliderknob"></div>
<div class="sliderbg"></div>
</div><span class="option-name" data-i18n="hide_new_canvas">Hide New Dashboard</span>
</div>
</div>
<div class="option-container">
<div class="customDomain">
@ -479,6 +507,10 @@
<input type="checkbox" id="export-dark" />
<span>Dark mode</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-dark-schedule" />
<span>Dark mode schedule</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-cards" />
<span>Card images</span>
@ -487,6 +519,10 @@
<input type="checkbox" id="export-colors" />
<span>Card colors</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-card-styles" />
<span>Card styles</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-font" />
<span>Custom font</span>
@ -495,13 +531,25 @@
<input type="checkbox" id="export-toggles" />
<span>On/off toggles</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-layout" />
<span>Layout</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-sidebar" />
<span>Sidebar</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-todo" />
<span>Todo list</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-gpa" />
<span>GPA settings</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-customStyles" />
<span>Custom card styles</span>
<span>Custom CSS</span>
</div>
<div class="sub-option">
<input type="checkbox" id="export-background" />
@ -510,6 +558,7 @@
</div>
<div style="flex: 1">
<textarea class="card-input" id="export-output" placeholder="Output"></textarea>
<button type="button" id="export-copy" class="customization-button" style="display:block;margin-top:8px;width:100%;">Copy</button>
</div>
</div>
</div>
@ -1035,7 +1084,6 @@
<textarea id="error_log_output" class="card-input"></textarea>
</div>
</div>
<p>5.11.2</p>
</div>
<script type="text/javascript" src="../js/themes.js"></script>

View File

@ -38,7 +38,7 @@ chrome.runtime.onInstalled.addListener(function () {
"dashboard_notes": false,
"dashboard_notes_text": "",
"dashboard_notes_mode": "edit",
"better_todo": false,
"better_todo": true,
"todo_hr24": false,
"todo_separate_scrollbar": false,
"better_sidebar": false,
@ -75,6 +75,7 @@ chrome.runtime.onInstalled.addListener(function () {
"relative_dues": false,
"equal_height_cards": false,
"hide_feedback": false,
"hide_new_canvas": true,
"dark_mode_fix": [],
"assignment_states": {},
"tab_icons": false,
@ -193,8 +194,10 @@ async function getNasaBackground() {
const url = data.thumbnail_url || data.hdurl || data.url;
if (!url) return null;
const result = { url, scale: 100, date: dateStr };
await chrome.storage.local.set({ [cacheKey]: result });
await chrome.storage.local.set({ [metadataKey]: { title: data.title || "", date: data.date || dateStr, copyright: data.copyright || "", explanation: data.explanation || "" } });
const meta = { title: data.title || "", date: data.date || dateStr, copyright: data.copyright || "", explanation: data.explanation || "" };
// Write image + metadata atomically so the info overlay never sees a
// cached image with missing metadata.
await chrome.storage.local.set({ [cacheKey]: result, [metadataKey]: meta });
return result;
}
return null;

View File

@ -46,6 +46,7 @@ function getSubmissionAssignmentLink() {
let submissionPageButtonObserver = null;
let profileLogoutButtonObserver = null;
let newCanvasButtonObserver = null;
function addSubmissionPageButton() {
const assignmentLink = getSubmissionAssignmentLink();
@ -158,6 +159,30 @@ function watchSubmissionPageButton() {
}, 10000);
}
function removeNewCanvasButton() {
document.querySelectorAll('[data-testid="switch-to-new-dashboard-button"]').forEach(btn => btn.remove());
}
function watchNewCanvasButton() {
if (newCanvasButtonObserver) {
newCanvasButtonObserver.disconnect();
newCanvasButtonObserver = null;
}
if (options.hide_new_canvas !== true) return;
removeNewCanvasButton();
newCanvasButtonObserver = new MutationObserver(() => {
if (options.hide_new_canvas !== true) {
if (newCanvasButtonObserver) {
newCanvasButtonObserver.disconnect();
newCanvasButtonObserver = null;
}
return;
}
removeNewCanvasButton();
});
newCanvasButtonObserver.observe(document.documentElement, { childList: true, subtree: true });
}
async function getActiveCustomBackground() {
const syncOpts = await chrome.storage.sync.get([
"customBackgroundDaily",
@ -259,19 +284,29 @@ function createNasaInfoOverlay() {
const panel = nasaInfoOverlayEl.querySelector("#nasa-info-panel");
const populatePanel = async () => {
const dateStr = new Date().toISOString().slice(0, 10);
const cacheKey = `nasa_apod_${dateStr}`;
const cached = await chrome.storage.local.get(cacheKey);
const metaDate = cached[cacheKey]?.date || dateStr;
const metadataKey = `nasa_apod_meta_${metaDate}`;
const metadata = await chrome.storage.local.get(metadataKey);
const meta = metadata[metadataKey];
if (!meta) return false;
document.getElementById("nasa-info-title").textContent = meta.title || "";
document.getElementById("nasa-info-date").textContent = `Date: ${meta.date}`;
document.getElementById("nasa-info-credit").textContent = meta.copyright ? `Credit: ${meta.copyright}` : "";
document.getElementById("nasa-info-explanation").textContent = meta.explanation || "No description available.";
return true;
// The NASA worker (background.js getNasaBackground) falls back up to 7 days
// when today's APOD isn't available yet, so search backward for the date
// that was actually cached and displayed instead of assuming today.
const date = new Date();
for (let i = 0; i < 7; i++) {
const dateStr = date.toISOString().slice(0, 10);
const cacheKey = `nasa_apod_${dateStr}`;
const cached = await chrome.storage.local.get(cacheKey);
if (!cached[cacheKey]) {
date.setDate(date.getDate() - 1);
continue;
}
const metadataKey = `nasa_apod_meta_${dateStr}`;
const metadata = await chrome.storage.local.get(metadataKey);
const meta = metadata[metadataKey];
if (!meta) return false;
document.getElementById("nasa-info-title").textContent = meta.title || "";
document.getElementById("nasa-info-date").textContent = `Date: ${meta.date}`;
document.getElementById("nasa-info-credit").textContent = meta.copyright ? `Credit: ${meta.copyright}` : "";
document.getElementById("nasa-info-explanation").textContent = meta.explanation || "No description available.";
return true;
}
return false;
};
let pinned = false;
@ -510,6 +545,7 @@ function startExtension() {
checkDashboardReady();
loadCustomFont();
applyAestheticChanges();
watchNewCanvasButton();
changeFavicon();
updateReminders();
applyCustomBackground();
@ -625,9 +661,13 @@ function applyOptionsChanges(changes) {
case "condensed_cards":
case "hide_feedback":
case "full_width":
case "center_cards":
case "custom_styles":
applyAestheticChanges();
break;
case "hide_new_canvas":
watchNewCanvasButton();
break;
case "customBackgroundScale":
applyCustomBackground();
break;
@ -2467,7 +2507,7 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
}
let sidebarList = makeElement("div", sidebarParent, { id: "better-sidebar-container",
style: `display:flex;flex-direction:column;width:50px;justify-content:center;align-items:center;box-sizing:border-box;position:relative;background-color:var(--bcbackground-0);height:100vh;position:sticky;top:0;left:0;`
style: `display:flex;flex-direction:column;width:50px;justify-content:center;align-items:center;box-sizing:border-box;position:relative;background-color:var(--bcsidebar);height:100vh;position:sticky;top:0;left:0;`
}, true);
let sidebarContent = makeElement("div", sidebarList, {
style: "display:flex;flex-direction:column;gap:20px;width:100%;flex:1;justify-content:flex-start;align-items:center;margin:40px;"
@ -2482,7 +2522,7 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="M20 4V20M4 12H16M16 12L12 8M16 12L12 16" stroke="var(--bctext-0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M20 4V20M4 12H16M16 12L12 8M16 12L12 16" stroke="var(--bcsidebar-text)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</svg>
`
@ -2508,7 +2548,7 @@ async function setupBetterSidebar(mode = getSidebarLayoutMode()) {
}
function createSidebarButton(text, url, parent, icon) {
let button = makeElement("a", parent, {
style: "width:40%;height:var(--bc-sidebar-btn-height,30px);cursor:pointer;text-align:center;text-decoration:none;display:inline-flex;justify-content:center;align-items:center;gap:var(--bc-sidebar-btn-gap,8px);color:var(--bctext-0) !important;font-weight:bold;position:relative;",
style: "width:40%;height:var(--bc-sidebar-btn-height,30px);cursor:pointer;text-align:center;text-decoration:none;display:inline-flex;justify-content:center;align-items:center;gap:var(--bc-sidebar-btn-gap,8px);color:var(--bcsidebar-text) !important;font-weight:bold;position:relative;",
className: "canvasrefined-custom-btn better-sidebar-btn",
href: url,
});
@ -2536,13 +2576,13 @@ function addSidebarButtonBadge(button, count) {
function populateSidebarFromNav(sidebarContent) {
const excludeIds = ["global_nav_help_link", "global_nav_history_link"];
const customIcons = {
"global_nav_profile_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M12 12C14.21 12 16 10.21 16 8C16 5.79 14.21 4 12 4C9.79 4 8 5.79 8 8C8 10.21 9.79 12 12 12ZM12 14C9.33 14 4 15.34 4 18V20H20V18C20 15.34 14.67 14 12 14Z" fill="white"></path></g></svg>`,
"global_nav_dashboard_link": `<svg fill="white" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><rect x="2" y="2" width="9" height="11" rx="2"></rect><rect x="13" y="2" width="9" height="7" rx="2"></rect><rect x="2" y="15" width="9" height="7" rx="2"></rect><rect x="13" y="11" width="9" height="11" rx="2"></rect></g></svg>`,
"global_nav_conversations_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M4 18L9 12M20 18L15 12M3 8L10.225 12.8166C10.8665 13.2443 11.1872 13.4582 11.5339 13.5412C11.8403 13.6147 12.1597 13.6147 12.4661 13.5412C12.8128 13.4582 13.1335 13.2443 13.775 12.8166L21 8M6.2 19H17.8C18.9201 19 19.4802 19 19.908 18.782C20.2843 18.5903 20.5903 18.2843 20.782 17.908C21 17.4802 21 16.9201 21 15.8V8.2C21 7.0799 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V15.8C3 16.9201 3 17.4802 3.21799 17.908C3.40973 18.2843 3.71569 18.5903 4.09202 18.782C4.51984 19 5.07989 19 6.2 19Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
"global_nav_calendar_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M3 9H21M7 3V5M17 3V5M6 12H8M11 12H13M16 12H18M6 15H8M11 15H13M16 15H18M6 18H8M11 18H13M16 18H18M6.2 21H17.8C18.9201 21 19.4802 21 19.908 20.782C20.2843 20.5903 20.5903 20.2843 20.782 19.908C21 19.4802 21 18.9201 21 17.8V8.2C21 7.07989 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V17.8C3 18.9201 3 19.4802 3.21799 19.908C3.40973 20.2843 3.71569 20.5903 4.09202 20.782C4.51984 21 5.07989 21 6.2 21Z" stroke="white" stroke-width="2" stroke-linecap="round"></path></g></svg>`,
"global_nav_courses_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M20 12V4C20 2.89543 19.1046 2 18 2H6C4.89543 2 4 2.89543 4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V18.5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M13 2V14L16.8182 11L20 14V5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
"global_nav_groups_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 6C14.3432 6 13 7.34315 13 9C13 10.6569 14.3432 12 16 12C17.6569 12 19 10.6569 19 9C19 7.34315 17.6569 6 16 6ZM11 9C11 6.23858 13.2386 4 16 4C18.7614 4 21 6.23858 21 9C21 10.3193 20.489 11.5193 19.6542 12.4128C21.4951 13.0124 22.9176 14.1993 23.8264 15.5329C24.1374 15.9893 24.0195 16.6114 23.5631 16.9224C23.1068 17.2334 22.4846 17.1155 22.1736 16.6591C21.1979 15.2273 19.4178 14 17 14C13.166 14 11 17.0742 11 19C11 19.5523 10.5523 20 10 20C9.44773 20 9.00001 19.5523 9.00001 19C9.00001 18.308 9.15848 17.57 9.46082 16.8425C9.38379 16.7931 9.3123 16.7323 9.24889 16.6602C8.42804 15.7262 7.15417 15 5.50001 15C3.84585 15 2.57199 15.7262 1.75114 16.6602C1.38655 17.075 0.754692 17.1157 0.339855 16.7511C-0.0749807 16.3865 -0.115709 15.7547 0.248886 15.3398C0.809035 14.7025 1.51784 14.1364 2.35725 13.7207C1.51989 12.9035 1.00001 11.7625 1.00001 10.5C1.00001 8.01472 3.01473 6 5.50001 6C7.98529 6 10 8.01472 10 10.5C10 11.7625 9.48013 12.9035 8.64278 13.7207C9.36518 14.0785 9.99085 14.5476 10.5083 15.0777C11.152 14.2659 11.9886 13.5382 12.9922 12.9945C11.7822 12.0819 11 10.6323 11 9ZM3.00001 10.5C3.00001 9.11929 4.1193 8 5.50001 8C6.88072 8 8.00001 9.11929 8.00001 10.5C8.00001 11.8807 6.88072 13 5.50001 13C4.1193 13 3.00001 11.8807 3.00001 10.5Z" fill="white"></path></g></svg>`,
"globalNavExternalTool-69": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 1C4.34315 1 3 2.34315 3 4V17V20C3 21.6569 4.34315 23 6 23H18C19.6569 23 21 21.6569 21 20V17V4C21 2.34315 19.6569 1 18 1H6ZM5 20V17C5 16.4477 5.44772 16 6 16H18C18.5523 16 19 16.4477 19 17V20C19 20.5523 18.5523 21 18 21H6C5.44772 21 5 20.5523 5 20ZM18 14C18.3506 14 18.6872 14.0602 19 14.1707V4C19 3.44772 18.5523 3 18 3H6C5.44772 3 5 3.44772 5 4V14.1707C5.31278 14.0602 5.64936 14 6 14H18ZM14.5 19.25C15.1904 19.25 15.75 18.6904 15.75 18C15.75 17.3096 15.1904 16.75 14.5 16.75C13.8096 16.75 13.25 17.3096 13.25 18C13.25 18.6904 13.8096 19.25 14.5 19.25Z" fill="white"></path></g></svg>`,
"global_nav_profile_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M12 12C14.21 12 16 10.21 16 8C16 5.79 14.21 4 12 4C9.79 4 8 5.79 8 8C8 10.21 9.79 12 12 12ZM12 14C9.33 14 4 15.34 4 18V20H20V18C20 15.34 14.67 14 12 14Z" fill="var(--bcsidebar-text)"></path></g></svg>`,
"global_nav_dashboard_link": `<svg fill="var(--bcsidebar-text)" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><rect x="2" y="2" width="9" height="11" rx="2"></rect><rect x="13" y="2" width="9" height="7" rx="2"></rect><rect x="2" y="15" width="9" height="7" rx="2"></rect><rect x="13" y="11" width="9" height="11" rx="2"></rect></g></svg>`,
"global_nav_conversations_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M4 18L9 12M20 18L15 12M3 8L10.225 12.8166C10.8665 13.2443 11.1872 13.4582 11.5339 13.5412C11.8403 13.6147 12.1597 13.6147 12.4661 13.5412C12.8128 13.4582 13.1335 13.2443 13.775 12.8166L21 8M6.2 19H17.8C18.9201 19 19.4802 19 19.908 18.782C20.2843 18.5903 20.5903 18.2843 20.782 17.908C21 17.4802 21 16.9201 21 15.8V8.2C21 7.0799 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V15.8C3 16.9201 3 17.4802 3.21799 17.908C3.40973 18.2843 3.71569 18.5903 4.09202 18.782C4.51984 19 5.07989 19 6.2 19Z" stroke="var(--bcsidebar-text)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
"global_nav_calendar_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M3 9H21M7 3V5M17 3V5M6 12H8M11 12H13M16 12H18M6 15H8M11 15H13M16 15H18M6 18H8M11 18H13M16 18H18M6.2 21H17.8C18.9201 21 19.4802 21 19.908 20.782C20.2843 20.5903 20.5903 20.2843 20.782 19.908C21 19.4802 21 18.9201 21 17.8V8.2C21 7.07989 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V17.8C3 18.9201 3 19.4802 3.21799 19.908C3.40973 20.2843 3.71569 20.5903 4.09202 20.782C4.51984 21 5.07989 21 6.2 21Z" stroke="var(--bcsidebar-text)" stroke-width="2" stroke-linecap="round"></path></g></svg>`,
"global_nav_courses_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M20 12V4C20 2.89543 19.1046 2 18 2H6C4.89543 2 4 2.89543 4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V18.5" stroke="var(--bcsidebar-text)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M13 2V14L16.8182 11L20 14V5" stroke="var(--bcsidebar-text)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
"global_nav_groups_link": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 6C14.3432 6 13 7.34315 13 9C13 10.6569 14.3432 12 16 12C17.6569 12 19 10.6569 19 9C19 7.34315 17.6569 6 16 6ZM11 9C11 6.23858 13.2386 4 16 4C18.7614 4 21 6.23858 21 9C21 10.3193 20.489 11.5193 19.6542 12.4128C21.4951 13.0124 22.9176 14.1993 23.8264 15.5329C24.1374 15.9893 24.0195 16.6114 23.5631 16.9224C23.1068 17.2334 22.4846 17.1155 22.1736 16.6591C21.1979 15.2273 19.4178 14 17 14C13.166 14 11 17.0742 11 19C11 19.5523 10.5523 20 10 20C9.44773 20 9.00001 19.5523 9.00001 19C9.00001 18.308 9.15848 17.57 9.46082 16.8425C9.38379 16.7931 9.3123 16.7323 9.24889 16.6602C8.42804 15.7262 7.15417 15 5.50001 15C3.84585 15 2.57199 15.7262 1.75114 16.6602C1.38655 17.075 0.754692 17.1157 0.339855 16.7511C-0.0749807 16.3865 -0.115709 15.7547 0.248886 15.3398C0.809035 14.7025 1.51784 14.1364 2.35725 13.7207C1.51989 12.9035 1.00001 11.7625 1.00001 10.5C1.00001 8.01472 3.01473 6 5.50001 6C7.98529 6 10 8.01472 10 10.5C10 11.7625 9.48013 12.9035 8.64278 13.7207C9.36518 14.0785 9.99085 14.5476 10.5083 15.0777C11.152 14.2659 11.9886 13.5382 12.9922 12.9945C11.7822 12.0819 11 10.6323 11 9ZM3.00001 10.5C3.00001 9.11929 4.1193 8 5.50001 8C6.88072 8 8.00001 9.11929 8.00001 10.5C8.00001 11.8807 6.88072 13 5.50001 13C4.1193 13 3.00001 11.8807 3.00001 10.5Z" fill="var(--bcsidebar-text)"></path></g></svg>`,
"globalNavExternalTool-69": `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 1C4.34315 1 3 2.34315 3 4V17V20C3 21.6569 4.34315 23 6 23H18C19.6569 23 21 21.6569 21 20V17V4C21 2.34315 19.6569 1 18 1H6ZM5 20V17C5 16.4477 5.44772 16 6 16H18C18.5523 16 19 16.4477 19 17V20C19 20.5523 18.5523 21 18 21H6C5.44772 21 5 20.5523 5 20ZM18 14C18.3506 14 18.6872 14.0602 19 14.1707V4C19 3.44772 18.5523 3 18 3H6C5.44772 3 5 3.44772 5 4V14.1707C5.31278 14.0602 5.64936 14 6 14H18ZM14.5 19.25C15.1904 19.25 15.75 18.6904 15.75 18C15.75 17.3096 15.1904 16.75 14.5 16.75C13.8096 16.75 13.25 17.3096 13.25 18C13.25 18.6904 13.8096 19.25 14.5 19.25Z" fill="var(--bcsidebar-text)"></path></g></svg>`,
};
const navMenu = document.getElementById("menu");
@ -2584,25 +2624,25 @@ function populateSidebarFromNav(sidebarContent) {
// Check if svg already has a style attribute
if (icon.includes('style="')) {
// Append to existing style
icon = icon.replace(/style="([^"]*)"/, `style="$1 width:20px;height:20px;flex-shrink:0;fill:white;stroke:white;"`);
icon = icon.replace(/style="([^"]*)"/, `style="$1 width:20px;height:20px;flex-shrink:0;fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);"`);
} else {
// Add new style attribute
icon = icon.replace("<svg", '<svg style="width:20px;height:20px;flex-shrink:0;fill:white;stroke:white;"');
icon = icon.replace("<svg", '<svg style="width:20px;height:20px;flex-shrink:0;fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);"');
}
} else {
// Smaller SVG - just add colors
if (icon.includes('style="')) {
icon = icon.replace(/style="([^"]*)"/, `style="$1 fill:white;stroke:white;flex-shrink:0;"`);
icon = icon.replace(/style="([^"]*)"/, `style="$1 fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);flex-shrink:0;"`);
} else {
icon = icon.replace("<svg", '<svg style="fill:white;stroke:white;flex-shrink:0;"');
icon = icon.replace("<svg", '<svg style="fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);flex-shrink:0;"');
}
}
} else {
// No viewBox - just add colors
if (icon.includes('style="')) {
icon = icon.replace(/style="([^"]*)"/, `style="$1 fill:white;stroke:white;"`);
icon = icon.replace(/style="([^"]*)"/, `style="$1 fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);"`);
} else {
icon = icon.replace("<svg", '<svg style="fill:white;stroke:white;"');
icon = icon.replace("<svg", '<svg style="fill:var(--bcsidebar-text);stroke:var(--bcsidebar-text);"');
}
}
}
@ -3016,36 +3056,67 @@ async function changeColorPreset(colors) {
Dark mode
*/
// Light-mode fallback values for the --bc* theme variables. These are always
// emitted (even when dark mode is OFF) so extension UI referencing
// var(--bctext-0) / var(--bcborders) / var(--bcbackground-*) still renders
// correctly in light mode (e.g. better sidebar/todo icons stay dark instead
// of falling back to the SVG initial values). Dark mode overrides these below.
const BC_LIGHT_DEFAULTS = {
"background-0": "#ffffff",
"background-1": "#c7c7c7",
"background-2": "#d9d9d9",
"borders": "#808080",
"links": "#418df1",
"sidebar": "#e3e3e3",
"sidebar-text": "#000000",
"text-0": "#000000",
"text-1": "#050505",
"text-2": "#4f4f4f"
};
function generateDarkModeCSS() {
let css =
(options.device_dark === true
? "@media (prefers-color-scheme: dark) {\n"
: "") + ":root{\n";
if (options.dark_preset) {
Object.keys(options.dark_preset).forEach((key) => {
css += " --bc" + key + ": " + options.dark_preset[key] + ";\n";
});
}
css += "}\n\n";
css += DARKMODE_CSS;
css += options.device_dark === true ? "\n}" : "";
return css;
// Always-on light-mode defaults so var(--bc*) resolves in light mode too.
let css = ":root{\n";
Object.keys(BC_LIGHT_DEFAULTS).forEach((key) => {
css += " --bc" + key + ": " + BC_LIGHT_DEFAULTS[key] + ";\n";
});
css += "}\n\n";
const darkOn = options.dark_mode === true || options.device_dark === true;
if (!darkOn) return css;
let darkBlock = ":root{\n";
if (options.dark_preset) {
Object.keys(options.dark_preset).forEach((key) => {
darkBlock += " --bc" + key + ": " + options.dark_preset[key] + ";\n";
});
}
darkBlock += "}\n\n";
darkBlock += DARKMODE_CSS;
if (options.device_dark === true) {
css += "@media (prefers-color-scheme: dark) {\n" + darkBlock + "\n}";
} else {
css += darkBlock;
}
return css;
}
let darkStyleInserted = false;
function toggleDarkMode() {
const css = generateDarkModeCSS();
if ((options.dark_mode === true || options.device_dark === true) && !darkStyleInserted) {
const darkOn = options.dark_mode === true || options.device_dark === true;
if (!darkStyleInserted) {
let style = document.createElement('style');
style.textContent = css;
document.documentElement.append(style);
style.id = 'darkcss';
style.className = "canvasrefined-darkmode-enabled";
style.className = darkOn ? "canvasrefined-darkmode-enabled" : "";
darkStyleInserted = true;
} else if (darkStyleInserted) {
} else {
let style = document.querySelector("#darkcss");
style.textContent = options.dark_mode === true || options.device_dark ? css : "";
style.className = options.dark_mode === true || options.device_dark ? "canvasrefined-darkmode-enabled" : "";
style.textContent = css;
style.className = darkOn ? "canvasrefined-darkmode-enabled" : "";
}
runiframeChecker();
}
@ -4043,6 +4114,7 @@ function applyAestheticChanges() {
if (options.disable_color_overlay === true) style.textContent += ".ic-DashboardCard__header_hero{opacity: 0!important} .ic-DashboardCard__header-button-bg{opacity: 1!important}";
if (options.hide_feedback === true) style.textContent += ".recent_feedback {display: none}";
if (options.full_width === true) style.textContent += "#wrapper,.ic-Layout-wrapper{max-width:100%!important}";
if (options.center_cards === true) style.textContent += ".ic-DashboardCard__box__container{display:flex!important;flex-wrap:wrap!important;justify-content:center!important;align-items:flex-start!important}";
if (options.customCardStyles === true) {
if (options.imageSize !== undefined && options.imageSize !== 100) style.textContent += `.ic-DashboardCard__header_image {transform: scale(${options.imageSize / 100})!important; }`;

View File

@ -1,14 +1,17 @@
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', 'hide_new_canvas', 'center_cards'];
const syncedSubOptions = [
"todo_hide_feedback",
"todo_full_height",
"todo_confetti",
"todo_progress_rings",
"device_dark",
"relative_dues",
"card_overdues",
"equal_height_cards",
// "todo_overdues",
"gpa_calc_prepend",
"gpa_calc_cumulative",
"gpa_calc_weighted",
"auto_dark",
"auto_dark_start",
"auto_dark_end",
@ -34,6 +37,22 @@ const syncedSubOptions = [
"sidebar_scale",
];
const localSwitches = [];
// Theme export categories. Only visual/appearance settings belong in a
// shared theme; personal productivity features (reminders, dashboard notes,
// the card-assignments list and its display prefs, card grades, item counts)
// are intentionally excluded so exported themes never carry personal stuff.
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"];
const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_height_cards", "remlogo", "hide_new_canvas", "hide_feedback", "tab_icons"];
const exportSidebar = ["better_sidebar", "sidebar_scale"];
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_hr24", "todo_separate_scrollbar"];
const exportGpa = ["gpa_calc", "gpa_calc_prepend", "gpa_calc_cumulative", "gpa_calc_weighted"];
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen"];
// Master "On/off toggles" = every visual toggle (no GPA, no dark-mode schedule,
// no personal productivity features).
const exportToggles = ["dark_mode"].concat(exportCardColorToggles, exportLayout, exportSidebar, exportTodo);
const fontsDropdownStateKey = "fonts_dropdown_open";
const apiurl = "none";
@ -74,12 +93,13 @@ const defaultOptions = {
"dashboard_notes": false,
"dashboard_notes_text": "",
"dashboard_notes_mode": "edit",
"better_todo": false,
"better_todo": true,
"better_sidebar": false,
"sidebar_scale": 100,
"todo_hr24": false,
"todo_separate_scrollbar": false,
"condensed_cards": false,
"center_cards": false,
"custom_cards": {},
"custom_cards_2": {},
"custom_cards_3": {},
@ -112,6 +132,7 @@ const defaultOptions = {
"relative_dues": false,
"equal_height_cards": false,
"hide_feedback": false,
"hide_new_canvas": true,
"dark_mode_fix": [],
"assignment_states": {},
"tab_icons": false,
@ -184,6 +205,28 @@ function displayDarkModeFixUrls() {
document.addEventListener("DOMContentLoaded", setup);
// toggle visibility of the Discord/GitHub social links (persists locally)
document.addEventListener("DOMContentLoaded", () => {
const toggle = document.getElementById("social-toggle");
const buttons = document.querySelector(".social-buttons");
if (!toggle || !buttons) return;
const KEY = "hide_socials";
const render = (hidden) => {
buttons.classList.toggle("hidden", hidden);
toggle.textContent = chrome.i18n.getMessage(hidden ? "show_links" : "hide_links");
};
chrome.storage.local.get(KEY, (res) => render(!!res[KEY]));
const flip = () => chrome.storage.local.get(KEY, (res) => {
const next = !res[KEY];
chrome.storage.local.set({ [KEY]: next });
render(next);
});
toggle.addEventListener("click", flip);
toggle.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); flip(); }
});
});
function setupAssignmentsSlider(initial) {
let el = document.querySelector('#numAssignmentsSlider');
el.value = initial;
@ -929,7 +972,9 @@ function setup() {
// give everything the appropirate i18n text
document.querySelectorAll('[data-i18n]').forEach(text => {
text.innerText = chrome.i18n.getMessage(text.dataset.i18n);
const msg = chrome.i18n.getMessage(text.dataset.i18n);
// keep the in-HTML fallback text when a key is missing instead of blanking it
if (msg) text.innerText = msg;
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
const msg = chrome.i18n.getMessage(el.dataset.i18nPlaceholder);
@ -1006,38 +1051,70 @@ function setup() {
importTheme(obj);
});
// copy export output to clipboard
document.querySelector("#export-copy").addEventListener("click", async () => {
const output = document.querySelector("#export-output");
const btn = document.querySelector("#export-copy");
const text = output.value;
if (!text) { displayAlert(true, "Nothing to copy — select what to export first."); return; }
const flash = () => { const old = btn.textContent; btn.textContent = "Copied!"; setTimeout(() => { btn.textContent = old; }, 1200); };
try {
await navigator.clipboard.writeText(text);
flash();
} catch (e) {
output.select();
document.execCommand("copy");
flash();
}
});
// activate export checkbox
document.querySelectorAll(".export-details input").forEach(input => {
input.addEventListener("change", () => {
chrome.storage.sync.get(syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds"]), async storage => {
chrome.storage.sync.get(syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds", "custom_styles"]), async storage => {
let final = {};
for await (item of document.querySelectorAll(".export-details input")) {
if (item.checked) {
switch (item.id) {
case "export-toggles":
final = { ...final, ...(await getExport(storage, syncedSwitches.concat(syncedSubOptions))) };
final = { ...final, ...(await getExport(storage, exportToggles)) };
break;
case "export-dark":
final = { ...final, ...(await getExport(storage, ["dark_preset"])) };
final = { ...final, ...(await getExport(storage, ["dark_preset", "dark_mode"])) };
break;
case "export-dark-schedule":
final = { ...final, ...(await getExport(storage, exportDarkSchedule)) };
break;
case "export-cards":
final = { ...final, ...(await getExport(storage, ["custom_cards"])) };
break;
case "export-colors":
final = { ...final, ...(await getExport(storage, ["card_colors", ...exportCardColorToggles])) };
break;
case "export-card-styles":
final = { ...final, ...(await getExport(storage, exportCardStyles)) };
break;
case "export-customStyles":
final = { ...final, ...(await getExport(storage, ["custom_styles"])) };
break;
case "export-font":
final = { ...final, ...(await getExport(storage, ["custom_font"])) };
break;
case "export-colors":
final = { ...final, ...(await getExport(storage, ["card_colors"])) }
case "export-background":
final = { ...final, ...(await getExport(storage, exportBackground)) };
break;
case "export-layout":
final = { ...final, ...(await getExport(storage, exportLayout)) };
break;
case "export-sidebar":
final = { ...final, ...(await getExport(storage, exportSidebar)) };
break;
case "export-todo":
final = { ...final, ...(await getExport(storage, exportTodo)) };
break;
case "export-gpa":
final = { ...final, ...(await getExport(storage, ["gpa_calc_bounds"])) }
final = { ...final, ...(await getExport(storage, [...exportGpa, "gpa_calc_bounds"])) };
break;
case "export-customStyles":
final = { ...final, ...(await getExport(storage, ["custom_styles"])) };
break;
case "export-background":
final = { ...final, ...(await getExport(storage, ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "fitImageToScreen"])) };
break;
}
}
}
@ -1348,12 +1425,7 @@ async function getExport(storage, options) {
}
break;
case "custom_styles":
final["customCardStyles"] = storage["customCardStyles"];
final["imageSize"] = storage["imageSize"];
final["cardRoundness"] = storage["cardRoundness"];
final["cardSpacing"] = storage["cardSpacing"];
final["cardWidth"] = storage["cardWidth"];
final["cardHeight"] = storage["cardHeight"];
final["custom_styles"] = storage["custom_styles"];
break;
default:
final[option] = storage[option];
@ -1458,7 +1530,7 @@ let fallback = false;
function saveCurrentTheme() {
const allOptions = syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds", "card_colors"]);
const allOptions = syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds", "card_colors", "custom_styles"]);
chrome.storage.local.get("saved_themes", local => {
chrome.storage.sync.get(allOptions, async sync => {
let current = await getExport(sync, allOptions);
@ -1473,10 +1545,10 @@ function saveCurrentTheme() {
"better_todo": current["better_todo"],
"todo_hide_feedback": current["todo_hide_feedback"],
"todo_full_height": current["todo_full_height"],
"todo_confetti": current["todo_confetti"],
"todo_progress_rings": current["todo_progress_rings"],
"todo_hr24": current["todo_hr24"],
"todo_separate_scrollbar": current["todo_separate_scrollbar"],
"num_todo_items": current["num_todo_items"],
"hover_preview": current["hover_preview"],
"better_sidebar": current["better_sidebar"],
"sidebar_scale": current["sidebar_scale"],
"imageSize": current["imageSize"],
@ -1484,9 +1556,13 @@ function saveCurrentTheme() {
"cardSpacing": current["cardSpacing"],
"cardWidth": current["cardWidth"],
"cardHeight": current["cardHeight"],
"custom_styles": current["custom_styles"],
"customCardStyles": current["customCardStyles"],
"customBackgroundLink": current["customBackgroundLink"],
"customBackgroundScale": current["customBackgroundScale"],
"customBackgroundDaily": current["customBackgroundDaily"],
"customBackgroundNasaDaily": current["customBackgroundNasaDaily"],
"fitImageToScreen": current["fitImageToScreen"],
}
const now = new Date();
local["saved_themes"][now.getTime()] = trimmed;
@ -1498,7 +1574,15 @@ function saveCurrentTheme() {
}
async function displayThemeList(direction = 0) {
function displayThemeList(direction = 0) {
// render the full sorted theme list (filtered by any active search).
// Previously this was an empty stub, so the browser showed nothing
// until the search box fired displayThemeSearchList directly.
let themesToShow = allThemes;
if (searchFor) {
themesToShow = allThemes.filter(theme => theme.title.toLowerCase().includes(searchFor.toLowerCase()));
}
displayThemeSearchList(themesToShow, direction);
}
@ -1507,7 +1591,7 @@ function displayThemeSearchList(themesToShow, pageDir = 0) {
const perPage = 24;
const maxPage = Math.ceil(themesToShow.length / perPage);
if (pageDir == -1 && current_page_num > 1) current_page_num--;
if (pageDir == 1 && curren_page_num < maxPage) current_page_num++;
if (pageDir == 1 && current_page_num < maxPage) current_page_num++;
let container = document.getElementById("premade-themes");
container.textContent = "";
let start = (current_page_num - 1) * perPage, end = start + perPage;
@ -1520,7 +1604,7 @@ function displayThemeSearchList(themesToShow, pageDir = 0) {
makeElement("p", themeBtn, {"className": "theme-button-title", "textContent": split[0] });
makeElement("p", themeBtn, {"className": "theme-button-creator", "textContent": split[1] });
themeBtn.addEventListener("click", () => {
const allOptions = syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds", "card_colors"]);
const allOptions = syncedSwitches.concat(syncedSubOptions).concat(["dark_preset", "custom_cards", "custom_font", "gpa_calc_bounds", "card_colors", "custom_styles"]);
chrome.storage.sync.get(allOptions, sync => {
chrome.storage.local.get(["previous_theme"], async local => {
if (local["previous_theme"] === null) {

View File

@ -1,8 +1,8 @@
{
"manifest_version": 3,
"name": "CanvasRefined",
"name": "Canvas Refined",
"description": "Even More Feature packed extension for Canvas.",
"version": "6.2.61",
"version": "6.3.0",
"icons": {
"16": "icon/icon-16.png",
"32": "icon/icon-32.png",

View File

@ -10,3 +10,5 @@ I made a few posts on reddit (probably not the best idea) to ask if someone can
They also said “if the user wants to make a net-new extension, we can't stop them. but if they use our code from our open source repo, a stipulaton is they can't post on webstore” despite me having the MIT license. They also emailed me again about it and started talking about their lawyers and not wanting to start billable hours which is when I contacted FULU because I didn't know who to contact and my friends suggested it. (Thank you so much Louis Rossmann!).
After only a few days he respond with a lot of inspiring words and made a community post on his youtube channel advertising my repo, which seemed to have gotten betterCampus off my back and is why the starchart looks like that.
Someone who I think is from BetterCampus but I'm not sure made a crazy one star review saying this is a blatent copy of BetterCampus and this project "undermines the work and goals of the people who originally created and grew BetterCanvas" (my bad for undermining the goal to charge money, stick unnessesary AI, and put brands in the student learning experience). I made a reddit post about it [here](https://www.reddit.com/r/opensource/comments/1vfudak/company_probably_is_mad_i_forked_an_old_version/)