Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8c65d09
ui(avatar): support square shape variants with rounded corners
wistant Jun 2, 2026
ccfe372
ui(profile): redesign ProfileHeader into premium X-style profile layout
wistant Jun 2, 2026
8f0a998
refactor(profile): simplify Overview component to render occupations …
wistant Jun 2, 2026
af08725
ui(cover): remove central logo and integrate minimal cyber-premium st…
wistant Jun 2, 2026
c3bb971
ui(profile): remove bio description and use premium square contact bu…
wistant Jun 2, 2026
cd41d2d
ui(profile): integrate flip sentences into header metadata box
wistant Jun 2, 2026
463cb7b
ui(social): add reddit and bluesky with local svg icons
wistant Jun 2, 2026
eaa778d
fix(github): improve contribution graph colors and data fetching robu…
wistant Jun 2, 2026
ef0aad6
ui(experiences): add work experiences for unity link and devia techno…
wistant Jun 2, 2026
724efe0
ui(experiences): link local image logos for unity link and devia tech…
wistant Jun 2, 2026
291f213
ui(experiences): add local company logo image files
wistant Jun 2, 2026
4ab0308
fix(profile): nest flip sentences children correctly to resolve eslin…
wistant Jun 2, 2026
c35adf8
style(app): format index.tsx
wistant Jun 2, 2026
c16fb8b
style(app): format profile-cover.tsx
wistant Jun 2, 2026
87916fa
style(app): format profile-header.tsx
wistant Jun 2, 2026
ad94e49
style(components): format avatar-lights.tsx
wistant Jun 2, 2026
24dacb4
style(style): format github-contributions.ts
wistant Jun 2, 2026
77ff447
add works and projects images and assets
wistant Jun 2, 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
Binary file added public/experiences/devia.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/experiences/unity-link.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/2.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/3.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/5.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/8.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/gallery/9.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/preview-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 0 additions & 73 deletions src/app/(app)/components/overview/index.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
import { USER } from "@/data/portfolio/user"
import { urlToName } from "@/utils/url"
import {
LinkIcon,
MapPinIcon,
MarsIcon,
NonBinaryIcon,
VenusIcon,
} from "lucide-react"

import type { User } from "@/types/user"

import { Panel, PanelContent } from "../panel"
import { CurrentLocalTimeItem } from "./current-local-time-item"
import { EmailItem } from "./email-item"
import {
IntroItem,
IntroItemContent,
IntroItemIcon,
IntroItemLink,
} from "./intro-item"
import { JobItem } from "./job-item"
import { PhoneItem } from "./phone-item"

export function Overview() {
return (
Expand All @@ -37,61 +18,7 @@ export function Overview() {
/>
)
})}

<div className="grid gap-x-4 gap-y-2.5 sm:grid-cols-2">
<IntroItem>
<IntroItemIcon>
<MapPinIcon />
</IntroItemIcon>
<IntroItemContent>
<IntroItemLink
href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(USER.address)}`}
aria-label={`Location: ${USER.address}`}
>
{USER.address}
</IntroItemLink>
</IntroItemContent>
</IntroItem>

<CurrentLocalTimeItem timeZone={USER.timeZone} />

<PhoneItem phoneNumber={USER.phoneNumber} />

<EmailItem email={USER.email} />

<IntroItem>
<IntroItemIcon>
<LinkIcon />
</IntroItemIcon>
<IntroItemContent>
<IntroItemLink
href={USER.website}
aria-label={`Personal website: ${urlToName(USER.website)}`}
>
{urlToName(USER.website)}
</IntroItemLink>
</IntroItemContent>
</IntroItem>

<IntroItem>
<IntroItemIcon>{getGenderIcon(USER.gender)}</IntroItemIcon>
<IntroItemContent aria-label={`Pronouns: ${USER.pronouns}`}>
{USER.pronouns}
</IntroItemContent>
</IntroItem>
</div>
</PanelContent>
</Panel>
)
}

function getGenderIcon(gender: User["gender"]) {
switch (gender) {
case "male":
return <MarsIcon />
case "female":
return <VenusIcon />
case "non-binary":
return <NonBinaryIcon />
}
}
26 changes: 13 additions & 13 deletions src/app/(app)/components/profile-cover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { useTheme } from "next-themes"

import { cn } from "@/lib/utils"
import { DotGridSpotlight } from "@/components/dot-grid-spotlight"
import { Magnet } from "@/components/react-bits/magnet"
import { WistantMark } from "@/components/wistant-mark"

const DOT_COLOR = {
light: {
Expand All @@ -31,24 +29,26 @@ export function ProfileCover() {
className={cn(
"relative flex aspect-2.5/1 items-center justify-center border-x border-line select-none sm:aspect-3.5/1",
"screen-line-top screen-line-bottom before:-top-px after:-bottom-px",
"bg-black/0.75 dark:bg-white/1"
"overflow-hidden bg-black/0.75 dark:bg-white/1"
)}
>
<DotGridSpotlight
dotColor={DOT_COLOR[theme]?.default}
activeDotColor={DOT_COLOR[theme]?.active}
/>

<Magnet
containerRef={containerRef}
magnetStrength={6}
wrapperClassName="pointer-events-none"
>
<WistantMark
id="js-cover-mark"
className="h-12 w-24 min-[25rem]:h-14 min-[25rem]:w-28 sm:h-16 sm:w-32"
/>
</Magnet>
{/* Creative Cyber-Premium Badges in the corners */}
<div className="pointer-events-none absolute top-3 left-4 flex items-center gap-2 font-mono text-[9px] tracking-wider text-muted-foreground/60 select-none sm:text-[10px]">
<span className="relative flex size-1.5">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex size-1.5 rounded-full bg-emerald-500"></span>
</span>
<span>SYS_OP // ACTIVE_NODE_0x7E4</span>
</div>

<div className="pointer-events-none absolute right-4 bottom-3 font-mono text-[9px] tracking-widest text-muted-foreground/40 uppercase select-none sm:text-[10px]">
wistant.me // dev_env_v4.2
</div>
</div>
)
}
150 changes: 121 additions & 29 deletions src/app/(app)/components/profile-header.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,150 @@
"use client"

import { USER } from "@/data/portfolio/user"
import { urlToName } from "@/utils/url"
import {
LinkIcon,
MapPinIcon,
MarsIcon,
NonBinaryIcon,
VenusIcon,
} from "lucide-react"

import type { User } from "@/types/user"
import { AvatarLights } from "@/components/avatar-lights"
import { Button } from "@/components/base/ui/button"

import { AvatarLightsToggle } from "./avatar-lights-toggle"
import { FlipSentences } from "./flip-sentences"
// Import interactive metadata items from Overview
import { CurrentLocalTimeItem } from "./overview/current-local-time-item"
import { EmailItem } from "./overview/email-item"
import { PhoneItem } from "./overview/phone-item"
import { PronounceMyName } from "./pronounce-my-name"
import { VerifiedIcon } from "./verified-icon"

function getGenderIcon(gender: User["gender"]) {
switch (gender) {
case "male":
return <MarsIcon className="size-4 shrink-0 text-muted-foreground/80" />
case "female":
return <VenusIcon className="size-4 shrink-0 text-muted-foreground/80" />
case "non-binary":
return (
<NonBinaryIcon className="size-4 shrink-0 text-muted-foreground/80" />
)
}
}

export function ProfileHeader() {
return (
<div className="screen-line-bottom flex border-x border-line">
<div className="shrink-0 border-r border-line">
<AvatarLightsToggle className="group/avatar-lights-toggle mx-0.5 my-0.75 flex outline-none">
<div className="relative flex flex-col gap-4 border-x border-line p-4 select-none sm:flex-row sm:gap-6 sm:p-6">
{/* Left side: Avatar Profile */}
<div className="size-30 shrink-0 rounded-2xl border border-line bg-background p-1 min-[24rem]:size-32 sm:size-40">
<AvatarLightsToggle className="group/avatar-lights-toggle flex h-full w-full outline-none">
<AvatarLights
className="ring-border ring-offset-background group-focus-visible/avatar-lights-toggle:ring-1 group-focus-visible/avatar-lights-toggle:ring-offset-2"
className="h-full w-full ring-border ring-offset-background group-focus-visible/avatar-lights-toggle:ring-1 group-focus-visible/avatar-lights-toggle:ring-offset-2"
variants={USER.avatarVariants}
shape="square"
/>
</AvatarLightsToggle>
</div>

<div className="flex flex-1 flex-col">
<div className="flex grow items-end pb-1 pl-4">
<div
className="line-clamp-1 font-mono text-xs text-zinc-300 select-none max-sm:hidden dark:text-zinc-800"
aria-hidden
>
{"text-3xl "}
<span className="inline dark:hidden">API</span>
<span className="hidden dark:inline">UI</span>
{" font-medium"}
{/* Right side: details */}
<div className="flex min-w-0 flex-1 flex-col justify-center">
{/* Row: Name, Gold Badge, Handle, and Contact Actions */}
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-1.5">
<h1 className="truncate text-xl font-bold tracking-tight text-foreground sm:text-3xl">
{USER.displayName}
</h1>
<VerifiedIcon
className="size-5 shrink-0 text-[#e1b12c] select-none sm:size-5.5" // PREMIUM GOLD BADGE
aria-hidden
/>
</div>
<div className="mt-0.5 font-mono text-xs text-muted-foreground sm:text-sm">
@{USER.username}
</div>
</div>
</div>

<div className="border-t border-line">
<div className="flex items-center gap-2 pl-4">
<h1 className="-translate-y-px text-3xl font-semibold tracking-tight">
{USER.displayName}
</h1>

<VerifiedIcon
className="size-4.5 text-info select-none"
aria-hidden
/>

{/* Contact Button & Name Pronunciation (Hidden on mobile) */}
<div className="hidden shrink-0 items-center gap-2 sm:flex">
{USER.namePronunciationUrl && (
<PronounceMyName
namePronunciationUrl={USER.namePronunciationUrl}
/>
)}

<Button
variant="outline"
size="sm"
className="h-8.5 rounded-lg border-line font-mono text-xs font-medium tracking-wider text-muted-foreground uppercase hover:bg-muted hover:text-foreground dark:border-input dark:bg-input/30"
>
<a href={`mailto:${atob(USER.email)}`}>contact</a>
</Button>
</div>
</div>

<FlipSentences className="h-12.5 border-t border-line py-1 pl-4 sm:h-9">
{USER.flipSentences}
</FlipSentences>
{/* Metadata List */}
<div className="mt-3 flex flex-col gap-1.5 text-sm text-muted-foreground sm:flex-row sm:flex-wrap sm:gap-x-4 sm:gap-y-2 [&_.gap-4]:gap-1.5 [&_.size-6]:size-4 [&_.size-6]:border-none [&_.size-6]:bg-transparent [&_.size-6]:shadow-none [&_.size-6]:ring-0 [&_.size-6]:ring-offset-0 [&_a]:text-sm [&_a]:text-foreground [&_a:hover]:underline [&_p]:text-sm [&_svg]:text-muted-foreground/80">
{/* Location */}
<div className="flex items-center gap-1.5">
<div className="flex size-4 shrink-0 items-center justify-center">
<MapPinIcon className="size-4 text-muted-foreground/80" />
</div>
<a
href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(USER.address)}`}
target="_blank"
rel="noopener noreferrer"
className="truncate text-sm hover:underline"
>
{USER.address}
</a>
</div>

{/* Website */}
<div className="flex items-center gap-1.5 text-[#e1b12c] [&_a]:text-[#e1b12c]!">
<div className="flex size-4 shrink-0 items-center justify-center">
<LinkIcon className="size-4 text-[#e1b12c]!" />
</div>
<a
href={USER.website}
target="_blank"
rel="noopener noreferrer"
className="truncate text-sm hover:underline"
>
{urlToName(USER.website)}
</a>
</div>

{/* Pronouns */}
<div className="flex items-center gap-1.5">
<div className="flex size-4 shrink-0 items-center justify-center">
{getGenderIcon(USER.gender)}
</div>
<span className="text-sm">{USER.pronouns}</span>
</div>

{/* Current Local Time */}
<CurrentLocalTimeItem timeZone={USER.timeZone} />

{/* Phone */}
<PhoneItem phoneNumber={USER.phoneNumber} />

{/* Email */}
<EmailItem email={USER.email} />

{/* Scrolling posts/sentences (FlipSentences) placed right after the email inside the metadata list box */}
<div className="flex items-center gap-1.5">
<div className="flex size-4 shrink-0 items-center justify-center">
<span className="flex size-1.5 animate-pulse rounded-full bg-emerald-500" />
</div>
<FlipSentences className="text-sm">
{USER.flipSentences}
</FlipSentences>
</div>
</div>
</div>
</div>
Expand Down
7 changes: 6 additions & 1 deletion src/app/(app)/components/social-links/social-link-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { UTM_PARAMS } from "@/config/site"
import { cn } from "@/lib/utils"

export function SocialLinkItem({ icon, title, href }: SocialLink) {
const isInvertible = title === "X" || title === "GitHub"

return (
<div
className={cn(
Expand All @@ -15,7 +17,10 @@ export function SocialLinkItem({ icon, title, href }: SocialLink) {
>
<div className="relative size-8 shrink-0 [--image-radius:var(--radius-lg)]">
<Image
className="rounded-(--image-radius) select-none"
className={cn(
"rounded-(--image-radius) select-none",
isInvertible && "dark:invert"
)}
src={icon}
alt={title}
width={32}
Expand Down
4 changes: 0 additions & 4 deletions src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { Blog } from "./components/blog"
import { Experiences } from "./components/experiences"
import { GitHubContributions } from "./components/github-contributions"
import { Insights } from "./components/insights"
import { Overview } from "./components/overview"
import { ProfileCover } from "./components/profile-cover"
import { ProfileHeader } from "./components/profile-header"
import { Projects } from "./components/projects"
import { SocialLinks } from "./components/social-links"
Expand All @@ -32,10 +30,8 @@ export default function HomePage() {
/>*/}

<div className="mx-auto md:max-w-3xl *:[[id]]:scroll-mt-22">
<ProfileCover />
<ProfileHeader />

<Overview />
<SocialLinks />
<Separator />

Expand Down
Loading
Loading