Repository navigation
Expand file tree
/
Copy pathclasses.js
More file actions
216 lines (199 loc) · 12.5 KB
/
Copy pathclasses.js
File metadata and controls
216 lines (199 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/**
* The class system: a developer's primary language is their class, their second
* language their subclass, and their level promotes the title through five
* tiers. Data mirrors GitLevel.md / classes.md.
*
* symbol — key into src/classIcons.js (the crest glyph)
* color — the class's signature accent (used to tint the crest)
* flourish — the elite decoration added to the crest at Tier 3+
* ("wings" | "banner" | "laurel"), per classes.md's global rules
* tiers — five titles by rarity band (see tierForLevel):
* [Common, Rare, Epic, Legendary, Mythic]
*
* Keys are GitHub's exact language names. Anything unmapped falls back to a
* generic path so every developer still gets a class.
*/
export const CLASS_PATHS = {
Python: { symbol: "eye", color: "#4b8bbe", flourish: "wings", tiers: ["Adept", "Oracle", "Seer", "Archoracle", "Godseer"] },
TypeScript: { symbol: "scales", color: "#3178c6", flourish: "wings", tiers: ["Scribe", "Arbiter", "Justicar", "High Arbiter", "Lawgiver"] },
JavaScript: { symbol: "dagger", color: "#f1e05a", flourish: "wings", tiers: ["Wanderer", "Maverick", "Outrider", "Legend", "Mythmaker"] },
Rust: { symbol: "shield", color: "#dea584", flourish: "wings", tiers: ["Watchman", "Sentinel", "Guardian", "Eternal Guardian", "Undying"] },
Go: { symbol: "compass", color: "#00add8", flourish: "banner", tiers: ["Explorer", "Pathfinder", "Trailblazer", "Wayfinder", "Worldwalker"] },
Java: { symbol: "throne", color: "#e76f00", flourish: "banner", tiers: ["Steward", "Chancellor", "Magistrate", "Grand Chancellor", "Sovereign"] },
"C++": { symbol: "greatsword", color: "#f34b7d", flourish: "banner", tiers: ["Soldier", "Warlord", "Conqueror", "Overlord", "Warbringer"] },
"C#": { symbol: "hammer", color: "#a371f7", flourish: "wings", tiers: ["Enchanter", "Spellsmith", "Spellmaster", "Archsmith", "Runelord"] },
Ruby: { symbol: "lyre", color: "#cc342d", flourish: "laurel", tiers: ["Performer", "Virtuoso", "Maestro", "Grand Maestro", "Luminary"] },
PHP: { symbol: "gear", color: "#8a91d0", flourish: "banner", tiers: ["Tinkerer", "Artificer", "Inventor", "Master Artificer", "Demiurge"] },
Kotlin: { symbol: "lotus", color: "#b57bff", flourish: "laurel", tiers: ["Disciple", "Ascendant", "Exemplar", "Paragon", "Ascended"] },
Swift: { symbol: "rapier", color: "#f05138", flourish: "laurel", tiers: ["Fencer", "Duelist", "Champion", "Grand Duelist", "Blademaster"] },
// Systems, hardware, and long-tail languages — bespoke ladders so these devs
// get a real class instead of the generic fallback. (Crest glyphs live in
// classIcons.js; rich portraits are a follow-up — these render the line glyph.)
C: { symbol: "chip", color: "#659ad2", flourish: "banner", tiers: ["Operator", "Machinist", "Systemwright", "Kernel Lord", "Machine God"] },
Zig: { symbol: "bolt", color: "#ec915c", flourish: "wings", tiers: ["Kindler", "Voltmage", "Tempest", "Stormlord", "Thunderking"] },
Lua: { symbol: "moon", color: "#6b7bd6", flourish: "laurel", tiers: ["Moonling", "Lunar Adept", "Moon Sage", "Selenarch", "Moonlord"] },
Verilog: { symbol: "circuit", color: "#b2b7f8", flourish: "banner", tiers: ["Drafter", "Circuitwright", "Logic Architect", "Chip Lord", "Silicon Sovereign"] },
VHDL: { symbol: "circuit", color: "#adb2cb", flourish: "banner", tiers: ["Drafter", "Circuitwright", "Logic Architect", "Chip Lord", "Silicon Sovereign"] },
Elixir: { symbol: "flask", color: "#a06fb5", flourish: "laurel", tiers: ["Brewer", "Alchemist", "Potion Sage", "Grand Alchemist", "Philosopher"] },
Haskell: { symbol: "lambda", color: "#8f7fd8", flourish: "wings", tiers: ["Scholar", "Lambda Adept", "Monadic Sage", "Category Archon", "The Pure"] },
Shell: { symbol: "prompt", color: "#89e051", flourish: "banner", tiers: ["Scripter", "Shellbinder", "Daemoncaller", "Terminal Lord", "Root Sovereign"] },
Dart: { symbol: "target", color: "#4bc0b8", flourish: "laurel", tiers: ["Thrower", "Marksman", "Sharpshooter", "Deadeye", "Truesight"] },
Scala: { symbol: "peak", color: "#dc3d54", flourish: "banner", tiers: ["Climber", "Ridgewright", "Summit Sage", "Peak Lord", "Skybreaker"] },
R: { symbol: "chart", color: "#2f9ff0", flourish: "wings", tiers: ["Analyst", "Statmage", "Data Augur", "Grand Statistician", "Numbermancer"] },
SQL: { symbol: "database", color: "#e0a13b", flourish: "banner", tiers: ["Clerk", "Archivist", "Query Weaver", "Grand Archivist", "Data Warden"] },
// The elder and the uncommon — the machine's own tongue, the sciences, and the
// mainframes still quietly running the world. Same treatment as everything
// above (bespoke ladder + a full portrait emblem), because a dev whose primary
// language is COBOL or Ada has earned a class, not the generic rune.
Assembly: { symbol: "anvil", color: "#c2703a", flourish: "banner", tiers: ["Bitsmith", "Ironhand", "Forgewright", "Anvil Lord", "Worldforger"] },
Julia: { symbol: "orbit", color: "#9a5ce0", flourish: "wings", tiers: ["Stargazer", "Starcaller", "Astral Sage", "Celestial Magus", "Cosmarch"] },
Ada: { symbol: "loom", color: "#35d17f", flourish: "laurel", tiers: ["Loomhand", "Patternwright", "Threadbinder", "Grand Weaver", "Fatespinner"] },
Fortran: { symbol: "obelisk", color: "#5f4bd8", flourish: "laurel", tiers: ["Reckoner", "Tabulator", "Elder Numerist", "Ancient Reckoner", "Progenitor"] },
COBOL: { symbol: "tome", color: "#c9a961", flourish: "banner", tiers: ["Bookkeeper", "Auditor", "Vault Keeper", "High Auditor", "Eternal Ledger"] },
Perl: { symbol: "quill", color: "#2aa9c9", flourish: "wings", tiers: ["Sigilist", "Glyphbinder", "Scriptmage", "Arch-Sorcerer", "Wordbender"] },
Erlang: { symbol: "nodes", color: "#c246a4", flourish: "laurel", tiers: ["Nodekeeper", "Supervisor", "Crashbinder", "Lord of Nines", "The Unbroken"] },
};
/**
* The filename a class's standalone crest art is published under:
* `public/crests/<crestSlug(language)>.svg`, written by scripts/landing-crests.mjs.
* Lives here, beside the class table, because two independent consumers now
* depend on the two agreeing — the generator that writes those files and the
* API payload that points the character sheet at them. A drifting copy of this
* would surface as a silently broken image, not an error.
*/
export const crestSlug = (language) =>
language.toLowerCase().replace(/\+/g, "p").replace(/#/g, "sharp").replace(/[^a-z0-9]+/g, "");
/**
* Creator's edition — the developers who built GitLevel get a unique class that
* overrides their language entirely: a bespoke gold sigil (see `creatorSigil` in
* portraits.js, keyed by `language: "Creator"`), a custom title, and full Mythic
* regalia (tier 4 → frame, crown, rune ring, 5 stars) regardless of real level.
* Match is case-insensitive on GitHub login. Add logins here.
*/
export const CREATOR_LOGINS = new Set(["gavinnntann"]);
export function creatorClassFor(login) {
if (!login || !CREATOR_LOGINS.has(login.toLowerCase())) return null;
return {
language: "Creator", // keys the bespoke portrait
title: "Creator",
name: "Creator of GitLevel", // the custom class line
symbol: "rune", // fallback glyph (a portrait exists, so unused)
color: "#ffd873", // signature gold — tints crest + class name
flourish: null, // the sigil carries its own wings; don't double up
tier: 4, // always Mythic (the pinnacle)
creator: true,
};
}
/** Any language without a dedicated path still gets a class. */
export const FALLBACK_PATH = {
symbol: "rune", color: "#8b949e", flourish: "laurel",
tiers: ["Novice", "Adept", "Expert", "Master", "Grandmaster"],
};
/**
* Rarity by tier — the prestige frame. Index = tier (0..4). See tierForLevel
* for the level bands. Mythic (top band) is reserved for the elite few.
*
* MapleStory-style palette: Legendary is green so it doesn't collide with
* `UNIQUE_RARITY`'s gold below, which sits *outside* this ladder entirely.
*/
export const RARITIES = [
{ name: "Common", color: "#9aa4af" },
{ name: "Rare", color: "#58a6ff" },
{ name: "Epic", color: "#a371f7" },
{ name: "Legendary", color: "#3fb950" },
{ name: "Mythic", color: "#ff5edb" },
];
export function rarityForTier(tier) {
return RARITIES[tier] ?? RARITIES[0];
}
/**
* The level band each tier covers (index = tier). Mirrors tierForLevel below —
* that function is still the authority; this is the same split expressed as
* ranges so a level can be located *within* its band. Mythic is open-ended.
*/
export const TIER_BANDS = [[1, 5], [6, 14], [15, 28], [29, 54], [55, Infinity]];
export const DIVISIONS = ["I", "II", "III"];
/**
* Sub-rank within a tier — I → II → III, ascending, so III is the top of the
* band and the ladder reads as forward progress.
*
* Mythic is deliberately out of human reach, which quietly makes Legendary the
* real summit — and Legendary spans Lv 29–54, more XP than every tier beneath
* it combined. Without divisions that entire range renders identically: the
* players most likely to embed a card are exactly the ones whose card would
* never visibly change again. Divisions give those 26 levels somewhere to go
* without inflating the tier ladder or touching the level curve.
*
* Returns null where a division would be meaningless — Mythic (unbounded), and
* any tier reached via a Fame floor rather than the curve, since then the level
* sits outside the band and its position within it says nothing.
*/
export function divisionForLevel(level, tier) {
const band = TIER_BANDS[tier];
if (!band || !Number.isFinite(band[1])) return null;
const [lo, hi] = band;
if (level < lo) return null;
const span = (hi - lo + 1) / DIVISIONS.length;
return DIVISIONS[Math.min(DIVISIONS.length - 1, Math.floor((Math.min(level, hi) - lo) / span))];
}
/**
* Unique — a bespoke rarity outside the community's five-tier ladder,
* reserved for `creatorClassFor` logins. Golden hue (border/glow/gem), a
* single faceted diamond instead of a star count (see solidDiamond in
* classIcons.js). Not reachable via level/tier — engine.js assigns it
* directly when `primaryClass.creator` is set.
*/
export const UNIQUE_RARITY = { name: "Unique", color: "#ffd873" };
/**
* Level → 0-based tier index. Front-loaded so the early tiers come quickly and
* strong contributors reach Legendary, while Mythic stays a rare summit:
* Common 1–5 · Rare 6–14 · Epic 15–28 · Legendary 29–54 · Mythic 55+
*/
export function tierForLevel(level) {
if (level <= 5) return 0;
if (level <= 14) return 1;
if (level <= 28) return 2;
if (level <= 54) return 3;
return 4;
}
/**
* Reach confers prestige the level curve never will: a figure like Linus
* Torvalds (Linux *and* Git) reads as Mythic even though GitHub-measured craft
* alone — the kernel isn't developed here — would never reach LV55. Fame sets a
* *floor* on the rarity tier (frame, stars, title, regalia) without touching the
* level number, gated at reach a single viral repo can't fake. Fame is
* followers + stars; thresholds are deliberately elite so Mythic stays rare.
*/
export const FAME_TIER_FLOORS = [
{ fame: 400000, tier: 4 }, // Mythic — the platform's legends (Linus-tier reach)
{ fame: 120000, tier: 3 }, // Legendary — a famous, widely-followed dev
];
/** The minimum rarity tier a profile's Fame earns it (0 if below all thresholds). */
export function fameTierFloor(fame) {
for (const { fame: threshold, tier } of FAME_TIER_FLOORS) {
if ((fame ?? 0) >= threshold) return tier;
}
return 0;
}
/**
* Resolve a class for a language at a level.
* `language` may be null (e.g. no repos / no second language).
* `minTier` lets Fame raise the rarity floor (see fameTierFloor); the effective
* tier is the higher of the level's tier and this floor.
* Returns null when there is no language to resolve.
*/
export function resolveClass(language, level, minTier = 0) {
if (!language) return null;
const path = CLASS_PATHS[language] ?? FALLBACK_PATH;
const tier = Math.max(tierForLevel(level), minTier);
const title = path.tiers[tier];
return {
language,
title, // "Oracle"
name: `${language} ${title}`, // "Python Oracle"
symbol: path.symbol,
color: path.color,
flourish: path.flourish,
tier, // 0..4
};
}