diff --git a/src/ui/webviewPanel.ts b/src/ui/webviewPanel.ts index 2c9417b..c570c34 100644 --- a/src/ui/webviewPanel.ts +++ b/src/ui/webviewPanel.ts @@ -820,3 +820,552 @@ export class ProofCodeWebviewPanel { } .routes-section-card { + background: rgba(30, 41, 59, 0.4); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 8px; + padding: 12px 16px; + margin-top: 16px; + } + + .route-item-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.04); + } + + .route-item-row:last-child { + border-bottom: none; + } + + .route-badge { + font-size: 10px; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + background: rgba(59, 130, 246, 0.2); + color: #60A5FA; + margin-right: 8px; + } + + /* Rules Table */ + .rules-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; + } + + .rules-table th, .rules-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid var(--card-border); + } + + .rules-table th { + color: var(--text-muted); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .empty-state { + text-align: center; + padding: 48px; + color: var(--text-muted); + } + + + + +
+
+ +
+

ProofCode

+
AI writes code. ProofCode proves the change.
+
+
+
+ + + +
+
+ +
+
+
Verification Score
+
+ + + + +
${score}%
+
+
${verdictText}
+
+ +
+
+
Files Changed
+
${summary.filesChanged}
+
+${summary.linesAdded} / -${summary.linesDeleted} lines
+
+
+
Functions Affected
+
${summary.symbolsAffected}
+
${summary.apiRoutesAffected} API routes
+
+
+
Critical Risks
+
+ ${summary.highRiskCount} +
+
+${summary.mediumRiskCount} medium, ${summary.lowRiskCount} low
+
+
+
Untested Callers
+
+ ${impact?.untestedCallersCount ?? 0} +
+
${impact?.totalCallersCount ?? 0} total callers
+
+
+
+ + + + +
+ ${ + risks.length === 0 + ? `
+

✅ No Static Risks Detected

+

No SQL injection, missing authorization, hardcoded secrets, or unhandled errors found in changed files.

+
` + : risks + .map( + (r) => ` +
+
+
+ ${r.severity} + ${r.title} +
+ +
+

${r.description}

+ + ${ + r.evidenceTrace.length > 0 + ? `
Evidence Trace:\n${r.evidenceTrace + .map((t, idx) => ` [Step ${idx + 1}] Line ${t.line}: ${t.description}`) + .join('\n')}` + : '' + }
+ + ${ + r.snippet + ? `
${escapeHtml(r.snippet)}
` + : '' + } + +
+ Action: ${r.recommendation} +
+
+ ` + ) + .join('') + } +
+ + +
+ ${ + breakage.length === 0 + ? `
+

✅ No Regression Breakage Risks Detected

+

No modified core services with untested dependents, critical route vulnerabilities, or rule breaches found.

+
` + : breakage + .map( + (b) => ` +
+
+
+ ${b.severity} + ${b.area}: ${b.trigger} +
+ ${ + b.evidenceFile + ? `` + : '' + } +
+

Impact: ${b.detail}

+ ${ + b.affectedCallersOrRoutes.length > 0 + ? `
+ Dependents at risk: ${b.affectedCallersOrRoutes + .map( + (c) => + `${c}` + ) + .join(' ')} +
` + : '' + } +
+ ` + ) + .join('') + } +
+ + +
+ ${ + !impact || impact.changedSymbols.length === 0 + ? `

⚡ No exported symbols were modified.

` + : ` + +
+ +
+ + + +
+
+ + +
+ ${impact.changedSymbols + .map((sym) => { + const untestedCallers = sym.callers.filter((c) => !c.hasTest); + return ` +
+
+
+
ƒ
+
+
+ ${escapeHtml(sym.symbolName)} + ${sym.kind} + ${sym.isApiRoute ? 'API ROUTE' : ''} +
+
+ in ${escapeHtml(path.relative(report?.repoRoot || '', sym.filePath))} +
+
+
+
+ ${sym.callers.length} Callers + ${untestedCallers.length} Untested + +
+
+ + + ${ + sym.callers.length === 0 + ? `
+ No downstream callers detected in repository (leaf module or top-level entrypoint). +
` + : ` +
+ ${sym.callers + .map( + (c) => ` +
+
+ ↳ + ${escapeHtml(c.callerName)} + ${escapeHtml(path.basename(c.filePath))}:${c.line} + ${ + c.hasTest + ? `✓ Test Covered` + : `⚠️ Untested Caller` + } +
+ +
+ ` + ) + .join('')} +
+ ` + } +
+ `; + }) + .join('')} +
+ + + ${ + impact.affectedRoutes.length > 0 + ? ` +
+
+ 🌐 Directly Affected API Endpoints (${impact.affectedRoutes.length}) +
+ ${impact.affectedRoutes + .map( + (r) => ` +
+
+ ${escapeHtml(r.method)} + ${escapeHtml(r.routePath)} + (${escapeHtml(path.basename(r.filePath))}:${r.line}) +
+ +
+ ` + ) + .join('')} +
+ ` + : '' + } + ` + } +
+ + +
+
+ + + + + + + + + + + ${ + rules.length === 0 + ? '' + : rules + .map( + (r) => ` + + + + + + + ` + ) + .join('') + } + +
StatusProject RuleEvidence / DetailsAction
No rules defined. Click "Edit Rules" to create .proofcode/rules.md.
+ + ${r.status} + + + ${r.rule.title} +
${r.rule.description}
+
+ ${r.detectedMessage || 'Compliant with policy.'} + + ${ + r.file && r.line + ? `` + : '—' + } +
+
+
+ + +
+
+ + + + + + + + + + ${ + checks + ? ` + + + + + + + + + + + + + + + + + + + + + ` + : '' + } + +
Check NameStatusDetails
${checks.typescript.name}${checks.typescript.status}${checks.typescript.message || 'Pass'}
${checks.eslint.name}${checks.eslint.status}${checks.eslint.message || 'Pass'}
${checks.unitTests.name}${checks.unitTests.status}${checks.unitTests.message || 'Pass'}
${checks.build.name}${checks.build.status}${checks.build.message || 'Pass'}
No live checks configured yet.
+
+
+ + + +`; + } +} + +function escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +}