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.
- package/.claude/settings.local.json +30 -0
- package/.env.example +3 -0
- package/MCP_README.md +200 -0
- package/README.md +148 -0
- package/bin/screencraft.js +61 -0
- package/package.json +31 -0
- package/src/auth/keystore.js +148 -0
- package/src/commands/init.js +119 -0
- package/src/commands/launch.js +405 -0
- package/src/detectors/detectBrand.js +1222 -0
- package/src/detectors/simulator.js +317 -0
- package/src/generators/analyzeStyleReference.js +471 -0
- package/src/generators/compositePSD.js +682 -0
- package/src/generators/copy.js +147 -0
- package/src/mcp/index.js +394 -0
- package/src/pipeline/aeSwap.js +369 -0
- package/src/pipeline/download.js +32 -0
- package/src/pipeline/queue.js +101 -0
- package/src/server/index.js +627 -0
- package/src/server/public/app.js +738 -0
- package/src/server/public/index.html +255 -0
- package/src/server/public/style.css +751 -0
- package/src/server/session.js +36 -0
- package/templates/ae/(Footage)/Assets/This Hip-Hop Upbeat (Short version).wav +0 -0
- package/templates/ae/(Footage)/Assets/screen_01_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_02_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_03_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_04_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_05_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_06_raw.png +0 -0
- package/templates/ae/Motion Forge Test 1.0 (converted).aep +0 -0
- package/templates/ae_swap.jsx +284 -0
- package/templates/layouts/minimal.psd +0 -0
- package/templates/screencraft.config.example.js +165 -0
- package/test/output/layout_test.png +0 -0
- package/test/output/style_profile.json +64 -0
- package/test/reference.png +0 -0
- package/test/test_brand.js +69 -0
- package/test/test_psd.js +83 -0
- package/test/test_style_analysis.js +114 -0
package/test/test_psd.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test/test_psd.js
|
|
3
|
+
* ----------------
|
|
4
|
+
* Tests the compositePSD function in isolation.
|
|
5
|
+
* Place a sample screenshot at test/sample_screenshot.png before running.
|
|
6
|
+
*
|
|
7
|
+
* Run: node test/test_psd.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
require('dotenv').config();
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const { compositePSD } = require('../src/generators/compositePSD');
|
|
14
|
+
|
|
15
|
+
const SAMPLE_SCREENSHOT = path.join(__dirname, 'sample_screenshot.png');
|
|
16
|
+
const OUTPUT_DIR = path.join(__dirname, 'output');
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
20
|
+
|
|
21
|
+
// Check for sample screenshot
|
|
22
|
+
if (!fs.existsSync(SAMPLE_SCREENSHOT)) {
|
|
23
|
+
console.log('');
|
|
24
|
+
console.log(' ✗ Missing: test/sample_screenshot.png');
|
|
25
|
+
console.log(' Add any app screenshot PNG to test/ and rename it sample_screenshot.png');
|
|
26
|
+
console.log('');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tests = [
|
|
31
|
+
{
|
|
32
|
+
name: 'innerloop_screen_01',
|
|
33
|
+
headlineWhite: 'Rewire your',
|
|
34
|
+
headlineAccent:'mind',
|
|
35
|
+
accentColor: '#9B7FD4',
|
|
36
|
+
bgColor: '#1B2A4A',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'innerloop_screen_02',
|
|
40
|
+
headlineWhite: 'Infinitely loopable',
|
|
41
|
+
headlineAccent:'tracks',
|
|
42
|
+
accentColor: '#9B7FD4',
|
|
43
|
+
bgColor: '#1B2A4A',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'different_brand',
|
|
47
|
+
headlineWhite: 'Built for',
|
|
48
|
+
headlineAccent:'focus',
|
|
49
|
+
accentColor: '#E84545',
|
|
50
|
+
bgColor: '#0D1117',
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const t of tests) {
|
|
55
|
+
console.log(` Testing: ${t.name}...`);
|
|
56
|
+
|
|
57
|
+
await compositePSD({
|
|
58
|
+
screenshotPath: SAMPLE_SCREENSHOT,
|
|
59
|
+
headlineWhite: t.headlineWhite,
|
|
60
|
+
headlineAccent: t.headlineAccent,
|
|
61
|
+
accentColor: t.accentColor,
|
|
62
|
+
bgColor: t.bgColor,
|
|
63
|
+
outputPng: path.join(OUTPUT_DIR, `${t.name}.png`),
|
|
64
|
+
outputPsd: path.join(OUTPUT_DIR, `${t.name}.psd`),
|
|
65
|
+
writePsd: true,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
console.log(` ✓ ${t.name}.png + .psd written to test/output/`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(' All tests passed. Open test/output/ to inspect results.');
|
|
73
|
+
console.log(' Check:');
|
|
74
|
+
console.log(' - Screenshot fills phone screen area');
|
|
75
|
+
console.log(' - White + accent text is visible above the phone');
|
|
76
|
+
console.log(' - Background color matches bgColor');
|
|
77
|
+
console.log(' - PSD opens in Photoshop with named layers');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main().catch(err => {
|
|
81
|
+
console.error(' ✗ Test failed:', err.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test/test_style_analysis.js
|
|
3
|
+
* ---------------------------
|
|
4
|
+
* Run: node test/test_style_analysis.js [path-to-image]
|
|
5
|
+
*
|
|
6
|
+
* Tests analyzeStyleReference() and prints the full extracted profile.
|
|
7
|
+
* Also shows what gets adapted to a sample brand.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
// Load API key from environment or .env file
|
|
14
|
+
const envPath = path.join(__dirname, '../.env');
|
|
15
|
+
if (fs.existsSync(envPath)) {
|
|
16
|
+
require('fs').readFileSync(envPath, 'utf8')
|
|
17
|
+
.split('\n')
|
|
18
|
+
.forEach(line => {
|
|
19
|
+
const [key, ...val] = line.split('=');
|
|
20
|
+
if (key && val.length) process.env[key.trim()] = val.join('=').trim();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { analyzeStyleReference } = require('../src/generators/analyzeStyleReference');
|
|
25
|
+
|
|
26
|
+
// Sample brand — simulating Innerloop detection
|
|
27
|
+
const SAMPLE_BRAND = {
|
|
28
|
+
appName: 'Innerloop',
|
|
29
|
+
tagline: 'Reprogram your subconscious',
|
|
30
|
+
primary: '#1B2A4A',
|
|
31
|
+
secondary: '#F5F5F5',
|
|
32
|
+
accent: '#9B7FD4',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const imagePath = process.argv[2] || path.join(__dirname, 'reference.png');
|
|
36
|
+
|
|
37
|
+
async function main() {
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log(' ◆ Style Reference Analyzer');
|
|
40
|
+
console.log(' ─────────────────────────────────────');
|
|
41
|
+
console.log(' Image: ', imagePath);
|
|
42
|
+
console.log(' Brand: ', SAMPLE_BRAND.appName, '/', SAMPLE_BRAND.primary, '/', SAMPLE_BRAND.accent);
|
|
43
|
+
console.log('');
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(imagePath)) {
|
|
46
|
+
console.log(' ✗ Image not found:', imagePath);
|
|
47
|
+
console.log(' Usage: node test/test_style_analysis.js /path/to/reference.png');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
52
|
+
console.log(' ✗ ANTHROPIC_API_KEY not set');
|
|
53
|
+
console.log(' Add it to .env or export it in your shell');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const profile = await analyzeStyleReference(imagePath, SAMPLE_BRAND);
|
|
59
|
+
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(' ─────────────────────────────────────');
|
|
62
|
+
console.log(' EXTRACTED STYLE PROFILE');
|
|
63
|
+
console.log(' ─────────────────────────────────────');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(' LAYOUT');
|
|
66
|
+
console.log(' Type: ', profile.layout.type);
|
|
67
|
+
console.log(' Phones: ', profile.layout.phoneCount);
|
|
68
|
+
console.log(' Arrangement: ', profile.layout.arrangement);
|
|
69
|
+
console.log(' Tilt angles: ', profile.layout.tiltAngles?.join('°, ') + '°');
|
|
70
|
+
console.log(' Text pos: ', profile.layout.textPosition);
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(' BACKGROUND');
|
|
73
|
+
console.log(' Type: ', profile.background.type);
|
|
74
|
+
console.log(' Color: ', profile.background.color, '← adapted to brand.primary?', profile.background.adaptToBrand);
|
|
75
|
+
console.log(' Noise: ', profile.background.noiseAmount);
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log(' PHONE FRAME');
|
|
78
|
+
console.log(' Style: ', profile.phoneFrame.style);
|
|
79
|
+
console.log(' Border: ', profile.phoneFrame.borderColor || 'none');
|
|
80
|
+
console.log(' Shadow: ', profile.phoneFrame.shadowStyle);
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(' TYPOGRAPHY');
|
|
83
|
+
console.log(' Headline: ', profile.typography.headlineStyle, '/', profile.typography.headlineSize);
|
|
84
|
+
console.log(' Position: ', profile.typography.headlinePosition);
|
|
85
|
+
console.log(' Text color: ', profile.typography.textColor);
|
|
86
|
+
console.log(' Accent color:', profile.typography.accentTextColor);
|
|
87
|
+
console.log(' Emoji: ', profile.typography.usesEmoji);
|
|
88
|
+
console.log(' Personality: ', profile.typography.fontPersonality);
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(' MOOD');
|
|
91
|
+
console.log(' Overall: ', profile.colorExtraction?.overallMood);
|
|
92
|
+
console.log(' Description: ', profile.layoutDescription);
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
if (profile.layoutFunctionPath) {
|
|
96
|
+
console.log(' GENERATED LAYOUT CODE');
|
|
97
|
+
console.log(' Path: ', profile.layoutFunctionPath);
|
|
98
|
+
console.log('');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Write full profile to disk for inspection
|
|
102
|
+
const outPath = path.join(__dirname, 'output', 'style_profile.json');
|
|
103
|
+
fs.mkdirSync(path.join(__dirname, 'output'), { recursive: true });
|
|
104
|
+
fs.writeFileSync(outPath, JSON.stringify(profile, null, 2));
|
|
105
|
+
console.log(' Full profile written to test/output/style_profile.json');
|
|
106
|
+
console.log('');
|
|
107
|
+
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error(' ✗ Error:', err.message);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
main();
|