Skip to content

Commit 622cf2a

Browse files
committed
feat(apps): add abstract and matrix themes to github thumbnail generator
1 parent 79cc4f1 commit 622cf2a

File tree

4 files changed

+204
-1
lines changed

4 files changed

+204
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const THEME_OPTIONS = [
3939
{ value: 'crtAmber', label: 'RETRO_AMBER_CRT' },
4040
{ value: 'gta', label: 'GRAND_THEFT_AUTO' },
4141
{ value: 'rich', label: 'LUXURY_GOLD' },
42+
{ value: 'abstract', label: 'ABSTRACT_SHAPES' },
43+
{ value: 'matrix', label: 'THE_MATRIX' },
4244
];
4345

4446
const GithubThumbnailGeneratorPage = () => {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { cod } from './themes/callofduty';
1919
import { crtAmber } from './themes/crtAmber';
2020
import { gta } from './themes/gta';
2121
import { rich } from './themes/rich';
22+
import { abstract } from './themes/abstract';
23+
import { matrix } from './themes/matrix';
2224

2325
export const themeRenderers = {
2426
modern,
@@ -42,4 +44,6 @@ export const themeRenderers = {
4244
crtAmber,
4345
gta,
4446
rich,
45-
};
47+
abstract,
48+
matrix,
49+
};
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { wrapText } from '../utils';
2+
3+
export const abstract = (ctx, width, height, scale, data) => {
4+
const { repoOwner, repoName, description, language, stars, forks } = data;
5+
// ABSTRACT SHAPES (Memphis-ish)
6+
7+
// Background: Soft Off-White
8+
ctx.fillStyle = '#fdfbf7';
9+
ctx.fillRect(0, 0, width, height);
10+
11+
// Random Shapes Background
12+
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f7d794', '#1a535c'];
13+
14+
// Helper to draw random shapes
15+
for(let i=0; i<15; i++) {
16+
ctx.fillStyle = colors[Math.floor(Math.random() * colors.length)];
17+
ctx.save();
18+
ctx.globalAlpha = 0.2;
19+
const type = Math.random();
20+
const x = Math.random() * width;
21+
const y = Math.random() * height;
22+
const size = (50 + Math.random() * 150) * scale;
23+
24+
ctx.translate(x, y);
25+
ctx.rotate(Math.random() * Math.PI * 2);
26+
27+
ctx.beginPath();
28+
if (type < 0.33) {
29+
// Circle
30+
ctx.arc(0, 0, size/2, 0, Math.PI*2);
31+
} else if (type < 0.66) {
32+
// Rect
33+
ctx.rect(-size/2, -size/2, size, size);
34+
} else {
35+
// Triangle
36+
ctx.moveTo(0, -size/2);
37+
ctx.lineTo(size/2, size/2);
38+
ctx.lineTo(-size/2, size/2);
39+
}
40+
ctx.fill();
41+
ctx.restore();
42+
}
43+
44+
// Main Card (Floating)
45+
const cardW = width * 0.7;
46+
const cardH = height * 0.6;
47+
const cardX = (width - cardW) / 2;
48+
const cardY = (height - cardH) / 2;
49+
50+
// Shadow
51+
ctx.fillStyle = '#000'; // Hard shadow
52+
ctx.fillRect(cardX + 20*scale, cardY + 20*scale, cardW, cardH);
53+
54+
// Card Body
55+
ctx.fillStyle = '#fff';
56+
ctx.strokeStyle = '#000';
57+
ctx.lineWidth = 4 * scale;
58+
ctx.fillRect(cardX, cardY, cardW, cardH);
59+
ctx.strokeRect(cardX, cardY, cardW, cardH);
60+
61+
// Header Decoration inside card
62+
ctx.fillStyle = '#ff6b6b';
63+
ctx.fillRect(cardX, cardY, cardW, 20 * scale);
64+
ctx.strokeRect(cardX, cardY, cardW, 20 * scale);
65+
66+
// Content
67+
const padding = 60 * scale;
68+
const contentY = cardY + padding + 20*scale;
69+
70+
ctx.fillStyle = '#000';
71+
ctx.textAlign = 'center';
72+
73+
// Title
74+
ctx.font = `900 ${70 * scale}px "Arial Black", sans-serif`;
75+
ctx.fillText(repoName, width/2, contentY + 40*scale);
76+
77+
// Owner
78+
ctx.font = `bold ${30 * scale}px "Courier New", monospace`;
79+
ctx.fillText(repoOwner.toUpperCase(), width/2, contentY - 20*scale);
80+
81+
// Description
82+
ctx.font = `normal ${28 * scale}px "Arial", sans-serif`;
83+
wrapText(ctx, description, width/2, contentY + 120*scale, cardW - padding*2, 40*scale);
84+
85+
// Stats Bubbles (Bottom of card)
86+
const statY = cardY + cardH - 60 * scale;
87+
88+
const drawStat = (text, x, color) => {
89+
ctx.fillStyle = color;
90+
const w = ctx.measureText(text).width + 40*scale;
91+
ctx.beginPath();
92+
ctx.roundRect(x - w/2, statY - 25*scale, w, 50*scale, 25*scale);
93+
ctx.fill();
94+
ctx.stroke(); // Outline
95+
96+
ctx.fillStyle = '#000';
97+
ctx.font = `bold ${20 * scale}px "Arial", sans-serif`;
98+
ctx.fillText(text, x, statY + 8*scale);
99+
};
100+
101+
let cx = width/2;
102+
// Spread based on content
103+
if (stars && forks) {
104+
drawStat(`★ ${stars}`, cx - 120*scale, '#f7d794');
105+
drawStat(language, cx, '#4ecdc4');
106+
drawStat(`⑂ ${forks}`, cx + 120*scale, '#45b7d1');
107+
} else {
108+
drawStat(language, cx, '#4ecdc4');
109+
}
110+
};
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { wrapText } from '../utils';
2+
3+
export const matrix = (ctx, width, height, scale, data) => {
4+
const { repoOwner, repoName, description, language, stars, forks } = data;
5+
// THE MATRIX / DIGITAL RAIN Style
6+
7+
// Black Background
8+
ctx.fillStyle = '#000000';
9+
ctx.fillRect(0, 0, width, height);
10+
11+
// Digital Rain Effect (Simulated)
12+
ctx.font = `bold ${20 * scale}px "Courier New", monospace`;
13+
const cols = Math.floor(width / (20 * scale));
14+
15+
// We'll use a deterministic random for preview stability or just random
16+
for(let i=0; i<cols; i++) {
17+
// Random column length
18+
const len = Math.floor(Math.random() * 20) + 5;
19+
20+
for(let j=0; j<len; j++) { // Fade out towards top
21+
const alpha = 1 - (j / len);
22+
ctx.fillStyle = `rgba(0, 255, 70, ${alpha * 0.5})`; // Matrix Green
23+
24+
// Random katakana or numbers
25+
const char = String.fromCharCode(0x30A0 + Math.random() * 96);
26+
ctx.fillText(char, i * 20 * scale, (j * 20 * scale + (Math.random()*height)) % height);
27+
}
28+
}
29+
30+
// Overlay Box
31+
const boxW = width * 0.8;
32+
const boxH = height * 0.6;
33+
const boxX = (width - boxW) / 2;
34+
const boxY = (height - boxH) / 2;
35+
36+
ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';
37+
ctx.strokeStyle = '#00ff41'; // Matrix Green
38+
ctx.lineWidth = 2 * scale;
39+
40+
ctx.fillRect(boxX, boxY, boxW, boxH);
41+
ctx.strokeRect(boxX, boxY, boxW, boxH);
42+
43+
// Terminal Content
44+
const padding = 60 * scale;
45+
let textY = boxY + padding;
46+
47+
ctx.fillStyle = '#00ff41';
48+
ctx.textAlign = 'left';
49+
50+
// Header
51+
ctx.font = `bold ${24 * scale}px "Courier New", monospace`;
52+
ctx.fillText(`root@matrix:~# analyze --target "${repoOwner}/${repoName}"`, boxX + padding, textY);
53+
textY += 50 * scale;
54+
55+
ctx.fillStyle = '#ffffff';
56+
ctx.fillText(`> TARGET IDENTIFIED:`, boxX + padding, textY);
57+
textY += 40 * scale;
58+
59+
// Title
60+
ctx.font = `900 ${80 * scale}px "Courier New", monospace`;
61+
ctx.fillStyle = '#00ff41';
62+
ctx.shadowColor = '#00ff41';
63+
ctx.shadowBlur = 10 * scale;
64+
ctx.fillText(repoName, boxX + padding, textY + 50*scale);
65+
ctx.shadowBlur = 0;
66+
textY += 120 * scale;
67+
68+
// Description
69+
ctx.fillStyle = '#cccccc';
70+
ctx.font = `normal ${28 * scale}px "Courier New", monospace`;
71+
wrapText(ctx, description, boxX + padding, textY, boxW - padding*2, 35 * scale);
72+
73+
// Bottom Stats
74+
const bottomY = boxY + boxH - padding;
75+
ctx.fillStyle = '#00ff41';
76+
ctx.font = `bold ${24 * scale}px "Courier New", monospace`;
77+
78+
let stats = `[ LANG: ${language} ]`;
79+
if (stars) stats += ` [ STARS: ${stars} ]`;
80+
if (forks) stats += ` [ FORKS: ${forks} ]`;
81+
82+
ctx.fillText(stats, boxX + padding, bottomY);
83+
84+
// Blinking Cursor
85+
const textWidth = ctx.measureText(stats).width;
86+
ctx.fillRect(boxX + padding + textWidth + 10*scale, bottomY - 20*scale, 15*scale, 25*scale);
87+
};

0 commit comments

Comments
 (0)