Compare changes

Choose any two refs to compare.

public/banner.png

This is a binary file and will not be displayed.

public/celeste_exhale.mp3

This is a binary file and will not be displayed.

+1
public/pause.svg
···
···
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#bac2de" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-pause"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>
+1
public/play.svg
···
···
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#bac2de" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-play"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
-1
src/assets/astro.svg
···
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="115" height="48"><path fill="#17191E" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="url(#a)" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="#17191E" d="M.02 30.31s4.02-1.95 8.05-1.95l3.04-9.4c.11-.45.44-.76.82-.76.37 0 .7.31.82.76l3.04 9.4c4.77 0 8.05 1.95 8.05 1.95L17 11.71c-.2-.56-.53-.91-.98-.91H7.83c-.44 0-.76.35-.97.9L.02 30.31Zm42.37-5.97c0 1.64-2.05 2.62-4.88 2.62-1.85 0-2.5-.45-2.5-1.41 0-1 .8-1.49 2.65-1.49 1.67 0 3.09.03 4.73.23v.05Zm.03-2.04a21.37 21.37 0 0 0-4.37-.36c-5.32 0-7.82 1.25-7.82 4.18 0 3.04 1.71 4.2 5.68 4.2 3.35 0 5.63-.84 6.46-2.92h.14c-.03.5-.05 1-.05 1.4 0 1.07.18 1.16 1.06 1.16h4.15a16.9 16.9 0 0 1-.36-4c0-1.67.06-2.93.06-4.62 0-3.45-2.07-5.64-8.56-5.64-2.8 0-5.9.48-8.26 1.19.22.93.54 2.83.7 4.06 2.04-.96 4.95-1.37 7.2-1.37 3.11 0 3.97.71 3.97 2.15v.57Zm11.37 3c-.56.07-1.33.07-2.12.07-.83 0-1.6-.03-2.12-.1l-.02.58c0 2.85 1.87 4.52 8.45 4.52 6.2 0 8.2-1.64 8.2-4.55 0-2.74-1.33-4.09-7.2-4.39-4.58-.2-4.99-.7-4.99-1.28 0-.66.59-1 3.65-1 3.18 0 4.03.43 4.03 1.35v.2a46.13 46.13 0 0 1 4.24.03l.02-.55c0-3.36-2.8-4.46-8.2-4.46-6.08 0-8.13 1.49-8.13 4.39 0 2.6 1.64 4.23 7.48 4.48 4.3.14 4.77.62 4.77 1.28 0 .7-.7 1.03-3.71 1.03-3.47 0-4.35-.48-4.35-1.47v-.13Zm19.82-12.05a17.5 17.5 0 0 1-6.24 3.48c.03.84.03 2.4.03 3.24l1.5.02c-.02 1.63-.04 3.6-.04 4.9 0 3.04 1.6 5.32 6.58 5.32 2.1 0 3.5-.23 5.23-.6a43.77 43.77 0 0 1-.46-4.13c-1.03.34-2.34.53-3.78.53-2 0-2.82-.55-2.82-2.13 0-1.37 0-2.65.03-3.84 2.57.02 5.13.07 6.64.11-.02-1.18.03-2.9.1-4.04-2.2.04-4.65.07-6.68.07l.07-2.93h-.16Zm13.46 6.04a767.33 767.33 0 0 1 .07-3.18H82.6c.07 1.96.07 3.98.07 6.92 0 2.95-.03 4.99-.07 6.93h5.18c-.09-1.37-.11-3.68-.11-5.65 0-3.1 1.26-4 4.12-4 1.33 0 2.28.16 3.1.46.03-1.16.26-3.43.4-4.43-.86-.25-1.81-.41-2.96-.41-2.46-.03-4.26.98-5.1 3.38l-.17-.02Zm22.55 3.65c0 2.5-1.8 3.66-4.64 3.66-2.81 0-4.61-1.1-4.61-3.66s1.82-3.52 4.61-3.52c2.82 0 4.64 1.03 4.64 3.52Zm4.71-.11c0-4.96-3.87-7.18-9.35-7.18-5.5 0-9.23 2.22-9.23 7.18 0 4.94 3.49 7.59 9.21 7.59 5.77 0 9.37-2.65 9.37-7.6Z"/><defs><linearGradient id="a" x1="6.33" x2="19.43" y1="40.8" y2="34.6" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient></defs></svg>
···
-1
src/assets/background.svg
···
-
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1024" fill="none"><path fill="url(#a)" fill-rule="evenodd" d="M-217.58 475.75c91.82-72.02 225.52-29.38 341.2-44.74C240 415.56 372.33 315.14 466.77 384.9c102.9 76.02 44.74 246.76 90.31 366.31 29.83 78.24 90.48 136.14 129.48 210.23 57.92 109.99 169.67 208.23 155.9 331.77-13.52 121.26-103.42 264.33-224.23 281.37-141.96 20.03-232.72-220.96-374.06-196.99-151.7 25.73-172.68 330.24-325.85 315.72-128.6-12.2-110.9-230.73-128.15-358.76-12.16-90.14 65.87-176.25 44.1-264.57-26.42-107.2-167.12-163.46-176.72-273.45-10.15-116.29 33.01-248.75 124.87-320.79Z" clip-rule="evenodd" style="opacity:.154"/><path fill="url(#b)" fill-rule="evenodd" d="M1103.43 115.43c146.42-19.45 275.33-155.84 413.5-103.59 188.09 71.13 409 212.64 407.06 413.88-1.94 201.25-259.28 278.6-414.96 405.96-130 106.35-240.24 294.39-405.6 265.3-163.7-28.8-161.93-274.12-284.34-386.66-134.95-124.06-436-101.46-445.82-284.6-9.68-180.38 247.41-246.3 413.54-316.9 101.01-42.93 207.83 21.06 316.62 6.61Z" clip-rule="evenodd" style="opacity:.154"/><defs><linearGradient id="b" x1="373" x2="1995.44" y1="1100" y2="118.03" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient><linearGradient id="a" x1="107.37" x2="1130.66" y1="1993.35" y2="1026.31" gradientUnits="userSpaceOnUse"><stop stop-color="#3245FF"/><stop offset="1" stop-color="#BC52EE"/></linearGradient></defs></svg>
···
+1
src/assets/butterfly.svg
···
···
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-butterfly-icon lucide-butterfly"><path d="M15.8 2C12 3.8 12 9 12 9s0-5.2-3.8-7"/><path d="M12 9v11"/><path d="M20 5c-3.5 0-6.5 3.9-8 6.3C10.5 8.9 7.5 5 4 5a2 2 0 0 0-2 2c0 2.3.6 4.4 1.5 5.6C4 13.5 4.9 14 6 14h2c-.9.4-2.1.9-2.6 1.5-1.6 1.6-.9 3.4.7 4.9 1.6 1.6 3.4 2.3 4.9.7.3-.3 1-1.1 1-1.1s.6.8 1 1.1c1.6 1.6 3.4.9 4.9-.7 1.6-1.6 2.3-3.4.7-4.9-.5-.5-1.7-1.1-2.6-1.5h2c1.1 0 2-.5 2.5-1.4.9-1.2 1.5-3.3 1.5-5.6a2 2 0 0 0-2-2"/></svg>
+34
src/components/Badgebar.astro
···
···
+
---
+
import LucideIcon from "./LucideIcon.astro";
+
+
interface Badge {
+
icon: string | any,
+
href: string
+
color: string
+
svg?: boolean
+
}
+
+
interface Props {
+
badges: Badge[]
+
size: string
+
}
+
+
const { badges, size } = Astro.props
+
+
---
+
<span class="flex gap-2 bg-ctp-crust p-2 pl-3 pr-3 rounded-full h-min">
+
{badges.map(badge =>
+
<a href=`${badge.href}` target="_blank" rel="noopener noreferrer">
+
{ badge.svg && badge.icon && (
+
<badge.icon width={size} height={size} style={`stroke:${badge.color}`}
+
class="hover:rotate-15 hover:scale-110 ease-in hover:ease-out transition-transform active:scale-80"
+
/>
+
)}
+
{ !badge.svg && (
+
<LucideIcon name=`${badge.icon}` width=`${size}` height=`${size}` style={`stroke:${badge.color}`}
+
class="hover:rotate-15 hover:scale-110 ease-in hover:ease-out transition-transform active:scale-80"
+
/>
+
)}
+
</a>
+
)}
+
</span>
-9
src/components/Contributions.astro
···
-
---
-
import Tag from "./Tag.astro"
-
import GitHubCalendar from "./GithubCalendar"
-
---
-
-
<div class="flex flex-col p-8 border-ctp-surface0 border w-full h-min relative">
-
<Tag name="Contributions"/>
-
<GitHubCalendar client:load username="imabanana80"/>
-
</div>
···
+8 -3
src/components/Footer.astro
···
---
-
import { Heart, Rocket, Copyright } from "@lucide/astro"
---
<div class="flex justify-between bg-ctp-base p-6 pl-12 pr-12">
-
<span class="inline-flex gap-2">
Copyright
<Copyright size="20" class="stroke-ctp-text"/>
2026 Banana
</span>
-
<span class="inline-flex gap-2">
Made with
<Heart size="20" class="stroke-ctp-red"/>
using Astro
···
---
+
import { Heart, Rocket, Copyright, Code } from "@lucide/astro"
---
<div class="flex justify-between bg-ctp-base p-6 pl-12 pr-12">
+
<span class="inline-flex gap-2 items-center">
Copyright
<Copyright size="20" class="stroke-ctp-text"/>
2026 Banana
</span>
+
<span class="inline-flex gap-2 items-center">
+
<a href="https://tangled.org/@banana.tngl.sh/mywebsite-2026"
+
class="text-xs text-ctp-overlay0 flex gap-1 hover:underline"><Code size="16" />src</a>
+
+
</span>
+
<span class="inline-flex gap-2 items-center">
Made with
<Heart size="20" class="stroke-ctp-red"/>
using Astro
+6 -6
src/components/GithubCalendar.jsx
···
const theme = {
dark: [
-
'#11111b',
-
'#2b7a24',
-
'#41b536',
-
'#6FD166',
-
'#A6E3A1',
],
};
React.useEffect(() => {
···
}
return <GitHubCalendar username={username} theme={theme}
-
blockMargin={4} blockRadius={8} blockSize={10}
{...props} />;
}
···
const theme = {
dark: [
+
'var(--catppuccin-color-crust)',
+
'var(--catppuccin-color-green-900)',
+
'var(--catppuccin-color-green-500)',
+
'var(--catppuccin-color-green-200)',
+
'var(--catppuccin-color-green-50)',
],
};
React.useEffect(() => {
···
}
return <GitHubCalendar username={username} theme={theme}
+
blockMargin={3} blockRadius={1} blockSize={10}
{...props} />;
}
+12
src/components/Label.astro
···
···
+
---
+
interface Props {
+
name: string;
+
}
+
+
const { name } = Astro.props;
+
---
+
+
<span
+
class=`absolute top-[-0.5rem] left-3 text-xs text-ctp-overlay1 bg-ctp-mantle pl-2 pr-2`
+
>{name}</span
+
>
+12
src/components/LucideIcon.astro
···
···
+
---
+
import { icons, type IconProps } from '@lucide/astro';
+
+
interface Props extends IconProps {
+
name: keyof typeof icons;
+
}
+
+
const { name, ...restProps } = Astro.props;
+
const Icon = icons[name];
+
---
+
+
<Icon {...restProps} />
-30
src/components/Navbar.astro
···
-
---
-
import Btn from "./NavbarLink.astro"
-
import { Code } from "@lucide/astro"
-
-
let href = Astro.url.toString()
-
href = href.replace(Astro.url.host.toString(), "")
-
href = href.replace("https://", "")
-
href = href.replace("http://", "")
-
---
-
<div class="absolute bg-ctp-base w-full pl-8 pr-8 pt-4 pb-4 flex justify-between rounded-bl-2xl rounded-br-2xl">
-
<div class="flex gap-4 items-center">
-
<a href="/doodlecat.png"><img src="doodlecat.png" width="48px" height="48px" class="rounded-full" /></a>
-
<span class="flex flex-col">
-
<span>~/banana{href}</span>
-
<a href="https://tangled.org/@banana.tngl.sh/mywebsite-2026"
-
class="text-xs text-ctp-overlay0 flex gap-1 hover:underline"><Code size="16" />src</a>
-
</span>
-
</div>
-
<div class="flex gap-6 items-center">
-
<Btn page="home"/>
-
<Btn page="projects"/>
-
<Btn page="experience"/>
-
</div>
-
</div>
-
<style>
-
@reference "../styles/global.css"
-
a {
-
@apply ;
-
}
-
</style>
···
-21
src/components/NavbarLink.astro
···
-
---
-
interface Props {
-
page: String;
-
}
-
const {page} = Astro.props
-
var href = page
-
if (href == "home") {
-
href = ""
-
}
-
---
-
<a
-
href=`/${href}`
-
class="font-bold
-
text-ctp-yellow hover:text-ctp-blue
-
hover:underline
-
ease-in hover:ease-out transition-all
-
">{page}</a>
-
-
-
-
···
+79
src/components/PortfolioItem.astro
···
···
+
---
+
interface Badge {
+
icon: string,
+
href: string
+
color: string
+
}
+
+
interface Props {
+
type: string
+
icon?: string,
+
title: string,
+
subtitle: string,
+
tag?: string,
+
tagcolor?: string,
+
href: string
+
badges?: Badge[]
+
}
+
+
const { type, icon = "Box", title, subtitle, tag, tagcolor, href, badges } = Astro.props
+
+
var color = "--catppuccin-color-yellow"
+
if (type=="project") {
+
color = "--catppuccin-color-red"
+
} else if (type=="repo") {
+
color = "--catppuccin-color-peach"
+
} else if (type=="team") {
+
color = "--catppuccin-color-mauve"
+
} else if (type=="misc") {
+
color = "--catppuccin-color-maroon"
+
} else if (type=="freelance") {
+
color = "--catppuccin-color-sky"
+
}
+
const colorCss = "var(" + color + ")"
+
+
var iconName = icon
+
if (iconName == "Box") {
+
if (type=="project") {
+
iconName = "Archive"
+
} else if (type=="repo") {
+
iconName = "BookMarked"
+
} else if (type=="team") {
+
iconName = "Users"
+
} else if (type=="misc") {
+
iconName = "Box"
+
} else if (type=="freelance") {
+
iconName = "ReceiptText"
+
}
+
}
+
+
import LucideIcon from "./LucideIcon.astro"
+
import Badgebar from "./Badgebar.astro"
+
import Tag from "./Tag.astro"
+
---
+
<div class="p-4 border border-ctp-surface0 flex flex-col -mt-px w-full">
+
<div class="flex flex-wrap sm:flex-nowrap justify-between gap-2 sm:gap-4">
+
<a href=`${href}` class="flex flex-col gap-1 group">
+
<span class="text-xl flex gap-2 items-center">
+
<LucideIcon name=`${iconName}` width="20" height="20" style={`stroke:${colorCss}`} />
+
<span class="group-hover:underline font-bold">{title} </span>
+
<span class="hidden md:flex items-center">
+
{tag && tagcolor && (
+
<Tag text={tag} color={tagcolor}/>
+
)}
+
</span>
+
</span>
+
<span class="text-sm">
+
<span class="flex gap-1">{subtitle}</span>
+
</span>
+
</a>
+
<span>
+
{ badges && (
+
<Badgebar badges={badges} size="20"/>
+
)}
+
</span>
+
</div>
+
<div>
+
<slot/>
+
</div>
+
</div>
-73
src/components/Profile.astro
···
-
---
-
import Tag from '../components/Tag.astro';
-
import { Github, Twitch, Youtube, Icon } from '@lucide/astro'
-
import { butterfly } from '@lucide/lab'
-
---
-
<div class="flex flex-col col-span-2 border border-ctp-surface0 gap-[-1px] w-full h-min">
-
<span class="p-4 pb-0 border-b border-ctp-surface0 relative">
-
<img src="/banner.png" class="w-full"/>
-
</span>
-
<div class="p-8 pt-12 flex-col flex flex-wrap relative">
-
<img src="doodlecat.png" width="96px" height="96px"
-
class="rounded-full absolute -top-12 left-8 border-6 border-ctp-mantle" />
-
<span class="text-base flex justify-between w-full flex-wrap gap-2">
-
<span class="flex flex-col">
-
<span class="text-2xl font-bold">Banana</span>
-
<a href="mailto:banana@potassium.sh" class="hover:underline ease-in hover:ease-out transition-all">banana@potassium.sh</a>
-
</span>
-
<span class="flex gap-2 bg-ctp-crust p-2 pl-3 pr-3 rounded-full h-min">
-
<a href="https://github.com/imabanana80">
-
<Github size="20"
-
class="stroke-ctp-green hover:rotate-15 ease-in hover:ease-out transition-transform"/></a>
-
<a href="https://bsky.app/profile/imabanana80.com">
-
<Icon iconNode={butterfly} size="20"
-
class="stroke-ctp-sapphire hover:rotate-15 ease-in hover:ease-out transition-transform"/></a>
-
<a href="https://twitch.tv/imabanana80">
-
<Twitch size="20"
-
class="stroke-ctp-mauve hover:rotate-15 ease-in hover:ease-out transition-transform"/></a>
-
<a href="https://youtube.com/@imabanana80">
-
<Youtube size="20"
-
class="stroke-ctp-red hover:rotate-15 ease-in hover:ease-out transition-transform"/></a>
-
</span>
-
</span>
-
<span class="relative p-3 mt-4 border border-ctp-surface0 text-sm">
-
<Tag name="About"/>
-
Hi, I'm Banana, a computer science student and software developer that specialises in
-
Web Development as well as developing and hosting Minecraft Events.
-
</span>
-
<span class="relative p-3 mt-4 border border-ctp-surface0 text-sm">
-
<Tag name="Currently"/>
-
Learning Svelte | Open for Hire
-
</span>
-
<span class="relative p-3 -mt-px border border-ctp-surface0 text-ctp-subtext0 text-sm">
-
<Tag name="Time"/>
-
<span id="clock" class="text-ctp-text"></span> (SGT/UTC+8)
-
</span>
-
</div >
-
</div>
-
-
-
<script>
-
const clockElement: HTMLElement | null = document.getElementById("clock");
-
-
if (!clockElement) {
-
console.error(`Error: Clock element with ID "clock" not found.`);
-
}
-
-
const displayTime = (): void => {
-
const now: Date = new Date();
-
-
const options: Intl.DateTimeFormatOptions = {
-
hour: '2-digit',
-
minute: '2-digit',
-
second: '2-digit',
-
hour12: false,
-
timeZone: "Asia/Singapore"
-
};
-
-
const timeString: string = now.toLocaleTimeString('en-US', options);
-
clockElement.textContent = timeString;
-
};
-
displayTime();
-
setInterval(displayTime, 1000);
-
</script>
···
+8 -7
src/components/Tag.astro
···
---
interface Props {
-
name: string;
}
-
const { name } = Astro.props;
---
-
-
<span
-
class=`absolute top-[-0.5rem] left-3 text-xs text-ctp-overlay1 bg-ctp-mantle pl-2 pr-2`
-
>{name}</span
-
>
···
---
interface Props {
+
text: string
+
color: string
}
+
const { text, color } = Astro.props
+
const colorCss = "var(" + color + "-700)"
---
+
<span class="p-1 pl-2 pr-2 text-xs rounded-full font-bold bg-ctp-base border border-ctp-surface0"
+
style={`color:${colorCss};`}>
+
{text}
+
</span>
+27
src/components/navbar/Navbar.astro
···
···
+
---
+
import Btn from "./NavbarLink.astro"
+
+
let href = Astro.url.toString()
+
href = href.replace(Astro.url.host.toString(), "")
+
href = href.replace("https://", "")
+
href = href.replace("http://", "")
+
---
+
<div class="fixed z-40 bg-ctp-base
+
left-[2%] right-[2%] md:left-[4%] md:right-[4%] xl:left-[8%] xl:right-[8%]
+
pl-8 pr-8 pt-4 pb-4 flex justify-between rounded-bl-2xl rounded-br-2xl squircle">
+
<div class="flex gap-4 items-center">
+
<span class="flex flex-col">
+
<span>~/banana{href}</span>
+
</span>
+
</div>
+
<div class="flex gap-6 items-center">
+
<Btn page="home"/>
+
<Btn page="projects"/>
+
</div>
+
</div>
+
<style>
+
@reference "../../styles/global.css"
+
a {
+
@apply ;
+
}
+
</style>
+21
src/components/navbar/NavbarLink.astro
···
···
+
---
+
interface Props {
+
page: String;
+
}
+
const {page} = Astro.props
+
var href = page
+
if (href == "home") {
+
href = ""
+
}
+
---
+
<a
+
href=`/${href}`
+
class="font-bold
+
text-ctp-yellow hover:text-ctp-blue
+
hover:underline
+
ease-in hover:ease-out transition-all
+
">{page}</a>
+
+
+
+
+34
src/components/projects/BiomeBattle.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="freelance"
+
title="BiomeBattle"
+
subtitle="A modern Minecraft creator event. As a developer, I helped with minor improvements to the cosmetics system."
+
icon="Earth"
+
href="https://biomebattle.xyz/"
+
tag="Java"
+
tagcolor="--catppuccin-color-teal"
+
badges={[
+
{
+
icon: "Bird",
+
href: "https://x.com/biomebattle",
+
color: "var(--catppuccin-color-sapphire)"
+
},
+
{
+
icon: "Github",
+
href: "https://github.com/biomebattle",
+
color: "var(--catppuccin-color-green)"
+
},
+
{
+
icon: "MessageCircle",
+
href: "https://biomebattle.xyz/discord",
+
color: "var(--catppuccin-color-blue)"
+
},
+
{
+
icon: "Youtube",
+
href: "https://youtube.com/@BiomeBattle",
+
color: "var(--catppuccin-color-red)"
+
},
+
]}
+
/>
+24
src/components/projects/Dotfiles.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
export const priority = 1;
+
---
+
<Item
+
type="repo"
+
title="dotfiles"
+
subtitle="My personal configuration dotfiles for endeavouros linux."
+
href="https://tangled.org/@banana.tngl.sh/dotfiles"
+
badges={[
+
{
+
icon: "Github",
+
href: "https://github.com/imabanana80/dotfiles",
+
color: "var(--catppuccin-color-green)"
+
},
+
+
{
+
icon: "Book",
+
href: "https://wiki.archlinux.org/title/Dotfiles",
+
color: "var(--catppuccin-color-sapphire)"
+
},
+
]}
+
/>
+
+28
src/components/projects/IsleStats.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="project"
+
title="IsleStats"
+
subtitle="An MCC Island Discord statistics bot that renders beautiful stat cards to show off your stats."
+
href="https://islestats.net"
+
tag="Python"
+
tagcolor="--catppuccin-color-blue"
+
badges={[
+
{
+
icon: "Github",
+
href: "https://github.com/islestats",
+
color: "var(--catppuccin-color-green)"
+
},
+
{
+
icon: "MessageCircle",
+
href: "https://discord.gg//islestats",
+
color: "var(--catppuccin-color-blue)"
+
},
+
{
+
icon: "Mail",
+
href: "mailto:contact@islestats.net",
+
color: "var(--catppuccin-color-maroon)"
+
}
+
]}
+
/>
+24
src/components/projects/MyWebsite2026.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="repo"
+
title="my-website-2026"
+
subtitle="My portfolio website for the year 2026 built with Astro. This is the current website you're on."
+
href="https://beta.imabanana80.com/"
+
tag="Astro"
+
tagcolor="--catppuccin-color-pink"
+
badges={[
+
{
+
icon: "Spool",
+
href: "https://tangled.org/@banana.tngl.sh/mywebsite-2026",
+
color: "var(--catppuccin-color-lavender)"
+
},
+
{
+
icon: "Github",
+
href: "https://github.com/imabanana80/mywebsite-2026",
+
color: "var(--catppuccin-color-green)"
+
},
+
]}
+
/>
+
+18
src/components/projects/PassgenAstro.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="repo"
+
title="passgen-astro"
+
subtitle="Static client-based password phrase generator built with astro."
+
href="https://pass.imabanana80.com"
+
tag="Astro"
+
tagcolor="--catppuccin-color-pink"
+
badges={[
+
{
+
icon: "Github",
+
href: "https://github.com/imabanana80/passgen-astro",
+
color: "var(--catppuccin-color-green)"
+
},
+
]}
+
/>
+29
src/components/projects/PotassiumSh.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
export const priority = 2;
+
---
+
<Item
+
type="team"
+
title="potassium.sh"
+
subtitle="An indie software development studio for the creative and insane, founded by me, with some friends!"
+
href="https://potassium.sh"
+
tag="Founder"
+
tagcolor="--catppuccin-color-rosewater"
+
badges={[
+
{
+
icon: "Mail",
+
href: "mailto:team@potassium.sh",
+
color: "var(--catppuccin-color-maroon)"
+
},
+
{
+
icon: "Spool",
+
href: "https://tangled.org/@potassium.sh",
+
color: "var(--catppuccin-color-lavender)"
+
},
+
{
+
icon: "Bird",
+
href: "https://bsky.app/profile/potassium.sh",
+
color: "var(--catppuccin-color-sapphire)"
+
},
+
]}
+
/>
+18
src/components/projects/SMPSales.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="team"
+
title="SMP Sales"
+
subtitle="A Minecraft SMP freelancing hub, from developers to artists, all you'll need to start an SMP of your own."
+
href="https://discord.gg/DUa3asXpvs"
+
tag="Developer"
+
tagcolor="--catppuccin-color-rosewater"
+
badges={[
+
{
+
icon: "Github",
+
href: "https://github.com/smpsales",
+
color: "var(--catppuccin-color-green)"
+
},
+
]}
+
/>
+11
src/components/projects/StatusPage.astro
···
···
+
---
+
import Item from "../PortfolioItem.astro"
+
---
+
<Item
+
type="project"
+
title="Status Page"
+
subtitle="A custom status page design that could automatically fetch data from a custom api to display live information."
+
href="https://status.imabanana80.com"
+
tag="Astro"
+
tagcolor="--catppuccin-color-pink"
+
/>
+9
src/components/section/Contributions.astro
···
···
+
---
+
import Label from "../Label.astro"
+
import GitHubCalendar from "../GithubCalendar"
+
---
+
+
<div class="flex flex-col p-8 border-ctp-surface0 border w-full h-min relative">
+
<Label name="Contributions"/>
+
<GitHubCalendar client:load username="imabanana80"/>
+
</div>
+62
src/components/section/MusicPlayer.astro
···
···
+
---
+
import Label from "../Label.astro"
+
import { Disc } from "@lucide/astro"
+
---
+
<div class="border border-ctp-surface0 p-8 pt-6 pb-6 relative">
+
<Label name="Music Player" />
+
<div id="music">
+
<audio
+
id="audio"
+
controls
+
src="/celeste_exhale.mp3"
+
class="hidden"
+
></audio>
+
<div class="mb-1">
+
<span class="text-base flex gap-2 items-center w-fit">
+
<Disc size="20" class="stroke-ctp-pink"/>
+
Celeste - Exhale
+
<span class="text-sm text-ctp-overlay0">by Lena Raine</span>
+
</span>
+
</div>
+
<div id="controls" class="flex gap-1">
+
<button id="control-button" class=""><img id="control-img" src="/play.svg"></button>
+
<div id="progress-bar" class="self-center border border-ctp-surface1 w-full h-1">
+
<div id="progress" class="bg-ctp-subtext0 h-[0.25rem] w-0"></div>
+
</div>
+
</div>
+
</div>
+
</div>
+
<script>
+
const controlButton = document.getElementById("control-button") as HTMLElement
+
const controlImg = document.getElementById("control-img") as HTMLImageElement
+
const audio = document.getElementById("audio") as HTMLAudioElement
+
const progressBar = document.getElementById("progress-bar") as HTMLElement
+
const progress = document.getElementById("progress") as HTMLElement
+
+
controlButton.addEventListener("click", () => {
+
if (audio.paused) {
+
audio.play();
+
controlImg.src = "/pause.svg"
+
} else {
+
audio.pause();
+
controlImg.src = "/play.svg"
+
}
+
});
+
+
audio.addEventListener("timeupdate", () => {
+
progress.style.width = `${(audio.currentTime / audio.duration) * 100}%`;
+
});
+
+
audio.addEventListener("ended", () => {
+
audio.currentTime = 0;
+
progress.style.width = "0%";
+
audio.play();
+
});
+
+
progressBar.addEventListener("click", (e) => {
+
const clickPosition =
+
(e.offsetX / progressBar.clientWidth) * audio.duration;
+
audio.currentTime = clickPosition;
+
});
+
</script>
+
+15
src/components/section/Pinned.astro
···
···
+
---
+
import Label from "../Label.astro"
+
import Dotfiles from "../projects/Dotfiles.astro"
+
import IsleStats from "../projects/IsleStats.astro"
+
import Potassium from "../projects/PotassiumSh.astro"
+
---
+
+
<div class="flex flex-col p-8 border-ctp-surface0 border w-full h-min relative">
+
<Label name="Pinned"/>
+
<div class="flex flex-wrap">
+
<IsleStats/>
+
<Potassium />
+
<Dotfiles />
+
</div>
+
</div>
+102
src/components/section/Profile.astro
···
···
+
---
+
import Label from '../Label.astro';
+
import Quote from './Quote.astro'
+
import BadgeBar from "../Badgebar.astro"
+
import Butterfly from "../../assets/butterfly.svg"
+
+
let home = "hidden lg:flex"
+
if (Astro.url.pathname == "/") {
+
home = "flex"
+
}
+
---
+
<div class=`${home} flex-col md:col-span-2 border border-ctp-surface0 gap-[-1px] w-full h-min`>
+
<span class="p-4 pb-0 border-b border-ctp-surface0 relative">
+
<img src="/banner.png" class="w-full"/>
+
</span>
+
<div class="p-8 pt-12 flex-col flex flex-wrap relative">
+
<img src="/doodlecat.png" width="96px" height="96px"
+
class="rounded-full absolute -top-12 left-8 border-6 border-ctp-mantle" />
+
<span class="text-base flex justify-between w-full flex-wrap gap-2">
+
<span class="flex flex-col">
+
<span class="text-2xl font-bold">Banana</span>
+
<a href="mailto:banana@potassium.sh" class="hover:underline ease-in hover:ease-out transition-all">
+
banana@potassium.sh</a>
+
</span>
+
<BadgeBar
+
badges={[
+
{
+
icon: "Github",
+
color: "var(--catppuccin-color-green)",
+
href: "https://github.com/imabanana80"
+
},
+
{
+
icon: "Spool",
+
color: "var(--catppuccin-color-lavender)",
+
href: "https://tangled.org/@banana.tngl.sh"
+
},
+
{
+
icon: Butterfly,
+
color: "var(--catppuccin-color-sapphire)",
+
href: "https://bsky.app/profile/imabanana80.com",
+
svg: true
+
}
+
]}
+
size='20'
+
/>
+
</span>
+
<span class="relative p-3 mt-4 border border-ctp-surface0 text-sm">
+
<Label name="Currently"/>
+
Switching to endeavoros | Open for Hire
+
</span>
+
<span class="relative p-3 mt-4 border border-ctp-surface0 text-sm">
+
<Label name="About"/>
+
Hi, I'm Banana, a computer science student and software developer that specialises in
+
Web Development as well as developing and hosting Minecraft Events.
+
</span>
+
<span class="relative p-3 mt-4 border border-ctp-surface0 text-ctp-subtext0 text-sm">
+
<Label name="Time"/>
+
<span id="clock" class="text-ctp-text"></span> (SGT/UTC+8)
+
</span>
+
<span class="relative p-3 mt-4 border border-ctp-surface0 text-sm">
+
<Label name="Languages"/>
+
<span>Java, Python, Golang, HTML/CSS/TS</span>
+
</span>
+
<span class="relative p-3 -mt-px border border-ctp-surface0 text-sm">
+
<Label name="Frameworks"/>
+
<span>PaperMC, Astro, Tailwindcss</span>
+
</span>
+
<span class="relative p-3 -mt-px border border-ctp-surface0 text-sm">
+
<Label name="Tools"/>
+
<span>Neovim, IntellIJ, Figma, VSC*de</span>
+
</span>
+
<div class="mt-4 flex">
+
<Quote />
+
</div>
+
</div >
+
</div>
+
+
+
<script>
+
const clockElement: HTMLElement | null = document.getElementById("clock");
+
+
if (!clockElement) {
+
console.error(`Error: Clock element with ID "clock" not found.`);
+
}
+
+
const displayTime = (): void => {
+
const now: Date = new Date();
+
+
const options: Intl.DateTimeFormatOptions = {
+
hour: '2-digit',
+
minute: '2-digit',
+
second: '2-digit',
+
hour12: false,
+
timeZone: "Asia/Singapore"
+
};
+
+
const timeString: string = now.toLocaleTimeString('en-US', options);
+
clockElement.textContent = timeString;
+
};
+
displayTime();
+
setInterval(displayTime, 1000);
+
</script>
+19
src/components/section/Projects.astro
···
···
+
---
+
import Label from "../Label.astro"
+
let projects = Object.values(import.meta.glob('../projects/*.astro', { eager: true }));
+
+
projects = projects.sort((a, b) => {
+
const projectA = a.priority ?? Infinity;
+
const projectB = b.priority ?? Infinity;
+
+
return projectA - projectB
+
})
+
---
+
<div class="flex flex-col p-8 border-ctp-surface0 border w-full h-min relative">
+
<Label name="Projects"/>
+
<div class="flex flex-wrap gap-4">
+
{projects.map(Project => (
+
<Project.default />
+
))}
+
</div>
+
</div>
+35
src/components/section/Quote.astro
···
···
+
---
+
import Label from "../Label.astro"
+
---
+
<script>
+
interface Quote {
+
quote: string,
+
quotee: string,
+
}
+
const quoteElement = document.getElementById("quote") as HTMLElement
+
const quoteeElement = document.getElementById("quotee") as HTMLElement
+
+
const quotes: Quote[] = [
+
{ quote: "America is a nation that can be defined in a single word: asufutimaehaehfutbw", quotee: "Joseph Robinette Biden Jr."},
+
{ quote: "What if they- what if anything- what if a bomb drops on your head right now?", quotee: "Donald John Trump"},
+
{ quote: "This is a tough hurricane. One of the wettest we've ever seen from the standpoint of water.", quotee: "Donald John Trump"},
+
{ quote: "Heh heh heh, oh it's focused. I say it's err- I think it's- I- I- I haven't-- look.", quotee: "Joseph Robinette Biden Jr."},
+
{ quote: "the color orange is named after the fruit", quotee: "Elon Reeve Musk" },
+
{ quote: "I'm excited about electric school busses, I love electric school busses, I just love them..", quotee: "Kamala Devi Harris"},
+
{ quote: "How dare we speak marry christmas! How dare we!", quotee: "Kamala Devi Harris"},
+
{ quote: "One thing that really encourages me is AI. I love AI. I love ChatGPT. I love it. ChatGPT is frankly fantastic.", quotee: "Alexander Boris de Pfeffel Johnson" },
+
{ quote: "I am a fighter and not a quitter!", quotee: "Elizabeth Mary Truss" },
+
{ quote: "I call again for an immediate ceasefire in Gaza, the return of the sausages- the hostages..", quotee: "Keir Rodney Starmer" },
+
]
+
const quote: Quote = quotes[Math.floor(Math.random()*quotes.length)]
+
+
quoteElement.textContent = '"' + quote.quote + '"'
+
quoteeElement.textContent = "- " + quote.quotee
+
</script>
+
<span class="relative p-3 -mt-px border border-ctp-surface0 text-sm w-full">
+
<Label name="Quote"/>
+
<div class="flex flex-col text-sm gap-1">
+
<span id="quote" class="italic"></span>
+
<span id="quotee" class="w-full text-right"></span>
+
</div>
+
</span>
+5 -3
src/layouts/Layout.astro
···
---
import "../styles/global.css"
-
import Navbar from "../components/Navbar.astro"
import Footer from "../components/Footer.astro"
---
<!doctype html>
···
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
-
<title>banana@potassium.sh</title>
</head>
-
<body class="bg-ctp-crust pl-[12%] pr-[12%] text-ctp-text">
<main class="w-full min-h-full max-h-max bg-ctp-mantle relative">
<Navbar/>
<slot />
···
---
import "../styles/global.css"
+
import Navbar from "../components/navbar/Navbar.astro"
import Footer from "../components/Footer.astro"
---
<!doctype html>
···
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
+
<title>banana's page</title>
</head>
+
<body class="bg-ctp-crust pl-[2%] pr-[2%]
+
md:pl-[4%] md:pr-[4%] xl:pl-[8%] xl:pr-[8%]
+
text-ctp-text">
<main class="w-full min-h-full max-h-max bg-ctp-mantle relative">
<Navbar/>
<slot />
+14
src/layouts/Page.astro
···
···
+
---
+
import Layout from './Layout.astro';
+
import Profile from "../components/section/Profile.astro";
+
---
+
<Layout>
+
<section class="h-[50%]
+
flex flex-col lg:grid-cols-5 lg:grid
+
p-10 pt-15 md:p-12 md:pt-18 xl:p-24 xl:pt-36 gap-4 md:gap-8 xl:gap-16">
+
<Profile/>
+
<div class="md:col-span-3 gap-4 md:gap-8 xl:gap-12 flex flex-col">
+
<slot/>
+
</div>
+
</section>
+
</Layout>
-2
src/pages/404.astro
···
const urlPathname = Astro.url.pathname
const urlOrigin = Astro.url.origin
-
const userAgent = Astro.request.headers.get('User-Agent') || 'Unknown User-Agent';
const astroGenerator = Astro.generator
const currentISODate = new Date().toISOString();
---
···
<span>Date: {currentISODate}</span>
<span>Content-Type: text/html; charset=utf-8</span>
<span>X-Request-Path: {urlPathname}</span>
-
<span>X-User-Agent: {userAgent}</span>
<span>X-Request-Origin: {urlOrigin}</span>
</div>
</div>
···
const urlPathname = Astro.url.pathname
const urlOrigin = Astro.url.origin
const astroGenerator = Astro.generator
const currentISODate = new Date().toISOString();
---
···
<span>Date: {currentISODate}</span>
<span>Content-Type: text/html; charset=utf-8</span>
<span>X-Request-Path: {urlPathname}</span>
<span>X-Request-Origin: {urlOrigin}</span>
</div>
</div>
+9 -11
src/pages/index.astro
···
---
-
import Layout from '../layouts/Layout.astro';
-
import Profile from "../components/Profile.astro";
-
import Contributions from "../components/Contributions.astro"
---
-
<Layout>
-
<section class="h-[50%] grid-cols-5 grid p-32 pt-48 gap-12">
-
<Profile/>
-
<div class="col-span-3 gap-8 flex flex-col">
-
<Contributions/>
-
</div>
-
</section>
-
</Layout>
···
---
+
import Page from '../layouts/Page.astro';
+
import Contributions from "../components/section/Contributions.astro"
+
import Pinned from "../components/section/Pinned.astro"
+
import MusicPlayer from "../components/section/MusicPlayer.astro"
---
+
<Page>
+
<Contributions/>
+
<Pinned/>
+
<MusicPlayer/>
+
</Page>
+7
src/pages/projects.astro
···
···
+
---
+
import Page from '../layouts/Page.astro';
+
import ProjectsC from "../components/section/Projects.astro"
+
---
+
<Page>
+
<ProjectsC/>
+
</Page>
+4
src/styles/global.css
···
--ease-out: cubic-bezier(0,0.69,0.44,0.99);
--default-font-family: "JetBrains Mono", monospaced;
}
···
--ease-out: cubic-bezier(0,0.69,0.44,0.99);
--default-font-family: "JetBrains Mono", monospaced;
}
+
+
.squircle {
+
corner-shape: squircle
+
}