Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions app/apple-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ImageResponse } from "next/og";

// iOS 홈 화면 추가용 아이콘 (180x180).
export const size = { width: 180, height: 180 };
export const contentType = "image/png";

export default function AppleIcon() {
return new ImageResponse(
(
<div
style={{
fontSize: 130,
background: "#0f172a",
color: "#ffffff",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontWeight: 700,
letterSpacing: -4,
}}
>
M
</div>
),
{ ...size },
);
}
30 changes: 30 additions & 0 deletions app/icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ImageResponse } from "next/og";

// Favicon (32x32). Latin "M" — 한국어 폰트 fetch 없이도 안정적으로 렌더.
export const size = { width: 32, height: 32 };
export const contentType = "image/png";

export default function Icon() {
return new ImageResponse(
(
<div
style={{
fontSize: 22,
background: "#0f172a",
color: "#ffffff",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontWeight: 700,
letterSpacing: -1,
borderRadius: 6,
}}
>
M
</div>
),
{ ...size },
);
}
42 changes: 42 additions & 0 deletions app/icons/[size]/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ImageResponse } from "next/og";

// PWA manifest 가 참조하는 동적 아이콘 라우트. `/icons/192`, `/icons/512` 만 허용.
// Korean 폰트 없이 안정적으로 렌더되도록 Latin "M" 사용.

export const runtime = "nodejs";

const ALLOWED_SIZES = new Set([192, 512]);

type Params = { size: string };

export async function GET(
_req: Request,
context: { params: Promise<Params> },
) {
const { size: raw } = await context.params;
const n = Number.parseInt(raw, 10);
if (!ALLOWED_SIZES.has(n)) {
return new Response("Not Found", { status: 404 });
}
return new ImageResponse(
(
<div
style={{
fontSize: Math.round(n * 0.72),
background: "#0f172a",
color: "#ffffff",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontWeight: 700,
letterSpacing: -Math.round(n * 0.02),
}}
>
M
</div>
),
{ width: n, height: n },
);
}
18 changes: 17 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,24 @@ const appUrl = process.env.NEXT_PUBLIC_APP_URL?.trim();

export const metadata: Metadata = {
metadataBase: appUrl ? new URL(appUrl) : undefined,
title: "뮤런",
title: {
default: "뮤런",
template: "%s · 뮤런",
},
description: "애니뮤 러닝 소모임의 정기 운동 아카이브",
applicationName: "뮤런",
openGraph: {
type: "website",
locale: "ko_KR",
siteName: "뮤런",
title: "뮤런",
description: "애니뮤 러닝 소모임의 정기 운동 아카이브",
},
twitter: {
card: "summary_large_image",
title: "뮤런",
description: "애니뮤 러닝 소모임의 정기 운동 아카이브",
},
};

export const viewport: Viewport = {
Expand Down
42 changes: 42 additions & 0 deletions app/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { MetadataRoute } from "next";

// PWA 매니페스트. iOS / Android 모두 홈 화면 추가 가능.
// 아이콘은 동적 라우트(`app/icons/[size]/route.tsx`)가 생성.
export default function manifest(): MetadataRoute.Manifest {
return {
name: "뮤런",
short_name: "뮤런",
description: "애니뮤 러닝 소모임의 정기 운동 아카이브",
start_url: "/",
display: "standalone",
orientation: "portrait",
background_color: "#ffffff",
theme_color: "#0f172a",
icons: [
{
src: "/icons/192",
sizes: "192x192",
type: "image/png",
purpose: "any",
},
{
src: "/icons/512",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "/icons/192",
sizes: "192x192",
type: "image/png",
purpose: "maskable",
},
{
src: "/icons/512",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
};
}
62 changes: 62 additions & 0 deletions app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ImageResponse } from "next/og";

import { getOgFonts } from "@/lib/og-font";

// 기본 사이트 OG (홈/그 외 페이지에서 더 구체적인 게 없을 때). 1200x630.
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const alt = "뮤런 — 애니뮤 러닝 소모임 아카이브";
export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export default async function DefaultOg() {
let fonts: Awaited<ReturnType<typeof getOgFonts>> | undefined;
try {
fonts = await getOgFonts();
} catch {
fonts = undefined;
}

return new ImageResponse(
(
<div
style={{
width: "100%",
height: "100%",
background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)",
color: "#ffffff",
display: "flex",
flexDirection: "column",
padding: 80,
fontFamily: fonts ? "Noto Sans KR" : "sans-serif",
}}
>
<div
style={{
fontSize: 28,
opacity: 0.7,
letterSpacing: 4,
}}
>
MURUN
</div>
<div
style={{
marginTop: "auto",
display: "flex",
flexDirection: "column",
gap: 16,
}}
>
<div style={{ fontSize: 96, fontWeight: 700, lineHeight: 1.1 }}>
뮤런
</div>
<div style={{ fontSize: 36, opacity: 0.85, fontWeight: 400 }}>
애니뮤 러닝 소모임의 정기 운동 아카이브
</div>
</div>
</div>
),
{ ...size, fonts },
);
}
Loading
Loading