@naeil/ui 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -21
- package/dist/chunk-JBTDRVZ7.js +46 -0
- package/dist/chunk-SYI3IKIC.js +355 -0
- package/dist/components/index.d.ts +87 -0
- package/dist/components/index.js +832 -0
- package/dist/components/ui/index.d.ts +83 -0
- package/dist/components/ui/index.js +481 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +7 -0
- package/package.json +52 -11
- package/src/app/globals.css +1 -1
- package/src/styles/theme.css +206 -0
- package/postcss.config.mjs +0 -7
- package/src/app/[locale]/(content)/baby-agent/page.tsx +0 -30
- package/src/app/[locale]/(content)/blog/page.tsx +0 -68
- package/src/app/[locale]/(content)/cc/page.tsx +0 -216
- package/src/app/[locale]/(content)/design/page.tsx +0 -703
- package/src/app/[locale]/(content)/layout.tsx +0 -7
- package/src/app/[locale]/(content)/pkm/page.tsx +0 -31
- package/src/app/[locale]/(content)/projects/page.tsx +0 -100
- package/src/app/[locale]/(content)/sa/page.tsx +0 -179
- package/src/app/[locale]/(landing)/page.tsx +0 -157
- package/src/app/[locale]/layout.tsx +0 -72
- package/src/app/[locale]/login/actions.ts +0 -10
- package/src/app/[locale]/login/oauth-buttons.tsx +0 -78
- package/src/app/[locale]/login/page.tsx +0 -85
- package/src/app/auth/callback/route.ts +0 -78
- package/src/app/favicon.ico +0 -0
- package/src/app/layout.tsx +0 -22
- package/src/app/sitemap.ts +0 -26
- package/src/components/accent-picker.tsx +0 -62
- package/src/components/auth-slot.tsx +0 -96
- package/src/components/footer-wrapper.tsx +0 -8
- package/src/components/footer.tsx +0 -193
- package/src/components/hero-scene.tsx +0 -948
- package/src/components/hero-section.tsx +0 -57
- package/src/components/locale-switcher.tsx +0 -74
- package/src/components/logo.tsx +0 -51
- package/src/components/nav-server-wrapper.tsx +0 -22
- package/src/components/nav-wrapper.tsx +0 -45
- package/src/components/nav.tsx +0 -550
- package/src/components/paraglider-cursor.tsx +0 -112
- package/src/components/project-layout.tsx +0 -194
- package/src/components/theme-provider.tsx +0 -10
- package/src/components/theme-toggle-icon.tsx +0 -62
- package/src/components/theme-toggle.tsx +0 -39
- package/src/components/typography.tsx +0 -27
- package/src/components/ui/avatar.tsx +0 -109
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -65
- package/src/components/ui/card.tsx +0 -106
- package/src/components/ui/dialog.tsx +0 -158
- package/src/components/ui/dropdown-menu.tsx +0 -257
- package/src/components/ui/input.tsx +0 -21
- package/src/components/ui/sonner.tsx +0 -54
- package/src/components/workflow-diagram.tsx +0 -414
- package/src/i18n/config.ts +0 -8
- package/src/i18n/request.ts +0 -16
- package/src/i18n/routing.ts +0 -12
- package/src/lib/supabase/client.ts +0 -18
- package/src/lib/supabase/middleware.ts +0 -42
- package/src/lib/supabase/server.ts +0 -37
- package/src/lib/utils.ts +0 -6
- package/src/proxy.ts +0 -62
- package/src/tokens/.gitkeep +0 -0
- package/src/tokens/color.json +0 -56
- package/src/tokens/radius.json +0 -9
- package/src/tokens/semantic.json +0 -81
- package/src/tokens/shadow.json +0 -7
- package/src/tokens/spacing.json +0 -16
- package/src/tokens/typography.json +0 -34
- package/src/types/i18n.d.ts +0 -6
- package/src/utils/.gitkeep +0 -0
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { setRequestLocale } from "next-intl/server";
|
|
2
|
-
import { useTranslations } from "next-intl";
|
|
3
|
-
import { ProjectLayout } from "@/components/project-layout";
|
|
4
|
-
import type { ProjectData } from "@/components/project-layout";
|
|
5
|
-
|
|
6
|
-
export default async function PKMPage({
|
|
7
|
-
params,
|
|
8
|
-
}: {
|
|
9
|
-
params: Promise<{ locale: string }>;
|
|
10
|
-
}) {
|
|
11
|
-
const { locale } = await params;
|
|
12
|
-
setRequestLocale(locale);
|
|
13
|
-
|
|
14
|
-
return <PKMContent />;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function PKMContent() {
|
|
18
|
-
const t = useTranslations("pkm");
|
|
19
|
-
|
|
20
|
-
const data: ProjectData = {
|
|
21
|
-
icon: "/images/jellyfish.png",
|
|
22
|
-
name: t("name"),
|
|
23
|
-
description: t("description"),
|
|
24
|
-
accent: "oklch(0.627 0.265 303)",
|
|
25
|
-
tags: ["Python", "Embeddings", "Semantic Search"],
|
|
26
|
-
github: "https://github.com/naeil-dev/pkm",
|
|
27
|
-
comingSoon: true,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return <ProjectLayout data={data} />;
|
|
31
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { setRequestLocale } from "next-intl/server";
|
|
2
|
-
import { useTranslations } from "next-intl";
|
|
3
|
-
import { Link } from "@/i18n/routing";
|
|
4
|
-
import {
|
|
5
|
-
Card,
|
|
6
|
-
CardHeader,
|
|
7
|
-
CardTitle,
|
|
8
|
-
CardDescription,
|
|
9
|
-
CardContent,
|
|
10
|
-
} from "@/components/ui/card";
|
|
11
|
-
import { Badge } from "@/components/ui/badge";
|
|
12
|
-
import { PageTitle } from "@/components/typography";
|
|
13
|
-
|
|
14
|
-
export default async function ProjectsPage({
|
|
15
|
-
params,
|
|
16
|
-
}: {
|
|
17
|
-
params: Promise<{ locale: string }>;
|
|
18
|
-
}) {
|
|
19
|
-
const { locale } = await params;
|
|
20
|
-
setRequestLocale(locale);
|
|
21
|
-
|
|
22
|
-
return <ProjectsContent />;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function ProjectsContent() {
|
|
26
|
-
const t = useTranslations();
|
|
27
|
-
|
|
28
|
-
const PROJECTS = [
|
|
29
|
-
{
|
|
30
|
-
name: t("projects.cc.name"),
|
|
31
|
-
description: t("projects.cc.description"),
|
|
32
|
-
href: "/cc" as const,
|
|
33
|
-
tags: ["Python", "RSS", "AI"],
|
|
34
|
-
status: t("projects.status.active"),
|
|
35
|
-
coming: false,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: t("projects.pkm.name"),
|
|
39
|
-
description: t("projects.pkm.description"),
|
|
40
|
-
href: "/pkm" as const,
|
|
41
|
-
tags: ["Python", "Embeddings", "Search"],
|
|
42
|
-
status: t("projects.status.coming"),
|
|
43
|
-
coming: true,
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: t("projects.naeilUi.name"),
|
|
47
|
-
description: t("projects.naeilUi.description"),
|
|
48
|
-
href: "/design" as const,
|
|
49
|
-
tags: ["React", "Tailwind", "Design Tokens"],
|
|
50
|
-
status: t("projects.status.active"),
|
|
51
|
-
coming: false,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: t("projects.babyAgent.name"),
|
|
55
|
-
description: t("projects.babyAgent.description"),
|
|
56
|
-
href: "/baby-agent" as const,
|
|
57
|
-
tags: ["OpenClaw", "LINE", "AI"],
|
|
58
|
-
status: t("projects.status.coming"),
|
|
59
|
-
coming: true,
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<section className="mx-auto max-w-4xl px-6 pb-24">
|
|
65
|
-
<div className="mb-10">
|
|
66
|
-
<PageTitle className="mb-3">{t("projects.title")}</PageTitle>
|
|
67
|
-
<p className="text-muted-foreground text-lg">
|
|
68
|
-
{t("projects.subtitle")}
|
|
69
|
-
</p>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<div className="grid gap-4">
|
|
73
|
-
{PROJECTS.map((project) => (
|
|
74
|
-
<Link key={project.name} href={project.href}>
|
|
75
|
-
<Card className="transition-colors hover:border-foreground/20">
|
|
76
|
-
<CardHeader>
|
|
77
|
-
<div className="flex items-center justify-between">
|
|
78
|
-
<CardTitle className="text-base">{project.name}</CardTitle>
|
|
79
|
-
<Badge variant={project.coming ? "outline" : "secondary"}>
|
|
80
|
-
{project.status}
|
|
81
|
-
</Badge>
|
|
82
|
-
</div>
|
|
83
|
-
<CardDescription>{project.description}</CardDescription>
|
|
84
|
-
</CardHeader>
|
|
85
|
-
<CardContent>
|
|
86
|
-
<div className="flex flex-wrap gap-1.5">
|
|
87
|
-
{project.tags.map((tag) => (
|
|
88
|
-
<Badge key={tag} variant="outline" className="text-xs">
|
|
89
|
-
{tag}
|
|
90
|
-
</Badge>
|
|
91
|
-
))}
|
|
92
|
-
</div>
|
|
93
|
-
</CardContent>
|
|
94
|
-
</Card>
|
|
95
|
-
</Link>
|
|
96
|
-
))}
|
|
97
|
-
</div>
|
|
98
|
-
</section>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { setRequestLocale } from "next-intl/server";
|
|
2
|
-
import { useTranslations } from "next-intl";
|
|
3
|
-
import { ProjectLayout } from "@/components/project-layout";
|
|
4
|
-
import type { ProjectData } from "@/components/project-layout";
|
|
5
|
-
|
|
6
|
-
export default async function SAPage({
|
|
7
|
-
params,
|
|
8
|
-
}: {
|
|
9
|
-
params: Promise<{ locale: string }>;
|
|
10
|
-
}) {
|
|
11
|
-
const { locale } = await params;
|
|
12
|
-
setRequestLocale(locale);
|
|
13
|
-
|
|
14
|
-
return <SAContent />;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function SAContent() {
|
|
18
|
-
const t = useTranslations("sa");
|
|
19
|
-
|
|
20
|
-
const data: ProjectData = {
|
|
21
|
-
icon: "/images/fish.png",
|
|
22
|
-
name: t("name"),
|
|
23
|
-
description: t("description"),
|
|
24
|
-
accent: "oklch(0.723 0.219 149)",
|
|
25
|
-
tags: ["Python", "PostgreSQL", "pgvector", "BGE-M3", "Claude", "Docling"],
|
|
26
|
-
github: "https://github.com/naeil-dev/sustainability-analyzer",
|
|
27
|
-
|
|
28
|
-
overview: t("overview"),
|
|
29
|
-
|
|
30
|
-
features: [
|
|
31
|
-
{
|
|
32
|
-
icon: "📄",
|
|
33
|
-
title: t("features.ingest.title"),
|
|
34
|
-
description: t("features.ingest.desc"),
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
icon: "🔍",
|
|
38
|
-
title: t("features.embed.title"),
|
|
39
|
-
description: t("features.embed.desc"),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
icon: "🗂️",
|
|
43
|
-
title: t("features.topics.title"),
|
|
44
|
-
description: t("features.topics.desc"),
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
icon: "📊",
|
|
48
|
-
title: t("features.assess.title"),
|
|
49
|
-
description: t("features.assess.desc"),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
icon: "⚖️",
|
|
53
|
-
title: t("features.compare.title"),
|
|
54
|
-
description: t("features.compare.desc"),
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
icon: "⚡",
|
|
58
|
-
title: t("features.zeroCost.title"),
|
|
59
|
-
description: t("features.zeroCost.desc"),
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
|
|
63
|
-
workflow: {
|
|
64
|
-
nodes: [
|
|
65
|
-
{
|
|
66
|
-
id: "pdf",
|
|
67
|
-
label: "ESG Reports",
|
|
68
|
-
type: "source",
|
|
69
|
-
position: { x: 50, y: 5 },
|
|
70
|
-
detail: {
|
|
71
|
-
description: t("workflow.pdf.desc"),
|
|
72
|
-
tech: ["PDF"],
|
|
73
|
-
meta: t("workflow.pdf.meta"),
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: "docling",
|
|
78
|
-
label: "Docling Parser",
|
|
79
|
-
type: "process",
|
|
80
|
-
position: { x: 50, y: 18 },
|
|
81
|
-
detail: {
|
|
82
|
-
description: t("workflow.docling.desc"),
|
|
83
|
-
tech: ["Docling", "Python"],
|
|
84
|
-
meta: t("workflow.docling.meta"),
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: "chunk",
|
|
89
|
-
label: "Hierarchical Chunker",
|
|
90
|
-
type: "process",
|
|
91
|
-
position: { x: 50, y: 32 },
|
|
92
|
-
detail: {
|
|
93
|
-
description: t("workflow.chunk.desc"),
|
|
94
|
-
tech: ["Parent-Child", "Layout-aware"],
|
|
95
|
-
meta: t("workflow.chunk.meta"),
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: "bgem3",
|
|
100
|
-
label: "BGE-M3",
|
|
101
|
-
type: "ai",
|
|
102
|
-
position: { x: 25, y: 46 },
|
|
103
|
-
detail: {
|
|
104
|
-
description: t("workflow.bgem3.desc"),
|
|
105
|
-
tech: ["FlagEmbedding", "PyTorch"],
|
|
106
|
-
meta: t("workflow.bgem3.meta"),
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
id: "pgvector",
|
|
111
|
-
label: "PostgreSQL",
|
|
112
|
-
type: "storage",
|
|
113
|
-
position: { x: 75, y: 46 },
|
|
114
|
-
detail: {
|
|
115
|
-
description: t("workflow.pgvector.desc"),
|
|
116
|
-
tech: ["PostgreSQL 17", "pgvector", "Docker"],
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
id: "search",
|
|
121
|
-
label: "Hybrid Search",
|
|
122
|
-
type: "process",
|
|
123
|
-
position: { x: 50, y: 60 },
|
|
124
|
-
detail: {
|
|
125
|
-
description: t("workflow.search.desc"),
|
|
126
|
-
tech: ["Dense", "Sparse", "pgvector"],
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
id: "claude",
|
|
131
|
-
label: "Claude Sonnet",
|
|
132
|
-
type: "ai",
|
|
133
|
-
position: { x: 50, y: 74 },
|
|
134
|
-
detail: {
|
|
135
|
-
description: t("workflow.claude.desc"),
|
|
136
|
-
tech: ["Claude Sonnet", "Anthropic API"],
|
|
137
|
-
meta: t("workflow.claude.meta"),
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: "report",
|
|
142
|
-
label: "Comparison Report",
|
|
143
|
-
type: "output",
|
|
144
|
-
position: { x: 50, y: 90 },
|
|
145
|
-
detail: {
|
|
146
|
-
description: t("workflow.report.desc"),
|
|
147
|
-
tech: ["Jinja2", "HTML"],
|
|
148
|
-
meta: t("workflow.report.meta"),
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
edges: [
|
|
153
|
-
{ from: "pdf", to: "docling" },
|
|
154
|
-
{ from: "docling", to: "chunk" },
|
|
155
|
-
{ from: "chunk", to: "bgem3" },
|
|
156
|
-
{ from: "chunk", to: "pgvector" },
|
|
157
|
-
{ from: "bgem3", to: "pgvector" },
|
|
158
|
-
{ from: "pgvector", to: "search" },
|
|
159
|
-
{ from: "search", to: "claude" },
|
|
160
|
-
{ from: "claude", to: "pgvector" },
|
|
161
|
-
{ from: "claude", to: "report" },
|
|
162
|
-
],
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
stack: [
|
|
166
|
-
{ icon: "🐍", name: "Python 3.12" },
|
|
167
|
-
{ icon: "🐘", name: "PostgreSQL 17" },
|
|
168
|
-
{ icon: "🧮", name: "pgvector 0.8" },
|
|
169
|
-
{ icon: "🔤", name: "BGE-M3" },
|
|
170
|
-
{ icon: "🧠", name: "Claude Sonnet" },
|
|
171
|
-
{ icon: "📄", name: "Docling" },
|
|
172
|
-
{ icon: "🔥", name: "PyTorch" },
|
|
173
|
-
{ icon: "🐳", name: "Docker Compose" },
|
|
174
|
-
{ icon: "📊", name: "Jinja2" },
|
|
175
|
-
],
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
return <ProjectLayout data={data} />;
|
|
179
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { setRequestLocale } from "next-intl/server";
|
|
2
|
-
import { useTranslations } from "next-intl";
|
|
3
|
-
import { Link } from "@/i18n/routing";
|
|
4
|
-
import { Badge } from "@/components/ui/badge";
|
|
5
|
-
import { HeroSection } from "@/components/hero-section";
|
|
6
|
-
import Image from "next/image";
|
|
7
|
-
|
|
8
|
-
export default async function HubPage({
|
|
9
|
-
params,
|
|
10
|
-
}: {
|
|
11
|
-
params: Promise<{ locale: string }>;
|
|
12
|
-
}) {
|
|
13
|
-
const { locale } = await params;
|
|
14
|
-
setRequestLocale(locale);
|
|
15
|
-
|
|
16
|
-
return <HubContent />;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const PROJECTS = [
|
|
20
|
-
{
|
|
21
|
-
key: "cc" as const,
|
|
22
|
-
href: "/cc" as const,
|
|
23
|
-
icon: "/images/coral.png",
|
|
24
|
-
accent: "oklch(0.704 0.140 181)",
|
|
25
|
-
tags: ["Python", "RSS", "AI"],
|
|
26
|
-
status: "active" as const,
|
|
27
|
-
featured: true,
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
key: "pkm" as const,
|
|
31
|
-
href: "/pkm" as const,
|
|
32
|
-
icon: "/images/jellyfish.png",
|
|
33
|
-
accent: "oklch(0.702 0.183 293)",
|
|
34
|
-
tags: ["Python", "Embeddings", "Search"],
|
|
35
|
-
status: "coming" as const,
|
|
36
|
-
featured: false,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
key: "naeilUi" as const,
|
|
40
|
-
href: "/design" as const,
|
|
41
|
-
icon: "/images/whale.png",
|
|
42
|
-
accent: "oklch(0.623 0.214 259)",
|
|
43
|
-
tags: ["React", "Tailwind", "OKLCH"],
|
|
44
|
-
status: "active" as const,
|
|
45
|
-
featured: false,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
key: "sa" as const,
|
|
49
|
-
href: "/sa" as const,
|
|
50
|
-
icon: "/images/fish.png",
|
|
51
|
-
accent: "oklch(0.723 0.219 149)",
|
|
52
|
-
tags: ["Python", "pgvector", "Claude"],
|
|
53
|
-
status: "active" as const,
|
|
54
|
-
featured: false,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
key: "babyAgent" as const,
|
|
58
|
-
href: "/baby-agent" as const,
|
|
59
|
-
icon: "/images/turtle.png",
|
|
60
|
-
accent: "oklch(0.769 0.188 70.08)",
|
|
61
|
-
tags: ["LINE", "AI"],
|
|
62
|
-
status: "coming" as const,
|
|
63
|
-
featured: false,
|
|
64
|
-
},
|
|
65
|
-
] as const;
|
|
66
|
-
|
|
67
|
-
function HubContent() {
|
|
68
|
-
const t = useTranslations();
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<>
|
|
72
|
-
{/* Hero */}
|
|
73
|
-
<HeroSection />
|
|
74
|
-
|
|
75
|
-
{/* Projects — Bento Grid */}
|
|
76
|
-
<section className="mx-auto max-w-4xl px-6 py-24">
|
|
77
|
-
<h2 className="text-foreground mb-2 text-2xl font-bold tracking-tight">
|
|
78
|
-
{t("projects.title")}
|
|
79
|
-
</h2>
|
|
80
|
-
<p className="text-muted-foreground mb-10 text-sm">
|
|
81
|
-
{t("projects.subtitle")}
|
|
82
|
-
</p>
|
|
83
|
-
|
|
84
|
-
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
85
|
-
{PROJECTS.map((project) => (
|
|
86
|
-
<Link
|
|
87
|
-
key={project.key}
|
|
88
|
-
href={project.href}
|
|
89
|
-
className={[
|
|
90
|
-
"group relative rounded-2xl border border-border-subtle bg-surface-subtle p-7 transition-all duration-250",
|
|
91
|
-
"hover:-translate-y-0.5 hover:border-border-subtle-hover hover:shadow-[0_8px_32px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_8px_32px_rgba(0,0,0,0.3)]",
|
|
92
|
-
project.featured ? "sm:col-span-2 sm:flex sm:items-center sm:gap-8" : "",
|
|
93
|
-
].join(" ")}
|
|
94
|
-
style={{ "--accent": project.accent } as React.CSSProperties}
|
|
95
|
-
>
|
|
96
|
-
{/* Icon */}
|
|
97
|
-
<div
|
|
98
|
-
className={[
|
|
99
|
-
"flex shrink-0 items-center justify-center rounded-[14px]",
|
|
100
|
-
project.featured
|
|
101
|
-
? "mb-5 h-14 w-14 sm:mb-0 sm:h-20 sm:w-20"
|
|
102
|
-
: "mb-5 h-14 w-14",
|
|
103
|
-
].join(" ")}
|
|
104
|
-
style={{
|
|
105
|
-
background: `color-mix(in oklch, ${project.accent} 8%, transparent)`,
|
|
106
|
-
border: `1px solid color-mix(in oklch, ${project.accent} 20%, transparent)`,
|
|
107
|
-
}}
|
|
108
|
-
>
|
|
109
|
-
<Image
|
|
110
|
-
src={project.icon}
|
|
111
|
-
alt=""
|
|
112
|
-
width={project.featured ? 52 : 36}
|
|
113
|
-
height={project.featured ? 52 : 36}
|
|
114
|
-
className="object-contain"
|
|
115
|
-
/>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
{/* Text */}
|
|
119
|
-
<div className={project.featured ? "flex-1" : ""}>
|
|
120
|
-
<div className="text-foreground mb-1.5 text-[15px] font-semibold">
|
|
121
|
-
{t(`projects.${project.key}.name`)}
|
|
122
|
-
{project.status === "coming" && (
|
|
123
|
-
<Badge
|
|
124
|
-
variant="outline"
|
|
125
|
-
className="text-muted-foreground ml-2 px-1.5 py-0 text-[9px] align-middle"
|
|
126
|
-
>
|
|
127
|
-
{t("projects.status.coming")}
|
|
128
|
-
</Badge>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
<p className="text-muted-foreground mb-3 text-[13px] leading-relaxed">
|
|
132
|
-
{t(`projects.${project.key}.description`)}
|
|
133
|
-
</p>
|
|
134
|
-
<div className="flex flex-wrap gap-1.5">
|
|
135
|
-
{project.tags.map((tag) => (
|
|
136
|
-
<span
|
|
137
|
-
key={tag}
|
|
138
|
-
className="rounded-full border border-border-subtle px-2 py-0.5 text-[10px] text-zinc-400"
|
|
139
|
-
>
|
|
140
|
-
{tag}
|
|
141
|
-
</span>
|
|
142
|
-
))}
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
{/* Arrow */}
|
|
147
|
-
<span className="text-muted-foreground/40 group-hover:text-foreground absolute right-6 top-6 text-sm transition-colors">
|
|
148
|
-
↗
|
|
149
|
-
</span>
|
|
150
|
-
</Link>
|
|
151
|
-
))}
|
|
152
|
-
</div>
|
|
153
|
-
</section>
|
|
154
|
-
|
|
155
|
-
</>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from "next";
|
|
2
|
-
import { NextIntlClientProvider } from "next-intl";
|
|
3
|
-
import {
|
|
4
|
-
getMessages,
|
|
5
|
-
getTranslations,
|
|
6
|
-
setRequestLocale,
|
|
7
|
-
} from "next-intl/server";
|
|
8
|
-
import { notFound } from "next/navigation";
|
|
9
|
-
import { ThemeProvider } from "@/components/theme-provider";
|
|
10
|
-
import { Toaster } from "@/components/ui/sonner";
|
|
11
|
-
import { NavServerWrapper } from "@/components/nav-server-wrapper";
|
|
12
|
-
import { FooterWrapper } from "@/components/footer-wrapper";
|
|
13
|
-
import { locales, type Locale } from "@/i18n/config";
|
|
14
|
-
|
|
15
|
-
export function generateStaticParams() {
|
|
16
|
-
return locales.map((locale) => ({ locale }));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function generateMetadata({
|
|
20
|
-
params,
|
|
21
|
-
}: {
|
|
22
|
-
params: Promise<{ locale: string }>;
|
|
23
|
-
}): Promise<Metadata> {
|
|
24
|
-
const { locale } = await params;
|
|
25
|
-
const t = await getTranslations({ locale, namespace: "common" });
|
|
26
|
-
return {
|
|
27
|
-
title: t("appName"),
|
|
28
|
-
description: "Personal projects by Jay",
|
|
29
|
-
alternates: {
|
|
30
|
-
canonical: `https://naeil.dev/${locale}`,
|
|
31
|
-
languages: {
|
|
32
|
-
en: "https://naeil.dev/en",
|
|
33
|
-
ko: "https://naeil.dev/ko",
|
|
34
|
-
ja: "https://naeil.dev/ja",
|
|
35
|
-
"x-default": "https://naeil.dev/en",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default async function LocaleLayout({
|
|
42
|
-
children,
|
|
43
|
-
params,
|
|
44
|
-
}: {
|
|
45
|
-
children: React.ReactNode;
|
|
46
|
-
params: Promise<{ locale: string }>;
|
|
47
|
-
}) {
|
|
48
|
-
const { locale } = await params;
|
|
49
|
-
|
|
50
|
-
if (!locales.includes(locale as Locale)) {
|
|
51
|
-
notFound();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
setRequestLocale(locale);
|
|
55
|
-
const messages = await getMessages();
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<NextIntlClientProvider messages={messages}>
|
|
59
|
-
<ThemeProvider
|
|
60
|
-
attribute="class"
|
|
61
|
-
defaultTheme="dark"
|
|
62
|
-
enableSystem
|
|
63
|
-
disableTransitionOnChange
|
|
64
|
-
>
|
|
65
|
-
<NavServerWrapper />
|
|
66
|
-
<main>{children}</main>
|
|
67
|
-
<FooterWrapper />
|
|
68
|
-
<Toaster />
|
|
69
|
-
</ThemeProvider>
|
|
70
|
-
</NextIntlClientProvider>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
|
|
3
|
-
import { redirect } from "@/i18n/routing";
|
|
4
|
-
import { createClient } from "@/lib/supabase/server";
|
|
5
|
-
|
|
6
|
-
export async function logout(locale: string) {
|
|
7
|
-
const supabase = await createClient();
|
|
8
|
-
await supabase.auth.signOut();
|
|
9
|
-
redirect({ href: "/", locale });
|
|
10
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useTranslations } from "next-intl";
|
|
4
|
-
import { Button } from "@/components/ui/button";
|
|
5
|
-
import { createClient } from "@/lib/supabase/client";
|
|
6
|
-
|
|
7
|
-
export function OAuthButtons({ next }: { next?: string }) {
|
|
8
|
-
const t = useTranslations("auth");
|
|
9
|
-
|
|
10
|
-
const handleOAuth = async (provider: "google" | "github") => {
|
|
11
|
-
const supabase = createClient();
|
|
12
|
-
await supabase.auth.signInWithOAuth({
|
|
13
|
-
provider,
|
|
14
|
-
options: {
|
|
15
|
-
redirectTo: `${window.location.origin}/auth/callback`,
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="flex flex-col gap-3">
|
|
22
|
-
<Button
|
|
23
|
-
variant="outline"
|
|
24
|
-
size="lg"
|
|
25
|
-
className="w-full"
|
|
26
|
-
onClick={() => handleOAuth("google")}
|
|
27
|
-
>
|
|
28
|
-
<GoogleIcon />
|
|
29
|
-
{t("loginWithGoogle")}
|
|
30
|
-
</Button>
|
|
31
|
-
<Button
|
|
32
|
-
variant="outline"
|
|
33
|
-
size="lg"
|
|
34
|
-
className="w-full"
|
|
35
|
-
onClick={() => handleOAuth("github")}
|
|
36
|
-
>
|
|
37
|
-
<GitHubIcon />
|
|
38
|
-
{t("loginWithGithub")}
|
|
39
|
-
</Button>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function GoogleIcon() {
|
|
45
|
-
return (
|
|
46
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
|
47
|
-
<path
|
|
48
|
-
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
|
|
49
|
-
fill="#4285F4"
|
|
50
|
-
/>
|
|
51
|
-
<path
|
|
52
|
-
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
53
|
-
fill="#34A853"
|
|
54
|
-
/>
|
|
55
|
-
<path
|
|
56
|
-
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
57
|
-
fill="#FBBC05"
|
|
58
|
-
/>
|
|
59
|
-
<path
|
|
60
|
-
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
61
|
-
fill="#EA4335"
|
|
62
|
-
/>
|
|
63
|
-
</svg>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function GitHubIcon() {
|
|
68
|
-
return (
|
|
69
|
-
<svg
|
|
70
|
-
width="16"
|
|
71
|
-
height="16"
|
|
72
|
-
viewBox="0 0 24 24"
|
|
73
|
-
fill="currentColor"
|
|
74
|
-
>
|
|
75
|
-
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
|
76
|
-
</svg>
|
|
77
|
-
);
|
|
78
|
-
}
|