diff --git a/public/icon/mizuki.svg b/public/icon/mizuki.svg new file mode 100644 index 0000000..ee95759 --- /dev/null +++ b/public/icon/mizuki.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/icon/project-sekai.webp b/public/icon/project-sekai.webp new file mode 100644 index 0000000..75b4baa Binary files /dev/null and b/public/icon/project-sekai.webp differ diff --git a/public/icon/steam.svg b/public/icon/steam.svg new file mode 100644 index 0000000..affefa5 --- /dev/null +++ b/public/icon/steam.svg @@ -0,0 +1 @@ +Steam \ No newline at end of file diff --git a/public/icon/vrchat.svg b/public/icon/vrchat.svg new file mode 100644 index 0000000..0c4a3ea --- /dev/null +++ b/public/icon/vrchat.svg @@ -0,0 +1 @@ +VRChat \ No newline at end of file diff --git a/public/projects/epc-finale.avif b/public/projects/epc-finale.avif new file mode 100644 index 0000000..78462bd Binary files /dev/null and b/public/projects/epc-finale.avif differ diff --git a/src/components/About.astro b/src/components/About.astro index 7ed6612..f4f5a49 100644 --- a/src/components/About.astro +++ b/src/components/About.astro @@ -1,25 +1,27 @@ --- import { events } from "@/lib/events"; +import AboutRow from "@/components/AboutRow.astro"; +import DDay from "./DDay.astro"; const sortedEvents = [...events].sort((a, b) => b.date.localeCompare(a.date)); const years = [...new Set(sortedEvents.map((event) => event.date.slice(0, 4)))]; // biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. const eventsByYear = years.map((year) => ({ - year, - events: sortedEvents.filter((event) => event.date.startsWith(year)), + year, + events: sortedEvents.filter((event) => event.date.startsWith(year)), })); // biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. const getShortDate = (date: string) => { - const [, month, day] = date.split("-"); - return `${month}.${day}`; + const [, month, day] = date.split("-"); + return `${month}.${day}`; }; const currentYear = new Date().getFullYear(); // biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. const daysUntilYearEnd = Math.ceil( - (new Date(currentYear + 1, 0, 1).getTime() - Date.now()) / - (1000 * 60 * 60 * 24), + (new Date(currentYear + 1, 0, 1).getTime() - Date.now()) / + (1000 * 60 * 60 * 24), ); --- @@ -31,103 +33,101 @@ const daysUntilYearEnd = Math.ceil( >
-
-

- 저는 {years.length}년간 {events.length}개의 - 개성있는 조각들을 모아왔어요. -

+

+ 안녕하세요, 정보보안과 소프트웨어 엔지니어링을 주로하고 있는 + 암냥입니다. +

+
+ 생일 : +
+ + 연락하기 + + +
+

저는...

+
+
+ + + 선린인터넷고등학교 정보보안 동아리 + + Layer7 + -
- { - eventsByYear.map(({ year, events: yearEvents }) => ( -
-
-

- {year} -

- - {yearEvents.length}{" "} - {yearEvents.length === 1 - ? "EVENT" - : "EVENTS"} - -
-
- -
    - {yearEvents.map((event) => ( -
  1. - -
    -
    - - {event.category} - - {event.link && ( - - )} -
    - {event.link ? ( - - {event.description} - - ) : ( -

    - {event.description} -

    - )} -
    -
  2. - ))} -
- {year === currentYear.toString() && ( -
-
- - ✨ | D-{daysUntilYearEnd}동안 - 만들어나갈 멋진 것들을 - 기대해주세요. - -
-
- )} -
- )) - } -
+ + + 선린인터넷고등학교 정보보호과 + + + + ADOFAI.gg Development Team + + + + 화이트햇스쿨 3기 이수 + +
+
+
+ 등등... + +

을 이루어냈어요.

@@ -135,13 +135,13 @@ const daysUntilYearEnd = Math.ceil( class="relative order-first h-[min(150vw,720px)] min-h-[600px] overflow-hidden lg:sticky lg:top-0 lg:order-none lg:h-[calc(100svh-64px)] lg:min-h-0 lg:self-start" aria-label="활동 사진" > - 활동 사진 콜라주 + /> -->
diff --git a/src/components/ExperienceRow.astro b/src/components/AboutRow.astro similarity index 100% rename from src/components/ExperienceRow.astro rename to src/components/AboutRow.astro diff --git a/src/components/ContactSection.astro b/src/components/ContactSection.astro new file mode 100644 index 0000000..c86c338 --- /dev/null +++ b/src/components/ContactSection.astro @@ -0,0 +1,583 @@ +--- +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const groups = [ + { + id: "direct", + title: "연락처", + items: [ + { + name: "개인 이메일", + value: "me@imnya.ng", + href: "mailto:me@imnya.ng", + icon: "mail", + tone: "pink", + email: true, + }, + { + name: "dazzle.st 이메일", + value: "imnyang@dazzle.st", + href: "mailto:imnyang@dazzle.st", + icon: "mail", + tone: "green", + email: true, + }, + { + name: "Discord", + value: "@imnyang", + href: "https://discord.com/users/909353223901569035", + icon: "discord", + tone: "purple", + }, + ], + }, + { + id: "social", + title: "소셜", + items: [ + { + name: "X (Twitter)", + value: "@imnya_ng", + href: "https://twitter.com/imnya_ng", + icon: "x", + tone: "blue", + }, + { + name: "Instagram", + value: "@imnya.ng", + href: "https://instagram.com/imnya.ng", + icon: "instagram", + tone: "pink", + }, + { + name: "YouTube", + value: "@imnyang", + href: "https://youtube.com/@imnyang", + icon: "youtube", + tone: "red", + }, + ], + }, + { + id: "work", + title: "코드와 글", + items: [ + { + name: "GitHub", + value: "@imnyang", + href: "https://github.com/imnyang", + icon: "github", + tone: "neutral", + }, + { + name: "git.mizuki.guru", + value: "@imnyang", + href: "https://git.mizuki.guru/imnyang", + icon: "mizuki", + tone: "orange", + }, + { + name: "Blog", + value: "blog.imnya.ng", + href: "https://blog.imnya.ng", + icon: "blog", + tone: "green", + }, + ], + }, + { + id: "game", + title: "Game", + items: [ + { + name: "Steam", + value: "@imnyang", + href: "https://steamcommunity.com/id/imnyang/", + icon: "steam", + tone: "blue", + }, + { + name: "maimai", + value: "@imnya.ng", + href: "https://mai.sft.sh/imnyang", + icon: "maimai", + tone: "blue", + }, + { + name: "Project Sekai KR", + value: "7354623251148069648", + href: "#", + icon: "project-sekai", + tone: "blue", + }, + { + name: "Project Sekai JP", + value: "729310364018688008", + href: "#", + icon: "project-sekai", + tone: "blue", + }, + { + name: "VRChat", + value: "@imnyang", + href: "https://vrchat.com/home/user/usr_d53ac697-182d-40d0-9fd8-1a7442b0ebfd", + icon: "vrchat", + tone: "blue", + }, + ], + }, +]; + +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const imageIcons: Record = { + github: { src: "/icon/github.svg", monochrome: true }, + discord: { src: "/icon/discord.svg", monochrome: true }, + mizuki: { src: "/icon/mizuki.svg", monochrome: false }, + // Source: https://github.com/simple-icons/simple-icons/blob/develop/icons/steam.svg + steam: { src: "/icon/steam.svg", monochrome: true }, + maimai: { src: "/icon/maimai.webp", monochrome: false }, + vrchat: { src: "/icon/vrchat.svg", monochrome: true }, + // Source: https://pjsekai.sega.jp/assets/data/webp/icon/icon_app.png.webp + "project-sekai": { src: "/icon/project-sekai.webp", monochrome: false }, +}; + +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const paths: Record = { + mail: "M22 12a10 10 0 1 0-4 8 M16 8v5a3 3 0 0 0 6 0v-1 M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0", + x: "M4 3h4l12 18h-4L4 3Z M20 3l-7 8 M4 21l7-8", + instagram: + "M7 3h10a4 4 0 0 1 4 4v10a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4Z M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0 M17.5 6.5h.01", + youtube: + "M7 5h10a5 5 0 0 1 5 5v4a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5v-4a5 5 0 0 1 5-5Z M10 9l5 3-5 3V9Z", + blog: "M5 3h11l3 3v15H5V3Z M15 3v4h4 M9 11h6 M9 15h6", +}; +--- + +
+
+
+

Contact

+

+ 새로운 이야기와 재미있는 만남은 언제나 환영해요. +

+
+ +
+ { + groups.map((group) => ( +
+

+ {group.title} +

+ +
+ )) + } +
+ +

+

+
+
+ + + + diff --git a/src/components/DDay.astro b/src/components/DDay.astro index a7ed4a9..2ffac2a 100644 --- a/src/components/DDay.astro +++ b/src/components/DDay.astro @@ -1,27 +1,31 @@ --- export interface Props { - targetDate: string; - label: string; + targetDate: string; + label?: string; } const { targetDate, label } = Astro.props; --- -
- D-Day - {label} | {new Date(targetDate).toDateString()} +
+ {label && `${label} | `}{ + new Date(targetDate).toLocaleDateString() + } + D-Day
diff --git a/src/components/EPCProjects.astro b/src/components/EPCProjects.astro index a5b3ef4..b477917 100644 --- a/src/components/EPCProjects.astro +++ b/src/components/EPCProjects.astro @@ -24,7 +24,7 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"]; class="mt-6 max-w-[32rem] text-base leading-relaxed text-foreground/65" > 불과 얼음의 춤(A Dance of Fire and Ice)의 이펙트 제작과 - 플레이를 함께 다루는 대회, Effect Playing Contest. 2025와 + 플레이를
함께 다루는 대회, Effect Playing Contest. 2025와 Finale 시즌에 참여하며 방송부터 ARG, 개발까지 함께했어요.

    EPC Finale의 두 선수 경기 화면과 실시간 판정을 보여주는 방송 화면 Finale 결승 방송 02:19:26부터 재생 + >후기 영상 +
b.date.localeCompare(a.date)); +const years = [...new Set(sortedEvents.map((event) => event.date.slice(0, 4)))]; +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const eventsByYear = years.map((year) => ({ + year, + events: sortedEvents.filter((event) => event.date.startsWith(year)), +})); + +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const getShortDate = (date: string) => { + const [, month, day] = date.split("-"); + return `${month}.${day}`; +}; + +const currentYear = new Date().getFullYear(); +// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below. +const daysUntilYearEnd = Math.ceil( + (new Date(currentYear + 1, 0, 1).getTime() - Date.now()) / + (1000 * 60 * 60 * 24), +); +--- + +
+
+
+
+

+ 저는 {years.length}년간 {events.length}개의 개성있는 + 조각들을 모아왔어요. +

+ +
+ { + eventsByYear.map(({ year, events: yearEvents }) => ( +
+
+

+ {year} +

+ + {yearEvents.length}{" "} + {yearEvents.length === 1 + ? "EVENT" + : "EVENTS"} + +
+
+ +
    + {yearEvents.map((event) => ( +
  1. + +
    +
    + + {event.category} + + {event.link && ( + + )} +
    + {event.link ? ( + + {event.description} + + ) : ( +

    + {event.description} +

    + )} +
    +
  2. + ))} +
+ {year === currentYear.toString() && ( +
+
+ + ✨ | D-{daysUntilYearEnd}동안 + 만들어나갈 멋진 것들을 기대해주세요. + +
+
+ )} +
+ )) + } +
+
+
+ +
+ 활동 사진 콜라주 +
+
+
diff --git a/src/components/Hero.astro b/src/components/Hero.astro index bffe7ce..0acf597 100644 --- a/src/components/Hero.astro +++ b/src/components/Hero.astro @@ -22,11 +22,11 @@ 현실로 만들어나가는{"로 만들어나가는 "} + - 남현석입니다. diff --git a/src/components/Projects.astro b/src/components/Projects.astro deleted file mode 100644 index 2ff7c9a..0000000 --- a/src/components/Projects.astro +++ /dev/null @@ -1,97 +0,0 @@ ---- -const projects = [ - { - name: 'EPC : Finale Broadcast Manager & Developer', - url: 'https://www.youtube.com/playlist?list=PLZeYZotn5_IPFvDUWdEUu9xOBwOkdZ0F0', - desc: 'ADOFAI is web-scale', - detail: '달성이 주관하고 ADOFAI.gg가 공동 주최하는 Effect Playing Contest : Finale의 방송 화면 및 ARG를 담당하였습니다.', - tags: ['React', 'ElysiaJS'], - }, - { - name: 'EPC 2025 Broadcast Manager', - url: 'https://www.youtube.com/playlist?list=PLZeYZotn5_IOJDek6e35NKzUtJm09yxZD', - desc: 'ADOFAI is web-scale', - detail: '달성이 주관하고 ADOFAI.gg가 공동 주최하는 Effect Playing Contest 2025 방송 화면의 대부분의 기능을 개발하였습니다.', - tags: ['React', 'ElysiaJS'], - }, - { - name: 'newsletter', - url: 'https://github.com/imnyang/newsletter', - desc: 'For Memos', - detail: '그저 이메일이 오면 Discord 웹훅으로 포워딩합니다.', - tags: ['Rust', 'IMAP'] - }, - { - name: 'memos-rss', - url: 'https://github.com/imnyang/memos-rss', - desc: 'For Memos', - detail: 'Discord 포럼 채널에 RSS 피드에 올라온 내용을 포워딩합니다.', - tags: ['Rust', 'Discord Bot', 'RSS'] - }, - { - name: 'today.isangjeong', - url: 'https://instagram.com/today.isangjeong', - desc: 'Instagram Bot', - detail: '매일 학교의 급식 메뉴를 자동으로 업로드하는 인스타그램 봇입니다.', - tags: ['TypeScript', 'Instagram', '@napi-rs/canvas'] - } -]; ---- - -
-
- {projects.map((project, idx) => ( -
= 3 ? 'hidden' : ''}`}> -
-
- {project.url ? ( - - {project.name} - - - ) : ( - {project.name} - )} - {project.desc} -
-
-

{project.detail}

-
-
-
- {project.tags.map((tag) => ( - {tag} - ))} -
-
- ))} -
- {projects.length > 3 && ( - - )} -
- - diff --git a/src/components/Timeline.astro b/src/components/Timeline.astro deleted file mode 100644 index ed22b97..0000000 --- a/src/components/Timeline.astro +++ /dev/null @@ -1,135 +0,0 @@ ---- -import { events } from "@/lib/events"; - -const years = [ - ...new Set(events.map((e) => new Date(e.date).getFullYear())), -].sort((a, b) => b - a); - -const eventsByYear: Record = {}; -for (const year of years) { - eventsByYear[year] = events.filter( - (e) => new Date(e.date).getFullYear() === year, - ); -} - -const currentYear = new Date().getFullYear(); -const daysUntilYearEnd = Math.ceil( - (new Date(currentYear + 1, 0, 1).getTime() - new Date().getTime()) / - (1000 * 60 * 60 * 24), -); ---- - -
-
-
- -
- { - years.map((year, i) => ( - - )) - } -
- - -
- { - years.map((year, i) => ( -
- {eventsByYear[year].map((event) => ( -
-
- - {new Date(event.date).toLocaleDateString("en-US", { - month: "short", - day: "2-digit", - })} - - - ㆍ{event.category} - -
- {event.link ? ( - - {event.description}{" "} - - - - - - ) : ( - {event.description} - )} -
- ))} - {year === currentYear && ( -
-
- - ✨ | D-{daysUntilYearEnd}동안 만들어나갈 멋진 것들을 - 기대해주세요. - -
-
- )} -
- )) - } -
-
-
-
- - diff --git a/src/pages/index.astro b/src/pages/index.astro index 50b0680..06a5d54 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,6 +1,8 @@ --- import About from "@/components/About.astro"; +import ContactSection from "@/components/ContactSection.astro"; import EPCProjects from "@/components/EPCProjects.astro"; +import Experience from "@/components/Experience.astro"; import Header from "@/components/Header.astro"; import Hero from "@/components/Hero.astro"; import "@/globals.css"; @@ -39,6 +41,8 @@ import "@/globals.css"; + +