atomsystem 0.4.3 → 0.5.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 +112 -52
- package/components/blocks/BlockDetailPageContent.tsx +337 -0
- package/components/blocks/BlockViewerCard.tsx +123 -123
- package/components/blocks/BlocksSidebar.tsx +93 -0
- package/components/blocks/CardListBlock.tsx +101 -196
- package/components/blocks/FileExplorerBlock.tsx +114 -446
- package/components/blocks/InteractiveChatBlock.tsx +107 -123
- package/components/blocks/MetricStatsBlock.tsx +78 -106
- package/components/blocks/PricingPlanBlock.tsx +88 -144
- package/components/blocks/RealtimeChartBlock.tsx +131 -250
- package/components/blocks/SliderShowcaseBlock.tsx +105 -305
- package/components/ui/Container.tsx +45 -0
- package/components/ui/Layout.tsx +220 -0
- package/components/ui/Switch.tsx +39 -0
- package/components/ui/Table.tsx +88 -0
- package/components/ui/Tabs.tsx +58 -0
- package/components/ui/index.ts +5 -0
- package/css/atom-ui.css +41 -816
- package/dist/cli.cjs +633 -41
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +630 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +176 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +176 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/presets/agents.guide.md +49 -0
- package/presets/blocks.md +101 -0
- package/presets/components.md +142 -0
- package/tokens/token.json +63 -1
- package/tokens/tokens.json +63 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AtomSystem (`atomsystem`)
|
|
2
2
|
|
|
3
3
|
> **Universal Design Token & Agent-Driven UI System**
|
|
4
|
-
> React, Vue, Svelte, Pure HTML 어디서든 즉시 동작하는 W3C DTCG 호환 디자인 시스템
|
|
4
|
+
> React, Vue, Svelte, Pure HTML 어디서든 즉시 동작하는 W3C DTCG 호환 디자인 시스템 & 로컬 토큰 스튜디오
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/atomsystem)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -20,21 +20,70 @@ npm install atomsystem
|
|
|
20
20
|
|
|
21
21
|
### 2. 프로젝트 초기화 (CLI)
|
|
22
22
|
|
|
23
|
-
프로젝트 루트에서 `init` 명령을 실행하면 AI Agent 전용
|
|
23
|
+
프로젝트 루트에서 `init` 명령을 실행하면 AI Agent 전용 가이드라인, 4단계 레고 조립 규격, W3C DTCG 토큰 파일이 한 번에 자동 세팅됩니다.
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npx atomsystem init
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
생성되는 파일:
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- `atom.
|
|
33
|
-
- `
|
|
30
|
+
- `AGENTS.md` / `CLAUDE.md` / `.cursorrules`: 범용 AI 어시스턴트(Antigravity, Cursor, Claude Code, Copilot 등) 공통 지침
|
|
31
|
+
- `.agents/skills/atomsystem/SKILL.md`: AI Agent 자동 분석 및 토큰 구축 스킬
|
|
32
|
+
- `atom.design.md` & `tokens/token.json`: 브랜드, 타이포그래피, 컬러, 여백, 곡률 W3C DTCG 표준 명세
|
|
33
|
+
- `atom.components.md`: 원자/분자 UI 컴포넌트 명세
|
|
34
|
+
- `atom.blocks.md`: 실시간 차트, AI 챗 등 완성형 블록 명세
|
|
35
|
+
- `atom.uxPattern.md`: 폼 검증, 결제 플로우, 로딩 상태 등 표준 UX 패턴 가이드
|
|
36
|
+
- `css/atom-ui.css`: 0KB 제로 런타임 시맨틱 순수 CSS
|
|
34
37
|
|
|
35
38
|
---
|
|
36
39
|
|
|
37
|
-
## 🎨
|
|
40
|
+
## 🎨 로컬 토큰 스튜디오 (Local Token Studio GUI)
|
|
41
|
+
|
|
42
|
+
설치 후 별도의 설정 없이 **로컬에서 직접 디자인 토큰을 변경하고 스타일을 실시간 재컴파일**할 수 있는 내장 웹 GUI 도구를 제공합니다.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 로컬 토큰 스튜디오 GUI 실행 (기본 포트: 4000)
|
|
46
|
+
npx atomsystem studio
|
|
47
|
+
|
|
48
|
+
# 또는 다른 포트로 실행
|
|
49
|
+
npx atomsystem studio --port=5000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
1. **브라우저 접속**: `http://localhost:4000`에 접속합니다.
|
|
53
|
+
2. **시각적 편집**: 컬러 피커 및 셀렉터를 통해 브랜드 색상, 서피스 배경, 모서리 곡률(Radius)을 직접 변경합니다.
|
|
54
|
+
3. **원클릭 저장 & 0ms 자동 컴파일**:
|
|
55
|
+
- `tokens/token.json`에 최신 W3C 토큰이 저장됩니다.
|
|
56
|
+
- 동시에 `css/atom-ui.css`가 제로 런타임 CSS 변수로 자동 재컴파일됩니다.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 터미널에서 CLI로 직접 CSS 재컴파일 시
|
|
60
|
+
npx atomsystem build:tokens
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 📦 컴포넌트 & 블록 추가 (CLI Add)
|
|
66
|
+
|
|
67
|
+
원하는 컴포넌트나 블록을 프로젝트에 소스 코드로 직접 내려받아 커스텀할 수 있습니다 (Component-First 원칙 보장).
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 지원 항목 목록 확인
|
|
71
|
+
npx atomsystem list
|
|
72
|
+
|
|
73
|
+
# 개별 컴포넌트 추가
|
|
74
|
+
npx atomsystem add button
|
|
75
|
+
npx atomsystem add container
|
|
76
|
+
npx atomsystem add layout
|
|
77
|
+
|
|
78
|
+
# 완성형 디자인 블록 추가 (필요 컴포넌트 자동 의존성 복사)
|
|
79
|
+
npx atomsystem add realtime-chart
|
|
80
|
+
npx atomsystem add interactive-chat
|
|
81
|
+
npx atomsystem add pricing-table
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 💻 사용 방법 (Usage)
|
|
38
87
|
|
|
39
88
|
### 1) CSS 스타일 불러오기
|
|
40
89
|
|
|
@@ -52,11 +101,28 @@ import 'atomsystem/css/atom-ui.css';
|
|
|
52
101
|
|
|
53
102
|
---
|
|
54
103
|
|
|
55
|
-
### 2) UI 컴포넌트 &
|
|
104
|
+
### 2) UI 컴포넌트 & 레이아웃 사용
|
|
56
105
|
|
|
57
|
-
|
|
106
|
+
프레임워크에 구애받지 않고 클래스명만으로 즉시 동작하거나, React 컴포넌트를 import하여 사용할 수 있습니다.
|
|
58
107
|
|
|
59
|
-
####
|
|
108
|
+
#### React 컴포넌트 Import
|
|
109
|
+
```tsx
|
|
110
|
+
import { Button, Card, Badge, Container, Layout, Stack, Grid } from 'atomsystem';
|
|
111
|
+
|
|
112
|
+
export function Example() {
|
|
113
|
+
return (
|
|
114
|
+
<Container size="xl">
|
|
115
|
+
<Card variant="interactive">
|
|
116
|
+
<Badge variant="primary">New</Badge>
|
|
117
|
+
<h3>대시보드</h3>
|
|
118
|
+
<Button variant="primary" size="md">시작하기</Button>
|
|
119
|
+
</Card>
|
|
120
|
+
</Container>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 순수 시맨틱 CSS 클래스 (HTML / Vue / Vanilla JS)
|
|
60
126
|
```html
|
|
61
127
|
<!-- 버튼 -->
|
|
62
128
|
<button class="atom-btn atom-btn-primary">확인</button>
|
|
@@ -67,64 +133,57 @@ AtomSystem의 모든 컴포넌트는 프레임워크에 구애받지 않고 클
|
|
|
67
133
|
<h2 class="atom-title-2">계좌 송금</h2>
|
|
68
134
|
<p class="atom-body-2">수수료 없는 간편 송금 서비스</p>
|
|
69
135
|
</div>
|
|
70
|
-
|
|
71
|
-
<!-- 인풋 -->
|
|
72
|
-
<div class="atom-input-group">
|
|
73
|
-
<label class="atom-label">이메일</label>
|
|
74
|
-
<input class="atom-input" type="email" placeholder="user@example.com" />
|
|
75
|
-
</div>
|
|
76
136
|
```
|
|
77
137
|
|
|
78
|
-
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### 3) 3,800+ 무료 오픈소스 아이콘 시스템 연동
|
|
141
|
+
|
|
142
|
+
Lucide 아이콘 풀세트(3,875개)를 지원하며, 시스템 및 테마 에디터와 완벽하게 연동됩니다.
|
|
143
|
+
|
|
79
144
|
```tsx
|
|
80
|
-
import {
|
|
81
|
-
import { Card } from 'atomsystem/components/Card';
|
|
82
|
-
import { Badge } from 'atomsystem/components/Badge';
|
|
145
|
+
import { Sparkles, Heart, Search } from 'lucide-react';
|
|
83
146
|
|
|
84
|
-
export function
|
|
147
|
+
export function IconWidget() {
|
|
85
148
|
return (
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
</Card>
|
|
149
|
+
<button className="inline-flex items-center gap-2 px-3 py-1.5 rounded-(--radius) bg-(--bg-secondary) text-(--text-primary)">
|
|
150
|
+
<Sparkles className="w-4 h-4 text-(--accent-primary)" />
|
|
151
|
+
<span>AI 추천</span>
|
|
152
|
+
</button>
|
|
91
153
|
);
|
|
92
154
|
}
|
|
93
155
|
```
|
|
94
156
|
|
|
95
157
|
---
|
|
96
158
|
|
|
97
|
-
###
|
|
98
|
-
|
|
99
|
-
W3C DTCG 규격 JSON 및 토큰 메타데이터를 직접 import하여 사용할 수 있습니다.
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import {
|
|
103
|
-
BASE_COLOR_TOKENS,
|
|
104
|
-
TYPOGRAPHY_TOKENS,
|
|
105
|
-
RADIUS_TOKENS,
|
|
106
|
-
generateCssVariables
|
|
107
|
-
} from 'atomsystem';
|
|
159
|
+
### 4) 다국어(i18n) 지원
|
|
108
160
|
|
|
109
|
-
|
|
110
|
-
import tokenJson from 'atomsystem/token.json';
|
|
111
|
-
|
|
112
|
-
// 동적 CSS Custom Properties 생성
|
|
113
|
-
const cssVars = generateCssVariables();
|
|
114
|
-
```
|
|
161
|
+
한국어(`ko`), 영어(`en`), 일본어(`ja`) 3개 국어를 기본 지원하며 헤더 스위처를 통해 즉시 전환할 수 있습니다.
|
|
115
162
|
|
|
116
163
|
---
|
|
117
164
|
|
|
118
|
-
###
|
|
165
|
+
### 5) AI Agent 연동 (Agent-Driven UI)
|
|
119
166
|
|
|
120
|
-
`npx atomsystem init`으로 초기화된 프로젝트에서는 AI 코딩 어시스턴트(Antigravity, Cursor,
|
|
167
|
+
`npx atomsystem init`으로 초기화된 프로젝트에서는 AI 코딩 어시스턴트(Antigravity, Cursor, Claude Code 등)에게 말로만 작업 지시를 내릴 수 있습니다:
|
|
121
168
|
|
|
122
169
|
```text
|
|
123
|
-
"atom.design.md
|
|
124
|
-
|
|
170
|
+
"atom.design.md의 색상 토큰과 atom.blocks.md의 realtime-chart 구조를 참고해서
|
|
171
|
+
주식 시세 대시보드 화면을 만들어줘."
|
|
125
172
|
```
|
|
126
173
|
|
|
127
|
-
AI Agent는 자동으로 디자인 토큰과
|
|
174
|
+
AI Agent는 자동으로 디자인 토큰과 컴포넌트 우선(Component-First) 레고 조립 파이프라인을 준수하여 일관된 고품질 UI 코드를 생성합니다.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 🛠️ CLI 명령어 요약
|
|
179
|
+
|
|
180
|
+
| 명령어 | 설명 |
|
|
181
|
+
| :--- | :--- |
|
|
182
|
+
| `npx atomsystem init` | AI 지침서, 4단계 규격서, W3C 토큰, CSS 초기화 |
|
|
183
|
+
| `npx atomsystem studio` | 로컬 웹 토큰 스튜디오(GUI) 실행 (`http://localhost:4000`) |
|
|
184
|
+
| `npx atomsystem build:tokens` | `tokens/token.json`을 `css/atom-ui.css`로 일괄 재컴파일 |
|
|
185
|
+
| `npx atomsystem add <name>` | 컴포넌트(`button`, `container`, `layout` 등) 또는 블록 다운로드 |
|
|
186
|
+
| `npx atomsystem list` | 추가 가능한 모든 컴포넌트 및 디자인 블록 목록 출력 |
|
|
128
187
|
|
|
129
188
|
---
|
|
130
189
|
|
|
@@ -132,10 +191,11 @@ AI Agent는 자동으로 디자인 토큰과 UX 표준을 적용하여 일관된
|
|
|
132
191
|
|
|
133
192
|
| 경로 | 설명 |
|
|
134
193
|
| :--- | :--- |
|
|
135
|
-
| `atomsystem` | 토큰 데이터 모델,
|
|
136
|
-
| `atomsystem/css/atom-ui.css` | 전역 스타일 및
|
|
137
|
-
| `atomsystem/components/*` | React UI 컴포넌트
|
|
138
|
-
| `atomsystem/
|
|
194
|
+
| `atomsystem` | 토큰 데이터 모델, 컴포넌트 스펙, 테마 엔진 |
|
|
195
|
+
| `atomsystem/css/atom-ui.css` | 전역 스타일 및 제로 런타임 시맨틱 UI 클래스 |
|
|
196
|
+
| `atomsystem/components/*` | React UI 컴포넌트 (`Button`, `Card`, `Input`, `Container`, `Layout` 등) |
|
|
197
|
+
| `atomsystem/blocks/*` | 레고 조합 완성형 블록 (`RealtimeChart`, `InteractiveChat` 등) |
|
|
198
|
+
| `atomsystem/token.json` | W3C DTCG 포맷 단일 진실 공급원(SSOT) 토큰 파일 |
|
|
139
199
|
| `atomsystem/design.md` | 마크다운 기반 디자인 시스템 가이드 규격 |
|
|
140
200
|
| `atomsystem/uxPattern.md` | 마크다운 기반 UX 인터랙션 패턴 가이드 |
|
|
141
201
|
| `atomsystem/skill` | AI Coding Agent 자동화 스킬 정의서 |
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { Header } from '@/components/Header';
|
|
6
|
+
import { BlocksSidebar } from '@/components/blocks/BlocksSidebar';
|
|
7
|
+
import { BlockItem } from '@/lib/data/blocks';
|
|
8
|
+
import {
|
|
9
|
+
RealtimeChartBlock,
|
|
10
|
+
InteractiveChatBlock,
|
|
11
|
+
CardListBlock,
|
|
12
|
+
SliderShowcaseBlock,
|
|
13
|
+
PricingPlanBlock,
|
|
14
|
+
MetricStatsBlock,
|
|
15
|
+
FileExplorerBlock,
|
|
16
|
+
} from '@/components/blocks';
|
|
17
|
+
import { Button } from '@/components/ui';
|
|
18
|
+
import { CodeHighlight } from '@/components/common/CodeHighlight';
|
|
19
|
+
import {
|
|
20
|
+
Terminal,
|
|
21
|
+
Copy,
|
|
22
|
+
Check,
|
|
23
|
+
Monitor,
|
|
24
|
+
Tablet,
|
|
25
|
+
Smartphone,
|
|
26
|
+
Code2,
|
|
27
|
+
Sparkles,
|
|
28
|
+
Layers,
|
|
29
|
+
ArrowLeft,
|
|
30
|
+
ChevronRight,
|
|
31
|
+
Sliders,
|
|
32
|
+
ExternalLink,
|
|
33
|
+
} from 'lucide-react';
|
|
34
|
+
|
|
35
|
+
interface BlockDetailPageContentProps {
|
|
36
|
+
block: BlockItem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function BlockDetailPageContent({ block }: BlockDetailPageContentProps) {
|
|
40
|
+
const [viewport, setViewport] = useState<'desktop' | 'tablet' | 'mobile'>('desktop');
|
|
41
|
+
const [codeTab, setCodeTab] = useState<'react' | 'html'>('react');
|
|
42
|
+
const [copiedCode, setCopiedCode] = useState(false);
|
|
43
|
+
const [copiedCli, setCopiedCli] = useState(false);
|
|
44
|
+
|
|
45
|
+
const cliCommand = `npx atomsystem add ${block.id}`;
|
|
46
|
+
|
|
47
|
+
const handleCopyCode = () => {
|
|
48
|
+
const code = codeTab === 'react' ? block.codeSnippets.react : block.codeSnippets.htmlCss;
|
|
49
|
+
navigator.clipboard.writeText(code);
|
|
50
|
+
setCopiedCode(true);
|
|
51
|
+
setTimeout(() => setCopiedCode(false), 2000);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleCopyCli = () => {
|
|
55
|
+
navigator.clipboard.writeText(cliCommand);
|
|
56
|
+
setCopiedCli(true);
|
|
57
|
+
setTimeout(() => setCopiedCli(false), 2000);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getViewportMaxWidth = () => {
|
|
61
|
+
switch (viewport) {
|
|
62
|
+
case 'mobile':
|
|
63
|
+
return 'max-w-[390px]';
|
|
64
|
+
case 'tablet':
|
|
65
|
+
return 'max-w-[768px]';
|
|
66
|
+
default:
|
|
67
|
+
return 'max-w-full';
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const renderBlock = () => {
|
|
72
|
+
switch (block.id) {
|
|
73
|
+
case 'realtime-chart':
|
|
74
|
+
return <RealtimeChartBlock />;
|
|
75
|
+
case 'interactive-chat':
|
|
76
|
+
return <InteractiveChatBlock />;
|
|
77
|
+
case 'card-list':
|
|
78
|
+
return <CardListBlock />;
|
|
79
|
+
case 'interactive-slider':
|
|
80
|
+
return <SliderShowcaseBlock />;
|
|
81
|
+
case 'pricing-table':
|
|
82
|
+
return <PricingPlanBlock />;
|
|
83
|
+
case 'metric-stats':
|
|
84
|
+
return <MetricStatsBlock />;
|
|
85
|
+
case 'file-explorer':
|
|
86
|
+
return <FileExplorerBlock />;
|
|
87
|
+
default:
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div className="min-h-screen bg-(--bg-primary) text-(--text-primary) flex flex-col font-sans transition-colors">
|
|
94
|
+
<Header />
|
|
95
|
+
|
|
96
|
+
<div className="flex-1 flex max-w-[1600px] w-full mx-auto overflow-hidden">
|
|
97
|
+
{/* 1. Left Docs Sidebar */}
|
|
98
|
+
<div className="hidden lg:block h-[calc(100vh-45px)] sticky top-11">
|
|
99
|
+
<BlocksSidebar activeId={block.id} />
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* 2. Main Content Area */}
|
|
103
|
+
<main className="flex-1 p-5 sm:p-8 lg:p-10 overflow-y-auto max-w-5xl">
|
|
104
|
+
{/* Breadcrumb */}
|
|
105
|
+
<div className="flex items-center gap-1.5 text-xs text-(--text-secondary) mb-4">
|
|
106
|
+
<Link href="/blocks" className="hover:text-(--text-primary) transition-colors">
|
|
107
|
+
Blocks
|
|
108
|
+
</Link>
|
|
109
|
+
<ChevronRight className="w-3 h-3 opacity-40" />
|
|
110
|
+
<span className="capitalize">{block.category}</span>
|
|
111
|
+
<ChevronRight className="w-3 h-3 opacity-40" />
|
|
112
|
+
<span className="text-(--text-primary) font-semibold">{block.id}</span>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Header & Meta */}
|
|
116
|
+
<div className="space-y-3 mb-8">
|
|
117
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
118
|
+
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight text-(--text-primary)">
|
|
119
|
+
{block.title}
|
|
120
|
+
</h1>
|
|
121
|
+
<span className="font-mono text-xs px-2 py-0.5 rounded-(--radius-sm) bg-(--bg-secondary) text-(--text-secondary) border border-(--border-subtle)">
|
|
122
|
+
{block.badge}
|
|
123
|
+
</span>
|
|
124
|
+
</div>
|
|
125
|
+
<p className="text-sm sm:text-base text-(--text-secondary) leading-relaxed">
|
|
126
|
+
{block.summary}
|
|
127
|
+
</p>
|
|
128
|
+
|
|
129
|
+
{/* CLI Install Box */}
|
|
130
|
+
<div className="flex items-center gap-2 pt-2">
|
|
131
|
+
<div className="flex items-center gap-2 px-3 py-1.5 rounded-(--radius-sm) border border-(--border-subtle) bg-(--bg-secondary)/40 font-mono text-xs text-(--text-primary)">
|
|
132
|
+
<Terminal className="w-3.5 h-3.5 text-(--accent-primary)" />
|
|
133
|
+
<span>{cliCommand}</span>
|
|
134
|
+
</div>
|
|
135
|
+
<Button
|
|
136
|
+
variant="outline"
|
|
137
|
+
size="sm"
|
|
138
|
+
onClick={handleCopyCli}
|
|
139
|
+
className="text-xs flex items-center gap-1"
|
|
140
|
+
>
|
|
141
|
+
{copiedCli ? <Check className="w-3.5 h-3.5 text-emerald-600" /> : <Copy className="w-3.5 h-3.5" />}
|
|
142
|
+
<span>{copiedCli ? '복사됨' : '복사'}</span>
|
|
143
|
+
</Button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Live Interactive Preview Container */}
|
|
148
|
+
<section className="space-y-3 mb-10">
|
|
149
|
+
<div className="flex items-center justify-between">
|
|
150
|
+
<h2 className="text-xs font-semibold uppercase tracking-wider text-(--text-secondary)">
|
|
151
|
+
라이브 인터랙티브 프리뷰
|
|
152
|
+
</h2>
|
|
153
|
+
|
|
154
|
+
{/* Viewport Resizer Controls */}
|
|
155
|
+
<div className="hidden sm:flex items-center gap-0.5 bg-(--bg-secondary) p-0.5 rounded-(--radius-sm) border border-(--border-subtle)">
|
|
156
|
+
<button
|
|
157
|
+
onClick={() => setViewport('desktop')}
|
|
158
|
+
className={`p-1 rounded-(--radius-sm) transition-colors ${
|
|
159
|
+
viewport === 'desktop'
|
|
160
|
+
? 'bg-(--bg-primary) text-(--text-primary)'
|
|
161
|
+
: 'text-(--text-secondary) hover:text-(--text-primary)'
|
|
162
|
+
}`}
|
|
163
|
+
title="Desktop 100%"
|
|
164
|
+
>
|
|
165
|
+
<Monitor className="w-3.5 h-3.5" />
|
|
166
|
+
</button>
|
|
167
|
+
<button
|
|
168
|
+
onClick={() => setViewport('tablet')}
|
|
169
|
+
className={`p-1 rounded-(--radius-sm) transition-colors ${
|
|
170
|
+
viewport === 'tablet'
|
|
171
|
+
? 'bg-(--bg-primary) text-(--text-primary)'
|
|
172
|
+
: 'text-(--text-secondary) hover:text-(--text-primary)'
|
|
173
|
+
}`}
|
|
174
|
+
title="Tablet 768px"
|
|
175
|
+
>
|
|
176
|
+
<Tablet className="w-3.5 h-3.5" />
|
|
177
|
+
</button>
|
|
178
|
+
<button
|
|
179
|
+
onClick={() => setViewport('mobile')}
|
|
180
|
+
className={`p-1 rounded-(--radius-sm) transition-colors ${
|
|
181
|
+
viewport === 'mobile'
|
|
182
|
+
? 'bg-(--bg-primary) text-(--text-primary)'
|
|
183
|
+
: 'text-(--text-secondary) hover:text-(--text-primary)'
|
|
184
|
+
}`}
|
|
185
|
+
title="Mobile 390px"
|
|
186
|
+
>
|
|
187
|
+
<Smartphone className="w-3.5 h-3.5" />
|
|
188
|
+
</button>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
{/* Clean White Preview Box */}
|
|
193
|
+
<div className="p-4 sm:p-6 rounded-(--radius) bg-(--bg-primary) border border-(--border-subtle) flex justify-center items-center">
|
|
194
|
+
<div className={`w-full transition-all duration-300 mx-auto ${getViewportMaxWidth()}`}>
|
|
195
|
+
{renderBlock()}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</section>
|
|
199
|
+
|
|
200
|
+
{/* Lego Assembly Pipeline: Components & Tokens & Template */}
|
|
201
|
+
<section className="space-y-3 mb-10">
|
|
202
|
+
<h2 className="text-xs font-semibold uppercase tracking-wider text-(--text-secondary)">
|
|
203
|
+
레고 조립 구조 (Token → Component → Block → Template)
|
|
204
|
+
</h2>
|
|
205
|
+
|
|
206
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
|
|
207
|
+
{/* 1. Tokens Used */}
|
|
208
|
+
<div className="p-4 rounded-(--radius) border border-(--border-subtle) bg-(--bg-primary) flex flex-col justify-between">
|
|
209
|
+
<div>
|
|
210
|
+
<div className="font-semibold text-(--text-primary) mb-0.5">
|
|
211
|
+
주입된 토큰 (Tokens)
|
|
212
|
+
</div>
|
|
213
|
+
<p className="text-[11px] text-(--text-secondary) mb-2.5">블록의 곡률, 간격, 컬러의 원천</p>
|
|
214
|
+
<div className="flex flex-wrap gap-1">
|
|
215
|
+
{block.tokensUsed.map((token) => (
|
|
216
|
+
<span key={token} className="font-mono text-[11px] px-1.5 py-0.5 rounded-(--radius-sm) bg-(--bg-secondary) text-(--text-secondary)">
|
|
217
|
+
{token}
|
|
218
|
+
</span>
|
|
219
|
+
))}
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{/* 2. Components Used */}
|
|
225
|
+
<div className="p-4 rounded-(--radius) border border-(--border-subtle) bg-(--bg-primary) flex flex-col justify-between">
|
|
226
|
+
<div>
|
|
227
|
+
<div className="font-semibold text-(--text-primary) mb-0.5">
|
|
228
|
+
조합된 컴포넌트 (Components)
|
|
229
|
+
</div>
|
|
230
|
+
<p className="text-[11px] text-(--text-secondary) mb-2.5">블록을 구성하는 기초 레고 피스</p>
|
|
231
|
+
<div className="flex flex-wrap gap-1.5">
|
|
232
|
+
{block.componentsUsed.map((comp) => (
|
|
233
|
+
<Link
|
|
234
|
+
key={comp}
|
|
235
|
+
href={`/components/${comp.toLowerCase()}`}
|
|
236
|
+
className="font-mono text-xs font-medium text-(--text-primary) hover:underline"
|
|
237
|
+
>
|
|
238
|
+
.{comp}
|
|
239
|
+
</Link>
|
|
240
|
+
))}
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
{/* 3. Template Usage */}
|
|
246
|
+
<div className="p-4 rounded-(--radius) border border-(--border-subtle) bg-(--bg-primary) flex flex-col justify-between">
|
|
247
|
+
<div>
|
|
248
|
+
<div className="font-semibold text-(--text-primary) mb-0.5">
|
|
249
|
+
결합 템플릿 (Template)
|
|
250
|
+
</div>
|
|
251
|
+
<p className="text-[11px] text-(--text-secondary) mb-2.5">이 블록이 꽂히는 실제 화면</p>
|
|
252
|
+
<div className="text-xs text-(--text-secondary) leading-relaxed">
|
|
253
|
+
{block.templateUsage}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</section>
|
|
259
|
+
|
|
260
|
+
{/* Features & Key Specs Section */}
|
|
261
|
+
<section className="space-y-3 mb-10">
|
|
262
|
+
<h2 className="text-xs font-semibold uppercase tracking-wider text-(--text-secondary)">
|
|
263
|
+
핵심 설계 기능 및 접근성 (Features & A11y)
|
|
264
|
+
</h2>
|
|
265
|
+
<ul className="space-y-1.5 p-4 rounded-(--radius) border border-(--border-subtle) bg-(--bg-primary) text-xs text-(--text-secondary)">
|
|
266
|
+
{block.features.map((f, i) => (
|
|
267
|
+
<li key={i} className="flex items-start gap-2">
|
|
268
|
+
<span className="text-(--text-muted) select-none">•</span>
|
|
269
|
+
<span className="leading-relaxed">{f}</span>
|
|
270
|
+
</li>
|
|
271
|
+
))}
|
|
272
|
+
</ul>
|
|
273
|
+
</section>
|
|
274
|
+
|
|
275
|
+
{/* Code Snippets Section */}
|
|
276
|
+
<section className="space-y-3 mb-12">
|
|
277
|
+
<div className="flex items-center justify-between">
|
|
278
|
+
<h2 className="text-xs font-semibold uppercase tracking-wider text-(--text-secondary)">
|
|
279
|
+
소스 코드 (Installation & Usage)
|
|
280
|
+
</h2>
|
|
281
|
+
|
|
282
|
+
<div className="flex items-center gap-2">
|
|
283
|
+
<div className="flex rounded-(--radius-sm) border border-(--border-subtle) p-0.5 bg-(--bg-secondary) text-xs">
|
|
284
|
+
<Button
|
|
285
|
+
variant={codeTab === 'react' ? 'primary' : 'ghost'}
|
|
286
|
+
size="sm"
|
|
287
|
+
onClick={() => setCodeTab('react')}
|
|
288
|
+
className="px-2.5 py-1 text-xs"
|
|
289
|
+
>
|
|
290
|
+
React / Next.js
|
|
291
|
+
</Button>
|
|
292
|
+
<Button
|
|
293
|
+
variant={codeTab === 'html' ? 'primary' : 'ghost'}
|
|
294
|
+
size="sm"
|
|
295
|
+
onClick={() => setCodeTab('html')}
|
|
296
|
+
className="px-2.5 py-1 text-xs"
|
|
297
|
+
>
|
|
298
|
+
HTML & CSS
|
|
299
|
+
</Button>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<button
|
|
303
|
+
onClick={handleCopyCode}
|
|
304
|
+
className="flex items-center gap-1 px-2.5 py-1 rounded-(--radius-sm) text-xs font-medium bg-(--bg-primary) hover:bg-(--bg-secondary) text-(--text-primary) border border-(--border-subtle) transition-colors"
|
|
305
|
+
>
|
|
306
|
+
{copiedCode ? (
|
|
307
|
+
<>
|
|
308
|
+
<Check className="w-3.5 h-3.5 text-emerald-600" />
|
|
309
|
+
<span className="text-emerald-600 font-semibold">복사됨</span>
|
|
310
|
+
</>
|
|
311
|
+
) : (
|
|
312
|
+
<>
|
|
313
|
+
<Copy className="w-3.5 h-3.5 text-(--text-secondary)" />
|
|
314
|
+
<span>코드 복사</span>
|
|
315
|
+
</>
|
|
316
|
+
)}
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div className="rounded-(--radius) border border-(--border-subtle) bg-(--bg-primary) overflow-hidden">
|
|
322
|
+
<div className="px-4 py-2 border-b border-(--border-subtle) bg-(--bg-secondary)/40 text-xs font-mono text-(--text-secondary) flex justify-between items-center">
|
|
323
|
+
<span>{codeTab === 'react' ? `${block.id}.tsx` : `${block.id}.html`}</span>
|
|
324
|
+
<span>Zero-Runtime CSS Tokens</span>
|
|
325
|
+
</div>
|
|
326
|
+
<CodeHighlight
|
|
327
|
+
code={codeTab === 'react' ? block.codeSnippets.react : block.codeSnippets.htmlCss}
|
|
328
|
+
language={codeTab === 'react' ? 'tsx' : 'html'}
|
|
329
|
+
className="p-4 max-h-[460px]"
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
</section>
|
|
333
|
+
</main>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
}
|