Fork of Chiri for Astro for my blog
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a2f7dec4d7c309420bc9d4e87b4de27dcd8a4142 104 lines 3.2 kB view raw
1<script is:inline> 2 // Favicon theme switcher for system theme-based favicon updates, using external SVG file 3 class FaviconThemeSwitcher { 4 constructor() { 5 this.faviconLink = 6 document.querySelector('link[rel="icon"]') || 7 document.querySelector('link[rel="shortcut icon"]') || 8 document.querySelector('link[rel="apple-touch-icon"]') 9 10 if (!this.faviconLink) { 11 console.warn('Favicon link not found, skipping theme switcher') 12 return 13 } 14 15 this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') 16 this.svgUrl = '/favicon.svg' 17 this.currentColor = null 18 this.svgContent = null 19 20 this.mediaQuery.addEventListener('change', () => this.updateFavicon()) 21 this.init() 22 } 23 24 async init() { 25 if (!this.svgContent) { 26 try { 27 const res = await fetch(this.svgUrl) 28 this.svgContent = await res.text() 29 } catch (e) { 30 console.warn('Failed to fetch favicon.svg:', e) 31 return 32 } 33 } 34 this.updateFavicon() 35 } 36 37 updateFavicon() { 38 const color = this.mediaQuery.matches ? '#ccc' : '#111' 39 if (this.currentColor === color) return 40 this.currentColor = color 41 this.updateFaviconColor(color) 42 } 43 44 updateFaviconColor(color) { 45 if (!this.svgContent) return 46 try { 47 const parser = new DOMParser() 48 const doc = parser.parseFromString(this.svgContent, 'image/svg+xml') 49 50 // Remove all <style> tags 51 doc.querySelectorAll('style').forEach((style) => style.remove()) 52 53 // Recursively set fill attribute for all elements 54 function setFillRecursively(node) { 55 if (node.nodeType === 1) { 56 // Element node 57 node.setAttribute('fill', color) 58 // Remove fill from style attribute if present 59 if (node.hasAttribute('style')) { 60 let style = node.getAttribute('style') 61 style = style.replace(/fill\s*:\s*[^;]+;?/gi, '') 62 node.setAttribute('style', style) 63 } 64 for (let i = 0; i < node.childNodes.length; i++) { 65 setFillRecursively(node.childNodes[i]) 66 } 67 } 68 } 69 setFillRecursively(doc.documentElement) 70 71 const serializer = new XMLSerializer() 72 const svg = serializer.serializeToString(doc) 73 const blob = new Blob([svg], { type: 'image/svg+xml' }) 74 const blobUrl = URL.createObjectURL(blob) 75 this.faviconLink.href = blobUrl 76 if (this.previousBlobUrl) { 77 URL.revokeObjectURL(this.previousBlobUrl) 78 } 79 this.previousBlobUrl = blobUrl 80 } catch (e) { 81 console.warn('Failed to update favicon color:', e) 82 } 83 } 84 } 85 86 // Initialize favicon theme switcher 87 function init() { 88 try { 89 new FaviconThemeSwitcher() 90 } catch (error) { 91 console.warn('Failed to initialize favicon theme switcher:', error) 92 } 93 } 94 95 // Initialize when DOM is ready 96 if (document.readyState === 'loading') { 97 document.addEventListener('DOMContentLoaded', init) 98 } else { 99 init() 100 } 101 102 // Re-initialize on Astro page loads 103 document.addEventListener('astro:page-load', init) 104</script>