From 7d4f21e2600cded4bdc0186d61a24017c70e3bc4 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Tue, 17 Feb 2026 14:22:31 +0100 Subject: [PATCH] feat: add semantic analysis to design-diff-analyzer - Detect design patterns (color-coding systems, contrast improvements, spacing) - Analyze design intent and explain **why** changes were made - Add critical warnings for dynamic implementations (e.g., color-coding must be type-based, not hardcoded) - Include intent analysis in output for Claude Code This helps Claude Code understand the **system** behind changes, not just apply spot modifications. --- scripts/design-diff-analyzer.js | 96 ++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/scripts/design-diff-analyzer.js b/scripts/design-diff-analyzer.js index 212bd058..a6769887 100755 --- a/scripts/design-diff-analyzer.js +++ b/scripts/design-diff-analyzer.js @@ -88,7 +88,58 @@ function analyzeDiff(diff) { return changes; } -function generateTasks(changes) { +function analyzeIntent(changes) { + const intent = { + patterns: [], + systems: [], + recommendations: [] + }; + + // Detect color-coding system for types + const accentColorIntro = changes.colors.filter(c => + c.to.includes('accent-') && !c.from.includes('accent-') + ); + + if (accentColorIntro.length >= 4) { + const colors = [...new Set(accentColorIntro.map(c => c.to))]; + intent.systems.push({ + type: 'color-coding', + description: 'Type-based color system', + colors: colors, + explanation: 'Each entity type (Project, Procedure, Person, Topic) has its own accent color. This color should be used consistently for: icons, active states, badges, and any UI element representing that type.', + critical: '⚠️ IMPORTANT: Active states must use the color **dynamically** based on the note type, not hardcoded. Example: if a Project is selected, use accent-red; if a Procedure is selected, use accent-purple.' + }); + } + + // Detect contrast improvements + const contrastChanges = changes.colors.filter(c => + c.from === '$--muted-foreground' && c.to === '$--foreground' + ); + + if (contrastChanges.length > 3) { + intent.patterns.push({ + type: 'contrast-improvement', + description: 'Increased text contrast for better readability', + count: contrastChanges.length + }); + } + + // Detect spacing improvements + if (changes.spacing.length > 0) { + const avgIncrease = changes.spacing.reduce((sum, s) => sum + (s.to - s.from), 0) / changes.spacing.length; + if (avgIncrease > 0) { + intent.patterns.push({ + type: 'spacing-increase', + description: 'Increased spacing for less cluttered UI', + avgIncrease: Math.round(avgIncrease) + }); + } + } + + return intent; +} + +function generateTasks(changes, intent) { const tasks = []; // Color changes → update theme.json or CSS variables @@ -97,10 +148,18 @@ function generateTasks(changes) { ` - ${c.type}: ${c.from} → ${c.to}` ).join('\n'); + let details = `Color changes detected:\n${colorList}\n\nUpdate src/theme.json or CSS variables to match the design.`; + + // Add intent explanation if color-coding system detected + const colorSystem = intent.systems.find(s => s.type === 'color-coding'); + if (colorSystem) { + details += `\n\n${colorSystem.explanation}\n\n${colorSystem.critical}`; + } + tasks.push({ priority: 'high', description: 'Update color palette', - details: `Color changes detected:\n${colorList}\n\nUpdate src/theme.json or CSS variables to match the design.` + details: details }); } @@ -133,13 +192,35 @@ function generateTasks(changes) { return tasks; } -function formatOutput(tasks, changes) { +function formatOutput(tasks, changes, intent) { if (tasks.length === 0 && changes.content.length === 0) { return null; } let output = '🎨 Design changes detected:\n\n'; + // Add design intent analysis + if (intent.systems.length > 0 || intent.patterns.length > 0) { + output += '🧠 Design Intent Analysis:\n\n'; + + intent.systems.forEach(system => { + output += `📐 ${system.description}\n`; + output += `${system.explanation}\n`; + if (system.critical) { + output += `\n${system.critical}\n`; + } + output += '\n'; + }); + + intent.patterns.forEach(pattern => { + output += `• ${pattern.description}`; + if (pattern.count) output += ` (${pattern.count} changes)`; + if (pattern.avgIncrease) output += ` (+${pattern.avgIncrease}px avg)`; + output += '\n'; + }); + output += '\n'; + } + if (tasks.length > 0) { output += '📋 Implementation tasks:\n\n'; tasks.forEach((task, i) => { @@ -156,7 +237,7 @@ function formatOutput(tasks, changes) { output += '\n'; } - return { output, tasks }; + return { output, tasks, intent }; } function main() { @@ -168,8 +249,9 @@ function main() { } const changes = analyzeDiff(diff); - const tasks = generateTasks(changes); - const result = formatOutput(tasks, changes); + const intent = analyzeIntent(changes); + const tasks = generateTasks(changes, intent); + const result = formatOutput(tasks, changes, intent); if (!result) { console.log('No significant design changes detected'); @@ -181,7 +263,7 @@ function main() { // Output JSON for automation if (process.argv.includes('--json')) { console.log('\n---JSON---'); - console.log(JSON.stringify({ tasks, changes }, null, 2)); + console.log(JSON.stringify({ tasks, changes, intent }, null, 2)); } process.exit(tasks.length > 0 ? 1 : 0); // Exit 1 if there are tasks to implement