your personal website on atproto - mirror
blento.app
1import type { CardDefinition } from '../types';
2import type GithubContributionsGraph from './GithubContributionsGraph.svelte';
3import GitHubProfileCard from './GitHubProfileCard.svelte';
4import type { GitHubContributionsData } from './types';
5
6export type GithubProfileLoadedData = Record<string, GitHubContributionsData | undefined>;
7
8export const GithubProfileCardDefitition = {
9 type: 'githubProfile',
10 contentComponent: GitHubProfileCard,
11
12 loadData: async (items) => {
13 const githubData: Record<string, GithubContributionsGraph> = {};
14 for (const item of items) {
15 try {
16 const response = await fetch(
17 `https://blento.app/api/github?user=${encodeURIComponent(item.cardData.user)}`
18 );
19 if (response.ok) {
20 githubData[item.cardData.user] = await response.json();
21 }
22 } catch (error) {
23 console.error('Failed to fetch GitHub contributions:', error);
24 }
25 }
26 return githubData;
27 },
28 onUrlHandler: (url, item) => {
29 const username = getGitHubUsername(url);
30
31 console.log(username);
32 if (!username) return;
33
34 item.cardData.href = url;
35 item.cardData.user = username;
36
37 item.w = 6;
38 item.mobileW = 8;
39 item.h = 3;
40 item.mobileH = 6;
41 return item;
42 },
43 urlHandlerPriority: 5,
44 minH: 2,
45 minW: 2,
46
47 canChange: (item) => Boolean(getGitHubUsername(item.cardData.href)),
48 change: (item) => {
49 item.cardData.user = getGitHubUsername(item.cardData.href);
50
51 return item;
52 },
53 name: 'Github Profile'
54} as CardDefinition & { type: 'githubProfile' };
55
56function getGitHubUsername(url: string | undefined): string | undefined {
57 if (!url) return;
58
59 try {
60 const parsed = new URL(url);
61
62 // Must be github.com (optionally with www.)
63 if (!/^(www\.)?github\.com$/.test(parsed.hostname)) {
64 return undefined;
65 }
66
67 // Remove empty segments
68 const segments = parsed.pathname.split('/').filter(Boolean);
69
70 // Profile URLs have exactly one path segment: /username
71 if (segments.length !== 1) {
72 return undefined;
73 }
74
75 const username = segments[0];
76
77 // GitHub username rules (simplified but accurate)
78 // - Alphanumeric or hyphens
79 // - Cannot start or end with a hyphen
80 // - Max length 39
81 if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/.test(username)) {
82 return undefined;
83 }
84
85 return username;
86 } catch {
87 // Invalid URL
88 return undefined;
89 }
90}