Update portfolio, roles and photo
This commit is contained in:
1 parent
7845cb5e6f
commit
4b271a08ab
7 files changed
+104
-11
No files matched your search
@@ -26,8 +26,8 @@
|
|||||||
"links": [{ "label": "Program", "url": "https://accessacademy.cehd.gmu.edu/ai-summer-camp" }]
|
"links": [{ "label": "Program", "url": "https://accessacademy.cehd.gmu.edu/ai-summer-camp" }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "Lead Software Engineer",
|
"role": "Team Lead, RAVC Team",
|
||||||
"organization": "Raytheon Autonomous Vehicle Competition",
|
"organization": "Raytheon Autonomous Vehicle Competition (RAVC)",
|
||||||
"period": "Dec 2025 – May 2026",
|
"period": "Dec 2025 – May 2026",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
"Beat four competing universities including Virginia Tech.",
|
"Beat four competing universities including Virginia Tech.",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[
|
[
|
||||||
{ "role": "Team Lead", "organization": "GMU RAVC/ARC Team", "url": "https://gmu.autonomousrobotics.club/" },
|
{ "role": "Team Lead", "organization": "GMU RAVC Team", "url": "https://gmu.autonomousrobotics.club/" },
|
||||||
{ "role": "President", "organization": "GMU Minecraft Club", "url": "https://gmmc.sirblob.co/" },
|
{ "role": "Secretary", "organization": "GMU Autonomous Robotics Club (ARC)", "url": "https://gmu.autonomousrobotics.club/" },
|
||||||
|
{ "role": "President", "organization": "GMU Minecraft Club", "url": "https://gmmc.club/" },
|
||||||
{ "role": "Eagle Scout", "organization": "BSA" },
|
{ "role": "Eagle Scout", "organization": "BSA" },
|
||||||
{ "role": "Volunteer", "organization": "Fairfax County CERT" }
|
{ "role": "Volunteer", "organization": "Fairfax County CERT" }
|
||||||
]
|
]
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"title": "Computer Science Student",
|
"title": "Computer Science Student",
|
||||||
"email": "sirblob0@gmail.com",
|
"email": "sirblob0@gmail.com",
|
||||||
"avatar": "/blob_nerd.png",
|
"avatar": "/blob_nerd.png",
|
||||||
|
"photo": "/portfolio-photo.jpg",
|
||||||
"bio": "An engineer who loves making things. I build fun coding projects, compete in hackathons and robotics competitions, and enjoy games like Minecraft and Pokémon TCG Live. I'm interested in Open Source, Robotics, Embedded Systems, and AI/ML.",
|
"bio": "An engineer who loves making things. I build fun coding projects, compete in hackathons and robotics competitions, and enjoy games like Minecraft and Pokémon TCG Live. I'm interested in Open Source, Robotics, Embedded Systems, and AI/ML.",
|
||||||
"tagline": "My passion is using Computer Science with practical Engineering to enhance technology for the future.",
|
"tagline": "My passion is using Computer Science with practical Engineering to enhance technology for the future.",
|
||||||
"education": {
|
"education": {
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { profile } from '#lib/content';
|
import { profile } from '#lib/content';
|
||||||
|
|
||||||
let { heading, subheading, text }: { heading: string; subheading: string; text: string } = $props();
|
let {
|
||||||
|
heading,
|
||||||
|
subheading,
|
||||||
|
text,
|
||||||
|
image = profile.avatar
|
||||||
|
}: { heading: string; subheading: string; text: string; image?: string } = $props();
|
||||||
|
|
||||||
|
const isPixelAvatar = $derived(image === profile.avatar);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="profile-intro">
|
<div class="profile-intro">
|
||||||
<img src={profile.avatar} alt={profile.displayName} />
|
<img src={image} alt={profile.displayName} class:pixelated={isPixelAvatar} />
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<h1>{heading}</h1>
|
<h1>{heading}</h1>
|
||||||
<div class="subheading">{subheading}</div>
|
<div class="subheading">{subheading}</div>
|
||||||
@@ -24,10 +31,15 @@
|
|||||||
|
|
||||||
img {
|
img {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
image-rendering: pixelated;
|
aspect-ratio: 1;
|
||||||
|
object-fit: cover;
|
||||||
border: 1px solid color-mix(in srgb, var(--foreground) 25%, transparent);
|
border: 1px solid color-mix(in srgb, var(--foreground) 25%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.pixelated {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 240px;
|
min-width: 240px;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { findProjectCategory } from '#lib/content';
|
||||||
|
import { getRepoStats } from '#lib/server/github';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
const openSourceProjects = findProjectCategory('opensource')?.projects ?? [];
|
||||||
|
const codeUrls = openSourceProjects.flatMap((project) => (project.links.code ? [project.links.code] : []));
|
||||||
|
|
||||||
|
return { repoStats: await getRepoStats(codeUrls) };
|
||||||
|
};
|
||||||
@@ -1,14 +1,27 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '@iconify/svelte';
|
import Icon from '@iconify/svelte';
|
||||||
import { experience, findProjectCategory, leadership, profile } from '#lib/content';
|
import { experience, findProjectCategory, leadership, profile, type Project } from '#lib/content';
|
||||||
import PageHeader from '#lib/components/ui/PageHeader.svelte';
|
import PageHeader from '#lib/components/ui/PageHeader.svelte';
|
||||||
import ProfileIntro from '#lib/components/ui/ProfileIntro.svelte';
|
import ProfileIntro from '#lib/components/ui/ProfileIntro.svelte';
|
||||||
import SectionNav from '#lib/components/ui/SectionNav.svelte';
|
import SectionNav from '#lib/components/ui/SectionNav.svelte';
|
||||||
import Section from '#lib/components/ui/Section.svelte';
|
import Section from '#lib/components/ui/Section.svelte';
|
||||||
import LinkButton from '#lib/components/ui/LinkButton.svelte';
|
import LinkButton from '#lib/components/ui/LinkButton.svelte';
|
||||||
import Tag from '#lib/components/ui/Tag.svelte';
|
import Tag from '#lib/components/ui/Tag.svelte';
|
||||||
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
const openSourceProjects = findProjectCategory('opensource')?.projects ?? [];
|
const openSourceProjects = findProjectCategory('opensource')?.projects ?? [];
|
||||||
|
const openSourcePagePath = '/projects/opensource';
|
||||||
|
|
||||||
|
function projectLinks(links: Project['links']) {
|
||||||
|
return [
|
||||||
|
{ label: 'Code', url: links.code },
|
||||||
|
{ label: 'Docs', url: links.docs },
|
||||||
|
{ label: 'Demo', url: links.demo },
|
||||||
|
{ label: 'Package', url: links.package }
|
||||||
|
].filter((link): link is { label: string; url: string } => !!link.url);
|
||||||
|
}
|
||||||
const skillColors = ['cyan', 'magenta', 'green', 'yellow', 'orange', 'blue'];
|
const skillColors = ['cyan', 'magenta', 'green', 'yellow', 'orange', 'blue'];
|
||||||
|
|
||||||
const sections = [
|
const sections = [
|
||||||
@@ -22,9 +35,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<PageHeader command="cat ~/portfolio.md" title="Portfolio" subtitle="Education, experience, skills, projects and leadership of {profile.name}." />
|
<PageHeader command="cat ~/portfolio.md" title="Portfolio" />
|
||||||
|
|
||||||
<ProfileIntro heading={profile.name} subheading={profile.title} text={profile.tagline} />
|
<ProfileIntro heading={profile.name} subheading={profile.title} text={profile.tagline} image={profile.photo} />
|
||||||
<SectionNav {sections} />
|
<SectionNav {sections} />
|
||||||
|
|
||||||
<Section title="Contact" id="contact">
|
<Section title="Contact" id="contact">
|
||||||
@@ -89,17 +102,38 @@
|
|||||||
<Section title="Projects" id="projects">
|
<Section title="Projects" id="projects">
|
||||||
{#each openSourceProjects as project (project.name)}
|
{#each openSourceProjects as project (project.name)}
|
||||||
<article class="project">
|
<article class="project">
|
||||||
|
<div class="project-heading">
|
||||||
<a class="project-name" href={project.links.code ?? project.links.demo} target="_blank" rel="noopener noreferrer">> {project.name}</a>
|
<a class="project-name" href={project.links.code ?? project.links.demo} target="_blank" rel="noopener noreferrer">> {project.name}</a>
|
||||||
{#if project.version}<span class="version">v{project.version}</span>{/if}
|
{#if project.version}<span class="version">v{project.version}</span>{/if}
|
||||||
<span class="muted"> - {project.description}</span>
|
{#if project.period}<span class="muted project-period">{project.period}</span>{/if}
|
||||||
|
{#if project.links.code && data.repoStats[project.links.code]}
|
||||||
|
{@const stats = data.repoStats[project.links.code]}
|
||||||
|
<a class="repo-stat stars" href="{project.links.code}/stargazers" target="_blank" rel="noopener noreferrer" title="GitHub stars">
|
||||||
|
<Icon icon="mdi:star-outline" width="15" />{stats.stars}
|
||||||
|
</a>
|
||||||
|
<a class="repo-stat forks" href="{project.links.code}/forks" target="_blank" rel="noopener noreferrer" title="GitHub forks">
|
||||||
|
<Icon icon="mdi:source-fork" width="15" />{stats.forks}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<p class="project-description">{project.description}</p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
{#each project.tags as tag, index (tag)}
|
{#each project.tags as tag, index (tag)}
|
||||||
<Tag label={tag} colorIndex={index} />
|
<Tag label={tag} colorIndex={index} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="entry-links">
|
||||||
|
{#each projectLinks(project.links) as projectLink (projectLink.url)}
|
||||||
|
<a href={projectLink.url} target="_blank" rel="noopener noreferrer">{projectLink.label}</a>
|
||||||
|
{/each}
|
||||||
|
<a href={openSourcePagePath}>More info</a>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
{/each}
|
{/each}
|
||||||
|
<div class="button-row">
|
||||||
|
<LinkButton href={openSourcePagePath} icon="ph:git-branch-duotone">Open source projects</LinkButton>
|
||||||
<LinkButton href="/projects" icon="mdi:folder">All projects</LinkButton>
|
<LinkButton href="/projects" icon="mdi:folder">All projects</LinkButton>
|
||||||
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Leadership" id="leadership">
|
<Section title="Leadership" id="leadership">
|
||||||
@@ -206,6 +240,41 @@
|
|||||||
margin-top: 0.35rem;
|
margin-top: 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-heading {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-period {
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-stat {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.2rem;
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-stat.stars {
|
||||||
|
color: var(--yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-stat.forks {
|
||||||
|
color: var(--cyan);
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-stat:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-description {
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.leadership {
|
.leadership {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 1.25rem;
|
padding-left: 1.25rem;
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
Reference in new issue
Block a user