forked from southleft/figmalint
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Sprint 6 — Advanced Analysis Engine (18 features) #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
bb6f860
feat: Sprint 6 — Advanced Analysis Engine (18 features)
lemone112 73fc3e8
feat: Sprint 7 — UX polish, new lint modules, 8 result cards, team co…
lemone112 6c53f6e
fix: address all 14 CodeRabbit review comments from PR #4
lemone112 3f15617
feat: wire end-to-end integration for all Sprint 6-7 features
lemone112 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /** | ||
| * Build the AI prompt for generating a comprehensive accessibility specification. | ||
| */ | ||
| export function buildA11ySpecPrompt( | ||
| componentInfo: string, | ||
| lintSummary: string, | ||
| ): string { | ||
| return `You are a WCAG 2.2 accessibility specialist generating a comprehensive accessibility specification from a UI design. | ||
|
|
||
| ## Component Information | ||
| ${componentInfo} | ||
|
|
||
| ## Lint Summary (deterministic analysis) | ||
| ${lintSummary} | ||
|
|
||
| ## Your Task | ||
| Analyze the screenshot and generate a complete accessibility specification. This spec will be used by developers to implement proper ARIA attributes, keyboard navigation, focus management, and screen reader support. | ||
|
|
||
| For each section, be specific to what you see in the design — don't generate generic boilerplate. | ||
|
|
||
| Respond in this exact JSON format: | ||
| { | ||
| "landmarks": [ | ||
| { "role": "<ARIA landmark role: banner|navigation|main|complementary|contentinfo|search|form|region>", "label": "<accessible name for the landmark>", "element": "<which UI element this maps to>" } | ||
| ], | ||
| "headingStructure": [ | ||
| { "level": 1, "text": "<heading text>", "element": "<which UI element>" } | ||
| ], | ||
| "focusOrder": [ | ||
| { "order": 1, "element": "<element description>", "type": "<interactive|informational|container>", "notes": "<special focus management notes>" } | ||
| ], | ||
| "ariaAnnotations": [ | ||
| { | ||
| "element": "<element description>", | ||
| "role": "<ARIA role if non-default>", | ||
| "ariaLabel": "<aria-label value if needed>", | ||
| "ariaDescribedBy": "<id of describing element if applicable>", | ||
| "ariaLive": "<polite|assertive if applicable>", | ||
| "notes": "<implementation notes>" | ||
| } | ||
| ], | ||
| "keyboardShortcuts": [ | ||
| { "key": "<key combination e.g. Enter, Space, Escape, Tab, Arrow keys>", "action": "<what happens>", "element": "<which element>" } | ||
| ], | ||
| "liveRegions": [ | ||
| { "element": "<element>", "type": "polite|assertive", "trigger": "<what causes the update>" } | ||
| ], | ||
| "colorContrastReport": [ | ||
| { "element": "<element>", "foreground": "<color>", "background": "<color>", "ratio": 4.5, "passes": "AA|AAA|fail" } | ||
| ], | ||
| "recommendations": [ | ||
| { | ||
| "title": "<concise title>", | ||
| "description": "<detailed recommendation>", | ||
| "wcagCriterion": "<e.g. 1.4.3 Contrast (Minimum)>", | ||
| "level": "A|AA|AAA" | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| Important: | ||
| - For focusOrder, list ALL interactive elements in the logical tab order you observe | ||
| - For ariaAnnotations, include every element that needs non-default ARIA attributes | ||
| - For colorContrastReport, estimate contrast ratios from what you see — flag anything that appears to have low contrast | ||
| - For recommendations, reference specific WCAG 2.2 success criteria | ||
| - Be thorough but avoid false positives — only flag real issues`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import { GROUNDING_INSTRUCTIONS } from './shared/grounding-instructions.js'; | ||
|
|
||
| /** | ||
| * Attention/visual-hierarchy analysis prompt. | ||
| * Used by the extended analyzer to evaluate where the eye is drawn. | ||
| */ | ||
| export function buildAttentionPrompt(componentInfo: string): string { | ||
| return `Analyze the visual attention flow in this UI screenshot. | ||
|
|
||
| ## Context | ||
| ${componentInfo} | ||
|
|
||
| ## Evaluation Criteria | ||
|
|
||
| ### 1. Focal Point Clarity | ||
| - Is there one clear primary focal point? | ||
| - Does the visual hierarchy guide the eye in a logical sequence (primary -> secondary -> tertiary)? | ||
| - Are F-pattern or Z-pattern reading flows supported? | ||
|
|
||
| ### 2. Visual Weight Distribution | ||
| - Is visual weight (size, color, contrast) distributed intentionally? | ||
| - Do important elements have the most visual weight? | ||
| - Are decorative elements competing with functional ones? | ||
|
|
||
| ### 3. CTA Prominence | ||
| - Is the primary call-to-action the most visually prominent interactive element? | ||
| - Is there clear differentiation between primary, secondary, and tertiary actions? | ||
| - Can a user identify the next step within 3 seconds? | ||
|
|
||
| ### 4. Information Density | ||
| - Is the content density appropriate for the page type? | ||
| - Are there clear content groups separated by whitespace? | ||
| - Is progressive disclosure used where appropriate? | ||
|
|
||
| ## Response Format (JSON) | ||
| { | ||
| "focalPoint": { | ||
| "exists": true|false, | ||
| "element": "<description of primary focal point>", | ||
| "strength": "strong|moderate|weak" | ||
| }, | ||
| "readingFlow": { | ||
| "pattern": "F-pattern|Z-pattern|scattered|linear", | ||
| "blockers": ["<elements that interrupt natural flow>"] | ||
| }, | ||
| "ctaProminence": { | ||
| "rating": "pass|needs_improvement|fail", | ||
| "primaryCta": "<description>", | ||
| "competingElements": ["<element competing for attention>"] | ||
| }, | ||
| "findings": [ | ||
| { | ||
| "finding": "<specific observation>", | ||
| "confidence": 0.0, | ||
| "evidence": "<element or region reference>", | ||
| "category": "attention", | ||
| "severity": "critical|warning|info" | ||
| } | ||
| ], | ||
| "summary": "<2-3 sentence summary>" | ||
| } | ||
| ${GROUNDING_INSTRUCTIONS}`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| /** | ||
| * Brand Consistency Analysis prompt. | ||
| * Evaluates a design screenshot against a structured brand guide. | ||
| */ | ||
|
|
||
| export interface BrandGuide { | ||
| colors: Record<string, { hex: string; tolerance: number; usage: string }>; | ||
| typography: { | ||
| heading: { family: string; weights: number[] }; | ||
| body: { family: string; weights: number[] }; | ||
| }; | ||
| spacing: { base: number; scale: number[] }; | ||
| personality: string[]; // e.g., ['professional', 'approachable', 'modern'] | ||
| rules?: Array<{ id: string; description: string; severity: 'error' | 'warning' }>; | ||
| } | ||
|
|
||
| export function buildBrandConsistencyPrompt( | ||
| brandGuide: BrandGuide, | ||
| lintContext: string, | ||
| ): string { | ||
| // Serialize color palette for the prompt | ||
| const colorLines = Object.entries(brandGuide.colors) | ||
| .map( | ||
| ([name, c]) => | ||
| ` - ${name}: ${c.hex} (tolerance: +/-${c.tolerance}%, usage: ${c.usage})`, | ||
| ) | ||
| .join('\n'); | ||
|
|
||
| // Serialize typography | ||
| const typoLines = [ | ||
| ` Headings: ${brandGuide.typography.heading.family} [weights: ${brandGuide.typography.heading.weights.join(', ')}]`, | ||
| ` Body: ${brandGuide.typography.body.family} [weights: ${brandGuide.typography.body.weights.join(', ')}]`, | ||
| ].join('\n'); | ||
|
|
||
| // Serialize spacing | ||
| const spacingLine = ` Base unit: ${brandGuide.spacing.base}px, scale: [${brandGuide.spacing.scale.join(', ')}]`; | ||
|
|
||
| // Serialize personality | ||
| const personalityLine = brandGuide.personality.join(', '); | ||
|
|
||
| // Serialize custom rules | ||
| const rulesBlock = | ||
| brandGuide.rules && brandGuide.rules.length > 0 | ||
| ? `\n## Custom Brand Rules\n${brandGuide.rules.map((r) => ` - [${r.severity.toUpperCase()}] ${r.id}: ${r.description}`).join('\n')}\n` | ||
| : ''; | ||
|
|
||
| return `You are a brand design auditor. Evaluate the screenshot against the brand guidelines provided below. Be precise and cite specific visual evidence from the screenshot. | ||
|
|
||
| ## Brand Color Palette | ||
| ${colorLines} | ||
|
|
||
| ## Typography Specifications | ||
| ${typoLines} | ||
|
|
||
| ## Spacing System | ||
| ${spacingLine} | ||
|
|
||
| ## Brand Personality Keywords | ||
| ${personalityLine} | ||
| ${rulesBlock} | ||
| ## Automated Lint Context | ||
| ${lintContext} | ||
|
|
||
| ## Evaluation Criteria | ||
|
|
||
| ### 1. Color Palette Adherence | ||
| Examine every visible color in the UI. Compare each against the brand palette. Flag colors that fall outside the specified tolerance. Note which element uses the off-brand color and what the expected color should be. | ||
|
|
||
| ### 2. Typography Compliance | ||
| Check all visible text for correct font family and weight usage. Headings must use the heading family/weights. Body text must use the body family/weights. Flag any text that appears to use a non-specified font or weight. | ||
|
|
||
| ### 3. Spacing System Consistency | ||
| Check that spacing between elements aligns with the base grid and scale values. Flag values that do not correspond to any value in the spacing scale. Estimate spacing in pixels from the screenshot. | ||
|
|
||
| ### 4. Brand Personality Match | ||
| Assess whether the overall visual impression matches the listed personality keywords. Consider color warmth/coolness, typography tone, whitespace usage, imagery style, and overall aesthetic. Rate the match as "strong", "moderate", or "weak" with specific evidence. | ||
|
|
||
| ### 5. Visual Language Consistency | ||
| Evaluate iconography style (outlined vs filled, rounded vs sharp), illustration style if present, and photography treatment. Flag inconsistencies within the screenshot. | ||
|
|
||
| Respond in this exact JSON format: | ||
| { | ||
| "overallScore": <0-100>, | ||
| "colorCompliance": { | ||
| "score": <0-100>, | ||
| "violations": [ | ||
| { | ||
| "element": "<element description>", | ||
| "found": "<hex or description of found color>", | ||
| "expected": "<expected brand color name and hex>", | ||
| "tolerance": <tolerance percentage that was exceeded> | ||
| } | ||
| ] | ||
| }, | ||
| "typographyCompliance": { | ||
| "score": <0-100>, | ||
| "violations": [ | ||
| { | ||
| "element": "<element description>", | ||
| "found": "<found font/weight>", | ||
| "expected": "<expected font/weight>" | ||
| } | ||
| ] | ||
| }, | ||
| "spacingCompliance": { | ||
| "score": <0-100>, | ||
| "offGridValues": [<pixel values that don't match the spacing scale>] | ||
| }, | ||
| "personalityMatch": { | ||
| "rating": "strong|moderate|weak", | ||
| "evidence": ["<specific observation 1>", "<specific observation 2>"] | ||
| }, | ||
| "recommendations": [ | ||
| { | ||
| "title": "<short title>", | ||
| "description": "<actionable recommendation>", | ||
| "severity": "error|warning|info" | ||
| } | ||
| ], | ||
| "summary": "<3-5 sentence overall brand consistency assessment>" | ||
| }`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,70 @@ | ||
| /** | ||
| * Build a structured follow-up prompt for the conversational chat phase. | ||
| * | ||
| * Injects the current analysis context (score, top issues, recent fixes) | ||
| * and tells the model exactly what capabilities the user can ask about. | ||
| */ | ||
| export function buildFollowupPrompt( | ||
| sessionContext: string, | ||
| analysisJson?: string, | ||
| ): string { | ||
| return `You are a design review assistant helping a designer improve their component. | ||
| // Extract top issues from the analysis JSON if available | ||
| let topIssuesSummary = ''; | ||
| if (analysisJson) { | ||
| try { | ||
| const analysis = JSON.parse(analysisJson); | ||
| const failCategories: string[] = []; | ||
| const needsImprovementCategories: string[] = []; | ||
|
|
||
| Session context: | ||
| ${sessionContext} | ||
| for (const [key, value] of Object.entries(analysis)) { | ||
| const cat = value as Record<string, unknown> | undefined; | ||
| if (cat && typeof cat === 'object' && 'rating' in cat) { | ||
| if (cat.rating === 'fail') failCategories.push(key); | ||
| else if (cat.rating === 'needs_improvement') needsImprovementCategories.push(key); | ||
| } | ||
| } | ||
|
|
||
| Respond naturally and helpfully. Be specific about what to fix and why. Keep responses concise.`; | ||
| if (failCategories.length > 0) { | ||
| topIssuesSummary += `\nFailing categories: ${failCategories.join(', ')}`; | ||
| } | ||
| if (needsImprovementCategories.length > 0) { | ||
| topIssuesSummary += `\nNeeds improvement: ${needsImprovementCategories.join(', ')}`; | ||
| } | ||
| } catch { | ||
| // Analysis JSON not parseable — proceed without it | ||
| } | ||
| } | ||
|
|
||
| return `You are a senior design review assistant embedded in FigmaLint, a Figma plugin that analyzes design quality. You are in a follow-up conversation after an initial analysis has been completed. | ||
|
|
||
| ## Current Analysis Context | ||
| ${sessionContext}${topIssuesSummary} | ||
|
|
||
| ## Your Capabilities | ||
| When the user asks questions, you can help with: | ||
|
|
||
| 1. **Explain findings** — Explain why a specific issue matters and cite design principles (Gestalt, WCAG, Nielsen heuristics) | ||
| 2. **Prioritize fixes** — Help the user decide which issues to fix first based on user impact | ||
| 3. **Suggest alternatives** — Propose concrete design changes (spacing values, color adjustments, layout modifications) | ||
| 4. **Compare with best practices** — Reference how top products (Stripe, Linear, Vercel, Figma) solve similar problems | ||
| 5. **Deep-dive a category** — Provide more detail on any review category (visual hierarchy, color harmony, etc.) | ||
| 6. **Re-evaluate after changes** — When the user says they've made changes, acknowledge and suggest re-running analysis | ||
|
|
||
| ## Response Guidelines | ||
| - Be specific: reference exact elements from the analysis ("the submit button", "the header spacing") | ||
| - Be concise: 2-4 sentences for simple questions, structured lists for complex ones | ||
| - Be actionable: every suggestion should be implementable in Figma | ||
| - Reference the current score and issues when relevant | ||
| - If the user asks about something outside the analysis scope, be honest about limitations | ||
|
|
||
| ## Structured Response | ||
| When the user requests a specific action (like "list all critical issues" or "prioritize fixes"), respond in JSON: | ||
| { | ||
| "action": "<action type>", | ||
| "items": [ | ||
| { "title": "<short title>", "description": "<detail>", "priority": "high|medium|low" } | ||
| ] | ||
| } | ||
|
|
||
| For conversational questions, respond in plain text. Do not use JSON for casual conversation.`; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: lemone112/figmalint
Length of output: 13474
В
saveAnalysisResult()не сериализуетсяaiReviewперед сохранением в БД.analyzer.tsпередаётaiReviewкак объектAiReviewResult, ноsaveAnalysisResult()(строка 25) сохраняет его напрямую безJSON.stringify(). База данных ожидаетai_review: string | null(backend/src/db/queries.ts:34), поэтому объект будет сохранён как"[object Object]"или аналогичная поломанная представление. Когда позжеloadSession()возвращает эту строку иbuildFollowupPrompt()пытается её распарсить,JSON.parse()молча падает вcatch, и данныеAiReviewResultтеряются.Исправление: в
saveAnalysisResult()нужно добавить условное сериализацию дляaiReviewиlintResult:🤖 Prompt for AI Agents