mirror of
https://github.com/digitalsamba/claude-code-video-toolkit.git
synced 2026-09-18 19:41:13 +08:00
0d96c3b764
Major changes: - Shared lib/ with reusable components (AnimatedBackground, SlideTransition, Label, Vignette, LogoWatermark, SplitScreen, NarratorPiP) - Theme system (ThemeProvider, useTheme) in lib/theme/ - Multi-session project system with project.json schema - Brand loader utility and brand.ts generation Commands: - /video - unified project management (replaced /new-video, /new-sprint-video) - /brand - brand profiles (replaced /new-brand) - /template - list templates - /skills - list/create skills (renamed from /skill to avoid Claude Code conflict) - /contribute - guided contribution workflow Infrastructure: - examples/ directory for shareable showcase projects - CONTRIBUTORS.md for recognition - FEEDBACK.md for capturing improvements - Evolution narrative in all commands Fix: - Renamed /skill to /skills - naming collision with built-in Skill tool was silently preventing ALL custom commands from loading 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
156 lines
4.0 KiB
TypeScript
156 lines
4.0 KiB
TypeScript
#!/usr/bin/env npx ts-node
|
|
/**
|
|
* Generate brand.ts file for a project
|
|
*
|
|
* This script reads a brand.json file and generates a TypeScript file
|
|
* that can be imported by Remotion templates.
|
|
*
|
|
* Usage:
|
|
* npx ts-node lib/generate-brand-ts.ts <brand-name> <output-path>
|
|
*
|
|
* Example:
|
|
* npx ts-node lib/generate-brand-ts.ts digital-samba projects/my-video/src/config/brand.ts
|
|
*/
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
interface Brand {
|
|
name: string;
|
|
description?: string;
|
|
colors: Record<string, string>;
|
|
fonts: { primary: string; mono: string };
|
|
spacing: Record<string, number>;
|
|
borderRadius: Record<string, number>;
|
|
typography: Record<string, { size: number; weight: number; letterSpacing?: number }>;
|
|
assets?: Record<string, string>;
|
|
}
|
|
|
|
function findToolkitRoot(): string {
|
|
let current = __dirname;
|
|
const root = path.parse(current).root;
|
|
|
|
while (current !== root) {
|
|
const brandsPath = path.join(current, 'brands');
|
|
if (fs.existsSync(brandsPath)) {
|
|
return current;
|
|
}
|
|
current = path.dirname(current);
|
|
}
|
|
|
|
throw new Error('Could not find toolkit root');
|
|
}
|
|
|
|
function loadBrand(brandName: string): Brand {
|
|
const toolkitRoot = findToolkitRoot();
|
|
const brandPath = path.join(toolkitRoot, 'brands', brandName, 'brand.json');
|
|
|
|
if (!fs.existsSync(brandPath)) {
|
|
throw new Error(`Brand not found: ${brandPath}`);
|
|
}
|
|
|
|
return JSON.parse(fs.readFileSync(brandPath, 'utf-8'));
|
|
}
|
|
|
|
function generateBrandTs(brand: Brand, brandName: string): string {
|
|
const colorsStr = Object.entries(brand.colors)
|
|
.map(([key, value]) => ` ${key}: '${value}',`)
|
|
.join('\n');
|
|
|
|
const typographyStr = Object.entries(brand.typography)
|
|
.map(([key, value]) => {
|
|
const props = [`size: ${value.size}`, `weight: ${value.weight}`];
|
|
if (value.letterSpacing) props.push(`letterSpacing: ${value.letterSpacing}`);
|
|
return ` ${key}: { ${props.join(', ')} },`;
|
|
})
|
|
.join('\n');
|
|
|
|
const assetsStr = brand.assets
|
|
? Object.entries(brand.assets)
|
|
.map(([key, value]) => ` ${key}: '${value}',`)
|
|
.join('\n')
|
|
: ' logo: undefined as string | undefined,\n logoLight: undefined as string | undefined,';
|
|
|
|
return `/**
|
|
* Brand Configuration
|
|
*
|
|
* This file is generated by /new-video command.
|
|
* It contains the brand settings for this project.
|
|
*
|
|
* Brand: ${brand.name}
|
|
* Generated: ${new Date().toISOString().split('T')[0]}
|
|
*
|
|
* To change brands, either:
|
|
* 1. Run /new-video again with a different brand
|
|
* 2. Manually copy values from brands/{brand-name}/brand.json
|
|
*/
|
|
|
|
import type { Theme } from './types';
|
|
|
|
// Project brand name (for reference)
|
|
export const brandName = '${brandName}';
|
|
|
|
// Brand colors and styling
|
|
export const brand = {
|
|
name: '${brand.name}',
|
|
colors: {
|
|
${colorsStr}
|
|
},
|
|
fonts: {
|
|
primary: '${brand.fonts.primary}',
|
|
mono: '${brand.fonts.mono}',
|
|
},
|
|
spacing: {
|
|
xs: ${brand.spacing.xs},
|
|
sm: ${brand.spacing.sm},
|
|
md: ${brand.spacing.md},
|
|
lg: ${brand.spacing.lg},
|
|
xl: ${brand.spacing.xl},
|
|
xxl: ${brand.spacing.xxl},
|
|
},
|
|
borderRadius: {
|
|
sm: ${brand.borderRadius.sm},
|
|
md: ${brand.borderRadius.md},
|
|
lg: ${brand.borderRadius.lg},
|
|
},
|
|
typography: {
|
|
${typographyStr}
|
|
},
|
|
assets: {
|
|
${assetsStr}
|
|
},
|
|
};
|
|
|
|
// Theme derived from brand (for component use)
|
|
export const brandTheme: Theme = {
|
|
colors: brand.colors,
|
|
fonts: brand.fonts,
|
|
spacing: brand.spacing,
|
|
borderRadius: brand.borderRadius,
|
|
typography: brand.typography,
|
|
};
|
|
`;
|
|
}
|
|
|
|
// CLI execution
|
|
if (require.main === module) {
|
|
const [brandName, outputPath] = process.argv.slice(2);
|
|
|
|
if (!brandName || !outputPath) {
|
|
console.error('Usage: npx ts-node lib/generate-brand-ts.ts <brand-name> <output-path>');
|
|
process.exit(1);
|
|
}
|
|
|
|
try {
|
|
const brand = loadBrand(brandName);
|
|
const content = generateBrandTs(brand, brandName);
|
|
fs.writeFileSync(outputPath, content);
|
|
console.log(`Generated ${outputPath} from brand '${brandName}'`);
|
|
} catch (error) {
|
|
console.error('Error:', (error as Error).message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
export { loadBrand, generateBrandTs };
|