atomsystem 0.4.0 → 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
@@ -191,19 +191,46 @@
191
191
  border-color: transparent;
192
192
  }
193
193
 
194
- /* Typography Helpers */
195
- .atom-title-1 {
196
- font-size: 1.5rem;
194
+ /* Typography & Heading Scale */
195
+ .atom-h1, .atom-title-1 {
196
+ font-size: 1.625rem;
197
197
  font-weight: 700;
198
198
  line-height: 1.25;
199
+ letter-spacing: -0.025em;
199
200
  color: var(--atom-text-primary);
200
201
  }
201
- .atom-title-2 {
202
+ .atom-h2, .atom-title-2 {
202
203
  font-size: 1.25rem;
203
204
  font-weight: 600;
205
+ line-height: 1.3;
206
+ letter-spacing: -0.02em;
207
+ color: var(--atom-text-primary);
208
+ }
209
+ .atom-h3, .atom-title-3 {
210
+ font-size: 1.0625rem;
211
+ font-weight: 600;
204
212
  line-height: 1.35;
213
+ letter-spacing: -0.015em;
214
+ color: var(--atom-text-primary);
215
+ }
216
+ .atom-h4 {
217
+ font-size: 0.9375rem;
218
+ font-weight: 600;
219
+ line-height: 1.4;
205
220
  color: var(--atom-text-primary);
206
221
  }
222
+ .atom-h5 {
223
+ font-size: 0.875rem;
224
+ font-weight: 600;
225
+ line-height: 1.45;
226
+ color: var(--atom-text-primary);
227
+ }
228
+ .atom-h6 {
229
+ font-size: 0.75rem;
230
+ font-weight: 600;
231
+ line-height: 1.5;
232
+ color: var(--atom-text-secondary);
233
+ }
207
234
  .atom-body-1 {
208
235
  font-size: 1rem;
209
236
  line-height: 1.5;
@@ -439,13 +466,16 @@
439
466
  color: var(--atom-text-primary);
440
467
  }
441
468
  .atom-toast-success {
442
- 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);
443
471
  }
444
472
  .atom-toast-error {
445
- 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);
446
475
  }
447
476
  .atom-toast-info {
448
- 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);
449
479
  }
450
480
 
451
481
  /* Pagination */
@@ -697,3 +727,100 @@
697
727
  font-size: 0.75rem;
698
728
  color: var(--atom-text-muted);
699
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.0");
85
+ console.log("atomsystem v0.4.3");
86
86
  }
87
87
  }
88
88
  switch (command) {