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
15 changes: 9 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,24 @@ on:
jobs:
ci:
runs-on: ubuntu-latest
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with: {version: 9}
with: { version: 9 }
- uses: actions/setup-node@v4
with:
with:
node-version: 20
cache: pnpm

- run: pnpm install --frozen-lockfile

- run: pnpm build:content

- run: pnpm typecheck
- run: pnpm lint

- run: pnpm build

- run: pnpm build
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface Props {
pageSize: number;
}

export default function SeriesPostList({
export default async function SeriesPostList({
category,
series,
sort,
Expand All @@ -19,7 +19,7 @@ export default function SeriesPostList({
}: Props) {
const appliedSort: PostSort = sort === "popular" ? "popular" : "latest";

const result = queryPosts({
const result = await queryPosts({
category,
series,
sort: appliedSort,
Expand All @@ -32,7 +32,7 @@ export default function SeriesPostList({
const seriesMeta = series ? getSeriesMeta(series) : null;

return (
<section id="series-post-section" className="mt-10 scroll-mt-24">
<section id="series-post-section" className="mt-20 scroll-mt-24">
<div>
<h2 className="text-3xl font-bold">{seriesMeta?.name ?? "모아보기"}</h2>

Expand Down
43 changes: 22 additions & 21 deletions src/app/(layout)/(shell)/_components/layout/SiteHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,28 +83,29 @@ export default function SiteHeader() {
</div>
</div>

<motion.button
type="button"
className="mr-2 md:hidden"
onClick={handleSearchClick}
aria-label="검색 열기"
whileTap={{ scale: 0.9, opacity: 0.8 }}
transition={{ duration: 0.08 }}
>
<Search size={22} />
</motion.button>

<motion.button
type="button"
className="md:hidden"
onClick={toggleMobileMenu}
aria-label="모바일 메뉴 열기"
whileTap={{ scale: 0.9, opacity: 0.8 }}
transition={{ duration: 0.08 }}
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</motion.button>
</div>
<section className="flex grow justify-between items-center py-1 md:hidden">
<motion.button
type="button"
className="md:hidden"
onClick={toggleMobileMenu}
aria-label="모바일 메뉴 열기"
whileTap={{ scale: 0.9, opacity: 0.8 }}
transition={{ duration: 0.08 }}
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</motion.button>
<motion.button
type="button"
className="md:hidden "
onClick={handleSearchClick}
aria-label="검색 열기"
whileTap={{ scale: 0.9, opacity: 0.8 }}
transition={{ duration: 0.08 }}
>
<Search size={22} />
</motion.button>
</section>
</div>

<AnimatePresence initial={false}>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(layout)/(shell)/_components/posts/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface PostCardProps {
category: string;
date: string;
title: string;
excerpt: string;
excerpt?: string;
className?: string;
size?: "md" | "sm";
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/(layout)/(shell)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default async function HomePage({

const tags = getAllTags(false);

const { posts, pagination, pageRange, applied } = queryPosts(params);
const { posts, pagination, pageRange, applied } = await queryPosts(params);

return (
<main className="px-5 sm:px-12 lg:px-40 space-y-12 ">
Expand Down
2 changes: 1 addition & 1 deletion src/app/(layout)/posts/[slug]/_components/PostToc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function PostToc({ items }: { items: TocItem[] }) {
"group relative block rounded-md py-1.5 pr-2 text-sm transition-colors",
"pl-4",
isActive
? "text-gray-900 dark:text-blue-300"
? "text-gray-900 dark:text-blue-300 font-semibold"
: "text-neutral-400 hover:text-gray-800 dark:text-neutral-400 dark:hover:text-blue-300"
)}
>
Expand Down
32 changes: 11 additions & 21 deletions src/app/(layout)/posts/[slug]/_components/RecommendedPosts.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
import RecommendedPostCard, {
RecommendedPost,
} from "@/app/(layout)/posts/[slug]/_components/post-recommended/RecommendedPostCard";
import { getRecommendedPostsForPost, VelitePost } from "@/lib/posts";

export default function RecommendedPosts() {
const items: RecommendedPost[] = [
{
title: "React 상태 관리, 왜 어려울까?",
overview:
"state, props, context가 섞일 때 복잡도가 급증하는 이유와 단순화 전략을 정리합니다.",
href: "#",
},
{
title: "useEffect를 덜 쓰는 패턴",
overview:
"불필요한 effect를 줄이고 서버/쿼리/파생 상태로 대체하는 실전 패턴을 소개합니다.",
href: "#",
},
{
title: "컴포넌트 분해 기준 5가지",
overview:
"재사용성과 응집도를 동시에 챙기는 컴포넌트 분해 체크리스트를 제공합니다.",
href: "#",
},
];
export default async function RecommendedPosts({ post }: { post: VelitePost }) {
const recommended = await getRecommendedPostsForPost(post, 3);
if (!recommended.length) return null;

const items: RecommendedPost[] = recommended.map((p) => ({
title: p.title,
overview: p.summary ?? "",
href: `/posts/${p.slug}`,
thumbnail: p.thumbnail,
}));

return (
<section className="space-y-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,30 @@ export default function RecommendedPostCard({
<a
href={post.href}
className={clsx(
"group relative block overflow-hidden rounded-xl bg-[#f2f3f6] transition-transform duration-200",
"group relative block overflow-hidden rounded-xl bg-[#f2f3f6] transition-transform duration-200",
"min-w-55",
"border border-gray-300",
"hover:scale-[1.02]",
"dark:bg-[#050a2a] dark:border-white/20"
)}
>
<div className="flex gap-3 py-4 px-3.5">
<div className="h-16 w-16 shrink-0 overflow-hidden rounded-lg bg-muted/20">
{post.thumbnail ? (
<Image
src={post.thumbnail}
alt=""
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-gray-300 dark:bg-background/50" />
)}
<div className="flex gap-2.5 p-3">
<div className="shrink-0 overflow-hidden rounded-lg bg-muted/20">
<Image
src={post.thumbnail ?? "/post-fallback.png"}
alt={post.title}
width={64}
height={64}
className="h-16 w-16 object-cover object-center"
/>
</div>

<div className="min-w-0 flex-1">
<div
className={clsx(
"text-sm font-semibold text-neutral-900 transition-colors",
"group-hover:text-neutral-950",
"dark:text-foreground/70",
"dark:text-foreground/90",
"line-clamp-1"
)}
>
Expand All @@ -52,9 +51,6 @@ export default function RecommendedPostCard({
</p>
</div>
</div>

<div className="pointer-events-none absolute -top-24 -right-24 hidden h-48 w-48 rounded-full bg-cyan-500/15 blur-3xl dark:block" />
<div className="pointer-events-none absolute -bottom-24 -left-24 hidden h-48 w-48 rounded-full bg-emerald-500/15 blur-3xl dark:block" />
</a>
);
}
10 changes: 5 additions & 5 deletions src/app/(layout)/posts/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import RecommendedPosts from "@/app/(layout)/posts/[slug]/_components/Recommende
import { adaptVeliteToc } from "@/lib/mdx/toc";
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { posts } from "../../../../../.velite";
import { velitePosts } from "@/lib/posts";

type PageProps = {
params: Promise<{ slug: string }>;
Expand All @@ -18,7 +18,7 @@ export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { slug } = await params;
const post = posts.find((p) => p.slug === slug);
const post = velitePosts.find((p) => p.slug === slug);
if (!post) return {};

const baseUrl = process.env.NEXT_PUBLIC_SITE_URL ?? "https://b0o0a.com";
Expand Down Expand Up @@ -56,7 +56,7 @@ export async function generateMetadata({
export default async function PostPage({ params }: PageProps) {
const { slug } = await params;

const post = posts.find((p) => p.slug === slug);
const post = velitePosts.find((p) => p.slug === slug);
if (!post) return notFound();

return (
Expand Down Expand Up @@ -90,13 +90,13 @@ export default async function PostPage({ params }: PageProps) {
</div>

<div className="pt-10 max-w-55 ml-auto">
<RecommendedPosts />
<RecommendedPosts post={post} />
</div>
</aside>
</div>

<div className="mt-10 xl:hidden">
<RecommendedPosts />
<RecommendedPosts post={post}/>
</div>
</main>
);
Expand Down
18 changes: 13 additions & 5 deletions src/components/common/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,33 @@ export function CommandPaletteProvider({
<Dialog.Root open={open} onOpenChange={handleOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-60 bg-black/40 backdrop-blur-sm" />
<Dialog.Content className="fixed left-1/2 top-50 z-70 w-full max-w-xl -translate-x-1/2 rounded-2xl border border-white/10 bg-foreground/70 dark:bg-foreground shadow-2xl">

<Dialog.Content
className={[
"fixed inset-x-3 top-[18%] z-70 w-auto rounded-2xl border border-white/10",
"bg-foreground/80 text-background shadow-2xl dark:bg-foreground",
"max-h-[70vh]",
"sm:left-1/2 sm:top-50 sm:w-full sm:max-w-xl sm:-translate-x-1/2 sm:inset-x-auto",
].join(" ")}
>
<Dialog.Title className="sr-only">사이트 검색</Dialog.Title>

<Command className="overflow-hidden rounded-2xl bg-transparent text-sm text-background">
<Command className="flex max-h-[70vh] flex-col overflow-hidden rounded-2xl bg-transparent text-sm text-background">
<div className="flex items-center gap-2 border-b border-white/10 px-3">
<Search className="h-4 w-4 text-background " />
<Search className="h-4 w-4 text-background" />
<Command.Input
autoFocus
value={query}
onValueChange={setQuery}
placeholder="무엇을 찾고 계신가요?"
className="flex-1 bg-transparent py-3 text-sm outline-none placeholder:text-background/70"
/>
<span className="rounded bg-foreground/70 dark:bg-background/30 px-1.5 py-0.5 text-[10px] text-white ">
<span className="hidden rounded bg-foreground/70 px-1.5 py-0.5 text-[10px] text-white sm:inline-block dark:bg-background/30">
ESC
</span>
</div>

<Command.List className="max-h-80 overflow-y-auto py-2">
<Command.List className="max-h-[60vh] overflow-y-auto py-2">
<Command.Empty className="px-4 py-3 text-xs text-background/70">
검색 결과가 없습니다.
</Command.Empty>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/fallback/BrowseFallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function BrowseFallback({
description = "조금만 기다리면 더 맛있게 정리될 거예요.",
}: BrowseFallbackProps) {
return (
<section className="flex flex-col items-center justify-center gap-4 mt-10 py-14 text-center">
<section className="flex flex-col items-center justify-center gap-4 mt-30 py-14 text-center">
<BoabamIcon className="w-50 text-foreground/25 dark:text-white/70" />

{/* 텍스트 */}
Expand Down
2 changes: 1 addition & 1 deletion src/components/mdx/Quote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default function Quote({
)}
/>

<div className="pl-6 pr-5 py-1 leading-0">
<div className="pl-6 pr-5 leading-snug wrap-break-word">
{children}
</div>
</div>
Expand Down
Loading
Loading