refactor: replace chromedp with browser native print for PDF export
Remove chromedp dependency, use window.print() instead. Docker image no longer needs Chromium (~200MB smaller). - Delete services/pdf.go and templates/reports/review.html - Remove PDF API route POST /api/repos/:id/review/pdf - Add @media print CSS to review page - Remove chromium from Dockerfile
This commit is contained in:
@@ -10,6 +10,61 @@
|
||||
<script src="/static/js/diff-viewer.js"></script>
|
||||
<script src="/static/js/review-inline.js"></script>
|
||||
<script src="/static/js/note-editor.js"></script>
|
||||
<style>
|
||||
@media print {
|
||||
/* Hide non-content elements */
|
||||
nav, footer, #review-actions, #btn-review, #btn-toggle-diff,
|
||||
.note-save-indicator, select, input, #progress,
|
||||
#diff-review-container, #history-select,
|
||||
label[for="base-ref"], label[for="head-ref"],
|
||||
label[for="top-n"], label[for="concurrency"] { display: none !important; }
|
||||
|
||||
/* Show results and print header even if hidden */
|
||||
#results { display: block !important; }
|
||||
.print-header { display: block !important; }
|
||||
|
||||
/* Page setup */
|
||||
body { background: white !important; padding: 0 !important; font-size: 12px; }
|
||||
main { max-width: 100% !important; padding: 16px !important; }
|
||||
|
||||
/* Cards without shadow */
|
||||
.bg-white { box-shadow: none !important; border: 1px solid #e5e7eb; }
|
||||
|
||||
/* Avoid page breaks inside cards */
|
||||
.bg-white.rounded-lg { break-inside: avoid; }
|
||||
|
||||
/* File review cards */
|
||||
#file-reviews > div { break-inside: avoid; margin-bottom: 12px; }
|
||||
|
||||
/* Note editors: show content as plain text */
|
||||
.note-textarea {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
resize: none !important;
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
white-space: pre-wrap;
|
||||
background: #f0f9ff !important;
|
||||
padding: 8px !important;
|
||||
border-left: 3px solid #3b82f6 !important;
|
||||
}
|
||||
|
||||
/* Suggestion cards: ensure colors print */
|
||||
.suggestion-card { break-inside: avoid; }
|
||||
* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
|
||||
|
||||
/* Score badge */
|
||||
.rounded-full { border: 1px solid currentColor; }
|
||||
|
||||
/* Branch info as text */
|
||||
#base-ref, #head-ref, #top-n, #concurrency {
|
||||
display: inline !important;
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
{{template "nav" .}}
|
||||
@@ -71,6 +126,16 @@
|
||||
|
||||
<!-- Results -->
|
||||
<div id="results" class="hidden">
|
||||
<!-- Print-only header -->
|
||||
<div class="print-header hidden mb-6">
|
||||
<h1 style="font-size:20px;font-weight:700;color:#111827;margin-bottom:8px;">PR-Helper 审查报告</h1>
|
||||
<p style="color:#6b7280;font-size:12px;">
|
||||
📦 仓库: {{.RepoURL}}
|
||||
🌿 分支: <span class="print-base-ref"></span> → <span class="print-head-ref"></span>
|
||||
📅 <span class="print-date"></span>
|
||||
</p>
|
||||
<hr style="margin-top:12px;border:none;border-top:1px solid #e5e7eb;">
|
||||
</div>
|
||||
<!-- Summary -->
|
||||
<div id="summary" class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">审查总结</h2>
|
||||
@@ -357,6 +422,7 @@
|
||||
document.getElementById('review-actions').classList.remove('hidden');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '开始审查';
|
||||
updatePrintHeader();
|
||||
}, 500);
|
||||
|
||||
// Render inline suggestions if diff is available
|
||||
@@ -466,6 +532,17 @@
|
||||
return Markdown.render(text);
|
||||
}
|
||||
|
||||
// Update print-only header with current branch info
|
||||
function updatePrintHeader() {
|
||||
const baseEl = document.getElementById('base-ref');
|
||||
const headEl = document.getElementById('head-ref');
|
||||
const baseText = baseEl?.options[baseEl.selectedIndex]?.text || baseEl?.value || '';
|
||||
const headText = headEl?.options[headEl.selectedIndex]?.text || headEl?.value || '';
|
||||
document.querySelectorAll('.print-base-ref').forEach(el => el.textContent = baseText);
|
||||
document.querySelectorAll('.print-head-ref').forEach(el => el.textContent = headText);
|
||||
document.querySelectorAll('.print-date').forEach(el => el.textContent = new Date().toLocaleString('zh-CN'));
|
||||
}
|
||||
|
||||
// ── History loading ────────────────────────────────────────
|
||||
|
||||
async function loadHistoryList() {
|
||||
@@ -539,6 +616,7 @@
|
||||
// Show results
|
||||
document.getElementById('results').classList.remove('hidden');
|
||||
document.getElementById('review-actions').classList.remove('hidden');
|
||||
updatePrintHeader();
|
||||
|
||||
// Load diff and render inline
|
||||
const base = data.base_ref;
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>PR-Helper 审查报告</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #1f2937;
|
||||
padding: 40px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 { font-size: 22px; margin-bottom: 8px; color: #111827; }
|
||||
h2 { font-size: 16px; margin: 24px 0 12px; color: #111827; border-bottom: 2px solid #e5e7eb; padding-bottom: 6px; }
|
||||
h3 { font-size: 14px; margin: 16px 0 8px; color: #374151; }
|
||||
p { margin-bottom: 8px; }
|
||||
.meta { color: #6b7280; font-size: 12px; margin-bottom: 20px; }
|
||||
.meta span { display: inline-block; margin-right: 16px; }
|
||||
.score { font-size: 28px; font-weight: 700; color: #2563eb; }
|
||||
.score-label { font-size: 14px; color: #6b7280; margin-left: 4px; }
|
||||
.section { margin-bottom: 24px; padding: 16px; border: 1px solid #e5e7eb; border-radius: 8px; }
|
||||
.severity { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; margin-right: 6px; }
|
||||
.severity-critical { background: #fef2f2; color: #991b1b; border: 1px solid #fecaca; }
|
||||
.severity-warning { background: #fefce8; color: #854d0e; border: 1px solid #fef08a; }
|
||||
.severity-info { background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; }
|
||||
.file-section { margin-bottom: 16px; padding: 12px; border: 1px solid #e5e7eb; border-radius: 6px; }
|
||||
.file-header { font-weight: 600; font-family: monospace; font-size: 13px; margin-bottom: 8px; color: #111827; }
|
||||
.suggestion { margin: 8px 0; padding: 8px 12px; border-radius: 6px; font-size: 12px; }
|
||||
.suggestion-critical { border-left: 3px solid #ef4444; background: #fef2f2; }
|
||||
.suggestion-warning { border-left: 3px solid #f59e0b; background: #fefce8; }
|
||||
.suggestion-info { border-left: 3px solid #22c55e; background: #f0fdf4; }
|
||||
.note { margin-top: 8px; padding: 8px 12px; background: #f0f9ff; border-left: 3px solid #3b82f6; font-size: 12px; border-radius: 4px; }
|
||||
.note-label { font-size: 11px; color: #6b7280; margin-bottom: 4px; }
|
||||
.overall-note { padding: 12px 16px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; margin-top: 12px; }
|
||||
pre { background: #f8fafc; border: 1px solid #e2e8f0; padding: 10px; border-radius: 6px; font-family: monospace; font-size: 12px; overflow-x: auto; margin: 8px 0; white-space: pre-wrap; word-wrap: break-word; }
|
||||
code { font-family: monospace; background: #f1f5f9; padding: 1px 4px; border-radius: 3px; font-size: 12px; }
|
||||
pre code { background: none; padding: 0; }
|
||||
.footer { margin-top: 40px; padding-top: 16px; border-top: 1px solid #e5e7eb; text-align: center; color: #9ca3af; font-size: 11px; }
|
||||
@media print {
|
||||
body { padding: 20px; }
|
||||
.section { break-inside: avoid; }
|
||||
.file-section { break-inside: avoid; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>PR-Helper 审查报告</h1>
|
||||
|
||||
<div class="meta">
|
||||
<span>📦 仓库: {{.RepoURL}}</span>
|
||||
<span>🌿 分支: {{.BaseRef}} → {{.HeadRef}}</span>
|
||||
<span>📅 审查时间: {{.ReviewedAt}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Overall Assessment -->
|
||||
<div class="section">
|
||||
<h2>整体评估</h2>
|
||||
{{if .Score}}
|
||||
<div style="margin-bottom: 12px;">
|
||||
<span class="score">{{.Score}}</span><span class="score-label">/10</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Overall}}
|
||||
<p><strong>总体评价:</strong> {{.Overall}}</p>
|
||||
{{end}}
|
||||
{{if .Findings}}
|
||||
<p><strong>主要发现:</strong></p>
|
||||
<p>{{.Findings}}</p>
|
||||
{{end}}
|
||||
{{if .Recommendations}}
|
||||
<p><strong>改进建议:</strong></p>
|
||||
<p>{{.Recommendations}}</p>
|
||||
{{end}}
|
||||
{{if .OverallNotes}}
|
||||
<div class="overall-note">
|
||||
<div class="note-label">💬 用户备注:</div>
|
||||
{{range .OverallNotes}}
|
||||
<p>{{.}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- File Reviews -->
|
||||
{{if .FileReviews}}
|
||||
<h2>逐文件审查</h2>
|
||||
{{range .FileReviews}}
|
||||
<div class="file-section">
|
||||
<div class="file-header">📄 {{.FileName}}
|
||||
{{if .ChangeLines}}<span style="font-weight:normal;color:#6b7280;font-size:12px;">({{.ChangeLines}} 行变更)</span>{{end}}
|
||||
</div>
|
||||
{{range .Suggestions}}
|
||||
<div class="suggestion suggestion-{{.Severity}}">
|
||||
<span class="severity severity-{{.Severity}}">{{.SeverityCN}}</span>
|
||||
<strong>{{.Description}}</strong>
|
||||
{{if .Suggestion}}<p style="margin-top:4px;">建议: {{.Suggestion}}</p>{{end}}
|
||||
{{if .CodeExample}}<pre><code>{{.CodeExample}}</code></pre>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{range .Notes}}
|
||||
<div class="note">
|
||||
<div class="note-label">💬 备注:</div>
|
||||
<p>{{.}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<!-- Raw result fallback if no structured data -->
|
||||
{{if and (not .FileReviews) .Result}}
|
||||
<div class="section">
|
||||
<h2>审查详情</h2>
|
||||
<pre>{{.Result}}</pre>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="footer">
|
||||
PR-Helper — AI 代码审查报告 · {{.ReviewedAt}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user