HTML 보고서 레거시 양식 호환을 보강한다
- TemplateService 공통 CSS에 h4/dl/matrix/comparison/board_report/metrics/roadmap 레거시 블록 호환 스타일을 추가해 raw body 기반 HTML 보고서의 뒤쪽 섹션도 동일한 폰트 크기와 카드 양식을 유지하도록 수정 - roadmap 내부 timeline/owner 배지가 전역 timeline 스타일과 충돌하던 문제를 override로 분리하고 다크 모드·모바일 레이아웃까지 함께 정리 - HtmlSkillLegacyBodyCompatibilityTests를 추가하고 dotnet build 및 HtmlSkillLegacyBodyCompatibilityTests|HtmlSkillConsultingSectionsTests 통과로 검증
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class HtmlSkillLegacyBodyCompatibilityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_WithLegacyBodyBlocks_ShouldInjectCompatibilityStyles()
|
||||
{
|
||||
var workDir = Path.Combine(Path.GetTempPath(), "ax-html-legacy-body-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(workDir);
|
||||
|
||||
try
|
||||
{
|
||||
var tool = new HtmlSkill();
|
||||
var context = new AgentContext
|
||||
{
|
||||
WorkFolder = workDir,
|
||||
Permission = "Auto",
|
||||
OperationMode = "external",
|
||||
};
|
||||
|
||||
var args = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"path": "legacy-report.html",
|
||||
"title": "Legacy Report",
|
||||
"body": "<h2>Overview</h2><div class=\"matrix\"><h3>Matrix</h3><div class=\"quadrant\"><h4>Quick Wins</h4><ul><li>Automate reporting</li></ul></div><div class=\"quadrant\"><h4>Strategic Bets</h4><ul><li>Rebuild workflow</li></ul></div></div><div class=\"board_report\"><h3>Board Ask</h3><div class=\"decision-summary\"><p>Approve phase 2</p></div><div class=\"metrics\"><div class=\"metric-item\"><span class=\"metric-label\">Margin</span><span class=\"metric-value\">+4.2pt</span><span class=\"metric-note\">pilot mix</span></div></div><div class=\"roadmap\"><div class=\"phase\"><h4>Phase 1</h4><p>Prepare rollout</p><span class=\"timeline\">Q2</span><span class=\"owner\">PMO</span></div></div></div><dl><dt>HBM</dt><dd>High bandwidth memory</dd></dl>"
|
||||
}
|
||||
""").RootElement;
|
||||
|
||||
var result = await tool.ExecuteAsync(args, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeTrue();
|
||||
|
||||
var html = File.ReadAllText(Path.Combine(workDir, "legacy-report.html"));
|
||||
html.Should().Contain(".matrix .quadrant");
|
||||
html.Should().Contain(".board_report");
|
||||
html.Should().Contain(".metric-item");
|
||||
html.Should().Contain(".roadmap .phase .timeline");
|
||||
html.Should().Contain("h4 { font-size: 13.5px;");
|
||||
html.Should().Contain("dt { font-size: 13px;");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(workDir))
|
||||
Directory.Delete(workDir, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1038,10 +1038,65 @@ public static class TemplateService
|
||||
padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }
|
||||
.card-header { font-size: 15px; font-weight: 700; margin-bottom: 8px; }
|
||||
|
||||
/* ── 레거시 보고서 블록 호환 스타일 ── */
|
||||
h4 { font-size: 13.5px; font-weight: 700; margin: 16px 0 8px; color: #1f4b7a; }
|
||||
h5 { font-size: 12.5px; font-weight: 700; margin: 12px 0 6px; color: #334155; }
|
||||
h6 { font-size: 12px; font-weight: 700; margin: 10px 0 6px; color: #475569; }
|
||||
dl { margin: 14px 0; }
|
||||
dt { font-size: 13px; font-weight: 700; color: #003366; margin-top: 10px; }
|
||||
dd { margin: 4px 0 10px; padding-left: 12px; font-size: 13.5px; color: #4b5563; }
|
||||
.matrix, .comparison, .decision_matrix, .board_report, .roadmap { margin: 18px 0 24px; }
|
||||
.matrix { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
||||
.matrix > h3 { grid-column: 1 / -1; }
|
||||
.matrix .quadrant { border: 1px solid #e5e7eb; border-radius: 12px; padding: 14px 16px;
|
||||
background: linear-gradient(180deg, #fff, #f8fafc); min-height: 168px; }
|
||||
.matrix .quadrant h4 { margin-top: 0; }
|
||||
.comparison { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
|
||||
.comparison .comparison-item { border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px 18px;
|
||||
background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.decision_matrix { margin-top: 16px; }
|
||||
.board_report { border: 1px solid #dbe4f0; border-radius: 16px; padding: 18px 20px;
|
||||
background: linear-gradient(180deg, #f8fbff, #fff);
|
||||
box-shadow: 0 10px 26px rgba(15,23,42,.05); }
|
||||
.board_report > h3 { margin-top: 0; }
|
||||
.board_report .decision-summary { padding: .9rem 1rem; border-radius: 12px; background: #eef2ff;
|
||||
color: #312e81; margin: .8rem 0 1rem; }
|
||||
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: .85rem;
|
||||
margin: 1rem 0 1.25rem; }
|
||||
.metric-item { border: 1px solid #dbe4f0; border-radius: 14px; padding: .9rem 1rem; background: #fff;
|
||||
box-shadow: 0 8px 24px rgba(15,23,42,.05); display: flex; flex-direction: column; gap: .25rem; }
|
||||
.metric-label { font-size: .78rem; font-weight: 700; color: #64748b; text-transform: uppercase;
|
||||
letter-spacing: .05em; }
|
||||
.metric-value { font-size: 1.35rem; font-weight: 800; color: #0f172a; }
|
||||
.metric-note { font-size: .82rem; color: #475569; line-height: 1.55; }
|
||||
.risks, .next-steps { margin-top: 1rem; padding: .95rem 1rem; border-radius: 14px; background: #fff;
|
||||
border: 1px solid #e5e7eb; }
|
||||
.risks h4, .next-steps h4 { margin-top: 0; }
|
||||
.roadmap { display: grid; gap: .9rem; }
|
||||
.roadmap .phase { display: grid; grid-template-columns: 160px 1fr; gap: 1rem; align-items: start;
|
||||
border: 1px solid #e5e7eb; border-radius: 14px; padding: 1rem 1.1rem;
|
||||
background: linear-gradient(180deg, #fff, #f8fafc); }
|
||||
.roadmap .phase h4 { margin: 0 0 .35rem; }
|
||||
.roadmap .phase p { margin: 0; }
|
||||
.roadmap .phase .timeline,
|
||||
.roadmap .phase .owner { position: static; padding-left: 0; margin: 0; display: inline-flex;
|
||||
align-items: center; width: auto; line-height: 1.2; }
|
||||
.roadmap .phase .timeline::before,
|
||||
.roadmap .phase .owner::before { content: none; }
|
||||
.roadmap .phase .timeline { margin: 0 0 .35rem; padding: .28rem .62rem; border-radius: 999px;
|
||||
background: #eef2ff; color: #4338ca; font-size: .82rem; font-weight: 700; }
|
||||
.roadmap .phase .owner { padding: .28rem .62rem; border-radius: 999px; background: #f3f4f6;
|
||||
color: #475569; font-size: .82rem; font-weight: 600; }
|
||||
|
||||
/* ── 구분선 ── */
|
||||
.divider { border: none; border-top: 1px solid #e5e7eb; margin: 32px 0; }
|
||||
.divider-thick { border: none; border-top: 3px solid #e5e7eb; margin: 40px 0; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.matrix { grid-template-columns: 1fr; }
|
||||
.roadmap .phase { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ── 인쇄/PDF 최적화 ── */
|
||||
@media print {
|
||||
.ax-theme-toggle, .ax-fab-toc { display: none !important; }
|
||||
@@ -1056,6 +1111,29 @@ public static class TemplateService
|
||||
a[href]::after { content: ' (' attr(href) ')'; font-size: 10px; color: #999; }
|
||||
.no-print { display: none !important; }
|
||||
}
|
||||
|
||||
[data-theme="dark"] h4,
|
||||
[data-theme="dark"] h5,
|
||||
[data-theme="dark"] h6 { color: #e2e8f0; }
|
||||
[data-theme="dark"] dt { color: #cbd5e1; }
|
||||
[data-theme="dark"] dd { color: #94a3b8; }
|
||||
[data-theme="dark"] .matrix .quadrant,
|
||||
[data-theme="dark"] .comparison .comparison-item,
|
||||
[data-theme="dark"] .board_report,
|
||||
[data-theme="dark"] .metric-item,
|
||||
[data-theme="dark"] .risks,
|
||||
[data-theme="dark"] .next-steps,
|
||||
[data-theme="dark"] .roadmap .phase {
|
||||
background: #1e293b;
|
||||
border-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
[data-theme="dark"] .board_report .decision-summary { background: rgba(99,102,241,0.14); color: #c7d2fe; }
|
||||
[data-theme="dark"] .metric-label { color: #94a3b8; }
|
||||
[data-theme="dark"] .metric-value { color: #f8fafc; }
|
||||
[data-theme="dark"] .metric-note { color: #cbd5e1; }
|
||||
[data-theme="dark"] .roadmap .phase .timeline { background: rgba(99,102,241,0.18); color: #c7d2fe; }
|
||||
[data-theme="dark"] .roadmap .phase .owner { background: rgba(148,163,184,0.18); color: #cbd5e1; }
|
||||
""";
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user