Jiseoup/showmycodePublic
EN|KO
  • Code
  • Commits
  • Pull Requests
← Back to list

feat: add mobile responsive layout with sidebar drawer

JiseoupJiseoup · Apr 10, 2026b2c517a

Files changed7+99 -28

Changed files

+99 -28 · 7

@@ -28,7 +28,7 @@ export default async function CommitsPage({ params, searchParams }: Props) {
`/${lang}/repository/${owner}/${repo}/commits?page=${p}`;
return (
- <main className="flex-1 overflow-auto max-w-4xl mx-auto w-full px-6 py-6">
+ <main className="flex-1 overflow-auto max-w-4xl mx-auto w-full px-3 md:px-6 py-4 md:py-6">
<h2 className="text-lg font-semibold mb-4">
{dict.commits.title}
<span className="ml-2 text-sm font-normal text-muted-foreground">
@@ -81,7 +81,7 @@ export default async function CommitsPage({ params, searchParams }: Props) {
</ul>
{(hasPrev || hasNext) && (
- <div className="flex items-center justify-between mt-6">
+ <div className="flex items-center justify-between mt-6 gap-2 flex-wrap">
{hasPrev ? (
<Link
href={pageUrl(page - 1)}
@@ -52,7 +52,7 @@ export default async function PullsPage({ params, searchParams }: Props) {
`/${lang}/repository/${owner}/${repo}/pulls?page=${p}`;
return (
- <main className="flex-1 overflow-auto max-w-4xl mx-auto w-full px-6 py-6">
+ <main className="flex-1 overflow-auto max-w-4xl mx-auto w-full px-3 md:px-6 py-4 md:py-6">
<h2 className="text-lg font-semibold mb-4">
{dict.pulls.title}
<span className="ml-2 text-sm font-normal text-muted-foreground">
@@ -85,7 +85,7 @@ export default async function PullsPage({ params, searchParams }: Props) {
<span className="text-xs text-muted-foreground">#{pr.number}</span>
</div>
- <p className="text-xs text-muted-foreground mt-1 font-mono">
+ <p className="text-xs text-muted-foreground mt-1 font-mono truncate">
{pr.head.ref} → {pr.base.ref}
</p>
@@ -124,7 +124,7 @@ export default async function PullsPage({ params, searchParams }: Props) {
)}
{(hasPrev || hasNext) && (
- <div className="flex items-center justify-between mt-6">
+ <div className="flex items-center justify-between mt-6 gap-2 flex-wrap">
{hasPrev ? (
<Link
href={pageUrl(page - 1)}
@@ -33,8 +33,8 @@ export default async function RepoLayout({ children, params }: Props) {
return (
<div className="min-h-screen bg-background flex flex-col">
- <header className="border-b border-border px-6 py-3 flex items-center justify-between gap-4">
- <div className="flex items-center gap-2 min-w-0">
+ <header className="border-b border-border px-3 md:px-6 py-3 flex items-center justify-between gap-2 md:gap-4">
+ <div className="flex items-center gap-2 min-w-0 overflow-hidden">
<Link href={`/${locale}`} className="text-muted-foreground hover:text-foreground transition-colors shrink-0">
<svg viewBox="0 0 16 16" className="w-5 h-5 fill-current" aria-hidden>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
@@ -56,7 +56,7 @@ export default async function RepoLayout({ children, params }: Props) {
</div>
</header>
- <nav className="border-b border-border px-6">
+ <nav className="border-b border-border px-3 md:px-6">
<ul className="flex gap-1">
{tabs.map((tab) => (
<li key={tab.href}>
@@ -1,4 +1,5 @@
import { Sidebar } from "@/components/Sidebar";
+import { SidebarDrawer } from "@/components/SidebarDrawer";
import { CodeViewer } from "@/components/CodeViewer";
import { getDictionary, type Locale } from "@/lib/i18n.server";
@@ -13,23 +14,30 @@ export default async function CodePage({ params, searchParams }: Props) {
const dict = await getDictionary(lang as Locale);
return (
- <div className="flex flex-1 overflow-hidden">
- <Sidebar owner={owner} repo={repo} selectedPath={selectedPath} lang={lang as Locale} filesLabel={dict.code.files} />
-
- <main className="flex-1 overflow-auto">
- {selectedPath ? (
- <div>
- <div className="px-4 py-2 border-b border-border bg-muted/40 text-sm font-mono text-muted-foreground">
- {selectedPath}
- </div>
- <CodeViewer owner={owner} repo={repo} path={selectedPath} />
- </div>
- ) : (
- <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
- {dict.code.selectFile}
+ <SidebarDrawer
+ filesLabel={dict.code.files}
+ sidebar={
+ <Sidebar
+ owner={owner}
+ repo={repo}
+ selectedPath={selectedPath}
+ lang={lang as Locale}
+ filesLabel={dict.code.files}
+ />
+ }
+ >
+ {selectedPath ? (
+ <div>
+ <div className="px-4 py-2 border-b border-border bg-muted/40 text-sm font-mono text-muted-foreground">
+ {selectedPath}
</div>
- )}
- </main>
- </div>
+ <CodeViewer owner={owner} repo={repo} path={selectedPath} />
+ </div>
+ ) : (
+ <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
+ {dict.code.selectFile}
+ </div>
+ )}
+ </SidebarDrawer>
);
}
@@ -18,7 +18,7 @@ export default async function HomePage({
return (
<div className="min-h-screen bg-background">
- <header className="border-b border-border px-6 py-4 flex items-center justify-between">
+ <header className="border-b border-border px-3 md:px-6 py-4 flex items-center justify-between">
<div className="flex items-center gap-2">
<svg viewBox="0 0 16 16" className="w-6 h-6 fill-current" aria-hidden>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
@@ -31,7 +31,7 @@ export default async function HomePage({
</div>
</header>
- <main className="max-w-3xl mx-auto px-6 py-10">
+ <main className="max-w-3xl mx-auto px-3 md:px-6 py-6 md:py-10">
<ul className="space-y-3">
{repos.map((repo) => (
<li key={repo.full_name}>
@@ -13,7 +13,7 @@ export async function Sidebar({ owner, repo, lang, filesLabel, selectedPath }: P
const { tree } = await getTree(owner, repo);
return (
- <aside className="w-64 shrink-0 border-r border-border overflow-y-auto flex flex-col">
+ <aside className="flex flex-col h-full overflow-hidden">
<div className="px-3 py-2.5 border-b border-border">
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
{filesLabel}
@@ -0,0 +1,63 @@
+"use client";
+
+import { useState } from "react";
+
+type Props = {
+ sidebar: React.ReactNode;
+ filesLabel: string;
+ children: React.ReactNode;
+};
+
+export function SidebarDrawer({ sidebar, filesLabel, children }: Props) {
+ const [open, setOpen] = useState(false);
+
+ return (
+ <div className="flex flex-1 overflow-hidden">
+ {/* Mobile: overlay backdrop. */}
+ {open && (
+ <div
+ className="fixed inset-0 z-40 bg-black/40 md:hidden"
+ onClick={() => setOpen(false)}
+ />
+ )}
+
+ {/* Sidebar: fixed drawer on mobile, static column on desktop. */}
+ <aside
+ className={[
+ "fixed inset-y-0 left-0 z-50 w-64 bg-background",
+ "border-r border-border flex flex-col overflow-hidden",
+ "transition-transform duration-200",
+ "md:relative md:inset-auto md:z-auto md:translate-x-0 md:shrink-0",
+ open ? "translate-x-0" : "-translate-x-full",
+ ].join(" ")}
+ >
+ {sidebar}
+ </aside>
+
+ {/* Main content area. */}
+ <main className="flex-1 overflow-auto min-w-0">
+ {/* Mobile-only Files toggle button. */}
+ <div className="md:hidden flex items-center px-3 py-2 border-b border-border">
+ <button
+ onClick={() => setOpen(true)}
+ aria-label={filesLabel}
+ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
+ >
+ <svg
+ viewBox="0 0 20 20"
+ className="w-4 h-4"
+ fill="none"
+ stroke="currentColor"
+ strokeWidth="2"
+ aria-hidden
+ >
+ <path strokeLinecap="round" d="M3 5h14M3 10h14M3 15h14" />
+ </svg>
+ {filesLabel}
+ </button>
+ </div>
+ {children}
+ </main>
+ </div>
+ );
+}