diff --git a/tdx-enhanced.js b/tdx-enhanced.js index ca10fe2..ca97a2a 100644 --- a/tdx-enhanced.js +++ b/tdx-enhanced.js @@ -1,21 +1,29 @@ // ==UserScript== // @name tdx-enhanced -// @namespace ecn -// @version 2025-02-05-01 +// @namespace purdue-it +// @version 2025-05-31-01 // @description enhanced tdx coloring & formatting. follows system color scheme. // @author Purdue STEM IT - it@purdue.edu -// @match https://service.purdue.edu/TDNext/* -// @match https://service.purdue.edu/TDWebApi/* +// @match https://service.purdue.edu/TDWorkManagement* +// @match https://service.purdue.edu/tdworkmanagement* +// @match https://service.purdue.edu/TDNext* +// @match https://service.purdue.edu/TDWebApi* +// @match https://purdue.teamdynamixpreview.com/TDWorkManagement* +// @match https://purdue.teamdynamixpreview.com/TDNext* // @require https://momentjs.com/downloads/moment.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.6.0/tinycolor.min.js // @downloadURL https://raw.github.itap.purdue.edu/ECN/tdx-userscript/main/tdx-enhanced.js // @updateURL https://raw.github.itap.purdue.edu/ECN/tdx-userscript/main/tdx-enhanced.js -// @grant GM.addStyle +// @grant GM_getResourceText +// @grant GM_addStyle // @run-at document-end // ==/UserScript== (function() { 'use strict'; + + const primaryTitle = "Purdue University IT" + /* QUEUE COLORS */ //from main webqueue script @@ -79,75 +87,381 @@ /* BEGIN FUNCTIONS */ - function updateHeading(mutation,numItems,totalItems) { - let headings = mutation.querySelectorAll(".panel-title") + //color manipulation via css filters: https://github.com/angel-rs/css-color-filter-generator + //modified to return an object instead of filter string + + class Color { + constructor(r, g, b) { + this.set(r, g, b); + } + + toString() { + return `rgb(${Math.round( + this.r + )}, ${Math.round(this.g)}, ${Math.round(this.b)})`; + } + + set(r, g, b) { + this.r = this.clamp(r); + this.g = this.clamp(g); + this.b = this.clamp(b); + } + + hueRotate(angle = 0) { + angle = (angle / 180) * Math.PI; + const sin = Math.sin(angle); + const cos = Math.cos(angle); + + this.multiply([ + 0.213 + cos * 0.787 - sin * 0.213, + 0.715 - cos * 0.715 - sin * 0.715, + 0.072 - cos * 0.072 + sin * 0.928, + 0.213 - cos * 0.213 + sin * 0.143, + 0.715 + cos * 0.285 + sin * 0.14, + 0.072 - cos * 0.072 - sin * 0.283, + 0.213 - cos * 0.213 - sin * 0.787, + 0.715 - cos * 0.715 + sin * 0.715, + 0.072 + cos * 0.928 + sin * 0.072 + ]); + } + + grayscale(value = 1) { + this.multiply([ + 0.2126 + 0.7874 * (1 - value), + 0.7152 - 0.7152 * (1 - value), + 0.0722 - 0.0722 * (1 - value), + 0.2126 - 0.2126 * (1 - value), + 0.7152 + 0.2848 * (1 - value), + 0.0722 - 0.0722 * (1 - value), + 0.2126 - 0.2126 * (1 - value), + 0.7152 - 0.7152 * (1 - value), + 0.0722 + 0.9278 * (1 - value) + ]); + } + + sepia(value = 1) { + this.multiply([ + 0.393 + 0.607 * (1 - value), + 0.769 - 0.769 * (1 - value), + 0.189 - 0.189 * (1 - value), + 0.349 - 0.349 * (1 - value), + 0.686 + 0.314 * (1 - value), + 0.168 - 0.168 * (1 - value), + 0.272 - 0.272 * (1 - value), + 0.534 - 0.534 * (1 - value), + 0.131 + 0.869 * (1 - value) + ]); + } + + saturate(value = 1) { + this.multiply([ + 0.213 + 0.787 * value, + 0.715 - 0.715 * value, + 0.072 - 0.072 * value, + 0.213 - 0.213 * value, + 0.715 + 0.285 * value, + 0.072 - 0.072 * value, + 0.213 - 0.213 * value, + 0.715 - 0.715 * value, + 0.072 + 0.928 * value + ]); + } + + multiply(matrix) { + const newR = this.clamp( + this.r * matrix[0] + this.g * matrix[1] + this.b * matrix[2] + ); + const newG = this.clamp( + this.r * matrix[3] + this.g * matrix[4] + this.b * matrix[5] + ); + const newB = this.clamp( + this.r * matrix[6] + this.g * matrix[7] + this.b * matrix[8] + ); + this.r = newR; + this.g = newG; + this.b = newB; + } + + brightness(value = 1) { + this.linear(value); + } + contrast(value = 1) { + this.linear(value, -(0.5 * value) + 0.5); + } + + linear(slope = 1, intercept = 0) { + this.r = this.clamp(this.r * slope + intercept * 255); + this.g = this.clamp(this.g * slope + intercept * 255); + this.b = this.clamp(this.b * slope + intercept * 255); + } + + invert(value = 1) { + this.r = this.clamp((value + (this.r / 255) * (1 - 2 * value)) * 255); + this.g = this.clamp((value + (this.g / 255) * (1 - 2 * value)) * 255); + this.b = this.clamp((value + (this.b / 255) * (1 - 2 * value)) * 255); + } + + hsl() { + // Code taken from https://stackoverflow.com/a/9493060/2688027, licensed under CC BY-SA. + const r = this.r / 255; + const g = this.g / 255; + const b = this.b / 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h, + s, + l = (max + min) / 2; + + if (max === min) { + h = s = 0; + } else { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + + case g: + h = (b - r) / d + 2; + break; + + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + return { + h: h * 100, + s: s * 100, + l: l * 100 + }; + } + + clamp(value) { + if (value > 255) { + value = 255; + } else if (value < 0) { + value = 0; + } + return value; + } + } + + class Solver { + constructor(target, baseColor) { + this.target = target; + this.targetHSL = target.hsl(); + this.reusedColor = new Color(0, 0, 0); + } - for (const heading of headings) { - //color the header if queue name is found - let headingTxt = heading.innerText - let headingFirst = headingTxt.split(" ")[0] - headingFirst = headingFirst.toLowerCase() + solve() { + const result = this.solveNarrow(this.solveWide()); + return { + values: result.values, + loss: result.loss, + filter: this.css(result.values) + }; + } - let parent = heading.parentElement + solveWide() { + const A = 5; + const c = 15; + const a = [60, 180, 18000, 600, 1.2, 1.2]; + + let best = { loss: Infinity }; + for (let i = 0; best.loss > 25 && i < 3; i++) { + const initial = [50, 20, 3750, 50, 100, 100]; + const result = this.spsa(A, a, c, initial, 1000); + if (result.loss < best.loss) { + best = result; + } + } + return best; + } - //console.log("HEADING:",headingTxt) + solveNarrow(wide) { + const A = wide.loss; + const c = 2; + const A1 = A + 1; + const a = [0.25 * A1, 0.25 * A1, A1, 0.25 * A1, 0.2 * A1, 0.2 * A1]; + return this.spsa(A, a, c, wide.values, 500); + } - for (const qK of Object.keys(colorsByQueue)) { - let q = colorsByQueue[qK] - if (headingFirst == qK) { - //console.log("Match:",parent) - heading.style.color = q.txt - parent.style.backgroundColor = q.bg + spsa(A, a, c, values, iters) { + const alpha = 1; + const gamma = 0.16666666666666666; + + let best = null; + let bestLoss = Infinity; + const deltas = new Array(6); + const highArgs = new Array(6); + const lowArgs = new Array(6); + + for (let k = 0; k < iters; k++) { + const ck = c / Math.pow(k + 1, gamma); + for (let i = 0; i < 6; i++) { + deltas[i] = Math.random() > 0.5 ? 1 : -1; + highArgs[i] = values[i] + ck * deltas[i]; + lowArgs[i] = values[i] - ck * deltas[i]; } - handleHighlight("report",headingTxt,heading.parentElement) - //try embedding - if (headingTxt.startsWith("{")) { - try { - let embedData = JSON.parse(headingTxt) - - let title = document.createElement("h4") - title.classList = heading.classList - title.innerText = embedData.name - let moduleBody = heading.parentNode.parentNode.querySelector(".ModuleContent") - let embed = document.createElement("iframe") - embed.classList = "customEmbed" - embed.src = embedData.url - heading.replaceWith(title) - moduleBody.replaceWith(embed) - } catch { + const lossDiff = this.loss(highArgs) - this.loss(lowArgs); + + for (let i = 0; i < 6; i++) { + const g = (lossDiff / (2 * ck)) * deltas[i]; + const ak = a[i] / Math.pow(A + k + 1, alpha); + values[i] = fix(values[i] - ak * g, i); + } + + const loss = this.loss(values); + if (loss < bestLoss) { + best = values.slice(0); + bestLoss = loss; + } + } + return { values: best, loss: bestLoss }; + + function fix(value, idx) { + let max = 100; + if (idx === 2 /* saturate */) { + max = 7500; + } else if (idx === 4 /* brightness */ || idx === 5 /* contrast */) { + max = 200; + } + + if (idx === 3 /* hue-rotate */) { + if (value > max) { + value %= max; + } else if (value < 0) { + value = max + (value % max); } + } else if (value < 0) { + value = 0; + } else if (value > max) { + value = max; } + return value; + } + } + + loss(filters) { + // Argument is array of percentages. + const color = this.reusedColor; + color.set(0, 0, 0); + + color.invert(filters[0] / 100); + color.sepia(filters[1] / 100); + color.saturate(filters[2] / 100); + color.hueRotate(filters[3] * 3.6); + color.brightness(filters[4] / 100); + color.contrast(filters[5] / 100); + + const colorHSL = color.hsl(); + return ( + Math.abs(color.r - this.target.r) + + Math.abs(color.g - this.target.g) + + Math.abs(color.b - this.target.b) + + Math.abs(colorHSL.h - this.targetHSL.h) + + Math.abs(colorHSL.s - this.targetHSL.s) + + Math.abs(colorHSL.l - this.targetHSL.l) + ); + } + css(filters) { + function fmt(idx, multiplier = 1) { + return Math.round(filters[idx] * multiplier); } - //append number of items to title - //let parentDiv = document.createElement("div") + //modified here + return { + invert: `${fmt(0)}%`, + sepia: `${fmt(1)}%`, + saturate: `${fmt(2)}%`, + hue_rotate: `${fmt(3, 3.6)}deg`, + brightness: `${fmt(4)}%`, + contrast: `${fmt(5)}%` + } - //let header = document.createElement("h4") - //header.innerText = headingTxt + //return `filter: invert(${fmt( + // 0 + //)}%) sepia(${fmt(1)}%) saturate(${fmt(2)}%) hue-rotate(${fmt(3, 3.6)}deg) brightness(${fmt(4)}%) contrast(${fmt(5)}%);`; + } + } - var countSpan = heading.querySelector(".subtitle") + function hexToRgb(hex) { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, (m, r, g, b) => { + return r + r + g + g + b + b; + }); - if (!countSpan) { - countSpan = document.createElement("span") - //countSpan.innerText = `${numItems} items` - countSpan.classList.add("subtitle") + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? [ + parseInt(result[1], 16), + parseInt(result[2], 16), + parseInt(result[3], 16) + ] + : null; + } - heading.innerText = headingTxt - heading.appendChild(countSpan) + function getFilterForColor(rgbColor) { + let bestResult = null; + let iterationCount = 0; // Initialize a counter for iterations + + for (let i = 0; i < 200; i++) { + iterationCount++; // Increment the counter for each iteration + const color = new Color(rgbColor[0], rgbColor[1], rgbColor[2]); + const solver = new Solver(color); + const result = solver.solve(); + + if (!bestResult || result.loss < bestResult.loss) { + bestResult = result; + if (bestResult.loss <= 0.01) { + // exit early if good result is found + break; + } } + } + + console.log("Total iterations: ", iterationCount); // Log the total iterations + return bestResult; + } - var countTxt = null + //end color manipulation - if (totalItems) { - countTxt = `${numItems} of ${totalItems} ${totalItems == 1 ? 'item' : 'items'}` - } else if (numItems) { - countTxt = `${numItems} ${numItems == 1 ? 'item' : 'items'}` - } + function updateHeading(mutation) { + let headings = mutation.querySelectorAll(".tdx-control-bar__title") + + for (const heading of headings) { + //color the header if queue name is found + let headingTxt = heading.innerText + let headingFirst = headingTxt.split(" ")[0] + headingFirst = headingFirst.toLowerCase() - if (countTxt) { - countSpan.innerText = countTxt + let parent = heading.parentElement + + handleHighlight("report",headingTxt,heading) + + //try embedding + if (headingTxt.startsWith("{")) { + try { + let embedData = JSON.parse(headingTxt) + + let title = document.createElement("h4") + title.classList = heading.classList + title.innerText = embedData.name + let moduleBody = heading.parentNode.parentNode.parentNode.querySelector(".tdx-widget") + let embed = document.createElement("iframe") + embed.classList = "customEmbed" + embed.src = embedData.url + heading.replaceWith(title) + moduleBody.replaceWith(embed) + } catch { + } } } } @@ -156,7 +470,7 @@ let qBlock = document.querySelector("#ctlAttribute2285") //ticket or task - let header = document.getElementById("thTicket_spnTitle") || document.querySelector("#upTaskHeader>h1") + let header = document.getElementById("thTicket_spnTitle") || document.querySelector("#upTaskHeader>div>h1") //find calendar inputs when editing tickets/tasks //let calendars = document.querySelectorAll("input.hasDatepicker") @@ -318,6 +632,8 @@ feedItems = document.querySelectorAll("#ttDescription") } + //console.log("Feed items:",feedItems) + if (feedItems) { for (const feedItem of feedItems) { [...feedItem.querySelectorAll("*[style*='color']")].forEach(feedElement=>{ @@ -360,7 +676,26 @@ } function parseOtherElements() { - let path = document.location.pathname + let path = document.location.pathname; + + //inject styles into search bar + [...document.querySelectorAll("tdx-search-bar, .js_sidePanelX")].forEach(searchBar=>{ + //console.log("SEARCH BAR:",searchBar) + let shadow = searchBar.shadowRoot + if (shadow) { + + injectOtherStyles(shadow); + + //check nested shadows + [...shadow.querySelectorAll("tdx-close-x")].forEach(nested=>{ + let nestedShadow = nested.shadowRoot + //console.log("NESTED:",nestedShadow) + if (nestedShadow) { + injectOtherStyles(nestedShadow) + } + }) + } + }) let desktopLayout = settings('get','layout') let desktop = document.querySelector("#divContent") @@ -401,9 +736,10 @@ //apply sticky columns let stickyColumns = settings('get','stickyColumns') if (stickyColumns) { - [...document.querySelectorAll(".desktop-column")].forEach(column=>{ + [...document.querySelectorAll(".tdx-dashboard__column")].forEach(column=>{ column.style.position = "sticky" - column.style.top = "45px" + column.style.alignSelf = "flex-start" + column.style.top = "4px" }); } } @@ -412,25 +748,24 @@ return (Number(`0x1${hex}`) ^ 0xFFFFFF).toString(16).substr(1).toUpperCase() } - function parseTable(mutation) { - let t = mutation.target; - //console.error("BEGIN MUTATION") - let table = t.querySelector("table.report-viewer") + function parseTable(element) { + //let t = mutation.target; + //console.error("BEGIN MUTATION",t) + let table = element.querySelector("table") let items = []; - var numItems = null - var totalItems = null - if (table) { let headers = []; //console.log("Table",table); //get headers of the tables [...table.querySelectorAll("th")].forEach((header)=>{ - var txt = header.querySelector("a") - txt = txt.dataset.sort - txt = txt.split(" ")[0] - headers.push(txt) + var txt = header.querySelector("span") + //txt = txt.dataset.sort + //txt = txt.split(" ")[0] + if (txt) { + headers.push(txt.textContent) + } //console.log("i:",header.cellIndex,txt) }); @@ -441,13 +776,16 @@ //console.log("row:",row); let item = {}; //parse each cell - [...row.querySelectorAll("td")].forEach((cell)=>{ - let i = cell.cellIndex; + let i = 0; + [...row.querySelectorAll("td:not(:has(input))")].forEach((cell)=>{ + //let i = cell.cellIndex; //can't use cellindex if there's a checkbox in the table let txt = cell.textContent; + txt = txt.trim() //console.log("Cell:",i,cell); item[headers[i]] = {txt:txt,cell:cell} + i++ }) item.row = row @@ -457,23 +795,9 @@ //being coloring parseItem(item) }) - - //update panel heading above table - numItems = items.length - //totalItems = null - - let pagination = t.querySelector(".pull-right .bootstrap-pagination-label") - if (pagination) { - let pTxt = pagination.innerText - pTxt = pTxt.split(" ")[0] - totalItems = pTxt - } - - - //console.log("Items:",items) } - updateHeading(t.parentElement,numItems,totalItems) + //updateHeading(element) } function createHighlightBubble(element,bgColor,txtColor) { @@ -499,10 +823,12 @@ //modify/color the cells function parseItem(item) { + //console.log("Parse item:",item) + //color queue names - if ('2285' in item) { - let qCell = item["2285"].cell - let qTxt = item["2285"].txt.toLowerCase() + if ('STEM Support' in item) { + let qCell = item["STEM Support"].cell + let qTxt = item["STEM Support"].txt.toLowerCase() if (qTxt in colorsByQueue) { let q = colorsByQueue[qTxt] @@ -511,7 +837,7 @@ } //change date modified - let relativeDates = ['LastModifiedDate','ModifiedDate','CreatedDate'] + let relativeDates = ['Modified','Created'] for (const dType of relativeDates) { if (dType in item) { let dTxt = item[dType].txt @@ -521,14 +847,14 @@ let dTxtNew = date.fromNow() if (dTxtNew == "Invalid date") { - console.warn("Failed date:",dTxt,item) + //console.warn("Failed date:",dTxt,item) } else { dCell.innerText = dTxtNew } } } - let detailedDates = ['DueDate','StartDate'] + let detailedDates = ['Date Due','Due','Created','Start'] for (const dType of detailedDates) { if (dType in item) { let dTxt = item[dType].txt @@ -538,7 +864,7 @@ let dTxtNew = date.calendar() if (dTxtNew == "Invalid date") { - console.warn("Failed date:",dTxt,item) + //console.warn("Failed date:",dTxt,item) } else { dCell.innerText = dTxtNew } @@ -546,7 +872,7 @@ } //highlight modified/age red - let modifiedDates = ['LastModifiedDate','ModifiedDate'] + let modifiedDates = ['Modified'] for (const dType of modifiedDates) { if (dType in item) { let modDate = item[dType] @@ -566,10 +892,10 @@ } //find user replies & last modified by internal - if ('CustomerName' in item && 'LastModifiedByFullName' in item && 'ResponsibleFullName' in item) { - let fromUser = item.CustomerName - let lastModified = item.LastModifiedByFullName - let assignedTo = item.ResponsibleFullName + if ('Requestor' in item && 'Modified By' in item && 'Prim Resp' in item) { + let fromUser = item["Requestor"] + let lastModified = item["Modified By"] + let assignedTo = item["Prim Resp"] //reply from user if (fromUser.txt == lastModified.txt && fromUser.txt != assignedTo.txt && assignedTo.txt != "Unassigned") { @@ -579,7 +905,7 @@ //modified by internal if (lastModified.txt != fromUser.txt && lastModified.txt != assignedTo.txt) { - let cell = item.LastModifiedByFullName.cell + let cell = lastModified.cell //cell.style.backgroundColor = "var(--col-modified)" handleHighlight("userModified",null,cell) } @@ -650,6 +976,8 @@ } } else { + return + //leave default link.onclick = (event)=>{ event.preventDefault() var params @@ -729,6 +1057,7 @@ if (type=="report" && customType=="report") { if (customHighlight.value==txt) { + element.classList.add("reportTitle") style = customHighlight.style } } @@ -776,9 +1105,20 @@ } function injectOtherStyles(element) { + + let s = document.createElement("style") + s.id = "customStyles" + s.innerText = customStyles + //find iframes - likely text editors if (element.tagName == "IFRAME") { - let frame = element.contentWindow.document + let frame = element.contentWindow.document; + + //attempt color coded tables on non-desktop pages + /*let tables = frame.querySelectorAll("table"); + [...tables].forEach(table=>{ + parseTable(table.parentElement) + })*/ //console.log("Found iFrame:",frame) @@ -787,31 +1127,51 @@ let head = frame.querySelector("head") let html = frame.querySelector("html") html.classList.add(settings("get","colorMode")) - let s = document.createElement("style") - s.id = "customStyles" - s.innerText = customStyles - head.appendChild(s) } + } else { + if (!element.querySelector("#customStyles")) { + setCssFilters(s) + element.appendChild(s) + } } -} + } //setup observer to watch report/table changes/refreshes let observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { let t = mutation.target - //console.log("Mutation:",mutation) - if (t.classList.contains("ModuleContent")) { - parseTable(mutation) + //console.error("Mutation:",t) + if (t.querySelector("div > table")) { + //let table = t.firstElementChild.querySelector("table") + //console.log("Matched table",t) + parseTable(t) } //parse the feed if it updates if (t.classList.contains("feed")) { parseTicket(mutation.addedNodes) + } else if (t.classList.contains("feed-body")) { + parseTicket(t.querySelectorAll(".feed-reply")) + } + + //parse dashboard widgets + if (t.id=="tdx-workmgmt-container-collection") { + parseOtherElements() + } + + //parse side panels + if (t.classList.contains("tdx-right-side-panel")) { + parseOtherElements() + } + + let module = t.querySelector("div > .tdx-dashboard__widget-container") + if (module) { + updateHeading(t) } //search whole document, frames may reset when reopened - let frames = document.querySelectorAll("iframe") + let frames = document.querySelectorAll("iframe:not(.customEmbed)") if (frames) { [...frames].forEach(frame=>{ injectOtherStyles(frame) @@ -831,9 +1191,9 @@ } async function changeTitle() { - let title = document.querySelector(".organization-link a") + let title = document.querySelector(".tdx-headerbar-headline a") if (title) { - title.innerText = "Purdue University - STEM" + title.innerText = primaryTitle } await injectToolbar() @@ -868,12 +1228,38 @@ let bg = `var(--${col}-${value}-bg)` style.setProperty(`--${col}-col-primary`, accent) style.setProperty(`--${col}-bg-primary`, `var(--${col}-bg-default)`) + } + break } } } + function setCssFilters(element=null) { + let style = element ? element.style : document.documentElement.style + //quick test for filter generation + let mode = getColorMode().split("Mode")[0] + let priCol = window.getComputedStyle(document.body).getPropertyValue("--dark-col-primary") + let rgb = hexToRgb(priCol) + //let filters = getFilterForColor(rgb) + + const color = new Color(rgb[0], rgb[1], rgb[2]); + const solver = new Solver(color); + const result = solver.solve(); + let filters = result.filter + + //console.log("STYLES:",priCol) + //console.log("FILTERS:",priCol,rgb,filters) + + style.setProperty("--filter-brightness",filters.brightness) + style.setProperty("--filter-contrast",filters.contrast) + style.setProperty("--filter-hue-rotate",filters.hue_rotate) + style.setProperty("--filter-invert",filters.invert) + style.setProperty("--filter-saturate",filters.saturate) + style.setProperty("--filter-sepia",filters.sepia) +} + /* function toggleColorMode() { let mode = getColorMode() == "darkMode" ? "lightMode" : "darkMode" @@ -914,62 +1300,12 @@ } - /* - function getColorMode() { - let localScheme = localStorage.getItem("styles") - let autoScheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? "darkMode" : "lightMode" - let scheme - - if (localScheme == "auto") { - scheme = autoScheme - } else if (!localScheme) { - localScheme = autoScheme == "darkMode" ? "lightMode" : "darkMode" - } else { - scheme = localScheme - } - - return scheme - } - - function checkColorMode() { - let localScheme = localStorage.getItem("styles") - let autoScheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? "darkMode" : "lightMode" - - let colors = localStorage.getItem("colors") - if (colors) { - console.log("Setting color from init") - setColors(colors) - } - - let primaryColor = document.querySelector("#primaryColor") - if (primaryColor) { - primaryColor.value = colors - } - - if (localScheme != autoScheme && localScheme != "auto") { - setColorMode(localScheme) - } else { - setColorMode(autoScheme,false) - } - - addEventListener("storage", (event) => { - //console.log("Storage event:",event) - if (event.key == "styles") { - setColorMode(event.newValue,false) - } else if (event.key == "colors") { - setColors(event.newValue) - } - }); - } - */ - - async function injectToolbar() { - let iconBar = document.querySelector(".tdbar-settings .pull-right") + let iconBar = document.querySelector("#globalSearchBar") let statusHTML = `
- +
+ +
` let settingsHTML = `