@forgespace/branding-mcp 0.6.2 → 0.7.1

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 (79) hide show
  1. package/README.md +41 -5
  2. package/dist/index.js +1 -1
  3. package/package.json +27 -9
  4. package/server.json +29 -0
  5. package/.env.example +0 -3
  6. package/.github/PULL_REQUEST_TEMPLATE.md +0 -22
  7. package/.github/workflows/ci.yml +0 -73
  8. package/.github/workflows/release-automation.yml +0 -56
  9. package/.github/workflows/security-scan.yml +0 -37
  10. package/.gitleaks.toml +0 -14
  11. package/.prettierrc.json +0 -10
  12. package/CHANGELOG.md +0 -92
  13. package/CONTRIBUTING.md +0 -203
  14. package/data/README.md +0 -13
  15. package/docs/API.md +0 -110
  16. package/docs/DATA_SOURCES.md +0 -69
  17. package/docs/INTEGRATION.md +0 -58
  18. package/eslint.config.js +0 -52
  19. package/jest.config.js +0 -40
  20. package/src/__tests__/integration/brand-generation.test.ts +0 -84
  21. package/src/__tests__/integration/mcp-server.test.ts +0 -18
  22. package/src/__tests__/unit/ai-interpreter.test.ts +0 -172
  23. package/src/__tests__/unit/border-system.test.ts +0 -77
  24. package/src/__tests__/unit/color-palette.test.ts +0 -161
  25. package/src/__tests__/unit/contrast-checker.test.ts +0 -124
  26. package/src/__tests__/unit/design-system-tool.test.ts +0 -176
  27. package/src/__tests__/unit/design-tokens.test.ts +0 -184
  28. package/src/__tests__/unit/favicon-generator.test.ts +0 -80
  29. package/src/__tests__/unit/gradient-system.test.ts +0 -122
  30. package/src/__tests__/unit/logo-generator.test.ts +0 -146
  31. package/src/__tests__/unit/motion-system.test.ts +0 -91
  32. package/src/__tests__/unit/og-image-generator.test.ts +0 -115
  33. package/src/__tests__/unit/shadow-system.test.ts +0 -63
  34. package/src/__tests__/unit/spacing-scale.test.ts +0 -60
  35. package/src/__tests__/unit/typography-system.test.ts +0 -71
  36. package/src/index.ts +0 -76
  37. package/src/lib/branding-core/ai/brand-interpreter.ts +0 -30
  38. package/src/lib/branding-core/ai/claude-interpreter.ts +0 -76
  39. package/src/lib/branding-core/ai/intent-applier.ts +0 -59
  40. package/src/lib/branding-core/ai/keyword-interpreter.ts +0 -95
  41. package/src/lib/branding-core/ai/prompts.ts +0 -93
  42. package/src/lib/branding-core/ai/types.ts +0 -36
  43. package/src/lib/branding-core/documents/html-generator.ts +0 -32
  44. package/src/lib/branding-core/documents/pdf-generator.ts +0 -21
  45. package/src/lib/branding-core/exporters/css-variables.ts +0 -71
  46. package/src/lib/branding-core/exporters/design-tokens.ts +0 -86
  47. package/src/lib/branding-core/exporters/figma-tokens.ts +0 -87
  48. package/src/lib/branding-core/exporters/react-theme.ts +0 -69
  49. package/src/lib/branding-core/exporters/sass-variables.ts +0 -74
  50. package/src/lib/branding-core/exporters/tailwind-preset.ts +0 -67
  51. package/src/lib/branding-core/generators/border-system.ts +0 -41
  52. package/src/lib/branding-core/generators/color-palette.ts +0 -147
  53. package/src/lib/branding-core/generators/favicon-generator.ts +0 -33
  54. package/src/lib/branding-core/generators/gradient-system.ts +0 -120
  55. package/src/lib/branding-core/generators/logo-generator.ts +0 -152
  56. package/src/lib/branding-core/generators/motion-system.ts +0 -98
  57. package/src/lib/branding-core/generators/og-image-generator.ts +0 -97
  58. package/src/lib/branding-core/generators/shadow-system.ts +0 -66
  59. package/src/lib/branding-core/generators/spacing-scale.ts +0 -29
  60. package/src/lib/branding-core/generators/typography-system.ts +0 -128
  61. package/src/lib/branding-core/index.ts +0 -28
  62. package/src/lib/branding-core/validators/brand-consistency.ts +0 -79
  63. package/src/lib/branding-core/validators/contrast-checker.ts +0 -37
  64. package/src/lib/branding-core/validators/token-schema.ts +0 -50
  65. package/src/lib/config.ts +0 -13
  66. package/src/lib/logger.ts +0 -12
  67. package/src/lib/types.ts +0 -236
  68. package/src/resources/brand-knowledge.ts +0 -60
  69. package/src/resources/brand-templates.ts +0 -385
  70. package/src/tools/create-brand-guidelines.ts +0 -94
  71. package/src/tools/export-design-tokens.ts +0 -52
  72. package/src/tools/generate-brand-assets.ts +0 -48
  73. package/src/tools/generate-brand-identity.ts +0 -115
  74. package/src/tools/generate-color-palette.ts +0 -43
  75. package/src/tools/generate-design-system.ts +0 -163
  76. package/src/tools/generate-typography-system.ts +0 -42
  77. package/src/tools/refine-brand-element.ts +0 -65
  78. package/src/tools/validate-brand-consistency.ts +0 -32
  79. package/tsconfig.json +0 -21
@@ -1,152 +0,0 @@
1
- import type { BrandStyle, LogoConfig, LogoOutput } from '../../types.js';
2
-
3
- export function defaultLogoConfig(brandName: string, primaryColor: string): LogoConfig {
4
- return {
5
- text: brandName,
6
- font: 'Inter',
7
- fontSize: 48,
8
- color: primaryColor,
9
- backgroundColor: 'transparent',
10
- width: 400,
11
- height: 120,
12
- };
13
- }
14
-
15
- function generateWordmark(config: LogoConfig): string {
16
- const { text, font, fontSize, color, backgroundColor, width, height } = config;
17
- const initial = text.charAt(0).toUpperCase();
18
- const circleR = height * 0.35;
19
- const circleX = circleR + 20;
20
- const circleY = height / 2;
21
- const textX = circleX + circleR + 16;
22
- const textY = height / 2;
23
- const fontFamily = `'${font}', sans-serif`;
24
-
25
- return [
26
- `<svg xmlns="http://www.w3.org/2000/svg" width="400" height="120" viewBox="0 0 400 120">`,
27
- backgroundColor !== 'transparent'
28
- ? ` <rect width="${width}" height="${height}" fill="${backgroundColor}" rx="8"/>`
29
- : '',
30
- ` <circle cx="${circleX}" cy="${circleY}" r="${circleR}" fill="${color}"/>`,
31
- ` <text x="${circleX}" y="${circleY}" fill="white" font-size="${fontSize * 0.7}" font-family="${fontFamily}" font-weight="700" text-anchor="middle" dominant-baseline="central">${initial}</text>`,
32
- ` <text x="${textX}" y="${textY}" fill="${color}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="600" dominant-baseline="central">${text}</text>`,
33
- '</svg>',
34
- ]
35
- .filter(Boolean)
36
- .join('\n');
37
- }
38
-
39
- const MONOGRAM_SHAPES: Record<BrandStyle, string> = {
40
- minimal: 'roundedSquare',
41
- bold: 'hexagon',
42
- elegant: 'thinCircle',
43
- playful: 'blob',
44
- corporate: 'rectangle',
45
- tech: 'diamond',
46
- organic: 'ellipse',
47
- retro: 'octagon',
48
- };
49
-
50
- function monogramContainer(style: BrandStyle, color: string): string {
51
- const shape = MONOGRAM_SHAPES[style] ?? 'thinCircle';
52
- const containers: Record<string, string> = {
53
- roundedSquare: `<rect x="10" y="10" width="100" height="100" rx="16" fill="${color}"/>`,
54
- hexagon: `<polygon points="60,5 110,30 110,90 60,115 10,90 10,30" fill="${color}"/>`,
55
- thinCircle: `<circle cx="60" cy="60" r="50" fill="none" stroke="${color}" stroke-width="3"/>`,
56
- blob: `<ellipse cx="60" cy="60" rx="52" ry="48" fill="${color}"/>`,
57
- rectangle: `<rect x="10" y="15" width="100" height="90" rx="4" fill="${color}"/>`,
58
- diamond: `<polygon points="60,5 115,60 60,115 5,60" fill="${color}"/>`,
59
- ellipse: `<ellipse cx="60" cy="60" rx="55" ry="45" fill="${color}"/>`,
60
- octagon: `<polygon points="35,5 85,5 115,35 115,85 85,115 35,115 5,85 5,35" fill="${color}"/>`,
61
- };
62
- return containers[shape] ?? containers.thinCircle;
63
- }
64
-
65
- function generateMonogram(config: LogoConfig): string {
66
- const { text, font, color } = config;
67
- const initial = text.charAt(0).toUpperCase();
68
- const style = config.style ?? 'minimal';
69
- const fontFamily = `'${font}', sans-serif`;
70
- const isThinCircle = MONOGRAM_SHAPES[style] === 'thinCircle';
71
- const textColor = isThinCircle ? color : 'white';
72
-
73
- return [
74
- `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120">`,
75
- ` ${monogramContainer(style, color)}`,
76
- ` <text x="60" y="60" fill="${textColor}" font-size="56" font-family="${fontFamily}" font-weight="700" text-anchor="middle" dominant-baseline="central">${initial}</text>`,
77
- '</svg>',
78
- ].join('\n');
79
- }
80
-
81
- const ABSTRACT_BUILDERS: Record<BrandStyle, (c: string) => string> = {
82
- minimal: (c) =>
83
- ` <circle cx="40" cy="60" r="30" fill="${c}" opacity="0.8"/>` +
84
- `\n <circle cx="60" cy="40" r="30" fill="${c}" opacity="0.5"/>` +
85
- `\n <circle cx="80" cy="60" r="30" fill="${c}" opacity="0.3"/>`,
86
- bold: (c) =>
87
- ` <rect x="10" y="10" width="50" height="50" fill="${c}" opacity="0.9"/>` +
88
- `\n <rect x="40" y="40" width="50" height="50" fill="${c}" opacity="0.6"/>` +
89
- `\n <rect x="60" y="20" width="40" height="40" fill="${c}" opacity="0.3"/>`,
90
- elegant: (c) =>
91
- ` <path d="M60,10 A50,50 0 0,1 110,60" fill="none" stroke="${c}" stroke-width="2"/>` +
92
- `\n <path d="M60,25 A35,35 0 0,1 95,60" fill="none" stroke="${c}" stroke-width="2"/>` +
93
- `\n <path d="M60,40 A20,20 0 0,1 80,60" fill="none" stroke="${c}" stroke-width="2"/>`,
94
- playful: (c) =>
95
- ` <circle cx="30" cy="40" r="8" fill="${c}"/>` +
96
- `\n <circle cx="60" cy="25" r="12" fill="${c}" opacity="0.7"/>` +
97
- `\n <circle cx="90" cy="50" r="10" fill="${c}" opacity="0.5"/>` +
98
- `\n <circle cx="50" cy="80" r="14" fill="${c}" opacity="0.6"/>`,
99
- corporate: (c) =>
100
- ` <rect x="10" y="10" width="30" height="100" fill="${c}" opacity="0.3"/>` +
101
- `\n <rect x="45" y="10" width="30" height="100" fill="${c}" opacity="0.5"/>` +
102
- `\n <rect x="80" y="10" width="30" height="100" fill="${c}" opacity="0.7"/>`,
103
- tech: (c) =>
104
- ` <line x1="10" y1="60" x2="50" y2="30" stroke="${c}" stroke-width="2"/>` +
105
- `\n <line x1="50" y1="30" x2="90" y2="50" stroke="${c}" stroke-width="2"/>` +
106
- `\n <line x1="90" y1="50" x2="110" y2="20" stroke="${c}" stroke-width="2"/>` +
107
- `\n <circle cx="50" cy="30" r="4" fill="${c}"/>` +
108
- `\n <circle cx="90" cy="50" r="4" fill="${c}"/>`,
109
- organic: (c) =>
110
- ` <path d="M10,60 Q30,20 60,60 Q90,100 110,60" fill="none" stroke="${c}" stroke-width="3"/>` +
111
- `\n <path d="M10,80 Q30,40 60,80 Q90,120 110,80" fill="none" stroke="${c}" stroke-width="2" opacity="0.5"/>`,
112
- retro: (c) =>
113
- ` <polygon points="60,10 75,35 110,35 82,55 93,85 60,67 27,85 38,55 10,35 45,35" fill="${c}"/>` +
114
- `\n <polygon points="60,25 70,42 90,42 74,53 80,70 60,60 40,70 46,53 30,42 50,42" fill="white" opacity="0.3"/>`,
115
- };
116
-
117
- function generateAbstract(config: LogoConfig): string {
118
- const { color } = config;
119
- const style = config.style ?? 'minimal';
120
- const builder = ABSTRACT_BUILDERS[style] ?? ABSTRACT_BUILDERS.minimal;
121
-
122
- return [
123
- `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120">`,
124
- builder(color),
125
- '</svg>',
126
- ].join('\n');
127
- }
128
-
129
- function generateIcon(config: LogoConfig): string {
130
- const { text, font, color } = config;
131
- const initial = text.charAt(0).toUpperCase();
132
- const fontFamily = `'${font}', sans-serif`;
133
-
134
- return [
135
- `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">`,
136
- ` <circle cx="32" cy="32" r="28" fill="${color}"/>`,
137
- ` <text x="32" y="32" fill="white" font-size="32" font-family="${fontFamily}" font-weight="700" text-anchor="middle" dominant-baseline="central">${initial}</text>`,
138
- '</svg>',
139
- ].join('\n');
140
- }
141
-
142
- export function generateSvgLogo(config: LogoConfig): LogoOutput {
143
- const wordmark = generateWordmark(config);
144
- const monogram = generateMonogram(config);
145
- const abstract = generateAbstract(config);
146
- const icon = generateIcon(config);
147
-
148
- return {
149
- svg: wordmark,
150
- variants: { wordmark, monogram, abstract, icon },
151
- };
152
- }
@@ -1,98 +0,0 @@
1
- import type { BrandStyle, DurationName, EasingName, MotionSystem } from '../../types.js';
2
-
3
- const STYLE_DURATIONS: Record<BrandStyle, Record<DurationName, number>> = {
4
- minimal: { instant: 0, fast: 100, normal: 200, slow: 300, slower: 400 },
5
- bold: { instant: 0, fast: 100, normal: 200, slow: 300, slower: 450 },
6
- elegant: { instant: 0, fast: 200, normal: 350, slow: 500, slower: 700 },
7
- playful: { instant: 0, fast: 150, normal: 250, slow: 400, slower: 600 },
8
- corporate: { instant: 0, fast: 120, normal: 200, slow: 300, slower: 400 },
9
- tech: { instant: 0, fast: 80, normal: 150, slow: 250, slower: 350 },
10
- organic: { instant: 0, fast: 180, normal: 300, slow: 450, slower: 650 },
11
- retro: { instant: 0, fast: 150, normal: 250, slow: 350, slower: 500 },
12
- };
13
-
14
- const STYLE_EASINGS: Record<BrandStyle, Record<EasingName, string>> = {
15
- minimal: {
16
- 'ease-in': 'cubic-bezier(0.4, 0, 1, 1)',
17
- 'ease-out': 'cubic-bezier(0, 0, 0.2, 1)',
18
- 'ease-in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
19
- spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
20
- bounce: 'cubic-bezier(0.34, 1.2, 0.64, 1)',
21
- },
22
- bold: {
23
- 'ease-in': 'cubic-bezier(0.5, 0, 1, 1)',
24
- 'ease-out': 'cubic-bezier(0, 0, 0.15, 1)',
25
- 'ease-in-out': 'cubic-bezier(0.5, 0, 0.15, 1)',
26
- spring: 'cubic-bezier(0.22, 1.8, 0.36, 1)',
27
- bounce: 'cubic-bezier(0.22, 1.5, 0.36, 1)',
28
- },
29
- elegant: {
30
- 'ease-in': 'cubic-bezier(0.42, 0, 1, 1)',
31
- 'ease-out': 'cubic-bezier(0, 0, 0.58, 1)',
32
- 'ease-in-out': 'cubic-bezier(0.42, 0, 0.58, 1)',
33
- spring: 'cubic-bezier(0.25, 1.2, 0.5, 1)',
34
- bounce: 'cubic-bezier(0.25, 1.1, 0.5, 1)',
35
- },
36
- playful: {
37
- 'ease-in': 'cubic-bezier(0.4, 0, 1, 1)',
38
- 'ease-out': 'cubic-bezier(0, 0, 0.2, 1)',
39
- 'ease-in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
40
- spring: 'cubic-bezier(0.18, 2.0, 0.4, 1)',
41
- bounce: 'cubic-bezier(0.18, 1.8, 0.4, 1)',
42
- },
43
- corporate: {
44
- 'ease-in': 'cubic-bezier(0.4, 0, 1, 1)',
45
- 'ease-out': 'cubic-bezier(0, 0, 0.2, 1)',
46
- 'ease-in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
47
- spring: 'cubic-bezier(0.3, 1.3, 0.6, 1)',
48
- bounce: 'cubic-bezier(0.3, 1.15, 0.6, 1)',
49
- },
50
- tech: {
51
- 'ease-in': 'cubic-bezier(0.55, 0, 1, 1)',
52
- 'ease-out': 'cubic-bezier(0, 0, 0.1, 1)',
53
- 'ease-in-out': 'cubic-bezier(0.55, 0, 0.1, 1)',
54
- spring: 'cubic-bezier(0.2, 1.6, 0.4, 1)',
55
- bounce: 'cubic-bezier(0.2, 1.4, 0.4, 1)',
56
- },
57
- organic: {
58
- 'ease-in': 'cubic-bezier(0.35, 0, 0.9, 1)',
59
- 'ease-out': 'cubic-bezier(0.1, 0, 0.3, 1)',
60
- 'ease-in-out': 'cubic-bezier(0.35, 0, 0.3, 1)',
61
- spring: 'cubic-bezier(0.28, 1.4, 0.5, 1)',
62
- bounce: 'cubic-bezier(0.28, 1.25, 0.5, 1)',
63
- },
64
- retro: {
65
- 'ease-in': 'cubic-bezier(0.5, 0, 1, 1)',
66
- 'ease-out': 'cubic-bezier(0, 0, 0.3, 1)',
67
- 'ease-in-out': 'cubic-bezier(0.5, 0, 0.3, 1)',
68
- spring: 'cubic-bezier(0.3, 1.5, 0.5, 1)',
69
- bounce: 'cubic-bezier(0.3, 1.3, 0.5, 1)',
70
- },
71
- };
72
-
73
- function buildTransitions(
74
- durations: Record<DurationName, number>,
75
- defaultEasing: string
76
- ): Record<string, string> {
77
- return {
78
- fade: `opacity ${durations.normal}ms ${defaultEasing}`,
79
- slide: `transform ${durations.normal}ms ${defaultEasing}`,
80
- scale: `transform ${durations.fast}ms ${defaultEasing}`,
81
- color: `color ${durations.slow}ms ${defaultEasing}, background-color ${durations.slow}ms ${defaultEasing}`,
82
- all: `all ${durations.normal}ms ${defaultEasing}`,
83
- };
84
- }
85
-
86
- export function generateMotionSystem(style: BrandStyle = 'minimal'): MotionSystem {
87
- const durationValues = STYLE_DURATIONS[style];
88
- const easingValues = STYLE_EASINGS[style];
89
- const durations = {} as Record<DurationName, string>;
90
- for (const [name, ms] of Object.entries(durationValues)) {
91
- durations[name as DurationName] = `${ms}ms`;
92
- }
93
- return {
94
- durations,
95
- easings: easingValues,
96
- transitions: buildTransitions(durationValues, easingValues['ease-out']),
97
- };
98
- }
@@ -1,97 +0,0 @@
1
- import type { BrandIdentity, OgImageOutput, OgTemplate } from '../../types.js';
2
-
3
- function getGradientColors(brand: BrandIdentity): [string, string] {
4
- return [brand.colors.primary.hex, brand.colors.secondary.hex];
5
- }
6
-
7
- function stripSvgWrapper(svg: string): string {
8
- return svg.replace(/<svg[^>]*>/, '').replace(/<\/svg>/, '');
9
- }
10
-
11
- function buildSvg(
12
- w: number,
13
- h: number,
14
- colors: [string, string],
15
- font: string,
16
- title: string,
17
- subtitle: string,
18
- logoSection: string
19
- ): string {
20
- return [
21
- `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`,
22
- ` <defs>`,
23
- ` <style>@import url('https://fonts.googleapis.com/css2?family=${encodeURIComponent(font)}');</style>`,
24
- ` <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">`,
25
- ` <stop offset="0%" style="stop-color:${colors[0]}"/>`,
26
- ` <stop offset="100%" style="stop-color:${colors[1]}"/>`,
27
- ` </linearGradient>`,
28
- ` </defs>`,
29
- ` <rect width="${w}" height="${h}" fill="url(#bg)"/>`,
30
- logoSection,
31
- ` <text x="${w / 2}" y="${h / 2 - 20}" fill="white" font-size="56" font-family="'${font}', sans-serif" font-weight="700" text-anchor="middle" dominant-baseline="central">${title}</text>`,
32
- subtitle
33
- ? ` <text x="${w / 2}" y="${h / 2 + 40}" fill="white" font-size="28" font-family="'${font}', sans-serif" font-weight="400" text-anchor="middle" dominant-baseline="central" opacity="0.8">${subtitle}</text>`
34
- : '',
35
- '</svg>',
36
- ]
37
- .filter(Boolean)
38
- .join('\n');
39
- }
40
-
41
- function buildDefaultTemplate(brand: BrandIdentity, title?: string, subtitle?: string): string {
42
- const displayTitle = title ?? brand.name;
43
- const displaySub = subtitle ?? brand.tagline ?? '';
44
- const font = brand.typography.headingFont;
45
- const logoSvg = brand.logo?.variants?.icon ?? '';
46
- const logoSection = logoSvg
47
- ? `<g transform="translate(540, 80) scale(1.5)">${stripSvgWrapper(logoSvg)}</g>`
48
- : '';
49
-
50
- return buildSvg(1200, 630, getGradientColors(brand), font, displayTitle, displaySub, logoSection);
51
- }
52
-
53
- function buildArticleTemplate(brand: BrandIdentity, title?: string, subtitle?: string): string {
54
- const displayTitle = title ?? 'Untitled Article';
55
- const displaySub = subtitle ?? '';
56
- const font = brand.typography.headingFont;
57
- const logoSvg = brand.logo?.variants?.icon ?? '';
58
- const logoSection = logoSvg
59
- ? `<g transform="translate(1080, 30) scale(0.8)">${stripSvgWrapper(logoSvg)}</g>`
60
- : '';
61
-
62
- return buildSvg(1200, 630, getGradientColors(brand), font, displayTitle, displaySub, logoSection);
63
- }
64
-
65
- function buildSocialTemplate(brand: BrandIdentity, title?: string): string {
66
- const displayTitle = title ?? brand.name;
67
- const font = brand.typography.headingFont;
68
- const colors = getGradientColors(brand);
69
- const logoSvg = brand.logo?.variants?.icon ?? '';
70
- const logoSection = logoSvg
71
- ? `<g transform="translate(536, 300) scale(2)">${stripSvgWrapper(logoSvg)}</g>`
72
- : '';
73
-
74
- return buildSvg(1200, 1200, colors, font, displayTitle, '', logoSection);
75
- }
76
-
77
- export function generateOgImage(
78
- brand: BrandIdentity,
79
- template: OgTemplate = 'default',
80
- title?: string,
81
- subtitle?: string
82
- ): OgImageOutput {
83
- const builders: Record<OgTemplate, () => string> = {
84
- default: () => buildDefaultTemplate(brand, title, subtitle),
85
- article: () => buildArticleTemplate(brand, title, subtitle),
86
- social: () => buildSocialTemplate(brand, title),
87
- };
88
-
89
- const svg = builders[template]();
90
- const dimensions: Record<OgTemplate, { width: number; height: number }> = {
91
- default: { width: 1200, height: 630 },
92
- article: { width: 1200, height: 630 },
93
- social: { width: 1200, height: 1200 },
94
- };
95
-
96
- return { template, svg, ...dimensions[template] };
97
- }
@@ -1,66 +0,0 @@
1
- import type {
2
- BrandStyle,
3
- ColorTheme,
4
- ShadowLevel,
5
- ShadowLevelName,
6
- ShadowSystem,
7
- } from '../../types.js';
8
- import { hexToHsl, hslToHex } from './color-palette.js';
9
-
10
- const LEVEL_CONFIGS: Record<
11
- ShadowLevelName,
12
- { y: number; blur: number; spread: number; opacity: number }
13
- > = {
14
- none: { y: 0, blur: 0, spread: 0, opacity: 0 },
15
- sm: { y: 1, blur: 2, spread: 0, opacity: 0.05 },
16
- md: { y: 2, blur: 4, spread: -1, opacity: 0.08 },
17
- lg: { y: 4, blur: 8, spread: -2, opacity: 0.1 },
18
- xl: { y: 8, blur: 16, spread: -4, opacity: 0.12 },
19
- '2xl': { y: 16, blur: 32, spread: -8, opacity: 0.15 },
20
- };
21
-
22
- function makeShadowColor(primaryHex: string, opacity: number): string {
23
- const hsl = hexToHsl(primaryHex);
24
- const tinted = hslToHex(hsl.h, Math.min(hsl.s, 30), 20);
25
- const r = parseInt(tinted.slice(1, 3), 16);
26
- const g = parseInt(tinted.slice(3, 5), 16);
27
- const b = parseInt(tinted.slice(5, 7), 16);
28
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
29
- }
30
-
31
- function buildLevel(
32
- cfg: (typeof LEVEL_CONFIGS)[ShadowLevelName],
33
- primaryHex: string,
34
- dark: boolean
35
- ): ShadowLevel {
36
- const sign = dark ? -1 : 1;
37
- const adjustedOpacity = dark ? cfg.opacity * 1.5 : cfg.opacity;
38
- const color = makeShadowColor(primaryHex, adjustedOpacity);
39
- const oY = cfg.y * sign;
40
- const cssValue =
41
- cfg.blur === 0
42
- ? 'none'
43
- : `${cfg.y === 0 ? 0 : `${oY}px`} ${oY === 0 ? '' : `${Math.abs(oY)}px `}${cfg.blur}px ${cfg.spread}px ${color}`.trim();
44
- return {
45
- offsetX: 0,
46
- offsetY: oY,
47
- blur: cfg.blur,
48
- spread: cfg.spread,
49
- color,
50
- opacity: adjustedOpacity,
51
- cssValue,
52
- };
53
- }
54
-
55
- export function generateShadowSystem(
56
- primaryHex = '#6B4CE6',
57
- theme: ColorTheme = 'light',
58
- _style?: BrandStyle
59
- ): ShadowSystem {
60
- const dark = theme === 'dark';
61
- const levels = {} as Record<ShadowLevelName, ShadowLevel>;
62
- for (const [name, cfg] of Object.entries(LEVEL_CONFIGS)) {
63
- levels[name as ShadowLevelName] = buildLevel(cfg, primaryHex, dark);
64
- }
65
- return { levels };
66
- }
@@ -1,29 +0,0 @@
1
- import type { SpacingScale } from '../../types.js';
2
-
3
- const SPACING_NAMES = [
4
- '0',
5
- '0.5',
6
- '1',
7
- '1.5',
8
- '2',
9
- '2.5',
10
- '3',
11
- '4',
12
- '5',
13
- '6',
14
- '8',
15
- '10',
16
- '12',
17
- '16',
18
- '20',
19
- '24',
20
- ];
21
-
22
- export function generateSpacingScale(baseUnit = 4): SpacingScale {
23
- const values: Record<string, string> = {};
24
- for (const name of SPACING_NAMES) {
25
- const multiplier = parseFloat(name);
26
- values[name] = `${multiplier * baseUnit}px`;
27
- }
28
- return { unit: baseUnit, values };
29
- }
@@ -1,128 +0,0 @@
1
- import type { FontCategory, TypeScaleRatio, TypeStep, TypographySystem } from '../../types.js';
2
-
3
- const SCALE_RATIOS: Record<TypeScaleRatio, number> = {
4
- 'minor-second': 1.067,
5
- 'major-second': 1.125,
6
- 'minor-third': 1.2,
7
- 'major-third': 1.25,
8
- 'perfect-fourth': 1.333,
9
- 'augmented-fourth': 1.414,
10
- 'perfect-fifth': 1.5,
11
- 'golden-ratio': 1.618,
12
- };
13
-
14
- const FONT_PAIRINGS: Record<FontCategory, Record<FontCategory, [string, string][]>> = {
15
- 'sans-serif': {
16
- serif: [
17
- ['Inter', 'Merriweather'],
18
- ['Helvetica Neue', 'Georgia'],
19
- ['Open Sans', 'Lora'],
20
- ],
21
- 'sans-serif': [
22
- ['Montserrat', 'Open Sans'],
23
- ['Poppins', 'Inter'],
24
- ['Raleway', 'Nunito'],
25
- ],
26
- monospace: [['Inter', 'JetBrains Mono']],
27
- display: [['Inter', 'Playfair Display']],
28
- handwriting: [['Inter', 'Caveat']],
29
- },
30
- serif: {
31
- 'sans-serif': [
32
- ['Playfair Display', 'Source Sans 3'],
33
- ['Merriweather', 'Open Sans'],
34
- ['Lora', 'Inter'],
35
- ],
36
- serif: [
37
- ['Playfair Display', 'Merriweather'],
38
- ['Lora', 'Georgia'],
39
- ],
40
- monospace: [['Merriweather', 'Fira Code']],
41
- display: [['Merriweather', 'Playfair Display']],
42
- handwriting: [['Merriweather', 'Caveat']],
43
- },
44
- monospace: {
45
- 'sans-serif': [['JetBrains Mono', 'Inter']],
46
- serif: [['Fira Code', 'Merriweather']],
47
- monospace: [['JetBrains Mono', 'Fira Code']],
48
- display: [['JetBrains Mono', 'Playfair Display']],
49
- handwriting: [['JetBrains Mono', 'Caveat']],
50
- },
51
- display: {
52
- 'sans-serif': [
53
- ['Playfair Display', 'Inter'],
54
- ['Bebas Neue', 'Open Sans'],
55
- ],
56
- serif: [['Playfair Display', 'Merriweather']],
57
- monospace: [['Playfair Display', 'JetBrains Mono']],
58
- display: [['Bebas Neue', 'Playfair Display']],
59
- handwriting: [['Playfair Display', 'Caveat']],
60
- },
61
- handwriting: {
62
- 'sans-serif': [['Caveat', 'Inter']],
63
- serif: [['Caveat', 'Merriweather']],
64
- monospace: [['Caveat', 'JetBrains Mono']],
65
- display: [['Caveat', 'Playfair Display']],
66
- handwriting: [['Caveat', 'Dancing Script']],
67
- },
68
- };
69
-
70
- const STEP_NAMES = ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl'];
71
-
72
- function pickPairing(headingCat: FontCategory, bodyCat: FontCategory): [string, string] {
73
- const options = FONT_PAIRINGS[headingCat]?.[bodyCat];
74
- if (!options?.length) return ['Inter', 'Inter'];
75
- return options[Math.floor(Math.random() * options.length)];
76
- }
77
-
78
- function lineHeightForSize(size: number): string {
79
- if (size <= 16) return '1.6';
80
- if (size <= 24) return '1.5';
81
- if (size <= 36) return '1.3';
82
- return '1.2';
83
- }
84
-
85
- function letterSpacingForSize(size: number): string {
86
- if (size <= 14) return '0.02em';
87
- if (size <= 20) return '0em';
88
- return '-0.01em';
89
- }
90
-
91
- function weightForStep(index: number, totalSteps: number): number {
92
- const mid = Math.floor(totalSteps / 2);
93
- if (index <= mid) return 400;
94
- if (index <= mid + 2) return 600;
95
- return 700;
96
- }
97
-
98
- export function generateTypographySystem(
99
- headingCategory: FontCategory = 'sans-serif',
100
- bodyCategory: FontCategory = 'serif',
101
- scaleRatio: TypeScaleRatio = 'major-third',
102
- baseSize = 16
103
- ): TypographySystem {
104
- const ratio = SCALE_RATIOS[scaleRatio];
105
- const [headingFont, bodyFont] = pickPairing(headingCategory, bodyCategory);
106
-
107
- const baseIndex = 2;
108
- const steps: TypeStep[] = STEP_NAMES.map((name, i) => {
109
- const exponent = i - baseIndex;
110
- const size = Math.round(baseSize * Math.pow(ratio, exponent) * 100) / 100;
111
- return {
112
- name,
113
- size: `${size}px`,
114
- lineHeight: lineHeightForSize(size),
115
- letterSpacing: letterSpacingForSize(size),
116
- weight: weightForStep(i, STEP_NAMES.length),
117
- };
118
- });
119
-
120
- return {
121
- headingFont,
122
- bodyFont,
123
- monoFont: 'JetBrains Mono',
124
- baseSize,
125
- scaleRatio: ratio,
126
- steps,
127
- };
128
- }
@@ -1,28 +0,0 @@
1
- export {
2
- generateColorPalette,
3
- checkContrast,
4
- hslToHex,
5
- hexToHsl,
6
- } from './generators/color-palette.js';
7
- export { generateTypographySystem } from './generators/typography-system.js';
8
- export { generateSpacingScale } from './generators/spacing-scale.js';
9
- export { generateShadowSystem } from './generators/shadow-system.js';
10
- export { generateBorderSystem } from './generators/border-system.js';
11
- export { generateMotionSystem } from './generators/motion-system.js';
12
- export { generateSvgLogo, defaultLogoConfig } from './generators/logo-generator.js';
13
- export { generateGradientSystem } from './generators/gradient-system.js';
14
- export { generateFavicons } from './generators/favicon-generator.js';
15
- export { generateOgImage } from './generators/og-image-generator.js';
16
- export { exportDesignTokens } from './exporters/design-tokens.js';
17
- export { exportCssVariables } from './exporters/css-variables.js';
18
- export { exportTailwindPreset } from './exporters/tailwind-preset.js';
19
- export { exportFigmaTokens } from './exporters/figma-tokens.js';
20
- export { exportReactTheme } from './exporters/react-theme.js';
21
- export { exportSassVariables } from './exporters/sass-variables.js';
22
- export { validateBrandConsistency } from './validators/brand-consistency.js';
23
- export { validateContrast } from './validators/contrast-checker.js';
24
- export { interpretFeedback } from './ai/brand-interpreter.js';
25
- export { interpretWithKeywords } from './ai/keyword-interpreter.js';
26
- export { applyIntent } from './ai/intent-applier.js';
27
- export type { BrandIntent, ColorIntent, TypographyIntent, InterpreterOptions } from './ai/types.js';
28
- export type { InterpreterStrategy } from './ai/brand-interpreter.js';
@@ -1,79 +0,0 @@
1
- import type { BrandIdentity, BrandValidationIssue, BrandValidationResult } from '../../types.js';
2
- import { validateContrast } from './contrast-checker.js';
3
-
4
- function validateColorCompleteness(brand: BrandIdentity): BrandValidationIssue[] {
5
- const issues: BrandValidationIssue[] = [];
6
- const { colors } = brand;
7
-
8
- if (!colors.primary.hex) {
9
- issues.push({
10
- severity: 'error',
11
- element: 'color.primary',
12
- message: 'Primary color is missing',
13
- });
14
- }
15
- if (!colors.secondary.hex) {
16
- issues.push({
17
- severity: 'error',
18
- element: 'color.secondary',
19
- message: 'Secondary color is missing',
20
- });
21
- }
22
- if (colors.neutral.length < 4) {
23
- issues.push({
24
- severity: 'warning',
25
- element: 'color.neutral',
26
- message: `Only ${colors.neutral.length} neutral shades defined (recommend 6-10)`,
27
- });
28
- }
29
-
30
- return issues;
31
- }
32
-
33
- function validateTypography(brand: BrandIdentity): BrandValidationIssue[] {
34
- const issues: BrandValidationIssue[] = [];
35
- const { typography } = brand;
36
-
37
- if (!typography.headingFont) {
38
- issues.push({
39
- severity: 'error',
40
- element: 'typography.headingFont',
41
- message: 'Heading font is missing',
42
- });
43
- }
44
- if (!typography.bodyFont) {
45
- issues.push({
46
- severity: 'error',
47
- element: 'typography.bodyFont',
48
- message: 'Body font is missing',
49
- });
50
- }
51
- if (typography.baseSize < 14 || typography.baseSize > 20) {
52
- issues.push({
53
- severity: 'warning',
54
- element: 'typography.baseSize',
55
- message: `Base size ${typography.baseSize}px is outside recommended range (14-20px)`,
56
- });
57
- }
58
-
59
- return issues;
60
- }
61
-
62
- export function validateBrandConsistency(brand: BrandIdentity): BrandValidationResult {
63
- const issues: BrandValidationIssue[] = [
64
- ...validateColorCompleteness(brand),
65
- ...validateContrast(brand),
66
- ...validateTypography(brand),
67
- ];
68
-
69
- const errorCount = issues.filter((i) => i.severity === 'error').length;
70
- const warningCount = issues.filter((i) => i.severity === 'warning').length;
71
- const maxScore = 100;
72
- const score = Math.max(0, maxScore - errorCount * 20 - warningCount * 5);
73
-
74
- return {
75
- valid: errorCount === 0,
76
- score,
77
- issues,
78
- };
79
- }