1// Theme switcher - light/dark 2const themeSwitcher = { 3 // Config 4 _scheme: "auto", 5 toggleButton: document.querySelectorAll(".theme-toggle"), 6 rootAttribute: "data-theme", 7 localStorageKey: "picoPreferredColorScheme", 8 9 // Init 10 init() { 11 this.scheme = this.schemeFromLocalStorage; 12 this.initToggle(); 13 this.updateIcon(); 14 }, 15 16 // Get color scheme from local storage 17 get schemeFromLocalStorage() { 18 return window.localStorage?.getItem(this.localStorageKey); 19 }, 20 21 // Preferred color scheme 22 get preferredColorScheme() { 23 return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; 24 }, 25 26 // Init toggle 27 initToggle() { 28 // for each toggle button add event listener 29 this.toggleButton.forEach((button) => { 30 button.addEventListener( 31 "click", 32 (event) => { 33 event.preventDefault(); 34 // Toggle scheme 35 this.scheme = this.scheme === "dark" ? "light" : "dark"; 36 this.updateIcon(); 37 }, 38 false 39 ); 40 }); 41 }, 42 43 // Set scheme 44 set scheme(scheme) { 45 if (scheme == "auto" || scheme == null) { 46 this._scheme = this.preferredColorScheme; 47 } else if (scheme == "dark" || scheme == "light") { 48 this._scheme = scheme; 49 } 50 this.applyScheme(); 51 this.schemeToLocalStorage(); 52 }, 53 54 // Get scheme 55 get scheme() { 56 return this._scheme; 57 }, 58 59 // Apply scheme 60 applyScheme() { 61 document.querySelector("html")?.setAttribute(this.rootAttribute, this.scheme); 62 }, 63 64 // Store scheme to local storage 65 schemeToLocalStorage() { 66 window.localStorage?.setItem(this.localStorageKey, this.scheme); 67 }, 68 69 // Update icon based on the current scheme 70 updateIcon() { 71 // for each toggle button update icon 72 this.toggleButton.forEach((button) => { 73 if (this.scheme === "dark") { 74 button.innerHTML = "☼"; // Sun icon for light mode 75 button.title = "light mode"; 76 } else { 77 button.innerHTML = "☽"; // Moon icon for dark mode 78 button.title = "dark mode"; 79 } 80 }); 81 }, 82}; 83 84// Init 85themeSwitcher.init(); 86 87// Menu 88 89const menuToggle = document.getElementById('menu-toggle'); 90const headerMenu = document.getElementById('header-menu'); 91 92menuToggle.addEventListener('click', function () { 93 headerMenu.classList.toggle('active'); 94}); 95 96 97// Selected menu animation 98document.addEventListener("DOMContentLoaded", function () { 99 const menuItems = document.querySelectorAll('.menu-item'); 100 const underline = document.querySelector('.underline'); 101 102 function setUnderline(item) { 103 underline.style.width = `${item.offsetWidth}px`; 104 underline.style.transform = `translateX(${item.offsetLeft}px)`; 105 } 106 107 const activeItem = document.querySelector('.menu-item.active'); 108 if (activeItem) { 109 setUnderline(activeItem); 110 } 111 112 menuItems.forEach(item => { 113 item.addEventListener('click', function (event) { 114 if (this.classList.contains('active')) { 115 return; 116 } 117 118 menuItems.forEach(i => { 119 i.classList.remove('active'); 120 }); 121 122 this.classList.add('active'); 123 124 setUnderline(this); 125 }); 126 }); 127}); 128 129 130// Colorscheme switcher 131function colorschemeSwitcher() { 132 const colorschemes = [ 133 'catppuccin', 134 'clean', 135 'dracula', 136 'github', 137 'gruvbox', 138 'iceberg', 139 'minimal', 140 'minimal_wb', 141 'monokai', 142 'nord', 143 'one', 144 'solarized', 145 'typewriter' 146 ]; 147 148 const colorschemeDropdown = document.querySelectorAll('.colorscheme-toggle'); 149 150 colorschemeDropdown.forEach((dropdown) => { 151 152 dropdown.addEventListener('change', function () { 153 const colorscheme = this.value; 154 const colorschemeLink = document.querySelector('#colorscheme-link'); 155 if (colorscheme === 'default') { 156 if (colorschemeLink) { 157 colorschemeLink.remove(); 158 } 159 160 localStorage.removeItem('marmitePreferredColorScheme'); 161 return; 162 } 163 if (colorschemeLink) { 164 colorschemeLink.href = `static/colorschemes/${colorscheme}.css`; 165 } else { 166 const link = document.createElement('link'); 167 link.id = 'colorscheme-link'; 168 link.rel = 'stylesheet'; 169 link.href = `static/colorschemes/${colorscheme}.css`; 170 document.head.appendChild(link); 171 } 172 localStorage.setItem('marmitePreferredColorScheme', colorscheme); 173 174 colorschemeDropdown.forEach((dropdown) => { 175 dropdown.value = colorscheme; 176 }); 177 }); 178
179 colorschemes.forEach((colorscheme) => { 180 const option = document.createElement('option'); 181 option.value = colorscheme; 182 option.textContent = colorscheme; 183 dropdown.appendChild(option); 184 }); 185 186 const colorscheme = localStorage.getItem('marmitePreferredColorScheme'); 187 if (colorscheme) { 188 dropdown.value = colorscheme; 189 dropdown.dispatchEvent(new Event('change')); 190 } 191 }); 192} 193 194// Function to change Giscus theme 195function changeGiscusTheme(newTheme) { 196 function sendMessage(message) { 197 const iframe = document.querySelector('iframe.giscus-frame'); 198 if (!iframe) return; 199 iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app'); 200 } 201 202 sendMessage({ 203 setConfig: { 204 theme: newTheme 205 } 206 }); 207} 208 209// Add event listener for system theme changes 210window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { 211 // Update theme switcher scheme 212 themeSwitcher.scheme = e.matches ? 'dark' : 'light'; 213 214 // Update giscus theme 215 changeGiscusTheme(e.matches ? 'dark' : 'light'); 216}); 217 218// Set initial giscus theme based on current theme when page loads 219document.addEventListener("DOMContentLoaded", function() { 220 // Check if giscus is present 221 if (document.querySelector('iframe.giscus-frame')) { 222 const isDarkMode = themeSwitcher.scheme === 'dark'; 223 changeGiscusTheme(isDarkMode ? 'dark' : 'light'); 224 } 225});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.