atomsystem 0.4.1 → 0.4.3

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.
@@ -0,0 +1,336 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import {
5
+ Users,
6
+ HardDrive,
7
+ Cpu,
8
+ ChevronLeft,
9
+ ChevronRight,
10
+ CheckCircle2,
11
+ Sparkles,
12
+ SlidersHorizontal,
13
+ Layers,
14
+ } from 'lucide-react';
15
+
16
+ interface ShowcaseSlide {
17
+ id: number;
18
+ tag: string;
19
+ title: string;
20
+ subtitle: string;
21
+ gradient: string;
22
+ stats: string;
23
+ }
24
+
25
+ const SLIDES: ShowcaseSlide[] = [
26
+ {
27
+ id: 1,
28
+ tag: 'W3C DTCG Engine',
29
+ title: '디자인 토큰 컴파일러',
30
+ subtitle: 'JSON 명세로부터 Tailwind, CSS Variables, SCSS 코드를 0KB 런타임으로 즉시 변환합니다.',
31
+ gradient: 'from-blue-600/20 via-indigo-600/10 to-transparent',
32
+ stats: '빌드 속도 12ms',
33
+ },
34
+ {
35
+ id: 2,
36
+ tag: 'Universal A11y',
37
+ title: 'WCAG 2.1 AAA 고대비 컬러',
38
+ subtitle: '명도 대비 7:1을 항상 충족하는 색상 스케일 매핑을 자동 보증합니다.',
39
+ gradient: 'from-purple-600/20 via-pink-600/10 to-transparent',
40
+ stats: '접근성 통과율 100%',
41
+ },
42
+ {
43
+ id: 3,
44
+ tag: 'Zero-Runtime',
45
+ title: '크로스 프레임워크 호환성',
46
+ subtitle: 'React, Vue, Svelte, Angular, 그리고 순수 바닐라 HTML까지 단 하나의 코드로 동작합니다.',
47
+ gradient: 'from-emerald-600/20 via-teal-600/10 to-transparent',
48
+ stats: '런타임 오버헤드 0KB',
49
+ },
50
+ ];
51
+
52
+ export function SliderShowcaseBlock() {
53
+ const [activeTab, setActiveTab] = useState<'calculator' | 'carousel'>('calculator');
54
+
55
+ // Calculator State
56
+ const [teamMembers, setTeamMembers] = useState<number>(18);
57
+ const [storageGb, setStorageGb] = useState<number>(350);
58
+ const [computeUnits, setComputeUnits] = useState<number>(4);
59
+ const [isAnnualBilling, setIsAnnualBilling] = useState<boolean>(true);
60
+
61
+ // Carousel State
62
+ const [currentSlide, setCurrentSlide] = useState<number>(0);
63
+
64
+ // Dynamic Calculation
65
+ const basePrice = teamMembers * 9 + Math.round(storageGb * 0.12) + computeUnits * 35;
66
+ const discountMultiplier = isAnnualBilling ? 0.8 : 1.0;
67
+ const finalPrice = Math.round(basePrice * discountMultiplier);
68
+ const annualSavings = Math.round(basePrice * 12 * 0.2);
69
+
70
+ const prevSlide = () => {
71
+ setCurrentSlide((prev) => (prev === 0 ? SLIDES.length - 1 : prev - 1));
72
+ };
73
+
74
+ const nextSlide = () => {
75
+ setCurrentSlide((prev) => (prev === SLIDES.length - 1 ? 0 : prev + 1));
76
+ };
77
+
78
+ return (
79
+ <div className="w-full bg-(--bg-primary) rounded-2xl border border-(--border-subtle) shadow-sm p-5 sm:p-6 transition-colors">
80
+ {/* Header and Switcher */}
81
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-5 border-b border-(--border-subtle)">
82
+ <div>
83
+ <span className="text-[11px] font-semibold text-(--accent-primary) uppercase tracking-wider">
84
+ 인터랙티브 슬라이더 모듈
85
+ </span>
86
+ <h3 className="text-lg font-bold text-(--text-primary) mt-0.5">
87
+ {activeTab === 'calculator' ? '실시간 사용량 & 비용 견적 시뮬레이터' : '피처 하이라이트 인터랙티브 캐러셀'}
88
+ </h3>
89
+ </div>
90
+
91
+ <div className="flex bg-(--bg-secondary) p-1 rounded-xl border border-(--border-subtle) text-xs">
92
+ <button
93
+ onClick={() => setActiveTab('calculator')}
94
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg font-semibold transition-all ${
95
+ activeTab === 'calculator'
96
+ ? 'bg-(--bg-primary) text-(--text-primary) shadow-xs'
97
+ : 'text-(--text-secondary) hover:text-(--text-primary)'
98
+ }`}
99
+ >
100
+ <SlidersHorizontal className="w-3.5 h-3.5" />
101
+ <span>비용 슬라이더</span>
102
+ </button>
103
+ <button
104
+ onClick={() => setActiveTab('carousel')}
105
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg font-semibold transition-all ${
106
+ activeTab === 'carousel'
107
+ ? 'bg-(--bg-primary) text-(--text-primary) shadow-xs'
108
+ : 'text-(--text-secondary) hover:text-(--text-primary)'
109
+ }`}
110
+ >
111
+ <Layers className="w-3.5 h-3.5" />
112
+ <span>쇼케이스 캐러셀</span>
113
+ </button>
114
+ </div>
115
+ </div>
116
+
117
+ {activeTab === 'calculator' ? (
118
+ /* Tab 1: Interactive Cost Calculator Slider */
119
+ <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 pt-6 items-center">
120
+ {/* Left: Sliders Controls (7 Cols) */}
121
+ <div className="lg:col-span-7 space-y-6">
122
+ {/* Slider 1: Team Members */}
123
+ <div className="space-y-2">
124
+ <div className="flex justify-between items-center text-xs">
125
+ <span className="font-semibold text-(--text-primary) flex items-center gap-1.5">
126
+ <Users className="w-4 h-4 text-(--accent-primary)" />
127
+ 활성 팀원 수 (Team Members)
128
+ </span>
129
+ <span className="font-mono font-bold text-sm text-(--accent-primary) bg-(--accent-primary)/10 px-2 py-0.5 rounded-md">
130
+ {teamMembers} 명
131
+ </span>
132
+ </div>
133
+ <input
134
+ type="range"
135
+ min="1"
136
+ max="100"
137
+ value={teamMembers}
138
+ onChange={(e) => setTeamMembers(Number(e.target.value))}
139
+ className="w-full h-2 bg-(--bg-secondary) rounded-lg appearance-none cursor-pointer accent-(--accent-primary)"
140
+ />
141
+ <div className="flex justify-between text-[10px] text-(--text-muted)">
142
+ <span>1명 (Solo)</span>
143
+ <span>50명 (Team)</span>
144
+ <span>100명 (Enterprise)</span>
145
+ </div>
146
+ </div>
147
+
148
+ {/* Slider 2: Storage */}
149
+ <div className="space-y-2">
150
+ <div className="flex justify-between items-center text-xs">
151
+ <span className="font-semibold text-(--text-primary) flex items-center gap-1.5">
152
+ <HardDrive className="w-4 h-4 text-emerald-500" />
153
+ 클라우드 스토리지 용량 (Storage)
154
+ </span>
155
+ <span className="font-mono font-bold text-sm text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded-md">
156
+ {storageGb} GB
157
+ </span>
158
+ </div>
159
+ <input
160
+ type="range"
161
+ min="10"
162
+ max="1000"
163
+ step="10"
164
+ value={storageGb}
165
+ onChange={(e) => setStorageGb(Number(e.target.value))}
166
+ className="w-full h-2 bg-(--bg-secondary) rounded-lg appearance-none cursor-pointer accent-emerald-500"
167
+ />
168
+ <div className="flex justify-between text-[10px] text-(--text-muted)">
169
+ <span>10 GB</span>
170
+ <span>500 GB</span>
171
+ <span>1,000 GB (1TB)</span>
172
+ </div>
173
+ </div>
174
+
175
+ {/* Slider 3: Compute */}
176
+ <div className="space-y-2">
177
+ <div className="flex justify-between items-center text-xs">
178
+ <span className="font-semibold text-(--text-primary) flex items-center gap-1.5">
179
+ <Cpu className="w-4 h-4 text-purple-500" />
180
+ 병렬 빌드 & 연산 인스턴스 (Compute Units)
181
+ </span>
182
+ <span className="font-mono font-bold text-sm text-purple-600 dark:text-purple-400 bg-purple-500/10 px-2 py-0.5 rounded-md">
183
+ {computeUnits} CU
184
+ </span>
185
+ </div>
186
+ <input
187
+ type="range"
188
+ min="1"
189
+ max="16"
190
+ step="1"
191
+ value={computeUnits}
192
+ onChange={(e) => setComputeUnits(Number(e.target.value))}
193
+ className="w-full h-2 bg-(--bg-secondary) rounded-lg appearance-none cursor-pointer accent-purple-500"
194
+ />
195
+ <div className="flex justify-between text-[10px] text-(--text-muted)">
196
+ <span>1 CU</span>
197
+ <span>8 CU</span>
198
+ <span>16 CU (Ultra)</span>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ {/* Right: Price Receipt Card (5 Cols) */}
204
+ <div className="lg:col-span-5 p-5 rounded-2xl bg-(--bg-secondary) border border-(--border-strong) flex flex-col justify-between space-y-4">
205
+ <div>
206
+ <div className="flex items-center justify-between mb-3">
207
+ <span className="text-xs font-semibold text-(--text-secondary)">결제 주기 선택</span>
208
+ <div className="flex items-center gap-2">
209
+ <span className={`text-[11px] ${!isAnnualBilling ? 'font-bold text-(--text-primary)' : 'text-(--text-muted)'}`}>월간</span>
210
+ <button
211
+ onClick={() => setIsAnnualBilling(!isAnnualBilling)}
212
+ aria-label="결제 주기 토글"
213
+ className="w-10 h-5 rounded-full bg-(--accent-primary) p-0.5 flex items-center transition-colors"
214
+ >
215
+ <div
216
+ className={`w-4 h-4 rounded-full bg-white transition-transform ${
217
+ isAnnualBilling ? 'translate-x-5' : 'translate-x-0'
218
+ }`}
219
+ />
220
+ </button>
221
+ <span className={`text-[11px] ${isAnnualBilling ? 'font-bold text-(--accent-primary)' : 'text-(--text-muted)'}`}>연간 (-20%)</span>
222
+ </div>
223
+ </div>
224
+
225
+ <div className="space-y-2 py-3 border-y border-(--border-subtle) text-xs">
226
+ <div className="flex justify-between text-(--text-secondary)">
227
+ <span>팀원 라이선스 ({teamMembers}명)</span>
228
+ <span className="font-medium text-(--text-primary)">₩{(teamMembers * 12000).toLocaleString()}</span>
229
+ </div>
230
+ <div className="flex justify-between text-(--text-secondary)">
231
+ <span>스토리지 ({storageGb}GB)</span>
232
+ <span className="font-medium text-(--text-primary)">₩{(storageGb * 150).toLocaleString()}</span>
233
+ </div>
234
+ <div className="flex justify-between text-(--text-secondary)">
235
+ <span>연산 인스턴스 ({computeUnits}CU)</span>
236
+ <span className="font-medium text-(--text-primary)">₩{(computeUnits * 45000).toLocaleString()}</span>
237
+ </div>
238
+ {isAnnualBilling && (
239
+ <div className="flex justify-between text-emerald-600 dark:text-emerald-400 font-semibold">
240
+ <span>연간 결제 프로모션 할인</span>
241
+ <span>-20% 적용</span>
242
+ </div>
243
+ )}
244
+ </div>
245
+ </div>
246
+
247
+ <div>
248
+ <div className="flex items-baseline justify-between mb-3">
249
+ <span className="text-xs text-(--text-muted)">월 예상 결제 금액</span>
250
+ <div className="text-right">
251
+ <span className="text-2xl sm:text-3xl font-black text-(--text-primary)">
252
+ \${finalPrice}
253
+ </span>
254
+ <span className="text-xs text-(--text-muted)"> /월</span>
255
+ </div>
256
+ </div>
257
+
258
+ {isAnnualBilling && (
259
+ <div className="text-[11px] text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 px-3 py-1.5 rounded-lg mb-3 flex items-center justify-between">
260
+ <span>연간 결제 시 연간 절감액</span>
261
+ <span className="font-bold">\${annualSavings} 절약</span>
262
+ </div>
263
+ )}
264
+
265
+ <button className="w-full py-2.5 rounded-xl bg-(--accent-primary) hover:bg-(--accent-hover) text-white font-semibold text-xs shadow-xs transition-colors">
266
+ 이 견적으로 즉시 시작하기
267
+ </button>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ ) : (
272
+ /* Tab 2: Carousel Showcase */
273
+ <div className="pt-6">
274
+ <div className="relative overflow-hidden rounded-2xl border border-(--border-subtle) bg-(--bg-secondary)/40 p-6 sm:p-10 min-h-[280px] flex flex-col justify-between">
275
+ {/* Background gradient decorative */}
276
+ <div
277
+ className={`absolute inset-0 bg-gradient-to-br ${SLIDES[currentSlide].gradient} pointer-events-none transition-all duration-500`}
278
+ />
279
+
280
+ <div className="relative z-10 max-w-xl">
281
+ <span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold bg-(--accent-primary)/15 text-(--accent-primary) mb-3">
282
+ <Sparkles className="w-3.5 h-3.5" />
283
+ {SLIDES[currentSlide].tag}
284
+ </span>
285
+ <h3 className="text-xl sm:text-2xl font-extrabold text-(--text-primary) tracking-tight mb-2">
286
+ {SLIDES[currentSlide].title}
287
+ </h3>
288
+ <p className="text-xs sm:text-sm text-(--text-secondary) leading-relaxed">
289
+ {SLIDES[currentSlide].subtitle}
290
+ </p>
291
+ </div>
292
+
293
+ <div className="relative z-10 flex flex-col sm:flex-row sm:items-center justify-between gap-4 pt-6 border-t border-(--border-subtle) mt-6">
294
+ <div className="flex items-center gap-2 text-xs font-semibold text-(--text-primary)">
295
+ <CheckCircle2 className="w-4 h-4 text-emerald-500" />
296
+ <span>검증 메트릭: {SLIDES[currentSlide].stats}</span>
297
+ </div>
298
+
299
+ {/* Navigation Controls */}
300
+ <div className="flex items-center gap-2">
301
+ {/* Dots */}
302
+ <div className="flex items-center gap-1.5 mr-2">
303
+ {SLIDES.map((_, i) => (
304
+ <button
305
+ key={i}
306
+ onClick={() => setCurrentSlide(i)}
307
+ aria-label={`슬라이드 ${i + 1}로 이동`}
308
+ className={`h-1.5 rounded-full transition-all ${
309
+ currentSlide === i ? 'w-6 bg-(--accent-primary)' : 'w-2 bg-(--border-strong)'
310
+ }`}
311
+ />
312
+ ))}
313
+ </div>
314
+
315
+ <button
316
+ onClick={prevSlide}
317
+ aria-label="이전 슬라이드"
318
+ className="p-2 rounded-lg bg-(--bg-primary) hover:bg-(--border-subtle) text-(--text-primary) border border-(--border-subtle) transition-colors"
319
+ >
320
+ <ChevronLeft className="w-4 h-4" />
321
+ </button>
322
+ <button
323
+ onClick={nextSlide}
324
+ aria-label="다음 슬라이드"
325
+ className="p-2 rounded-lg bg-(--bg-primary) hover:bg-(--border-subtle) text-(--text-primary) border border-(--border-subtle) transition-colors"
326
+ >
327
+ <ChevronRight className="w-4 h-4" />
328
+ </button>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+ )}
334
+ </div>
335
+ );
336
+ }
@@ -0,0 +1,8 @@
1
+ export * from './RealtimeChartBlock';
2
+ export * from './InteractiveChatBlock';
3
+ export * from './CardListBlock';
4
+ export * from './SliderShowcaseBlock';
5
+ export * from './PricingPlanBlock';
6
+ export * from './MetricStatsBlock';
7
+ export * from './FileExplorerBlock';
8
+ export * from './BlockViewerCard';
@@ -0,0 +1,121 @@
1
+ import React, { useState } from 'react';
2
+
3
+ export interface TreeNode {
4
+ id: string;
5
+ label: string;
6
+ icon?: React.ReactNode;
7
+ children?: TreeNode[];
8
+ disabled?: boolean;
9
+ }
10
+
11
+ export interface TreeProps {
12
+ data: TreeNode[];
13
+ selectedId?: string;
14
+ onSelect?: (node: TreeNode) => void;
15
+ defaultExpandedIds?: string[];
16
+ className?: string;
17
+ }
18
+
19
+ export const Tree: React.FC<TreeProps> = ({
20
+ data,
21
+ selectedId,
22
+ onSelect,
23
+ defaultExpandedIds = [],
24
+ className = '',
25
+ }) => {
26
+ const [expandedIds, setExpandedIds] = useState<string[]>(defaultExpandedIds);
27
+ const [internalSelectedId, setInternalSelectedId] = useState<string | undefined>(selectedId);
28
+
29
+ const activeSelectedId = selectedId !== undefined ? selectedId : internalSelectedId;
30
+
31
+ const toggleExpand = (id: string, e: React.MouseEvent) => {
32
+ e.stopPropagation();
33
+ setExpandedIds((prev) =>
34
+ prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
35
+ );
36
+ };
37
+
38
+ const handleSelect = (node: TreeNode) => {
39
+ if (node.disabled) return;
40
+ setInternalSelectedId(node.id);
41
+ if (onSelect) {
42
+ onSelect(node);
43
+ }
44
+ };
45
+
46
+ const renderNodes = (nodes: TreeNode[]) => {
47
+ return (
48
+ <ul className="atom-tree-list" role="group">
49
+ {nodes.map((node) => {
50
+ const hasChildren = Boolean(node.children && node.children.length > 0);
51
+ const isExpanded = expandedIds.includes(node.id);
52
+ const isSelected = activeSelectedId === node.id;
53
+
54
+ return (
55
+ <li
56
+ key={node.id}
57
+ className="atom-tree-item"
58
+ role="treeitem"
59
+ aria-expanded={hasChildren ? isExpanded : undefined}
60
+ aria-selected={isSelected}
61
+ aria-disabled={node.disabled}
62
+ >
63
+ <div
64
+ className={`atom-tree-node ${isSelected ? 'is-selected' : ''} ${
65
+ node.disabled ? 'is-disabled' : ''
66
+ }`}
67
+ onClick={() => {
68
+ if (hasChildren && !isExpanded) {
69
+ setExpandedIds((prev) => [...prev, node.id]);
70
+ }
71
+ handleSelect(node);
72
+ }}
73
+ tabIndex={node.disabled ? -1 : 0}
74
+ onKeyDown={(e) => {
75
+ if (e.key === 'Enter' || e.key === ' ') {
76
+ e.preventDefault();
77
+ if (hasChildren) {
78
+ setExpandedIds((prev) =>
79
+ prev.includes(node.id)
80
+ ? prev.filter((item) => item !== node.id)
81
+ : [...prev, node.id]
82
+ );
83
+ }
84
+ handleSelect(node);
85
+ }
86
+ }}
87
+ >
88
+ {hasChildren ? (
89
+ <button
90
+ type="button"
91
+ className={`atom-tree-toggle ${isExpanded ? 'is-expanded' : ''}`}
92
+ onClick={(e) => toggleExpand(node.id, e)}
93
+ aria-label={isExpanded ? '접기' : '펼치기'}
94
+ tabIndex={-1}
95
+ >
96
+ ▶
97
+ </button>
98
+ ) : (
99
+ <span className="atom-tree-toggle-spacer" aria-hidden="true" />
100
+ )}
101
+
102
+ {node.icon && <span className="atom-tree-icon">{node.icon}</span>}
103
+ <span className="atom-tree-label">{node.label}</span>
104
+ </div>
105
+
106
+ {hasChildren && isExpanded && (
107
+ <div className="atom-tree-children">{renderNodes(node.children!)}</div>
108
+ )}
109
+ </li>
110
+ );
111
+ })}
112
+ </ul>
113
+ );
114
+ };
115
+
116
+ return (
117
+ <div className={`atom-tree ${className}`} role="tree">
118
+ {renderNodes(data)}
119
+ </div>
120
+ );
121
+ };
@@ -16,3 +16,4 @@ export * from './Skeleton';
16
16
  export * from './Breadcrumb';
17
17
  export * from './Separator';
18
18
  export * from './Textarea';
19
+ export * from './Tree';
package/css/atom-ui.css CHANGED
@@ -466,13 +466,16 @@
466
466
  color: var(--atom-text-primary);
467
467
  }
468
468
  .atom-toast-success {
469
- border-left: 4px solid #10b981;
469
+ border-color: rgba(16, 185, 129, 0.35);
470
+ box-shadow: 0 4px 20px rgba(16, 185, 129, 0.08);
470
471
  }
471
472
  .atom-toast-error {
472
- border-left: 4px solid #f43f5e;
473
+ border-color: rgba(244, 63, 94, 0.35);
474
+ box-shadow: 0 4px 20px rgba(244, 63, 94, 0.08);
473
475
  }
474
476
  .atom-toast-info {
475
- border-left: 4px solid var(--atom-primary);
477
+ border-color: rgba(49, 130, 246, 0.35);
478
+ box-shadow: 0 4px 20px rgba(49, 130, 246, 0.08);
476
479
  }
477
480
 
478
481
  /* Pagination */
@@ -724,3 +727,100 @@
724
727
  font-size: 0.75rem;
725
728
  color: var(--atom-text-muted);
726
729
  }
730
+
731
+ /* Tree Component */
732
+ .atom-tree {
733
+ list-style: none;
734
+ padding: 0;
735
+ margin: 0;
736
+ font-size: 0.8125rem;
737
+ color: var(--atom-text-primary);
738
+ user-select: none;
739
+ }
740
+ .atom-tree-list {
741
+ list-style: none;
742
+ padding: 0;
743
+ margin: 0;
744
+ }
745
+ .atom-tree-item {
746
+ position: relative;
747
+ }
748
+ .atom-tree-node {
749
+ display: flex;
750
+ align-items: center;
751
+ gap: 0.375rem;
752
+ width: 100%;
753
+ padding: 0.35rem 0.5rem;
754
+ border-radius: var(--atom-radius-sm);
755
+ border: 1px solid transparent;
756
+ background: transparent;
757
+ color: inherit;
758
+ font-size: inherit;
759
+ text-align: left;
760
+ cursor: pointer;
761
+ transition: background-color 0.15s ease, color 0.15s ease;
762
+ }
763
+ .atom-tree-node:hover {
764
+ background-color: var(--atom-bg-secondary);
765
+ }
766
+ .atom-tree-node:focus-visible {
767
+ outline: 2px solid var(--atom-primary);
768
+ outline-offset: -1px;
769
+ }
770
+ .atom-tree-node.is-selected {
771
+ background-color: var(--atom-primary-light);
772
+ color: var(--atom-primary);
773
+ font-weight: 600;
774
+ }
775
+ .atom-tree-node.is-disabled {
776
+ opacity: 0.5;
777
+ cursor: not-allowed;
778
+ pointer-events: none;
779
+ }
780
+ .atom-tree-toggle {
781
+ display: inline-flex;
782
+ align-items: center;
783
+ justify-content: center;
784
+ width: 1rem;
785
+ height: 1rem;
786
+ padding: 0;
787
+ border: none;
788
+ background: transparent;
789
+ color: var(--atom-text-muted);
790
+ cursor: pointer;
791
+ font-size: 0.75rem;
792
+ transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), color 0.15s ease;
793
+ shrink: 0;
794
+ }
795
+ .atom-tree-toggle:hover {
796
+ color: var(--atom-text-primary);
797
+ }
798
+ .atom-tree-toggle.is-expanded {
799
+ transform: rotate(90deg);
800
+ }
801
+ .atom-tree-toggle-spacer {
802
+ width: 1rem;
803
+ height: 1rem;
804
+ shrink: 0;
805
+ }
806
+ .atom-tree-icon {
807
+ display: inline-flex;
808
+ align-items: center;
809
+ justify-content: center;
810
+ width: 1rem;
811
+ height: 1rem;
812
+ color: var(--atom-text-muted);
813
+ shrink: 0;
814
+ }
815
+ .atom-tree-label {
816
+ flex: 1;
817
+ min-width: 0;
818
+ overflow: hidden;
819
+ text-overflow: ellipsis;
820
+ white-space: nowrap;
821
+ }
822
+ .atom-tree-children {
823
+ padding-left: 1.25rem;
824
+ border-left: 1px dashed var(--atom-border-subtle);
825
+ margin-left: 0.5rem;
826
+ }
package/dist/cli.cjs CHANGED
@@ -82,7 +82,7 @@ function showVersion() {
82
82
  const pkg = JSON.parse(fs__default.default.readFileSync(pkgPath, "utf-8"));
83
83
  console.log(`atomsystem v${pkg.version}`);
84
84
  } else {
85
- console.log("atomsystem v0.4.1");
85
+ console.log("atomsystem v0.4.3");
86
86
  }
87
87
  }
88
88
  switch (command) {
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"names":["path","fileURLToPath","fs"],"mappings":"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","file":"cli.cjs","sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nfunction getPkgRoot(): string {\n try {\n if (typeof __dirname !== 'undefined') {\n return path.resolve(__dirname, '..');\n }\n const currentFile = fileURLToPath(import.meta.url);\n return path.resolve(path.dirname(currentFile), '..');\n } catch {\n return process.cwd();\n }\n}\n\nconst PKG_ROOT = getPkgRoot();\nconst args = process.argv.slice(2);\nconst command = args[0] || 'help';\n\nfunction copyFileSafe(src: string, dest: string, label: string) {\n const targetDir = path.dirname(dest);\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.copyFileSync(src, dest);\n console.log(` ✓ Created ${label} -> ${path.relative(process.cwd(), dest)}`);\n}\n\nfunction handleInit() {\n const cwd = process.cwd();\n console.log('\\n🚀 Initializing AtomSystem in your project...\\n');\n\n // 1. Install AI Agent Skill\n const srcSkill = path.join(PKG_ROOT, 'skills', 'atomsystem.skill.md');\n const destSkill = path.join(cwd, '.agents', 'skills', 'atomsystem', 'SKILL.md');\n if (fs.existsSync(srcSkill)) {\n copyFileSafe(srcSkill, destSkill, 'AI Skill (.agents/skills/atomsystem/SKILL.md)');\n }\n\n // 2. Install Design Guide (.design.md)\n const srcDesign = path.join(PKG_ROOT, 'presets', 'default.design.md');\n const destDesign = path.join(cwd, 'atom.design.md');\n if (fs.existsSync(srcDesign)) {\n copyFileSafe(srcDesign, destDesign, 'Design Guide (atom.design.md)');\n }\n\n // 3. Install UX Pattern Guide (.uxPattern.md)\n const srcPattern = path.join(PKG_ROOT, 'presets', 'uxPattern.md');\n const destPattern = path.join(cwd, 'atom.uxPattern.md');\n if (fs.existsSync(srcPattern)) {\n copyFileSafe(srcPattern, destPattern, 'UX Pattern Guide (atom.uxPattern.md)');\n }\n\n // 4. Install Token JSON (tokens.json)\n const srcToken = path.join(PKG_ROOT, 'tokens', 'token.json');\n const destToken = path.join(cwd, 'tokens', 'token.json');\n if (fs.existsSync(srcToken)) {\n copyFileSafe(srcToken, destToken, 'Token JSON (tokens/token.json)');\n }\n\n console.log('\\n✨ AtomSystem initialization completed!');\n console.log('\\n📌 Next Steps:');\n console.log('1. Import AtomSystem CSS in your app (e.g. layout.tsx, App.vue, index.html):');\n console.log(' import \"atomsystem/css/atom-ui.css\";');\n console.log('2. Ask your AI coding assistant (Antigravity, Cursor, Copilot) to build UI:');\n console.log(' \"atom.design.md와 atom.uxPattern.md를 참고하여 결제 화면 컴포넌트를 만들어줘.\"\\n');\n}\n\nfunction showHelp() {\n console.log(`\nAtomSystem CLI - Universal Design Token & Agentic UI System\n\nUsage:\n npx atomsystem <command> [options]\n\nCommands:\n init Initialize AtomSystem assets (AI Skill, design.md, uxPattern.md, token.json)\n help Display this help message\n version Print version info\n`);\n}\n\nfunction showVersion() {\n const pkgPath = path.join(PKG_ROOT, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n console.log(`atomsystem v${pkg.version}`);\n } else {\n console.log('atomsystem v0.4.1');\n }\n}\n\nswitch (command) {\n case 'init':\n handleInit();\n break;\n case 'version':\n case '-v':\n case '--version':\n showVersion();\n break;\n case 'help':\n case '-h':\n case '--help':\n default:\n showHelp();\n break;\n}\n\n"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"names":["path","fileURLToPath","fs"],"mappings":"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","file":"cli.cjs","sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nfunction getPkgRoot(): string {\n try {\n if (typeof __dirname !== 'undefined') {\n return path.resolve(__dirname, '..');\n }\n const currentFile = fileURLToPath(import.meta.url);\n return path.resolve(path.dirname(currentFile), '..');\n } catch {\n return process.cwd();\n }\n}\n\nconst PKG_ROOT = getPkgRoot();\nconst args = process.argv.slice(2);\nconst command = args[0] || 'help';\n\nfunction copyFileSafe(src: string, dest: string, label: string) {\n const targetDir = path.dirname(dest);\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.copyFileSync(src, dest);\n console.log(` ✓ Created ${label} -> ${path.relative(process.cwd(), dest)}`);\n}\n\nfunction handleInit() {\n const cwd = process.cwd();\n console.log('\\n🚀 Initializing AtomSystem in your project...\\n');\n\n // 1. Install AI Agent Skill\n const srcSkill = path.join(PKG_ROOT, 'skills', 'atomsystem.skill.md');\n const destSkill = path.join(cwd, '.agents', 'skills', 'atomsystem', 'SKILL.md');\n if (fs.existsSync(srcSkill)) {\n copyFileSafe(srcSkill, destSkill, 'AI Skill (.agents/skills/atomsystem/SKILL.md)');\n }\n\n // 2. Install Design Guide (.design.md)\n const srcDesign = path.join(PKG_ROOT, 'presets', 'default.design.md');\n const destDesign = path.join(cwd, 'atom.design.md');\n if (fs.existsSync(srcDesign)) {\n copyFileSafe(srcDesign, destDesign, 'Design Guide (atom.design.md)');\n }\n\n // 3. Install UX Pattern Guide (.uxPattern.md)\n const srcPattern = path.join(PKG_ROOT, 'presets', 'uxPattern.md');\n const destPattern = path.join(cwd, 'atom.uxPattern.md');\n if (fs.existsSync(srcPattern)) {\n copyFileSafe(srcPattern, destPattern, 'UX Pattern Guide (atom.uxPattern.md)');\n }\n\n // 4. Install Token JSON (tokens.json)\n const srcToken = path.join(PKG_ROOT, 'tokens', 'token.json');\n const destToken = path.join(cwd, 'tokens', 'token.json');\n if (fs.existsSync(srcToken)) {\n copyFileSafe(srcToken, destToken, 'Token JSON (tokens/token.json)');\n }\n\n console.log('\\n✨ AtomSystem initialization completed!');\n console.log('\\n📌 Next Steps:');\n console.log('1. Import AtomSystem CSS in your app (e.g. layout.tsx, App.vue, index.html):');\n console.log(' import \"atomsystem/css/atom-ui.css\";');\n console.log('2. Ask your AI coding assistant (Antigravity, Cursor, Copilot) to build UI:');\n console.log(' \"atom.design.md와 atom.uxPattern.md를 참고하여 결제 화면 컴포넌트를 만들어줘.\"\\n');\n}\n\nfunction showHelp() {\n console.log(`\nAtomSystem CLI - Universal Design Token & Agentic UI System\n\nUsage:\n npx atomsystem <command> [options]\n\nCommands:\n init Initialize AtomSystem assets (AI Skill, design.md, uxPattern.md, token.json)\n help Display this help message\n version Print version info\n`);\n}\n\nfunction showVersion() {\n const pkgPath = path.join(PKG_ROOT, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n console.log(`atomsystem v${pkg.version}`);\n } else {\n console.log('atomsystem v0.4.3');\n }\n}\n\nswitch (command) {\n case 'init':\n handleInit();\n break;\n case 'version':\n case '-v':\n case '--version':\n showVersion();\n break;\n case 'help':\n case '-h':\n case '--help':\n default:\n showHelp();\n break;\n}\n\n"]}
package/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ function showVersion() {
74
74
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
75
75
  console.log(`atomsystem v${pkg.version}`);
76
76
  } else {
77
- console.log("atomsystem v0.4.1");
77
+ console.log("atomsystem v0.4.3");
78
78
  }
79
79
  }
80
80
  switch (command) {