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/dist/cli.js CHANGED
@@ -1,15 +1,438 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'fs';
3
- import path from 'path';
2
+ import fs2 from 'fs';
3
+ import path2 from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import http from 'http';
5
6
 
7
+ // src/tokenCompiler.ts
8
+ function compileTokensToCss(tokenJson) {
9
+ const cssVars = [];
10
+ function traverse(obj) {
11
+ if (!obj || typeof obj !== "object") return;
12
+ if (obj.variable && obj.$value !== void 0) {
13
+ cssVars.push(` ${obj.variable}: ${obj.$value};`);
14
+ return;
15
+ }
16
+ for (const key of Object.keys(obj)) {
17
+ if (key.startsWith("$")) continue;
18
+ traverse(obj[key]);
19
+ }
20
+ }
21
+ traverse(tokenJson);
22
+ return `:root {
23
+ ${cssVars.join("\n")}
24
+ }
25
+ `;
26
+ }
27
+
28
+ // src/studioServer.ts
29
+ function startLocalTokenStudio(options = {}) {
30
+ const port = options.port || 4e3;
31
+ const cwd = options.cwd || process.cwd();
32
+ const tokenJsonPath = path2.join(cwd, "tokens", "token.json");
33
+ const cssPath = path2.join(cwd, "css", "atom-ui.css");
34
+ path2.join(cwd, "atom.design.md");
35
+ const server = http.createServer((req, res) => {
36
+ res.setHeader("Access-Control-Allow-Origin", "*");
37
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
38
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
39
+ if (req.method === "OPTIONS") {
40
+ res.writeHead(200);
41
+ res.end();
42
+ return;
43
+ }
44
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
45
+ if (url.pathname === "/api/tokens" && req.method === "GET") {
46
+ let data = {};
47
+ if (fs2.existsSync(tokenJsonPath)) {
48
+ try {
49
+ data = JSON.parse(fs2.readFileSync(tokenJsonPath, "utf-8"));
50
+ } catch (e) {
51
+ data = { error: "Failed to parse tokens/token.json" };
52
+ }
53
+ }
54
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
55
+ res.end(JSON.stringify(data));
56
+ return;
57
+ }
58
+ if (url.pathname === "/api/tokens" && req.method === "POST") {
59
+ let body = "";
60
+ req.on("data", (chunk) => {
61
+ body += chunk;
62
+ });
63
+ req.on("end", () => {
64
+ try {
65
+ const newTokens = JSON.parse(body);
66
+ const tokensDir = path2.dirname(tokenJsonPath);
67
+ if (!fs2.existsSync(tokensDir)) fs2.mkdirSync(tokensDir, { recursive: true });
68
+ fs2.writeFileSync(tokenJsonPath, JSON.stringify(newTokens, null, 2), "utf-8");
69
+ const compiledCss = compileTokensToCss(newTokens);
70
+ const cssDir = path2.dirname(cssPath);
71
+ if (!fs2.existsSync(cssDir)) fs2.mkdirSync(cssDir, { recursive: true });
72
+ fs2.writeFileSync(cssPath, compiledCss, "utf-8");
73
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
74
+ res.end(JSON.stringify({ success: true, message: "Tokens and CSS compiled successfully!" }));
75
+ } catch (err) {
76
+ res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
77
+ res.end(JSON.stringify({ success: false, error: err.message }));
78
+ }
79
+ });
80
+ return;
81
+ }
82
+ if (url.pathname === "/" || url.pathname === "/index.html") {
83
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
84
+ res.end(getStudioHtml());
85
+ return;
86
+ }
87
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
88
+ res.end("Not Found");
89
+ });
90
+ server.listen(port, () => {
91
+ console.log(`
92
+ \u{1F3A8} AtomSystem Local Token Studio running at:`);
93
+ console.log(` \u{1F449} http://localhost:${port}
94
+ `);
95
+ console.log(` Tokens file: ${path2.relative(cwd, tokenJsonPath)}`);
96
+ console.log(` Compiled CSS: ${path2.relative(cwd, cssPath)}`);
97
+ console.log(`
98
+ Press Ctrl+C to stop the studio server.
99
+ `);
100
+ });
101
+ return server;
102
+ }
103
+ function getStudioHtml() {
104
+ return `<!DOCTYPE html>
105
+ <html lang="ko">
106
+ <head>
107
+ <meta charset="UTF-8">
108
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
109
+ <title>AtomSystem Local Token Studio</title>
110
+ <style>
111
+ :root {
112
+ --bg: #0a0a0a;
113
+ --card-bg: #141414;
114
+ --border: #262626;
115
+ --text: #ededed;
116
+ --muted: #888888;
117
+ --accent: #3182f6;
118
+ --radius: 8px;
119
+ }
120
+ * { box-sizing: border-box; margin: 0; padding: 0; }
121
+ body {
122
+ background: var(--bg);
123
+ color: var(--text);
124
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Pretendard, sans-serif;
125
+ padding: 32px 20px;
126
+ line-height: 1.5;
127
+ }
128
+ .container { max-width: 1100px; margin: 0 auto; }
129
+ header {
130
+ display: flex;
131
+ justify-content: space-between;
132
+ align-items: flex-end;
133
+ padding-bottom: 24px;
134
+ border-bottom: 1px solid var(--border);
135
+ margin-bottom: 32px;
136
+ }
137
+ h1 { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; }
138
+ p.desc { font-size: 13px; color: var(--muted); margin-top: 4px; }
139
+ .badge {
140
+ font-size: 11px;
141
+ font-family: monospace;
142
+ padding: 3px 8px;
143
+ background: #1a1a1a;
144
+ border: 1px solid var(--border);
145
+ border-radius: 4px;
146
+ color: var(--muted);
147
+ }
148
+ .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
149
+ @media (max-width: 860px) { .grid { grid-template-columns: 1fr; } }
150
+ .card {
151
+ background: var(--card-bg);
152
+ border: 1px solid var(--border);
153
+ border-radius: var(--radius);
154
+ padding: 20px;
155
+ }
156
+ h2 { font-size: 15px; font-weight: 600; margin-bottom: 16px; display: flex; align-items: center; justify-content: space-between; }
157
+ .field { margin-bottom: 16px; }
158
+ label { display: block; font-size: 12px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
159
+ .meta { font-size: 11px; color: var(--muted); font-family: monospace; margin-top: 4px; }
160
+ .input-row { display: flex; gap: 8px; align-items: center; }
161
+ input[type="text"], select {
162
+ flex: 1;
163
+ background: #1e1e1e;
164
+ border: 1px solid var(--border);
165
+ color: var(--text);
166
+ padding: 8px 12px;
167
+ font-size: 13px;
168
+ border-radius: 6px;
169
+ outline: none;
170
+ }
171
+ input[type="color"] {
172
+ -webkit-appearance: none;
173
+ border: 1px solid var(--border);
174
+ width: 38px;
175
+ height: 38px;
176
+ border-radius: 6px;
177
+ background: none;
178
+ cursor: pointer;
179
+ padding: 2px;
180
+ }
181
+ .btn-save {
182
+ background: var(--text);
183
+ color: var(--bg);
184
+ border: none;
185
+ padding: 10px 20px;
186
+ font-size: 13px;
187
+ font-weight: 600;
188
+ border-radius: 6px;
189
+ cursor: pointer;
190
+ transition: opacity 0.2s;
191
+ }
192
+ .btn-save:hover { opacity: 0.9; }
193
+ .preview-box {
194
+ border: 1px solid var(--border);
195
+ border-radius: 8px;
196
+ padding: 20px;
197
+ background: #0f0f0f;
198
+ display: flex;
199
+ flex-direction: column;
200
+ gap: 12px;
201
+ }
202
+ .preview-btn {
203
+ padding: 10px 18px;
204
+ border-radius: var(--sample-radius, 8px);
205
+ background: var(--sample-primary, #3182f6);
206
+ color: #ffffff;
207
+ border: none;
208
+ font-size: 13px;
209
+ font-weight: 600;
210
+ cursor: pointer;
211
+ display: inline-flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ gap: 6px;
215
+ }
216
+ .preview-card {
217
+ padding: 16px;
218
+ border-radius: var(--sample-radius, 8px);
219
+ background: var(--sample-surface, #18181b);
220
+ border: 1px solid var(--border);
221
+ color: var(--text);
222
+ font-size: 13px;
223
+ }
224
+ .toast {
225
+ position: fixed;
226
+ bottom: 24px;
227
+ right: 24px;
228
+ background: #10b981;
229
+ color: #000;
230
+ padding: 10px 18px;
231
+ border-radius: 6px;
232
+ font-size: 13px;
233
+ font-weight: 600;
234
+ box-shadow: 0 4px 12px rgba(0,0,0,0.5);
235
+ display: none;
236
+ }
237
+ </style>
238
+ </head>
239
+ <body>
240
+ <div class="container">
241
+ <header>
242
+ <div>
243
+ <div class="badge" style="margin-bottom: 8px; display: inline-block;">W3C DTCG Zero-Runtime</div>
244
+ <h1>AtomSystem Local Token Studio</h1>
245
+ <p class="desc">\uB85C\uCEEC \uD658\uACBD(tokens/token.json)\uC758 \uD1A0\uD070\uC744 GUI\uB85C \uC9C1\uC811 \uC218\uC815\uD558\uACE0 \uC2E4\uC2DC\uAC04\uC73C\uB85C css/atom-ui.css\uB85C \uC7AC\uCEF4\uD30C\uC77C\uD569\uB2C8\uB2E4.</p>
246
+ </div>
247
+ <button class="btn-save" onclick="saveTokens()">\uC800\uC7A5 \uBC0F CSS \uC7AC\uCEF4\uD30C\uC77C</button>
248
+ </header>
249
+
250
+ <div class="grid">
251
+ <!-- 1. Color Tokens -->
252
+ <div class="card">
253
+ <h2>\u{1F3A8} \uC2DC\uB9E8\uD2F1 \uBE0C\uB79C\uB4DC & \uC11C\uD53C\uC2A4 \uD1A0\uD070</h2>
254
+
255
+ <div class="field">
256
+ <label>\uBE0C\uB79C\uB4DC \uB300\uD45C \uC0C9\uC0C1 (Primary Color)</label>
257
+ <div class="input-row">
258
+ <input type="color" id="picker-primary" oninput="onColorChange('primary', this.value)">
259
+ <input type="text" id="val-primary" oninput="onColorChange('primary', this.value)">
260
+ </div>
261
+ <div class="meta">var(--atom-color-primary)</div>
262
+ </div>
263
+
264
+ <div class="field">
265
+ <label>\uD638\uBC84 \uC0C1\uD0DC \uC0C9\uC0C1 (Primary Hover)</label>
266
+ <div class="input-row">
267
+ <input type="color" id="picker-primary-hover" oninput="onColorChange('primary-hover', this.value)">
268
+ <input type="text" id="val-primary-hover" oninput="onColorChange('primary-hover', this.value)">
269
+ </div>
270
+ <div class="meta">var(--atom-color-primary-hover)</div>
271
+ </div>
272
+
273
+ <div class="field">
274
+ <label>\uC11C\uD53C\uC2A4 \uBC30\uACBD (Surface Background)</label>
275
+ <div class="input-row">
276
+ <input type="color" id="picker-bg-surface" oninput="onColorChange('bg-surface', this.value)">
277
+ <input type="text" id="val-bg-surface" oninput="onColorChange('bg-surface', this.value)">
278
+ </div>
279
+ <div class="meta">var(--atom-bg-surface)</div>
280
+ </div>
281
+
282
+ <div class="field">
283
+ <label>\uAE30\uBCF8 \uBAA8\uC11C\uB9AC \uACE1\uB960 (Border Radius)</label>
284
+ <select id="val-radius" onchange="onRadiusChange(this.value)">
285
+ <option value="4px">4px (Sharp Minimal)</option>
286
+ <option value="6px">6px (Swiss Modern)</option>
287
+ <option value="8px">8px (Standard Clean)</option>
288
+ <option value="12px">12px (Rounded Friendly)</option>
289
+ <option value="16px">16px (Toss Soft)</option>
290
+ </select>
291
+ <div class="meta">var(--atom-radius-md)</div>
292
+ </div>
293
+ </div>
294
+
295
+ <!-- 2. Live Previewer -->
296
+ <div class="card">
297
+ <h2>\u26A1 \uC2E4\uC2DC\uAC04 \uD1A0\uD070 \uBC18\uC601 \uD504\uB9AC\uBDF0</h2>
298
+ <div class="preview-box" id="preview-box">
299
+ <div class="preview-card" id="sample-card">
300
+ <div style="font-weight: 600; margin-bottom: 4px;">\uB300\uC2DC\uBCF4\uB4DC \uC704\uC82F \uCEF4\uD3EC\uB10C\uD2B8</div>
301
+ <div style="color: var(--muted); font-size: 12px; margin-bottom: 14px;">
302
+ \uD1A0\uD070 \uBCC0\uACBD \uC2DC \uC2E4\uC2DC\uAC04\uC73C\uB85C \uB514\uC790\uC778\uC774 \uBC18\uC601\uB418\uBA70 \uC989\uC2DC CSS \uD30C\uC77C\uC5D0 \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.
303
+ </div>
304
+ <button class="preview-btn" id="sample-btn">\uD655\uC778 \uBC0F \uC2B9\uC778</button>
305
+ </div>
306
+
307
+ <div style="font-size: 11px; font-family: monospace; color: var(--muted); background: #161616; padding: 12px; border-radius: 6px; border: 1px solid var(--border);">
308
+ <div>// \uCEF4\uD30C\uC77C \uB300\uC0C1 \uD30C\uC77C</div>
309
+ <div style="color: #60a5fa; margin-top: 4px;">tokens/token.json (W3C DTCG)</div>
310
+ <div style="color: #34d399; margin-top: 2px;">css/atom-ui.css (0KB Pure CSS)</div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <div id="toast" class="toast">\u2713 \uD1A0\uD070 \uBC0F CSS\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC7AC\uCEF4\uD30C\uC77C\uB418\uC5C8\uC2B5\uB2C8\uB2E4!</div>
318
+
319
+ <script>
320
+ let currentTokens = {};
321
+
322
+ async function loadTokens() {
323
+ try {
324
+ const res = await fetch('/api/tokens');
325
+ currentTokens = await res.json();
326
+ populateFields();
327
+ updatePreview();
328
+ } catch (e) {
329
+ console.error('Failed to fetch tokens', e);
330
+ }
331
+ }
332
+
333
+ function populateFields() {
334
+ const primary = currentTokens?.color?.brand?.['color-primary']?.$value || '#3182f6';
335
+ const hover = currentTokens?.color?.brand?.['color-primary-hover']?.$value || '#1b64da';
336
+ const surface = currentTokens?.color?.surface?.['bg-surface']?.$value || '#18181b';
337
+ const radius = currentTokens?.borderRadius?.md?.$value || '8px';
338
+
339
+ document.getElementById('picker-primary').value = primary.startsWith('#') ? primary : '#3182f6';
340
+ document.getElementById('val-primary').value = primary;
341
+
342
+ document.getElementById('picker-primary-hover').value = hover.startsWith('#') ? hover : '#1b64da';
343
+ document.getElementById('val-primary-hover').value = hover;
344
+
345
+ document.getElementById('picker-bg-surface').value = surface.startsWith('#') ? surface : '#18181b';
346
+ document.getElementById('val-bg-surface').value = surface;
347
+
348
+ document.getElementById('val-radius').value = radius;
349
+ }
350
+
351
+ function onColorChange(key, val) {
352
+ if (!currentTokens.color) currentTokens.color = {};
353
+ if (key === 'primary') {
354
+ document.getElementById('picker-primary').value = val.startsWith('#') ? val : '#3182f6';
355
+ document.getElementById('val-primary').value = val;
356
+ if (!currentTokens.color.brand) currentTokens.color.brand = {};
357
+ if (!currentTokens.color.brand['color-primary']) currentTokens.color.brand['color-primary'] = { variable: '--atom-color-primary' };
358
+ currentTokens.color.brand['color-primary'].$value = val;
359
+ }
360
+ if (key === 'primary-hover') {
361
+ document.getElementById('picker-primary-hover').value = val.startsWith('#') ? val : '#1b64da';
362
+ document.getElementById('val-primary-hover').value = val;
363
+ if (!currentTokens.color.brand) currentTokens.color.brand = {};
364
+ if (!currentTokens.color.brand['color-primary-hover']) currentTokens.color.brand['color-primary-hover'] = { variable: '--atom-color-primary-hover' };
365
+ currentTokens.color.brand['color-primary-hover'].$value = val;
366
+ }
367
+ if (key === 'bg-surface') {
368
+ document.getElementById('picker-bg-surface').value = val.startsWith('#') ? val : '#18181b';
369
+ document.getElementById('val-bg-surface').value = val;
370
+ if (!currentTokens.color.surface) currentTokens.color.surface = {};
371
+ if (!currentTokens.color.surface['bg-surface']) currentTokens.color.surface['bg-surface'] = { variable: '--atom-bg-surface' };
372
+ currentTokens.color.surface['bg-surface'].$value = val;
373
+ }
374
+ updatePreview();
375
+ }
376
+
377
+ function onRadiusChange(val) {
378
+ if (!currentTokens.borderRadius) currentTokens.borderRadius = {};
379
+ if (!currentTokens.borderRadius.md) currentTokens.borderRadius.md = { variable: '--atom-radius-md' };
380
+ currentTokens.borderRadius.md.$value = val;
381
+ updatePreview();
382
+ }
383
+
384
+ function updatePreview() {
385
+ const primary = document.getElementById('val-primary').value;
386
+ const surface = document.getElementById('val-bg-surface').value;
387
+ const radius = document.getElementById('val-radius').value;
388
+
389
+ const btn = document.getElementById('sample-btn');
390
+ const card = document.getElementById('sample-card');
391
+
392
+ btn.style.backgroundColor = primary;
393
+ btn.style.borderRadius = radius;
394
+ card.style.backgroundColor = surface;
395
+ card.style.borderRadius = radius;
396
+ }
397
+
398
+ async function saveTokens() {
399
+ try {
400
+ const res = await fetch('/api/tokens', {
401
+ method: 'POST',
402
+ headers: { 'Content-Type': 'application/json' },
403
+ body: JSON.stringify(currentTokens)
404
+ });
405
+ const result = await res.json();
406
+ if (result.success) {
407
+ showToast();
408
+ } else {
409
+ alert('\uC800\uC7A5 \uC2E4\uD328: ' + result.error);
410
+ }
411
+ } catch (e) {
412
+ alert('\uC11C\uBC84 \uD1B5\uC2E0 \uC624\uB958: ' + e.message);
413
+ }
414
+ }
415
+
416
+ function showToast() {
417
+ const toast = document.getElementById('toast');
418
+ toast.style.display = 'block';
419
+ setTimeout(() => { toast.style.display = 'none'; }, 2500);
420
+ }
421
+
422
+ loadTokens();
423
+ </script>
424
+ </body>
425
+ </html>`;
426
+ }
427
+
428
+ // src/cli.ts
6
429
  function getPkgRoot() {
7
430
  try {
8
431
  if (typeof __dirname !== "undefined") {
9
- return path.resolve(__dirname, "..");
432
+ return path2.resolve(__dirname, "..");
10
433
  }
11
434
  const currentFile = fileURLToPath(import.meta.url);
12
- return path.resolve(path.dirname(currentFile), "..");
435
+ return path2.resolve(path2.dirname(currentFile), "..");
13
436
  } catch {
14
437
  return process.cwd();
15
438
  }
@@ -17,43 +440,190 @@ function getPkgRoot() {
17
440
  var PKG_ROOT = getPkgRoot();
18
441
  var args = process.argv.slice(2);
19
442
  var command = args[0] || "help";
443
+ var targetName = args[1];
20
444
  function copyFileSafe(src, dest, label) {
21
- const targetDir = path.dirname(dest);
22
- if (!fs.existsSync(targetDir)) {
23
- fs.mkdirSync(targetDir, { recursive: true });
445
+ const targetDir = path2.dirname(dest);
446
+ if (!fs2.existsSync(targetDir)) {
447
+ fs2.mkdirSync(targetDir, { recursive: true });
24
448
  }
25
- fs.copyFileSync(src, dest);
26
- console.log(` \u2713 Created ${label} -> ${path.relative(process.cwd(), dest)}`);
449
+ fs2.copyFileSync(src, dest);
450
+ console.log(` \u2713 ${label} -> ${path2.relative(process.cwd(), dest)}`);
27
451
  }
28
452
  function handleInit() {
29
453
  const cwd = process.cwd();
30
- console.log("\n\u{1F680} Initializing AtomSystem in your project...\n");
31
- const srcSkill = path.join(PKG_ROOT, "skills", "atomsystem.skill.md");
32
- const destSkill = path.join(cwd, ".agents", "skills", "atomsystem", "SKILL.md");
33
- if (fs.existsSync(srcSkill)) {
34
- copyFileSafe(srcSkill, destSkill, "AI Skill (.agents/skills/atomsystem/SKILL.md)");
454
+ console.log("\n\u{1F680} Initializing AtomSystem Universal Design System...\n");
455
+ const srcAgentGuide = path2.join(PKG_ROOT, "presets", "agents.guide.md");
456
+ if (fs2.existsSync(srcAgentGuide)) {
457
+ copyFileSafe(srcAgentGuide, path2.join(cwd, "AGENTS.md"), "Universal AI Guide (AGENTS.md)");
458
+ copyFileSafe(srcAgentGuide, path2.join(cwd, "CLAUDE.md"), "Claude Code Guidelines (CLAUDE.md)");
459
+ copyFileSafe(srcAgentGuide, path2.join(cwd, ".cursorrules"), "Cursor Rules (.cursorrules)");
460
+ }
461
+ const srcSkill = path2.join(PKG_ROOT, "skills", "atomsystem.skill.md");
462
+ if (fs2.existsSync(srcSkill)) {
463
+ copyFileSafe(srcSkill, path2.join(cwd, ".agents", "skills", "atomsystem", "SKILL.md"), "Agent Skill (.agents/skills/atomsystem/SKILL.md)");
464
+ }
465
+ const specs = [
466
+ { src: path2.join(PKG_ROOT, "presets", "default.design.md"), dest: path2.join(cwd, "atom.design.md"), label: "Design Tokens (atom.design.md)" },
467
+ { src: path2.join(PKG_ROOT, "presets", "components.md"), dest: path2.join(cwd, "atom.components.md"), label: "Components Spec (atom.components.md)" },
468
+ { src: path2.join(PKG_ROOT, "presets", "blocks.md"), dest: path2.join(cwd, "atom.blocks.md"), label: "Design Blocks Spec (atom.blocks.md)" },
469
+ { src: path2.join(PKG_ROOT, "presets", "uxPattern.md"), dest: path2.join(cwd, "atom.uxPattern.md"), label: "UX Patterns (atom.uxPattern.md)" }
470
+ ];
471
+ specs.forEach((s) => {
472
+ if (fs2.existsSync(s.src)) {
473
+ copyFileSafe(s.src, s.dest, s.label);
474
+ }
475
+ });
476
+ const srcToken = path2.join(PKG_ROOT, "tokens", "token.json");
477
+ if (fs2.existsSync(srcToken)) {
478
+ copyFileSafe(srcToken, path2.join(cwd, "tokens", "token.json"), "W3C DTCG Token JSON (tokens/token.json)");
479
+ }
480
+ const srcCss = path2.join(PKG_ROOT, "css", "atom-ui.css");
481
+ if (fs2.existsSync(srcCss)) {
482
+ copyFileSafe(srcCss, path2.join(cwd, "css", "atom-ui.css"), "Zero-Runtime Semantic CSS (css/atom-ui.css)");
483
+ }
484
+ console.log("\n\u2728 AtomSystem initialization completed successfully!");
485
+ console.log("\n\u{1F3A8} Local Token Studio Available:");
486
+ console.log(' Run "npx atomsystem studio" to launch the local web customizer GUI.');
487
+ console.log(" You can visually edit tokens and immediately recompile css/atom-ui.css.");
488
+ console.log("\n\u{1F4CC} How AI Coding Works Now:");
489
+ console.log(" Any AI assistant (Claude Code, Cursor, Copilot, Antigravity) will now");
490
+ console.log(" AUTOMATICALLY detect the design tokens, components, and UX patterns.");
491
+ console.log("\n\u{1F4CC} You can now ask your AI:");
492
+ console.log(' "\uB300\uC2DC\uBCF4\uB4DC \uD654\uBA74\uC5D0 \uC2E4\uC2DC\uAC04 \uCC28\uD2B8\uC640 \uAC00\uACA9 \uC815\uCC45 \uBE14\uB85D\uC744 \uB808\uACE0\uCC98\uB7FC \uC870\uB9BD\uD574\uC918."');
493
+ console.log(' "atom.design.md\uC758 \uC0C9\uC0C1 \uD1A0\uD070\uC744 \uBC14\uD0D5\uC73C\uB85C \uACB0\uC81C \uD655\uC778 \uBAA8\uB2EC\uC744 \uB9CC\uB4E4\uC5B4\uC918."\n');
494
+ }
495
+ var COMPONENT_MAP = {
496
+ button: "Button.tsx",
497
+ card: "Card.tsx",
498
+ badge: "Badge.tsx",
499
+ input: "Input.tsx",
500
+ table: "Table.tsx",
501
+ tabs: "Tabs.tsx",
502
+ switch: "Switch.tsx",
503
+ modal: "Modal.tsx",
504
+ container: "Container.tsx",
505
+ layout: "Layout.tsx"
506
+ };
507
+ var BLOCK_MAP = {
508
+ "realtime-chart": { file: "RealtimeChartBlock.tsx", deps: ["card", "button", "badge"] },
509
+ "interactive-chat": { file: "InteractiveChatBlock.tsx", deps: ["card", "input", "button", "badge"] },
510
+ "pricing-table": { file: "PricingPlanBlock.tsx", deps: ["card", "button", "badge"] },
511
+ "card-list": { file: "CardListBlock.tsx", deps: ["card", "input", "button", "badge"] },
512
+ "interactive-slider": { file: "SliderShowcaseBlock.tsx", deps: ["card", "button", "badge"] },
513
+ "metric-stats": { file: "MetricStatsBlock.tsx", deps: ["card", "badge"] },
514
+ "file-explorer": { file: "FileExplorerBlock.tsx", deps: ["card", "button", "badge"] }
515
+ };
516
+ function handleAdd(name) {
517
+ if (!name) {
518
+ console.error("\n\u274C Please specify an item to add. Example: npx atomsystem add button\n");
519
+ handleList();
520
+ return;
521
+ }
522
+ const cwd = process.cwd();
523
+ const normalized = name.toLowerCase().trim();
524
+ if (COMPONENT_MAP[normalized]) {
525
+ const fileName = COMPONENT_MAP[normalized];
526
+ const src = path2.join(PKG_ROOT, "components", "ui", fileName);
527
+ const dest = path2.join(cwd, "components", "ui", fileName);
528
+ if (fs2.existsSync(src)) {
529
+ copyFileSafe(src, dest, `Component (${fileName})`);
530
+ console.log(`
531
+ \u2705 Added ${normalized} component to components/ui/${fileName}
532
+ `);
533
+ } else {
534
+ console.error(`
535
+ \u274C Source component file not found: ${src}
536
+ `);
537
+ }
538
+ return;
35
539
  }
36
- const srcDesign = path.join(PKG_ROOT, "presets", "default.design.md");
37
- const destDesign = path.join(cwd, "atom.design.md");
38
- if (fs.existsSync(srcDesign)) {
39
- copyFileSafe(srcDesign, destDesign, "Design Guide (atom.design.md)");
540
+ if (BLOCK_MAP[normalized]) {
541
+ const { file, deps } = BLOCK_MAP[normalized];
542
+ console.log(`
543
+ \u{1F4E6} Adding block "${normalized}" and its dependent components...
544
+ `);
545
+ deps.forEach((dep) => {
546
+ const depFile = COMPONENT_MAP[dep];
547
+ if (depFile) {
548
+ const srcDep = path2.join(PKG_ROOT, "components", "ui", depFile);
549
+ const destDep = path2.join(cwd, "components", "ui", depFile);
550
+ if (fs2.existsSync(srcDep)) {
551
+ copyFileSafe(srcDep, destDep, `Dependent Component (${depFile})`);
552
+ }
553
+ }
554
+ });
555
+ const srcBlock = path2.join(PKG_ROOT, "components", "blocks", file);
556
+ const destBlock = path2.join(cwd, "components", "blocks", file);
557
+ if (fs2.existsSync(srcBlock)) {
558
+ copyFileSafe(srcBlock, destBlock, `Block (${file})`);
559
+ console.log(`
560
+ \u2705 Block "${normalized}" successfully added to components/blocks/${file}
561
+ `);
562
+ } else {
563
+ console.error(`
564
+ \u274C Source block file not found: ${srcBlock}
565
+ `);
566
+ }
567
+ return;
40
568
  }
41
- const srcPattern = path.join(PKG_ROOT, "presets", "uxPattern.md");
42
- const destPattern = path.join(cwd, "atom.uxPattern.md");
43
- if (fs.existsSync(srcPattern)) {
44
- copyFileSafe(srcPattern, destPattern, "UX Pattern Guide (atom.uxPattern.md)");
569
+ console.error(`
570
+ \u274C Unknown item: "${name}".`);
571
+ handleList();
572
+ }
573
+ function handleList() {
574
+ console.log(`
575
+ Available Components:
576
+ - button (Button, ButtonVariants)
577
+ - card (Card container)
578
+ - badge (Status badge)
579
+ - input (Input with status)
580
+ - table (Table, Header, Row, Cell)
581
+ - tabs (TabsList, TabsTrigger)
582
+ - switch (A11y Switch toggle)
583
+ - modal (Dialog with Esc key support)
584
+
585
+ Available Design Blocks:
586
+ - realtime-chart (Live streaming candlestick/line chart)
587
+ - interactive-chat (AI chatbot feed with suggestions)
588
+ - pricing-table (Monthly/annual tier pricing table)
589
+ - card-list (Responsive grid & list filter)
590
+ - interactive-slider (Cost simulation calculator & carousel)
591
+ - metric-stats (4 KPI executive metric cards)
592
+ - file-explorer (Hierarchical directory tree explorer)
593
+
594
+ Install any item:
595
+ npx atomsystem add <item-name>
596
+ `);
597
+ }
598
+ function handleBuildTokens() {
599
+ const cwd = process.cwd();
600
+ const tokenJsonPath = path2.join(cwd, "tokens", "token.json");
601
+ const cssPath = path2.join(cwd, "css", "atom-ui.css");
602
+ if (!fs2.existsSync(tokenJsonPath)) {
603
+ console.error(`
604
+ \u274C tokens/token.json not found at ${tokenJsonPath}. Run 'npx atomsystem init' first.
605
+ `);
606
+ return;
45
607
  }
46
- const srcToken = path.join(PKG_ROOT, "tokens", "token.json");
47
- const destToken = path.join(cwd, "tokens", "token.json");
48
- if (fs.existsSync(srcToken)) {
49
- copyFileSafe(srcToken, destToken, "Token JSON (tokens/token.json)");
608
+ try {
609
+ const tokens = JSON.parse(fs2.readFileSync(tokenJsonPath, "utf-8"));
610
+ const css = compileTokensToCss(tokens);
611
+ const cssDir = path2.dirname(cssPath);
612
+ if (!fs2.existsSync(cssDir)) fs2.mkdirSync(cssDir, { recursive: true });
613
+ fs2.writeFileSync(cssPath, css, "utf-8");
614
+ console.log(`
615
+ \u2728 Successfully compiled tokens to ${path2.relative(cwd, cssPath)}!
616
+ `);
617
+ } catch (err) {
618
+ console.error(`
619
+ \u274C Failed to compile tokens: ${err.message}
620
+ `);
50
621
  }
51
- console.log("\n\u2728 AtomSystem initialization completed!");
52
- console.log("\n\u{1F4CC} Next Steps:");
53
- console.log("1. Import AtomSystem CSS in your app (e.g. layout.tsx, App.vue, index.html):");
54
- console.log(' import "atomsystem/css/atom-ui.css";');
55
- console.log("2. Ask your AI coding assistant (Antigravity, Cursor, Copilot) to build UI:");
56
- console.log(' "atom.design.md\uC640 atom.uxPattern.md\uB97C \uCC38\uACE0\uD558\uC5EC \uACB0\uC81C \uD654\uBA74 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uB9CC\uB4E4\uC5B4\uC918."\n');
622
+ }
623
+ function handleStudio() {
624
+ const portArg = args.find((a) => a.startsWith("--port="));
625
+ const port = portArg ? parseInt(portArg.split("=")[1], 10) : 4e3;
626
+ startLocalTokenStudio({ port, cwd: process.cwd() });
57
627
  }
58
628
  function showHelp() {
59
629
  console.log(`
@@ -63,24 +633,45 @@ Usage:
63
633
  npx atomsystem <command> [options]
64
634
 
65
635
  Commands:
66
- init Initialize AtomSystem assets (AI Skill, design.md, uxPattern.md, token.json)
67
- help Display this help message
68
- version Print version info
636
+ init Initialize AI guidelines, tokens, specs, and CSS in your project
637
+ studio Launch Local Web Token Studio GUI (interactive token customizer)
638
+ build:tokens Compile tokens/token.json into zero-runtime css/atom-ui.css
639
+ add <name> Add a component or block directly to your project
640
+ list List all available components and blocks
641
+ help Display this help message
642
+ version Print version info
643
+
644
+ Options for studio:
645
+ --port=<port> Port for the local studio server (default: 4000)
69
646
  `);
70
647
  }
71
648
  function showVersion() {
72
- const pkgPath = path.join(PKG_ROOT, "package.json");
73
- if (fs.existsSync(pkgPath)) {
74
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
649
+ const pkgPath = path2.join(PKG_ROOT, "package.json");
650
+ if (fs2.existsSync(pkgPath)) {
651
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
75
652
  console.log(`atomsystem v${pkg.version}`);
76
653
  } else {
77
- console.log("atomsystem v0.4.3");
654
+ console.log("atomsystem v0.5.0");
78
655
  }
79
656
  }
80
657
  switch (command) {
81
658
  case "init":
82
659
  handleInit();
83
660
  break;
661
+ case "studio":
662
+ case "editor":
663
+ handleStudio();
664
+ break;
665
+ case "build:tokens":
666
+ case "compile":
667
+ handleBuildTokens();
668
+ break;
669
+ case "add":
670
+ handleAdd(targetName);
671
+ break;
672
+ case "list":
673
+ handleList();
674
+ break;
84
675
  case "version":
85
676
  case "-v":
86
677
  case "--version":