From e6e5336209126f97d5e919305ae423b4c01d02db Mon Sep 17 00:00:00 2001 From: Kaden Napper Date: Mon, 27 Jul 2026 18:33:38 +1000 Subject: [PATCH] feat: initialise Lildra browser extension --- .gitignore | 15 +++++ README.md | 11 ++++ background.js | 21 ++++++ icons/icon128.png | Bin 0 -> 687 bytes icons/icon16.png | Bin 0 -> 150 bytes icons/icon32.png | Bin 0 -> 221 bytes icons/icon48.png | Bin 0 -> 279 bytes lildra.js | 162 ++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 31 +++++++++ 9 files changed, 240 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 background.js create mode 100644 icons/icon128.png create mode 100644 icons/icon16.png create mode 100644 icons/icon32.png create mode 100644 icons/icon48.png create mode 100644 lildra.js create mode 100644 manifest.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4aa6a69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Packaged extension releases +*.zip + +# Operating system files +.DS_Store +Thumbs.db + +# Editor files +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json + +# Temporary files +*.tmp +*.log \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee4d395 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Lildra v0.2.3 + +A minimal Canvas/QLearn viewing mode. + +- Hides the global top navigation and course navigation +- Keeps the native Canvas page content unchanged +- Centres the normal-width content area +- Shows a compact centred single-line page title with ellipsis +- Shows the product name and version at the bottom-right + +Toggle with the extension button or **Alt+L**. diff --git a/background.js b/background.js new file mode 100644 index 0000000..d90494f --- /dev/null +++ b/background.js @@ -0,0 +1,21 @@ +"use strict"; + +async function toggleLildra(tab) { + if (!tab?.id) return; + + try { + await chrome.scripting.executeScript({ + target: { tabId: tab.id }, + files: ["lildra.js"] + }); + } catch (error) { + console.warn("Lildra could not run on this page:", error); + } +} + +chrome.action.onClicked.addListener(toggleLildra); +chrome.commands.onCommand.addListener(async (command) => { + if (command !== "toggle-lildra") return; + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + await toggleLildra(tab); +}); diff --git a/icons/icon128.png b/icons/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..29f48e5790f032af1dfee46c23e32e1f0bc65f32 GIT binary patch literal 687 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU@G%;aSW-L^Y-pWFJng$hlia% zvcjSyYrH&HMf^-y#&N{yQb$7BvwJ(4pVw@)=P5pa?%gK)l<%ecVrFlUTP40Aij5(i zp#U4vf0a99?bg4iH&$_~@-4sqN_9)@(c=oQ4*U-mJaUCW=1*-}n}I3=`~7q`j+KlS zKO|-Y zvp@Lo+U3n&`Q`_0OdnpYH@9N;XV~$MVb8DYhDO{#<~k7bvbC^3g9UR#^rQbQ_iAeP z<^KBpeEIh!QwwtJ829{NzdGIQ9@7IJhI0%9OHBK9bI00k|IWm;Uah}%P8i)idBqIv zm$Q#qMPfrzd}jGFkBb zX3kAvhvE|U2|vviBn46R2!t7 zcbq?Sxj2i#yzRsGuG?vz4EqC{BOY32b~1z1F< y#)b#qXYJW=#w9DO9k3`JAvues8d#jg}y>8c;53qe-}8#2Q@ur~=gyTVZJnKYEi4A0m5Lx$O*1N0IC zXvUhw)A9DN94oH(Q}NoNq=~l*&M6Ve3ZQ~>aw5qd&=Q^~pT&F=ivpTOb(yV?M8*5Bp2&tGe*_30Wfq(1ZY*5I_?GXhHx@2%rf8G$DW{1kf}e>N)(7 zUqjD8z002ovPDHLkV1g_^cfkMv literal 0 HcmV?d00001 diff --git a/lildra.js b/lildra.js new file mode 100644 index 0000000..777f78e --- /dev/null +++ b/lildra.js @@ -0,0 +1,162 @@ +(() => { + "use strict"; + + const VERSION = "0.2.3"; + const ROOT_CLASS = "lildra-active"; + const STYLE_ID = "lildra-layout-style"; + const HEADER_ID = "lildra-page-header"; + const FOOTER_ID = "lildra-product-footer"; + + function removeLildra() { + document.documentElement.classList.remove(ROOT_CLASS); + document.getElementById(STYLE_ID)?.remove(); + document.getElementById(HEADER_ID)?.remove(); + document.getElementById(FOOTER_ID)?.remove(); + } + + // Toggle off when run a second time. + if (document.documentElement.classList.contains(ROOT_CLASS)) { + removeLildra(); + return; + } + + function getPageTitle() { + const candidates = [ + document.querySelector("#breadcrumbs [aria-current='page'] .ellipsible")?.textContent, + document.querySelector("#breadcrumbs [aria-current='page']")?.textContent, + document.querySelector(".mobile-header-title div:last-child")?.textContent, + document.querySelector("#wiki_page_show .page-title")?.textContent, + document.querySelector("#content h1.page-title")?.textContent + ]; + + const found = candidates + .map((value) => value?.trim()) + .find((value) => value && value.length > 0); + + if (found) return found; + + // Canvas document titles normally end with ": Course name". + const browserTitle = document.title.trim(); + const separator = browserTitle.lastIndexOf(":"); + return separator > 0 ? browserTitle.slice(0, separator).trim() : browserTitle; + } + + const style = document.createElement("style"); + style.id = STYLE_ID; + style.textContent = ` + /* Hide Canvas/QLearn navigation only. */ + html.${ROOT_CLASS} #header, + html.${ROOT_CLASS} #mobile-header, + html.${ROOT_CLASS} #mobileContextNavContainer, + html.${ROOT_CLASS} .ic-app-nav-toggle-and-crumbs, + html.${ROOT_CLASS} #left-side { + display: none !important; + } + + /* Remove navigation offsets while keeping a normal, centred reading width. */ + html.${ROOT_CLASS} #wrapper { + margin-left: 0 !important; + padding-top: 42px !important; + } + + html.${ROOT_CLASS} body.primary-nav-expanded #wrapper, + html.${ROOT_CLASS} body.course-menu-expanded #wrapper, + html.${ROOT_CLASS} body.with-left-side #wrapper { + margin-left: 0 !important; + } + + html.${ROOT_CLASS} #main { + display: block !important; + width: 100% !important; + max-width: none !important; + margin: 0 !important; + } + + html.${ROOT_CLASS} #not_right_side { + display: block !important; + width: min(1100px, calc(100% - 32px)) !important; + max-width: 1100px !important; + margin: 0 auto !important; + float: none !important; + } + + html.${ROOT_CLASS} #content-wrapper, + html.${ROOT_CLASS} #content { + width: 100% !important; + max-width: none !important; + margin-left: 0 !important; + margin-right: 0 !important; + } + + /* Small fixed title bar. */ + html.${ROOT_CLASS} #${HEADER_ID} { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 2147483646 !important; + box-sizing: border-box !important; + height: 42px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + padding: 0 18px !important; + margin: 0 !important; + overflow: hidden !important; + white-space: nowrap !important; + text-overflow: ellipsis !important; + text-align: center !important; + background: #ffffff !important; + border-bottom: 1px solid rgba(0, 0, 0, 0.14) !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07) !important; + color: #1d2733 !important; + font-family: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-size: 16px !important; + font-weight: 700 !important; + line-height: 1 !important; + } + + html.${ROOT_CLASS} #${FOOTER_ID} { + position: fixed !important; + right: 14px !important; + bottom: 9px !important; + z-index: 2147483646 !important; + padding: 4px 7px !important; + border-radius: 4px !important; + background: rgba(255, 255, 255, 0.9) !important; + color: #5f259f !important; + font-family: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-size: 12px !important; + font-weight: 700 !important; + line-height: 1 !important; + pointer-events: none !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important; + } + + @media (max-width: 700px) { + html.${ROOT_CLASS} #wrapper { + padding-top: 38px !important; + } + + html.${ROOT_CLASS} #${HEADER_ID} { + height: 38px !important; + padding: 0 12px !important; + font-size: 14px !important; + } + } + `; + + const header = document.createElement("div"); + header.id = HEADER_ID; + header.setAttribute("role", "banner"); + header.textContent = getPageTitle(); + + const footer = document.createElement("div"); + footer.id = FOOTER_ID; + footer.textContent = `Lildra v${VERSION}`; + + document.documentElement.appendChild(style); + document.body.appendChild(header); + document.body.appendChild(footer); + document.documentElement.classList.add(ROOT_CLASS); +})(); diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..34bb526 --- /dev/null +++ b/manifest.json @@ -0,0 +1,31 @@ +{ + "manifest_version": 3, + "name": "Lildra", + "version": "0.2.3", + "description": "A minimal Canvas and QLearn page view with hidden navigation, centred content, a compact fixed page title and version label.", + "permissions": [ + "activeTab", + "scripting" + ], + "background": { + "service_worker": "background.js" + }, + "action": { + "default_title": "Toggle Lildra" + }, + "commands": { + "toggle-lildra": { + "suggested_key": { + "default": "Alt+L", + "mac": "Alt+L" + }, + "description": "Toggle Lildra" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } +} \ No newline at end of file