@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
package/README.md CHANGED
@@ -1,36 +1,75 @@
1
- This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
1
+ # @naeil/ui
2
2
 
3
- ## Getting Started
3
+ Design system and component library for [naeil.dev](https://naeil.dev) — a portfolio, blog, and project showcase with cross-subdomain SSO authentication.
4
+
5
+ ## Tech Stack
6
+
7
+ - **Framework:** Next.js 16 (App Router)
8
+ - **UI:** React 19, Tailwind CSS v4, Radix UI
9
+ - **Language:** TypeScript (strict)
10
+ - **Auth:** Supabase Auth (OAuth via Google/GitHub)
11
+ - **i18n:** next-intl (en, ko, ja)
12
+ - **3D:** React Three Fiber (hero scene)
13
+ - **Hosting:** Vercel (Hobby)
14
+
15
+ ## Prerequisites
16
+
17
+ - Node.js 22+
18
+ - pnpm
19
+
20
+ ## Environment Variables
4
21
 
5
- First, run the development server:
22
+ | Variable | Description |
23
+ |---|---|
24
+ | `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL |
25
+ | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anonymous/public key |
26
+
27
+ ## Getting Started
6
28
 
7
29
  ```bash
8
- npm run dev
9
- # or
10
- yarn dev
11
- # or
30
+ pnpm install
12
31
  pnpm dev
13
- # or
14
- bun dev
32
+ # Open http://localhost:3000
15
33
  ```
16
34
 
17
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
-
19
- You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
35
+ ## Project Structure
20
36
 
21
- This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
37
+ ```
38
+ src/
39
+ ├── app/ # Next.js App Router pages & API routes
40
+ │ ├── [locale]/ # Locale-prefixed pages (en/ko/ja)
41
+ │ ├── api/health/ # Health check endpoint
42
+ │ └── auth/callback/ # OAuth callback handler
43
+ ├── components/ # Shared UI components
44
+ │ └── ui/ # Primitive components (button, card, etc.)
45
+ ├── i18n/ # Internationalization config & routing
46
+ ├── lib/
47
+ │ ├── auth/ # Auth utilities (cookie domain, redirect, avatar, routes)
48
+ │ └── supabase/ # Supabase client (server, middleware, browser)
49
+ ├── styles/ # Theme tokens & global CSS
50
+ └── proxy.ts # Middleware (auth guard, i18n, cookie sync)
51
+ ```
22
52
 
23
- ## Learn More
53
+ ## Auth / SSO Architecture
24
54
 
25
- To learn more about Next.js, take a look at the following resources:
55
+ The auth system enables cross-subdomain SSO between `naeil.dev` and `esg.naeil.dev`:
26
56
 
27
- - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
- - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
57
+ - OAuth flow: Login page → Supabase OAuth → `/auth/callback` → redirect to original page
58
+ - Cookie domain is set to `.naeil.dev` in production, enabling session sharing across subdomains
59
+ - Protected routes (e.g., `/sa/reports/:id`) redirect unauthenticated users to login with a `?next` param
60
+ - The middleware (`proxy.ts`) handles session refresh, route protection, i18n routing, and cookie sync
29
61
 
30
- You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
62
+ ## Available Scripts
31
63
 
32
- ## Deploy on Vercel
64
+ | Script | Description |
65
+ |---|---|
66
+ | `pnpm dev` | Start development server |
67
+ | `pnpm build` | Production build |
68
+ | `pnpm lint` | Run ESLint |
69
+ | `pnpm test` | Run unit tests (Vitest) |
70
+ | `pnpm build:tokens` | Generate design tokens |
71
+ | `pnpm check:contrast` | Check color contrast ratios |
33
72
 
34
- The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
73
+ ## Deployment
35
74
 
36
- Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
75
+ Auto-deploys from `main` via Vercel (Hobby plan). Push to `main` triggers a production deployment.
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __objRest = (source, exclude) => {
22
+ var target = {};
23
+ for (var prop in source)
24
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
+ target[prop] = source[prop];
26
+ if (source != null && __getOwnPropSymbols)
27
+ for (var prop of __getOwnPropSymbols(source)) {
28
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
+ target[prop] = source[prop];
30
+ }
31
+ return target;
32
+ };
33
+
34
+ // src/lib/utils.ts
35
+ import { clsx } from "clsx";
36
+ import { twMerge } from "tailwind-merge";
37
+ function cn(...inputs) {
38
+ return twMerge(clsx(inputs));
39
+ }
40
+
41
+ export {
42
+ __spreadValues,
43
+ __spreadProps,
44
+ __objRest,
45
+ cn
46
+ };
@@ -0,0 +1,355 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues,
6
+ cn
7
+ } from "./chunk-JBTDRVZ7.js";
8
+
9
+ // src/components/ui/badge.tsx
10
+ import { cva } from "class-variance-authority";
11
+ import { Slot } from "radix-ui";
12
+ import { jsx } from "react/jsx-runtime";
13
+ var badgeVariants = cva(
14
+ "inline-flex items-center justify-center rounded-sm border px-2 py-0.5 text-[11px] font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:ring-accent/25 focus-visible:ring-[2px] focus-visible:outline-none aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ default: "bg-primary text-primary-foreground border-transparent [a&]:hover:bg-primary/90",
19
+ secondary: "bg-secondary text-secondary-foreground border-transparent [a&]:hover:bg-secondary/80",
20
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
21
+ destructive: "bg-destructive text-destructive-foreground border-transparent [a&]:hover:bg-destructive/90"
22
+ }
23
+ },
24
+ defaultVariants: {
25
+ variant: "default"
26
+ }
27
+ }
28
+ );
29
+ function Badge(_a) {
30
+ var _b = _a, {
31
+ className,
32
+ variant = "default",
33
+ asChild = false
34
+ } = _b, props = __objRest(_b, [
35
+ "className",
36
+ "variant",
37
+ "asChild"
38
+ ]);
39
+ const Comp = asChild ? Slot.Root : "span";
40
+ return /* @__PURE__ */ jsx(
41
+ Comp,
42
+ __spreadValues({
43
+ "data-slot": "badge",
44
+ "data-variant": variant,
45
+ className: cn(badgeVariants({ variant }), className)
46
+ }, props)
47
+ );
48
+ }
49
+
50
+ // src/components/ui/dropdown-menu.tsx
51
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
52
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
53
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
54
+ function DropdownMenu(_a) {
55
+ var props = __objRest(_a, []);
56
+ return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Root, __spreadValues({ "data-slot": "dropdown-menu" }, props));
57
+ }
58
+ function DropdownMenuPortal(_a) {
59
+ var props = __objRest(_a, []);
60
+ return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Portal, __spreadValues({ "data-slot": "dropdown-menu-portal" }, props));
61
+ }
62
+ function DropdownMenuTrigger(_a) {
63
+ var props = __objRest(_a, []);
64
+ return /* @__PURE__ */ jsx2(
65
+ DropdownMenuPrimitive.Trigger,
66
+ __spreadValues({
67
+ "data-slot": "dropdown-menu-trigger"
68
+ }, props)
69
+ );
70
+ }
71
+ function DropdownMenuContent(_a) {
72
+ var _b = _a, {
73
+ className,
74
+ sideOffset = 4
75
+ } = _b, props = __objRest(_b, [
76
+ "className",
77
+ "sideOffset"
78
+ ]);
79
+ return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx2(
80
+ DropdownMenuPrimitive.Content,
81
+ __spreadValues({
82
+ "data-slot": "dropdown-menu-content",
83
+ sideOffset,
84
+ className: cn(
85
+ "bg-card/95 text-popover-foreground backdrop-blur-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-border-subtle p-1 shadow-md",
86
+ className
87
+ )
88
+ }, props)
89
+ ) });
90
+ }
91
+ function DropdownMenuGroup(_a) {
92
+ var props = __objRest(_a, []);
93
+ return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Group, __spreadValues({ "data-slot": "dropdown-menu-group" }, props));
94
+ }
95
+ function DropdownMenuItem(_a) {
96
+ var _b = _a, {
97
+ className,
98
+ inset,
99
+ variant = "default"
100
+ } = _b, props = __objRest(_b, [
101
+ "className",
102
+ "inset",
103
+ "variant"
104
+ ]);
105
+ return /* @__PURE__ */ jsx2(
106
+ DropdownMenuPrimitive.Item,
107
+ __spreadValues({
108
+ "data-slot": "dropdown-menu-item",
109
+ "data-inset": inset,
110
+ "data-variant": variant,
111
+ className: cn(
112
+ "focus:bg-surface-subtle data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none transition-colors duration-150 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
113
+ className
114
+ )
115
+ }, props)
116
+ );
117
+ }
118
+ function DropdownMenuCheckboxItem(_a) {
119
+ var _b = _a, {
120
+ className,
121
+ children,
122
+ checked
123
+ } = _b, props = __objRest(_b, [
124
+ "className",
125
+ "children",
126
+ "checked"
127
+ ]);
128
+ return /* @__PURE__ */ jsxs(
129
+ DropdownMenuPrimitive.CheckboxItem,
130
+ __spreadProps(__spreadValues({
131
+ "data-slot": "dropdown-menu-checkbox-item",
132
+ className: cn(
133
+ "focus:bg-surface-subtle relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none transition-colors duration-150 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
134
+ className
135
+ ),
136
+ checked
137
+ }, props), {
138
+ children: [
139
+ /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(CheckIcon, { className: "size-4" }) }) }),
140
+ children
141
+ ]
142
+ })
143
+ );
144
+ }
145
+ function DropdownMenuRadioGroup(_a) {
146
+ var props = __objRest(_a, []);
147
+ return /* @__PURE__ */ jsx2(
148
+ DropdownMenuPrimitive.RadioGroup,
149
+ __spreadValues({
150
+ "data-slot": "dropdown-menu-radio-group"
151
+ }, props)
152
+ );
153
+ }
154
+ function DropdownMenuRadioItem(_a) {
155
+ var _b = _a, {
156
+ className,
157
+ children
158
+ } = _b, props = __objRest(_b, [
159
+ "className",
160
+ "children"
161
+ ]);
162
+ return /* @__PURE__ */ jsxs(
163
+ DropdownMenuPrimitive.RadioItem,
164
+ __spreadProps(__spreadValues({
165
+ "data-slot": "dropdown-menu-radio-item",
166
+ className: cn(
167
+ "focus:bg-surface-subtle relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none transition-colors duration-150 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
168
+ className
169
+ )
170
+ }, props), {
171
+ children: [
172
+ /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(CircleIcon, { className: "size-2 fill-current" }) }) }),
173
+ children
174
+ ]
175
+ })
176
+ );
177
+ }
178
+ function DropdownMenuLabel(_a) {
179
+ var _b = _a, {
180
+ className,
181
+ inset
182
+ } = _b, props = __objRest(_b, [
183
+ "className",
184
+ "inset"
185
+ ]);
186
+ return /* @__PURE__ */ jsx2(
187
+ DropdownMenuPrimitive.Label,
188
+ __spreadValues({
189
+ "data-slot": "dropdown-menu-label",
190
+ "data-inset": inset,
191
+ className: cn(
192
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
193
+ className
194
+ )
195
+ }, props)
196
+ );
197
+ }
198
+ function DropdownMenuSeparator(_a) {
199
+ var _b = _a, {
200
+ className
201
+ } = _b, props = __objRest(_b, [
202
+ "className"
203
+ ]);
204
+ return /* @__PURE__ */ jsx2(
205
+ DropdownMenuPrimitive.Separator,
206
+ __spreadValues({
207
+ "data-slot": "dropdown-menu-separator",
208
+ className: cn("bg-border-subtle -mx-1 my-1 h-px", className)
209
+ }, props)
210
+ );
211
+ }
212
+ function DropdownMenuShortcut(_a) {
213
+ var _b = _a, {
214
+ className
215
+ } = _b, props = __objRest(_b, [
216
+ "className"
217
+ ]);
218
+ return /* @__PURE__ */ jsx2(
219
+ "span",
220
+ __spreadValues({
221
+ "data-slot": "dropdown-menu-shortcut",
222
+ className: cn(
223
+ "text-muted-foreground ml-auto text-xs tracking-widest",
224
+ className
225
+ )
226
+ }, props)
227
+ );
228
+ }
229
+ function DropdownMenuSub(_a) {
230
+ var props = __objRest(_a, []);
231
+ return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Sub, __spreadValues({ "data-slot": "dropdown-menu-sub" }, props));
232
+ }
233
+ function DropdownMenuSubTrigger(_a) {
234
+ var _b = _a, {
235
+ className,
236
+ inset,
237
+ children
238
+ } = _b, props = __objRest(_b, [
239
+ "className",
240
+ "inset",
241
+ "children"
242
+ ]);
243
+ return /* @__PURE__ */ jsxs(
244
+ DropdownMenuPrimitive.SubTrigger,
245
+ __spreadProps(__spreadValues({
246
+ "data-slot": "dropdown-menu-sub-trigger",
247
+ "data-inset": inset,
248
+ className: cn(
249
+ "focus:bg-surface-subtle data-[state=open]:bg-surface-subtle [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none transition-colors duration-150 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
250
+ className
251
+ )
252
+ }, props), {
253
+ children: [
254
+ children,
255
+ /* @__PURE__ */ jsx2(ChevronRightIcon, { className: "ml-auto size-4" })
256
+ ]
257
+ })
258
+ );
259
+ }
260
+ function DropdownMenuSubContent(_a) {
261
+ var _b = _a, {
262
+ className
263
+ } = _b, props = __objRest(_b, [
264
+ "className"
265
+ ]);
266
+ return /* @__PURE__ */ jsx2(
267
+ DropdownMenuPrimitive.SubContent,
268
+ __spreadValues({
269
+ "data-slot": "dropdown-menu-sub-content",
270
+ className: cn(
271
+ "bg-card/95 text-popover-foreground backdrop-blur-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg border border-border-subtle p-1 shadow-lg",
272
+ className
273
+ )
274
+ }, props)
275
+ );
276
+ }
277
+
278
+ // src/components/ui/button.tsx
279
+ import { cva as cva2 } from "class-variance-authority";
280
+ import { Slot as Slot2 } from "radix-ui";
281
+ import { jsx as jsx3 } from "react/jsx-runtime";
282
+ var buttonVariants = cva2(
283
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-150 cursor-pointer active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-accent focus-visible:ring-accent/25 focus-visible:ring-[2px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
284
+ {
285
+ variants: {
286
+ variant: {
287
+ default: "bg-primary text-primary-foreground shadow-[inset_0_1px_0_0_var(--color-primary-foreground)/0.1] hover:bg-primary/90",
288
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
289
+ outline: "border border-border-subtle bg-background hover:bg-surface-subtle",
290
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
291
+ ghost: "hover:bg-surface-subtle",
292
+ link: "text-primary underline-offset-4 hover:underline"
293
+ },
294
+ size: {
295
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
296
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
297
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
298
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
299
+ icon: "size-9",
300
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
301
+ "icon-sm": "size-8",
302
+ "icon-lg": "size-10"
303
+ }
304
+ },
305
+ defaultVariants: {
306
+ variant: "default",
307
+ size: "default"
308
+ }
309
+ }
310
+ );
311
+ function Button(_a) {
312
+ var _b = _a, {
313
+ className,
314
+ variant = "default",
315
+ size = "default",
316
+ asChild = false
317
+ } = _b, props = __objRest(_b, [
318
+ "className",
319
+ "variant",
320
+ "size",
321
+ "asChild"
322
+ ]);
323
+ const Comp = asChild ? Slot2.Root : "button";
324
+ return /* @__PURE__ */ jsx3(
325
+ Comp,
326
+ __spreadValues({
327
+ "data-slot": "button",
328
+ "data-variant": variant,
329
+ "data-size": size,
330
+ className: cn(buttonVariants({ variant, size, className }))
331
+ }, props)
332
+ );
333
+ }
334
+
335
+ export {
336
+ badgeVariants,
337
+ Badge,
338
+ DropdownMenu,
339
+ DropdownMenuPortal,
340
+ DropdownMenuTrigger,
341
+ DropdownMenuContent,
342
+ DropdownMenuGroup,
343
+ DropdownMenuItem,
344
+ DropdownMenuCheckboxItem,
345
+ DropdownMenuRadioGroup,
346
+ DropdownMenuRadioItem,
347
+ DropdownMenuLabel,
348
+ DropdownMenuSeparator,
349
+ DropdownMenuShortcut,
350
+ DropdownMenuSub,
351
+ DropdownMenuSubTrigger,
352
+ DropdownMenuSubContent,
353
+ buttonVariants,
354
+ Button
355
+ };
@@ -0,0 +1,87 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ThemeProvider as ThemeProvider$1 } from 'next-themes';
3
+ import * as React$1 from 'react';
4
+
5
+ interface NavProps {
6
+ /** Internal Link component (from consumer's next-intl routing) */
7
+ Link: React.ComponentType<React.ComponentPropsWithoutRef<"a"> & {
8
+ href: string;
9
+ }>;
10
+ /** Current pathname value (already resolved, not a hook) */
11
+ usePathname: () => string;
12
+ /** Where the .naeil logo links to. Default: "/" */
13
+ brandHref?: string;
14
+ /** Whether brandHref is external (renders <a>) or internal (renders Link). Default: false */
15
+ brandExternal?: boolean;
16
+ /** Custom brand content (replaces default Logo + ".naeil" text) */
17
+ brandSlot?: React.ReactNode;
18
+ /** When true, project links (except current) are rendered as external <a> to externalBaseUrl. Default: false */
19
+ projectsExternal?: boolean;
20
+ /** Base URL for external project links. Default: "https://naeil.dev" */
21
+ externalBaseUrl?: string;
22
+ /** Override href for the current project in the dropdown */
23
+ currentProjectKey?: string;
24
+ currentProjectHref?: string;
25
+ /** App-specific sub-navigation items after the project dropdown */
26
+ subNav?: Array<{
27
+ label: string;
28
+ href: string;
29
+ }>;
30
+ /** React node for auth UI (rendered in desktop right side + mobile bottom) */
31
+ authSlot?: React.ReactNode;
32
+ /** Show Projects dropdown in nav. Default: true */
33
+ showProjects?: boolean;
34
+ /** Show blog link in nav. Default: true */
35
+ showBlog?: boolean;
36
+ /** Right-side toolbar (theme toggle, locale switcher, etc.) */
37
+ toolbarSlot?: React.ReactNode;
38
+ }
39
+ declare function Nav({ Link, usePathname, brandHref, brandExternal, brandSlot, projectsExternal, externalBaseUrl, currentProjectKey, currentProjectHref, subNav, authSlot, showProjects, showBlog, toolbarSlot, }: NavProps): react_jsx_runtime.JSX.Element;
40
+
41
+ interface FooterProps {
42
+ /** Link component from consumer */
43
+ Link?: React.ComponentType<{
44
+ href: string;
45
+ children: React.ReactNode;
46
+ className?: string;
47
+ }>;
48
+ /** Whether links go to naeil.dev (external) or are internal. Default: false */
49
+ linksExternal?: boolean;
50
+ }
51
+ declare function Footer({ Link, linksExternal }: FooterProps): react_jsx_runtime.JSX.Element;
52
+
53
+ interface LocaleSwitcherProps {
54
+ /** Available locales */
55
+ locales: Array<{
56
+ code: string;
57
+ short: string;
58
+ full: string;
59
+ }>;
60
+ /** Current locale */
61
+ currentLocale: string;
62
+ /** Called when user selects a new locale */
63
+ onLocaleChange: (locale: string) => void;
64
+ }
65
+ declare function LocaleSwitcher({ locales, currentLocale, onLocaleChange, }: LocaleSwitcherProps): react_jsx_runtime.JSX.Element;
66
+
67
+ declare function ThemeToggle(): react_jsx_runtime.JSX.Element;
68
+
69
+ declare function ThemeProvider({ children, ...props }: React.ComponentProps<typeof ThemeProvider$1>): react_jsx_runtime.JSX.Element;
70
+
71
+ declare function ThemeToggleIcon(): react_jsx_runtime.JSX.Element;
72
+
73
+ declare function Logo({ size, mono, className, }: {
74
+ size?: number;
75
+ mono?: boolean;
76
+ className?: string;
77
+ }): react_jsx_runtime.JSX.Element;
78
+
79
+ type HeadingProps = React$1.HTMLAttributes<HTMLHeadingElement> & {
80
+ children: React$1.ReactNode;
81
+ };
82
+ declare const pageTitleClass = "text-foreground text-4xl font-bold tracking-tight lg:text-5xl";
83
+ declare const sectionTitleClass = "text-foreground text-lg font-bold";
84
+ declare function PageTitle({ className, children, ...props }: HeadingProps): react_jsx_runtime.JSX.Element;
85
+ declare function SectionTitle({ className, children, ...props }: HeadingProps): react_jsx_runtime.JSX.Element;
86
+
87
+ export { Footer, LocaleSwitcher, Logo, Nav, PageTitle, SectionTitle, ThemeProvider, ThemeToggle, ThemeToggleIcon, pageTitleClass, sectionTitleClass };