@@ -7,11 +7,12 @@ export interface WeeklyBriefData {
77 tldr : string [ ] ;
88 riskyChanges : string [ ] ;
99 suggestedActions : string [ ] ;
10- schedule : 'weekly' | 'biweekly' ;
10+ schedule : 'weekly' | 'biweekly' | 'manual' ;
1111}
1212
1313/**
1414 * Builds the HTML email for weekly/biweekly maintainer brief
15+ * Light theme matching the RepoMind platform UI
1516 */
1617export function buildWeeklyBriefEmail ( data : WeeklyBriefData ) : { subject : string ; html : string } {
1718 const frontendUrl = config . FRONTEND_URL ;
@@ -29,25 +30,30 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
2930 <meta name="viewport" content="width=device-width, initial-scale=1.0">
3031 <title>${ subject } </title>
3132</head>
32- <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #0a0a0a ; color: #fafafa ;">
33- <table width="100%" cellpadding="0" cellspacing="0" style="background-color: #0a0a0a ; padding: 40px 20px;">
33+ <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f8fafc ; color: #1e293b ;">
34+ <table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f8fafc ; padding: 40px 20px;">
3435 <tr>
3536 <td align="center">
36- <table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; background-color: #111111 ; border-radius: 16px; border: 1px solid #262626 ; overflow: hidden;">
37+ <table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; background-color: #ffffff ; border-radius: 16px; border: 1px solid #e2e8f0; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) ; overflow: hidden;">
3738
3839 <!-- Header -->
3940 <tr>
40- <td style="padding: 32px 32px 24px 32px; border-bottom: 1px solid #262626 ;">
41+ <td style="padding: 32px 32px 24px 32px; border-bottom: 1px solid #e2e8f0 ;">
4142 <table width="100%" cellpadding="0" cellspacing="0">
4243 <tr>
4344 <td>
44- <div style="font-size: 24px; font-weight: 700; color: #fafafa; margin-bottom: 8px;">
45- 📋 ${ frequency } Brief
45+ <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
46+ <div style="width: 48px; height: 48px; background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); border-radius: 12px; display: inline-flex; align-items: center; justify-content: center;">
47+ <span style="font-size: 24px;">📋</span>
48+ </div>
4649 </div>
47- <div style="font-size: 16px; color: #a1a1aa;">
50+ <div style="font-size: 24px; font-weight: 700; color: #1e293b; margin-bottom: 8px;">
51+ ${ frequency } Brief
52+ </div>
53+ <div style="font-size: 16px; color: #475569; font-weight: 500;">
4854 ${ data . repoFullName }
4955 </div>
50- <div style="font-size: 14px; color: #71717a ; margin-top: 4px;">
56+ <div style="font-size: 14px; color: #94a3b8 ; margin-top: 4px;">
5157 ${ data . date }
5258 </div>
5359 </td>
@@ -59,14 +65,14 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
5965 <!-- TL;DR Section -->
6066 <tr>
6167 <td style="padding: 24px 32px;">
62- <div style="font-size: 14px ; font-weight: 600 ; color: #a78bfa ; text-transform: uppercase; letter-spacing: 0.5px ; margin-bottom: 16px;">
68+ <div style="font-size: 12px ; font-weight: 700 ; color: #6366f1 ; text-transform: uppercase; letter-spacing: 1px ; margin-bottom: 16px;">
6369 TL;DR
6470 </div>
6571 <table width="100%" cellpadding="0" cellspacing="0">
6672 ${ data . tldr . map ( item => `
6773 <tr>
68- <td style="padding: 8px 0; color: #e4e4e7 ; font-size: 15px; line-height: 1.6;">
69- <span style="color: #a78bfa ; margin-right: 8px ;">•</span>
74+ <td style="padding: 10px 0; color: #334155 ; font-size: 15px; line-height: 1.6;">
75+ <span style="color: #6366f1 ; margin-right: 10px; font-weight: 600 ;">•</span>
7076 ${ escapeHtml ( item ) }
7177 </td>
7278 </tr>
@@ -79,14 +85,14 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
7985 <!-- Risky Changes Section -->
8086 <tr>
8187 <td style="padding: 0 32px 24px 32px;">
82- <div style="background-color: rgba(239, 68, 68, 0.1) ; border: 1px solid rgba(239, 68, 68, 0.2) ; border-radius: 12px; padding: 20px;">
83- <div style="font-size: 14px ; font-weight: 600 ; color: #ef4444 ; text-transform: uppercase; letter-spacing: 0.5px ; margin-bottom: 12px;">
88+ <div style="background-color: #fef2f2 ; border: 1px solid #fecaca ; border-radius: 12px; padding: 20px;">
89+ <div style="font-size: 12px ; font-weight: 700 ; color: #dc2626 ; text-transform: uppercase; letter-spacing: 1px ; margin-bottom: 12px;">
8490 ⚠️ Risky Changes
8591 </div>
8692 <table width="100%" cellpadding="0" cellspacing="0">
8793 ${ data . riskyChanges . map ( item => `
8894 <tr>
89- <td style="padding: 6px 0; color: #fca5a5 ; font-size: 14px; line-height: 1.5;">
95+ <td style="padding: 8px 0; color: #991b1b ; font-size: 14px; line-height: 1.5;">
9096 • ${ escapeHtml ( item ) }
9197 </td>
9298 </tr>
@@ -101,14 +107,14 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
101107 <!-- Suggested Actions Section -->
102108 <tr>
103109 <td style="padding: 0 32px 24px 32px;">
104- <div style="font-size: 14px ; font-weight: 600 ; color: #22c55e ; text-transform: uppercase; letter-spacing: 0.5px ; margin-bottom: 12px ;">
110+ <div style="font-size: 12px ; font-weight: 700 ; color: #16a34a ; text-transform: uppercase; letter-spacing: 1px ; margin-bottom: 16px ;">
105111 ✅ Suggested Actions
106112 </div>
107113 <table width="100%" cellpadding="0" cellspacing="0">
108114 ${ data . suggestedActions . slice ( 0 , 3 ) . map ( ( item , idx ) => `
109115 <tr>
110- <td style="padding: 8px 0; color: #e4e4e7 ; font-size: 14px; line-height: 1.5;">
111- <span style="display: inline-block; width: 24px ; height: 24px ; background-color: #262626 ; border-radius: 50%; text-align: center; line-height: 24px ; font-size: 12px ; font-weight: 600; color: #a1a1aa ; margin-right: 12px;">${ idx + 1 } </span>
116+ <td style="padding: 10px 0; color: #334155 ; font-size: 14px; line-height: 1.5;">
117+ <span style="display: inline-block; width: 26px ; height: 26px ; background-color: #f1f5f9 ; border-radius: 50%; text-align: center; line-height: 26px ; font-size: 13px ; font-weight: 600; color: #64748b ; margin-right: 12px;">${ idx + 1 } </span>
112118 ${ escapeHtml ( item ) }
113119 </td>
114120 </tr>
@@ -123,13 +129,13 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
123129 <td style="padding: 8px 32px 32px 32px;">
124130 <table width="100%" cellpadding="0" cellspacing="0">
125131 <tr>
126- <td style="padding-right: 8px;">
127- <a href="${ briefUrl } " style="display: block; text-align: center; padding: 14px 24px; background: linear-gradient(135deg, #a78bfa 0%, #8b5cf6 100%); color: #ffffff; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 14px;">
132+ <td style="padding-right: 8px; width: 50%; ">
133+ <a href="${ briefUrl } " style="display: block; text-align: center; padding: 14px 24px; background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); color: #ffffff; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 14px;">
128134 View Full Brief
129135 </a>
130136 </td>
131- <td style="padding-left: 8px;">
132- <a href="${ briefUrl } ?tab=issues" style="display: block; text-align: center; padding: 14px 24px; background-color: #262626 ; color: #fafafa ; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid #3f3f46 ;">
137+ <td style="padding-left: 8px; width: 50%; ">
138+ <a href="${ briefUrl } ?tab=issues" style="display: block; text-align: center; padding: 14px 24px; background-color: #f1f5f9 ; color: #334155 ; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid #e2e8f0 ;">
133139 Good First Issues
134140 </a>
135141 </td>
@@ -140,11 +146,11 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
140146
141147 <!-- Footer -->
142148 <tr>
143- <td style="padding: 24px 32px; border-top: 1px solid #262626 ; background-color: #0a0a0a ;">
144- <div style="font-size: 13px; color: #71717a ; line-height: 1.6;">
145- You're receiving this because you enabled ${ data . schedule } delivery for <strong style="color: #a1a1aa ;">${ data . repoFullName } </strong>.
149+ <td style="padding: 24px 32px; border-top: 1px solid #e2e8f0 ; background-color: #f8fafc ;">
150+ <div style="font-size: 13px; color: #64748b ; line-height: 1.6;">
151+ You're receiving this because you enabled ${ data . schedule } delivery for <strong style="color: #475569 ;">${ data . repoFullName } </strong>.
146152 <br>
147- <a href="${ settingsUrl } " style="color: #a78bfa ; text-decoration: none;">Manage notification settings →</a>
153+ <a href="${ settingsUrl } " style="color: #6366f1 ; text-decoration: none; font-weight: 500 ;">Manage notification settings →</a>
148154 </div>
149155 </td>
150156 </tr>
@@ -155,8 +161,8 @@ export function buildWeeklyBriefEmail(data: WeeklyBriefData): { subject: string;
155161 <table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; margin-top: 24px;">
156162 <tr>
157163 <td align="center">
158- <div style="font-size: 14px; color: #52525b ;">
159- Powered by <strong style="color: #a78bfa ;">RepoMind</strong>
164+ <div style="font-size: 14px; color: #94a3b8 ;">
165+ Powered by <strong style="color: #6366f1 ;">RepoMind</strong>
160166 </div>
161167 </td>
162168 </tr>
@@ -187,34 +193,30 @@ function escapeHtml(text: string): string {
187193 * Extracts TL;DR bullets from maintainer brief markdown
188194 */
189195export function extractTldr ( markdown : string ) : string [ ] {
190- // Look for the Summary section
191- const summaryMatch = markdown . match ( / # # \s * S u m m a r y \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
192- if ( ! summaryMatch ) {
193- // Fallback: try to find any bullet points at the start
194- const bullets = markdown . match ( / ^ [ \s ] * [ - • * ] \s * ( .+ ) $ / gm ) ;
196+ // Look for TL;DR or Summary section
197+ const tldrMatch = markdown . match ( / # # \s * (?: T L ; D R | S u m m a r y | O v e r v i e w ) \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
198+ if ( tldrMatch ) {
199+ const content = tldrMatch [ 1 ] ;
200+ const bullets = content . match ( / [ - • * ] \s * ( .+ ) / g ) ;
195201 if ( bullets ) {
196- return bullets . slice ( 0 , 5 ) . map ( b => b . replace ( / ^ [ \s ] * [ - • * ] \s * / , '' ) . trim ( ) ) ;
202+ return bullets . slice ( 0 , 5 ) . map ( b => b . replace ( / ^ [ - • * ] \s * / , '' ) . trim ( ) ) ;
197203 }
198- return [ 'Analysis complete. View the full brief for details.' ] ;
199204 }
200-
201- const summaryContent = summaryMatch [ 1 ] ;
202- const bullets = summaryContent . match ( / [ - • * ] \s * ( .+ ) / g) ;
203205
204- if ( ! bullets ) {
205- // If no bullets, take first few sentences
206- const sentences = summaryContent . split ( / [ . ! ? ] + / ) . filter ( s => s . trim ( ) ) ;
207- return sentences . slice ( 0 , 5 ) . map ( s => s . trim ( ) ) ;
206+ // Fallback: try to find any bullet points at the start
207+ const bullets = markdown . match ( / ^ [ \s ] * [ - • * ] \s * ( . + ) $ / gm ) ;
208+ if ( bullets ) {
209+ return bullets . slice ( 0 , 5 ) . map ( b => b . replace ( / ^ [ \s ] * [ - • * ] \s * / , '' ) . trim ( ) ) ;
208210 }
209-
210- return bullets . slice ( 0 , 5 ) . map ( b => b . replace ( / ^ [ - • * ] \s * / , '' ) . trim ( ) ) ;
211+
212+ return [ 'Analysis complete. View the full brief for details.' ] ;
211213}
212214
213215/**
214216 * Extracts risky changes from maintainer brief markdown
215217 */
216218export function extractRiskyChanges ( markdown : string ) : string [ ] {
217- const riskyMatch = markdown . match ( / # # \s * R i s k y C h a n g e s \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
219+ const riskyMatch = markdown . match ( / # # \s * (?: R i s k y C h a n g e s | R i s k | R i s k s | C o n c e r n s ) \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
218220 if ( ! riskyMatch ) return [ ] ;
219221
220222 const content = riskyMatch [ 1 ] ;
@@ -229,7 +231,7 @@ export function extractRiskyChanges(markdown: string): string[] {
229231 * Extracts suggested actions from maintainer brief markdown
230232 */
231233export function extractSuggestedActions ( markdown : string ) : string [ ] {
232- const actionsMatch = markdown . match ( / # # \s * S u g g e s t e d A c t i o n s \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
234+ const actionsMatch = markdown . match ( / # # \s * (?: S u g g e s t e d A c t i o n s | A c t i o n s | R e c o m m e n d a t i o n s | N e x t S t e p s ) \s * \n ( [ \s \S ] * ?) (? = \n # # | \n # | $ ) / i) ;
233235 if ( ! actionsMatch ) return [ ] ;
234236
235237 const content = actionsMatch [ 1 ] ;
@@ -240,4 +242,3 @@ export function extractSuggestedActions(markdown: string): string[] {
240242
241243 return items . slice ( 0 , 3 ) . map ( item => item . replace ( / ^ (?: \d + \. \s * | [ - • * ] \s * ) / , '' ) . trim ( ) ) ;
242244}
243-
0 commit comments