@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.
Files changed (73) hide show
  1. package/README.md +60 -21
  2. package/dist/chunk-JBTDRVZ7.js +46 -0
  3. package/dist/chunk-SYI3IKIC.js +355 -0
  4. package/dist/components/index.d.ts +87 -0
  5. package/dist/components/index.js +832 -0
  6. package/dist/components/ui/index.d.ts +83 -0
  7. package/dist/components/ui/index.js +481 -0
  8. package/dist/lib/utils.d.ts +5 -0
  9. package/dist/lib/utils.js +7 -0
  10. package/package.json +52 -11
  11. package/src/app/globals.css +1 -1
  12. package/src/styles/theme.css +206 -0
  13. package/postcss.config.mjs +0 -7
  14. package/src/app/[locale]/(content)/baby-agent/page.tsx +0 -30
  15. package/src/app/[locale]/(content)/blog/page.tsx +0 -68
  16. package/src/app/[locale]/(content)/cc/page.tsx +0 -216
  17. package/src/app/[locale]/(content)/design/page.tsx +0 -703
  18. package/src/app/[locale]/(content)/layout.tsx +0 -7
  19. package/src/app/[locale]/(content)/pkm/page.tsx +0 -31
  20. package/src/app/[locale]/(content)/projects/page.tsx +0 -100
  21. package/src/app/[locale]/(content)/sa/page.tsx +0 -179
  22. package/src/app/[locale]/(landing)/page.tsx +0 -157
  23. package/src/app/[locale]/layout.tsx +0 -72
  24. package/src/app/[locale]/login/actions.ts +0 -10
  25. package/src/app/[locale]/login/oauth-buttons.tsx +0 -78
  26. package/src/app/[locale]/login/page.tsx +0 -85
  27. package/src/app/auth/callback/route.ts +0 -78
  28. package/src/app/favicon.ico +0 -0
  29. package/src/app/layout.tsx +0 -22
  30. package/src/app/sitemap.ts +0 -26
  31. package/src/components/accent-picker.tsx +0 -62
  32. package/src/components/auth-slot.tsx +0 -96
  33. package/src/components/footer-wrapper.tsx +0 -8
  34. package/src/components/footer.tsx +0 -193
  35. package/src/components/hero-scene.tsx +0 -948
  36. package/src/components/hero-section.tsx +0 -57
  37. package/src/components/locale-switcher.tsx +0 -74
  38. package/src/components/logo.tsx +0 -51
  39. package/src/components/nav-server-wrapper.tsx +0 -22
  40. package/src/components/nav-wrapper.tsx +0 -45
  41. package/src/components/nav.tsx +0 -550
  42. package/src/components/paraglider-cursor.tsx +0 -112
  43. package/src/components/project-layout.tsx +0 -194
  44. package/src/components/theme-provider.tsx +0 -10
  45. package/src/components/theme-toggle-icon.tsx +0 -62
  46. package/src/components/theme-toggle.tsx +0 -39
  47. package/src/components/typography.tsx +0 -27
  48. package/src/components/ui/avatar.tsx +0 -109
  49. package/src/components/ui/badge.tsx +0 -47
  50. package/src/components/ui/button.tsx +0 -65
  51. package/src/components/ui/card.tsx +0 -106
  52. package/src/components/ui/dialog.tsx +0 -158
  53. package/src/components/ui/dropdown-menu.tsx +0 -257
  54. package/src/components/ui/input.tsx +0 -21
  55. package/src/components/ui/sonner.tsx +0 -54
  56. package/src/components/workflow-diagram.tsx +0 -414
  57. package/src/i18n/config.ts +0 -8
  58. package/src/i18n/request.ts +0 -16
  59. package/src/i18n/routing.ts +0 -12
  60. package/src/lib/supabase/client.ts +0 -18
  61. package/src/lib/supabase/middleware.ts +0 -42
  62. package/src/lib/supabase/server.ts +0 -37
  63. package/src/lib/utils.ts +0 -6
  64. package/src/proxy.ts +0 -62
  65. package/src/tokens/.gitkeep +0 -0
  66. package/src/tokens/color.json +0 -56
  67. package/src/tokens/radius.json +0 -9
  68. package/src/tokens/semantic.json +0 -81
  69. package/src/tokens/shadow.json +0 -7
  70. package/src/tokens/spacing.json +0 -16
  71. package/src/tokens/typography.json +0 -34
  72. package/src/types/i18n.d.ts +0 -6
  73. package/src/utils/.gitkeep +0 -0
@@ -0,0 +1,832 @@
1
+ "use client";
2
+ import {
3
+ Badge,
4
+ Button,
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuTrigger
9
+ } from "../chunk-SYI3IKIC.js";
10
+ import {
11
+ __objRest,
12
+ __spreadProps,
13
+ __spreadValues,
14
+ cn
15
+ } from "../chunk-JBTDRVZ7.js";
16
+
17
+ // src/components/nav.tsx
18
+ import { useEffect, useState } from "react";
19
+ import { useTranslations } from "next-intl";
20
+ import Image from "next/image";
21
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
22
+ var PROJECTS = [
23
+ {
24
+ key: "cc",
25
+ href: "/cc",
26
+ accent: "oklch(0.704 0.140 181)",
27
+ icon: "/images/coral.png",
28
+ status: "active"
29
+ },
30
+ {
31
+ key: "pkm",
32
+ href: "/pkm",
33
+ accent: "oklch(0.702 0.183 293)",
34
+ icon: "/images/jellyfish.png",
35
+ status: "coming"
36
+ },
37
+ {
38
+ key: "naeilUi",
39
+ href: "/design",
40
+ accent: "oklch(0.623 0.214 259)",
41
+ icon: "/images/whale.png",
42
+ status: "active"
43
+ },
44
+ {
45
+ key: "sa",
46
+ href: "/sa",
47
+ accent: "oklch(0.723 0.219 149)",
48
+ icon: "/images/fish.png",
49
+ status: "active"
50
+ },
51
+ {
52
+ key: "babyAgent",
53
+ href: "/baby-agent",
54
+ accent: "oklch(0.769 0.188 70.08)",
55
+ icon: "/images/turtle.png",
56
+ status: "coming"
57
+ }
58
+ ];
59
+ function Nav({
60
+ Link,
61
+ usePathname,
62
+ brandHref = "/",
63
+ brandExternal = false,
64
+ brandSlot,
65
+ projectsExternal = false,
66
+ externalBaseUrl = "https://naeil.dev",
67
+ currentProjectKey,
68
+ currentProjectHref,
69
+ subNav,
70
+ authSlot,
71
+ showProjects = true,
72
+ showBlog = true,
73
+ toolbarSlot
74
+ }) {
75
+ const t = useTranslations("nav");
76
+ const pt = useTranslations("projects");
77
+ const pathname = usePathname();
78
+ const [scrolled, setScrolled] = useState(false);
79
+ const [mobileOpen, setMobileOpen] = useState(false);
80
+ const [projectsOpen, setProjectsOpen] = useState(false);
81
+ useEffect(() => {
82
+ const onScroll = () => setScrolled(window.scrollY > 16);
83
+ window.addEventListener("scroll", onScroll, { passive: true });
84
+ return () => window.removeEventListener("scroll", onScroll);
85
+ }, []);
86
+ useEffect(() => {
87
+ setMobileOpen(false);
88
+ setProjectsOpen(false);
89
+ }, [pathname]);
90
+ useEffect(() => {
91
+ document.body.style.overflow = mobileOpen ? "hidden" : "";
92
+ return () => {
93
+ document.body.style.overflow = "";
94
+ };
95
+ }, [mobileOpen]);
96
+ const linkClass = (href) => {
97
+ const isActive = pathname === href;
98
+ return [
99
+ "text-sm font-medium transition-colors",
100
+ isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"
101
+ ].join(" ");
102
+ };
103
+ const isProjectActive = pathname.startsWith("/projects") || pathname.startsWith("/cc") || pathname.startsWith("/pkm") || pathname.startsWith("/sa") || pathname.startsWith("/design");
104
+ function getProjectLink(project) {
105
+ if (currentProjectKey && project.key === currentProjectKey) {
106
+ return { href: currentProjectHref || "/", external: false };
107
+ }
108
+ if (projectsExternal) {
109
+ return { href: `${externalBaseUrl}${project.href}`, external: true };
110
+ }
111
+ return { href: project.href, external: false };
112
+ }
113
+ const brandContent = brandSlot != null ? brandSlot : /* @__PURE__ */ jsxs("span", { className: "text-foreground flex items-center gap-3 text-xl font-semibold tracking-tight", children: [
114
+ /* @__PURE__ */ jsx(Logo, { size: 28 }),
115
+ ".naeil"
116
+ ] });
117
+ const brandElement = brandExternal ? /* @__PURE__ */ jsx("a", { href: brandHref, children: brandContent }) : /* @__PURE__ */ jsx(Link, { href: brandHref, children: brandContent });
118
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
119
+ /* @__PURE__ */ jsx(
120
+ "nav",
121
+ {
122
+ className: [
123
+ "fixed top-0 z-50 w-full transition-[background-color,border-color] duration-300",
124
+ scrolled || mobileOpen ? "border-b border-border/40 bg-background/80 backdrop-blur-md" : "border-b border-transparent bg-transparent"
125
+ ].join(" "),
126
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex h-[58px] max-w-7xl items-center px-6", children: [
127
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center md:w-[200px]", children: brandElement }),
128
+ /* @__PURE__ */ jsxs("div", { className: "hidden flex-1 items-center gap-6 md:flex", children: [
129
+ showProjects && /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
130
+ /* @__PURE__ */ jsxs(
131
+ "button",
132
+ {
133
+ className: [
134
+ "text-sm font-medium transition-colors",
135
+ isProjectActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"
136
+ ].join(" "),
137
+ children: [
138
+ t("projects"),
139
+ /* @__PURE__ */ jsx(
140
+ "svg",
141
+ {
142
+ width: "10",
143
+ height: "10",
144
+ viewBox: "0 0 24 24",
145
+ fill: "none",
146
+ stroke: "currentColor",
147
+ strokeWidth: "2",
148
+ strokeLinecap: "round",
149
+ strokeLinejoin: "round",
150
+ className: "ml-1 inline-block transition-transform group-hover:rotate-180",
151
+ children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" })
152
+ }
153
+ )
154
+ ]
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute left-0 top-full pt-3 opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100", children: /* @__PURE__ */ jsx("div", { className: "border-border/40 bg-background/95 w-[520px] rounded-xl border p-4 shadow-xl backdrop-blur-md", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", children: PROJECTS.map((project) => {
158
+ const { href, external } = getProjectLink(project);
159
+ const inner = /* @__PURE__ */ jsxs("div", { className: "hover:bg-muted/50 flex items-start gap-3.5 rounded-lg px-3 py-3 transition-colors", children: [
160
+ /* @__PURE__ */ jsx(
161
+ "div",
162
+ {
163
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border",
164
+ style: {
165
+ borderColor: `color-mix(in oklch, ${project.accent} 30%, transparent)`,
166
+ backgroundColor: `color-mix(in oklch, ${project.accent} 8%, transparent)`
167
+ },
168
+ children: /* @__PURE__ */ jsx(
169
+ Image,
170
+ {
171
+ src: project.icon,
172
+ alt: "",
173
+ width: 24,
174
+ height: 24,
175
+ className: "object-contain"
176
+ }
177
+ )
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-0.5", children: [
181
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
182
+ /* @__PURE__ */ jsx("span", { className: "text-foreground text-sm font-medium", children: pt(`${project.key}.name`) }),
183
+ project.status === "coming" && /* @__PURE__ */ jsx(
184
+ Badge,
185
+ {
186
+ variant: "outline",
187
+ className: "text-muted-foreground px-1 py-0 text-[9px]",
188
+ children: pt("status.coming")
189
+ }
190
+ )
191
+ ] }),
192
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-0.5 text-xs leading-snug", children: pt(`${project.key}.description`) })
193
+ ] })
194
+ ] });
195
+ if (external) {
196
+ return /* @__PURE__ */ jsx("a", { href, children: inner }, project.key);
197
+ }
198
+ return /* @__PURE__ */ jsx(Link, { href, children: inner }, project.key);
199
+ }) }) }) })
200
+ ] }),
201
+ showBlog && /* @__PURE__ */ jsx(Fragment, { children: projectsExternal ? /* @__PURE__ */ jsx(
202
+ "a",
203
+ {
204
+ href: `${externalBaseUrl}/blog`,
205
+ className: "text-sm font-medium text-muted-foreground transition-colors hover:text-foreground",
206
+ children: t("blog")
207
+ }
208
+ ) : /* @__PURE__ */ jsx(Link, { href: "/blog", className: linkClass("/blog"), children: t("blog") }) }),
209
+ subNav && subNav.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
210
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-border/40" }),
211
+ subNav.map((item) => /* @__PURE__ */ jsx(
212
+ Link,
213
+ {
214
+ href: item.href,
215
+ className: linkClass(item.href),
216
+ children: item.label
217
+ },
218
+ item.href
219
+ ))
220
+ ] })
221
+ ] }),
222
+ /* @__PURE__ */ jsxs("div", { className: "hidden items-center gap-3 md:flex", children: [
223
+ authSlot,
224
+ authSlot && toolbarSlot && /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-border/40" }),
225
+ toolbarSlot && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: toolbarSlot })
226
+ ] }),
227
+ /* @__PURE__ */ jsx(
228
+ "button",
229
+ {
230
+ className: "text-muted-foreground hover:text-foreground ml-auto transition-colors md:hidden",
231
+ onClick: () => setMobileOpen(!mobileOpen),
232
+ "aria-label": "Menu",
233
+ children: mobileOpen ? /* @__PURE__ */ jsx(CloseIcon, {}) : /* @__PURE__ */ jsx(MenuIcon, {})
234
+ }
235
+ )
236
+ ] })
237
+ }
238
+ ),
239
+ mobileOpen && /* @__PURE__ */ jsx("div", { className: "bg-background/95 fixed inset-0 z-40 overflow-y-auto pt-[58px] backdrop-blur-md md:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col px-6 py-6", children: [
240
+ showProjects && /* @__PURE__ */ jsxs(Fragment, { children: [
241
+ /* @__PURE__ */ jsxs(
242
+ "button",
243
+ {
244
+ className: "text-foreground flex items-center justify-between py-3 text-base font-medium",
245
+ onClick: () => setProjectsOpen(!projectsOpen),
246
+ children: [
247
+ t("projects"),
248
+ /* @__PURE__ */ jsx(
249
+ "svg",
250
+ {
251
+ width: "14",
252
+ height: "14",
253
+ viewBox: "0 0 24 24",
254
+ fill: "none",
255
+ stroke: "currentColor",
256
+ strokeWidth: "2",
257
+ strokeLinecap: "round",
258
+ strokeLinejoin: "round",
259
+ className: [
260
+ "transition-transform duration-200",
261
+ projectsOpen ? "rotate-180" : ""
262
+ ].join(" "),
263
+ children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" })
264
+ }
265
+ )
266
+ ]
267
+ }
268
+ ),
269
+ projectsOpen && /* @__PURE__ */ jsx("div", { className: "mb-2 flex flex-col gap-1 pl-1", children: PROJECTS.map((project) => {
270
+ const { href, external } = getProjectLink(project);
271
+ const inner = /* @__PURE__ */ jsxs("div", { className: "hover:bg-muted/50 flex items-center gap-3 rounded-lg px-3 py-3 transition-colors", children: [
272
+ /* @__PURE__ */ jsx(
273
+ "div",
274
+ {
275
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border",
276
+ style: {
277
+ borderColor: `color-mix(in oklch, ${project.accent} 30%, transparent)`,
278
+ backgroundColor: `color-mix(in oklch, ${project.accent} 8%, transparent)`
279
+ },
280
+ children: /* @__PURE__ */ jsx(
281
+ Image,
282
+ {
283
+ src: project.icon,
284
+ alt: "",
285
+ width: 20,
286
+ height: 20,
287
+ className: "object-contain"
288
+ }
289
+ )
290
+ }
291
+ ),
292
+ /* @__PURE__ */ jsxs("div", { children: [
293
+ /* @__PURE__ */ jsx("span", { className: "text-foreground text-sm font-medium", children: pt(`${project.key}.name`) }),
294
+ project.status === "coming" && /* @__PURE__ */ jsx(
295
+ Badge,
296
+ {
297
+ variant: "outline",
298
+ className: "text-muted-foreground ml-2 px-1 py-0 text-[9px]",
299
+ children: pt("status.coming")
300
+ }
301
+ )
302
+ ] })
303
+ ] });
304
+ if (external) {
305
+ return /* @__PURE__ */ jsx(
306
+ "a",
307
+ {
308
+ href,
309
+ onClick: () => setMobileOpen(false),
310
+ children: inner
311
+ },
312
+ project.key
313
+ );
314
+ }
315
+ return /* @__PURE__ */ jsx(
316
+ Link,
317
+ {
318
+ href,
319
+ onClick: () => setMobileOpen(false),
320
+ children: inner
321
+ },
322
+ project.key
323
+ );
324
+ }) })
325
+ ] }),
326
+ showBlog && /* @__PURE__ */ jsx(Fragment, { children: projectsExternal ? /* @__PURE__ */ jsx(
327
+ "a",
328
+ {
329
+ href: `${externalBaseUrl}/blog`,
330
+ className: "text-foreground border-border/40 border-t py-3 text-base font-medium",
331
+ onClick: () => setMobileOpen(false),
332
+ children: t("blog")
333
+ }
334
+ ) : /* @__PURE__ */ jsx(
335
+ Link,
336
+ {
337
+ href: "/blog",
338
+ className: "text-foreground border-border/40 border-t py-3 text-base font-medium",
339
+ onClick: () => setMobileOpen(false),
340
+ children: t("blog")
341
+ }
342
+ ) }),
343
+ subNav && subNav.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-border/40 border-t", children: subNav.map((item) => /* @__PURE__ */ jsx(
344
+ Link,
345
+ {
346
+ href: item.href,
347
+ className: "text-foreground block py-3 text-base font-medium",
348
+ onClick: () => setMobileOpen(false),
349
+ children: item.label
350
+ },
351
+ item.href
352
+ )) }),
353
+ authSlot && /* @__PURE__ */ jsx("div", { className: "border-border/40 border-t pt-4", children: authSlot }),
354
+ toolbarSlot && /* @__PURE__ */ jsx("div", { className: "border-border/40 flex items-center gap-4 border-t pt-4 mt-2", children: toolbarSlot })
355
+ ] }) })
356
+ ] });
357
+ }
358
+ function Logo({
359
+ size = 20
360
+ }) {
361
+ const id = `naeil-nav-logo-${size}`;
362
+ return /* @__PURE__ */ jsxs(
363
+ "svg",
364
+ {
365
+ width: size,
366
+ height: size,
367
+ viewBox: "0 0 24 24",
368
+ fill: "none",
369
+ "aria-label": "naeil",
370
+ children: [
371
+ /* @__PURE__ */ jsx(
372
+ "path",
373
+ {
374
+ d: "M6 4 L6 16 L20 16",
375
+ stroke: "currentColor",
376
+ strokeWidth: "2",
377
+ strokeLinecap: "square",
378
+ strokeLinejoin: "miter"
379
+ }
380
+ ),
381
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "16", r: "3.5", fill: `url(#${id}-grad)`, mask: `url(#${id})` }),
382
+ /* @__PURE__ */ jsxs("defs", { children: [
383
+ /* @__PURE__ */ jsxs("linearGradient", { id: `${id}-grad`, x1: "15", y1: "12.5", x2: "15", y2: "16", children: [
384
+ /* @__PURE__ */ jsx("stop", { stopColor: "#eab308" }),
385
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#dc2626" })
386
+ ] }),
387
+ /* @__PURE__ */ jsx("mask", { id, children: /* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: "24", height: "16", fill: "white" }) })
388
+ ] })
389
+ ]
390
+ }
391
+ );
392
+ }
393
+ function MenuIcon() {
394
+ return /* @__PURE__ */ jsx(
395
+ "svg",
396
+ {
397
+ width: "22",
398
+ height: "22",
399
+ viewBox: "0 0 24 24",
400
+ fill: "none",
401
+ stroke: "currentColor",
402
+ strokeWidth: "1.5",
403
+ strokeLinecap: "round",
404
+ strokeLinejoin: "round",
405
+ children: /* @__PURE__ */ jsx("path", { d: "M4 6h16M4 12h16M4 18h16" })
406
+ }
407
+ );
408
+ }
409
+ function CloseIcon() {
410
+ return /* @__PURE__ */ jsx(
411
+ "svg",
412
+ {
413
+ width: "22",
414
+ height: "22",
415
+ viewBox: "0 0 24 24",
416
+ fill: "none",
417
+ stroke: "currentColor",
418
+ strokeWidth: "1.5",
419
+ strokeLinecap: "round",
420
+ strokeLinejoin: "round",
421
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
422
+ }
423
+ );
424
+ }
425
+
426
+ // src/components/footer.tsx
427
+ import { useTranslations as useTranslations2 } from "next-intl";
428
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
429
+ function NavLink({
430
+ href,
431
+ children,
432
+ className,
433
+ linksExternal,
434
+ Link
435
+ }) {
436
+ if (linksExternal) {
437
+ return /* @__PURE__ */ jsx2(
438
+ "a",
439
+ {
440
+ href: `https://naeil.dev${href}`,
441
+ target: "_blank",
442
+ rel: "noopener noreferrer",
443
+ className,
444
+ children
445
+ }
446
+ );
447
+ }
448
+ if (Link) {
449
+ return /* @__PURE__ */ jsx2(Link, { href, className, children });
450
+ }
451
+ return /* @__PURE__ */ jsx2("a", { href, className, children });
452
+ }
453
+ function Footer({ Link, linksExternal = false }) {
454
+ const t = useTranslations2("footer");
455
+ return /* @__PURE__ */ jsx2("footer", { className: "border-border/40 border-t", children: /* @__PURE__ */ jsxs2("div", { className: "mx-auto flex max-w-7xl flex-col gap-10 px-6 py-16 sm:flex-row sm:justify-between", children: [
456
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-4", children: [
457
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
458
+ /* @__PURE__ */ jsx2(Logo2, { size: 20 }),
459
+ /* @__PURE__ */ jsx2("span", { className: "text-foreground text-sm font-semibold tracking-tight", children: ".naeil" })
460
+ ] }),
461
+ /* @__PURE__ */ jsx2("p", { className: "text-muted-foreground max-w-[240px] text-sm leading-relaxed", children: t("tagline") }),
462
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
463
+ /* @__PURE__ */ jsx2(
464
+ "a",
465
+ {
466
+ href: "https://github.com/naeil-dev",
467
+ target: "_blank",
468
+ rel: "noopener noreferrer",
469
+ className: "text-muted-foreground hover:text-foreground transition-colors",
470
+ "aria-label": "GitHub",
471
+ children: /* @__PURE__ */ jsx2(GitHubIcon, {})
472
+ }
473
+ ),
474
+ /* @__PURE__ */ jsx2(
475
+ "a",
476
+ {
477
+ href: "https://x.com",
478
+ target: "_blank",
479
+ rel: "noopener noreferrer",
480
+ className: "text-muted-foreground hover:text-foreground transition-colors",
481
+ "aria-label": "X",
482
+ children: /* @__PURE__ */ jsx2(XIcon, {})
483
+ }
484
+ )
485
+ ] })
486
+ ] }),
487
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-10 sm:gap-16", children: [
488
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
489
+ /* @__PURE__ */ jsx2("span", { className: "text-foreground text-xs font-medium uppercase tracking-wider", children: t("navigation") }),
490
+ /* @__PURE__ */ jsx2(
491
+ NavLink,
492
+ {
493
+ href: "/projects",
494
+ className: "text-muted-foreground hover:text-foreground text-sm transition-colors",
495
+ linksExternal,
496
+ Link,
497
+ children: t("projects")
498
+ }
499
+ ),
500
+ /* @__PURE__ */ jsx2(
501
+ NavLink,
502
+ {
503
+ href: "/blog",
504
+ className: "text-muted-foreground hover:text-foreground text-sm transition-colors",
505
+ linksExternal,
506
+ Link,
507
+ children: t("blog")
508
+ }
509
+ )
510
+ ] }),
511
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
512
+ /* @__PURE__ */ jsx2("span", { className: "text-foreground text-xs font-medium uppercase tracking-wider", children: t("social") }),
513
+ /* @__PURE__ */ jsx2(
514
+ "a",
515
+ {
516
+ href: "https://github.com/naeil-dev",
517
+ target: "_blank",
518
+ rel: "noopener noreferrer",
519
+ className: "text-muted-foreground hover:text-foreground text-sm transition-colors",
520
+ children: "GitHub \u2197"
521
+ }
522
+ )
523
+ ] })
524
+ ] })
525
+ ] }) });
526
+ }
527
+ function Logo2({ size = 20 }) {
528
+ const id = `naeil-footer-logo-${size}`;
529
+ return /* @__PURE__ */ jsxs2(
530
+ "svg",
531
+ {
532
+ width: size,
533
+ height: size,
534
+ viewBox: "0 0 24 24",
535
+ fill: "none",
536
+ "aria-label": "naeil",
537
+ children: [
538
+ /* @__PURE__ */ jsx2(
539
+ "path",
540
+ {
541
+ d: "M6 4 L6 16 L20 16",
542
+ stroke: "currentColor",
543
+ strokeWidth: "2",
544
+ strokeLinecap: "square",
545
+ strokeLinejoin: "miter"
546
+ }
547
+ ),
548
+ /* @__PURE__ */ jsx2(
549
+ "circle",
550
+ {
551
+ cx: "15",
552
+ cy: "16",
553
+ r: "3.5",
554
+ fill: `url(#${id}-grad)`,
555
+ mask: `url(#${id})`
556
+ }
557
+ ),
558
+ /* @__PURE__ */ jsxs2("defs", { children: [
559
+ /* @__PURE__ */ jsxs2("linearGradient", { id: `${id}-grad`, x1: "15", y1: "12.5", x2: "15", y2: "16", children: [
560
+ /* @__PURE__ */ jsx2("stop", { stopColor: "#eab308" }),
561
+ /* @__PURE__ */ jsx2("stop", { offset: "1", stopColor: "#dc2626" })
562
+ ] }),
563
+ /* @__PURE__ */ jsx2("mask", { id, children: /* @__PURE__ */ jsx2("rect", { x: "0", y: "0", width: "24", height: "16", fill: "white" }) })
564
+ ] })
565
+ ]
566
+ }
567
+ );
568
+ }
569
+ function GitHubIcon() {
570
+ return /* @__PURE__ */ jsx2(
571
+ "svg",
572
+ {
573
+ width: "16",
574
+ height: "16",
575
+ viewBox: "0 0 24 24",
576
+ fill: "currentColor",
577
+ "aria-hidden": "true",
578
+ children: /* @__PURE__ */ jsx2("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" })
579
+ }
580
+ );
581
+ }
582
+ function XIcon() {
583
+ return /* @__PURE__ */ jsx2(
584
+ "svg",
585
+ {
586
+ width: "14",
587
+ height: "14",
588
+ viewBox: "0 0 24 24",
589
+ fill: "currentColor",
590
+ "aria-hidden": "true",
591
+ children: /* @__PURE__ */ jsx2("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" })
592
+ }
593
+ );
594
+ }
595
+
596
+ // src/components/locale-switcher.tsx
597
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
598
+ function LocaleSwitcher({
599
+ locales,
600
+ currentLocale,
601
+ onLocaleChange
602
+ }) {
603
+ var _a, _b;
604
+ const current = locales.find((l) => l.code === currentLocale);
605
+ return /* @__PURE__ */ jsxs3(DropdownMenu, { children: [
606
+ /* @__PURE__ */ jsx3(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
607
+ "button",
608
+ {
609
+ className: "text-muted-foreground hover:text-foreground flex items-center gap-1.5 text-sm transition-colors focus:outline-none",
610
+ "aria-label": `Change language \u2014 current: ${(_a = current == null ? void 0 : current.short) != null ? _a : currentLocale}`,
611
+ children: [
612
+ /* @__PURE__ */ jsx3(GlobeIcon, {}),
613
+ (_b = current == null ? void 0 : current.short) != null ? _b : currentLocale.toUpperCase()
614
+ ]
615
+ }
616
+ ) }),
617
+ /* @__PURE__ */ jsx3(DropdownMenuContent, { align: "end", className: "min-w-[120px]", children: locales.map((l) => /* @__PURE__ */ jsxs3(
618
+ DropdownMenuItem,
619
+ {
620
+ onClick: () => onLocaleChange(l.code),
621
+ className: l.code === currentLocale ? "font-medium" : "",
622
+ children: [
623
+ /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground mr-2 w-5 text-xs", children: l.short }),
624
+ l.full
625
+ ]
626
+ },
627
+ l.code
628
+ )) })
629
+ ] });
630
+ }
631
+ function GlobeIcon() {
632
+ return /* @__PURE__ */ jsxs3(
633
+ "svg",
634
+ {
635
+ xmlns: "http://www.w3.org/2000/svg",
636
+ width: "14",
637
+ height: "14",
638
+ viewBox: "0 0 24 24",
639
+ fill: "none",
640
+ stroke: "currentColor",
641
+ strokeWidth: "1.5",
642
+ strokeLinecap: "round",
643
+ strokeLinejoin: "round",
644
+ className: "shrink-0",
645
+ children: [
646
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "10" }),
647
+ /* @__PURE__ */ jsx3("path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }),
648
+ /* @__PURE__ */ jsx3("path", { d: "M2 12h20" })
649
+ ]
650
+ }
651
+ );
652
+ }
653
+
654
+ // src/components/theme-toggle.tsx
655
+ import { useTheme } from "next-themes";
656
+ import { useSyncExternalStore } from "react";
657
+ import { jsx as jsx4 } from "react/jsx-runtime";
658
+ var emptySubscribe = () => () => {
659
+ };
660
+ function useIsMounted() {
661
+ return useSyncExternalStore(
662
+ emptySubscribe,
663
+ () => true,
664
+ () => false
665
+ );
666
+ }
667
+ function ThemeToggle() {
668
+ const { theme, setTheme } = useTheme();
669
+ const mounted = useIsMounted();
670
+ if (!mounted) {
671
+ return /* @__PURE__ */ jsx4(Button, { variant: "outline", size: "sm", className: "w-24", children: "\u2026" });
672
+ }
673
+ return /* @__PURE__ */ jsx4(
674
+ Button,
675
+ {
676
+ variant: "outline",
677
+ size: "sm",
678
+ className: "w-24",
679
+ onClick: () => setTheme(theme === "dark" ? "light" : "dark"),
680
+ children: theme === "dark" ? "\u2600\uFE0F Light" : "\u{1F319} Dark"
681
+ }
682
+ );
683
+ }
684
+
685
+ // src/components/theme-provider.tsx
686
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
687
+ import { jsx as jsx5 } from "react/jsx-runtime";
688
+ function ThemeProvider(_a) {
689
+ var _b = _a, {
690
+ children
691
+ } = _b, props = __objRest(_b, [
692
+ "children"
693
+ ]);
694
+ return /* @__PURE__ */ jsx5(NextThemesProvider, __spreadProps(__spreadValues({}, props), { children }));
695
+ }
696
+
697
+ // src/components/theme-toggle-icon.tsx
698
+ import { useTheme as useTheme2 } from "next-themes";
699
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
700
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
701
+ var emptySubscribe2 = () => () => {
702
+ };
703
+ function useIsMounted2() {
704
+ return useSyncExternalStore2(
705
+ emptySubscribe2,
706
+ () => true,
707
+ () => false
708
+ );
709
+ }
710
+ function ThemeToggleIcon() {
711
+ const { theme, setTheme } = useTheme2();
712
+ const mounted = useIsMounted2();
713
+ if (!mounted) {
714
+ return /* @__PURE__ */ jsx6("div", { className: "h-4 w-4" });
715
+ }
716
+ const isDark = theme === "dark";
717
+ return /* @__PURE__ */ jsx6(
718
+ "button",
719
+ {
720
+ onClick: () => setTheme(isDark ? "light" : "dark"),
721
+ className: "text-muted-foreground hover:text-foreground transition-colors",
722
+ "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode",
723
+ children: isDark ? /* @__PURE__ */ jsxs4(
724
+ "svg",
725
+ {
726
+ width: "16",
727
+ height: "16",
728
+ viewBox: "0 0 24 24",
729
+ fill: "none",
730
+ stroke: "currentColor",
731
+ strokeWidth: "1.5",
732
+ strokeLinecap: "round",
733
+ strokeLinejoin: "round",
734
+ children: [
735
+ /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "5" }),
736
+ /* @__PURE__ */ jsx6("path", { d: "M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" })
737
+ ]
738
+ }
739
+ ) : /* @__PURE__ */ jsx6(
740
+ "svg",
741
+ {
742
+ width: "16",
743
+ height: "16",
744
+ viewBox: "0 0 24 24",
745
+ fill: "none",
746
+ stroke: "currentColor",
747
+ strokeWidth: "1.5",
748
+ strokeLinecap: "round",
749
+ strokeLinejoin: "round",
750
+ children: /* @__PURE__ */ jsx6("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
751
+ }
752
+ )
753
+ }
754
+ );
755
+ }
756
+
757
+ // src/components/logo.tsx
758
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
759
+ function Logo3({
760
+ size = 20,
761
+ mono = false,
762
+ className
763
+ }) {
764
+ const id = `naeil-logo-${size}`;
765
+ return /* @__PURE__ */ jsxs5(
766
+ "svg",
767
+ {
768
+ width: size,
769
+ height: size,
770
+ viewBox: "0 0 24 24",
771
+ fill: "none",
772
+ "aria-label": "naeil",
773
+ className,
774
+ children: [
775
+ /* @__PURE__ */ jsx7(
776
+ "path",
777
+ {
778
+ d: "M6 4 L6 16 L20 16",
779
+ stroke: "currentColor",
780
+ strokeWidth: "2",
781
+ strokeLinecap: "square",
782
+ strokeLinejoin: "miter"
783
+ }
784
+ ),
785
+ mono ? /* @__PURE__ */ jsx7("circle", { cx: "15", cy: "16", r: "3.5", fill: "currentColor", mask: `url(#${id})` }) : /* @__PURE__ */ jsx7("circle", { cx: "15", cy: "16", r: "3.5", fill: `url(#${id}-grad)`, mask: `url(#${id})` }),
786
+ /* @__PURE__ */ jsxs5("defs", { children: [
787
+ !mono && /* @__PURE__ */ jsxs5(
788
+ "linearGradient",
789
+ {
790
+ id: `${id}-grad`,
791
+ x1: "15",
792
+ y1: "12.5",
793
+ x2: "15",
794
+ y2: "16",
795
+ children: [
796
+ /* @__PURE__ */ jsx7("stop", { stopColor: "#eab308" }),
797
+ /* @__PURE__ */ jsx7("stop", { offset: "1", stopColor: "#dc2626" })
798
+ ]
799
+ }
800
+ ),
801
+ /* @__PURE__ */ jsx7("mask", { id, children: /* @__PURE__ */ jsx7("rect", { x: "0", y: "0", width: "24", height: "16", fill: "white" }) })
802
+ ] })
803
+ ]
804
+ }
805
+ );
806
+ }
807
+
808
+ // src/components/typography.tsx
809
+ import { jsx as jsx8 } from "react/jsx-runtime";
810
+ var pageTitleClass = "text-foreground text-4xl font-bold tracking-tight lg:text-5xl";
811
+ var sectionTitleClass = "text-foreground text-lg font-bold";
812
+ function PageTitle(_a) {
813
+ var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
814
+ return /* @__PURE__ */ jsx8("h1", __spreadProps(__spreadValues({ className: cn(pageTitleClass, className) }, props), { children }));
815
+ }
816
+ function SectionTitle(_a) {
817
+ var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
818
+ return /* @__PURE__ */ jsx8("h2", __spreadProps(__spreadValues({ className: cn(sectionTitleClass, className) }, props), { children }));
819
+ }
820
+ export {
821
+ Footer,
822
+ LocaleSwitcher,
823
+ Logo3 as Logo,
824
+ Nav,
825
+ PageTitle,
826
+ SectionTitle,
827
+ ThemeProvider,
828
+ ThemeToggle,
829
+ ThemeToggleIcon,
830
+ pageTitleClass,
831
+ sectionTitleClass
832
+ };