Enhance styling and functionality: add scrollbar styles, update iframe source, adjust project section width, implement MagicalGirl toggle, and create ASCII art HTML page.

This commit is contained in:
암냥 2025-11-20 00:02:08 +09:00
commit deca6506a9
No known key found for this signature in database
8 changed files with 178 additions and 22 deletions

View file

@ -11,7 +11,7 @@ export default function NeoFetch() {
<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="/art.html"
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">

View file

@ -22,7 +22,7 @@ const projects = [
export default function Projects() {
return (
<section className="break-keep break-words w-full md:w-1/2">
<section className="break-keep break-words w-full lg:w-1/2">
<div className="space-y-8">
{projects.map((project, idx) => (
<div className="space-y-2" key={idx}>

View file

@ -78,7 +78,14 @@ export default function SUPERCOMMAND() {
</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={() => {
document.cookie = "MagicalGirl=true; path=/";
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>

View file

@ -1,14 +1,21 @@
"use client";
import Sidebar from "@/components/sidebar";
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 () => {
@ -18,8 +25,8 @@ export default function Top() {
window.addEventListener("deviceorientation", handleDeviceOrientation);
try {
const res = await (DeviceOrientationEvent as any).requestPermission();
if (res === "granted") {
const permission = await (DeviceOrientationEvent as unknown as { requestPermission: () => Promise<string> }).requestPermission();
if (permission === "granted") {
window.addEventListener("devicemotion", handleDeviceMotion);
window.addEventListener("deviceorientation", handleDeviceOrientation);
}
@ -51,6 +58,36 @@ export default function Top() {
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);
@ -62,10 +99,10 @@ export default function Top() {
<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));
// }}
// onWheel={(e) => {
// e.preventDefault();
// window.scrollBy(0, window.innerHeight * (e.deltaY > 0 ? 1 : -1));
// }}
>
<Sidebar />
<div
@ -74,6 +111,10 @@ export default function Top() {
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;
@ -90,15 +131,17 @@ export default function Top() {
}
}}
>
{/* <Image
src={"/char.avif"}
alt="character"
width={4096}
height={4096}
className="w-[50vh] lg:w-[30vw] translate-y-[10%] transition-transform duration-100 ease-out"
unoptimized
onClick={requestPermission}
/> */} {/* 이제 그런 애는 없어 */}
{isMagicalGirlEnabled && (
<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>
);

View file

@ -25,7 +25,7 @@ export default function TimelineComponent() {
return (
<div
id="timeline"
className="w-full md:w-1/2 flex flex-col items-center justify-center px-12 mt-8"
className="w-full lg:w-1/2 flex flex-col items-center justify-center px-12 mt-8"
>
<div className="w-full">
<h1 className="text-2xl font-bold mb-4 w-full">🌠 </h1>
@ -36,6 +36,7 @@ export default function TimelineComponent() {
{years.map((year) => (
<button
key={year}
type="button"
onClick={() => setSelectedYear(year)}
className={`px-4 py-2 rounded-lg font-semibold transition-all text-sm ${
selectedYear === year