feat(dashboard): add context sections for assets, dependencies, test context, and conflict detection

This commit is contained in:
catlog22
2025-12-06 21:04:50 +08:00
parent 5b14c8a832
commit a9a2004d4a
2 changed files with 426 additions and 4 deletions

View File

@@ -1328,10 +1328,40 @@ code {
gap: 1rem;
}
.context-section {
padding: 1rem;
background: hsl(var(--muted) / 0.3);
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
}
.context-section-title {
font-size: 1rem;
font-weight: 600;
color: hsl(var(--foreground));
margin-bottom: 0.75rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid hsl(var(--border));
}
.context-field {
padding: 0.75rem;
background: hsl(var(--muted));
border-radius: 0.375rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.context-label {
font-size: 0.75rem;
font-weight: 600;
color: hsl(var(--muted-foreground));
text-transform: uppercase;
letter-spacing: 0.025em;
}
.context-value {
font-size: 0.875rem;
color: hsl(var(--foreground));
line-height: 1.6;
}
.context-field label {
@@ -1712,6 +1742,21 @@ code {
line-height: 1.6;
}
/* Summary Item Direct (No collapse) */
.summary-item-direct {
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
padding: 1rem;
background: hsl(var(--card));
}
.summary-item-direct .summary-content-pre {
margin-top: 0.5rem;
padding: 0.75rem;
background: hsl(var(--muted));
border-radius: 0.375rem;
}
.markdown-content {
background: hsl(var(--muted));
padding: 1rem;

View File

@@ -943,11 +943,15 @@ function renderContextContent(context) {
const techStack = projectContext.tech_stack || metadata.tech_stack || {};
const codingConventions = projectContext.coding_conventions || {};
const architecturePatterns = projectContext.architecture_patterns || [];
const assets = context.assets || {};
const dependencies = context.dependencies || {};
const testContext = context.test_context || {};
const conflictDetection = context.conflict_detection || {};
return `
<div class="context-tab-content space-y-6">
<!-- Header with View JSON button -->
<div class="flex justify-between items-center">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-foreground">Context Package</h3>
<button class="px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors" onclick="openMarkdownModal('context-package.json', window._currentContextJson, 'json')">
👁️ View JSON
@@ -1024,6 +1028,46 @@ function renderContextContent(context) {
</div>
</div>
` : ''}
<!-- Assets Section -->
${Object.keys(assets).length > 0 ? `
<div class="context-section">
<h4 class="context-section-title">📚 Assets & Resources</h4>
<div class="space-y-4">
${renderAssetsSection(assets)}
</div>
</div>
` : ''}
<!-- Dependencies Section -->
${(dependencies.internal && dependencies.internal.length > 0) || (dependencies.external && dependencies.external.length > 0) ? `
<div class="context-section">
<h4 class="context-section-title">🔗 Dependencies</h4>
<div class="space-y-4">
${renderDependenciesSection(dependencies)}
</div>
</div>
` : ''}
<!-- Test Context Section -->
${Object.keys(testContext).length > 0 ? `
<div class="context-section">
<h4 class="context-section-title">🧪 Test Context</h4>
<div class="space-y-4">
${renderTestContextSection(testContext)}
</div>
</div>
` : ''}
<!-- Conflict Detection Section -->
${Object.keys(conflictDetection).length > 0 ? `
<div class="context-section">
<h4 class="context-section-title">⚠️ Conflict Detection & Risk Analysis</h4>
<div class="space-y-4">
${renderConflictDetectionSection(conflictDetection)}
</div>
</div>
` : ''}
</div>
`;
}
@@ -1148,6 +1192,339 @@ function renderCodingConventions(conventions) {
return sections.join('');
}
function renderAssetsSection(assets) {
const sections = [];
// Documentation
if (assets.documentation && assets.documentation.length > 0) {
sections.push(`
<div class="asset-category">
<h5 class="asset-category-title">📄 Documentation</h5>
<div class="asset-grid">
${assets.documentation.map(doc => `
<div class="asset-card">
<div class="asset-card-header">
<span class="asset-path">${escapeHtml(doc.path)}</span>
<span class="relevance-score" style="background: ${getRelevanceColor(doc.relevance_score)}">${(doc.relevance_score * 100).toFixed(0)}%</span>
</div>
<div class="asset-card-body">
<div class="asset-scope">${escapeHtml(doc.scope || '')}</div>
${doc.contains && doc.contains.length > 0 ? `
<div class="asset-tags">
${doc.contains.map(tag => `<span class="asset-tag">${escapeHtml(tag)}</span>`).join('')}
</div>
` : ''}
</div>
</div>
`).join('')}
</div>
</div>
`);
}
// Source Code
if (assets.source_code && assets.source_code.length > 0) {
sections.push(`
<div class="asset-category">
<h5 class="asset-category-title">💻 Source Code</h5>
<div class="asset-grid">
${assets.source_code.map(src => `
<div class="asset-card">
<div class="asset-card-header">
<span class="asset-path">${escapeHtml(src.path)}</span>
<span class="relevance-score" style="background: ${getRelevanceColor(src.relevance_score)}">${(src.relevance_score * 100).toFixed(0)}%</span>
</div>
<div class="asset-card-body">
<div class="asset-role-badge badge-${getRoleBadgeClass(src.role)}">${escapeHtml(src.role || '')}</div>
${src.exports && src.exports.length > 0 ? `
<div class="asset-meta"><strong>Exports:</strong> ${src.exports.map(e => `<code class="inline-code">${escapeHtml(e)}</code>`).join(', ')}</div>
` : ''}
${src.features && src.features.length > 0 ? `
<div class="asset-features">${src.features.map(f => `<span class="feature-tag">${escapeHtml(f)}</span>`).join('')}</div>
` : ''}
</div>
</div>
`).join('')}
</div>
</div>
`);
}
// Tests
if (assets.tests && assets.tests.length > 0) {
sections.push(`
<div class="asset-category">
<h5 class="asset-category-title">🧪 Tests</h5>
<div class="asset-grid">
${assets.tests.map(test => `
<div class="asset-card">
<div class="asset-card-header">
<span class="asset-path">${escapeHtml(test.path)}</span>
${test.test_count ? `<span class="test-count-badge">${test.test_count} tests</span>` : ''}
</div>
<div class="asset-card-body">
<div class="asset-type">${escapeHtml(test.type || '')}</div>
${test.test_classes ? `<div class="asset-meta"><strong>Classes:</strong> ${escapeHtml(test.test_classes.join(', '))}</div>` : ''}
${test.coverage ? `<div class="asset-meta"><strong>Coverage:</strong> ${escapeHtml(test.coverage)}</div>` : ''}
</div>
</div>
`).join('')}
</div>
</div>
`);
}
return sections.join('');
}
function renderDependenciesSection(dependencies) {
const sections = [];
// Internal Dependencies
if (dependencies.internal && dependencies.internal.length > 0) {
sections.push(`
<div class="dep-category">
<h5 class="dep-category-title">🔄 Internal Dependencies</h5>
<div class="dep-graph">
${dependencies.internal.slice(0, 10).map(dep => `
<div class="dep-item">
<div class="dep-from">${escapeHtml(dep.from)}</div>
<div class="dep-arrow">
<span class="dep-type-badge badge-${dep.type}">${escapeHtml(dep.type)}</span>
</div>
<div class="dep-to">${escapeHtml(dep.to)}</div>
</div>
`).join('')}
${dependencies.internal.length > 10 ? `<div class="dep-more">... and ${dependencies.internal.length - 10} more</div>` : ''}
</div>
</div>
`);
}
// External Dependencies
if (dependencies.external && dependencies.external.length > 0) {
sections.push(`
<div class="dep-category">
<h5 class="dep-category-title">📦 External Dependencies</h5>
<div class="dep-grid">
${dependencies.external.map(dep => `
<div class="dep-external-card">
<div class="dep-package-name">${escapeHtml(dep.package)}</div>
<div class="dep-version">${escapeHtml(dep.version || '')}</div>
<div class="dep-usage">${escapeHtml(dep.usage || '')}</div>
</div>
`).join('')}
</div>
</div>
`);
}
return sections.join('');
}
function renderTestContextSection(testContext) {
const sections = [];
// Test Frameworks
if (testContext.frameworks) {
const frameworks = testContext.frameworks;
sections.push(`
<div class="test-category">
<h5 class="test-category-title">🛠 Test Frameworks</h5>
<div class="test-frameworks-grid">
${frameworks.backend ? `
<div class="framework-card framework-installed">
<div class="framework-header">
<span class="framework-label">Backend</span>
<span class="framework-name">${escapeHtml(frameworks.backend.name || 'N/A')}</span>
</div>
${frameworks.backend.plugins ? `
<div class="framework-plugins">${frameworks.backend.plugins.map(p => `<span class="plugin-tag">${escapeHtml(p)}</span>`).join('')}</div>
` : ''}
</div>
` : ''}
${frameworks.frontend ? `
<div class="framework-card ${frameworks.frontend.name && frameworks.frontend.name.includes('NONE') ? 'framework-missing' : 'framework-installed'}">
<div class="framework-header">
<span class="framework-label">Frontend</span>
<span class="framework-name">${escapeHtml(frameworks.frontend.name || 'N/A')}</span>
</div>
${frameworks.frontend.recommended ? `<div class="framework-recommended">Recommended: ${escapeHtml(frameworks.frontend.recommended)}</div>` : ''}
${frameworks.frontend.gap ? `<div class="framework-gap">⚠️ ${escapeHtml(frameworks.frontend.gap)}</div>` : ''}
</div>
` : ''}
</div>
</div>
`);
}
// Existing Tests Statistics
if (testContext.existing_tests) {
const tests = testContext.existing_tests;
let totalTests = 0;
let totalClasses = 0;
if (tests.backend) {
if (tests.backend.integration) {
totalTests += tests.backend.integration.tests || 0;
totalClasses += tests.backend.integration.classes || 0;
}
if (tests.backend.api_endpoints) {
totalTests += tests.backend.api_endpoints.tests || 0;
totalClasses += tests.backend.api_endpoints.classes || 0;
}
}
sections.push(`
<div class="test-category">
<h5 class="test-category-title">📊 Test Statistics</h5>
<div class="test-stats-grid">
<div class="stat-card">
<div class="stat-value">${totalTests}</div>
<div class="stat-label">Total Tests</div>
</div>
<div class="stat-card">
<div class="stat-value">${totalClasses}</div>
<div class="stat-label">Test Classes</div>
</div>
${testContext.coverage_config && testContext.coverage_config.target ? `
<div class="stat-card">
<div class="stat-value">${escapeHtml(testContext.coverage_config.target)}</div>
<div class="stat-label">Coverage Target</div>
</div>
` : ''}
</div>
</div>
`);
}
// Test Markers
if (testContext.test_markers) {
sections.push(`
<div class="test-category">
<h5 class="test-category-title">🏷 Test Markers</h5>
<div class="test-markers-grid">
${Object.entries(testContext.test_markers).map(([marker, desc]) => `
<div class="marker-card">
<span class="marker-name">@${escapeHtml(marker)}</span>
<span class="marker-desc">${escapeHtml(desc)}</span>
</div>
`).join('')}
</div>
</div>
`);
}
return sections.join('');
}
function renderConflictDetectionSection(conflictDetection) {
const sections = [];
// Risk Level Indicator
if (conflictDetection.risk_level) {
const riskLevel = conflictDetection.risk_level;
const riskColor = riskLevel === 'high' ? '#ef4444' : riskLevel === 'medium' ? '#f59e0b' : '#10b981';
sections.push(`
<div class="risk-indicator" style="border-color: ${riskColor}">
<div class="risk-level" style="background: ${riskColor}">
${escapeHtml(riskLevel.toUpperCase())} RISK
</div>
${conflictDetection.mitigation_strategy ? `
<div class="risk-mitigation">
<strong>Mitigation Strategy:</strong> ${escapeHtml(conflictDetection.mitigation_strategy)}
</div>
` : ''}
</div>
`);
}
// Risk Factors
if (conflictDetection.risk_factors) {
const factors = conflictDetection.risk_factors;
sections.push(`
<div class="conflict-category">
<h5 class="conflict-category-title"> Risk Factors</h5>
<div class="risk-factors-list">
${factors.test_gaps && factors.test_gaps.length > 0 ? `
<div class="risk-factor">
<strong class="risk-factor-title">Test Gaps:</strong>
<ul class="risk-factor-items">
${factors.test_gaps.map(gap => `<li>${escapeHtml(gap)}</li>`).join('')}
</ul>
</div>
` : ''}
${factors.existing_implementations && factors.existing_implementations.length > 0 ? `
<div class="risk-factor">
<strong class="risk-factor-title">Existing Implementations:</strong>
<ul class="risk-factor-items">
${factors.existing_implementations.map(impl => `<li>${escapeHtml(impl)}</li>`).join('')}
</ul>
</div>
` : ''}
</div>
</div>
`);
}
// Affected Modules
if (conflictDetection.affected_modules && conflictDetection.affected_modules.length > 0) {
sections.push(`
<div class="conflict-category">
<h5 class="conflict-category-title">📦 Affected Modules</h5>
<div class="affected-modules-grid">
${conflictDetection.affected_modules.map(mod => `
<span class="affected-module-tag">${escapeHtml(mod)}</span>
`).join('')}
</div>
</div>
`);
}
// Historical Conflicts
if (conflictDetection.historical_conflicts && conflictDetection.historical_conflicts.length > 0) {
sections.push(`
<div class="conflict-category">
<h5 class="conflict-category-title">📜 Historical Lessons</h5>
<div class="historical-conflicts-list">
${conflictDetection.historical_conflicts.map(conflict => `
<div class="historical-conflict-card">
<div class="conflict-source">Source: ${escapeHtml(conflict.source || 'Unknown')}</div>
${conflict.lesson ? `<div class="conflict-lesson"><strong>Lesson:</strong> ${escapeHtml(conflict.lesson)}</div>` : ''}
${conflict.recommendation ? `<div class="conflict-recommendation"><strong>Recommendation:</strong> ${escapeHtml(conflict.recommendation)}</div>` : ''}
${conflict.challenge ? `<div class="conflict-challenge"><strong>Challenge:</strong> ${escapeHtml(conflict.challenge)}</div>` : ''}
</div>
`).join('')}
</div>
</div>
`);
}
return sections.join('');
}
// Helper functions
function getRelevanceColor(score) {
if (score >= 0.95) return '#10b981';
if (score >= 0.90) return '#3b82f6';
if (score >= 0.80) return '#f59e0b';
return '#6b7280';
}
function getRoleBadgeClass(role) {
const roleMap = {
'core-hook': 'primary',
'api-client': 'success',
'api-router': 'info',
'service-layer': 'warning',
'pydantic-schemas': 'secondary',
'orm-model': 'secondary',
'typescript-types': 'info'
};
return roleMap[role] || 'secondary';
}
async function loadAndRenderSummaryTab(session, contentArea) {
contentArea.innerHTML = '<div class="tab-loading">Loading summaries...</div>';