homepage: add creative SVG favicon (inline data URL) — stylized D with speed bolt on dark rounded card; link rel=icon in head
- commit
- c6e3d36c95579b0af3ddb063940b81848b73bf9c
- author
- bamen · · 2025-09-09 16:21 UTC
- committer
- bamen · · 2025-09-09 21:51 UTC
- parent
- d3c3c1d
- signature
- unsigned
1 file · +38 -0
M frontend/src/utils/html-builder.ts +38 -0
| @@ -1,5 +1,6 @@ | |||
| 1 | 1 | export class HTMLBuilder { | |
| 2 | 2 | static createBaseHTML(): string { | |
| 3 | + | const faviconData = this.getFaviconDataURL(); | |
| 3 | 4 | return ` | |
| 4 | 5 | <!DOCTYPE html> | |
| 5 | 6 | <html lang="en"> | |
| @@ -7,6 +8,7 @@ export class HTMLBuilder { | |||
| 7 | 8 | <meta charset="UTF-8"> | |
| 8 | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| 9 | 10 | <title>Dumber Browser</title> | |
| 11 | + | <link rel="icon" type="image/svg+xml" href="${faviconData}"> | |
| 10 | 12 | <style> | |
| 11 | 13 | ${this.getStyles()} | |
| 12 | 14 | </style> | |
| @@ -32,6 +34,42 @@ export class HTMLBuilder { | |||
| 32 | 34 | </html>`; | |
| 33 | 35 | } | |
| 34 | 36 | ||
| 37 | + | private static getFaviconDataURL(): string { | |
| 38 | + | const svg = this.getFaviconSVG(); | |
| 39 | + | // Minimal encoding for safe inline usage | |
| 40 | + | const encoded = encodeURIComponent(svg) | |
| 41 | + | .replace(/'/g, '%27') | |
| 42 | + | .replace(/\(/g, '%28') | |
| 43 | + | .replace(/\)/g, '%29'); | |
| 44 | + | return `data:image/svg+xml;utf8,${encoded}`; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | private static getFaviconSVG(): string { | |
| 48 | + | // Stylized D with speed bolt on a subtle dark rounded card | |
| 49 | + | return `<?xml version="1.0" encoding="UTF-8"?> | |
| 50 | + | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> | |
| 51 | + | <defs> | |
| 52 | + | <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"> | |
| 53 | + | <stop offset="0%" stop-color="#101214"/> | |
| 54 | + | <stop offset="100%" stop-color="#1b1e22"/> | |
| 55 | + | </linearGradient> | |
| 56 | + | <filter id="inset" x="-20%" y="-20%" width="140%" height="140%"> | |
| 57 | + | <feOffset dx="0" dy="1"/> | |
| 58 | + | <feGaussianBlur stdDeviation="0.6" result="blur"/> | |
| 59 | + | <feComposite in2="SourceAlpha" operator="out"/> | |
| 60 | + | <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> | |
| 61 | + | <feBlend in="SourceGraphic" mode="normal"/> | |
| 62 | + | </filter> | |
| 63 | + | </defs> | |
| 64 | + | <rect x="4" y="4" width="56" height="56" rx="14" ry="14" fill="url(#bg)" filter="url(#inset)"/> | |
| 65 | + | <rect x="4.5" y="4.5" width="55" height="55" rx="13.5" ry="13.5" fill="none" stroke="#252a2f" stroke-width="1"/> | |
| 66 | + | <!-- D glyph --> | |
| 67 | + | <path d="M20 16 L20 48 M20 16 H34 q12 0 12 16 t-12 16 H20" fill="none" stroke="#e9eef3" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/> | |
| 68 | + | <!-- Speed bolt accent --> | |
| 69 | + | <path d="M36 22 L32 34 h6 l-8 16 4-12 h-6 z" fill="#ff9800" opacity="0.95"/> | |
| 70 | + | </svg>`; | |
| 71 | + | } | |
| 72 | + | ||
| 35 | 73 | private static getStyles(): string { | |
| 36 | 74 | return ` | |
| 37 | 75 | * { | |
| @@ -1,5 +1,6 @@ | |||||
| 1 | export class HTMLBuilder { | 1 | export class HTMLBuilder { | ||
| 2 | static createBaseHTML(): string { | 2 | static createBaseHTML(): string { | ||
| 3 | + | const faviconData = this.getFaviconDataURL(); | |||
| 3 | return ` | 4 | return ` | ||
| 4 | <!DOCTYPE html> | 5 | <!DOCTYPE html> | ||
| 5 | <html lang="en"> | 6 | <html lang="en"> | ||
| @@ -7,6 +8,7 @@ export class HTMLBuilder { | |||||
| 7 | <meta charset="UTF-8"> | 8 | <meta charset="UTF-8"> | ||
| 8 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 9 | <title>Dumber Browser</title> | 10 | <title>Dumber Browser</title> | ||
| 11 | + | <link rel="icon" type="image/svg+xml" href="${faviconData}"> | |||
| 10 | <style> | 12 | <style> | ||
| 11 | ${this.getStyles()} | 13 | ${this.getStyles()} | ||
| 12 | </style> | 14 | </style> | ||
| @@ -32,6 +34,42 @@ export class HTMLBuilder { | |||||
| 32 | </html>`; | 34 | </html>`; | ||
| 33 | } | 35 | } | ||
| 34 | 36 | ||||
| 37 | + | private static getFaviconDataURL(): string { | |||
| 38 | + | const svg = this.getFaviconSVG(); | |||
| 39 | + | // Minimal encoding for safe inline usage | |||
| 40 | + | const encoded = encodeURIComponent(svg) | |||
| 41 | + | .replace(/'/g, '%27') | |||
| 42 | + | .replace(/\(/g, '%28') | |||
| 43 | + | .replace(/\)/g, '%29'); | |||
| 44 | + | return `data:image/svg+xml;utf8,${encoded}`; | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | private static getFaviconSVG(): string { | |||
| 48 | + | // Stylized D with speed bolt on a subtle dark rounded card | |||
| 49 | + | return `<?xml version="1.0" encoding="UTF-8"?> | |||
| 50 | + | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> | |||
| 51 | + | <defs> | |||
| 52 | + | <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"> | |||
| 53 | + | <stop offset="0%" stop-color="#101214"/> | |||
| 54 | + | <stop offset="100%" stop-color="#1b1e22"/> | |||
| 55 | + | </linearGradient> | |||
| 56 | + | <filter id="inset" x="-20%" y="-20%" width="140%" height="140%"> | |||
| 57 | + | <feOffset dx="0" dy="1"/> | |||
| 58 | + | <feGaussianBlur stdDeviation="0.6" result="blur"/> | |||
| 59 | + | <feComposite in2="SourceAlpha" operator="out"/> | |||
| 60 | + | <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> | |||
| 61 | + | <feBlend in="SourceGraphic" mode="normal"/> | |||
| 62 | + | </filter> | |||
| 63 | + | </defs> | |||
| 64 | + | <rect x="4" y="4" width="56" height="56" rx="14" ry="14" fill="url(#bg)" filter="url(#inset)"/> | |||
| 65 | + | <rect x="4.5" y="4.5" width="55" height="55" rx="13.5" ry="13.5" fill="none" stroke="#252a2f" stroke-width="1"/> | |||
| 66 | + | <!-- D glyph --> | |||
| 67 | + | <path d="M20 16 L20 48 M20 16 H34 q12 0 12 16 t-12 16 H20" fill="none" stroke="#e9eef3" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/> | |||
| 68 | + | <!-- Speed bolt accent --> | |||
| 69 | + | <path d="M36 22 L32 34 h6 l-8 16 4-12 h-6 z" fill="#ff9800" opacity="0.95"/> | |||
| 70 | + | </svg>`; | |||
| 71 | + | } | |||
| 72 | + | ||||
| 35 | private static getStyles(): string { | 73 | private static getStyles(): string { | ||
| 36 | return ` | 74 | return ` | ||
| 37 | * { | 75 | * { | ||