Add prototype UI clients

This commit is contained in:
pascal
2025-11-01 12:12:49 +01:00
parent 96f71ff1e1
commit 76b1003810
180 changed files with 44281 additions and 18 deletions

View File

@@ -0,0 +1,30 @@
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('./src/assets/button-full.json', 'utf8'));
// Check frame 0 colors - this should be in the first asset
const asset0 = data.assets[0]; // button start connecting (frames 0-78)
console.log('Asset 0:', asset0.nm);
// Look at layer 0 which has the main shapes
const layer0 = asset0.layers[0];
console.log('\nLayer 0:', layer0.nm);
if (layer0.shapes) {
layer0.shapes.forEach((shape, idx) => {
console.log(`\nShape ${idx}:`, shape.nm || 'unnamed');
if (shape.it) {
shape.it.forEach((item, iIdx) => {
if (item.ty === 'fl' && item.c) {
console.log(` Item ${iIdx} (fill):`);
console.log(` Color:`, item.c.k);
if (item.c.k && Array.isArray(item.c.k) && item.c.k[0] && item.c.k[0].t !== undefined) {
console.log(` Keyframes:`, item.c.k.length);
item.c.k.slice(0, 3).forEach(kf => {
console.log(` Frame ${kf.t}: start =`, kf.s);
});
}
}
});
}
});
}

View File

@@ -0,0 +1,150 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const animationPath = path.join(__dirname, '../src/assets/button-full.json');
console.log('Reading animation file...');
const animationData = JSON.parse(fs.readFileSync(animationPath, 'utf8'));
console.log('\n🔍 Checking frames 259-339 (disconnecting fade-out)...\n');
// Function to check opacity at specific frames
function checkOpacityAtFrames(obj, path = '', frameRange = { start: 259, end: 339 }) {
const findings = [];
function traverse(obj, currentPath = '') {
if (Array.isArray(obj)) {
obj.forEach((item, index) => traverse(item, `${currentPath}[${index}]`));
} else if (obj && typeof obj === 'object') {
// Check for opacity keyframes
if (obj.o && obj.o.k) {
const opacity = obj.o.k;
// Animated opacity (keyframes)
if (Array.isArray(opacity) && opacity[0] && typeof opacity[0] === 'object' && opacity[0].t !== undefined) {
opacity.forEach((keyframe, idx) => {
if (keyframe.t >= frameRange.start && keyframe.t <= frameRange.end) {
const value = keyframe.s ? keyframe.s[0] : null;
if (value !== null && value < 50) {
findings.push({
path: currentPath,
frame: keyframe.t,
opacity: value,
type: 'keyframe'
});
}
}
});
}
// Static opacity
else if (typeof opacity === 'number') {
if (opacity < 50) {
findings.push({
path: currentPath,
opacity: opacity,
type: 'static'
});
}
}
}
// Recursively process all properties
for (const key in obj) {
traverse(obj[key], `${currentPath}.${key}`);
}
}
}
traverse(obj, path);
return findings;
}
// Check main layers
console.log('📊 Checking main layers:');
if (animationData.layers) {
const findings = checkOpacityAtFrames(animationData.layers, 'layers');
if (findings.length > 0) {
console.log(` ⚠️ Found ${findings.length} opacity issues:`);
findings.forEach(f => {
console.log(` - ${f.path}`);
console.log(` ${f.type === 'keyframe' ? `Frame ${f.frame}:` : 'Static:'} opacity = ${f.opacity}`);
});
} else {
console.log(' ✅ No low opacity values found in main layers');
}
}
// Check assets (compositions)
console.log('\n📊 Checking asset compositions:');
if (animationData.assets) {
animationData.assets.forEach((asset, idx) => {
if (asset.layers) {
const findings = checkOpacityAtFrames(asset.layers, `assets[${idx}].layers`);
if (findings.length > 0) {
console.log(` ⚠️ Asset "${asset.nm || asset.id}" has ${findings.length} opacity issues:`);
findings.forEach(f => {
console.log(` - ${f.path}`);
console.log(` ${f.type === 'keyframe' ? `Frame ${f.frame}:` : 'Static:'} opacity = ${f.opacity}`);
});
}
}
});
}
// Also check for in/out points that might hide layers during this range
console.log('\n📊 Checking layer in/out points (frames 259-339):');
function checkLayerTiming(layers, prefix = '') {
const issues = [];
layers.forEach((layer, idx) => {
const layerName = layer.nm || `Layer ${idx}`;
const inPoint = layer.ip !== undefined ? layer.ip : 0;
const outPoint = layer.op !== undefined ? layer.op : 999;
// Check if layer is hidden during our critical range (259-339)
if (outPoint < 339 || inPoint > 259) {
if (!(inPoint > 339 || outPoint < 259)) {
// Layer is partially visible in our range
issues.push({
name: layerName,
inPoint: inPoint,
outPoint: outPoint,
issue: outPoint < 339 ? `ends at frame ${outPoint} (before 339)` : `starts at frame ${inPoint} (after 259)`
});
}
}
});
return issues;
}
if (animationData.layers) {
const timingIssues = checkLayerTiming(animationData.layers);
if (timingIssues.length > 0) {
console.log(' ⚠️ Found layers with timing issues:');
timingIssues.forEach(issue => {
console.log(` - "${issue.name}": in=${issue.inPoint}, out=${issue.outPoint}`);
console.log(` Issue: ${issue.issue}`);
});
} else {
console.log(' ✅ All main layers are visible throughout frames 259-339');
}
}
if (animationData.assets) {
animationData.assets.forEach((asset, idx) => {
if (asset.layers) {
const timingIssues = checkLayerTiming(asset.layers);
if (timingIssues.length > 0) {
console.log(` ⚠️ Asset "${asset.nm || asset.id}" has timing issues:`);
timingIssues.forEach(issue => {
console.log(` - "${issue.name}": in=${issue.inPoint}, out=${issue.outPoint}`);
console.log(` Issue: ${issue.issue}`);
});
}
}
});
}
console.log('\n✅ Diagnosis complete!');

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const animationPath = path.join(__dirname, '../src/assets/button-full.json');
console.log('Reading animation file...');
const animationData = JSON.parse(fs.readFileSync(animationPath, 'utf8'));
// Function to recursively find and modify background layers
function removeBackgrounds(obj) {
if (Array.isArray(obj)) {
return obj.map(item => removeBackgrounds(item));
} else if (obj && typeof obj === 'object') {
// Check if this is a layer with "Shape Layer" in the name
if (obj.nm && (obj.nm.includes('Shape Layer') || obj.nm.includes('background'))) {
console.log(`Found potential background layer: ${obj.nm}`);
// Look for fill color in shapes
if (obj.shapes) {
obj.shapes = obj.shapes.map(shape => {
if (shape.it) {
shape.it = shape.it.map(item => {
// If it's a fill with white/light color, make it transparent
if (item.ty === 'fl' && item.c && item.c.k) {
const color = item.c.k;
// Check if it's a white or very light gray (close to 1.0 in RGB)
if (Array.isArray(color) && color.length >= 3) {
const [r, g, b] = color;
if (r > 0.8 && g > 0.8 && b > 0.8) {
console.log(` Removing white fill from ${obj.nm}`);
item.o = { a: 0, k: 0, ix: 5 }; // Set opacity to 0
}
}
}
return item;
});
}
return shape;
});
}
}
// Recursively process all properties
const result = {};
for (const key in obj) {
result[key] = removeBackgrounds(obj[key]);
}
return result;
}
return obj;
}
console.log('Processing animation layers...');
animationData.layers = removeBackgrounds(animationData.layers);
// Also check for asset compositions
if (animationData.assets) {
console.log('Processing animation assets...');
animationData.assets = animationData.assets.map(asset => {
if (asset.layers) {
asset.layers = removeBackgrounds(asset.layers);
}
return asset;
});
}
console.log('Writing modified animation...');
fs.writeFileSync(animationPath, JSON.stringify(animationData, null, 2));
console.log('✓ Animation background modified successfully!');

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const animationPath = path.join(__dirname, '../src/assets/button-full.json');
console.log('Reading animation file...');
const animationData = JSON.parse(fs.readFileSync(animationPath, 'utf8'));
// Icy blue color: #a3d7e5 -> RGB normalized: [0.639, 0.843, 0.898]
const ICY_BLUE = [0.639, 0.843, 0.898, 1];
function isOrangeColor(color) {
if (!Array.isArray(color) || color.length < 3) return false;
const [r, g, b] = color;
// Check if it's orange-ish (high red, medium green, low blue)
return r > 0.85 && g > 0.3 && g < 0.6 && b < 0.3;
}
// Function to recursively find and modify orange colors
function replaceOrangeWithIcyBlue(obj, path = '') {
if (Array.isArray(obj)) {
return obj.map((item, index) => replaceOrangeWithIcyBlue(item, `${path}[${index}]`));
} else if (obj && typeof obj === 'object') {
// Check if this object has a color property 'c' with keyframe data 'k'
if (obj.c && obj.c.k) {
const color = obj.c.k;
// Handle static color (array)
if (Array.isArray(color) && isOrangeColor(color)) {
console.log(` Replacing orange color at ${path} -> icy blue`);
obj.c.k = [...ICY_BLUE];
}
// Handle animated color (keyframes)
if (Array.isArray(color) && color[0] && color[0].s) {
color.forEach((keyframe, idx) => {
if (keyframe.s && isOrangeColor(keyframe.s)) {
console.log(` Replacing orange keyframe at ${path}.c.k[${idx}].s -> icy blue`);
keyframe.s = [...ICY_BLUE];
}
if (keyframe.e && isOrangeColor(keyframe.e)) {
console.log(` Replacing orange keyframe at ${path}.c.k[${idx}].e -> icy blue`);
keyframe.e = [...ICY_BLUE];
}
});
}
}
// Recursively process all properties
const result = {};
for (const key in obj) {
result[key] = replaceOrangeWithIcyBlue(obj[key], `${path}.${key}`);
}
return result;
}
return obj;
}
console.log('\n🎨 Replacing orange colors with icy blue (#a3d7e5)...\n');
console.log('Only processing connecting/active states (not disconnected state)\n');
// Process assets (compositions)
if (animationData.assets) {
animationData.assets.forEach((asset, idx) => {
// Only replace colors in these precomps (NOT in "button off" which is the gray disconnected state)
const shouldProcess = [
'button start connecting', // comp_0: frames 0-78
'connecting loop', // comp_1: frames 78-120
'connecting to active', // comp_2: frames 120-150
'button activate', // comp_3: frames 150-310 (connected state)
].includes(asset.nm || asset.id);
if (shouldProcess && asset.layers) {
console.log(`📦 Processing asset "${asset.nm || asset.id}"...`);
asset.layers = replaceOrangeWithIcyBlue(asset.layers, `assets[${idx}].layers`);
} else if (asset.nm === 'button off' || asset.id === 'comp_4') {
console.log(`⏭️ Skipping asset "${asset.nm || asset.id}" (keeping gray disconnected state)`);
}
});
}
// Also process main layers (though they're just references to precomps)
if (animationData.layers) {
console.log('\n📦 Processing main timeline layers...');
animationData.layers = replaceOrangeWithIcyBlue(animationData.layers, 'layers');
}
console.log('\nWriting updated animation...');
fs.writeFileSync(animationPath, JSON.stringify(animationData, null, 2));
console.log('✅ Animation colors updated!');
console.log(' - Connecting/active states: icy blue (#a3d7e5)');
console.log(' - Disconnected state: original gray');

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const animationPath = path.join(__dirname, '../src/assets/button-full.json');
console.log('Reading animation file...');
const animationData = JSON.parse(fs.readFileSync(animationPath, 'utf8'));
// Icy blue color: #a3d7e5 -> RGB normalized: [0.639, 0.843, 0.898]
const ICY_BLUE = [0.639, 0.843, 0.898, 1];
// Orange colors to replace (normalized RGB):
// - 0.964705944061, 0.51372551918, 0.1882353127 (primary orange)
// - 0.952941236309, 0.36862745098, 0.196078446332 (secondary orange)
// - 0.952941179276, 0.368627458811, 0.196078434587 (another variant)
function isOrangeColor(color) {
if (!Array.isArray(color) || color.length < 3) return false;
const [r, g, b] = color;
// Check if it's orange-ish (high red, medium green, low blue)
return r > 0.85 && g > 0.3 && g < 0.6 && b < 0.3;
}
// Function to recursively find and modify orange colors
function replaceOrangeWithIcyBlue(obj, path = '') {
if (Array.isArray(obj)) {
return obj.map((item, index) => replaceOrangeWithIcyBlue(item, `${path}[${index}]`));
} else if (obj && typeof obj === 'object') {
// Check if this object has a color property 'c' with keyframe data 'k'
if (obj.c && obj.c.k) {
const color = obj.c.k;
// Handle static color (array)
if (Array.isArray(color) && isOrangeColor(color)) {
console.log(` Replacing orange color at ${path} -> icy blue`);
obj.c.k = [...ICY_BLUE];
}
// Handle animated color (keyframes)
if (Array.isArray(color) && color[0] && color[0].s) {
color.forEach((keyframe, idx) => {
if (keyframe.s && isOrangeColor(keyframe.s)) {
console.log(` Replacing orange keyframe at ${path}.c.k[${idx}].s -> icy blue`);
keyframe.s = [...ICY_BLUE];
}
if (keyframe.e && isOrangeColor(keyframe.e)) {
console.log(` Replacing orange keyframe at ${path}.c.k[${idx}].e -> icy blue`);
keyframe.e = [...ICY_BLUE];
}
});
}
}
// Recursively process all properties
const result = {};
for (const key in obj) {
result[key] = replaceOrangeWithIcyBlue(obj[key], `${path}.${key}`);
}
return result;
}
return obj;
}
console.log('Replacing orange colors with icy blue (#a3d7e5)...');
// Process layers
if (animationData.layers) {
animationData.layers = replaceOrangeWithIcyBlue(animationData.layers, 'layers');
}
// Process assets (compositions)
if (animationData.assets) {
animationData.assets = animationData.assets.map((asset, idx) => {
if (asset.layers) {
asset.layers = replaceOrangeWithIcyBlue(asset.layers, `assets[${idx}].layers`);
}
return asset;
});
}
console.log('Writing updated animation...');
fs.writeFileSync(animationPath, JSON.stringify(animationData, null, 2));
console.log('✓ Animation colors updated to icy blue theme!');