chore: update the project

This commit is contained in:
eshanized
2025-01-09 18:15:12 +05:30
commit d9e441e04e
19 changed files with 5726 additions and 0 deletions

623
src/App.tsx Normal file
View File

@@ -0,0 +1,623 @@
import React, { useState } from 'react';
import { Terminal, Github, Copy, Check, RefreshCw, Code2, AlertCircle, Search, Briefcase, GraduationCap, Heart, Settings, Layout, ExternalLink, Twitter, Linkedin, Mail, Globe, MapPin, Building2, BookOpen, Coffee, Sparkles, Palette, Trophy, Activity, Clock, Calendar, Users, GitFork, Star, User } from 'lucide-react';
import { themes } from './themes';
import { generateReadme } from './utils/generateReadme';
import { Octokit } from 'octokit';
const octokit = new Octokit();
const tabs = [
{ id: 'basic', label: 'Basic Info', icon: Layout },
{ id: 'education', label: 'Education', icon: GraduationCap },
{ id: 'professional', label: 'Professional', icon: Briefcase },
{ id: 'interests', label: 'Interests', icon: Heart },
{ id: 'appearance', label: 'Appearance', icon: Settings },
];
function App() {
const [copied, setCopied] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [searchResults, setSearchResults] = useState<any[]>([]);
const [searching, setSearching] = useState(false);
const [activeTab, setActiveTab] = useState('basic');
const [formData, setFormData] = useState({
username: '',
fullName: '',
bio: '',
location: '',
company: '',
website: '',
email: '',
twitter: '',
linkedin: '',
skills: '',
education: '',
interests: '',
theme: 'github-dark',
showStats: true,
showLanguages: true,
showStreak: false,
showActivityGraph: false,
showWakaTime: false,
showTrophies: false,
showContributionCalendar: false,
showSocial: true,
showSkills: true,
showEducation: true,
languages: [] as string[],
githubJoinDate: '',
totalContributions: '',
totalRepositories: '',
followers: '',
following: ''
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value, type } = e.target;
if (type === 'checkbox') {
const checkbox = e.target as HTMLInputElement;
setFormData(prev => ({ ...prev, [name]: checkbox.checked }));
} else {
setFormData(prev => ({ ...prev, [name]: value }));
}
};
const handleSearchChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const query = e.target.value;
setSearchQuery(query);
if (query.length < 3) {
setSearchResults([]);
return;
}
setSearching(true);
try {
const response = await octokit.rest.search.users({
q: query,
per_page: 5
});
setSearchResults(response.data.items);
} catch (err) {
setError('Failed to search users');
} finally {
setSearching(false);
}
};
const fetchGitHubInfo = async (username: string) => {
setLoading(true);
setError(null);
try {
const [userResponse, reposResponse] = await Promise.all([
octokit.rest.users.getByUsername({ username }),
octokit.rest.repos.listForUser({ username, per_page: 100 })
]);
const user = userResponse.data;
setFormData(prev => ({
...prev,
username: user.login,
fullName: user.name || '',
bio: user.bio || '',
location: user.location || '',
company: user.company || '',
website: user.blog || '',
email: user.email || '',
twitter: user.twitter_username || '',
githubJoinDate: new Date(user.created_at).toLocaleDateString(),
followers: user.followers.toString(),
following: user.following.toString(),
totalRepositories: user.public_repos.toString()
}));
setSearchResults([]);
setSearchQuery('');
} catch (err) {
setError('Failed to fetch user information');
} finally {
setLoading(false);
}
};
const handleCopy = () => {
navigator.clipboard.writeText(generateReadme(formData));
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 text-gray-100">
<nav className="border-b border-gray-800/50 backdrop-blur-sm bg-gray-900/50 sticky top-0 z-50 px-6 py-4">
<div className="container mx-auto flex justify-between items-center">
<div className="flex items-center space-x-3">
<Terminal className="w-6 h-6 text-green-400" />
<h1 className="text-xl font-bold bg-gradient-to-r from-green-400 to-blue-500 text-transparent bg-clip-text">GitHub README Generator</h1>
</div>
<div className="flex items-center space-x-6">
<a href="https://github.com/eshanized?tab=repositories" target="_blank" rel="noopener noreferrer" className="flex items-center space-x-2 text-gray-300 hover:text-green-400 transition-colors">
<Github className="w-4 h-4" />
<span>View Projects</span>
</a>
</div>
</div>
</nav>
<main className="container mx-auto px-4 py-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="space-y-6">
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 shadow-xl border border-gray-700/50">
<h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
<Search className="w-5 h-5 text-green-400" />
Find GitHub User
</h2>
<div className="space-y-4">
<div className="relative">
<input
type="text"
value={searchQuery}
onChange={handleSearchChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 pl-10 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="Search GitHub users..."
/>
<Search className="absolute left-3 top-2.5 w-5 h-5 text-gray-400" />
{searching && (
<RefreshCw className="absolute right-3 top-2.5 w-5 h-5 animate-spin text-gray-400" />
)}
</div>
<div className="flex gap-2">
<input
type="text"
value={formData.username}
onChange={(e) => setFormData(prev => ({ ...prev, username: e.target.value }))}
className="flex-1 bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="Enter GitHub username directly..."
/>
<button
onClick={() => formData.username && fetchGitHubInfo(formData.username)}
disabled={loading || !formData.username}
className="px-4 py-2 bg-gradient-to-r from-green-500 to-blue-500 hover:from-green-600 hover:to-blue-600 disabled:from-gray-600 disabled:to-gray-600 rounded-lg flex items-center gap-2 transition-all shadow-lg"
>
{loading ? <RefreshCw className="w-4 h-4 animate-spin" /> : <Github className="w-4 h-4" />}
Fetch
</button>
</div>
{error && (
<div className="flex items-center gap-2 text-red-400 text-sm bg-red-400/10 p-3 rounded-lg">
<AlertCircle className="w-4 h-4" />
{error}
</div>
)}
{searchResults.length > 0 && (
<div className="absolute z-10 w-full mt-1 bg-gray-700/90 backdrop-blur-sm rounded-lg shadow-xl border border-gray-600/50 max-h-60 overflow-auto">
{searchResults.map((user) => (
<button
key={user.id}
onClick={() => fetchGitHubInfo(user.login)}
className="w-full text-left px-4 py-3 hover:bg-gray-600/50 flex items-center space-x-3 transition-colors"
>
<img src={user.avatar_url} alt={user.login} className="w-8 h-8 rounded-full ring-2 ring-gray-700" />
<span className="font-medium">{user.login}</span>
</button>
))}
</div>
)}
</div>
</div>
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl overflow-hidden shadow-xl border border-gray-700/50">
<div className="flex border-b border-gray-700/50">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex-1 px-4 py-3 flex items-center justify-center gap-2 text-sm font-medium transition-all ${
activeTab === tab.id
? 'bg-gradient-to-r from-green-500/10 to-blue-500/10 text-green-400'
: 'hover:bg-gray-700/30'
}`}
>
<tab.icon className="w-4 h-4" />
{tab.label}
</button>
))}
</div>
<div className="p-6 space-y-6">
{activeTab === 'basic' && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<User className="w-4 h-4 text-green-400" />
Full Name
</label>
<input
type="text"
name="fullName"
value={formData.fullName}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="John Doe"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<BookOpen className="w-4 h-4 text-green-400" />
Bio
</label>
<textarea
name="bio"
value={formData.bio}
onChange={handleChange}
rows={3}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="A passionate developer..."
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<MapPin className="w-4 h-4 text-green-400" />
Location
</label>
<input
type="text"
name="location"
value={formData.location}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="San Francisco, CA"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Globe className="w-4 h-4 text-green-400" />
Website
</label>
<input
type="url"
name="website"
value={formData.website}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="https://example.com"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Mail className="w-4 h-4 text-green-400" />
Email
</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="john@example.com"
/>
</div>
</div>
)}
{activeTab === 'education' && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<GraduationCap className="w-4 h-4 text-green-400" />
Education History
</label>
<textarea
name="education"
value={formData.education}
onChange={handleChange}
rows={4}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="🎓 BSc in Computer Science - University Name (2018-2022)&#10;📚 Relevant Coursework: Data Structures, Algorithms, Web Development"
/>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showEducation"
checked={formData.showEducation}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm">Show education section in README</label>
</div>
</div>
)}
{activeTab === 'professional' && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Building2 className="w-4 h-4 text-green-400" />
Company
</label>
<input
type="text"
name="company"
value={formData.company}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="Company Name"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Code2 className="w-4 h-4 text-green-400" />
Skills
</label>
<textarea
name="skills"
value={formData.skills}
onChange={handleChange}
rows={3}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="React, Node.js, TypeScript, Python..."
/>
<p className="text-sm text-gray-400 mt-1">Separate skills with commas</p>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Linkedin className="w-4 h-4 text-green-400" />
LinkedIn
</label>
<input
type="url"
name="linkedin"
value={formData.linkedin}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="https://linkedin.com/in/username"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Twitter className="w-4 h-4 text-green-400" />
Twitter
</label>
<input
type="text"
name="twitter"
value={formData.twitter}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="username (without @)"
/>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showSkills"
checked={formData.showSkills}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm">Show skills section in README</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showSocial"
checked={formData.showSocial}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm">Show social links in README</label>
</div>
</div>
)}
{activeTab === 'interests' && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Sparkles className="w-4 h-4 text-green-400" />
Interests & Hobbies
</label>
<textarea
name="interests"
value={formData.interests}
onChange={handleChange}
rows={3}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
placeholder="Open Source, Machine Learning, Game Development..."
/>
<p className="text-sm text-gray-400 mt-1">Add your interests and hobbies, one per line</p>
</div>
</div>
)}
{activeTab === 'appearance' && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 flex items-center gap-2">
<Palette className="w-4 h-4 text-green-400" />
Theme
</label>
<select
name="theme"
value={formData.theme}
onChange={handleChange}
className="w-full bg-gray-700/50 rounded-lg px-3 py-2 focus:ring-2 focus:ring-green-400 focus:outline-none border border-gray-600/50"
>
{themes.map(theme => (
<option key={theme.id} value={theme.id}>
{theme.name}
</option>
))}
</select>
</div>
<div className="space-y-2">
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showStats"
checked={formData.showStats}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Activity className="w-4 h-4" />
Show GitHub stats
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showLanguages"
checked={formData.showLanguages}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Code2 className="w-4 h-4" />
Show most used languages
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showStreak"
checked={formData.showStreak}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<GitFork className="w-4 h-4" />
Show GitHub streak stats
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showActivityGraph"
checked={formData.showActivityGraph}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Activity className="w-4 h-4" />
Show activity graph
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showWakaTime"
checked={formData.showWakaTime}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Clock className="w-4 h-4" />
Show WakaTime stats
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showTrophies"
checked={formData.showTrophies}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Trophy className="w-4 h-4" />
Show GitHub trophies
</label>
</div>
<div className="flex items-center space-x-2">
<input
type="checkbox"
name="showContributionCalendar"
checked={formData.showContributionCalendar}
onChange={handleChange}
className="rounded bg-gray-700 border-gray-600 text-green-400 focus:ring-green-400"
/>
<label className="text-sm flex items-center gap-2">
<Calendar className="w-4 h-4" />
Show contribution calendar
</label>
</div>
</div>
</div>
)}
</div>
</div>
</div>
<div className="space-y-6">
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 shadow-xl border border-gray-700/50">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Code2 className="w-5 h-5 text-green-400" />
Preview
</h2>
<button
onClick={handleCopy}
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-blue-500 hover:from-green-600 hover:to-blue-600 transition-all shadow-lg"
>
{copied ? (
<>
<Check className="w-4 h-4" />
<span>Copied!</span>
</>
) : (
<>
<Copy className="w-4 h-4" />
<span>Copy</span>
</>
)}
</button>
</div>
<div className="bg-gray-900/50 rounded-lg p-4 font-mono text-sm overflow-auto max-h-[600px] border border-gray-700/50">
<pre className="whitespace-pre-wrap">{generateReadme(formData)}</pre>
</div>
</div>
</div>
</div>
</main>
<footer className="border-t border-gray-800/50 backdrop-blur-sm bg-gray-900/50 py-8">
<div className="container mx-auto px-6">
<div className="flex flex-col md:flex-row justify-between items-center gap-4">
<div className="flex items-center gap-2">
<Terminal className="w-5 h-5 text-green-400" />
<span className="text-gray-400">GitHub README Generator</span>
<span className="text-gray-500">by</span>
<a href="https://github.com/eshanized" target="_blank" rel="noopener noreferrer" className="text-green-400 hover:text-green-300 transition-colors">eshanized</a>
</div>
<div className="flex items-center gap-6">
<a href="https://github.com/eshanized" target="_blank" rel="noopener noreferrer" className="text-gray-400 hover:text-green-400 transition-colors flex items-center gap-2">
<Github className="w-4 h-4" />
<span>GitHub</span>
</a>
<a href="https://twitter.com/eshanized" target="_blank" rel="noopener noreferrer" className="text-gray-400 hover:text-green-400 transition-colors flex items-center gap-2">
<Twitter className="w-4 h-4" />
<span>Twitter</span>
</a>
<a href="https://linkedin.com/in/eshanized" target="_blank" rel="noopener noreferrer" className="text-gray-400 hover:text-green-400 transition-colors flex items-center gap-2">
<Linkedin className="w-4 h-4" />
<span>LinkedIn</span>
</a>
<a href="https://eshanized.com" target="_blank" rel="noopener noreferrer" className="text-gray-400 hover:text-green-400 transition-colors flex items-center gap-2">
<Globe className="w-4 h-4" />
<span>Website</span>
</a>
</div>
</div>
</div>
</footer>
</div>
);
}
export default App;

9
src/index.css Normal file
View File

@@ -0,0 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
font-family: 'Fira Code', monospace;
}
}

10
src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
);

12
src/themes.ts Normal file
View File

@@ -0,0 +1,12 @@
export const themes = [
{ id: 'github-dark', name: 'GitHub Dark', color: '#0d1117' },
{ id: 'dracula', name: 'Dracula', color: '#282a36' },
{ id: 'nord', name: 'Nord', color: '#2e3440' },
{ id: 'monokai', name: 'Monokai', color: '#272822' },
{ id: 'solarized-dark', name: 'Solarized Dark', color: '#002b36' },
{ id: 'material', name: 'Material', color: '#263238' },
{ id: 'one-dark', name: 'One Dark', color: '#282c34' },
{ id: 'synthwave', name: 'Synthwave', color: '#2b213a' },
{ id: 'cobalt', name: 'Cobalt', color: '#193549' },
{ id: 'tokyo-night', name: 'Tokyo Night', color: '#1a1b26' }
];

199
src/utils/generateReadme.ts Normal file
View File

@@ -0,0 +1,199 @@
import { type } from "os";
interface FormData {
username: string;
fullName: string;
bio: string;
location: string;
company: string;
website: string;
email: string;
twitter: string;
linkedin: string;
skills: string;
education: string;
interests: string;
theme: string;
showStats: boolean;
showLanguages: boolean;
showStreak: boolean;
showActivityGraph: boolean;
showWakaTime: boolean;
showTrophies: boolean;
showContributionCalendar: boolean;
showSocial: boolean;
showSkills: boolean;
showEducation: boolean;
languages: string[];
githubJoinDate: string;
totalContributions: string;
totalRepositories: string;
followers: string;
following: string;
}
function generateNeofetch(data: FormData): string {
const username = data.username || 'username';
const fullName = data.fullName || 'Full Name';
const location = data.location || 'Location';
const company = data.company || 'Company';
const pythonArt = `
.?77777777777777$.
777..777777777777$+
.77 7777777777$$$
.777 .7777777777$$$$
.7777777777777$$$$$$
..........:77$$$$$$$
.77777777777777777$$$$$$$$$.=======.
777777777777777777$$$$$$$$$$.========
7777777777777777$$$$$$$$$$$$$.=========
77777777777777$$$$$$$$$$$$$$$.=========
777777777777$$$$$$$$$$$$$$$$ :========+.
77777777777$$$$$$$$$$$$$$+..=========++~
777777777$$..~=====================+++++
77777777$~.~~~~=~=================+++++.
777777$$$.~~~===================+++++++.
77777$$$$.~~==================++++++++:
7$$$$$$$.==================++++++++++.
.,$$$$$$.================++++++++++~.
.=========~.........
.=============++++++
.===========+++..+++
.==========+++. .++
,=======++++++,,++,
..=====+++++++++=.
..~+=...
`;
const rightPart = [
`${fullName}`,
`----------------`,
`🐍 Language: Python`,
`⏱️ Runtime: ${new Date().getFullYear() - 2020}y`,
`🔧 Version: 3.${new Date().getFullYear() - 2015}.0`,
`----------------`,
`👤 User: ${username}`,
`📍 Location: ${location}`,
`🏢 Company: ${company}`,
`📅 GitHub: ${data.githubJoinDate || 'N/A'}`,
`📊 Contributions: ${data.totalContributions || 'N/A'}`,
`📚 Repositories: ${data.totalRepositories || 'N/A'}`,
`👥 Followers: ${data.followers || 'N/A'}`,
`👣 Following: ${data.following || 'N/A'}`,
data.bio && `📝 Bio: ${data.bio}`,
data.email && `📧 Email: ${data.email}`,
data.website && `🌐 Website: ${data.website}`,
data.twitter && `🐦 Twitter: @${data.twitter}`,
data.linkedin && `💼 LinkedIn: ${data.linkedin}`,
data.interests && `🌟 Interests: ${data.interests}`
].filter(Boolean);
const lines = pythonArt.split('\n');
const maxLeftWidth = Math.max(...lines.map(line => line.length));
return lines.map((line, i) => {
const padding = ' '.repeat(maxLeftWidth - line.length + 4);
return `${line}${padding}${rightPart[i] || ''}`;
}).join('\n');
}
export function generateReadme(data: FormData): string {
const sections: string[] = [];
// Top Wave Banner
sections.push(`<div align="center">
<img src="https://capsule-render.vercel.app/api?type=waving&color=gradient&height=200&section=header&text=${encodeURIComponent(`Hi 👋, I'm ${data.fullName || data.username}`)}&fontSize=50&animation=fadeIn&fontAlignY=30&desc=${encodeURIComponent('A passionate developer from ' + (data.location || 'Earth'))}&descAlignY=50" width="100%" />
<img src="https://raw.githubusercontent.com/platane/snk/output/github-contribution-grid-snake.svg" alt="Snake animation" />
</div>`);
// Terminal Style Info
sections.push('\n```bash\n' + generateNeofetch(data) + '\n```\n');
// About Me Section
const aboutPoints: string[] = [];
if (data.company) aboutPoints.push(`🏢 I'm currently working at **${data.company}**`);
if (data.location) aboutPoints.push(`📍 Based in **${data.location}**`);
if (data.website) aboutPoints.push(`🌐 Visit my portfolio at [${data.website}](${data.website})`);
if (data.email) aboutPoints.push(`📫 Contact me at **${data.email}**`);
if (aboutPoints.length > 0) {
sections.push('\n### About Me\n');
sections.push(aboutPoints.join('\n\n'));
}
// Skills Section
if (data.showSkills && data.skills) {
sections.push('\n### 💻 Tech Stack\n');
const skills = data.skills.split(',').map(skill => skill.trim());
sections.push(skills.map(skill => `![${skill}](https://img.shields.io/badge/${skill}-333333?style=flat&logo=${skill.toLowerCase()})`).join(' '));
}
// Education Section
if (data.showEducation && data.education) {
sections.push('\n### 🎓 Education\n');
sections.push(data.education);
}
// Interests Section
if (data.interests) {
sections.push('\n### 🌟 Interests\n');
sections.push(data.interests);
}
// Social Links
if (data.showSocial) {
const socials: string[] = [];
if (data.github) socials.push(`[<img src="https://img.shields.io/badge/GitHub-%2312100E.svg?&style=for-the-badge&logo=Github&logoColor=white" />](https://github.com/${data.username})`);
if (data.twitter) socials.push(`[<img src="https://img.shields.io/badge/twitter-%231DA1F2.svg?&style=for-the-badge&logo=twitter&logoColor=white" />](https://twitter.com/${data.twitter})`);
if (data.linkedin) socials.push(`[<img src="https://img.shields.io/badge/linkedin-%230077B5.svg?&style=for-the-badge&logo=linkedin&logoColor=white" />](${data.linkedin})`);
if (socials.length > 0) {
sections.push('\n### 🌐 Connect with me\n');
sections.push(socials.join(' '));
}
}
// GitHub Stats Section
if (data.showStats) {
sections.push('\n### 📊 GitHub Stats\n');
sections.push('<div align="center">\n');
sections.push(`<img src="https://github-readme-stats.vercel.app/api?username=${data.username}&show_icons=true&theme=${data.theme}" alt="GitHub Stats" />\n`);
if (data.showStreak) {
sections.push(`<img src="https://github-readme-streak-stats.herokuapp.com/?user=${data.username}&theme=${data.theme}" alt="GitHub Streak" />\n`);
}
if (data.showLanguages) {
sections.push(`<img src="https://github-readme-stats.vercel.app/api/top-langs/?username=${data.username}&layout=compact&theme=${data.theme}" alt="Top Languages" />\n`);
}
if (data.showActivityGraph) {
sections.push(`<img src="https://github-readme-activity-graph.vercel.app/graph?username=${data.username}&theme=${data.theme === 'github-dark' ? 'github-dark' : 'react-dark'}" alt="Activity Graph" />\n`);
}
if (data.showWakaTime) {
sections.push(`<img src="https://github-readme-stats.vercel.app/api/wakatime?username=${data.username}&theme=${data.theme}" alt="WakaTime Stats" />\n`);
}
if (data.showTrophies) {
sections.push(`<img src="https://github-profile-trophy.vercel.app/?username=${data.username}&theme=${data.theme === 'github-dark' ? 'darkhub' : 'onedark'}&column=4&margin-w=15&margin-h=15" alt="Trophies" />\n`);
}
sections.push('</div>\n');
if (data.showWakaTime) {
sections.push('> Note: To track coding time statistics, connect your GitHub account with [WakaTime](https://wakatime.com).');
}
}
// Profile Views Counter
sections.push(`\n<img src="https://komarev.com/ghpvc/?username=${data.username}&label=Profile%20Views&color=0e75b6&style=flat" alt="Profile Views" />`);
// Bottom Wave Banner
sections.push(`\n<img src="https://capsule-render.vercel.app/api?type=waving&color=gradient&height=150&section=footer" width="100%" />`);
return sections.join('\n');
}

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />