@conscience-technology/ctds 0.0.0-stage → 0.1.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.
- package/README.md +128 -2
- package/assets/components/AppFrame/AppFrame.module.css +177 -0
- package/assets/components/AppFrame/AppFrame.tsx +93 -0
- package/assets/components/AppFrame/index.ts +6 -0
- package/assets/components/AttachmentChip/AttachmentChip.module.css +71 -0
- package/assets/components/AttachmentChip/AttachmentChip.tsx +74 -0
- package/assets/components/AttachmentChip/index.ts +2 -0
- package/assets/components/AttachmentThumb/AttachmentThumb.module.css +107 -0
- package/assets/components/AttachmentThumb/AttachmentThumb.tsx +91 -0
- package/assets/components/AttachmentThumb/index.ts +2 -0
- package/assets/components/Avatar/Avatar.module.css +167 -0
- package/assets/components/Avatar/Avatar.tsx +228 -0
- package/assets/components/Avatar/index.ts +2 -0
- package/assets/components/AvatarUpload/AvatarUpload.module.css +130 -0
- package/assets/components/AvatarUpload/AvatarUpload.tsx +139 -0
- package/assets/components/AvatarUpload/index.ts +2 -0
- package/assets/components/Badge/Badge.module.css +76 -0
- package/assets/components/Badge/Badge.tsx +68 -0
- package/assets/components/Badge/index.ts +2 -0
- package/assets/components/Breadcrumb/Breadcrumb.module.css +213 -0
- package/assets/components/Breadcrumb/Breadcrumb.tsx +279 -0
- package/assets/components/Breadcrumb/index.ts +8 -0
- package/assets/components/Button/Button.module.css +216 -0
- package/assets/components/Button/Button.tsx +149 -0
- package/assets/components/Button/index.ts +2 -0
- package/assets/components/Callout/Callout.module.css +146 -0
- package/assets/components/Callout/Callout.tsx +138 -0
- package/assets/components/Callout/index.ts +2 -0
- package/assets/components/ChatInput/ChatInput.module.css +241 -0
- package/assets/components/ChatInput/ChatInput.tsx +322 -0
- package/assets/components/ChatInput/ModelSelector.tsx +40 -0
- package/assets/components/ChatInput/SendButton.tsx +41 -0
- package/assets/components/ChatInput/index.ts +10 -0
- package/assets/components/Checkbox/Checkbox.module.css +66 -0
- package/assets/components/Checkbox/Checkbox.tsx +36 -0
- package/assets/components/Checkbox/index.ts +2 -0
- package/assets/components/Chip/Chip.module.css +128 -0
- package/assets/components/Chip/Chip.tsx +103 -0
- package/assets/components/Chip/index.ts +2 -0
- package/assets/components/ChoiceCard/ChoiceCard.module.css +142 -0
- package/assets/components/ChoiceCard/ChoiceCard.tsx +132 -0
- package/assets/components/ChoiceCard/index.ts +2 -0
- package/assets/components/ContextMenu/ContextMenu.module.css +102 -0
- package/assets/components/ContextMenu/ContextMenu.tsx +392 -0
- package/assets/components/ContextMenu/index.ts +9 -0
- package/assets/components/DatePicker/Calendar.tsx +234 -0
- package/assets/components/DatePicker/DESIGN_NOTES.md +106 -0
- package/assets/components/DatePicker/DESIGN_NOTES_TIME.md +125 -0
- package/assets/components/DatePicker/DatePicker.module.css +295 -0
- package/assets/components/DatePicker/DatePicker.tsx +387 -0
- package/assets/components/DatePicker/TimeSelector.tsx +69 -0
- package/assets/components/DatePicker/index.ts +8 -0
- package/assets/components/DatePicker/presets.ts +80 -0
- package/assets/components/DatePicker/utils.ts +148 -0
- package/assets/components/Disclosure/Disclosure.module.css +73 -0
- package/assets/components/Disclosure/Disclosure.tsx +129 -0
- package/assets/components/Disclosure/index.ts +6 -0
- package/assets/components/Drawer/Drawer.module.css +257 -0
- package/assets/components/Drawer/Drawer.tsx +126 -0
- package/assets/components/Drawer/index.ts +7 -0
- package/assets/components/EmptyState/EmptyState.module.css +102 -0
- package/assets/components/EmptyState/EmptyState.tsx +80 -0
- package/assets/components/EmptyState/index.ts +2 -0
- package/assets/components/FileIcon/FileIcon.module.css +66 -0
- package/assets/components/FileIcon/FileIcon.tsx +84 -0
- package/assets/components/FileIcon/fileExtMap.ts +112 -0
- package/assets/components/FileIcon/index.ts +9 -0
- package/assets/components/FilterButton/FilterButton.module.css +130 -0
- package/assets/components/FilterButton/FilterButton.tsx +94 -0
- package/assets/components/FilterButton/index.ts +2 -0
- package/assets/components/GalleryItem/GalleryItem.module.css +220 -0
- package/assets/components/GalleryItem/GalleryItem.tsx +163 -0
- package/assets/components/GalleryItem/index.ts +7 -0
- package/assets/components/Icon/Icon.tsx +616 -0
- package/assets/components/Icon/context.tsx +39 -0
- package/assets/components/Icon/index.ts +4 -0
- package/assets/components/IconButton/IconButton.module.css +109 -0
- package/assets/components/IconButton/IconButton.tsx +110 -0
- package/assets/components/IconButton/index.ts +2 -0
- package/assets/components/Input/Input.module.css +338 -0
- package/assets/components/Input/Input.tsx +371 -0
- package/assets/components/Input/index.ts +2 -0
- package/assets/components/ListItem/ListItem.module.css +318 -0
- package/assets/components/ListItem/ListItem.tsx +262 -0
- package/assets/components/ListItem/index.ts +9 -0
- package/assets/components/MenuItem/MenuItem.module.css +437 -0
- package/assets/components/MenuItem/MenuItem.tsx +529 -0
- package/assets/components/MenuItem/index.ts +8 -0
- package/assets/components/MetricCard/MetricCard.module.css +153 -0
- package/assets/components/MetricCard/MetricCard.tsx +140 -0
- package/assets/components/MetricCard/index.ts +2 -0
- package/assets/components/Modal/Modal.module.css +175 -0
- package/assets/components/Modal/Modal.tsx +98 -0
- package/assets/components/Modal/index.ts +2 -0
- package/assets/components/Onboarding/DocsIndex.tsx +82 -0
- package/assets/components/Onboarding/LayoutBuilder.tsx +160 -0
- package/assets/components/Onboarding/Onboarding.module.css +1394 -0
- package/assets/components/Onboarding/OnboardingAxisPanel.tsx +515 -0
- package/assets/components/Onboarding/OnboardingPage.tsx +1193 -0
- package/assets/components/Onboarding/PresetsGallery.tsx +65 -0
- package/assets/components/Onboarding/actions.ts +250 -0
- package/assets/components/Onboarding/index.ts +8 -0
- package/assets/components/Onboarding/palette.css +40 -0
- package/assets/components/Onboarding/wizardData.ts +265 -0
- package/assets/components/Pagination/Pagination.module.css +82 -0
- package/assets/components/Pagination/Pagination.tsx +248 -0
- package/assets/components/Pagination/index.ts +6 -0
- package/assets/components/ProgressBar/ProgressBar.module.css +64 -0
- package/assets/components/ProgressBar/ProgressBar.tsx +54 -0
- package/assets/components/ProgressBar/index.ts +2 -0
- package/assets/components/QuotationBar/QuotationBar.module.css +61 -0
- package/assets/components/QuotationBar/QuotationBar.tsx +48 -0
- package/assets/components/QuotationBar/index.ts +2 -0
- package/assets/components/Radio/Radio.module.css +63 -0
- package/assets/components/Radio/Radio.tsx +33 -0
- package/assets/components/Radio/index.ts +2 -0
- package/assets/components/Select/Select.module.css +151 -0
- package/assets/components/Select/Select.tsx +425 -0
- package/assets/components/Select/index.ts +10 -0
- package/assets/components/SelectableCard/SelectableCard.module.css +177 -0
- package/assets/components/SelectableCard/SelectableCard.tsx +83 -0
- package/assets/components/SelectableCard/index.ts +2 -0
- package/assets/components/Shortcut/Shortcut.module.css +56 -0
- package/assets/components/Shortcut/Shortcut.tsx +156 -0
- package/assets/components/Shortcut/index.ts +2 -0
- package/assets/components/Sidebar/Sidebar.module.css +242 -0
- package/assets/components/Sidebar/Sidebar.tsx +264 -0
- package/assets/components/Sidebar/index.ts +7 -0
- package/assets/components/SidebarSplit/SidebarSplit.module.css +126 -0
- package/assets/components/SidebarSplit/SidebarSplit.tsx +222 -0
- package/assets/components/SidebarSplit/index.ts +6 -0
- package/assets/components/Slider/Slider.module.css +120 -0
- package/assets/components/Slider/Slider.tsx +88 -0
- package/assets/components/Slider/index.ts +2 -0
- package/assets/components/SplitButton/SplitButton.module.css +190 -0
- package/assets/components/SplitButton/SplitButton.tsx +145 -0
- package/assets/components/SplitButton/index.ts +6 -0
- package/assets/components/Stepper/Stepper.module.css +182 -0
- package/assets/components/Stepper/Stepper.tsx +185 -0
- package/assets/components/Stepper/index.ts +2 -0
- package/assets/components/Switch/Switch.module.css +116 -0
- package/assets/components/Switch/Switch.tsx +58 -0
- package/assets/components/Switch/index.ts +2 -0
- package/assets/components/TableCell/TableCell.module.css +137 -0
- package/assets/components/TableCell/TableCellAvatar.tsx +42 -0
- package/assets/components/TableCell/TableCellBadge.tsx +23 -0
- package/assets/components/TableCell/TableCellIcon.tsx +39 -0
- package/assets/components/TableCell/TableCellImage.tsx +49 -0
- package/assets/components/TableCell/TableCellLink.tsx +33 -0
- package/assets/components/TableCell/TableCellMeter.tsx +55 -0
- package/assets/components/TableCell/TableCellNumber.tsx +43 -0
- package/assets/components/TableCell/TableCellText.tsx +30 -0
- package/assets/components/TableCell/index.ts +23 -0
- package/assets/components/TableHeader/TableHeader.module.css +287 -0
- package/assets/components/TableHeader/TableHeader.tsx +590 -0
- package/assets/components/TableHeader/index.ts +9 -0
- package/assets/components/TableRow/TableRow.module.css +290 -0
- package/assets/components/TableRow/TableRow.tsx +253 -0
- package/assets/components/TableRow/index.ts +8 -0
- package/assets/components/TableRowEdit/TableRowEdit.module.css +126 -0
- package/assets/components/TableRowEdit/TableRowEdit.tsx +247 -0
- package/assets/components/TableRowEdit/index.ts +2 -0
- package/assets/components/TableRowSelect/TableRowSelect.module.css +23 -0
- package/assets/components/TableRowSelect/TableRowSelect.tsx +175 -0
- package/assets/components/TableRowSelect/index.ts +2 -0
- package/assets/components/Tabs/Tabs.module.css +234 -0
- package/assets/components/Tabs/Tabs.tsx +426 -0
- package/assets/components/Tabs/index.ts +9 -0
- package/assets/components/Textarea/Textarea.module.css +318 -0
- package/assets/components/Textarea/Textarea.tsx +426 -0
- package/assets/components/Textarea/index.ts +7 -0
- package/assets/components/ThemeController.tsx +271 -0
- package/assets/components/TimePicker/TimePicker.module.css +317 -0
- package/assets/components/TimePicker/TimePicker.tsx +821 -0
- package/assets/components/TimePicker/index.ts +8 -0
- package/assets/components/TimePicker/utils.ts +115 -0
- package/assets/components/Toast/Toast.module.css +264 -0
- package/assets/components/Toast/Toast.tsx +243 -0
- package/assets/components/Toast/index.ts +15 -0
- package/assets/components/Toolbar/Toolbar.module.css +72 -0
- package/assets/components/Toolbar/Toolbar.tsx +75 -0
- package/assets/components/Toolbar/index.ts +2 -0
- package/assets/components/Tooltip/Tooltip.module.css +42 -0
- package/assets/components/Tooltip/Tooltip.tsx +332 -0
- package/assets/components/Tooltip/index.ts +2 -0
- package/assets/components/Topbar/Topbar.module.css +26 -0
- package/assets/components/Topbar/Topbar.tsx +47 -0
- package/assets/components/Topbar/index.ts +2 -0
- package/assets/components/_cn.ts +3 -0
- package/assets/components/_color.ts +48 -0
- package/assets/components/_data.ts +659 -0
- package/assets/components/_utils.css +19 -0
- package/assets/components/axisOptions.ts +24 -0
- package/assets/components/tokens.css +742 -0
- package/assets/prompts/GUIDE.md +251 -0
- package/assets/prompts/OVERRIDE.md +170 -0
- package/assets/prompts/PRINCIPLES.md +130 -0
- package/assets/prompts/axes/density.md +38 -0
- package/assets/prompts/axes/font.md +36 -0
- package/assets/prompts/axes/hue.md +36 -0
- package/assets/prompts/axes/icon.md +48 -0
- package/assets/prompts/axes/mode.md +50 -0
- package/assets/prompts/axes/motion.md +77 -0
- package/assets/prompts/axes/palette.md +35 -0
- package/assets/prompts/axes/shape.md +33 -0
- package/assets/prompts/axes/voice.md +54 -0
- package/assets/prompts/components/README.md +84 -0
- package/assets/prompts/components/app-frame.md +94 -0
- package/assets/prompts/components/attachment-chip.md +46 -0
- package/assets/prompts/components/attachment-thumb.md +49 -0
- package/assets/prompts/components/avatar.md +86 -0
- package/assets/prompts/components/badge.md +104 -0
- package/assets/prompts/components/breadcrumb.md +77 -0
- package/assets/prompts/components/button.md +99 -0
- package/assets/prompts/components/callout.md +81 -0
- package/assets/prompts/components/chat-input.md +84 -0
- package/assets/prompts/components/checkbox.md +46 -0
- package/assets/prompts/components/chip.md +84 -0
- package/assets/prompts/components/choice-card.md +127 -0
- package/assets/prompts/components/context-menu.md +145 -0
- package/assets/prompts/components/date-picker.md +104 -0
- package/assets/prompts/components/drawer.md +152 -0
- package/assets/prompts/components/empty-state.md +69 -0
- package/assets/prompts/components/file-icon.md +73 -0
- package/assets/prompts/components/filter-button.md +42 -0
- package/assets/prompts/components/form-field.md +74 -0
- package/assets/prompts/components/gallery-item.md +158 -0
- package/assets/prompts/components/icon-button.md +82 -0
- package/assets/prompts/components/icon.md +68 -0
- package/assets/prompts/components/input.md +95 -0
- package/assets/prompts/components/list-item.md +235 -0
- package/assets/prompts/components/menu-item.md +234 -0
- package/assets/prompts/components/metric-card.md +132 -0
- package/assets/prompts/components/modal.md +131 -0
- package/assets/prompts/components/pagination.md +92 -0
- package/assets/prompts/components/progress-bar.md +61 -0
- package/assets/prompts/components/quotation-bar.md +39 -0
- package/assets/prompts/components/radio.md +41 -0
- package/assets/prompts/components/select.md +104 -0
- package/assets/prompts/components/selectable-card.md +67 -0
- package/assets/prompts/components/shortcut.md +76 -0
- package/assets/prompts/components/sidebar.md +107 -0
- package/assets/prompts/components/slider.md +89 -0
- package/assets/prompts/components/split-button.md +71 -0
- package/assets/prompts/components/stepper.md +93 -0
- package/assets/prompts/components/switch.md +86 -0
- package/assets/prompts/components/table-cell.md +87 -0
- package/assets/prompts/components/table-header.md +157 -0
- package/assets/prompts/components/table-row-edit.md +129 -0
- package/assets/prompts/components/table-row-select.md +82 -0
- package/assets/prompts/components/table-row.md +156 -0
- package/assets/prompts/components/tabs.md +122 -0
- package/assets/prompts/components/textarea.md +105 -0
- package/assets/prompts/components/toast.md +149 -0
- package/assets/prompts/components/tooltip.md +71 -0
- package/assets/prompts/components/topbar.md +148 -0
- package/assets/prompts/primitives/emerge.md +43 -0
- package/assets/prompts/primitives/fade.md +31 -0
- package/assets/prompts/primitives/scale.md +32 -0
- package/assets/prompts/primitives/scroll.md +75 -0
- package/assets/prompts/primitives/slide.md +31 -0
- package/assets/prompts/themes/_SCHEMA.md +135 -0
- package/assets/prompts/themes/_TEMPLATE.md +73 -0
- package/assets/prompts/themes/expressive.md +118 -0
- package/assets/prompts/themes/pure.md +87 -0
- package/assets/prompts/themes/spare.md +73 -0
- package/assets/prompts/tokens/breakpoint.md +29 -0
- package/assets/prompts/tokens/color.md +78 -0
- package/assets/prompts/tokens/radius.md +33 -0
- package/assets/prompts/tokens/shadow.md +35 -0
- package/assets/prompts/tokens/spacing.md +86 -0
- package/assets/prompts/tokens/typography.md +76 -0
- package/assets/prompts/tokens/z-index.md +35 -0
- package/assets/public/expressive.png +0 -0
- package/assets/public/pure.png +0 -0
- package/assets/public/spare.png +0 -0
- package/assets/skill/SKILL.md +36 -0
- package/bin/ctds.js +76 -0
- package/package.json +35 -4
- package/src/commands/init.js +215 -0
- package/src/commands/update.js +275 -0
- package/src/utils/assets.js +78 -0
- package/src/utils/deps.js +51 -0
- package/src/utils/detect.js +47 -0
- package/src/utils/diff.js +52 -0
- package/src/utils/hash.js +16 -0
- package/src/utils/log.js +41 -0
- package/src/utils/meta.js +73 -0
- package/src/utils/prompt.js +28 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Radius
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
컴포넌트 크기별 상대 위계. 이름은 고정, 값은 Shape 축이 결정.
|
|
5
|
+
Tooltip < Button < Card < Modal 순서는 어떤 프로젝트에서도 유지.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
- 컴포넌트 크기에 맞춰 스케일 선택: 작을수록 작은 radius.
|
|
9
|
+
- 완전한 원형(Avatar, Pill Chip)은 `radius-full`.
|
|
10
|
+
- 각져야 하는 표·시트 셀은 `radius-none`.
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- px 하드코딩 금지. Shape 축이 값 스왑을 담당.
|
|
14
|
+
- Modal에 `radius-sm`, Tooltip에 `radius-xl` 같은 위계 역전 금지.
|
|
15
|
+
|
|
16
|
+
### Fallback
|
|
17
|
+
애매하면 `radius-sm` (Button·Input 기본 스케일).
|
|
18
|
+
|
|
19
|
+
### 관련
|
|
20
|
+
- [[axes/shape]] — radius 값을 스왑
|
|
21
|
+
|
|
22
|
+
### 토큰 표
|
|
23
|
+
| Token | 용도 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `radius-none` | 표·시트 셀, 각져야 하는 경계 |
|
|
26
|
+
| `radius-xs` | Chip, Badge, Tooltip |
|
|
27
|
+
| `radius-sm` | Input, Button, Icon Button (기본) |
|
|
28
|
+
| `radius-md` | Card, Panel |
|
|
29
|
+
| `radius-lg` | Modal, Popover, Dropdown |
|
|
30
|
+
| `radius-xl` | 대형 카드 |
|
|
31
|
+
| `radius-2xl` | 히어로 카드 |
|
|
32
|
+
| `radius-3xl` | 랜딩·대형 오버레이 |
|
|
33
|
+
| `radius-full` | Avatar, Pill Chip, 원형 버튼 |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Shadow
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
표면이 얼마나 위로 떠 있는가 — 시각 위계(elevation). Shape 축이 스타일 스왑.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 컴포넌트 elevation에 맞춰 스케일 선택.
|
|
8
|
+
- Input focus·미세한 depth: `shadow-xs`.
|
|
9
|
+
- Card: `shadow-sm`. Dropdown·Popover: `shadow-md`. Modal: `shadow-lg`. Command Palette: `shadow-xl`.
|
|
10
|
+
- 눌린 상태(pressed inset)는 `shadow-inner`.
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- `box-shadow` 리터럴 하드코딩 금지. 색도 포함 — `rgba(0,0,0,...)`·`oklch(0 0 0 / ...)` 직접 쓰지 말고 `--shadow-*` 토큰 경유.
|
|
14
|
+
- 아래 요소(카드)가 위 요소(Modal)보다 강한 그림자 갖지 않는다 (elevation 역전 금지).
|
|
15
|
+
|
|
16
|
+
### Shadow 색
|
|
17
|
+
- `--shadow-color`: oklch(L C H) 세 성분. 알파는 shadow 단계별 상수로 보존.
|
|
18
|
+
- 기본은 중성 검정(`0 0 0`), 다크 모드도 동일(그림자는 "빛의 반대", Mode 반전 안 함) — 알파만 다크에서 상향.
|
|
19
|
+
- 테마가 override 하면 전체 shadow 톤이 스왑: 예 `--shadow-color: 0.15 0.03 40` → warm 브랜드의 갈색 틴트 그림자.
|
|
20
|
+
|
|
21
|
+
### Fallback
|
|
22
|
+
카드·패널이면 `shadow-sm`, 오버레이면 `shadow-md`.
|
|
23
|
+
|
|
24
|
+
### 관련
|
|
25
|
+
- [[axes/shape]] — Shape가 그림자 스타일을 스왑
|
|
26
|
+
|
|
27
|
+
### 토큰 표
|
|
28
|
+
| Token | 용도 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `shadow-xs` | Input focus, 미세 depth |
|
|
31
|
+
| `shadow-sm` | Card 기본 |
|
|
32
|
+
| `shadow-md` | Dropdown, Popover, Hover Card |
|
|
33
|
+
| `shadow-lg` | Modal, Drawer |
|
|
34
|
+
| `shadow-xl` | Command Palette, 화면 중심 |
|
|
35
|
+
| `shadow-inner` | 눌린 상태 (inset) |
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Spacing
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
관계의 밀도를 시각화. 가까울수록 관련 강하고, 멀수록 문맥 분리.
|
|
5
|
+
4px 배수(`--space-*`)만 사용. Density 축이 `--space-scale`로 계열을 통째로 스왑.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
공간은 **"맥락"으로 선택**한다. 토큰에 고정 용도가 박혀있지 않다. 아래 두 리듬 중 하나를 먼저 고르고, 그 안에서 스케일을 쓴다.
|
|
9
|
+
|
|
10
|
+
**앱·대시보드·콘솔 (디폴트 — 매일 쓰는 UI)**
|
|
11
|
+
- 작은 컴포넌트 사이(버튼↔버튼, 칩↔칩, 아이콘 묶음): **`space-1`(4px) 고정.** 묶음으로 읽히는 밀도. `space-2` 이상 금지.
|
|
12
|
+
- 컴포넌트 내부 요소(버튼의 아이콘-텍스트, chip 등): `space-1` ~ `space-2`.
|
|
13
|
+
- 폼 필드 간, 카드 내부 블록 간: `space-2` ~ `space-3`.
|
|
14
|
+
- 패널 헤더-바디, 카드 상하 padding: `space-3` 안팎 (상하는 좌우보다 한 단계 작게).
|
|
15
|
+
- 패널·섹션 간 세로 gap: `space-3` ~ `space-4`. border나 bg-subtle이 분리를 담당하므로 공간은 좁게.
|
|
16
|
+
- 페이지 body padding: `space-4` ~ `space-6`.
|
|
17
|
+
|
|
18
|
+
**랜딩·마케팅·에디토리얼 (선언형 UI)**
|
|
19
|
+
- 선언 섹션 간: `space-10` ~ `space-16`.
|
|
20
|
+
- 페이지 최상단·히어로 상하 호흡: `space-16` 이상.
|
|
21
|
+
- 큰 타이틀-서브헤드 간: `space-4` ~ `space-6`.
|
|
22
|
+
|
|
23
|
+
**공통 규칙**
|
|
24
|
+
- 어느 리듬이든, **카드·패널 그리드(border·bg로 이미 구분된 요소)의 가로/세로 gap은 균일하게.** 가로 `space-3`이면 세로도 `space-3`.
|
|
25
|
+
- 어느 리듬이든, 선언 섹션(히어로)과 콘솔 섹션(테이블/리스트)을 **같은 화면에서 섞을 때는 콘솔 리듬을 베이스로 두고** 선언 섹션 하나만 큰 호흡을 허락한다. 반대로 하지 않는다 (화면 전체가 랜딩처럼 뜨고 머리가 비게 됨).
|
|
26
|
+
|
|
27
|
+
**카드·패널 내부 padding — 시각적으로 상하좌우 동일**
|
|
28
|
+
- 카드, 패널, 모달, Callout 같은 "닫힌 박스" 안쪽은 **상하좌우 padding이 시각적으로 같아 보이게** 쓴다. 기본은 네 면 같은 토큰(`padding: var(--space-N)` 하나).
|
|
29
|
+
- 폰트는 line-height 여백을 품으므로, 콘텐츠가 큰 텍스트 블록이면 **상하를 수치상 한 단계 줄여** 시각 균형을 맞춘다 (line-height 보정). 예: 큰 타이틀 위주 카드면 `var(--space-2) var(--space-3)`. 이건 "비대칭을 쓰려는 게 아니라, 눈에 같아 보이게 하는 보정".
|
|
30
|
+
- 리스트 row·테이블 row처럼 **반복되는 가로 긴 아이템**도 동일 원칙 — 기본은 정사각, 큰 텍스트가 들어가면 line-height 보정으로만 상하 한 단계 축소.
|
|
31
|
+
- 예외는 panel 헤더(한 줄 고정 높이) 정도. 그 외엔 **"비대칭 padding이 눈에 보이면 틀린 것"**.
|
|
32
|
+
|
|
33
|
+
### Don't
|
|
34
|
+
- 스케일 밖의 임의 px 사용 금지. 필요하면 디자인 재검토 신호.
|
|
35
|
+
- 인접 스케일을 나란히 반복 금지 — 위계가 사라짐. 최소 2단계 차이. (line-height 보정은 예외)
|
|
36
|
+
- 컴포넌트 간 간격을 `space-2` 이하로 좁히기 금지 — **단, 버튼↔버튼·칩↔칩 등 "작은 컴포 묶음"은 역으로 `space-1`(4px) 고정.** 큰 섹션 간은 좁히지 말고, 작은 묶음은 넓히지 말라.
|
|
37
|
+
- 카드 그리드의 세로 gap을 가로 gap보다 크게 잡지 않는다 — 격자가 세로로만 벌어져 "열은 붙고 행은 떨어진" 어긋남이 생긴다.
|
|
38
|
+
- 카드 그리드끼리 또는 카드 그리드와 다음 패널을 `space-6` 이상 gap으로 분리하지 않는다 — 카드가 이미 border/배경으로 분리돼 있어 과한 공간은 "구조가 끊겨 보이는" 결과를 만든다.
|
|
39
|
+
- 카드·패널 내부 padding을 눈에 띄게 비대칭으로 쓰지 않는다 — 상하좌우가 시각적으로 같아 보이는 게 디폴트. 비대칭을 쓰는 경우는 "line-height 여백 보정" 목적일 때만, 상하를 한 단계 줄이는 쪽으로.
|
|
40
|
+
|
|
41
|
+
### Fallback
|
|
42
|
+
관련성이 확실치 않으면 `space-4`부터 시도.
|
|
43
|
+
|
|
44
|
+
### 관련
|
|
45
|
+
- [[axes/density]] — `--space-scale`로 모든 spacing 값 스왑
|
|
46
|
+
|
|
47
|
+
### 토큰 표
|
|
48
|
+
토큰에는 **고정 용도가 없다.** 아래 "전형적인 자리"는 가장 자주 쓰는 예시일 뿐, "섹션 간은 반드시 space-8" 식으로 못박지 않는다. 어느 자리에 어느 스케일을 쓸지는 위의 Do(앱 리듬 vs 랜딩 리듬)에서 결정한다.
|
|
49
|
+
|
|
50
|
+
| Token | 전형적인 자리 (예시) |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `space-0.5` | 아이콘-텍스트 미세 간격 |
|
|
53
|
+
| `space-1` | 텍스트-아이콘, 아이콘 묶음, **버튼↔버튼 gap(고정)** |
|
|
54
|
+
| `space-1.5` | 컨트롤 내부 gap |
|
|
55
|
+
| `space-2` | 작은 gap (컴포넌트 사이는 제외 — space-1 고정) |
|
|
56
|
+
| `space-2.5` | 버튼 md 좌우 |
|
|
57
|
+
| `space-3` | 카드 내부, 콘솔 패널 세로 리듬 |
|
|
58
|
+
| `space-4` | 콘솔 패널 좌우 padding, 카드 그리드 gap |
|
|
59
|
+
| `space-5` | 콘솔 body padding |
|
|
60
|
+
| `space-6` | 콘솔 body padding (넉넉) / 랜딩 요소 간 |
|
|
61
|
+
| `space-8` | 선언 섹션 상하 호흡 |
|
|
62
|
+
| `space-10` | 랜딩 섹션 간 |
|
|
63
|
+
| `space-12` | 랜딩 섹션 간 (넉넉) |
|
|
64
|
+
| `space-16` | 랜딩 히어로·페이지 최상단 |
|
|
65
|
+
|
|
66
|
+
### 전역 element selector로 padding/margin 걸지 않기
|
|
67
|
+
`section`, `article`, `main`, `h1~h6` 등 **element selector에 전역 padding/margin을 걸면 그 태그를 쓰는 모든 자리를 오염**시킨다. 특히 `section`은 CTDS에서 "panel/카드 하나"를 뜻하는 자리로도 쓰이고 "docs의 스크롤 네비 섹션"으로도 쓰이므로, 두 자리의 리듬이 서로 다른 걸 하나의 전역 룰로 묶으면 반드시 한 쪽이 터진다.
|
|
68
|
+
|
|
69
|
+
- 전역 룰은 **특정 wrapper 클래스 안으로 scope**한다 — `section` ❌ → `.docs section` ✅.
|
|
70
|
+
- 섹션 간 호흡은 **부모 flex-column의 `gap`으로** 담당하고, 섹션 자체에는 padding을 걸지 않는 쪽을 선호.
|
|
71
|
+
- 디버깅 체크리스트: 어떤 자리의 패딩이 "내가 안 걸었는데 들어있다"면 **상위 globals.css의 element selector 규칙**부터 의심한다.
|
|
72
|
+
|
|
73
|
+
### 텍스트 gap·line-height
|
|
74
|
+
- **섹션 타이틀(h1~h3)에는 반드시 명시적 `line-height` 지정.** 지정 없으면 body base(1.5)를 상속해 큰 폰트에서 "위가 뭉텅 비어 보임" 함정이 발생한다. 큰 선언형은 `--lh-tight`(1.2), 패널 헤더는 1.3 안팎.
|
|
75
|
+
- 상하좌우 균일 padding의 함정: 모달 상단 텍스트(타이틀), 하단 버튼일 때 균일 padding이면 위가 넓어 보일 수 있다 — 이 때는 **먼저 타이틀의 line-height를 조이고**, 그래도 위가 뜨면 상단 padding을 한 단계 축소.
|
|
76
|
+
- 텍스트 ↔ 텍스트 gap: 두 요소 모두 line-height를 품으므로 보수적으로. 라벨-값은 `space-1~2`.
|
|
77
|
+
- 텍스트 ↔ 그래픽 gap: 그래픽은 line-height 여백이 없으므로 표준 스케일 그대로.
|
|
78
|
+
- 그래픽 ↔ 그래픽: 필요하면 한 단계 크게.
|
|
79
|
+
|
|
80
|
+
### 한 줄에 크기가 다른 요소 수직 정렬
|
|
81
|
+
한 flex row 안에 폰트 크기가 다른 두 요소(큰 숫자값 + 작은 라벨/[[components/badge]]/작은 아이콘 등)가 나란히 올 때, `align-items`는 다음 기준으로 선택한다. 임의 기본값(특히 `baseline`) 금지.
|
|
82
|
+
|
|
83
|
+
- **기본은 `center`.** 큰 글자와 작은 글자/배지의 중심을 맞춘다. 카드 안에서 "값 + 변화량 Badge" 같은 조합이 전형. baseline으로 맞추면 작은 요소가 아래로 쏠려 위쪽 패딩이 비어 보이고 카드 내부 균형이 깨진다.
|
|
84
|
+
- **같은 폰트 크기의 텍스트끼리** 가로로 나열될 때만 `baseline`. 다른 크기 섞이면 금지.
|
|
85
|
+
- 아이콘 ↔ 텍스트는 `center` 고정 (아이콘엔 baseline이 없다).
|
|
86
|
+
- 패딩이 균일한 카드 안의 row는 `align-items: center` + 균일 padding으로 "실제로도, 시각적으로도" 가운데 정렬이 성립한다. 큰 요소 하나 때문에 상하 여백이 어긋나 보이면 **패딩이 아니라 정렬을 의심한다.**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Typography
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
텍스트 크기·라인하이트 스케일. 크기 위계는 항상 유지 (xs < sm < base < ...).
|
|
5
|
+
스케일 전체는 [[axes/density]]의 `--font-scale`로 연쇄 스왑되지만 위계·라인하이트 비율은 불변.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
- 본문은 `text-base`. 애매하면 이것부터.
|
|
9
|
+
- Table·리스트 밀도가 중요한 곳은 `text-sm`.
|
|
10
|
+
- 헤딩은 `text-xl` 이상, 페이지 타이틀은 `text-4xl` (h1) 관례.
|
|
11
|
+
- line-height는 스케일에 이미 포함. 별도 조정하지 않는다.
|
|
12
|
+
- **라벨/UI 텍스트(버튼, 메뉴, 사이드바 Item, Chip, Badge)는 `--label-{sm,md,lg}-size`**. 본문 스케일(`--text-*`)과 라벨 스케일은 분리돼 있고 라벨 쪽이 UI 전용으로 보정됨.
|
|
13
|
+
|
|
14
|
+
### Don't
|
|
15
|
+
- font-size에 임의 px 하드코딩 금지.
|
|
16
|
+
- `line-height`를 spacing 토큰으로 조정하지 않는다 (별개 시스템).
|
|
17
|
+
- 한 화면에서 3단계 이상 크기 건너뛰지 않는다 (위계 어색).
|
|
18
|
+
- **20px(=`text-xl`) 미만에 `semibold`/`bold` 사용 금지**. 20px 이상이어도 **컴포넌트 내부 자체 타이틀/display에만** 허용 — 페이지의 섹션 타이틀, 범용 h2/h3처럼 "컴포넌트에 포함되지 않은" 자리에는 사용 금지. 그 외(본문·라벨·버튼·사이드바·메뉴·테이블·배지, 페이지 섹션 헤딩 전부)는 **`medium` 또는 `regular`만** 쓴다. 작은 글자에서 두꺼운 획은 가독성·시각 균형을 깨뜨린다. 강조가 필요하면 색(`fg-strong`) 또는 medium으로 해결.
|
|
19
|
+
- **유령 토큰 금지**. 아래 "CSS 변수 whitelist" 밖의 이름(예: `--font-size-sm`, `--text-size-sm`, `--fs-sm`)은 **존재하지 않는 토큰**이고 참조 시 브라우저 base(≈16px)로 상속돼 사이즈감이 깨진다. 반드시 whitelist의 정확한 이름만 사용.
|
|
20
|
+
|
|
21
|
+
### Fallback
|
|
22
|
+
본문 `text-base`, 라벨 `text-sm`, 헤딩 `text-2xl`.
|
|
23
|
+
|
|
24
|
+
### 관련
|
|
25
|
+
- [[axes/font]] — 폰트 스택·포인트 폰트 범위 결정
|
|
26
|
+
- [[axes/density]] — `--font-scale`로 사이즈 스케일 전체를 스왑 (compact 0.95× / default 1× / comfortable 1.05×)
|
|
27
|
+
|
|
28
|
+
### CSS 변수 whitelist (이 이름만 유효 — 다른 접두어는 전부 유령 토큰)
|
|
29
|
+
|
|
30
|
+
| 용도 | CSS 변수 | 참고 |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| 본문/캡션 크기 | `--text-{3xs,2xs,xs,sm,base,lg,xl,2xl,3xl,4xl,5xl}` | line-height는 `--lh-{...}` 짝으로 매칭 |
|
|
33
|
+
| 라벨/UI 크기 | `--label-{sm,md,lg}-size` | 라벨 폰트는 `--label-font` |
|
|
34
|
+
| 라인하이트 | `--lh-{3xs,2xs,xs,sm,base,lg,xl,2xl,3xl,4xl,5xl}` | font-size 토큰과 짝 |
|
|
35
|
+
| Weight | `--font-weight-{regular,medium,semibold,bold}` | |
|
|
36
|
+
| Font family | `--font-normal`, `--label-font` | |
|
|
37
|
+
|
|
38
|
+
금지 예: `--font-size-sm`, `--font-size-xs`, `--text-size-sm`, `--fs-sm`, `--size-text-sm`. 이 중 어느 것도 globals.css에 정의돼 있지 **않다**.
|
|
39
|
+
|
|
40
|
+
### 토큰 표
|
|
41
|
+
|
|
42
|
+
**Font Size / Line-height**
|
|
43
|
+
| Token | CSS 변수 | 용도 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `text-3xs` | `--text-3xs` / `--lh-3xs` | Micro meta (사용 자제) |
|
|
46
|
+
| `text-2xs` | `--text-2xs` / `--lh-2xs` | Dense table meta, tag |
|
|
47
|
+
| `text-xs` | `--text-xs` / `--lh-xs` | Caption, Helper, Badge |
|
|
48
|
+
| `text-sm` | `--text-sm` / `--lh-sm` | Body Small, Table, Label |
|
|
49
|
+
| `text-base` | `--text-base` / `--lh-base` | Body (기본) |
|
|
50
|
+
| `text-lg` | `--text-lg` / `--lh-lg` | Lead 문단, 강조 본문 |
|
|
51
|
+
| `text-xl` | `--text-xl` / `--lh-xl` | H4 |
|
|
52
|
+
| `text-2xl` | `--text-2xl` / `--lh-2xl` | H3 |
|
|
53
|
+
| `text-3xl` | `--text-3xl` / `--lh-3xl` | H2 |
|
|
54
|
+
| `text-4xl` | `--text-4xl` / `--lh-4xl` | H1, Page Title |
|
|
55
|
+
| `text-5xl` | `--text-5xl` / `--lh-5xl` | Display (Hero) |
|
|
56
|
+
|
|
57
|
+
**Font Weight**
|
|
58
|
+
| Token | 용도 | 허용 자리 |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `regular` | 본문·대부분의 UI 텍스트 | 모든 자리 |
|
|
61
|
+
| `medium` | 라벨, 강조 본문, 서브 헤딩, 작은 UI 강조 | 모든 자리 |
|
|
62
|
+
| `semibold` | **20px(`text-xl`) 이상 + 컴포넌트 내부 자체 타이틀/display** | 아래 조건 모두 |
|
|
63
|
+
| `bold` | **컴포넌트 내부 디스플레이·히어로만** (극히 제한적) | 아래 조건 모두 |
|
|
64
|
+
|
|
65
|
+
**규칙 (semibold/bold 사용 조건):**
|
|
66
|
+
1. `font-size ≥ var(--text-xl)` (20px) — 미만이면 무조건 금지
|
|
67
|
+
2. **컴포넌트에 포함된 자리**여야 함 — Hero·MetricCard value·Dialog 내부 타이틀 등. 범용 섹션 h2/h3, 페이지 내부 heading처럼 "컴포넌트가 아닌 자리"에선 사이즈와 무관하게 금지.
|
|
68
|
+
|
|
69
|
+
본문·라벨·버튼·메뉴·사이드바·테이블·배지·페이지 섹션 헤딩은 전부 `medium`/`regular`만. 강조는 색(`fg-strong`)·medium으로 해결.
|
|
70
|
+
|
|
71
|
+
**Letter Spacing**
|
|
72
|
+
| Token | 용도 |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `tight` | 큰 헤딩(text-2xl 이상) |
|
|
75
|
+
| `normal` | 본문 |
|
|
76
|
+
| `wide` | 대문자 라벨, 뱃지 텍스트 |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Z-index
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
겹침 순서(layering)의 계층. 이름 붙은 스케일만 사용해 stacking context 사고를 방지.
|
|
5
|
+
Tooltip은 언제나 최상단.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
- 오버레이 레이어는 이름 있는 z-index 토큰 사용.
|
|
9
|
+
- Sticky header는 `z-sticky`, dropdown류는 `z-dropdown`.
|
|
10
|
+
- Modal은 `z-modal`, 그 위 popover는 `z-popover`.
|
|
11
|
+
- Tooltip은 `z-tooltip` (항상 최상단).
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- 임의 정수(9999 등) 하드코딩 금지.
|
|
15
|
+
- Tooltip을 Modal 아래에 두지 않는다.
|
|
16
|
+
- Toast를 Modal 아래에 두지 않는다 (Modal 열린 상태에서도 알림은 보여야 함).
|
|
17
|
+
|
|
18
|
+
### Fallback
|
|
19
|
+
정할 수 없으면 `z-base` (0).
|
|
20
|
+
|
|
21
|
+
### 관련
|
|
22
|
+
N/A
|
|
23
|
+
|
|
24
|
+
### 토큰 표
|
|
25
|
+
| Token | 용도 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `z-base` | 기본 |
|
|
28
|
+
| `z-docked` | 도킹된 요소 (컨텐츠 내 sticky sub-header 등) |
|
|
29
|
+
| `z-dropdown` | Dropdown, Select |
|
|
30
|
+
| `z-sticky` | Page-level sticky header |
|
|
31
|
+
| `z-overlay` | 배경 dim overlay |
|
|
32
|
+
| `z-modal` | Modal, Drawer |
|
|
33
|
+
| `z-popover` | Popover, Hover Card |
|
|
34
|
+
| `z-toast` | Toast |
|
|
35
|
+
| `z-tooltip` | Tooltip (최상단) |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ctds
|
|
3
|
+
description: CTDS 하이브리드 디자인 시스템. 8축(Mode/Palette/Hue/Shape/Icon/Density/Motion/Font) CSS 변수 스왑. 색/크기/시간/폰트 하드코딩 금지, 토큰·프리미티브 경유 강제. CTDS 작업 시작점 — 작업 유형 식별 후 하위 스킬로 라우팅.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CTDS Design System — Foundation
|
|
7
|
+
|
|
8
|
+
Layer 1(shadcn 방식 컴포넌트 코드) + Layer 2(AI 프롬프트 컨텍스트) 하이브리드. 상세는 `prompts/` 원본 문서 참조.
|
|
9
|
+
|
|
10
|
+
## 발동 시 — 온보딩 페이지 자동 오픈
|
|
11
|
+
|
|
12
|
+
1. `curl -sf http://localhost:3000 -o /dev/null && echo ok || echo down`
|
|
13
|
+
2. ok → `open http://localhost:3000/onboarding`
|
|
14
|
+
3. down → "dev 서버 꺼져있어요. `cd web && pnpm dev` 후 다시" 안내
|
|
15
|
+
|
|
16
|
+
## 절대 규칙 (항상 적용)
|
|
17
|
+
|
|
18
|
+
1. **하드코딩 금지** — 색(hex/rgb/oklch), 크기(px/rem/em), 시간(ms/s), 그림자, 폰트명. 전부 CSS 변수·토큰 경유. 예외: 1px border, 100%, 0.
|
|
19
|
+
2. **축 우선** — 8축 값 확인 전에 컴포넌트 결정 금지.
|
|
20
|
+
3. **접근성 > 축** — `prefers-reduced-motion:reduce`면 `motion=none`. WCAG AA 미달 금지.
|
|
21
|
+
4. **플로팅 서페이스 = `.m-float`만** — Tooltip/Popover/ContextMenu/DropdownMenu/Select/DatePicker/HoverCard. 컴포넌트별 keyframe/animation 재정의 금지.
|
|
22
|
+
5. **애매하면 묻는다** — 규칙으로 판단 안 서면 사용자 확인.
|
|
23
|
+
|
|
24
|
+
전체 원칙: `prompts/PRINCIPLES.md`.
|
|
25
|
+
|
|
26
|
+
## 작업 유형 → 스킬 라우팅
|
|
27
|
+
|
|
28
|
+
| 작업 | 호출 스킬 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| 컴포넌트 신규 제작·variant/prop 수정 (`web/components/<Name>/` 또는 `prompts/components/<name>.md`) | `ctds-component` |
|
|
31
|
+
| 페이지·대시보드·레이아웃 조합 (`web/app/**/page.tsx`) | `ctds-page` |
|
|
32
|
+
| 테마 프리셋 생성·수정 (`prompts/themes/*` 또는 `web/app/theme-demo/*`) | `ctds-theme` |
|
|
33
|
+
| 코드 검증 (하드코딩·유령 토큰·메모리 규칙·문서-코드 동기화) | `ctds-validate` |
|
|
34
|
+
| 3D 아이콘 일러스트 자산 생성 | `ctds-illust` |
|
|
35
|
+
|
|
36
|
+
각 스킬이 자기 영역의 축·토큰 매트릭스를 들고 간다. foundation은 라우팅만.
|
package/bin/ctds.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CTDS CLI entry.
|
|
4
|
+
*
|
|
5
|
+
* 사용법:
|
|
6
|
+
* ctds init [--dry] [--force]
|
|
7
|
+
* ctds update [--dry] [--force]
|
|
8
|
+
* ctds --help
|
|
9
|
+
* ctds --version
|
|
10
|
+
*
|
|
11
|
+
* 서브커맨드는 ../src/commands/<name>.js 로 디스패치.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const pkg = JSON.parse(
|
|
20
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf8")
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const [, , cmd, ...rest] = process.argv;
|
|
24
|
+
|
|
25
|
+
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
26
|
+
printHelp();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
30
|
+
console.log(pkg.version);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const commands = {
|
|
35
|
+
init: () => import("../src/commands/init.js").then((m) => m.run(rest)),
|
|
36
|
+
update: () => import("../src/commands/update.js").then((m) => m.run(rest)),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (!(cmd in commands)) {
|
|
40
|
+
console.error(`unknown command: ${cmd}`);
|
|
41
|
+
console.error("");
|
|
42
|
+
printHelp();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
commands[cmd]().catch((err) => {
|
|
47
|
+
console.error("");
|
|
48
|
+
console.error("❌ 실패:", err?.message ?? err);
|
|
49
|
+
if (process.env.CTDS_DEBUG) console.error(err);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
function printHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
CTDS — 하이브리드 디자인 시스템 CLI (v${pkg.version})
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
ctds <command> [options]
|
|
59
|
+
|
|
60
|
+
Commands:
|
|
61
|
+
init 현재 Next.js 플젝에 CTDS 설치 (components, prompts, /ctds 라우트)
|
|
62
|
+
update 설치된 CTDS 자산을 최신으로 교체 (수정한 파일은 자동 보호)
|
|
63
|
+
|
|
64
|
+
Options:
|
|
65
|
+
--help, -h 도움말
|
|
66
|
+
--version, -v 버전
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
ctds init
|
|
70
|
+
ctds init --dry 아무것도 쓰지 않고 어떤 파일이 생성될지만 출력
|
|
71
|
+
ctds init --force 기존 파일이 있어도 덮어쓰기
|
|
72
|
+
ctds update 최신으로 업데이트 (충돌 파일만 prompt)
|
|
73
|
+
ctds update --dry 어떤 파일이 바뀔지, 충돌이 몇 개인지만 보기
|
|
74
|
+
ctds update --force 수정 감지 무시, 전부 덮어쓰기 (git 커밋 후에만)
|
|
75
|
+
`);
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conscience-technology/ctds",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CTDS — 하이브리드 디자인 시스템 설치 · 온보딩 CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ctds": "./bin/ctds.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"assets",
|
|
13
|
+
"templates",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"bundle-assets": "node ./scripts/bundle-assets.mjs",
|
|
18
|
+
"prepublishOnly": "npm run bundle-assets"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ctds",
|
|
25
|
+
"design-system",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/Conscience-Technology/CTDS.git",
|
|
35
|
+
"directory": "packages/ctds-cli"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctds init` — 사용자 Next.js 플젝에 CTDS 설치.
|
|
3
|
+
*
|
|
4
|
+
* 하는 일:
|
|
5
|
+
* 1) Next.js app router 플젝인지 감지. 아니면 중단.
|
|
6
|
+
* 2) 자산 복사 (components/ctds/, prompts/, .claude/skills/ctds/)
|
|
7
|
+
* 3) /ctds 라우트 생성 (app/ctds/page.tsx)
|
|
8
|
+
* 4) 사용자가 수동으로 넣어야 하는 코드 블록 출력
|
|
9
|
+
* (layout.tsx wrapper, globals.css import — 자동 패치는 깨지기 쉬워서 안내만)
|
|
10
|
+
*
|
|
11
|
+
* 플래그:
|
|
12
|
+
* --dry 실제 쓰기 없이 어떤 파일이 생성될지만 출력
|
|
13
|
+
* --force 기존 파일 있어도 덮어쓰기
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { dirname, join, resolve } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { detectProject } from "../utils/detect.js";
|
|
20
|
+
import { getAssetSources } from "../utils/assets.js";
|
|
21
|
+
import { checkMissingDeps, renderInstallCmd } from "../utils/deps.js";
|
|
22
|
+
import { c, step, success, warn, info, kv } from "../utils/log.js";
|
|
23
|
+
import { computeFilesMap, writeMeta } from "../utils/meta.js";
|
|
24
|
+
|
|
25
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const CLI_PKG_PATH = join(__dirname, "..", "..", "package.json");
|
|
27
|
+
function readCliVersion() {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(readFileSync(CLI_PKG_PATH, "utf8")).version;
|
|
30
|
+
} catch {
|
|
31
|
+
return "unknown";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function run(args) {
|
|
36
|
+
const dry = args.includes("--dry");
|
|
37
|
+
const force = args.includes("--force");
|
|
38
|
+
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log(c.bold("CTDS 설치"));
|
|
41
|
+
console.log(c.dim("—").repeat(40));
|
|
42
|
+
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const project = detectProject(cwd);
|
|
45
|
+
if (!project.ok) {
|
|
46
|
+
console.error(c.red("✗"), project.reason);
|
|
47
|
+
console.error("");
|
|
48
|
+
console.error(" CTDS는 Next.js App Router 플젝에서 사용합니다.");
|
|
49
|
+
console.error(" 현재 폴더에 package.json과 app/ (또는 src/app/) 디렉토리가 필요합니다.");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
kv("프로젝트", project.name);
|
|
54
|
+
kv("경로", cwd);
|
|
55
|
+
kv("app router", project.appDir);
|
|
56
|
+
kv("src/ 사용", project.useSrcDir ? "yes" : "no");
|
|
57
|
+
if (dry) kv("모드", c.yellow("dry run (쓰기 없음)"));
|
|
58
|
+
console.log("");
|
|
59
|
+
|
|
60
|
+
const assets = getAssetSources();
|
|
61
|
+
info(`자산 소스: ${assets.mode === "bundled" ? "번들" : "로컬 레포"}`);
|
|
62
|
+
console.log("");
|
|
63
|
+
|
|
64
|
+
// 복사 대상 매핑: [src 절대경로, dest 상대경로(사용자 플젝 기준), 레이블]
|
|
65
|
+
const copyPlan = [
|
|
66
|
+
[
|
|
67
|
+
assets.components,
|
|
68
|
+
project.useSrcDir ? "src/components/ctds" : "components/ctds",
|
|
69
|
+
"components",
|
|
70
|
+
],
|
|
71
|
+
[assets.prompts, "prompts", "prompts"],
|
|
72
|
+
[assets.skill, ".claude/skills/ctds", "skill"],
|
|
73
|
+
[assets.publicAssets, "public/onboarding", "onboarding preview images"],
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// 1) 복사
|
|
77
|
+
for (const [src, to, label] of copyPlan) {
|
|
78
|
+
const dst = resolve(cwd, to);
|
|
79
|
+
if (!existsSync(src)) {
|
|
80
|
+
warn(`자산 누락 (스킵): ${label}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (existsSync(dst) && !force) {
|
|
84
|
+
warn(`이미 존재 (스킵, 덮어쓰려면 --force): ${to}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
step(dry ? `[dry] 복사` : `복사`, `${label} → ${to}`);
|
|
88
|
+
if (!dry) {
|
|
89
|
+
mkdirSync(dst, { recursive: true });
|
|
90
|
+
cpSync(src, dst, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 2) /ctds 라우트 생성
|
|
95
|
+
const ctdsRouteDir = resolve(cwd, project.appDir, "ctds");
|
|
96
|
+
const ctdsPagePath = join(ctdsRouteDir, "page.tsx");
|
|
97
|
+
if (existsSync(ctdsPagePath) && !force) {
|
|
98
|
+
warn(`이미 존재 (스킵): ${project.appDir}/ctds/page.tsx`);
|
|
99
|
+
} else {
|
|
100
|
+
step(dry ? `[dry] 생성` : `생성`, `${project.appDir}/ctds/page.tsx`);
|
|
101
|
+
if (!dry) {
|
|
102
|
+
mkdirSync(ctdsRouteDir, { recursive: true });
|
|
103
|
+
writeFileSync(ctdsPagePath, renderCtdsRoute(project), "utf8");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 2.5) components/ctds/.ctds-meta.json 생성 — update가 참조하는 설치 시점 해시 스냅샷
|
|
108
|
+
const componentsDir = resolve(
|
|
109
|
+
cwd,
|
|
110
|
+
project.useSrcDir ? "src/components/ctds" : "components/ctds",
|
|
111
|
+
);
|
|
112
|
+
if (existsSync(componentsDir)) {
|
|
113
|
+
step(dry ? `[dry] 메타 생성` : `메타 생성`, `components/ctds/.ctds-meta.json`);
|
|
114
|
+
if (!dry) {
|
|
115
|
+
const files = computeFilesMap(componentsDir);
|
|
116
|
+
const now = new Date().toISOString();
|
|
117
|
+
writeMeta(componentsDir, {
|
|
118
|
+
version: readCliVersion(),
|
|
119
|
+
installedAt: now,
|
|
120
|
+
updatedAt: now,
|
|
121
|
+
files,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 3) 수동 안내 — layout.tsx wrapper + globals.css import + AI 에이전트 안내
|
|
127
|
+
console.log("");
|
|
128
|
+
console.log(c.bold("설치 완료"));
|
|
129
|
+
console.log(c.dim("—").repeat(40));
|
|
130
|
+
success("자동으로 복사·생성된 파일은 위 리스트와 같습니다.");
|
|
131
|
+
console.log("");
|
|
132
|
+
|
|
133
|
+
console.log(c.bold("다음 두 가지를 수동으로 설정해주세요:"));
|
|
134
|
+
console.log("");
|
|
135
|
+
|
|
136
|
+
// 3-1) globals.css — tokens.css import (폰트 + CTDS 토큰 + 축 셀렉터 전부 포함)
|
|
137
|
+
const globalsRel = project.useSrcDir ? "src/app/globals.css" : "app/globals.css";
|
|
138
|
+
info(`${c.bold("1)")} ${c.cyan(globalsRel)} 최상단에 다음 한 줄을 추가:`);
|
|
139
|
+
console.log(c.dim("```css"));
|
|
140
|
+
console.log(`@import "../components/ctds/tokens.css";`);
|
|
141
|
+
console.log(c.dim("```"));
|
|
142
|
+
console.log(c.dim(" tokens.css가 폰트 로드 + CTDS 토큰 + Palette/Shape/Density/Motion/Mode"));
|
|
143
|
+
console.log(c.dim(" 축 셀렉터까지 전부 담당합니다. 이 import 하나면 CTDS 작동 OK."));
|
|
144
|
+
console.log("");
|
|
145
|
+
|
|
146
|
+
// 3-2) layout.tsx wrapper
|
|
147
|
+
const layoutRel = project.useSrcDir ? "src/app/layout.tsx" : "app/layout.tsx";
|
|
148
|
+
info(`${c.bold("2)")} ${c.cyan(layoutRel)} 의 <html>·<body>를 아래 형태로 수정:`);
|
|
149
|
+
console.log(c.dim("```tsx"));
|
|
150
|
+
console.log(renderLayoutSnippet(project));
|
|
151
|
+
console.log(c.dim("```"));
|
|
152
|
+
console.log("");
|
|
153
|
+
|
|
154
|
+
// 3-3) 의존성 체크
|
|
155
|
+
const { missing } = checkMissingDeps(cwd);
|
|
156
|
+
if (missing.length > 0) {
|
|
157
|
+
info(`${c.bold("3)")} 다음 패키지를 설치해주세요 (CTDS 컴포넌트가 필요):`);
|
|
158
|
+
console.log(c.dim("```bash"));
|
|
159
|
+
console.log(renderInstallCmd(missing));
|
|
160
|
+
console.log(c.dim("```"));
|
|
161
|
+
console.log("");
|
|
162
|
+
} else {
|
|
163
|
+
success("필요한 의존성이 모두 설치돼 있어요.");
|
|
164
|
+
console.log("");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 4) 다음 단계
|
|
168
|
+
console.log(c.bold("다음 단계"));
|
|
169
|
+
console.log(c.dim("—").repeat(40));
|
|
170
|
+
console.log("");
|
|
171
|
+
console.log(` ${c.cyan("npm run dev")}`);
|
|
172
|
+
console.log(` → ${c.cyan("http://localhost:3000/ctds")} 접속`);
|
|
173
|
+
console.log(` → 온보딩 돌리고 "적용"하면 ${c.bold("ctds.config.json")}·${c.bold("DESIGN.md")}가 생성됩니다.`);
|
|
174
|
+
console.log("");
|
|
175
|
+
console.log(c.dim("이 두 파일을 커밋하면 AI 에이전트(Claude Code 등)가 참조해"));
|
|
176
|
+
console.log(c.dim("프로젝트의 축에 맞게 컴포넌트·페이지를 생성합니다."));
|
|
177
|
+
console.log("");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function renderCtdsRoute(project) {
|
|
181
|
+
// 사용자 플젝 alias 호환성 — 상대경로로 안전하게.
|
|
182
|
+
// app/ctds/page.tsx → ../../components/ctds/Onboarding (useSrcDir도 동일 레벨)
|
|
183
|
+
const importPath = "../../components/ctds/Onboarding";
|
|
184
|
+
return `/**
|
|
185
|
+
* CTDS 온보딩 라우트.
|
|
186
|
+
* 이 파일은 \`npx ctds init\`으로 생성됐습니다.
|
|
187
|
+
* 수정해도 됩니다 — 다음 init은 --force 없이는 덮어쓰지 않아요.
|
|
188
|
+
*/
|
|
189
|
+
import { OnboardingPage } from "${importPath}";
|
|
190
|
+
|
|
191
|
+
export default function Page() {
|
|
192
|
+
return <OnboardingPage />;
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function renderLayoutSnippet(project) {
|
|
198
|
+
// app/layout.tsx → ../components/ctds/ThemeController
|
|
199
|
+
const importPath = "../components/ctds/ThemeController";
|
|
200
|
+
return `import { ThemeController } from "${importPath}";
|
|
201
|
+
// 가능하면 ctds.config.json도 import — 적용 후 생성됩니다.
|
|
202
|
+
// import ctdsConfig from "../../ctds.config.json";
|
|
203
|
+
|
|
204
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
205
|
+
return (
|
|
206
|
+
<html lang="ko">
|
|
207
|
+
<body>
|
|
208
|
+
<ThemeController /* initialConfig={ctdsConfig} */>
|
|
209
|
+
{children}
|
|
210
|
+
</ThemeController>
|
|
211
|
+
</body>
|
|
212
|
+
</html>
|
|
213
|
+
);
|
|
214
|
+
}`;
|
|
215
|
+
}
|