Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f299da2
fix(ci-analytics): parallelize API requests with Promise.all to preve…
pisum-sativum Jul 21, 2026
5a9ecf1
feat: Automatically sort SOCIALS list alphabetically
pisum-sativum Jul 22, 2026
e6f1ded
Merge remote-tracking branch 'origin/main' into fix-socials-sort
pisum-sativum Jul 22, 2026
6978cd3
fix: resolve TS2322 type error in SOCIALS sort by casting array as So…
pisum-sativum Jul 22, 2026
dff1d44
fix: resolve GraphQL request deduplication and sanitize PAT errors (#…
pisum-sativum Jul 22, 2026
4d4e10f
fix: resolve garbage characters extraction in resume parser
pisum-sativum Jul 22, 2026
aaa38b7
fix(resume-parser): propagate buffer.toString errors instead of swall…
pisum-sativum Jul 22, 2026
9dace07
chore: revert unrelated changes from PR
pisum-sativum Jul 23, 2026
8172a7f
fix(BurnoutRiskTable): add missing Image import to fix TS2322 and pre…
pisum-sativum Jul 23, 2026
126c4da
Merge remote-tracking branch 'origin/main' into fix-resume-parser-gar…
pisum-sativum Jul 23, 2026
32f1c03
style: fix prettier formatting in BurnoutRiskTable, InteractiveViewer…
pisum-sativum Jul 23, 2026
6b41c1d
style: fix UTF-8 encoding and prettier formatting; restore unrelated …
pisum-sativum Jul 23, 2026
5e844ac
style: restore InteractiveViewer and achievements to origin/main exac…
pisum-sativum Jul 23, 2026
2e2730a
style: fix prettier formatting for remaining files
pisum-sativum Jul 23, 2026
aff9f34
revert: revert all unrelated file changes so PR only targets lib/resu…
pisum-sativum Jul 23, 2026
e81ebaa
Fix typescript error in BurnoutRiskTable and format code
pisum-sativum Jul 24, 2026
58897b7
Fix EditorPanel tests by mocking useGitHubUserExists to prevent netwo…
pisum-sativum Jul 24, 2026
006bffb
chore: resolve formatting and build errors by reverting unrelated files
pisum-sativum Jul 25, 2026
51cb9b9
merge: sync with origin/main to pull latest formatting and build fixes
pisum-sativum Jul 25, 2026
d89756a
style: format BurnoutRiskTable with prettier after merging origin/main
pisum-sativum Jul 25, 2026
f858896
fix(resume-parser): resolve unused variable warning in catch block
pisum-sativum Jul 25, 2026
de2df9a
style: revert InteractiveViewer, BurnoutRiskTable, and achievements t…
pisum-sativum Jul 27, 2026
357f081
style: format all files with prettier to fix CI checks
pisum-sativum Jul 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 42 additions & 37 deletions components/InteractiveViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,23 @@ interface ParallaxParticle {
* Deterministic math prevents random values from causing SSR/CSR mismatches. */
function buildParticles(): ParallaxParticle[] {
const colors = ['#10b981', '#8b5cf6', '#06b6d4', '#3b82f6', '#f59e0b'];
return Array.from({ length: PARALLAX_PARTICLE_COUNT }, (_, i): ParallaxParticle => ({
id: i,
// Spread particles across the container using prime-number strides
x: (i * 17 + 11) % 100,
y: (i * 23 + 7) % 100,
size: 4 + (i % 5) * 2, // range: 4–12 px
// Keep opacity low so particles never obscure the badge
opacity: 0.05 + (i % 4) * 0.025, // range: 0.05–0.125
// Vary depth so each "layer" of particles shifts by a different amount,
// creating the illusion of 3-D depth. depth 0.1 = farthest; 0.7 = nearest.
depth: 0.1 + (i % 6) * 0.1, // range: 0.1–0.6
color: colors[i % colors.length],
isCircle: i % 4 === 0,
}));
return Array.from(
{ length: PARALLAX_PARTICLE_COUNT },
(_, i): ParallaxParticle => ({
id: i,
// Spread particles across the container using prime-number strides
x: (i * 17 + 11) % 100,
y: (i * 23 + 7) % 100,
size: 4 + (i % 5) * 2, // range: 4–12 px
// Keep opacity low so particles never obscure the badge
opacity: 0.05 + (i % 4) * 0.025, // range: 0.05–0.125
// Vary depth so each "layer" of particles shifts by a different amount,
// creating the illusion of 3-D depth. depth 0.1 = farthest; 0.7 = nearest.
depth: 0.1 + (i % 6) * 0.1, // range: 0.1–0.6
color: colors[i % colors.length],
isCircle: i % 4 === 0,
})
);
}

// How many pixels a depth-1.0 particle shifts when the cursor is at the
Expand Down Expand Up @@ -399,29 +402,31 @@ export default function InteractiveViewer({
Each particle shifts by (parallaxX * depth, parallaxY * depth) px relative
to its base position, so "closer" particles (higher depth) shift more —
creating the impression of a multi-layered isometric space. */}
{particles.map((particle): ReactElement => (
<div
key={particle.id}
style={{
position: 'absolute',
left: `${particle.x}%`,
top: `${particle.y}%`,
width: particle.size,
height: particle.size,
backgroundColor: particle.color,
borderRadius: particle.isCircle ? '50%' : '2px',
boxShadow: `0 0 ${particle.size * 2}px ${particle.color}55`,
opacity: isHovering ? particle.opacity * 1.8 : particle.opacity,
// Particles shift in the SAME direction as the cursor offset to create
// a realistic parallax: near objects (depth ~0.6) move more than far ones.
transform: `translate(${parallaxX * particle.depth}px, ${parallaxY * particle.depth}px)`,
// Smooth lerp toward the new position; opacity fades independently
transition: `transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.5s ease`,
pointerEvents: 'none',
willChange: 'transform',
}}
/>
))}
{particles.map(
(particle): ReactElement => (
<div
key={particle.id}
style={{
position: 'absolute',
left: `${particle.x}%`,
top: `${particle.y}%`,
width: particle.size,
height: particle.size,
backgroundColor: particle.color,
borderRadius: particle.isCircle ? '50%' : '2px',
boxShadow: `0 0 ${particle.size * 2}px ${particle.color}55`,
opacity: isHovering ? particle.opacity * 1.8 : particle.opacity,
// Particles shift in the SAME direction as the cursor offset to create
// a realistic parallax: near objects (depth ~0.6) move more than far ones.
transform: `translate(${parallaxX * particle.depth}px, ${parallaxY * particle.depth}px)`,
// Smooth lerp toward the new position; opacity fades independently
transition: `transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.5s ease`,
pointerEvents: 'none',
willChange: 'transform',
}}
/>
)
)}
</div>

{/* ── Card content ──────────────────────────────────────────────────────
Expand Down
7 changes: 6 additions & 1 deletion components/burnout/BurnoutRiskTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ interface BurnoutRiskTableProps {
}

type SortColumn =
'username' | 'commitShare' | 'highIntensityWeeks' | 'restWeeks' | 'burnoutScore' | 'totalCommits';
| 'username'
| 'commitShare'
| 'highIntensityWeeks'
| 'restWeeks'
| 'burnoutScore'
| 'totalCommits';
type SortDirection = 'asc' | 'desc';

// Custom Pure SVG Sparkline for visual performance
Expand Down
39 changes: 26 additions & 13 deletions lib/resume-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,45 +136,58 @@ async function extractTextFromBuffer(buffer: Buffer, mimeType: string): Promise<
let rawText = '';

if (mimeType === 'application/pdf') {
try {
if (buffer.toString('utf-8', 0, 4) === '%PDF') {
const header = buffer.toString('utf-8', 0, 4);
if (header === '%PDF') {
try {
const { PDFParse } = await import('pdf-parse');

const parser = new PDFParse({ data: buffer });
const result = await parser.getText();
await parser.destroy();

rawText = result.text;
} else {
rawText = buffer.toString('utf-8');
} catch (error) {
console.warn('Failed to parse PDF using pdf-parse:', error);
rawText = '';
}
} catch (error) {
console.warn('Failed to parse PDF using pdf-parse, falling back to UTF-8 decoding:', error);
} else {
rawText = buffer.toString('utf-8');
}
} else if (
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
) {
try {
if (buffer.toString('utf-8', 0, 2) === 'PK') {
const header = buffer.toString('utf-8', 0, 2);
if (header === 'PK') {
try {
const mammothModule = await import('mammoth');
const mammothParser = ((mammothModule as unknown as { default?: unknown }).default ||
mammothModule) as typeof mammothModule;
const result = await mammothParser.extractRawText({ buffer });
rawText = result.value;
} else {
rawText = buffer.toString('utf-8');
} catch (error) {
console.warn('Failed to parse DOCX using mammoth:', error);
rawText = '';
}
} catch (error) {
console.warn('Failed to parse DOCX using mammoth, falling back to UTF-8 decoding:', error);
} else {
rawText = buffer.toString('utf-8');
}
} else {
rawText = buffer.toString('utf-8');
}

try {
if (rawText.includes('Ã')) {
const fixedText = Buffer.from(rawText, 'latin1').toString('utf-8');
if (!fixedText.includes('\uFFFD')) {
rawText = fixedText;
}
}
} catch (_e) {
// Ignore encoding fix errors
}

const printable = rawText
.replace(/[^\x20-\x7E\n\r]/g, ' ')
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFFFD]/g, '')
.replace(/[ \t]+/g, ' ')
.replace(/\r/g, '')
.trim();
Expand Down
6 changes: 5 additions & 1 deletion types/achievements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ export type AchievementTier = 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamo
export type AchievementRarity = 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary' | 'mythic';

export type AchievementCategory =
'contribution' | 'pull-request' | 'repository' | 'collaboration' | 'technology';
| 'contribution'
| 'pull-request'
| 'repository'
| 'collaboration'
| 'technology';

export interface AchievementLevelDef {
tier: AchievementTier;
Expand Down
Loading