@mandujs/cli 0.15.1 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +33 -33
- package/README.md +354 -354
- package/package.json +2 -2
- package/src/commands/check.ts +71 -7
- package/src/commands/contract.ts +173 -173
- package/src/commands/dev.ts +9 -42
- package/src/commands/guard-arch.ts +303 -303
- package/src/commands/init.ts +50 -5
- package/src/commands/monitor.ts +300 -300
- package/src/commands/openapi.ts +107 -107
- package/src/commands/registry.ts +1 -0
- package/src/commands/start.ts +9 -42
- package/src/errors/codes.ts +35 -35
- package/src/errors/index.ts +2 -2
- package/src/errors/messages.ts +143 -143
- package/src/hooks/index.ts +17 -17
- package/src/hooks/preaction.ts +256 -256
- package/src/main.ts +9 -7
- package/src/terminal/banner.ts +166 -166
- package/src/terminal/help.ts +306 -306
- package/src/terminal/index.ts +71 -71
- package/src/terminal/output.ts +295 -295
- package/src/terminal/palette.ts +30 -30
- package/src/terminal/progress.ts +327 -327
- package/src/terminal/stream-writer.ts +214 -214
- package/src/terminal/table.ts +354 -354
- package/src/terminal/theme.ts +142 -142
- package/src/util/bun.ts +6 -6
- package/src/util/fs.ts +23 -23
- package/src/util/handlers.ts +49 -5
- package/src/util/lockfile.ts +66 -0
- package/src/util/output.ts +22 -22
- package/src/util/port.ts +71 -71
- package/templates/default/AGENTS.md +96 -96
- package/templates/default/app/api/health/route.ts +13 -13
- package/templates/default/app/globals.css +49 -49
- package/templates/default/app/layout.tsx +27 -27
- package/templates/default/app/page.tsx +38 -38
- package/templates/default/src/client/shared/lib/utils.ts +16 -16
- package/templates/default/src/client/shared/ui/button.tsx +57 -57
- package/templates/default/src/client/shared/ui/card.tsx +1 -1
- package/templates/default/src/client/shared/ui/index.ts +21 -21
- package/templates/default/src/client/shared/ui/input.tsx +5 -1
- package/templates/default/tests/example.test.ts +58 -58
- package/templates/default/tests/helpers.ts +52 -52
- package/templates/default/tests/setup.ts +9 -9
- package/templates/default/tsconfig.json +23 -23
- package/templates/realtime-chat/AGENTS.md +96 -0
- package/templates/realtime-chat/app/api/chat/messages/route.ts +63 -0
- package/templates/realtime-chat/app/api/chat/stream/route.ts +48 -0
- package/templates/realtime-chat/app/api/health/route.ts +13 -0
- package/templates/realtime-chat/app/globals.css +49 -0
- package/templates/realtime-chat/app/layout.tsx +27 -0
- package/templates/realtime-chat/app/page.tsx +16 -0
- package/templates/realtime-chat/package.json +34 -0
- package/templates/realtime-chat/src/client/app/index.ts +1 -0
- package/templates/realtime-chat/src/client/entities/index.ts +1 -0
- package/templates/realtime-chat/src/client/features/chat/chat-api.ts +177 -0
- package/templates/realtime-chat/src/client/features/chat/realtime-chat-starter.client.tsx +89 -0
- package/templates/realtime-chat/src/client/features/chat/use-realtime-chat.ts +73 -0
- package/templates/realtime-chat/src/client/features/index.ts +1 -0
- package/templates/realtime-chat/src/client/pages/index.ts +1 -0
- package/templates/realtime-chat/src/client/shared/index.ts +1 -0
- package/templates/realtime-chat/src/client/shared/lib/utils.ts +16 -0
- package/templates/realtime-chat/src/client/shared/ui/button.tsx +57 -0
- package/templates/realtime-chat/src/client/shared/ui/card.tsx +78 -0
- package/templates/realtime-chat/src/client/shared/ui/index.ts +21 -0
- package/templates/realtime-chat/src/client/shared/ui/input.tsx +28 -0
- package/templates/realtime-chat/src/client/widgets/index.ts +1 -0
- package/templates/realtime-chat/src/server/api/index.ts +1 -0
- package/templates/realtime-chat/src/server/application/ai-adapter.ts +24 -0
- package/templates/realtime-chat/src/server/application/chat-store.ts +88 -0
- package/templates/realtime-chat/src/server/application/index.ts +1 -0
- package/templates/realtime-chat/src/server/core/index.ts +1 -0
- package/templates/realtime-chat/src/server/domain/index.ts +1 -0
- package/templates/realtime-chat/src/server/infra/index.ts +1 -0
- package/templates/realtime-chat/src/shared/contracts/chat.ts +29 -0
- package/templates/realtime-chat/src/shared/contracts/index.ts +1 -0
- package/templates/realtime-chat/src/shared/env/index.ts +1 -0
- package/templates/realtime-chat/src/shared/schema/index.ts +1 -0
- package/templates/realtime-chat/src/shared/types/index.ts +1 -0
- package/templates/realtime-chat/src/shared/utils/client/index.ts +1 -0
- package/templates/realtime-chat/src/shared/utils/server/index.ts +1 -0
- package/templates/realtime-chat/tests/chat-api.sse.test.ts +151 -0
- package/templates/realtime-chat/tests/chat-starter.test.ts +149 -0
- package/templates/realtime-chat/tests/chat-store.concurrency.test.ts +39 -0
- package/templates/realtime-chat/tests/example.test.ts +58 -0
- package/templates/realtime-chat/tests/helpers.ts +52 -0
- package/templates/realtime-chat/tests/setup.ts +9 -0
- package/templates/realtime-chat/tsconfig.json +23 -0
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
# AI Agent Instructions for Mandu Project
|
|
2
|
-
|
|
3
|
-
이 프로젝트는 **Mandu Framework**로 구축되었습니다. AI 에이전트가 이 프로젝트를 다룰 때 아래 지침을 따라주세요.
|
|
4
|
-
|
|
5
|
-
## 패키지 매니저: Bun (필수)
|
|
6
|
-
|
|
7
|
-
**⚠️ 중요: 이 프로젝트는 Bun만 사용합니다. npm/yarn/pnpm을 사용하지 마세요.**
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# ✅ 올바른 명령어
|
|
11
|
-
bun install # 의존성 설치
|
|
12
|
-
bun add <package> # 패키지 추가
|
|
13
|
-
bun remove <package> # 패키지 제거
|
|
14
|
-
bun run dev # 개발 서버 시작
|
|
15
|
-
bun run build # 프로덕션 빌드
|
|
16
|
-
bun test # 테스트 실행
|
|
17
|
-
|
|
18
|
-
# ❌ 사용 금지
|
|
19
|
-
npm install / yarn install / pnpm install
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## 프로젝트 구조
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
├── app/ # FS 기반 라우팅 (페이지, API)
|
|
26
|
-
│ ├── page.tsx # / 라우트
|
|
27
|
-
│ ├── layout.tsx # 루트 레이아웃
|
|
28
|
-
│ ├── globals.css # Tailwind CSS (v4)
|
|
29
|
-
│ └── api/ # API 라우트
|
|
30
|
-
├── src/
|
|
31
|
-
│ ├── client/ # 클라이언트 코드 (FSD 구조)
|
|
32
|
-
│ │ ├── shared/ # 공용 UI, 유틸리티
|
|
33
|
-
│ │ ├── entities/ # 엔티티 컴포넌트
|
|
34
|
-
│ │ ├── features/ # 기능 컴포넌트
|
|
35
|
-
│ │ └── widgets/ # 위젯/Island 컴포넌트
|
|
36
|
-
│ ├── server/ # 서버 코드 (Clean Architecture)
|
|
37
|
-
│ │ ├── domain/ # 도메인 모델
|
|
38
|
-
│ │ ├── application/ # 비즈니스 로직
|
|
39
|
-
│ │ └── infra/ # 인프라/DB
|
|
40
|
-
│ └── shared/ # 클라이언트-서버 공유 코드
|
|
41
|
-
│ ├── contracts/ # API 계약 타입
|
|
42
|
-
│ └── types/ # 공용 타입
|
|
43
|
-
└── mandu.config.ts # Mandu 설정 (선택)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## 주요 규칙
|
|
47
|
-
|
|
48
|
-
### 1. Island 컴포넌트
|
|
49
|
-
클라이언트 상호작용이 필요한 컴포넌트는 `*.island.tsx`로 명명:
|
|
50
|
-
```tsx
|
|
51
|
-
// src/client/widgets/counter/Counter.island.tsx
|
|
52
|
-
"use client";
|
|
53
|
-
export function CounterIsland() { ... }
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 2. API 라우트
|
|
57
|
-
`app/api/` 폴더에 `route.ts` 파일로 정의:
|
|
58
|
-
```typescript
|
|
59
|
-
// app/api/users/route.ts
|
|
60
|
-
import { Mandu } from "@mandujs/core";
|
|
61
|
-
export default Mandu.filling()
|
|
62
|
-
.get((ctx) => ctx.ok({ users: [] }))
|
|
63
|
-
.post(async (ctx) => { ... });
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### 3. Tailwind CSS v4
|
|
67
|
-
CSS-first 설정 사용 (`tailwind.config.ts` 없음):
|
|
68
|
-
```css
|
|
69
|
-
/* app/globals.css */
|
|
70
|
-
@import "tailwindcss";
|
|
71
|
-
@theme {
|
|
72
|
-
--color-primary: hsl(222.2 47.4% 11.2%);
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 4. Import Alias
|
|
77
|
-
`@/` = `src/` 경로:
|
|
78
|
-
```typescript
|
|
79
|
-
import { Button } from "@/client/shared/ui/button";
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## 실행 방법
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
bun install # 최초 설치
|
|
86
|
-
bun run dev # 개발 서버 (http://localhost:4000)
|
|
87
|
-
bun run build # 프로덕션 빌드
|
|
88
|
-
bun run guard # 아키텍처 검증
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## 기술 스택
|
|
92
|
-
|
|
93
|
-
- **Runtime**: Bun 1.x
|
|
94
|
-
- **Framework**: Mandu (React 19 + Bun native)
|
|
95
|
-
- **Styling**: Tailwind CSS v4
|
|
96
|
-
- **Language**: TypeScript 5.x
|
|
1
|
+
# AI Agent Instructions for Mandu Project
|
|
2
|
+
|
|
3
|
+
이 프로젝트는 **Mandu Framework**로 구축되었습니다. AI 에이전트가 이 프로젝트를 다룰 때 아래 지침을 따라주세요.
|
|
4
|
+
|
|
5
|
+
## 패키지 매니저: Bun (필수)
|
|
6
|
+
|
|
7
|
+
**⚠️ 중요: 이 프로젝트는 Bun만 사용합니다. npm/yarn/pnpm을 사용하지 마세요.**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# ✅ 올바른 명령어
|
|
11
|
+
bun install # 의존성 설치
|
|
12
|
+
bun add <package> # 패키지 추가
|
|
13
|
+
bun remove <package> # 패키지 제거
|
|
14
|
+
bun run dev # 개발 서버 시작
|
|
15
|
+
bun run build # 프로덕션 빌드
|
|
16
|
+
bun test # 테스트 실행
|
|
17
|
+
|
|
18
|
+
# ❌ 사용 금지
|
|
19
|
+
npm install / yarn install / pnpm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 프로젝트 구조
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
├── app/ # FS 기반 라우팅 (페이지, API)
|
|
26
|
+
│ ├── page.tsx # / 라우트
|
|
27
|
+
│ ├── layout.tsx # 루트 레이아웃
|
|
28
|
+
│ ├── globals.css # Tailwind CSS (v4)
|
|
29
|
+
│ └── api/ # API 라우트
|
|
30
|
+
├── src/
|
|
31
|
+
│ ├── client/ # 클라이언트 코드 (FSD 구조)
|
|
32
|
+
│ │ ├── shared/ # 공용 UI, 유틸리티
|
|
33
|
+
│ │ ├── entities/ # 엔티티 컴포넌트
|
|
34
|
+
│ │ ├── features/ # 기능 컴포넌트
|
|
35
|
+
│ │ └── widgets/ # 위젯/Island 컴포넌트
|
|
36
|
+
│ ├── server/ # 서버 코드 (Clean Architecture)
|
|
37
|
+
│ │ ├── domain/ # 도메인 모델
|
|
38
|
+
│ │ ├── application/ # 비즈니스 로직
|
|
39
|
+
│ │ └── infra/ # 인프라/DB
|
|
40
|
+
│ └── shared/ # 클라이언트-서버 공유 코드
|
|
41
|
+
│ ├── contracts/ # API 계약 타입
|
|
42
|
+
│ └── types/ # 공용 타입
|
|
43
|
+
└── mandu.config.ts # Mandu 설정 (선택)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 주요 규칙
|
|
47
|
+
|
|
48
|
+
### 1. Island 컴포넌트
|
|
49
|
+
클라이언트 상호작용이 필요한 컴포넌트는 `*.island.tsx`로 명명:
|
|
50
|
+
```tsx
|
|
51
|
+
// src/client/widgets/counter/Counter.island.tsx
|
|
52
|
+
"use client";
|
|
53
|
+
export function CounterIsland() { ... }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. API 라우트
|
|
57
|
+
`app/api/` 폴더에 `route.ts` 파일로 정의:
|
|
58
|
+
```typescript
|
|
59
|
+
// app/api/users/route.ts
|
|
60
|
+
import { Mandu } from "@mandujs/core";
|
|
61
|
+
export default Mandu.filling()
|
|
62
|
+
.get((ctx) => ctx.ok({ users: [] }))
|
|
63
|
+
.post(async (ctx) => { ... });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. Tailwind CSS v4
|
|
67
|
+
CSS-first 설정 사용 (`tailwind.config.ts` 없음):
|
|
68
|
+
```css
|
|
69
|
+
/* app/globals.css */
|
|
70
|
+
@import "tailwindcss";
|
|
71
|
+
@theme {
|
|
72
|
+
--color-primary: hsl(222.2 47.4% 11.2%);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Import Alias
|
|
77
|
+
`@/` = `src/` 경로:
|
|
78
|
+
```typescript
|
|
79
|
+
import { Button } from "@/client/shared/ui/button";
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 실행 방법
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bun install # 최초 설치
|
|
86
|
+
bun run dev # 개발 서버 (http://localhost:4000)
|
|
87
|
+
bun run build # 프로덕션 빌드
|
|
88
|
+
bun run guard # 아키텍처 검증
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 기술 스택
|
|
92
|
+
|
|
93
|
+
- **Runtime**: Bun 1.x
|
|
94
|
+
- **Framework**: Mandu (React 19 + Bun native)
|
|
95
|
+
- **Styling**: Tailwind CSS v4
|
|
96
|
+
- **Language**: TypeScript 5.x
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Health Check API
|
|
3
|
-
*
|
|
4
|
-
* GET /api/health
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export function GET() {
|
|
8
|
-
return Response.json({
|
|
9
|
-
status: "ok",
|
|
10
|
-
timestamp: new Date().toISOString(),
|
|
11
|
-
framework: "Mandu",
|
|
12
|
-
});
|
|
13
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Health Check API
|
|
3
|
+
*
|
|
4
|
+
* GET /api/health
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function GET() {
|
|
8
|
+
return Response.json({
|
|
9
|
+
status: "ok",
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
|
+
framework: "Mandu",
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Tailwind CSS v4 - CSS-first Configuration
|
|
5
|
-
* https://tailwindcss.com/docs/v4
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
@theme {
|
|
9
|
-
/* Colors - shadcn/ui compatible */
|
|
10
|
-
--color-background: hsl(0 0% 100%);
|
|
11
|
-
--color-foreground: hsl(222.2 84% 4.9%);
|
|
12
|
-
--color-card: hsl(0 0% 100%);
|
|
13
|
-
--color-card-foreground: hsl(222.2 84% 4.9%);
|
|
14
|
-
--color-popover: hsl(0 0% 100%);
|
|
15
|
-
--color-popover-foreground: hsl(222.2 84% 4.9%);
|
|
16
|
-
--color-primary: hsl(222.2 47.4% 11.2%);
|
|
17
|
-
--color-primary-foreground: hsl(210 40% 98%);
|
|
18
|
-
--color-secondary: hsl(210 40% 96.1%);
|
|
19
|
-
--color-secondary-foreground: hsl(222.2 47.4% 11.2%);
|
|
20
|
-
--color-muted: hsl(210 40% 96.1%);
|
|
21
|
-
--color-muted-foreground: hsl(215.4 16.3% 46.9%);
|
|
22
|
-
--color-accent: hsl(210 40% 96.1%);
|
|
23
|
-
--color-accent-foreground: hsl(222.2 47.4% 11.2%);
|
|
24
|
-
--color-destructive: hsl(0 84.2% 60.2%);
|
|
25
|
-
--color-destructive-foreground: hsl(210 40% 98%);
|
|
26
|
-
--color-border: hsl(214.3 31.8% 91.4%);
|
|
27
|
-
--color-input: hsl(214.3 31.8% 91.4%);
|
|
28
|
-
--color-ring: hsl(222.2 84% 4.9%);
|
|
29
|
-
|
|
30
|
-
/* Radius */
|
|
31
|
-
--radius-sm: 0.25rem;
|
|
32
|
-
--radius-md: 0.5rem;
|
|
33
|
-
--radius-lg: 0.75rem;
|
|
34
|
-
--radius-xl: 1rem;
|
|
35
|
-
|
|
36
|
-
/* Fonts */
|
|
37
|
-
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Base styles */
|
|
41
|
-
* {
|
|
42
|
-
border-color: var(--color-border);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
body {
|
|
46
|
-
background-color: var(--color-background);
|
|
47
|
-
color: var(--color-foreground);
|
|
48
|
-
font-family: var(--font-sans);
|
|
49
|
-
}
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Tailwind CSS v4 - CSS-first Configuration
|
|
5
|
+
* https://tailwindcss.com/docs/v4
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
@theme {
|
|
9
|
+
/* Colors - shadcn/ui compatible */
|
|
10
|
+
--color-background: hsl(0 0% 100%);
|
|
11
|
+
--color-foreground: hsl(222.2 84% 4.9%);
|
|
12
|
+
--color-card: hsl(0 0% 100%);
|
|
13
|
+
--color-card-foreground: hsl(222.2 84% 4.9%);
|
|
14
|
+
--color-popover: hsl(0 0% 100%);
|
|
15
|
+
--color-popover-foreground: hsl(222.2 84% 4.9%);
|
|
16
|
+
--color-primary: hsl(222.2 47.4% 11.2%);
|
|
17
|
+
--color-primary-foreground: hsl(210 40% 98%);
|
|
18
|
+
--color-secondary: hsl(210 40% 96.1%);
|
|
19
|
+
--color-secondary-foreground: hsl(222.2 47.4% 11.2%);
|
|
20
|
+
--color-muted: hsl(210 40% 96.1%);
|
|
21
|
+
--color-muted-foreground: hsl(215.4 16.3% 46.9%);
|
|
22
|
+
--color-accent: hsl(210 40% 96.1%);
|
|
23
|
+
--color-accent-foreground: hsl(222.2 47.4% 11.2%);
|
|
24
|
+
--color-destructive: hsl(0 84.2% 60.2%);
|
|
25
|
+
--color-destructive-foreground: hsl(210 40% 98%);
|
|
26
|
+
--color-border: hsl(214.3 31.8% 91.4%);
|
|
27
|
+
--color-input: hsl(214.3 31.8% 91.4%);
|
|
28
|
+
--color-ring: hsl(222.2 84% 4.9%);
|
|
29
|
+
|
|
30
|
+
/* Radius */
|
|
31
|
+
--radius-sm: 0.25rem;
|
|
32
|
+
--radius-md: 0.5rem;
|
|
33
|
+
--radius-lg: 0.75rem;
|
|
34
|
+
--radius-xl: 1rem;
|
|
35
|
+
|
|
36
|
+
/* Fonts */
|
|
37
|
+
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Base styles */
|
|
41
|
+
* {
|
|
42
|
+
border-color: var(--color-border);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
body {
|
|
46
|
+
background-color: var(--color-background);
|
|
47
|
+
color: var(--color-foreground);
|
|
48
|
+
font-family: var(--font-sans);
|
|
49
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Root Layout
|
|
3
|
-
*
|
|
4
|
-
* 모든 페이지의 공통 레이아웃
|
|
5
|
-
* globals.css를 여기서 임포트
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import "./globals.css";
|
|
9
|
-
|
|
10
|
-
interface RootLayoutProps {
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default function RootLayout({ children }: RootLayoutProps) {
|
|
15
|
-
return (
|
|
16
|
-
<html lang="ko">
|
|
17
|
-
<head>
|
|
18
|
-
<meta charSet="UTF-8" />
|
|
19
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
-
<title>{{PROJECT_NAME}}</title>
|
|
21
|
-
</head>
|
|
22
|
-
<body className="min-h-screen bg-background font-sans antialiased">
|
|
23
|
-
{children}
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Root Layout
|
|
3
|
+
*
|
|
4
|
+
* 모든 페이지의 공통 레이아웃
|
|
5
|
+
* globals.css를 여기서 임포트
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import "./globals.css";
|
|
9
|
+
|
|
10
|
+
interface RootLayoutProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function RootLayout({ children }: RootLayoutProps) {
|
|
15
|
+
return (
|
|
16
|
+
<html lang="ko">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charSet="UTF-8" />
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
+
<title>{{PROJECT_NAME}}</title>
|
|
21
|
+
</head>
|
|
22
|
+
<body className="min-h-screen bg-background font-sans antialiased">
|
|
23
|
+
{children}
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Home Page
|
|
3
|
-
*
|
|
4
|
-
* Edit this file and see changes at http://localhost:3000
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Button } from "@/client/shared/ui/button";
|
|
8
|
-
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/client/shared/ui/card";
|
|
9
|
-
|
|
10
|
-
export default function HomePage() {
|
|
11
|
-
return (
|
|
12
|
-
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-br from-indigo-500 to-purple-600 p-8">
|
|
13
|
-
<Card className="w-full max-w-md">
|
|
14
|
-
<CardHeader className="text-center">
|
|
15
|
-
<CardTitle className="text-4xl">🥟 Mandu</CardTitle>
|
|
16
|
-
<CardDescription>
|
|
17
|
-
Welcome to your new Mandu project!
|
|
18
|
-
</CardDescription>
|
|
19
|
-
</CardHeader>
|
|
20
|
-
<CardContent className="flex flex-col gap-4">
|
|
21
|
-
<p className="text-center text-muted-foreground">
|
|
22
|
-
Edit <code className="rounded bg-muted px-1.5 py-0.5 text-sm">app/page.tsx</code> to get started.
|
|
23
|
-
</p>
|
|
24
|
-
<div className="flex justify-center gap-2">
|
|
25
|
-
<Button asChild variant="default">
|
|
26
|
-
<a href="/api/health">API Health →</a>
|
|
27
|
-
</Button>
|
|
28
|
-
<Button asChild variant="outline">
|
|
29
|
-
<a href="https://mandujs.dev/docs" target="_blank" rel="noopener noreferrer">
|
|
30
|
-
Documentation
|
|
31
|
-
</a>
|
|
32
|
-
</Button>
|
|
33
|
-
</div>
|
|
34
|
-
</CardContent>
|
|
35
|
-
</Card>
|
|
36
|
-
</main>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Home Page
|
|
3
|
+
*
|
|
4
|
+
* Edit this file and see changes at http://localhost:3000
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Button } from "@/client/shared/ui/button";
|
|
8
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/client/shared/ui/card";
|
|
9
|
+
|
|
10
|
+
export default function HomePage() {
|
|
11
|
+
return (
|
|
12
|
+
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-br from-indigo-500 to-purple-600 p-8">
|
|
13
|
+
<Card className="w-full max-w-md">
|
|
14
|
+
<CardHeader className="text-center">
|
|
15
|
+
<CardTitle className="text-4xl">🥟 Mandu</CardTitle>
|
|
16
|
+
<CardDescription>
|
|
17
|
+
Welcome to your new Mandu project!
|
|
18
|
+
</CardDescription>
|
|
19
|
+
</CardHeader>
|
|
20
|
+
<CardContent className="flex flex-col gap-4">
|
|
21
|
+
<p className="text-center text-muted-foreground">
|
|
22
|
+
Edit <code className="rounded bg-muted px-1.5 py-0.5 text-sm">app/page.tsx</code> to get started.
|
|
23
|
+
</p>
|
|
24
|
+
<div className="flex justify-center gap-2">
|
|
25
|
+
<Button asChild variant="default">
|
|
26
|
+
<a href="/api/health">API Health →</a>
|
|
27
|
+
</Button>
|
|
28
|
+
<Button asChild variant="outline">
|
|
29
|
+
<a href="https://mandujs.dev/docs" target="_blank" rel="noopener noreferrer">
|
|
30
|
+
Documentation
|
|
31
|
+
</a>
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
</CardContent>
|
|
35
|
+
</Card>
|
|
36
|
+
</main>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { type ClassValue, clsx } from "clsx";
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* cn - Tailwind CSS 클래스 병합 유틸리티
|
|
6
|
-
*
|
|
7
|
-
* clsx로 조건부 클래스를 결합하고
|
|
8
|
-
* tailwind-merge로 충돌하는 클래스를 스마트하게 병합
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* cn("px-4 py-2", isActive && "bg-primary", className)
|
|
12
|
-
* cn("text-sm", "text-lg") // → "text-lg" (충돌 해결)
|
|
13
|
-
*/
|
|
14
|
-
export function cn(...inputs: ClassValue[]) {
|
|
15
|
-
return twMerge(clsx(inputs));
|
|
16
|
-
}
|
|
1
|
+
import { type ClassValue, clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* cn - Tailwind CSS 클래스 병합 유틸리티
|
|
6
|
+
*
|
|
7
|
+
* clsx로 조건부 클래스를 결합하고
|
|
8
|
+
* tailwind-merge로 충돌하는 클래스를 스마트하게 병합
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* cn("px-4 py-2", isActive && "bg-primary", className)
|
|
12
|
+
* cn("text-sm", "text-lg") // → "text-lg" (충돌 해결)
|
|
13
|
+
*/
|
|
14
|
+
export function cn(...inputs: ClassValue[]) {
|
|
15
|
+
return twMerge(clsx(inputs));
|
|
16
|
+
}
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
-
import { cn } from "@/client/shared/lib/utils";
|
|
7
|
-
|
|
8
|
-
const buttonVariants = cva(
|
|
9
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
-
{
|
|
11
|
-
variants: {
|
|
12
|
-
variant: {
|
|
13
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
-
destructive:
|
|
15
|
-
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
16
|
-
outline:
|
|
17
|
-
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
18
|
-
secondary:
|
|
19
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-10 px-4 py-2",
|
|
25
|
-
sm: "h-9 rounded-md px-3",
|
|
26
|
-
lg: "h-11 rounded-md px-8",
|
|
27
|
-
icon: "h-10 w-10",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultVariants: {
|
|
31
|
-
variant: "default",
|
|
32
|
-
size: "default",
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
export interface ButtonProps
|
|
38
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
-
VariantProps<typeof buttonVariants> {
|
|
40
|
-
asChild?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
-
const Comp = asChild ? Slot : "button";
|
|
46
|
-
return (
|
|
47
|
-
<Comp
|
|
48
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
-
ref={ref}
|
|
50
|
-
{...props}
|
|
51
|
-
/>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
Button.displayName = "Button";
|
|
56
|
-
|
|
57
|
-
export { Button, buttonVariants };
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
+
import { cn } from "@/client/shared/lib/utils";
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
16
|
+
outline:
|
|
17
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-10 px-4 py-2",
|
|
25
|
+
sm: "h-9 rounded-md px-3",
|
|
26
|
+
lg: "h-11 rounded-md px-8",
|
|
27
|
+
icon: "h-10 w-10",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export interface ButtonProps
|
|
38
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
+
VariantProps<typeof buttonVariants> {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
+
const Comp = asChild ? Slot : "button";
|
|
46
|
+
return (
|
|
47
|
+
<Comp
|
|
48
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
+
ref={ref}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
Button.displayName = "Button";
|
|
56
|
+
|
|
57
|
+
export { Button, buttonVariants };
|
|
@@ -29,7 +29,7 @@ const CardHeader = React.forwardRef<
|
|
|
29
29
|
CardHeader.displayName = "CardHeader";
|
|
30
30
|
|
|
31
31
|
const CardTitle = React.forwardRef<
|
|
32
|
-
|
|
32
|
+
HTMLHeadingElement,
|
|
33
33
|
React.HTMLAttributes<HTMLHeadingElement>
|
|
34
34
|
>(({ className, ...props }, ref) => (
|
|
35
35
|
<h3
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UI Components
|
|
3
|
-
*
|
|
4
|
-
* shadcn/ui 스타일의 컴포넌트 라이브러리
|
|
5
|
-
* Radix UI primitives + Tailwind CSS + cva 기반
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { Button, buttonVariants } from "./button";
|
|
9
|
-
export type { ButtonProps } from "./button";
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
Card,
|
|
13
|
-
CardHeader,
|
|
14
|
-
CardFooter,
|
|
15
|
-
CardTitle,
|
|
16
|
-
CardDescription,
|
|
17
|
-
CardContent,
|
|
18
|
-
} from "./card";
|
|
19
|
-
|
|
20
|
-
export { Input } from "./input";
|
|
21
|
-
export type { InputProps } from "./input";
|
|
1
|
+
/**
|
|
2
|
+
* UI Components
|
|
3
|
+
*
|
|
4
|
+
* shadcn/ui 스타일의 컴포넌트 라이브러리
|
|
5
|
+
* Radix UI primitives + Tailwind CSS + cva 기반
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { Button, buttonVariants } from "./button";
|
|
9
|
+
export type { ButtonProps } from "./button";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Card,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardFooter,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
} from "./card";
|
|
19
|
+
|
|
20
|
+
export { Input } from "./input";
|
|
21
|
+
export type { InputProps } from "./input";
|