feat(ui/ux): add timeline, hero, and NeoFetch components + data/hooks

- Add timeline route helper
  - src/app/timeline/route.ts
  - simple helper to navigate to #timeline

- Add NeoFetch component (client)
  - src/components/NeoFetch.tsx
  - Displays avatar iframe, uptime calculation, experience count, WakaTime stats, terminal/ip, locale and colour palette
  - Uses custom hooks useIpData and useWakaTimeData, and events data

- Add Top (hero) component (client)
  - src/components/Top.tsx
  - Full-screen hero with randomized background, parallax on mouse, device orientation & motion handlers, requestPermission trigger on image click
  - Includes Sidebar import and optimized Image usage

- Add Timeline UI component (client)
  - src/components/timeline.tsx
  - Year selector + filtered event list with links and icons
  - Handles initial selection and rendering grouped by year

- Add reusable Timeline primitives (client)
  - src/components/ui/timeline.tsx
  - Timeline context and composable parts: Timeline, TimelineItem, Indicator, Separator, Date, Title, Content, Header
  - Orientation support and controlled/uncontrolled API

- Add data & hooks
  - src/lib/events.ts
    - Seeded events array (education/awards/conference entries) used by timeline and NeoFetch
  - src/hooks/use-ip-data.ts
    - Fetches terminal/ip info from https://api.imnya.ng/ip
  - src/hooks/use-wakatime-data.ts
    - Fetches WakaTime summary from https://api.imnya.ng/wakatime

Notes:
- All new components are client-side ("use client")
- Adds device motion/orientation listeners with cleanup
- Provides basic error handling for network hooks
- Improves homepage/UX with interactive hero and timeline data visualization
This commit is contained in:
암냥 2025-10-22 22:33:40 +09:00
commit 96a43a9a3c
34 changed files with 771 additions and 63 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

View file

@ -1,12 +1,12 @@
@import url("https://cdn.jsdelivr.net/gh/wanteddev/wanted-sans@v1.0.3/packages/wanted-sans/fonts/webfonts/variable/split/WantedSansVariable.min.css");
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@font-face {
font-family: 'NType82Headline';
src: url('https://f.imnya.ng/font/NType82-Headline.woff2') format('woff2');
font-family: "NType82Headline";
src: url("https://f.imnya.ng/font/NType82-Headline.woff2") format("woff2");
}
.font-ntype {
@ -14,43 +14,6 @@
}
:root {
--background: hsl(340 40% 98%);
--foreground: hsl(315 21% 8%);
--card: hsl(340 40% 98%);
--card-foreground: hsl(315 21% 8%);
--popover: hsl(340 40% 98%);
--popover-foreground: hsl(315 21% 8%);
--primary: hsl(340 25% 15%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(340 25% 95%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(340 20% 95%);
--muted-foreground: hsl(340 10% 60%);
--accent: hsl(340 25% 94%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(340 25% 90%);
--input: hsl(340 25% 90%);
--ring: hsl(315 21% 8%);
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
--radius: 0.6rem;
--sidebar-background: hsl(340 25% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(340 20% 95%);
--sidebar-accent-foreground: hsl(240 5.9% 10%);
--sidebar-border: hsl(340 20% 90%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--sidebar: hsl(0 0% 98%);
}
.dark {
--background: hsl(315 21% 8%);
--foreground: hsl(0 0% 98%);
--card: hsl(315 21% 8%);
@ -151,5 +114,9 @@
}
body {
@apply bg-background text-foreground;
font-family: "Wanted Sans Variable", "Wanted Sans", -apple-system,
BlinkMacSystemFont, system-ui, "Segoe UI", "Apple SD Gothic Neo",
"Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", sans-serif;
}
}

View file

@ -1,6 +1,5 @@
import type { Metadata } from "next";
import "./globals.css";
import Sidebar from "@/components/sidebar";
export const metadata: Metadata = {
title: "남현석 | :two_hearts: imnya.ng",
@ -15,7 +14,6 @@ export default function RootLayout({
return (
<html lang="ko">
<body className="antialiased">
<Sidebar />
{children}
</body>
</html>

View file

@ -1,18 +1,26 @@
import NeoFetch from "@/components/NeoFetch";
import TimelineComponent from "@/components/timeline";
import Top from "@/components/Top";
export default function Home() {
return (
<main>
<div id="top" className="h-screen flex">
<div
className="absolute top-0 right-0 min-w-[calc(100vw-100px)] min-h-[calc(100vh-56px)] flex flex-col rounded-bl-[160px]"
style={{
background: "linear-gradient(110deg, #FF8989 21.29%, rgba(85, 0, 255, 0.2) 100%)",
}}
></div>
</div>
<div id="about" className="h-screen flex p-4">
<h1 className="text-2xl">About</h1>
<p> .</p>
</div>
<main className="min-h-screen">
<Top />
<section id="terminal" className="h-screen">
<NeoFetch />
<div className="px-12">
<h1 className="text-2xl font-bold mb-4 w-full">💕 About</h1>
<p> , .</p>
<p> 4 Python으로 , TypeScript를 .</p>
<p> .</p>
<br />
<p> .</p>
<a href="https://www.youtube.com/watch?v=XTdqpdTMZbw&list=PLZeYZotn5_IOJDek6e35NKzUtJm09yxZD">Effect Playing Contest 2025 Broadcast Develop</a><br />
<a href="https://github.com/imnyang/today.isangjeong">today.isangjeong</a>
</div>
<TimelineComponent />
</section>
</main>
);
}

View file

@ -0,0 +1,3 @@
export const timeline = () => {
window.location.hash = '#timeline';
};