screencraft 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.
Files changed (40) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.env.example +3 -0
  3. package/MCP_README.md +200 -0
  4. package/README.md +148 -0
  5. package/bin/screencraft.js +61 -0
  6. package/package.json +31 -0
  7. package/src/auth/keystore.js +148 -0
  8. package/src/commands/init.js +119 -0
  9. package/src/commands/launch.js +405 -0
  10. package/src/detectors/detectBrand.js +1222 -0
  11. package/src/detectors/simulator.js +317 -0
  12. package/src/generators/analyzeStyleReference.js +471 -0
  13. package/src/generators/compositePSD.js +682 -0
  14. package/src/generators/copy.js +147 -0
  15. package/src/mcp/index.js +394 -0
  16. package/src/pipeline/aeSwap.js +369 -0
  17. package/src/pipeline/download.js +32 -0
  18. package/src/pipeline/queue.js +101 -0
  19. package/src/server/index.js +627 -0
  20. package/src/server/public/app.js +738 -0
  21. package/src/server/public/index.html +255 -0
  22. package/src/server/public/style.css +751 -0
  23. package/src/server/session.js +36 -0
  24. package/templates/ae/(Footage)/Assets/This Hip-Hop Upbeat (Short version).wav +0 -0
  25. package/templates/ae/(Footage)/Assets/screen_01_raw.png +0 -0
  26. package/templates/ae/(Footage)/Assets/screen_02_raw.png +0 -0
  27. package/templates/ae/(Footage)/Assets/screen_03_raw.png +0 -0
  28. package/templates/ae/(Footage)/Assets/screen_04_raw.png +0 -0
  29. package/templates/ae/(Footage)/Assets/screen_05_raw.png +0 -0
  30. package/templates/ae/(Footage)/Assets/screen_06_raw.png +0 -0
  31. package/templates/ae/Motion Forge Test 1.0 (converted).aep +0 -0
  32. package/templates/ae_swap.jsx +284 -0
  33. package/templates/layouts/minimal.psd +0 -0
  34. package/templates/screencraft.config.example.js +165 -0
  35. package/test/output/layout_test.png +0 -0
  36. package/test/output/style_profile.json +64 -0
  37. package/test/reference.png +0 -0
  38. package/test/test_brand.js +69 -0
  39. package/test/test_psd.js +83 -0
  40. package/test/test_style_analysis.js +114 -0
@@ -0,0 +1,751 @@
1
+ /* ScreenCraft Web UI — Dark Theme */
2
+
3
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
4
+
5
+ :root {
6
+ --bg: #0a0a0f;
7
+ --bg-card: #12121a;
8
+ --bg-hover: #1a1a26;
9
+ --border: #1e1e2e;
10
+ --text: #e4e4e7;
11
+ --text-dim: #71717a;
12
+ --accent: #A78BFA;
13
+ --accent-dim: #7c5cbf;
14
+ --green: #6BC46A;
15
+ --gold: #C9A84C;
16
+ --red: #ef4444;
17
+ --radius: 10px;
18
+ --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
19
+ --mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
20
+ }
21
+
22
+ body {
23
+ background: var(--bg);
24
+ color: var(--text);
25
+ font-family: var(--font);
26
+ font-size: 15px;
27
+ line-height: 1.5;
28
+ min-height: 100vh;
29
+ overflow-x: hidden;
30
+ }
31
+
32
+ /* ── Layout ──────────────────────────────────────────── */
33
+
34
+ .app {
35
+ max-width: 960px;
36
+ margin: 0 auto;
37
+ padding: 40px 24px 80px;
38
+ }
39
+
40
+ .logo {
41
+ text-align: center;
42
+ margin-bottom: 48px;
43
+ }
44
+
45
+ .logo h1 {
46
+ font-size: 28px;
47
+ font-weight: 600;
48
+ letter-spacing: -0.5px;
49
+ }
50
+
51
+ .logo h1 .diamond {
52
+ color: var(--accent);
53
+ margin-right: 6px;
54
+ }
55
+
56
+ .logo p {
57
+ color: var(--text-dim);
58
+ font-size: 14px;
59
+ margin-top: 4px;
60
+ }
61
+
62
+ /* ── Steps ───────────────────────────────────────────── */
63
+
64
+ .step { display: none; animation: fadeIn 0.3s ease; }
65
+ .step.active { display: block; }
66
+
67
+ @keyframes fadeIn {
68
+ from { opacity: 0; transform: translateY(8px); }
69
+ to { opacity: 1; transform: translateY(0); }
70
+ }
71
+
72
+ .step-header {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 12px;
76
+ margin-bottom: 24px;
77
+ }
78
+
79
+ .step-number {
80
+ width: 32px;
81
+ height: 32px;
82
+ border-radius: 50%;
83
+ background: var(--accent);
84
+ color: #fff;
85
+ font-size: 14px;
86
+ font-weight: 600;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ .step-header h2 {
94
+ font-size: 20px;
95
+ font-weight: 600;
96
+ }
97
+
98
+ .step-header .step-sub {
99
+ color: var(--text-dim);
100
+ font-size: 13px;
101
+ margin-left: auto;
102
+ }
103
+
104
+ /* ── Progress bar (top) ──────────────────────────────── */
105
+
106
+ .progress-bar {
107
+ display: flex;
108
+ gap: 4px;
109
+ margin-bottom: 40px;
110
+ }
111
+
112
+ .progress-bar .seg {
113
+ flex: 1;
114
+ height: 3px;
115
+ background: var(--border);
116
+ border-radius: 2px;
117
+ transition: background 0.3s;
118
+ }
119
+
120
+ .progress-bar .seg.done { background: var(--accent); }
121
+ .progress-bar .seg.current { background: var(--accent-dim); }
122
+
123
+ /* ── Cards ───────────────────────────────────────────── */
124
+
125
+ .card {
126
+ background: var(--bg-card);
127
+ border: 1px solid var(--border);
128
+ border-radius: var(--radius);
129
+ padding: 24px;
130
+ margin-bottom: 16px;
131
+ }
132
+
133
+ .card-row {
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: space-between;
137
+ padding: 8px 0;
138
+ }
139
+
140
+ .card-row + .card-row { border-top: 1px solid var(--border); }
141
+
142
+ .card-row .label {
143
+ color: var(--text-dim);
144
+ font-size: 13px;
145
+ min-width: 100px;
146
+ }
147
+
148
+ .card-row .value {
149
+ font-weight: 500;
150
+ }
151
+
152
+ /* ── Buttons ─────────────────────────────────────────── */
153
+
154
+ .btn {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ gap: 8px;
158
+ padding: 10px 24px;
159
+ border-radius: 8px;
160
+ font-size: 14px;
161
+ font-weight: 500;
162
+ font-family: var(--font);
163
+ cursor: pointer;
164
+ border: none;
165
+ transition: all 0.15s;
166
+ }
167
+
168
+ .btn-primary {
169
+ background: var(--accent);
170
+ color: #fff;
171
+ }
172
+ .btn-primary:hover { background: var(--accent-dim); }
173
+ .btn-primary:disabled {
174
+ opacity: 0.4;
175
+ cursor: not-allowed;
176
+ }
177
+
178
+ .btn-secondary {
179
+ background: transparent;
180
+ color: var(--text-dim);
181
+ border: 1px solid var(--border);
182
+ }
183
+ .btn-secondary:hover {
184
+ background: var(--bg-hover);
185
+ color: var(--text);
186
+ }
187
+
188
+ .btn-ghost {
189
+ background: transparent;
190
+ color: var(--accent);
191
+ padding: 6px 12px;
192
+ }
193
+ .btn-ghost:hover { background: var(--bg-hover); }
194
+
195
+ .btn-row {
196
+ display: flex;
197
+ gap: 12px;
198
+ margin-top: 32px;
199
+ }
200
+
201
+ /* ── Inputs ──────────────────────────────────────────── */
202
+
203
+ input[type="text"], input[type="color"] {
204
+ background: var(--bg);
205
+ border: 1px solid var(--border);
206
+ border-radius: 6px;
207
+ color: var(--text);
208
+ padding: 8px 12px;
209
+ font-size: 14px;
210
+ font-family: var(--font);
211
+ outline: none;
212
+ transition: border-color 0.15s;
213
+ }
214
+
215
+ input[type="text"]:focus {
216
+ border-color: var(--accent);
217
+ }
218
+
219
+ input[type="text"].full {
220
+ width: 100%;
221
+ padding: 12px 16px;
222
+ font-size: 15px;
223
+ }
224
+
225
+ input[type="color"] {
226
+ width: 40px;
227
+ height: 40px;
228
+ padding: 2px;
229
+ cursor: pointer;
230
+ border-radius: 8px;
231
+ }
232
+
233
+ /* ── Color swatches ──────────────────────────────────── */
234
+
235
+ .color-grid {
236
+ display: grid;
237
+ grid-template-columns: repeat(4, 1fr);
238
+ gap: 12px;
239
+ margin-top: 16px;
240
+ }
241
+
242
+ .color-swatch {
243
+ text-align: center;
244
+ }
245
+
246
+ .color-swatch .swatch {
247
+ width: 48px;
248
+ height: 48px;
249
+ border-radius: 12px;
250
+ margin: 0 auto 6px;
251
+ border: 2px solid var(--border);
252
+ cursor: pointer;
253
+ transition: transform 0.15s;
254
+ position: relative;
255
+ overflow: hidden;
256
+ }
257
+ .color-swatch .swatch:hover { transform: scale(1.1); }
258
+
259
+ .color-swatch .swatch input[type="color"] {
260
+ position: absolute;
261
+ inset: -4px;
262
+ width: calc(100% + 8px);
263
+ height: calc(100% + 8px);
264
+ border: none;
265
+ cursor: pointer;
266
+ opacity: 0;
267
+ }
268
+
269
+ .color-swatch .name {
270
+ font-size: 11px;
271
+ color: var(--text-dim);
272
+ text-transform: uppercase;
273
+ letter-spacing: 0.5px;
274
+ }
275
+
276
+ .color-swatch .hex {
277
+ font-size: 12px;
278
+ font-family: var(--mono);
279
+ color: var(--text);
280
+ }
281
+
282
+ /* ── Screenshot grid ─────────────────────────────────── */
283
+
284
+ .screenshot-grid {
285
+ display: grid;
286
+ grid-template-columns: repeat(3, 1fr);
287
+ gap: 16px;
288
+ margin-top: 16px;
289
+ }
290
+
291
+ .screenshot-card {
292
+ background: var(--bg);
293
+ border: 1px solid var(--border);
294
+ border-radius: var(--radius);
295
+ padding: 12px;
296
+ text-align: center;
297
+ cursor: grab;
298
+ transition: border-color 0.15s;
299
+ }
300
+ .screenshot-card:hover { border-color: var(--accent-dim); }
301
+ .screenshot-card.dragging { opacity: 0.5; }
302
+
303
+ .screenshot-card img {
304
+ width: 100%;
305
+ border-radius: 6px;
306
+ margin-bottom: 8px;
307
+ }
308
+
309
+ .screenshot-card .screen-label {
310
+ font-size: 12px;
311
+ color: var(--text-dim);
312
+ }
313
+
314
+ /* ── Phone mockup ────────────────────────────────────── */
315
+
316
+ .phone-mockup {
317
+ position: relative;
318
+ width: 220px;
319
+ margin: 0 auto;
320
+ }
321
+
322
+ .phone-frame {
323
+ position: relative;
324
+ width: 100%;
325
+ aspect-ratio: 9 / 19.5;
326
+ background: #000;
327
+ border-radius: 28px;
328
+ border: 3px solid #2a2a3a;
329
+ overflow: hidden;
330
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
331
+ }
332
+
333
+ .phone-frame img {
334
+ width: 100%;
335
+ height: 100%;
336
+ object-fit: cover;
337
+ }
338
+
339
+ .phone-notch {
340
+ position: absolute;
341
+ top: 0;
342
+ left: 50%;
343
+ transform: translateX(-50%);
344
+ width: 40%;
345
+ height: 24px;
346
+ background: #000;
347
+ border-radius: 0 0 16px 16px;
348
+ }
349
+
350
+ .phone-headline {
351
+ position: absolute;
352
+ bottom: 20%;
353
+ left: 0;
354
+ right: 0;
355
+ text-align: center;
356
+ padding: 0 16px;
357
+ pointer-events: none;
358
+ }
359
+
360
+ .phone-headline .white {
361
+ color: #fff;
362
+ font-size: 16px;
363
+ font-weight: 700;
364
+ text-shadow: 0 1px 4px rgba(0,0,0,0.5);
365
+ }
366
+
367
+ .phone-headline .accent-text {
368
+ color: var(--accent);
369
+ font-size: 16px;
370
+ font-weight: 700;
371
+ text-shadow: 0 1px 4px rgba(0,0,0,0.5);
372
+ }
373
+
374
+ /* ── Headline editor ─────────────────────────────────── */
375
+
376
+ .headline-editor {
377
+ display: flex;
378
+ gap: 32px;
379
+ margin-bottom: 32px;
380
+ align-items: flex-start;
381
+ }
382
+
383
+ .headline-preview {
384
+ flex-shrink: 0;
385
+ }
386
+
387
+ .headline-controls {
388
+ flex: 1;
389
+ min-width: 0;
390
+ }
391
+
392
+ .headline-controls h3 {
393
+ font-size: 15px;
394
+ margin-bottom: 4px;
395
+ }
396
+
397
+ .headline-controls .screen-desc {
398
+ color: var(--text-dim);
399
+ font-size: 13px;
400
+ margin-bottom: 16px;
401
+ }
402
+
403
+ .option-list {
404
+ display: flex;
405
+ flex-direction: column;
406
+ gap: 8px;
407
+ margin-bottom: 16px;
408
+ }
409
+
410
+ .option-btn {
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 10px;
414
+ padding: 10px 14px;
415
+ background: var(--bg);
416
+ border: 1px solid var(--border);
417
+ border-radius: 8px;
418
+ cursor: pointer;
419
+ transition: all 0.15s;
420
+ text-align: left;
421
+ font-family: var(--font);
422
+ font-size: 14px;
423
+ color: var(--text);
424
+ }
425
+
426
+ .option-btn:hover { border-color: var(--accent-dim); }
427
+ .option-btn.selected {
428
+ border-color: var(--accent);
429
+ background: rgba(167, 139, 250, 0.08);
430
+ }
431
+
432
+ .option-btn .opt-num {
433
+ width: 24px;
434
+ height: 24px;
435
+ border-radius: 50%;
436
+ border: 1px solid var(--border);
437
+ display: flex;
438
+ align-items: center;
439
+ justify-content: center;
440
+ font-size: 12px;
441
+ color: var(--text-dim);
442
+ flex-shrink: 0;
443
+ }
444
+
445
+ .option-btn.selected .opt-num {
446
+ background: var(--accent);
447
+ border-color: var(--accent);
448
+ color: #fff;
449
+ }
450
+
451
+ .option-btn .opt-white { color: var(--text); }
452
+ .option-btn .opt-accent { color: var(--accent); margin-left: 4px; }
453
+
454
+ .custom-inputs {
455
+ display: flex;
456
+ gap: 8px;
457
+ margin-top: 8px;
458
+ }
459
+
460
+ .custom-inputs input {
461
+ flex: 1;
462
+ }
463
+
464
+ .headline-nav {
465
+ display: flex;
466
+ align-items: center;
467
+ justify-content: space-between;
468
+ margin-top: 24px;
469
+ }
470
+
471
+ .headline-dots {
472
+ display: flex;
473
+ gap: 6px;
474
+ }
475
+
476
+ .headline-dots .dot {
477
+ width: 8px;
478
+ height: 8px;
479
+ border-radius: 50%;
480
+ background: var(--border);
481
+ cursor: pointer;
482
+ transition: background 0.15s;
483
+ }
484
+
485
+ .headline-dots .dot.active { background: var(--accent); }
486
+ .headline-dots .dot.done { background: var(--green); }
487
+
488
+ /* ── License gate ────────────────────────────────────── */
489
+
490
+ .license-box {
491
+ text-align: center;
492
+ padding: 40px 24px;
493
+ }
494
+
495
+ .license-box .lock-icon {
496
+ font-size: 48px;
497
+ margin-bottom: 16px;
498
+ opacity: 0.6;
499
+ }
500
+
501
+ .license-box h3 {
502
+ font-size: 18px;
503
+ margin-bottom: 8px;
504
+ }
505
+
506
+ .license-box p {
507
+ color: var(--text-dim);
508
+ font-size: 14px;
509
+ margin-bottom: 24px;
510
+ }
511
+
512
+ .license-input-row {
513
+ display: flex;
514
+ gap: 8px;
515
+ max-width: 400px;
516
+ margin: 0 auto;
517
+ }
518
+
519
+ .license-input-row input { flex: 1; }
520
+
521
+ .tier-badge {
522
+ display: inline-block;
523
+ padding: 4px 12px;
524
+ border-radius: 20px;
525
+ font-size: 12px;
526
+ font-weight: 600;
527
+ text-transform: uppercase;
528
+ letter-spacing: 0.5px;
529
+ }
530
+ .tier-badge.launch { background: rgba(107, 196, 106, 0.15); color: var(--green); }
531
+ .tier-badge.pro { background: rgba(167, 139, 250, 0.15); color: var(--accent); }
532
+ .tier-badge.agency { background: rgba(201, 168, 76, 0.15); color: var(--gold); }
533
+
534
+ /* ── Render progress ─────────────────────────────────── */
535
+
536
+ .render-progress {
537
+ text-align: center;
538
+ padding: 60px 24px;
539
+ }
540
+
541
+ .render-spinner {
542
+ width: 48px;
543
+ height: 48px;
544
+ border: 3px solid var(--border);
545
+ border-top-color: var(--accent);
546
+ border-radius: 50%;
547
+ animation: spin 0.8s linear infinite;
548
+ margin: 0 auto 24px;
549
+ }
550
+
551
+ @keyframes spin { to { transform: rotate(360deg); } }
552
+
553
+ .render-bar {
554
+ width: 100%;
555
+ height: 4px;
556
+ background: var(--border);
557
+ border-radius: 2px;
558
+ margin: 16px 0;
559
+ overflow: hidden;
560
+ }
561
+
562
+ .render-bar .fill {
563
+ height: 100%;
564
+ background: var(--accent);
565
+ border-radius: 2px;
566
+ transition: width 0.3s;
567
+ }
568
+
569
+ .render-message {
570
+ color: var(--text-dim);
571
+ font-size: 14px;
572
+ }
573
+
574
+ /* ── Output grid ─────────────────────────────────────── */
575
+
576
+ .output-grid {
577
+ display: grid;
578
+ grid-template-columns: repeat(3, 1fr);
579
+ gap: 16px;
580
+ margin-top: 16px;
581
+ }
582
+
583
+ .output-card {
584
+ background: var(--bg);
585
+ border: 1px solid var(--border);
586
+ border-radius: var(--radius);
587
+ overflow: hidden;
588
+ transition: border-color 0.15s;
589
+ }
590
+ .output-card:hover { border-color: var(--accent-dim); }
591
+
592
+ .output-card img {
593
+ width: 100%;
594
+ aspect-ratio: 9/19.5;
595
+ object-fit: cover;
596
+ }
597
+
598
+ .output-card .output-info {
599
+ padding: 10px 12px;
600
+ display: flex;
601
+ align-items: center;
602
+ justify-content: space-between;
603
+ }
604
+
605
+ .output-card .output-name {
606
+ font-size: 13px;
607
+ color: var(--text-dim);
608
+ }
609
+
610
+ .output-card .output-download {
611
+ color: var(--accent);
612
+ font-size: 13px;
613
+ text-decoration: none;
614
+ }
615
+ .output-card .output-download:hover { text-decoration: underline; }
616
+
617
+ /* ── Loading state ───────────────────────────────────── */
618
+
619
+ .loading {
620
+ display: flex;
621
+ flex-direction: column;
622
+ align-items: center;
623
+ justify-content: center;
624
+ padding: 60px;
625
+ color: var(--text-dim);
626
+ gap: 16px;
627
+ }
628
+
629
+ .loading .render-spinner { margin: 0; }
630
+
631
+ /* ── Status badges ───────────────────────────────────── */
632
+
633
+ .badge {
634
+ display: inline-flex;
635
+ align-items: center;
636
+ gap: 4px;
637
+ padding: 3px 10px;
638
+ border-radius: 20px;
639
+ font-size: 12px;
640
+ font-weight: 500;
641
+ }
642
+
643
+ .badge-framework {
644
+ background: rgba(167, 139, 250, 0.12);
645
+ color: var(--accent);
646
+ }
647
+
648
+ .badge-found {
649
+ background: rgba(107, 196, 106, 0.12);
650
+ color: var(--green);
651
+ }
652
+
653
+ .badge-missing {
654
+ background: rgba(201, 168, 76, 0.12);
655
+ color: var(--gold);
656
+ }
657
+
658
+ /* ── Template grid ───────────────────────────────────── */
659
+
660
+ .template-grid {
661
+ display: grid;
662
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
663
+ gap: 16px;
664
+ }
665
+
666
+ .template-card {
667
+ background: var(--bg-card);
668
+ border: 2px solid var(--border);
669
+ border-radius: var(--radius);
670
+ padding: 24px;
671
+ cursor: pointer;
672
+ transition: all 0.15s;
673
+ text-align: center;
674
+ }
675
+
676
+ .template-card:hover { border-color: var(--accent-dim); }
677
+
678
+ .template-card.selected {
679
+ border-color: var(--accent);
680
+ background: rgba(167, 139, 250, 0.06);
681
+ }
682
+
683
+ .template-card .template-name {
684
+ font-size: 16px;
685
+ font-weight: 600;
686
+ margin-bottom: 4px;
687
+ }
688
+
689
+ .template-card .template-desc {
690
+ font-size: 13px;
691
+ color: var(--text-dim);
692
+ }
693
+
694
+ .template-card .template-badge {
695
+ display: inline-block;
696
+ margin-top: 10px;
697
+ padding: 2px 8px;
698
+ border-radius: 4px;
699
+ font-size: 11px;
700
+ background: rgba(167, 139, 250, 0.1);
701
+ color: var(--accent);
702
+ }
703
+
704
+ /* ── Preview card (headline above phone) ─────────────── */
705
+
706
+ .preview-card {
707
+ background: var(--bg-card);
708
+ border: 1px solid var(--border);
709
+ border-radius: var(--radius);
710
+ padding: 20px 12px 12px;
711
+ text-align: center;
712
+ }
713
+
714
+ .preview-headline {
715
+ margin-bottom: 12px;
716
+ min-height: 48px;
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ gap: 6px;
721
+ flex-wrap: wrap;
722
+ }
723
+
724
+ .preview-headline .white {
725
+ font-size: 20px;
726
+ font-weight: 700;
727
+ color: #fff;
728
+ }
729
+
730
+ .preview-headline .accent-text {
731
+ font-size: 20px;
732
+ font-weight: 700;
733
+ color: var(--accent);
734
+ }
735
+
736
+ .preview-card .phone-frame {
737
+ width: 100%;
738
+ max-width: 200px;
739
+ margin: 0 auto;
740
+ }
741
+
742
+ /* ── Utility ─────────────────────────────────────────── */
743
+
744
+ .mt-8 { margin-top: 8px; }
745
+ .mt-16 { margin-top: 16px; }
746
+ .mt-24 { margin-top: 24px; }
747
+ .mb-16 { margin-bottom: 16px; }
748
+ .text-dim { color: var(--text-dim); }
749
+ .text-sm { font-size: 13px; }
750
+ .text-center { text-align: center; }
751
+ .flex-center { display: flex; align-items: center; justify-content: center; }