Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "~/styles/globals.css";
import "~/styles/DarkMode.css";

import { GeistSans } from "geist/font/sans";
import { type Metadata } from "next";
Expand Down Expand Up @@ -76,7 +77,7 @@ export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${GeistSans.variable}`}>
<html lang="en" className={`${GeistSans.variable} light-mode`}>
<CSPostHogProvider>
<body className="flex min-h-screen flex-col">
<Header />
Expand Down
18 changes: 9 additions & 9 deletions src/components/api-key-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function ApiKeyDialog({ isOpen, onClose, onSubmit }: ApiKeyDialogProps) {

return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="border-[3px] border-black bg-purple-200 p-6 shadow-[8px_8px_0_0_#000000] sm:max-w-md">
<DialogContent className="border-[3px] border bg-card p-6 shadow-[8px_8px_0_0_#000000] sm:max-w-md">
<DialogHeader>
<DialogTitle className="text-xl font-bold text-black">
<DialogTitle className="text-xl font-bold text-foreground">
Enter OpenAI API Key
</DialogTitle>
</DialogHeader>
Expand All @@ -54,14 +54,14 @@ export function ApiKeyDialog({ isOpen, onClose, onSubmit }: ApiKeyDialogProps) {
<span className="font-medium">Get your OpenAI API key </span>
<Link
href="https://platform.openai.com/api-keys"
className="font-medium text-purple-600 transition-colors duration-200 hover:text-purple-500"
className="font-medium text-primary transition-opacity duration-200 hover:opacity-80"
>
here
</Link>
.
</div>
<details className="group text-sm [&>summary:focus-visible]:outline-none">
<summary className="cursor-pointer font-medium text-purple-700 hover:text-purple-600">
<summary className="cursor-pointer font-medium text-primary hover:opacity-80">
Data storage disclaimer
</summary>
<div className="animate-accordion-down mt-2 space-y-2 overflow-hidden pl-2">
Expand All @@ -71,7 +71,7 @@ export function ApiKeyDialog({ isOpen, onClose, onSubmit }: ApiKeyDialogProps) {
following the instructions in the{" "}
<Link
href="https://github.com/ahmedkhaleel2004/gitdiagram"
className="text-purple-600 transition-colors duration-200 hover:text-purple-500"
className="text-primary transition-opacity duration-200 hover:opacity-80"
>
README
</Link>
Expand All @@ -84,29 +84,29 @@ export function ApiKeyDialog({ isOpen, onClose, onSubmit }: ApiKeyDialogProps) {
placeholder="sk-..."
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
className="flex-1 rounded-md border-[3px] border-black px-3 py-2 text-base font-bold shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-gray-700"
className="flex-1 rounded-md border-[3px] border px-3 py-2 text-base font-bold text-foreground shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-muted-foreground"
required
/>
<div className="flex items-center justify-between">
<button
type="button"
onClick={handleClear}
className="text-sm text-purple-600 hover:text-purple-500"
className="text-sm text-primary hover:opacity-80"
>
Clear
</button>
<div className="flex gap-3">
<Button
type="button"
onClick={onClose}
className="border-[3px] border-black bg-gray-200 px-4 py-2 text-black shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-gray-300"
className="border-[3px] border bg-secondary px-4 py-2 text-foreground shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:opacity-90"
>
Cancel
</Button>
<Button
type="submit"
disabled={!apiKey.startsWith("sk-")}
className="border-[3px] border-black bg-purple-400 px-4 py-2 text-black shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-purple-300 disabled:opacity-50"
className="border-[3px] border bg-primary px-4 py-2 text-primary-foreground shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:opacity-90 disabled:opacity-50"
>
Save Key
</Button>
Expand Down
6 changes: 3 additions & 3 deletions src/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import Link from "next/link";

export function Footer() {
return (
<footer className="mt-auto border-t-[3px] border-black py-4 lg:px-8">
<footer className="mt-auto border-t-[3px] border py-4 lg:px-8">
<div className="container mx-auto flex h-8 max-w-4xl items-center justify-center">
<span className="text-sm font-medium text-black">
<span className="text-sm font-medium text-foreground">
Made by{" "}
<Link
href="https://ahmedkhaleel.dev"
className="text-purple-600 hover:underline"
className="text-primary hover:underline"
>
Ahmed Khaleel
</Link>
Expand Down
58 changes: 50 additions & 8 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,44 @@ export function Header() {
useState(false);
const [isApiKeyDialogOpen, setIsApiKeyDialogOpen] = useState(false);
const [starCount, setStarCount] = useState<number | null>(null);
const [isDark, setIsDark] = useState(false);

useEffect(() => {
void getStarCount().then(setStarCount);
}, []);

// Initialize theme from localStorage and apply class on <html>
useEffect(() => {
if (typeof document === "undefined") return;
const saved = localStorage.getItem("theme");
const html = document.documentElement;
if (saved === "dark") {
html.classList.remove("light-mode");
html.classList.add("dark-mode");
setIsDark(true);
} else {
html.classList.remove("dark-mode");
html.classList.add("light-mode");
setIsDark(false);
}
}, []);

const toggleTheme = () => {
if (typeof document === "undefined") return;
const html = document.documentElement;
if (isDark) {
html.classList.remove("dark-mode");
html.classList.add("light-mode");
localStorage.setItem("theme", "light");
setIsDark(false);
} else {
html.classList.remove("light-mode");
html.classList.add("dark-mode");
localStorage.setItem("theme", "dark");
setIsDark(true);
}
};

const formatStarCount = (count: number | null) => {
if (!count) return "10.0k";
if (count >= 1000) {
Expand All @@ -37,22 +70,22 @@ export function Header() {
};

return (
<header className="border-b-[3px] border-black">
<header className="border-b-[3px] border">
<div className="mx-auto flex h-16 max-w-4xl items-center justify-between px-4 sm:px-8">
<Link href="/" className="flex items-center">
<span className="text-lg font-semibold sm:text-xl">
<span className="text-black transition-colors duration-200 hover:text-gray-600">
<span className="text-foreground transition-colors duration-200">
Git
</span>
<span className="text-purple-600 transition-colors duration-200 hover:text-purple-500">
<span className="text-primary transition-opacity duration-200 hover:opacity-80">
Diagram
</span>
</span>
</Link>
<nav className="flex items-center gap-3 sm:gap-6">
<span
onClick={() => setIsApiKeyDialogOpen(true)}
className="cursor-pointer text-sm font-medium text-black transition-transform hover:translate-y-[-2px] hover:text-purple-600"
className="cursor-pointer text-sm font-medium text-foreground transition-transform hover:translate-y-[-2px] hover:text-primary"
>
<span className="flex items-center sm:hidden">
<span>API Key</span>
Expand All @@ -63,20 +96,29 @@ export function Header() {
</span>
<span
onClick={() => setIsPrivateReposDialogOpen(true)}
className="cursor-pointer text-sm font-medium text-black transition-transform hover:translate-y-[-2px] hover:text-purple-600"
className="cursor-pointer text-sm font-medium text-foreground transition-transform hover:translate-y-[-2px] hover:text-primary"
>
<span className="sm:hidden">Private Repos</span>
<span className="hidden sm:inline">Private Repos</span>
</span>
<Link
href="https://github.com/ahmedkhaleel2004/gitdiagram"
className="flex items-center gap-1 text-sm font-medium text-black transition-transform hover:translate-y-[-2px] hover:text-purple-600 sm:gap-2"
className="flex items-center gap-1 text-sm font-medium text-foreground transition-transform hover:translate-y-[-2px] hover:text-primary sm:gap-2"
>
<FaGithub className="h-5 w-5" />
<span className="hidden sm:inline">GitHub</span>
</Link>
<span className="flex items-center gap-1 text-sm font-medium text-black">
<span className="text-amber-400">★</span>
<button
type="button"
onClick={toggleTheme}
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
title={isDark ? "Light mode" : "Dark mode"}
className="flex h-10 w-10 items-center justify-center rounded-full border text-lg transition-colors hover:bg-accent"
>
<span>{isDark ? "🌙" : "☀️"}</span>
</button>
<span className="flex items-center gap-1 text-sm font-medium text-foreground">
<span className="text-accent">★</span>
{formatStarCount(starCount)}
</span>
</nav>
Expand Down
28 changes: 14 additions & 14 deletions src/components/main-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,25 @@ export default function MainCard({
};

return (
<Card className="relative w-full max-w-3xl border-[3px] border-black bg-purple-200 p-4 shadow-[8px_8px_0_0_#000000] sm:p-8">
<Card className="relative w-full max-w-3xl border-[3px] border border-l-[3px] border-t-[3px] bg-card p-4 shadow-[8px_8px_0_0_#000000] sm:p-8">
<form onSubmit={handleSubmit} className="space-y-4 sm:space-y-6">
<div className="flex flex-col gap-3 sm:flex-row sm:gap-4">
<Input
placeholder="https://github.com/username/repo"
className="flex-1 rounded-md border-[3px] border-black px-3 py-4 text-base font-bold shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-gray-700 sm:px-4 sm:py-6 sm:text-lg sm:placeholder:text-lg"
className="flex-1 rounded-md border-[3px] border px-3 py-4 text-base font-bold text-foreground shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-muted-foreground sm:px-4 sm:py-6 sm:text-lg sm:placeholder:text-lg"
value={repoUrl}
onChange={(e) => setRepoUrl(e.target.value)}
required
/>
<Button
type="submit"
className="border-[3px] border-black bg-purple-400 p-4 px-4 text-base text-black shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:transform hover:bg-purple-400 sm:p-6 sm:px-6 sm:text-lg"
className="border-[3px] border bg-primary p-4 px-4 text-base text-primary-foreground shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:transform hover:opacity-90 sm:p-6 sm:px-6 sm:text-lg"
>
Diagram
</Button>
</div>

{error && <p className="text-sm text-red-600">{error}</p>}
{error && <p className="text-sm text-destructive">{error}</p>}

{/* Dropdowns Container */}
{!isHome && (
Expand All @@ -131,10 +131,10 @@ export default function MainCard({
e.preventDefault();
handleDropdownToggle("customize");
}}
className={`flex items-center justify-between gap-2 rounded-md border-[3px] border-black px-4 py-2 font-medium text-black transition-colors sm:max-w-[250px] ${
className={`flex items-center justify-between gap-2 rounded-md border-[3px] border px-4 py-2 font-medium text-foreground transition-colors sm:max-w-[250px] ${
activeDropdown === "customize"
? "bg-purple-400"
: "bg-purple-300 hover:bg-purple-400"
? "bg-primary text-primary-foreground"
: "bg-secondary hover:opacity-90"
}`}
>
<span>Customize Diagram</span>
Expand All @@ -153,10 +153,10 @@ export default function MainCard({
e.preventDefault();
handleDropdownToggle("export");
}}
className={`flex items-center justify-between gap-2 rounded-md border-[3px] border-black px-4 py-2 font-medium text-black transition-colors sm:max-w-[250px] ${
className={`flex items-center justify-between gap-2 rounded-md border-[3px] border px-4 py-2 font-medium text-foreground transition-colors sm:max-w-[250px] ${
activeDropdown === "export"
? "bg-purple-400"
: "bg-purple-300 hover:bg-purple-400"
? "bg-primary text-primary-foreground"
: "bg-secondary hover:opacity-90"
}`}
>
<span>Export Diagram</span>
Expand All @@ -170,7 +170,7 @@ export default function MainCard({
)}
{lastGenerated && (
<>
<label className="font-medium text-black">
<label className="font-medium text-foreground">
Enable Zoom
</label>
<Switch
Expand Down Expand Up @@ -214,15 +214,15 @@ export default function MainCard({
{/* Example Repositories */}
{isHome && (
<div className="space-y-2">
<div className="text-sm text-gray-700 sm:text-base">
<div className="text-sm text-muted-foreground sm:text-base">
Try these example repositories:
</div>
<div className="flex flex-wrap gap-2">
{Object.entries(exampleRepos).map(([name, path]) => (
<Button
key={name}
variant="outline"
className="border-2 border-black bg-purple-400 text-sm text-black transition-transform hover:-translate-y-0.5 hover:transform hover:bg-purple-300 sm:text-base"
className="border-2 border bg-primary text-sm text-primary-foreground transition-transform hover:-translate-y-0.5 hover:transform hover:opacity-90 sm:text-base"
onClick={(e) => handleExampleClick(path, e)}
>
{name}
Expand All @@ -236,7 +236,7 @@ export default function MainCard({
{/* Decorative Sparkle */}
<div className="absolute -bottom-8 -left-12 hidden sm:block">
<Sparkles
className="h-20 w-20 fill-sky-400 text-black"
className="h-20 w-20 fill-primary text-foreground"
strokeWidth={0.6}
style={{ transform: "rotate(-15deg)" }}
/>
Expand Down
18 changes: 9 additions & 9 deletions src/components/private-repos-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export function PrivateReposDialog({

return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="border-[3px] border-black bg-purple-200 p-6 shadow-[8px_8px_0_0_#000000] sm:max-w-md">
<DialogContent className="border-[3px] border bg-card p-6 shadow-[8px_8px_0_0_#000000] sm:max-w-md">
<DialogHeader>
<DialogTitle className="text-xl font-bold text-black">
<DialogTitle className="text-xl font-bold text-foreground">
Enter GitHub Personal Access Token
</DialogTitle>
</DialogHeader>
Expand All @@ -52,14 +52,14 @@ export function PrivateReposDialog({
locally in your browser. Find out how{" "}
<Link
href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens"
className="text-purple-600 transition-colors duration-200 hover:text-purple-500"
className="text-primary transition-opacity duration-200 hover:opacity-80"
>
here
</Link>
.
</div>
<details className="group text-sm [&>summary:focus-visible]:outline-none">
<summary className="cursor-pointer font-medium text-purple-700 hover:text-purple-600">
<summary className="cursor-pointer font-medium text-primary hover:opacity-80">
Data storage disclaimer
</summary>
<div className="animate-accordion-down mt-2 space-y-2 overflow-hidden pl-2">
Expand All @@ -69,7 +69,7 @@ export function PrivateReposDialog({
self-host this app by following the instructions in the{" "}
<Link
href="https://github.com/ahmedkhaleel2004/gitdiagram"
className="text-purple-600 transition-colors duration-200 hover:text-purple-500"
className="text-primary transition-opacity duration-200 hover:opacity-80"
>
README
</Link>
Expand All @@ -82,29 +82,29 @@ export function PrivateReposDialog({
placeholder="ghp_..."
value={pat}
onChange={(e) => setPat(e.target.value)}
className="flex-1 rounded-md border-[3px] border-black px-3 py-2 text-base font-bold shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-gray-700"
className="flex-1 rounded-md border-[3px] border px-3 py-2 text-base font-bold text-foreground shadow-[4px_4px_0_0_#000000] placeholder:text-base placeholder:font-normal placeholder:text-muted-foreground"
required
/>
<div className="flex items-center justify-between">
<button
type="button"
onClick={handleClear}
className="text-sm text-purple-600 hover:text-purple-500"
className="text-sm text-primary hover:opacity-80"
>
Clear
</button>
<div className="flex gap-3">
<Button
type="button"
onClick={onClose}
className="border-[3px] border-black bg-gray-200 px-4 py-2 text-black shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-gray-300"
className="border-[3px] border bg-secondary px-4 py-2 text-foreground shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:opacity-90"
>
Cancel
</Button>
<Button
type="submit"
disabled={!pat.startsWith("ghp_")}
className="border-[3px] border-black bg-purple-400 px-4 py-2 text-black shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-purple-300 disabled:opacity-50"
className="border-[3px] border bg-primary px-4 py-2 text-primary-foreground shadow-[4px_4px_0_0_#000000] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:opacity-90 disabled:opacity-50"
>
Save Token
</Button>
Expand Down
Loading