Skip to content

Commit 934330e

Browse files
committed
feat: new github thumbnails
1 parent aad6750 commit 934330e

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const THEME_OPTIONS = [
5858
{ value: 'modernEdge', label: 'MODERN_EDGE' },
5959
{ value: 'auroraWave', label: 'AURORA_WAVE' },
6060
{ value: 'newspaper', label: 'NEWSPRINT_HERALD' },
61+
{ value: 'postModern', label: 'POST_MODERN_ARTSY' },
6162
];
6263

6364
const GithubThumbnailGeneratorPage = () => {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { tacticalMap } from './themes/tacticalMap';
3838
import { modernEdge } from './themes/modernEdge';
3939
import { auroraWave } from './themes/auroraWave';
4040
import { newspaper } from './themes/newspaper';
41+
import { postModern } from './themes/postModern';
4142

4243
export const themeRenderers = {
4344
modern,
@@ -80,4 +81,5 @@ export const themeRenderers = {
8081
modernEdge,
8182
auroraWave,
8283
newspaper,
84+
postModern,
8385
};
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
import { wrapText } from '../utils';
2+
3+
export const postModern = (ctx, width, height, scale, data) => {
4+
const {
5+
primaryColor,
6+
secondaryColor,
7+
bgColor,
8+
showPattern,
9+
repoOwner,
10+
repoName,
11+
description,
12+
language,
13+
stars,
14+
forks,
15+
supportUrl,
16+
} = data;
17+
18+
// --- Deconstructed color blocks ---
19+
// Large diagonal primary slab
20+
ctx.save();
21+
ctx.globalAlpha = 0.18;
22+
ctx.fillStyle = primaryColor;
23+
ctx.beginPath();
24+
ctx.moveTo(width * 0.55, 0);
25+
ctx.lineTo(width, 0);
26+
ctx.lineTo(width, height * 0.7);
27+
ctx.lineTo(width * 0.35, height * 0.45);
28+
ctx.closePath();
29+
ctx.fill();
30+
ctx.restore();
31+
32+
// Secondary color wedge bottom-left
33+
ctx.save();
34+
ctx.globalAlpha = 0.14;
35+
ctx.fillStyle = secondaryColor;
36+
ctx.beginPath();
37+
ctx.moveTo(0, height * 0.5);
38+
ctx.lineTo(width * 0.4, height);
39+
ctx.lineTo(0, height);
40+
ctx.closePath();
41+
ctx.fill();
42+
ctx.restore();
43+
44+
// Small accent rectangle (floating)
45+
ctx.save();
46+
ctx.fillStyle = primaryColor;
47+
ctx.globalAlpha = 0.7;
48+
ctx.translate(width * 0.78, height * 0.12);
49+
ctx.rotate(-0.12);
50+
ctx.fillRect(0, 0, 90 * scale, 90 * scale);
51+
ctx.restore();
52+
53+
// Secondary circle
54+
ctx.save();
55+
ctx.fillStyle = secondaryColor;
56+
ctx.globalAlpha = 0.25;
57+
ctx.beginPath();
58+
ctx.arc(width * 0.15, height * 0.25, 80 * scale, 0, Math.PI * 2);
59+
ctx.fill();
60+
ctx.restore();
61+
62+
// bgColor block — bottom right corner accent
63+
ctx.save();
64+
ctx.fillStyle = bgColor;
65+
ctx.globalAlpha = 0.5;
66+
ctx.fillRect(width - 180 * scale, height - 120 * scale, 180 * scale, 120 * scale);
67+
ctx.restore();
68+
69+
// --- Deconstructed grid / ruling (showPattern) ---
70+
if (showPattern) {
71+
ctx.save();
72+
ctx.globalAlpha = 0.06;
73+
ctx.strokeStyle = '#ffffff';
74+
ctx.lineWidth = 1 * scale;
75+
76+
// Irregular horizontal rules at random-ish intervals
77+
const rules = [0.18, 0.33, 0.47, 0.62, 0.78, 0.91];
78+
rules.forEach((r) => {
79+
ctx.beginPath();
80+
ctx.moveTo(0, height * r);
81+
ctx.lineTo(width, height * r);
82+
ctx.stroke();
83+
});
84+
85+
// A few vertical cuts
86+
const vcuts = [0.22, 0.52, 0.73];
87+
vcuts.forEach((v) => {
88+
ctx.beginPath();
89+
ctx.moveTo(width * v, 0);
90+
ctx.lineTo(width * v, height);
91+
ctx.stroke();
92+
});
93+
94+
ctx.restore();
95+
96+
// Scattered tiny marks
97+
ctx.save();
98+
ctx.globalAlpha = 0.04;
99+
ctx.fillStyle = '#ffffff';
100+
for (let i = 0; i < 30; i++) {
101+
const mx = ((i * 7919 + 31) % 100) / 100 * width;
102+
const my = ((i * 6271 + 17) % 100) / 100 * height;
103+
ctx.fillRect(mx, my, 3 * scale, 3 * scale);
104+
}
105+
ctx.restore();
106+
}
107+
108+
const padding = 80 * scale;
109+
110+
// --- Oversized rotated repo name (background text) ---
111+
ctx.save();
112+
ctx.globalAlpha = 0.04;
113+
ctx.fillStyle = '#ffffff';
114+
ctx.font = `900 ${220 * scale}px "Inter", sans-serif`;
115+
ctx.translate(width * 0.5, height * 0.55);
116+
ctx.rotate(-0.08);
117+
ctx.textAlign = 'center';
118+
ctx.fillText(repoName.toUpperCase(), 0, 0);
119+
ctx.restore();
120+
121+
// --- Owner — small, rotated sideways on the left edge ---
122+
ctx.save();
123+
ctx.fillStyle = secondaryColor;
124+
ctx.font = `600 ${14 * scale}px "JetBrains Mono", monospace`;
125+
ctx.translate(padding - 40 * scale, height * 0.55);
126+
ctx.rotate(-Math.PI / 2);
127+
ctx.textAlign = 'center';
128+
ctx.fillText(repoOwner.toUpperCase(), 0, 0);
129+
ctx.restore();
130+
131+
// --- Main repo name — deconstructed, split across two lines ---
132+
ctx.textAlign = 'left';
133+
const nameChars = repoName.split('');
134+
const midPoint = Math.ceil(nameChars.length / 2);
135+
const topHalf = repoName.slice(0, midPoint);
136+
const bottomHalf = repoName.slice(midPoint);
137+
138+
// Top half — large bold
139+
ctx.fillStyle = '#ffffff';
140+
ctx.font = `900 ${88 * scale}px "Inter", sans-serif`;
141+
ctx.fillText(topHalf, padding, padding + 80 * scale);
142+
143+
// Bottom half — shifted right, different weight
144+
ctx.fillStyle = primaryColor;
145+
ctx.font = `300 ${88 * scale}px "Inter", sans-serif`;
146+
const topW = ctx.measureText(topHalf).width;
147+
ctx.font = `300 ${88 * scale}px "Inter", sans-serif`;
148+
ctx.fillText(bottomHalf, padding + 60 * scale, padding + 170 * scale);
149+
150+
// Strikethrough line across the name (deconstructed element)
151+
ctx.save();
152+
ctx.strokeStyle = secondaryColor;
153+
ctx.lineWidth = 3 * scale;
154+
ctx.globalAlpha = 0.5;
155+
ctx.beginPath();
156+
ctx.moveTo(padding, padding + 105 * scale);
157+
ctx.lineTo(padding + topW + 40 * scale, padding + 105 * scale);
158+
ctx.stroke();
159+
ctx.restore();
160+
161+
// --- Description — in a semi-transparent box ---
162+
const descBoxX = padding;
163+
const descBoxY = padding + 210 * scale;
164+
const descBoxW = width * 0.48;
165+
166+
ctx.save();
167+
ctx.fillStyle = '#000000';
168+
ctx.globalAlpha = 0.3;
169+
ctx.fillRect(descBoxX, descBoxY, descBoxW, 100 * scale);
170+
ctx.restore();
171+
172+
ctx.fillStyle = 'rgba(255,255,255,0.7)';
173+
ctx.font = `300 ${22 * scale}px "Inter", sans-serif`;
174+
wrapText(ctx, description, descBoxX + 16 * scale, descBoxY + 30 * scale, descBoxW - 32 * scale, 32 * scale);
175+
176+
// --- Language — large, rotated, overlapping ---
177+
ctx.save();
178+
ctx.fillStyle = primaryColor;
179+
ctx.globalAlpha = 0.15;
180+
ctx.font = `900 ${120 * scale}px "Inter", sans-serif`;
181+
ctx.translate(width * 0.72, height * 0.68);
182+
ctx.rotate(0.15);
183+
ctx.textAlign = 'center';
184+
ctx.fillText(language.toUpperCase(), 0, 0);
185+
ctx.restore();
186+
187+
// Language small label
188+
ctx.fillStyle = '#ffffff';
189+
ctx.font = `600 ${16 * scale}px "JetBrains Mono", monospace`;
190+
ctx.textAlign = 'left';
191+
ctx.globalAlpha = 0.6;
192+
ctx.fillText(`[ ${language} ]`, width * 0.62, height - padding - 60 * scale);
193+
ctx.globalAlpha = 1;
194+
195+
// --- Stats — scattered, different sizes ---
196+
const bottomY = height - padding;
197+
198+
if (stars) {
199+
// Stars — big number
200+
ctx.fillStyle = primaryColor;
201+
ctx.font = `900 ${52 * scale}px "Inter", sans-serif`;
202+
ctx.textAlign = 'left';
203+
ctx.fillText(stars, padding, bottomY - 10 * scale);
204+
205+
ctx.fillStyle = 'rgba(255,255,255,0.35)';
206+
ctx.font = `300 ${16 * scale}px "JetBrains Mono", monospace`;
207+
ctx.fillText('stars', padding + ctx.measureText(stars).width + 10 * scale, bottomY - 10 * scale);
208+
209+
// Reset font for stars measurement
210+
ctx.font = `900 ${52 * scale}px "Inter", sans-serif`;
211+
}
212+
213+
if (forks) {
214+
// Forks — smaller, offset
215+
const forksX = padding + 220 * scale;
216+
ctx.fillStyle = secondaryColor;
217+
ctx.font = `900 ${36 * scale}px "Inter", sans-serif`;
218+
ctx.fillText(forks, forksX, bottomY - 16 * scale);
219+
220+
ctx.fillStyle = 'rgba(255,255,255,0.35)';
221+
ctx.font = `300 ${14 * scale}px "JetBrains Mono", monospace`;
222+
ctx.fillText('forks', forksX + ctx.measureText(forks).width + 8 * scale, bottomY - 16 * scale);
223+
224+
ctx.font = `900 ${36 * scale}px "Inter", sans-serif`;
225+
}
226+
227+
// --- Color swatches — stacked vertically on far right ---
228+
const swatchX = width - padding - 20 * scale;
229+
const swatchS = 24 * scale;
230+
[bgColor, primaryColor, secondaryColor].forEach((color, i) => {
231+
ctx.save();
232+
ctx.fillStyle = color;
233+
ctx.translate(swatchX, padding + i * (swatchS + 8 * scale));
234+
ctx.rotate(0.05 * (i - 1));
235+
ctx.fillRect(0, 0, swatchS, swatchS);
236+
ctx.strokeStyle = 'rgba(255,255,255,0.15)';
237+
ctx.lineWidth = 1 * scale;
238+
ctx.strokeRect(0, 0, swatchS, swatchS);
239+
ctx.restore();
240+
});
241+
242+
// --- Pattern status — tiny rotated label ---
243+
ctx.save();
244+
ctx.fillStyle = showPattern ? primaryColor : 'rgba(255,255,255,0.15)';
245+
ctx.font = `600 ${10 * scale}px "JetBrains Mono", monospace`;
246+
ctx.translate(width - padding + 10 * scale, height * 0.45);
247+
ctx.rotate(Math.PI / 2);
248+
ctx.textAlign = 'center';
249+
ctx.fillText(showPattern ? 'TEXTURE:ON' : 'TEXTURE:OFF', 0, 0);
250+
ctx.restore();
251+
252+
// --- Support URL — bottom right, muted ---
253+
if (supportUrl) {
254+
ctx.textAlign = 'right';
255+
ctx.fillStyle = 'rgba(255,255,255,0.2)';
256+
ctx.font = `300 ${15 * scale}px "JetBrains Mono", monospace`;
257+
ctx.fillText(supportUrl, width - padding, bottomY);
258+
}
259+
260+
// --- Decorative slash marks (post-modern accent) ---
261+
ctx.save();
262+
ctx.strokeStyle = secondaryColor;
263+
ctx.lineWidth = 2 * scale;
264+
ctx.globalAlpha = 0.3;
265+
for (let i = 0; i < 4; i++) {
266+
const sx = padding + 10 * scale + i * 14 * scale;
267+
ctx.beginPath();
268+
ctx.moveTo(sx, padding + 190 * scale);
269+
ctx.lineTo(sx + 10 * scale, padding + 205 * scale);
270+
ctx.stroke();
271+
}
272+
ctx.restore();
273+
};

0 commit comments

Comments
 (0)