@naeil/ui 0.1.0 → 0.3.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.
Files changed (170) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/DESIGN.md +186 -0
  3. package/LICENSE +21 -0
  4. package/README.md +85 -22
  5. package/THIRD_PARTY_NOTICES.md +49 -0
  6. package/dist/chunk-JBTDRVZ7.js +46 -0
  7. package/dist/chunk-QKVN6Q6C.js +438 -0
  8. package/dist/components/index.d.ts +87 -0
  9. package/dist/components/index.js +832 -0
  10. package/dist/components/ui/index.d.ts +105 -0
  11. package/dist/components/ui/index.js +736 -0
  12. package/dist/lib/utils.d.ts +5 -0
  13. package/dist/lib/utils.js +7 -0
  14. package/dist/source/components/footer.d.ts +15 -0
  15. package/dist/source/components/footer.js +172 -0
  16. package/dist/source/components/index.d.ts +11 -0
  17. package/dist/source/components/index.js +26 -0
  18. package/dist/source/components/locale-switcher.d.ts +17 -0
  19. package/dist/source/components/locale-switcher.js +66 -0
  20. package/dist/source/components/logo.d.ts +9 -0
  21. package/dist/source/components/logo.js +52 -0
  22. package/dist/source/components/nav.d.ts +39 -0
  23. package/dist/source/components/nav.js +412 -0
  24. package/dist/source/components/theme-provider.d.ts +6 -0
  25. package/dist/source/components/theme-provider.js +45 -0
  26. package/dist/source/components/theme-toggle-icon.d.ts +5 -0
  27. package/dist/source/components/theme-toggle-icon.js +62 -0
  28. package/dist/source/components/theme-toggle.d.ts +5 -0
  29. package/dist/source/components/theme-toggle.js +34 -0
  30. package/dist/source/components/typography.d.ts +12 -0
  31. package/dist/source/components/typography.js +49 -0
  32. package/dist/source/components/ui/avatar.d.ts +14 -0
  33. package/dist/source/components/ui/avatar.js +137 -0
  34. package/dist/source/components/ui/badge.d.ts +13 -0
  35. package/dist/source/components/ui/badge.js +76 -0
  36. package/dist/source/components/ui/button.d.ts +14 -0
  37. package/dist/source/components/ui/button.js +88 -0
  38. package/dist/source/components/ui/card.d.ts +13 -0
  39. package/dist/source/components/ui/card.js +135 -0
  40. package/dist/source/components/ui/checkbox.d.ts +7 -0
  41. package/dist/source/components/ui/checkbox.js +58 -0
  42. package/dist/source/components/ui/dialog.d.ts +20 -0
  43. package/dist/source/components/ui/dialog.js +182 -0
  44. package/dist/source/components/ui/dropdown-menu.d.ts +28 -0
  45. package/dist/source/components/ui/dropdown-menu.js +282 -0
  46. package/dist/source/components/ui/index.d.ts +19 -0
  47. package/dist/source/components/ui/index.js +131 -0
  48. package/dist/source/components/ui/input.d.ts +6 -0
  49. package/dist/source/components/ui/input.js +49 -0
  50. package/dist/source/components/ui/select.d.ts +16 -0
  51. package/dist/source/components/ui/select.js +197 -0
  52. package/dist/source/components/ui/sonner.d.ts +6 -0
  53. package/dist/source/components/ui/sonner.js +80 -0
  54. package/dist/source/components/ui/switch.d.ts +7 -0
  55. package/dist/source/components/ui/switch.js +54 -0
  56. package/dist/source/components/ui/tabs.d.ts +10 -0
  57. package/dist/source/components/ui/tabs.js +57 -0
  58. package/dist/source/components/ui/textarea.d.ts +6 -0
  59. package/dist/source/components/ui/textarea.js +46 -0
  60. package/dist/source/i18n/config.d.ts +6 -0
  61. package/dist/source/i18n/config.js +12 -0
  62. package/dist/source/i18n/routing.d.ts +371 -0
  63. package/dist/source/i18n/routing.js +17 -0
  64. package/dist/source/lib/design/modal-inert.d.ts +6 -0
  65. package/dist/source/lib/design/modal-inert.js +73 -0
  66. package/dist/source/lib/utils.d.ts +5 -0
  67. package/dist/source/lib/utils.js +8 -0
  68. package/dist/tokens.ts +217 -0
  69. package/docs/components/README.md +21 -0
  70. package/docs/components/avatar.md +47 -0
  71. package/docs/components/badge.md +29 -0
  72. package/docs/components/button.md +39 -0
  73. package/docs/components/card.md +32 -0
  74. package/docs/components/checkbox.md +35 -0
  75. package/docs/components/common.md +31 -0
  76. package/docs/components/composition.md +68 -0
  77. package/docs/components/dialog.md +41 -0
  78. package/docs/components/dropdown-menu.md +38 -0
  79. package/docs/components/input.md +32 -0
  80. package/docs/components/select.md +39 -0
  81. package/docs/components/sonner.md +37 -0
  82. package/docs/components/switch.md +33 -0
  83. package/docs/components/tabs.md +40 -0
  84. package/docs/components/textarea.md +35 -0
  85. package/docs/design/component-demand.md +30 -0
  86. package/docs/design/public-ui-final-verification.md +96 -0
  87. package/docs/design/public-ui-release-validation.md +86 -0
  88. package/docs/design/public-ui-stage3-verification.md +98 -0
  89. package/docs/design/public-ui-stage4-verification.md +129 -0
  90. package/docs/design/public-ui-support.md +40 -0
  91. package/docs/design/v2-migration.md +199 -0
  92. package/docs/package-release.md +89 -0
  93. package/licenses/Noto-Sans-JP-OFL.txt +93 -0
  94. package/licenses/Nunito-Sans-OFL.txt +93 -0
  95. package/licenses/Pretendard-OFL.txt +94 -0
  96. package/licenses/React-MIT.txt +21 -0
  97. package/licenses/Storybook-MIT.txt +21 -0
  98. package/licenses/popper-MIT.txt +25 -0
  99. package/licenses/react-remove-scroll-bar-MIT.txt +21 -0
  100. package/licenses/store2-MIT.txt +22 -0
  101. package/licenses/toggle-selection-MIT.txt +21 -0
  102. package/package.json +215 -26
  103. package/src/styles/components.css +276 -0
  104. package/src/styles/globals.css +18 -0
  105. package/src/styles/theme.css +293 -0
  106. package/messages/en.json +0 -221
  107. package/messages/ja.json +0 -221
  108. package/messages/ko.json +0 -221
  109. package/postcss.config.mjs +0 -7
  110. package/src/app/[locale]/(content)/baby-agent/page.tsx +0 -30
  111. package/src/app/[locale]/(content)/blog/page.tsx +0 -68
  112. package/src/app/[locale]/(content)/cc/page.tsx +0 -216
  113. package/src/app/[locale]/(content)/design/page.tsx +0 -703
  114. package/src/app/[locale]/(content)/layout.tsx +0 -7
  115. package/src/app/[locale]/(content)/pkm/page.tsx +0 -31
  116. package/src/app/[locale]/(content)/projects/page.tsx +0 -100
  117. package/src/app/[locale]/(content)/sa/page.tsx +0 -179
  118. package/src/app/[locale]/(landing)/page.tsx +0 -157
  119. package/src/app/[locale]/layout.tsx +0 -72
  120. package/src/app/[locale]/login/actions.ts +0 -10
  121. package/src/app/[locale]/login/oauth-buttons.tsx +0 -78
  122. package/src/app/[locale]/login/page.tsx +0 -85
  123. package/src/app/auth/callback/route.ts +0 -78
  124. package/src/app/favicon.ico +0 -0
  125. package/src/app/globals.css +0 -32
  126. package/src/app/layout.tsx +0 -22
  127. package/src/app/sitemap.ts +0 -26
  128. package/src/components/accent-picker.tsx +0 -62
  129. package/src/components/auth-slot.tsx +0 -96
  130. package/src/components/footer-wrapper.tsx +0 -8
  131. package/src/components/footer.tsx +0 -193
  132. package/src/components/hero-scene.tsx +0 -948
  133. package/src/components/hero-section.tsx +0 -57
  134. package/src/components/locale-switcher.tsx +0 -74
  135. package/src/components/logo.tsx +0 -51
  136. package/src/components/nav-server-wrapper.tsx +0 -22
  137. package/src/components/nav-wrapper.tsx +0 -45
  138. package/src/components/nav.tsx +0 -550
  139. package/src/components/paraglider-cursor.tsx +0 -112
  140. package/src/components/project-layout.tsx +0 -194
  141. package/src/components/theme-provider.tsx +0 -10
  142. package/src/components/theme-toggle-icon.tsx +0 -62
  143. package/src/components/theme-toggle.tsx +0 -39
  144. package/src/components/typography.tsx +0 -27
  145. package/src/components/ui/avatar.tsx +0 -109
  146. package/src/components/ui/badge.tsx +0 -47
  147. package/src/components/ui/button.tsx +0 -65
  148. package/src/components/ui/card.tsx +0 -106
  149. package/src/components/ui/dialog.tsx +0 -158
  150. package/src/components/ui/dropdown-menu.tsx +0 -257
  151. package/src/components/ui/input.tsx +0 -21
  152. package/src/components/ui/sonner.tsx +0 -54
  153. package/src/components/workflow-diagram.tsx +0 -414
  154. package/src/i18n/config.ts +0 -8
  155. package/src/i18n/request.ts +0 -16
  156. package/src/i18n/routing.ts +0 -12
  157. package/src/lib/supabase/client.ts +0 -18
  158. package/src/lib/supabase/middleware.ts +0 -42
  159. package/src/lib/supabase/server.ts +0 -37
  160. package/src/lib/utils.ts +0 -6
  161. package/src/proxy.ts +0 -62
  162. package/src/tokens/.gitkeep +0 -0
  163. package/src/tokens/color.json +0 -56
  164. package/src/tokens/radius.json +0 -9
  165. package/src/tokens/semantic.json +0 -81
  166. package/src/tokens/shadow.json +0 -7
  167. package/src/tokens/spacing.json +0 -16
  168. package/src/tokens/typography.json +0 -34
  169. package/src/types/i18n.d.ts +0 -6
  170. package/src/utils/.gitkeep +0 -0
@@ -1,7 +0,0 @@
1
- export default function ContentLayout({
2
- children,
3
- }: {
4
- children: React.ReactNode;
5
- }) {
6
- return <main className="pt-28">{children}</main>;
7
- }
@@ -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
- }