6464
6565 const result = await model.generateContent(prompt);
6666 const report = result.response.text();
67+
68+ // Escape HTML to prevent XSS
69+ const escapeHtml = (text) => {
70+ const map = {
71+ '&': '&',
72+ '<': '<',
73+ '>': '>',
74+ '"': '"',
75+ "'": '''
76+ };
77+ return text.replace(/[&<>"']/g, m => map[m]);
78+ };
79+
80+ // Convert markdown to HTML safely
81+ const markdownToHtml = (md) => {
82+ let html = escapeHtml(md);
83+ html = html.replace(/^### (.*?)$/gm, '<h3>$1</h3>');
84+ html = html.replace(/^## (.*?)$/gm, '<h2>$1</h2>');
85+ html = html.replace(/^# (.*?)$/gm, '<h1>$1</h1>');
86+ html = html.replace(/\n\n/g, '</p><p>');
87+ html = html.replace(/^- (.*?)$/gm, '<li>$1</li>');
88+ html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
89+ html = html.replace(/\`(.*?)\`/g, '<code>$1</code>');
90+ html = '<p>' + html + '</p>';
91+ return html;
92+ };
6793
6894 const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
6995 const reportName = `report-${timestamp}.html`;
@@ -75,221 +101,221 @@ jobs:
75101 <meta charset="UTF-8">
76102 <meta name="viewport" content="width=device-width, initial-scale=1.0">
77103 <title>Gemini Code Review</title>
78- <link rel="stylesheet" href="/style.css">
104+ <link rel="stylesheet" href=".. /style.css">
79105 <style>
80- .container {
81- max-width: 900px ;
82- margin: 2rem auto;
106+ .report- container {
107+ max-width: 800px ;
108+ margin: 1.5rem auto;
83109 background: var(--bg-light);
84- border-radius: 8px ;
110+ border-radius: 6px ;
85111 border: 1px solid var(--border);
86112 overflow: hidden;
87113 }
88- .header {
114+ .report- header {
89115 background: var(--bg);
90116 color: var(--text);
91- padding: 3rem 2rem;
117+ padding: 2rem 1.5rem ;
92118 text-align: center;
93119 border-bottom: 1px solid var(--border);
94120 }
95- .header h1 {
96- font-size: 2.5rem ;
97- margin-bottom: 0.5rem ;
121+ .report- header h1 {
122+ font-size: 1.8rem ;
123+ margin-bottom: 0.3rem ;
98124 font-weight: 700;
99125 color: var(--primary);
100126 }
101- .header p {
102- font-size: 1rem ;
127+ .report- header p {
128+ font-size: 0.9rem ;
103129 color: var(--text-muted);
104130 }
105- .timestamp {
106- font-size: 0.85rem ;
107- margin-top: 1rem ;
131+ .report- timestamp {
132+ font-size: 0.75rem ;
133+ margin-top: 0.5rem ;
108134 color: var(--text-muted);
109135 }
110- .content {
111- padding: 2.5rem;
136+ .report-content {
137+ padding: 1.5rem;
138+ font-size: 0.95rem;
112139 }
113- .content h2 {
140+ .report- content h2 {
114141 color: var(--primary);
115- margin-top: 2rem;
116- margin-bottom: 1rem;
117- font-size: 1.5rem;
118- border-left: 3px solid var(--primary);
119- padding-left: 1rem;
120- }
121- .content h3 {
122- color: var(--secondary);
123142 margin-top: 1.5rem;
124143 margin-bottom: 0.8rem;
125- font-size: 1.2rem;
144+ font-size: 1.3rem;
145+ border-left: 3px solid var(--primary);
146+ padding-left: 0.8rem;
126147 }
127- .content h4 {
128- color: var(--text-muted);
129- margin-top: 1rem;
130- margin-bottom: 0.5rem;
131- font-size: 1rem;
148+ .report- content h3 {
149+ color: var(--secondary);
150+ margin-top: 1rem;
151+ margin-bottom: 0.5rem;
152+ font-size: 1. 1rem;
132153 }
133- .content p {
134- margin-bottom: 1rem ;
154+ .report- content p {
155+ margin-bottom: 0.8rem ;
135156 color: var(--text);
136157 }
137- .content ul, .content ol {
138- margin-left: 2rem ;
139- margin-bottom: 1rem ;
158+ .report- content ul {
159+ margin-left: 1.5rem ;
160+ margin-bottom: 0.8rem ;
140161 }
141- .content li {
142- margin-bottom: 0.5rem ;
162+ .report- content li {
163+ margin-bottom: 0.3rem ;
143164 color: var(--text);
144165 }
145- .content code {
166+ .report- content code {
146167 background: var(--bg);
147- padding: 0.2rem 0.5rem ;
148- border-radius: 4px ;
168+ padding: 0.15rem 0.4rem ;
169+ border-radius: 3px ;
149170 font-family: 'Courier New', monospace;
150171 color: var(--secondary);
151- font-size: 0.9em ;
172+ font-size: 0.85em ;
152173 }
153- .content pre {
174+ .report- content pre {
154175 background: var(--bg-dark);
155176 color: var(--text);
156- padding: 1.5rem ;
157- border-radius: 6px ;
177+ padding: 1rem ;
178+ border-radius: 4px ;
158179 overflow-x: auto;
159- margin: 1rem 0;
180+ margin: 0.8rem 0;
160181 font-family: 'Courier New', monospace;
161- font-size: 0.85rem ;
182+ font-size: 0.8rem ;
162183 border: 1px solid var(--border);
163184 }
164- .content pre code {
165- background: none;
166- padding: 0;
167- color: inherit;
168- }
169- .footer {
185+ .report-footer {
170186 background: var(--bg);
171- padding: 1.5rem ;
187+ padding: 1rem ;
172188 text-align: center;
173189 color: var(--text-muted);
174190 border-top: 1px solid var(--border);
175- font-size: 0.9rem ;
191+ font-size: 0.8rem ;
176192 }
177- .footer a {
193+ .report- footer a {
178194 color: var(--primary);
179195 text-decoration: none;
180196 }
181- .footer a:hover {
197+ .report- footer a:hover {
182198 color: var(--secondary);
183199 }
184- @media (max-width: 768px) {
185- .header h1 { font-size: 1.8rem; }
186- .content { padding: 1.5rem; }
187- }
188200 </style>
189201 </head>
190202 <body>
191- <div class="container">
192- <div class="header">
203+ <div class="report- container">
204+ <div class="report- header">
193205 <h1>🔍 Code Review</h1>
194- <p>Gemini Analysis Report </p>
195- <p class="timestamp">${date.toUTCString()}</p>
206+ <p>Gemini Analysis</p>
207+ <p class="report- timestamp">${date.toUTCString()}</p>
196208 </div>
197- <div class="content">
198- ${report}
209+ <div class="report- content">
210+ ${markdownToHtml( report) }
199211 </div>
200- <div class="footer">
212+ <div class="report- footer">
201213 <p><a href="index.html">← Back to Reports</a></p>
202214 </div>
203215 </div>
204216 </body>
205217 </html>`;
206218
207- fs.writeFileSync(reportName, html);
219+ // Create reports directory if it doesn't exist
220+ if (!fs.existsSync("reports")) {
221+ fs.mkdirSync("reports", { recursive: true });
222+ }
208223
224+ fs.writeFileSync(\`reports/\${reportName}\`, html);
225+
226+ // Create or update index.html for reports
227+ let indexPath = "reports/index.html";
209228 let index = "";
210- let reportsHTML = "";
211-
212- if (fs.existsSync("report.html")) {
213- index = fs.readFileSync("report.html", "utf8");
229+
230+ if (fs.existsSync(indexPath)) {
231+ index = fs.readFileSync(indexPath, "utf8");
214232 } else {
215- index = `<!DOCTYPE html>
233+ index = \ `<!DOCTYPE html>
216234 <html lang="en" data-theme="dark">
217235 <head>
218236 <meta charset="UTF-8">
219237 <meta name="viewport" content="width=device-width, initial-scale=1.0">
220238 <title>Code Reviews</title>
221- <link rel="stylesheet" href="/style.css">
239+ <link rel="stylesheet" href=".. /style.css">
222240 <style>
223- .reports -container {
224- max-width: 1200px ;
225- margin: 2rem auto;
226- padding: 0 2rem ;
241+ .reviews -container {
242+ max-width: 900px ;
243+ margin: 1.5rem auto;
244+ padding: 0 1rem ;
227245 }
228- .reports -header {
246+ .reviews -header {
229247 text-align: center;
230- margin-bottom: 3rem ;
248+ margin-bottom: 2rem ;
231249 }
232- .reports -header h1 {
233- font-size: 2.5rem ;
250+ .reviews -header h1 {
251+ font-size: 2rem ;
234252 color: var(--primary);
235- margin-bottom: 0.5rem ;
253+ margin-bottom: 0.3rem ;
236254 }
237- .reports -header p {
255+ .reviews -header p {
238256 color: var(--text-muted);
239- font-size: 1rem ;
257+ font-size: 0.95rem ;
240258 }
241- .reports -grid {
259+ .reviews -grid {
242260 display: grid;
243- grid-template-columns: repeat(auto-fill, minmax(300px , 1fr));
244- gap: 1.5rem ;
261+ grid-template-columns: repeat(auto-fill, minmax(250px , 1fr));
262+ gap: 1rem ;
245263 }
246- .report -card {
264+ .review -card {
247265 background: var(--bg-light);
248266 border: 1px solid var(--border);
249- border-radius: 8px ;
250- padding: 1.5rem ;
267+ border-radius: 6px ;
268+ padding: 1.2rem ;
251269 text-decoration: none;
252270 color: inherit;
253271 display: flex;
254272 flex-direction: column;
255273 transition: all 0.3s ease;
256274 }
257- .report -card:hover {
275+ .review -card:hover {
258276 border-color: var(--primary);
259277 background: var(--bg);
260278 }
261- .report -card h2 {
279+ .review -card h3 {
262280 color: var(--primary);
263- margin-bottom: 0.5rem ;
264- font-size: 1.2rem ;
281+ margin-bottom: 0.3rem ;
282+ font-size: 1rem ;
265283 }
266- .report -card .date {
284+ .review -card .date {
267285 color: var(--text-muted);
268- font-size: 0.85rem ;
286+ font-size: 0.8rem ;
269287 }
270288 @media (max-width: 768px) {
271- .reports -header h1 { font-size: 1.8rem ; }
272- .reports -grid {
289+ .reviews -header h1 { font-size: 1.5rem ; }
290+ .reviews -grid {
273291 grid-template-columns: 1fr;
274292 }
275293 }
276294 </style>
277295 </head>
278296 <body>
279- <div class="reports -container">
280- <div class="reports -header">
297+ <div class="reviews -container">
298+ <div class="reviews -header">
281299 <h1>Code Reviews</h1>
282300 <p>Gemini-powered analysis reports</p>
283301 </div>
284- <div class="reports -grid" id="reports "></div>
302+ <div class="reviews -grid" id="reviews "></div>
285303 </div>
286304 </body>
287- </html>`;
305+ </html>\ `;
288306 }
289307
290- const button = `<a href="./${reportName}" class="report-card"><h2>📝 Report</h2><p class="date">${date.toUTCString()}</p></a>`;
291- index = index.replace('<div class="reports-grid" id="reports">', `<div class="reports-grid" id="reports">\n ${button}`);
292- fs.writeFileSync("report.html", index);
308+ // Get all existing reports
309+ const files = fs.readdirSync("reports").filter(f => f.startsWith("report-") && f.endsWith(".html"));
310+ files.sort().reverse();
311+
312+ let cardsHtml = files.map(file => {
313+ const displayName = file.replace("report-", "").replace(".html", "").replace(/[T-]/g, " ").trim();
314+ return \`<a href="./ file\\" class="review-card"><h3>📝 Review</h3><p class="date">\${displayName}</p></a>\`;
315+ }).join("\\n ");
316+
317+ index = index.replace('<div class="reviews-grid" id="reviews"></div>', \`<div class="reviews-grid">\${cardsHtml}</div>\`);
318+ fs.writeFileSync(indexPath, index);
293319
294320 console.log("=== Gemini Code Review Complete ===");
295321 console.log(report);
@@ -302,15 +328,14 @@ jobs:
302328 run : |
303329 git config user.name "gemini-bot"
304330 git config user.email "gemini@actions"
305- git add report*.html report.html
331+ git add reports/
306332 git commit -m "Add Gemini code report" || echo "No changes to commit"
307333 mkdir -p /tmp/pages-repo
308334 cd /tmp/pages-repo
309335 git clone https://x-access-token:${GH_PAGES_PAT}@github.com/Mod-Sauce/mod-sauce.github.io.git .
310- cp -r $GITHUB_WORKSPACE/report*.html .
311- cp $GITHUB_WORKSPACE/report.html .
336+ cp -r $GITHUB_WORKSPACE/reports .
312337 git config user.name "gemini-bot"
313338 git config user.email "gemini@actions"
314- git add report*.html report.html
339+ git add reports/
315340 git commit -m "Add Gemini code report" || echo "No changes to commit"
316341 git push origin main
0 commit comments