2025-10-16 09:55:14 +09:00
|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
Select,
|
|
|
|
|
|
SelectContent,
|
|
|
|
|
|
SelectGroup,
|
|
|
|
|
|
SelectItem,
|
|
|
|
|
|
SelectLabel,
|
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
|
SelectValue,
|
|
|
|
|
|
} from "@/components/ui/select";
|
|
|
|
|
|
import { Monitor } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
|
|
export type Resolution = "hd" | "fhd" | "qhd" | "uhd";
|
|
|
|
|
|
|
|
|
|
|
|
export interface ResolutionConfig {
|
|
|
|
|
|
width: number;
|
|
|
|
|
|
height: number;
|
|
|
|
|
|
label: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const RESOLUTIONS: Record<Resolution, ResolutionConfig> = {
|
|
|
|
|
|
hd: {
|
|
|
|
|
|
width: 1280 - 360,
|
|
|
|
|
|
height: 720 - 312,
|
|
|
|
|
|
label: "HD (1280x720)",
|
|
|
|
|
|
},
|
|
|
|
|
|
fhd: {
|
|
|
|
|
|
width: 1920 - 360,
|
|
|
|
|
|
height: 1080 - 312,
|
|
|
|
|
|
label: "Full HD (1920x1080)",
|
|
|
|
|
|
},
|
|
|
|
|
|
qhd: {
|
|
|
|
|
|
width: 2560 - 360,
|
|
|
|
|
|
height: 1440 - 312,
|
|
|
|
|
|
label: "QHD (2560x1440)",
|
|
|
|
|
|
},
|
|
|
|
|
|
uhd: {
|
|
|
|
|
|
width: 3840 - 360,
|
|
|
|
|
|
height: 2160 - 312,
|
|
|
|
|
|
label: "4K UHD (3840x2160)",
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
interface ResolutionSelectorProps {
|
|
|
|
|
|
value: Resolution;
|
|
|
|
|
|
onChange: (resolution: Resolution) => void;
|
|
|
|
|
|
currentScreenResolution?: Resolution;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 현재 화면 해상도 감지
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function detectScreenResolution(): Resolution {
|
|
|
|
|
|
if (typeof window === "undefined") return "fhd";
|
|
|
|
|
|
|
2025-10-23 10:06:00 +09:00
|
|
|
|
// 1. 브라우저 뷰포트 크기 (실제 사용 가능한 공간)
|
|
|
|
|
|
const viewportWidth = window.innerWidth;
|
|
|
|
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 화면 해상도 + devicePixelRatio (Retina 디스플레이 대응)
|
|
|
|
|
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
|
|
|
|
const physicalWidth = window.screen.width;
|
|
|
|
|
|
const physicalHeight = window.screen.height;
|
|
|
|
|
|
const logicalWidth = physicalWidth / pixelRatio;
|
|
|
|
|
|
const logicalHeight = physicalHeight / pixelRatio;
|
2025-10-16 09:55:14 +09:00
|
|
|
|
|
2025-10-23 09:56:35 +09:00
|
|
|
|
let detectedResolution: Resolution;
|
|
|
|
|
|
|
2025-10-23 10:06:00 +09:00
|
|
|
|
// 뷰포트와 논리적 해상도 중 더 큰 값을 기준으로 결정
|
|
|
|
|
|
// (크램쉘 모드나 특수한 경우에도 대응)
|
|
|
|
|
|
const effectiveWidth = Math.max(viewportWidth, logicalWidth);
|
|
|
|
|
|
const effectiveHeight = Math.max(viewportHeight, logicalHeight);
|
|
|
|
|
|
|
|
|
|
|
|
// 캔버스가 여유있게 들어갈 수 있는 크기로 결정
|
|
|
|
|
|
// 여유 공간: 좌우 패딩, 사이드바 등을 고려하여 약 400-500px 여유
|
|
|
|
|
|
if (effectiveWidth >= 3400) {
|
|
|
|
|
|
// UHD 캔버스 2940px + 여유 460px
|
2025-10-23 09:56:35 +09:00
|
|
|
|
detectedResolution = "uhd";
|
2025-10-23 10:06:00 +09:00
|
|
|
|
} else if (effectiveWidth >= 2400) {
|
|
|
|
|
|
// QHD 캔버스 1960px + 여유 440px
|
2025-10-23 09:56:35 +09:00
|
|
|
|
detectedResolution = "qhd";
|
2025-10-23 10:06:00 +09:00
|
|
|
|
} else if (effectiveWidth >= 1900) {
|
|
|
|
|
|
// FHD 캔버스 1560px + 여유 340px
|
2025-10-23 09:56:35 +09:00
|
|
|
|
detectedResolution = "fhd";
|
|
|
|
|
|
} else {
|
2025-10-23 10:06:00 +09:00
|
|
|
|
// HD 캔버스 1160px 이하
|
2025-10-23 09:56:35 +09:00
|
|
|
|
detectedResolution = "hd";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("🖥️ 화면 해상도 자동 감지:", {
|
2025-10-23 10:06:00 +09:00
|
|
|
|
viewportWidth,
|
|
|
|
|
|
viewportHeight,
|
|
|
|
|
|
physicalWidth,
|
|
|
|
|
|
physicalHeight,
|
|
|
|
|
|
pixelRatio,
|
|
|
|
|
|
logicalWidth: Math.round(logicalWidth),
|
|
|
|
|
|
logicalHeight: Math.round(logicalHeight),
|
|
|
|
|
|
effectiveWidth: Math.round(effectiveWidth),
|
|
|
|
|
|
effectiveHeight: Math.round(effectiveHeight),
|
2025-10-23 09:56:35 +09:00
|
|
|
|
detectedResolution,
|
|
|
|
|
|
canvasSize: RESOLUTIONS[detectedResolution],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return detectedResolution;
|
2025-10-16 09:55:14 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 해상도 선택 컴포넌트
|
|
|
|
|
|
* - HD, Full HD, QHD, 4K UHD 지원
|
|
|
|
|
|
* - 12칸 그리드 유지, 셀 크기만 변경
|
|
|
|
|
|
* - 현재 화면 해상도 감지 및 경고 표시
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function ResolutionSelector({ value, onChange, currentScreenResolution }: ResolutionSelectorProps) {
|
|
|
|
|
|
const currentConfig = RESOLUTIONS[value];
|
|
|
|
|
|
const screenConfig = currentScreenResolution ? RESOLUTIONS[currentScreenResolution] : null;
|
|
|
|
|
|
|
|
|
|
|
|
// 현재 선택된 해상도가 화면보다 큰지 확인
|
|
|
|
|
|
const isTooLarge =
|
|
|
|
|
|
screenConfig &&
|
|
|
|
|
|
(currentConfig.width > screenConfig.width + 360 || currentConfig.height > screenConfig.height + 312);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<Monitor className="h-4 w-4 text-gray-500" />
|
|
|
|
|
|
<Select value={value} onValueChange={(v) => onChange(v as Resolution)}>
|
|
|
|
|
|
<SelectTrigger className={`w-[180px] ${isTooLarge ? "border-orange-500" : ""}`}>
|
|
|
|
|
|
<SelectValue />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent className="z-[99999]">
|
|
|
|
|
|
<SelectGroup>
|
|
|
|
|
|
<SelectLabel>캔버스 해상도</SelectLabel>
|
|
|
|
|
|
<SelectItem value="hd">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<span>HD</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">1280x720</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="fhd">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<span>Full HD</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">1920x1080</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="qhd">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<span>QHD</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">2560x1440</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="uhd">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<span>4K UHD</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">3840x2160</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
</SelectGroup>
|
|
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
{isTooLarge && <span className="text-xs text-orange-600">⚠️ 현재 화면보다 큽니다</span>}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|