// eslint-disable-next-line @typescript-eslint/no-unused-vars import React from 'react'; import { InsightData } from './types'; /** * Theme configuration for the share card */ export type Theme = 'light' | 'dark'; interface ThemeConfig { background: string; textPrimary: string; textSecondary: string; textMuted: string; cardBackground: string; cardBackgroundSecondary: string; borderColor: string; heatmapColors: string[]; heatmapEmpty: string; } const themes: Record = { light: { background: 'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)', textPrimary: '#0f172a', textSecondary: '#475569', textMuted: '#64748b', cardBackground: 'rgba(255,255,255,0.7)', cardBackgroundSecondary: 'rgba(255,255,255,0.5)', borderColor: '#e2e8f0', // GitHub contribution graph color palette (light mode) heatmapColors: ['#9be9a8', '#40c463', '#30a14e', '#216e39'], heatmapEmpty: '#ebedf0', }, dark: { background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)', textPrimary: '#f8fafc', textSecondary: '#e2e8f0', textMuted: '#94a3b8', cardBackground: 'rgba(255,255,255,0.05)', cardBackgroundSecondary: 'rgba(255,255,255,0.04)', borderColor: 'rgba(255,255,255,0.08)', // GitHub contribution graph color palette (dark mode) heatmapColors: ['#0e4429', '#006d32', '#26a641', '#39d353'], heatmapEmpty: '#2d333b', }, }; /** * A hidden 1200x675 card optimized for Twitter/X sharing. * Rendered off-screen; captured by html2canvas when the user clicks "Share as Card". */ export function ShareCard({ data, theme = 'light', }: { data: InsightData; theme?: Theme; }) { const t = themes[theme]; const { totalMessages = 0, totalSessions = 0, totalLinesAdded = 0, totalLinesRemoved = 0, totalFiles = 0, currentStreak = 0, longestStreak = 0, activeHours = {}, } = data; // Calculate active days const heatmapKeys = Object.keys(data.heatmap || {}); let activeDays = 0; let dateRangeStr = ''; if (heatmapKeys.length > 0) { activeDays = heatmapKeys.length; const timestamps = heatmapKeys.map((d) => new Date(d).getTime()); const minDate = new Date(Math.min(...timestamps)); const maxDate = new Date(Math.max(...timestamps)); const fmt = (d: Date) => d.toISOString().split('T')[0]; dateRangeStr = `${fmt(minDate)} — ${fmt(maxDate)}`; } // Key pattern (truncated for card) const keyPattern = data.qualitative?.interactionStyle?.key_pattern ?? null; // Memorable moment headline (truncated) const truncatedHeadline = data.qualitative?.memorableMoment?.headline ?? null; // Mini heatmap: last 52 weeks (simplified 7-row grid) const miniHeatmap = buildMiniHeatmap(data.heatmap || {}, t); return (
{/* Header */}
Qwen Code Insights
{dateRangeStr}
qwen.ai
{/* Stats Grid */}
{/* Body: Heatmap + Tools + Moment */}
{/* Left: Mini Heatmap */}
Activity · {activeDays} active days
{/* Right: Active Hours + Moment */}
{/* Active Hours */}
Active Hours
{/* Key Pattern + Memorable Moment */}
{/* Decorative large quote mark */}
{keyPattern && (
{keyPattern}
)} {truncatedHeadline && (
{truncatedHeadline}
)}
{/* Footer */}
Generated by Qwen Code · {new Date().toISOString().split('T')[0]}
github.com/QwenLM/qwen-code
); } function StatBox({ value, label, small, theme, }: { value: string; label: string; small?: boolean; theme: ThemeConfig; }) { return (
{value}
{label}
); } function ActiveHoursChart({ activeHours, theme, }: { activeHours: { [hour: number]: number }; theme: ThemeConfig; }) { const phases = [ { label: 'Morning', time: '06–12', hours: [6, 7, 8, 9, 10, 11], color: '#fbbf24', }, { label: 'Afternoon', time: '12–18', hours: [12, 13, 14, 15, 16, 17], color: '#0ea5e9', }, { label: 'Evening', time: '18–22', hours: [18, 19, 20, 21], color: '#6366f1', }, { label: 'Night', time: '22–06', hours: [22, 23, 0, 1, 2, 3, 4, 5], color: '#475569', }, ]; const data = phases.map((phase) => ({ ...phase, total: phase.hours.reduce((acc, h) => acc + (activeHours[h] || 0), 0), })); const maxTotal = Math.max(...data.map((d) => d.total), 1); return ( <> {data.map((item) => { const pct = maxTotal > 0 ? (item.total / maxTotal) * 100 : 0; return (
{item.label} {item.time}
{item.total}
); })} ); } /** Build a 7x~26 grid of intensity values for the mini heatmap (last ~6 months). */ function buildMiniHeatmap( heatmap: Record, theme: ThemeConfig, ): { color: string }[] { const today = new Date(); const weeksToShow = 26; const totalDays = weeksToShow * 7; const startDate = new Date(today); startDate.setDate(startDate.getDate() - totalDays + 1); // Align to the beginning of the week (Sunday) startDate.setDate(startDate.getDate() - startDate.getDay()); const cells: { color: string }[] = []; const endDate = new Date(today); endDate.setDate(endDate.getDate() + (6 - endDate.getDay())); // end of this week const d = new Date(startDate); while (d <= endDate) { const key = d.toISOString().split('T')[0]; const val = heatmap[key] || 0; cells.push({ color: heatColor(val, theme) }); d.setDate(d.getDate() + 1); } return cells; } // GitHub contribution graph color palette - theme aware function heatColor(val: number, theme: ThemeConfig): string { if (val === 0) return theme.heatmapEmpty; if (val < 2) return theme.heatmapColors[0]; if (val < 4) return theme.heatmapColors[1]; if (val < 10) return theme.heatmapColors[2]; return theme.heatmapColors[3]; } function MiniHeatmapGrid({ cells }: { cells: { color: string }[] }) { const rows = 7; const cols = Math.ceil(cells.length / rows); const cellSize = 14; const gap = 3; const svgWidth = cols * (cellSize + gap); const svgHeight = rows * (cellSize + gap); return ( {cells.map((cell, i) => { const col = Math.floor(i / rows); const row = i % rows; return ( ); })} ); } // Mini Heatmap Legend Component for ShareCard (theme aware) function MiniHeatmapLegend({ theme }: { theme: ThemeConfig }) { return (
Less {[ theme.heatmapEmpty, theme.heatmapColors[0], theme.heatmapColors[1], theme.heatmapColors[2], theme.heatmapColors[3], ].map((color, index) => ( ))} More
); }