removed
This commit is contained in:
parent
5be9625af3
commit
4fd76bfbfd
6 changed files with 0 additions and 383 deletions
|
|
@ -3,7 +3,6 @@ import { ThemeProvider } from "@/components/theme-provider"
|
||||||
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import "./scrollbar.css";
|
import "./scrollbar.css";
|
||||||
import SUPERCOMMAND from "@/components/SUPERCOMMAND";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "남현석 | :two_hearts: imnya.ng",
|
title: "남현석 | :two_hearts: imnya.ng",
|
||||||
|
|
@ -26,7 +25,6 @@ export default function RootLayout({
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SUPERCOMMAND />
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import NeoFetch from "@/components/NeoFetch";
|
|
||||||
import Projects from "@/components/Projects";
|
import Projects from "@/components/Projects";
|
||||||
import TimelineComponent from "@/components/timeline";
|
import TimelineComponent from "@/components/timeline";
|
||||||
import Top from "@/components/Top";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import DraggableWindow from "@/components/DraggableWindow";
|
import DraggableWindow from "@/components/DraggableWindow";
|
||||||
import ReadmeWindow from "@/components/ReadmeWindow";
|
import ReadmeWindow from "@/components/ReadmeWindow";
|
||||||
|
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { events } from "@/lib/events";
|
|
||||||
import { useIpData } from "../hooks/use-ip-data";
|
|
||||||
import { useWakaTimeData } from "../hooks/use-wakatime-data";
|
|
||||||
|
|
||||||
export default function NeoFetch() {
|
|
||||||
const ipData = useIpData();
|
|
||||||
const wakaTimeData = useWakaTimeData();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full bg-[#191017] text-[#fcf8f9] font-[Google Sans Code] rounded-b-4xl">
|
|
||||||
<div className="flex flex-col md:flex-row">
|
|
||||||
<iframe
|
|
||||||
src="/ascii-art.html"
|
|
||||||
className="border-0 min-w-[430px] min-h-[430px] scale-75"
|
|
||||||
></iframe>
|
|
||||||
<div className="px-12 md:py-12 text-lg">
|
|
||||||
<a href="mailto:contact@imnya.ng" className="text-[#FFD7D7]">
|
|
||||||
imnyang<span className="text-[#fcf8f9]">@</span><a href="https://adofai.gg">adofai.gg</a>
|
|
||||||
</a>
|
|
||||||
<p>----------</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">Uptime</span>:{" "}
|
|
||||||
{(() => {
|
|
||||||
const startDate = new Date("2010-11-08T03:00:00+09:00");
|
|
||||||
const now = new Date();
|
|
||||||
let diff = now.getTime() - startDate.getTime();
|
|
||||||
|
|
||||||
const years = Math.floor(diff / (1000 * 60 * 60 * 24 * 365));
|
|
||||||
diff %= 1000 * 60 * 60 * 24 * 365;
|
|
||||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
||||||
diff %= 1000 * 60 * 60 * 24;
|
|
||||||
const hours = Math.floor(diff / (1000 * 60 * 60));
|
|
||||||
diff %= 1000 * 60 * 60;
|
|
||||||
const mins = Math.floor(diff / (1000 * 60));
|
|
||||||
|
|
||||||
return `${years} years, ${days} days, ${hours} hours, ${mins} mins`;
|
|
||||||
})()}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">Experience</span>:{" "}
|
|
||||||
{Object.values(events).flat().length}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">WakaTime</span>:{" "}
|
|
||||||
{wakaTimeData?.data?.total_seconds_including_other_language
|
|
||||||
? (() => {
|
|
||||||
const seconds = wakaTimeData.data.total_seconds_including_other_language;
|
|
||||||
const days = Math.floor(seconds / 86400);
|
|
||||||
const hours = Math.floor((seconds % 86400) / 3600);
|
|
||||||
const minutes = Math.floor((seconds % 3600) / 60);
|
|
||||||
return `${days} days, ${hours} hours, ${minutes} minutes, ${Math.round(
|
|
||||||
seconds % 60
|
|
||||||
)} seconds`;
|
|
||||||
})()
|
|
||||||
: "N/A"}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">Most used Language</span>:{" "}
|
|
||||||
{wakaTimeData?.data?.languages[0]?.name || "N/A"}{" "}
|
|
||||||
{wakaTimeData?.data?.languages[0]?.total_seconds
|
|
||||||
? (() => {
|
|
||||||
const seconds = wakaTimeData.data.languages[0].total_seconds;
|
|
||||||
const days = Math.floor(seconds / 86400);
|
|
||||||
const hours = Math.floor((seconds % 86400) / 3600);
|
|
||||||
const minutes = Math.floor((seconds % 3600) / 60);
|
|
||||||
return `(${days} days, ${hours} hours, ${minutes} minutes, ${Math.round(
|
|
||||||
seconds % 60
|
|
||||||
)} seconds)`;
|
|
||||||
})()
|
|
||||||
: "N/A"}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">Terminal</span>: {ipData}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span className="text-[#FFD7D7]">Locale</span>: ko_KR.UTF-8
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<div id="color" className="grid grid-cols-8 w-fit pb-8">
|
|
||||||
<div className="bg-[#191017] min-w-8"> </div>
|
|
||||||
<div className="bg-[#f38ba8] min-w-8"></div>
|
|
||||||
<div className="bg-[#a6e3a1] min-w-8"></div>
|
|
||||||
<div className="bg-[#f9e2af] min-w-8"></div>
|
|
||||||
<div className="bg-[#89b4fa] min-w-8"></div>
|
|
||||||
<div className="bg-[#f5c2e7] min-w-8"></div>
|
|
||||||
<div className="bg-[#94e2d5] min-w-8"></div>
|
|
||||||
<div className="bg-muted-foreground min-w-8"></div>
|
|
||||||
|
|
||||||
<div className="bg-[#45475a] min-w-8"> </div>
|
|
||||||
<div className="bg-[#f0c0cd] min-w-8"></div>
|
|
||||||
<div className="bg-[#c3e9bf] min-w-8"></div>
|
|
||||||
<div className="bg-[#f0e0bf] min-w-8"></div>
|
|
||||||
<div className="bg-[#c3d9fd] min-w-8"></div>
|
|
||||||
<div className="bg-[#f8d2ee] min-w-8"></div>
|
|
||||||
<div className="bg-[#b1faee] min-w-8"></div>
|
|
||||||
<div className="bg-[#fcf8f9] min-w-8"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React from "react";
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog"
|
|
||||||
|
|
||||||
export default function SUPERCOMMAND() {
|
|
||||||
const [keySequence, setKeySequence] = React.useState("");
|
|
||||||
const [showDialog, setShowDialog] = React.useState(false);
|
|
||||||
const targetSequence = "MAGICALWORLD";
|
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
// 영문자만 처리
|
|
||||||
if (event.key.length === 1 && /[a-zA-Z]/.test(event.key)) {
|
|
||||||
console.log(keySequence);
|
|
||||||
console.log(targetSequence);
|
|
||||||
console.log("Key pressed:", event.key);
|
|
||||||
setKeySequence(prev => {
|
|
||||||
const newSequence = prev + event.key;
|
|
||||||
console.log("New sequence:", newSequence);
|
|
||||||
|
|
||||||
// 목표 시퀀스와 정확히 일치하는지 확인 (대문자만)
|
|
||||||
if (newSequence === targetSequence) {
|
|
||||||
console.log("Sequence matched!");
|
|
||||||
setShowDialog(true);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 목표 시퀀스의 시작 부분과 일치하는지 확인 (대문자만)
|
|
||||||
if (targetSequence.startsWith(newSequence)) {
|
|
||||||
return newSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일치하지 않으면 현재 키부터 다시 시작
|
|
||||||
const restartSequence = event.key;
|
|
||||||
if (targetSequence.startsWith(restartSequence)) {
|
|
||||||
return restartSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 완전히 초기화
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 특수키나 숫자가 입력되면 시퀀스 초기화
|
|
||||||
setKeySequence("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={showDialog} onOpenChange={setShowDialog}>
|
|
||||||
<DialogContent className="flex flex-col gap-0 p-0 sm:max-h-[min(640px,80vh)] sm:max-w-lg [&>button:last-child]:top-3.5">
|
|
||||||
<DialogHeader className="contents space-y-0 text-left">
|
|
||||||
<DialogTitle className="border-b px-6 py-4 text-base">
|
|
||||||
???
|
|
||||||
</DialogTitle>
|
|
||||||
<div className="overflow-y-auto">
|
|
||||||
<DialogDescription asChild>
|
|
||||||
<div className="px-6 py-4">
|
|
||||||
<div className="[&_strong]:text-foreground space-y-4 [&_strong]:font-semibold pb-32">
|
|
||||||
<p style={{ whiteSpace: 'pre-line' }}>{`지금바로 윤회!`}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogDescription>
|
|
||||||
<DialogFooter className="px-6 py-6 sm:justify-start fixed bottom-0 left-0 w-full bg-background/80 backdrop-blur-sm">
|
|
||||||
<Button type="button" onClick={() => {
|
|
||||||
const existingCookie = document.cookie.split('; ').find(row => row.startsWith('MagicalGirl='));
|
|
||||||
if (existingCookie) {
|
|
||||||
document.cookie = "MagicalGirl=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC";
|
|
||||||
window.location.reload();
|
|
||||||
} else {
|
|
||||||
document.cookie = "MagicalGirl=true; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT";
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
setShowDialog(false);
|
|
||||||
}}>Okay</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</div>
|
|
||||||
</DialogHeader>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
"use client";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import Sidebar from "@/components/sidebar";
|
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
|
||||||
|
|
||||||
export default function Top() {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [randomBg, setRandomBg] = useState(1);
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
const [clickCount, setClickCount] = useState(0);
|
|
||||||
const clickTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
||||||
const [isMagicalGirlEnabled, setIsMagicalGirlEnabled] = useState(false);
|
|
||||||
const REQUIRED_CLICKS = 6;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setRandomBg(Math.floor(Math.random() * 14) + 1);
|
|
||||||
setIsMagicalGirlEnabled(document.cookie.includes('MagicalGirl=true'));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const requestPermission = async () => {
|
|
||||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
||||||
|
|
||||||
if (!isIOS)
|
|
||||||
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const permission = await (DeviceOrientationEvent as unknown as { requestPermission: () => Promise<string> }).requestPermission();
|
|
||||||
if (permission === "granted") {
|
|
||||||
window.addEventListener("devicemotion", handleDeviceMotion);
|
|
||||||
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeviceOrientation = (event: DeviceOrientationEvent) => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
const gamma = event.gamma || 0;
|
|
||||||
const beta = event.beta || 0;
|
|
||||||
|
|
||||||
const x = (gamma / 90) * -50;
|
|
||||||
const y = (beta / 180) * -50;
|
|
||||||
|
|
||||||
const bg = containerRef.current;
|
|
||||||
bg.style.backgroundPosition = `${50 + x}% ${50 + y}%`;
|
|
||||||
|
|
||||||
const img = bg.querySelector("img");
|
|
||||||
if (img) img.style.transform = `translate(${x * 2}px, ${y * 2}px)`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeviceMotion = (event: DeviceMotionEvent) => {
|
|
||||||
if (!containerRef.current || !event.accelerationIncludingGravity) return;
|
|
||||||
const { x, y } = event.accelerationIncludingGravity;
|
|
||||||
if (x === null || y === null) return;
|
|
||||||
const bg = containerRef.current;
|
|
||||||
bg.style.backgroundPosition = `${50 - x * 3}% ${50 - y * 3}%`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBackgroundTouchClick = () => {
|
|
||||||
if (!isMobile) return;
|
|
||||||
|
|
||||||
setClickCount((prev) => {
|
|
||||||
const newCount = prev + 1;
|
|
||||||
|
|
||||||
// Clear existing timeout
|
|
||||||
if (clickTimeoutRef.current) {
|
|
||||||
clearTimeout(clickTimeoutRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if required clicks reached
|
|
||||||
if (newCount >= REQUIRED_CLICKS) {
|
|
||||||
const newValue = !isMagicalGirlEnabled;
|
|
||||||
setIsMagicalGirlEnabled(newValue);
|
|
||||||
|
|
||||||
document.cookie = `MagicalGirl=${newValue}; path=/; max-age=${60 * 60 * 24 * 365}`;
|
|
||||||
window.location.reload();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset click count after 1 second if not enough clicks
|
|
||||||
clickTimeoutRef.current = setTimeout(() => {
|
|
||||||
setClickCount(0);
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
return newCount;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("deviceorientation", handleDeviceOrientation);
|
|
||||||
window.removeEventListener("devicemotion", handleDeviceMotion);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
id="top"
|
|
||||||
className="snap-start h-screen w-full relative"
|
|
||||||
// onWheel={(e) => {
|
|
||||||
// e.preventDefault();
|
|
||||||
// window.scrollBy(0, window.innerHeight * (e.deltaY > 0 ? 1 : -1));
|
|
||||||
// }}
|
|
||||||
>
|
|
||||||
<Sidebar />
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
className="absolute top-0 right-0 w-[calc(100%-100px)] h-screen lg:h-[calc(100vh-56px)] flex flex-col items-center justify-end lg:rounded-bl-[160px] bg-cover bg-center overflow-hidden"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `url('/background/${randomBg}.avif')`,
|
|
||||||
}}
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
aria-label="Toggle MagicalGirl mode on mobile"
|
|
||||||
onClick={handleBackgroundTouchClick}
|
|
||||||
onMouseMove={(e) => {
|
|
||||||
const { clientX, clientY } = e;
|
|
||||||
const { innerWidth, innerHeight } = window;
|
|
||||||
const x = (clientX / innerWidth - 0.5) * -50;
|
|
||||||
const y = (clientY / innerHeight - 0.5) * -50;
|
|
||||||
|
|
||||||
const bg = e.currentTarget;
|
|
||||||
if (bg) {
|
|
||||||
bg.style.backgroundPosition = `${50 + x}% ${50 + y}%`;
|
|
||||||
}
|
|
||||||
const img = e.currentTarget.querySelector("img");
|
|
||||||
if (img) {
|
|
||||||
img.style.transform = `translate(${x}px, ${y}px)`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={"/char.avif"}
|
|
||||||
alt="character"
|
|
||||||
width={4740}
|
|
||||||
height={7584}
|
|
||||||
className="w-[50vh] lg:w-[30vw] translate-y-[10%] transition-transform duration-100 ease-out"
|
|
||||||
unoptimized
|
|
||||||
onClick={requestPermission}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import Image from "next/image";
|
|
||||||
import { Popover } from "./ui/popover";
|
|
||||||
|
|
||||||
export default function Sidebar() {
|
|
||||||
return (
|
|
||||||
<div className="w-[100px] h-screen absolute top-0 left-0 flex flex-col items-center justify-between">
|
|
||||||
<div className="w-full h-full flex flex-col items-center gap-6">
|
|
||||||
<Image
|
|
||||||
src="/Frame.svg"
|
|
||||||
alt="logo"
|
|
||||||
className="w-fit h-fit mt-[50px]"
|
|
||||||
width={30}
|
|
||||||
height={30}
|
|
||||||
/>
|
|
||||||
<div className="font-ntype rotate-90 mt-8 text-3xl opacity-70 flex flex-row gap-2 w-full">
|
|
||||||
<h1>
|
|
||||||
<a href="mailto:me@imnya.ng">me@imnya.ng</a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Popover />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue