@hua-labs/create-hua-ux 0.1.0-alpha.0.1
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/LICENSE +21 -0
- package/README.md +183 -0
- package/dist/bin/create-hua-ux.d.ts +9 -0
- package/dist/bin/create-hua-ux.d.ts.map +1 -0
- package/dist/bin/create-hua-ux.js +37 -0
- package/dist/constants/versions.d.ts +55 -0
- package/dist/constants/versions.d.ts.map +1 -0
- package/dist/constants/versions.js +57 -0
- package/dist/create-project.d.ts +18 -0
- package/dist/create-project.d.ts.map +1 -0
- package/dist/create-project.js +237 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +259 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +177 -0
- package/dist/utils.d.ts +108 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +896 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/package.json +46 -0
- package/templates/nextjs/.claude/project-context.md +310 -0
- package/templates/nextjs/.claude/skills/hua-ux-framework/SKILL.md +187 -0
- package/templates/nextjs/.cursorrules +302 -0
- package/templates/nextjs/.eslintrc.json +1 -0
- package/templates/nextjs/README.md +431 -0
- package/templates/nextjs/ai-context.md +332 -0
- package/templates/nextjs/app/api/translations/[language]/[namespace]/route.ts +86 -0
- package/templates/nextjs/app/globals.css +24 -0
- package/templates/nextjs/app/layout-with-geo.example.tsx +106 -0
- package/templates/nextjs/app/layout.tsx +30 -0
- package/templates/nextjs/app/page-with-geo.example.tsx +80 -0
- package/templates/nextjs/app/page.tsx +28 -0
- package/templates/nextjs/components/I18nProviderWrapper.tsx +19 -0
- package/templates/nextjs/lib/i18n-setup.ts +11 -0
- package/templates/nextjs/middleware.ts.example +22 -0
- package/templates/nextjs/next.config.ts +36 -0
- package/templates/nextjs/postcss.config.js +6 -0
- package/templates/nextjs/store/useAppStore.ts +8 -0
- package/templates/nextjs/tailwind.config.js +8 -0
- package/templates/nextjs/translations/en/common.json +6 -0
- package/templates/nextjs/translations/ko/common.json +6 -0
- package/templates/nextjs/tsconfig.json +41 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# hua-ux Project AI Context
|
|
2
|
+
|
|
3
|
+
This document is a guide for AI tools (Cursor, etc.) to understand the structure and usage of this project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This project uses the **hua-ux framework** for Next.js applications.
|
|
8
|
+
|
|
9
|
+
**Core Philosophy**: "You don't need to know Next.js. Just configure and tell AI what to do."
|
|
10
|
+
|
|
11
|
+
## Architecture Layers
|
|
12
|
+
|
|
13
|
+
### Top Layer: AI Context & CLI (Current Location)
|
|
14
|
+
- `.cursorrules`: Rules for AI to follow when generating code
|
|
15
|
+
- `ai-context.md`: This document (project structure explanation)
|
|
16
|
+
- `.claude/project-context.md`: Claude-specific context
|
|
17
|
+
|
|
18
|
+
### Middle Layer: Framework & Config
|
|
19
|
+
- `hua-ux.config.ts`: Framework configuration
|
|
20
|
+
- `HuaUxLayout`: Automatic Provider setup
|
|
21
|
+
- `HuaUxPage`: Page wrapper (Motion, i18n, SEO automatically applied)
|
|
22
|
+
|
|
23
|
+
### Bottom Layer: Core & Types
|
|
24
|
+
- `@hua-labs/state`: State management
|
|
25
|
+
- `@hua-labs/motion-core`: Motion/animations
|
|
26
|
+
- `@hua-labs/i18n-core`: Internationalization
|
|
27
|
+
- `@hua-labs/ui`: UI component library
|
|
28
|
+
|
|
29
|
+
## Project Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Project Root/
|
|
33
|
+
├── app/ # Next.js App Router pages
|
|
34
|
+
│ ├── layout.tsx # Root layout (uses HuaUxLayout)
|
|
35
|
+
│ ├── page.tsx # Home page (uses HuaUxPage)
|
|
36
|
+
│ └── api/ # API Routes
|
|
37
|
+
│ └── translations/ # i18n translation API
|
|
38
|
+
├── components/ # Reusable components
|
|
39
|
+
├── lib/ # Utility functions
|
|
40
|
+
│ └── i18n-setup.ts # i18n setup
|
|
41
|
+
├── store/ # Zustand stores
|
|
42
|
+
│ └── useAppStore.ts # Global state
|
|
43
|
+
├── translations/ # Translation files
|
|
44
|
+
│ ├── ko/ # Korean
|
|
45
|
+
│ │ └── common.json # Common translations
|
|
46
|
+
│ └── en/ # English
|
|
47
|
+
│ └── common.json # Common translations
|
|
48
|
+
└── hua-ux.config.ts # Framework configuration
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Available Components and Hooks
|
|
52
|
+
|
|
53
|
+
### @hua-labs/ui Components
|
|
54
|
+
|
|
55
|
+
**Core UI**:
|
|
56
|
+
- `Button`, `Action`, `Input`, `Link`, `Icon`, `Avatar`, `Modal`
|
|
57
|
+
|
|
58
|
+
**Layout**:
|
|
59
|
+
- `Container`, `Grid`, `Stack`, `Divider`, `Card` (CardHeader, CardTitle, CardDescription, CardContent, CardFooter), `Panel`, `ActionToolbar`, `ComponentLayout`
|
|
60
|
+
|
|
61
|
+
**Navigation**:
|
|
62
|
+
- `Navigation`, `Breadcrumb`, `Pagination`, `PageNavigation`, `PageTransition`
|
|
63
|
+
|
|
64
|
+
**Data Display**:
|
|
65
|
+
- `Table`, `Badge`, `Progress`, `Skeleton` (various variants)
|
|
66
|
+
|
|
67
|
+
**Feedback**:
|
|
68
|
+
- `Alert` (Success, Warning, Error, Info), `Toast` (ToastProvider, useToast), `LoadingSpinner`, `Tooltip`
|
|
69
|
+
|
|
70
|
+
**Overlay**:
|
|
71
|
+
- `Popover`, `Dropdown`, `Drawer`, `BottomSheet`, `ConfirmModal`
|
|
72
|
+
|
|
73
|
+
**Form**:
|
|
74
|
+
- `Form`, `Label`, `Checkbox`, `Radio`, `Select`, `Switch`, `Slider`, `Textarea`, `DatePicker`, `Upload`, `Autocomplete`
|
|
75
|
+
|
|
76
|
+
**Interactive**:
|
|
77
|
+
- `Accordion`, `Tabs`, `Menu`, `ContextMenu`, `Command`
|
|
78
|
+
|
|
79
|
+
**Specialized**:
|
|
80
|
+
- `ScrollArea`, `ScrollToTop`, `ThemeProvider`, `ThemeToggle`, `useTheme`
|
|
81
|
+
|
|
82
|
+
### @hua-labs/hua-ux/framework
|
|
83
|
+
|
|
84
|
+
**Framework Components**:
|
|
85
|
+
- `HuaUxLayout`: Automatic Provider setup
|
|
86
|
+
- `HuaUxPage`: Page wrapper (Motion, i18n, SEO automatically applied)
|
|
87
|
+
- `UnifiedProviders`: All Providers unified
|
|
88
|
+
- `BrandedButton`, `BrandedCard`: Components with automatic branding
|
|
89
|
+
- `ErrorBoundary`: Error boundary
|
|
90
|
+
|
|
91
|
+
**Hooks**:
|
|
92
|
+
- `useMotion`: Unified motion hook
|
|
93
|
+
- `useData`: Client data fetching
|
|
94
|
+
- `useFocusManagement`, `useFocusTrap`: Accessibility hooks
|
|
95
|
+
- `useDelayedLoading`, `useLoadingState`: Loading state hooks
|
|
96
|
+
- `useLiveRegion`: Screen reader support
|
|
97
|
+
|
|
98
|
+
**Utilities**:
|
|
99
|
+
- `fetchData`: Server data fetching
|
|
100
|
+
- `generatePageMetadata`: SEO metadata generation
|
|
101
|
+
- `generateGEOMetadata`: GEO metadata generation
|
|
102
|
+
- `createI18nMiddleware`: i18n middleware creation
|
|
103
|
+
|
|
104
|
+
**Configuration**:
|
|
105
|
+
- `defineConfig`: Framework configuration definition
|
|
106
|
+
- `getConfig`: Get configuration
|
|
107
|
+
|
|
108
|
+
### @hua-labs/motion-core
|
|
109
|
+
|
|
110
|
+
**Motion Hooks**:
|
|
111
|
+
- `useFadeIn`, `useSlideUp`, `useSlideLeft`, `useSlideRight`, `useScaleIn`, `useBounceIn`, `usePulse`, `useSpringMotion`
|
|
112
|
+
- `useHoverMotion`, `useClickToggle`, `useFocusToggle`
|
|
113
|
+
- `useScrollReveal`, `useScrollProgress`
|
|
114
|
+
|
|
115
|
+
### @hua-labs/i18n-core
|
|
116
|
+
|
|
117
|
+
**i18n Hooks**:
|
|
118
|
+
- `useTranslation`: Translation hook
|
|
119
|
+
- `useLanguage`: Language change hook
|
|
120
|
+
|
|
121
|
+
## Key Patterns
|
|
122
|
+
|
|
123
|
+
### 1. Page Creation Pattern
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
// app/my-page/page.tsx
|
|
127
|
+
import { HuaUxPage } from '@hua-labs/hua-ux/framework';
|
|
128
|
+
import { useTranslation } from '@hua-labs/i18n-core';
|
|
129
|
+
|
|
130
|
+
export default function MyPage() {
|
|
131
|
+
const { t } = useTranslation('my-page');
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<HuaUxPage title={t('title')} description={t('description')}>
|
|
135
|
+
<h1>{t('title')}</h1>
|
|
136
|
+
{/* content */}
|
|
137
|
+
</HuaUxPage>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Important**:
|
|
143
|
+
- Wrapping with `HuaUxPage` automatically applies Motion, i18n, SEO
|
|
144
|
+
- Add translation keys to `translations/{language}/my-page.json`
|
|
145
|
+
- Create as Server Component (add `'use client'` only when client features are needed)
|
|
146
|
+
|
|
147
|
+
### 2. Client Component Creation Pattern
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// components/MyComponent.tsx
|
|
151
|
+
'use client';
|
|
152
|
+
|
|
153
|
+
import { Card, Button } from '@hua-labs/ui';
|
|
154
|
+
import { useMotion } from '@hua-labs/hua-ux/framework';
|
|
155
|
+
import { useTranslation } from '@hua-labs/i18n-core';
|
|
156
|
+
|
|
157
|
+
export function MyComponent() {
|
|
158
|
+
const { t } = useTranslation('my-component');
|
|
159
|
+
const motion = useMotion();
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<Card ref={motion.ref} style={motion.style}>
|
|
163
|
+
<h2>{t('title')}</h2>
|
|
164
|
+
<Button>{t('button')}</Button>
|
|
165
|
+
</Card>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Important**:
|
|
171
|
+
- Client components require `'use client'`
|
|
172
|
+
- Utilize framework components (`@hua-labs/ui`, `@hua-labs/motion-core`)
|
|
173
|
+
- Consider applying motion
|
|
174
|
+
|
|
175
|
+
### 3. Data Fetching Pattern
|
|
176
|
+
|
|
177
|
+
**Server Component**:
|
|
178
|
+
```tsx
|
|
179
|
+
// app/data/page.tsx
|
|
180
|
+
import { fetchData } from '@hua-labs/hua-ux/framework';
|
|
181
|
+
|
|
182
|
+
export default async function DataPage() {
|
|
183
|
+
const data = await fetchData<DataType>('/api/data');
|
|
184
|
+
return <div>{/* display data */}</div>;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Client Component**:
|
|
189
|
+
```tsx
|
|
190
|
+
// components/DataComponent.tsx
|
|
191
|
+
'use client';
|
|
192
|
+
|
|
193
|
+
import { useData } from '@hua-labs/hua-ux/framework';
|
|
194
|
+
import { LoadingSpinner, AlertError } from '@hua-labs/ui';
|
|
195
|
+
|
|
196
|
+
export function DataComponent() {
|
|
197
|
+
const { data, isLoading, error } = useData<DataType>('/api/data');
|
|
198
|
+
|
|
199
|
+
if (isLoading) return <LoadingSpinner />;
|
|
200
|
+
if (error) return <AlertError>{error.message}</AlertError>;
|
|
201
|
+
return <div>{/* display data */}</div>;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 4. Translation File Pattern
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
// translations/ko/my-page.json
|
|
209
|
+
{
|
|
210
|
+
"title": "Title",
|
|
211
|
+
"description": "Description",
|
|
212
|
+
"button": "Button"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// translations/en/my-page.json
|
|
216
|
+
{
|
|
217
|
+
"title": "Title",
|
|
218
|
+
"description": "Description",
|
|
219
|
+
"button": "Button"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Important**:
|
|
224
|
+
- Add all translation keys to both Korean and English
|
|
225
|
+
- Namespace should match page name
|
|
226
|
+
- Use `common` namespace for shared translations
|
|
227
|
+
|
|
228
|
+
## Understanding Configuration Files
|
|
229
|
+
|
|
230
|
+
### hua-ux.config.ts
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { defineConfig } from '@hua-labs/hua-ux/framework';
|
|
234
|
+
|
|
235
|
+
export default defineConfig({
|
|
236
|
+
preset: 'product', // 'product' or 'marketing'
|
|
237
|
+
|
|
238
|
+
i18n: {
|
|
239
|
+
defaultLanguage: 'ko',
|
|
240
|
+
supportedLanguages: ['ko', 'en'],
|
|
241
|
+
fallbackLanguage: 'en',
|
|
242
|
+
namespaces: ['common'],
|
|
243
|
+
translationLoader: 'api',
|
|
244
|
+
translationApiPath: '/api/translations',
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
motion: {
|
|
248
|
+
defaultPreset: 'product',
|
|
249
|
+
enableAnimations: true,
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
state: {
|
|
253
|
+
persist: true,
|
|
254
|
+
ssr: true,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Preset Selection**:
|
|
260
|
+
- `'product'`: For product pages (professional, efficient)
|
|
261
|
+
- `'marketing'`: For marketing pages (dramatic, eye-catching)
|
|
262
|
+
|
|
263
|
+
**Branding Configuration** (optional):
|
|
264
|
+
```typescript
|
|
265
|
+
branding: {
|
|
266
|
+
colors: {
|
|
267
|
+
primary: '#000000',
|
|
268
|
+
secondary: '#666666',
|
|
269
|
+
},
|
|
270
|
+
typography: {
|
|
271
|
+
fontFamily: {
|
|
272
|
+
sans: ['Pretendard', 'sans-serif'],
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Guidelines for AI Code Generation
|
|
279
|
+
|
|
280
|
+
1. **When Creating Pages**:
|
|
281
|
+
- Wrap with `HuaUxPage`
|
|
282
|
+
- Generate translation files together
|
|
283
|
+
- Use `useTranslation` hook
|
|
284
|
+
- Prefer Server Components (add `'use client'` only when client features are needed)
|
|
285
|
+
|
|
286
|
+
2. **When Creating Components**:
|
|
287
|
+
- Add `'use client'` directive (for client components)
|
|
288
|
+
- Utilize framework components
|
|
289
|
+
- Consider applying motion
|
|
290
|
+
- Consider accessibility (aria-label, etc.)
|
|
291
|
+
|
|
292
|
+
3. **When Adding Translations**:
|
|
293
|
+
- Add both Korean and English
|
|
294
|
+
- Maintain namespace consistency
|
|
295
|
+
- Use `common` namespace for shared translations
|
|
296
|
+
|
|
297
|
+
4. **When Changing Configuration**:
|
|
298
|
+
- Only modify `hua-ux.config.ts`
|
|
299
|
+
- Prefer Preset (over manual configuration)
|
|
300
|
+
|
|
301
|
+
5. **When Fetching Data**:
|
|
302
|
+
- Use `fetchData` in Server Components
|
|
303
|
+
- Use `useData` in Client Components
|
|
304
|
+
- Error handling is required
|
|
305
|
+
|
|
306
|
+
## Common Commands
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# Start development server
|
|
310
|
+
pnpm dev
|
|
311
|
+
|
|
312
|
+
# Build
|
|
313
|
+
pnpm build
|
|
314
|
+
|
|
315
|
+
# Start production
|
|
316
|
+
pnpm start
|
|
317
|
+
|
|
318
|
+
# Lint
|
|
319
|
+
pnpm lint
|
|
320
|
+
|
|
321
|
+
# Fix linting automatically
|
|
322
|
+
pnpm lint:fix
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## References
|
|
326
|
+
|
|
327
|
+
- Framework docs: `node_modules/@hua-labs/hua-ux/README.md`
|
|
328
|
+
- UI components: `node_modules/@hua-labs/ui/README.md`
|
|
329
|
+
- Motion: `node_modules/@hua-labs/motion-core/README.md`
|
|
330
|
+
- i18n: `node_modules/@hua-labs/i18n-core-zustand/README.md`
|
|
331
|
+
- `.cursorrules`: Cursor IDE rules
|
|
332
|
+
- `.claude/project-context.md`: Claude-specific context
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Translation API Route (Dynamic Path)
|
|
7
|
+
*
|
|
8
|
+
* 번역 파일을 제공하는 API 엔드포인트입니다.
|
|
9
|
+
* Provides translation files via API endpoint.
|
|
10
|
+
*
|
|
11
|
+
* 경로: /api/translations/[language]/[namespace]
|
|
12
|
+
* Path: /api/translations/[language]/[namespace]
|
|
13
|
+
*
|
|
14
|
+
* @param request - Next.js request object
|
|
15
|
+
* @param params - Route parameters: { language: string, namespace: string }
|
|
16
|
+
* @returns Translation object or error response
|
|
17
|
+
*/
|
|
18
|
+
export async function GET(
|
|
19
|
+
request: NextRequest,
|
|
20
|
+
{ params }: { params: { language: string; namespace: string } }
|
|
21
|
+
) {
|
|
22
|
+
const { language, namespace } = params;
|
|
23
|
+
|
|
24
|
+
// 언어 검증
|
|
25
|
+
// Validate language
|
|
26
|
+
const supportedLanguages = ['ko', 'en'];
|
|
27
|
+
if (!supportedLanguages.includes(language)) {
|
|
28
|
+
return NextResponse.json(
|
|
29
|
+
{ error: 'Unsupported language' },
|
|
30
|
+
{ status: 400 }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 네임스페이스 검증 (보안: 파일 경로 조작 방지)
|
|
35
|
+
// Validate namespace (security: prevent path traversal)
|
|
36
|
+
const safeNamespace = namespace.replace(/[^a-zA-Z0-9-_]/g, '');
|
|
37
|
+
if (safeNamespace !== namespace) {
|
|
38
|
+
return NextResponse.json(
|
|
39
|
+
{ error: 'Invalid namespace' },
|
|
40
|
+
{ status: 400 }
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const filePath = join(process.cwd(), 'translations', language, `${safeNamespace}.json`);
|
|
46
|
+
const fileContents = readFileSync(filePath, 'utf-8');
|
|
47
|
+
const translations = JSON.parse(fileContents);
|
|
48
|
+
|
|
49
|
+
return NextResponse.json(translations, {
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
52
|
+
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// 에러 타입 구분
|
|
57
|
+
// Distinguish error types
|
|
58
|
+
|
|
59
|
+
// 파일이 없는 경우
|
|
60
|
+
// File not found
|
|
61
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ error: 'Translation file not found' },
|
|
64
|
+
{ status: 404 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// JSON 파싱 에러
|
|
69
|
+
// JSON parse error
|
|
70
|
+
if (error instanceof SyntaxError) {
|
|
71
|
+
console.error('Translation JSON parse error:', error);
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: 'Invalid translation file format' },
|
|
74
|
+
{ status: 500 }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 기타 에러
|
|
79
|
+
// Other errors
|
|
80
|
+
console.error('Translation load error:', error);
|
|
81
|
+
return NextResponse.json(
|
|
82
|
+
{ error: 'Internal server error' },
|
|
83
|
+
{ status: 500 }
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--background: #ffffff;
|
|
5
|
+
--foreground: #171717;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@theme inline {
|
|
9
|
+
--color-background: var(--background);
|
|
10
|
+
--color-foreground: var(--foreground);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@media (prefers-color-scheme: dark) {
|
|
14
|
+
:root {
|
|
15
|
+
--background: #0a0a0a;
|
|
16
|
+
--foreground: #ededed;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
background: var(--background);
|
|
22
|
+
color: var(--foreground);
|
|
23
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
24
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root Layout with GEO (Generative Engine Optimization)
|
|
3
|
+
*
|
|
4
|
+
* AI 검색 엔진 최적화를 위한 예제 레이아웃
|
|
5
|
+
* Example layout with AI search engine optimization
|
|
6
|
+
*
|
|
7
|
+
* 사용 방법:
|
|
8
|
+
* 1. 이 파일을 app/layout.tsx로 복사
|
|
9
|
+
* 2. GEO 설정을 프로젝트에 맞게 수정
|
|
10
|
+
* 3. JSON-LD 스크립트를 추가하려면 Script 컴포넌트 import
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Metadata } from "next";
|
|
14
|
+
import { headers } from "next/headers";
|
|
15
|
+
import Script from "next/script";
|
|
16
|
+
import "./globals.css";
|
|
17
|
+
import { HuaUxLayout } from "@hua-labs/hua-ux/framework";
|
|
18
|
+
import { generateGEOMetadata, renderJSONLD } from "@hua-labs/hua-ux/framework";
|
|
19
|
+
|
|
20
|
+
// GEO 메타데이터 생성
|
|
21
|
+
// Generate GEO metadata for AI search engines
|
|
22
|
+
const geoMetadata = generateGEOMetadata({
|
|
23
|
+
name: 'My App',
|
|
24
|
+
description: 'Built with hua-ux framework - A modern UX framework for Next.js',
|
|
25
|
+
version: '1.0.0',
|
|
26
|
+
applicationCategory: ['UX Framework', 'Developer Tool'],
|
|
27
|
+
programmingLanguage: ['TypeScript', 'React', 'Next.js'],
|
|
28
|
+
features: [
|
|
29
|
+
'Internationalization (i18n)',
|
|
30
|
+
'Motion animations',
|
|
31
|
+
'Accessibility (WCAG 2.1)',
|
|
32
|
+
'Error handling',
|
|
33
|
+
'Loading state optimization',
|
|
34
|
+
],
|
|
35
|
+
useCases: [
|
|
36
|
+
'Building multilingual Next.js applications',
|
|
37
|
+
'Creating accessible web applications',
|
|
38
|
+
'Rapid prototyping with AI-friendly documentation',
|
|
39
|
+
],
|
|
40
|
+
keywords: [
|
|
41
|
+
'nextjs',
|
|
42
|
+
'react',
|
|
43
|
+
'ux',
|
|
44
|
+
'i18n',
|
|
45
|
+
'internationalization',
|
|
46
|
+
'accessibility',
|
|
47
|
+
'a11y',
|
|
48
|
+
'motion',
|
|
49
|
+
'animation',
|
|
50
|
+
'framework',
|
|
51
|
+
],
|
|
52
|
+
codeRepository: 'https://github.com/your-org/your-app',
|
|
53
|
+
documentationUrl: 'https://your-app.com/docs',
|
|
54
|
+
license: 'MIT',
|
|
55
|
+
author: {
|
|
56
|
+
name: 'Your Organization',
|
|
57
|
+
url: 'https://your-org.com',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Next.js Metadata와 GEO 통합
|
|
62
|
+
// Integrate GEO with Next.js Metadata
|
|
63
|
+
export const metadata: Metadata = {
|
|
64
|
+
title: geoMetadata.meta.find((m) => m.name === 'description')?.content || 'My App',
|
|
65
|
+
description: geoMetadata.meta.find((m) => m.name === 'description')?.content,
|
|
66
|
+
keywords: geoMetadata.meta.find((m) => m.name === 'keywords')?.content?.split(', '),
|
|
67
|
+
openGraph: {
|
|
68
|
+
title: geoMetadata.openGraph?.find((og) => og.property === 'og:title')?.content,
|
|
69
|
+
description: geoMetadata.openGraph?.find((og) => og.property === 'og:description')?.content,
|
|
70
|
+
type: 'website',
|
|
71
|
+
},
|
|
72
|
+
twitter: {
|
|
73
|
+
card: geoMetadata.twitter?.find((t) => t.name === 'twitter:card')?.content as 'summary_large_image',
|
|
74
|
+
title: geoMetadata.twitter?.find((t) => t.name === 'twitter:title')?.content,
|
|
75
|
+
description: geoMetadata.twitter?.find((t) => t.name === 'twitter:description')?.content,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default function RootLayout({
|
|
80
|
+
children,
|
|
81
|
+
}: Readonly<{
|
|
82
|
+
children: React.ReactNode;
|
|
83
|
+
}>) {
|
|
84
|
+
// 동적 언어 설정 (middleware에서 설정한 헤더 사용)
|
|
85
|
+
// Dynamic language setting (use header set by middleware)
|
|
86
|
+
const headersList = headers();
|
|
87
|
+
const language = headersList.get('x-language') || 'ko'; // 기본값: 'ko'
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<html lang={language}>
|
|
91
|
+
<head>
|
|
92
|
+
{/* JSON-LD Structured Data for AI search engines */}
|
|
93
|
+
{/* AI 검색 엔진을 위한 구조화된 데이터 */}
|
|
94
|
+
{geoMetadata.jsonLd.map((jsonLd, index) => (
|
|
95
|
+
<Script
|
|
96
|
+
key={index}
|
|
97
|
+
{...renderJSONLD(jsonLd)}
|
|
98
|
+
/>
|
|
99
|
+
))}
|
|
100
|
+
</head>
|
|
101
|
+
<body className="antialiased">
|
|
102
|
+
<HuaUxLayout>{children}</HuaUxLayout>
|
|
103
|
+
</body>
|
|
104
|
+
</html>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { headers } from "next/headers";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
import { HuaUxLayout } from "@hua-labs/hua-ux/framework";
|
|
5
|
+
|
|
6
|
+
export const metadata: Metadata = {
|
|
7
|
+
title: "HUA UX App",
|
|
8
|
+
description: "Created with hua-ux",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function RootLayout({
|
|
12
|
+
children,
|
|
13
|
+
}: Readonly<{
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}>) {
|
|
16
|
+
// 동적 언어 설정 (middleware에서 설정한 헤더 사용)
|
|
17
|
+
// Dynamic language setting (use header set by middleware)
|
|
18
|
+
// middleware.ts에서 'x-language' 헤더를 설정하면 여기서 읽을 수 있습니다.
|
|
19
|
+
// If middleware.ts sets 'x-language' header, it can be read here.
|
|
20
|
+
const headersList = headers();
|
|
21
|
+
const language = headersList.get('x-language') || 'ko'; // 기본값: 'ko'
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<html lang={language}>
|
|
25
|
+
<body className="antialiased">
|
|
26
|
+
<HuaUxLayout>{children}</HuaUxLayout>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home Page with GEO and SEO Metadata
|
|
3
|
+
*
|
|
4
|
+
* GEO 및 SEO 메타데이터가 포함된 홈 페이지 예제
|
|
5
|
+
* Example home page with GEO and SEO metadata
|
|
6
|
+
*
|
|
7
|
+
* 사용 방법:
|
|
8
|
+
* 1. Server Component로 page.tsx 생성
|
|
9
|
+
* 2. Client Component로 page-content.tsx 생성
|
|
10
|
+
* 3. 이 예제를 참고하여 구현
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================
|
|
14
|
+
// app/page.tsx (Server Component)
|
|
15
|
+
// ============================================
|
|
16
|
+
import { generatePageMetadata, generateGEOMetadata, renderJSONLD } from '@hua-labs/hua-ux/framework';
|
|
17
|
+
import Script from 'next/script';
|
|
18
|
+
import { HomePageContent } from './page-content';
|
|
19
|
+
|
|
20
|
+
// 페이지별 GEO 메타데이터 생성
|
|
21
|
+
// Generate page-specific GEO metadata
|
|
22
|
+
const pageGeoMetadata = generateGEOMetadata({
|
|
23
|
+
name: 'Home - My App',
|
|
24
|
+
description: 'Welcome to My App - Built with hua-ux framework',
|
|
25
|
+
applicationCategory: 'WebApplication',
|
|
26
|
+
keywords: ['home', 'welcome', 'nextjs', 'react'],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Next.js Metadata 생성 (GEO와 통합)
|
|
30
|
+
// Generate Next.js Metadata (integrated with GEO)
|
|
31
|
+
export const metadata = generatePageMetadata({
|
|
32
|
+
title: 'Home',
|
|
33
|
+
description: pageGeoMetadata.meta.find((m) => m.name === 'description')?.content || 'Welcome',
|
|
34
|
+
seo: {
|
|
35
|
+
keywords: ['home', 'welcome'],
|
|
36
|
+
ogImage: '/og-home.png',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export default function HomePage() {
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
{/* JSON-LD Structured Data */}
|
|
44
|
+
<Script {...renderJSONLD(pageGeoMetadata.jsonLd[0])} />
|
|
45
|
+
<HomePageContent />
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================
|
|
51
|
+
// app/page-content.tsx (Client Component)
|
|
52
|
+
// ============================================
|
|
53
|
+
'use client';
|
|
54
|
+
|
|
55
|
+
import { HuaUxPage } from "@hua-labs/hua-ux/framework";
|
|
56
|
+
import { Button, Card } from "@hua-labs/hua-ux";
|
|
57
|
+
import { useTranslation } from '@hua-labs/i18n-core';
|
|
58
|
+
|
|
59
|
+
export function HomePageContent() {
|
|
60
|
+
const { t } = useTranslation('common');
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<HuaUxPage title={t('title')} description={t('welcome')}>
|
|
64
|
+
<div className="min-h-screen flex items-center justify-center p-8">
|
|
65
|
+
<Card className="max-w-2xl w-full">
|
|
66
|
+
<div className="p-8 text-center space-y-6">
|
|
67
|
+
<h1 className="text-4xl font-bold">{t('title')}</h1>
|
|
68
|
+
<p className="text-gray-600 dark:text-gray-400">
|
|
69
|
+
{t('welcome')}
|
|
70
|
+
</p>
|
|
71
|
+
<div className="flex gap-4 justify-center">
|
|
72
|
+
<Button variant="default">{t('getStarted')}</Button>
|
|
73
|
+
<Button variant="outline">{t('learnMore')}</Button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</Card>
|
|
77
|
+
</div>
|
|
78
|
+
</HuaUxPage>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { HuaUxPage } from "@hua-labs/hua-ux/framework";
|
|
4
|
+
import { Button, Card } from "@hua-labs/hua-ux";
|
|
5
|
+
import { useTranslation } from '@hua-labs/i18n-core';
|
|
6
|
+
|
|
7
|
+
export default function HomePage() {
|
|
8
|
+
const { t } = useTranslation('common');
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<HuaUxPage title={t('title')} description={t('welcome')}>
|
|
12
|
+
<div className="min-h-screen flex items-center justify-center p-8">
|
|
13
|
+
<Card className="max-w-2xl w-full">
|
|
14
|
+
<div className="p-8 text-center space-y-6">
|
|
15
|
+
<h1 className="text-4xl font-bold">{t('title')}</h1>
|
|
16
|
+
<p className="text-gray-600 dark:text-gray-400">
|
|
17
|
+
{t('welcome')}
|
|
18
|
+
</p>
|
|
19
|
+
<div className="flex gap-4 justify-center">
|
|
20
|
+
<Button variant="default">{t('getStarted')}</Button>
|
|
21
|
+
<Button variant="outline">{t('learnMore')}</Button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</Card>
|
|
25
|
+
</div>
|
|
26
|
+
</HuaUxPage>
|
|
27
|
+
);
|
|
28
|
+
}
|