Spaces:
Sleeping
Sleeping
| // src/components/ContributionChart.jsx | |
| // Horizontal bar chart showing per-modality contribution to the final decision | |
| import { useEffect, useState } from 'react'; | |
| const MODALITIES = [ | |
| { | |
| key: 'visual', | |
| label: 'Visual', | |
| icon: '👁️', | |
| color: '#7c3aed', | |
| bg: 'linear-gradient(90deg, #7c3aed, #9d6ff7)', | |
| }, | |
| { | |
| key: 'audio', | |
| label: 'Audio', | |
| icon: '🎙️', | |
| color: '#06b6d4', | |
| bg: 'linear-gradient(90deg, #06b6d4, #22d3ee)', | |
| }, | |
| { | |
| key: 'lipsync', | |
| label: 'Lip-Sync', | |
| icon: '👄', | |
| color: '#f43f5e', | |
| bg: 'linear-gradient(90deg, #f43f5e, #fb7185)', | |
| }, | |
| ]; | |
| export default function ContributionChart({ contributions }) { | |
| const [animated, setAnimated] = useState(false); | |
| useEffect(() => { | |
| const t = setTimeout(() => setAnimated(true), 200); | |
| return () => clearTimeout(t); | |
| }, []); | |
| return ( | |
| <div | |
| className="contribution-card glass-card" | |
| role="figure" | |
| aria-label="Modality contribution chart" | |
| > | |
| <h3>Modality Contributions</h3> | |
| <div className="contribution-bars"> | |
| {MODALITIES.map((m, i) => { | |
| const value = contributions[m.key] ?? 0; | |
| const pct = Math.round(value * 100); | |
| return ( | |
| <div | |
| key={m.key} | |
| className="contribution-row" | |
| style={{ animationDelay: `${i * 100}ms` }} | |
| > | |
| {/* Label */} | |
| <div className="contribution-label"> | |
| <span aria-hidden="true">{m.icon}</span>{' '} | |
| {m.label} | |
| </div> | |
| {/* Track + fill */} | |
| <div | |
| className="contribution-track" | |
| role="progressbar" | |
| aria-valuenow={pct} | |
| aria-valuemin={0} | |
| aria-valuemax={100} | |
| aria-label={`${m.label} contribution: ${pct}%`} | |
| > | |
| <div | |
| className="contribution-fill" | |
| style={{ | |
| width: animated ? `${pct}%` : '0%', | |
| background: m.bg, | |
| boxShadow: `0 0 10px ${m.color}55`, | |
| transitionDelay: `${i * 120}ms`, | |
| }} | |
| /> | |
| </div> | |
| {/* Value */} | |
| <div | |
| className="contribution-value" | |
| style={{ color: m.color }} | |
| > | |
| {pct}% | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| {/* Legend footnote */} | |
| <p | |
| style={{ | |
| marginTop: '20px', | |
| fontSize: '0.75rem', | |
| color: 'var(--text-muted)', | |
| borderTop: '1px solid var(--border-subtle)', | |
| paddingTop: '12px', | |
| }} | |
| > | |
| Contribution weights are learned during multimodal fusion training on FF++ + DFDC datasets. | |
| </p> | |
| </div> | |
| ); | |
| } | |