diff --git a/app/figma-plugin/src/ui.template.html b/app/figma-plugin/src/ui.template.html index cd6d5b18..84748639 100644 --- a/app/figma-plugin/src/ui.template.html +++ b/app/figma-plugin/src/ui.template.html @@ -150,25 +150,29 @@

Ready to analyze

'
' + scores.overall.percentage + ' / 100
' + '
Overall Score · ' + result.nodeCount + ' nodes
'; - // Category grid + // Category list var categories = CanICode.CATEGORIES || CATEGORIES; + var catTips = { structure: 'Can AI read the layout? (Auto Layout, nesting, positioning)', token: 'Can AI reproduce exact values? (colors, fonts, shadows, spacing)', component: 'Is the design efficient for AI context? (reuse, variants)', naming: 'Can AI infer meaning? (semantic names, conventions)', behavior: 'Can AI know what happens? (overflow, truncation, interactions)' }; var gridEl = document.getElementById('cat-grid'); gridEl.innerHTML = categories.map(function(cat) { var cs = scores.byCategory[cat]; - return '
' + + var barColor = cs.percentage >= 80 ? 'var(--green)' : cs.percentage >= 60 ? 'var(--amber)' : 'var(--red)'; + return '
' + '
' + cs.percentage + '
' + - '
' + (CATEGORY_LABELS[cat] || cat) + '
' + - '
' + cs.issueCount + ' issues
' + + '
' + (CATEGORY_LABELS[cat] || cat) + '
' + + '
' + + '
' + cs.issueCount + '
' + '
'; }).join(''); // Summary bar var s = scores.summary; + var sevTips = { blocking: 'Cannot implement correctly without fixing. Direct impact on screen reproduction.', risk: 'Implementable now but will break or increase cost later.', missing: 'Information is absent, forcing AI to guess.', suggestion: 'Not immediately problematic, but improves systemization.' }; document.getElementById('summary-bar').innerHTML = - '
' + s.blocking + ' Blocking
' + - '
' + s.risk + ' Risk
' + - '
' + s.missingInfo + ' Missing
' + - '
' + s.suggestion + ' Suggestion
' + + '
' + s.blocking + ' Blocking
' + + '
' + s.risk + ' Risk
' + + '
' + s.missingInfo + ' Missing
' + + '
' + s.suggestion + ' Suggestion
' + '
' + s.totalIssues + ' total
'; // Filter tabs @@ -260,10 +264,6 @@

Ready to analyze

document.querySelectorAll('.filter-tab').forEach(function(tab) { tab.classList.toggle('active', tab.dataset.cat === cat); }); - // Update category card highlights - document.querySelectorAll('.cat-card').forEach(function(card) { - card.classList.toggle('active', card.dataset.cat === cat); - }); if (currentResult) { renderIssues(currentResult.issues, cat); } @@ -343,6 +343,41 @@

Ready to analyze

} } }; + + // ---- Tooltip (JS-based, works inside Figma iframe) ---- + (function() { + var tip = document.createElement('div'); + tip.className = 'cic-tooltip'; + var arrow = document.createElement('div'); + arrow.className = 'cic-tooltip-arrow'; + document.body.appendChild(tip); + document.body.appendChild(arrow); + + function hide() { tip.classList.remove('visible'); arrow.classList.remove('visible'); } + + document.addEventListener('mouseover', function(e) { + var el = e.target.closest('[data-tip]'); + if (!el) { hide(); return; } + tip.textContent = el.getAttribute('data-tip'); + tip.classList.add('visible'); + var rect = el.getBoundingClientRect(); + var tipH = tip.offsetHeight; + var tipW = tip.offsetWidth; + var cx = rect.left + rect.width / 2; + var tipLeft = Math.max(4, Math.min(cx - tipW / 2, window.innerWidth - tipW - 4)); + var above = rect.top - tipH - 10 >= 0; + var tipTop = above ? rect.top - tipH - 10 : rect.bottom + 10; + tip.style.left = tipLeft + 'px'; + tip.style.top = tipTop + 'px'; + arrow.style.left = (cx - 4) + 'px'; + arrow.style.top = (above ? rect.top - 14 : rect.bottom + 6) + 'px'; + arrow.classList.add('visible'); + }); + document.addEventListener('mouseleave', hide); + document.addEventListener('mouseout', function(e) { + if (!e.target.closest('[data-tip]')) hide(); + }); + })(); diff --git a/app/shared/styles.css b/app/shared/styles.css index 4d27fdfe..d0599837 100644 --- a/app/shared/styles.css +++ b/app/shared/styles.css @@ -55,45 +55,92 @@ body { margin-top: 2px; } -/* ---- Category grid ---- */ +/* ---- Category list ---- */ .cat-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 8px; - margin-bottom: 16px; -} -.cat-card { + display: flex; + flex-direction: column; + gap: 0; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-sm); - padding: 10px 8px; - text-align: center; - cursor: pointer; - transition: all 0.15s; + margin-bottom: 16px; } -.cat-card:hover { border-color: #a1a1aa; } -.cat-card.active { border-color: var(--fg); } +.cat-row { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; + transition: background 0.15s; +} +.cat-row:not(:last-child) { border-bottom: 1px solid var(--border); } +.cat-row:hover { background: rgba(0,0,0,0.03); } .cat-score { - font-size: 20px; + font-size: 16px; font-weight: 700; - line-height: 1.2; + min-width: 32px; + text-align: right; } .cat-score.green { color: var(--green); } .cat-score.amber { color: var(--amber); } .cat-score.red { color: var(--red); } .cat-label { - font-size: 10px; - color: var(--fg-muted); - margin-top: 2px; + font-size: 12px; + color: var(--fg); + flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.cat-bar { + flex: 1; + height: 4px; + background: var(--border); + border-radius: 2px; + overflow: hidden; +} +.cat-bar-fill { + height: 100%; + border-radius: 2px; + transition: width 0.3s; +} .cat-issues { font-size: 10px; color: var(--fg-muted); + white-space: nowrap; } +/* ---- Tooltips ---- */ +.cic-tooltip { + position: fixed; + padding: 5px 10px; + background: #18181b; + color: #fafafa; + font-size: 11px; + font-weight: 400; + line-height: 1.4; + border-radius: 6px; + white-space: normal; + max-width: 220px; + width: max-content; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s; + z-index: 1000; +} +.cic-tooltip.visible { opacity: 1; } +.cic-tooltip-arrow { + position: fixed; + width: 8px; + height: 8px; + background: #18181b; + transform: rotate(45deg); + pointer-events: none; + opacity: 0; + transition: opacity 0.15s; + z-index: 999; +} +.cic-tooltip-arrow.visible { opacity: 1; } + /* ---- Summary bar ---- */ .summary-bar { display: flex; diff --git a/app/web/src/index.html b/app/web/src/index.html index 0cb0d188..13c31b2a 100644 --- a/app/web/src/index.html +++ b/app/web/src/index.html @@ -154,7 +154,7 @@

Analyze your Figma designs

-
+
@@ -356,9 +356,11 @@

document.getElementById('category-gauges').innerHTML = CATEGORIES.map(function(cat) { var cs = scores.byCategory[cat]; + var desc = CATEGORY_DESCRIPTIONS[cat]; return '' + renderGaugeSvg(cs.percentage, 100, 7) + '' + CATEGORY_LABELS[cat] + '' + - '' + cs.issueCount + ' issues'; + '' + cs.issueCount + ' issues' + + ''; }).join(''); document.getElementById('summary-dots').innerHTML = @@ -387,7 +389,8 @@

document.getElementById('footer-meta').textContent = new Date().toLocaleString() + ' \u00B7 ' + result.nodeCount + ' nodes \u00B7 Max depth ' + result.maxDepth; } - function renderDot(cls, count, label) { return '
' + count + '' + label + '
'; } + var SEV_TIPS = { Blocking: 'Cannot implement correctly without fixing. Direct impact on screen reproduction.', Risk: 'Implementable now but will break or increase cost later.', 'Missing Info': 'Information is absent, forcing AI to guess.', Suggestion: 'Not immediately problematic, but improves systemization.' }; + function renderDot(cls, count, label) { var tip = SEV_TIPS[label] || ''; return '
' + count + '' + label + '
'; } function renderCat(cat, scores, issues, fk) { var cs = scores.byCategory[cat], hp = issues.some(function(i) { return i.config.severity === 'blocking' || i.config.severity === 'risk'; }); diff --git a/src/core/report-html/index.ts b/src/core/report-html/index.ts index bdac2c6b..4f114b67 100644 --- a/src/core/report-html/index.ts +++ b/src/core/report-html/index.ts @@ -122,7 +122,7 @@ ${CATEGORIES.map(cat => { ${renderGaugeSvg(cs.percentage, 100, 7)} ${CATEGORY_LABELS[cat]} ${cs.issueCount} issues -