|
1 | 1 | import { Log } from "$lib/service/LogService"; |
2 | 2 |
|
3 | | -function createFallbackImageImage(title: string, maxWidth: number, maxHeight: number): string { |
4 | | - const canvas = document.createElement('canvas'); |
5 | | - canvas.width = maxWidth; |
6 | | - canvas.height = maxHeight; |
7 | | - const ctx = canvas.getContext('2d')!; |
8 | | - |
9 | | - // White background |
10 | | - ctx.fillStyle = '#FFFFFF'; |
11 | | - ctx.fillRect(0, 0, canvas.width, canvas.height); |
12 | | - |
13 | | - // Grey text |
14 | | - const fontSize = 36; |
15 | | - ctx.fillStyle = '#666666'; |
16 | | - ctx.font = `bold ${fontSize}px system-ui`; |
17 | | - ctx.textAlign = 'center'; |
18 | | - ctx.textBaseline = 'middle'; |
19 | | - |
20 | | - // Word wrap |
21 | | - const words = title.split(' '); |
22 | | - const lines: string[] = []; |
23 | | - let currentLine = words[0]; |
24 | | - |
25 | | - for (let i = 1; i < words.length; i++) { |
26 | | - const testLine = currentLine + ' ' + words[i]; |
27 | | - const { width } = ctx.measureText(testLine); |
28 | | - if (width < maxWidth - 40) { // 20px padding each side |
29 | | - currentLine = testLine; |
30 | | - } else { |
31 | | - lines.push(currentLine); |
32 | | - currentLine = words[i]; |
33 | | - } |
| 3 | +export async function cacheBase64Image(base64Image: string, cacheKey: string): Promise<void> { |
| 4 | + // Strip the data URL prefix if present |
| 5 | + const cleaned = base64Image.replace(/^data:image\/png;base64,/, ''); |
| 6 | + |
| 7 | + // Decode base64 into binary |
| 8 | + const binary = atob(cleaned); |
| 9 | + const byteArray = new Uint8Array(binary.length); |
| 10 | + for (let i = 0; i < binary.length; i++) { |
| 11 | + byteArray[i] = binary.charCodeAt(i); |
34 | 12 | } |
35 | | - lines.push(currentLine); |
36 | 13 |
|
37 | | - // Vertically center lines |
38 | | - const lineHeight = fontSize * 1.3; |
39 | | - const totalHeight = lines.length * lineHeight; |
40 | | - const startY = (canvas.height - totalHeight) / 2 + lineHeight / 2; |
| 14 | + const blob = new Blob([byteArray], { type: 'image/png' }); |
41 | 15 |
|
42 | | - lines.forEach((line, i) => { |
43 | | - ctx.fillText(line, canvas.width / 2, startY + i * lineHeight); |
| 16 | + const cacheResponse = new Response(blob, { |
| 17 | + headers: { |
| 18 | + 'Content-Type': 'image/png', |
| 19 | + 'Content-Length': blob.size.toString() |
| 20 | + } |
44 | 21 | }); |
45 | 22 |
|
46 | | - return canvas.toDataURL('image/png'); |
| 23 | + const cache = await caches.open('icon-cache'); |
| 24 | + const absoluteUrl = new URL(`/icon/${cacheKey}.png`, location.origin).toString(); |
| 25 | + await cache.put(absoluteUrl, cacheResponse); |
47 | 26 | } |
48 | 27 |
|
49 | 28 | export async function resizeBase64Image( |
@@ -103,5 +82,52 @@ export async function resizeBase64Image( |
103 | 82 | const backupResult = await loadImage(backupUrl); |
104 | 83 | if (backupResult) return backupResult; |
105 | 84 | } |
106 | | - return createFallbackImageImage(title, maxWidth, maxHeight); |
| 85 | + return createFallbackImage(title, maxWidth, maxHeight); |
107 | 86 | } |
| 87 | + |
| 88 | +function createFallbackImage(title: string, maxWidth: number, maxHeight: number): string { |
| 89 | + const canvas = document.createElement('canvas'); |
| 90 | + canvas.width = maxWidth; |
| 91 | + canvas.height = maxHeight; |
| 92 | + const ctx = canvas.getContext('2d')!; |
| 93 | + |
| 94 | + // White background |
| 95 | + ctx.fillStyle = '#FFFFFF'; |
| 96 | + ctx.fillRect(0, 0, canvas.width, canvas.height); |
| 97 | + |
| 98 | + // Grey text |
| 99 | + const fontSize = 36; |
| 100 | + ctx.fillStyle = '#666666'; |
| 101 | + ctx.font = `bold ${fontSize}px system-ui`; |
| 102 | + ctx.textAlign = 'center'; |
| 103 | + ctx.textBaseline = 'middle'; |
| 104 | + |
| 105 | + // Word wrap |
| 106 | + const words = title.split(' '); |
| 107 | + const lines: string[] = []; |
| 108 | + let currentLine = words[0]; |
| 109 | + |
| 110 | + for (let i = 1; i < words.length; i++) { |
| 111 | + const testLine = currentLine + ' ' + words[i]; |
| 112 | + const { width } = ctx.measureText(testLine); |
| 113 | + if (width < maxWidth - 40) { // 20px padding each side |
| 114 | + currentLine = testLine; |
| 115 | + } else { |
| 116 | + lines.push(currentLine); |
| 117 | + currentLine = words[i]; |
| 118 | + } |
| 119 | + } |
| 120 | + lines.push(currentLine); |
| 121 | + |
| 122 | + // Vertically center lines |
| 123 | + const lineHeight = fontSize * 1.3; |
| 124 | + const totalHeight = lines.length * lineHeight; |
| 125 | + const startY = (canvas.height - totalHeight) / 2 + lineHeight / 2; |
| 126 | + |
| 127 | + lines.forEach((line, i) => { |
| 128 | + ctx.fillText(line, canvas.width / 2, startY + i * lineHeight); |
| 129 | + }); |
| 130 | + |
| 131 | + return canvas.toDataURL('image/png'); |
| 132 | +} |
| 133 | + |
0 commit comments