@mizumi25/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,713 @@
1
+ // packages/cli/docs-generator.js
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+
6
+ export class DocsGenerator {
7
+ constructor(config) {
8
+ this.config = config;
9
+ this.tokens = config.tokens || {};
10
+ this.patterns = config.patterns || {};
11
+ this.animations = config.animations || {};
12
+ }
13
+
14
+ generate() {
15
+ return `<!DOCTYPE html>
16
+ <html lang="en">
17
+ <head>
18
+ <meta charset="UTF-8">
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <title>Mizumi Docs 🌊</title>
21
+ <style>
22
+ * { margin:0; padding:0; box-sizing:border-box; }
23
+
24
+ body {
25
+ font-family: system-ui, sans-serif;
26
+ background: #0f172a;
27
+ color: #e2e8f0;
28
+ min-height: 100vh;
29
+ }
30
+
31
+ /* Layout */
32
+ .sidebar {
33
+ position: fixed;
34
+ top: 0; left: 0;
35
+ width: 240px;
36
+ height: 100vh;
37
+ background: #1e293b;
38
+ border-right: 1px solid #334155;
39
+ overflow-y: auto;
40
+ padding: 24px 0;
41
+ }
42
+
43
+ .main {
44
+ margin-left: 240px;
45
+ padding: 40px;
46
+ max-width: 1000px;
47
+ }
48
+
49
+ /* Sidebar */
50
+ .sidebar-logo {
51
+ padding: 0 20px 24px;
52
+ border-bottom: 1px solid #334155;
53
+ margin-bottom: 16px;
54
+ }
55
+
56
+ .sidebar-logo h1 {
57
+ font-size: 20px;
58
+ font-weight: 800;
59
+ color: #38bdf8;
60
+ letter-spacing: -0.5px;
61
+ }
62
+
63
+ .sidebar-logo span {
64
+ font-size: 12px;
65
+ color: #64748b;
66
+ }
67
+
68
+ .nav-section {
69
+ padding: 8px 20px 4px;
70
+ font-size: 11px;
71
+ font-weight: 700;
72
+ text-transform: uppercase;
73
+ letter-spacing: 1px;
74
+ color: #475569;
75
+ }
76
+
77
+ .nav-link {
78
+ display: block;
79
+ padding: 8px 20px;
80
+ color: #94a3b8;
81
+ text-decoration: none;
82
+ font-size: 14px;
83
+ transition: all 0.15s;
84
+ border-left: 3px solid transparent;
85
+ }
86
+
87
+ .nav-link:hover {
88
+ color: #e2e8f0;
89
+ background: #334155;
90
+ border-left-color: #38bdf8;
91
+ }
92
+
93
+ /* Sections */
94
+ .section {
95
+ margin-bottom: 64px;
96
+ }
97
+
98
+ .section-title {
99
+ font-size: 28px;
100
+ font-weight: 700;
101
+ color: #f1f5f9;
102
+ margin-bottom: 8px;
103
+ padding-bottom: 16px;
104
+ border-bottom: 1px solid #334155;
105
+ letter-spacing: -0.5px;
106
+ }
107
+
108
+ .section-sub {
109
+ font-size: 18px;
110
+ font-weight: 600;
111
+ color: #cbd5e1;
112
+ margin: 32px 0 16px;
113
+ }
114
+
115
+ /* Cards */
116
+ .card-grid {
117
+ display: grid;
118
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
119
+ gap: 12px;
120
+ margin-bottom: 24px;
121
+ }
122
+
123
+ .doc-card {
124
+ background: #1e293b;
125
+ border: 1px solid #334155;
126
+ border-radius: 10px;
127
+ padding: 16px;
128
+ transition: border-color 0.15s;
129
+ }
130
+
131
+ .doc-card:hover { border-color: #38bdf8; }
132
+
133
+ .doc-card-name {
134
+ font-family: monospace;
135
+ font-size: 13px;
136
+ color: #38bdf8;
137
+ margin-bottom: 8px;
138
+ font-weight: 600;
139
+ }
140
+
141
+ .doc-card-value {
142
+ font-size: 12px;
143
+ color: #64748b;
144
+ word-break: break-all;
145
+ }
146
+
147
+ /* Color swatches */
148
+ .color-swatch {
149
+ width: 100%;
150
+ height: 40px;
151
+ border-radius: 6px;
152
+ margin-bottom: 8px;
153
+ border: 1px solid rgba(255,255,255,0.1);
154
+ }
155
+
156
+ /* Pattern rows */
157
+ .pattern-row {
158
+ background: #1e293b;
159
+ border: 1px solid #334155;
160
+ border-radius: 10px;
161
+ padding: 16px 20px;
162
+ margin-bottom: 8px;
163
+ display: flex;
164
+ align-items: flex-start;
165
+ gap: 16px;
166
+ flex-wrap: wrap;
167
+ }
168
+
169
+ .pattern-name {
170
+ font-family: monospace;
171
+ font-size: 14px;
172
+ color: #38bdf8;
173
+ font-weight: 600;
174
+ min-width: 160px;
175
+ }
176
+
177
+ .pattern-arrow {
178
+ color: #475569;
179
+ font-size: 14px;
180
+ }
181
+
182
+ .pattern-value {
183
+ font-family: monospace;
184
+ font-size: 13px;
185
+ color: #94a3b8;
186
+ flex: 1;
187
+ }
188
+
189
+ /* Animation rows */
190
+ .anim-row {
191
+ background: #1e293b;
192
+ border: 1px solid #334155;
193
+ border-radius: 10px;
194
+ padding: 16px 20px;
195
+ margin-bottom: 8px;
196
+ }
197
+
198
+ .anim-header {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 12px;
202
+ margin-bottom: 8px;
203
+ }
204
+
205
+ .anim-name {
206
+ font-family: monospace;
207
+ font-size: 14px;
208
+ color: #38bdf8;
209
+ font-weight: 600;
210
+ }
211
+
212
+ .anim-type {
213
+ font-size: 11px;
214
+ font-weight: 700;
215
+ padding: 2px 8px;
216
+ border-radius: 999px;
217
+ text-transform: uppercase;
218
+ letter-spacing: 0.5px;
219
+ }
220
+
221
+ .type-entrance { background: #0d3b26; color: #34d399; }
222
+ .type-hover { background: #1e1a3b; color: #a78bfa; }
223
+ .type-click { background: #3b1a1a; color: #f87171; }
224
+ .type-scroll { background: #1a2e3b; color: #38bdf8; }
225
+ .type-stagger { background: #2e2a0d; color: #fbbf24; }
226
+ .type-loop { background: #2e1a3b; color: #e879f9; }
227
+
228
+ .anim-config {
229
+ font-family: monospace;
230
+ font-size: 12px;
231
+ color: #64748b;
232
+ line-height: 1.6;
233
+ }
234
+
235
+ /* Usage box */
236
+ .usage-box {
237
+ background: #0f172a;
238
+ border: 1px solid #334155;
239
+ border-radius: 6px;
240
+ padding: 10px 14px;
241
+ margin-top: 8px;
242
+ font-family: monospace;
243
+ font-size: 12px;
244
+ color: #86efac;
245
+ }
246
+
247
+ /* Spacing preview */
248
+ .spacing-preview {
249
+ display: flex;
250
+ align-items: center;
251
+ gap: 12px;
252
+ }
253
+
254
+ .spacing-bar {
255
+ background: #38bdf8;
256
+ height: 16px;
257
+ border-radius: 3px;
258
+ opacity: 0.7;
259
+ }
260
+
261
+ /* Search */
262
+ .search-bar {
263
+ width: 100%;
264
+ background: #1e293b;
265
+ border: 1px solid #334155;
266
+ border-radius: 10px;
267
+ padding: 12px 16px;
268
+ color: #e2e8f0;
269
+ font-size: 14px;
270
+ margin-bottom: 32px;
271
+ outline: none;
272
+ transition: border-color 0.15s;
273
+ }
274
+
275
+ .search-bar:focus { border-color: #38bdf8; }
276
+ .search-bar::placeholder { color: #475569; }
277
+
278
+ /* Stats bar */
279
+ .stats-bar {
280
+ display: flex;
281
+ gap: 24px;
282
+ margin-bottom: 48px;
283
+ flex-wrap: wrap;
284
+ }
285
+
286
+ .stat {
287
+ background: #1e293b;
288
+ border: 1px solid #334155;
289
+ border-radius: 10px;
290
+ padding: 16px 24px;
291
+ text-align: center;
292
+ }
293
+
294
+ .stat-number {
295
+ font-size: 28px;
296
+ font-weight: 800;
297
+ color: #38bdf8;
298
+ }
299
+
300
+ .stat-label {
301
+ font-size: 12px;
302
+ color: #64748b;
303
+ margin-top: 2px;
304
+ }
305
+
306
+ /* Header */
307
+ .docs-header {
308
+ margin-bottom: 48px;
309
+ }
310
+
311
+ .docs-header h1 {
312
+ font-size: 40px;
313
+ font-weight: 800;
314
+ color: #f1f5f9;
315
+ letter-spacing: -1px;
316
+ margin-bottom: 8px;
317
+ }
318
+
319
+ .docs-header p {
320
+ color: #64748b;
321
+ font-size: 16px;
322
+ }
323
+
324
+ .badge {
325
+ display: inline-block;
326
+ background: #0c2a1a;
327
+ color: #34d399;
328
+ border: 1px solid #34d399;
329
+ padding: 3px 10px;
330
+ border-radius: 999px;
331
+ font-size: 12px;
332
+ font-weight: 600;
333
+ margin-left: 8px;
334
+ vertical-align: middle;
335
+ }
336
+
337
+ /* Scrollbar */
338
+ ::-webkit-scrollbar { width: 6px; }
339
+ ::-webkit-scrollbar-track { background: #0f172a; }
340
+ ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
341
+ </style>
342
+ </head>
343
+ <body>
344
+
345
+ <!-- SIDEBAR -->
346
+ <nav class="sidebar">
347
+ <div class="sidebar-logo">
348
+ <h1>🌊 Mizumi</h1>
349
+ <span>v0.1.0 Docs</span>
350
+ </div>
351
+
352
+ <div class="nav-section">Getting Started</div>
353
+ <a href="#overview" class="nav-link">Overview</a>
354
+
355
+ <div class="nav-section">Design Tokens</div>
356
+ <a href="#colors" class="nav-link">Colors</a>
357
+ <a href="#spacing" class="nav-link">Spacing</a>
358
+ <a href="#typography" class="nav-link">Typography</a>
359
+ <a href="#radius" class="nav-link">Border Radius</a>
360
+ <a href="#shadows" class="nav-link">Shadows</a>
361
+
362
+ <div class="nav-section">Patterns</div>
363
+ <a href="#patterns" class="nav-link">All Patterns</a>
364
+
365
+ <div class="nav-section">Animations</div>
366
+ <a href="#animations" class="nav-link">All Animations</a>
367
+ <a href="#modifiers" class="nav-link">Modifiers</a>
368
+ </nav>
369
+
370
+ <!-- MAIN -->
371
+ <main class="main">
372
+
373
+ <!-- Header -->
374
+ <div class="docs-header">
375
+ <h1>Mizumi Docs <span class="badge">v0.1.0</span></h1>
376
+ <p>Auto-generated from your mizumi.config.js</p>
377
+ </div>
378
+
379
+ <!-- Search -->
380
+ <input
381
+ class="search-bar"
382
+ type="text"
383
+ placeholder="Search tokens, patterns, animations..."
384
+ oninput="handleSearch(this.value)"
385
+ >
386
+
387
+ <!-- Stats -->
388
+ <div class="stats-bar">
389
+ <div class="stat">
390
+ <div class="stat-number">${this.countTokens()}</div>
391
+ <div class="stat-label">Tokens</div>
392
+ </div>
393
+ <div class="stat">
394
+ <div class="stat-number">${Object.keys(this.patterns).length}</div>
395
+ <div class="stat-label">Patterns</div>
396
+ </div>
397
+ <div class="stat">
398
+ <div class="stat-number">${Object.keys(this.animations).length}</div>
399
+ <div class="stat-label">Animations</div>
400
+ </div>
401
+ <div class="stat">
402
+ <div class="stat-number">${Object.keys(this.config.rules?.breakpoints || {}).length}</div>
403
+ <div class="stat-label">Breakpoints</div>
404
+ </div>
405
+ </div>
406
+
407
+ <!-- COLORS -->
408
+ <section class="section" id="colors">
409
+ <h2 class="section-title">Colors</h2>
410
+ <div class="card-grid">
411
+ ${this.generateColorCards()}
412
+ </div>
413
+ </section>
414
+
415
+ <!-- SPACING -->
416
+ <section class="section" id="spacing">
417
+ <h2 class="section-title">Spacing</h2>
418
+ <div style="display:flex; flex-direction:column; gap:8px;">
419
+ ${this.generateSpacingRows()}
420
+ </div>
421
+ </section>
422
+
423
+ <!-- TYPOGRAPHY -->
424
+ <section class="section" id="typography">
425
+ <h2 class="section-title">Typography</h2>
426
+ <div style="display:flex; flex-direction:column; gap:12px;">
427
+ ${this.generateTypographyRows()}
428
+ </div>
429
+ </section>
430
+
431
+ <!-- RADIUS -->
432
+ <section class="section" id="radius">
433
+ <h2 class="section-title">Border Radius</h2>
434
+ <div class="card-grid">
435
+ ${this.generateRadiusCards()}
436
+ </div>
437
+ </section>
438
+
439
+ <!-- SHADOWS -->
440
+ <section class="section" id="shadows">
441
+ <h2 class="section-title">Shadows</h2>
442
+ <div style="display:flex; flex-direction:column; gap:12px;">
443
+ ${this.generateShadowCards()}
444
+ </div>
445
+ </section>
446
+
447
+ <!-- PATTERNS -->
448
+ <section class="section" id="patterns">
449
+ <h2 class="section-title">Patterns</h2>
450
+ <div id="patterns-list">
451
+ ${this.generatePatternRows()}
452
+ </div>
453
+ </section>
454
+
455
+ <!-- ANIMATIONS -->
456
+ <section class="section" id="animations">
457
+ <h2 class="section-title">Animations</h2>
458
+ <div id="animations-list">
459
+ ${this.generateAnimationRows()}
460
+ </div>
461
+ </section>
462
+
463
+ <!-- MODIFIERS -->
464
+ <section class="section" id="modifiers">
465
+ <h2 class="section-title">Animation Modifiers</h2>
466
+ <p style="color:#64748b; margin-bottom:24px; font-size:14px;">
467
+ Stack these onto any animation class to customize behavior.
468
+ </p>
469
+
470
+ <h3 class="section-sub">Duration</h3>
471
+ <div class="card-grid">
472
+ ${[100,150,200,300,500,800,1000].map(d => `
473
+ <div class="doc-card">
474
+ <div class="doc-card-name">duration-${d}</div>
475
+ <div class="doc-card-value">${d}ms → ${d/1000}s</div>
476
+ </div>
477
+ `).join('')}
478
+ ${['fast','normal','slow','slower'].map(n => `
479
+ <div class="doc-card">
480
+ <div class="doc-card-name">duration-${n}</div>
481
+ <div class="doc-card-value">Token-based</div>
482
+ </div>
483
+ `).join('')}
484
+ </div>
485
+
486
+ <h3 class="section-sub">Delay</h3>
487
+ <div class="card-grid">
488
+ ${[0,100,150,200,300,500,1000].map(d => `
489
+ <div class="doc-card">
490
+ <div class="doc-card-name">delay-${d}</div>
491
+ <div class="doc-card-value">${d}ms → ${d/1000}s</div>
492
+ </div>
493
+ `).join('')}
494
+ </div>
495
+
496
+ <h3 class="section-sub">Easing</h3>
497
+ <div class="card-grid">
498
+ ${['smooth','bouncy','sharp','back','linear'].map(e => `
499
+ <div class="doc-card">
500
+ <div class="doc-card-name">ease-${e}</div>
501
+ <div class="doc-card-value">${this.easeToGSAP(e)}</div>
502
+ </div>
503
+ `).join('')}
504
+ </div>
505
+
506
+ <h3 class="section-sub">Data Attribute Overrides</h3>
507
+ <div class="card-grid">
508
+ ${['data-gsap-duration','data-gsap-delay','data-gsap-ease','data-gsap-y','data-gsap-x','data-gsap-scale','data-gsap-opacity'].map(a => `
509
+ <div class="doc-card">
510
+ <div class="doc-card-name">${a}</div>
511
+ <div class="doc-card-value">Highest priority override</div>
512
+ </div>
513
+ `).join('')}
514
+ </div>
515
+ </section>
516
+
517
+ </main>
518
+
519
+ <script>
520
+ // Search functionality
521
+ function handleSearch(query) {
522
+ const q = query.toLowerCase().trim();
523
+ const allRows = document.querySelectorAll(
524
+ '.doc-card, .pattern-row, .anim-row'
525
+ );
526
+
527
+ allRows.forEach(row => {
528
+ const text = row.textContent.toLowerCase();
529
+ row.style.display = !q || text.includes(q) ? '' : 'none';
530
+ });
531
+ }
532
+
533
+ // Smooth scroll for nav links
534
+ document.querySelectorAll('.nav-link').forEach(link => {
535
+ link.addEventListener('click', e => {
536
+ e.preventDefault();
537
+ const target = document.querySelector(link.getAttribute('href'));
538
+ if (target) target.scrollIntoView({ behavior: 'smooth' });
539
+ });
540
+ });
541
+ </script>
542
+
543
+ </body>
544
+ </html>`;
545
+ }
546
+
547
+ generateColorCards() {
548
+ const cards = [];
549
+ if (!this.tokens.colors) return '';
550
+
551
+ for (const [k, v] of Object.entries(this.tokens.colors)) {
552
+ if (typeof v === 'object') {
553
+ for (const [shade, color] of Object.entries(v)) {
554
+ const name = shade === 'DEFAULT' ? k : `${k}-${shade}`;
555
+ cards.push(`
556
+ <div class="doc-card">
557
+ <div class="color-swatch" style="background:${color};"></div>
558
+ <div class="doc-card-name">${name}</div>
559
+ <div class="doc-card-value">${color}</div>
560
+ <div class="doc-card-value" style="margin-top:4px;">
561
+ bg-${name} Ā· color-${name}
562
+ </div>
563
+ </div>
564
+ `);
565
+ }
566
+ } else {
567
+ cards.push(`
568
+ <div class="doc-card">
569
+ <div class="color-swatch" style="background:${v};"></div>
570
+ <div class="doc-card-name">${k}</div>
571
+ <div class="doc-card-value">${v}</div>
572
+ <div class="doc-card-value" style="margin-top:4px;">
573
+ bg-${k} Ā· color-${k}
574
+ </div>
575
+ </div>
576
+ `);
577
+ }
578
+ }
579
+ return cards.join('');
580
+ }
581
+
582
+ generateSpacingRows() {
583
+ if (!this.tokens.spacing) return '';
584
+ return Object.entries(this.tokens.spacing).map(([k, v]) => {
585
+ const px = parseInt(v);
586
+ return `
587
+ <div class="pattern-row">
588
+ <div class="pattern-name">pad-${k} Ā· mar-${k} Ā· gap-${k}</div>
589
+ <div class="spacing-preview">
590
+ <div class="spacing-bar" style="width:${Math.min(px * 2, 200)}px;"></div>
591
+ <span style="color:#64748b; font-size:13px;">${v}</span>
592
+ </div>
593
+ </div>
594
+ `;
595
+ }).join('');
596
+ }
597
+
598
+ generateTypographyRows() {
599
+ if (!this.tokens.typography) return '';
600
+ return Object.entries(this.tokens.typography).map(([k, v]) => `
601
+ <div class="pattern-row">
602
+ <div class="pattern-name">text-${k}</div>
603
+ <div style="flex:1;">
604
+ <div style="font-size:${v.size}; font-weight:${v.weight}; line-height:${v.line}; color:#e2e8f0; margin-bottom:4px;">
605
+ The quick brown fox
606
+ </div>
607
+ <div style="font-size:12px; color:#64748b;">
608
+ ${v.size} Ā· weight ${v.weight} Ā· line ${v.line}
609
+ </div>
610
+ </div>
611
+ </div>
612
+ `).join('');
613
+ }
614
+
615
+ generateRadiusCards() {
616
+ if (!this.tokens.radius) return '';
617
+ return Object.entries(this.tokens.radius).map(([k, v]) => `
618
+ <div class="doc-card" style="text-align:center;">
619
+ <div style="
620
+ width:48px; height:48px;
621
+ background:#38bdf8;
622
+ border-radius:${v};
623
+ margin:0 auto 12px;
624
+ opacity:0.7;
625
+ "></div>
626
+ <div class="doc-card-name">rounded-${k}</div>
627
+ <div class="doc-card-value">${v}</div>
628
+ </div>
629
+ `).join('');
630
+ }
631
+
632
+ generateShadowCards() {
633
+ if (!this.tokens.shadows) return '';
634
+ return Object.entries(this.tokens.shadows).map(([k, v]) => `
635
+ <div class="pattern-row">
636
+ <div class="pattern-name">shadow-${k}</div>
637
+ <div style="
638
+ width:60px; height:32px;
639
+ background:#1e293b;
640
+ border-radius:6px;
641
+ box-shadow:${v};
642
+ flex-shrink:0;
643
+ "></div>
644
+ <div class="doc-card-value" style="font-family:monospace; font-size:12px;">${v}</div>
645
+ </div>
646
+ `).join('');
647
+ }
648
+
649
+ generatePatternRows() {
650
+ return Object.entries(this.patterns).map(([name, value]) => `
651
+ <div class="pattern-row">
652
+ <div class="pattern-name">.${name}</div>
653
+ <div class="pattern-arrow">→</div>
654
+ <div class="pattern-value">${value}</div>
655
+ <div class="usage-box" style="width:100%;">
656
+ &lt;div class="${name}"&gt;...&lt;/div&gt;
657
+ </div>
658
+ </div>
659
+ `).join('');
660
+ }
661
+
662
+ generateAnimationRows() {
663
+ return Object.entries(this.animations).map(([name, config]) => {
664
+ const type = this.getAnimType(config);
665
+ return `
666
+ <div class="anim-row">
667
+ <div class="anim-header">
668
+ <div class="anim-name">.${name}</div>
669
+ <span class="anim-type type-${type}">${type}</span>
670
+ </div>
671
+ <div class="anim-config">${JSON.stringify(config, null, 2)}</div>
672
+ <div class="usage-box">
673
+ &lt;div class="${name}"&gt;...&lt;/div&gt;
674
+ </div>
675
+ </div>
676
+ `;
677
+ }).join('');
678
+ }
679
+
680
+ getAnimType(config) {
681
+ if (config.hover) return 'hover';
682
+ if (config.active) return 'click';
683
+ if (config.targets === 'children') return 'stagger';
684
+ if (config.scrollTrigger) return 'scroll';
685
+ if (config.repeat === -1) return 'loop';
686
+ return 'entrance';
687
+ }
688
+
689
+ easeToGSAP(name) {
690
+ const map = {
691
+ smooth: 'power2.out',
692
+ bouncy: 'elastic.out(1, 0.5)',
693
+ sharp: 'power4.inOut',
694
+ back: 'back.out(1.7)',
695
+ linear: 'none'
696
+ };
697
+ return map[name] || name;
698
+ }
699
+
700
+ countTokens() {
701
+ let count = 0;
702
+ for (const [, v] of Object.entries(this.tokens)) {
703
+ if (typeof v === 'object') {
704
+ for (const [, val] of Object.entries(v)) {
705
+ if (typeof val === 'object') count += Object.keys(val).length;
706
+ else count++;
707
+ }
708
+ }
709
+ }
710
+ return count;
711
+ }
712
+ }
713
+
package/index.js ADDED
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+ // packages/cli/index.js
3
+
4
+ import { watch } from './watcher.js';
5
+ import { DocsGenerator } from './docs-generator.js';
6
+ import path from 'path';
7
+ import fs from 'fs';
8
+ import Mizumi from '../core/index.js';
9
+ import { fileURLToPath, pathToFileURL } from 'url';
10
+
11
+ // Get command from args
12
+ const args = process.argv.slice(2);
13
+ const command = args[0];
14
+
15
+ // CLI commands
16
+ const commands = {
17
+
18
+ /**
19
+ * Initialize Mizumi in current project
20
+ * npx mizumi init
21
+ */
22
+ init() {
23
+ console.log('🌊 Initializing Mizumi...\n');
24
+
25
+ // Default config template
26
+ const configTemplate = `// mizumi.config.js
27
+ export {
28
+ tokens: {
29
+ colors: {
30
+ primary: {
31
+ DEFAULT: '#3B82F6',
32
+ 50: '#EFF6FF',
33
+ 600: '#2563EB',
34
+ 900: '#1E3A8A'
35
+ },
36
+ secondary: '#8B5CF6',
37
+ neutral: {
38
+ 50: '#F9FAFB',
39
+ 100: '#F3F4F6',
40
+ 200: '#E5E7EB',
41
+ 500: '#6B7280',
42
+ 900: '#111827'
43
+ },
44
+ surface: '#FFFFFF',
45
+ text: '#111827',
46
+ success: '#10B981',
47
+ error: '#EF4444'
48
+ },
49
+
50
+ spacing: {
51
+ xs: '4px',
52
+ sm: '8px',
53
+ md: '16px',
54
+ lg: '24px',
55
+ xl: '32px',
56
+ '2xl': '48px',
57
+ '3xl': '64px'
58
+ },
59
+
60
+ typography: {
61
+ h1: { size: '48px', weight: 700, line: 1.2 },
62
+ h2: { size: '36px', weight: 600, line: 1.3 },
63
+ h3: { size: '24px', weight: 600, line: 1.4 },
64
+ body: { size: '16px', weight: 400, line: 1.6 },
65
+ small: { size: '14px', weight: 400, line: 1.5 }
66
+ },
67
+
68
+ radius: {
69
+ none: '0',
70
+ sm: '4px',
71
+ md: '8px',
72
+ lg: '16px',
73
+ full: '9999px'
74
+ },
75
+
76
+ shadows: {
77
+ none: 'none',
78
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
79
+ md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
80
+ lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
81
+ xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1)'
82
+ },
83
+
84
+ animations: {
85
+ duration: {
86
+ fast: 150,
87
+ normal: 300,
88
+ slow: 500
89
+ },
90
+ easing: {
91
+ smooth: 'power2.out',
92
+ bouncy: 'elastic.out(1, 0.5)',
93
+ sharp: 'power4.inOut'
94
+ }
95
+ }
96
+ },
97
+
98
+ patterns: {
99
+ // Layout
100
+ 'flex-center': 'flex items-center justify-center',
101
+ 'flex-between': 'flex items-center justify-between',
102
+ 'flex-col-center': 'flex flex-col items-center justify-center',
103
+
104
+ // Containers
105
+ container: 'max-w-6xl mx-auto pad-x-md',
106
+ section: 'pad-y-2xl',
107
+
108
+ // Cards
109
+ card: 'bg-surface pad-md rounded-lg shadow-md',
110
+ 'card-elevated': 'card shadow-xl',
111
+ 'card-flat': 'card shadow-sm',
112
+ 'card-hover': 'card transition cursor-pointer',
113
+
114
+ // Buttons
115
+ button: 'pad-sm pad-x-lg rounded-md cursor-pointer transition inline-flex items-center justify-center',
116
+ 'btn-primary': 'button bg-primary color-white',
117
+ 'btn-secondary': 'button bg-secondary color-white',
118
+ 'btn-ghost': 'button bg-transparent color-primary',
119
+ 'btn-outline': 'button border border-primary color-primary',
120
+
121
+ // Inputs
122
+ input: 'pad-sm rounded-md border border-neutral-200 transition',
123
+
124
+ // Text
125
+ heading: 'text-h1 color-text',
126
+ subheading: 'text-h3 color-neutral-500',
127
+ 'text-muted': 'color-neutral-500'
128
+ },
129
+
130
+ animations: {
131
+ // Entrance
132
+ 'animate-fade-in': {
133
+ from: { opacity: 0 },
134
+ to: { opacity: 1 },
135
+ duration: 300,
136
+ ease: 'power2.out'
137
+ },
138
+ 'animate-slide-up': {
139
+ from: { y: 100, opacity: 0 },
140
+ to: { y: 0, opacity: 1 },
141
+ duration: 300,
142
+ ease: 'power2.out'
143
+ },
144
+ 'animate-scale-in': {
145
+ from: { scale: 0, opacity: 0 },
146
+ to: { scale: 1, opacity: 1 },
147
+ duration: 300,
148
+ ease: 'back.out'
149
+ },
150
+
151
+ // Scroll
152
+ 'scroll-trigger': {
153
+ scrollTrigger: {
154
+ trigger: 'self',
155
+ start: 'top 80%',
156
+ toggleActions: 'play none none reverse'
157
+ }
158
+ },
159
+
160
+ // Hover
161
+ 'hover-lift': {
162
+ hover: { y: -8, duration: 150 }
163
+ },
164
+ 'hover-scale': {
165
+ hover: { scale: 1.05, duration: 150 }
166
+ },
167
+ 'hover-glow': {
168
+ hover: {
169
+ boxShadow: '0 0 20px rgba(59, 130, 246, 0.5)',
170
+ duration: 150
171
+ }
172
+ },
173
+
174
+ // Click
175
+ 'active-press': {
176
+ active: { scale: 0.95, duration: 100 }
177
+ },
178
+
179
+ // Stagger
180
+ 'stagger-children-100': {
181
+ targets: 'children',
182
+ stagger: 0.1,
183
+ from: { opacity: 0, y: 20 },
184
+ to: { opacity: 1, y: 0 }
185
+ }
186
+ },
187
+
188
+ rules: {
189
+ darkMode: false,
190
+ responsive: true,
191
+ breakpoints: {
192
+ sm: '640px',
193
+ md: '768px',
194
+ lg: '1024px',
195
+ xl: '1280px'
196
+ }
197
+ }
198
+ };
199
+ `;
200
+
201
+ // Check if config already exists
202
+ const configPath = path.join(process.cwd(), 'mizumi.config.js');
203
+
204
+ if (fs.existsSync(configPath)) {
205
+ console.log('āš ļø mizumi.config.js already exists, skipping...');
206
+ } else {
207
+ fs.writeFileSync(configPath, configTemplate);
208
+ console.log('āœ… Created: mizumi.config.js');
209
+ }
210
+
211
+ // Create .mizumi directory
212
+ const mizumiDir = path.join(process.cwd(), '.mizumi');
213
+ if (!fs.existsSync(mizumiDir)) {
214
+ fs.mkdirSync(mizumiDir);
215
+ }
216
+
217
+ // Create .gitignore entry
218
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
219
+ if (fs.existsSync(gitignorePath)) {
220
+ const gitignore = fs.readFileSync(gitignorePath, 'utf8');
221
+ if (!gitignore.includes('.mizumi')) {
222
+ fs.appendFileSync(gitignorePath, '\n# Mizumi generated files\n.mizumi/\n');
223
+ console.log('āœ… Updated: .gitignore');
224
+ }
225
+ } else {
226
+ fs.writeFileSync(gitignorePath, '# Mizumi generated files\n.mizumi/\n');
227
+ console.log('āœ… Created: .gitignore');
228
+ }
229
+
230
+ console.log('\nšŸŽ‰ Mizumi initialized successfully!');
231
+ console.log('\nNext steps:');
232
+ console.log(' 1. Edit mizumi.config.js to customize your tokens');
233
+ console.log(' 2. Run: node mizumi build');
234
+ console.log(' 3. Import .mizumi/mizumi.css in your project');
235
+ },
236
+
237
+ /**
238
+ * Build CSS from config
239
+ * npx mizumi build
240
+ */
241
+ build: async function() {
242
+ console.log('🌊 Building Mizumi CSS...\n');
243
+
244
+ // Find config file
245
+ const configPath = path.join(process.cwd(), 'mizumi.config.js');
246
+ if (!fs.existsSync(configPath)) {
247
+ console.error('āŒ mizumi.config.js not found!');
248
+ console.error(' Run: node mizumi init');
249
+ process.exit(1);
250
+ }
251
+
252
+ // Load config
253
+ const configModule = await import(pathToFileURL(configPath).href);
254
+ const config = configModule.default || configModule; // ← handles both ESM and CommonJS
255
+
256
+ // Generate CSS + Runtime JS
257
+ const mizumi = new Mizumi(config);
258
+
259
+
260
+ const outDir = path.join(process.cwd(), '.mizumi');
261
+ mizumi.build(outDir); // ← now passes directory, not file path
262
+
263
+ console.log('\nšŸŽ‰ Build complete!');
264
+ console.log(' Add to your HTML:');
265
+ console.log(' <link rel="stylesheet" href=".mizumi/mizumi.css">');
266
+ console.log(' <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>');
267
+ console.log(' <script src=".mizumi/mizumi-runtime.js"></script>');
268
+ },
269
+
270
+
271
+
272
+
273
+ /**
274
+ * List all patterns
275
+ * npx mizumi list
276
+ */
277
+ list: async function() {
278
+ const configPath = path.join(process.cwd(), 'mizumi.config.js');
279
+
280
+ if (!fs.existsSync(configPath)) {
281
+ console.error('āŒ mizumi.config.js not found! Run: node mizumi init');
282
+ process.exit(1);
283
+ }
284
+
285
+
286
+ const configModule = await import(pathToFileURL(configPath).href);
287
+ const config = configModule.default || configModule; // ← handles both ESM and CommonJS
288
+
289
+ // Generate CSS + Runtime JS
290
+ const mizumi = new Mizumi(config);
291
+
292
+ console.log('\n🌊 Mizumi Patterns:\n');
293
+
294
+ for (const [name, value] of Object.entries(config.patterns || {})) {
295
+ const expanded = mizumi.expandClassName(name);
296
+ console.log(` .${name}`);
297
+ console.log(` → ${expanded}`);
298
+ console.log('');
299
+ }
300
+
301
+ console.log('\n🌊 Mizumi Animations:\n');
302
+
303
+ for (const [name, value] of Object.entries(config.animations || {})) {
304
+ console.log(` .${name}`);
305
+ console.log(` → GSAP: ${JSON.stringify(value).slice(0, 60)}...`);
306
+ console.log('');
307
+ }
308
+ },
309
+
310
+ /**
311
+ * Explain what a class does
312
+ * npx mizumi explain "card"
313
+ */
314
+ explain: async function() {
315
+ const className = args[1];
316
+
317
+ if (!className) {
318
+ console.error('āŒ Please provide a class name');
319
+ console.error(' Usage: node mizumi explain "card"');
320
+ process.exit(1);
321
+ }
322
+
323
+ const configPath = path.join(process.cwd(), 'mizumi.config.js');
324
+
325
+ const configModule = await import(pathToFileURL(configPath).href);
326
+ const config = configModule.default || configModule;
327
+
328
+ const mizumi = new Mizumi(config);
329
+
330
+ console.log(`\n🌊 Explaining: "${className}"\n`);
331
+
332
+ const classes = className.split(' ');
333
+
334
+ for (const cls of classes) {
335
+ // Check if it's a pattern
336
+ if (config.patterns?.[cls]) {
337
+ const expanded = mizumi.expandClassName(cls);
338
+ console.log(` Pattern: .${cls}`);
339
+ console.log(` Expands to: ${expanded}`);
340
+ }
341
+ // Check if it's an animation
342
+ else if (config.animations?.[cls]) {
343
+ console.log(` Animation: .${cls}`);
344
+ console.log(` Config: ${JSON.stringify(config.animations[cls], null, 4)}`);
345
+ }
346
+ // Check if it's a utility
347
+ else {
348
+ console.log(` Utility: .${cls}`);
349
+ console.log(` (Built-in utility class)`);
350
+ }
351
+ console.log('');
352
+ }
353
+ },
354
+
355
+ watch() {
356
+ watch();
357
+ },
358
+
359
+ /**
360
+ * Show help
361
+ */
362
+ help() {
363
+ console.log(`
364
+ 🌊 Mizumi CSS Framework v0.1.0
365
+
366
+ Commands:
367
+ init Initialize Mizumi in project
368
+ build Generate CSS + runtime JS
369
+ watch Watch config and auto-rebuild
370
+ docs Generate documentation page
371
+ list List all patterns & animations
372
+ explain <class> Explain what a class does
373
+ help Show help
374
+
375
+ Examples:
376
+ node mizumi init
377
+ node mizumi build
378
+ node mizumi watch
379
+ node mizumi list
380
+ node mizumi explain "card animate-fade-in"
381
+ `);
382
+ },
383
+
384
+
385
+
386
+ docs: async function() {
387
+ console.log('🌊 Generating Mizumi Docs...\n');
388
+ const cfgPath = path.join(process.cwd(), 'mizumi.config.js');
389
+ if (!fs.existsSync(cfgPath)) {
390
+ console.error('āŒ mizumi.config.js not found!');
391
+ process.exit(1);
392
+ }
393
+
394
+ const configModule = await import(pathToFileURL(cfgPath).href);
395
+ const config = configModule.default || configModule;
396
+
397
+ const generator = new DocsGenerator(config);
398
+ const html = generator.generate();
399
+ const outDir = path.join(process.cwd(), '.mizumi');
400
+ const outPath = path.join(outDir, 'docs.html');
401
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir);
402
+ fs.writeFileSync(outPath, html);
403
+ console.log(`āœ… Docs generated: ${outPath}`);
404
+ console.log('\nšŸŽ‰ Open in browser:');
405
+ console.log(` .mizumi/docs.html`);
406
+ },
407
+
408
+ };
409
+
410
+ // Run command
411
+ if (commands[command]) {
412
+ await commands[command](); // top-level await is allowed in ESM
413
+ } else {
414
+ console.error(`āŒ Unknown command: ${command}`);
415
+ commands.help();
416
+ }
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@mizumi25/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Mizumi CSS framework",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "mizumi": "./index.js"
9
+ },
10
+ "keywords": ["cli", "mizumi", "css", "design-tokens"],
11
+ "author": "Mizumi Kaito / James Rafty Damasing Libago",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/Mizumi25/MizumiPackage"
16
+ },
17
+ "dependencies": {
18
+ "chokidar": "^4.0.3"
19
+ }
20
+ }
package/watcher.js ADDED
@@ -0,0 +1,56 @@
1
+ // packages/cli/watcher.js
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { DocsGenerator } from './docs-generator.js';
5
+ import Mizumi from '../core/index.js';
6
+
7
+
8
+ function watch() {
9
+ const configPath = path.join(process.cwd(), 'mizumi.config.js');
10
+ const outDir = path.join(process.cwd(), '.mizumi');
11
+
12
+ if (!fs.existsSync(configPath)) {
13
+ console.error('āŒ mizumi.config.js not found! Run: node mizumi init');
14
+ process.exit(1);
15
+ }
16
+
17
+ console.log('🌊 Mizumi watching for changes...');
18
+ console.log(` Watching: mizumi.config.js`);
19
+ console.log(' Press Ctrl+C to stop\n');
20
+
21
+ // Run initial build
22
+ runBuild(configPath, outDir);
23
+
24
+ // Watch the config file
25
+ const watcher = chokidar.watch(configPath, {
26
+ persistent: true,
27
+ ignoreInitial: true
28
+ });
29
+
30
+ watcher.on('change', () => {
31
+ console.log('\nšŸ“ Config changed! Rebuilding...');
32
+ runBuild(configPath, outDir);
33
+ });
34
+
35
+ watcher.on('error', err => {
36
+ console.error('āŒ Watcher error:', err);
37
+ });
38
+ }
39
+
40
+ function runBuild(configPath, outDir) {
41
+ try {
42
+ // Clear require cache so config changes are picked up
43
+ delete require.cache[require.resolve(configPath)];
44
+
45
+ const config = require(configPath);
46
+ const mizumi = new Mizumi(config);
47
+ mizumi.build(outDir);
48
+
49
+ console.log(`ā° ${new Date().toLocaleTimeString()} — Ready!\n`);
50
+ } catch (err) {
51
+ console.error('āŒ Build failed:', err.message);
52
+ console.log(' Fix the error and save again...\n');
53
+ }
54
+ }
55
+
56
+ export { watch };