Skip to content

Commit b345773

Browse files
committed
Enhance Philosophy section: generate contextual body from GitHub data and update title to 'Core principles'
1 parent 51f9586 commit b345773

File tree

3 files changed

+95
-51
lines changed

3 files changed

+95
-51
lines changed

scripts/generate-github-data.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,70 @@ async function fetchGraphQL(query, variables, token) {
122122
return json.data
123123
}
124124

125+
/**
126+
* Generate a contextual philosophy body paragraph from the user's actual GitHub data.
127+
* Picks the most relevant angle based on their top topics, language, and activity.
128+
*/
129+
function generatePhilosophyBody(profile, topLanguage, allTopics, totalStars) {
130+
const name = profile.name || profile.login
131+
const topics = allTopics.map((t) => t.toLowerCase())
132+
133+
const has = (...keywords) => keywords.some((kw) => topics.includes(kw))
134+
135+
const hasAutomation = has('automation', 'ci-cd', 'devops', 'infrastructure', 'github-action', 'github-actions', 'pipeline')
136+
const hasAI = has('ai', 'machine-learning', 'ml', 'deep-learning', 'nlp', 'llm', 'openai')
137+
const hasOpenSource = has('open-source', 'oss', 'hacktoberfest')
138+
const hasWeb = has('web', 'frontend', 'react', 'nextjs', 'vue', 'svelte', 'tailwind')
139+
const hasCLI = has('cli', 'terminal', 'command-line', 'shell', 'bash')
140+
const hasAPI = has('api', 'rest', 'graphql', 'backend', 'nodejs', 'fastapi')
141+
const hasMobile = has('android', 'ios', 'flutter', 'react-native', 'mobile')
142+
const hasData = has('data', 'analytics', 'pandas', 'jupyter', 'visualization', 'notebook')
143+
144+
const langNote = topLanguage
145+
? ` Working primarily in ${topLanguage}, the focus stays on code that is easy to read, easy to run, and hard to break silently.`
146+
: ''
147+
148+
if (hasAI && hasAutomation) {
149+
return `${name} combines AI and automation to build workflows that stay predictable — the goal is explainability over magic, with every step traceable and inspectable.${langNote}`
150+
}
151+
152+
if (hasAI) {
153+
return `${name} approaches AI as infrastructure: models are most useful when they're composable, observable, and easy to swap out — intelligence should enhance workflow, not obscure it.${langNote}`
154+
}
155+
156+
if (hasAutomation) {
157+
return `${name} believes great developer tooling should make the right path the easy path — automated, reproducible, and opinionated enough that teams spend time building instead of configuring.${langNote}`
158+
}
159+
160+
if (hasOpenSource && totalStars > 20) {
161+
return `${name} builds in the open because the best tools are shaped by the people who use them — every star and issue is signal worth acting on.${langNote}`
162+
}
163+
164+
if (hasCLI || hasAPI) {
165+
return `${name} gravitates toward tools that compose well: small, focused interfaces that do one thing reliably and can be wired together without surprises.${langNote}`
166+
}
167+
168+
if (hasData) {
169+
return `${name} treats data as a first-class citizen — clean inputs, reproducible pipelines, and visualisations that make the answer obvious rather than requiring interpretation.${langNote}`
170+
}
171+
172+
if (hasWeb) {
173+
return `${name} believes the best interfaces get out of the way — fast to load, easy to navigate, and built so the next developer can understand the code without a tour.${langNote}`
174+
}
175+
176+
if (hasMobile) {
177+
return `${name} builds mobile experiences with a platform-first mindset — respecting OS conventions, optimising for battery and connectivity, and shipping updates that feel native.${langNote}`
178+
}
179+
180+
// Generic fallback derived from the profile data
181+
const repoNote =
182+
profile.public_repos > 20
183+
? `Across ${profile.public_repos} public repositories, the pattern is consistent`
184+
: `Every project starts from the same principle`
185+
186+
return `${repoNote}: code should be clear enough to hand off, stable enough to deploy with confidence, and scoped tightly enough to stay maintainable over time.${langNote}`
187+
}
188+
125189
async function main() {
126190
// Load .env file if it exists
127191
const envPath = path.join(rootDir, '.env')
@@ -820,8 +884,7 @@ async function main() {
820884
},
821885
philosophy: {
822886
title: 'Philosophy',
823-
body:
824-
'Guardrails over guesswork. The goal is to keep infrastructure and developer tooling deterministic, explainable, and easy to inspect — even when AI is part of the workflow.',
887+
body: generatePhilosophyBody(profile, topLanguage, allTopics, totalStars),
825888
cards: philosophyCards,
826889
},
827890
projects: {

src/templates/threejs/PhilosophyScene.tsx

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -91,61 +91,42 @@ function buildFaceTexture(card: PhilosophyCardData): THREE.CanvasTexture {
9191
const ctx = canvas.getContext('2d')
9292
if (!ctx) return new THREE.CanvasTexture(canvas)
9393

94+
// Background
9495
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
9596
gradient.addColorStop(0, '#07142d')
96-
gradient.addColorStop(1, '#081021')
97+
gradient.addColorStop(1, '#060e1e')
9798
ctx.fillStyle = gradient
9899
ctx.fillRect(0, 0, canvas.width, canvas.height)
99100

100-
ctx.strokeStyle = 'rgba(56, 189, 248, 0.5)'
101-
ctx.lineWidth = 6
102-
ctx.strokeRect(24, 24, canvas.width - 48, canvas.height - 48)
101+
// Border
102+
ctx.strokeStyle = 'rgba(56, 189, 248, 0.45)'
103+
ctx.lineWidth = 5
104+
ctx.strokeRect(28, 28, canvas.width - 56, canvas.height - 56)
103105

104-
ctx.strokeStyle = 'rgba(56, 189, 248, 0.28)'
106+
const PAD = 80
107+
108+
// Label
109+
ctx.fillStyle = 'rgba(125, 211, 252, 0.8)'
110+
ctx.font = '600 52px Inter, ui-sans-serif, system-ui'
111+
ctx.fillText('PRINCIPLE', PAD, 120)
112+
113+
// Accent line under label
114+
ctx.strokeStyle = 'rgba(56, 189, 248, 0.35)'
105115
ctx.lineWidth = 3
106116
ctx.beginPath()
107-
ctx.moveTo(74, 148)
108-
ctx.lineTo(300, 148)
117+
ctx.moveTo(PAD, 148)
118+
ctx.lineTo(PAD + 180, 148)
109119
ctx.stroke()
110120

111-
ctx.fillStyle = 'rgba(125, 211, 252, 0.9)'
112-
ctx.font = '600 36px Inter, ui-sans-serif, system-ui'
113-
ctx.fillText('PHILOSOPHY', 74, 110)
114-
115-
ctx.fillStyle = '#e2e8f0'
116-
ctx.font = '700 62px Inter, ui-sans-serif, system-ui'
117-
wrapText(ctx, card.title, 74, 215, canvas.width - 148, 76, 3)
121+
// Title — large, prominent
122+
ctx.fillStyle = '#f1f5f9'
123+
ctx.font = '700 98px Inter, ui-sans-serif, system-ui'
124+
wrapText(ctx, card.title, PAD, 280, canvas.width - PAD * 2, 114, 2)
118125

119-
ctx.fillStyle = 'rgba(203, 213, 225, 0.95)'
120-
ctx.font = '500 40px Inter, ui-sans-serif, system-ui'
121-
wrapText(ctx, card.body, 74, 420, canvas.width - 148, 56, 8)
122-
123-
const iconX = 824
124-
const iconY = 188
125-
const iconSize = 98
126-
const half = iconSize / 2
127-
ctx.strokeStyle = 'rgba(56, 189, 248, 0.78)'
128-
ctx.lineWidth = 4
129-
ctx.beginPath()
130-
ctx.moveTo(iconX, iconY - half)
131-
ctx.lineTo(iconX + half, iconY - half / 2)
132-
ctx.lineTo(iconX + half, iconY + half / 2)
133-
ctx.lineTo(iconX, iconY + half)
134-
ctx.lineTo(iconX - half, iconY + half / 2)
135-
ctx.lineTo(iconX - half, iconY - half / 2)
136-
ctx.closePath()
137-
ctx.stroke()
138-
ctx.beginPath()
139-
ctx.moveTo(iconX, iconY - half)
140-
ctx.lineTo(iconX, iconY)
141-
ctx.lineTo(iconX + half, iconY + half / 2)
142-
ctx.moveTo(iconX, iconY)
143-
ctx.lineTo(iconX - half, iconY + half / 2)
144-
ctx.stroke()
145-
ctx.fillStyle = 'rgba(56, 189, 248, 0.85)'
146-
ctx.beginPath()
147-
ctx.arc(iconX, iconY, 6, 0, Math.PI * 2)
148-
ctx.fill()
126+
// Body — readable weight
127+
ctx.fillStyle = 'rgba(203, 213, 225, 0.88)'
128+
ctx.font = '400 64px Inter, ui-sans-serif, system-ui'
129+
wrapText(ctx, card.body, PAD, 470, canvas.width - PAD * 2, 82, 6)
149130

150131
const texture = new THREE.CanvasTexture(canvas)
151132
texture.colorSpace = THREE.SRGBColorSpace
@@ -216,10 +197,10 @@ export default function PhilosophyScene({ cards, activeIndex, onActiveIndexChang
216197
)
217198

218199
const faceConfigs = [
219-
{ position: new THREE.Vector3(0, 0, size / 2 + 0.02), rotationY: 0 },
200+
{ position: new THREE.Vector3(0, 0, size / 2 + 0.02), rotationY: 0 },
220201
{ position: new THREE.Vector3(0, 0, -size / 2 - 0.02), rotationY: Math.PI },
221-
{ position: new THREE.Vector3(-size / 2 - 0.02, 0, 0), rotationY: Math.PI / 2 },
222-
{ position: new THREE.Vector3(size / 2 + 0.02, 0, 0), rotationY: -Math.PI / 2 },
202+
{ position: new THREE.Vector3(-size / 2 - 0.02, 0, 0), rotationY: -Math.PI / 2 },
203+
{ position: new THREE.Vector3(size / 2 + 0.02, 0, 0), rotationY: Math.PI / 2 },
223204
]
224205

225206
const faceMaterials = cards.map((card) => (

src/templates/threejs/PhilosophySection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ export default function PhilosophySection({ philosophy }: { philosophy: Philosop
3434
<div className="grid gap-10 lg:grid-cols-2 lg:items-center">
3535
<header className="max-w-xl">
3636
<p className="mb-3 text-[11px] font-semibold uppercase tracking-[0.28em] text-blue-400/90">
37-
{philosophy.title}
37+
Core principles
3838
</p>
3939
<h2 id="philosophy-title" className="text-3xl font-semibold leading-tight text-slate-100 sm:text-4xl">
4040
{philosophy.title}
4141
</h2>
4242
<p className="mt-4 text-lg leading-relaxed text-slate-300">{philosophy.body}</p>
4343
</header>
4444

45-
<div className="h-[430px] w-full overflow-hidden rounded-2xl border border-blue-900/35 bg-[#070d1d]/45 backdrop-blur-sm">
45+
<div className="h-[430px] w-full">
4646
<Suspense fallback={null}>
4747
<PhilosophyScene cards={cubeCards} activeIndex={activeFace} onActiveIndexChange={setActiveFace} />
4848
</Suspense>

0 commit comments

Comments
 (0)