diff --git a/ogre/wrangler.jsonc b/ogre/wrangler.jsonc --- a/ogre/wrangler.jsonc +++ b/ogre/wrangler.jsonc @@ -6,6 +6,9 @@ "observability": { "enabled": true, }, + "limits": { + "cpu_ms": 300000 + }, "routes": [ { "pattern": "ogre.tangled.network", @@ -18,8 +21,34 @@ "rules": [ { "type": "Data", - "globs": ["**/*.woff"], + "globs": ["**/*.wasm", "**/*.woff"], "fallthrough": true, }, ], + "env": { + "dev": { + "name": "ogre-dev", + "vars": { + "ENVIRONMENT": "development" + }, + "routes": [ + { + "pattern": "ogre-dev.tangled.network", + "custom_domain": true + } + ] + }, + "production": { + "name": "ogre", + "vars": { + "ENVIRONMENT": "production" + }, + "routes": [ + { + "pattern": "ogre.tangled.network", + "custom_domain": true + } + ] + } + } } diff --git a/ogre/src/__tests__/render.test.ts b/ogre/src/__tests__/render.test.ts --- a/ogre/src/__tests__/render.test.ts +++ b/ogre/src/__tests__/render.test.ts @@ -43,8 +43,13 @@ writeFileSync(join(outputDir, filename), buffer); }; +const saveSvg = (filename: string, svg: string) => { + writeFileSync(join(outputDir, filename), svg); +}; + const renderAndSave = async

(component: VNode

, filename: string) => { - const { png } = await renderCard(component as VNode); + const { svg, png } = await renderCard(component as VNode); + saveSvg(filename.replace(".png", ".svg"), svg); savePng(filename, png); }; diff --git a/ogre/src/components/cards/repository.tsx b/ogre/src/components/cards/repository.tsx --- a/ogre/src/components/cards/repository.tsx +++ b/ogre/src/components/cards/repository.tsx @@ -8,10 +8,10 @@ import type { RepositoryCardData } from "../../validation"; function repoNameFontSize(name: string): number { - // Available width ~1000px (1104px card content minus language circles area). + // Available width ~756px // Inter 600 average char width is ~0.58× the font size. const maxSize = TYPOGRAPHY.repoName.fontSize; - const fitted = Math.floor(1000 / (name.length * 0.58)); + const fitted = Math.floor(756 / (name.length * 0.58)); return Math.min(maxSize, Math.max(fitted, 48)); } @@ -19,13 +19,21 @@ const fontSize = repoNameFontSize(data.repoName); return ( - +

+ +
- - + +
{data.repoName} - +
diff --git a/ogre/src/components/shared/language-circles.tsx b/ogre/src/components/shared/language-circles.tsx --- a/ogre/src/components/shared/language-circles.tsx +++ b/ogre/src/components/shared/language-circles.tsx @@ -2,55 +2,45 @@ interface LanguageCirclesProps { languages: Language[]; + width: number; } -const MAX_RADIUS = 380; +export function LanguageCircles({ languages, width }: LanguageCirclesProps) { + const MAX_RADIUS = width || 100; -function percentageToThickness(percentage: number): number { - return (percentage / 100) * MAX_RADIUS; -} - -export function LanguageCircles({ languages }: LanguageCirclesProps) { const sortedLanguages = [...languages] .sort((a, b) => b.percentage - a.percentage) - .slice(0, 5) - .reverse(); + .slice(0, 5); - let cumulativeRadius = 0; + let cumulativePercentage = 0; + const circles: { color: string; radius: number }[] = []; + + for (const lang of sortedLanguages) { + // Radius decreases as we go inward, but ring area is proportional to percentage + // Using sqrt to make area (πr²) proportional to remaining percentage + const radius = Math.max( + 1, + Math.round(MAX_RADIUS * Math.sqrt(1 - cumulativePercentage / 100) * 100) / + 100, + ); + circles.push({ color: lang.color, radius }); + cumulativePercentage += lang.percentage; + } return ( -
- {sortedLanguages.map((lang, i) => { - const thickness = percentageToThickness(lang.percentage); - const contentSize = cumulativeRadius * 2; - - cumulativeRadius += thickness; - - return ( -
- ); - })} -
+ + {circles.map((circle, i) => ( + + ))} + ); }