Skip to content

Commit 7842c69

Browse files
committed
feat: add neo-brutalist, quantum-overlay and terminal-pro themes to github thumbnail generator
1 parent 2e1e2e5 commit 7842c69

File tree

5 files changed

+393
-1
lines changed

5 files changed

+393
-1
lines changed

src/pages/apps/github-thumbnail/GithubThumbnailGeneratorPage.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const THEME_OPTIONS = [
4747
{ value: 'gradient', label: 'GRADIENT_MESH' },
4848
{ value: 'comic', label: 'COMIC_BOOK' },
4949
{ value: 'cybernetic', label: 'CYBERNETIC_HUD' },
50+
{ value: 'neoBrutalist', label: 'NEO_BRUTALIST' },
51+
{ value: 'quantumOverlay', label: 'QUANTUM_OVERLAY' },
52+
{ value: 'terminalPro', label: 'TERMINAL_PRO_CLI' },
5053
];
5154

5255
const GithubThumbnailGeneratorPage = () => {
@@ -87,7 +90,7 @@ const GithubThumbnailGeneratorPage = () => {
8790
ctx.globalAlpha = 0.05;
8891
ctx.fillStyle = '#ffffff';
8992

90-
if (theme === 'brutalist') {
93+
if (['brutalist', 'neoBrutalist', 'terminalPro'].includes(theme)) {
9194
// Grid Pattern
9295
const gridSize = 40 * scale;
9396
for (let x = 0; x < width; x += gridSize) {

src/pages/apps/github-thumbnail/themes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import { minimalDark } from './themes/minimalDark';
2727
import { gradient } from './themes/gradient';
2828
import { comic } from './themes/comic';
2929
import { cybernetic } from './themes/cybernetic';
30+
import { neoBrutalist } from './themes/neoBrutalist';
31+
import { quantumOverlay } from './themes/quantumOverlay';
32+
import { terminalPro } from './themes/terminalPro';
3033

3134
export const themeRenderers = {
3235
modern,
@@ -58,4 +61,7 @@ export const themeRenderers = {
5861
gradient,
5962
comic,
6063
cybernetic,
64+
neoBrutalist,
65+
quantumOverlay,
66+
terminalPro,
6167
};
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { wrapText } from '../utils';
2+
3+
export const neoBrutalist = (ctx, width, height, scale, data) => {
4+
const {
5+
primaryColor,
6+
secondaryColor,
7+
bgColor,
8+
repoOwner,
9+
repoName,
10+
description,
11+
language,
12+
stars,
13+
forks,
14+
} = data;
15+
16+
const padding = 60 * scale;
17+
18+
// 1. Background (using bgColor)
19+
ctx.fillStyle = bgColor;
20+
ctx.fillRect(0, 0, width, height);
21+
22+
// 2. Bold Shapes (Neo-Brutalist elements)
23+
ctx.save();
24+
// Large circle in background
25+
ctx.fillStyle = primaryColor;
26+
ctx.beginPath();
27+
ctx.arc(width * 0.8, height * 0.3, 200 * scale, 0, Math.PI * 2);
28+
ctx.fill();
29+
30+
// Secondary color accent block with shadow
31+
const shadowOffset = 15 * scale;
32+
ctx.fillStyle = '#000000';
33+
ctx.fillRect(padding + shadowOffset, padding + 120 * scale + shadowOffset, 400 * scale, 80 * scale);
34+
ctx.fillStyle = secondaryColor;
35+
ctx.strokeStyle = '#000000';
36+
ctx.lineWidth = 4 * scale;
37+
ctx.fillRect(padding, padding + 120 * scale, 400 * scale, 80 * scale);
38+
ctx.strokeRect(padding, padding + 120 * scale, 400 * scale, 80 * scale);
39+
ctx.restore();
40+
41+
// 3. Main Title (Repo Name) with Heavy Shadow
42+
ctx.save();
43+
ctx.font = `900 ${120 * scale}px "Inter", "Arial Black", sans-serif`;
44+
ctx.textAlign = 'left';
45+
46+
// Text Shadow (Offset)
47+
ctx.fillStyle = '#000000';
48+
ctx.fillText(repoName, padding + 10 * scale, padding + 320 * scale);
49+
50+
// Main Text
51+
ctx.fillStyle = '#ffffff';
52+
ctx.strokeStyle = '#000000';
53+
ctx.lineWidth = 4 * scale;
54+
ctx.fillText(repoName, padding, padding + 310 * scale);
55+
ctx.strokeText(repoName, padding, padding + 310 * scale);
56+
ctx.restore();
57+
58+
// 4. Repo Owner (Pill style)
59+
ctx.font = `bold ${30 * scale}px "JetBrains Mono", monospace`;
60+
const ownerW = ctx.measureText(repoOwner.toUpperCase()).width + 40 * scale;
61+
ctx.fillStyle = '#000000';
62+
ctx.fillRect(padding + 5 * scale, padding + 5 * scale, ownerW, 60 * scale);
63+
ctx.fillStyle = primaryColor;
64+
ctx.fillRect(padding, padding, ownerW, 60 * scale);
65+
ctx.strokeStyle = '#000000';
66+
ctx.lineWidth = 3 * scale;
67+
ctx.strokeRect(padding, padding, ownerW, 60 * scale);
68+
69+
ctx.fillStyle = '#000000';
70+
ctx.textAlign = 'center';
71+
ctx.fillText(repoOwner.toUpperCase(), padding + ownerW / 2, padding + 42 * scale);
72+
73+
// 5. Description
74+
ctx.save();
75+
ctx.textAlign = 'left';
76+
ctx.font = `bold ${32 * scale}px "Inter", sans-serif`;
77+
ctx.fillStyle = '#ffffff';
78+
wrapText(
79+
ctx,
80+
description,
81+
padding,
82+
padding + 400 * scale,
83+
width - padding * 2,
84+
45 * scale
85+
);
86+
ctx.restore();
87+
88+
// 6. Footer Stats (Cards)
89+
const statBoxW = 180 * scale;
90+
const statBoxH = 100 * scale;
91+
const startX = padding;
92+
const startY = height - padding - statBoxH;
93+
94+
const drawNeoStat = (x, y, label, value, color) => {
95+
if (!value) return;
96+
// Shadow
97+
ctx.fillStyle = '#000000';
98+
ctx.fillRect(x + 10 * scale, y + 10 * scale, statBoxW, statBoxH);
99+
// Box
100+
ctx.fillStyle = color;
101+
ctx.fillRect(x, y, statBoxW, statBoxH);
102+
ctx.strokeStyle = '#000000';
103+
ctx.lineWidth = 4 * scale;
104+
ctx.strokeRect(x, y, statBoxW, statBoxH);
105+
106+
// Text
107+
ctx.fillStyle = '#000000';
108+
ctx.font = `bold ${18 * scale}px "JetBrains Mono", monospace`;
109+
ctx.textAlign = 'center';
110+
ctx.fillText(label, x + statBoxW / 2, y + 35 * scale);
111+
ctx.font = `900 ${32 * scale}px "Inter", sans-serif`;
112+
ctx.fillText(value, x + statBoxW / 2, y + 75 * scale);
113+
};
114+
115+
drawNeoStat(startX, startY, 'STARS', stars, primaryColor);
116+
drawNeoStat(startX + statBoxW + 40 * scale, startY, 'FORKS', forks, secondaryColor);
117+
drawNeoStat(startX + (statBoxW + 40 * scale) * 2, startY, 'LANG', language.toUpperCase(), '#ffffff');
118+
};
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { wrapText } from '../utils';
2+
3+
export const quantumOverlay = (ctx, width, height, scale, data) => {
4+
const {
5+
primaryColor,
6+
secondaryColor,
7+
bgColor,
8+
repoOwner,
9+
repoName,
10+
description,
11+
language,
12+
stars,
13+
forks,
14+
} = data;
15+
16+
const padding = 60 * scale;
17+
18+
// 1. Deep Space Background (using bgColor)
19+
ctx.fillStyle = bgColor;
20+
ctx.fillRect(0, 0, width, height);
21+
22+
// 2. Quantum Particle Field (Background)
23+
ctx.save();
24+
for (let i = 0; i < 40; i++) {
25+
const x = Math.random() * width;
26+
const y = Math.random() * height;
27+
const radius = Math.random() * 3 * scale;
28+
29+
ctx.beginPath();
30+
ctx.arc(x, y, radius, 0, Math.PI * 2);
31+
ctx.fillStyle = i % 2 === 0 ? primaryColor : secondaryColor;
32+
ctx.globalAlpha = 0.2;
33+
ctx.fill();
34+
35+
// Random Connection Lines
36+
if (i < 15) {
37+
ctx.beginPath();
38+
ctx.moveTo(x, y);
39+
ctx.lineTo(x + (Math.random() - 0.5) * 200 * scale, y + (Math.random() - 0.5) * 200 * scale);
40+
ctx.strokeStyle = primaryColor;
41+
ctx.globalAlpha = 0.1;
42+
ctx.stroke();
43+
}
44+
}
45+
ctx.restore();
46+
47+
// 3. Central HUD Ring (Gradient Stroke)
48+
ctx.save();
49+
const centerX = width / 2;
50+
const centerY = height / 2;
51+
const radius = 280 * scale;
52+
53+
ctx.beginPath();
54+
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
55+
const hudGradient = ctx.createLinearGradient(centerX - radius, centerY, centerX + radius, centerY);
56+
hudGradient.addColorStop(0, primaryColor);
57+
hudGradient.addColorStop(1, secondaryColor);
58+
ctx.strokeStyle = hudGradient;
59+
ctx.lineWidth = 1 * scale;
60+
ctx.globalAlpha = 0.3;
61+
ctx.stroke();
62+
63+
// HUD Accents (Arcs)
64+
ctx.lineWidth = 4 * scale;
65+
ctx.globalAlpha = 0.8;
66+
ctx.beginPath();
67+
ctx.arc(centerX, centerY, radius, -Math.PI / 4, Math.PI / 4);
68+
ctx.stroke();
69+
ctx.beginPath();
70+
ctx.arc(centerX, centerY, radius, Math.PI * 0.75, Math.PI * 1.25);
71+
ctx.stroke();
72+
ctx.restore();
73+
74+
// 4. Content - Floating in Center
75+
ctx.textAlign = 'center';
76+
ctx.textBaseline = 'middle';
77+
78+
// Repo Owner (Technical Label)
79+
ctx.font = `bold ${24 * scale}px "JetBrains Mono", monospace`;
80+
ctx.fillStyle = secondaryColor;
81+
ctx.fillText(`// NODE: ${repoOwner.toUpperCase()}`, centerX, centerY - 160 * scale);
82+
83+
// Repo Name (Bold, Glowing)
84+
ctx.save();
85+
ctx.font = `900 ${120 * scale}px "Inter", "Arial Black", sans-serif`;
86+
ctx.shadowColor = primaryColor;
87+
ctx.shadowBlur = 30 * scale;
88+
ctx.fillStyle = '#ffffff';
89+
ctx.fillText(repoName, centerX, centerY - 40 * scale);
90+
ctx.restore();
91+
92+
// Description (Wrapped in middle)
93+
ctx.font = `300 ${32 * scale}px "Inter", sans-serif`;
94+
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
95+
wrapText(
96+
ctx,
97+
description,
98+
centerX,
99+
centerY + 60 * scale,
100+
width * 0.6,
101+
45 * scale
102+
);
103+
104+
// 5. Data Points (Floating around)
105+
const drawDataPoint = (x, y, label, value, color) => {
106+
ctx.textAlign = 'center';
107+
ctx.font = `bold ${16 * scale}px "JetBrains Mono", monospace`;
108+
ctx.fillStyle = color;
109+
ctx.fillText(label, x, y - 25 * scale);
110+
111+
ctx.font = `900 ${36 * scale}px "Inter", sans-serif`;
112+
ctx.fillStyle = '#ffffff';
113+
ctx.fillText(value, x, y + 10 * scale);
114+
115+
// Decorative bracket
116+
ctx.strokeStyle = color;
117+
ctx.lineWidth = 2 * scale;
118+
ctx.beginPath();
119+
ctx.moveTo(x - 40 * scale, y - 40 * scale);
120+
ctx.lineTo(x - 50 * scale, y - 40 * scale);
121+
ctx.lineTo(x - 50 * scale, y + 20 * scale);
122+
ctx.lineTo(x - 40 * scale, y + 20 * scale);
123+
ctx.stroke();
124+
};
125+
126+
const dataY = height - padding - 60 * scale;
127+
drawDataPoint(width * 0.25, dataY, 'STARS_QUANTUM', stars, primaryColor);
128+
drawDataPoint(width * 0.75, dataY, 'FORKS_DISTRIB', forks, secondaryColor);
129+
130+
// Language (Bottom Center)
131+
ctx.font = `bold ${20 * scale}px "JetBrains Mono", monospace`;
132+
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
133+
ctx.fillText(`RUNTIME::${language.toUpperCase()}`, centerX, height - padding);
134+
135+
// Scanline/Grid Overlay
136+
ctx.save();
137+
ctx.globalAlpha = 0.03;
138+
ctx.fillStyle = '#ffffff';
139+
for (let i = 0; i < height; i += 4 * scale) {
140+
ctx.fillRect(0, i, width, 1 * scale);
141+
}
142+
ctx.restore();
143+
};

0 commit comments

Comments
 (0)