-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfavicon.js
More file actions
151 lines (129 loc) · 4.45 KB
/
favicon.js
File metadata and controls
151 lines (129 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Author: Cascade
Editor: Claude
* Date: 2025-09-27
* PURPOSE: Generate a dynamic favicon with a 2x2 grid of random colors in a random shape (circle, hexagon, triangle)
* This script creates a new favicon.ico file with random colors in a random shape mask each time it runs
* SRP and DRY check: Pass - This script has a single responsibility of generating a favicon
*/
const fs = require('fs');
const path = require('path');
// Try to use canvas for generating favicon
let useCanvas = true;
let createCanvas;
try {
({ createCanvas } = require('canvas'));
console.log('Using canvas for favicon generation');
} catch (error) {
console.log('Canvas not available, using SVG fallback');
useCanvas = false;
}
/**
* Generate a random hex color
* @returns A random hex color string
*/
function getRandomColor() {
// Generate a random number between 0 and 16777215 (0xFFFFFF)
// Convert to hexadecimal and pad with zeros to ensure 6 digits
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
}
/**
* Generate a favicon using canvas
* @returns A buffer containing the favicon PNG data
*/
function generateFaviconWithCanvas() {
// Create a canvas
const canvas = createCanvas(32, 32);
const ctx = canvas.getContext('2d');
// Configuration constants
const GRID_SIZE = 3;
const squareSize = Math.floor(canvas.width / GRID_SIZE);
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(canvas.width, canvas.height) / 2 - 2; // Leave 2px margin
// Draw white background
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw 3x3 grid of colored squares
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
// Generate random color for each square
const color = getRandomColor();
ctx.fillStyle = color;
// Draw square at calculated position
ctx.fillRect(col * squareSize, row * squareSize, squareSize, squareSize);
}
}
// Create circular mask by using composite operation
ctx.globalCompositeOperation = 'destination-in';
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.fill();
// Convert to PNG buffer
return canvas.toBuffer('image/png');
}
/**
* Generate a favicon as an SVG string (fallback)
* @returns An SVG string representing the favicon
*/
function generateFaviconSVG() {
// Configuration constants
const GRID_SIZE = 3;
const squareSize = 32 / GRID_SIZE;
const centerX = 16;
const centerY = 16;
const radius = 14; // Leave 2px margin
// Start SVG with clipping path definition
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<defs>
<clipPath id="circleClip">
<circle cx="${centerX}" cy="${centerY}" r="${radius}"/>
</clipPath>
</defs>
<rect width="32" height="32" fill="#FFFFFF"/>
<g clip-path="url(#circleClip)">`;
// Draw 3x3 grid of colored squares
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
// Generate random color for each square
const color = getRandomColor();
// Draw square at calculated position
svg += `<rect x="${col * squareSize}" y="${row * squareSize}" width="${squareSize}" height="${squareSize}" fill="${color}" />`;
}
}
// End clipped group and SVG
svg += `</g></svg>`;
return svg;
}
// Generate favicon
let faviconData;
let isSVG = false;
if (useCanvas) {
try {
faviconData = generateFaviconWithCanvas();
console.log('Favicon generated using canvas');
} catch (error) {
console.log('Canvas failed, falling back to SVG');
faviconData = generateFaviconSVG();
isSVG = true;
}
} else {
faviconData = generateFaviconSVG();
isSVG = true;
}
// Save to public directory
const outputPath = path.join(__dirname, '..', 'public', isSVG ? 'favicon.svg' : 'favicon.ico');
fs.writeFileSync(outputPath, faviconData);
// Also create the other format for compatibility
if (isSVG) {
// Create ICO as well for broader compatibility
const icoPath = path.join(__dirname, '..', 'public', 'favicon.ico');
fs.writeFileSync(icoPath, faviconData);
} else {
// Create SVG as well for browsers that prefer it
const svgPath = path.join(__dirname, '..', 'public', 'favicon.svg');
const svgData = generateFaviconSVG();
fs.writeFileSync(svgPath, svgData);
}
console.log('Dynamic favicon generated successfully!');
console.log(`Saved to: ${outputPath}`);