this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve daily summary display with structured format

- Parse structured JSON from generateObject output
- Display as list with bold project names
- Fallback to plain text for legacy summaries
- Copy button exports readable text format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

alice c3172a06 d8784f4f

+45 -12
+45 -12
src/web/app/components/BragSummary.tsx
··· 1 - import React, { useState } from 'react'; 1 + import React, { useState, useMemo } from 'react'; 2 2 import { Copy, Check, Sparkles } from 'lucide-react'; 3 3 4 4 interface Props { 5 5 summary: string; 6 6 } 7 7 8 + interface DailySummary { 9 + projects: { name: string; summary: string }[]; 10 + } 11 + 12 + function parseSummary(summary: string): DailySummary | null { 13 + try { 14 + const parsed = JSON.parse(summary); 15 + if (parsed.projects && Array.isArray(parsed.projects)) { 16 + return parsed as DailySummary; 17 + } 18 + } catch { 19 + // Not JSON, return null 20 + } 21 + return null; 22 + } 23 + 8 24 export default function BragSummary({ summary }: Props) { 9 25 const [copied, setCopied] = useState(false); 10 26 27 + const parsed = useMemo(() => parseSummary(summary), [summary]); 28 + 11 29 const handleCopy = () => { 12 - navigator.clipboard.writeText(summary); 30 + // Copy as readable text 31 + const text = parsed 32 + ? parsed.projects.map(p => `${p.name}: ${p.summary}`).join('\n') 33 + : summary; 34 + navigator.clipboard.writeText(text); 13 35 setCopied(true); 14 36 setTimeout(() => setCopied(false), 2000); 15 37 }; ··· 17 39 if (!summary) return null; 18 40 19 41 return ( 20 - <div className="relative group rounded-2xl p-6 mb-8 bg-gradient-to-br from-indigo-50 to-blue-50 border border-indigo-100 shadow-sm"> 21 - <div className="absolute top-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity"> 42 + <div className="relative group rounded-lg p-4 mb-4 bg-gradient-to-br from-indigo-50 to-blue-50 border border-indigo-100 shadow-sm"> 43 + <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity"> 22 44 <button 23 45 onClick={handleCopy} 24 - className="p-2 rounded-lg bg-white/80 hover:bg-white text-indigo-600 shadow-sm transition-all" 46 + className="p-1.5 rounded bg-white/80 hover:bg-white text-indigo-600 shadow-sm transition-all" 25 47 title="Copy to clipboard" 26 48 > 27 - {copied ? <Check size={18} /> : <Copy size={18} />} 49 + {copied ? <Check size={14} /> : <Copy size={14} />} 28 50 </button> 29 51 </div> 30 52 31 - <div className="flex items-start gap-3"> 32 - <div className="mt-1 p-1.5 bg-indigo-100 text-indigo-600 rounded-md"> 33 - <Sparkles size={20} /> 53 + <div className="flex items-start gap-2"> 54 + <div className="p-1 bg-indigo-100 text-indigo-600 rounded shrink-0"> 55 + <Sparkles size={16} /> 34 56 </div> 35 - <div className="prose prose-blue max-w-none"> 36 - <h3 className="text-sm font-semibold text-indigo-900 uppercase tracking-wider mb-2">Daily Summary</h3> 37 - <p className="text-slate-700 leading-relaxed whitespace-pre-wrap">{summary}</p> 57 + <div className="min-w-0"> 58 + <h3 className="text-xs font-semibold text-indigo-900 uppercase tracking-wider mb-2">Daily Summary</h3> 59 + {parsed ? ( 60 + <ul className="space-y-1.5"> 61 + {parsed.projects.map((project, i) => ( 62 + <li key={i} className="text-sm"> 63 + <span className="font-semibold text-slate-800">{project.name}:</span>{' '} 64 + <span className="text-slate-600">{project.summary}</span> 65 + </li> 66 + ))} 67 + </ul> 68 + ) : ( 69 + <p className="text-slate-700 text-sm leading-relaxed">{summary}</p> 70 + )} 38 71 </div> 39 72 </div> 40 73 </div>