mobile-snap 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.html ADDED
@@ -0,0 +1,862 @@
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MobileSnap - Dokumentasi & Arsitektur</title>
7
+ <!-- Google Fonts -->
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;600;800&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
11
+
12
+ <style>
13
+ :root {
14
+ --primary: #6366f1;
15
+ --primary-light: #818cf8;
16
+ --primary-glow: rgba(99, 102, 241, 0.15);
17
+ --secondary: #d946ef;
18
+ --secondary-glow: rgba(217, 70, 239, 0.15);
19
+ --accent: #10b981;
20
+ --accent-glow: rgba(16, 185, 129, 0.15);
21
+ --warning: #f59e0b;
22
+ --warning-glow: rgba(245, 158, 11, 0.15);
23
+ --bg-dark: #090a0f;
24
+ --bg-card: rgba(255, 255, 255, 0.03);
25
+ --border-card: rgba(255, 255, 255, 0.08);
26
+ --text-main: #f3f4f6;
27
+ --text-muted: #9ca3af;
28
+ }
29
+
30
+ * {
31
+ box-sizing: border-box;
32
+ margin: 0;
33
+ padding: 0;
34
+ }
35
+
36
+ body {
37
+ font-family: 'Inter', sans-serif;
38
+ background-color: var(--bg-dark);
39
+ background-image:
40
+ radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.05) 0%, transparent 40%),
41
+ radial-gradient(circle at 80% 80%, rgba(217, 70, 239, 0.05) 0%, transparent 40%);
42
+ color: var(--text-main);
43
+ line-height: 1.6;
44
+ -webkit-font-smoothing: antialiased;
45
+ overflow-x: hidden;
46
+ }
47
+
48
+ /* Scrollbar */
49
+ ::-webkit-scrollbar {
50
+ width: 8px;
51
+ }
52
+ ::-webkit-scrollbar-track {
53
+ background: #090a0f;
54
+ }
55
+ ::-webkit-scrollbar-thumb {
56
+ background: #1f2937;
57
+ border-radius: 4px;
58
+ }
59
+ ::-webkit-scrollbar-thumb:hover {
60
+ background: #374151;
61
+ }
62
+
63
+ header {
64
+ position: relative;
65
+ padding: 5rem 2rem 3rem 2rem;
66
+ text-align: center;
67
+ overflow: hidden;
68
+ }
69
+
70
+ .header-glow {
71
+ position: absolute;
72
+ top: -150px;
73
+ left: 50%;
74
+ transform: translateX(-50%);
75
+ width: 500px;
76
+ height: 250px;
77
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.3) 0%, transparent 70%);
78
+ filter: blur(40px);
79
+ z-index: 0;
80
+ pointer-events: none;
81
+ }
82
+
83
+ .container {
84
+ max-width: 1100px;
85
+ margin: 0 auto;
86
+ padding: 0 1.5rem;
87
+ position: relative;
88
+ z-index: 10;
89
+ }
90
+
91
+ h1 {
92
+ font-family: 'Outfit', sans-serif;
93
+ font-size: 3.5rem;
94
+ font-weight: 800;
95
+ margin-bottom: 1rem;
96
+ background: linear-gradient(135deg, #fff 30%, #818cf8 100%);
97
+ -webkit-background-clip: text;
98
+ -webkit-text-fill-color: transparent;
99
+ letter-spacing: -0.03em;
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 0.5rem;
103
+ }
104
+
105
+ .tagline {
106
+ font-size: 1.2rem;
107
+ color: var(--text-muted);
108
+ max-width: 600px;
109
+ margin: 0 auto 2rem auto;
110
+ font-weight: 300;
111
+ }
112
+
113
+ .badge-container {
114
+ display: flex;
115
+ justify-content: center;
116
+ gap: 0.75rem;
117
+ margin-bottom: 2.5rem;
118
+ }
119
+
120
+ .badge {
121
+ background: rgba(255, 255, 255, 0.05);
122
+ border: 1px solid var(--border-card);
123
+ padding: 0.4rem 1rem;
124
+ border-radius: 100px;
125
+ font-size: 0.85rem;
126
+ font-weight: 500;
127
+ color: var(--primary-light);
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 0.4rem;
131
+ }
132
+
133
+ .badge-accent {
134
+ color: var(--accent);
135
+ border-color: rgba(16, 185, 129, 0.3);
136
+ }
137
+
138
+ /* Glass Card */
139
+ .glass-card {
140
+ background: var(--bg-card);
141
+ border: 1px solid var(--border-card);
142
+ border-radius: 16px;
143
+ padding: 2.5rem;
144
+ margin-bottom: 2rem;
145
+ backdrop-filter: blur(12px);
146
+ transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
147
+ }
148
+
149
+ .glass-card:hover {
150
+ transform: translateY(-2px);
151
+ border-color: rgba(99, 102, 241, 0.25);
152
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
153
+ }
154
+
155
+ h2 {
156
+ font-family: 'Outfit', sans-serif;
157
+ font-size: 1.8rem;
158
+ font-weight: 600;
159
+ margin-bottom: 1.5rem;
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 0.6rem;
163
+ color: #fff;
164
+ }
165
+
166
+ h3 {
167
+ font-family: 'Outfit', sans-serif;
168
+ font-size: 1.3rem;
169
+ font-weight: 600;
170
+ margin: 1.5rem 0 1rem 0;
171
+ color: #fff;
172
+ border-left: 3px solid var(--primary);
173
+ padding-left: 0.5rem;
174
+ }
175
+
176
+ /* Grid Layout */
177
+ .grid-2 {
178
+ display: grid;
179
+ grid-template-columns: 1fr 1fr;
180
+ gap: 1.5rem;
181
+ }
182
+
183
+ .grid-3 {
184
+ display: grid;
185
+ grid-template-columns: 1fr 1fr 1fr;
186
+ gap: 1.5rem;
187
+ }
188
+
189
+ @media (max-width: 768px) {
190
+ .grid-2, .grid-3 {
191
+ grid-template-columns: 1fr;
192
+ }
193
+ h1 {
194
+ font-size: 2.5rem;
195
+ }
196
+ }
197
+
198
+ /* Architecture Steps Visual */
199
+ .arch-flow {
200
+ display: grid;
201
+ grid-template-columns: repeat(4, 1fr);
202
+ gap: 1rem;
203
+ margin-top: 2rem;
204
+ }
205
+
206
+ @media (max-width: 820px) {
207
+ .arch-flow {
208
+ grid-template-columns: 1fr;
209
+ }
210
+ }
211
+
212
+ .arch-node {
213
+ background: rgba(255, 255, 255, 0.02);
214
+ border: 1px solid var(--border-card);
215
+ border-radius: 12px;
216
+ padding: 1.25rem;
217
+ text-align: center;
218
+ position: relative;
219
+ transition: all 0.3s ease;
220
+ }
221
+
222
+ .arch-node:hover {
223
+ border-color: var(--primary);
224
+ background: rgba(99, 102, 241, 0.05);
225
+ }
226
+
227
+ .node-num {
228
+ width: 28px;
229
+ height: 28px;
230
+ background: var(--primary);
231
+ color: white;
232
+ border-radius: 50%;
233
+ display: flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ margin: 0 auto 0.75rem auto;
237
+ font-weight: bold;
238
+ font-size: 0.85rem;
239
+ box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
240
+ }
241
+
242
+ .arch-node h4 {
243
+ font-family: 'Outfit', sans-serif;
244
+ margin-bottom: 0.4rem;
245
+ font-size: 1rem;
246
+ }
247
+
248
+ .arch-node p {
249
+ font-size: 0.78rem;
250
+ color: var(--text-muted);
251
+ }
252
+
253
+ /* Device Visual Mockup */
254
+ .device-showcase {
255
+ display: grid;
256
+ grid-template-columns: 1fr 1fr;
257
+ gap: 1.5rem;
258
+ margin-top: 0.5rem;
259
+ }
260
+
261
+ @media (max-width: 640px) {
262
+ .device-showcase {
263
+ grid-template-columns: 1fr;
264
+ }
265
+ }
266
+
267
+ .device-card {
268
+ background: rgba(255, 255, 255, 0.02);
269
+ border: 1px solid var(--border-card);
270
+ border-radius: 16px;
271
+ padding: 1.25rem;
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 1.25rem;
275
+ transition: border-color 0.3s ease;
276
+ }
277
+
278
+ .device-card:hover {
279
+ border-color: var(--secondary);
280
+ background: rgba(217, 70, 239, 0.02);
281
+ }
282
+
283
+ .iphone-frame {
284
+ width: 60px;
285
+ height: 120px;
286
+ border: 4px solid #1f2937;
287
+ border-radius: 12px;
288
+ background: #000;
289
+ position: relative;
290
+ flex-shrink: 0;
291
+ display: flex;
292
+ flex-direction: column;
293
+ justify-content: space-between;
294
+ padding: 4px;
295
+ box-shadow: 0 10px 20px rgba(0,0,0,0.5);
296
+ }
297
+
298
+ .iphone-notch {
299
+ width: 25px;
300
+ height: 5px;
301
+ background: #1f2937;
302
+ border-radius: 0 0 4px 4px;
303
+ margin: 0 auto;
304
+ position: absolute;
305
+ top: 0;
306
+ left: 50%;
307
+ transform: translateX(-50%);
308
+ }
309
+
310
+ .iphone-screen {
311
+ flex-grow: 1;
312
+ background: linear-gradient(135deg, #1e1b4b 0%, #311042 100%);
313
+ border-radius: 6px;
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: center;
317
+ color: rgba(255, 255, 255, 0.2);
318
+ font-size: 0.55rem;
319
+ font-weight: bold;
320
+ }
321
+
322
+ .device-info h4 {
323
+ font-family: 'Outfit', sans-serif;
324
+ font-size: 1.1rem;
325
+ margin-bottom: 0.2rem;
326
+ color: #fff;
327
+ }
328
+
329
+ .device-info .resolution {
330
+ color: var(--secondary);
331
+ font-weight: 700;
332
+ font-size: 0.9rem;
333
+ margin-bottom: 0.3rem;
334
+ }
335
+
336
+ .device-info p {
337
+ font-size: 0.78rem;
338
+ color: var(--text-muted);
339
+ }
340
+
341
+ /* Terminal Window */
342
+ .terminal {
343
+ background: #0b0c10;
344
+ border: 1px solid var(--border-card);
345
+ border-radius: 8px;
346
+ overflow: hidden;
347
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
348
+ margin-top: 1rem;
349
+ }
350
+
351
+ .terminal-header {
352
+ background: #16171d;
353
+ padding: 0.75rem 1rem;
354
+ display: flex;
355
+ align-items: center;
356
+ justify-content: space-between;
357
+ border-bottom: 1px solid var(--border-card);
358
+ }
359
+
360
+ .terminal-dots {
361
+ display: flex;
362
+ gap: 6px;
363
+ }
364
+
365
+ .dot {
366
+ width: 10px;
367
+ height: 10px;
368
+ border-radius: 50%;
369
+ }
370
+
371
+ .dot-red { background: #ef4444; }
372
+ .dot-yellow { background: #eab308; }
373
+ .dot-green { background: #22c55e; }
374
+
375
+ .terminal-title {
376
+ font-size: 0.75rem;
377
+ font-family: 'JetBrains Mono', monospace;
378
+ color: var(--text-muted);
379
+ }
380
+
381
+ .terminal-body {
382
+ padding: 1.25rem;
383
+ font-family: 'JetBrains Mono', monospace;
384
+ font-size: 0.88rem;
385
+ color: #d1d5db;
386
+ overflow-x: auto;
387
+ position: relative;
388
+ }
389
+
390
+ .terminal-line {
391
+ margin-bottom: 0.4rem;
392
+ }
393
+
394
+ .terminal-prompt {
395
+ color: var(--primary-light);
396
+ margin-right: 0.5rem;
397
+ user-select: none;
398
+ }
399
+
400
+ .terminal-comment {
401
+ color: #6b7280;
402
+ user-select: none;
403
+ }
404
+
405
+ /* Copy Button */
406
+ .btn-copy {
407
+ position: absolute;
408
+ top: 10px;
409
+ right: 10px;
410
+ background: rgba(255, 255, 255, 0.05);
411
+ border: 1px solid var(--border-card);
412
+ color: var(--text-muted);
413
+ padding: 0.35rem 0.7rem;
414
+ border-radius: 6px;
415
+ font-size: 0.75rem;
416
+ cursor: pointer;
417
+ transition: all 0.2s ease;
418
+ font-family: 'Inter', sans-serif;
419
+ }
420
+
421
+ .btn-copy:hover {
422
+ background: var(--primary);
423
+ color: #fff;
424
+ border-color: var(--primary-light);
425
+ }
426
+
427
+ /* Command Builder */
428
+ .builder-container {
429
+ background: rgba(255, 255, 255, 0.02);
430
+ border: 1px solid var(--border-card);
431
+ border-radius: 12px;
432
+ padding: 1.5rem;
433
+ margin-top: 1.5rem;
434
+ }
435
+
436
+ .form-group {
437
+ margin-bottom: 1rem;
438
+ }
439
+
440
+ .form-group label {
441
+ display: block;
442
+ font-size: 0.85rem;
443
+ font-weight: 500;
444
+ color: var(--text-muted);
445
+ margin-bottom: 0.4rem;
446
+ }
447
+
448
+ .form-control {
449
+ width: 100%;
450
+ background: #111318;
451
+ border: 1px solid var(--border-card);
452
+ border-radius: 6px;
453
+ padding: 0.6rem 0.8rem;
454
+ color: #fff;
455
+ font-family: inherit;
456
+ font-size: 0.9rem;
457
+ transition: border-color 0.2s ease;
458
+ }
459
+
460
+ .form-control:focus {
461
+ outline: none;
462
+ border-color: var(--primary);
463
+ }
464
+
465
+ .builder-output {
466
+ margin-top: 1.5rem;
467
+ padding: 1rem;
468
+ background: #090a0e;
469
+ border-radius: 8px;
470
+ border-left: 3px solid var(--primary);
471
+ display: flex;
472
+ align-items: center;
473
+ justify-content: space-between;
474
+ gap: 1rem;
475
+ }
476
+
477
+ .builder-cmd {
478
+ font-family: 'JetBrains Mono', monospace;
479
+ font-size: 0.88rem;
480
+ color: #f3f4f6;
481
+ word-break: break-all;
482
+ }
483
+
484
+ .btn-action-copy {
485
+ background: var(--primary);
486
+ color: white;
487
+ border: none;
488
+ padding: 0.5rem 1rem;
489
+ border-radius: 6px;
490
+ font-size: 0.8rem;
491
+ font-weight: 600;
492
+ cursor: pointer;
493
+ transition: background 0.2s ease;
494
+ flex-shrink: 0;
495
+ }
496
+
497
+ .btn-action-copy:hover {
498
+ background: var(--primary-light);
499
+ }
500
+
501
+ /* Warnings & Alert boxes */
502
+ .alert-box {
503
+ border-radius: 12px;
504
+ padding: 1.25rem;
505
+ margin-bottom: 1.25rem;
506
+ display: flex;
507
+ gap: 1rem;
508
+ border: 1px solid transparent;
509
+ }
510
+
511
+ .alert-warning {
512
+ background: var(--warning-glow);
513
+ border-color: rgba(245, 158, 11, 0.2);
514
+ }
515
+
516
+ .alert-icon {
517
+ font-size: 1.5rem;
518
+ line-height: 1;
519
+ }
520
+
521
+ .alert-text h5 {
522
+ font-family: 'Outfit', sans-serif;
523
+ font-size: 1.05rem;
524
+ color: #fff;
525
+ margin-bottom: 0.2rem;
526
+ }
527
+
528
+ .alert-text p {
529
+ font-size: 0.85rem;
530
+ color: var(--text-muted);
531
+ }
532
+
533
+ /* Table */
534
+ .option-table {
535
+ width: 100%;
536
+ border-collapse: collapse;
537
+ margin-top: 1rem;
538
+ font-size: 0.9rem;
539
+ }
540
+
541
+ .option-table th, .option-table td {
542
+ padding: 0.8rem 1rem;
543
+ text-align: left;
544
+ border-bottom: 1px solid var(--border-card);
545
+ }
546
+
547
+ .option-table th {
548
+ background: rgba(255, 255, 255, 0.02);
549
+ color: #fff;
550
+ font-weight: 600;
551
+ }
552
+
553
+ .option-table tr:hover td {
554
+ background: rgba(255, 255, 255, 0.01);
555
+ }
556
+
557
+ .param-tag {
558
+ font-family: 'JetBrains Mono', monospace;
559
+ background: rgba(99, 102, 241, 0.1);
560
+ color: var(--primary-light);
561
+ padding: 0.15rem 0.4rem;
562
+ border-radius: 4px;
563
+ font-size: 0.8rem;
564
+ border: 1px solid rgba(99, 102, 241, 0.2);
565
+ }
566
+
567
+ footer {
568
+ text-align: center;
569
+ padding: 4rem 2rem;
570
+ color: var(--text-muted);
571
+ font-size: 0.85rem;
572
+ border-top: 1px solid var(--border-card);
573
+ margin-top: 4rem;
574
+ }
575
+ </style>
576
+ </head>
577
+ <body>
578
+
579
+ <header>
580
+ <div class="header-glow"></div>
581
+ <div class="container">
582
+ <h1>MobileSnap 📸</h1>
583
+ <p class="tagline">Otomasi Pixel-Precise App Store & Play Store Screenshots dari Server Lokal secara Dual-Vendor menggunakan NPX.</p>
584
+
585
+ <div class="badge-container">
586
+ <div class="badge">
587
+ <span>Node.js / NPX</span>
588
+ </div>
589
+ <div class="badge">
590
+ <span>Playwright</span>
591
+ </div>
592
+ <div class="badge badge-accent">
593
+ <span>iOS & Android</span>
594
+ </div>
595
+ </div>
596
+ </div>
597
+ </header>
598
+
599
+ <div class="container">
600
+ <!-- Arsitektur Utama -->
601
+ <section class="glass-card">
602
+ <h2>🏗️ Arsitektur Aliran Kerja</h2>
603
+ <p style="color: var(--text-muted); margin-bottom: 1.5rem;">MobileSnap dibangun menggunakan <strong>Commander</strong> untuk antarmuka CLI yang kaya, dan <strong>Playwright Node API</strong> untuk mengontrol Chromium secara berurutan pada berbagai dimensi.</p>
604
+
605
+ <div class="arch-flow">
606
+ <div class="arch-node">
607
+ <div class="node-num">1</div>
608
+ <h4>CLI Parser</h4>
609
+ <p>Membaca parameter URL, rute (paths), platform target, dan folder keluaran.</p>
610
+ </div>
611
+ <div class="arch-node">
612
+ <div class="node-num">2</div>
613
+ <h4>Browser Engine</h4>
614
+ <p>Inisiasi Chromium secara terisolasi dan asinkronus.</p>
615
+ </div>
616
+ <div class="arch-node">
617
+ <div class="node-num">3</div>
618
+ <h4>Viewport Emulation</h4>
619
+ <p>Meniru dimensi layar Retina (iOS) & Android secara dinamis.</p>
620
+ </div>
621
+ <div class="arch-node">
622
+ <div class="node-num">4</div>
623
+ <h4>Hydration Check</h4>
624
+ <p>Menunggu aktivitas jaringan idle agar rendering JavaScript selesai.</p>
625
+ </div>
626
+ </div>
627
+ </section>
628
+
629
+ <!-- Target Dimensi -->
630
+ <section class="glass-card">
631
+ <h2>📱 Dimensi Layar Target (Wajib Store)</h2>
632
+
633
+ <h3>🍎 iOS (Apple App Store)</h3>
634
+ <div class="device-showcase">
635
+ <div class="device-card">
636
+ <div class="iphone-frame">
637
+ <div class="iphone-notch"></div>
638
+ <div class="iphone-screen">6.7"</div>
639
+ </div>
640
+ <div class="device-info">
641
+ <h4>iPhone 6.7" Display</h4>
642
+ <div class="resolution">1290 x 2796 pixels</div>
643
+ <p>Digunakan untuk jajaran iPhone Max/Pro Max terbaru.</p>
644
+ <p style="margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem;">Berkas: 6.7_inch_[path].png</p>
645
+ </div>
646
+ </div>
647
+
648
+ <div class="device-card">
649
+ <div class="iphone-frame">
650
+ <div class="iphone-notch"></div>
651
+ <div class="iphone-screen">6.5"</div>
652
+ </div>
653
+ <div class="device-info">
654
+ <h4>iPhone 6.5" Display</h4>
655
+ <div class="resolution">1242 x 2688 pixels</div>
656
+ <p>Digunakan untuk seri iPhone Xs Max / 11 Pro Max.</p>
657
+ <p style="margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem;">Berkas: 6.5_inch_[path].png</p>
658
+ </div>
659
+ </div>
660
+ </div>
661
+
662
+ <h3 style="margin-top: 2rem;">🤖 Android (Google Play Store)</h3>
663
+ <div class="device-showcase">
664
+ <div class="device-card">
665
+ <div class="iphone-frame" style="border-radius: 8px;">
666
+ <!-- Android style frame (no notch) -->
667
+ <div class="iphone-screen">Android</div>
668
+ </div>
669
+ <div class="device-info">
670
+ <h4>Android Phone</h4>
671
+ <div class="resolution">1080 x 2400 pixels</div>
672
+ <p>Rasio aspek modern 20:9 untuk Google Play Phone screenshots.</p>
673
+ <p style="margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem;">Berkas: android_phone_[path].png</p>
674
+ </div>
675
+ </div>
676
+
677
+ <div class="device-card">
678
+ <div class="iphone-frame" style="width: 100px; height: 70px; border-radius: 8px;">
679
+ <!-- Tablet style landscape/wide -->
680
+ <div class="iphone-screen">Tablet</div>
681
+ </div>
682
+ <div class="device-info">
683
+ <h4>Android Tablet (10")</h4>
684
+ <div class="resolution">1600 x 2560 pixels</div>
685
+ <p>Dimensi tablet standar untuk kebutuhan rilis Google Play Tablet.</p>
686
+ <p style="margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem;">Berkas: android_tablet_[path].png</p>
687
+ </div>
688
+ </div>
689
+ </div>
690
+ </section>
691
+
692
+ <!-- Cara Instalasi -->
693
+ <section class="glass-card">
694
+ <h2>🚀 Cara Penggunaan Instan (NPX)</h2>
695
+ <p style="color: var(--text-muted); margin-bottom: 1rem;">Anda tidak perlu melakukan penyiapan manual. Cukup gunakan <code>npx</code> untuk menjalankan MobileSnap.</p>
696
+
697
+ <div class="terminal">
698
+ <div class="terminal-header">
699
+ <div class="terminal-dots">
700
+ <div class="dot dot-red"></div>
701
+ <div class="dot dot-yellow"></div>
702
+ <div class="dot dot-green"></div>
703
+ </div>
704
+ <div class="terminal-title">PowerShell - Setup</div>
705
+ </div>
706
+ <div class="terminal-body">
707
+ <button class="btn-copy" onclick="copyText('setup-cmds')">Copy</button>
708
+ <pre id="setup-cmds" style="font-family: inherit;">
709
+ <span class="terminal-comment"># 1. Jalankan instan menggunakan npx (ganti port sesuai server lokal Anda)</span>
710
+ <span class="terminal-prompt">PS></span>npx mobile-snap --url http://localhost:4321
711
+
712
+ <span class="terminal-comment"># 2. Opsional: Unduh browser binaries jika Playwright belum dikonfigurasi di sistem</span>
713
+ <span class="terminal-prompt">PS></span>npx playwright install chromium</pre>
714
+ </div>
715
+ </div>
716
+ </section>
717
+
718
+ <!-- Interaktif Command Builder -->
719
+ <section class="glass-card">
720
+ <h2>⚡ CLI Command Builder (NPM / NPX)</h2>
721
+ <p style="color: var(--text-muted);">Gunakan builder interaktif ini untuk membuat dan menyalin perintah sesuai kebutuhan pengembangan Anda:</p>
722
+
723
+ <div class="builder-container">
724
+ <div class="grid-3">
725
+ <div class="form-group">
726
+ <label for="input-url">URL Dev Server Lokal</label>
727
+ <input type="text" id="input-url" class="form-control" value="http://localhost:4321" oninput="updateCommand()">
728
+ </div>
729
+ <div class="form-group">
730
+ <label for="input-paths">Rute / Paths (pisahkan koma)</label>
731
+ <input type="text" id="input-paths" class="form-control" value="/, /scan, /profile" oninput="updateCommand()">
732
+ </div>
733
+ <div class="form-group">
734
+ <label for="input-platform">Platform Target</label>
735
+ <select id="input-platform" class="form-control" onchange="updateCommand()">
736
+ <option value="ios">iOS (App Store)</option>
737
+ <option value="android">Android (Play Store)</option>
738
+ <option value="both">Both (iOS & Android Sekaligus)</option>
739
+ </select>
740
+ </div>
741
+ </div>
742
+ <div class="form-group">
743
+ <label for="input-output">Folder Output</label>
744
+ <input type="text" id="input-output" class="form-control" value="mobilesnap_output" oninput="updateCommand()">
745
+ </div>
746
+
747
+ <div class="builder-output">
748
+ <div class="builder-cmd" id="cmd-preview">npx mobile-snap --url http://localhost:4321 --paths "/, /scan, /profile" --output mobilesnap_output</div>
749
+ <button class="btn-action-copy" onclick="copyText('cmd-preview', true)">Copy Command</button>
750
+ </div>
751
+ </div>
752
+ </section>
753
+
754
+ <!-- Detail Opsi Parameter -->
755
+ <section class="glass-card">
756
+ <h2>⚙️ Parameter & Opsi CLI</h2>
757
+ <table class="option-table">
758
+ <thead>
759
+ <tr>
760
+ <th>Parameter</th>
761
+ <th>Alias</th>
762
+ <th>Deskripsi</th>
763
+ <th>Bawaan (Default)</th>
764
+ </tr>
765
+ </thead>
766
+ <tbody>
767
+ <tr>
768
+ <td><span class="param-tag">--url</span></td>
769
+ <td><span class="param-tag">-u</span></td>
770
+ <td><strong>(Wajib)</strong> URL server lokal yang sedang berjalan (contoh: Astro).</td>
771
+ <td>-</td>
772
+ </tr>
773
+ <tr>
774
+ <td><span class="param-tag">--paths</span></td>
775
+ <td><span class="param-tag">-p</span></td>
776
+ <td>Daftar rute halaman yang dipisahkan dengan tanda koma.</td>
777
+ <td><code style="color: var(--secondary);">"/"</code></td>
778
+ </tr>
779
+ <tr>
780
+ <td><span class="param-tag">--platform</span></td>
781
+ <td><span class="param-tag">-l</span></td>
782
+ <td>Platform yang ingin ditangkap: <code style="color: var(--secondary);">"ios"</code>, <code style="color: var(--secondary);">"android"</code>, atau <code style="color: var(--secondary);">"both"</code>.</td>
783
+ <td><code style="color: var(--secondary);">"ios"</code></td>
784
+ </tr>
785
+ <tr>
786
+ <td><span class="param-tag">--output</span></td>
787
+ <td><span class="param-tag">-o</span></td>
788
+ <td>Folder tujuan untuk menaruh berkas gambar hasil tangkapan.</td>
789
+ <td><code style="color: var(--secondary);">"mobilesnap_output"</code></td>
790
+ </tr>
791
+ </tbody>
792
+ </table>
793
+ </section>
794
+
795
+ <!-- Penanganan Masalah & Ketahanan -->
796
+ <section class="glass-card">
797
+ <h2>🛡️ Ketahanan Sistem & Fitur Khusus</h2>
798
+
799
+ <div class="alert-box alert-warning">
800
+ <div class="alert-icon">💡</div>
801
+ <div class="alert-text">
802
+ <h5>Skema Protokol URL & Auto-Normalisasi</h5>
803
+ <p>MobileSnap secara otomatis mendeteksi jika URL tidak memiliki protokol (seperti <code>localhost:3000</code>) dan menambahkan <code>http://</code> secara dinamis.</p>
804
+ </div>
805
+ </div>
806
+
807
+ <div class="alert-box alert-warning">
808
+ <div class="alert-icon">🌐</div>
809
+ <div class="alert-text">
810
+ <h5>User-Agent Dinamis per Platform</h5>
811
+ <p>Ketika platform Android berjalan, mesin otomatisasi mengemulasi Agen Pengguna Android 13 (Pixel 7). Sedangkan untuk platform iOS, digunakan Agen Pengguna iPhone. Ini menjamin server memberikan respon tampilan yang tepat sesuai target vendor.</p>
812
+ </div>
813
+ </div>
814
+
815
+ <div class="alert-box alert-warning">
816
+ <div class="alert-icon">📁</div>
817
+ <div class="alert-text">
818
+ <h5>Skala Resolusi Retina (DPI 3x)</h5>
819
+ <p>Semua screenshot ditangkap dengan pengaturan <code>deviceScaleFactor: 3</code> untuk menjaga kualitas output tetap tajam untuk standar unggahan store resmi.</p>
820
+ </div>
821
+ </div>
822
+ </section>
823
+ </div>
824
+
825
+ <footer>
826
+ <p>&copy; 2026 MobileSnap CLI Tool. Dibuat dengan presisi visual untuk kebutuhan App Store & Play Store Anda.</p>
827
+ </footer>
828
+
829
+ <script>
830
+ function updateCommand() {
831
+ const url = document.getElementById('input-url').value.trim() || 'http://localhost:4321';
832
+ const paths = document.getElementById('input-paths').value.trim();
833
+ const output = document.getElementById('input-output').value.trim() || 'mobilesnap_output';
834
+ const platform = document.getElementById('input-platform').value;
835
+
836
+ let command = `npx mobile-snap --url ${url}`;
837
+ if (paths) {
838
+ command += ` --paths "${paths}"`;
839
+ }
840
+ if (platform !== 'ios') {
841
+ command += ` --platform ${platform}`;
842
+ }
843
+ if (output !== 'mobilesnap_output') {
844
+ command += ` --output ${output}`;
845
+ }
846
+
847
+ document.getElementById('cmd-preview').innerText = command;
848
+ }
849
+
850
+ function copyText(elementId, isInner) {
851
+ const element = document.getElementById(elementId);
852
+ const textToCopy = isInner ? element.innerText : element.textContent;
853
+
854
+ navigator.clipboard.writeText(textToCopy).then(() => {
855
+ alert('Teks berhasil disalin ke clipboard!');
856
+ }).catch(err => {
857
+ console.error('Gagal menyalin teks: ', err);
858
+ });
859
+ }
860
+ </script>
861
+ </body>
862
+ </html>